refactor(cdp): route page work through explicit owners

This commit is contained in:
ldm0
2026-09-01 00:12:24 +08:00
committed by Donough Liu
parent 42ca2882d2
commit c391bfdd38
26 changed files with 865 additions and 433 deletions
+94 -91
View File
@@ -1692,9 +1692,21 @@ impl CdpConnection {
pub fn has_pending_document_navigation_for_session_owner(
&self,
session_id: Option<&str>,
) -> bool {
let none_session_owner_route = self.none_session_owner_route_override();
self.has_pending_document_navigation_for_route(
session_id,
none_session_owner_route.as_ref(),
)
}
pub(crate) fn has_pending_document_navigation_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> bool {
let Some((browser_context_id, target_id)) =
self.target_owner_identity_for_session(session_id)
self.target_owner_identity_for_route(session_id, owner_route)
else {
return false;
};
@@ -1704,14 +1716,22 @@ impl CdpConnection {
})
}
fn document_navigation_state_for_session_owner(
fn document_navigation_state_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> DevToolsDocumentNavigationState {
if self.target_owner_identity_for_session(session_id).is_none() {
let Some((browser_context_id, target_id)) =
self.target_owner_identity_for_route(session_id, owner_route)
else {
return DevToolsDocumentNavigationState::Unavailable;
}
if self.has_pending_document_navigation_for_session_owner(session_id) {
};
if self
.browser_context_by_id(&browser_context_id)
.is_some_and(|browser_context| {
browser_context.has_pending_document_navigation_for_target(target_id.as_deref())
})
{
return DevToolsDocumentNavigationState::PendingNavigation;
}
// The initial empty Document has a real loader identity before any
@@ -1721,34 +1741,26 @@ impl CdpConnection {
// Otherwise a materialized `about:blank` is misclassified as
// `AwaitingCommit`, and ChromeDriver-style pre-command navigation
// waits can never complete.
match self.target_session_owner_frame_tree_loader_id(session_id) {
match self.target_session_owner_frame_tree_loader_id_for_route(session_id, owner_route) {
Some(loader_id) => DevToolsDocumentNavigationState::Committed { loader_id },
None => DevToolsDocumentNavigationState::AwaitingCommit,
}
}
/// Resolves Document readiness through the exact target captured by a
/// protocol-neutral command context. A target-id context intentionally
/// uses the owner-route override rather than whichever target is currently
/// active in the browser context.
/// protocol-neutral command context. A target-id context resolves its
/// explicit route rather than whichever target is currently active in the
/// browser context.
pub fn devtools_context_document_navigation_state(
&mut self,
context: &DevToolsCommandContext,
) -> DevToolsDocumentNavigationState {
if let Some(target_id) = context.target_id.as_ref() {
let Some(route) = self.target_session_route_for_target_id(target_id.as_str()) else {
return DevToolsDocumentNavigationState::Unavailable;
};
let mut route_scope = self.scoped_none_session_owner_route_override(route);
return route_scope
.conn_mut()
.document_navigation_state_for_session_owner(None);
}
self.document_navigation_state_for_session_owner(
context
.session_id
.as_ref()
.map(|session_id| session_id.as_str()),
let Some(owner_scope) = self.command_owner_scope_for_devtools_context(context) else {
return DevToolsDocumentNavigationState::Unavailable;
};
self.document_navigation_state_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
}
@@ -1783,7 +1795,16 @@ impl CdpConnection {
&self,
session_id: Option<&str>,
) -> Result<(), String> {
if self.has_pending_document_navigation_for_session_owner(session_id) {
let none_session_owner_route = self.none_session_owner_route_override();
self.ensure_document_accessible_for_route(session_id, none_session_owner_route.as_ref())
}
pub(crate) fn ensure_document_accessible_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Result<(), String> {
if self.has_pending_document_navigation_for_route(session_id, owner_route) {
return Err("Navigation is changing the document".to_owned());
}
Ok(())
@@ -1844,8 +1865,20 @@ impl CdpConnection {
&mut self,
session_id: Option<&str>,
) -> Result<&mut Page, String> {
self.ensure_document_accessible_for_session_owner(session_id)?;
self.loaded_page_mut_for_interruptible_protocol_access(session_id)
let none_session_owner_route = self.none_session_owner_route_override();
self.loaded_page_mut_for_protocol_access_for_route(
session_id,
none_session_owner_route.as_ref(),
)
}
pub(crate) fn loaded_page_mut_for_protocol_access_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Result<&mut Page, String> {
self.ensure_document_accessible_for_route(session_id, owner_route)?;
self.loaded_page_mut_for_interruptible_protocol_access_for_route(session_id, owner_route)
}
/// Returns the exact Page that remains attached while a cross-Document
@@ -1859,7 +1892,19 @@ impl CdpConnection {
&mut self,
session_id: Option<&str>,
) -> Result<&mut Page, String> {
self.runtime_session_owner_slot_mut(session_id)?
let none_session_owner_route = self.none_session_owner_route_override();
self.loaded_page_mut_for_interruptible_protocol_access_for_route(
session_id,
none_session_owner_route.as_ref(),
)
}
pub(crate) fn loaded_page_mut_for_interruptible_protocol_access_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Result<&mut Page, String> {
self.runtime_session_owner_slot_mut_for_route(session_id, owner_route)?
.loaded_page_mut()
.ok_or_else(|| "NoDocumentLoaded".to_owned())
}
@@ -2231,11 +2276,10 @@ impl CdpConnection {
context: &DevToolsCommandContext,
) -> Option<DevToolsPageResidenceIdentity> {
let owner_scope = self.command_owner_scope_for_devtools_context(context)?;
let session_id = owner_scope.session_id().map(str::to_owned);
let mut route_scope = owner_scope.enter(self);
route_scope
.conn_mut()
.target_page_residence_identity_for_session(session_id.as_deref())
self.target_page_residence_identity_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
}
pub fn capture_devtools_document_lifecycle_wait_key(
@@ -2244,26 +2288,15 @@ impl CdpConnection {
expected_loader_id: &str,
milestone: moli_core::page::RendererDocumentLifecycleMilestone,
) -> Option<DevToolsDocumentLifecycleWaitKey> {
let registration = if let Some(target_id) = context.target_id.as_ref() {
let route = self.target_session_route_for_target_id(target_id.as_str())?;
let mut route_scope = self.scoped_none_session_owner_route_override(route);
route_scope
.conn_mut()
.runtime_session_owner_slot_mut(None)
.ok()?
.page_slot_mut()
.register_renderer_document_lifecycle_waiter(milestone, expected_loader_id)
} else {
self.runtime_session_owner_slot_mut(
context
.session_id
.as_ref()
.map(|session_id| session_id.as_str()),
let owner_scope = self.command_owner_scope_for_devtools_context(context)?;
let registration = self
.runtime_session_owner_slot_mut_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.ok()?
.page_slot_mut()
.register_renderer_document_lifecycle_waiter(milestone, expected_loader_id)
};
.register_renderer_document_lifecycle_waiter(milestone, expected_loader_id);
let (registration_id, binding) = registration?;
Some(DevToolsDocumentLifecycleWaitKey {
registration_id,
@@ -2280,24 +2313,12 @@ impl CdpConnection {
context: &DevToolsCommandContext,
key: &DevToolsDocumentLifecycleWaitKey,
) -> DevToolsDocumentLifecycleWaitState {
if let Some(target_id) = context.target_id.as_ref() {
let Some(route) = self.target_session_route_for_target_id(target_id.as_str()) else {
return DevToolsDocumentLifecycleWaitState::Unavailable;
};
let mut route_scope = self.scoped_none_session_owner_route_override(route);
return route_scope
.conn_mut()
.runtime_session_owner_slot(None)
.ok()
.map_or(DevToolsDocumentLifecycleWaitState::Unavailable, |slot| {
devtools_document_lifecycle_wait_state_for_slot(slot, key)
});
}
self.runtime_session_owner_slot(
context
.session_id
.as_ref()
.map(|session_id| session_id.as_str()),
let Some(owner_scope) = self.command_owner_scope_for_devtools_context(context) else {
return DevToolsDocumentLifecycleWaitState::Unavailable;
};
self.runtime_session_owner_slot_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.ok()
.map_or(DevToolsDocumentLifecycleWaitState::Unavailable, |slot| {
@@ -2310,30 +2331,12 @@ impl CdpConnection {
context: &DevToolsCommandContext,
key: &DevToolsDocumentLifecycleWaitKey,
) -> bool {
if let Some(target_id) = context.target_id.as_ref() {
let Some(route) = self.target_session_route_for_target_id(target_id.as_str()) else {
return false;
};
let mut route_scope = self.scoped_none_session_owner_route_override(route);
return route_scope
.conn_mut()
.runtime_session_owner_slot_mut(None)
.is_ok_and(|slot| {
slot.page_slot_mut()
.release_renderer_document_lifecycle_waiter(
key.registration_id,
key.renderer_document,
key.renderer_epoch,
&key.frame_id,
&key.loader_id,
)
});
}
self.runtime_session_owner_slot_mut(
context
.session_id
.as_ref()
.map(|session_id| session_id.as_str()),
let Some(owner_scope) = self.command_owner_scope_for_devtools_context(context) else {
return false;
};
self.runtime_session_owner_slot_mut_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.is_ok_and(|slot| {
slot.page_slot_mut()
@@ -913,27 +913,25 @@ impl CdpConnection {
.unwrap_or(false)
}
pub(crate) fn begin_page_screencast_capture_for_session_owner(
pub(crate) fn begin_page_screencast_capture_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
generation: i32,
) -> Option<bool> {
self.with_target_session_owner_mut(session_id, |owner| {
owner.begin_page_screencast_capture(generation)
})
.flatten()
self.target_session_owner_mut_for_route(session_id, owner_route)?
.begin_page_screencast_capture(generation)
}
pub(crate) fn complete_page_screencast_capture_for_session_owner(
pub(crate) fn complete_page_screencast_capture_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
generation: i32,
frame_emitted: bool,
) -> Option<bool> {
self.with_target_session_owner_mut(session_id, |owner| {
owner.complete_page_screencast_capture(generation, frame_emitted)
})
.flatten()
self.target_session_owner_mut_for_route(session_id, owner_route)?
.complete_page_screencast_capture(generation, frame_emitted)
}
pub(crate) fn acknowledge_page_screencast_frame_for_session_owner(
@@ -38,11 +38,26 @@ impl CdpConnection {
pub(super) fn target_session_owner(
&self,
session_id: Option<&str>,
) -> Option<TargetSessionOwner> {
let none_session_owner_route = self.none_session_owner_route_override();
self.target_session_owner_for_route(session_id, none_session_owner_route.as_ref())
}
/// Resolves target ownership from an explicit command route.
///
/// This is the non-ambient owner boundary used while migrating deferred
/// commands away from `none_session_owner_route_override`. A concrete
/// session always resolves through the session registry; `owner_route`
/// only selects the owner of an implicit primary Page attachment.
pub(super) fn target_session_owner_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<TargetSessionOwner> {
let route = match session_id {
Some(session_id) => self.session_route(Some(session_id))?,
None => match self.none_session_owner_route_override() {
Some(route) => route,
None => match owner_route {
Some(route) => route.clone(),
None => {
let Some(browser_context) = self.browser_context.as_ref() else {
return Some(TargetSessionOwner::NoLoadedBrowserContext);
@@ -2470,16 +2470,17 @@ impl CdpConnection {
.await
}
pub(crate) async fn close_page_target_for_session_owner_async(
pub(crate) async fn close_page_target_for_route_async(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<ClosedPageTarget> {
let collected_network_data_artifacts = self
.target_session_owner_ref(session_id)?
.target_session_owner_ref_for_route(session_id, owner_route)?
.runtime_slot()?
.collected_network_data_artifacts();
let closed = self
.target_session_owner_mut(session_id)?
.target_session_owner_mut_for_route(session_id, owner_route)?
.close_page_target_async()
.await?;
self.record_collected_network_data_artifacts(collected_network_data_artifacts);
@@ -2676,6 +2677,15 @@ impl CdpConnection {
.page_session_state()
}
pub(crate) fn target_page_session_state_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<&TargetPageSessionState> {
self.target_session_owner_ref_for_route(session_id, owner_route)?
.page_session_state()
}
pub(crate) fn target_devtools_session_state_for_session(
&self,
session_id: Option<&str>,
@@ -2789,6 +2799,16 @@ impl CdpConnection {
.mutate_session_state(|mut state| state.devtools_session_state_mut().map(f))
}
pub(crate) fn with_target_devtools_session_state_for_route_mut<R>(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
f: impl FnOnce(&mut DevToolsSessionState) -> R,
) -> Option<R> {
self.target_session_owner_mut_for_route(session_id, owner_route)?
.mutate_session_state(|mut state| state.devtools_session_state_mut().map(f))
}
pub(crate) fn target_owner_identity_for_session(
&self,
session_id: Option<&str>,
@@ -2796,6 +2816,15 @@ impl CdpConnection {
self.target_session_owner_ref(session_id)?.owner_identity()
}
pub(crate) fn target_owner_identity_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<(String, Option<String>)> {
self.target_session_owner_ref_for_route(session_id, owner_route)?
.owner_identity()
}
/// Captures the exact target-local Page residence currently addressed by
/// `session_id`.
///
@@ -2806,9 +2835,18 @@ impl CdpConnection {
pub(crate) fn target_page_residence_identity_for_session(
&self,
session_id: Option<&str>,
) -> Option<TargetPageResidenceIdentity> {
let none_session_owner_route = self.none_session_owner_route_override();
self.target_page_residence_identity_for_route(session_id, none_session_owner_route.as_ref())
}
pub(crate) fn target_page_residence_identity_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<TargetPageResidenceIdentity> {
let (browser_context_id, routed_target_id) =
self.target_owner_identity_for_session(session_id)?;
self.target_owner_identity_for_route(session_id, owner_route)?;
// `None` in a `CdpSessionRoute::ActiveTarget` is a routing shorthand:
// it means "whichever target is currently active". A Page residence
// identity must never retain that mutable shorthand. Freeze the
@@ -2821,7 +2859,7 @@ impl CdpConnection {
.map(str::to_owned)
});
let page_attachment_id = self
.runtime_session_owner_slot(session_id)
.runtime_session_owner_slot_for_route(session_id, owner_route)
.ok()?
.page_attachment_id()?;
Some(TargetPageResidenceIdentity::new(
@@ -3121,11 +3159,20 @@ impl CdpConnection {
pub(crate) fn runtime_session_owner_slot_mut(
&mut self,
session_id: Option<&str>,
) -> Result<&mut TargetRuntimeSlot, String> {
let none_session_owner_route = self.none_session_owner_route_override();
self.runtime_session_owner_slot_mut_for_route(session_id, none_session_owner_route.as_ref())
}
pub(crate) fn runtime_session_owner_slot_mut_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Result<&mut TargetRuntimeSlot, String> {
let renderer_inspector_session_id =
self.target_renderer_runtime_inspector_session_id_for_session(session_id);
let slot = self
.target_session_owner_mut(session_id)
.target_session_owner_mut_for_route(session_id, owner_route)
.and_then(TargetSessionOwnerMut::into_runtime_slot_mut)
.ok_or_else(|| "NoDocumentLoaded".to_owned())?;
if let Some(page) = slot.loaded_page_mut() {
@@ -3138,7 +3185,16 @@ impl CdpConnection {
&self,
session_id: Option<&str>,
) -> Result<&TargetRuntimeSlot, String> {
self.target_session_owner_ref(session_id)
let none_session_owner_route = self.none_session_owner_route_override();
self.runtime_session_owner_slot_for_route(session_id, none_session_owner_route.as_ref())
}
pub(crate) fn runtime_session_owner_slot_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Result<&TargetRuntimeSlot, String> {
self.target_session_owner_ref_for_route(session_id, owner_route)
.and_then(|owner| owner.runtime_slot())
.ok_or_else(|| "NoDocumentLoaded".to_owned())
}
@@ -3315,6 +3371,15 @@ impl CdpConnection {
.frame_tree_loader_id()
}
pub(crate) fn target_session_owner_frame_tree_loader_id_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<String> {
self.target_session_owner_ref_for_route(session_id, owner_route)?
.frame_tree_loader_id()
}
pub(crate) fn target_session_owner_emulated_device_metrics(
&self,
session_id: Option<&str>,
@@ -3323,6 +3388,15 @@ impl CdpConnection {
.emulated_device_metrics()
}
pub(crate) fn target_session_owner_emulated_device_metrics_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<crate::conn::EmulatedDeviceMetrics> {
self.target_session_owner_ref_for_route(session_id, owner_route)?
.emulated_device_metrics()
}
pub(crate) fn target_session_owner_navigation_history_snapshot(
&mut self,
session_id: Option<&str>,
@@ -3400,7 +3474,16 @@ impl CdpConnection {
&mut self,
session_id: Option<&str>,
) -> Option<TargetSessionOwnerMut<'_>> {
match self.target_session_owner(session_id)? {
let none_session_owner_route = self.none_session_owner_route_override();
self.target_session_owner_mut_for_route(session_id, none_session_owner_route.as_ref())
}
pub(super) fn target_session_owner_mut_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<TargetSessionOwnerMut<'_>> {
match self.target_session_owner_for_route(session_id, owner_route)? {
TargetSessionOwner::ActiveTarget {
browser_context_id,
is_auxiliary_target_session,
@@ -3439,7 +3522,16 @@ impl CdpConnection {
&self,
session_id: Option<&str>,
) -> Option<TargetSessionOwnerRef<'_>> {
match self.target_session_owner(session_id)? {
let none_session_owner_route = self.none_session_owner_route_override();
self.target_session_owner_ref_for_route(session_id, none_session_owner_route.as_ref())
}
pub(super) fn target_session_owner_ref_for_route(
&self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Option<TargetSessionOwnerRef<'_>> {
match self.target_session_owner_for_route(session_id, owner_route)? {
TargetSessionOwner::ActiveTarget {
browser_context_id,
is_auxiliary_target_session,
@@ -32,6 +32,15 @@ impl CommandOwnerScope {
self.session_id.as_deref()
}
/// Returns the exact route captured for an implicit-session command.
///
/// A concrete CDP session remains authoritative through `session_id`; the
/// route is only needed for protocol-neutral and deferred work which uses
/// Chromium's implicit primary Page attachment.
pub(crate) fn session_owner_route(&self) -> Option<&CdpSessionRoute> {
self.session_owner_route.as_ref()
}
pub(crate) fn enter<'a>(
&self,
conn: &'a mut CdpConnection,
+6 -6
View File
@@ -108,16 +108,16 @@ impl PendingCdpCommandDispatchKind {
fn owner_scope_capture_session_id(&self) -> Option<Option<&str>> {
match self {
Self::Autofill(pending) => Some(pending.session_id()),
Self::Accessibility(pending) => Some(pending.session_id()),
Self::Input(pending) => Some(pending.session_id()),
Self::Css(pending) => Some(pending.session_id()),
Self::Dom(pending) => Some(pending.session_id()),
Self::DomDebugger(pending) => Some(pending.session_id()),
Self::DomSnapshot(pending) => Some(pending.session_id()),
Self::Performance(pending) => Some(pending.session_id()),
Self::Tracing(pending) => Some(pending.session_id()),
Self::Runtime(_)
| Self::Autofill(_)
| Self::Accessibility(_)
| Self::Css(_)
| Self::DomDebugger(_)
| Self::DomSnapshot(_)
| Self::Performance(_)
| Self::DomStorage(_)
| Self::Page(_)
| Self::Emulation(_)
@@ -256,6 +256,19 @@ impl CdpConnection {
.map_err(|error| format!("failed to update page resource runtime: {error}"))
}
pub(crate) fn finish_rebuild_resource_runtime_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&super::CdpSessionRoute>,
completion: CompletedPageCommand,
) -> Result<(), String> {
let Some(page) = self.resource_runtime_apply_page_for_route(session_id, owner_route) else {
return Ok(());
};
page.finish_replace_browser_resource_runtime(completion)
.map_err(|error| format!("failed to update page resource runtime: {error}"))
}
fn resource_runtime_apply_page_for_session_owner(
&mut self,
session_id: Option<&str>,
@@ -271,4 +284,22 @@ impl CdpConnection {
}
self.loaded_page_mut_for_protocol_access(session_id).ok()
}
fn resource_runtime_apply_page_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&super::CdpSessionRoute>,
) -> Option<&mut moli_core::page::Page> {
if matches!(
session_id.and_then(|session_id| self.session_route(Some(session_id))),
Some(super::CdpSessionRoute::Browser)
) {
return self
.browser_context
.as_mut()
.and_then(|bc| bc.active_target.runtime_slot.loaded_page_mut());
}
self.loaded_page_mut_for_protocol_access_for_route(session_id, owner_route)
.ok()
}
}
+23
View File
@@ -1042,6 +1042,29 @@ impl CdpConnection {
) == Some(correlation)
}
pub(crate) fn take_renderer_call_if_correlation_matches_for_route(
&mut self,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
correlation: RendererCommandCorrelation,
) -> bool {
if session_id.is_some() {
return self.take_renderer_call_if_correlation_matches_for_session_owner(
session_id,
correlation,
);
}
self.with_target_devtools_session_state_for_route_mut(session_id, owner_route, |state| {
state.take_renderer_call_for_frontend_if_matches(
correlation.frontend_command_id().get(),
correlation.renderer_call_id(),
correlation.dispatched_attachment_id(),
)
})
.flatten()
== Some(correlation)
}
fn take_frontend_command_for_renderer_if_attachment_matches_for_session_owner(
&mut self,
session_id: Option<&str>,
@@ -1705,6 +1705,60 @@ fn devtools_document_lifecycle_wait_key_observes_interruption_and_target_loss()
);
}
#[test]
fn devtools_target_context_resolves_background_page_without_ambient_route() {
let mut conn = CdpConnection::new();
let mut browser_context = BrowserContext::new("BID-explicit-owner".into());
browser_context.set_active_target_id("TID-active");
browser_context
.active_target
.runtime_slot
.set_page_attachment_id_for_test(1001);
assert!(
browser_context.insert_page_target_host(PageTargetHost::with_url(
"TID-background".to_owned(),
None,
"about:blank".to_owned(),
))
);
let background = browser_context
.background_target_mut("TID-background")
.expect("background PageTargetHost");
background
.active_target
.runtime_slot
.set_page_attachment_id_for_test(1002);
background
.active_target
.runtime_slot
.start_document_navigation("TID-background".to_owned(), "LID-background".to_owned());
conn.browser_context = Some(browser_context);
let context = crate::devtools_runtime::DevToolsCommandContext {
protocol: crate::devtools_runtime::DevToolsProtocol::WebDriverBidi,
session_id: None,
target_id: Some(crate::devtools_runtime::DevToolsTargetId::from(
"TID-background",
)),
browser_context_id: None,
};
assert_eq!(conn.none_session_owner_route_override(), None);
let residence = conn
.page_residence_identity_for_devtools_context(&context)
.expect("explicit target context should resolve its Page");
assert_eq!(residence.target_id(), Some("TID-background"));
assert_eq!(
conn.devtools_context_document_navigation_state(&context),
crate::DevToolsDocumentNavigationState::PendingNavigation
);
assert_eq!(
conn.none_session_owner_route_override(),
None,
"explicit lookup must not mutate connection-global routing state"
);
}
#[tokio::test]
async fn direct_runtime_evaluate_same_document_navigation_updates_inactive_owner() {
let mut ctx = crate::testing::TestContext::new();
+49 -34
View File
@@ -1,4 +1,4 @@
use crate::conn::{CdpConnection, Cmd};
use crate::conn::{CdpConnection, Cmd, CommandOwnerScope};
use crate::domains::actions::AccessibilityAction;
use crate::domains::command_output::CommandOutputPlan;
use moli_core::page::{
@@ -12,14 +12,14 @@ mod tests;
pub(crate) struct PendingAccessibilityCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
kind: PendingAccessibilityCommandKind,
pending: PendingAccessibilityCommandWork,
}
pub(crate) struct CompletedAccessibilityCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
kind: PendingAccessibilityCommandKind,
completed: CompletedAccessibilityCommandWork,
}
@@ -82,8 +82,18 @@ struct PendingAccessibilityCommandStartError {
}
impl PendingAccessibilityCommandDispatch {
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
fn from_command(
conn: &CdpConnection,
cmd: &Cmd<'_>,
kind: PendingAccessibilityCommandKind,
pending: PendingAccessibilityCommandWork,
) -> Self {
Self {
command_id: cmd.id,
owner_scope: CommandOwnerScope::capture(conn, cmd.session_id),
kind,
pending,
}
}
pub async fn wait(self) -> CompletedAccessibilityCommandDispatch {
@@ -96,7 +106,7 @@ impl PendingAccessibilityCommandDispatch {
};
CompletedAccessibilityCommandDispatch {
command_id: self.command_id,
session_id: self.session_id,
owner_scope: self.owner_scope,
kind: self.kind,
completed,
}
@@ -109,7 +119,7 @@ impl CompletedAccessibilityCommandDispatch {
}
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
self.owner_scope.session_id()
}
}
@@ -437,16 +447,16 @@ fn start_pending_object_reference_command(
&operation,
)
.map_err(PendingAccessibilityCommandStartError::renderer_error)?;
Ok(Some(PendingAccessibilityCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingAccessibilityCommandKind::ObjectAccessibilityPayloads {
Ok(Some(PendingAccessibilityCommandDispatch::from_command(
conn,
cmd,
PendingAccessibilityCommandKind::ObjectAccessibilityPayloads {
frame_id: resolved_frame_id,
top_frame_id,
operation,
},
pending: PendingAccessibilityCommandWork::Page(pending),
}))
PendingAccessibilityCommandWork::Page(pending),
)))
}
fn renderer_backend_node_id_for_reference(reference: &helpers::NodeReferenceParams) -> Option<u32> {
@@ -481,16 +491,16 @@ fn start_pending_dom_node_reference_command(
let pending = page
.start_document_frontend_node_binding(renderer_inspector_session_id, frontend_node_id)
.map_err(PendingAccessibilityCommandStartError::renderer_error)?;
Ok(Some(PendingAccessibilityCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingAccessibilityCommandKind::FrontendAccessibilityPayloads {
Ok(Some(PendingAccessibilityCommandDispatch::from_command(
conn,
cmd,
PendingAccessibilityCommandKind::FrontendAccessibilityPayloads {
frame_id: resolved_frame_id,
top_frame_id,
operation,
},
pending: PendingAccessibilityCommandWork::Page(pending),
}))
PendingAccessibilityCommandWork::Page(pending),
)))
}
fn start_accessibility_object_page_command(
@@ -545,16 +555,16 @@ fn start_pending_backend_reference_command(
};
let pending = start_accessibility_backend_page_command(page, backend_node_id, &operation)
.map_err(PendingAccessibilityCommandStartError::renderer_error)?;
Ok(Some(PendingAccessibilityCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingAccessibilityCommandKind::BackendAccessibilityPayloads {
Ok(Some(PendingAccessibilityCommandDispatch::from_command(
conn,
cmd,
PendingAccessibilityCommandKind::BackendAccessibilityPayloads {
frame_id: resolved_frame_id,
top_frame_id,
operation,
},
pending: PendingAccessibilityCommandWork::Page(pending),
}))
PendingAccessibilityCommandWork::Page(pending),
)))
}
fn start_accessibility_backend_page_command(
@@ -631,12 +641,12 @@ fn start_pending_frame_scoped_accessibility_command(
.map_err(PendingAccessibilityCommandStartError::renderer_error)?;
(child_frame_kind, pending)
};
Ok(Some(PendingAccessibilityCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
Ok(Some(PendingAccessibilityCommandDispatch::from_command(
conn,
cmd,
kind,
pending: PendingAccessibilityCommandWork::Page(pending),
}))
PendingAccessibilityCommandWork::Page(pending),
)))
}
fn start_child_frame_accessibility_page_command(
@@ -661,20 +671,25 @@ pub(crate) async fn complete_pending_accessibility_command(
) -> AccessibilityCommandDispatchStep {
let CompletedAccessibilityCommandDispatch {
command_id,
session_id,
owner_scope,
kind,
completed,
} = completed;
let CompletedAccessibilityCommandWork::Page(completed) = completed;
let completed = *completed;
let session_id = owner_scope.session_id().map(str::to_owned);
let session_id_ref = session_id.as_deref();
if let Err(message) = conn.ensure_document_accessible_for_session_owner(session_id_ref) {
let owner_route = owner_scope.session_owner_route();
if let Err(message) = conn.ensure_document_accessible_for_route(session_id_ref, owner_route) {
return AccessibilityCommandDispatchStep::Complete(CommandOutputPlan::error(
-32000, message,
));
}
let Some(page) = helpers::loaded_page_mut_for_session(conn, session_id_ref) else {
let Some(page) = conn
.loaded_page_mut_for_protocol_access_for_route(session_id_ref, owner_route)
.ok()
else {
return AccessibilityCommandDispatchStep::Complete(CommandOutputPlan::error(
-32000,
"NoDocumentLoaded",
@@ -785,7 +800,7 @@ pub(crate) async fn complete_pending_accessibility_command(
return AccessibilityCommandDispatchStep::Pending(
PendingAccessibilityCommandDispatch {
command_id,
session_id,
owner_scope,
kind: PendingAccessibilityCommandKind::BackendAccessibilityPayloads {
frame_id,
top_frame_id,
@@ -155,6 +155,21 @@ impl RuntimeCommandCausalOwner {
target_id,
})
}
fn capture_for_scope(conn: &CdpConnection, scope: &CommandOwnerScope) -> Option<Self> {
if let Some(page) = conn.target_page_residence_identity_for_route(
scope.session_id(),
scope.session_owner_route(),
) {
return Some(Self::Page(page));
}
let (browser_context_id, target_id) =
conn.target_owner_identity_for_route(scope.session_id(), scope.session_owner_route())?;
Some(Self::Target {
browser_context_id,
target_id,
})
}
}
impl RuntimeCommandOutputRoute {
@@ -360,12 +375,10 @@ impl RuntimeCommandOutputBarriers {
fn command_owner_is_current(
&self,
conn: &mut CdpConnection,
conn: &CdpConnection,
barrier: &ActiveRuntimeCommandOutputBarrier,
) -> bool {
let mut scope = barrier.command_scope.enter(conn);
RuntimeCommandCausalOwner::capture(scope.conn_mut(), barrier.command_scope.session_id())
.as_ref()
RuntimeCommandCausalOwner::capture_for_scope(conn, &barrier.command_scope).as_ref()
== Some(&barrier.causal_owner)
}
+10 -11
View File
@@ -5,19 +5,19 @@ use moli_core::page::{
};
use crate::{
conn::{CdpConnection, Cmd},
conn::{CdpConnection, Cmd, CommandOwnerScope},
domains::{actions::AutofillAction, command_output::CommandOutputPlan},
};
pub(crate) struct PendingAutofillCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
pending: PendingPageCommand,
}
pub(crate) struct CompletedAutofillCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
completed: Result<CompletedPageCommand, String>,
}
@@ -27,14 +27,10 @@ pub(crate) enum AutofillCommandTaskStep {
}
impl PendingAutofillCommandDispatch {
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
}
pub(crate) async fn wait(self) -> CompletedAutofillCommandDispatch {
CompletedAutofillCommandDispatch {
command_id: self.command_id,
session_id: self.session_id,
owner_scope: self.owner_scope,
completed: self.pending.wait().await.map_err(|error| error.to_string()),
}
}
@@ -46,7 +42,7 @@ impl CompletedAutofillCommandDispatch {
}
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
self.owner_scope.session_id()
}
}
@@ -111,6 +107,7 @@ pub(crate) fn try_start_autofill_command_dispatch(
card,
address,
};
let owner_scope = CommandOwnerScope::capture(conn, cmd.session_id);
let pending = conn
.loaded_page_mut_for_protocol_access(cmd.session_id)
.and_then(|page| {
@@ -120,7 +117,7 @@ pub(crate) fn try_start_autofill_command_dispatch(
match pending {
Ok(pending) => AutofillCommandTaskStep::Pending(PendingAutofillCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
owner_scope,
pending,
}),
Err(message) => {
@@ -133,8 +130,10 @@ pub(crate) fn complete_pending_autofill_command(
conn: &mut CdpConnection,
completed: CompletedAutofillCommandDispatch,
) -> CommandOutputPlan {
let session_id = completed.owner_scope.session_id();
let owner_route = completed.owner_scope.session_owner_route();
let outcome = completed.completed.and_then(|completion| {
conn.loaded_page_mut_for_protocol_access(completed.session_id.as_deref())
conn.loaded_page_mut_for_protocol_access_for_route(session_id, owner_route)
.and_then(|page| {
page.finish_autofill_trigger(completion)
.map_err(|error| error.to_string())
+67 -46
View File
@@ -1,4 +1,4 @@
use crate::conn::{CdpConnection, Cmd};
use crate::conn::{CdpConnection, Cmd, CommandOwnerScope};
use crate::domains::actions::CssAction;
use crate::domains::command_output::CommandOutputPlan;
use chromiumoxide_cdp::cdp::browser_protocol::css::{
@@ -27,14 +27,14 @@ struct NodeReferenceParams {
pub(crate) struct PendingCssCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
kind: PendingCssCommandKind,
pending: PendingPageCommand,
}
pub(crate) struct CompletedCssCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
kind: PendingCssCommandKind,
completed: Result<CompletedPageCommand, String>,
}
@@ -78,14 +78,24 @@ struct PendingCssCommandStartError {
}
impl PendingCssCommandDispatch {
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
fn from_command(
conn: &CdpConnection,
cmd: &Cmd<'_>,
kind: PendingCssCommandKind,
pending: PendingPageCommand,
) -> Self {
Self {
command_id: cmd.id,
owner_scope: CommandOwnerScope::capture(conn, cmd.session_id),
kind,
pending,
}
}
pub async fn wait(self) -> CompletedCssCommandDispatch {
CompletedCssCommandDispatch {
command_id: self.command_id,
session_id: self.session_id,
owner_scope: self.owner_scope,
kind: self.kind,
completed: self.pending.wait().await.map_err(|error| error.to_string()),
}
@@ -98,7 +108,7 @@ impl CompletedCssCommandDispatch {
}
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
self.owner_scope.session_id()
}
}
@@ -251,15 +261,15 @@ fn start_pending_get_style_sheet_command(
&style_sheet_id,
)
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::GetStyleSheet {
Ok(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::GetStyleSheet {
style_sheet_id,
frame_id,
},
pending,
})
))
}
fn start_pending_set_style_sheet_text_command(
@@ -283,12 +293,12 @@ fn start_pending_set_style_sheet_text_command(
&params.text,
)
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::SetStyleSheetText { style_sheet_id },
Ok(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::SetStyleSheetText { style_sheet_id },
pending,
})
))
}
fn start_pending_get_computed_style_for_node_command(
@@ -309,12 +319,12 @@ fn start_pending_get_computed_style_for_node_command(
object_id,
)
.map_err(PendingCssCommandStartError::renderer_error)?;
return Ok(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::GetComputedStyleForNode,
return Ok(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::GetComputedStyleForNode,
pending,
});
));
}
if let Some(cdp_node_id) = params.node_id {
return node_references::start_frontend_node_binding_for_computed_style(
@@ -332,12 +342,12 @@ fn start_pending_get_computed_style_for_node_command(
return Err(PendingCssCommandStartError::node_not_found());
}
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::GetComputedStyleForNode,
Ok(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::GetComputedStyleForNode,
pending,
})
))
}
fn start_pending_inline_style_command(
@@ -369,29 +379,40 @@ fn start_pending_inline_style_command(
return Err(PendingCssCommandStartError::node_not_found());
}
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(Some(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::GetInlineStyleForNode { kind },
Ok(Some(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::GetInlineStyleForNode { kind },
pending,
}))
)))
}
pub(crate) fn complete_pending_css_command(
conn: &mut CdpConnection,
completed: CompletedCssCommandDispatch,
) -> CssCommandDispatchStep {
let command_id = completed.command_id;
let session_id = completed.session_id.as_deref();
let Some(page) = loaded_page_mut_for_session(conn, session_id) else {
let CompletedCssCommandDispatch {
command_id,
owner_scope,
kind,
completed,
} = completed;
let session_id = owner_scope.session_id();
let Some(page) = conn
.loaded_page_mut_for_protocol_access_for_route(
session_id,
owner_scope.session_owner_route(),
)
.ok()
else {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
-32000,
"NoDocumentLoaded",
));
};
match completed.kind {
match kind {
PendingCssCommandKind::Enable { frame_id } => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -409,7 +430,7 @@ pub(crate) fn complete_pending_css_command(
)
}
PendingCssCommandKind::Disable => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -425,7 +446,7 @@ pub(crate) fn complete_pending_css_command(
)
}
PendingCssCommandKind::ResolveFrontendNodeForComputedStyle => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -464,13 +485,13 @@ pub(crate) fn complete_pending_css_command(
};
CssCommandDispatchStep::Pending(PendingCssCommandDispatch {
command_id,
session_id: session_id.map(str::to_owned),
owner_scope,
kind: PendingCssCommandKind::GetComputedStyleForNode,
pending,
})
}
PendingCssCommandKind::ResolveFrontendNodeForInlineStyle { kind } => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -509,7 +530,7 @@ pub(crate) fn complete_pending_css_command(
};
CssCommandDispatchStep::Pending(PendingCssCommandDispatch {
command_id,
session_id: session_id.map(str::to_owned),
owner_scope,
kind: PendingCssCommandKind::GetInlineStyleForNode { kind },
pending,
})
@@ -518,7 +539,7 @@ pub(crate) fn complete_pending_css_command(
style_sheet_id,
frame_id,
} => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -539,7 +560,7 @@ pub(crate) fn complete_pending_css_command(
})
}
PendingCssCommandKind::SetStyleSheetText { style_sheet_id } => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -561,7 +582,7 @@ pub(crate) fn complete_pending_css_command(
)
}
PendingCssCommandKind::GetComputedStyleForNode => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -583,7 +604,7 @@ pub(crate) fn complete_pending_css_command(
)
}
PendingCssCommandKind::GetInlineStyleForNode { kind } => {
let completion = match completed.completed {
let completion = match completed {
Ok(completion) => completion,
Err(error) => {
return CssCommandDispatchStep::Complete(CommandOutputPlan::error(
@@ -18,12 +18,12 @@ pub(super) fn start_frontend_node_binding_for_computed_style(
let pending = page
.start_document_frontend_node_binding(renderer_inspector_session_id, frontend_node_id)
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::ResolveFrontendNodeForComputedStyle,
Ok(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::ResolveFrontendNodeForComputedStyle,
pending,
})
))
}
pub(super) fn start_frontend_node_binding_for_inline_style(
@@ -40,12 +40,12 @@ pub(super) fn start_frontend_node_binding_for_inline_style(
let pending = page
.start_document_frontend_node_binding(renderer_inspector_session_id, frontend_node_id)
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(Some(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::ResolveFrontendNodeForInlineStyle { kind },
Ok(Some(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::ResolveFrontendNodeForInlineStyle { kind },
pending,
}))
)))
}
pub(super) fn backend_node_id_from_frontend_resolution(
+10 -10
View File
@@ -32,12 +32,12 @@ pub(super) fn start_pending_enable_command(
renderer_inspector_session_id,
)
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(Some(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::Enable { frame_id },
Ok(Some(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::Enable { frame_id },
pending,
}))
)))
}
pub(super) fn start_pending_disable_command(
@@ -53,12 +53,12 @@ pub(super) fn start_pending_disable_command(
let pending = page
.start_reset_css_agent_session(renderer_inspector_session_id)
.map_err(PendingCssCommandStartError::renderer_error)?;
Ok(Some(PendingCssCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
kind: PendingCssCommandKind::Disable,
Ok(Some(PendingCssCommandDispatch::from_command(
conn,
cmd,
PendingCssCommandKind::Disable,
pending,
}))
)))
}
pub(super) fn complete_enable_command_output_plan(
+42 -36
View File
@@ -6,20 +6,20 @@ use moli_core::page::{
use serde::Deserialize;
use serde_json::{Map, Value, json};
use crate::conn::{CdpConnection, Cmd};
use crate::conn::{CdpConnection, Cmd, CommandOwnerScope};
use crate::domains::actions::DomDebuggerAction;
use crate::domains::command_output::CommandOutputPlan;
pub(crate) struct PendingDomDebuggerCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
pending: PendingPageCommand,
operation: CompletedDomDebuggerOperation,
}
pub(crate) struct CompletedDomDebuggerCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
completed: Result<CompletedPageCommand, String>,
operation: CompletedDomDebuggerOperation,
}
@@ -78,14 +78,10 @@ fn default_depth() -> i32 {
}
impl PendingDomDebuggerCommandDispatch {
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
}
pub(crate) async fn wait(self) -> CompletedDomDebuggerCommandDispatch {
CompletedDomDebuggerCommandDispatch {
command_id: self.command_id,
session_id: self.session_id,
owner_scope: self.owner_scope,
completed: self.pending.wait().await.map_err(|error| error.to_string()),
operation: self.operation,
}
@@ -98,7 +94,7 @@ impl CompletedDomDebuggerCommandDispatch {
}
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
self.owner_scope.session_id()
}
}
@@ -228,7 +224,7 @@ pub(crate) fn try_start_dom_debugger_command_dispatch(
Ok((pending, operation)) => {
DomDebuggerCommandTaskStep::Pending(PendingDomDebuggerCommandDispatch {
command_id: cmd.id,
session_id: cmd.session_id.map(str::to_owned),
owner_scope: CommandOwnerScope::capture(conn, cmd.session_id),
pending,
operation,
})
@@ -247,15 +243,19 @@ pub(crate) fn complete_pending_dom_debugger_command(
conn: &mut CdpConnection,
completed: CompletedDomDebuggerCommandDispatch,
) -> CommandOutputPlan {
let session_id = completed.session_id;
let session_id = completed.owner_scope.session_id().map(str::to_owned);
let owner_route = completed.owner_scope.session_owner_route();
match completed.operation {
CompletedDomDebuggerOperation::GetEventListeners => {
let resolution = completed.completed.and_then(|completion| {
conn.loaded_page_mut_for_protocol_access(session_id.as_deref())
.and_then(|page| {
page.finish_dom_debugger_get_event_listeners(completion)
.map_err(|error| error.to_string())
})
conn.loaded_page_mut_for_protocol_access_for_route(
session_id.as_deref(),
owner_route,
)
.and_then(|page| {
page.finish_dom_debugger_get_event_listeners(completion)
.map_err(|error| error.to_string())
})
});
match resolution {
Ok(RendererDomDebuggerEventListenersResolution::Found(listeners)) => {
@@ -272,14 +272,17 @@ pub(crate) fn complete_pending_dom_debugger_command(
enabled,
} => {
let completion = completed.completed.and_then(|completion| {
conn.loaded_page_mut_for_protocol_access(session_id.as_deref())
.and_then(|page| {
page.finish_unit_runtime_page_command(
completion,
"DOMDebugger event listener breakpoint",
)
.map_err(|error| error.to_string())
})
conn.loaded_page_mut_for_protocol_access_for_route(
session_id.as_deref(),
owner_route,
)
.and_then(|page| {
page.finish_unit_runtime_page_command(
completion,
"DOMDebugger event listener breakpoint",
)
.map_err(|error| error.to_string())
})
});
if let Err(message) = completion {
return CommandOutputPlan::error(-32000, message);
@@ -308,14 +311,14 @@ pub(crate) fn complete_pending_dom_debugger_command(
enabled,
} => {
let completion = completed.completed.and_then(|completion| {
conn.loaded_page_mut_for_protocol_access(session_id.as_deref())
.and_then(|page| {
page.finish_unit_runtime_page_command(
completion,
"DOMDebugger XHR breakpoint",
)
conn.loaded_page_mut_for_protocol_access_for_route(
session_id.as_deref(),
owner_route,
)
.and_then(|page| {
page.finish_unit_runtime_page_command(completion, "DOMDebugger XHR breakpoint")
.map_err(|error| error.to_string())
})
})
});
if let Err(message) = completion {
return CommandOutputPlan::error(-32000, message);
@@ -337,11 +340,14 @@ pub(crate) fn complete_pending_dom_debugger_command(
}
CompletedDomDebuggerOperation::ConfigureDomBreakpoint => {
let resolution = completed.completed.and_then(|completion| {
conn.loaded_page_mut_for_protocol_access(session_id.as_deref())
.and_then(|page| {
page.finish_dom_debugger_configure_dom_breakpoint(completion)
.map_err(|error| error.to_string())
})
conn.loaded_page_mut_for_protocol_access_for_route(
session_id.as_deref(),
owner_route,
)
.and_then(|page| {
page.finish_dom_debugger_configure_dom_breakpoint(completion)
.map_err(|error| error.to_string())
})
});
match resolution {
Ok(RendererDomDebuggerDomBreakpointResolution::Configured) => {
+12 -12
View File
@@ -9,19 +9,19 @@ use moli_core::page::{
};
use serde::Deserialize;
use crate::conn::{CdpConnection, Cmd};
use crate::conn::{CdpConnection, Cmd, CommandOwnerScope};
use crate::domains::actions::DomSnapshotAction;
use crate::domains::command_output::CommandOutputPlan;
pub(crate) struct PendingDomSnapshotCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
pending: PendingPageCommand,
}
pub(crate) struct CompletedDomSnapshotCommandDispatch {
command_id: Option<u64>,
session_id: Option<String>,
owner_scope: CommandOwnerScope,
completed: Result<CompletedPageCommand, String>,
}
@@ -31,14 +31,10 @@ pub(crate) enum DomSnapshotCommandDispatchStep {
}
impl PendingDomSnapshotCommandDispatch {
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
}
pub async fn wait(self) -> CompletedDomSnapshotCommandDispatch {
CompletedDomSnapshotCommandDispatch {
command_id: self.command_id,
session_id: self.session_id,
owner_scope: self.owner_scope,
completed: self.pending.wait().await.map_err(|error| error.to_string()),
}
}
@@ -50,7 +46,7 @@ impl CompletedDomSnapshotCommandDispatch {
}
pub(crate) fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
self.owner_scope.session_id()
}
}
@@ -143,7 +139,7 @@ fn start_capture_snapshot_command_with_params(
DomSnapshotCommandDispatchStep::Pending(PendingDomSnapshotCommandDispatch {
command_id,
session_id: session_id.map(str::to_owned),
owner_scope: CommandOwnerScope::capture(conn, session_id),
pending,
})
}
@@ -152,8 +148,12 @@ pub(crate) fn complete_pending_dom_snapshot_command(
conn: &mut CdpConnection,
completed: CompletedDomSnapshotCommandDispatch,
) -> DomSnapshotCommandDispatchStep {
let session_id = completed.session_id.as_deref();
let Some(page) = loaded_page_mut_for_session(conn, session_id) else {
let session_id = completed.owner_scope.session_id();
let owner_route = completed.owner_scope.session_owner_route();
let Some(page) = conn
.loaded_page_mut_for_protocol_access_for_route(session_id, owner_route)
.ok()
else {
return DomSnapshotCommandDispatchStep::Complete(CommandOutputPlan::error(
-32000,
"NoDocumentLoaded",
@@ -6,7 +6,9 @@ use serde_json::json;
use url::Url;
use crate::{
conn::{BrowserContextPageStorageHandles, CdpConnection, Cmd, CommandOwnerScope},
conn::{
BrowserContextPageStorageHandles, CdpConnection, CdpSessionRoute, Cmd, CommandOwnerScope,
},
domains::{actions::DomStorageAction, command_output::CommandOutputPlan},
};
@@ -199,7 +201,12 @@ fn start_storage_operation(
storage_id: DomStorageId,
operation: DomStorageOperation,
) -> DomStorageCommandTaskStep {
if let Err(plan) = storage_handles_for_session_owner(conn, cmd.session_id) {
let owner_scope = CommandOwnerScope::capture(conn, cmd.session_id);
if let Err(plan) = storage_handles_for_route(
conn,
owner_scope.session_id(),
owner_scope.session_owner_route(),
) {
return DomStorageCommandTaskStep::Complete(plan);
}
@@ -210,14 +217,17 @@ fn start_storage_operation(
return DomStorageCommandTaskStep::Complete(complete_storage_operation(
conn,
cmd.session_id,
owner_scope.session_owner_route(),
storage_id,
operation,
&storage_key,
));
}
let owner_scope = CommandOwnerScope::capture(conn, cmd.session_id);
if let Ok(page) = conn.loaded_page_mut_for_protocol_access(cmd.session_id) {
if let Ok(page) = conn.loaded_page_mut_for_protocol_access_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
) {
return match page.start_document_storage_key_snapshot() {
Ok(pending) => {
DomStorageCommandTaskStep::Pending(Box::new(PendingDomStorageCommandDispatch {
@@ -248,6 +258,7 @@ fn start_storage_operation(
DomStorageCommandTaskStep::Complete(complete_storage_operation(
conn,
cmd.session_id,
owner_scope.session_owner_route(),
storage_id,
operation,
&storage_key,
@@ -301,12 +312,11 @@ fn complete_top_frame_resolution(
return DomStorageCommandTaskStep::Complete(CommandOutputPlan::error(-32000, error));
}
};
let mut route_scope = owner_scope.enter(conn);
let storage_key = {
let Ok(page) = route_scope
.conn_mut()
.loaded_page_mut_for_protocol_access(owner_scope.session_id())
else {
let Ok(page) = conn.loaded_page_mut_for_protocol_access_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
) else {
return DomStorageCommandTaskStep::Complete(frame_not_found_plan());
};
match page.finish_document_storage_key_snapshot(completion) {
@@ -322,8 +332,9 @@ fn complete_top_frame_resolution(
if storage_id_matches_key(&storage_id, &storage_key) {
return DomStorageCommandTaskStep::Complete(complete_storage_operation(
route_scope.conn_mut(),
conn,
owner_scope.session_id(),
owner_scope.session_owner_route(),
storage_id,
operation,
&storage_key,
@@ -331,15 +342,14 @@ fn complete_top_frame_resolution(
}
let child_pending = {
let Ok(page) = route_scope
.conn_mut()
.loaded_page_mut_for_protocol_access(owner_scope.session_id())
else {
let Ok(page) = conn.loaded_page_mut_for_protocol_access_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
) else {
return DomStorageCommandTaskStep::Complete(frame_not_found_plan());
};
page.start_child_frame_tree_snapshot()
};
drop(route_scope);
match child_pending {
Ok(pending) => {
DomStorageCommandTaskStep::Pending(Box::new(PendingDomStorageCommandDispatch {
@@ -372,12 +382,11 @@ fn complete_child_frame_resolution(
return DomStorageCommandTaskStep::Complete(CommandOutputPlan::error(-32000, error));
}
};
let mut route_scope = owner_scope.enter(conn);
let child_frames = {
let Ok(page) = route_scope
.conn_mut()
.loaded_page_mut_for_protocol_access(owner_scope.session_id())
else {
let Ok(page) = conn.loaded_page_mut_for_protocol_access_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
) else {
return DomStorageCommandTaskStep::Complete(frame_not_found_plan());
};
match page.finish_child_frame_tree_snapshot(completion) {
@@ -394,8 +403,9 @@ fn complete_child_frame_resolution(
return DomStorageCommandTaskStep::Complete(frame_not_found_plan());
};
DomStorageCommandTaskStep::Complete(complete_storage_operation(
route_scope.conn_mut(),
conn,
owner_scope.session_id(),
owner_scope.session_owner_route(),
storage_id,
operation,
&storage_key,
@@ -405,6 +415,7 @@ fn complete_child_frame_resolution(
fn complete_storage_operation(
conn: &CdpConnection,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
storage_id: DomStorageId,
operation: DomStorageOperation,
storage_key: &str,
@@ -412,7 +423,7 @@ fn complete_storage_operation(
if !storage_id_matches_key(&storage_id, storage_key) {
return frame_not_found_plan();
}
let handles = match storage_handles_for_session_owner(conn, session_id) {
let handles = match storage_handles_for_route(conn, session_id, owner_route) {
Ok(handles) => handles,
Err(plan) => return plan,
};
@@ -458,9 +469,21 @@ where
fn storage_handles_for_session_owner(
conn: &CdpConnection,
session_id: Option<&str>,
) -> Result<BrowserContextPageStorageHandles, CommandOutputPlan> {
let none_session_owner_route = session_id
.is_none()
.then(|| conn.none_session_owner_route_override())
.flatten();
storage_handles_for_route(conn, session_id, none_session_owner_route.as_ref())
}
fn storage_handles_for_route(
conn: &CdpConnection,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> Result<BrowserContextPageStorageHandles, CommandOutputPlan> {
let Some((browser_context_id, Some(target_id))) =
conn.target_owner_identity_for_session(session_id)
conn.target_owner_identity_for_route(session_id, owner_route)
else {
return Err(CommandOutputPlan::error(
-32000,
+10 -10
View File
@@ -2832,18 +2832,18 @@ fn finish_pending_emulation_page_command(
) -> Result<(), String> {
match target {
PendingEmulationPageTarget::SessionOwner { owner_scope } => {
let mut route_scope = owner_scope.enter(conn);
if matches!(operation, PendingEmulationPageOperation::SetUserAgentLoader) {
return route_scope
.conn_mut()
.finish_rebuild_resource_runtime_for_session_owner(
owner_scope.session_id(),
completion,
);
return conn.finish_rebuild_resource_runtime_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
completion,
);
}
let page = route_scope
.conn_mut()
.loaded_page_mut_for_interruptible_protocol_access(owner_scope.session_id())
let page = conn
.loaded_page_mut_for_interruptible_protocol_access_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.ok();
finish_emulation_page_operation_on_current_attachment(page, operation, completion)
}
+63 -34
View File
@@ -1876,10 +1876,10 @@ impl CdpConnection {
&mut self,
registration: &PageScreencastRegistration,
) -> PageScreencastSubscriptionStatus {
let mut route_scope = registration.owner_scope.enter(self);
page_screencast_subscription_status_for_current_route(
route_scope.conn_mut(),
page_screencast_subscription_status_for_route(
self,
registration.session_id(),
registration.owner_scope.session_owner_route(),
registration.generation,
)
}
@@ -1892,22 +1892,25 @@ impl CdpConnection {
let session_id = registration.session_id().map(str::to_owned);
let generation = registration.generation;
let owner_scope = registration.owner_scope.clone();
let mut route_scope = owner_scope.enter(self);
let conn = route_scope.conn_mut();
let session_id_ref = session_id.as_deref();
if page_screencast_subscription_status_for_current_route(conn, session_id_ref, generation)
!= PageScreencastSubscriptionStatus::Ready
let owner_route = owner_scope.session_owner_route();
if page_screencast_subscription_status_for_route(
self,
session_id_ref,
owner_route,
generation,
) != PageScreencastSubscriptionStatus::Ready
{
return PageScreencastCaptureStart::Stale;
}
let Some(config) = conn
.target_page_session_state_for_session(session_id_ref)
let Some(config) = self
.target_page_session_state_for_route(session_id_ref, owner_route)
.and_then(|state| state.page_screencast.config())
.cloned()
else {
return PageScreencastCaptureStart::Stale;
};
let viewport = current_viewport_surface(conn, session_id_ref);
let viewport = current_viewport_surface_for_route(self, session_id_ref, owner_route);
let request = RendererCaptureScreencastFrameRequest {
format: match config.format() {
PageScreencastFormat::Png => RendererScreenshotFormat::Png,
@@ -1919,17 +1922,18 @@ impl CdpConnection {
max_height: config.max_height(),
known_visual_state,
};
let pending = match conn.loaded_page_mut_for_protocol_access(session_id_ref) {
Ok(page) => match page.start_capture_screencast_frame(request) {
Ok(pending) => pending,
Err(error) => {
tracing::debug!(?error, "failed to start screencast frame capture");
return PageScreencastCaptureStart::Retry;
}
},
Err(_) => return PageScreencastCaptureStart::Retry,
};
if conn.begin_page_screencast_capture_for_session_owner(session_id_ref, generation)
let pending =
match self.loaded_page_mut_for_protocol_access_for_route(session_id_ref, owner_route) {
Ok(page) => match page.start_capture_screencast_frame(request) {
Ok(pending) => pending,
Err(error) => {
tracing::debug!(?error, "failed to start screencast frame capture");
return PageScreencastCaptureStart::Retry;
}
},
Err(_) => return PageScreencastCaptureStart::Retry,
};
if self.begin_page_screencast_capture_for_route(session_id_ref, owner_route, generation)
!= Some(true)
{
tracing::debug!(
@@ -1959,22 +1963,28 @@ impl CdpConnection {
viewport,
completed,
} = completed;
let mut route_scope = owner_scope.enter(self);
let conn = route_scope.conn_mut();
let session_id_ref = session_id.as_deref();
if page_screencast_subscription_status_for_current_route(conn, session_id_ref, generation)
!= PageScreencastSubscriptionStatus::CaptureInProgress
let owner_route = owner_scope.session_owner_route();
if page_screencast_subscription_status_for_route(
self,
session_id_ref,
owner_route,
generation,
) != PageScreencastSubscriptionStatus::CaptureInProgress
{
return PageScreencastCaptureCompletion::Stale;
}
let frame = match completed {
Ok(completion) => {
let page = match conn.loaded_page_mut_for_protocol_access(session_id_ref) {
let page = match self
.loaded_page_mut_for_protocol_access_for_route(session_id_ref, owner_route)
{
Ok(page) => page,
Err(_) => {
let _ = conn.complete_page_screencast_capture_for_session_owner(
let _ = self.complete_page_screencast_capture_for_route(
session_id_ref,
owner_route,
generation,
false,
);
@@ -1984,8 +1994,9 @@ impl CdpConnection {
match page.finish_capture_screencast_frame(*completion) {
Ok(RendererCaptureScreencastFrameReply::Captured(frame)) => frame,
Ok(RendererCaptureScreencastFrameReply::Unchanged) => {
if conn.complete_page_screencast_capture_for_session_owner(
if self.complete_page_screencast_capture_for_route(
session_id_ref,
owner_route,
generation,
false,
) != Some(true)
@@ -1999,8 +2010,9 @@ impl CdpConnection {
| RendererCaptureScreencastFrameReply::NoDocument,
)
| Err(_) => {
let _ = conn.complete_page_screencast_capture_for_session_owner(
let _ = self.complete_page_screencast_capture_for_route(
session_id_ref,
owner_route,
generation,
false,
);
@@ -2009,8 +2021,9 @@ impl CdpConnection {
}
}
Err(_) => {
let _ = conn.complete_page_screencast_capture_for_session_owner(
let _ = self.complete_page_screencast_capture_for_route(
session_id_ref,
owner_route,
generation,
false,
);
@@ -2019,8 +2032,12 @@ impl CdpConnection {
};
let visual_state = frame.visual_state;
if conn.complete_page_screencast_capture_for_session_owner(session_id_ref, generation, true)
!= Some(true)
if self.complete_page_screencast_capture_for_route(
session_id_ref,
owner_route,
generation,
true,
) != Some(true)
{
return PageScreencastCaptureCompletion::Stale;
}
@@ -2048,12 +2065,13 @@ impl CdpConnection {
}
}
fn page_screencast_subscription_status_for_current_route(
fn page_screencast_subscription_status_for_route(
conn: &CdpConnection,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
generation: i32,
) -> PageScreencastSubscriptionStatus {
let Some(state) = conn.target_page_session_state_for_session(session_id) else {
let Some(state) = conn.target_page_session_state_for_route(session_id, owner_route) else {
return PageScreencastSubscriptionStatus::Inactive;
};
let screencast = &state.page_screencast;
@@ -5783,6 +5801,17 @@ fn current_viewport_surface(
)
}
fn current_viewport_surface_for_route(
conn: &CdpConnection,
session_id: Option<&str>,
owner_route: Option<&CdpSessionRoute>,
) -> EmulatedViewportSurface {
EmulatedViewportSurface::from_metrics(
conn.target_session_owner_emulated_device_metrics_for_route(session_id, owner_route)
.as_ref(),
)
}
async fn execute_devtools_get_layout_metrics_command(
conn: &mut CdpConnection,
command: DevToolsGetLayoutMetricsCommand,
@@ -647,11 +647,12 @@ pub(crate) async fn complete_page_target_termination_owner_action_async(
action: PageTargetTerminationOwnerAction,
) -> crate::conn::CdpTurnOutcome {
let (owner_scope, expected_target_id, kind) = action.into_parts();
let mut route_scope = owner_scope.enter(conn);
let conn = route_scope.conn_mut();
let mut out = Vec::new();
let current_target_id = conn
.target_owner_identity_for_session(owner_scope.session_id())
.target_owner_identity_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.and_then(|(_, target_id)| target_id);
if current_target_id.as_deref() != Some(expected_target_id.as_str()) {
return crate::conn::CdpTurnOutcome::new_with_protocol_events(
@@ -662,8 +663,11 @@ pub(crate) async fn complete_page_target_termination_owner_action_async(
let target_host_closure = conn.prepare_target_host_closure(&expected_target_id);
let closed = match kind {
PageTargetTerminationKind::PageClose => {
conn.close_page_target_for_session_owner_async(owner_scope.session_id())
.await
conn.close_page_target_for_route_async(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.await
}
PageTargetTerminationKind::TargetClose => {
let is_active_target = conn
@@ -3640,15 +3640,12 @@ async fn screencast_commands_update_page_session_state() {
assert_eq!(
ctx.conn
.begin_page_screencast_capture_for_session_owner(Some("SID-screencast"), 1),
.begin_page_screencast_capture_for_route(Some("SID-screencast"), None, 1),
Some(true)
);
assert_eq!(
ctx.conn.complete_page_screencast_capture_for_session_owner(
Some("SID-screencast"),
1,
true,
),
ctx.conn
.complete_page_screencast_capture_for_route(Some("SID-screencast"), None, 1, true,),
Some(true)
);
@@ -4125,15 +4122,17 @@ async fn repeated_start_invalidates_old_screencast_ack_generation() {
let generation = i32::try_from(id - 62).expect("small screencast generation");
assert_eq!(
ctx.conn.begin_page_screencast_capture_for_session_owner(
ctx.conn.begin_page_screencast_capture_for_route(
Some("SID-screencast-generation"),
None,
generation,
),
Some(true)
);
assert_eq!(
ctx.conn.complete_page_screencast_capture_for_session_owner(
ctx.conn.complete_page_screencast_capture_for_route(
Some("SID-screencast-generation"),
None,
generation,
true,
),
+136 -47
View File
@@ -1,6 +1,7 @@
use crate::conn::{
CdpConnection, CdpRendererCommandAccess, Cmd, CommandOwnerScope, PerformanceTimeDomain,
RendererCommandCorrelation, RendererCommandDescriptor, monotonic_timestamp_seconds,
CdpConnection, CdpRendererCommandAccess, CdpSessionRoute, Cmd, CommandOwnerScope,
PerformanceTimeDomain, RendererCommandCorrelation, RendererCommandDescriptor,
monotonic_timestamp_seconds,
};
use crate::domains::actions::PerformanceAction;
use crate::domains::command_output::CommandOutputPlan;
@@ -66,10 +67,6 @@ pub(crate) enum PerformanceCommandTaskStep {
}
impl PendingPerformanceCommandDispatch {
pub(crate) fn session_id(&self) -> Option<&str> {
self.owner_scope.session_id()
}
pub async fn wait(self) -> CompletedPerformanceCommandDispatch {
let completed = match *self.pending {
PendingPerformanceRendererCommand::Main(pending) => pending
@@ -119,14 +116,14 @@ fn loaded_page_mut_for_renderer_access<'a>(
conn: &'a mut CdpConnection,
session_id: Option<&str>,
renderer_access: CdpRendererCommandAccess,
owner_route: Option<&CdpSessionRoute>,
) -> Result<&'a mut Page, String> {
match renderer_access {
CdpRendererCommandAccess::MainThread => {
conn.loaded_page_mut_for_protocol_access(session_id)
}
CdpRendererCommandAccess::Io => {
conn.loaded_page_mut_for_interruptible_protocol_access(session_id)
conn.loaded_page_mut_for_protocol_access_for_route(session_id, owner_route)
}
CdpRendererCommandAccess::Io => conn
.loaded_page_mut_for_interruptible_protocol_access_for_route(session_id, owner_route),
CdpRendererCommandAccess::OwnerIndependent => {
Err("Performance.getMetrics requires a renderer Page".to_owned())
}
@@ -239,15 +236,19 @@ pub(crate) fn try_start_performance_command_dispatch(
let owner_scope = CommandOwnerScope::capture(conn, cmd.session_id);
if renderer_access == CdpRendererCommandAccess::Io {
let (renderer_page, attachment_id, snapshot) = {
let page =
match loaded_page_mut_for_renderer_access(conn, cmd.session_id, renderer_access) {
Ok(page) => page,
Err(_) => {
return PerformanceCommandTaskStep::Complete(
default_metrics_command_output_plan(),
);
}
};
let page = match loaded_page_mut_for_renderer_access(
conn,
cmd.session_id,
renderer_access,
owner_scope.session_owner_route(),
) {
Ok(page) => page,
Err(_) => {
return PerformanceCommandTaskStep::Complete(
default_metrics_command_output_plan(),
);
}
};
(
crate::conn::RendererPageResidenceIdentity::from_page(page),
page.renderer_agent_attachment_id(),
@@ -260,8 +261,13 @@ pub(crate) fn try_start_performance_command_dispatch(
if cmd.id.is_none()
|| response_delivery == moli_page_types::RendererInspectorResponseDelivery::CommandReply
{
let page = loaded_page_mut_for_renderer_access(conn, cmd.session_id, renderer_access)
.expect("the captured Performance Page must remain loaded synchronously");
let page = loaded_page_mut_for_renderer_access(
conn,
cmd.session_id,
renderer_access,
owner_scope.session_owner_route(),
)
.expect("the captured Performance Page must remain loaded synchronously");
let (pending, snapshot) = page.start_performance_metric_snapshot_from_io();
return PerformanceCommandTaskStep::Pending(PendingPerformanceCommandDispatch {
command_id: cmd.id,
@@ -306,21 +312,26 @@ pub(crate) fn try_start_performance_command_dispatch(
response_rx.is_none(),
"Performance session output must not allocate a command-reply receiver",
);
let pending = loaded_page_mut_for_renderer_access(conn, cmd.session_id, renderer_access)
.ok()
.filter(|page| {
crate::conn::RendererPageResidenceIdentity::from_page(page) == renderer_page
&& page.renderer_agent_attachment_id() == Some(attachment_id)
})
.ok_or_else(|| "Performance renderer attachment changed before IO dispatch".to_owned())
.and_then(|page| {
page.start_performance_get_metrics_from_io_with_response(
renderer_inspector_session_id,
result,
response,
)
.map_err(|error| error.to_string())
});
let pending = loaded_page_mut_for_renderer_access(
conn,
cmd.session_id,
renderer_access,
owner_scope.session_owner_route(),
)
.ok()
.filter(|page| {
crate::conn::RendererPageResidenceIdentity::from_page(page) == renderer_page
&& page.renderer_agent_attachment_id() == Some(attachment_id)
})
.ok_or_else(|| "Performance renderer attachment changed before IO dispatch".to_owned())
.and_then(|page| {
page.start_performance_get_metrics_from_io_with_response(
renderer_inspector_session_id,
result,
response,
)
.map_err(|error| error.to_string())
});
let pending = match pending {
Ok(pending) => pending,
Err(error) => {
@@ -345,7 +356,12 @@ pub(crate) fn try_start_performance_command_dispatch(
}),
});
}
let page = match loaded_page_mut_for_renderer_access(conn, cmd.session_id, renderer_access) {
let page = match loaded_page_mut_for_renderer_access(
conn,
cmd.session_id,
renderer_access,
owner_scope.session_owner_route(),
) {
Ok(page) => page,
Err(_) => {
return PerformanceCommandTaskStep::Complete(default_metrics_command_output_plan());
@@ -397,13 +413,13 @@ pub(crate) async fn complete_pending_performance_command(
completed,
} = completed;
let session_id = owner_scope.session_id().map(str::to_owned);
let mut owner_scope = owner_scope.enter(conn);
let snapshot = match completed {
Ok(CompletedPerformanceRendererCommand::Main(completed_page)) => {
loaded_page_mut_for_renderer_access(
owner_scope.conn_mut(),
conn,
session_id.as_deref(),
renderer_access,
owner_scope.session_owner_route(),
)
.ok()
.filter(|page| {
@@ -414,9 +430,10 @@ pub(crate) async fn complete_pending_performance_command(
}
Ok(CompletedPerformanceRendererCommand::IoCommandReply(snapshot)) => {
let remains_current = loaded_page_mut_for_renderer_access(
owner_scope.conn_mut(),
conn,
session_id.as_deref(),
renderer_access,
owner_scope.session_owner_route(),
)
.ok()
.is_some_and(|page| {
@@ -439,13 +456,11 @@ pub(crate) async fn complete_pending_performance_command(
plan.set_renderer_output_predecessor(predecessor);
return plan;
}
if !owner_scope
.conn_mut()
.take_renderer_call_if_correlation_matches_for_session_owner(
session_id.as_deref(),
correlation,
)
{
if !conn.take_renderer_call_if_correlation_matches_for_route(
session_id.as_deref(),
owner_scope.session_owner_route(),
correlation,
) {
return CommandOutputPlan::default();
}
RendererPerformanceMetricSnapshot::default()
@@ -929,6 +944,80 @@ mod tests {
assert!(metrics["Nodes"] >= 4.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn pending_performance_metrics_keep_captured_implicit_page_owner() {
let mut ctx = TestContext::new();
let active_url = "data:text/html,<body>active</body>";
let background_url = "data:text/html,<body><main><section></section><section></section><section></section><section></section></main></body>";
let mut browser_context = BrowserContext::new("BID-performance-owner".to_owned());
browser_context.set_active_target_id("TID-performance-active");
browser_context.attach_active_session("SID-performance-active");
browser_context.set_target_url(active_url.to_owned());
browser_context.stage_background_target(
"TID-performance-background".to_owned(),
Some("SID-performance-background".to_owned()),
background_url.to_owned(),
None,
None,
);
ctx.conn.browser_context = Some(browser_context);
ctx.install_navigation_fixture_for_session_owner(
active_url,
Some("SID-performance-active"),
)
.await;
ctx.install_navigation_fixture_for_session_owner(
background_url,
Some("SID-performance-background"),
)
.await;
let background_route = ctx
.conn
.target_session_route_for_target_id("TID-performance-background")
.expect("background Performance route");
let previous_route = ctx
.conn
.replace_none_session_owner_route_override(Some(background_route.clone()));
let enable = ctx
.conn
.start_command_dispatch(r#"{"id":4200,"method":"Performance.enable"}"#);
let enable_messages = complete_immediate_command_task_step_for_test(enable);
ctx.conn
.replace_none_session_owner_route_override(previous_route);
assert_eq!(enable_messages[0]["result"], json!({}));
let previous_route = ctx
.conn
.replace_none_session_owner_route_override(Some(background_route));
let pending = ctx
.conn
.start_command_dispatch(r#"{"id":4201,"method":"Performance.getMetrics"}"#);
ctx.conn
.replace_none_session_owner_route_override(previous_route);
assert!(matches!(pending, CdpCommandTaskStep::Pending(_)));
let active_route = ctx
.conn
.target_session_route_for_target_id("TID-performance-active")
.expect("active Performance route");
let previous_route = ctx
.conn
.replace_none_session_owner_route_override(Some(active_route));
let messages = complete_command_task_step_for_test(&mut ctx, pending, 4201).await;
ctx.conn
.replace_none_session_owner_route_override(previous_route);
let response = messages
.iter()
.find(|message| message["id"] == json!(4201))
.expect("pending background Performance response");
assert!(
metric_map(response)["Nodes"] >= 8.0,
"completion must finish the snapshot on the captured background Page"
);
assert_eq!(ctx.conn.none_session_owner_route_override(), None);
}
#[tokio::test(flavor = "multi_thread")]
async fn performance_get_metrics_reads_the_snapshot_bound_at_each_replacement() {
let mut ctx = TestContext::new();
+6 -6
View File
@@ -96,12 +96,12 @@ pub(crate) fn complete_pending_security_command(
Ok(completion) => completion,
Err(error) => return CommandOutputPlan::error(-32000, error),
};
let owner_scope = completed.owner_scope.clone();
let mut route_scope = owner_scope.enter(conn);
match route_scope
.conn_mut()
.finish_rebuild_resource_runtime_for_session_owner(owner_scope.session_id(), completion)
{
let owner_scope = completed.owner_scope;
match conn.finish_rebuild_resource_runtime_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
completion,
) {
Ok(()) => CommandOutputPlan::success(),
Err(error) => CommandOutputPlan::error(-32000, error),
}
+19 -9
View File
@@ -495,7 +495,9 @@ fn start_get_storage_key_for_frame_command(
if params.frame_id == target_id {
let owner_scope = CommandOwnerScope::capture(conn, cmd.session_id);
if let Some(page) = loaded_page_mut_for_session(conn, cmd.session_id) {
if let Some(page) =
loaded_page_mut_for_session(conn, cmd.session_id, owner_scope.session_owner_route())
{
return match page.start_document_storage_key_snapshot() {
Ok(pending) => StorageCommandTaskStep::Pending(PendingStorageCommandDispatch {
command_id: cmd.id,
@@ -522,7 +524,9 @@ fn start_get_storage_key_for_frame_command(
return StorageCommandTaskStep::Complete(CommandOutputPlan::error(-32000, message));
}
let owner_scope = CommandOwnerScope::capture(conn, cmd.session_id);
let Some(page) = loaded_page_mut_for_session(conn, cmd.session_id) else {
let Some(page) =
loaded_page_mut_for_session(conn, cmd.session_id, owner_scope.session_owner_route())
else {
return StorageCommandTaskStep::Complete(CommandOutputPlan::error(
-32000,
"NoFrameForGivenId",
@@ -773,9 +777,11 @@ fn complete_get_storage_key_for_top_frame_command(
Ok(completion) => completion,
Err(error) => return CommandOutputPlan::error(-32000, error),
};
let mut route_scope = owner_scope.enter(conn);
let Some(page) = loaded_page_mut_for_session(route_scope.conn_mut(), owner_scope.session_id())
else {
let Some(page) = loaded_page_mut_for_session(
conn,
owner_scope.session_id(),
owner_scope.session_owner_route(),
) else {
return CommandOutputPlan::error(-32000, "NoFrameForGivenId");
};
match page.finish_document_storage_key_snapshot(completion) {
@@ -794,9 +800,11 @@ fn complete_get_storage_key_for_frame_command(
Ok(completion) => completion,
Err(error) => return CommandOutputPlan::error(-32000, error),
};
let mut route_scope = owner_scope.enter(conn);
let Some(page) = loaded_page_mut_for_session(route_scope.conn_mut(), owner_scope.session_id())
else {
let Some(page) = loaded_page_mut_for_session(
conn,
owner_scope.session_id(),
owner_scope.session_owner_route(),
) else {
return CommandOutputPlan::error(-32000, "NoFrameForGivenId");
};
let child_frames = match page.finish_child_frame_tree_snapshot(completion) {
@@ -910,8 +918,10 @@ fn complete_set_cookies_result(
fn loaded_page_mut_for_session<'a>(
conn: &'a mut CdpConnection,
session_id: Option<&str>,
owner_route: Option<&crate::conn::CdpSessionRoute>,
) -> Option<&'a mut moli_core::page::Page> {
conn.loaded_page_mut_for_protocol_access(session_id).ok()
conn.loaded_page_mut_for_protocol_access_for_route(session_id, owner_route)
.ok()
}
fn find_child_frame<'a>(
+9 -10
View File
@@ -566,16 +566,15 @@ pub(crate) async fn complete_popup_target_activation_action_async(
action: PopupTargetActivationAction,
) -> crate::conn::CdpTurnOutcome {
let (owner_scope, browser_context_id, target_id) = action.into_parts();
let target_is_current = {
let mut route_scope = owner_scope.enter(conn);
let conn = route_scope.conn_mut();
conn.target_owner_identity_for_session(None).is_some_and(
|(current_browser_context_id, current_target_id)| {
current_browser_context_id == browser_context_id
&& current_target_id.as_deref() == Some(target_id.as_str())
},
) && popup_target_has_loaded_page(conn, &browser_context_id, &target_id)
};
let target_is_current =
conn.target_owner_identity_for_route(
owner_scope.session_id(),
owner_scope.session_owner_route(),
)
.is_some_and(|(current_browser_context_id, current_target_id)| {
current_browser_context_id == browser_context_id
&& current_target_id.as_deref() == Some(target_id.as_str())
}) && popup_target_has_loaded_page(conn, &browser_context_id, &target_id);
if !target_is_current {
tracing::debug!(
browser_context_id,