From cb79d4d371bfe815be12d638e4af8a01afbb3f32 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Thu, 1 Oct 2026 23:11:42 +0800 Subject: [PATCH] feat(media): expose MediaSource handle and buffer interface shells --- .../exposed_interfaces/metadata.rs | 8 ++++ .../exposed_interfaces/tests.rs | 37 ++++++++++++++++++ .../src/context_bootstrap/specs/registry.rs | 12 ++++++ ...ce_buffer_interface_exposure_interfaces.rs | 39 +++++++++++++++++++ moli-renderer-v8/src/script_vm/tests/mod.rs | 1 + moli-renderer-v8/src/web_api_interfaces.rs | 3 ++ 6 files changed, 100 insertions(+) create mode 100644 moli-renderer-v8/src/script_vm/tests/media_source_buffer_interface_exposure_interfaces.rs 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 eee2c8436f..4d97431efb 100644 --- a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs +++ b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs @@ -64,6 +64,9 @@ pub(in crate::context_bootstrap) const WORKER_SHARED_INTERFACE_NAMES: &[&str] = "BroadcastChannel", "Notification", "ImageData", + "MediaSourceHandle", + "SourceBuffer", + "SourceBufferList", "EncodedVideoChunk", "ImageBitmap", "ImageBitmapRenderingContext", @@ -168,6 +171,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", @@ -446,6 +451,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 52fff725a5..1e524ba059 100644 --- a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/tests.rs +++ b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/tests.rs @@ -732,3 +732,40 @@ fn worker_rectangle_interfaces_share_native_state_and_inheritance() { ); } } + +#[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 3054e60403..22956003c9 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs @@ -1383,6 +1383,18 @@ const CONSTRUCTOR_SPECS_AFTER_STREAMS: &[ConstructorSpec] = &[ interface: web_api_interfaces::MediaQueryList::DESCRIPTOR, kind: ConstructorKind::MediaQueryList, }, + ConstructorSpec { + interface: web_api_interfaces::SourceBufferList::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, + ConstructorSpec { + interface: web_api_interfaces::SourceBuffer::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, + ConstructorSpec { + interface: web_api_interfaces::MediaSourceHandle::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, ConstructorSpec { interface: web_api_interfaces::MediaSource::DESCRIPTOR, kind: ConstructorKind::MediaSource, 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 4c21849802..2abdafc2fc 100644 --- a/moli-renderer-v8/src/script_vm/tests/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/mod.rs @@ -2169,6 +2169,7 @@ mod http_fixture; mod iframe_load_delivery; mod media_owner_playback_interfaces; mod media_recorder_shell; +mod media_source_buffer_interface_exposure_interfaces; mod performance_observer_contract; mod performance_receiver_consolidation; mod performance_timing_interface_exposure_interfaces; diff --git a/moli-renderer-v8/src/web_api_interfaces.rs b/moli-renderer-v8/src/web_api_interfaces.rs index f5aff879cf..df9a3ac508 100644 --- a/moli-renderer-v8/src/web_api_interfaces.rs +++ b/moli-renderer-v8/src/web_api_interfaces.rs @@ -325,6 +325,9 @@ interfaces! { MediaQueryListEvent: Event; MediaRecorder: EventTarget; MediaSession; + MediaSourceHandle; + SourceBuffer: EventTarget; + SourceBufferList: EventTarget; MediaSource: EventTarget; MemoryInfo; MIDIPort: EventTarget;