From 7b2a7a4cf837fc6887eefac0b17ddb7bfc7bd50c Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 2 Sep 2026 06:14:24 +0800 Subject: [PATCH] fix(events): align lifecycle event initialization --- .../src/context_bootstrap/events.rs | 22 ++++++++++++++----- .../context_bootstrap/navigation_events.rs | 12 ++-------- .../context_host/child_documents/loads.rs | 3 +-- .../script_vm/tests/browser_api/navigation.rs | 18 +++++++++++---- .../tests/dom_elements/dom_surface.rs | 17 +++++++++----- 5 files changed, 45 insertions(+), 27 deletions(-) diff --git a/moli-renderer-v8/src/context_bootstrap/events.rs b/moli-renderer-v8/src/context_bootstrap/events.rs index 6a089db3b3..6d3c4863b2 100644 --- a/moli-renderer-v8/src/context_bootstrap/events.rs +++ b/moli-renderer-v8/src/context_bootstrap/events.rs @@ -19,6 +19,10 @@ const BEFORE_UNLOAD_EVENT_RETURN_VALUE_SLOT: &str = "__moliBeforeUnloadEventRetu #[derive(WebApiObject)] #[webapi(plain)] struct PageTransitionEventInitDeclaration { + #[webapi(data_property, enumerable)] + bubbles: bool, + #[webapi(data_property, enumerable)] + cancelable: bool, #[webapi(data_property, enumerable)] persisted: bool, } @@ -56,11 +60,13 @@ pub(crate) fn construct_original_event<'s>( let event_ctor = super::exposed_interfaces::ensure_intrinsic_interface_constructor(scope, "Event").ok()?; let event_type = v8_string(scope, event_type)?; - { + let event = { let try_catch = std::pin::pin!(v8::TryCatch::new(scope)); let scope = try_catch.init(); event_ctor.new_instance(&scope, &[event_type.into()]) - } + }?; + mark_event_trusted(scope, event); + Some(event) } fn new_before_unload_event<'s>( @@ -86,7 +92,9 @@ fn new_before_unload_event<'s>( pub(crate) fn construct_original_before_unload_event<'s>( scope: &mut v8::PinScope<'s, '_>, ) -> Option> { - new_before_unload_event(scope, "beforeunload", true) + let event = new_before_unload_event(scope, "beforeunload", true)?; + mark_event_trusted(scope, event); + Some(event) } pub(in crate::context_bootstrap) fn new_uninitialized_before_unload_event<'s>( @@ -192,14 +200,16 @@ pub(crate) fn construct_original_page_transition_event<'s>( ) .ok()?; let event_type = v8_string(scope, event_type)?; - let init = PageTransitionEventInitDeclaration::new(persisted) + let init = PageTransitionEventInitDeclaration::new(true, true, persisted) .bind(scope) .expect("PageTransitionEvent init declaration should bind"); - { + let event = { let try_catch = std::pin::pin!(v8::TryCatch::new(scope)); let scope = try_catch.init(); event_ctor.new_instance(&scope, &[event_type.into(), init.into()]) - } + }?; + mark_event_trusted(scope, event); + Some(event) } pub(crate) fn construct_original_storage_event_utf16<'s>( diff --git a/moli-renderer-v8/src/context_bootstrap/navigation_events.rs b/moli-renderer-v8/src/context_bootstrap/navigation_events.rs index 32285832ed..61c5423799 100644 --- a/moli-renderer-v8/src/context_bootstrap/navigation_events.rs +++ b/moli-renderer-v8/src/context_bootstrap/navigation_events.rs @@ -1,6 +1,6 @@ use super::events::{ construct_original_before_unload_event, construct_original_event, - run_navigate_event_precommit_handlers, + construct_original_page_transition_event, run_navigate_event_precommit_handlers, }; use super::location_history_storage::{ NAVIGATION_ENTRY_EVENT_LISTENERS_SLOT, NAVIGATION_EVENT_LISTENERS_SLOT, @@ -106,13 +106,6 @@ struct HashChangeEventStateDeclaration { new_url: String, } -#[derive(WebApiObject)] -#[webapi(plain)] -struct PageTransitionEventStateDeclaration { - #[webapi(data_property)] - persisted: bool, -} - #[derive(WebApiObject)] #[webapi(plain)] struct NavigateEventInitDeclaration<'scope> { @@ -645,10 +638,9 @@ pub(crate) fn dispatch_pagehide_for_runtime_owner<'s>( scope: &mut v8::PinScope<'s, '_>, owner: v8::Local<'s, v8::Object>, ) { - let Some(event) = construct_original_event(scope, "pagehide") else { + let Some(event) = construct_original_page_transition_event(scope, "pagehide", false) else { return; }; - let _ = PageTransitionEventStateDeclaration::new(false).initialize(scope, event); dispatch_unload_lifecycle_event_for_runtime_owner(scope, owner, "pagehide", event); } diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_documents/loads.rs b/moli-renderer-v8/src/native_bridge/context_host/child_documents/loads.rs index 62d19dd0a7..f6d7579da0 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_documents/loads.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_documents/loads.rs @@ -10,7 +10,7 @@ use crate::{ content_security_policy::{ ContentSecurityPolicyViolationEventFields, send_content_security_policy_reports, }, - context_bootstrap::{construct_original_event, mark_event_trusted}, + context_bootstrap::construct_original_event, document_runtime::{ DocumentNavigationEmbeddingContext, DocumentPolicyContainer, DomHandle, EventTargetHandle, }, @@ -651,7 +651,6 @@ impl JsContextHost { if object_fallback_required { self.enter_object_fallback_state(scope, handle); if let Some(event) = construct_original_event(scope, "error") { - mark_event_trusted(scope, event); let host_ptr: *mut JsContextHost = self; let runtime = unsafe { &mut *self.runtime }; let _ = runtime.dispatch_public_event_best_effort( diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/navigation.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/navigation.rs index 11daeaf5f1..b905a0bd02 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/navigation.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/navigation.rs @@ -2029,9 +2029,16 @@ fn cross_document_unload_lifecycle_orders_pagehide_before_unload_without_timer() r##" (() => { const log = []; - addEventListener("beforeunload", () => log.push("beforeunload")); - addEventListener("pagehide", event => log.push(`pagehide:${event.persisted}`)); - addEventListener("unload", () => log.push("unload")); + addEventListener("beforeunload", event => log.push(`beforeunload:${event.isTrusted}`)); + addEventListener("pagehide", event => log.push([ + "pagehide", + event instanceof PageTransitionEvent, + event.persisted, + event.bubbles, + event.cancelable, + event.isTrusted + ].join(":"))); + addEventListener("unload", event => log.push(`unload:${event.isTrusted}`)); navigation.navigate("/next-document"); return log.join("|"); })() @@ -2039,7 +2046,10 @@ fn cross_document_unload_lifecycle_orders_pagehide_before_unload_without_timer() ) .expect("cross-document unload lifecycle should evaluate"); - assert_eq!(lifecycle, "beforeunload|pagehide:false|unload"); + assert_eq!( + lifecycle, + "beforeunload:true|pagehide:true:false:true:true:true|unload:true" + ); assert!( !vm.has_ready_timeout(), "pagehide is part of the unload step and must not create an independent timer task" diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs index 8ee1a256b0..0b3d7e97e8 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs @@ -13780,7 +13780,14 @@ fn window_pageshow_uses_original_page_transition_event() { throw new Error("page replacement should not be invoked"); }; addEventListener("pageshow", event => { - __pageshowShape = `${event.type}:${event.persisted === false}:${'persisted' in event}`; + __pageshowShape = [ + event.type, + event.persisted === false, + 'persisted' in event, + event.bubbles, + event.cancelable, + event.isTrusted + ].join(':'); }); return "ready"; })() @@ -13793,7 +13800,7 @@ fn window_pageshow_uses_original_page_transition_event() { let shape = vm .eval("globalThis.__pageshowShape") .expect("pageshow shape should evaluate"); - assert_eq!(shape, "pageshow:true:true"); + assert_eq!(shape, "pageshow:true:true:true:true:true"); } #[test] @@ -13808,12 +13815,12 @@ fn window_load_uses_original_event_after_global_constructors_are_deleted() { globalThis.__windowLifecycleEvents = []; addEventListener('load', event => { __windowLifecycleEvents.push( - `load:${event instanceof OriginalEvent}:${event.target === document}:${event.currentTarget === window}` + `load:${event instanceof OriginalEvent}:${event.target === document}:${event.currentTarget === window}:${event.isTrusted}:${event.bubbles}:${event.cancelable}` ); }); addEventListener('pageshow', event => { __windowLifecycleEvents.push( - `pageshow:${event instanceof OriginalPageTransitionEvent}:${event.persisted}` + `pageshow:${event instanceof OriginalPageTransitionEvent}:${event.persisted}:${event.isTrusted}:${event.bubbles}:${event.cancelable}` ); }); delete globalThis.Event; @@ -13829,7 +13836,7 @@ fn window_load_uses_original_event_after_global_constructors_are_deleted() { assert_eq!( vm.eval("__windowLifecycleEvents.join('|')") .expect("window lifecycle results should evaluate"), - "load:true:true:true|pageshow:true:false" + "load:true:true:true:true:false:false|pageshow:true:false:true:true:true" ); }