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" ); }