mirror of
https://github.com/lexmount/moli.git
synced 2026-11-04 16:06:44 +00:00
fix(refresh): use child document as navigation referrer
This commit is contained in:
@@ -157,6 +157,7 @@ pub(crate) use location_navigation::{
|
||||
dispatch_top_level_navigation_event_with_source_element, is_native_location,
|
||||
meta_refresh_navigation_kind, navigate_borrowed_location,
|
||||
navigate_location_object_with_child_navigate_event,
|
||||
navigate_location_object_with_child_navigate_event_and_initiator_url,
|
||||
navigate_location_object_with_source_element, navigate_top_level_meta_refresh,
|
||||
navigate_top_level_same_document_from_browser, resolve_cross_window_location_target,
|
||||
sandbox_blocks_ancestor_or_top_navigation_from_source,
|
||||
|
||||
@@ -77,6 +77,13 @@ pub(super) enum NavigationNavigateHistoryKind {
|
||||
Replace,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LocationNavigationOptions {
|
||||
dispatch_child_navigate_event_for_all_kinds: bool,
|
||||
force_exact_same_document_navigation: bool,
|
||||
explicit_initiator_url: Option<url::Url>,
|
||||
}
|
||||
|
||||
pub(crate) fn navigate_location_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
location: v8::Local<'s, v8::Object>,
|
||||
@@ -84,7 +91,12 @@ pub(crate) fn navigate_location_object<'s>(
|
||||
raw_target: Option<String>,
|
||||
) {
|
||||
navigate_location_object_with_source_element_and_child_navigate_event(
|
||||
scope, location, kind, raw_target, None, false, false,
|
||||
scope,
|
||||
location,
|
||||
kind,
|
||||
raw_target,
|
||||
None,
|
||||
LocationNavigationOptions::default(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,8 +143,10 @@ pub(crate) fn navigate_top_level_same_document_from_browser(
|
||||
kind,
|
||||
Some(target),
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
LocationNavigationOptions {
|
||||
force_exact_same_document_navigation: true,
|
||||
..LocationNavigationOptions::default()
|
||||
},
|
||||
);
|
||||
true
|
||||
}
|
||||
@@ -186,8 +200,10 @@ pub(crate) fn navigate_top_level_meta_refresh(
|
||||
kind,
|
||||
Some(target.to_string()),
|
||||
None,
|
||||
false,
|
||||
same_document_fragment,
|
||||
LocationNavigationOptions {
|
||||
force_exact_same_document_navigation: same_document_fragment,
|
||||
..LocationNavigationOptions::default()
|
||||
},
|
||||
);
|
||||
context_host_ptr_from_global_bridge(scope)
|
||||
.is_some_and(|host_ptr| unsafe { &*host_ptr }.has_pending_location_navigation())
|
||||
@@ -208,8 +224,7 @@ pub(crate) fn navigate_location_object_with_source_element<'s>(
|
||||
kind,
|
||||
raw_target,
|
||||
source_element,
|
||||
false,
|
||||
false,
|
||||
LocationNavigationOptions::default(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -220,7 +235,36 @@ pub(crate) fn navigate_location_object_with_child_navigate_event<'s>(
|
||||
raw_target: Option<String>,
|
||||
) {
|
||||
navigate_location_object_with_source_element_and_child_navigate_event(
|
||||
scope, location, kind, raw_target, None, true, false,
|
||||
scope,
|
||||
location,
|
||||
kind,
|
||||
raw_target,
|
||||
None,
|
||||
LocationNavigationOptions {
|
||||
dispatch_child_navigate_event_for_all_kinds: true,
|
||||
..LocationNavigationOptions::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn navigate_location_object_with_child_navigate_event_and_initiator_url<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
location: v8::Local<'s, v8::Object>,
|
||||
kind: LocationNavigationKind,
|
||||
raw_target: Option<String>,
|
||||
initiator_url: url::Url,
|
||||
) {
|
||||
navigate_location_object_with_source_element_and_child_navigate_event(
|
||||
scope,
|
||||
location,
|
||||
kind,
|
||||
raw_target,
|
||||
None,
|
||||
LocationNavigationOptions {
|
||||
dispatch_child_navigate_event_for_all_kinds: true,
|
||||
explicit_initiator_url: Some(initiator_url),
|
||||
..LocationNavigationOptions::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -230,8 +274,7 @@ fn navigate_location_object_with_source_element_and_child_navigate_event<'s>(
|
||||
kind: LocationNavigationKind,
|
||||
raw_target: Option<String>,
|
||||
source_element: Option<v8::Local<'s, v8::Object>>,
|
||||
dispatch_child_navigate_event_for_all_kinds: bool,
|
||||
force_exact_same_document_navigation: bool,
|
||||
options: LocationNavigationOptions,
|
||||
) {
|
||||
let owner = runtime_window_owner(scope, location);
|
||||
if let Some(context) = location.get_creation_context(scope)
|
||||
@@ -246,6 +289,11 @@ fn navigate_location_object_with_source_element_and_child_navigate_event<'s>(
|
||||
}
|
||||
}
|
||||
let location = window_location_for_holder(scope, owner).unwrap_or(location);
|
||||
let LocationNavigationOptions {
|
||||
dispatch_child_navigate_event_for_all_kinds,
|
||||
force_exact_same_document_navigation,
|
||||
explicit_initiator_url,
|
||||
} = options;
|
||||
let current_href = location_href_slot(scope, location).unwrap_or_default();
|
||||
let current_url = url::Url::parse(¤t_href).ok();
|
||||
let raw_target_is_fragment_only = raw_target
|
||||
@@ -619,7 +667,8 @@ fn navigate_location_object_with_source_element_and_child_navigate_event<'s>(
|
||||
}
|
||||
if let Some(host_ptr) = host_ptr {
|
||||
let host = unsafe { &mut *host_ptr };
|
||||
let initiator_url = location_navigation_initiator_url(scope, host);
|
||||
let initiator_url =
|
||||
explicit_initiator_url.or_else(|| location_navigation_initiator_url(scope, host));
|
||||
if matches!(kind, LocationNavigationKind::Reload) {
|
||||
host.queue_child_browsing_context_reload_from_existing_seed(
|
||||
handle,
|
||||
|
||||
@@ -2,7 +2,8 @@ use super::{FrameDocumentLoadDeliveryTask, JsContextHost};
|
||||
use crate::{
|
||||
context_bootstrap::{
|
||||
LocationNavigationKind, construct_original_event, construct_original_page_transition_event,
|
||||
meta_refresh_navigation_kind, navigate_location_object_with_child_navigate_event,
|
||||
meta_refresh_navigation_kind,
|
||||
navigate_location_object_with_child_navigate_event_and_initiator_url,
|
||||
record_performance_load_event_end_for_window,
|
||||
record_performance_load_event_start_for_window,
|
||||
},
|
||||
@@ -794,6 +795,9 @@ fn child_meta_refresh_callback<'s>(
|
||||
if host.current_child_document_task_owner(handle) != Some(task.owner) {
|
||||
return;
|
||||
}
|
||||
let Some(initiator_url) = host.child_browsing_context_current_url(handle) else {
|
||||
return;
|
||||
};
|
||||
let Some(window) = host.child_browsing_context_window_wrapper(scope, handle) else {
|
||||
return;
|
||||
};
|
||||
@@ -803,11 +807,12 @@ fn child_meta_refresh_callback<'s>(
|
||||
else {
|
||||
return;
|
||||
};
|
||||
navigate_location_object_with_child_navigate_event(
|
||||
navigate_location_object_with_child_navigate_event_and_initiator_url(
|
||||
scope,
|
||||
location,
|
||||
task.navigation_kind,
|
||||
Some(task.target_url.to_string()),
|
||||
initiator_url,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+73
@@ -3697,3 +3697,76 @@ async fn parent_location_assignment_uses_parent_document_as_navigation_referrer(
|
||||
assert_eq!(requests[1].header_value("referer"), Some(top_url.as_str()));
|
||||
assert_eq!(document_referrer, top_url.as_str());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn child_meta_refresh_after_same_document_navigation_uses_child_referrer() {
|
||||
const HOST: &str = "child-meta-refresh-referrer.test";
|
||||
|
||||
let server = StaticHttpServer::spawn_with_bodies(vec![
|
||||
r#"<!doctype html>
|
||||
<script>location.hash = '#section'</script>
|
||||
<meta http-equiv="refresh" content="0; url=/refresh/target.html">"#
|
||||
.to_owned(),
|
||||
"<!doctype html><body>refresh target</body>".to_owned(),
|
||||
])
|
||||
.await;
|
||||
let top_url = server.url_for_host(HOST, "/refresh/parent.html");
|
||||
let child_url = server.url_for_host(HOST, "/refresh/source.html");
|
||||
let target_url = server.url_for_host(HOST, "/refresh/target.html");
|
||||
let loader = static_http_loader([server.resolve_entry(HOST)]);
|
||||
let mut vm = new_storage_page_task_executor_test_vm_with_loader(top_url.as_str(), &loader);
|
||||
|
||||
vm.eval(&format!(
|
||||
r#"
|
||||
(() => {{
|
||||
globalThis.__childMetaRefreshReferrerLoadCount = 0;
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = {};
|
||||
frame.onload = () => {{
|
||||
globalThis.__childMetaRefreshReferrerLoadCount++;
|
||||
}};
|
||||
(document.body || document.documentElement || document).appendChild(frame);
|
||||
}})()
|
||||
"#,
|
||||
serde_json::to_string(child_url.as_str()).expect("serialize meta refresh child URL")
|
||||
))
|
||||
.expect("child meta refresh referrer setup should evaluate");
|
||||
advance_page_task_executor_until_eval_equals(
|
||||
&mut vm,
|
||||
&loader,
|
||||
"String(__childMetaRefreshReferrerLoadCount)",
|
||||
"1",
|
||||
"initial meta refresh child document should load",
|
||||
)
|
||||
.await;
|
||||
|
||||
advance_page_task_executor_until_eval_equals(
|
||||
&mut vm,
|
||||
&loader,
|
||||
"String(__childMetaRefreshReferrerLoadCount)",
|
||||
"2",
|
||||
"child meta refresh navigation should load",
|
||||
)
|
||||
.await;
|
||||
|
||||
let state = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const frame = document.querySelector('iframe');
|
||||
return [frame.contentWindow.location.href, frame.contentDocument.referrer].join('|');
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("child meta refresh referrer state should evaluate");
|
||||
assert_eq!(state, format!("{target_url}|{child_url}"));
|
||||
|
||||
let requests = server.finish().await;
|
||||
assert_eq!(requests[0].target, "/refresh/source.html");
|
||||
assert_eq!(requests[0].header_value("referer"), Some(top_url.as_str()));
|
||||
assert_eq!(requests[1].target, "/refresh/target.html");
|
||||
assert_eq!(
|
||||
requests[1].header_value("referer"),
|
||||
Some(child_url.as_str())
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user