mirror of
https://github.com/lexmount/moli.git
synced 2026-10-06 00:00:56 +00:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!(
|
||||
"<script type=module>import {};</script>",
|
||||
serde_json::to_string(&module_url(&source))?
|
||||
),
|
||||
"external" => format!(
|
||||
"<script type=module src=\"{}\"></script>",
|
||||
module_url(&source)
|
||||
),
|
||||
_ => format!("<script type=module>{source}</script>"),
|
||||
};
|
||||
let child = markup_url(
|
||||
&server,
|
||||
&format!("<!doctype html><head>{script}</head><body>original"),
|
||||
);
|
||||
let parent = format!(
|
||||
r#"<!doctype html><body><script>
|
||||
window.events = [];
|
||||
window.writeDone = new Promise(resolve => window.finishWrite = resolve);
|
||||
const frame = document.createElement('iframe');
|
||||
frame.id = 'target';
|
||||
frame.onload = () => events.push('load:' + frame.contentDocument.body.textContent);
|
||||
frame.src = {};
|
||||
document.body.append(frame);
|
||||
</script>"#,
|
||||
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<_>>(),
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ impl PageVm {
|
||||
task: RendererPageDomManipulationTask,
|
||||
) -> anyhow::Result<PageDomManipulationTurnOutcome> {
|
||||
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)),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(_)
|
||||
),
|
||||
|
||||
@@ -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<T, K: Eq>(
|
||||
&self,
|
||||
task: T,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user