From 3aff12bbab9b59b9dad7e39a3af7aa3f4788221d Mon Sep 17 00:00:00 2001 From: Ben Eidson Date: Thu, 19 Jun 2025 09:47:41 -0400 Subject: [PATCH] LibWeb/WebAudio: Implement AudioNode::disconnect() Destubs AudioNode::disconnect() and its related overloads. Ensures that inverse connections are properly removed when disconnecting AudioNodeConnections. Adds associated WPT but skips them for now because they rely on OfflineRenderContext::start_rendering to be fully implemented. --- Libraries/LibWeb/WebAudio/AudioNode.cpp | 118 ++++++- Libraries/LibWeb/WebAudio/AudioNode.h | 4 +- Tests/LibWeb/TestConfig.ini | 5 + .../audionode-disconnect-audioparam.txt | 0 .../audionode-disconnect.txt | 0 .../audionode-disconnect-audioparam.html | 221 +++++++++++++ .../audionode-disconnect.html | 298 ++++++++++++++++++ 7 files changed, 630 insertions(+), 16 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html diff --git a/Libraries/LibWeb/WebAudio/AudioNode.cpp b/Libraries/LibWeb/WebAudio/AudioNode.cpp index 64bd4465fb8..b6ce073c25d 100644 --- a/Libraries/LibWeb/WebAudio/AudioNode.cpp +++ b/Libraries/LibWeb/WebAudio/AudioNode.cpp @@ -119,7 +119,16 @@ WebIDL::ExceptionOr AudioNode::connect(GC::Ref destination_par // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect void AudioNode::disconnect() { - dbgln("FIXME: Implement AudioNode::disconnect()"); + while (!m_output_connections.is_empty()) { + auto connection = m_output_connections.take_last(); + auto destination = connection.destination_node; + + destination->m_input_connections.remove_all_matching([&](AudioNodeConnection& input_connection) { + return input_connection.destination_node.ptr() == this; + }); + } + + m_param_connections.clear(); } // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-output @@ -132,35 +141,81 @@ WebIDL::ExceptionOr AudioNode::disconnect(WebIDL::UnsignedLong output) return WebIDL::IndexSizeError::create(realm(), MUST(String::formatted("Output index {} exceeds number of outputs", output))); } - dbgln("FIXME: Implement AudioNode::disconnect(output)"); + m_output_connections.remove_all_matching([&](AudioNodeConnection& connection) { + if (connection.output != output) + return false; + + connection.destination_node->m_input_connections.remove_all_matching([&](AudioNodeConnection& reverse_connection) { + return reverse_connection.destination_node.ptr() == this && reverse_connection.output == output; + }); + + return true; + }); + + m_param_connections.remove_all_matching([&](AudioParamConnection& connection) { + return connection.output == output; + }); + return {}; } // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode -void AudioNode::disconnect(GC::Ref destination_node) +WebIDL::ExceptionOr AudioNode::disconnect(GC::Ref destination_node) { - (void)destination_node; - dbgln("FIXME: Implement AudioNode::disconnect(destination_node)"); + // The destinationNode parameter is the AudioNode to disconnect. + // It disconnects all outgoing connections to the given destinationNode. + auto before = m_output_connections.size(); + m_output_connections.remove_all_matching([&](AudioNodeConnection& connection) { + if (connection.destination_node != destination_node) + return false; + + connection.destination_node->m_input_connections.remove_all_matching([&](AudioNodeConnection& reverse_connection) { + return reverse_connection.destination_node.ptr() == this; + }); + + return true; + }); + // If there is no connection to the destinationNode, an InvalidAccessError exception MUST be thrown. + if (m_output_connections.size() == before) { + return WebIDL::InvalidAccessError::create(realm(), MUST(String::formatted("No connection to given AudioNode"))); + } + + return {}; } // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output WebIDL::ExceptionOr AudioNode::disconnect(GC::Ref destination_node, WebIDL::UnsignedLong output) { - (void)destination_node; // The output parameter is an index describing which output of the AudioNode from which to disconnect. // If this parameter is out-of-bounds, an IndexSizeError exception MUST be thrown. if (output >= number_of_outputs()) { return WebIDL::IndexSizeError::create(realm(), MUST(String::formatted("Output index {} exceeds number of outputs", output))); } - dbgln("FIXME: Implement AudioNode::disconnect(destination_node, output)"); + // The destinationNode parameter is the AudioNode to disconnect. + auto before = m_output_connections.size(); + m_output_connections.remove_all_matching([&](AudioNodeConnection& connection) { + if (connection.destination_node != destination_node || connection.output != output) + return false; + + connection.destination_node->m_input_connections.remove_all_matching([&](AudioNodeConnection& reverse_connection) { + return reverse_connection.destination_node.ptr() == this && reverse_connection.output == output; + }); + + return true; + }); + + // If there is no connection to the destinationNode from the given output, an InvalidAccessError exception MUST be thrown. + if (m_output_connections.size() == before) { + return WebIDL::InvalidAccessError::create(realm(), MUST(String::formatted("No connection from output {} to given AudioNode", output))); + } + return {}; } // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output-input WebIDL::ExceptionOr AudioNode::disconnect(GC::Ref destination_node, WebIDL::UnsignedLong output, WebIDL::UnsignedLong input) { - (void)destination_node; // The output parameter is an index describing which output of the AudioNode from which to disconnect. // If this parameter is out-of-bounds, an IndexSizeError exception MUST be thrown. if (output >= number_of_outputs()) { @@ -173,28 +228,63 @@ WebIDL::ExceptionOr AudioNode::disconnect(GC::Ref destination_n return WebIDL::IndexSizeError::create(realm(), MUST(String::formatted("Input index '{}' exceeds number of inputs", input))); } - dbgln("FIXME: Implement AudioNode::disconnect(destination_node, output, input)"); + // The destinationNode parameter is the AudioNode to disconnect. + auto before = m_output_connections.size(); + m_output_connections.remove_all_matching([&](AudioNodeConnection& connection) { + if (connection.destination_node != destination_node || connection.output != output || connection.input != input) + return false; + + connection.destination_node->m_input_connections.remove_all_matching([&](AudioNodeConnection& reverse_connection) { + return reverse_connection.destination_node.ptr() == this && reverse_connection.output == output && reverse_connection.input == input; + }); + + return true; + }); + + // If there is no connection to the destinationNode from the given output to the given input, an InvalidAccessError exception MUST be thrown. + if (m_output_connections.size() == before) { + return WebIDL::InvalidAccessError::create(realm(), MUST(String::formatted("No connection from output {} to input {} of given AudioNode", output, input))); + } + return {}; } // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationparam -void AudioNode::disconnect(GC::Ref destination_param) +WebIDL::ExceptionOr AudioNode::disconnect(GC::Ref destination_param) { - (void)destination_param; - dbgln("FIXME: Implement AudioNode::disconnect(destination_param)"); + // The destinationParam parameter is the AudioParam to disconnect. + auto before = m_param_connections.size(); + m_param_connections.remove_all_matching([&](AudioParamConnection& connection) { + return connection.destination_param == destination_param; + }); + + // If there is no connection to the destinationParam, an InvalidAccessError exception MUST be thrown. + if (m_param_connections.size() == before) { + return WebIDL::InvalidAccessError::create(realm(), MUST(String::formatted("No connection to given AudioParam"))); + } + + return {}; } // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationparam-output WebIDL::ExceptionOr AudioNode::disconnect(GC::Ref destination_param, WebIDL::UnsignedLong output) { - (void)destination_param; // The output parameter is an index describing which output of the AudioNode from which to disconnect. // If this parameter is out-of-bounds, an IndexSizeError exception MUST be thrown. if (output >= number_of_outputs()) { return WebIDL::IndexSizeError::create(realm(), MUST(String::formatted("Output index {} exceeds number of outputs", output))); } + // The destinationParam parameter is the AudioParam to disconnect. + auto before = m_param_connections.size(); + m_param_connections.remove_all_matching([&](AudioParamConnection& connection) { + return connection.destination_param == destination_param && connection.output == output; + }); + + // If there is no connection to the destinationParam, an InvalidAccessError exception MUST be thrown. + if (m_param_connections.size() == before) { + return WebIDL::InvalidAccessError::create(realm(), MUST(String::formatted("No connection from output {} to given AudioParam", output))); + } - dbgln("FIXME: Implement AudioNode::disconnect(destination_param, output)"); return {}; } diff --git a/Libraries/LibWeb/WebAudio/AudioNode.h b/Libraries/LibWeb/WebAudio/AudioNode.h index a1628e38b23..1b3ce6a1e71 100644 --- a/Libraries/LibWeb/WebAudio/AudioNode.h +++ b/Libraries/LibWeb/WebAudio/AudioNode.h @@ -57,10 +57,10 @@ public: void disconnect(); WebIDL::ExceptionOr disconnect(WebIDL::UnsignedLong output); - void disconnect(GC::Ref destination_node); + WebIDL::ExceptionOr disconnect(GC::Ref destination_node); WebIDL::ExceptionOr disconnect(GC::Ref destination_node, WebIDL::UnsignedLong output); WebIDL::ExceptionOr disconnect(GC::Ref destination_node, WebIDL::UnsignedLong output, WebIDL::UnsignedLong input); - void disconnect(GC::Ref destination_param); + WebIDL::ExceptionOr disconnect(GC::Ref destination_param); WebIDL::ExceptionOr disconnect(GC::Ref destination_param, WebIDL::UnsignedLong output); // https://webaudio.github.io/web-audio-api/#dom-audionode-context diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini index 57b93909d6c..754e759ffb4 100644 --- a/Tests/LibWeb/TestConfig.ini +++ b/Tests/LibWeb/TestConfig.ini @@ -318,3 +318,8 @@ Text/input/wpt-import/IndexedDB/idbfactory_open.any.html Text/input/XHR/XMLHttpRequest-override-mimetype-blob.html Text/input/wpt-import/webaudio/the-audio-api/the-periodicwave-interface/periodicWave.html + +; Will fail until OfflineRenderContext::start_rendering is implemented +; https://github.com/LadybirdBrowser/ladybird/issues/5333 +Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html +Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html diff --git a/Tests/LibWeb/Text/expected/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.txt b/Tests/LibWeb/Text/expected/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Tests/LibWeb/Text/expected/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.txt b/Tests/LibWeb/Text/expected/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html b/Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html new file mode 100644 index 00000000000..23256b13665 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html @@ -0,0 +1,221 @@ + + + + + audionode-disconnect-audioparam.html + + + + + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html b/Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html new file mode 100644 index 00000000000..f5550c4f682 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect.html @@ -0,0 +1,298 @@ + + + + + audionode-disconnect.html + + + + + + + + + +