sl4f_lib/webdriver/
facade.rs1use crate::webdriver::types::{EnableDevToolsResult, GetDevToolsPortsResult};
6use anyhow::{format_err, Error};
7use fidl::endpoints::{create_request_stream, ServerEnd};
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 let mut internal = self.internal.lock();
41 if internal.is_none() {
42 let initialized_internal = WebdriverFacadeInternal::new().await?;
43 internal.replace(initialized_internal);
44 Ok(EnableDevToolsResult::Success)
45 } else {
46 Err(format_err!("DevTools already enabled."))
47 }
48 }
49
50 pub async fn get_dev_tools_ports(&self) -> Result<GetDevToolsPortsResult, Error> {
53 let mut internal = self.internal.lock();
54 match internal.deref_mut() {
55 Some(facade) => Ok(GetDevToolsPortsResult::new(facade.get_ports())),
56 None => Err(format_err!("DevTools are not enabled.")),
57 }
58 }
59}
60
61#[derive(Debug)]
64struct WebdriverFacadeInternal {
65 dev_tools_ports: HashSet<u16>,
67 port_update_receiver: mpsc::UnboundedReceiver<PortUpdateMessage>,
69}
70
71impl WebdriverFacadeInternal {
72 pub async fn new() -> Result<WebdriverFacadeInternal, Error> {
75 let port_update_receiver = Self::get_port_event_receiver().await?;
76 Ok(WebdriverFacadeInternal { dev_tools_ports: HashSet::new(), port_update_receiver })
77 }
78
79 pub fn get_ports(&mut self) -> Vec<u16> {
81 self.update_port_set();
82 Vec::from_iter(self.dev_tools_ports.iter().cloned())
83 }
84
85 fn update_port_set(&mut self) {
87 while let Ok(Some(update)) = self.port_update_receiver.try_next() {
88 match update {
89 PortUpdateMessage::PortOpened(port) => self.dev_tools_ports.insert(port),
90 PortUpdateMessage::PortClosed(port) => self.dev_tools_ports.remove(&port),
91 };
92 }
93 }
94
95 async fn get_port_event_receiver() -> Result<mpsc::UnboundedReceiver<PortUpdateMessage>, Error>
98 {
99 let (port_update_sender, port_update_receiver) = mpsc::unbounded();
100
101 let debug = Self::spawn_dev_tools_listener_at_path(
102 "/svc/fuchsia.web.Debug",
103 port_update_sender.clone(),
104 );
105 let debug_context_provider = Self::spawn_dev_tools_listener_at_path(
106 "/svc/fuchsia.web.Debug-context_provider",
107 port_update_sender,
108 );
109
110 let debug_result = debug.await;
112 let debug_context_provider_result = debug_context_provider.await;
113 debug_result.or(debug_context_provider_result)?;
114
115 Ok(port_update_receiver)
116 }
117
118 async fn spawn_dev_tools_listener_at_path(
122 protocol_path: &str,
123 port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>,
124 ) -> Result<(), Error> {
125 let debug_proxy = fuchsia_component::client::connect_to_protocol_at_path::<
127 fidl_fuchsia_web::DebugMarker,
128 >(protocol_path)?;
129
130 let (dev_tools_client, dev_tools_stream) =
132 create_request_stream::<DevToolsListenerMarker>();
133 debug_proxy.enable_dev_tools(dev_tools_client).await?;
134
135 fasync::Task::spawn(async move {
137 let dev_tools_listener = DevToolsListener::new(port_update_sender);
138 dev_tools_listener
139 .handle_requests_from_stream(dev_tools_stream)
140 .await
141 .unwrap_or_else(|_| print!("Error handling DevToolsListener channel!"));
142 })
143 .detach();
144
145 Ok(())
146 }
147}
148
149#[derive(Debug)]
152enum PortUpdateMessage {
153 PortOpened(u16),
155 PortClosed(u16),
157}
158
159struct DevToolsListener {
162 port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>,
164}
165
166impl DevToolsListener {
167 fn new(port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>) -> Self {
169 DevToolsListener { port_update_sender }
170 }
171
172 pub async fn handle_requests_from_stream(
174 &self,
175 mut stream: DevToolsListenerRequestStream,
176 ) -> Result<(), Error> {
177 while let Some(request) = stream.try_next().await? {
178 let DevToolsListenerRequest::OnContextDevToolsAvailable { listener, .. } = request;
179 self.on_context_created(listener)?;
180 }
181 Ok(())
182 }
183
184 fn on_context_created(
187 &self,
188 listener: ServerEnd<DevToolsPerContextListenerMarker>,
189 ) -> Result<(), Error> {
190 info!("Chrome context created");
191 let listener_request_stream = listener.into_stream();
192 let port_update_sender = mpsc::UnboundedSender::clone(&self.port_update_sender);
193 fasync::Task::spawn(async move {
194 let mut per_context_listener = DevToolsPerContextListener::new(port_update_sender);
195 per_context_listener
196 .handle_requests_from_stream(listener_request_stream)
197 .await
198 .unwrap_or_else(|_| warn!("Error handling DevToolsListener channel!"));
199 })
200 .detach();
201 Ok(())
202 }
203}
204
205struct DevToolsPerContextListener {
208 port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>,
210}
211
212impl DevToolsPerContextListener {
213 fn new(port_update_sender: mpsc::UnboundedSender<PortUpdateMessage>) -> Self {
215 DevToolsPerContextListener { port_update_sender }
216 }
217
218 pub async fn handle_requests_from_stream(
222 &mut self,
223 mut stream: DevToolsPerContextListenerRequestStream,
224 ) -> Result<(), Error> {
225 let mut context_port = None;
226
227 while let Ok(Some(request)) = stream.try_next().await {
228 let DevToolsPerContextListenerRequest::OnHttpPortOpen { port, .. } = request;
229 context_port.replace(port);
230 self.on_port_open(port)?;
231 }
232
233 if let Some(port) = context_port {
235 self.on_port_closed(port)?;
236 }
237 Ok(())
238 }
239
240 fn on_port_open(&mut self, port: u16) -> Result<(), Error> {
242 info!("DevTools port {:?} opened", port);
243 self.port_update_sender
244 .unbounded_send(PortUpdateMessage::PortOpened(port))
245 .map_err(|_| format_err!("Error sending port open message"))
246 }
247
248 fn on_port_closed(&mut self, port: u16) -> Result<(), Error> {
250 info!("DevTools port {:?} closed", port);
251 self.port_update_sender
252 .unbounded_send(PortUpdateMessage::PortClosed(port))
253 .map_err(|_| format_err!("Error sending port closed message"))
254 }
255}