From 8f0ddb48e2bb6138285ee9f07548bc7bd8042bc8 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Tue, 13 Oct 2026 04:26:20 +0800 Subject: [PATCH] fix(webrtc): preserve RTCError stacks and native proxy slots --- .../constructors/dom_exception/rtc_error.rs | 5 +- .../tests/browser_api/structured_clone.rs | 76 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/moli-renderer-v8/src/context_bootstrap/constructors/dom_exception/rtc_error.rs b/moli-renderer-v8/src/context_bootstrap/constructors/dom_exception/rtc_error.rs index 2fa26f80e4..72b6239e69 100644 --- a/moli-renderer-v8/src/context_bootstrap/constructors/dom_exception/rtc_error.rs +++ b/moli-renderer-v8/src/context_bootstrap/constructors/dom_exception/rtc_error.rs @@ -142,6 +142,7 @@ pub(crate) fn rtc_error_constructor_callback<'s>( nullable_double_slot_value(scope, parsed.init.sent_alert.map(f64::from)), ); if declaration.initialize(scope, args.this()).is_ok() { + capture_dom_exception_stack(scope, args.this()); apply_webidl_constructor_prototype_fallback( scope, args.this(), @@ -160,7 +161,9 @@ fn slot_getter<'s>( let slot = v8::Local::::try_from(args.data()) .expect("RTCError attribute slot") .to_rust_string_lossy(scope); - if let Some(value) = get_private_value(scope, args.this(), &slot) { + let receiver = moli_webapi_declare::web_api_object_target(scope, args.this()) + .expect("RTCError receiver was validated"); + if let Some(value) = get_private_value(scope, receiver, &slot) { rv.set(value); } } diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs index 8d0f8e656b..896161f6ab 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs @@ -774,3 +774,79 @@ fn observer_and_webgl_factory_results_have_native_identity() { "DataCloneError|DataCloneError|DataCloneError|DataCloneError" ); } + +#[test] +fn dom_exception_factories_capture_v8_stacks_without_author_lookup() { + let mut vm = new_storage_page_task_executor_test_vm("https://native-error-stack.test/"); + assert_eq!(vm.eval(r#"(() => { + document.body.innerHTML = ''; + const other = document.querySelector('iframe').contentWindow; + for (const realm of [window, other]) { + const descriptor = Object.getOwnPropertyDescriptor(realm.Error, 'captureStackTrace'); + Object.defineProperty(realm.Error, 'captureStackTrace', {configurable:true, get() {throw 42;}}); + try { + for (const error of [new realm.DOMException('message', 'AbortError'), + new realm.QuotaExceededError('full'), new realm.RTCError({errorDetail:'dtls-failure'}, 'rtc')]) { + const stack = error.stack; + if (typeof stack !== 'string' || !stack.startsWith(error.name + ': ' + error.message) || + !Object.hasOwn(error,'stack') || Object.getOwnPropertyDescriptor(error,'stack').enumerable) throw Error('native stack'); + } + let syntax; + try { document.querySelector('['); } catch(error) { syntax = error; } + if (!(syntax instanceof DOMException) || typeof syntax.stack !== 'string') throw Error('native factory'); + } finally { + if(descriptor)Object.defineProperty(realm.Error,'captureStackTrace',descriptor); + else delete realm.Error.captureStackTrace; + } + } + return true; + })()"#).unwrap(), "true"); +} + +#[test] +fn dom_exception_getters_read_registered_native_proxy_targets() { + let mut vm = new_storage_page_task_executor_test_vm("https://native-error-receiver.test/"); + vm.eval("globalThis.errors = [new DOMException('message','AbortError'),new QuotaExceededError('full',{quota:12}),new RTCError({errorDetail:'dtls-failure'},'rtc')]").unwrap(); + let context_ptr = &vm.page_default_runtime.context as *const _; + vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { + let global = scope.get_current_context().global(scope); + let key = crate::util::v8str(scope, "errors"); + let errors = + v8::Local::::try_from(global.get(scope, key.into()).unwrap()).unwrap(); + let proxies = v8::Array::new(scope, 3); + for index in 0..3 { + let error = + v8::Local::::try_from(errors.get_index(scope, index).unwrap()).unwrap(); + let handler = crate::util::new_null_prototype_object(scope); + let proxy = v8::Proxy::new(scope, error, handler).unwrap(); + moli_webapi_declare::register_web_api_proxy(scope, proxy).unwrap(); + assert!(crate::web_api_interfaces::DOMException::is_instance( + scope, + proxy.into() + )); + assert_eq!(proxies.set_index(scope, index, proxy.into()), Some(true)); + } + let key = crate::util::v8str(scope, "nativeProxies"); + assert_eq!( + global.create_data_property(scope, key.into(), proxies.into()), + Some(true) + ); + Ok(()) + }) + .unwrap(); + assert_eq!(vm.eval(r#"(() => { + for(let index=0;index