From cc23c0fbd73cfabe2a17ee4aca7047b6487e4e6e Mon Sep 17 00:00:00 2001 From: ldm0 Date: Thu, 5 Nov 2026 18:46:04 +0800 Subject: [PATCH] refactor(renderer): model pending and ready main worlds explicitly --- .../moli_cdp_smoke/groups/lazy_main_world.py | 11 ++ moli-renderer-v8/src/runtime/page_commands.rs | 6 +- .../tests/extracted/lazy_main_world.rs | 52 ++++++ moli-renderer-v8/src/script_vm.rs | 159 +++++++++--------- .../src/script_vm/blob_inspector.rs | 3 +- .../src/script_vm/browsing_contexts.rs | 30 ++-- .../src/script_vm/child_frame_runtime.rs | 16 +- .../src/script_vm/child_host_load.rs | 2 +- .../src/script_vm/classic_script_exception.rs | 50 +++--- .../src/script_vm/context_scope.rs | 67 +++++++- .../script_vm/document_context_transition.rs | 6 +- .../src/script_vm/document_environment.rs | 55 ++---- .../src/script_vm/document_isolate.rs | 4 +- .../src/script_vm/dom_debugger.rs | 3 +- .../src/script_vm/drop_cleanup.rs | 4 +- moli-renderer-v8/src/script_vm/eval_exec.rs | 25 +-- .../src/script_vm/execution_contexts.rs | 19 +-- .../src/script_vm/font_resources.rs | 2 +- .../src/script_vm/initial_window_document.rs | 9 +- moli-renderer-v8/src/script_vm/main_world.rs | 118 +++++++++---- .../src/script_vm/native_module.rs | 2 +- .../script_vm/native_module/module_graph.rs | 25 +-- .../native_module/module_reactions.rs | 98 +++++------ .../src/script_vm/native_module/tests.rs | 6 +- .../tests/constructor_preservation.rs | 4 +- .../src/script_vm/navigation_history.rs | 8 +- .../src/script_vm/page_task_enqueue.rs | 148 ++++++---------- .../src/script_vm/post_parse_lifecycle.rs | 136 +++++---------- .../src/script_vm/prepared_scripts.rs | 36 ++-- .../src/script_vm/related_page.rs | 62 ++++--- .../src/script_vm/runtime_evaluation.rs | 14 +- .../src/script_vm/runtime_observability.rs | 61 +++---- .../src/script_vm/script_event_body.rs | 94 ++++------- .../src/script_vm/script_tasks.rs | 96 +++++------ .../src/script_vm/security_policy.rs | 40 ++--- .../postmessage_selection_and_errors.rs | 3 +- .../window_security_and_domain.rs | 3 +- .../insert_adjacent_element_conversion.rs | 2 +- .../tests/browser_api/message_event_init.rs | 2 +- .../tests/browser_api/midi_events.rs | 2 +- .../browser_api/misc/extracted/css_surface.rs | 2 +- .../tests/browser_api/structured_clone.rs | 2 +- .../browser_api/web_transport_error_values.rs | 6 +- .../tests/browser_api/webrtc_events.rs | 2 +- .../src/script_vm/tests/canvas_fill_rect.rs | 2 +- .../tests/canvas_transform_snapshots.rs | 2 +- .../script_vm/tests/credential_container.rs | 2 +- .../script_vm/tests/credential_interfaces.rs | 2 +- .../tests/cross_origin_navigation.rs | 2 +- .../tests/css_keyframes_mutations.rs | 2 +- .../tests/css_keyframes_receivers.rs | 2 +- .../script_vm/tests/document_create_event.rs | 2 +- .../extracted/frame_navigation_and_history.rs | 2 +- .../tests/dom_exception_proxy_identity.rs | 2 +- .../computed_style/computed_style_access.rs | 3 +- .../src/script_vm/tests/dom_xhr/receivers.rs | 2 +- .../split30_gethtml_derived_options.rs | 2 +- .../src/script_vm/tests/dom_xhr/xhr.rs | 40 +++-- .../script_vm/tests/encoded_video_chunk.rs | 2 +- .../tests/extracted/realms_and_teardown.rs | 15 +- .../tests/extracted/script_execution.rs | 2 +- .../src/script_vm/tests/gamepad_interfaces.rs | 2 +- .../script_vm/tests/idb_sqlite_lifecycle.rs | 2 +- .../src/script_vm/tests/idb_transaction_gc.rs | 2 +- .../src/script_vm/tests/indexed_db.rs | 2 +- .../src/script_vm/tests/indexed_db_gc.rs | 4 +- .../src/script_vm/tests/lazy_storage.rs | 2 +- .../tests/media_device_interfaces.rs | 3 +- .../script_vm/tests/message_port_transfer.rs | 2 +- .../src/script_vm/tests/observer_callbacks.rs | 2 +- .../src/script_vm/tests/payment_events.rs | 2 +- .../tests/picture_in_picture_interfaces.rs | 4 +- .../src/script_vm/tests/rtc_configuration.rs | 4 +- .../src/script_vm/tests/rtc_encoded_frames.rs | 4 +- .../script_vm/tests/svg_geometry_methods.rs | 2 +- .../src/script_vm/tests/time_ranges.rs | 2 +- .../url_and_initializer_semantics.rs | 28 +-- .../webidl_and_transform_streams.rs | 3 +- .../tests/writable_stream_native_receivers.rs | 2 +- .../src/script_vm/window_realm_runtime.rs | 41 ++--- .../src/script_vm/worker_host_bridge_body.rs | 5 +- 81 files changed, 844 insertions(+), 855 deletions(-) diff --git a/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py b/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py index 3e1c35794f..13dd2180cb 100644 --- a/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py +++ b/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py @@ -91,6 +91,13 @@ async def run_lazy_main_world_group(endpoint: str, _fixture: str, if "Native content" not in outer: raise SmokeError(f"native DOM query returned {outer!r}") if activation == "native-io": + await wire.call("Emulation.setTouchEmulationEnabled", { + "enabled": True, "maxTouchPoints": 7, + }, session) + await wire.call("Emulation.setEmulatedMedia", { + "media": "print", + "features": [{"name": "prefers-color-scheme", "value": "dark"}], + }, session) await wire.call("Performance.enable", session=session) metrics = {m["name"]: m["value"] for m in (await wire.call("Performance.getMetrics", session=session))["metrics"]} @@ -120,6 +127,10 @@ async def run_lazy_main_world_group(endpoint: str, _fixture: str, assert_equal(observed, {"title": "Lazy activation", "state": "complete", "text": "Native content", "color": "rgb(0, 0, 255)"}, "first main-world evaluation sees completed native Document") + if activation == "native-io": + assert_equal(await wire.evaluate(session, "({touch:navigator.maxTouchPoints, print:matchMedia('print').matches, dark:matchMedia('(prefers-color-scheme: dark)').matches})"), + {"touch": 7, "print": True, "dark": True}, + "native emulation survives the first main-world activation") if activation in ("preload", "deferred-script", "inline-load"): assert_equal(await wire.evaluate(session, "activationCount"), 1, "initial script runs exactly once") diff --git a/moli-renderer-v8/src/runtime/page_commands.rs b/moli-renderer-v8/src/runtime/page_commands.rs index 6d56f07379..8cbd3c7b0b 100644 --- a/moli-renderer-v8/src/runtime/page_commands.rs +++ b/moli-renderer-v8/src/runtime/page_commands.rs @@ -337,11 +337,11 @@ impl PageVm { Ok(RendererPageReply::Unit) } RendererPageCommand::DefaultExecutionContextId => { - self.vm_mut().initialize_main_world_if_needed()?; + self.vm_mut().ensure_main_world()?; Ok(RendererPageReply::OptionalExecutionContextId(self.default_execution_context_id())) } RendererPageCommand::DefaultOrInitialExecutionContextId => { - self.vm_mut().initialize_main_world_if_needed()?; + self.vm_mut().ensure_main_world()?; Ok(RendererPageReply::OptionalExecutionContextId(self.default_or_initial_execution_context_id())) } RendererPageCommand::HasIsolatedWorldNamed { name, frame_id } => { @@ -370,7 +370,7 @@ impl PageVm { self.isolated_execution_context_id_for_inspector_context(execution_context_id), )), RendererPageCommand::RuntimeRealmInventory => { - self.vm_mut().initialize_main_world_if_needed()?; + self.vm_mut().ensure_main_world()?; Ok(RendererPageReply::RuntimeRealmInventory(self.runtime_realm_inventory())) } RendererPageCommand::LiveChildDefaultRuntimeRealmInventory => { diff --git a/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs b/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs index b8d30b2817..00ff6d7007 100644 --- a/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs +++ b/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs @@ -115,6 +115,58 @@ async fn evaluate_lazy_test(page: &RendererPageHandle, source: &str) -> serde_js renderer_json_value(reply).expect("evaluation returns a JSON value") } +#[tokio::test(flavor = "multi_thread")] +async fn native_environment_updates_survive_main_world_activation() { + let (_runtime, mut page) = lazy_test_page( + "

Native environment before script

", + Default::default(), + ) + .await; + assert_main_world_count(&page, 0).await; + for command in [ + RendererPageCommand::SetNavigatorOverrides(moli_page_types::NavigatorOverrides { + online: Some(false), + max_touch_points: 7, + ..Default::default() + }), + RendererPageCommand::SetIdleOverride(Some(moli_page_types::EmulatedIdleOverride { + is_user_active: false, + is_screen_unlocked: false, + })), + RendererPageCommand::SetEmulatedMedia(moli_page_types::EmulatedMediaOverrides { + media: Some("print".to_owned()), + color_scheme: Some("dark".to_owned()), + ..Default::default() + }), + ] { + let (reply, _) = page.run_async_command(command).await.unwrap(); + assert!(matches!(reply, RendererPageReply::Unit)); + assert_main_world_count(&page, 0).await; + } + let expression = "JSON.stringify({online:navigator.onLine, touch:navigator.maxTouchPoints, print:matchMedia('print').matches, dark:matchMedia('(prefers-color-scheme: dark)').matches})"; + assert_eq!( + evaluate_lazy_test(&page, expression).await, + r#"{"online":false,"touch":7,"print":true,"dark":true}"# + ); + assert_main_world_count(&page, 1).await; + + page.run_async_command(RendererPageCommand::SetNavigatorOverrides( + moli_page_types::NavigatorOverrides { + online: Some(true), + max_touch_points: 2, + ..Default::default() + }, + )) + .await + .unwrap(); + assert_eq!( + evaluate_lazy_test(&page, expression).await, + r#"{"online":true,"touch":2,"print":true,"dark":true}"# + ); + assert_main_world_count(&page, 1).await; + page.close_async().await.unwrap(); +} + #[tokio::test(flavor = "multi_thread")] async fn cold_runtime_binding_install_initializes_main_world() { for execution_context_id in [None, Some(0)] { diff --git a/moli-renderer-v8/src/script_vm.rs b/moli-renderer-v8/src/script_vm.rs index 32d7767d7d..e8a3042949 100644 --- a/moli-renderer-v8/src/script_vm.rs +++ b/moli-renderer-v8/src/script_vm.rs @@ -751,6 +751,7 @@ mod input_dispatch; mod input_helpers; mod inspector; mod main_world; +use main_world::MainWorld; mod native_drag_data; pub(crate) use inspector::{dispatch_inspector_io_owner_wake, dispatch_inspector_main_owner_wake}; mod document_context_transition; @@ -1015,15 +1016,12 @@ pub(super) struct ScriptVm { page_inspector: DocumentInspectorBinding, renderer_document_isolate_teardown: RendererDocumentIsolateTeardown, renderer_page_script_environment: Option, - page_default_runtime: WindowRealmRuntime, - // Release the sizeable History/navigator seeds after the realm is initialized. - pending_main_world: Option>, + main_world: MainWorld, page_isolated_world_contexts: PageIsolatedWorldRegistry, child_frame_realm_store: child_frame_realm::ChildFrameRealmStore, prebootstrapped_child_default_contexts: SharedPrebootstrappedChildDefaultContexts, child_document_modulator_store: ChildDocumentModulatorStore, root_frame_id: Option, - baseline_globals: ScriptGlobalsBaseline, // `JsContextHost` stores a non-owning pointer into `document_runtime`, so it // must be dropped before the runtime field during normal Rust field teardown. _context_host: Rc>, @@ -1236,9 +1234,10 @@ impl ScriptVm { let mut context_ptrs: Vec<*const v8::Global> = Vec::with_capacity( 1 + self.page_isolated_world_contexts.len() + self.child_frame_realm_store.len(), ); - if self.main_world_is_initialized() { - context_ptrs.push(&self.page_default_runtime.context as *const _); - } + context_ptrs.extend( + self.current_main_world() + .map(WindowRealmRuntime::context_ptr), + ); context_ptrs.extend( self.page_isolated_world_contexts .contexts() @@ -1277,9 +1276,10 @@ impl ScriptVm { let mut context_ptrs: Vec<*const v8::Global> = Vec::with_capacity( 1 + self.page_isolated_world_contexts.len() + self.child_frame_realm_store.len(), ); - if self.main_world_is_initialized() { - context_ptrs.push(&self.page_default_runtime.context as *const _); - } + context_ptrs.extend( + self.current_main_world() + .map(WindowRealmRuntime::context_ptr), + ); context_ptrs.extend( self.page_isolated_world_contexts .contexts() @@ -1363,8 +1363,7 @@ impl ScriptVm { inspector_session_id: Option<&str>, object_id: &str, ) -> Result> { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + let context_ptr = self.ensure_main_world()?.context_ptr(); let context_host = self._context_host.clone(); let page_inspector = &self.page_inspector; let renderer_document_isolate = self.renderer_document_isolate.clone(); @@ -1600,8 +1599,7 @@ impl ScriptVm { }))?, )); } - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + let context_ptr = self.ensure_main_world()?.context_ptr(); let runtime_binding_replay_context_ptrs = self.runtime_binding_replay_context_ptrs(); let renderer_document_isolate = self.renderer_document_isolate.clone(); let page_inspector = &mut self.page_inspector; @@ -1745,7 +1743,7 @@ impl ScriptVm { command_output: Option, internal_dispatch_call_id: Option, ) -> Result> { - self.initialize_main_world_if_needed()?; + let context_ptr = self.ensure_main_world()?.context_ptr(); let user_gesture = runtime_protocol_message_user_gesture(raw_json); let file_prompt_handler = runtime_protocol_message_file_prompt_handler(raw_json); if user_gesture { @@ -1770,6 +1768,7 @@ impl ScriptVm { .borrow_mut() .replace_active_inspector_dispatch(true); let result = self.dispatch_inspector_protocol_message_with_current_activation( + context_ptr, target, raw_json, deferred_response, @@ -1807,6 +1806,7 @@ impl ScriptVm { fn dispatch_inspector_protocol_message_with_current_activation( &mut self, + context_ptr: *const v8::Global, target: PageInspectorSessionTarget<'_>, raw_json: &str, deferred_response: Option, @@ -1814,7 +1814,6 @@ impl ScriptVm { internal_dispatch_call_id: Option, ) -> Result> { let timing_started = moli_trace::cdp_nav_timing_enabled().then(Instant::now); - let context_ptr: *const v8::Global = &self.page_default_runtime.context; let inspector_window_dispatch_scope = runtime_protocol_message_window_dispatch_target(raw_json) .and_then(|target| self.inspector_window_dispatch_scope_for_target(target)); @@ -2270,7 +2269,7 @@ impl ScriptVmPageRealmBootstrap { inherited_security_token, resource_owner_id, promise_reject_dispatch, - page_inspector, + mut page_inspector, renderer_document_isolate, renderer_document_isolate_teardown, document_runtime, @@ -2284,16 +2283,46 @@ impl ScriptVmPageRealmBootstrap { renderer_page_script_environment, reuse_main_window_proxy, } = self; - let mut bootstrap = ScriptVmDefaultWorldBootstrap { - page_default_runtime: WindowRealmRuntime::uninitialized( - renderer_document_isolate.deferred_context_host_release_queue(), - ), - pending_main_world: Some(Box::new(main_world::PendingMainWorld { - inherited_security_token, - reuse_main_window_proxy, - ..Default::default() - })), - baseline_globals: Default::default(), + let pending = Box::new(main_world::PendingMainWorld { + inherited_security_token, + reuse_main_window_proxy, + ..Default::default() + }); + let main_world = if initialize_main_world { + match main_world::initialize( + &renderer_document_isolate, + &context_host, + resource_owner_id, + &promise_reject_dispatch, + storage_bucket_store.clone(), + None, + renderer_page_script_environment.clone(), + root_frame_id.as_deref(), + &mut page_inspector, + &pending, + ) { + Ok((runtime, baseline_globals)) => MainWorld::Ready { + runtime, + _baseline_globals: baseline_globals, + }, + Err(error) => { + drop(page_inspector); + return Err(Box::new(( + error, + recover_bootstrap_dom_host_from_holder( + renderer_document_isolate, + renderer_document_isolate_teardown, + context_host, + document_runtime, + ), + ))); + } + } + } else { + MainWorld::Pending(pending) + }; + Ok(ScriptVmDefaultWorldBootstrap { + main_world, resource_owner_id, promise_reject_dispatch, page_inspector, @@ -2308,40 +2337,7 @@ impl ScriptVmPageRealmBootstrap { page_runtime_wake_tx, storage_bucket_store, renderer_page_script_environment, - }; - if initialize_main_world { - match main_world::initialize( - &bootstrap.renderer_document_isolate, - &bootstrap.context_host, - bootstrap.resource_owner_id, - &bootstrap.promise_reject_dispatch, - bootstrap.storage_bucket_store.clone(), - None, - bootstrap.renderer_page_script_environment.clone(), - bootstrap.root_frame_id.as_deref(), - &mut bootstrap.page_inspector, - bootstrap.pending_main_world.as_ref().unwrap(), - ) { - Ok((runtime, baseline)) => { - bootstrap.page_default_runtime = runtime; - bootstrap.baseline_globals = baseline; - bootstrap.pending_main_world = None; - } - Err(error) => { - drop(bootstrap.page_inspector); - return Err(Box::new(( - error, - recover_bootstrap_dom_host_from_holder( - bootstrap.renderer_document_isolate, - bootstrap.renderer_document_isolate_teardown, - bootstrap.context_host, - bootstrap.document_runtime, - ), - ))); - } - } - } - Ok(bootstrap) + }) } } @@ -2489,9 +2485,7 @@ impl ScriptVmDefaultWorldBootstrap { renderer_document_isolate, renderer_document_isolate_teardown, renderer_page_script_environment, - page_default_runtime, - pending_main_world, - baseline_globals, + main_world, document_runtime, context_host, prebootstrapped_child_default_contexts, @@ -2510,14 +2504,12 @@ impl ScriptVmDefaultWorldBootstrap { renderer_document_isolate, renderer_document_isolate_teardown, renderer_page_script_environment, - page_default_runtime, - pending_main_world, + main_world, page_isolated_world_contexts: PageIsolatedWorldRegistry::new(), child_frame_realm_store: child_frame_realm::ChildFrameRealmStore::default(), prebootstrapped_child_default_contexts, child_document_modulator_store: ChildDocumentModulatorStore::default(), root_frame_id, - baseline_globals, document_runtime: ScriptVmDocumentRuntimeOwner::new(document_runtime), _context_host: context_host, page_context_cancel_tx, @@ -2571,12 +2563,12 @@ impl ScriptVmDefaultWorldBootstrap { pub fn finish(self) -> std::result::Result { let vm = self.finish_ownership(); - if vm.page_default_runtime.is_initialized() { + if let Some(runtime) = vm.current_main_world() { vm.renderer_document_isolate .with_renderer_document_isolate_mut(|isolate| { let scope = pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - vm.retain_native_hosts_in_scope(scope); + vm.retain_native_hosts_in_scope(scope, runtime); }); } Ok(vm) @@ -2584,15 +2576,21 @@ impl ScriptVmDefaultWorldBootstrap { fn finish_in_scope(self, scope: &mut v8::PinScope<'_, '_>) -> ScriptVm { let vm = self.finish_ownership(); - vm.retain_native_hosts_in_scope(scope); + if let Some(runtime) = vm.current_main_world() { + vm.retain_native_hosts_in_scope(scope, runtime); + } vm } } impl ScriptVm { - fn retain_native_hosts_in_scope(&self, scope: &mut v8::PinScope<'_, '_, ()>) { + fn retain_native_hosts_in_scope( + &self, + scope: &mut v8::PinScope<'_, '_, ()>, + runtime: &WindowRealmRuntime, + ) { crate::util::retain_context_host_for_document_realm( - v8::Local::new(scope, &self.page_default_runtime.context), + v8::Local::new(scope, &runtime.context), self._context_host.clone(), self.renderer_document_isolate .deferred_context_host_release_queue(), @@ -2672,7 +2670,9 @@ impl ScriptVm { self._context_host .borrow_mut() .close_page_context_resources_for_teardown(); - self.page_default_runtime.mark_closed(); + if let Some(runtime) = self.main_world.current_mut() { + runtime.mark_closed(); + } for world in self.page_isolated_world_contexts.contexts_mut() { world.runtime.mark_closed(); } @@ -2705,9 +2705,10 @@ impl ScriptVm { + prebootstrapped_contexts.len() + pending_isolated.len(), ); - if self.main_world_is_initialized() { - context_ptrs.push(&self.page_default_runtime.context as *const _); - } + context_ptrs.extend( + self.current_main_world() + .map(WindowRealmRuntime::context_ptr), + ); context_ptrs.extend( self.page_isolated_world_contexts .contexts() @@ -2783,14 +2784,14 @@ impl ScriptVm { "main navigation crossed page script environment ownership" )); } - if !self.main_world_is_initialized() { + let Some(runtime) = self.current_main_world() else { return Ok(MainWindowProxyNavigationCommit { context: None, renderer_document_isolate: self.renderer_document_isolate.clone(), page_id, }); - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + }; + let context_ptr = runtime.context_ptr(); let context = self .renderer_document_isolate .with_entered_renderer_document_isolate(|isolate| { diff --git a/moli-renderer-v8/src/script_vm/blob_inspector.rs b/moli-renderer-v8/src/script_vm/blob_inspector.rs index f0b22c293d..fa59c5e0c5 100644 --- a/moli-renderer-v8/src/script_vm/blob_inspector.rs +++ b/moli-renderer-v8/src/script_vm/blob_inspector.rs @@ -10,8 +10,7 @@ impl ScriptVm { inspector_session_id: Option<&str>, object_id: &str, ) -> Result { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + let context_ptr = self.ensure_main_world()?.context_ptr(); let page_inspector = &self.page_inspector; let renderer_document_isolate = self.renderer_document_isolate.clone(); renderer_document_isolate.with_entered_renderer_document_isolate_and_inspector_mut( diff --git a/moli-renderer-v8/src/script_vm/browsing_contexts.rs b/moli-renderer-v8/src/script_vm/browsing_contexts.rs index 46184ef942..75113f2fec 100644 --- a/moli-renderer-v8/src/script_vm/browsing_contexts.rs +++ b/moli-renderer-v8/src/script_vm/browsing_contexts.rs @@ -70,10 +70,7 @@ impl ScriptVm { pub(crate) fn upgrade_late_defined_custom_elements_after_parser_checkpoint( &mut self, ) -> Result<()> { - if !self.main_world_is_initialized() { - return Ok(()); - } - self.with_default_context_scope(|scope, host_ptr| { + self.with_current_main_world_scope(|scope, host_ptr| { let document_handle = unsafe { &*host_ptr }.dom_host().document_handle(); let _ = custom_elements::upgrade_late_defined_connected_tree_after_parser_sync( scope, @@ -83,6 +80,7 @@ impl ScriptVm { perform_microtask_checkpoint_and_report_pending_promise_rejections(scope); Ok(()) }) + .map(|_| ()) } // Parser operations reach ScriptVm only when they must enter the page default @@ -98,17 +96,17 @@ impl ScriptVm { token_attributes: &[Attribute], intended_parent: Option, ) -> Result> { - if !self.main_world_is_initialized() { + let Some(runtime) = self.current_main_world() else { return Ok(None); - } + }; + let context_ptr = runtime.context_ptr(); let context_host = self._context_host.clone(); - let context_ptr: *const v8::Global = &self.page_default_runtime.context; let document_runtime = &mut self.document_runtime; self.renderer_document_isolate .with_entered_renderer_document_isolate(|isolate| { let scope = pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - // SAFETY: `context_ptr` points to `self.page_default_runtime.context`, which is kept + // SAFETY: `context_ptr` points to an owned ready main-world Context, which is kept // alive for the duration of this non-escaping closure while the document // isolate is exclusively borrowed. let context = unsafe { v8::Local::new(scope, &*context_ptr) }; @@ -209,7 +207,7 @@ impl ScriptVm { &mut self, navigator_identity: &moli_browser_profile::BrowserIdentityProfile, ) { - if let Some(pending) = self.pending_main_world.as_mut() { + if let Some(pending) = self.main_world.pending_mut() { pending.navigator_identity = Some(navigator_identity.clone()); return; } @@ -274,9 +272,10 @@ impl ScriptVm { pub(super) fn top_level_context_ptrs(&self) -> Vec<*const v8::Global> { let mut contexts = Vec::new(); - if self.main_world_is_initialized() { - contexts.push(&self.page_default_runtime.context as *const _); - } + contexts.extend( + self.current_main_world() + .map(WindowRealmRuntime::context_ptr), + ); contexts.extend( self.page_isolated_world_contexts .contexts() @@ -292,9 +291,10 @@ impl ScriptVm { let mut contexts = Vec::with_capacity( 1 + self.page_isolated_world_contexts.len() + self.child_frame_realm_store.len(), ); - if self.main_world_is_initialized() { - contexts.push(&self.page_default_runtime.context as *const _); - } + contexts.extend( + self.current_main_world() + .map(WindowRealmRuntime::context_ptr), + ); contexts.extend( self.page_isolated_world_contexts .contexts() diff --git a/moli-renderer-v8/src/script_vm/child_frame_runtime.rs b/moli-renderer-v8/src/script_vm/child_frame_runtime.rs index bbdb7fe7d6..1a23ceafa8 100644 --- a/moli-renderer-v8/src/script_vm/child_frame_runtime.rs +++ b/moli-renderer-v8/src/script_vm/child_frame_runtime.rs @@ -618,7 +618,7 @@ impl ScriptVm { &mut self, effects: DomMutationEffects, ) -> Result<()> { - if !self.main_world_is_initialized() { + if self.current_main_world().is_none() { self.document_runtime .apply_parser_mutation_effects_without_window_realm( (*self._context_host).as_ptr(), @@ -637,7 +637,7 @@ impl ScriptVm { &mut self, mutation: crate::parser::ParserDomMutation, ) -> Result<()> { - if !self.main_world_is_initialized() + if self.current_main_world().is_none() && !self .document_runtime .parser_mutation_needs_window_realm(&mutation) @@ -680,20 +680,18 @@ impl ScriptVm { } pub(crate) fn flush_parser_custom_element_handoff_replacements(&mut self) -> Result<()> { - if !self.main_world_is_initialized() { - return Ok(()); - } - self.with_default_context_scope(|scope, host_ptr| { + self.with_current_main_world_scope(|scope, host_ptr| { custom_elements::flush_parser_custom_element_handoff_replacements(scope, host_ptr); Ok(()) }) + .map(|_| ()) } pub(crate) fn install_session_history_position( &mut self, position: moli_session_history::SessionHistoryPosition, ) { - if let Some(pending) = self.pending_main_world.as_mut() { + if let Some(pending) = self.main_world.pending_mut() { pending.history_position = Some(position); return; } @@ -707,7 +705,7 @@ impl ScriptVm { &mut self, entry_seed: Option, ) { - if let Some(pending) = self.pending_main_world.as_mut() { + if let Some(pending) = self.main_world.pending_mut() { pending.history_entry = entry_seed; return; } @@ -725,7 +723,7 @@ impl ScriptVm { url: &url::Url, position: moli_session_history::SessionHistoryPosition, ) -> Result> { - if let Some(pending) = self.pending_main_world.as_ref() { + if let Some(pending) = self.main_world.pending() { // No author realm has mutated History. Preserve the native seed // without materializing the outgoing Window during commit. return Ok(pending.history_entry.as_ref().and_then(|seed| { diff --git a/moli-renderer-v8/src/script_vm/child_host_load.rs b/moli-renderer-v8/src/script_vm/child_host_load.rs index 09ce119e0b..1e3b495e77 100644 --- a/moli-renderer-v8/src/script_vm/child_host_load.rs +++ b/moli-renderer-v8/src/script_vm/child_host_load.rs @@ -23,7 +23,7 @@ impl<'vm> ChildHostLoadOwner<'vm> { pub(crate) fn resync_child_browsing_contexts(&mut self) { let context_host = self.vm._context_host.clone(); - if !self.vm.main_world_is_initialized() + if self.vm.current_main_world().is_none() && context_host .borrow() .dom_host() diff --git a/moli-renderer-v8/src/script_vm/classic_script_exception.rs b/moli-renderer-v8/src/script_vm/classic_script_exception.rs index e473c231bf..f3ff4b8d82 100644 --- a/moli-renderer-v8/src/script_vm/classic_script_exception.rs +++ b/moli-renderer-v8/src/script_vm/classic_script_exception.rs @@ -4,14 +4,11 @@ //! neither the script element's later load/error terminal nor the enclosing //! selected Page task completion. -use std::pin::pin; - use anyhow::Result; use super::ScriptVm; use crate::context_bootstrap::dispatch_window_error_event_with_details; use crate::exception_reporting::{V8ExceptionReport, log_uncaught_script_exception}; -use crate::native_bridge::JsContextHost; impl ScriptVm { pub(super) fn report_classic_script_exception_and_finish_evaluation_best_effort( @@ -30,33 +27,24 @@ impl ScriptVm { &mut self, report: &V8ExceptionReport, ) -> Result<()> { - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let error_value = report - .exception - .as_ref() - .map(|exception| v8::Local::new(scope, exception)); - // SAFETY: as_ptr() — V8 callbacks are re-entrant; borrow_mut() panics. See util.rs. - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - let dispatch_result = dispatch_window_error_event_with_details( - scope, - host_ptr, - &report.summary, - report.source.as_deref().unwrap_or(""), - report.line.unwrap_or(0) as u32, - report.column.unwrap_or(0) as u32, - error_value, - ) - .map_err(anyhow::Error::msg); - let checkpoint_result = Self::perform_microtask_checkpoints(scope, None); - dispatch_result?; - checkpoint_result - }) + self.with_default_context_scope(|scope, host_ptr| { + let error_value = report + .exception + .as_ref() + .map(|exception| v8::Local::new(scope, exception)); + let dispatch_result = dispatch_window_error_event_with_details( + scope, + host_ptr, + &report.summary, + report.source.as_deref().unwrap_or(""), + report.line.unwrap_or(0) as u32, + report.column.unwrap_or(0) as u32, + error_value, + ) + .map_err(anyhow::Error::msg); + let checkpoint_result = Self::perform_microtask_checkpoints(scope, None); + dispatch_result?; + checkpoint_result + }) } } diff --git a/moli-renderer-v8/src/script_vm/context_scope.rs b/moli-renderer-v8/src/script_vm/context_scope.rs index 14818958e2..500b615f51 100644 --- a/moli-renderer-v8/src/script_vm/context_scope.rs +++ b/moli-renderer-v8/src/script_vm/context_scope.rs @@ -5,6 +5,7 @@ use anyhow::Result; use super::ScriptVm; #[cfg(test)] use super::perform_microtask_checkpoint_and_report_pending_promise_rejections; +use crate::document_runtime::DocumentRuntime; use crate::{frame_owner_model::FrameRealmId, native_bridge::JsContextHost}; impl ScriptVm { @@ -101,11 +102,73 @@ impl ScriptVm { &mut self, op: impl FnOnce(&mut v8::PinScope<'_, '_>, *mut JsContextHost) -> Result, ) -> Result { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + let context_ptr = self.ensure_main_world()?.context_ptr(); self.with_context_scope_by_ptr(context_ptr, op) } + /// Update an already-materialized JavaScript surface without creating one. + pub(super) fn with_current_main_world_scope( + &mut self, + op: impl FnOnce(&mut v8::PinScope<'_, '_>, *mut JsContextHost) -> Result, + ) -> Result> { + let Some(runtime) = self.current_main_world() else { + return Ok(None); + }; + self.with_context_scope_by_ptr(runtime.context_ptr(), op) + .map(Some) + } + + /// Script/event entry with the live native Document and an entered realm. + pub(super) fn with_main_world_document_scope( + &mut self, + op: impl FnOnce( + &mut v8::PinScope<'_, '_>, + *mut JsContextHost, + &mut DocumentRuntime, + ) -> Result, + ) -> Result { + let context_ptr = self.ensure_main_world()?.context_ptr(); + self.with_document_context_scope_by_ptr(context_ptr, op) + } + + pub(super) fn with_current_main_world_document_scope( + &mut self, + op: impl FnOnce( + &mut v8::PinScope<'_, '_>, + *mut JsContextHost, + &mut DocumentRuntime, + ) -> Result, + ) -> Result> { + let Some(runtime) = self.current_main_world() else { + return Ok(None); + }; + self.with_document_context_scope_by_ptr(runtime.context_ptr(), op) + .map(Some) + } + + fn with_document_context_scope_by_ptr( + &mut self, + context_ptr: *const v8::Global, + op: impl FnOnce( + &mut v8::PinScope<'_, '_>, + *mut JsContextHost, + &mut DocumentRuntime, + ) -> Result, + ) -> Result { + let context_host = self._context_host.clone(); + let document_runtime = &mut self.document_runtime; + self.renderer_document_isolate + .with_entered_renderer_document_isolate(|isolate| { + let scope = pin!(v8::HandleScope::new(isolate)); + let scope = &mut scope.init(); + // SAFETY: the caller obtains this pointer from an owned live + // realm before borrowing the other ScriptVm fields. + let context = unsafe { v8::Local::new(scope, &*context_ptr) }; + let scope = &mut v8::ContextScope::new(scope, context); + op(scope, (*context_host).as_ptr(), document_runtime) + }) + } + /// Inspect the current realm without completing a task or running reactions. #[cfg(test)] pub(crate) fn with_default_context_scope_for_test( diff --git a/moli-renderer-v8/src/script_vm/document_context_transition.rs b/moli-renderer-v8/src/script_vm/document_context_transition.rs index 788e5f5f63..ca5ebcebb6 100644 --- a/moli-renderer-v8/src/script_vm/document_context_transition.rs +++ b/moli-renderer-v8/src/script_vm/document_context_transition.rs @@ -142,7 +142,11 @@ impl ScriptVm { let journal = environment.output_journal(); let isolate = self.renderer_document_isolate.clone(); let inspector_isolate = isolate.clone(); - let context = &self.page_default_runtime.context; + let context = &self + .main_world + .current() + .ok_or_else(|| anyhow!("Window reuse requires its initialized realm"))? + .context; let source_document = self ._context_host .borrow() diff --git a/moli-renderer-v8/src/script_vm/document_environment.rs b/moli-renderer-v8/src/script_vm/document_environment.rs index f554dc1cbd..36529524c4 100644 --- a/moli-renderer-v8/src/script_vm/document_environment.rs +++ b/moli-renderer-v8/src/script_vm/document_environment.rs @@ -127,12 +127,8 @@ impl ScriptVm { .forces_opaque_origin; self.document_runtime .set_main_navigation_policy_container(policy); - // A deferred Context reads the committed policy during its first bootstrap. - if !self.main_world_is_initialized() { - return; - } - // Refresh an existing token so a CSP sandbox cannot retain the URL's tuple token. - let _ = self.with_default_context_scope(|scope, host_ptr| { + // A deferred Context reads the policy at bootstrap; update only an existing token. + let _ = self.with_current_main_world_scope(|scope, host_ptr| { let key = unsafe { &*host_ptr }.main_default_world_security_token_key(); let context = scope.get_current_context(); if !crate::native_bridge::set_window_security_token(scope, context, key.as_deref()) { @@ -264,14 +260,11 @@ impl ScriptVm { idle_override: Option, ) -> Result<()> { self.set_idle_override(idle_override); - if !self.main_world_is_initialized() { - return Ok(()); - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - self.with_context_scope_by_ptr(context_ptr, |scope, _| { + self.with_current_main_world_scope(|scope, _| { crate::context_bootstrap::apply_idle_override_to_current_context(scope, idle_override); Ok(()) }) + .map(|_| ()) } pub(crate) fn stylesheet_preload_media_matches(&self, media: Option<&str>) -> bool { @@ -356,22 +349,16 @@ impl ScriptVm { } pub(super) fn sync_document_fonts_for_environment(&mut self) { - if !self.main_world_is_initialized() { - return; - } - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - let scope = &mut v8::ContextScope::new(scope, context); - let host = self._context_host.borrow(); - for document in host.documents_with_adopted_style_sheets() { - crate::native_bridge::document::sync_document_fonts_for_handle( - scope, &host, document, - ); - } - }); + let context_host = self._context_host.clone(); + let _ = self.with_current_main_world_scope(|scope, _| { + let host = context_host.borrow(); + for document in host.documents_with_adopted_style_sheets() { + crate::native_bridge::document::sync_document_fonts_for_handle( + scope, &host, document, + ); + } + Ok(()) + }); } pub(crate) fn set_network_offline(&mut self, offline: bool) { @@ -396,11 +383,7 @@ impl ScriptVm { .borrow_mut() .set_navigator_overrides(overrides); if changed { - if !self.main_world_is_initialized() { - return Ok(()); - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - self.with_context_scope_by_ptr(context_ptr, |scope, _| { + self.with_current_main_world_scope(|scope, _| { crate::context_bootstrap::notify_geolocation_override_changed(scope); Ok(()) })?; @@ -484,15 +467,13 @@ impl ScriptVm { storage_key: Some(host.top_web_storage_scope().storage_key().clone()), } }; - if !self.main_world_is_initialized() { - return Ok(capture(&mut self._context_host.borrow_mut(), None)); - } - self.with_default_context_scope(|scope, host_ptr| { + let environment = self.with_current_main_world_scope(|scope, host_ptr| { let token = scope.get_current_context().get_security_token(scope); Ok(capture( unsafe { &mut *host_ptr }, Some(v8::Global::new(scope, token)), )) - }) + })?; + Ok(environment.unwrap_or_else(|| capture(&mut self._context_host.borrow_mut(), None))) } } diff --git a/moli-renderer-v8/src/script_vm/document_isolate.rs b/moli-renderer-v8/src/script_vm/document_isolate.rs index 74b37b8fdb..aca0b652e4 100644 --- a/moli-renderer-v8/src/script_vm/document_isolate.rs +++ b/moli-renderer-v8/src/script_vm/document_isolate.rs @@ -607,14 +607,12 @@ impl RendererPageScriptEnvironment { } pub(crate) struct ScriptVmDefaultWorldBootstrap { - pub(super) pending_main_world: Option>, + pub(super) main_world: super::main_world::MainWorld, pub(super) resource_owner_id: ResourceOwnerId, pub(super) promise_reject_dispatch: PromiseRejectDispatchSlot, pub(super) page_inspector: DocumentInspectorBinding, pub(super) renderer_document_isolate_teardown: RendererDocumentIsolateTeardown, pub(super) renderer_page_script_environment: Option, - pub(super) page_default_runtime: super::WindowRealmRuntime, - pub(super) baseline_globals: super::ScriptGlobalsBaseline, pub(super) root_frame_id: Option, pub(super) prebootstrapped_child_default_contexts: SharedPrebootstrappedChildDefaultContexts, pub(super) page_context_cancel_tx: RendererPageContextCancelSender, diff --git a/moli-renderer-v8/src/script_vm/dom_debugger.rs b/moli-renderer-v8/src/script_vm/dom_debugger.rs index e383a1298f..225246a561 100644 --- a/moli-renderer-v8/src/script_vm/dom_debugger.rs +++ b/moli-renderer-v8/src/script_vm/dom_debugger.rs @@ -35,8 +35,7 @@ impl ScriptVm { depth: i32, pierce: bool, ) -> Result { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + let context_ptr = self.ensure_main_world()?.context_ptr(); let context_host = self._context_host.clone(); let page_inspector = &self.page_inspector; let renderer_document_isolate = self.renderer_document_isolate.clone(); diff --git a/moli-renderer-v8/src/script_vm/drop_cleanup.rs b/moli-renderer-v8/src/script_vm/drop_cleanup.rs index 6a3a2274b4..92536d41ee 100644 --- a/moli-renderer-v8/src/script_vm/drop_cleanup.rs +++ b/moli-renderer-v8/src/script_vm/drop_cleanup.rs @@ -18,7 +18,9 @@ impl Drop for ScriptVm { self.page_inspector .deactivate_page_vm_binding_for_teardown(); - self.page_default_runtime.release_bridge_ref(); + if let Some(runtime) = self.main_world.current_mut() { + runtime.release_bridge_ref(); + } if let Some(runtime) = self.document_runtime.take_for_retained_document_host() { self._context_host .borrow_mut() diff --git a/moli-renderer-v8/src/script_vm/eval_exec.rs b/moli-renderer-v8/src/script_vm/eval_exec.rs index 90e6928ed7..72bfeb0424 100644 --- a/moli-renderer-v8/src/script_vm/eval_exec.rs +++ b/moli-renderer-v8/src/script_vm/eval_exec.rs @@ -404,11 +404,10 @@ impl ScriptVm { &mut self, job: MainFrameScriptJob<'_>, ) -> PendingScriptTurn> { - if let Err(error) = self.initialize_main_world_if_needed() { - return PendingScriptTurn::new(Err(error.into())); - } - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; + let context_ptr = match self.ensure_main_world() { + Ok(runtime) => runtime.context_ptr(), + Err(error) => return PendingScriptTurn::new(Err(error.into())), + }; self.exec_in_context_ptr_without_turn_drain( context_ptr, job.source, @@ -690,9 +689,7 @@ impl ScriptVm { } pub(crate) fn eval(&mut self, source: &str) -> Result { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; + let context_ptr = self.ensure_main_world()?.context_ptr(); self.eval_string_in_context_ptr_runtime_turn(context_ptr, source, false) } @@ -701,11 +698,9 @@ impl ScriptVm { source: &str, base_url: &Url, ) -> Result> { - self.initialize_main_world_if_needed()?; + let context_ptr = self.ensure_main_world()?.context_ptr(); let source = source.to_owned(); let base_url = base_url.clone(); - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; let provenance = CompiledStringProvenance::at_url(base_url.clone()); let pending = PendingScriptTurn::new( self.renderer_document_isolate @@ -763,9 +758,7 @@ impl ScriptVm { #[cfg(test)] pub(crate) fn eval_with_child_record_sync(&mut self, source: &str) -> Result { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; + let context_ptr = self.ensure_main_world()?.context_ptr(); self.eval_string_in_context_ptr_runtime_turn(context_ptr, source, true) } @@ -816,9 +809,7 @@ impl ScriptVm { &mut self, source: &str, ) -> Result { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; + let context_ptr = self.ensure_main_world()?.context_ptr(); self.eval_string_in_context_ptr_without_turn_drain( context_ptr, source, diff --git a/moli-renderer-v8/src/script_vm/execution_contexts.rs b/moli-renderer-v8/src/script_vm/execution_contexts.rs index 60fa18e669..9dc197707e 100644 --- a/moli-renderer-v8/src/script_vm/execution_contexts.rs +++ b/moli-renderer-v8/src/script_vm/execution_contexts.rs @@ -28,11 +28,8 @@ impl ScriptVm { ) -> Option { let execution_context_id = match target { InspectorWindowDispatchTarget::DefaultTop => { - if !self.main_world_is_initialized() { - return None; - } return Some(InspectorWindowDispatchScope { - context_ptr: &self.page_default_runtime.context as *const _, + context_ptr: self.current_main_world()?.context_ptr(), child_handle: None, }); } @@ -42,7 +39,7 @@ impl ScriptVm { }; if self.runtime_observable_default_execution_context_id() == Some(execution_context_id) { return Some(InspectorWindowDispatchScope { - context_ptr: &self.page_default_runtime.context as *const _, + context_ptr: self.current_main_world()?.context_ptr(), child_handle: None, }); } @@ -69,11 +66,10 @@ impl ScriptVm { realm_id: FrameRealmId, ) -> Result<*const v8::Global> { if realm_id.0 == 0 { - anyhow::ensure!( - self.main_world_is_initialized(), - "main world is not initialized" - ); - return Ok(&self.page_default_runtime.context as *const _); + return self + .current_main_world() + .map(WindowRealmRuntime::context_ptr) + .ok_or_else(|| anyhow!("main world is not initialized")); } self.child_frame_realm_context_ptr(realm_id) } @@ -890,6 +886,7 @@ impl ScriptVm { frame_id: Option, child_handle: Option, ) -> Result { + self.ensure_main_world()?; let document_owner = match child_handle { Some(child_handle) => self ._context_host @@ -1216,7 +1213,6 @@ impl ScriptVm { name: &str, grant_universal_access: bool, ) -> Result { - self.initialize_main_world_if_needed()?; self.publish_prebootstrapped_isolated_window_contexts()?; if let Some(execution_context_id) = self .page_isolated_world_contexts @@ -1260,7 +1256,6 @@ impl ScriptVm { name: &str, grant_universal_access: bool, ) -> Result { - self.initialize_main_world_if_needed()?; self.publish_prebootstrapped_isolated_window_contexts()?; if let Some(execution_context_id) = self .page_isolated_world_contexts diff --git a/moli-renderer-v8/src/script_vm/font_resources.rs b/moli-renderer-v8/src/script_vm/font_resources.rs index e25a5b4521..3ff1d8e0e8 100644 --- a/moli-renderer-v8/src/script_vm/font_resources.rs +++ b/moli-renderer-v8/src/script_vm/font_resources.rs @@ -5,7 +5,7 @@ use super::*; impl ScriptVm { pub(super) fn reconcile_document_font_resources(&mut self) { - if !self.main_world_is_initialized() { + if self.current_main_world().is_none() { let host = self._context_host.borrow(); let fonts_enabled = host.current_main_document_resource_loader() diff --git a/moli-renderer-v8/src/script_vm/initial_window_document.rs b/moli-renderer-v8/src/script_vm/initial_window_document.rs index 9d76f3c362..df4e89ac22 100644 --- a/moli-renderer-v8/src/script_vm/initial_window_document.rs +++ b/moli-renderer-v8/src/script_vm/initial_window_document.rs @@ -8,11 +8,11 @@ impl ScriptVm { inherited: Option<&ScriptVmInitialDocumentEnvironment>, ) -> Result { // No WindowProxy or realm has been exposed, so there is no JS identity to retain. - if !self.main_world_is_initialized() { + let Some(runtime) = self.current_main_world() else { return Ok( crate::frame_owner_model::FrameDocumentLocalWindowTransition::ReplaceLocalWindow, ); - } + }; // Serialized opaque origins are all "null". Compare the inherited // identity instead, so a shared opaque origin can reuse its Window // without treating independently sandboxed documents as same-origin. @@ -24,7 +24,7 @@ impl ScriptVm { .with_entered_renderer_document_isolate(|isolate| { let scope = pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); + let context = v8::Local::new(scope, &runtime.context); let token = v8::Local::new(scope, token); Ok(context.get_security_token(scope).strict_equals(token)) })? @@ -160,7 +160,8 @@ impl ScriptVm { host.service_worker_client_id(), ); } - let context = &vm.page_default_runtime.context; + let context = &vm.main_world.current() + .ok_or_else(|| anyhow!("initial Window reuse requires an existing realm"))?.context; vm.renderer_document_isolate .with_entered_renderer_document_isolate(|isolate| { let scope = pin!(v8::HandleScope::new(isolate)); diff --git a/moli-renderer-v8/src/script_vm/main_world.rs b/moli-renderer-v8/src/script_vm/main_world.rs index 0e6c01c8a2..9e638507fb 100644 --- a/moli-renderer-v8/src/script_vm/main_world.rs +++ b/moli-renderer-v8/src/script_vm/main_world.rs @@ -1,5 +1,64 @@ use super::*; +pub(super) enum MainWorld { + // The large startup seeds are released when the realm is materialized. + Pending(Box), + Ready { + runtime: WindowRealmRuntime, + _baseline_globals: ScriptGlobalsBaseline, + }, +} + +impl MainWorld { + pub(super) fn record_lifecycle_timing( + &mut self, + timing: MainWorldLifecycleTiming, + ) -> Option<&WindowRealmRuntime> { + match self { + Self::Pending(pending) => { + pending.performance.record(timing as usize); + None + } + Self::Ready { runtime, .. } => Some(runtime), + } + } + + pub(super) fn current(&self) -> Option<&WindowRealmRuntime> { + match self { + Self::Pending(_) => None, + Self::Ready { runtime, .. } => Some(runtime), + } + } + + pub(super) fn current_mut(&mut self) -> Option<&mut WindowRealmRuntime> { + match self { + Self::Pending(_) => None, + Self::Ready { runtime, .. } => Some(runtime), + } + } + + pub(super) fn pending(&self) -> Option<&PendingMainWorld> { + match self { + Self::Pending(pending) => Some(pending), + Self::Ready { .. } => None, + } + } + + pub(super) fn pending_mut(&mut self) -> Option<&mut PendingMainWorld> { + match self { + Self::Pending(pending) => Some(pending), + Self::Ready { .. } => None, + } + } +} + +pub(super) enum MainWorldLifecycleTiming { + DomContentLoadedStart, + DomContentLoadedEnd, + LoadStart, + LoadEnd, +} + /// Native Document state is already live. These are the remaining inputs for /// its first main-world Context, not a second Document or bootstrap DOM. #[derive(Default)] @@ -131,7 +190,7 @@ pub(super) fn initialize( impl ScriptVm { pub(super) fn initialize_main_world_for_window_handler(&mut self) -> Result<()> { - if self.main_world_is_initialized() { + if self.current_main_world().is_some() { return Ok(()); } let dom = self.document_runtime.dom_host().dom(); @@ -145,39 +204,40 @@ impl ScriptVm { .any(|name| body.attribute(name).is_some()) }); if has_handler { - self.initialize_main_world_if_needed()?; + self.ensure_main_world()?; } Ok(()) } - pub(crate) fn main_world_is_initialized(&self) -> bool { - self.page_default_runtime.is_initialized() + pub(super) fn current_main_world(&self) -> Option<&WindowRealmRuntime> { + self.main_world.current() } - pub(crate) fn initialize_main_world_if_needed(&mut self) -> Result<()> { - let Some(pending) = self.pending_main_world.as_ref() else { - return Ok(()); - }; - let (runtime, baseline) = initialize( - &self.renderer_document_isolate, - &self._context_host, - self.resource_owner_id, - &self.promise_reject_dispatch, - self.storage_bucket_store.clone(), - self.indexed_db_manager.clone(), - self.renderer_page_script_environment.clone(), - self.root_frame_id.as_deref(), - &mut self.page_inspector, - pending, - )?; - self.page_default_runtime = runtime; - self.baseline_globals = baseline; - self.pending_main_world = None; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - self.retain_native_hosts_in_scope(&mut scope.init()); - }); - Ok(()) + pub(crate) fn ensure_main_world(&mut self) -> Result<&WindowRealmRuntime> { + if let Some(pending) = self.main_world.pending() { + let (runtime, baseline_globals) = initialize( + &self.renderer_document_isolate, + &self._context_host, + self.resource_owner_id, + &self.promise_reject_dispatch, + self.storage_bucket_store.clone(), + self.indexed_db_manager.clone(), + self.renderer_page_script_environment.clone(), + self.root_frame_id.as_deref(), + &mut self.page_inspector, + pending, + )?; + self.renderer_document_isolate + .with_renderer_document_isolate_mut(|isolate| { + let scope = pin!(v8::HandleScope::new(isolate)); + self.retain_native_hosts_in_scope(&mut scope.init(), &runtime); + }); + self.main_world = MainWorld::Ready { + runtime, + _baseline_globals: baseline_globals, + }; + } + self.current_main_world() + .ok_or_else(|| anyhow!("main-world initialization did not produce a Window realm")) } } diff --git a/moli-renderer-v8/src/script_vm/native_module.rs b/moli-renderer-v8/src/script_vm/native_module.rs index 9c60e68792..9ce6790679 100644 --- a/moli-renderer-v8/src/script_vm/native_module.rs +++ b/moli-renderer-v8/src/script_vm/native_module.rs @@ -4,7 +4,7 @@ use anyhow::Result; use moli_webapi_declare::WebApiObject; use url::Url; -use super::ScriptVm; +use super::{ScriptVm, WindowRealmRuntime}; use crate::context_bootstrap::{ ORIGINAL_WEBASSEMBLY_COMPILE_ERROR_CONSTRUCTOR_SLOT, ORIGINAL_WEBASSEMBLY_LINK_ERROR_CONSTRUCTOR_SLOT, diff --git a/moli-renderer-v8/src/script_vm/native_module/module_graph.rs b/moli-renderer-v8/src/script_vm/native_module/module_graph.rs index e661e8b0e8..ba92a57344 100644 --- a/moli-renderer-v8/src/script_vm/native_module/module_graph.rs +++ b/moli-renderer-v8/src/script_vm/native_module/module_graph.rs @@ -10,13 +10,7 @@ impl ScriptVm { source_url: &Url, fetch_metadata: &crate::module_runtime::ModuleFetchMetadata, ) -> std::result::Result<(ModuleRecordEntry, ModuleIdentityHash), ModuleLoadError> { - self.initialize_main_world_if_needed().map_err(|error| { - ModuleLoadError::new( - ModuleLoadStage::Compile, - format!("main world initialization failed: {error}"), - ) - })?; - let context_ptr = self.native_module_default_context_ptr(); + let context_ptr = self.native_module_default_context_ptr(ModuleLoadStage::Compile)?; self.compile_native_module_record_in_context( context_ptr, key, @@ -47,8 +41,15 @@ impl ScriptVm { fetch_metadata, ) } - pub(super) fn native_module_default_context_ptr(&self) -> *const v8::Global { - &self.page_default_runtime.context as *const _ + pub(super) fn native_module_default_context_ptr( + &mut self, + stage: ModuleLoadStage, + ) -> std::result::Result<*const v8::Global, ModuleLoadError> { + self.ensure_main_world() + .map(WindowRealmRuntime::context_ptr) + .map_err(|error| { + ModuleLoadError::new(stage, format!("main world initialization failed: {error}")) + }) } pub(super) fn compile_native_module_record_in_context( &mut self, @@ -282,7 +283,7 @@ impl ScriptVm { &mut self, graph: &crate::module_runtime::ModuleGraphHandle, ) -> std::result::Result<(), ModuleLoadError> { - let context_ptr = self.native_module_default_context_ptr(); + let context_ptr = self.native_module_default_context_ptr(ModuleLoadStage::Instantiate)?; self.instantiate_native_module_graph_in_context(context_ptr, graph) } pub(super) fn instantiate_native_module_graph_in_context( @@ -365,7 +366,7 @@ impl ScriptVm { &mut self, root_entry: crate::module_runtime::ModuleEntryId, ) -> std::result::Result>, ModuleLoadError> { - let context_ptr = self.native_module_default_context_ptr(); + let context_ptr = self.native_module_default_context_ptr(ModuleLoadStage::Evaluate)?; self.evaluate_native_module_graph_with_owner_in_context( context_ptr, root_entry, @@ -377,7 +378,7 @@ impl ScriptVm { &mut self, root_entry: crate::module_runtime::ModuleEntryId, ) -> std::result::Result { - let context_ptr = self.native_module_default_context_ptr(); + let context_ptr = self.native_module_default_context_ptr(ModuleLoadStage::Evaluate)?; self.evaluate_native_module_graph_with_owner_in_context( context_ptr, root_entry, diff --git a/moli-renderer-v8/src/script_vm/native_module/module_reactions.rs b/moli-renderer-v8/src/script_vm/native_module/module_reactions.rs index 4dc980ebba..479fbd6c52 100644 --- a/moli-renderer-v8/src/script_vm/native_module/module_reactions.rs +++ b/moli-renderer-v8/src/script_vm/native_module/module_reactions.rs @@ -87,63 +87,51 @@ impl ScriptVm { "main module evaluation reaction has no current Document owner", ) })?; - self.renderer_document_isolate - .with_entered_renderer_document_isolate(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - let scope = &mut v8::ContextScope::new(scope, context); - let reaction_id_value = v8::BigInt::new_from_u64(scope, reaction_id); - let data = NativeModuleScriptReactionDataDeclaration { - module_script_reaction_id: reaction_id_value, - scheduler_lane_id: v8::BigInt::new_from_u64( - scope, - document_owner.scheduler_lane_id.0, - ), - local_window_id: v8::BigInt::new_from_u64( - scope, - document_owner.local_window_id.0, - ), - document_id: v8::BigInt::new_from_u64(scope, document_owner.document_id.0), - } - .bind(scope) - .map_err(|error| { - anyhow::anyhow!("failed to create native module script reaction data: {error}") - })?; - let on_fulfilled = - v8::Function::builder(native_module_script_reaction_fulfilled_callback) - .data(data.into()) - .build(scope) - .ok_or_else(|| { - anyhow::anyhow!( - "failed to create native module script success reaction" - ) - })?; - let on_rejected = - v8::Function::builder(native_module_script_reaction_rejected_callback) - .data(data.into()) - .build(scope) - .ok_or_else(|| { - anyhow::anyhow!( - "failed to create native module script failure reaction" - ) - })?; - let promise = v8::Local::new(scope, &promise); - promise - .then2(scope, on_fulfilled, on_rejected) - .map(|_| ()) + self.with_default_context_scope(|scope, _| { + let reaction_id_value = v8::BigInt::new_from_u64(scope, reaction_id); + let data = NativeModuleScriptReactionDataDeclaration { + module_script_reaction_id: reaction_id_value, + scheduler_lane_id: v8::BigInt::new_from_u64( + scope, + document_owner.scheduler_lane_id.0, + ), + local_window_id: v8::BigInt::new_from_u64(scope, document_owner.local_window_id.0), + document_id: v8::BigInt::new_from_u64(scope, document_owner.document_id.0), + } + .bind(scope) + .map_err(|error| { + anyhow::anyhow!("failed to create native module script reaction data: {error}") + })?; + let on_fulfilled = + v8::Function::builder(native_module_script_reaction_fulfilled_callback) + .data(data.into()) + .build(scope) .ok_or_else(|| { - anyhow::anyhow!("failed to attach native module script reactions") + anyhow::anyhow!("failed to create native module script success reaction") })?; - // `then` schedules the module-script evaluation reaction as a - // V8 microtask, even if the evaluation promise is already - // settled. Chromium runs module evaluation's error handling - // before the script runner advances to later pending scripts; - // drain this owner-lane checkpoint now so TLA rejection cannot - // be observed after a later dynamic module. - Self::perform_microtask_checkpoints(scope, None) - }) - .map_err(|error| ModuleLoadError::new(ModuleLoadStage::Evaluate, error.to_string())) + let on_rejected = + v8::Function::builder(native_module_script_reaction_rejected_callback) + .data(data.into()) + .build(scope) + .ok_or_else(|| { + anyhow::anyhow!("failed to create native module script failure reaction") + })?; + let promise = v8::Local::new(scope, &promise); + promise + .then2(scope, on_fulfilled, on_rejected) + .map(|_| ()) + .ok_or_else(|| { + anyhow::anyhow!("failed to attach native module script reactions") + })?; + // `then` schedules the module-script evaluation reaction as a + // V8 microtask, even if the evaluation promise is already + // settled. Chromium runs module evaluation's error handling + // before the script runner advances to later pending scripts; + // drain this owner-lane checkpoint now so TLA rejection cannot + // be observed after a later dynamic module. + Self::perform_microtask_checkpoints(scope, None) + }) + .map_err(|error| ModuleLoadError::new(ModuleLoadStage::Evaluate, error.to_string())) } pub(crate) fn attach_child_parser_module_script_evaluation_reactions( &mut self, diff --git a/moli-renderer-v8/src/script_vm/native_module/tests.rs b/moli-renderer-v8/src/script_vm/native_module/tests.rs index 3719541cc4..47a8f3c572 100644 --- a/moli-renderer-v8/src/script_vm/native_module/tests.rs +++ b/moli-renderer-v8/src/script_vm/native_module/tests.rs @@ -87,7 +87,7 @@ fn dynamic_import_reaction_parts_in_vm( crate::module_runtime::DynamicModuleEvaluationTarget, ) { let context_ptr: *const v8::Global = match owner.child_parts() { - None => &vm.page_default_runtime.context, + None => &vm.current_main_world().unwrap().context, Some((_child_handle, _task_owner, realm_id)) => vm .frame_realm_context_ptr(realm_id) .expect("child dynamic-import reaction test realm must be materialized"), @@ -150,7 +150,7 @@ fn dynamic_import_request_in_vm( .with_entered_renderer_document_isolate(|isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &vm.page_default_runtime.context); + let context = v8::Local::new(scope, &vm.current_main_world().unwrap().context); let scope = &mut v8::ContextScope::new(scope, context); let owner = context_host .borrow() @@ -733,7 +733,7 @@ fn selected_dynamic_import_rejection_body_defers_user_reaction_to_task_end() { .with_entered_renderer_document_isolate(|isolate| { let scope = pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &vm.page_default_runtime.context); + let context = v8::Local::new(scope, &vm.current_main_world().unwrap().context); let scope = &mut v8::ContextScope::new(scope, context); let owner = context_host .borrow() diff --git a/moli-renderer-v8/src/script_vm/native_module/tests/constructor_preservation.rs b/moli-renderer-v8/src/script_vm/native_module/tests/constructor_preservation.rs index fab29109c9..bd6b7fd56d 100644 --- a/moli-renderer-v8/src/script_vm/native_module/tests/constructor_preservation.rs +++ b/moli-renderer-v8/src/script_vm/native_module/tests/constructor_preservation.rs @@ -25,7 +25,7 @@ fn reject_with_error(vm: &mut ScriptVm, error: &ModuleLoadError) -> v8::Global v8::Global Result { - self.initialize_main_world_if_needed()?; + self.ensure_main_world()?; let realms = self.live_navigation_history_realms(); let mut plans: Vec<(NavigationHistoryRealm, NavigationHistoryPrunePlan)> = Vec::with_capacity(realms.len()); @@ -136,9 +136,9 @@ impl ScriptVm { realm: NavigationHistoryRealm, ) -> Option<*const v8::Global> { match realm { - NavigationHistoryRealm::TopDefault => { - Some(&self.page_default_runtime.context as *const _) - } + NavigationHistoryRealm::TopDefault => self + .current_main_world() + .map(|runtime| runtime.context_ptr()), NavigationHistoryRealm::ChildDefault { execution_context_id, child_handle, diff --git a/moli-renderer-v8/src/script_vm/page_task_enqueue.rs b/moli-renderer-v8/src/script_vm/page_task_enqueue.rs index 76d98824fc..7b54c5eba7 100644 --- a/moli-renderer-v8/src/script_vm/page_task_enqueue.rs +++ b/moli-renderer-v8/src/script_vm/page_task_enqueue.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result, anyhow}; use std::pin::pin; use std::time::{Duration, Instant}; -use super::ScriptVm; +use super::{ScriptVm, main_world::MainWorldLifecycleTiming}; use crate::context_bootstrap::{ construct_original_event, construct_original_page_transition_event, record_performance_dom_content_loaded_event_end, @@ -27,44 +27,20 @@ const LIFECYCLE_EVENT_WATCHDOG_TIMEOUT: Duration = Duration::from_secs(8); impl ScriptVm { fn dispatch_document_event_body(&mut self, event_type: &str) -> Result<()> { - if !self.main_world_is_initialized() { - return Ok(()); - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - // SAFETY: as_ptr() — V8 callbacks are re-entrant; borrow_mut() panics. See util.rs. - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - document_runtime - .dispatch_document_event(scope, host_ptr, event_type) - .map_err(anyhow::Error::msg) - }) + self.with_current_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime + .dispatch_document_event(scope, host_ptr, event_type) + .map_err(anyhow::Error::msg) + }) + .map(|_| ()) } pub(super) fn perform_owner_lane_task_microtask_checkpoints(&mut self) -> Result<()> { - if !self.main_world_is_initialized() { - return Ok(()); - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - self.renderer_document_isolate - .with_entered_renderer_document_isolate(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - // SAFETY: `context_ptr` points at `self.page_default_runtime.context`, and this closure runs with - // the document isolate entered. `ScriptVm` owns both values, so the context belongs - // to the entered isolate for the full checkpoint scope. - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - Self::perform_microtask_checkpoints(scope, None)?; - Ok(()) - }) - .context("owner-lane microtask checkpoint failed") + self.with_current_main_world_scope(|scope, _| { + Self::perform_microtask_checkpoints(scope, None) + }) + .map(|_| ()) + .context("owner-lane microtask checkpoint failed") } /// Execute the body of one V8 foreground continuation. @@ -122,12 +98,15 @@ impl ScriptVm { &mut self, event_type: &str, ) -> Result<()> { - if let Some(pending) = self.pending_main_world.as_mut() { - if event_type == "DOMContentLoaded" { - pending.performance.record(0); - } + let runtime = if event_type == "DOMContentLoaded" { + self.main_world + .record_lifecycle_timing(MainWorldLifecycleTiming::DomContentLoadedStart) + } else { + self.current_main_world() + }; + let Some(context_ptr) = runtime.map(super::WindowRealmRuntime::context_ptr) else { return Ok(()); - } + }; let watchdog = self .renderer_document_isolate .with_renderer_document_isolate_mut(|isolate| { @@ -141,7 +120,6 @@ impl ScriptVm { }); let watchdog_timeout = watchdog.timeout(); let result = { - let context_ptr: *const v8::Global = &self.page_default_runtime.context; self.renderer_document_isolate .with_renderer_document_isolate_mut(|isolate| { let scope = pin!(v8::HandleScope::new(isolate)); @@ -169,22 +147,18 @@ impl ScriptVm { /// Publish the event-end timing that is intentionally observable only /// after the owning lifecycle task's checkpoint. pub(super) fn record_document_lifecycle_event_end(&mut self, event_type: &str) { - if let Some(pending) = self.pending_main_world.as_mut() { - if event_type == "DOMContentLoaded" { - pending.performance.record(1); - } + if event_type != "DOMContentLoaded" { return; } - if event_type == "DOMContentLoaded" { - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - record_performance_dom_content_loaded_event_end(scope); - }); + let context_ptr = self + .main_world + .record_lifecycle_timing(MainWorldLifecycleTiming::DomContentLoadedEnd) + .map(super::WindowRealmRuntime::context_ptr); + if let Some(context_ptr) = context_ptr { + let _ = self.with_context_scope_by_ptr(context_ptr, |scope, _| { + record_performance_dom_content_loaded_event_end(scope); + Ok(()) + }); } } @@ -215,7 +189,7 @@ impl ScriptVm { } pub(super) fn queue_current_main_document_image_load_events(&mut self) -> Result<()> { - if !self.main_world_is_initialized() { + if self.current_main_world().is_none() { let has_resources = self .document_runtime .dom_host() @@ -231,26 +205,15 @@ impl ScriptVm { if !has_resources { return Ok(()); } - self.initialize_main_world_if_needed()?; } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - // SAFETY: as_ptr() - V8 callbacks are re-entrant; borrow_mut() panics. See util.rs. - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - document_runtime.queue_current_main_document_image_load_events(scope, host_ptr); - Ok(()) - }) + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime.queue_current_main_document_image_load_events(scope, host_ptr); + Ok(()) + }) } pub(super) fn queue_current_main_document_media_loads(&mut self) -> Result<()> { - if !self.main_world_is_initialized() { + if self.current_main_world().is_none() { let has_resources = self .document_runtime .dom_host() @@ -268,36 +231,27 @@ impl ScriptVm { if !has_resources { return Ok(()); } - self.initialize_main_world_if_needed()?; } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - // SAFETY: as_ptr() - V8 callbacks are re-entrant; borrow_mut() panics. See util.rs. - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - document_runtime.queue_current_main_document_media_loads(scope, host_ptr); - Ok(()) - }) + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime.queue_current_main_document_media_loads(scope, host_ptr); + Ok(()) + }) } /// Dispatch the Window `load` body without crossing the load-to-pageshow /// lifecycle checkpoint. pub(super) fn dispatch_window_load_event_body(&mut self) -> Result<()> { self.initialize_main_world_for_window_handler()?; - if let Some(pending) = self.pending_main_world.as_mut() { - pending.performance.record(2); + let Some(context_ptr) = self + .main_world + .record_lifecycle_timing(MainWorldLifecycleTiming::LoadStart) + .map(super::WindowRealmRuntime::context_ptr) + else { return Ok(()); - } + }; let timing_enabled = moli_trace::cdp_nav_timing_enabled(); let task_started = timing_enabled.then(Instant::now); self.prune_stale_child_default_execution_contexts(); - let context_ptr: *const v8::Global = &self.page_default_runtime.context; let context_host = self._context_host.clone(); let document_runtime = &mut self.document_runtime; self.renderer_document_isolate @@ -393,11 +347,13 @@ impl ScriptVm { /// checkpoint. This method deliberately does not perform the final task-end /// checkpoint after pageshow. pub(super) fn dispatch_window_pageshow_event_body(&mut self) -> Result<()> { - if let Some(pending) = self.pending_main_world.as_mut() { - pending.performance.record(3); + let Some(context_ptr) = self + .main_world + .record_lifecycle_timing(MainWorldLifecycleTiming::LoadEnd) + .map(super::WindowRealmRuntime::context_ptr) + else { return Ok(()); - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; + }; let context_host = self._context_host.clone(); let document_runtime = &mut self.document_runtime; self.renderer_document_isolate diff --git a/moli-renderer-v8/src/script_vm/post_parse_lifecycle.rs b/moli-renderer-v8/src/script_vm/post_parse_lifecycle.rs index 62e2a739d9..1bcc51b025 100644 --- a/moli-renderer-v8/src/script_vm/post_parse_lifecycle.rs +++ b/moli-renderer-v8/src/script_vm/post_parse_lifecycle.rs @@ -1,4 +1,3 @@ -use std::pin::pin; use std::sync::Arc; use std::time::Instant; use tracing::debug; @@ -30,7 +29,6 @@ use crate::host::{ use crate::module_script_continuation::{ ModuleScriptContinuation, ModuleScriptEvaluationContinuation, ModuleScriptEvaluationUpdate, }; -use crate::native_bridge::JsContextHost; use crate::network::ResourceRequestClient; use crate::page_task_queue::PageTaskQueue; use crate::page_task_queue::{ @@ -2274,7 +2272,7 @@ impl ScriptVm { &mut self, ready: crate::document_runtime::ReadyConnectedStyleLoad, ) -> bool { - if !self.main_world_is_initialized() { + if self.current_main_world().is_none() { let host_ptr = (*self._context_host).as_ptr(); if ready.load_event_binding().is_some_and(|binding| { !unsafe { &*host_ptr }.main_style_load_event_is_current(binding) @@ -2292,48 +2290,26 @@ impl ScriptVm { .consume_pending_style_load(host_ptr, &ready); return true; } - if let Err(error) = self.initialize_main_world_if_needed() { - self.record_runtime_warning(format_args!( - "style handler initialization failed: {error}" - )); - return false; - } } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - if ready.load_event_binding().is_some_and(|binding| { - !unsafe { &*host_ptr }.main_style_load_event_is_current(binding) - }) { - return false; - } - document_runtime.dispatch_pending_style_load(scope, host_ptr, ready) - }) + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + if ready.load_event_binding().is_some_and(|binding| { + !unsafe { &*host_ptr }.main_style_load_event_is_current(binding) + }) { + return Ok(false); + } + Ok(document_runtime.dispatch_pending_style_load(scope, host_ptr, ready)) + }) + .unwrap_or_else(|error| { + self.record_runtime_warning(format_args!("style handler dispatch failed: {error}")); + false + }) } pub(crate) fn apply_pending_stylesheet_source_css_projections(&mut self) { - if !self.main_world_is_initialized() { - return; - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - document_runtime.apply_pending_stylesheet_source_css_projections(scope, host_ptr); - }); + let _ = self.with_current_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime.apply_pending_stylesheet_source_css_projections(scope, host_ptr); + Ok(()) + }); } pub(crate) fn settle_stylesheet_link_clients( @@ -2343,47 +2319,29 @@ impl ScriptVm { if clients.is_empty() { return; } - if let Err(error) = self.initialize_main_world_if_needed() { - self.record_runtime_warning(format_args!( - "stylesheet main world initialization failed: {error}" - )); - return; - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); + if let Err(error) = + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { document_runtime .settle_stylesheet_link_clients_in_current_scope(scope, host_ptr, clients); - }); + Ok(()) + }) + { + self.record_runtime_warning(format_args!( + "stylesheet client settlement failed: {error}" + )); + } } pub(crate) fn dispatch_preload_like_link_error_event(&mut self, handle: DomHandle) -> bool { - if let Err(error) = self.initialize_main_world_if_needed() { - self.record_runtime_warning(format_args!( - "stylesheet main world initialization failed: {error}" - )); - return false; - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - document_runtime.dispatch_preload_like_link_error_event(scope, host_ptr, handle); - }); - true + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime.dispatch_preload_like_link_error_event(scope, host_ptr, handle); + Ok(()) + }) + .map(|_| true) + .unwrap_or_else(|error| { + self.record_runtime_warning(format_args!("stylesheet error dispatch failed: {error}")); + false + }) } pub(crate) fn bind_prepared_script_handle_if_needed( @@ -2494,28 +2452,18 @@ impl ScriptVm { return; } - if let Err(error) = self.initialize_main_world_if_needed() { - self.record_runtime_warning(format_args!( - "CSP main world initialization failed: {error}" - )); - return; - } - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); + if let Err(error) = + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { for violation in violations { document_runtime.queue_content_security_policy_violation_event_best_effort( scope, host_ptr, &violation, ); } - }); + Ok(()) + }) + { + self.record_runtime_warning(format_args!("CSP violation dispatch failed: {error}")); + } } pub(crate) fn record_ready_stylesheet_network_results(&mut self) { diff --git a/moli-renderer-v8/src/script_vm/prepared_scripts.rs b/moli-renderer-v8/src/script_vm/prepared_scripts.rs index 94fb1481a8..232533ee35 100644 --- a/moli-renderer-v8/src/script_vm/prepared_scripts.rs +++ b/moli-renderer-v8/src/script_vm/prepared_scripts.rs @@ -663,28 +663,18 @@ impl ScriptVm { source: &str, request: ContentSecurityPolicyScriptElementRequest<'_>, ) -> Option { - if let Err(error) = self.initialize_main_world_if_needed() { - self.record_runtime_warning(format_args!("main world initialization failed: {error}")); - return None; - } - let context_host = self._context_host.clone(); - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - self.renderer_document_isolate - .with_entered_renderer_document_isolate(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - // SAFETY: context_ptr points to self.page_default_runtime.context, which - // remains live for this non-escaping isolate closure. - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - Ok( - crate::native_bridge::element::inline_script_source_for_execution( - scope, host_ptr, node_id, source, request, - ), - ) - }) - .ok() - .flatten() + self.with_default_context_scope(|scope, host_ptr| { + Ok( + crate::native_bridge::element::inline_script_source_for_execution( + scope, host_ptr, node_id, source, request, + ), + ) + }) + .unwrap_or_else(|error| { + self.record_runtime_warning(format_args!( + "main world source preparation failed: {error}" + )); + None + }) } } diff --git a/moli-renderer-v8/src/script_vm/related_page.rs b/moli-renderer-v8/src/script_vm/related_page.rs index 10ce741945..07c363dbc0 100644 --- a/moli-renderer-v8/src/script_vm/related_page.rs +++ b/moli-renderer-v8/src/script_vm/related_page.rs @@ -62,8 +62,8 @@ impl ScriptVmDefaultWorldBootstrap { .borrow_mut() .mark_main_document_initial_empty(); } - let bootstrap = bootstrap.default_world_in_entered_scope(scope, global_template)?; - let context = v8::Local::new(scope, &bootstrap.page_default_runtime.context); + let (bootstrap, context) = + bootstrap.default_world_in_entered_scope(scope, global_template)?; // A propagated origin sandbox gives the popup a fresh opaque origin. // Keep its unique token; copying the opener's token would bypass SOP. if !env.document_policy_container.sandbox.forces_opaque_origin { @@ -86,7 +86,7 @@ impl ScriptVmDefaultWorldBootstrap { } let mut vm = bootstrap.finish_in_scope(scope); vm.set_initial_storage_backends_in_scope( - scope, + context, env.indexed_db_manager.clone(), env.storage_bucket_store.clone(), ); @@ -99,7 +99,10 @@ impl ScriptVmPageRealmBootstrap { self, scope: &mut v8::PinScope<'s, '_, ()>, global_template: v8::Local<'s, v8::ObjectTemplate>, - ) -> std::result::Result { + ) -> std::result::Result< + (ScriptVmDefaultWorldBootstrap, v8::Local<'s, v8::Context>), + ScriptVmBootstrapError, + > { let Self { inherited_security_token: _, initialize_main_world: _, @@ -186,36 +189,39 @@ impl ScriptVmPageRealmBootstrap { ))); } }; - Ok(ScriptVmDefaultWorldBootstrap { - pending_main_world: None, - resource_owner_id, - promise_reject_dispatch, - page_inspector, - renderer_document_isolate, - renderer_document_isolate_teardown, - page_default_runtime: runtime, - baseline_globals, - document_runtime, - root_frame_id, - context_host, - prebootstrapped_child_default_contexts, - page_context_cancel_tx, - post_domcontentloaded_page_task_tx, - page_runtime_wake_tx, - storage_bucket_store, - renderer_page_script_environment, - }) + Ok(( + ScriptVmDefaultWorldBootstrap { + main_world: MainWorld::Ready { + runtime, + _baseline_globals: baseline_globals, + }, + resource_owner_id, + promise_reject_dispatch, + page_inspector, + renderer_document_isolate, + renderer_document_isolate_teardown, + document_runtime, + root_frame_id, + context_host, + prebootstrapped_child_default_contexts, + page_context_cancel_tx, + post_domcontentloaded_page_task_tx, + page_runtime_wake_tx, + storage_bucket_store, + renderer_page_script_environment, + }, + local_context, + )) } } impl ScriptVm { fn set_initial_storage_backends_in_scope( &mut self, - scope: &mut v8::PinScope<'_, '_>, + context: v8::Local<'_, v8::Context>, manager: Option, storage: Option, ) { - let context = v8::Local::new(scope, &self.page_default_runtime.context); self.indexed_db_manager = manager.clone(); self._context_host .borrow_mut() @@ -254,7 +260,11 @@ impl ScriptVm { self.root_frame_id = root_frame_id; let isolate = self.renderer_document_isolate.clone(); let inspector_isolate = isolate.clone(); - let context = &self.page_default_runtime.context; + let context = &self + .main_world + .current() + .ok_or_else(|| anyhow!("related Page adoption requires its initialized Window realm"))? + .context; let document_url = self.document_runtime.document_url().clone(); let root_frame_id = self.root_frame_id.as_deref(); let source_document = self diff --git a/moli-renderer-v8/src/script_vm/runtime_evaluation.rs b/moli-renderer-v8/src/script_vm/runtime_evaluation.rs index df08c8cfb5..4e679bfbcd 100644 --- a/moli-renderer-v8/src/script_vm/runtime_evaluation.rs +++ b/moli-renderer-v8/src/script_vm/runtime_evaluation.rs @@ -88,7 +88,7 @@ impl ScriptVm { result_mode: RuntimeEvaluateResultMode, ) -> Result { if execution_context_id.is_none() { - self.initialize_main_world_if_needed()?; + self.ensure_main_world()?; } self.validate_runtime_evaluate_context(execution_context_id)?; let call_id = self.next_internal_runtime_evaluate_call_id()?; @@ -348,10 +348,8 @@ impl ScriptVm { } pub(super) fn install_runtime_binding_in_default_context(&mut self, name: &str) -> Result<()> { - self.initialize_main_world_if_needed()?; + let context_ptr = self.ensure_main_world()?.context_ptr(); let owner = self.runtime_binding_document_owner(None)?; - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; let execution_context_id = self.default_or_initial_execution_context_id().unwrap_or(0); self.install_runtime_binding_in_context(context_ptr, execution_context_id, owner, name) } @@ -456,18 +454,14 @@ impl ScriptVm { } pub(super) fn remove_runtime_binding_from_default_context(&mut self, name: &str) -> Result<()> { - if !self.main_world_is_initialized() { - return Ok(()); - } - let context_ptr: *const v8::Global = - &self.page_default_runtime.context as *const _; - self.with_context_scope_by_ptr(context_ptr, |scope, _| { + self.with_current_main_world_scope(|scope, _| { let global = scope.get_current_context().global(scope); let key = v8_string(scope, name) .ok_or_else(|| anyhow!("failed to allocate runtime binding key `{name}`"))?; let _ = global.delete(scope, key.into()); Ok(()) }) + .map(|_| ()) } pub(super) fn remove_runtime_binding_from_isolated_context( diff --git a/moli-renderer-v8/src/script_vm/runtime_observability.rs b/moli-renderer-v8/src/script_vm/runtime_observability.rs index 161a3e35c8..8698314543 100644 --- a/moli-renderer-v8/src/script_vm/runtime_observability.rs +++ b/moli-renderer-v8/src/script_vm/runtime_observability.rs @@ -3,10 +3,15 @@ use super::*; impl ScriptVm { #[cfg(any(test, feature = "test-support"))] pub(crate) fn snapshot_globals(&mut self) -> Result>> { - if !self.main_world_is_initialized() { + let MainWorld::Ready { + runtime, + _baseline_globals: baseline_globals, + } = &self.main_world + else { return Ok(Some(BTreeMap::new())); - } - let baseline_json = serde_json::to_string(&self.baseline_globals) + }; + let default_context = runtime.context_ptr(); + let baseline_json = serde_json::to_string(baseline_globals) .context("failed to encode baseline globals for script snapshot")?; let snapshot_source = format!( r#" @@ -97,7 +102,6 @@ impl ScriptVm { }})() "# ); - let default_context = &self.page_default_runtime.context as *const _; let snapshot_json = self .eval_string_in_context_ptr_internal_snapshot(default_context, &snapshot_source) .context("failed to evaluate script state snapshot")?; @@ -114,7 +118,6 @@ impl ScriptVm { #[cfg(not(any(test, feature = "test-support")))] pub(crate) fn snapshot_globals(&mut self) -> Result>> { - let _ = &self.baseline_globals; Ok(None) } @@ -126,8 +129,8 @@ impl ScriptVm { .runtime_observable_source_queue .take_report_observable_output( self.runtime_observable_default_execution_context_id(), - self.main_world_is_initialized() - .then(|| self.page_default_runtime.runtime_observable_context_token), + self.current_main_world() + .map(|runtime| runtime.runtime_observable_context_token), )) } @@ -156,14 +159,15 @@ impl ScriptVm { } pub(super) fn page_runtime_observable_contexts(&self) -> Vec { - if !self.main_world_is_initialized() { - return Vec::new(); - } - let mut contexts = vec![PageRuntimeObservableContext { - execution_context_id: self.runtime_observable_default_execution_context_id(), - context_token: self.page_default_runtime.runtime_observable_context_token, - context: &self.page_default_runtime.context as *const _, - }]; + let mut contexts = self + .current_main_world() + .map(|runtime| PageRuntimeObservableContext { + execution_context_id: self.runtime_observable_default_execution_context_id(), + context_token: runtime.runtime_observable_context_token, + context: runtime.context_ptr(), + }) + .into_iter() + .collect::>(); let mut isolated_context_ids = self .page_isolated_world_contexts .execution_context_ids() @@ -397,19 +401,16 @@ impl ScriptVm { pub(super) fn moli_memory_counters(&self) -> RendererMoliMemoryDiagnostics { let document = self.document_runtime.snapshot_document(); let dom = moli_dom_memory_counters(&document); - let main_window_proxy_identity_hash = self - .main_world_is_initialized() - .then(|| { - self.renderer_document_isolate - .with_entered_renderer_document_isolate(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - Ok(context.global(scope).get_identity_hash().get()) - }) - .ok() - }) - .flatten(); + let main_window_proxy_identity_hash = self.current_main_world().and_then(|runtime| { + self.renderer_document_isolate + .with_entered_renderer_document_isolate(|isolate| { + let scope = pin!(v8::HandleScope::new(isolate)); + let scope = &mut scope.init(); + let context = v8::Local::new(scope, &runtime.context); + Ok(context.global(scope).get_identity_hash().get()) + }) + .ok() + }); let host = self._context_host.borrow(); RendererMoliMemoryDiagnostics { scope: RendererMoliMemoryScopeDiagnostics { @@ -507,7 +508,7 @@ impl ScriptVm { &mut self, context_ptr: *const v8::Global, ) -> Result> { - // SAFETY: callers pass pointers to `self.page_default_runtime.context` or page realm context entries owned by this `ScriptVm`. + // SAFETY: callers pass pointers to an owned ready main-world Context or page realm context entries owned by this `ScriptVm`. // The snapshot operation only reads a context slot; it does not mutate or remove any // context while the raw pointer is used. // This is an internal console snapshot, not an owner-visible script turn. @@ -521,7 +522,7 @@ impl ScriptVm { &mut self, context_ptr: *const v8::Global, ) -> Result> { - // SAFETY: callers pass pointers to `self.page_default_runtime.context` or page realm context entries owned by this `ScriptVm`. + // SAFETY: callers pass pointers to an owned ready main-world Context or page realm context entries owned by this `ScriptVm`. // The snapshot operation only reads a context slot; it does not mutate or remove any // context while the raw pointer is used. // This is an internal console snapshot, not an owner-visible script turn. diff --git a/moli-renderer-v8/src/script_vm/script_event_body.rs b/moli-renderer-v8/src/script_vm/script_event_body.rs index d4cde1f4d8..e374001d93 100644 --- a/moli-renderer-v8/src/script_vm/script_event_body.rs +++ b/moli-renderer-v8/src/script_vm/script_event_body.rs @@ -5,8 +5,6 @@ //! synchronous parser/module/runtime algorithms that still need a checkpoint //! consume them through their own named completion boundary. -use std::pin::pin; - use anyhow::{Result, anyhow}; use super::ScriptVm; @@ -15,7 +13,6 @@ use crate::context_bootstrap::{ ORIGINAL_WEBASSEMBLY_LINK_ERROR_CONSTRUCTOR_SLOT, dispatch_window_error_event_with_details, }; use crate::host::ScriptEventTask; -use crate::native_bridge::JsContextHost; use crate::util::{get_private_value, v8_string, v8str}; impl ScriptVm { @@ -30,22 +27,11 @@ impl ScriptVm { } pub(crate) fn dispatch_script_event_body(&mut self, task: &ScriptEventTask) -> Result<()> { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - let document_runtime = &mut self.document_runtime; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - // SAFETY: as_ptr() — V8 callbacks are re-entrant; borrow_mut() panics. See util.rs. - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - document_runtime - .host_dispatch_script_event(scope, host_ptr, task) - .map_err(anyhow::Error::msg) - }) + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime + .host_dispatch_script_event(scope, host_ptr, task) + .map_err(anyhow::Error::msg) + }) } pub(crate) fn report_window_error_body_best_effort( @@ -68,50 +54,36 @@ impl ScriptVm { filename: Option<&str>, error_constructor: Option, ) -> Result<()> { - self.initialize_main_world_if_needed()?; - let context_ptr: *const v8::Global = &self.page_default_runtime.context; - let context_host = self._context_host.clone(); - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = unsafe { v8::Local::new(scope, &*context_ptr) }; - let scope = &mut v8::ContextScope::new(scope, context); - let global = scope.get_current_context().global(scope); - let message_value = v8_string(scope, message) - .ok_or_else(|| anyhow!("failed to allocate reportError message"))?; - let error_value = window_script_failure_error_value( + self.with_default_context_scope(|scope, host_ptr| { + let global = scope.get_current_context().global(scope); + let message_value = v8_string(scope, message) + .ok_or_else(|| anyhow!("failed to allocate reportError message"))?; + let error_value = + window_script_failure_error_value(scope, global, error_constructor, message_value); + if let Some(filename) = filename + && let Some(filename_value) = v8_string(scope, filename) + && let Ok(error_object) = v8::Local::::try_from(error_value) + { + let _ = error_object.set( scope, - global, - error_constructor, - message_value, + v8str(scope, "fileName").into(), + filename_value.into(), ); - if let Some(filename) = filename - && let Some(filename_value) = v8_string(scope, filename) - && let Ok(error_object) = v8::Local::::try_from(error_value) - { - let _ = error_object.set( - scope, - v8str(scope, "fileName").into(), - filename_value.into(), - ); - } - // Internal script failure reporting must not call the page-visible - // window.reportError function while lifecycle state is unwinding. - // Dispatch the equivalent ErrorEvent body directly. - // SAFETY: as_ptr() — V8 callbacks are re-entrant; borrow_mut() panics. See util.rs. - let host_ptr: *mut JsContextHost = (*context_host).as_ptr(); - dispatch_window_error_event_with_details( - scope, - host_ptr, - message, - filename.unwrap_or(""), - 0, - 0, - Some(error_value), - ) - .map_err(anyhow::Error::msg) - }) + } + // Internal script failure reporting must not call the page-visible + // window.reportError function while lifecycle state is unwinding. + // Dispatch the equivalent ErrorEvent body directly. + dispatch_window_error_event_with_details( + scope, + host_ptr, + message, + filename.unwrap_or(""), + 0, + 0, + Some(error_value), + ) + .map_err(anyhow::Error::msg) + }) } } diff --git a/moli-renderer-v8/src/script_vm/script_tasks.rs b/moli-renderer-v8/src/script_vm/script_tasks.rs index 0d71601f7c..24d9fd8f9d 100644 --- a/moli-renderer-v8/src/script_vm/script_tasks.rs +++ b/moli-renderer-v8/src/script_vm/script_tasks.rs @@ -355,38 +355,31 @@ impl ScriptVm { .deferred_page_tasks_mut() .enter_scope(); } - self.initialize_main_world_if_needed() - .map_err(|error| error.to_string())?; - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| -> std::result::Result<(), String> { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - let scope = &mut v8::ContextScope::new(scope, context); - let checkpoint_started = moli_trace::cdp_nav_timing_enabled().then(Instant::now); - Self::reset_dom_binding_trace_window(); - let dom_binding_checkpoint_started = - moli_trace::dom_binding_timing_enabled().then(Instant::now); - Self::perform_microtask_checkpoints(scope, Some(&script.url)) - .map_err(|error| error.to_string())?; - if let Some(started) = dom_binding_checkpoint_started { - Self::emit_dom_binding_trace_window( - "renderer_prepared_script_dom_binding_summary", - "post_script_checkpoint", - Some(&script.url), - started.elapsed(), - ); - } - if let Some(started) = checkpoint_started { - tracing::info!( - target: "moli_cdp_nav_timing", - url = %script.url, - stage = "renderer_prepared_script_checkpoint_done", - elapsed_ms = started.elapsed().as_millis(), - ); - } - Ok(()) - })?; + self.with_current_main_world_scope(|scope, _| { + let checkpoint_started = moli_trace::cdp_nav_timing_enabled().then(Instant::now); + Self::reset_dom_binding_trace_window(); + let dom_binding_checkpoint_started = + moli_trace::dom_binding_timing_enabled().then(Instant::now); + Self::perform_microtask_checkpoints(scope, Some(&script.url))?; + if let Some(started) = dom_binding_checkpoint_started { + Self::emit_dom_binding_trace_window( + "renderer_prepared_script_dom_binding_summary", + "post_script_checkpoint", + Some(&script.url), + started.elapsed(), + ); + } + if let Some(started) = checkpoint_started { + tracing::info!( + target: "moli_cdp_nav_timing", + url = %script.url, + stage = "renderer_prepared_script_checkpoint_done", + elapsed_ms = started.elapsed().as_millis(), + ); + } + Ok(()) + }) + .map_err(|error| error.to_string())?; let current_script_load_disposition = if skip_current_script_load_enqueue { FollowupPageTaskDisposition::Skipped } else { @@ -600,28 +593,21 @@ impl ScriptVm { &mut self, script_url: Option<&Url>, ) -> anyhow::Result<()> { - if !self.main_world_is_initialized() { - return Ok(()); - } - self.renderer_document_isolate - .with_renderer_document_isolate_mut(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - let scope = &mut v8::ContextScope::new(scope, context); - Self::reset_dom_binding_trace_window(); - let dom_binding_checkpoint_started = - moli_trace::dom_binding_timing_enabled().then(Instant::now); - Self::perform_microtask_checkpoints(scope, script_url)?; - if let Some(started) = dom_binding_checkpoint_started { - Self::emit_dom_binding_trace_window( - "renderer_script_task_dom_binding_summary", - "pre_task_checkpoint", - script_url, - started.elapsed(), - ); - } - Ok(()) - }) + self.with_current_main_world_scope(|scope, _| { + Self::reset_dom_binding_trace_window(); + let dom_binding_checkpoint_started = + moli_trace::dom_binding_timing_enabled().then(Instant::now); + Self::perform_microtask_checkpoints(scope, script_url)?; + if let Some(started) = dom_binding_checkpoint_started { + Self::emit_dom_binding_trace_window( + "renderer_script_task_dom_binding_summary", + "pre_task_checkpoint", + script_url, + started.elapsed(), + ); + } + Ok(()) + }) + .map(|_| ()) } } diff --git a/moli-renderer-v8/src/script_vm/security_policy.rs b/moli-renderer-v8/src/script_vm/security_policy.rs index 9ec343a298..d7968822e6 100644 --- a/moli-renderer-v8/src/script_vm/security_policy.rs +++ b/moli-renderer-v8/src/script_vm/security_policy.rs @@ -1,12 +1,9 @@ -use std::pin::pin; - use anyhow::Result; use super::ScriptVm; use crate::{ context_bootstrap::{TrustedTypesCodeGenerationCheck, trusted_types_code_generation_check}, document_runtime::DocumentContentSecurityPolicyViolation, - native_bridge::JsContextHost, page_task_queue::ContentSecurityPolicyViolationEventTask, util::context_host_ptr_from_global_bridge, }; @@ -38,18 +35,11 @@ impl ScriptVm { &mut self, violation: &DocumentContentSecurityPolicyViolation, ) -> Result<()> { - self.initialize_main_world_if_needed()?; - self.renderer_document_isolate - .with_entered_renderer_document_isolate(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*self._context_host).as_ptr(); - self.document_runtime - .queue_content_security_policy_violation_event(scope, host_ptr, violation)?; - Ok(()) - }) + self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime + .queue_content_security_policy_violation_event(scope, host_ptr, violation)?; + Ok(()) + }) } #[cfg(test)] @@ -87,20 +77,12 @@ impl ScriptVm { if self.current_main_document_task_owner() != Some(task.owner()) { return ContentSecurityPolicyViolationBodyExecution::DiscardedStaleDocument; } - let result = self - .renderer_document_isolate - .with_entered_renderer_document_isolate(|isolate| { - let scope = pin!(v8::HandleScope::new(isolate)); - let scope = &mut scope.init(); - let context = v8::Local::new(scope, &self.page_default_runtime.context); - let scope = &mut v8::ContextScope::new(scope, context); - let host_ptr: *mut JsContextHost = (*self._context_host).as_ptr(); - self.document_runtime - .dispatch_content_security_policy_violation_event_page_task( - scope, host_ptr, task, - )?; - Ok(()) - }); + let result = self.with_main_world_document_scope(|scope, host_ptr, document_runtime| { + document_runtime.dispatch_content_security_policy_violation_event_page_task( + scope, host_ptr, task, + )?; + Ok(()) + }); ContentSecurityPolicyViolationBodyExecution::DispatchAttempted(result) } diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/postmessage_selection_and_errors.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/postmessage_selection_and_errors.rs index 92fdd92337..7a0da87e00 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/postmessage_selection_and_errors.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/postmessage_selection_and_errors.rs @@ -221,7 +221,8 @@ fn message_channel_construction_keeps_popup_realm_with_ambient_child_marker() { ) }; - let top_context_ptr = &vm.page_default_runtime.context as *const v8::Global; + let top_context_ptr = + &vm.current_main_world().unwrap().context as *const v8::Global; vm.with_context_scope_by_ptr(top_context_ptr, |scope, _host_ptr| { let _previous_child = crate::native_bridge::enter_active_child_window_scope(scope, Some(child_handle)); diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/window_security_and_domain.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/window_security_and_domain.rs index 6f8a1334d6..b91642be0d 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/window_security_and_domain.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/events_selection_storage/window_security_and_domain.rs @@ -1419,7 +1419,8 @@ fn top_web_storage_receiver_ignores_ambient_opaque_child_owner() { "sandboxed child should have an opaque storage origin" ); - let top_context_ptr = &vm.page_default_runtime.context as *const v8::Global; + let top_context_ptr = + &vm.current_main_world().unwrap().context as *const v8::Global; vm.with_context_scope_by_ptr(top_context_ptr, |scope, _host_ptr| { let _previous = crate::native_bridge::enter_active_child_window_scope(scope, Some(child_handle)); diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/insert_adjacent_element_conversion.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/insert_adjacent_element_conversion.rs index a790dcfc85..80b51326e5 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/insert_adjacent_element_conversion.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/insert_adjacent_element_conversion.rs @@ -21,7 +21,7 @@ fn insert_adjacent_element_preserves_registered_native_alias_identity() { "", ); vm.eval("globalThis.adjacentOwner=document.getElementById('child').contentWindow;globalThis.adjacentRoot=adjacentOwner.document.createElement('div');globalThis.adjacentNode=adjacentOwner.document.createElement('span');adjacentOwner.document.body.appendChild(adjacentRoot)").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, alias) in [ diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/message_event_init.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/message_event_init.rs index e4ccac2bcc..89d1cbc3ed 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/message_event_init.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/message_event_init.rs @@ -160,7 +160,7 @@ fn message_event_sources_preserve_registered_native_proxy_identity() { let mut vm = new_storage_html_test_vm("https://message-event-native-source.test/"); vm.eval("document.body.innerHTML = ''; globalThis.sourcePort = new MessageChannel().port1;") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "sourcePort"); diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/midi_events.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/midi_events.rs index 07e4080b2a..9da6b02773 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/midi_events.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/midi_events.rs @@ -19,7 +19,7 @@ fn midi_events_preserve_payloads_and_webidl_buffer_policy() { fn midi_event_payloads_accept_native_ports_and_registered_event_proxies() { let mut vm = new_storage_page_task_executor_test_vm("https://midi-event-proxies.test/"); vm.eval("document.body.innerHTML = ''; globalThis.childWindow = document.querySelector('iframe').contentWindow;").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "childWindow"); diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/css_surface.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/css_surface.rs index 495b838be1..2718ecbf0c 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/css_surface.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/css_surface.rs @@ -320,7 +320,7 @@ fn dom_quad_generated_receivers_keep_registered_proxy_identity_and_error_realms( materialize_single_child_default_realm_for_test(&mut vm, "native Quad child Realm"); vm.eval("globalThis.quadRealm = document.getElementById('native-quad-frame').contentWindow;") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "quadTarget"); 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 fed6f82d9f..999d79e51d 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 @@ -889,7 +889,7 @@ fn dom_exception_factories_capture_v8_stacks_without_author_lookup() { 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 _; + let context_ptr = &vm.current_main_world().unwrap().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"); diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/web_transport_error_values.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/web_transport_error_values.rs index 1c69e61ff0..3abd2a8f40 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/web_transport_error_values.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/web_transport_error_values.rs @@ -22,7 +22,7 @@ fn web_transport_error_getters_accept_native_proxies_and_reject_author_wrappers( vm.eval(r#"document.body.innerHTML=''; globalThis.childWindow=document.querySelector('iframe').contentWindow; globalThis.errors=[new childWindow.WebTransportError('m\udfff',{source:'session',streamErrorCode:4294967295})];"#).unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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"); @@ -77,7 +77,7 @@ fn web_transport_error_getters_accept_native_proxies_and_reject_author_wrappers( fn web_transport_error_deserialization_respects_native_realm_exposure() { let mut source = new_storage_page_task_executor_test_vm("https://transport-error-source.test/"); source.eval("globalThis.value=new WebTransportError('message',{source:'session',streamErrorCode:42})").unwrap(); - let context_ptr = &source.page_default_runtime.context as *const _; + let context_ptr = &source.current_main_world().unwrap().context as *const _; let mut payload = None; source .with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { @@ -98,7 +98,7 @@ fn web_transport_error_deserialization_respects_native_realm_exposure() { .unwrap(), "false|undefined" ); - let context_ptr = &target.page_default_runtime.context as *const _; + let context_ptr = &target.current_main_world().unwrap().context as *const _; target .with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let scope = std::pin::pin!(v8::TryCatch::new(scope)); diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/webrtc_events.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/webrtc_events.rs index 2caf975128..d7ecdeed36 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/webrtc_events.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/webrtc_events.rs @@ -283,7 +283,7 @@ fn webrtc_event_dictionary_snapshots_preserve_registered_native_proxy_payloads() "#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for name in ["snapshotCandidate", "snapshotChannel", "snapshotError"] { diff --git a/moli-renderer-v8/src/script_vm/tests/canvas_fill_rect.rs b/moli-renderer-v8/src/script_vm/tests/canvas_fill_rect.rs index ccec434250..2743cda1a0 100644 --- a/moli-renderer-v8/src/script_vm/tests/canvas_fill_rect.rs +++ b/moli-renderer-v8/src/script_vm/tests/canvas_fill_rect.rs @@ -34,7 +34,7 @@ fn canvas_fill_rect_registered_native_proxy_keeps_context_identity() { ctx.setTransform(1,0,0,1,20,10); ctx.fillStyle='red'; ctx.globalAlpha=.5;", ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "ctx"); diff --git a/moli-renderer-v8/src/script_vm/tests/canvas_transform_snapshots.rs b/moli-renderer-v8/src/script_vm/tests/canvas_transform_snapshots.rs index 32fd324490..1d67c49f0c 100644 --- a/moli-renderer-v8/src/script_vm/tests/canvas_transform_snapshots.rs +++ b/moli-renderer-v8/src/script_vm/tests/canvas_transform_snapshots.rs @@ -36,7 +36,7 @@ fn canvas_transform_snapshots_accept_registered_native_proxies_and_share_paint_s for (const ctx of nativeCanvasContexts) ctx.setTransform(1,0,0,1,20,0);"#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "nativeCanvasContexts"); diff --git a/moli-renderer-v8/src/script_vm/tests/credential_container.rs b/moli-renderer-v8/src/script_vm/tests/credential_container.rs index 6cf79b1cad..a15f5fa135 100644 --- a/moli-renderer-v8/src/script_vm/tests/credential_container.rs +++ b/moli-renderer-v8/src/script_vm/tests/credential_container.rs @@ -47,7 +47,7 @@ fn credentials_container_accepts_registered_native_proxies_and_native_credential let mut vm = new_storage_page_task_executor_test_vm("https://credentials-container.test/"); vm.eval("globalThis.nativeSignal = AbortSignal.abort({})") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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 fixtures = v8::Object::new(scope); diff --git a/moli-renderer-v8/src/script_vm/tests/credential_interfaces.rs b/moli-renderer-v8/src/script_vm/tests/credential_interfaces.rs index e7678dbea5..acfcce6636 100644 --- a/moli-renderer-v8/src/script_vm/tests/credential_interfaces.rs +++ b/moli-renderer-v8/src/script_vm/tests/credential_interfaces.rs @@ -5,7 +5,7 @@ fn credential_members_preserve_native_receiver_inheritance_and_error_realms() { let mut vm = new_storage_page_task_executor_test_vm("https://credential-interfaces.test/"); vm.eval("document.body.innerHTML = ''") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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 objects = v8::Object::new(scope); diff --git a/moli-renderer-v8/src/script_vm/tests/cross_origin_navigation.rs b/moli-renderer-v8/src/script_vm/tests/cross_origin_navigation.rs index 05c915a95e..ad9316d388 100644 --- a/moli-renderer-v8/src/script_vm/tests/cross_origin_navigation.rs +++ b/moli-renderer-v8/src/script_vm/tests/cross_origin_navigation.rs @@ -388,7 +388,7 @@ root.appendChild(frame); // Use the production retirement path, pausing before a successor Realm // materializes, so the saved Location really belongs to a facade. vm.destroy_child_default_context(context_id); - let context = &vm.page_default_runtime.context as *const v8::Global; + let context = &vm.current_main_world().unwrap().context as *const v8::Global; vm.with_context_scope_by_ptr(context, |scope, _| { let host_ptr = crate::util::context_host_ptr_from_global_bridge(scope).unwrap(); let host = unsafe { &mut *host_ptr }; diff --git a/moli-renderer-v8/src/script_vm/tests/css_keyframes_mutations.rs b/moli-renderer-v8/src/script_vm/tests/css_keyframes_mutations.rs index a7c34dee0f..63541ebae1 100644 --- a/moli-renderer-v8/src/script_vm/tests/css_keyframes_mutations.rs +++ b/moli-renderer-v8/src/script_vm/tests/css_keyframes_mutations.rs @@ -22,7 +22,7 @@ fn css_keyframes_mutations_accept_native_proxies_and_preserve_intrinsic_exceptio const sheet=new other.CSSStyleSheet();sheet.replaceSync('@keyframes native {from {left:0px}}'); globalThis.keyframes=sheet.cssRules[0];globalThis.frame=keyframes[0]; "#).unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, proxy_name) in [("keyframes", "keyframesProxy"), ("frame", "frameProxy")] { diff --git a/moli-renderer-v8/src/script_vm/tests/css_keyframes_receivers.rs b/moli-renderer-v8/src/script_vm/tests/css_keyframes_receivers.rs index c406660823..af8bd34c88 100644 --- a/moli-renderer-v8/src/script_vm/tests/css_keyframes_receivers.rs +++ b/moli-renderer-v8/src/script_vm/tests/css_keyframes_receivers.rs @@ -40,7 +40,7 @@ fn css_keyframes_receivers_share_native_proxy_backing_slots_and_owner_realm_cach "#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, proxy_name) in [("keyframes", "framesProxy"), ("frame", "frameProxy")] { diff --git a/moli-renderer-v8/src/script_vm/tests/document_create_event.rs b/moli-renderer-v8/src/script_vm/tests/document_create_event.rs index a03696abc4..20cb5d7d9a 100644 --- a/moli-renderer-v8/src/script_vm/tests/document_create_event.rs +++ b/moli-renderer-v8/src/script_vm/tests/document_create_event.rs @@ -97,7 +97,7 @@ fn document_create_event_uses_intrinsics_and_registered_native_receivers() { new_storage_page_task_executor_test_vm("https://document-create-event-native.test/"); vm.eval("document.body.innerHTML=''; globalThis.child=document.querySelector('iframe').contentWindow;") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "document"); diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/frame_navigation_and_history.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/frame_navigation_and_history.rs index 9cfcade898..7ff5d7142c 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/frame_navigation_and_history.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/frame_navigation_and_history.rs @@ -3445,7 +3445,7 @@ fn window_named_access_uses_its_realm_during_child_history_callbacks() { ._context_host .borrow() .child_browsing_context_handles_in_document_order()[0]; - let context = &vm.page_default_runtime.context as *const v8::Global; + let context = &vm.current_main_world().unwrap().context as *const v8::Global; vm.with_context_scope_by_ptr(context, |scope, _| { crate::native_bridge::enter_active_child_window_scope(scope, Some(child)); Ok(()) diff --git a/moli-renderer-v8/src/script_vm/tests/dom_exception_proxy_identity.rs b/moli-renderer-v8/src/script_vm/tests/dom_exception_proxy_identity.rs index 40e5f51344..6949ca671c 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_exception_proxy_identity.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_exception_proxy_identity.rs @@ -4,7 +4,7 @@ use super::*; 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})]").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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"); diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/computed_style_access.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/computed_style_access.rs index 410e4b7d7a..8b8039d142 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/computed_style_access.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/computed_style/computed_style_access.rs @@ -112,7 +112,8 @@ fn inspector_default_runtime_evaluate_masks_ambient_child_owner_scope() { .get(&child_context_id) .expect("Inspector child owner realm record should exist") .child_handle; - let top_context_ptr = &vm.page_default_runtime.context as *const v8::Global; + let top_context_ptr = + &vm.current_main_world().unwrap().context as *const v8::Global; vm.with_context_scope_by_ptr(top_context_ptr, |scope, _host_ptr| { let _previous = crate::native_bridge::enter_active_child_window_scope(scope, Some(child_handle)); diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/receivers.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/receivers.rs index 50373e9cee..bb72cbbd51 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/receivers.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/receivers.rs @@ -76,7 +76,7 @@ async fn xhr_generated_receivers_preserve_registered_native_proxy_state_and_load vm.eval( "globalThis.xhrOwner = document.querySelector('iframe').contentWindow; globalThis.realXhr = new xhrOwner.XMLHttpRequest(); globalThis.realUpload = realXhr.upload;", ).unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, alias) in [("realXhr", "xhrProxy"), ("realUpload", "uploadProxy")] { diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/shadow_dom/split30_gethtml_derived_options.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/shadow_dom/split30_gethtml_derived_options.rs index 3d7bdcc1df..b5162db7ce 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/shadow_dom/split30_gethtml_derived_options.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/shadow_dom/split30_gethtml_derived_options.rs @@ -47,7 +47,7 @@ fn get_html_accepts_registered_native_proxies_and_rejects_author_receivers() { true"#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, alias) in [ diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs index 5c3ef1a27a..e6e57f9a9c 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs @@ -993,7 +993,7 @@ fn xml_http_request_blob_response_uses_final_mime_type() { let headers = moli_fetch::headers_from_byte_strings(&headers).unwrap(); let context_ptr: *const v8::Global = - &vm.page_default_runtime.context as *const _; + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -1094,7 +1094,8 @@ fn xml_http_request_default_response_type_parses_response_xml_for_document_mime( ) .expect("xhr responseXML setup should run"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -1183,7 +1184,8 @@ fn xml_http_request_default_response_type_parses_response_xml_for_document_mime( #[test] fn xml_http_request_send_body_applies_webidl_conversion() { let vm = new_storage_test_vm("https://xhr-send-body-webidl.test/"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { @@ -2011,7 +2013,8 @@ fn pending_network_body_stream_emits_small_chunks_before_close() { let mut vm = new_storage_test_vm("https://pending-stream-small-chunks.test/"); let body_source_id = crate::network_host::new_network_body_source_id(); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -2045,7 +2048,8 @@ fn pending_network_body_stream_emits_small_chunks_before_close() { .expect("pending stream read should be registered"); assert_eq!(initial, "[]"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -2075,7 +2079,8 @@ fn pending_network_body_stream_buffers_future_chunks_until_next_pull() { let mut vm = new_storage_test_vm("https://pending-stream-backpressure.test/"); let body_source_id = crate::network_host::new_network_body_source_id(); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -2106,7 +2111,8 @@ fn pending_network_body_stream_buffers_future_chunks_until_next_pull() { ) .expect("first pending stream read should be registered"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let buffered_len = vm .renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { @@ -2393,7 +2399,8 @@ async fn streaming_subresource_finish_preserves_response_head_cache_state() { let mut body_writer = crate::types::SubresourceResponseBodyWriter::default(); body_writer.append(b"cached-body"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let context_host = vm._context_host.clone(); vm.renderer_document_isolate .with_entered_renderer_document_isolate({ @@ -2551,7 +2558,8 @@ async fn async_subresource_failure_network_error_override_preserves_fetch_reject Url::parse("https://sw-non-stream-failure.test/data").expect("request URL should parse"); let rejection_message = "FetchEvent.respondWith rejected an error Response"; - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let context_host = vm._context_host.clone(); vm.renderer_document_isolate .with_entered_renderer_document_isolate({ @@ -2689,7 +2697,8 @@ async fn streaming_fetch_body_error_records_response_started_then_body_failed() let final_url = Url::parse("https://streaming-fetch-body-error.test/final") .expect("final URL should parse"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let context_host = vm._context_host.clone(); vm.renderer_document_isolate .with_entered_renderer_document_isolate({ @@ -2836,7 +2845,8 @@ fn install_streaming_fetch_response_fixture( global_name: &'static str, load_client: crate::network::ResourceRequestClient, ) { - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let context_host = vm._context_host.clone(); vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { @@ -2952,7 +2962,8 @@ async fn streaming_fetch_body_cancel_aborts_streaming_subresource() { let request_url = Url::parse("https://streaming-fetch-body-cancel.test/data") .expect("request URL should parse"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let context_host = vm._context_host.clone(); let cancel_handle_for_state = cancel_handle.clone(); vm.renderer_document_isolate @@ -3619,7 +3630,8 @@ async fn streaming_xhr_materialization_failure_errors_body_source_before_close() .truncate_file_for_test(0) .expect("test should truncate the pooled body file"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let context_host = vm._context_host.clone(); vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { @@ -4263,7 +4275,7 @@ fn xml_http_request_document_response_requires_an_eligible_mime_and_well_formed_ .expect("finish document response"); } else { let context_ptr: *const v8::Global = - &vm.page_default_runtime.context; + &vm.current_main_world().unwrap().context; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); diff --git a/moli-renderer-v8/src/script_vm/tests/encoded_video_chunk.rs b/moli-renderer-v8/src/script_vm/tests/encoded_video_chunk.rs index eab1416bca..e04f919472 100644 --- a/moli-renderer-v8/src/script_vm/tests/encoded_video_chunk.rs +++ b/moli-renderer-v8/src/script_vm/tests/encoded_video_chunk.rs @@ -37,7 +37,7 @@ fn encoded_video_chunk_preserves_bytes_transfer_conversion_and_serialization() { fn encoded_video_chunk_accepts_registered_native_proxy_receivers() { let mut vm = new_storage_page_task_executor_test_vm("https://encoded-video-chunk.test/"); vm.eval("globalThis.nativeChunk = new EncodedVideoChunk({type:'delta', timestamp:-5, data:new Uint8Array([4,5])})").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "nativeChunk"); diff --git a/moli-renderer-v8/src/script_vm/tests/extracted/realms_and_teardown.rs b/moli-renderer-v8/src/script_vm/tests/extracted/realms_and_teardown.rs index e3210738a4..137dcee569 100644 --- a/moli-renderer-v8/src/script_vm/tests/extracted/realms_and_teardown.rs +++ b/moli-renderer-v8/src/script_vm/tests/extracted/realms_and_teardown.rs @@ -177,7 +177,7 @@ fn retained_response_body_keeps_bytes_until_the_last_v8_reference() { let callback = isolate.with_renderer_document_isolate_mut(|isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &vm.page_default_runtime.context); + let context = v8::Local::new(scope, &vm.current_main_world().unwrap().context); let scope = &mut v8::ContextScope::new(scope, context); let value = context .global(scope) @@ -430,7 +430,7 @@ fn retained_response_body_keeps_error_reason_until_the_last_v8_reference() { let callback = isolate.with_renderer_document_isolate_mut(|isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &vm.page_default_runtime.context); + let context = v8::Local::new(scope, &vm.current_main_world().unwrap().context); let scope = &mut v8::ContextScope::new(scope, context); let value = context .global(scope) @@ -786,7 +786,7 @@ fn retained_module_function_keeps_native_values_until_the_last_v8_reference() { let callback = isolate.with_renderer_document_isolate_mut(|isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &vm.page_default_runtime.context); + let context = v8::Local::new(scope, &vm.current_main_world().unwrap().context); let scope = &mut v8::ContextScope::new(scope, context); let value = context .global(scope) @@ -1196,7 +1196,7 @@ fn capture_initial_environment_for_gc_test( let environment = isolate.with_renderer_document_isolate_mut(|isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); let scope = &mut scope.init(); - let context = v8::Local::new(scope, &vm.page_default_runtime.context); + let context = v8::Local::new(scope, &vm.current_main_world().unwrap().context); let scope = &mut v8::ContextScope::new(scope, context); let opener = context.global(scope); crate::script_vm::ScriptVmInitialDocumentEnvironment::inherited_in_scope( @@ -1316,7 +1316,7 @@ fn retained_document_realm_keeps_native_values_until_the_last_v8_reference() { .unwrap(); let weak_host = vm.context_host_weak_for_test(); let isolate = vm.renderer_document_isolate.clone(); - let context = vm.page_default_runtime.context.clone(); + let context = vm.current_main_world().unwrap().context.clone(); drop(vm); assert!( weak_host.upgrade().is_some(), @@ -2959,7 +2959,10 @@ fn resource_owner_id_is_available_from_current_context_slot() { #[test] fn runtime_observable_context_token_is_available_from_current_context_slot() { let mut vm = new_parsed_test_vm("https://example.test/", "

