From 37b6dcce2e83043ff835f742cbdf44becb95609d 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 | 2 + moli-renderer-v8/src/web_api_interfaces.rs | 3 ++ 6 files changed, 101 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 bfbd97a502..0e0bc720c4 100644 --- a/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs +++ b/moli-renderer-v8/src/context_bootstrap/exposed_interfaces/metadata.rs @@ -25,6 +25,9 @@ pub(in crate::context_bootstrap) const WORKER_SHARED_INTERFACE_NAMES: &[&str] = "DOMPoint", "DOMMatrixReadOnly", "DOMMatrix", + "MediaSourceHandle", + "SourceBuffer", + "SourceBufferList", "EventTarget", "Event", "CustomEvent", @@ -135,6 +138,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", @@ -413,6 +418,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 4a8a09fbfe..0349f61ca4 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 1490e7d66b..7144f6a409 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::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::ModelContext::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 50fc9f050e..97047fff0c 100644 --- a/moli-renderer-v8/src/script_vm/tests/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/mod.rs @@ -2214,3 +2214,5 @@ mod media_device_interfaces; mod dom_rect_factory_descriptors; mod dom_rect_structured_clone; mod domrect_receiver_consolidation; + +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 6a47c6f59d..91d218da2f 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;