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
parent 2edd0812ca
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)
: AudioScheduledSourceNode(realm, context)
, m_buffer(options.buffer)
, m_playback_rate(AudioParam::create(realm, 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_playback_rate(AudioParam::create(realm, context, options.playback_rate, 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_start(options.loop_start)
, m_loop_end(options.loop_end)

View file

@ -12,23 +12,23 @@ namespace Web::WebAudio {
GC_DEFINE_ALLOCATOR(AudioListener);
AudioListener::AudioListener(JS::Realm& realm)
AudioListener::AudioListener(JS::Realm& realm, GC::Ref<BaseAudioContext> context)
: Bindings::PlatformObject(realm)
, m_forward_x(AudioParam::create(realm, 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_z(AudioParam::create(realm, -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_y(AudioParam::create(realm, 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_up_x(AudioParam::create(realm, 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_z(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, context, 0.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, context, 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, context, 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, context, 1.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;

View file

@ -20,7 +20,7 @@ class AudioListener final : public Bindings::PlatformObject {
GC_DECLARE_ALLOCATOR(AudioListener);
public:
static GC::Ref<AudioListener> create(JS::Realm&);
static GC::Ref<AudioListener> create(JS::Realm&, GC::Ref<BaseAudioContext>);
virtual ~AudioListener() override;
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);
private:
explicit AudioListener(JS::Realm&);
explicit AudioListener(JS::Realm&, GC::Ref<BaseAudioContext>);
virtual void initialize(JS::Realm&) 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
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.
// 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/Intrinsics.h>
#include <LibWeb/WebAudio/AudioParam.h>
#include <LibWeb/WebAudio/BaseAudioContext.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::WebAudio {
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)
, m_context(context)
, m_current_value(default_value)
, m_default_value(default_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;
@ -143,6 +145,7 @@ void AudioParam::initialize(JS::Realm& realm)
void AudioParam::visit_edges(Cell::Visitor& 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);
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;
GC::Ref<BaseAudioContext> context() const { return m_context; }
float value() const;
void set_value(float);
@ -41,7 +43,9 @@ public:
WebIDL::ExceptionOr<GC::Ref<AudioParam>> cancel_and_hold_at_time(double cancel_time);
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
float m_current_value {}; // [[current value]]

View file

@ -29,7 +29,7 @@ namespace Web::WebAudio {
BaseAudioContext::BaseAudioContext(JS::Realm& realm, float sample_rate)
: DOM::EventTarget(realm)
, 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)
: AudioNode(realm, context)
, m_type(options.type)
, m_frequency(AudioParam::create(realm, 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_q(AudioParam::create(realm, 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_frequency(AudioParam::create(realm, context, options.frequency, 0, context->nyquist_frequency(), 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, context, options.q, NumericLimits<float>::lowest(), 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)
: 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)
: 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)
: AudioNode(realm, context)
, m_threshold(AudioParam::create(realm, options.threshold, -100, 0, Bindings::AutomationRate::KRate))
, m_knee(AudioParam::create(realm, options.knee, 0, 40, Bindings::AutomationRate::KRate))
, m_ratio(AudioParam::create(realm, options.ratio, 1, 20, Bindings::AutomationRate::KRate))
, m_attack(AudioParam::create(realm, options.attack, 0, 1, Bindings::AutomationRate::KRate))
, m_release(AudioParam::create(realm, options.release, 0, 1, Bindings::AutomationRate::KRate))
, m_threshold(AudioParam::create(realm, context, options.threshold, -100, 0, Bindings::AutomationRate::KRate))
, m_knee(AudioParam::create(realm, context, options.knee, 0, 40, Bindings::AutomationRate::KRate))
, m_ratio(AudioParam::create(realm, context, options.ratio, 1, 20, Bindings::AutomationRate::KRate))
, m_attack(AudioParam::create(realm, context, options.attack, 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)
: 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)
: AudioScheduledSourceNode(realm, context)
, m_type(options.type)
, m_frequency(AudioParam::create(realm, 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_frequency(AudioParam::create(realm, context, options.frequency, -context->nyquist_frequency(), context->nyquist_frequency(), 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)
: AudioNode(realm, context)
, 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_y(AudioParam::create(realm, 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_orientation_x(AudioParam::create(realm, 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_z(AudioParam::create(realm, options.orientation_z, 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, context, options.position_y, 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, context, options.orientation_x, 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, context, options.orientation_z, NumericLimits<float>::lowest(), NumericLimits<float>::max(), Bindings::AutomationRate::ARate))
, m_distance_model(options.distance_model)
, m_ref_distance(options.ref_distance)
, m_max_distance(options.max_distance)