diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt
index b14202c79c..b5cb849662 100644
--- a/moli-benchmark/wpt-cross-current/failed-cases.txt
+++ b/moli-benchmark/wpt-cross-current/failed-cases.txt
@@ -2978,9 +2978,6 @@ html/semantics/text-level-semantics/historical.html
html/semantics/the-button-element/command-and-commandfor/on-dialog-behavior.html
html/syntax/charset/xhr.html
html/syntax/parsing/cdata-in-integration-point-fragment.html
-html/webappapis/dynamic-markup-insertion/document-write/module-delayed.html
-html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html
-html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html
html/webappapis/dynamic-markup-insertion/document-write/write-active-document.html
html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe-url-base.html
html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-detached-iframe.html
diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt
index 59108b0077..45985ec9b6 100644
--- a/moli-benchmark/wpt-cross-current/passed-cases.txt
+++ b/moli-benchmark/wpt-cross-current/passed-cases.txt
@@ -7012,8 +7012,11 @@ html/webappapis/dynamic-markup-insertion/document-write/iframe_006.html
html/webappapis/dynamic-markup-insertion/document-write/iframe_007.html
html/webappapis/dynamic-markup-insertion/document-write/iframe_008.html
html/webappapis/dynamic-markup-insertion/document-write/iframe_009.html
+html/webappapis/dynamic-markup-insertion/document-write/module-delayed.html
html/webappapis/dynamic-markup-insertion/document-write/module-dynamic-import.html
+html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html
html/webappapis/dynamic-markup-insertion/document-write/module-static-import.html
+html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html
html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html
html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html
html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html
diff --git a/moli-core/tests/scripts/module_document_write.rs b/moli-core/tests/scripts/module_document_write.rs
index 2660432ee7..8999182f69 100644
--- a/moli-core/tests/scripts/module_document_write.rs
+++ b/moli-core/tests/scripts/module_document_write.rs
@@ -15,6 +15,95 @@ fn module_url(source: &str) -> String {
)
}
+async fn child_module_load_precedes_delayed_write(variant: &str) -> Result<()> {
+ let server = FixtureServer::spawn().await?;
+ let browser = Browser::new(AppConfig::default())?;
+ for timer_owner in ["parent", "window"] {
+ let write = r#"
+ parent.events.push('timer:' + document.readyState);
+ document.write('replacement');
+ document.close();
+ parent.events.push('write');
+ parent.finishWrite();
+ "#;
+ let source = if variant == "tla" {
+ format!("await new Promise(resolve => {timer_owner}.setTimeout(resolve, 0)); {write}")
+ } else {
+ format!("{timer_owner}.setTimeout(() => {{ {write} }}, 0);")
+ };
+ let script = match variant {
+ "import" => format!(
+ "",
+ serde_json::to_string(&module_url(&source))?
+ ),
+ "external" => format!(
+ "",
+ module_url(&source)
+ ),
+ _ => format!(""),
+ };
+ let child = markup_url(
+ &server,
+ &format!("
{script}original"),
+ );
+ let parent = format!(
+ r#""#,
+ serde_json::to_string(&child)?
+ );
+ let mut page = browser.fetch(&markup_url(&server, &parent)).await?;
+ let result = page.evaluate_runtime_expression_with_await_async(
+ "writeDone.then(() => JSON.stringify({events, body: document.getElementById('target').contentDocument.body.textContent}))",
+ true,
+ ).await?;
+ let result: serde_json::Value =
+ serde_json::from_str(result["value"].as_str().expect("delayed write observation"))?;
+ let events = result["events"].as_array().expect("child event sequence");
+ assert_eq!(
+ events.iter().take(3).collect::>(),
+ vec![
+ &serde_json::json!("load:original"),
+ &serde_json::json!("timer:complete"),
+ &serde_json::json!("write")
+ ],
+ "variant={variant}, timer_owner={timer_owner}, events={events:?}"
+ );
+ assert_eq!(
+ result["body"], "replacement",
+ "variant={variant}, timer_owner={timer_owner}"
+ );
+ }
+ server.shutdown().await;
+ Ok(())
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn child_inline_module_load_precedes_delayed_document_write() -> Result<()> {
+ child_module_load_precedes_delayed_write("inline").await
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn child_imported_module_load_precedes_delayed_document_write() -> Result<()> {
+ child_module_load_precedes_delayed_write("import").await
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn child_external_module_load_precedes_delayed_document_write() -> Result<()> {
+ child_module_load_precedes_delayed_write("external").await
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn child_module_load_precedes_timer_that_resumes_top_level_await() -> Result<()> {
+ child_module_load_precedes_delayed_write("tla").await
+}
+
const WRITE_ATTEMPTS: &str = r#"
for (const method of ['write', 'writeln']) {
let converted = false;
diff --git a/moli-renderer-v8/src/native_bridge/context_host/host_loads.rs b/moli-renderer-v8/src/native_bridge/context_host/host_loads.rs
index 5cb097d01e..eb0a80641a 100644
--- a/moli-renderer-v8/src/native_bridge/context_host/host_loads.rs
+++ b/moli-renderer-v8/src/native_bridge/context_host/host_loads.rs
@@ -150,8 +150,13 @@ impl JsContextHost {
return false;
};
let routed = self
- .page_child_frame_task_sender()
- .send_host_load(crate::page_task_queue::RendererPageChildHostLoadTarget::new(admission))
+ .page_task_capabilities
+ .get()
+ .expect("child load delivery must retain its Page task capabilities")
+ .dom_manipulation()
+ .send_child_host_load(
+ crate::page_task_queue::RendererPageChildHostLoadTarget::new(admission),
+ )
.is_ok();
if !routed {
let _ = self
@@ -162,7 +167,7 @@ impl JsContextHost {
tracing::debug!(
child_handle = ?task.child_handle,
owner = ?task.owner,
- "queued exact child load delivery on the stable child-frame source"
+ "queued exact child load delivery on the DOM manipulation source"
);
true
}
diff --git a/moli-renderer-v8/src/page_task_queue/child_frame_task.rs b/moli-renderer-v8/src/page_task_queue/child_frame_task.rs
index 95cbc6355f..94b65d3ee3 100644
--- a/moli-renderer-v8/src/page_task_queue/child_frame_task.rs
+++ b/moli-renderer-v8/src/page_task_queue/child_frame_task.rs
@@ -577,13 +577,6 @@ impl RendererPageChildFrameTaskSender {
self.send(RendererPageChildFrameTaskTarget::DocumentLifecycle(target))
}
- pub(crate) fn send_host_load(
- &self,
- target: RendererPageChildHostLoadTarget,
- ) -> Result<(), RendererPageChildFrameTaskRouteClosed> {
- self.send(RendererPageChildFrameTaskTarget::HostLoad(target))
- }
-
pub(crate) fn send_parser_module_root_start(
&self,
target: RendererPageChildParserModuleRootStartTarget,
diff --git a/moli-renderer-v8/src/page_task_queue/dom_manipulation.rs b/moli-renderer-v8/src/page_task_queue/dom_manipulation.rs
index b25df6df0d..0b8c7d1245 100644
--- a/moli-renderer-v8/src/page_task_queue/dom_manipulation.rs
+++ b/moli-renderer-v8/src/page_task_queue/dom_manipulation.rs
@@ -80,6 +80,7 @@ pub(crate) enum RendererPageDomManipulationOwner {
ImageLoadEvent(RendererPageImageLoadEventOwner),
MainDocumentLifecycle(super::RendererPageMainDocumentLifecycleOwner),
ChildDocumentLifecycle(super::RendererPageChildFrameTaskOwner),
+ ChildHostLoad(super::RendererPageChildFrameTaskOwner),
PopupLoadEvent(RendererPagePopupLoadEventOwner),
PopupClose(RendererPagePopupCloseOwner),
ConnectedStyleEvent(RendererPageStylesheetTaskOwner),
@@ -101,6 +102,7 @@ pub(crate) enum RendererPageDomManipulationTask {
ImageLoadEvent(RendererPageImageLoadEventTask),
MainDocumentLifecycle(super::RendererPageMainDocumentLifecycleTask),
ChildDocumentLifecycle(super::RendererPageChildFrameTask),
+ ChildHostLoad(super::RendererPageChildFrameTask),
PopupLoadEvent(RendererPagePopupLoadEventTask),
PopupClose(RendererPagePopupCloseTask),
ConnectedStyleEvent(RendererPageConnectedStyleEventTask),
@@ -140,6 +142,9 @@ impl RendererPageDomManipulationTask {
Self::ChildDocumentLifecycle(task) => {
RendererPageDomManipulationOwner::ChildDocumentLifecycle(task.owner())
}
+ Self::ChildHostLoad(task) => {
+ RendererPageDomManipulationOwner::ChildHostLoad(task.owner())
+ }
Self::PopupLoadEvent(task) => {
RendererPageDomManipulationOwner::PopupLoadEvent(task.owner())
}
@@ -181,6 +186,7 @@ pub(crate) enum PageDomManipulationTurnAction {
ImageLoadEvent(super::PageImageLoadEventTurnAction),
MainDocumentLifecycle(super::PageMainDocumentLifecycleTurnAction),
ChildDocumentLifecycle(super::PageChildDocumentLifecycleTurnAction),
+ ChildHostLoad(super::PageChildHostLoadTurnAction),
PopupLoadEvent(super::PagePopupLoadEventTurnAction),
PopupClose(super::PagePopupCloseTurnAction),
ConnectedStyleEvent(PageConnectedStyleEventTurnAction),
@@ -270,6 +276,22 @@ impl RendererPageDomManipulationSender {
.map_err(|_| super::child_frame_task::RendererPageChildFrameTaskRouteClosed)
}
+ pub(crate) fn send_child_host_load(
+ &self,
+ target: super::RendererPageChildHostLoadTarget,
+ ) -> Result<(), super::child_frame_task::RendererPageChildFrameTaskRouteClosed> {
+ self.route
+ .send(RendererPageDomManipulationTask::ChildHostLoad(
+ super::RendererPageChildFrameTask::new(
+ super::RendererPageChildFrameTaskOwner::new(
+ self.root_document,
+ super::RendererPageChildFrameTaskTarget::HostLoad(target),
+ ),
+ ),
+ ))
+ .map_err(|_| super::child_frame_task::RendererPageChildFrameTaskRouteClosed)
+ }
+
pub(crate) fn popup_load_event(&self) -> RendererPagePopupLoadEventSender {
RendererPagePopupLoadEventSender::new(self.route.clone(), self.root_document)
}
@@ -328,18 +350,17 @@ pub(crate) struct RendererPageDomManipulationSource {
}
impl RendererPageDomManipulationSource {
- pub(crate) fn has_main_document_lifecycle_task(
+ pub(crate) fn has_document_lifecycle_task(
&mut self,
- root_document: RendererDocumentToken,
- document: crate::frame_owner_model::FrameDocumentTaskOwner,
+ is_current: impl Fn(RendererPageDomManipulationOwner) -> bool,
) -> bool {
self.source.has_matching_task(|ready| {
matches!(
ready.value(),
- RendererPageDomManipulationTask::MainDocumentLifecycle(task)
- if task.owner.root_document == root_document
- && task.owner.body.owner() == document
- )
+ RendererPageDomManipulationTask::MainDocumentLifecycle(_)
+ | RendererPageDomManipulationTask::ChildDocumentLifecycle(_)
+ | RendererPageDomManipulationTask::ChildHostLoad(_)
+ ) && is_current(ready.value().owner())
})
}
diff --git a/moli-renderer-v8/src/page_task_queue/owner_sources.rs b/moli-renderer-v8/src/page_task_queue/owner_sources.rs
index 1d28325ae0..c8d984fab8 100644
--- a/moli-renderer-v8/src/page_task_queue/owner_sources.rs
+++ b/moli-renderer-v8/src/page_task_queue/owner_sources.rs
@@ -627,13 +627,12 @@ pub(crate) enum RendererPageSchedulerTask {
}
impl RendererPageOwnedTaskSources {
- pub(crate) fn has_queued_main_document_lifecycle_dom_task(
+ pub(crate) fn has_queued_document_lifecycle_dom_task(
&mut self,
- root_document: crate::runtime::RendererDocumentToken,
- document: crate::frame_owner_model::FrameDocumentTaskOwner,
+ is_current: impl Fn(super::RendererPageDomManipulationOwner) -> bool,
) -> bool {
self.dom_manipulation
- .has_main_document_lifecycle_task(root_document, document)
+ .has_document_lifecycle_task(is_current)
}
pub(crate) fn new(
@@ -1982,7 +1981,7 @@ impl RendererPageOwnedTaskSourcesTestHarness {
| RendererPageReadyDescriptor::DomManipulation {
owner: super::RendererPageDomManipulationOwner::ChildDocumentLifecycle(
_
- ),
+ ) | super::RendererPageDomManipulationOwner::ChildHostLoad(_),
..
}
)
@@ -1997,7 +1996,9 @@ impl RendererPageOwnedTaskSourcesTestHarness {
.map(|descriptor| match descriptor {
RendererPageReadyDescriptor::ChildFrameTask { owner, .. }
| RendererPageReadyDescriptor::DomManipulation {
- owner: super::RendererPageDomManipulationOwner::ChildDocumentLifecycle(owner),
+ owner:
+ super::RendererPageDomManipulationOwner::ChildDocumentLifecycle(owner)
+ | super::RendererPageDomManipulationOwner::ChildHostLoad(owner),
..
} => owner.target(),
_ => unreachable!("child semantic selector must retain its exact target"),
diff --git a/moli-renderer-v8/src/runtime/owner_local_store/bound.rs b/moli-renderer-v8/src/runtime/owner_local_store/bound.rs
index c9569b5783..a36709378d 100644
--- a/moli-renderer-v8/src/runtime/owner_local_store/bound.rs
+++ b/moli-renderer-v8/src/runtime/owner_local_store/bound.rs
@@ -768,16 +768,11 @@ pub(super) fn select_page_scheduler_turn(
document_lifecycle_owner_turn_is_runnable,
has_ready_main_parser_script_continuation,
);
- let has_lifecycle_dom_task = entry
- .page_vm()
- .vm()
- .current_main_document_task_owner()
- .is_some_and(|owner| {
- task_sources.has_queued_main_document_lifecycle_dom_task(
- entry.page_vm().document_lifecycle.identity().document,
- owner,
- )
- });
+ let has_lifecycle_dom_task = task_sources.has_queued_document_lifecycle_dom_task(|owner| {
+ entry
+ .page_vm()
+ .document_lifecycle_dom_owner_is_current(owner)
+ });
if has_lifecycle_dom_task
&& !matches!(
document_lifecycle,
diff --git a/moli-renderer-v8/src/runtime/page_vm/mod.rs b/moli-renderer-v8/src/runtime/page_vm/mod.rs
index 9dfc624f1e..54b4f1f313 100644
--- a/moli-renderer-v8/src/runtime/page_vm/mod.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/mod.rs
@@ -2126,6 +2126,20 @@ impl PageVm {
},
};
+ if expected == ChildFrameSemanticTurnKind::HostLoad {
+ return self
+ .page_task_executor_sources_for_test()
+ .has_scheduler_task_for_executor_test(|descriptor| {
+ matches!(
+ descriptor,
+ RendererPageReadyDescriptor::DomManipulation {
+ owner: RendererPageDomManipulationOwner::ChildHostLoad(_),
+ ..
+ }
+ )
+ });
+ }
+
if expected == ChildFrameSemanticTurnKind::DocumentLifecycle {
return self.page_task_executor_sources_for_test().has_scheduler_task_for_executor_test(|descriptor| {
matches!(descriptor,
diff --git a/moli-renderer-v8/src/runtime/page_vm/page_child_host_load_body_test_support.rs b/moli-renderer-v8/src/runtime/page_vm/page_child_host_load_body_test_support.rs
index 978d8b9df6..7b4bf195e3 100644
--- a/moli-renderer-v8/src/runtime/page_vm/page_child_host_load_body_test_support.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/page_child_host_load_body_test_support.rs
@@ -20,12 +20,17 @@ impl PageVm {
.take_scheduler_task_for_executor_test(|descriptor| {
matches!(
descriptor,
- RendererPageReadyDescriptor::ChildFrameTask { owner, .. }
+ RendererPageReadyDescriptor::DomManipulation {
+ owner: crate::page_task_queue::RendererPageDomManipulationOwner::ChildHostLoad(owner), ..
+ }
if matches!(owner.target(), RendererPageChildFrameTaskTarget::HostLoad(_))
)
})?;
- let RendererPageSchedulerTask::ChildFrameTask(task) = task else {
- unreachable!("HostLoad descriptor must dequeue a child-frame task")
+ let RendererPageSchedulerTask::DomManipulation(
+ crate::page_task_queue::RendererPageDomManipulationTask::ChildHostLoad(task),
+ ) = task
+ else {
+ unreachable!("HostLoad descriptor must dequeue its DOM task")
};
Some(self.apply_selected_page_child_host_load_turn(task))
}
diff --git a/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation.rs b/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation.rs
index a9610941e9..0610677f56 100644
--- a/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation.rs
@@ -10,6 +10,9 @@ impl PageVm {
task: RendererPageDomManipulationTask,
) -> anyhow::Result {
match task {
+ RendererPageDomManipulationTask::ChildHostLoad(task) => Ok(self
+ .apply_selected_page_child_host_load_turn(task)
+ .map_action(PageDomManipulationTurnAction::ChildHostLoad)),
RendererPageDomManipulationTask::ChildDocumentLifecycle(task) => Ok(self
.apply_selected_page_child_document_lifecycle_turn(task)
.map_action(PageDomManipulationTurnAction::ChildDocumentLifecycle)),
diff --git a/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation_task_completion.rs b/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation_task_completion.rs
index a39139e114..4dc1b2722f 100644
--- a/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation_task_completion.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/page_dom_manipulation_task_completion.rs
@@ -18,6 +18,9 @@ impl PageVm {
loader: &crate::network::ResourceRequestClient,
) -> Result<()> {
let completion = match action {
+ PageDomManipulationTurnAction::ChildHostLoad(action) => {
+ action.into_page_task_completion()
+ }
PageDomManipulationTurnAction::ChildDocumentLifecycle(action) => {
action.into_page_task_completion()
}
diff --git a/moli-renderer-v8/src/runtime/page_vm/page_selected_task_test_harness.rs b/moli-renderer-v8/src/runtime/page_vm/page_selected_task_test_harness.rs
index 5549dd0ced..945f41c1ea 100644
--- a/moli-renderer-v8/src/runtime/page_vm/page_selected_task_test_harness.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/page_selected_task_test_harness.rs
@@ -108,7 +108,9 @@ impl PageSelectedTaskTestSelector {
),
Self::ChildHostLoad => matches!(
descriptor,
- RendererPageReadyDescriptor::ChildFrameTask { owner, .. }
+ RendererPageReadyDescriptor::DomManipulation {
+ owner: crate::page_task_queue::RendererPageDomManipulationOwner::ChildHostLoad(owner), ..
+ }
if matches!(
owner.target(),
crate::page_task_queue::RendererPageChildFrameTaskTarget::HostLoad(_)
@@ -336,7 +338,12 @@ impl PageSelectedTaskTestSelector {
)
)
}
- (Self::ChildHostLoad, RendererPageSchedulerTask::ChildFrameTask(task)) => matches!(
+ (
+ Self::ChildHostLoad,
+ RendererPageSchedulerTask::DomManipulation(
+ RendererPageDomManipulationTask::ChildHostLoad(task),
+ ),
+ ) => matches!(
task.owner().target(),
crate::page_task_queue::RendererPageChildFrameTaskTarget::HostLoad(_)
),
diff --git a/moli-renderer-v8/src/runtime/page_vm/page_typed_immediate_source.rs b/moli-renderer-v8/src/runtime/page_vm/page_typed_immediate_source.rs
index 48f11b8e12..7159fd31fe 100644
--- a/moli-renderer-v8/src/runtime/page_vm/page_typed_immediate_source.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/page_typed_immediate_source.rs
@@ -49,6 +49,38 @@ impl StaleWindowDocumentTaskAdmission {
}
impl PageVm {
+ pub(in crate::runtime) fn document_lifecycle_dom_owner_is_current(
+ &self,
+ owner: crate::page_task_queue::RendererPageDomManipulationOwner,
+ ) -> bool {
+ use crate::page_task_queue::{
+ RendererPageChildFrameTaskTarget, RendererPageDomManipulationOwner,
+ };
+ let root_document = self.document_lifecycle.identity().document;
+ match owner {
+ RendererPageDomManipulationOwner::MainDocumentLifecycle(owner) => {
+ owner.root_document == root_document
+ && self.vm().current_main_document_task_owner() == Some(owner.body.owner())
+ }
+ RendererPageDomManipulationOwner::ChildDocumentLifecycle(owner) => {
+ let RendererPageChildFrameTaskTarget::DocumentLifecycle(target) = owner.target()
+ else {
+ unreachable!("child lifecycle DOM task must retain its typed target");
+ };
+ owner.root_document() == root_document
+ && self.vm().current_child_document_lifecycle_target(target) == Some(target)
+ }
+ RendererPageDomManipulationOwner::ChildHostLoad(owner) => {
+ let RendererPageChildFrameTaskTarget::HostLoad(target) = owner.target() else {
+ unreachable!("child load DOM task must retain its typed target");
+ };
+ owner.root_document() == root_document
+ && self.vm().current_child_host_load_target(target) == Some(target)
+ }
+ _ => false,
+ }
+ }
+
pub(in crate::runtime::page_vm) fn authorize_current_window_document_task(
&self,
task: T,
diff --git a/moli-renderer-v8/src/script_vm.rs b/moli-renderer-v8/src/script_vm.rs
index 3b7f61197b..5fdfbc4b47 100644
--- a/moli-renderer-v8/src/script_vm.rs
+++ b/moli-renderer-v8/src/script_vm.rs
@@ -5292,6 +5292,23 @@ impl ScriptVm {
},
};
+ if expected == ChildFrameSemanticTurnKind::HostLoad {
+ return self
+ ._page_task_residence_for_executor_test
+ .as_ref()
+ .expect("semantic fixture must retain its sources")
+ .task_sources()
+ .has_scheduler_task_for_executor_test(|descriptor| {
+ matches!(
+ descriptor,
+ RendererPageReadyDescriptor::DomManipulation {
+ owner: RendererPageDomManipulationOwner::ChildHostLoad(_),
+ ..
+ }
+ )
+ });
+ }
+
if expected == ChildFrameSemanticTurnKind::DocumentLifecycle {
return self._page_task_residence_for_executor_test.as_ref().expect("semantic fixture must retain its sources").task_sources().has_scheduler_task_for_executor_test(|descriptor| {
matches!(descriptor,
diff --git a/moli-renderer-v8/src/script_vm/child_host_load.rs b/moli-renderer-v8/src/script_vm/child_host_load.rs
index 5b07ac29e7..6755077760 100644
--- a/moli-renderer-v8/src/script_vm/child_host_load.rs
+++ b/moli-renderer-v8/src/script_vm/child_host_load.rs
@@ -116,16 +116,19 @@ impl ScriptVm {
let Some(task) = source.take_scheduler_task_for_executor_test(|descriptor| {
matches!(
descriptor,
- crate::page_task_queue::RendererPageReadyDescriptor::ChildFrameTask {
- owner,
+ crate::page_task_queue::RendererPageReadyDescriptor::DomManipulation {
+ owner: crate::page_task_queue::RendererPageDomManipulationOwner::ChildHostLoad(owner),
..
} if matches!(owner.target(), RendererPageChildFrameTaskTarget::HostLoad(_))
)
}) else {
return Ok(None);
};
- let crate::page_task_queue::RendererPageSchedulerTask::ChildFrameTask(task) = task else {
- unreachable!("child-frame descriptor must dequeue its own family source")
+ let crate::page_task_queue::RendererPageSchedulerTask::DomManipulation(
+ crate::page_task_queue::RendererPageDomManipulationTask::ChildHostLoad(task),
+ ) = task
+ else {
+ unreachable!("HostLoad descriptor must dequeue its DOM task")
};
let RendererPageChildFrameTaskTarget::HostLoad(target) = task.owner().target() else {
unreachable!("HostLoad selector must only dequeue HostLoad tasks")
diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs
index b98ad76687..78bbee2e0a 100644
--- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs
+++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs
@@ -14528,6 +14528,25 @@ async fn moving_pending_child_image_rebinds_event_without_consuming_new_request(
"moving the image should release complete on a later lifecycle turn",
)
.await;
+ assert!(
+ !vm.has_ready_child_frame_semantic_turn_for_test(ChildFrameSemanticTurnKind::HostLoad),
+ "the rebound image event must keep its earlier position in the shared DOM FIFO"
+ );
+ assert!(
+ vm.apply_next_image_load_event_body_for_test()
+ .expect("rebound image DOM task"),
+ "the new image request must deliver before the later HostLoad task"
+ );
+ assert_eq!(
+ vm.eval("__movedChildImageEvents.join('|')")
+ .expect("rebound image trace"),
+ "child-dcl|moved-image-load",
+ "the rebound image request must deliver once without running the later child load"
+ );
+ assert!(
+ vm.has_ready_child_frame_semantic_turn_for_test(ChildFrameSemanticTurnKind::HostLoad),
+ "the child load must now be at the DOM FIFO head"
+ );
expect_child_frame_task_source_after_realm_prerequisite(
&mut vm,
ChildFrameSemanticTurnKind::HostLoad,
@@ -14535,20 +14554,14 @@ async fn moving_pending_child_image_rebinds_event_without_consuming_new_request(
)
.await;
assert_eq!(
- vm.eval("globalThis.__movedChildImageEvents.join('|')")
- .expect("moved image child lifecycle should evaluate"),
- "child-dcl|frame-load",
- "moving the image must release the old child delay through a later lifecycle turn"
- );
-
- assert!(
- drain_image_load_event_bodies_for_test(&mut vm) > 0,
- "old and rebound image tasks should drain"
+ drain_image_load_event_bodies_for_test(&mut vm),
+ 0,
+ "the old and rebound image tasks must both have been consumed exactly once"
);
assert_eq!(
vm.eval("globalThis.__movedChildImageEvents.join('|')")
.expect("moved image event result should evaluate"),
- "child-dcl|frame-load|moved-image-load",
+ "child-dcl|moved-image-load|frame-load",
"the stale child task must not consume or duplicate the rebound image request"
);
}