Ver código fonte

LibWeb: Implement pending promises in BaseAudioContext

Move the pending promises list from AudioContext to BaseAudioContext and
deal with all remaining FIXMEs.
Jelle Raaijmakers 8 meses atrás
pai
commit
2df3488840

+ 13 - 12
Userland/Libraries/LibWeb/WebAudio/AudioContext.cpp

@@ -92,7 +92,6 @@ void AudioContext::initialize(JS::Realm& realm)
 void AudioContext::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
-    visitor.visit(m_pending_promises);
     visitor.visit(m_pending_resume_promises);
 }
 
@@ -107,7 +106,6 @@ AudioTimestamp AudioContext::get_output_timestamp()
 WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> AudioContext::resume()
 {
     auto& realm = this->realm();
-    auto& vm = realm.vm();
 
     // 1. If this's relevant global object's associated Document is not fully active then return a promise rejected with "InvalidStateError" DOMException.
     auto const& associated_document = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).associated_document();
@@ -128,8 +126,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> AudioContext::resume()
 
     // 5. If the context is not allowed to start, append promise to [[pending promises]] and [[pending resume promises]] and abort these steps, returning promise.
     if (m_allowed_to_start) {
-        TRY_OR_THROW_OOM(vm, m_pending_promises.try_append(promise));
-        TRY_OR_THROW_OOM(vm, m_pending_resume_promises.try_append(promise));
+        m_pending_promises.append(promise);
+        m_pending_resume_promises.append(promise);
     }
 
     // 6. Set the [[control thread state]] on the AudioContext to running.
@@ -150,25 +148,29 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> AudioContext::resume()
             // 7.4.1: Reject all promises from [[pending resume promises]] in order, then clear [[pending resume promises]].
             for (auto const& promise : m_pending_resume_promises) {
                 WebIDL::reject_promise(realm, promise, JS::js_null());
+
+                // 7.4.2: Additionally, remove those promises from [[pending promises]].
+                m_pending_promises.remove_first_matching([&promise](auto& pending_promise) {
+                    return pending_promise == promise;
+                });
             }
             m_pending_resume_promises.clear();
-
-            // FIXME: 7.4.2: Additionally, remove those promises from [[pending promises]].
         }));
     }
 
     // 7.5: queue a media element task to execute the following steps:
     queue_a_media_element_task(JS::create_heap_function(heap(), [&realm, promise, this]() {
         // 7.5.1: Resolve all promises from [[pending resume promises]] in order.
+        // 7.5.2: Clear [[pending resume promises]]. Additionally, remove those promises from
+        //        [[pending promises]].
         for (auto const& pending_resume_promise : m_pending_resume_promises) {
             *pending_resume_promise->resolve();
+            m_pending_promises.remove_first_matching([&pending_resume_promise](auto& pending_promise) {
+                return pending_promise == pending_resume_promise;
+            });
         }
-
-        // 7.5.2: Clear [[pending resume promises]].
         m_pending_resume_promises.clear();
 
-        // FIXME: Additionally, remove those promises from [[pending promises]].
-
         // 7.5.3: Resolve promise.
         *promise->resolve();
 
@@ -192,7 +194,6 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> AudioContext::resume()
 WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> AudioContext::suspend()
 {
     auto& realm = this->realm();
-    auto& vm = realm.vm();
 
     // 1. If this's relevant global object's associated Document is not fully active then return a promise rejected with "InvalidStateError" DOMException.
     auto const& associated_document = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).associated_document();
@@ -209,7 +210,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> AudioContext::suspend()
     }
 
     // 4. Append promise to [[pending promises]].
-    TRY_OR_THROW_OOM(vm, m_pending_promises.try_append(promise));
+    m_pending_promises.append(promise);
 
     // 5. Set [[suspended by user]] to true.
     m_suspended_by_user = true;

+ 0 - 1
Userland/Libraries/LibWeb/WebAudio/AudioContext.h

@@ -49,7 +49,6 @@ private:
     double m_output_latency { 0 };
 
     bool m_allowed_to_start = true;
-    Vector<JS::NonnullGCPtr<WebIDL::Promise>> m_pending_promises;
     Vector<JS::NonnullGCPtr<WebIDL::Promise>> m_pending_resume_promises;
     bool m_suspended_by_user = false;
 

+ 11 - 4
Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.cpp

@@ -43,6 +43,7 @@ void BaseAudioContext::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
     visitor.visit(m_destination);
+    visitor.visit(m_pending_promises);
 }
 
 void BaseAudioContext::set_onstatechange(WebIDL::CallbackType* event_handler)
@@ -144,7 +145,8 @@ JS::NonnullGCPtr<JS::Promise> BaseAudioContext::decode_audio_data(JS::Handle<Web
 
     // FIXME: 3. If audioData is detached, execute the following steps:
     if (true) {
-        // FIXME: 3.1. Append promise to [[pending promises]].
+        // 3.1. Append promise to [[pending promises]].
+        m_pending_promises.append(promise);
 
         // FIXME: 3.2. Detach the audioData ArrayBuffer. If this operations throws, jump to the step 3.
 
@@ -159,6 +161,9 @@ JS::NonnullGCPtr<JS::Promise> BaseAudioContext::decode_audio_data(JS::Handle<Web
 
         // 4.2. Reject promise with error, and remove it from [[pending promises]].
         WebIDL::reject_promise(realm, promise, error);
+        m_pending_promises.remove_first_matching([&promise](auto& pending_promise) {
+            return pending_promise == promise;
+        });
 
         // 4.3. Queue a media element task to invoke errorCallback with error.
         if (error_callback) {
@@ -201,13 +206,15 @@ void BaseAudioContext::queue_a_decoding_operation(JS::NonnullGCPtr<JS::PromiseCa
     // 4. If can decode is false,
     if (!can_decode) {
         // queue a media element task to execute the following steps:
-        queue_a_media_element_task(JS::create_heap_function(heap(), [&realm, promise, error_callback] {
+        queue_a_media_element_task(JS::create_heap_function(heap(), [this, &realm, promise, error_callback] {
             // 4.1. Let error be a DOMException whose name is EncodingError.
             auto error = WebIDL::EncodingError::create(realm, "Unable to decode."_string);
 
-            // 4.1.2. Reject promise with error,
+            // 4.1.2. Reject promise with error, and remove it from [[pending promises]].
             WebIDL::reject_promise(realm, promise, error);
-            // FIXME: and remove it from [[pending promises]].
+            m_pending_promises.remove_first_matching([&promise](auto& pending_promise) {
+                return pending_promise == promise;
+            });
 
             // 4.2. If errorCallback is not missing, invoke errorCallback with error.
             if (error_callback) {

+ 1 - 0
Userland/Libraries/LibWeb/WebAudio/BaseAudioContext.h

@@ -70,6 +70,7 @@ protected:
     virtual void visit_edges(Cell::Visitor&) override;
 
     JS::NonnullGCPtr<AudioDestinationNode> m_destination;
+    Vector<JS::NonnullGCPtr<WebIDL::Promise>> m_pending_promises;
 
 private:
     void queue_a_decoding_operation(JS::NonnullGCPtr<JS::PromiseCapability>, JS::Handle<WebIDL::BufferSource>, JS::GCPtr<WebIDL::CallbackType>, JS::GCPtr<WebIDL::CallbackType>);