fix(fetch): integrate shared redirect and response metadata

Validate Window and Worker requests with the original client origin and
complete ResponseHead URL list. Preserve response-filter ownership, remove
duplicate origin helpers, and include Script requests in per-hop checks.

Retain first-hop memory caching while isolating transport redirect handling
and client origins. Keep network ExtraInfo off synthetic redirects, and
adapt response-document tests to process ICU defaults.

Validation: cargo fmt --all; cargo clippy --workspace --all-targets
--all-features -- -D warnings; cargo nextest run --no-fail-fast
(18,276 passed, 13 skipped). The four targeted cache, redirect and Service
Worker regressions also pass.
This commit is contained in:
ldm0
2026-09-13 17:18:27 +08:00
parent acb5055617
commit 43eeefb50e
16 changed files with 233 additions and 155 deletions
+4 -6
View File
@@ -1061,20 +1061,18 @@ async fn service_worker_redirect_preserves_worker_response_filter() -> Result<()
}})()"#, cross = servers.cross_origin), true,
).await?;
let results: serde_json::Value = serde_json::from_str(result["value"].as_str().unwrap())?;
// A synthesized Response has an empty URL list and remains basic after
// the intercepted redirect, including its ordinary response headers.
for kind in ["basic", "basic-buffered", "cors", "default"] {
assert_eq!(results[kind]["body"], SCRIPT, "{kind}: {results}");
assert_eq!(
results[kind]["type"],
if kind.starts_with("basic") {
"basic"
} else {
"cors"
},
if kind != "cors" { "basic" } else { "cors" },
"{kind}: {results}"
);
assert_eq!(
results[kind]["privateHeader"],
if kind.starts_with("basic") {
if kind != "cors" {
serde_json::json!("yes")
} else {
serde_json::Value::Null
+2 -4
View File
@@ -355,8 +355,7 @@ fn append_browser_subresource_headers(
request.request_mode.as_ref().to_owned(),
);
append_header_if_missing(outgoing, "Sec-Fetch-Dest", "script".to_owned());
if let Some(origin) = request_origin_header_value(request, request_url, redirect_chain)
{
if let Some(origin) = request_origin_header_value(request, request_url) {
append_header_if_missing(outgoing, "Origin", origin);
}
append_browser_client_hints(outgoing, config);
@@ -1308,8 +1307,7 @@ mod tests {
.with_request_origin(moli_url::WebOrigin::Opaque)
.with_browser_request_metadata(BrowserRequestMetadata::Fetch);
let headers =
outgoing_request_headers_for_url(&config, &request, &request_url, &Vec::new(), None);
let headers = outgoing_request_headers_for_url(&config, &request, &request_url, None);
assert_eq!(header_value(&headers, "origin").as_deref(), Some("null"));
assert_eq!(
@@ -62,6 +62,7 @@ pub(in crate::network) struct RawSubresourceCacheKey {
page_cache_partition_id: u64,
url: String,
resource_type: &'static str,
follow_redirects: bool,
credentials_mode: String,
request_origin: Option<String>,
request_mode: String,
@@ -635,6 +636,7 @@ pub(in crate::network) fn raw_subresource_memory_cache_key(
page_cache_partition_id: 0,
url: request.url.as_str().to_owned(),
resource_type: raw_subresource_cache_resource_type_key(request.resource_type),
follow_redirects: request.follow_redirects,
credentials_mode: request.credentials_mode.as_ref().to_owned(),
request_origin: request
.request_origin()
@@ -668,7 +670,10 @@ fn raw_subresource_request_is_memory_cacheable(request: &Request) -> bool {
&& request.method.eq_ignore_ascii_case("GET")
&& request.body.is_none()
&& request.auth().is_none()
&& request.follow_redirects
// The browser's CORS loop follows redirects itself. Its individual
// transport requests can still reuse cached responses with an empty
// URL list, in a separate bucket from automatically followed fetches.
&& request.redirect_mode == moli_fetch::RequestRedirectMode::Follow
&& raw_subresource_memory_cacheable_headers(request)
}
@@ -870,6 +875,45 @@ mod tests {
load
}
#[test]
fn per_hop_cache_keeps_transport_redirects_and_client_origins_separate() {
let automatic = Request::get("https://cache.test/resource")
.unwrap()
.with_resource_type(RequestResourceType::Raw)
.with_browser_request_metadata(BrowserRequestMetadata::Fetch)
.with_initiator_url(&Url::parse("https://cache.test/page").unwrap());
let per_hop = automatic.clone().with_follow_redirects(false);
let key = raw_subresource_memory_cache_key(&per_hop).expect("cacheable first CORS hop");
let mut cache = SharedMemoryResourceCache::with_limits(usize::MAX, usize::MAX);
cache.insert_raw_subresource(
key.clone(),
raw_response(per_hop.url.as_str(), b"cached"),
u64::MAX,
);
assert!(cache.lookup_raw_subresource(&key).is_some());
for isolated in [
automatic,
per_hop
.clone()
.with_request_origin(moli_url::WebOrigin::Opaque),
] {
let isolated_key = raw_subresource_memory_cache_key(&isolated).unwrap();
assert!(
cache.lookup_raw_subresource(&isolated_key).is_none(),
"a cached hop cannot replace an automatic redirect or another client's response"
);
}
for mode in [
moli_fetch::RequestRedirectMode::Manual,
moli_fetch::RequestRedirectMode::Error,
] {
assert!(
raw_subresource_memory_cache_key(&per_hop.clone().with_redirect_mode(mode))
.is_none()
);
}
}
#[test]
fn cancelling_one_shared_script_consumer_preserves_its_sibling() {
let load = ScriptTextLoad::pending();
@@ -99,6 +99,7 @@ impl RendererNetworkResourceLoadResponse {
moli_fetch::ResponseHead {
final_url: self.final_url.clone(),
status: self.status,
status_text: None,
headers: self.headers.clone(),
request_cookie_report: self.request_cookie_report.clone(),
cookie_set_reports: self.cookie_set_reports.clone(),
+11 -29
View File
@@ -152,37 +152,19 @@ pub(crate) use self::response::{
build_fetch_response_object_from_subresource_body_for_request_mode_with_filter,
build_filtered_cached_response_object,
build_navigation_preload_response_object_from_stream_for_request_mode,
cors_preflight_request_headers,
cors_preflight_request_headers_for_origin,
cors_request_origin_after_redirects,
fetch_response_needs_orb_body_validation,
filter_cors_exposed_response_headers,
filter_cors_exposed_response_headers_for_origin,
is_cors_policy_failure_message,
materialize_response_object_body,
materialize_response_object_body_with_chunk_callback,
materialized_body_bytes_from_value,
network_response_filter,
response_constructor_callback,
validate_cors_preflight_response,
validate_cors_preflight_response_for_origin,
validate_cors_response_chain,
validate_cors_response_chain_for_origin,
response_has_null_body,
materialize_response_object_internal_head,
set_filtered_response_internal_head,
materialize_cache_response_object_head,
validate_cors_response_for_origin,
validate_cross_origin_embedder_and_document_isolation_policy,
validate_cross_origin_resource_policy,
validate_fetch_response_headers_for_origin,
validate_fetch_response_security_policy,
validate_fetch_response_security_policy_for_origin,
cors_preflight_request_headers, cors_preflight_request_headers_for_origin,
fetch_response_needs_orb_body_validation, filter_cors_exposed_response_headers_for_origin,
is_cors_policy_failure_message, materialize_cache_response_object_head,
materialize_response_object_body, materialize_response_object_body_with_chunk_callback,
materialize_response_object_internal_head, materialized_body_bytes_from_value,
network_response_filter, response_constructor_callback, response_has_null_body,
set_filtered_response_internal_head, validate_cors_preflight_response,
validate_cors_response_chain, validate_cross_origin_embedder_and_document_isolation_policy,
validate_cross_origin_resource_policy, validate_fetch_response_headers_for_origin,
validate_fetch_response_security_policy, validate_fetch_response_security_policy_for_origin,
validate_fetch_response_security_policy_with_body,
validate_fetch_response_security_policy_with_body_classified_for_origin,
validate_fetch_response_security_policy_with_body_for_origin,
validate_opaque_response_blocking_with_body,
validated_opaque_response_body,
validate_fetch_response_security_policy_with_body_for_origin, validated_opaque_response_body,
};
#[cfg(test)]
pub(crate) use self::response::{materialize_response_object, materialize_response_object_head};
@@ -128,6 +128,7 @@ fn browser_request_needs_cors_redirect_checks(request: &Request) -> bool {
| BrowserRequestMetadata::EventSource
| BrowserRequestMetadata::JsonModule
| BrowserRequestMetadata::Manifest
| BrowserRequestMetadata::Script
| BrowserRequestMetadata::StyleModule
| BrowserRequestMetadata::Xhr,
)
@@ -198,7 +199,9 @@ impl ManualCorsRedirectState {
) -> Result<ManualCorsRedirectTransition, String> {
// Redirect request records describe the next hop's cookie decision,
// just as they do when the transport follows redirects internally.
if let Some(previous) = self.request.redirect_chain_mut().last_mut() {
if let Some(previous) = self.request.redirect_chain_mut().last_mut()
&& previous.source == moli_fetch::RedirectSource::Network
{
previous.request_cookie_report = head.request_cookie_report.clone();
previous.request_extra_info = request_extra_info.cloned();
}
@@ -236,7 +239,8 @@ impl ManualCorsRedirectState {
.password()
.is_some_and(|password| !password.is_empty()))
&& self.request.request_origin().is_some_and(|origin| {
self.request.has_cross_origin_url(&head.final_url) || !origin.same_origin_url(&next_url)
self.request.has_cross_origin_url(&head.final_url)
|| !origin.same_origin_url(&next_url)
})
{
return Err("CORS redirect URL must not include credentials".to_owned());
+8 -23
View File
@@ -18,30 +18,16 @@ pub(in crate::network_host) use self::bindings::{ParsedResponseInit, parse_respo
pub(crate) use self::bindings::{build_error_response_object, response_constructor_callback};
pub(super) use self::body_methods::install_response_body_methods;
pub(crate) use self::cors::{
FetchResponseSecurityViolation,
cors_preflight_request_headers,
cors_preflight_request_headers_for_origin,
cors_request_origin_after_redirects,
fetch_response_needs_orb_body_validation,
filter_cors_exposed_response_headers,
filter_cors_exposed_response_headers_for_origin,
is_cors_policy_failure_message,
validate_cors_preflight_response,
validate_cors_preflight_response_for_origin,
validate_cors_response_chain,
validate_cors_response_chain_for_origin,
validate_cors_response_for_origin,
validate_cors_response_headers,
FetchResponseSecurityViolation, cors_preflight_request_headers,
cors_preflight_request_headers_for_origin, fetch_response_needs_orb_body_validation,
filter_cors_exposed_response_headers_for_origin, is_cors_policy_failure_message,
validate_cors_preflight_response, validate_cors_response_chain, validate_cors_response_headers,
validate_cross_origin_embedder_and_document_isolation_policy,
validate_cross_origin_resource_policy,
validate_fetch_response_headers_for_origin,
validate_fetch_response_security_policy,
validate_fetch_response_security_policy_for_origin,
validate_cross_origin_resource_policy, validate_fetch_response_headers_for_origin,
validate_fetch_response_security_policy, validate_fetch_response_security_policy_for_origin,
validate_fetch_response_security_policy_with_body,
validate_fetch_response_security_policy_with_body_classified_for_origin,
validate_fetch_response_security_policy_with_body_for_origin,
validate_opaque_response_blocking_with_body,
validated_opaque_response_body,
validate_fetch_response_security_policy_with_body_for_origin, validated_opaque_response_body,
};
pub(crate) use self::materialize::{
FetchResponseRequest, MaterializedResponseBody, MaterializedResponseHead,
@@ -54,8 +40,7 @@ pub(crate) use self::materialize::{
materialize_cache_response_object_head, materialize_response_object_body,
materialize_response_object_body_with_chunk_callback,
materialize_response_object_internal_head, materialized_body_bytes_from_value,
set_filtered_response_internal_head,
network_response_filter,
network_response_filter, set_filtered_response_internal_head,
};
#[cfg(test)]
pub(crate) use self::materialize::{materialize_response_object, materialize_response_object_head};
@@ -38,20 +38,6 @@ pub(crate) fn is_cors_policy_failure_message(message: &str) -> bool {
message.contains("CORS check failed:") || message.contains("CORS preflight failed:")
}
pub(crate) fn cors_request_origin_after_redirects<'a>(
request_origin: &WebOrigin,
redirects: impl IntoIterator<Item = (&'a url::Url, &'a url::Url)>,
) -> WebOrigin {
if redirects
.into_iter()
.any(|(from, to)| !same_origin(from, to) && !request_origin.same_origin_url(from))
{
WebOrigin::Opaque
} else {
request_origin.clone()
}
}
/// Validates an already fetched network response and its network redirects.
/// Service worker and browser-generated redirects still contribute to taint,
/// but their synthetic response headers are not subject to the CORS check.
@@ -95,7 +81,8 @@ pub(crate) fn validate_cors_response_chain_for_origin(
validate_cors_response_chain(request_origin, head, credentials_mode)
}
pub(crate) fn validate_cors_response_for_origin(
#[cfg(test)]
fn validate_cors_response_for_origin(
request_origin: &WebOrigin,
response_url: &url::Url,
response_headers: &[(String, String)],
@@ -106,7 +93,11 @@ pub(crate) fn validate_cors_response_for_origin(
{
return Ok(());
}
validate_cors_response_headers(request_origin.ascii_serialization(), response_headers, credentials_mode)
validate_cors_response_headers(
request_origin.ascii_serialization(),
response_headers,
credentials_mode,
)
}
pub(crate) fn validate_fetch_response_security_policy(
@@ -271,8 +262,15 @@ pub(crate) fn validate_fetch_response_security_policy_with_body_for_origin(
policy_context: crate::types::SubresourcePolicyContext,
) -> Result<(), String> {
validate_fetch_response_security_policy_with_body_classified_for_origin(
document_url, request_origin, head, response_body, request_mode, credentials_mode, policy_context,
).map_err(FetchResponseSecurityViolation::into_message)
document_url,
request_origin,
head,
response_body,
request_mode,
credentials_mode,
policy_context,
)
.map_err(FetchResponseSecurityViolation::into_message)
}
pub(crate) fn validate_fetch_response_security_policy_with_body_classified_for_origin(
@@ -518,7 +516,13 @@ pub(crate) fn cors_preflight_request_headers_for_origin(
request_headers: &[(String, String)],
use_cors_preflight: bool,
) -> Option<Vec<(String, String)>> {
cors_preflight_request_headers(!request_origin.same_origin_url(request_url), request_url, method, request_headers, use_cors_preflight)
cors_preflight_request_headers(
!request_origin.same_origin_url(request_url),
request_url,
method,
request_headers,
use_cors_preflight,
)
}
pub(crate) fn validate_cors_preflight_response(
@@ -595,7 +599,8 @@ pub(crate) fn validate_cors_preflight_response(
Ok(())
}
pub(crate) fn validate_cors_preflight_response_for_origin(
#[cfg(test)]
fn validate_cors_preflight_response_for_origin(
request_origin: &WebOrigin,
credentials_mode: RequestCredentialsMode,
requested_method: &str,
@@ -604,7 +609,15 @@ pub(crate) fn validate_cors_preflight_response_for_origin(
response_headers: &[(String, String)],
use_cors_preflight: bool,
) -> Result<(), String> {
validate_cors_preflight_response(request_origin.ascii_serialization(), credentials_mode, requested_method, request_headers, response_status, response_headers, use_cors_preflight)
validate_cors_preflight_response(
request_origin.ascii_serialization(),
credentials_mode,
requested_method,
request_headers,
response_status,
response_headers,
use_cors_preflight,
)
}
pub(crate) fn filter_cors_exposed_response_headers_for_origin(
@@ -721,6 +734,7 @@ mod tests {
moli_fetch::ResponseHead {
final_url,
status: 200,
status_text: None,
headers,
request_cookie_report: None,
cookie_set_reports: Vec::new(),
@@ -737,6 +751,7 @@ mod tests {
let mut head = moli_fetch::ResponseHead {
final_url: url("https://final.test/script.js"),
status: 200,
status_text: None,
headers: vec![("Access-Control-Allow-Origin".to_owned(), "*".to_owned())],
request_cookie_report: None,
cookie_set_reports: Vec::new(),
@@ -864,12 +879,24 @@ mod tests {
RequestCredentialsMode::Include,
] {
assert_eq!(
validate_cors_response_for_origin(&WebOrigin::from_url(&document_url), &response_url, &headers, mode).is_ok(),
validate_cors_response_for_origin(
&WebOrigin::from_url(&document_url),
&response_url,
&headers,
mode
)
.is_ok(),
mode != RequestCredentialsMode::Include || values == ["true"],
"mode={mode:?}, values={values:?}"
);
assert!(
validate_cors_response_for_origin(&WebOrigin::from_url(&document_url), &document_url, &headers, mode).is_ok(),
validate_cors_response_for_origin(
&WebOrigin::from_url(&document_url),
&document_url,
&headers,
mode
)
.is_ok(),
"same-origin responses do not require CORS permission"
);
}
@@ -881,36 +908,75 @@ mod tests {
let home = url("https://page.test/a");
let away = url("https://script.test/b");
let elsewhere = url("https://other.test/c");
let origin = WebOrigin::from_url(&home);
assert_eq!(
cors_request_origin_after_redirects(&origin, [(&home, &away)]),
origin
);
for redirects in [
vec![(&away, &elsewhere)],
vec![(&home, &away), (&away, &home)],
] {
let tainted = cors_request_origin_after_redirects(&origin, redirects);
assert!(tainted.is_opaque());
assert!(
validate_cors_response_for_origin(
&tainted,
&home,
&[("access-control-allow-origin".into(), "null".into())],
RequestCredentialsMode::SameOrigin,
)
.is_ok()
);
assert!(
validate_cors_response_for_origin(
&tainted,
&home,
&[],
RequestCredentialsMode::SameOrigin,
)
.is_err(),
"returning to the initial origin must not bypass CORS"
);
for origin in [WebOrigin::from_url(&home), WebOrigin::Opaque] {
for source in [
RedirectSource::Network,
RedirectSource::ServiceWorker,
RedirectSource::Internal,
] {
for (urls, tuple_origin) in [
(vec![&home, &away], "https://page.test"),
(vec![&away, &elsewhere], "null"),
(vec![&home, &away, &home], "null"),
] {
let expected_origin = if origin.is_opaque() {
"null"
} else {
tuple_origin
};
let mut head = header_response(
(*urls.last().unwrap()).clone(),
vec![("Access-Control-Allow-Origin".into(), expected_origin.into())],
);
head.redirect_chain = urls
.windows(2)
.map(|pair| moli_fetch::RedirectInfo {
source,
from_url: pair[0].clone(),
to_url: pair[1].clone(),
status: 302,
// The response to each hop is checked before its redirect
// changes the serialized origin for the following request.
headers: if source == RedirectSource::Network {
vec![(
"Access-Control-Allow-Origin".into(),
origin.ascii_serialization().into(),
)]
} else {
Vec::new()
},
network_extra_info_available: false,
request_extra_info: None,
response_extra_info: None,
redirect_has_extra_info: false,
request_cookie_report: None,
cookie_set_reports: Vec::new(),
from_cache: false,
negotiated_http_version: None,
})
.collect();
assert_eq!(
head.url_list().serialized_origin_for_origin(&origin),
expected_origin
);
assert!(head.url_list().has_cross_origin_url_for_origin(&origin));
validate_cors_response_chain_for_origin(
&origin,
&head,
RequestCredentialsMode::SameOrigin,
)
.expect("authorize network hops using their individual request origins");
head.headers.clear();
validate_cors_response_chain_for_origin(
&origin,
&head,
RequestCredentialsMode::SameOrigin,
)
.expect_err(
"returning to the client origin cannot bypass final CORS validation",
);
}
}
}
}
@@ -927,8 +993,13 @@ mod tests {
("X-Other".to_owned(), "ok".to_owned()),
];
let preflight =
cors_preflight_request_headers(true, &url("http://other.test/data"), "PUT", &headers, false);
let preflight = cors_preflight_request_headers(
true,
&url("http://other.test/data"),
"PUT",
&headers,
false,
);
assert_eq!(
preflight,
@@ -954,7 +1025,13 @@ mod tests {
];
assert_eq!(
cors_preflight_request_headers(true, &url("http://other.test/data"), "POST", &headers, false),
cors_preflight_request_headers(
true,
&url("http://other.test/data"),
"POST",
&headers,
false
),
None
);
}
+7 -3
View File
@@ -416,7 +416,7 @@ pub(crate) async fn load_service_worker_aware_external_script_source_outcome(
ClassicScriptResponseProvenance::Network
} else {
ClassicScriptResponseProvenance::ServiceWorker {
filter: response.response_filter,
filter: response.response_filter.clone(),
}
};
external_script_source_load_outcome_from_response_inner(
@@ -586,8 +586,12 @@ pub(crate) fn external_script_source_load_outcome_from_response(
) -> PreparedScriptSourceLoadOutcome {
let request_mode = external_script_request_mode(script.kind, &script.fetch_metadata);
let head = response.head();
let response_filter =
crate::network_host::network_response_filter(&script.initiator_url, &head, request_mode, moli_fetch::RequestRedirectMode::Follow);
let response_filter = crate::network_host::network_response_filter(
&script.initiator_url,
&head,
request_mode,
moli_fetch::RequestRedirectMode::Follow,
);
let cors_error = if request_mode == RequestMode::Cors {
crate::network_host::validate_cors_response_chain(
&script.initiator_url,
@@ -3721,8 +3721,9 @@ impl ScriptVm {
trace_fields,
record_started,
);
let request_origin = pending.request_origin();
let mut observable_response = response;
if pending.info.resource_type == SubresourceResourceType::Xhr && !response_filter.is_some_and(|filter| filter.is_readable()) {
if pending.info.resource_type == SubresourceResourceType::Xhr && !response_filter.as_ref().is_some_and(|filter| filter.is_readable()) {
observable_response.headers =
crate::network_host::filter_cors_exposed_response_headers(
&pending.request_origin,
@@ -3747,7 +3748,7 @@ impl ScriptVm {
};
let response_filter = response_filter.or_else(|| Some(response_request.network_response_filter(
&pending.info.document_url,
&pending.request_origin(),
&request_origin,
&head,
pending.credentials_mode,
)));
@@ -4881,7 +4882,7 @@ impl ScriptVm {
}
let mut observable_head = started.head.clone();
if pending.info.resource_type == SubresourceResourceType::Xhr && !started.response_filter.is_some_and(|filter| filter.is_readable()) {
if pending.info.resource_type == SubresourceResourceType::Xhr && !started.response_filter.as_ref().is_some_and(|filter| filter.is_readable()) {
observable_head.headers = crate::network_host::filter_cors_exposed_response_headers_for_origin(
&pending.request_origin(),
&observable_head,
@@ -5852,9 +5853,7 @@ impl ScriptVm {
trace_fields,
record_started,
);
let request_origin = streaming
.pending
.response_request_origin(streaming.head.redirect_chain.iter().map(|redirect| (&redirect.from_url, &redirect.to_url)));
let request_origin = streaming.pending.request_origin();
if let PendingSubresourceContinuation::Xhr { xhr, .. } =
streaming.pending.continuation
&& let Some(response_body) = xhr_delivery_body
@@ -1284,6 +1284,7 @@ fn xml_http_request_default_response_type_parses_response_xml_for_document_mime(
#[test]
fn xml_http_request_response_document_uses_response_url_and_requester_origin() {
let environment = moli_v8_platform::ProcessEnvironmentOwner::default();
for child_realm in [false, true] {
for (mime, response_type) in [
("application/xml", ""),
@@ -1294,8 +1295,7 @@ fn xml_http_request_response_document_uses_response_url_and_requester_origin() {
"https://requester.example/page/index.html",
"<!doctype html><html><body></body></html>",
);
vm.set_timezone_override_and_sync_surface(Some("UTC"))
.unwrap();
environment.set_timezone(Some("UTC")).unwrap();
vm.eval(&format!(
r#"(() => {{
const frame = document.createElement('iframe');
@@ -1512,6 +1512,7 @@ fn xml_http_request_document_response_requires_an_eligible_mime_and_well_formed_
#[test]
fn xhr_streamed_response_documents_keep_distinct_source_modification_times() {
let environment = moli_v8_platform::ProcessEnvironmentOwner::default();
for (mime, response_type) in [
("application/xml", ""),
("application/xml", "document"),
@@ -1520,8 +1521,7 @@ fn xhr_streamed_response_documents_keep_distinct_source_modification_times() {
let mut vm = new_storage_test_vm("https://xhr-document-modified.test/");
vm.document_runtime
.set_document_source_last_modified(Some(5_025_000.0));
vm.set_timezone_override_and_sync_surface(Some("UTC"))
.unwrap();
environment.set_timezone(Some("UTC")).unwrap();
vm.set_fetch_subresource_interception(
true,
Some(crate::types::SubresourceResourceType::Xhr),
@@ -1633,8 +1633,7 @@ fn xhr_streamed_response_documents_keep_distinct_source_modification_times() {
.unwrap(),
r#"["11/06/1994 08:49:37","01/01/1970 00:00:00","01/01/1970 01:23:45",true]"#
);
vm.set_timezone_override_and_sync_surface(Some("Asia/Shanghai"))
.unwrap();
environment.set_timezone(Some("Asia/Shanghai")).unwrap();
assert_eq!(
vm.eval("__responseDocuments[0].lastModified").unwrap(),
"11/06/1994 16:49:37"
-11
View File
@@ -561,17 +561,6 @@ impl PendingSubresourceFetchState {
self.request_origin.clone()
}
pub(super) fn response_request_origin<'a>(
&self,
redirects: impl IntoIterator<Item = (&'a Url, &'a Url)>,
) -> moli_url::WebOrigin {
let origin = self.request_origin();
if self.request_mode != moli_fetch::RequestMode::Cors {
return origin;
}
crate::network_host::cors_request_origin_after_redirects(&origin, redirects)
}
pub(super) fn detach_keepalive_window_fetch(&mut self) -> bool {
let PendingSubresourceExecutionContext::WindowFetch(context) = &self.execution_context
else {
@@ -240,8 +240,7 @@ pub(super) fn fetch_worker_import_source_blocking(
validate_fetch_response_security_policy_for_origin(
initiator_url,
&WebOrigin::from_url(initiator_url),
&response.final_url,
&response.headers,
&response.head(),
RequestMode::NoCors,
RequestCredentialsMode::SameOrigin,
policy_context,
@@ -72,8 +72,7 @@ use crate::network_host::{
XHR_SEND_FLAG_SLOT, XHR_TIMEOUT_SLOT, XHR_TIMEOUT_START_MS_SLOT, XHR_TIMEOUT_TIMER_SLOT,
XHR_URL_SLOT, XHR_WITH_CREDENTIALS_SLOT, append_default_body_content_type, apply_xhr_failure,
apply_xhr_response, apply_xhr_response_body_source, apply_xhr_timeout, apply_xhr_upload_event,
capture_xhr_upload_listener_flag, close_pending_network_body_stream,
cors_request_origin_after_redirects, dispatch_xhr_loadstart,
capture_xhr_upload_listener_flag, close_pending_network_body_stream, dispatch_xhr_loadstart,
enqueue_pending_network_body_chunk, error_pending_network_body_stream_with_reason,
extract_subresource_auth_challenge,
fetch_browser_subresource_raw_stream_with_preflight_headers_and_network_metadata,
@@ -82,8 +81,7 @@ use crate::network_host::{
is_cors_policy_failure_message, parse_fetch_init, prepare_xhr_send_body_from_args,
request_input_snapshot, request_object_credentials_mode, resolve_context_url,
set_xhr_state_bool, set_xhr_state_number, throw_synchronous_xhr_failure,
validate_cors_response_for_origin, validate_fetch_response_headers_for_origin,
validate_fetch_response_security_policy_for_origin,
validate_fetch_response_headers_for_origin, validate_fetch_response_security_policy_for_origin,
validate_fetch_response_security_policy_with_body_classified_for_origin,
xhr_author_request_headers, xhr_ensure_send_allowed, xhr_state_bool_property,
xhr_state_number_property, xhr_state_string_property,
@@ -718,6 +718,7 @@ fn send_synchronous_worker_xhr(
throw_synchronous_xhr_failure(scope, xhr, &request_url_text, "NetworkError");
return;
}
let request_origin = WebOrigin::from_url(&prepared.document_url);
record_worker_subresource_success(
&state.borrow(),
prepared.document_url,
@@ -731,8 +732,7 @@ fn send_synchronous_worker_xhr(
);
response.headers = filter_cors_exposed_response_headers_for_origin(
&request_origin,
&response.final_url,
&response.headers,
&response.head(),
prepared.credentials_mode,
);
apply_xhr_response(scope, xhr, response);
@@ -672,6 +672,7 @@ fn start_worker_module_graph_fetch(
.with_credentials_mode(request.credentials_mode());
let requested_url = request.url().clone();
let request_initiator_url = request.initiator_url().clone();
let request_credentials_mode = request.credentials_mode();
let requested_module_type = request.module_type().map(str::to_owned);
let requested_kind = request.kind();
let response_content_security_policies = content_security_policies.clone();