fix: enhance target tracking and update page information handling (#969)
This commit is contained in:
@@ -9,13 +9,13 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
|||||||
use tokio::sync::{broadcast, oneshot, RwLock};
|
use tokio::sync::{broadcast, oneshot, RwLock};
|
||||||
|
|
||||||
use super::auth;
|
use super::auth;
|
||||||
use super::browser::{BrowserManager, WaitUntil};
|
use super::browser::{should_track_target, BrowserManager, WaitUntil};
|
||||||
use super::cdp::chrome::LaunchOptions;
|
use super::cdp::chrome::LaunchOptions;
|
||||||
use super::cdp::client::CdpClient;
|
use super::cdp::client::CdpClient;
|
||||||
use super::cdp::types::{
|
use super::cdp::types::{
|
||||||
AttachToTargetParams, AttachToTargetResult, CdpEvent, ConsoleApiCalledEvent,
|
AttachToTargetParams, AttachToTargetResult, CdpEvent, ConsoleApiCalledEvent,
|
||||||
CreateTargetResult, DispatchMouseEventParams, ExceptionThrownEvent, TargetCreatedEvent,
|
CreateTargetResult, DispatchMouseEventParams, ExceptionThrownEvent, TargetCreatedEvent,
|
||||||
TargetDestroyedEvent,
|
TargetDestroyedEvent, TargetInfoChangedEvent,
|
||||||
};
|
};
|
||||||
use super::cookies;
|
use super::cookies;
|
||||||
use super::diff;
|
use super::diff;
|
||||||
@@ -137,6 +137,7 @@ pub struct MouseState {
|
|||||||
struct DrainedEvents {
|
struct DrainedEvents {
|
||||||
pending_acks: Vec<i64>,
|
pending_acks: Vec<i64>,
|
||||||
new_targets: Vec<TargetCreatedEvent>,
|
new_targets: Vec<TargetCreatedEvent>,
|
||||||
|
changed_targets: Vec<TargetInfoChangedEvent>,
|
||||||
destroyed_targets: Vec<String>,
|
destroyed_targets: Vec<String>,
|
||||||
/// Cross-origin iframe (frame_id, session_id) pairs from Target.attachedToTarget.
|
/// Cross-origin iframe (frame_id, session_id) pairs from Target.attachedToTarget.
|
||||||
attached_iframe_sessions: Vec<(String, String)>,
|
attached_iframe_sessions: Vec<(String, String)>,
|
||||||
@@ -379,6 +380,7 @@ impl DaemonState {
|
|||||||
|
|
||||||
let mut pending_acks: Vec<i64> = Vec::new();
|
let mut pending_acks: Vec<i64> = Vec::new();
|
||||||
let mut new_targets: Vec<TargetCreatedEvent> = Vec::new();
|
let mut new_targets: Vec<TargetCreatedEvent> = Vec::new();
|
||||||
|
let mut changed_targets: Vec<TargetInfoChangedEvent> = Vec::new();
|
||||||
let mut destroyed_targets: Vec<String> = Vec::new();
|
let mut destroyed_targets: Vec<String> = Vec::new();
|
||||||
let mut attached_iframe_sessions: Vec<(String, String)> = Vec::new();
|
let mut attached_iframe_sessions: Vec<(String, String)> = Vec::new();
|
||||||
let mut detached_iframe_sessions: Vec<String> = Vec::new();
|
let mut detached_iframe_sessions: Vec<String> = Vec::new();
|
||||||
@@ -392,10 +394,7 @@ impl DaemonState {
|
|||||||
if let Ok(te) =
|
if let Ok(te) =
|
||||||
serde_json::from_value::<TargetCreatedEvent>(event.params.clone())
|
serde_json::from_value::<TargetCreatedEvent>(event.params.clone())
|
||||||
{
|
{
|
||||||
if (te.target_info.target_type == "page"
|
if should_track_target(&te.target_info) {
|
||||||
|| te.target_info.target_type == "webview")
|
|
||||||
&& !te.target_info.url.is_empty()
|
|
||||||
{
|
|
||||||
let already_tracked = self
|
let already_tracked = self
|
||||||
.browser
|
.browser
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -407,6 +406,16 @@ impl DaemonState {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
"Target.targetInfoChanged" => {
|
||||||
|
if let Ok(te) = serde_json::from_value::<TargetInfoChangedEvent>(
|
||||||
|
event.params.clone(),
|
||||||
|
) {
|
||||||
|
if should_track_target(&te.target_info) {
|
||||||
|
changed_targets.push(te);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
"Target.targetDestroyed" => {
|
"Target.targetDestroyed" => {
|
||||||
if let Ok(te) =
|
if let Ok(te) =
|
||||||
serde_json::from_value::<TargetDestroyedEvent>(event.params.clone())
|
serde_json::from_value::<TargetDestroyedEvent>(event.params.clone())
|
||||||
@@ -687,6 +696,7 @@ impl DaemonState {
|
|||||||
DrainedEvents {
|
DrainedEvents {
|
||||||
pending_acks,
|
pending_acks,
|
||||||
new_targets,
|
new_targets,
|
||||||
|
changed_targets,
|
||||||
destroyed_targets,
|
destroyed_targets,
|
||||||
attached_iframe_sessions,
|
attached_iframe_sessions,
|
||||||
detached_iframe_sessions,
|
detached_iframe_sessions,
|
||||||
@@ -716,6 +726,7 @@ pub async fn execute_command(cmd: &Value, state: &mut DaemonState) -> Value {
|
|||||||
let DrainedEvents {
|
let DrainedEvents {
|
||||||
pending_acks,
|
pending_acks,
|
||||||
new_targets,
|
new_targets,
|
||||||
|
changed_targets,
|
||||||
destroyed_targets,
|
destroyed_targets,
|
||||||
attached_iframe_sessions,
|
attached_iframe_sessions,
|
||||||
detached_iframe_sessions,
|
detached_iframe_sessions,
|
||||||
@@ -795,6 +806,12 @@ pub async fn execute_command(cmd: &Value, state: &mut DaemonState) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for te in &changed_targets {
|
||||||
|
if let Some(ref mut mgr) = state.browser {
|
||||||
|
mgr.update_page_target_info(&te.target_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hot-reload and check action policy
|
// Hot-reload and check action policy
|
||||||
if let Some(ref mut policy) = state.policy {
|
if let Some(ref mut policy) = state.policy {
|
||||||
let _ = policy.reload();
|
let _ = policy.reload();
|
||||||
|
|||||||
@@ -95,6 +95,21 @@ fn is_internal_chrome_target(url: &str) -> bool {
|
|||||||
|| url.starts_with("devtools://")
|
|| url.starts_with("devtools://")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn should_track_target(target: &TargetInfo) -> bool {
|
||||||
|
(target.target_type == "page" || target.target_type == "webview")
|
||||||
|
&& (target.url.is_empty() || !is_internal_chrome_target(&target.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_page_target_info_in_pages(pages: &mut [PageInfo], target: &TargetInfo) -> bool {
|
||||||
|
if let Some(page) = pages.iter_mut().find(|p| p.target_id == target.target_id) {
|
||||||
|
page.url = target.url.clone();
|
||||||
|
page.title = target.title.clone();
|
||||||
|
page.target_type = target.target_type.clone();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts common error messages into AI-friendly, actionable descriptions.
|
/// Converts common error messages into AI-friendly, actionable descriptions.
|
||||||
pub fn to_ai_friendly_error(error: &str) -> String {
|
pub fn to_ai_friendly_error(error: &str) -> String {
|
||||||
let lower = error.to_lowercase();
|
let lower = error.to_lowercase();
|
||||||
@@ -334,11 +349,7 @@ impl BrowserManager {
|
|||||||
let page_targets: Vec<TargetInfo> = result
|
let page_targets: Vec<TargetInfo> = result
|
||||||
.target_infos
|
.target_infos
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|t| {
|
.filter(should_track_target)
|
||||||
(t.target_type == "page" || t.target_type == "webview")
|
|
||||||
&& !t.url.is_empty()
|
|
||||||
&& !is_internal_chrome_target(&t.url)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if page_targets.is_empty() {
|
if page_targets.is_empty() {
|
||||||
@@ -1072,6 +1083,10 @@ impl BrowserManager {
|
|||||||
self.active_page_index = index;
|
self.active_page_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_page_target_info(&mut self, target: &TargetInfo) -> bool {
|
||||||
|
update_page_target_info_in_pages(&mut self.pages, target)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_page_by_target_id(&mut self, target_id: &str) {
|
pub fn remove_page_by_target_id(&mut self, target_id: &str) {
|
||||||
if let Some(pos) = self.pages.iter().position(|p| p.target_id == target_id) {
|
if let Some(pos) = self.pages.iter().position(|p| p.target_id == target_id) {
|
||||||
self.pages.remove(pos);
|
self.pages.remove(pos);
|
||||||
@@ -1328,6 +1343,57 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_track_popup_target_with_empty_url() {
|
||||||
|
let target = TargetInfo {
|
||||||
|
target_id: "popup-1".to_string(),
|
||||||
|
target_type: "page".to_string(),
|
||||||
|
title: String::new(),
|
||||||
|
url: String::new(),
|
||||||
|
attached: None,
|
||||||
|
browser_context_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(should_track_target(&target));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_not_track_internal_chrome_target() {
|
||||||
|
let target = TargetInfo {
|
||||||
|
target_id: "chrome-tab".to_string(),
|
||||||
|
target_type: "page".to_string(),
|
||||||
|
title: "New Tab".to_string(),
|
||||||
|
url: "chrome://newtab/".to_string(),
|
||||||
|
attached: None,
|
||||||
|
browser_context_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(!should_track_target(&target));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_page_target_info_in_pages_updates_existing_page() {
|
||||||
|
let mut pages = vec![PageInfo {
|
||||||
|
target_id: "popup-1".to_string(),
|
||||||
|
session_id: "session-1".to_string(),
|
||||||
|
url: String::new(),
|
||||||
|
title: String::new(),
|
||||||
|
target_type: "page".to_string(),
|
||||||
|
}];
|
||||||
|
let target = TargetInfo {
|
||||||
|
target_id: "popup-1".to_string(),
|
||||||
|
target_type: "page".to_string(),
|
||||||
|
title: "Popup".to_string(),
|
||||||
|
url: "https://example.com/popup".to_string(),
|
||||||
|
attached: None,
|
||||||
|
browser_context_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(update_page_target_info_in_pages(&mut pages, &target));
|
||||||
|
assert_eq!(pages[0].url, "https://example.com/popup");
|
||||||
|
assert_eq!(pages[0].title, "Popup");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_launch_options_extensions_and_cdp() {
|
fn test_validate_launch_options_extensions_and_cdp() {
|
||||||
let ext = vec!["/path/to/ext".to_string()];
|
let ext = vec!["/path/to/ext".to_string()];
|
||||||
|
|||||||
Reference in New Issue
Block a user