mirror of
https://github.com/lexmount/moli.git
synced 2026-10-06 16:01:00 +00:00
fix(runtime): admit document lifecycle during awaited evaluation
This commit is contained in:
@@ -6711,3 +6711,40 @@ async fn parse_time_lifecycle_queue_can_stop_cleanly_at_load_stage_after_load_mi
|
||||
|
||||
#[path = "scripts/document_write_errors.rs"]
|
||||
mod document_write_errors;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn main_document_close_can_resolve_an_awaited_lifecycle_promise() -> Result<()> {
|
||||
let server = FixtureServer::spawn().await?;
|
||||
let browser = Browser::new(AppConfig::default())?;
|
||||
let mut url = url::Url::parse(&server.url("/compat/child-dynamic-markup-document"))?;
|
||||
url.query_pairs_mut()
|
||||
.append_pair("markup", "<!doctype html><body>initial");
|
||||
let mut page = browser.fetch(url.as_str()).await?;
|
||||
let result = tokio::time::timeout(
|
||||
Duration::from_secs(10),
|
||||
page.evaluate_runtime_expression_with_await_async(
|
||||
r#"new Promise(resolve => {
|
||||
document.open();
|
||||
document.addEventListener('DOMContentLoaded', () => resolve(JSON.stringify({
|
||||
readyState: document.readyState,
|
||||
text: document.getElementById('replacement').textContent
|
||||
})), {once: true});
|
||||
document.write('<!doctype html><body><p id=replacement>replacement</p>');
|
||||
document.close();
|
||||
})"#,
|
||||
true,
|
||||
),
|
||||
)
|
||||
.await??;
|
||||
let observed: serde_json::Value = serde_json::from_str(
|
||||
result["value"]
|
||||
.as_str()
|
||||
.expect("replacement lifecycle observation"),
|
||||
)?;
|
||||
assert_eq!(
|
||||
observed,
|
||||
serde_json::json!({"readyState": "interactive", "text": "replacement"})
|
||||
);
|
||||
server.shutdown().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -592,7 +592,10 @@ pub(in crate::runtime) async fn advance_runtime_expression_await_turn_on_entry_v
|
||||
) -> (LivePageEntry, Result<PageVmRuntimeExpressionAwaitAdvance>) {
|
||||
run_entry_on_bound_owner_local_store_local_task(local_executor, entry, move |entry| {
|
||||
Box::pin(async move {
|
||||
entry
|
||||
let replacement_lifecycle_snapshot = entry
|
||||
.page_vm()
|
||||
.document_replacement_lifecycle_action_snapshot();
|
||||
let evaluation = entry
|
||||
.page_vm_mut()
|
||||
.advance_runtime_expression_await_turn(
|
||||
execution_context_id,
|
||||
@@ -601,7 +604,29 @@ pub(in crate::runtime) async fn advance_runtime_expression_await_turn_on_entry_v
|
||||
remaining,
|
||||
result_mode,
|
||||
)
|
||||
.await
|
||||
.await;
|
||||
// An awaited expression can synchronously open and close a new
|
||||
// Document, then return a promise resolved by its DCL/load tasks.
|
||||
// Admit those tasks at this action boundary, before awaiting the
|
||||
// result, including when evaluation reports an error after mutation.
|
||||
let reconciliation = {
|
||||
let (page_vm, pending_document_lifecycle_turn) =
|
||||
entry.page_vm_and_document_lifecycle_turn_mut();
|
||||
page_vm
|
||||
.reconcile_document_replacement_lifecycle_after_owner_action(
|
||||
replacement_lifecycle_snapshot,
|
||||
pending_document_lifecycle_turn,
|
||||
)
|
||||
.await
|
||||
};
|
||||
match (evaluation, reconciliation) {
|
||||
(Ok(outcome), Ok(_)) => Ok(outcome),
|
||||
(Err(evaluation_error), Ok(_)) => Err(evaluation_error),
|
||||
(Ok(_), Err(reconciliation_error)) => Err(reconciliation_error),
|
||||
(Err(evaluation_error), Err(reconciliation_error)) => Err(anyhow!(
|
||||
"runtime evaluation failed ({evaluation_error:#}) and its Document replacement lifecycle reconciliation also failed ({reconciliation_error:#})"
|
||||
)),
|
||||
}
|
||||
})
|
||||
})
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user