sl4f_lib/webdriver/
facade.rs1use crate::webdriver::types::{EnableDevToolsResult, GetDevToolsPortsResult};
6use anyhow::{Error, format_err};
7use fidl::endpoints::{ServerEnd, create_request_stream};
8use fidl_fuchsia_web::{
9 DevToolsListenerMarker, DevToolsListenerRequest, DevToolsListenerRequestStream,
10 DevToolsPerContextListenerMarker, DevToolsPerContextListenerRequest,
11 DevToolsPerContextListenerRequestStream,
12};
13use fuchsia_async as fasync;
14use fuchsia_sync::Mutex;
15use futures::channel::mpsc;
16use futures::prelude::*;
17use log::*;
18use std::collections::HashSet;
19use std::ops::DerefMut;
20
21#[derive(Debug)]
25pub struct WebdriverFacade {
26 internal: Mutex<Option<WebdriverFacadeInternal>>,
29}
30
31impl WebdriverFacade {
32 pub fn new() -> WebdriverFacade {
34 WebdriverFacade { internal: Mutex::new(None) }
35 }
36
37 pub async fn enable_dev_tools(&self) -> Result<EnableDevToolsResult, Error> {
40 if self.internal.lock().is_some() {
41 return Err(format_err!("DevTools already enabled."));
42 }
43 let initialized_internal = WebdriverFacadeInternal::new().await?;
44
45 let mut internal = self.internal.lock();
46
47 if internal.is_none() {
48 *internal = Some(initialized_internal);
49 Ok(EnableDevToolsResult::Success)
50 } else {
51 Err(format_err!("DevTools already enabled."))
52 }
53 }
54
55 pub async fn get_dev_tools_ports(&self) -> Result<GetDevToolsPortsResult, Error> {
58 let mut internal = self.internal.lock();
59 match internal.deref_mut() {
60 Some(facade) => Ok(GetDevToolsPortsResult::new(facade.get_ports())),
61 None => Err(format_err!("DevTools are not enabled.")),
62 }
63 }
64}
65
66#[derive(Debug)]
69struct WebdriverFacadeInternal {
70 dev_tools_ports: HashSet<u16>,
72 port_update_receiver: mpsc::UnboundedReceiver<PortUpdateMessage>,
74}
75
76impl WebdriverFacadeInternal {
77 pub async fn new() -> Result<WebdriverFacadeInternal, Error> {
80 let port_update_receiver = Self::get_port_event_receiver().await?;
81 Ok(WebdriverFacadeInternal { dev_tools_ports: HashSet::new(), port_update_receiver })
82 }
83
84 pub fn get_ports(&mut self) -> Vec<u16> {
86 self.update_port_set();
87 Vec::from_iter(self.dev_tools_ports.iter().cloned())
88 }
89
90 fn update_port_set(&mut self) {
92 while let Ok(update) = self.port_update_receiver.try_recv() {
93 match update {
94 PortUpdateMessage::PortOpened(port) => self.dev_tools_ports.insert(port),
95 PortUpdateMessage::PortClosed(port) => self.dev_tools_ports.remove(&port),
96 };
97 }
98 }
99
100 async fn get_port_event_receiver() -> Result<mpsc::UnboundedReceiver<PortUpdateMessage>, Error>
103 {
104 let (port_update_sender, port_update_receiver) = mpsc::unbounded();
105
106 let debug = Self::spawn_dev_tools_listener_at_path(
107 "/svc/fuchsia.web.Debug",
108 port_update_sender.clone(),
109 );
110 let debug_context_provider = Self::spawn_dev_tools_listener_at_path(
111 "/svc/fuchsia.web.Debug-context_provider",
112 port_update_sender,
113 );
114
115 let debug_result = debug.await;
117 let debug_context_provider_result = debug_context_provider.await;
118 debug_result.or(debug_context_provider_result)?;
119
120 Ok(port_update_receiver)
121 }
122
123 async fn spawn_dev_tools_listener_at_path(
127 protocol_path: &str,
128 port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>,
129 ) -> Result<(), Error> {
130 let debug_proxy = fuchsia_component::client::connect_to_protocol_at_path::<
132 fidl_fuchsia_web::DebugMarker,
133 >(protocol_path)?;
134
135 let (dev_tools_client, dev_tools_stream) =
137 create_request_stream::<DevToolsListenerMarker>();
138 debug_proxy.enable_dev_tools(dev_tools_client).await?;
139
140 fasync::Task::spawn(async move {
142 let dev_tools_listener = DevToolsListener::new(port_update_sender);
143 dev_tools_listener
144 .handle_requests_from_stream(dev_tools_stream)
145 .await
146 .unwrap_or_else(|_| print!("Error handling DevToolsListener channel!"));
147 })
148 .detach();
149
150 Ok(())
151 }
152}
153
154#[derive(Debug)]
157enum PortUpdateMessage {
158 PortOpened(u16),
160 PortClosed(u16),
162}
163
164struct DevToolsListener {
167 port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>,
169}
170
171impl DevToolsListener {
172 fn new(port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>) -> Self {
174 DevToolsListener { port_update_sender }
175 }
176
177 pub async fn handle_requests_from_stream(
179 &self,
180 mut stream: DevToolsListenerRequestStream,
181 ) -> Result<(), Error> {
182 while let Some(request) = stream.try_next().await? {
183 let DevToolsListenerRequest::OnContextDevToolsAvailable { listener, .. } = request;
184 self.on_context_created(listener)?;
185 }
186 Ok(())
187 }
188
189 fn on_context_created(
192 &self,
193 listener: ServerEnd<DevToolsPerContextListenerMarker>,
194 ) -> Result<(), Error> {
195 info!("Chrome context created");
196 let listener_request_stream = listener.into_stream();
197 let port_update_sender = mpsc::UnboundedSender::clone(&self.port_update_sender);
198 fasync::Task::spawn(async move {
199 let mut per_context_listener = DevToolsPerContextListener::new(port_update_sender);
200 per_context_listener
201 .handle_requests_from_stream(listener_request_stream)
202 .await
203 .unwrap_or_else(|_| warn!("Error handling DevToolsListener channel!"));
204 })
205 .detach();
206 Ok(())
207 }
208}
209
210struct DevToolsPerContextListener {
213 port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>,
215}
216
217impl DevToolsPerContextListener {
218 fn new(port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>) -> Self {
220 DevToolsPerContextListener { port_update_sender }
221 }
222
223 pub async fn handle_requests_from_stream(
227 &mut self,
228 mut stream: DevToolsPerContextListenerRequestStream,
229 ) -> Result<(), Error> {
230 let mut context_port = None;
231
232 while let Ok(Some(request)) = stream.try_next().await {
233 let DevToolsPerContextListenerRequest::OnHttpPortOpen { port, .. } = request;
234 context_port.replace(port);
235 self.on_port_open(port)?;
236 }
237
238 if let Some(port) = context_port {
240 self.on_port_closed(port)?;
241 }
242 Ok(())
243 }
244
245 fn on_port_open(&mut self, port: u16) -> Result<(), Error> {
247 info!("DevTools port {:?} opened", port);
248 self.port_update_sender
249 .unbounded_send(PortUpdateMessage::PortOpened(port))
250 .map_err(|_| format_err!("Error sending port open message"))
251 }
252
253 fn on_port_closed(&mut self, port: u16) -> Result<(), Error> {
255 info!("DevTools port {:?} closed", port);
256 self.port_update_sender
257 .unbounded_send(PortUpdateMessage::PortClosed(port))
258 .map_err(|_| format_err!("Error sending port closed message"))
259 }
260}