LibWeb: Validate AudioParam context in AudioNode::connect()

An exception is now thown if an `AudioNode` attempts to connect to an
`AudioParam` from a different `BaseAudioContext`.
This commit is contained in:
Tim Ledbetter 2025-01-08 20:04:28 +00:00 committed by Tim Ledbetter
commit 5c57acf140
Notes: github-actions[bot] 2025-01-09 11:35:50 +00:00
14 changed files with 54 additions and 43 deletions

View file

@ -18,8 +18,8 @@ GC_DEFINE_ALLOCATOR(AudioBufferSourceNode);
AudioBufferSourceNode::AudioBufferSourceNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, AudioBufferSourceOptions const& options) AudioBufferSourceNode::AudioBufferSourceNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, AudioBufferSourceOptions const& options)
: AudioScheduledSourceNode(realm, context) : AudioScheduledSourceNode(realm, context)
, m_buffer(options.buffer) , m_buffer(options.buffer)
, m_playback_rate(AudioParam::create(realm, options.playback_rate, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_playback_rate(AudioParam::create(realm, context, options.playback_rate, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_detune(AudioParam::create(realm, options.detune, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_detune(AudioParam::create(realm, context, options.detune, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_loop(options.loop) , m_loop(options.loop)
, m_loop_start(options.loop_start) , m_loop_start(options.loop_start)
, m_loop_end(options.loop_end) , m_loop_end(options.loop_end)

View file

@ -12,23 +12,23 @@ namespace Web::WebAudio {
GC_DEFINE_ALLOCATOR(AudioListener); GC_DEFINE_ALLOCATOR(AudioListener);
AudioListener::AudioListener(JS::Realm& realm) AudioListener::AudioListener(JS::Realm& realm, GC::Ref<BaseAudioContext> context)
: Bindings::PlatformObject(realm) : Bindings::PlatformObject(realm)
, m_forward_x(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_forward_x(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_forward_y(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_forward_y(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_forward_z(AudioParam::create(realm, -1.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_forward_z(AudioParam::create(realm, context, -1.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_position_x(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_position_x(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_position_y(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_position_y(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_position_z(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_position_z(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_up_x(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_up_x(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_up_y(AudioParam::create(realm, 1.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_up_y(AudioParam::create(realm, context, 1.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_up_z(AudioParam::create(realm, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_up_z(AudioParam::create(realm, context, 0.f, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
{ {
} }
GC::Ref<AudioListener> AudioListener::create(JS::Realm& realm) GC::Ref<AudioListener> AudioListener::create(JS::Realm& realm, GC::Ref<BaseAudioContext> context)
{ {
return realm.create<AudioListener>(realm); return realm.create<AudioListener>(realm, context);
} }
AudioListener::~AudioListener() = default; AudioListener::~AudioListener() = default;

View file

@ -20,7 +20,7 @@ class AudioListener final : public Bindings::PlatformObject {
GC_DECLARE_ALLOCATOR(AudioListener); GC_DECLARE_ALLOCATOR(AudioListener);
public: public:
static GC::Ref<AudioListener> create(JS::Realm&); static GC::Ref<AudioListener> create(JS::Realm&, GC::Ref<BaseAudioContext>);
virtual ~AudioListener() override; virtual ~AudioListener() override;
GC::Ref<AudioParam> forward_x() const { return m_forward_x; } GC::Ref<AudioParam> forward_x() const { return m_forward_x; }
@ -37,7 +37,7 @@ public:
WebIDL::ExceptionOr<void> set_orientation(float x, float y, float z, float x_up, float y_up, float z_up); WebIDL::ExceptionOr<void> set_orientation(float x, float y, float z, float x_up, float y_up, float z_up);
private: private:
explicit AudioListener(JS::Realm&); explicit AudioListener(JS::Realm&, GC::Ref<BaseAudioContext>);
virtual void initialize(JS::Realm&) override; virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;

View file

@ -78,7 +78,11 @@ WebIDL::ExceptionOr<GC::Ref<AudioNode>> AudioNode::connect(GC::Ref<AudioNode> de
// https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output // https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output
WebIDL::ExceptionOr<void> AudioNode::connect(GC::Ref<AudioParam> destination_param, WebIDL::UnsignedLong output) WebIDL::ExceptionOr<void> AudioNode::connect(GC::Ref<AudioParam> destination_param, WebIDL::UnsignedLong output)
{ {
(void)destination_param; // If destinationParam belongs to an AudioNode that belongs to a BaseAudioContext that is different from the BaseAudioContext
// that has created the AudioNode on which this method was called, an InvalidAccessError MUST be thrown.
if (m_context != destination_param->context()) {
return WebIDL::InvalidAccessError::create(realm(), "Cannot connect to an AudioParam in a different AudioContext"_string);
}
// The output parameter is an index describing which output of the AudioNode from which to connect. // The output parameter is an index describing which output of the AudioNode from which to connect.
// If the parameter is out-of-bounds, an IndexSizeError exception MUST be thrown. // If the parameter is out-of-bounds, an IndexSizeError exception MUST be thrown.

View file

@ -7,14 +7,16 @@
#include <LibWeb/Bindings/AudioParamPrototype.h> #include <LibWeb/Bindings/AudioParamPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebAudio/AudioParam.h> #include <LibWeb/WebAudio/AudioParam.h>
#include <LibWeb/WebAudio/BaseAudioContext.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::WebAudio { namespace Web::WebAudio {
GC_DEFINE_ALLOCATOR(AudioParam); GC_DEFINE_ALLOCATOR(AudioParam);
AudioParam::AudioParam(JS::Realm& realm, float default_value, float min_value, float max_value, Bindings::AutomationRate automation_rate) AudioParam::AudioParam(JS::Realm& realm, GC::Ref<BaseAudioContext> context, float default_value, float min_value, float max_value, Bindings::AutomationRate automation_rate)
: Bindings::PlatformObject(realm) : Bindings::PlatformObject(realm)
, m_context(context)
, m_current_value(default_value) , m_current_value(default_value)
, m_default_value(default_value) , m_default_value(default_value)
, m_min_value(min_value) , m_min_value(min_value)
@ -23,9 +25,9 @@ AudioParam::AudioParam(JS::Realm& realm, float default_value, float min_value, f
{ {
} }
GC::Ref<AudioParam> AudioParam::create(JS::Realm& realm, float default_value, float min_value, float max_value, Bindings::AutomationRate automation_rate) GC::Ref<AudioParam> AudioParam::create(JS::Realm& realm, GC::Ref<BaseAudioContext> context, float default_value, float min_value, float max_value, Bindings::AutomationRate automation_rate)
{ {
return realm.create<AudioParam>(realm, default_value, min_value, max_value, automation_rate); return realm.create<AudioParam>(realm, context, default_value, min_value, max_value, automation_rate);
} }
AudioParam::~AudioParam() = default; AudioParam::~AudioParam() = default;
@ -143,6 +145,7 @@ void AudioParam::initialize(JS::Realm& realm)
void AudioParam::visit_edges(Cell::Visitor& visitor) void AudioParam::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
visitor.visit(m_context);
} }
} }

View file

@ -18,10 +18,12 @@ class AudioParam final : public Bindings::PlatformObject {
GC_DECLARE_ALLOCATOR(AudioParam); GC_DECLARE_ALLOCATOR(AudioParam);
public: public:
static GC::Ref<AudioParam> create(JS::Realm&, float default_value, float min_value, float max_value, Bindings::AutomationRate); static GC::Ref<AudioParam> create(JS::Realm&, GC::Ref<BaseAudioContext>, float default_value, float min_value, float max_value, Bindings::AutomationRate);
virtual ~AudioParam() override; virtual ~AudioParam() override;
GC::Ref<BaseAudioContext> context() const { return m_context; }
float value() const; float value() const;
void set_value(float); void set_value(float);
@ -41,7 +43,9 @@ public:
WebIDL::ExceptionOr<GC::Ref<AudioParam>> cancel_and_hold_at_time(double cancel_time); WebIDL::ExceptionOr<GC::Ref<AudioParam>> cancel_and_hold_at_time(double cancel_time);
private: private:
AudioParam(JS::Realm&, float default_value, float min_value, float max_value, Bindings::AutomationRate); AudioParam(JS::Realm&, GC::Ref<BaseAudioContext>, float default_value, float min_value, float max_value, Bindings::AutomationRate);
GC::Ref<BaseAudioContext> m_context;
// https://webaudio.github.io/web-audio-api/#dom-audioparam-current-value-slot // https://webaudio.github.io/web-audio-api/#dom-audioparam-current-value-slot
float m_current_value {}; // [[current value]] float m_current_value {}; // [[current value]]

View file

@ -29,7 +29,7 @@ namespace Web::WebAudio {
BaseAudioContext::BaseAudioContext(JS::Realm& realm, float sample_rate) BaseAudioContext::BaseAudioContext(JS::Realm& realm, float sample_rate)
: DOM::EventTarget(realm) : DOM::EventTarget(realm)
, m_sample_rate(sample_rate) , m_sample_rate(sample_rate)
, m_listener(AudioListener::create(realm)) , m_listener(AudioListener::create(realm, *this))
{ {
} }

View file

@ -20,10 +20,10 @@ GC_DEFINE_ALLOCATOR(BiquadFilterNode);
BiquadFilterNode::BiquadFilterNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, BiquadFilterOptions const& options) BiquadFilterNode::BiquadFilterNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, BiquadFilterOptions const& options)
: AudioNode(realm, context) : AudioNode(realm, context)
, m_type(options.type) , m_type(options.type)
, m_frequency(AudioParam::create(realm, options.frequency, 0, context->nyquist_frequency(), Bindings::AutomationRate::ARate)) , m_frequency(AudioParam::create(realm, context, options.frequency, 0, context->nyquist_frequency(), Bindings::AutomationRate::ARate))
, m_detune(AudioParam::create(realm, options.detune, -1200 * AK::log2(NumericLimits<float>::max()), 1200 * AK::log2(NumericLimits<float>::max()), Bindings::AutomationRate::ARate)) , m_detune(AudioParam::create(realm, context, options.detune, -1200 * AK::log2(NumericLimits<float>::max()), 1200 * AK::log2(NumericLimits<float>::max()), Bindings::AutomationRate::ARate))
, m_q(AudioParam::create(realm, options.q, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_q(AudioParam::create(realm, context, options.q, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_gain(AudioParam::create(realm, options.gain, NumericLimits<float>::lowest(), 40 * AK::log10(NumericLimits<float>::max()), Bindings::AutomationRate::ARate)) , m_gain(AudioParam::create(realm, context, options.gain, NumericLimits<float>::lowest(), 40 * AK::log10(NumericLimits<float>::max()), Bindings::AutomationRate::ARate))
{ {
} }

View file

@ -16,7 +16,7 @@ GC_DEFINE_ALLOCATOR(ConstantSourceNode);
ConstantSourceNode::ConstantSourceNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, ConstantSourceOptions const& options) ConstantSourceNode::ConstantSourceNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, ConstantSourceOptions const& options)
: AudioScheduledSourceNode(realm, context) : AudioScheduledSourceNode(realm, context)
, m_offset(AudioParam::create(realm, options.offset, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_offset(AudioParam::create(realm, context, options.offset, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
{ {
} }

View file

@ -15,7 +15,7 @@ GC_DEFINE_ALLOCATOR(DelayNode);
DelayNode::DelayNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, DelayOptions const& options) DelayNode::DelayNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, DelayOptions const& options)
: AudioNode(realm, context) : AudioNode(realm, context)
, m_delay_time(AudioParam::create(realm, options.delay_time, 0, options.max_delay_time, Bindings::AutomationRate::ARate)) , m_delay_time(AudioParam::create(realm, context, options.delay_time, 0, options.max_delay_time, Bindings::AutomationRate::ARate))
{ {
} }

View file

@ -41,11 +41,11 @@ WebIDL::ExceptionOr<GC::Ref<DynamicsCompressorNode>> DynamicsCompressorNode::con
DynamicsCompressorNode::DynamicsCompressorNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, DynamicsCompressorOptions const& options) DynamicsCompressorNode::DynamicsCompressorNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, DynamicsCompressorOptions const& options)
: AudioNode(realm, context) : AudioNode(realm, context)
, m_threshold(AudioParam::create(realm, options.threshold, -100, 0, Bindings::AutomationRate::KRate)) , m_threshold(AudioParam::create(realm, context, options.threshold, -100, 0, Bindings::AutomationRate::KRate))
, m_knee(AudioParam::create(realm, options.knee, 0, 40, Bindings::AutomationRate::KRate)) , m_knee(AudioParam::create(realm, context, options.knee, 0, 40, Bindings::AutomationRate::KRate))
, m_ratio(AudioParam::create(realm, options.ratio, 1, 20, Bindings::AutomationRate::KRate)) , m_ratio(AudioParam::create(realm, context, options.ratio, 1, 20, Bindings::AutomationRate::KRate))
, m_attack(AudioParam::create(realm, options.attack, 0, 1, Bindings::AutomationRate::KRate)) , m_attack(AudioParam::create(realm, context, options.attack, 0, 1, Bindings::AutomationRate::KRate))
, m_release(AudioParam::create(realm, options.release, 0, 1, Bindings::AutomationRate::KRate)) , m_release(AudioParam::create(realm, context, options.release, 0, 1, Bindings::AutomationRate::KRate))
{ {
} }

View file

@ -41,7 +41,7 @@ WebIDL::ExceptionOr<GC::Ref<GainNode>> GainNode::construct_impl(JS::Realm& realm
GainNode::GainNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, GainOptions const& options) GainNode::GainNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, GainOptions const& options)
: AudioNode(realm, context) : AudioNode(realm, context)
, m_gain(AudioParam::create(realm, options.gain, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_gain(AudioParam::create(realm, context, options.gain, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
{ {
} }

View file

@ -50,8 +50,8 @@ WebIDL::ExceptionOr<GC::Ref<OscillatorNode>> OscillatorNode::construct_impl(JS::
OscillatorNode::OscillatorNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, OscillatorOptions const& options) OscillatorNode::OscillatorNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, OscillatorOptions const& options)
: AudioScheduledSourceNode(realm, context) : AudioScheduledSourceNode(realm, context)
, m_type(options.type) , m_type(options.type)
, m_frequency(AudioParam::create(realm, options.frequency, -context->nyquist_frequency(), context->nyquist_frequency(), Bindings::AutomationRate::ARate)) , m_frequency(AudioParam::create(realm, context, options.frequency, -context->nyquist_frequency(), context->nyquist_frequency(), Bindings::AutomationRate::ARate))
, m_detune(AudioParam::create(realm, options.detune, -1200 * AK::log2(NumericLimits<float>::max()), 1200 * AK::log2(NumericLimits<float>::max()), Bindings::AutomationRate::ARate)) , m_detune(AudioParam::create(realm, context, options.detune, -1200 * AK::log2(NumericLimits<float>::max()), 1200 * AK::log2(NumericLimits<float>::max()), Bindings::AutomationRate::ARate))
{ {
} }

View file

@ -62,12 +62,12 @@ WebIDL::ExceptionOr<GC::Ref<PannerNode>> PannerNode::construct_impl(JS::Realm& r
PannerNode::PannerNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options) PannerNode::PannerNode(JS::Realm& realm, GC::Ref<BaseAudioContext> context, PannerOptions const& options)
: AudioNode(realm, context) : AudioNode(realm, context)
, m_panning_model(options.panning_model) , m_panning_model(options.panning_model)
, m_position_x(AudioParam::create(realm, options.position_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_position_x(AudioParam::create(realm, context, options.position_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_position_y(AudioParam::create(realm, options.position_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_position_y(AudioParam::create(realm, context, options.position_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_position_z(AudioParam::create(realm, options.position_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_position_z(AudioParam::create(realm, context, options.position_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_orientation_x(AudioParam::create(realm, options.orientation_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_orientation_x(AudioParam::create(realm, context, options.orientation_x, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_orientation_y(AudioParam::create(realm, options.orientation_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_orientation_y(AudioParam::create(realm, context, options.orientation_y, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_orientation_z(AudioParam::create(realm, options.orientation_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate)) , m_orientation_z(AudioParam::create(realm, context, options.orientation_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_distance_model(options.distance_model) , m_distance_model(options.distance_model)
, m_ref_distance(options.ref_distance) , m_ref_distance(options.ref_distance)
, m_max_distance(options.max_distance) , m_max_distance(options.max_distance)