refactor(parser): simplify resume identity

This commit is contained in:
ldm0
2026-08-12 20:45:32 +08:00
parent d327134e17
commit 64c016d3ea
10 changed files with 38 additions and 173 deletions
+2 -3
View File
@@ -60,8 +60,7 @@ use crate::{
frame_owner_model::FrameDocumentTaskOwner,
live_document_parser::{
DocumentParserLifetime, DocumentParserRunState, DocumentParserSession,
DocumentParserSessionControlHandle, ParserResumeApplication, ParserResumePermit,
ParserSuspensionCause,
DocumentParserSessionControlHandle, ParserResumePermit, ParserSuspensionCause,
},
module_runtime::ModuleMapKey,
network::ResourceRequestClient,
@@ -238,7 +237,7 @@ impl ParserInsertionController {
self.parser_control.suspend(cause)
}
pub(crate) fn resume(&self, permit: ParserResumePermit) -> ParserResumeApplication {
pub(crate) fn resume(&self, permit: ParserResumePermit) -> bool {
self.parser_control.resume(permit)
}
@@ -320,19 +320,15 @@ impl ParserElementCreationConsumer for DocumentWriteParserMutationOwner<'_, '_,
// "HTML string -> parser output -> document.write/fragment effects" rather than mixing two different
// mutation models in one refactor slice.
impl DocumentRuntime {
pub(crate) fn start_root_document_parser_stream(
&mut self,
owner: crate::frame_owner_model::FrameDocumentTaskOwner,
) {
pub(crate) fn start_root_document_parser_stream(&mut self) {
debug_assert!(
self.root_document_parser.is_none(),
"opening a root document must discard the previous parser stream first"
);
let mut parser = DocumentParserSession::start_open_live_document(
let parser = DocumentParserSession::start_open_live_document(
self.document_url().clone(),
self.document_handle(),
);
parser.bind_owner(owner, self.runtime_reset_generation());
self.root_document_parser = Some(parser);
}
@@ -1614,12 +1610,9 @@ impl DocumentRuntime {
}
let resumed = match insertion.parser_insertion_controller.run_state() {
DocumentParserRunState::Ready => false,
DocumentParserRunState::Suspended { .. } => {
insertion
.parser_insertion_controller
.resume(insertion.resume_permit)
== ParserResumeApplication::Resumed
}
DocumentParserRunState::Suspended { .. } => insertion
.parser_insertion_controller
.resume(insertion.resume_permit),
DocumentParserRunState::Pumping { .. }
| DocumentParserRunState::Finishing
| DocumentParserRunState::Finished
@@ -2761,13 +2754,7 @@ mod tests {
let document = HtmlParser.parse(document_url, "<!doctype html>".to_owned());
let mut runtime = DocumentRuntime::new(&document);
runtime.start_root_document_parser_stream(
crate::frame_owner_model::FrameDocumentTaskOwner::new(
crate::frame_owner_model::FrameSchedulerLaneId(1),
crate::frame_owner_model::LocalWindowId(1),
crate::frame_owner_model::DocumentId(1),
),
);
runtime.start_root_document_parser_stream();
let parser = runtime
.root_document_parser
+16 -122
View File
@@ -1,7 +1,6 @@
use crate::{
DocumentOwnedBlockingStylesheetDiscoveryInput,
dom::native::{DomHost, NativeNodeId},
frame_owner_model::FrameDocumentTaskOwner,
parser::{
DocumentStream, HtmlParser, ParserBlockingStylesheetPause,
ParserCustomElementConstructionHandoff, ParserDomMutationConsumer, ParserDomReadConsumer,
@@ -194,21 +193,6 @@ pub(crate) struct ParserSessionId(u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ParserSuspensionId(u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct DocumentParserOwnerIdentity {
document_owner: FrameDocumentTaskOwner,
runtime_generation: u64,
}
impl DocumentParserOwnerIdentity {
fn new(document_owner: FrameDocumentTaskOwner, runtime_generation: u64) -> Self {
Self {
document_owner,
runtime_generation,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ParserSuspensionCause {
ParserClassicSource { script: NativeNodeId },
@@ -221,12 +205,10 @@ pub(crate) enum ParserSuspensionCause {
struct ParserSuspension {
id: ParserSuspensionId,
cause: ParserSuspensionCause,
parser_commit_epoch: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ParserResumePermit {
owner_identity: Option<DocumentParserOwnerIdentity>,
session_id: ParserSessionId,
suspension_id: ParserSuspensionId,
}
@@ -247,7 +229,6 @@ pub(crate) enum DocumentParserRunState {
Suspended {
id: ParserSuspensionId,
cause: ParserSuspensionCause,
parser_commit_epoch: u64,
},
Finishing,
Finished,
@@ -259,7 +240,6 @@ impl From<ParserSuspension> for DocumentParserRunState {
Self::Suspended {
id: suspension.id,
cause: suspension.cause,
parser_commit_epoch: suspension.parser_commit_epoch,
}
}
}
@@ -267,36 +247,17 @@ impl From<ParserSuspension> for DocumentParserRunState {
impl DocumentParserRunState {
fn suspension(self) -> Option<ParserSuspension> {
match self {
Self::Suspended {
id,
cause,
parser_commit_epoch,
} => Some(ParserSuspension {
id,
cause,
parser_commit_epoch,
}),
Self::Suspended { id, cause } => Some(ParserSuspension { id, cause }),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ParserResumeApplication {
Resumed,
RejectedSession,
RejectedOwner,
RejectedSuspension,
ParserNotSuspended,
}
#[derive(Debug)]
struct DocumentParserSessionControl {
session_id: ParserSessionId,
owner_identity: Option<DocumentParserOwnerIdentity>,
next_suspension_id: u64,
next_pump_epoch: u64,
parser_commit_epoch: u64,
run_state: DocumentParserRunState,
}
@@ -309,10 +270,8 @@ impl DocumentParserSessionControlHandle {
ParserSessionId(NEXT_DOCUMENT_PARSER_SESSION_ID.fetch_add(1, Ordering::Relaxed));
Self(Rc::new(RefCell::new(DocumentParserSessionControl {
session_id,
owner_identity: None,
next_suspension_id: 1,
next_pump_epoch: 1,
parser_commit_epoch: 0,
run_state: DocumentParserRunState::Ready,
})))
}
@@ -325,17 +284,6 @@ impl DocumentParserSessionControlHandle {
self.0.borrow().run_state
}
fn bind_owner(&self, owner_identity: DocumentParserOwnerIdentity) {
let mut control = self.0.borrow_mut();
match control.owner_identity {
None => control.owner_identity = Some(owner_identity),
Some(current) => assert_eq!(
current, owner_identity,
"one live parser session cannot be rebound to a different Document owner"
),
}
}
pub(crate) fn suspend(&self, cause: ParserSuspensionCause) -> ParserResumePermit {
let mut control = self.0.borrow_mut();
assert_eq!(
@@ -348,11 +296,9 @@ impl DocumentParserSessionControlHandle {
let suspension = ParserSuspension {
id: suspension_id,
cause,
parser_commit_epoch: control.parser_commit_epoch,
};
control.run_state = suspension.into();
ParserResumePermit {
owner_identity: control.owner_identity,
session_id: control.session_id,
suspension_id,
}
@@ -362,28 +308,24 @@ impl DocumentParserSessionControlHandle {
let control = self.0.borrow();
let suspension = control.run_state.suspension()?;
Some(ParserResumePermit {
owner_identity: control.owner_identity,
session_id: control.session_id,
suspension_id: suspension.id,
})
}
pub(crate) fn resume(&self, permit: ParserResumePermit) -> ParserResumeApplication {
pub(crate) fn resume(&self, permit: ParserResumePermit) -> bool {
let mut control = self.0.borrow_mut();
if permit.session_id != control.session_id {
return ParserResumeApplication::RejectedSession;
}
if permit.owner_identity != control.owner_identity {
return ParserResumeApplication::RejectedOwner;
return false;
}
let Some(suspension) = control.run_state.suspension() else {
return ParserResumeApplication::ParserNotSuspended;
return false;
};
if suspension.id != permit.suspension_id {
return ParserResumeApplication::RejectedSuspension;
return false;
}
control.run_state = DocumentParserRunState::Ready;
ParserResumeApplication::Resumed
true
}
pub(crate) fn begin_pump(&self) -> DocumentParserPumpGuard {
@@ -395,7 +337,6 @@ impl DocumentParserSessionControlHandle {
);
let epoch = control.next_pump_epoch;
control.next_pump_epoch = control.next_pump_epoch.wrapping_add(1).max(1);
control.parser_commit_epoch = control.parser_commit_epoch.wrapping_add(1);
control.run_state = DocumentParserRunState::Pumping { epoch };
drop(control);
DocumentParserPumpGuard {
@@ -622,17 +563,6 @@ impl DocumentParserSession {
.expect("a finished parser session no longer owns a backend")
}
pub(crate) fn bind_owner(
&mut self,
document_owner: FrameDocumentTaskOwner,
runtime_generation: u64,
) {
self.control.bind_owner(DocumentParserOwnerIdentity::new(
document_owner,
runtime_generation,
));
}
pub(crate) fn run_state(&self) -> DocumentParserRunState {
self.control.run_state()
}
@@ -649,7 +579,7 @@ impl DocumentParserSession {
self.control.current_resume_permit()
}
pub(crate) fn resume(&mut self, permit: ParserResumePermit) -> ParserResumeApplication {
pub(crate) fn resume(&mut self, permit: ParserResumePermit) -> bool {
self.control.resume(permit)
}
@@ -1033,15 +963,6 @@ fn unwrap_exclusive_xml_parser_stream(stream: XmlDocumentParserStreamHandle) ->
#[cfg(test)]
mod session_state_tests {
use super::*;
use crate::frame_owner_model::{DocumentId, FrameSchedulerLaneId, LocalWindowId};
fn owner(lane: u64, window: u64, document: u64) -> FrameDocumentTaskOwner {
FrameDocumentTaskOwner::new(
FrameSchedulerLaneId(lane),
LocalWindowId(window),
DocumentId(document),
)
}
fn session() -> DocumentParserSession {
DocumentParserSession::start_finite_live_document(
@@ -1053,59 +974,34 @@ mod session_state_tests {
#[test]
fn parser_resume_permit_is_exact_and_one_shot() {
let mut parser = session();
let owner = owner(1, 2, 3);
parser.bind_owner(owner, 7);
let permit = parser.suspend(ParserSuspensionCause::ParserClassicSource {
script: NativeNodeId::new(8),
});
assert_eq!(
permit.owner_identity,
Some(DocumentParserOwnerIdentity::new(owner, 7))
);
assert_eq!(permit.session_id, parser.control.session_id());
assert_eq!(parser.resume(permit), ParserResumeApplication::Resumed);
assert_eq!(
parser.resume(permit),
ParserResumeApplication::ParserNotSuspended,
assert!(parser.resume(permit));
assert!(
!parser.resume(permit),
"a copied permit cannot resume the same suspension twice"
);
}
#[test]
fn parser_resume_rejects_wrong_owner_session_and_suspension() {
fn parser_resume_rejects_wrong_session_and_suspension() {
let mut parser = session();
let parser_owner = owner(1, 2, 3);
parser.bind_owner(parser_owner, 11);
let first = parser.suspend(ParserSuspensionCause::ParserCreatedStylesheet {
owner: NativeNodeId::new(5),
});
let wrong_owner = ParserResumePermit {
owner_identity: Some(DocumentParserOwnerIdentity::new(owner(9, 9, 9), 11)),
..first
};
assert_eq!(
parser.resume(wrong_owner),
ParserResumeApplication::RejectedOwner
);
let mut other = session();
other.bind_owner(parser_owner, 11);
assert_eq!(
other.resume(first),
ParserResumeApplication::RejectedSession
);
assert!(!other.resume(first));
assert_eq!(parser.resume(first), ParserResumeApplication::Resumed);
assert!(parser.resume(first));
let second = parser.suspend(ParserSuspensionCause::DocumentWriteExternalScript {
script: NativeNodeId::new(6),
});
assert_eq!(
parser.resume(first),
ParserResumeApplication::RejectedSuspension
);
assert_eq!(parser.resume(second), ParserResumeApplication::Resumed);
assert!(!parser.resume(first));
assert!(parser.resume(second));
}
#[test]
@@ -1114,8 +1010,6 @@ mod session_state_tests {
Url::parse("https://parser-session.test/").expect("test URL"),
NativeNodeId::new(1),
);
let parser_owner = owner(1, 2, 3);
parser.bind_owner(parser_owner, 11);
let permit = parser.suspend(ParserSuspensionCause::ParserClassicSource {
script: NativeNodeId::new(8),
});
@@ -1133,7 +1027,7 @@ mod session_state_tests {
);
assert_eq!(parser.current_resume_permit(), Some(permit));
assert_eq!(parser.resume(permit), ParserResumeApplication::Resumed);
assert!(parser.resume(permit));
assert_eq!(
parser.request_close(),
DocumentParserCloseDisposition::DrainNow,
@@ -15,7 +15,7 @@ use crate::{
live_document_parser::{
DocumentParserCloseDisposition, DocumentParserLifetime, DocumentParserRunState,
DocumentParserSession, LiveDocumentParserOwner, LiveDocumentParserStepOutcome,
ParserResumeApplication, ParserSuspensionCause,
ParserSuspensionCause,
},
modulepreload::{
invalid_modulepreload_as_value, invalid_modulepreload_as_warning,
@@ -999,10 +999,7 @@ impl JsContextHost {
.current_child_document_task_owner(child_handle)
.expect("committed child document-open parser must have a task owner");
assert_eq!(task_owner.document_owner(), owner);
let runtime_generation = self.runtime_reset_generation();
let mut parser =
DocumentParserSession::start_open_live_document(document_url, document_handle);
parser.bind_owner(task_owner, runtime_generation);
let parser = DocumentParserSession::start_open_live_document(document_url, document_handle);
self.child_document_parsers.replace(owner, parser);
}
@@ -1515,7 +1512,6 @@ impl JsContextHost {
.current_child_document_task_owner(child_handle)
.expect("committed child parser must have a current task owner");
assert_eq!(task_owner.document_owner(), owner);
parser.bind_owner(task_owner, self.runtime_reset_generation());
parser.queue_arrived_chunk(markup.to_owned());
parser.declare_eof();
let outcome = self.drive_live_child_document_parser(
@@ -1614,7 +1610,7 @@ impl JsContextHost {
FrameDocumentClassicParserResumeSkipReason::StaleParserSuspension,
);
};
if entry.resume(permit) != ParserResumeApplication::Resumed {
if !entry.resume(permit) {
self.child_document_parsers.replace(owner, entry);
return FrameDocumentClassicParserResumeApplication::skipped(
FrameDocumentClassicParserResumeSkipReason::StaleParserSuspension,
@@ -1703,7 +1699,7 @@ impl JsContextHost {
{
entry
.current_resume_permit()
.is_some_and(|permit| entry.resume(permit) == ParserResumeApplication::Resumed)
.is_some_and(|permit| entry.resume(permit))
}
DocumentParserRunState::Pumping { .. }
| DocumentParserRunState::Suspended { .. }
@@ -3,8 +3,7 @@ use crate::live_document_parser::DocumentParserLifetime;
use crate::{
frame_owner_model::FrameDocumentOwner,
live_document_parser::{
DocumentParserSession, ParserResumeApplication, ParserResumePermit, ParserStopReason,
ParserSuspensionCause,
DocumentParserSession, ParserResumePermit, ParserStopReason, ParserSuspensionCause,
},
};
use std::collections::HashMap;
@@ -83,7 +82,7 @@ impl ChildDocumentParserStore {
&mut self,
owner: FrameDocumentOwner,
permit: ParserResumePermit,
) -> Option<ParserResumeApplication> {
) -> Option<bool> {
self.sessions
.get_mut(&owner)
.map(|parser| parser.resume(permit))
@@ -27,7 +27,6 @@ use crate::{
FrameRequestKind, FrameScriptJob, LocalWindowId, PendingChildExternalClassicDocumentScript,
frame_script_job_kind_from_parser_classic_ready_kind,
},
live_document_parser::ParserResumeApplication,
page_task_queue::RendererPageChildClassicScriptSourceLoadTarget,
parser_script::action::{
ParserPendingClassicScriptExecution, ParserPendingClassicScriptNotification,
@@ -1729,7 +1728,7 @@ impl JsContextHost {
&& self
.child_document_parsers
.resume_parser_script_for_execution(owner, permit)
!= Some(ParserResumeApplication::Resumed)
!= Some(true)
{
tracing::debug!(
child_handle = ?child_handle,
@@ -530,7 +530,7 @@ impl JsContextHost {
.expect("main document owner must exist before document.open() replacement");
{
let runtime: &mut DocumentRuntime = self;
runtime.start_root_document_parser_stream(transition.current_owner());
runtime.start_root_document_parser_stream();
}
self.dom_agent_state
.reset_for_document_replacement(transition.current_owner().document_id);
@@ -5,9 +5,7 @@ use super::parser_blocking_task::PendingParsingBlockingClassicScriptBlockedOnExe
use super::*;
use crate::document_runtime::ParserInsertionController;
use crate::document_script_scheduler::DocumentScriptExecutionOutcome;
use crate::live_document_parser::{
DocumentParserSession, ParserResumeApplication, ParserStopReason,
};
use crate::live_document_parser::{DocumentParserSession, ParserStopReason};
use crate::parser_script::owner::ParserScriptExecutionBlocker;
use crate::parser_script::projection::{
ParserClassicScriptExecutionGateProjection, ParserClassicScriptNextActionWithBlockedScript,
@@ -52,7 +50,7 @@ pub(super) async fn resolve_main_parser_blocking_classic_after_runtime_gate(
if let Some(permit) = pending_runner
.current_parser_blocking_context()
.and_then(|context| context.resume_permit())
&& parser_session.resume(permit) != ParserResumeApplication::Resumed
&& !parser_session.resume(permit)
{
tracing::debug!(
?permit,
@@ -19,8 +19,7 @@ use super::*;
use crate::document_runtime::parser_script_preparation_failure_page_owned_work;
use crate::dom::native::{Attribute, DomMutationEffects, NativeNodeId};
use crate::live_document_parser::{
LiveDocumentParserOwner, LiveDocumentParserStepOutcome, ParserResumeApplication,
ParserSuspensionCause,
LiveDocumentParserOwner, LiveDocumentParserStepOutcome, ParserSuspensionCause,
};
use crate::parser::{
ParserDomMutation, ParserDomMutationConsumer, ParserDomReadConsumer,
@@ -524,9 +523,8 @@ impl<'loader, 'state> ParserDriver<'loader, 'state> {
.parser_session
.current_resume_permit()
.expect("a stylesheet-suspended parser must retain its resume permit");
assert_eq!(
assert!(
self.parser_session.resume(permit),
ParserResumeApplication::Resumed,
"the admitted stylesheet continuation must resume its exact parser suspension"
);
}
@@ -545,9 +543,8 @@ impl<'loader, 'state> ParserDriver<'loader, 'state> {
.parser_session
.current_resume_permit()
.expect("a document.write-suspended parser must retain its resume permit");
assert_eq!(
assert!(
self.parser_session.resume(permit),
ParserResumeApplication::Resumed,
"the admitted document.write continuation must resume its exact parser suspension"
);
}
@@ -135,7 +135,7 @@ impl ConcurrentParseTimeRuntime {
pub(super) fn new_parser_owner(
loader: ResourceRequestClient,
stage: PageVmInitStage,
mut state: ParseTimeDriverState,
state: ParseTimeDriverState,
mut page_vm: PageVm,
) -> Self {
page_vm.set_target_stage(stage);
@@ -151,10 +151,6 @@ impl ConcurrentParseTimeRuntime {
.vm()
.current_main_document_task_owner()
.expect("phase-one parser runtime requires an installed main document owner");
state.parser_session.bind_owner(
parser_document_owner,
page_vm.vm().document_runtime.runtime_reset_generation(),
);
page_vm
.vm_mut()
.document_runtime