From a4b974547aa22efea854228c879a1dced5a3e347 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Thu, 10 Sep 2026 02:25:06 +0800 Subject: [PATCH] fix(html): queue child load delivery through the DOM source Queue iframe load delivery alongside other DOM tasks and include current child lifecycle owners in the existing lifecycle scheduling preference. This keeps initial load ahead of module timers that replace the document. Cover inline, imported, external, and top-level-await modules using parent and child timers. Check adopted-image events against the shared DOM FIFO. Update the WPT ledgers for the three repaired delayed module-write cases. Validation: - cargo fmt --all - cargo clippy --workspace --all-targets --all-features -- -D warnings - cargo nextest run --no-fail-fast: 17,875 passed, 13 skipped - 270 WPT cases: 254 to 257 passes, no case or subtest regressions --- .../wpt-cross-current/failed-cases.txt | 3 - .../wpt-cross-current/passed-cases.txt | 3 + .../tests/scripts/module_document_write.rs | 89 +++++++++++++++++++ .../native_bridge/context_host/host_loads.rs | 11 ++- .../src/page_task_queue/child_frame_task.rs | 7 -- .../src/page_task_queue/dom_manipulation.rs | 35 ++++++-- .../src/page_task_queue/owner_sources.rs | 13 +-- .../src/runtime/owner_local_store/bound.rs | 15 ++-- moli-renderer-v8/src/runtime/page_vm/mod.rs | 14 +++ .../page_child_host_load_body_test_support.rs | 11 ++- .../runtime/page_vm/page_dom_manipulation.rs | 3 + .../page_dom_manipulation_task_completion.rs | 3 + .../page_selected_task_test_harness.rs | 11 ++- .../page_vm/page_typed_immediate_source.rs | 32 +++++++ moli-renderer-v8/src/script_vm.rs | 17 ++++ .../src/script_vm/child_host_load.rs | 11 ++- .../tests/dom_elements/dom_surface.rs | 33 ++++--- 17 files changed, 256 insertions(+), 55 deletions(-) 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" ); }