mirror of
https://github.com/lexmount/moli.git
synced 2026-10-04 16:00:47 +00:00
@@ -118,6 +118,10 @@ impl Document {
|
||||
self.quirks_mode
|
||||
}
|
||||
|
||||
pub fn is_quirks_mode(&self) -> bool {
|
||||
self.quirks_mode == QuirksMode::Quirks
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> DocumentKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
@@ -3598,6 +3598,10 @@ impl StylesheetBlockingReadView for ParserStreamHtmlTreeSinkTarget {
|
||||
self.parser_owner_document_node_id()
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
matches!(self.state.html_quirks_mode, QuirksMode::Quirks)
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
target_node_id: Option<NodeId>,
|
||||
|
||||
@@ -43,6 +43,12 @@ impl StylesheetBlockingReadView for ParserStreamDocumentSnapshot {
|
||||
self.document_node_id()
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
self.node(self.document_node_id())
|
||||
.and_then(moli_dom::native::Node::as_document)
|
||||
.is_some_and(|document| document.is_quirks_mode())
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
target_node_id: Option<NodeId>,
|
||||
@@ -158,6 +164,34 @@ mod tests {
|
||||
disposition.url().as_str(),
|
||||
"https://example.com/assets/app.css"
|
||||
);
|
||||
assert!(!disposition.options().quirks_mode_mime_compatibility());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stylesheet_link_captures_quirks_mode_mime_compatibility() {
|
||||
let parser = HtmlParser::SCRIPTING_ENABLED;
|
||||
let document = parser.parse(
|
||||
url::Url::parse("https://example.com/page.html").unwrap(),
|
||||
"<html><head><link rel=stylesheet href=app.css></head></html>".to_owned(),
|
||||
);
|
||||
let link = document
|
||||
.document_head_handle()
|
||||
.and_then(|head| document.child_nodes(head))
|
||||
.and_then(|children| {
|
||||
children.into_iter().find(|handle| {
|
||||
document
|
||||
.node(*handle)
|
||||
.and_then(Node::as_element)
|
||||
.is_some_and(|element| element.is_html_element("link"))
|
||||
})
|
||||
})
|
||||
.expect("stylesheet link");
|
||||
|
||||
let disposition =
|
||||
stylesheet_link_disposition(&document, moli_dom::NodeId::new(link.index()))
|
||||
.expect("stylesheet disposition");
|
||||
|
||||
assert!(disposition.options().quirks_mode_mime_compatibility());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1287,6 +1287,12 @@ impl super::stylesheet_blocking::StylesheetBlockingReadView for LiveRuntimeDomHo
|
||||
self.borrow().document_node_id()
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
<DomHost as super::stylesheet_blocking::StylesheetBlockingReadView>::document_is_quirks_mode(
|
||||
self.borrow(),
|
||||
)
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
target_node_id: Option<NodeId>,
|
||||
@@ -1397,6 +1403,24 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_runtime_dom_host_forwards_quirks_mode_for_stylesheet_processing() {
|
||||
let document = HtmlParser::SCRIPTING_ENABLED.parse(
|
||||
Url::parse("https://example.test/page.html").unwrap(),
|
||||
"<html><head><link rel=stylesheet href=app.css></head></html>".to_owned(),
|
||||
);
|
||||
let link = first_element_handle(&document, "link");
|
||||
let host = LiveRuntimeDomHost::from_dom_host(DomHost::from_dom(document));
|
||||
|
||||
let disposition = crate::stylesheet_blocking::stylesheet_link_disposition(
|
||||
&host,
|
||||
NodeId::new(link.index()),
|
||||
)
|
||||
.expect("stylesheet disposition");
|
||||
|
||||
assert!(disposition.options().quirks_mode_mime_compatibility());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dom_host_builds_from_parsed_document_and_updates_text_content() {
|
||||
let parser = HtmlParser::SCRIPTING_ENABLED;
|
||||
|
||||
@@ -119,6 +119,14 @@ impl StylesheetBlockingReadView for ChildFrameLiveParserOwner<'_, '_, '_> {
|
||||
self.child_document_handle
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
self.host
|
||||
.dom_host()
|
||||
.node(self.child_document_handle)
|
||||
.and_then(Node::as_document)
|
||||
.is_some_and(|document| document.is_quirks_mode())
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
target_node_id: Option<crate::dom::NodeId>,
|
||||
|
||||
@@ -3460,6 +3460,32 @@ document.body.setAttribute('data-error-state', [
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quirks_stylesheet_parser_client_claims_mode_neutral_scanner_descriptor() {
|
||||
let final_url = Url::parse("https://example.test/docs/page.html").expect("test url");
|
||||
let loader = ResourceRequestClient::new(&FetchConfig::default()).expect("default loader");
|
||||
let mut cache = BufferedDocumentPreloadState::default();
|
||||
cache.append_to_main_document_scan(
|
||||
&final_url,
|
||||
r#"<link rel="stylesheet" href="/app.css">"#,
|
||||
&loader,
|
||||
);
|
||||
assert_eq!(cache.pending_preload_counts_for_test(), (0, 1));
|
||||
|
||||
let stylesheet_candidate =
|
||||
moli_stylesheet_blocking::DocumentOwnedBlockingStylesheetCandidate::Link {
|
||||
node_id: NodeId::new(11),
|
||||
url: Url::parse("https://example.test/app.css").expect("stylesheet URL"),
|
||||
options: crate::stylesheet_blocking::StylesheetFetchOptions::default()
|
||||
.with_quirks_mode_mime_compatibility(true),
|
||||
};
|
||||
cache.claim_pending_stylesheet_preloads_for_parser(&[
|
||||
DocumentOwnedBlockingStylesheetDiscoveryInput::from(&stylesheet_candidate),
|
||||
]);
|
||||
|
||||
assert_eq!(cache.pending_preload_counts_for_test(), (0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meta_csp_pending_descriptor_budget_falls_back_to_parser() {
|
||||
let final_url = Url::parse("https://example.test/docs/page.html").expect("test url");
|
||||
|
||||
@@ -504,7 +504,17 @@ impl BufferedDocumentPreloadState {
|
||||
.iter()
|
||||
.filter_map(|input| match input.signature() {
|
||||
DocumentBlockingStylesheetSignature::Link { url, options } => {
|
||||
Some(options.resource_key(url.clone()))
|
||||
// The scanner runs ahead of the parser and cannot know the
|
||||
// Document's final quirks mode. MIME compatibility changes
|
||||
// response processing, not the request metadata, so claim
|
||||
// the buffered descriptor using the scanner's neutral
|
||||
// processing mode before it starts a duplicate fetch.
|
||||
Some(
|
||||
options
|
||||
.clone()
|
||||
.with_quirks_mode_mime_compatibility(false)
|
||||
.resource_key(url.clone()),
|
||||
)
|
||||
}
|
||||
DocumentBlockingStylesheetSignature::ParserCreatedStyleImport { .. } => None,
|
||||
})
|
||||
|
||||
@@ -264,11 +264,16 @@ enum StylesheetResponseProvenance {
|
||||
}
|
||||
|
||||
impl StylesheetResponseProvenance {
|
||||
fn is_cors_same_origin(self, document_url: &Url, head: &moli_fetch::ResponseHead) -> bool {
|
||||
fn is_cors_same_origin(
|
||||
self,
|
||||
document_url: &Url,
|
||||
request_url: &Url,
|
||||
response: &crate::protocol_types::NavigationResponse,
|
||||
) -> bool {
|
||||
match self {
|
||||
Self::Network => !head
|
||||
.url_list()
|
||||
.has_cross_origin_url(&moli_url::WebOrigin::from_url(document_url)),
|
||||
Self::Network => {
|
||||
stylesheet_response_url_chain_is_same_origin(document_url, request_url, response)
|
||||
}
|
||||
Self::ServiceWorker { filter } => !matches!(
|
||||
filter,
|
||||
Some(
|
||||
@@ -280,6 +285,18 @@ impl StylesheetResponseProvenance {
|
||||
}
|
||||
}
|
||||
|
||||
fn stylesheet_response_url_chain_is_same_origin(
|
||||
document_url: &Url,
|
||||
request_url: &Url,
|
||||
response: &crate::protocol_types::NavigationResponse,
|
||||
) -> bool {
|
||||
moli_url::same_origin(document_url, request_url)
|
||||
&& !response
|
||||
.head()
|
||||
.url_list()
|
||||
.has_cross_origin_url(&moli_url::WebOrigin::from_url(document_url))
|
||||
}
|
||||
|
||||
fn stylesheet_terminal_from_response(
|
||||
document_url: &Url,
|
||||
request_url: &Url,
|
||||
@@ -311,7 +328,7 @@ fn stylesheet_terminal_from_response(
|
||||
}
|
||||
});
|
||||
let origin_clean = cors_usability.as_ref().map_or_else(
|
||||
|| response_provenance.is_cors_same_origin(document_url, &head),
|
||||
|| response_provenance.is_cors_same_origin(document_url, request_url, &response),
|
||||
Result::is_ok,
|
||||
);
|
||||
let usability = if !(200..=299).contains(&response.status) {
|
||||
@@ -322,7 +339,11 @@ fn stylesheet_terminal_from_response(
|
||||
} else {
|
||||
cors_usability.unwrap_or(Ok(()))
|
||||
}
|
||||
.and_then(|()| validate_stylesheet_response_ref(request_url, &response));
|
||||
.and_then(|()| {
|
||||
let allow_non_css_mime = options.quirks_mode_mime_compatibility()
|
||||
&& stylesheet_response_url_chain_is_same_origin(document_url, request_url, &response);
|
||||
validate_stylesheet_response_ref(request_url, &response, allow_non_css_mime)
|
||||
});
|
||||
|
||||
match usability {
|
||||
Ok(()) => StylesheetFetchTerminal::ready(response, origin_clean),
|
||||
@@ -334,13 +355,14 @@ pub(crate) fn validate_stylesheet_response(
|
||||
url: &Url,
|
||||
response: crate::protocol_types::NavigationResponse,
|
||||
) -> Result<crate::protocol_types::NavigationResponse, String> {
|
||||
validate_stylesheet_response_ref(url, &response)?;
|
||||
validate_stylesheet_response_ref(url, &response, false)?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn validate_stylesheet_response_ref(
|
||||
url: &Url,
|
||||
response: &crate::protocol_types::NavigationResponse,
|
||||
allow_non_css_mime: bool,
|
||||
) -> Result<(), String> {
|
||||
if should_response_be_blocked_due_to_nosniff(&response.headers, FetchDestination::Style) {
|
||||
return Err(format!(
|
||||
@@ -352,7 +374,7 @@ fn validate_stylesheet_response_ref(
|
||||
MimeSniffingContext::Style,
|
||||
response.body_bytes(),
|
||||
);
|
||||
if !is_css_mime(&computed_mime_type) {
|
||||
if !allow_non_css_mime && !is_css_mime(&computed_mime_type) {
|
||||
return Err(format!(
|
||||
"failed to fetch stylesheet `{url}`: unsupported stylesheet MIME type `{computed_mime_type}`"
|
||||
));
|
||||
@@ -380,6 +402,24 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn stylesheet_redirect(from_url: &Url, to_url: &Url) -> crate::types::NavigationRedirect {
|
||||
crate::types::NavigationRedirect {
|
||||
source: moli_fetch::RedirectSource::Network,
|
||||
from_url: from_url.clone(),
|
||||
to_url: to_url.clone(),
|
||||
status: 302,
|
||||
headers: Vec::new(),
|
||||
network_extra_info_available: true,
|
||||
request_extra_info: None,
|
||||
response_extra_info: None,
|
||||
redirect_has_extra_info: true,
|
||||
request_cookie_report: None,
|
||||
cookie_set_reports: Vec::new(),
|
||||
from_cache: false,
|
||||
negotiated_http_version: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validates_stylesheet_response_rejects_explicit_non_css_mime() {
|
||||
let url = Url::parse("https://example.com/app.css").unwrap();
|
||||
@@ -402,6 +442,111 @@ mod tests {
|
||||
assert_eq!(response.body_text(), "body { color: red; }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quirks_mode_mime_compatibility_requires_same_origin_final_url() {
|
||||
let document_url = Url::parse("https://page.example.test/document").unwrap();
|
||||
let request_url = Url::parse("https://page.example.test/app.css").unwrap();
|
||||
let options = StylesheetFetchOptions::default().with_quirks_mode_mime_compatibility(true);
|
||||
let same_origin_response =
|
||||
stylesheet_response(&request_url, Some("text/plain"), "body { color: green; }");
|
||||
|
||||
let same_origin_terminal = stylesheet_terminal_from_response(
|
||||
&document_url,
|
||||
&request_url,
|
||||
&options,
|
||||
same_origin_response,
|
||||
StylesheetResponseProvenance::Network,
|
||||
);
|
||||
|
||||
assert!(same_origin_terminal.is_ready());
|
||||
|
||||
let cross_origin_url = Url::parse("https://cdn.example.test/app.css").unwrap();
|
||||
let cross_origin_response = stylesheet_response(
|
||||
&cross_origin_url,
|
||||
Some("text/plain"),
|
||||
"body { color: red; }",
|
||||
);
|
||||
let cross_origin_terminal = stylesheet_terminal_from_response(
|
||||
&document_url,
|
||||
&request_url,
|
||||
&options,
|
||||
cross_origin_response,
|
||||
StylesheetResponseProvenance::Network,
|
||||
);
|
||||
|
||||
assert!(!cross_origin_terminal.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quirks_mode_mime_compatibility_rejects_cross_origin_redirect_taint() {
|
||||
let document_url = Url::parse("https://page.example.test/document").unwrap();
|
||||
let same_origin_url = Url::parse("https://page.example.test/app.css").unwrap();
|
||||
let cross_origin_url = Url::parse("https://cdn.example.test/app.css").unwrap();
|
||||
let options = StylesheetFetchOptions::default().with_quirks_mode_mime_compatibility(true);
|
||||
|
||||
let mut cross_to_same_response =
|
||||
stylesheet_response(&same_origin_url, Some("text/plain"), "body { color: red; }");
|
||||
cross_to_same_response.redirected = true;
|
||||
cross_to_same_response.redirect_chain =
|
||||
vec![stylesheet_redirect(&cross_origin_url, &same_origin_url)];
|
||||
|
||||
let cross_to_same_terminal = stylesheet_terminal_from_response(
|
||||
&document_url,
|
||||
&cross_origin_url,
|
||||
&options,
|
||||
cross_to_same_response,
|
||||
StylesheetResponseProvenance::Network,
|
||||
);
|
||||
|
||||
assert!(!cross_to_same_terminal.is_ready());
|
||||
assert_eq!(cross_to_same_terminal.origin_clean(), Some(false));
|
||||
|
||||
let mut through_cross_response =
|
||||
stylesheet_response(&same_origin_url, Some("text/plain"), "body { color: red; }");
|
||||
through_cross_response.redirected = true;
|
||||
through_cross_response.redirect_chain = vec![
|
||||
stylesheet_redirect(&same_origin_url, &cross_origin_url),
|
||||
stylesheet_redirect(&cross_origin_url, &same_origin_url),
|
||||
];
|
||||
|
||||
let through_cross_terminal = stylesheet_terminal_from_response(
|
||||
&document_url,
|
||||
&same_origin_url,
|
||||
&options,
|
||||
through_cross_response,
|
||||
StylesheetResponseProvenance::Network,
|
||||
);
|
||||
|
||||
assert!(!through_cross_terminal.is_ready());
|
||||
assert_eq!(through_cross_terminal.origin_clean(), Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quirks_mode_mime_compatibility_does_not_bypass_nosniff() {
|
||||
let document_url = Url::parse("https://page.example.test/document").unwrap();
|
||||
let stylesheet_url = Url::parse("https://page.example.test/app.css").unwrap();
|
||||
let options = StylesheetFetchOptions::default().with_quirks_mode_mime_compatibility(true);
|
||||
let response = crate::protocol_types::NavigationResponse::from_text_body(
|
||||
stylesheet_url.clone(),
|
||||
200,
|
||||
vec![
|
||||
("Content-Type".to_owned(), "text/plain".to_owned()),
|
||||
("x-content-type-options".to_owned(), "nosniff".to_owned()),
|
||||
],
|
||||
"body { color: red; }".to_owned(),
|
||||
);
|
||||
|
||||
let terminal = stylesheet_terminal_from_response(
|
||||
&document_url,
|
||||
&stylesheet_url,
|
||||
&options,
|
||||
response,
|
||||
StylesheetResponseProvenance::Network,
|
||||
);
|
||||
|
||||
assert!(!terminal.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_stylesheet_request_uses_captured_processing_attributes() {
|
||||
let document_url = Url::parse("https://example.com/page").unwrap();
|
||||
|
||||
@@ -251,6 +251,7 @@ pub trait StylesheetBlockingReadView {
|
||||
fn final_url_clone(&self) -> Option<Url>;
|
||||
fn document_base_url_clone(&self) -> Option<Url>;
|
||||
fn document_node_id(&self) -> NativeNodeId;
|
||||
fn document_is_quirks_mode(&self) -> bool;
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
@@ -284,6 +285,11 @@ impl StylesheetBlockingReadView for NativeDom {
|
||||
self.document_node_id()
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
self.document()
|
||||
.is_some_and(|document| document.is_quirks_mode())
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
target_node_id: Option<NodeId>,
|
||||
@@ -325,6 +331,12 @@ impl StylesheetBlockingReadView for DomHost {
|
||||
self.document_handle()
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
self.node(self.document_handle())
|
||||
.and_then(Node::as_document)
|
||||
.is_some_and(|document| document.is_quirks_mode())
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
target_node_id: Option<NodeId>,
|
||||
@@ -409,7 +421,8 @@ fn stylesheet_link_disposition_in_view(
|
||||
element.nonce.as_deref(),
|
||||
element.charset.as_deref(),
|
||||
element.fetch_priority.as_deref(),
|
||||
);
|
||||
)
|
||||
.with_quirks_mode_mime_compatibility(document.document_is_quirks_mode());
|
||||
let is_alternate = link_rel_includes_token(rel, "alternate");
|
||||
let blocking = !is_alternate && media_blocks_scripts(element.media.as_deref());
|
||||
Some(if blocking {
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::types::{
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct StylesheetFetchOptions(Arc<StylesheetFetchOptionsData>);
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
|
||||
struct StylesheetFetchOptionsData {
|
||||
cross_origin: Option<String>,
|
||||
referrer_policy: Option<String>,
|
||||
@@ -27,6 +27,7 @@ struct StylesheetFetchOptionsData {
|
||||
nonce: Option<String>,
|
||||
charset: Option<String>,
|
||||
fetch_priority: Option<String>,
|
||||
quirks_mode_mime_compatibility: bool,
|
||||
}
|
||||
|
||||
impl Default for StylesheetFetchOptions {
|
||||
@@ -51,9 +52,15 @@ impl StylesheetFetchOptions {
|
||||
nonce: normalize_preserved_value(nonce),
|
||||
charset: normalize_token(charset),
|
||||
fetch_priority: normalize_token(fetch_priority),
|
||||
quirks_mode_mime_compatibility: false,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn with_quirks_mode_mime_compatibility(mut self, enabled: bool) -> Self {
|
||||
Arc::make_mut(&mut self.0).quirks_mode_mime_compatibility = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cross_origin(&self) -> Option<&str> {
|
||||
self.0.cross_origin.as_deref()
|
||||
}
|
||||
@@ -78,6 +85,10 @@ impl StylesheetFetchOptions {
|
||||
self.0.fetch_priority.as_deref()
|
||||
}
|
||||
|
||||
pub fn quirks_mode_mime_compatibility(&self) -> bool {
|
||||
self.0.quirks_mode_mime_compatibility
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self == &Self::default()
|
||||
}
|
||||
@@ -122,6 +133,7 @@ pub struct StylesheetResourceKey {
|
||||
referrer_policy: Option<String>,
|
||||
integrity: Option<String>,
|
||||
charset: Option<String>,
|
||||
quirks_mode_mime_compatibility: bool,
|
||||
}
|
||||
|
||||
impl StylesheetResourceKey {
|
||||
@@ -135,6 +147,7 @@ impl StylesheetResourceKey {
|
||||
referrer_policy: options.referrer_policy().map(str::to_owned),
|
||||
integrity: options.integrity().map(str::to_owned),
|
||||
charset: options.charset().map(str::to_owned),
|
||||
quirks_mode_mime_compatibility: options.quirks_mode_mime_compatibility(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -767,6 +767,10 @@ mod tests {
|
||||
NativeNodeId::new(0)
|
||||
}
|
||||
|
||||
fn document_is_quirks_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn document_order_stylesheet_candidate_ids_before(
|
||||
&self,
|
||||
_target_node_id: Option<NodeId>,
|
||||
@@ -1500,6 +1504,31 @@ mod tests {
|
||||
assert!(!plain_fetch.ptr_eq(&anonymous_fetch));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn physical_key_keeps_mime_processing_compatibility_boundary() {
|
||||
let mut state = StylesheetBlockingState::default();
|
||||
let document_url = Url::parse("https://example.com/").expect("static document url");
|
||||
let stylesheet_url =
|
||||
Url::parse("https://example.com/shared.css").expect("static stylesheet url");
|
||||
let standard = StylesheetFetchOptions::default();
|
||||
let quirks = StylesheetFetchOptions::default().with_quirks_mode_mime_compatibility(true);
|
||||
|
||||
let standard_fetch = state.preload_stylesheet(
|
||||
&PendingStylesheetFetcher,
|
||||
document_url.clone(),
|
||||
stylesheet_url.clone(),
|
||||
standard,
|
||||
);
|
||||
let quirks_fetch = state.preload_stylesheet(
|
||||
&PendingStylesheetFetcher,
|
||||
document_url,
|
||||
stylesheet_url,
|
||||
quirks,
|
||||
);
|
||||
|
||||
assert!(!standard_fetch.ptr_eq(&quirks_fetch));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn physical_key_ignores_fragment_nonce_and_fetch_priority() {
|
||||
let mut state = StylesheetBlockingState::default();
|
||||
|
||||
Reference in New Issue
Block a user