diff --git a/moli-renderer-v8/src/runtime/phase_one/mod.rs b/moli-renderer-v8/src/runtime/phase_one/mod.rs
index a133ca13f8..88eea8855f 100644
--- a/moli-renderer-v8/src/runtime/phase_one/mod.rs
+++ b/moli-renderer-v8/src/runtime/phase_one/mod.rs
@@ -18370,7 +18370,7 @@ document.body.setAttribute("data-range", [
let outcome = driver
.advance_parser_step(
&mut page_vm,
- "
",
+ "",
None,
)
.await
@@ -18380,6 +18380,20 @@ document.body.setAttribute("data-range", [
matches!(outcome, ParserStepAdvanceOutcome::BlockedOnStylesheet(_)),
"parser-created style import should gate parser-blocking script on live PageVm"
);
+ let style = page_vm
+ .vm()
+ .document_runtime
+ .dom_host()
+ .element_handle_by_id("blocking-style")
+ .expect("parser-created style owner");
+ assert_eq!(
+ page_vm
+ .vm()
+ .document_runtime
+ .pending_style_import_binding_for_test(style),
+ Some((1, true)),
+ "the parser-discovered import must bind its live stylesheet root before the script gate is released"
+ );
}));
}
diff --git a/moli-renderer-v8/src/script_vm/main_document_owner.rs b/moli-renderer-v8/src/script_vm/main_document_owner.rs
index 66e69ceb6a..247923f5e1 100644
--- a/moli-renderer-v8/src/script_vm/main_document_owner.rs
+++ b/moli-renderer-v8/src/script_vm/main_document_owner.rs
@@ -63,6 +63,22 @@ impl ScriptVm {
.document_runtime
.note_discovered_document_owned_blocking_stylesheet_inputs(inputs.iter());
self.settle_stylesheet_link_clients(completed_stylesheet_clients);
+ if inputs.iter().any(|input| {
+ matches!(
+ input.signature(),
+ crate::stylesheet_blocking::DocumentBlockingStylesheetSignature::ParserCreatedStyleImport { .. }
+ )
+ }) {
+ // The import graph owns the parser gate, while the connected
+ // style operation owns its CSSOM installation. Bind both views to
+ // the same live root before an already-fast graph can release the
+ // parser-blocking script.
+ self.sync_live_document_style_sources();
+ let prepared = self
+ .document_runtime
+ .prepare_parser_discovered_style_import_loads(inputs);
+ self.commit_and_apply_connected_style_loads(prepared);
+ }
inputs.len()
}
diff --git a/moli-renderer-v8/src/stylesheet_runtime/connected.rs b/moli-renderer-v8/src/stylesheet_runtime/connected.rs
index e4a58dea69..e1ec05afba 100644
--- a/moli-renderer-v8/src/stylesheet_runtime/connected.rs
+++ b/moli-renderer-v8/src/stylesheet_runtime/connected.rs
@@ -9,9 +9,9 @@ use crate::module_runtime::{
use crate::planning::{ScriptFetchMetadata, module_script_credentials_mode};
use crate::service_worker_runtime::ServiceWorkerRequestDestination;
use crate::stylesheet_blocking::{
- StylesheetFetchOptions, connected_preload_like_link_url,
- document_owned_blocking_stylesheet_candidate_for_node, link_rel_includes_token,
- preload_like_link_loads_stylesheet, stylesheet_link_disposition,
+ DocumentOwnedBlockingStylesheetDiscoveryInput, StylesheetFetchOptions,
+ connected_preload_like_link_url, document_owned_blocking_stylesheet_candidate_for_node,
+ link_rel_includes_token, preload_like_link_loads_stylesheet, stylesheet_link_disposition,
stylesheet_preload_link_request,
};
use crate::types::{AsyncSubresourceFetchResponseFilter, SubresourceResourceType};
@@ -238,6 +238,24 @@ impl DocumentRuntime {
prepared
}
+ pub(crate) fn prepare_parser_discovered_style_import_loads(
+ &mut self,
+ inputs: &[DocumentOwnedBlockingStylesheetDiscoveryInput],
+ ) -> Vec {
+ inputs
+ .iter()
+ .filter(|input| {
+ matches!(
+ input.signature(),
+ DocumentBlockingStylesheetSignature::ParserCreatedStyleImport { .. }
+ )
+ })
+ .flat_map(|input| {
+ self.prepare_connected_style_loads(DomHandle::new(input.node_id().index()), true)
+ })
+ .collect()
+ }
+
#[cfg(test)]
pub(crate) fn queue_initial_connected_style_loads(&mut self) {
let prepared = self.prepare_initial_connected_style_loads();
@@ -580,6 +598,21 @@ impl DocumentRuntime {
self.connected_style_load_is_queued(handle)
}
+ #[cfg(test)]
+ pub(crate) fn pending_style_import_binding_for_test(
+ &self,
+ handle: DomHandle,
+ ) -> Option<(usize, bool)> {
+ let operation = self
+ .stylesheet_lifecycle
+ .owner_states
+ .pending_operation(handle)?;
+ let ConnectedLoadParameters::StyleImports { roots, .. } = &operation.parameters else {
+ return None;
+ };
+ Some((roots.len(), operation.blocking_operation.is_some()))
+ }
+
pub(super) fn push_ready_connected_style_load(&mut self, ready: ReadyConnectedStyleLoad) {
let producer = self
.stylesheet_lifecycle
@@ -671,11 +704,17 @@ impl DocumentRuntime {
if !node.flags().parser_created() || !node.is_html_element_named("style") {
return false;
}
- document_owned_blocking_stylesheet_candidate_for_node(
+ let Some(candidate) = document_owned_blocking_stylesheet_candidate_for_node(
&self.dom_host,
NodeId::new(handle.index()),
- )
- .is_some()
+ ) else {
+ return false;
+ };
+ let signature = DocumentBlockingStylesheetSignature::from_candidate(&candidate);
+ self.stylesheet_lifecycle
+ .fetches
+ .blocking_operation(NodeId::new(handle.index()), &signature)
+ .is_none()
}
pub(crate) fn has_pending_style_loads(&self) -> bool {
diff --git a/moli-stylesheet-blocking/src/discovery.rs b/moli-stylesheet-blocking/src/discovery.rs
index d58be82217..a4490e6105 100644
--- a/moli-stylesheet-blocking/src/discovery.rs
+++ b/moli-stylesheet-blocking/src/discovery.rs
@@ -691,7 +691,7 @@ fn parser_created_style_import_urls(
if !element.is_html_element("style") || !element.parser_blocking_eligible {
return None;
}
- if element.disabled || !media_blocks_scripts(element.media.as_deref()) {
+ if !media_blocks_scripts(element.media.as_deref()) {
return None;
}
let css_text = document.text_content(native_node_id)?;
@@ -777,6 +777,37 @@ mod tests {
);
}
+ #[test]
+ fn style_disabled_content_attribute_does_not_suppress_parser_import_blocking() {
+ let document_url = url::Url::parse("https://example.com/path/page.html").unwrap();
+ let mut host = DomHost::from_dom(NativeDom::new_html(document_url));
+ let style = host.create_parser_element_without_attributes(
+ "style".to_owned(),
+ "http://www.w3.org/1999/xhtml".to_owned(),
+ None,
+ );
+ let text = host.create_text_node("@import url('theme.css');");
+ assert!(host.set_attribute(style, "disabled", ""));
+ assert!(host.append_child(style, text));
+ assert!(host.append_child(host.document_handle(), style));
+
+ let candidate = document_owned_blocking_stylesheet_candidate_for_node(
+ &host,
+ moli_dom::NodeId::new(style.index()),
+ )
+ .expect("the unsupported style content attribute must not disable import blocking");
+ let super::DocumentOwnedBlockingStylesheetCandidate::ParserCreatedStyleImport {
+ urls, ..
+ } = candidate
+ else {
+ panic!("expected a parser-created style import candidate");
+ };
+ assert_eq!(
+ urls,
+ [url::Url::parse("https://example.com/path/theme.css").unwrap()]
+ );
+ }
+
#[test]
fn hidden_nonce_remains_part_of_stylesheet_request_identity() {
let document_url = url::Url::parse("https://example.com/page.html").unwrap();