runtime

"); - let expected = vm.page_default_runtime.runtime_observable_context_token; + let expected = vm + .current_main_world() + .unwrap() + .runtime_observable_context_token; vm.with_default_context_scope_and_checkpoint_for_test(|scope, _runtime_ptr| { assert_eq!( diff --git a/moli-renderer-v8/src/script_vm/tests/extracted/script_execution.rs b/moli-renderer-v8/src/script_vm/tests/extracted/script_execution.rs index c522f8182c..4d1d30c6c8 100644 --- a/moli-renderer-v8/src/script_vm/tests/extracted/script_execution.rs +++ b/moli-renderer-v8/src/script_vm/tests/extracted/script_execution.rs @@ -170,7 +170,7 @@ fn runtime_await_promise_sync_result_survives_queued_allocation_gc() { // deterministic: the page microtask runs after Inspector has converted the // synchronous string to an awaitable promise, but before Inspector's // reaction publishes the Runtime.evaluate response. - let context_ptr: *const v8::Global = &vm.page_default_runtime.context; + let context_ptr: *const v8::Global = &vm.current_main_world().unwrap().context; vm.renderer_document_isolate .clone() .with_entered_renderer_document_isolate(|isolate| { diff --git a/moli-renderer-v8/src/script_vm/tests/gamepad_interfaces.rs b/moli-renderer-v8/src/script_vm/tests/gamepad_interfaces.rs index fb5e35f502..736341429f 100644 --- a/moli-renderer-v8/src/script_vm/tests/gamepad_interfaces.rs +++ b/moli-renderer-v8/src/script_vm/tests/gamepad_interfaces.rs @@ -20,7 +20,7 @@ fn gamepad_events_preserve_native_payload_identity_and_registered_proxy_brands() let mut vm = new_storage_page_task_executor_test_vm("https://gamepad-payload.test/"); vm.eval("document.body.innerHTML = ''") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); // Native identity fixtures only; these are not devices published by diff --git a/moli-renderer-v8/src/script_vm/tests/idb_sqlite_lifecycle.rs b/moli-renderer-v8/src/script_vm/tests/idb_sqlite_lifecycle.rs index 0f01fe6540..2c03fdf966 100644 --- a/moli-renderer-v8/src/script_vm/tests/idb_sqlite_lifecycle.rs +++ b/moli-renderer-v8/src/script_vm/tests/idb_sqlite_lifecycle.rs @@ -197,7 +197,7 @@ impl SqlBackend { panic!("expected connection receipt"); }; self.connection.borrow_mut().replace(connection.downgrade()); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let database = crate::context_bootstrap::create_sql_database_for_test(scope, connection) diff --git a/moli-renderer-v8/src/script_vm/tests/idb_transaction_gc.rs b/moli-renderer-v8/src/script_vm/tests/idb_transaction_gc.rs index 81811d9729..0f3a236da5 100644 --- a/moli-renderer-v8/src/script_vm/tests/idb_transaction_gc.rs +++ b/moli-renderer-v8/src/script_vm/tests/idb_transaction_gc.rs @@ -13,7 +13,7 @@ fn collect(vm: &mut PageVmTaskExecutorTestHarness) { } fn native_entries(vm: &mut PageVmTaskExecutorTestHarness) -> usize { - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); Ok(crate::context_bootstrap::indexed_db_transaction_state_entries_for_test(scope, global)) diff --git a/moli-renderer-v8/src/script_vm/tests/indexed_db.rs b/moli-renderer-v8/src/script_vm/tests/indexed_db.rs index 5f05341b41..864ca9cf71 100644 --- a/moli-renderer-v8/src/script_vm/tests/indexed_db.rs +++ b/moli-renderer-v8/src/script_vm/tests/indexed_db.rs @@ -6871,7 +6871,7 @@ fn indexed_db_request_getters_preserve_native_proxy_identity_and_reject_author_w nativeRequest.onupgradeneeded=()=>nativeRequest.result.createObjectStore('s');"#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "nativeRequest"); diff --git a/moli-renderer-v8/src/script_vm/tests/indexed_db_gc.rs b/moli-renderer-v8/src/script_vm/tests/indexed_db_gc.rs index 7395d937c2..fd2ad50421 100644 --- a/moli-renderer-v8/src/script_vm/tests/indexed_db_gc.rs +++ b/moli-renderer-v8/src/script_vm/tests/indexed_db_gc.rs @@ -27,7 +27,7 @@ fn indexed_db_completed_cursor_graphs_collect_and_retained_cursors_survive_gc() ); collect(&mut vm); assert_eq!(vm.eval("checkIndexedDbCursorGc()").unwrap(), "true"); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { assert_eq!( crate::context_bootstrap::indexed_db_native_cursor_count_for_test(scope), @@ -101,7 +101,7 @@ fn indexed_db_schema_getters_accept_registered_native_proxies_in_callee_realms() ) .unwrap(); vm.eval_after_selected_page_tasks("'prepared'").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "schemaValues"); diff --git a/moli-renderer-v8/src/script_vm/tests/lazy_storage.rs b/moli-renderer-v8/src/script_vm/tests/lazy_storage.rs index 383ad6b2dc..405814d476 100644 --- a/moli-renderer-v8/src/script_vm/tests/lazy_storage.rs +++ b/moli-renderer-v8/src/script_vm/tests/lazy_storage.rs @@ -741,7 +741,7 @@ fn native_dom_exception_materializes_in_a_detached_realm() { 0 ); let isolate = vm.renderer_document_isolate.clone(); - let context = vm.page_default_runtime.context.clone(); + let context = vm.current_main_world().unwrap().context.clone(); drop(vm); isolate.with_renderer_document_isolate_mut(|isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); diff --git a/moli-renderer-v8/src/script_vm/tests/media_device_interfaces.rs b/moli-renderer-v8/src/script_vm/tests/media_device_interfaces.rs index efd839c76d..9dddc1bb49 100644 --- a/moli-renderer-v8/src/script_vm/tests/media_device_interfaces.rs +++ b/moli-renderer-v8/src/script_vm/tests/media_device_interfaces.rs @@ -23,7 +23,8 @@ fn media_device_interfaces_preserve_inheritance_brands_and_secure_exposure() { #[test] fn media_device_serialization_reads_native_fields_and_preserves_utf16() { let mut vm = new_storage_page_task_executor_test_vm("https://media-device-values.test/"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); diff --git a/moli-renderer-v8/src/script_vm/tests/message_port_transfer.rs b/moli-renderer-v8/src/script_vm/tests/message_port_transfer.rs index 57508c189d..b3739e7a9f 100644 --- a/moli-renderer-v8/src/script_vm/tests/message_port_transfer.rs +++ b/moli-renderer-v8/src/script_vm/tests/message_port_transfer.rs @@ -47,7 +47,7 @@ fn message_port_transfer_respects_registered_native_proxy_identity() { let mut vm = new_storage_page_task_executor_test_vm("https://port-transfer.test/"); vm.eval("globalThis.channel=new MessageChannel();globalThis.original=channel.port1") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "original"); diff --git a/moli-renderer-v8/src/script_vm/tests/observer_callbacks.rs b/moli-renderer-v8/src/script_vm/tests/observer_callbacks.rs index c43f0bb544..3efc2fb602 100644 --- a/moli-renderer-v8/src/script_vm/tests/observer_callbacks.rs +++ b/moli-renderer-v8/src/script_vm/tests/observer_callbacks.rs @@ -265,7 +265,7 @@ fn mutation_observer_observe_accepts_registered_native_proxy_targets_and_receive rv.set(proxy.into()); } let mut vm = new_storage_html_test_vm("https://mutation-observer-native-proxy.test/"); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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 function = v8::Function::new(scope, register_proxy).unwrap(); diff --git a/moli-renderer-v8/src/script_vm/tests/payment_events.rs b/moli-renderer-v8/src/script_vm/tests/payment_events.rs index 9f266cade4..b64f0b2353 100644 --- a/moli-renderer-v8/src/script_vm/tests/payment_events.rs +++ b/moli-renderer-v8/src/script_vm/tests/payment_events.rs @@ -42,7 +42,7 @@ fn payment_event_payloads_and_update_with_accept_registered_native_proxies() { let mut vm = new_storage_page_task_executor_test_vm("https://payment-native-proxy.test/"); vm.eval("globalThis.paymentEvent = new PaymentMethodChangeEvent('x', {methodName:'pay', methodDetails:{token:42}})") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "paymentEvent"); diff --git a/moli-renderer-v8/src/script_vm/tests/picture_in_picture_interfaces.rs b/moli-renderer-v8/src/script_vm/tests/picture_in_picture_interfaces.rs index 0ee0dfbee8..f7cd6c2c75 100644 --- a/moli-renderer-v8/src/script_vm/tests/picture_in_picture_interfaces.rs +++ b/moli-renderer-v8/src/script_vm/tests/picture_in_picture_interfaces.rs @@ -14,7 +14,7 @@ fn picture_in_picture_interfaces_validate_descriptors_receivers_and_required_dic } fn install_closed_window(vm: &mut ScriptVm) { - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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 window = crate::context_bootstrap::closed_picture_in_picture_window_for_test(scope); @@ -107,7 +107,7 @@ fn picture_in_picture_native_event_proxies_read_the_original_private_payload() { new_storage_page_task_executor_test_vm("https://picture-in-picture-event-proxy.test/"); install_closed_window(&mut vm); vm.eval("globalThis.nativeEvent = new PictureInPictureEvent('enterpictureinpicture', {pictureInPictureWindow:nativeWindow})").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "nativeEvent"); diff --git a/moli-renderer-v8/src/script_vm/tests/rtc_configuration.rs b/moli-renderer-v8/src/script_vm/tests/rtc_configuration.rs index fd89d1dc39..fe8986abe1 100644 --- a/moli-renderer-v8/src/script_vm/tests/rtc_configuration.rs +++ b/moli-renderer-v8/src/script_vm/tests/rtc_configuration.rs @@ -28,7 +28,7 @@ fn rtc_configuration_uses_registered_native_proxy_identity() { let mut vm = new_storage_page_task_executor_test_vm("https://rtc-configuration.test/"); vm.eval("globalThis.pc = new RTCPeerConnection({iceTransportPolicy:'relay'})") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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 target = global @@ -66,7 +66,7 @@ fn rtc_configuration_uses_registered_native_proxy_identity() { #[test] fn rtc_configuration_snapshots_preserve_certificate_identity_and_immutability() { let mut vm = new_storage_page_task_executor_test_vm("https://rtc-configuration.test/"); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let certificate = v8::Object::new(scope); crate::web_api_interfaces::RTCCertificate::DESCRIPTOR diff --git a/moli-renderer-v8/src/script_vm/tests/rtc_encoded_frames.rs b/moli-renderer-v8/src/script_vm/tests/rtc_encoded_frames.rs index a788350fc6..6d0085f031 100644 --- a/moli-renderer-v8/src/script_vm/tests/rtc_encoded_frames.rs +++ b/moli-renderer-v8/src/script_vm/tests/rtc_encoded_frames.rs @@ -2,7 +2,7 @@ use super::*; fn install_frames(vm: &mut ScriptVm) { use crate::context_bootstrap::encoded_frames::{FrameKind, native_frame_for_test}; - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, kind, metadata) in [ @@ -124,7 +124,7 @@ fn rtc_encoded_frame_cross_isolate_clone_respects_worker_exposure_and_private_in let mut vm = new_storage_page_task_executor_test_vm("https://rtc-encoded-frames.test/"); install_frames(&mut vm); vm.eval("globalThis.__wireSource = [audioFrame, audioFrame.data, audioFrame, videoFrame, videoFrame.data]; true").unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; let payload = vm .with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); diff --git a/moli-renderer-v8/src/script_vm/tests/svg_geometry_methods.rs b/moli-renderer-v8/src/script_vm/tests/svg_geometry_methods.rs index 93110952e9..0727b9a2a5 100644 --- a/moli-renderer-v8/src/script_vm/tests/svg_geometry_methods.rs +++ b/moli-renderer-v8/src/script_vm/tests/svg_geometry_methods.rs @@ -30,7 +30,7 @@ fn svg_geometry_methods_use_registered_proxy_geometry_without_author_traps() { "#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().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, "rect"); diff --git a/moli-renderer-v8/src/script_vm/tests/time_ranges.rs b/moli-renderer-v8/src/script_vm/tests/time_ranges.rs index 84ae24a579..a48ba4f293 100644 --- a/moli-renderer-v8/src/script_vm/tests/time_ranges.rs +++ b/moli-renderer-v8/src/script_vm/tests/time_ranges.rs @@ -19,7 +19,7 @@ fn time_ranges_nonempty_native_snapshots_preserve_endpoints_and_uint32_conversio let mut vm = new_storage_page_task_executor_test_vm("https://time-ranges-native.test/"); vm.eval("document.body.innerHTML = ''") .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let intervals = [(-3.5, -1.0), (0.0, 0.0), (0.25, 1.75), (4.0, f64::INFINITY)]; let ranges = crate::context_bootstrap::new_time_ranges_value(scope, &intervals); diff --git a/moli-renderer-v8/src/script_vm/tests/webidl_fetch/url_and_initializer_semantics.rs b/moli-renderer-v8/src/script_vm/tests/webidl_fetch/url_and_initializer_semantics.rs index d12801e6c7..95092b51cd 100644 --- a/moli-renderer-v8/src/script_vm/tests/webidl_fetch/url_and_initializer_semantics.rs +++ b/moli-renderer-v8/src/script_vm/tests/webidl_fetch/url_and_initializer_semantics.rs @@ -733,7 +733,8 @@ async fn response_clone_tees_pending_network_body_after_parent_consumption() { let response_url = Url::parse("https://response-clone-pending-stream.test/data.json") .expect("response URL should parse"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -799,7 +800,8 @@ async fn response_clone_tees_pending_network_body_after_parent_consumption() { ) .expect("pending fetch clone fan-out should evaluate"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -843,7 +845,8 @@ fn pending_fetch_body_pipe_through_text_decoder_stream_pulls_future_chunks() { let response_url = Url::parse("https://pending-fetch-pipe-through.test/stream.txt") .expect("response URL should parse"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -905,7 +908,8 @@ fn pending_fetch_body_pipe_through_text_decoder_stream_pulls_future_chunks() { ) .expect("pending fetch pipeThrough setup should evaluate"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -930,7 +934,8 @@ fn pending_fetch_body_pipe_through_text_decoder_stream_pulls_future_chunks() { .expect("first pending pipeThrough result should evaluate"); assert_eq!(first, r#"["chunk:O"]"#); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -965,7 +970,8 @@ fn materialize_response_object_preserves_redirected_slot() { .expect("document URL should parse"); let final_url = Url::parse("https://response-materialize-redirected.test/final.txt") .expect("final URL should parse"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { @@ -1026,7 +1032,7 @@ async fn filtered_response_materialization_preserves_urls_across_clone_and_cache ) .expect("final URL should parse"); let context_ptr: *const v8::Global = - &vm.page_default_runtime.context as *const _; + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate({ @@ -1134,7 +1140,7 @@ async fn filtered_response_materialization_preserves_urls_across_clone_and_cache ); let context_ptr: *const v8::Global = - &vm.page_default_runtime.context as *const _; + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -1186,7 +1192,8 @@ fn materialize_response_object_rejects_locked_response_body() { .expect("locked response setup should evaluate"); assert_eq!(state, "true|false"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { let scope = std::pin::pin!(v8::HandleScope::new(isolate)); @@ -1497,7 +1504,8 @@ fn fetched_null_bodies_discard_payloads_without_registering_pending_streams() { use crate::network_host::{FetchResponseRequest, MaterializedResponseBody}; let vm = new_storage_test_vm("https://null-response.test/"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; let host = vm._context_host.clone(); vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { diff --git a/moli-renderer-v8/src/script_vm/tests/webidl_fetch/webidl_and_transform_streams.rs b/moli-renderer-v8/src/script_vm/tests/webidl_fetch/webidl_and_transform_streams.rs index 35035443a9..9bc9ff22bd 100644 --- a/moli-renderer-v8/src/script_vm/tests/webidl_fetch/webidl_and_transform_streams.rs +++ b/moli-renderer-v8/src/script_vm/tests/webidl_fetch/webidl_and_transform_streams.rs @@ -17,7 +17,8 @@ fn text_codec_runtime_preserves_streaming_bom_and_fatal_errors() { #[test] fn webidl_required_nullable_dictionary_rejects_undefined_member() { let vm = new_storage_test_vm("https://webidl-dictionary-nullable-required.test/"); - let context_ptr: *const v8::Global = &vm.page_default_runtime.context as *const _; + let context_ptr: *const v8::Global = + &vm.current_main_world().unwrap().context as *const _; vm.renderer_document_isolate .with_entered_renderer_document_isolate(move |isolate| { diff --git a/moli-renderer-v8/src/script_vm/tests/writable_stream_native_receivers.rs b/moli-renderer-v8/src/script_vm/tests/writable_stream_native_receivers.rs index d8594fda08..68975bf0d1 100644 --- a/moli-renderer-v8/src/script_vm/tests/writable_stream_native_receivers.rs +++ b/moli-renderer-v8/src/script_vm/tests/writable_stream_native_receivers.rs @@ -12,7 +12,7 @@ globalThis.aborting = new WritableStream(); "#, ) .unwrap(); - let context_ptr = &vm.page_default_runtime.context as *const _; + let context_ptr = &vm.current_main_world().unwrap().context as *const _; vm.with_context_scope_by_ptr_and_checkpoint_for_test(context_ptr, |scope, _| { let global = scope.get_current_context().global(scope); for (name, alias) in [ diff --git a/moli-renderer-v8/src/script_vm/window_realm_runtime.rs b/moli-renderer-v8/src/script_vm/window_realm_runtime.rs index 8bafda1f7b..cd2453684f 100644 --- a/moli-renderer-v8/src/script_vm/window_realm_runtime.rs +++ b/moli-renderer-v8/src/script_vm/window_realm_runtime.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, ops::Deref, rc::Weak}; +use std::{cell::RefCell, mem::ManuallyDrop, ops::Deref, rc::Weak}; use crate::native_bridge::{JsContextHost, JsContextHostBridgeRef, RuntimeObservableContextToken}; @@ -8,7 +8,8 @@ use super::RendererDeferredContextHostReleaseQueue; /// with the Context through prebootstrap and publication. Retained JS objects /// own their native backing separately; they never own this runtime record. pub(crate) struct WindowRealmRuntime { - state: Option, + // Always live until Drop moves it into the deferred-close queue. + state: ManuallyDrop, deferred_releases: RendererDeferredContextHostReleaseQueue, closed: bool, } @@ -31,20 +32,6 @@ impl std::fmt::Debug for WindowRealmRuntimeState { } impl WindowRealmRuntime { - pub(super) fn uninitialized( - deferred_releases: RendererDeferredContextHostReleaseQueue, - ) -> Self { - Self { - state: None, - deferred_releases, - closed: false, - } - } - - pub(super) fn is_initialized(&self) -> bool { - self.state.is_some() - } - pub(super) fn new( scope: &mut v8::PinScope<'_, '_, ()>, context: v8::Local<'_, v8::Context>, @@ -54,7 +41,7 @@ impl WindowRealmRuntime { isolated: bool, ) -> Self { Self { - state: Some(WindowRealmRuntimeState { + state: ManuallyDrop::new(WindowRealmRuntimeState { context: v8::Global::new(scope, context), runtime_observable_context_token, bridge_ref: None, @@ -67,7 +54,7 @@ impl WindowRealmRuntime { } pub(super) fn install_bridge_ref(&mut self, bridge_ref: JsContextHostBridgeRef) { - let previous = self.state.as_mut().unwrap().bridge_ref.replace(bridge_ref); + let previous = self.state.bridge_ref.replace(bridge_ref); assert!( previous.is_none(), "a Window realm has one native bridge owner" @@ -79,9 +66,11 @@ impl WindowRealmRuntime { } pub(super) fn release_bridge_ref(&mut self) { - if let Some(state) = self.state.as_mut() { - state.bridge_ref.take(); - } + self.state.bridge_ref.take(); + } + + pub(super) fn context_ptr(&self) -> *const v8::Global { + &self.context } } @@ -89,19 +78,19 @@ impl Deref for WindowRealmRuntime { type Target = WindowRealmRuntimeState; fn deref(&self) -> &Self::Target { - self.state - .as_ref() - .expect("a live runtime owns its Context") + &self.state } } impl Drop for WindowRealmRuntime { fn drop(&mut self) { - if !self.closed && self.state.is_some() { + // SAFETY: new() initializes state, no method moves it, and Drop takes + // it exactly once. The ManuallyDrop field never drops the moved state. + let mut state = unsafe { ManuallyDrop::take(&mut self.state) }; + if !self.closed { // A cache replacement or bootstrap error can occur during a V8 // callback with the host already borrowed. Drop only queues the // record; cleanup runs after the outer entered-isolate operation. - let mut state = self.state.take().unwrap(); // Before publication this bridge token is the only native owner // installed in the Context. Do not defer it past recovery of the // bootstrap's DocumentRuntime, which the host only borrows. diff --git a/moli-renderer-v8/src/script_vm/worker_host_bridge_body.rs b/moli-renderer-v8/src/script_vm/worker_host_bridge_body.rs index b48640be2b..94357b89ea 100644 --- a/moli-renderer-v8/src/script_vm/worker_host_bridge_body.rs +++ b/moli-renderer-v8/src/script_vm/worker_host_bridge_body.rs @@ -323,11 +323,12 @@ impl ScriptVm { stack: message.stack, }, ); - } else { + } else if let Some(runtime) = self.current_main_world() { + let context_token = runtime.runtime_observable_context_token; self.runtime_observable_source_queue .record_pending_console_event( crate::native_bridge::PendingRuntimeObservableConsoleSourceEvent::new( - self.page_default_runtime.runtime_observable_context_token, + context_token, message.message, message.args, message.stack,