diff --git a/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py b/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py
index 3e1c35794f..13dd2180cb 100644
--- a/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py
+++ b/moli-cdp-smoke/moli_cdp_smoke/groups/lazy_main_world.py
@@ -91,6 +91,13 @@ async def run_lazy_main_world_group(endpoint: str, _fixture: str,
if "Native content" not in outer:
raise SmokeError(f"native DOM query returned {outer!r}")
if activation == "native-io":
+ await wire.call("Emulation.setTouchEmulationEnabled", {
+ "enabled": True, "maxTouchPoints": 7,
+ }, session)
+ await wire.call("Emulation.setEmulatedMedia", {
+ "media": "print",
+ "features": [{"name": "prefers-color-scheme", "value": "dark"}],
+ }, session)
await wire.call("Performance.enable", session=session)
metrics = {m["name"]: m["value"] for m in
(await wire.call("Performance.getMetrics", session=session))["metrics"]}
@@ -120,6 +127,10 @@ async def run_lazy_main_world_group(endpoint: str, _fixture: str,
assert_equal(observed, {"title": "Lazy activation", "state": "complete",
"text": "Native content", "color": "rgb(0, 0, 255)"},
"first main-world evaluation sees completed native Document")
+ if activation == "native-io":
+ assert_equal(await wire.evaluate(session, "({touch:navigator.maxTouchPoints, print:matchMedia('print').matches, dark:matchMedia('(prefers-color-scheme: dark)').matches})"),
+ {"touch": 7, "print": True, "dark": True},
+ "native emulation survives the first main-world activation")
if activation in ("preload", "deferred-script", "inline-load"):
assert_equal(await wire.evaluate(session, "activationCount"), 1,
"initial script runs exactly once")
diff --git a/moli-renderer-v8/src/runtime/page_commands.rs b/moli-renderer-v8/src/runtime/page_commands.rs
index 6d56f07379..8cbd3c7b0b 100644
--- a/moli-renderer-v8/src/runtime/page_commands.rs
+++ b/moli-renderer-v8/src/runtime/page_commands.rs
@@ -337,11 +337,11 @@ impl PageVm {
Ok(RendererPageReply::Unit)
}
RendererPageCommand::DefaultExecutionContextId => {
- self.vm_mut().initialize_main_world_if_needed()?;
+ self.vm_mut().ensure_main_world()?;
Ok(RendererPageReply::OptionalExecutionContextId(self.default_execution_context_id()))
}
RendererPageCommand::DefaultOrInitialExecutionContextId => {
- self.vm_mut().initialize_main_world_if_needed()?;
+ self.vm_mut().ensure_main_world()?;
Ok(RendererPageReply::OptionalExecutionContextId(self.default_or_initial_execution_context_id()))
}
RendererPageCommand::HasIsolatedWorldNamed { name, frame_id } => {
@@ -370,7 +370,7 @@ impl PageVm {
self.isolated_execution_context_id_for_inspector_context(execution_context_id),
)),
RendererPageCommand::RuntimeRealmInventory => {
- self.vm_mut().initialize_main_world_if_needed()?;
+ self.vm_mut().ensure_main_world()?;
Ok(RendererPageReply::RuntimeRealmInventory(self.runtime_realm_inventory()))
}
RendererPageCommand::LiveChildDefaultRuntimeRealmInventory => {
diff --git a/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs b/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs
index b8d30b2817..00ff6d7007 100644
--- a/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs
+++ b/moli-renderer-v8/src/runtime/tests/extracted/lazy_main_world.rs
@@ -115,6 +115,58 @@ async fn evaluate_lazy_test(page: &RendererPageHandle, source: &str) -> serde_js
renderer_json_value(reply).expect("evaluation returns a JSON value")
}
+#[tokio::test(flavor = "multi_thread")]
+async fn native_environment_updates_survive_main_world_activation() {
+ let (_runtime, mut page) = lazy_test_page(
+ "
Native environment before script
",
+ Default::default(),
+ )
+ .await;
+ assert_main_world_count(&page, 0).await;
+ for command in [
+ RendererPageCommand::SetNavigatorOverrides(moli_page_types::NavigatorOverrides {
+ online: Some(false),
+ max_touch_points: 7,
+ ..Default::default()
+ }),
+ RendererPageCommand::SetIdleOverride(Some(moli_page_types::EmulatedIdleOverride {
+ is_user_active: false,
+ is_screen_unlocked: false,
+ })),
+ RendererPageCommand::SetEmulatedMedia(moli_page_types::EmulatedMediaOverrides {
+ media: Some("print".to_owned()),
+ color_scheme: Some("dark".to_owned()),
+ ..Default::default()
+ }),
+ ] {
+ let (reply, _) = page.run_async_command(command).await.unwrap();
+ assert!(matches!(reply, RendererPageReply::Unit));
+ assert_main_world_count(&page, 0).await;
+ }
+ let expression = "JSON.stringify({online:navigator.onLine, touch:navigator.maxTouchPoints, print:matchMedia('print').matches, dark:matchMedia('(prefers-color-scheme: dark)').matches})";
+ assert_eq!(
+ evaluate_lazy_test(&page, expression).await,
+ r#"{"online":false,"touch":7,"print":true,"dark":true}"#
+ );
+ assert_main_world_count(&page, 1).await;
+
+ page.run_async_command(RendererPageCommand::SetNavigatorOverrides(
+ moli_page_types::NavigatorOverrides {
+ online: Some(true),
+ max_touch_points: 2,
+ ..Default::default()
+ },
+ ))
+ .await
+ .unwrap();
+ assert_eq!(
+ evaluate_lazy_test(&page, expression).await,
+ r#"{"online":true,"touch":2,"print":true,"dark":true}"#
+ );
+ assert_main_world_count(&page, 1).await;
+ page.close_async().await.unwrap();
+}
+
#[tokio::test(flavor = "multi_thread")]
async fn cold_runtime_binding_install_initializes_main_world() {
for execution_context_id in [None, Some(0)] {
diff --git a/moli-renderer-v8/src/script_vm.rs b/moli-renderer-v8/src/script_vm.rs
index 32d7767d7d..e8a3042949 100644
--- a/moli-renderer-v8/src/script_vm.rs
+++ b/moli-renderer-v8/src/script_vm.rs
@@ -751,6 +751,7 @@ mod input_dispatch;
mod input_helpers;
mod inspector;
mod main_world;
+use main_world::MainWorld;
mod native_drag_data;
pub(crate) use inspector::{dispatch_inspector_io_owner_wake, dispatch_inspector_main_owner_wake};
mod document_context_transition;
@@ -1015,15 +1016,12 @@ pub(super) struct ScriptVm {
page_inspector: DocumentInspectorBinding,
renderer_document_isolate_teardown: RendererDocumentIsolateTeardown,
renderer_page_script_environment: Option,
- page_default_runtime: WindowRealmRuntime,
- // Release the sizeable History/navigator seeds after the realm is initialized.
- pending_main_world: Option>,
+ main_world: MainWorld,
page_isolated_world_contexts: PageIsolatedWorldRegistry,
child_frame_realm_store: child_frame_realm::ChildFrameRealmStore,
prebootstrapped_child_default_contexts: SharedPrebootstrappedChildDefaultContexts,
child_document_modulator_store: ChildDocumentModulatorStore,
root_frame_id: Option,
- baseline_globals: ScriptGlobalsBaseline,
// `JsContextHost` stores a non-owning pointer into `document_runtime`, so it
// must be dropped before the runtime field during normal Rust field teardown.
_context_host: Rc>,
@@ -1236,9 +1234,10 @@ impl ScriptVm {
let mut context_ptrs: Vec<*const v8::Global> = Vec::with_capacity(
1 + self.page_isolated_world_contexts.len() + self.child_frame_realm_store.len(),
);
- if self.main_world_is_initialized() {
- context_ptrs.push(&self.page_default_runtime.context as *const _);
- }
+ context_ptrs.extend(
+ self.current_main_world()
+ .map(WindowRealmRuntime::context_ptr),
+ );
context_ptrs.extend(
self.page_isolated_world_contexts
.contexts()
@@ -1277,9 +1276,10 @@ impl ScriptVm {
let mut context_ptrs: Vec<*const v8::Global> = Vec::with_capacity(
1 + self.page_isolated_world_contexts.len() + self.child_frame_realm_store.len(),
);
- if self.main_world_is_initialized() {
- context_ptrs.push(&self.page_default_runtime.context as *const _);
- }
+ context_ptrs.extend(
+ self.current_main_world()
+ .map(WindowRealmRuntime::context_ptr),
+ );
context_ptrs.extend(
self.page_isolated_world_contexts
.contexts()
@@ -1363,8 +1363,7 @@ impl ScriptVm {
inspector_session_id: Option<&str>,
object_id: &str,
) -> Result