refactor(renderer): model pending and ready main worlds explicitly

This commit is contained in:
ldm0
2026-11-06 00:05:30 +08:00
committed by Donough Liu
parent 736dbc385b
commit cc23c0fbd7
81 changed files with 844 additions and 855 deletions
@@ -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")
@@ -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 => {
@@ -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(
"<!doctype html><p>Native environment before script</p>",
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)] {
+80 -79
View File
@@ -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<RendererPageScriptEnvironment>,
page_default_runtime: WindowRealmRuntime,
// Release the sizeable History/navigator seeds after the realm is initialized.
pending_main_world: Option<Box<main_world::PendingMainWorld>>,
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<String>,
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<RefCell<JsContextHost>>,
@@ -1236,9 +1234,10 @@ impl ScriptVm {
let mut context_ptrs: Vec<*const v8::Global<v8::Context>> = 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<v8::Context>> = 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<Option<DomHandle>> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> = &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<v8::Context> = &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<crate::runtime::RendererRuntimeCommandOutputRecorder>,
internal_dispatch_call_id: Option<i32>,
) -> Result<Vec<RendererRuntimeInspectorMessage>> {
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<v8::Context>,
target: PageInspectorSessionTarget<'_>,
raw_json: &str,
deferred_response: Option<RendererRuntimeInspectorResponseSender>,
@@ -1814,7 +1814,6 @@ impl ScriptVm {
internal_dispatch_call_id: Option<i32>,
) -> Result<Vec<RendererRuntimeInspectorMessage>> {
let timing_started = moli_trace::cdp_nav_timing_enabled().then(Instant::now);
let context_ptr: *const v8::Global<v8::Context> = &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<ScriptVm, ScriptVmBootstrapError> {
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<v8::Context> = &self.page_default_runtime.context;
};
let context_ptr = runtime.context_ptr();
let context = self
.renderer_document_isolate
.with_entered_renderer_document_isolate(|isolate| {
@@ -10,8 +10,7 @@ impl ScriptVm {
inspector_session_id: Option<&str>,
object_id: &str,
) -> Result<String> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> = &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(
@@ -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<DomHandle>,
) -> Result<Option<DomHandle>> {
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<v8::Context> = &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<v8::Context>> {
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()
@@ -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<crate::native_bridge::NavigationHistoryEntrySeed>,
) {
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<Option<crate::native_bridge::NavigationHistoryEntrySeed>> {
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| {
@@ -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()
@@ -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<v8::Context> = &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
})
}
}
@@ -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<T>,
) -> Result<T> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> = &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<T>(
&mut self,
op: impl FnOnce(&mut v8::PinScope<'_, '_>, *mut JsContextHost) -> Result<T>,
) -> Result<Option<T>> {
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<T>(
&mut self,
op: impl FnOnce(
&mut v8::PinScope<'_, '_>,
*mut JsContextHost,
&mut DocumentRuntime,
) -> Result<T>,
) -> Result<T> {
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<T>(
&mut self,
op: impl FnOnce(
&mut v8::PinScope<'_, '_>,
*mut JsContextHost,
&mut DocumentRuntime,
) -> Result<T>,
) -> Result<Option<T>> {
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<T>(
&mut self,
context_ptr: *const v8::Global<v8::Context>,
op: impl FnOnce(
&mut v8::PinScope<'_, '_>,
*mut JsContextHost,
&mut DocumentRuntime,
) -> Result<T>,
) -> Result<T> {
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<T>(
@@ -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()
@@ -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<crate::protocol_types::EmulatedIdleOverride>,
) -> Result<()> {
self.set_idle_override(idle_override);
if !self.main_world_is_initialized() {
return Ok(());
}
let context_ptr: *const v8::Global<v8::Context> = &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<v8::Context> = &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)))
}
}
@@ -607,14 +607,12 @@ impl RendererPageScriptEnvironment {
}
pub(crate) struct ScriptVmDefaultWorldBootstrap {
pub(super) pending_main_world: Option<Box<super::main_world::PendingMainWorld>>,
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<RendererPageScriptEnvironment>,
pub(super) page_default_runtime: super::WindowRealmRuntime,
pub(super) baseline_globals: super::ScriptGlobalsBaseline,
pub(super) root_frame_id: Option<String>,
pub(super) prebootstrapped_child_default_contexts: SharedPrebootstrappedChildDefaultContexts,
pub(super) page_context_cancel_tx: RendererPageContextCancelSender,
@@ -35,8 +35,7 @@ impl ScriptVm {
depth: i32,
pierce: bool,
) -> Result<RendererDomDebuggerEventListenersResolution> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> = &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();
@@ -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()
+8 -17
View File
@@ -404,11 +404,10 @@ impl ScriptVm {
&mut self,
job: MainFrameScriptJob<'_>,
) -> PendingScriptTurn<RawScriptExecutionResult<()>> {
if let Err(error) = self.initialize_main_world_if_needed() {
return PendingScriptTurn::new(Err(error.into()));
}
let context_ptr: *const v8::Global<v8::Context> =
&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<String> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> =
&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<Option<String>> {
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<v8::Context> =
&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<String> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> =
&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<String> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> =
&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,
@@ -28,11 +28,8 @@ impl ScriptVm {
) -> Option<InspectorWindowDispatchScope> {
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<v8::Context>> {
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<String>,
child_handle: Option<DomHandle>,
) -> Result<i64> {
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<i64> {
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<i64> {
self.initialize_main_world_if_needed()?;
self.publish_prebootstrapped_isolated_window_contexts()?;
if let Some(execution_context_id) = self
.page_isolated_world_contexts
@@ -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()
@@ -8,11 +8,11 @@ impl ScriptVm {
inherited: Option<&ScriptVmInitialDocumentEnvironment>,
) -> Result<crate::frame_owner_model::FrameDocumentLocalWindowTransition> {
// 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));
+89 -29
View File
@@ -1,5 +1,64 @@
use super::*;
pub(super) enum MainWorld {
// The large startup seeds are released when the realm is materialized.
Pending(Box<PendingMainWorld>),
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"))
}
}
@@ -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,
@@ -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<v8::Context> {
&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<v8::Context>, 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<Option<v8::Global<v8::Promise>>, 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<NativeDynamicModuleEvaluation, 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,
@@ -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,
@@ -87,7 +87,7 @@ fn dynamic_import_reaction_parts_in_vm(
crate::module_runtime::DynamicModuleEvaluationTarget,
) {
let context_ptr: *const v8::Global<v8::Context> = 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()
@@ -25,7 +25,7 @@ fn reject_with_error(vm: &mut ScriptVm, error: &ModuleLoadError) -> v8::Global<v
.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);
v8::Local::new(scope, &resolver)
.get_promise(scope)
@@ -39,7 +39,7 @@ fn reject_with_error(vm: &mut ScriptVm, error: &ModuleLoadError) -> v8::Global<v
.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 promise = v8::Local::new(scope, &resolver).get_promise(scope);
assert_eq!(promise.state(), v8::PromiseState::Rejected);
@@ -31,7 +31,7 @@ impl NavigationHistoryRealm {
impl ScriptVm {
pub(crate) fn reset_navigation_history(&mut self) -> Result<bool> {
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<v8::Context>> {
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,
@@ -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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &self.page_default_runtime.context;
};
let context_host = self._context_host.clone();
let document_runtime = &mut self.document_runtime;
self.renderer_document_isolate
@@ -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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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<v8::Context> = &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) {
@@ -663,28 +663,18 @@ impl ScriptVm {
source: &str,
request: ContentSecurityPolicyScriptElementRequest<'_>,
) -> Option<String> {
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<v8::Context> = &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
})
}
}
+36 -26
View File
@@ -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<ScriptVmDefaultWorldBootstrap, ScriptVmBootstrapError> {
) -> 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<crate::context_bootstrap::WeakIndexedDbManager>,
storage: Option<crate::context_bootstrap::SharedStorageBucketStore>,
) {
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
@@ -88,7 +88,7 @@ impl ScriptVm {
result_mode: RuntimeEvaluateResultMode,
) -> Result<RuntimeEvaluateOutcome> {
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<v8::Context> =
&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<v8::Context> =
&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(
@@ -3,10 +3,15 @@ use super::*;
impl ScriptVm {
#[cfg(any(test, feature = "test-support"))]
pub(crate) fn snapshot_globals(&mut self) -> Result<Option<BTreeMap<String, JsValueSnapshot>>> {
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<Option<BTreeMap<String, JsValueSnapshot>>> {
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<PageRuntimeObservableContext> {
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::<Vec<_>>();
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<v8::Context>,
) -> Result<Vec<String>> {
// 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<v8::Context>,
) -> Result<Vec<Value>> {
// 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.
@@ -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<v8::Context> = &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<crate::types::ScriptErrorConstructorKind>,
) -> Result<()> {
self.initialize_main_world_if_needed()?;
let context_ptr: *const v8::Global<v8::Context> = &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::<v8::Object>::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::<v8::Object>::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)
})
}
}
+41 -55
View File
@@ -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(|_| ())
}
}
@@ -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)
}
@@ -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<v8::Context>;
let top_context_ptr =
&vm.current_main_world().unwrap().context as *const v8::Global<v8::Context>;
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));
@@ -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<v8::Context>;
let top_context_ptr =
&vm.current_main_world().unwrap().context as *const v8::Global<v8::Context>;
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));
@@ -21,7 +21,7 @@ fn insert_adjacent_element_preserves_registered_native_alias_identity() {
"<!doctype html><body><iframe id=child></iframe></body>",
);
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 [
@@ -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 = '<iframe id=child></iframe>'; 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");
@@ -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 = '<iframe></iframe>'; 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");
@@ -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");
@@ -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");
@@ -22,7 +22,7 @@ fn web_transport_error_getters_accept_native_proxies_and_reject_author_wrappers(
vm.eval(r#"document.body.innerHTML='<iframe></iframe>';
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));
@@ -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"] {
@@ -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");
@@ -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");
@@ -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);
@@ -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 = '<iframe id=child></iframe>'")
.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);
@@ -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<v8::Context>;
let context = &vm.current_main_world().unwrap().context as *const v8::Global<v8::Context>;
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 };
@@ -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")] {
@@ -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")] {
@@ -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='<iframe></iframe>'; 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");
@@ -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<v8::Context>;
let context = &vm.current_main_world().unwrap().context as *const v8::Global<v8::Context>;
vm.with_context_scope_by_ptr(context, |scope, _| {
crate::native_bridge::enter_active_child_window_scope(scope, Some(child));
Ok(())
@@ -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");
@@ -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<v8::Context>;
let top_context_ptr =
&vm.current_main_world().unwrap().context as *const v8::Global<v8::Context>;
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));
@@ -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")] {
@@ -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 [
@@ -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<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> =
&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));
@@ -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");
@@ -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/", "<!doctype html><p>runtime</p>");
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!(
@@ -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<v8::Context> = &vm.page_default_runtime.context;
let context_ptr: *const v8::Global<v8::Context> = &vm.current_main_world().unwrap().context;
vm.renderer_document_isolate
.clone()
.with_entered_renderer_document_isolate(|isolate| {
@@ -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 = '<iframe id=child></iframe>'")
.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
@@ -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)
@@ -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))
@@ -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");
@@ -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");
@@ -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));
@@ -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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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));
@@ -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");
@@ -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();
@@ -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");
@@ -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");
@@ -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
@@ -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);
@@ -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");
@@ -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 = '<iframe id=child></iframe>'")
.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);
@@ -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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> =
&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<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&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| {
@@ -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<v8::Context> = &vm.page_default_runtime.context as *const _;
let context_ptr: *const v8::Global<v8::Context> =
&vm.current_main_world().unwrap().context as *const _;
vm.renderer_document_isolate
.with_entered_renderer_document_isolate(move |isolate| {
@@ -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 [
@@ -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<WindowRealmRuntimeState>,
// Always live until Drop moves it into the deferred-close queue.
state: ManuallyDrop<WindowRealmRuntimeState>,
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<v8::Context> {
&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.
@@ -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,