diff --git a/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_identity.rs b/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_identity.rs
index 1a80a468f5..41c358225f 100644
--- a/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_identity.rs
+++ b/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_identity.rs
@@ -844,6 +844,34 @@ async fn explicit_browser_and_noopener_blank_documents_keep_their_committed_hist
abort_test_cdp_server(server).await;
}
+#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+async fn auxiliary_initial_domain_updates_follow_the_creator() {
+ let (fixture_addr, _fixture) = spawn_dedicated_fixture_server(
+ Router::new().fallback(get(|| async {
+ axum::response::Html("
domain
")
+ })),
+ "auxiliary-domain",
+ );
+ let (addr, server) = spawn_test_protocol_server().await;
+ let (mut browser, _) =
+ connect_async(format!("ws://{addr}/devtools/browser/{DEFAULT_BROWSER_ID}"))
+ .await
+ .unwrap();
+ let opener_id = create_dynamic_target(&mut browser, 1).await;
+ let mut opener = connect_dynamic_page(addr, &opener_id).await;
+ send_cdp_command(&mut opener, 1, "Page.enable", None, json!({})).await;
+ navigate_dynamic_page_and_wait_for_load(
+ &mut opener,
+ 2,
+ &format!("http://{fixture_addr}/parent"),
+ )
+ .await;
+ let (_, _child) = open_auxiliary(addr, &mut opener, "").await;
+ assert_eq!(evaluate_window_name_probe(&mut opener,3,
+ "document.domain='127.0.0.1';[p.document.domain,p.document.body.ownerDocument===p.document]").await,json!(["127.0.0.1",true]));
+ abort_test_cdp_server(server).await;
+}
+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn auxiliary_blank_reload_preserves_origin_storage_and_fallback_base() {
let (fixture_addr, _fixture) = spawn_dedicated_fixture_server(
diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/window.rs b/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/window.rs
index fc6c99f6e3..79f966e160 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/window.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/child_frame_runtime/window.rs
@@ -660,7 +660,11 @@ fn contexts_can_script_access<'s>(
});
};
if accessing_host_ptr != accessed_host_ptr {
- return false;
+ return host.window_execution_context_can_access_other_host(
+ accessing_identity,
+ unsafe { &*accessed_host_ptr },
+ accessed_identity,
+ );
}
host.window_execution_context_can_access(accessing_identity, accessed_identity)
diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_frames.rs b/moli-renderer-v8/src/native_bridge/context_host/child_frames.rs
index 0a7fa822ea..102ac09f7f 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/child_frames.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/child_frames.rs
@@ -19,7 +19,7 @@ use moli_page_types::{
apply_child_browsing_context_navigation_to_entry_seed as apply_child_navigation_to_seed,
replace_child_browsing_context_navigation_in_entry_seed as replace_child_navigation_in_seed,
};
-use std::collections::HashSet;
+use std::{cell::RefCell, collections::HashSet, rc::Rc};
use url::Url;
mod classic_scripts;
@@ -58,7 +58,7 @@ pub(super) struct ChildBrowsingContextEntry {
performance_time_origin: ChildPerformanceTimeOrigin,
pending_document_load_id: Option,
classic_script_document_state: classic_scripts::ChildClassicScriptDocumentState,
- document_domain_override: Option,
+ document_domain_override: Rc>>,
credentialless: bool,
service_worker_client_id: Option,
pending_service_worker_client_id: Option,
@@ -117,15 +117,19 @@ impl ChildBrowsingContextEntry {
}
pub(super) fn document_domain_override(&self) -> Option {
+ self.document_domain_override.borrow().clone()
+ }
+
+ pub(super) fn shared_document_domain_override(&self) -> Rc>> {
self.document_domain_override.clone()
}
pub(super) fn set_document_domain_override(&mut self, domain: String) {
- self.document_domain_override = Some(domain);
+ *self.document_domain_override.borrow_mut() = Some(domain);
}
pub(super) fn clear_document_domain_override(&mut self) {
- self.document_domain_override = None;
+ self.document_domain_override = Rc::new(RefCell::new(None));
}
pub(super) fn window_name(&self) -> &str {
diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_frames/registry.rs b/moli-renderer-v8/src/native_bridge/context_host/child_frames/registry.rs
index 328fb3fc5e..23b1bc606d 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/child_frames/registry.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/child_frames/registry.rs
@@ -502,7 +502,8 @@ impl JsContextHost {
// Document keeps its mutable origin until replacement commits.
document_domain_override: existing
.as_ref()
- .and_then(|entry| entry.document_domain_override()),
+ .map(|entry| entry.document_domain_override.clone())
+ .unwrap_or_default(),
credentialless,
service_worker_client_id: existing
.as_ref()
diff --git a/moli-renderer-v8/src/native_bridge/context_host/core.rs b/moli-renderer-v8/src/native_bridge/context_host/core.rs
index 751b2d3912..bdfba522a7 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/core.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/core.rs
@@ -47,9 +47,11 @@ impl JsContextHost {
&self,
origin: &str,
inherited_opaque_origin_matches: bool,
+ inherited_document_domain: Option<&str>,
policy: &crate::document_runtime::DocumentPolicyContainer,
) -> crate::frame_owner_model::FrameDocumentLocalWindowTransition {
- let same_origin = self.document_domain_override.is_none()
+ let same_origin = self.document_domain_override.borrow().as_deref()
+ == inherited_document_domain
&& (inherited_opaque_origin_matches
|| moli_url::WebOrigin::from_serialized(&self.main_document_security_origin())
.same_origin(&moli_url::WebOrigin::from_serialized(origin)));
@@ -389,7 +391,7 @@ impl JsContextHost {
#[cfg(test)]
force_child_default_context_preflight_failure: false,
child_browsing_context_document_handles: HashMap::new(),
- document_domain_override: None,
+ document_domain_override: Rc::new(RefCell::new(None)),
next_child_browsing_context_id: 1,
next_child_document_load_id: 0,
next_child_classic_script_load_id: 0,
diff --git a/moli-renderer-v8/src/native_bridge/context_host/document_domain.rs b/moli-renderer-v8/src/native_bridge/context_host/document_domain.rs
index f0db959a29..197db536b2 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/document_domain.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/document_domain.rs
@@ -4,6 +4,22 @@ use std::net::IpAddr;
use url::{Host, Url};
impl JsContextHost {
+ pub(crate) fn inherit_document_domain_override(&mut self, domain: Rc>>) {
+ self.document_domain_override = domain;
+ }
+
+ pub(crate) fn inherited_document_domain(
+ &self,
+ child: Option,
+ ) -> Rc>> {
+ match child.and_then(|handle| self.child_browsing_context_security_origin_owner(handle)) {
+ Some(ChildSecurityOriginOwner::Child(owner)) => {
+ self.child_browsing_contexts[&owner].shared_document_domain_override()
+ }
+ Some(ChildSecurityOriginOwner::Main) | None => self.document_domain_override.clone(),
+ }
+ }
+
pub(crate) fn document_domain_value_for_document_handle(
&self,
document_handle: DomHandle,
@@ -11,6 +27,7 @@ impl JsContextHost {
if document_handle == self.document_handle() {
return self
.document_domain_override
+ .borrow()
.clone()
.unwrap_or_else(|| self.main_document_domain_host().unwrap_or_default());
}
@@ -48,7 +65,7 @@ impl JsContextHost {
if !document_domain_is_allowed_for_host(¤t_host, &domain) {
return false;
}
- self.document_domain_override = Some(domain);
+ *self.document_domain_override.borrow_mut() = Some(domain);
return true;
}
let Some(child_handle) =
@@ -68,7 +85,7 @@ impl JsContextHost {
handle: DomHandle,
) -> Option {
match self.child_browsing_context_security_origin_owner(handle)? {
- ChildSecurityOriginOwner::Main => self.document_domain_override.clone(),
+ ChildSecurityOriginOwner::Main => self.document_domain_override.borrow().clone(),
ChildSecurityOriginOwner::Child(owner) => self
.child_browsing_contexts
.get(&owner)
@@ -115,7 +132,9 @@ impl JsContextHost {
return false;
};
match origin_owner {
- ChildSecurityOriginOwner::Main => self.document_domain_override = Some(domain),
+ ChildSecurityOriginOwner::Main => {
+ *self.document_domain_override.borrow_mut() = Some(domain);
+ }
ChildSecurityOriginOwner::Child(owner) => {
let Some(entry) = self.child_browsing_contexts.get_mut(&owner) else {
return false;
diff --git a/moli-renderer-v8/src/native_bridge/context_host/mod.rs b/moli-renderer-v8/src/native_bridge/context_host/mod.rs
index 732c2f80de..0e81db50d0 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/mod.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/mod.rs
@@ -867,7 +867,7 @@ pub(crate) struct JsContextHost {
#[cfg(test)]
force_child_default_context_preflight_failure: bool,
child_browsing_context_document_handles: HashMap,
- document_domain_override: Option,
+ document_domain_override: Rc>>,
next_child_browsing_context_id: u64,
next_child_document_load_id: u64,
next_child_classic_script_load_id: u64,
diff --git a/moli-renderer-v8/src/native_bridge/context_host/popups/renderer_owned.rs b/moli-renderer-v8/src/native_bridge/context_host/popups/renderer_owned.rs
index 5bdc403798..38c0128aee 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/popups/renderer_owned.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/popups/renderer_owned.rs
@@ -271,6 +271,12 @@ impl JsContextHost {
};
let (pending, window_proxy) =
allocator.stage_in_scope(scope, &environment, &self.bridge.bindings, init)?;
+ if !policy.sandbox.forces_opaque_origin {
+ let target = crate::util::context_host_ptr_from_window_object(scope, window_proxy)
+ .expect("a related Window retains its native host");
+ unsafe { &mut *target }.document_domain_override =
+ self.inherited_document_domain(creator_child_handle);
+ }
Ok(OpenedRendererWindow {
window_proxy,
window: Some(window),
diff --git a/moli-renderer-v8/src/native_bridge/context_host/window_execution_context/settings.rs b/moli-renderer-v8/src/native_bridge/context_host/window_execution_context/settings.rs
index ba2280913f..2f641b81d8 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/window_execution_context/settings.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/window_execution_context/settings.rs
@@ -105,7 +105,7 @@ impl WindowEnvironmentSettings {
} = &mut origin
{
if self.domain_owner_document == host.document_handle() {
- *document_domain = host.document_domain_override.clone();
+ *document_domain = host.document_domain_override.borrow().clone();
} else if let Some(handle) =
host.child_browsing_context_handle_for_stored_document(self.domain_owner_document)
{
diff --git a/moli-renderer-v8/src/native_bridge/context_host/window_security_tokens.rs b/moli-renderer-v8/src/native_bridge/context_host/window_security_tokens.rs
index ed88d682c3..f57634414f 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/window_security_tokens.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/window_security_tokens.rs
@@ -18,7 +18,7 @@ impl JsContextHost {
return None;
}
let origin = self.main_document_security_origin();
- if self.document_domain_override.is_some() {
+ if self.document_domain_override.borrow().is_some() {
return None;
}
window_security_token_key(origin)
@@ -47,7 +47,7 @@ impl JsContextHost {
}
window_isolated_world_security_token_key(
self.main_document_security_origin(),
- self.document_domain_override.is_some(),
+ self.document_domain_override.borrow().is_some(),
)
}
@@ -107,6 +107,39 @@ impl JsContextHost {
}
}
self.refresh_child_window_access_surfaces_after_origin_mutation(scope);
+ if let Some(environment) = self.page_script_environment() {
+ for related in environment.related_document_hosts() {
+ let related_ptr = related.as_ptr();
+ if std::ptr::eq(related_ptr, self) {
+ continue;
+ }
+ let related_host = unsafe { &*related_ptr };
+ if !std::rc::Rc::ptr_eq(
+ &self.document_domain_override,
+ &related_host.document_domain_override,
+ ) {
+ continue;
+ }
+ let dispatch_scope = OwnerDispatchScope::Top;
+ let Some(owner) =
+ related_host.current_window_execution_context_owner(dispatch_scope)
+ else {
+ continue;
+ };
+ if let Some((_, context)) =
+ related_host.window_execution_context(scope, owner, dispatch_scope)
+ && set_window_security_token(
+ scope,
+ context,
+ related_host
+ .main_default_world_security_token_key()
+ .as_deref(),
+ )
+ {
+ updated += 1;
+ }
+ }
+ }
updated
}
@@ -179,6 +212,39 @@ impl JsContextHost {
)
}
+ pub(crate) fn window_execution_context_can_access_other_host(
+ &self,
+ accessing: WindowExecutionContextIdentity,
+ target_host: &JsContextHost,
+ accessed: WindowExecutionContextIdentity,
+ ) -> bool {
+ if !self.window_execution_context_identity_is_current(accessing)
+ || !target_host.window_execution_context_identity_is_current(accessed)
+ {
+ return false;
+ }
+ if accessing.grants_universal_access() {
+ return true;
+ }
+ let Some(origin) = self.window_access_origin(accessing) else {
+ return false;
+ };
+ let Some(target) =
+ target_host.window_access_origin_for_dispatch_scope(accessed.dispatch_scope())
+ else {
+ return false;
+ };
+ // Opaque owner identifiers are local to each host. Shared opaque
+ // origins are authorized by their V8 security token before this path.
+ matches!(
+ (&origin, &target),
+ (
+ WindowAccessOrigin::Tuple { .. },
+ WindowAccessOrigin::Tuple { .. }
+ )
+ ) && origin.can_access(&target)
+ }
+
/// Checks Window access before the target realm is entered or materialized.
///
/// WebIDL operations such as a borrowed `fetch()` must authorize the
@@ -262,7 +328,7 @@ impl JsContextHost {
}
WindowAccessOrigin::from_serialized_origin(
serialized_origin,
- self.document_domain_override.clone(),
+ self.document_domain_override.borrow().clone(),
)
}
diff --git a/moli-renderer-v8/src/script_vm.rs b/moli-renderer-v8/src/script_vm.rs
index 129f5796a4..0ef7a5b5a9 100644
--- a/moli-renderer-v8/src/script_vm.rs
+++ b/moli-renderer-v8/src/script_vm.rs
@@ -1953,6 +1953,7 @@ impl ScriptVm {
pub(crate) struct ScriptVmInitialDocumentEnvironment {
security_token: Option>,
+ document_domain_override: Rc>>,
origin: String,
pub(crate) policy_container: crate::document_runtime::DocumentPolicyContainer,
fallback_base_url: Option,
@@ -2052,6 +2053,9 @@ impl ScriptVmPageRealmBootstrap {
let (page_context_cancel_tx, page_context_cancel_rx) =
renderer_page_context_cancel_channel();
+ let inherited_document_domain = initial_environment
+ .as_ref()
+ .map(|environment| environment.document_domain_override.clone());
let inherited_security_token =
initial_environment.and_then(|environment| environment.security_token);
let RendererDocumentIsolateBootstrap {
@@ -2106,6 +2110,12 @@ impl ScriptVmPageRealmBootstrap {
top_level_storage_key,
reserved_service_worker_client_id,
)));
+ if let Some(domain) = inherited_document_domain {
+ context_host
+ .borrow_mut()
+ .inherit_document_domain_override(domain);
+ }
+
context_host
.borrow_mut()
.bind_deferred_context_host_release_queue(
diff --git a/moli-renderer-v8/src/script_vm/document_environment.rs b/moli-renderer-v8/src/script_vm/document_environment.rs
index 3dabdf28ee..9b82d25189 100644
--- a/moli-renderer-v8/src/script_vm/document_environment.rs
+++ b/moli-renderer-v8/src/script_vm/document_environment.rs
@@ -71,8 +71,23 @@ impl ScriptVmInitialDocumentEnvironment {
.ok_or_else(|| anyhow!("navigation initiator has no live creation context"))?;
let security_token = (!policy_container.sandbox.forces_opaque_origin)
.then(|| v8::Global::new(scope, context.get_security_token(scope)));
+ let document_domain_override = (!policy_container.sandbox.forces_opaque_origin)
+ .then(|| crate::util::context_host_ptr_from_context_slot(context))
+ .flatten()
+ .map(|host_ptr| {
+ let host = unsafe { &*host_ptr };
+ let child = host
+ .window_execution_context_identity_for_access_check(context)
+ .and_then(|identity| match identity.dispatch_scope() {
+ crate::native_bridge::OwnerDispatchScope::Child(handle) => Some(handle),
+ _ => None,
+ });
+ host.inherited_document_domain(child)
+ })
+ .unwrap_or_default();
Ok(Self {
security_token,
+ document_domain_override,
origin,
policy_container,
fallback_base_url: Some(base_url),
@@ -435,6 +450,7 @@ impl ScriptVm {
let token = scope.get_current_context().get_security_token(scope);
Ok(ScriptVmInitialDocumentEnvironment {
security_token: Some(v8::Global::new(scope, token)),
+ document_domain_override: host.inherited_document_domain(None),
origin,
policy_container,
fallback_base_url,
diff --git a/moli-renderer-v8/src/script_vm/document_isolate.rs b/moli-renderer-v8/src/script_vm/document_isolate.rs
index 27e360b77e..060879e278 100644
--- a/moli-renderer-v8/src/script_vm/document_isolate.rs
+++ b/moli-renderer-v8/src/script_vm/document_isolate.rs
@@ -495,6 +495,17 @@ impl RendererPageScriptEnvironment {
.find_map(|inner| matches(&inner).map(|()| Self { inner }))
}
+ pub(crate) fn related_document_hosts(&self) -> Vec>> {
+ self.inner
+ .renderer_document_isolate
+ .related_pages
+ .borrow()
+ .values()
+ .filter_map(Weak::upgrade)
+ .filter_map(|page| page.document_host.borrow().upgrade())
+ .collect()
+ }
+
pub(crate) fn window_identity(
&self,
) -> Option<(
diff --git a/moli-renderer-v8/src/script_vm/initial_window_document.rs b/moli-renderer-v8/src/script_vm/initial_window_document.rs
index 48a587456b..bc7fdcc2f3 100644
--- a/moli-renderer-v8/src/script_vm/initial_window_document.rs
+++ b/moli-renderer-v8/src/script_vm/initial_window_document.rs
@@ -25,12 +25,17 @@ impl ScriptVm {
} else {
false
};
+ let inherited_domain =
+ inherited.map(|environment| environment.document_domain_override.borrow());
Ok(self
._context_host
.borrow()
.main_document_local_window_transition_for_commit(
origin,
inherited_opaque_origin_matches,
+ inherited_domain
+ .as_ref()
+ .and_then(|domain| domain.as_deref()),
policy,
))
}
@@ -111,6 +116,11 @@ impl ScriptVm {
vm.page_runtime_wake_tx = page_task_tx.page_runtime_wake_sender();
{
let mut host = vm._context_host.borrow_mut();
+ if let Some(environment) = inherited.as_ref() {
+ host.inherit_document_domain_override(
+ environment.document_domain_override.clone(),
+ );
+ }
host.rebind_initial_main_document_senders(
resource_completion,
page_task_tx.top_level_navigation_handoff_sender(),
diff --git a/moli-renderer-v8/src/script_vm/related_page.rs b/moli-renderer-v8/src/script_vm/related_page.rs
index 6321e33719..ddae6de294 100644
--- a/moli-renderer-v8/src/script_vm/related_page.rs
+++ b/moli-renderer-v8/src/script_vm/related_page.rs
@@ -38,6 +38,7 @@ impl ScriptVmDefaultWorldBootstrap {
None,
Some(ScriptVmInitialDocumentEnvironment {
security_token: None,
+ document_domain_override: Rc::default(),
fallback_base_url: None,
storage_key: None,
origin: inherited_origin.clone(),