Bläddra i källkod

LibWeb: Make ExceptionOr capable of holding all error types in the spec

The WebIDL spec specifies a few "simple" exception types in addition to
the DOMException type, let's support all of those.
This allows functions returning ExceptionOr<T> to throw regular
javascript exceptions (as limited by the webidl spec) by returning a
`DOM::SimpleException { DOM::SimpleExceptionType::T, "error message" }`
which is pretty damn cool :^)
Ali Mohammad Pur 4 år sedan
förälder
incheckning
fd72597999

+ 9 - 1
Userland/Libraries/LibWeb/Bindings/ExceptionOrUtils.h

@@ -24,7 +24,10 @@ template<typename T>
 ALWAYS_INLINE bool throw_dom_exception(JS::VM& vm, JS::GlobalObject& global_object, DOM::ExceptionOr<T>& result)
 {
     if (result.is_exception()) {
-        vm.throw_exception(global_object, DOMExceptionWrapper::create(global_object, const_cast<DOM::DOMException&>(result.exception())));
+        result.materialized_exception(global_object)
+            .visit(
+                [&](NonnullRefPtr<DOM::DOMException> dom_exception) { vm.throw_exception(global_object, DOMExceptionWrapper::create(global_object, move(dom_exception))); },
+                [&](auto* js_exception) { vm.throw_exception(global_object, js_exception); });
         return true;
     }
     return false;
@@ -47,6 +50,11 @@ struct ExtractExceptionOrValueType<void> {
     using Type = JS::Value;
 };
 
+template<>
+struct ExtractExceptionOrValueType<DOM::ExceptionOr<Empty>> {
+    using Type = JS::Value;
+};
+
 template<>
 struct ExtractExceptionOrValueType<DOM::ExceptionOr<void>> {
     using Type = JS::Value;

+ 2 - 2
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -224,7 +224,7 @@ ExceptionOr<void> Document::set_body(HTML::HTMLElement& new_body)
     if (existing_body) {
         auto replace_result = existing_body->parent()->replace_child(new_body, *existing_body);
         if (replace_result.is_exception())
-            return NonnullRefPtr<DOMException>(replace_result.exception());
+            return replace_result.exception();
         return {};
     }
 
@@ -234,7 +234,7 @@ ExceptionOr<void> Document::set_body(HTML::HTMLElement& new_body)
 
     auto append_result = document_element->append_child(new_body);
     if (append_result.is_exception())
-        return NonnullRefPtr<DOMException>(append_result.exception());
+        return append_result.exception();
     return {};
 }
 

+ 65 - 31
Userland/Libraries/LibWeb/DOM/ExceptionOr.h

@@ -13,9 +13,29 @@
 
 namespace Web::DOM {
 
+#define ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E) \
+    E(EvalError)                                   \
+    E(RangeError)                                  \
+    E(ReferenceError)                              \
+    E(TypeError)                                   \
+    E(URIError)
+
+#define E(x) x,
+enum class SimpleExceptionType {
+    ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E)
+};
+#undef E
+
+struct SimpleException {
+    SimpleExceptionType type;
+    String message;
+};
+
 template<typename ValueType>
 class ExceptionOr {
 public:
+    ExceptionOr() requires(IsSame<ValueType, Empty>) = default;
+
     ExceptionOr(const ValueType& result)
         : m_result(result)
     {
@@ -26,8 +46,18 @@ public:
     {
     }
 
-    ExceptionOr(const NonnullRefPtr<DOMException> exception)
-        : m_exception(exception)
+    ExceptionOr(NonnullRefPtr<DOMException> exception)
+        : m_exception(move(exception))
+    {
+    }
+
+    ExceptionOr(SimpleException exception)
+        : m_exception(move(exception))
+    {
+    }
+
+    ExceptionOr(Variant<SimpleException, NonnullRefPtr<DOMException>> exception)
+        : m_exception(move(exception).template downcast<Empty, SimpleException, NonnullRefPtr<DOMException>>())
     {
     }
 
@@ -35,56 +65,60 @@ public:
     ExceptionOr(const ExceptionOr& other) = default;
     ~ExceptionOr() = default;
 
-    ValueType& value()
+    ValueType& value() requires(!IsSame<ValueType, Empty>)
     {
         return m_result.value();
     }
 
-    ValueType release_value()
+    ValueType release_value() requires(!IsSame<ValueType, Empty>)
     {
         return m_result.release_value();
     }
 
-    const DOMException& exception() const
+    Variant<SimpleException, NonnullRefPtr<DOMException>> exception() const
     {
-        return *m_exception;
+        return m_exception.template downcast<SimpleException, NonnullRefPtr<DOMException>>();
+    }
+
+    auto materialized_exception(JS::GlobalObject& global_object) const
+    {
+#define E(x) JS::x*,
+        using ResultType = Variant<ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E) NonnullRefPtr<DOMException>>;
+#undef E
+
+        return m_exception.visit(
+            [&](SimpleException& exception) -> ResultType {
+                switch (exception.type) {
+#define E(x)                     \
+    case SimpleExceptionType::x: \
+        return JS::x::create(global_object, exception.message);
+
+                    ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E)
+
+#undef E
+                default:
+                    VERIFY_NOT_REACHED();
+                }
+            },
+            [&](NonnullRefPtr<DOMException> const& exception) -> ResultType { return exception; },
+            [](Empty) -> ResultType { VERIFY_NOT_REACHED(); });
     }
 
     bool is_exception() const
     {
-        return m_exception;
+        return !m_exception.template has<Empty>();
     }
 
 private:
     Optional<ValueType> m_result;
-    RefPtr<DOMException> m_exception;
+    // https://heycam.github.io/webidl/#idl-exceptions
+    Variant<Empty, SimpleException, NonnullRefPtr<DOMException>> m_exception { Empty {} };
 };
 
 template<>
-class ExceptionOr<void> {
+class ExceptionOr<void> : public ExceptionOr<Empty> {
 public:
-    ExceptionOr(const NonnullRefPtr<DOMException> exception)
-        : m_exception(exception)
-    {
-    }
-
-    ExceptionOr() = default;
-    ExceptionOr(ExceptionOr&& other) = default;
-    ExceptionOr(const ExceptionOr& other) = default;
-    ~ExceptionOr() = default;
-
-    const DOMException& exception() const
-    {
-        return *m_exception;
-    }
-
-    bool is_exception() const
-    {
-        return m_exception;
-    }
-
-private:
-    RefPtr<DOMException> m_exception;
+    using ExceptionOr<Empty>::ExceptionOr;
 };
 
 }

+ 1 - 1
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -267,7 +267,7 @@ ExceptionOr<NonnullRefPtr<Node>> Node::pre_insert(NonnullRefPtr<Node> node, RefP
 {
     auto validity_result = ensure_pre_insertion_validity(node, child);
     if (validity_result.is_exception())
-        return NonnullRefPtr<DOMException>(validity_result.exception());
+        return validity_result.exception();
 
     auto reference_child = child;
     if (reference_child == node)