diff --git a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs index d47353d180..f4e5a23f6c 100644 --- a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs +++ b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs @@ -19,6 +19,9 @@ pub(in crate::context_bootstrap) const STORAGE_INTERFACE_NAMES: &[&str] = &[ ]; pub(in crate::context_bootstrap) const WORKER_SHARED_INTERFACE_NAMES: &[&str] = &[ + "MediaSourceHandle", + "SourceBuffer", + "SourceBufferList", "EventTarget", "Event", "CustomEvent", @@ -118,6 +121,8 @@ const WINDOW_DEDICATED_AND_SHARED_WORKER_INTERFACE_NAMES: &[&str] = &[ "XMLHttpRequest", ]; const DEDICATED_AND_SHARED_WORKER_INTERFACE_NAMES: &[&str] = &["FileReaderSync"]; +const WINDOW_AND_DEDICATED_WORKER_INTERFACE_NAMES: &[&str] = + &["MediaSourceHandle", "SourceBuffer", "SourceBufferList"]; pub(in crate::context_bootstrap) const INDEXED_DB_INTERFACE_NAMES: &[&str] = &[ "DOMStringList", @@ -233,6 +238,7 @@ impl ExposureSet { const SERVICE_WORKER: u8 = 1 << 3; const WINDOW_ONLY: Self = Self(Self::WINDOW); + const WINDOW_AND_DEDICATED_WORKER: Self = Self(Self::WINDOW | Self::DEDICATED_WORKER); const ALL_REALMS: Self = Self(Self::WINDOW | Self::DEDICATED_WORKER | Self::SHARED_WORKER | Self::SERVICE_WORKER); const WORKERS: Self = Self(Self::DEDICATED_WORKER | Self::SHARED_WORKER | Self::SERVICE_WORKER); @@ -394,6 +400,9 @@ fn exposure_for_name(name: &str) -> ExposureSet { _ if WINDOW_DEDICATED_AND_SHARED_WORKER_INTERFACE_NAMES.contains(&name) => { ExposureSet::WINDOW_DEDICATED_AND_SHARED_WORKER } + _ if WINDOW_AND_DEDICATED_WORKER_INTERFACE_NAMES.contains(&name) => { + ExposureSet::WINDOW_AND_DEDICATED_WORKER + } _ if DEDICATED_AND_SHARED_WORKER_INTERFACE_NAMES.contains(&name) => { ExposureSet::DEDICATED_AND_SHARED_WORKER } diff --git a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/tests.rs b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/tests.rs index ec057407af..f088b84371 100644 --- a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/tests.rs +++ b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/tests.rs @@ -386,3 +386,40 @@ fn worker_realm_lazy_properties_follow_chromium_exposure_sets() { vec![false, false, false, false, false, true, false, false] ); } + +#[test] +fn source_buffer_interfaces_are_exposed_only_to_window_and_dedicated_workers() { + crate::ensure_v8_for_test(); + for (realm, expected) in [ + (super::RealmKind::DedicatedWorker, true), + (super::RealmKind::SharedWorker, false), + (super::RealmKind::ServiceWorker, false), + ] { + let mut isolate = v8::Isolate::new(Default::default()); + let scope = pin!(v8::HandleScope::new(&mut isolate)); + let scope = &mut scope.init(); + let context = v8::Context::new(scope, Default::default()); + let scope = &mut v8::ContextScope::new(scope, context); + let global = context.global(scope); + crate::context_bootstrap::install_worker_lazy_exposed_interfaces( + scope, global, realm, true, + ) + .expect("worker interfaces"); + for name in ["MediaSourceHandle", "SourceBuffer", "SourceBufferList"] { + assert_eq!( + global.has_own_property(scope, v8str(scope, name).into()), + Some(expected), + "{realm:?}/{name}" + ); + if expected { + assert!( + global + .get(scope, v8str(scope, name).into()) + .unwrap() + .is_function(), + "{name} materialization" + ); + } + } + } +} diff --git a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs index 0f3304f01b..0703c98e37 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs @@ -4,6 +4,18 @@ use crate::web_api_interfaces; use std::collections::HashSet; const CONSTRUCTOR_SPECS_BEFORE_STREAMS: &[ConstructorSpec] = &[ + ConstructorSpec { + interface: web_api_interfaces::MediaSourceHandle::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, + ConstructorSpec { + interface: web_api_interfaces::SourceBuffer::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, + ConstructorSpec { + interface: web_api_interfaces::SourceBufferList::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, ConstructorSpec { interface: web_api_interfaces::External::DESCRIPTOR, kind: ConstructorKind::Illegal, diff --git a/moli-renderer-v8/src/script_vm/tests/media_source_buffer_interface_exposure_interfaces.rs b/moli-renderer-v8/src/script_vm/tests/media_source_buffer_interface_exposure_interfaces.rs new file mode 100644 index 0000000000..ac32111d26 --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/media_source_buffer_interface_exposure_interfaces.rs @@ -0,0 +1,39 @@ +use super::*; + +#[test] +fn media_source_buffer_interface_exposure_interfaces_have_realm_local_constructors_and_exposure() { + for url in [ + "https://interface-exposure.test/", + "http://interface-exposure.test/", + ] { + let mut vm = new_storage_test_vm(url); + assert_eq!(vm.eval(r#" +(() => { + const assert = (ok, message) => { if (!ok) throw Error(message); }; + const names = ["MediaSourceHandle", "SourceBuffer", "SourceBufferList"]; + const parents = {"MediaSourceHandle": null, "SourceBuffer": "EventTarget", "SourceBufferList": "EventTarget"}; + if (!document.documentElement) document.appendChild(document.createElement('html')); + if (!document.body) document.documentElement.appendChild(document.createElement('body')); + const child = document.createElement('iframe'); + document.body.appendChild(child); + const other = child.contentWindow; + for (const realm of [window, other]) { + for (const name of names) { + const C = realm[name]; + assert(typeof C === 'function' && C.name === name && C.length === 0, name + ' constructor'); + assert(C.prototype.constructor === C, name + ' prototype identity'); + assert(Object.getPrototypeOf(C.prototype) === (parents[name] ? realm[parents[name]].prototype : realm.Object.prototype), name + ' prototype inheritance'); + assert(realm[name] === C, name + ' stable lazy materialization'); + for (const invoke of [() => C(), () => new C(), () => Reflect.construct(C, [], function Subclass() {})]) { + let error; + try { invoke(); } catch (caught) { error = caught; } + assert(error instanceof realm.TypeError, name + ' rejects construction in callee realm'); + } + assert(C !== (realm === window ? other[name] : window[name]), name + ' distinct realm constructors'); + } + } + return 'passed'; +})() +"#).unwrap(), "passed", "{url}"); + } +} diff --git a/moli-renderer-v8/src/script_vm/tests/mod.rs b/moli-renderer-v8/src/script_vm/tests/mod.rs index 699018b728..e58a055c97 100644 --- a/moli-renderer-v8/src/script_vm/tests/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/mod.rs @@ -2180,3 +2180,5 @@ mod response_blob_mime; mod intersection_target_order; mod media_device_interfaces; + +mod media_source_buffer_interface_exposure_interfaces; diff --git a/moli-renderer-v8/src/web_api_interfaces.rs b/moli-renderer-v8/src/web_api_interfaces.rs index 7e4bb8f88c..9bad2688bd 100644 --- a/moli-renderer-v8/src/web_api_interfaces.rs +++ b/moli-renderer-v8/src/web_api_interfaces.rs @@ -20,6 +20,9 @@ macro_rules! interfaces { } interfaces! { + MediaSourceHandle; + SourceBuffer: EventTarget; + SourceBufferList: EventTarget; AbortController; AbortSignal: EventTarget; AbstractRange;