From 1a40265083e9fb8d0a418fc7ee765b47d507fcc3 Mon Sep 17 00:00:00 2001 From: lanyue-llk <270302213+lanyue-llk@users.noreply.github.com> Date: Thu, 15 Oct 2026 21:04:50 +0800 Subject: [PATCH] fix(permissions): give PermissionStatus inherited EventTarget behavior --- .../window_runtime/navigator.rs | 6 ++- .../misc/extracted/document_navigator.rs | 45 +++++++++++++++++++ moli-renderer-v8/src/web_api_interfaces.rs | 2 +- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs b/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs index d9bb1cd615..018ebe419f 100644 --- a/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs +++ b/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs @@ -608,9 +608,11 @@ fn build_permission_status_object<'s>( name: &str, state: &str, ) -> v8::Local<'s, v8::Object> { - PermissionStatusObjectDeclaration::new(name.to_owned(), state.to_owned()) + let status = PermissionStatusObjectDeclaration::new(name.to_owned(), state.to_owned()) .bind(scope) - .expect("PermissionStatus declaration should bind") + .expect("PermissionStatus declaration should bind"); + mark_simple_event_target_slot(scope, status, "__moliPermissionStatusListeners"); + status } pub(in crate::context_bootstrap) fn permission_status_name_getter_callback<'s>( diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs index b9c82e4fcb..ad4a700c8e 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs @@ -870,3 +870,48 @@ async fn request_relative_urls_follow_live_http_iframe_base_urls() { assert_retained_request_uses_child_document(&mut vm, child_url.as_str()); server.await.expect("iframe response server should finish"); } + +#[test] +fn permission_status_supports_inherited_event_target_listeners() { + let mut vm = new_storage_test_vm("https://permission-status-events.test/"); + vm.eval( + r#" + globalThis.permissionEvents = "pending"; + Promise.all([ + navigator.permissions.query({name: "geolocation"}), + navigator.permissions.query({name: "clipboard-write"}) + ]).then(([first, second]) => { + const checks = [ + first instanceof PermissionStatus, + first instanceof EventTarget, + Object.getPrototypeOf(PermissionStatus.prototype) === EventTarget.prototype, + first.addEventListener === EventTarget.prototype.addEventListener + ]; + let calls = 0; + let otherCalls = 0; + const listener = event => { + checks.push(event.target === first, event.currentTarget === first); + calls++; + }; + first.addEventListener("change", listener); + second.addEventListener("change", () => otherCalls++); + first.dispatchEvent(new Event("change")); + first.removeEventListener("change", listener); + first.dispatchEvent(new Event("change")); + let once = 0; + first.addEventListener("change", () => once++, {once: true}); + first.dispatchEvent(new Event("change")); + first.dispatchEvent(new Event("change")); + checks.push(calls === 1, otherCalls === 0, once === 1); + try { new PermissionStatus(); checks.push(false); } + catch (error) { checks.push(error instanceof TypeError); } + globalThis.permissionEvents = checks.join(","); + }).catch(error => { globalThis.permissionEvents = String(error); }); + "#, + ) + .expect("permission listener scenario should evaluate"); + let result = vm + .eval("String(globalThis.permissionEvents)") + .expect("permission queries should settle"); + assert_eq!(result, "true,true,true,true,true,true,true,true,true,true"); +} diff --git a/moli-renderer-v8/src/web_api_interfaces.rs b/moli-renderer-v8/src/web_api_interfaces.rs index 11f938ead0..273e48b341 100644 --- a/moli-renderer-v8/src/web_api_interfaces.rs +++ b/moli-renderer-v8/src/web_api_interfaces.rs @@ -391,7 +391,7 @@ interfaces! { PerformanceTiming; PeriodicSyncEvent: ExtendableEvent; PeriodicSyncManager; - PermissionStatus; + PermissionStatus: EventTarget; Permissions; Plugin; PluginArray;