From fd72597999b61843a176d1e8a8b0d15a68d69e1e Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Sun, 27 Jun 2021 00:17:13 +0430 Subject: [PATCH] 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 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 :^) --- .../LibWeb/Bindings/ExceptionOrUtils.h | 10 +- Userland/Libraries/LibWeb/DOM/Document.cpp | 4 +- Userland/Libraries/LibWeb/DOM/ExceptionOr.h | 96 +++++++++++++------ Userland/Libraries/LibWeb/DOM/Node.cpp | 2 +- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/Userland/Libraries/LibWeb/Bindings/ExceptionOrUtils.h b/Userland/Libraries/LibWeb/Bindings/ExceptionOrUtils.h index 94d5bf62f1c..eb089070af4 100644 --- a/Userland/Libraries/LibWeb/Bindings/ExceptionOrUtils.h +++ b/Userland/Libraries/LibWeb/Bindings/ExceptionOrUtils.h @@ -24,7 +24,10 @@ template ALWAYS_INLINE bool throw_dom_exception(JS::VM& vm, JS::GlobalObject& global_object, DOM::ExceptionOr& result) { if (result.is_exception()) { - vm.throw_exception(global_object, DOMExceptionWrapper::create(global_object, const_cast(result.exception()))); + result.materialized_exception(global_object) + .visit( + [&](NonnullRefPtr 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 { using Type = JS::Value; }; +template<> +struct ExtractExceptionOrValueType> { + using Type = JS::Value; +}; + template<> struct ExtractExceptionOrValueType> { using Type = JS::Value; diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 68824b541a1..660828084f9 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -224,7 +224,7 @@ ExceptionOr 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(replace_result.exception()); + return replace_result.exception(); return {}; } @@ -234,7 +234,7 @@ ExceptionOr Document::set_body(HTML::HTMLElement& new_body) auto append_result = document_element->append_child(new_body); if (append_result.is_exception()) - return NonnullRefPtr(append_result.exception()); + return append_result.exception(); return {}; } diff --git a/Userland/Libraries/LibWeb/DOM/ExceptionOr.h b/Userland/Libraries/LibWeb/DOM/ExceptionOr.h index df1e32cee78..5b5da29b837 100644 --- a/Userland/Libraries/LibWeb/DOM/ExceptionOr.h +++ b/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 class ExceptionOr { public: + ExceptionOr() requires(IsSame) = default; + ExceptionOr(const ValueType& result) : m_result(result) { @@ -26,8 +46,18 @@ public: { } - ExceptionOr(const NonnullRefPtr exception) - : m_exception(exception) + ExceptionOr(NonnullRefPtr exception) + : m_exception(move(exception)) + { + } + + ExceptionOr(SimpleException exception) + : m_exception(move(exception)) + { + } + + ExceptionOr(Variant> exception) + : m_exception(move(exception).template downcast>()) { } @@ -35,56 +65,60 @@ public: ExceptionOr(const ExceptionOr& other) = default; ~ExceptionOr() = default; - ValueType& value() + ValueType& value() requires(!IsSame) { return m_result.value(); } - ValueType release_value() + ValueType release_value() requires(!IsSame) { return m_result.release_value(); } - const DOMException& exception() const + Variant> exception() const { - return *m_exception; + return m_exception.template downcast>(); + } + + auto materialized_exception(JS::GlobalObject& global_object) const + { +#define E(x) JS::x*, + using ResultType = Variant>; +#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 const& exception) -> ResultType { return exception; }, + [](Empty) -> ResultType { VERIFY_NOT_REACHED(); }); } bool is_exception() const { - return m_exception; + return !m_exception.template has(); } private: Optional m_result; - RefPtr m_exception; + // https://heycam.github.io/webidl/#idl-exceptions + Variant> m_exception { Empty {} }; }; template<> -class ExceptionOr { +class ExceptionOr : public ExceptionOr { public: - ExceptionOr(const NonnullRefPtr 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 m_exception; + using ExceptionOr::ExceptionOr; }; } diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp index 058cd1f459e..aacf3cc5b5d 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.cpp +++ b/Userland/Libraries/LibWeb/DOM/Node.cpp @@ -267,7 +267,7 @@ ExceptionOr> Node::pre_insert(NonnullRefPtr node, RefP { auto validity_result = ensure_pre_insertion_validity(node, child); if (validity_result.is_exception()) - return NonnullRefPtr(validity_result.exception()); + return validity_result.exception(); auto reference_child = child; if (reference_child == node)