mirror of
https://github.com/lexmount/moli.git
synced 2026-10-05 00:00:55 +00:00
refactor(cdp): route page work through explicit owners
This commit is contained in:
+94
-91
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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(
|
||||
¶ms.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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user