mirror of
https://github.com/lexmount/moli.git
synced 2026-10-01 00:00:48 +00:00
fix(document): initialize child navigation readiness
This commit is contained in:
@@ -22,6 +22,9 @@ mod preparation_errors;
|
||||
#[path = "scripts/child_error_insertion.rs"]
|
||||
mod child_error_insertion;
|
||||
|
||||
#[path = "scripts/child_readiness.rs"]
|
||||
mod child_readiness;
|
||||
|
||||
fn diagnostic_global<'a>(
|
||||
page: &'a moli_core::page::Page,
|
||||
name: &str,
|
||||
|
||||
@@ -28,7 +28,7 @@ async fn child_script_error_write_probe(
|
||||
parent.childErrorObserved({text: document.body.textContent,
|
||||
currentScriptIsNull: document.currentScript === null,
|
||||
tailMissing: document.getElementById('tail') === null,
|
||||
eventType: event.type,
|
||||
readyState: document.readyState, eventType: event.type,
|
||||
nestedRan: globalThis.nestedRan === true});
|
||||
}`;
|
||||
frame.srcdoc = '<!doctype html><head><script>' + handler + '<' + '/script></head>' +
|
||||
@@ -73,3 +73,10 @@ async fn child_parser_fetch_error_write_executes_nested_inline_scripts() -> Resu
|
||||
assert_eq!(result["nestedRan"], true, "{result}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn child_parser_fetch_error_handler_observes_loading_document() -> Result<()> {
|
||||
let result = child_script_error_write_probe(false, false).await?;
|
||||
assert_eq!(result["during"]["readyState"], "loading", "{result}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use super::*;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn child_document_readiness_starts_loading_before_parser_scripts() -> Result<()> {
|
||||
let server = FixtureServer::spawn().await?;
|
||||
let browser = Browser::new(AppConfig::default())?;
|
||||
let mut page = browser.fetch(&server.url("/static")).await?;
|
||||
let result = page
|
||||
.evaluate_runtime_expression_with_await_async(
|
||||
r#"(async () => {
|
||||
const srcdoc = await new Promise(resolve => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.onload = () => {
|
||||
resolve({states: frame.contentWindow.readyStates,
|
||||
readyState: frame.contentDocument.readyState});
|
||||
frame.remove();
|
||||
};
|
||||
frame.srcdoc = '<!doctype html><head><script>' +
|
||||
'window.readyStates = [document.readyState];' +
|
||||
'document.addEventListener("readystatechange", () => readyStates.push(document.readyState));' +
|
||||
'<' + '/script></head><body>ready';
|
||||
document.body.append(frame);
|
||||
});
|
||||
const network = await new Promise(resolve => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.onload = () => {
|
||||
resolve({duringScriptLoad: frame.contentWindow.parserConnectedLoadWriteReadyState,
|
||||
readyState: frame.contentDocument.readyState});
|
||||
frame.remove();
|
||||
};
|
||||
frame.src = '/compat/parser-connected-external-classic-load-document-write-insertion-point';
|
||||
document.body.append(frame);
|
||||
});
|
||||
return JSON.stringify({srcdoc, network, parent: document.readyState});
|
||||
})()"#,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
server.shutdown().await;
|
||||
let value: serde_json::Value =
|
||||
serde_json::from_str(result["value"].as_str().expect("child readiness probe"))?;
|
||||
assert_eq!(
|
||||
value,
|
||||
serde_json::json!({
|
||||
"srcdoc": {"states": ["loading", "interactive", "complete"], "readyState": "complete"},
|
||||
"network": {"duringScriptLoad": "loading", "readyState": "complete"},
|
||||
"parent": "complete"
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn child_document_readiness_keeps_initial_empty_and_detached_documents_complete() -> Result<()>
|
||||
{
|
||||
let server = FixtureServer::spawn().await?;
|
||||
let browser = Browser::new(AppConfig::default())?;
|
||||
let mut page = browser.fetch(&server.url("/static")).await?;
|
||||
let result = page
|
||||
.evaluate_runtime_expression_with_await_async(
|
||||
r#"(() => {
|
||||
const frame = document.createElement('iframe');
|
||||
document.body.append(frame);
|
||||
const initialEmpty = frame.contentDocument.readyState;
|
||||
frame.remove();
|
||||
return JSON.stringify({initialEmpty,
|
||||
detachedHtml: new DOMParser().parseFromString('<p>detached', 'text/html').readyState,
|
||||
detachedXml: new DOMParser().parseFromString('<detached/>', 'application/xml').readyState,
|
||||
createdHtml: document.implementation.createHTMLDocument('').readyState,
|
||||
constructed: new Document().readyState});
|
||||
})()"#,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
server.shutdown().await;
|
||||
let value: serde_json::Value =
|
||||
serde_json::from_str(result["value"].as_str().expect("detached readiness probe"))?;
|
||||
assert_eq!(
|
||||
value,
|
||||
serde_json::json!({
|
||||
"initialEmpty": "complete", "detachedHtml": "complete", "detachedXml": "complete",
|
||||
"createdHtml": "complete", "constructed": "complete"
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -5,7 +5,10 @@ use crate::frame_owner_model::{
|
||||
DocumentCreationKind, FrameDocumentInteractiveLifecycleAction,
|
||||
FrameDocumentLocalWindowTransition, FrameLocalWindowOwnerTransition,
|
||||
};
|
||||
use crate::{document_runtime::DomHandle, frame_owner_model::FrameDocumentOwnerTransition};
|
||||
use crate::{
|
||||
document_runtime::DomHandle, dom::native::DocumentReadyState,
|
||||
frame_owner_model::FrameDocumentOwnerTransition,
|
||||
};
|
||||
use moli_web_mime::is_dom_parser_xml_mime;
|
||||
use url::Url;
|
||||
|
||||
@@ -200,6 +203,10 @@ impl JsContextHost {
|
||||
);
|
||||
document_handle
|
||||
};
|
||||
// Navigation-created Documents start loading before parser scripts can
|
||||
// observe them. Keep the generic detached/initial-empty default complete.
|
||||
let _ = self
|
||||
.set_dom_document_ready_state_for_handle(document_handle, DocumentReadyState::Loading);
|
||||
let document_url = self.document_url_for_handle(document_handle);
|
||||
let document_base_url = self.document_base_url_for_handle(document_handle);
|
||||
let parser_document_url = document_url.clone();
|
||||
|
||||
@@ -3674,8 +3674,8 @@ async fn page_vm_child_parser_blocking_classic_waits_for_preceding_stylesheet()
|
||||
page_vm
|
||||
.vm_mut()
|
||||
.eval("__childParserStylesheetEvents.join('|')")?,
|
||||
"script:stylesheet-ready:complete",
|
||||
"stylesheet source must be installed before the parser-blocking script executes"
|
||||
"script:stylesheet-ready:loading",
|
||||
"stylesheet source must be installed before the parser-blocking script executes while the document is still loading"
|
||||
);
|
||||
followup_sources.push(
|
||||
run_expected_child_frame_task_source_after_realm_prerequisite_for_wait(
|
||||
@@ -3752,7 +3752,7 @@ async fn page_vm_child_parser_blocking_classic_waits_for_preceding_stylesheet()
|
||||
ChildFrameSemanticTurnKind::HostLoad,
|
||||
]
|
||||
);
|
||||
assert_eq!(final_events, "script:stylesheet-ready:complete|load");
|
||||
assert_eq!(final_events, "script:stylesheet-ready:loading|load");
|
||||
server.await.expect("child parser stylesheet server should finish");
|
||||
})
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user