From 47b512ec56fc4087a40cbfaebaed93f36dfac4aa Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Tue, 18 Nov 2025 16:49:02 +0000 Subject: [PATCH] LibWeb/WebAudio: Fire complete event with OfflineAudioCompletionEvent --- Libraries/LibWeb/CMakeLists.txt | 1 + Libraries/LibWeb/Forward.h | 1 + .../WebAudio/OfflineAudioCompletionEvent.cpp | 40 +++++++++++++++++++ .../WebAudio/OfflineAudioCompletionEvent.h | 39 ++++++++++++++++++ .../WebAudio/OfflineAudioCompletionEvent.idl | 14 +++++++ .../LibWeb/WebAudio/OfflineAudioContext.cpp | 13 +++++- Libraries/LibWeb/idl_files.cmake | 1 + .../Text/expected/all-window-properties.txt | 1 + .../webaudio/idlharness.https.window.txt | 24 +++++------ 9 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.cpp create mode 100644 Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.h create mode 100644 Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.idl diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 74df23d1c0a..e4e89152e0e 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -1013,6 +1013,7 @@ set(SOURCES WebAudio/DynamicsCompressorNode.cpp WebAudio/GainNode.cpp WebAudio/MediaElementAudioSourceNode.cpp + WebAudio/OfflineAudioCompletionEvent.cpp WebAudio/OfflineAudioContext.cpp WebAudio/OscillatorNode.cpp WebAudio/PannerNode.cpp diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 25e218ca759..51989292539 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -1203,6 +1203,7 @@ class BaseAudioContext; class BiquadFilterNode; class DynamicsCompressorNode; class GainNode; +class OfflineAudioCompletionEvent; class OfflineAudioContext; class OscillatorNode; class PannerNode; diff --git a/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.cpp b/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.cpp new file mode 100644 index 00000000000..7a9e06e7923 --- /dev/null +++ b/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::WebAudio { + +GC_DEFINE_ALLOCATOR(OfflineAudioCompletionEvent); + +WebIDL::ExceptionOr> OfflineAudioCompletionEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, OfflineAudioCompletionEventInit const& event_init) +{ + return realm.create(realm, event_name, event_init); +} + +OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(JS::Realm& realm, FlyString const& event_name, OfflineAudioCompletionEventInit const& event_init) + : DOM::Event(realm, event_name, event_init) + , m_rendered_buffer(event_init.rendered_buffer) +{ +} + +OfflineAudioCompletionEvent::~OfflineAudioCompletionEvent() = default; + +void OfflineAudioCompletionEvent::initialize(JS::Realm& realm) +{ + WEB_SET_PROTOTYPE_FOR_INTERFACE(OfflineAudioCompletionEvent); + Base::initialize(realm); +} + +void OfflineAudioCompletionEvent::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_rendered_buffer); +} + +} diff --git a/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.h b/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.h new file mode 100644 index 00000000000..21862457ae2 --- /dev/null +++ b/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::WebAudio { + +struct OfflineAudioCompletionEventInit : public DOM::EventInit { + GC::Ptr rendered_buffer; +}; + +class OfflineAudioCompletionEvent final : public DOM::Event { + WEB_PLATFORM_OBJECT(OfflineAudioCompletionEvent, DOM::Event); + GC_DECLARE_ALLOCATOR(OfflineAudioCompletionEvent); + +public: + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, FlyString const& event_name, OfflineAudioCompletionEventInit const& event_init); + + virtual ~OfflineAudioCompletionEvent() override; + + GC::Ptr rendered_buffer() const { return m_rendered_buffer; } + +private: + OfflineAudioCompletionEvent(JS::Realm&, FlyString const& event_name, OfflineAudioCompletionEventInit const& event_init); + + void initialize(JS::Realm&) override; + + virtual void visit_edges(Cell::Visitor&) override; + + GC::Ptr m_rendered_buffer; +}; + +} diff --git a/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.idl b/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.idl new file mode 100644 index 00000000000..125fb10ae10 --- /dev/null +++ b/Libraries/LibWeb/WebAudio/OfflineAudioCompletionEvent.idl @@ -0,0 +1,14 @@ +#import +#import + +// https://webaudio.github.io/web-audio-api/#OfflineAudioCompletionEvent +[Exposed=Window] +interface OfflineAudioCompletionEvent : Event { + constructor(DOMString type, OfflineAudioCompletionEventInit eventInitDict); + readonly attribute AudioBuffer renderedBuffer; +}; + +// https://webaudio.github.io/web-audio-api/#OfflineAudioCompletionEventInit +dictionary OfflineAudioCompletionEventInit : EventInit { + required AudioBuffer renderedBuffer; +}; diff --git a/Libraries/LibWeb/WebAudio/OfflineAudioContext.cpp b/Libraries/LibWeb/WebAudio/OfflineAudioContext.cpp index 0e684657d88..729965127c6 100644 --- a/Libraries/LibWeb/WebAudio/OfflineAudioContext.cpp +++ b/Libraries/LibWeb/WebAudio/OfflineAudioContext.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace Web::WebAudio { @@ -138,9 +139,17 @@ void OfflineAudioContext::begin_offline_rendering(GC::Ref promi // 4.2: Queue a media element task to fire an event named complete at the OfflineAudioContext using OfflineAudioCompletionEvent // whose renderedBuffer property is set to [[rendered buffer]]. - // FIXME: Need to implement OfflineAudioCompletionEvent. queue_a_media_element_task(GC::create_function(heap(), [&realm, this]() { - this->dispatch_event(DOM::Event::create(realm, HTML::EventNames::complete)); + auto event_init = OfflineAudioCompletionEventInit { + { + .bubbles = false, + .cancelable = false, + .composed = false, + }, + this->m_rendered_buffer, + }; + auto event = MUST(OfflineAudioCompletionEvent::construct_impl(realm, HTML::EventNames::complete, event_init)); + this->dispatch_event(event); })); })); } diff --git a/Libraries/LibWeb/idl_files.cmake b/Libraries/LibWeb/idl_files.cmake index 594e8508112..7ae73ed1f7c 100644 --- a/Libraries/LibWeb/idl_files.cmake +++ b/Libraries/LibWeb/idl_files.cmake @@ -473,6 +473,7 @@ libweb_js_bindings(WebAudio/ConstantSourceNode) libweb_js_bindings(WebAudio/DelayNode) libweb_js_bindings(WebAudio/GainNode) libweb_js_bindings(WebAudio/MediaElementAudioSourceNode) +libweb_js_bindings(WebAudio/OfflineAudioCompletionEvent) libweb_js_bindings(WebAudio/OfflineAudioContext) libweb_js_bindings(WebAudio/OscillatorNode) libweb_js_bindings(WebAudio/PannerNode) diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt index 3a505814c05..df481d23410 100644 --- a/Tests/LibWeb/Text/expected/all-window-properties.txt +++ b/Tests/LibWeb/Text/expected/all-window-properties.txt @@ -317,6 +317,7 @@ NodeList Notification Number Object +OfflineAudioCompletionEvent OfflineAudioContext OffscreenCanvas OffscreenCanvasRenderingContext2D diff --git a/Tests/LibWeb/Text/expected/wpt-import/webaudio/idlharness.https.window.txt b/Tests/LibWeb/Text/expected/wpt-import/webaudio/idlharness.https.window.txt index 4d1b1fabea5..d5761f5e5db 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/webaudio/idlharness.https.window.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/webaudio/idlharness.https.window.txt @@ -2,8 +2,8 @@ Harness status: OK Found 1147 tests -882 Pass -265 Fail +892 Pass +255 Fail Fail idl_test setup Pass idl_test validation Pass Partial interface Element: member names are unique @@ -184,16 +184,16 @@ Pass BaseAudioContext interface: new OfflineAudioContext(1, 1, sample_rate) must Fail BaseAudioContext interface: new OfflineAudioContext(1, 1, sample_rate) must inherit property "createWaveShaper()" with the proper type Pass BaseAudioContext interface: new OfflineAudioContext(1, 1, sample_rate) must inherit property "decodeAudioData(ArrayBuffer, optional DecodeSuccessCallback?, optional DecodeErrorCallback?)" with the proper type Pass BaseAudioContext interface: calling decodeAudioData(ArrayBuffer, optional DecodeSuccessCallback?, optional DecodeErrorCallback?) on new OfflineAudioContext(1, 1, sample_rate) with too few arguments must throw TypeError -Fail OfflineAudioCompletionEvent interface: existence and properties of interface object -Fail OfflineAudioCompletionEvent interface object length -Fail OfflineAudioCompletionEvent interface object name -Fail OfflineAudioCompletionEvent interface: existence and properties of interface prototype object -Fail OfflineAudioCompletionEvent interface: existence and properties of interface prototype object's "constructor" property -Fail OfflineAudioCompletionEvent interface: existence and properties of interface prototype object's @@unscopables property -Fail OfflineAudioCompletionEvent interface: attribute renderedBuffer -Fail OfflineAudioCompletionEvent must be primary interface of new OfflineAudioCompletionEvent("", {renderedBuffer: buffer}) -Fail Stringification of new OfflineAudioCompletionEvent("", {renderedBuffer: buffer}) -Fail OfflineAudioCompletionEvent interface: new OfflineAudioCompletionEvent("", {renderedBuffer: buffer}) must inherit property "renderedBuffer" with the proper type +Pass OfflineAudioCompletionEvent interface: existence and properties of interface object +Pass OfflineAudioCompletionEvent interface object length +Pass OfflineAudioCompletionEvent interface object name +Pass OfflineAudioCompletionEvent interface: existence and properties of interface prototype object +Pass OfflineAudioCompletionEvent interface: existence and properties of interface prototype object's "constructor" property +Pass OfflineAudioCompletionEvent interface: existence and properties of interface prototype object's @@unscopables property +Pass OfflineAudioCompletionEvent interface: attribute renderedBuffer +Pass OfflineAudioCompletionEvent must be primary interface of new OfflineAudioCompletionEvent("", {renderedBuffer: buffer}) +Pass Stringification of new OfflineAudioCompletionEvent("", {renderedBuffer: buffer}) +Pass OfflineAudioCompletionEvent interface: new OfflineAudioCompletionEvent("", {renderedBuffer: buffer}) must inherit property "renderedBuffer" with the proper type Pass AudioBuffer interface: existence and properties of interface object Pass AudioBuffer interface object length Pass AudioBuffer interface object name