Browse Source

LibWeb: Add and use a helper to reject a promise with an exception

Sam Atkins 1 year ago
parent
commit
4bdb7dba8c

+ 6 - 12
Userland/Libraries/LibWeb/Crypto/SubtleCrypto.cpp

@@ -122,10 +122,8 @@ JS::NonnullGCPtr<JS::Promise> SubtleCrypto::digest(AlgorithmIdentifier const& al
 
     // 2. Let data be the result of getting a copy of the bytes held by the data parameter passed to the digest() method.
     auto data_buffer_or_error = WebIDL::get_buffer_source_copy(*data->raw_object());
-    if (data_buffer_or_error.is_error()) {
-        auto promise = WebIDL::create_rejected_promise(realm, WebIDL::OperationError::create(realm, "Failed to copy bytes from ArrayBuffer"_fly_string));
-        return verify_cast<JS::Promise>(*promise->promise());
-    }
+    if (data_buffer_or_error.is_error())
+        return WebIDL::create_rejected_promise_from_exception(realm, WebIDL::OperationError::create(realm, "Failed to copy bytes from ArrayBuffer"_fly_string));
     auto data_buffer = data_buffer_or_error.release_value();
 
     // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and op set to "digest".
@@ -133,10 +131,8 @@ JS::NonnullGCPtr<JS::Promise> SubtleCrypto::digest(AlgorithmIdentifier const& al
 
     // 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
     // FIXME: Spec bug: link to https://webidl.spec.whatwg.org/#a-promise-rejected-with
-    if (normalized_algorithm.is_error()) {
-        auto promise = WebIDL::create_rejected_promise(realm, normalized_algorithm.release_error().release_value().value());
-        return verify_cast<JS::Promise>(*promise->promise());
-    }
+    if (normalized_algorithm.is_error())
+        return WebIDL::create_rejected_promise_from_exception(realm, normalized_algorithm.release_error());
 
     // 5. Let promise be a new Promise.
     auto promise = WebIDL::create_promise(realm);
@@ -219,10 +215,8 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Promise>> SubtleCrypto::import_key(Bi
     auto normalized_algorithm = normalize_an_algorithm(algorithm, "importKey"_string);
 
     // 6. If an error occurred, return a Promise rejected with normalizedAlgorithm.
-    if (normalized_algorithm.is_error()) {
-        auto promise = WebIDL::create_rejected_promise(realm, normalized_algorithm.release_error().release_value().value());
-        return verify_cast<JS::Promise>(*promise->promise());
-    }
+    if (normalized_algorithm.is_error())
+        return WebIDL::create_rejected_promise_from_exception(realm, normalized_algorithm.release_error());
 
     // 7. Let promise be a new Promise.
     auto promise = WebIDL::create_promise(realm);

+ 2 - 3
Userland/Libraries/LibWeb/Fetch/Body.cpp

@@ -155,9 +155,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> consume_body(JS::Realm& realm
 {
     // 1. If object is unusable, then return a promise rejected with a TypeError.
     if (object.is_unusable()) {
-        auto exception = JS::TypeError::create(realm, "Body is unusable"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise().ptr()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "Body is unusable"sv };
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 2. Let promise be a new promise.

+ 4 - 11
Userland/Libraries/LibWeb/FileAPI/Blob.cpp

@@ -367,11 +367,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> Blob::text()
 
     // 2. Let reader be the result of getting a reader from stream. If that threw an exception, return a new promise rejected with that exception.
     auto reader_or_exception = acquire_readable_stream_default_reader(*stream);
-    if (reader_or_exception.is_exception()) {
-        auto throw_completion = Bindings::dom_exception_to_throw_completion(vm, reader_or_exception.exception());
-        auto promise_capability = WebIDL::create_rejected_promise(realm, *throw_completion.value());
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise().ptr()) };
-    }
+    if (reader_or_exception.is_exception())
+        return WebIDL::create_rejected_promise_from_exception(realm, reader_or_exception.release_error());
     auto reader = reader_or_exception.release_value();
 
     // 3. Let promise be the result of reading all bytes from stream with reader
@@ -393,18 +390,14 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> Blob::text()
 WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> Blob::array_buffer()
 {
     auto& realm = this->realm();
-    auto& vm = realm.vm();
 
     // 1. Let stream be the result of calling get stream on this.
     auto stream = TRY(this->get_stream());
 
     // 2. Let reader be the result of getting a reader from stream. If that threw an exception, return a new promise rejected with that exception.
     auto reader_or_exception = acquire_readable_stream_default_reader(*stream);
-    if (reader_or_exception.is_exception()) {
-        auto throw_completion = Bindings::dom_exception_to_throw_completion(vm, reader_or_exception.exception());
-        auto promise_capability = WebIDL::create_rejected_promise(realm, *throw_completion.value());
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise().ptr()) };
-    }
+    if (reader_or_exception.is_exception())
+        return WebIDL::create_rejected_promise_from_exception(realm, reader_or_exception.release_error());
     auto reader = reader_or_exception.release_value();
 
     // 3. Let promise be the result of reading all bytes from stream with reader.

+ 2 - 4
Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp

@@ -340,10 +340,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> HTMLMediaElement::play()
     // 2. If the media element's error attribute is not null and its code is MEDIA_ERR_SRC_NOT_SUPPORTED, then return a promise
     //    rejected with a "NotSupportedError" DOMException.
     if (m_error && m_error->code() == MediaError::Code::SrcNotSupported) {
-        auto error = WebIDL::NotSupportedError::create(realm, m_error->message());
-        auto promise = WebIDL::create_rejected_promise(realm, error);
-
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise->promise()) };
+        auto exception = WebIDL::NotSupportedError::create(realm, m_error->message());
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 3. Let promise be a new promise and append promise to the list of pending play promises.

+ 8 - 12
Userland/Libraries/LibWeb/Streams/ReadableStreamBYOBReader.cpp

@@ -110,30 +110,26 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> ReadableStreamBYOBReader::rea
 
     // 1. If view.[[ByteLength]] is 0, return a promise rejected with a TypeError exception.
     if (view->byte_length() == 0) {
-        auto exception = JS::TypeError::create(realm, "Cannot read in an empty buffer"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "Cannot read in an empty buffer"sv };
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 2. If view.[[ViewedArrayBuffer]].[[ArrayBufferByteLength]] is 0, return a promise rejected with a TypeError exception.
     if (view->viewed_array_buffer()->byte_length() == 0) {
-        auto exception = JS::TypeError::create(realm, "Cannot read in an empty buffer"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "Cannot read in an empty buffer"sv };
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 3. If ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true, return a promise rejected with a TypeError exception.
     if (view->viewed_array_buffer()->is_detached()) {
-        auto exception = JS::TypeError::create(realm, "Cannot read in a detached buffer"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "Cannot read in a detached buffer"sv };
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 4. If this.[[stream]] is undefined, return a promise rejected with a TypeError exception.
     if (!m_stream) {
-        auto exception = JS::TypeError::create(realm, "Cannot read from an empty stream"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "Cannot read from an empty stream"sv };
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 5. Let promise be a new promise.

+ 2 - 3
Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp

@@ -158,9 +158,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> ReadableStreamDefaultReader::
 
     // 1. If this.[[stream]] is undefined, return a promise rejected with a TypeError exception.
     if (!m_stream) {
-        auto exception = JS::TypeError::create(realm, "Cannot read from an empty stream"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "Cannot read from an empty stream"sv };
+        return WebIDL::create_rejected_promise_from_exception(realm, move(exception));
     }
 
     // 2. Let promise be a new promise.

+ 2 - 3
Userland/Libraries/LibWeb/Streams/ReadableStreamGenericReader.cpp

@@ -26,9 +26,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> ReadableStreamGenericReaderMi
 {
     // 1. If this.[[stream]] is undefined, return a promise rejected with a TypeError exception.
     if (!m_stream) {
-        auto exception = JS::TypeError::create(m_realm, "No stream present to cancel"sv);
-        auto promise_capability = WebIDL::create_rejected_promise(m_realm, exception);
-        return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise().ptr()) };
+        WebIDL::SimpleException exception { WebIDL::SimpleExceptionType::TypeError, "No stream present to cancel"sv };
+        return WebIDL::create_rejected_promise_from_exception(m_realm, move(exception));
     }
 
     // 2. Return ! ReadableStreamReaderGenericCancel(this, reason).

+ 5 - 3
Userland/Libraries/LibWeb/WebIDL/ExceptionOr.h

@@ -32,6 +32,8 @@ struct SimpleException {
     Variant<String, StringView> message;
 };
 
+using Exception = Variant<SimpleException, JS::NonnullGCPtr<DOMException>, JS::Completion>;
+
 template<typename ValueType>
 class [[nodiscard]] ExceptionOr {
 public:
@@ -78,7 +80,7 @@ public:
         VERIFY(completion.is_error());
     }
 
-    ExceptionOr(Variant<SimpleException, JS::NonnullGCPtr<DOMException>, JS::Completion> exception)
+    ExceptionOr(Exception exception)
         : m_result_or_exception(move(exception))
     {
         if (auto* completion = m_result_or_exception.template get_pointer<JS::Completion>())
@@ -100,7 +102,7 @@ public:
         return move(m_result_or_exception.template get<ValueType>());
     }
 
-    Variant<SimpleException, JS::NonnullGCPtr<DOMException>, JS::Completion> exception() const
+    Exception exception() const
     {
         return m_result_or_exception.template downcast<SimpleException, JS::NonnullGCPtr<DOMException>, JS::Completion>();
     }
@@ -118,7 +120,7 @@ public:
 
     // These are for compatibility with the TRY() macro in AK.
     [[nodiscard]] bool is_error() const { return is_exception(); }
-    Variant<SimpleException, JS::NonnullGCPtr<DOMException>, JS::Completion> release_error() { return exception(); }
+    Exception release_error() { return exception(); }
 
 private:
     // https://webidl.spec.whatwg.org/#idl-exceptions

+ 7 - 1
Userland/Libraries/LibWeb/WebIDL/Promise.cpp

@@ -13,7 +13,6 @@
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/HostDefined.h>
 #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
-#include <LibWeb/WebIDL/ExceptionOr.h>
 #include <LibWeb/WebIDL/Promise.h>
 
 namespace Web::WebIDL {
@@ -295,4 +294,11 @@ void wait_for_all(JS::Realm& realm, Vector<JS::NonnullGCPtr<Promise>> const& pro
     }
 }
 
+JS::NonnullGCPtr<JS::Promise> create_rejected_promise_from_exception(JS::Realm& realm, Exception exception)
+{
+    auto throw_completion = Bindings::dom_exception_to_throw_completion(realm.vm(), move(exception));
+    auto promise_capability = WebIDL::create_rejected_promise(realm, *throw_completion.value());
+    return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise().ptr()) };
+}
+
 }

+ 4 - 0
Userland/Libraries/LibWeb/WebIDL/Promise.h

@@ -12,6 +12,7 @@
 #include <LibJS/Runtime/Value.h>
 #include <LibJS/SafeFunction.h>
 #include <LibWeb/Forward.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
 
 namespace Web::WebIDL {
 
@@ -31,4 +32,7 @@ JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const&, ReactionSteps);
 void mark_promise_as_handled(Promise const&);
 void wait_for_all(JS::Realm&, Vector<JS::NonnullGCPtr<Promise>> const& promises, Function<void(Vector<JS::Value> const&)> success_steps, Function<void(JS::Value)> failure_steps);
 
+// Non-spec, convenience method.
+JS::NonnullGCPtr<JS::Promise> create_rejected_promise_from_exception(JS::Realm&, Exception);
+
 }