diff --git a/src/browser.rs b/src/browser.rs index a896b79a..f605367c 100644 --- a/src/browser.rs +++ b/src/browser.rs @@ -13,6 +13,7 @@ use futures::SinkExt; use chromiumoxide_cdp::cdp::browser_protocol::target::{ CreateBrowserContextParams, CreateTargetParams, DisposeBrowserContextParams, TargetId, + TargetInfo, }; use chromiumoxide_cdp::cdp::{CdpEventMessage, IntoEventKind}; use chromiumoxide_types::*; @@ -177,6 +178,26 @@ impl Browser { Ok((browser, fut)) } + /// Request to fetch all existing browser targets. + /// + /// By default, only targets launched after the browser connection are tracked + /// when connecting to a existing browser instance with the devtools websocket url + /// This function fetches existing targets on the browser and adds them as pages internally + /// + /// The pages are not guaranteed to be ready as soon as the function returns + /// You should wait a few millis if you need to use a page + /// Returns [TargetInfo] + pub async fn fetch_targets(&mut self) -> Result> { + let (tx, rx) = oneshot_channel(); + + self.sender + .clone() + .send(HandlerMessage::FetchTargets(tx)) + .await?; + + rx.await? + } + /// Request for the browser to close completely. /// /// If the browser was spawned by [`Browser::launch`], it is recommended to wait for the diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 36cd7df6..c781de43 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -213,6 +213,30 @@ impl Handler { } } } + PendingRequest::GetTargets(tx) => { + match to_command_response::(resp, method) { + Ok(resp) => { + let targets: Vec = resp.result.target_infos; + let results = targets.clone(); + for target_info in targets { + let target_id = target_info.target_id.clone(); + let event: EventTargetCreated = EventTargetCreated { target_info }; + self.on_target_created(event); + let attach = AttachToTargetParams::new(target_id); + let _ = self.conn.submit_command( + attach.identifier(), + None, + serde_json::to_value(attach).unwrap(), + ); + } + + let _ = tx.send(Ok(results)).ok(); + } + Err(err) => { + let _ = tx.send(Err(err)).ok(); + } + } + } PendingRequest::Navigate(id) => { self.on_navigation_response(id, resp); } @@ -266,6 +290,18 @@ impl Handler { Ok(()) } + fn submit_fetch_targets(&mut self, tx: OneshotSender>>, now: Instant) { + let msg = GetTargetsParams { filter: None }; + let method = msg.identifier(); + let call_id = self + .conn + .submit_command(method.clone(), None, serde_json::to_value(msg).unwrap()) + .unwrap(); + + self.pending_commands + .insert(call_id, (PendingRequest::GetTargets(tx), method, now)); + } + /// Send the Request over to the server and store its identifier to handle /// the response once received. fn submit_navigation(&mut self, id: NavigationId, req: CdpRequest, now: Instant) { @@ -456,6 +492,9 @@ impl Handler { PendingRequest::CreateTarget(tx) => { let _ = tx.send(Err(CdpError::Timeout)); } + PendingRequest::GetTargets(tx) => { + let _ = tx.send(Err(CdpError::Timeout)); + } PendingRequest::Navigate(nav) => { if let Some(nav) = self.navigations.remove(&nav) { match nav { @@ -498,6 +537,9 @@ impl Stream for Handler { HandlerMessage::Command(cmd) => { pin.submit_external_command(cmd, now)?; } + HandlerMessage::FetchTargets(tx) => { + pin.submit_fetch_targets(tx, now); + } HandlerMessage::CloseBrowser(tx) => { pin.submit_close(tx, now); } @@ -679,6 +721,8 @@ enum PendingRequest { /// A Request to create a new `Target` that results in the creation of a /// `Page` that represents a browser page. CreateTarget(OneshotSender>), + /// A Request to fetch old `Target`s created before connection + GetTargets(OneshotSender>>), /// A Request to navigate a specific `Target`. /// /// Navigation requests are not automatically completed once the response to @@ -701,6 +745,7 @@ enum PendingRequest { #[derive(Debug)] pub(crate) enum HandlerMessage { CreatePage(CreateTargetParams, OneshotSender>), + FetchTargets(OneshotSender>>), InsertContext(BrowserContext), DisposeContext(BrowserContext), GetPages(OneshotSender>),