1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use crate::browser::{Browser, LocalBrowser, RemoteBrowser};
6 use crate::build;
7 use crate::capabilities::{FirefoxCapabilities, FirefoxOptions, ProfileType};
8 use crate::command::{
9     AddonInstallParameters, AddonUninstallParameters, GeckoContextParameters,
10     GeckoExtensionCommand, GeckoExtensionRoute, CHROME_ELEMENT_KEY,
11 };
12 use crate::logging;
13 use marionette_rs::common::{
14     Cookie as MarionetteCookie, Date as MarionetteDate, Frame as MarionetteFrame,
15     Timeouts as MarionetteTimeouts, WebElement as MarionetteWebElement, Window,
16 };
17 use marionette_rs::marionette::AppStatus;
18 use marionette_rs::message::{Command, Message, MessageId, Request};
19 use marionette_rs::webdriver::{
20     Command as MarionetteWebDriverCommand, Keys as MarionetteKeys, LegacyWebElement,
21     Locator as MarionetteLocator, NewWindow as MarionetteNewWindow,
22     PrintMargins as MarionettePrintMargins, PrintOrientation as MarionettePrintOrientation,
23     PrintPage as MarionettePrintPage, PrintParameters as MarionettePrintParameters,
24     ScreenshotOptions, Script as MarionetteScript, Selector as MarionetteSelector,
25     Url as MarionetteUrl, WindowRect as MarionetteWindowRect,
26 };
27 use mozdevice::AndroidStorageInput;
28 use serde::de::{self, Deserialize, Deserializer};
29 use serde::ser::{Serialize, Serializer};
30 use serde_json::{self, Map, Value};
31 use std::io::prelude::*;
32 use std::io::Error as IoError;
33 use std::io::ErrorKind;
34 use std::io::Result as IoResult;
35 use std::net::{Shutdown, TcpListener, TcpStream};
36 use std::path::PathBuf;
37 use std::sync::Mutex;
38 use std::thread;
39 use std::time;
40 use url::{Host, Url};
41 use webdriver::capabilities::BrowserCapabilities;
42 use webdriver::command::WebDriverCommand::{
43     AcceptAlert, AddCookie, CloseWindow, DeleteCookie, DeleteCookies, DeleteSession, DismissAlert,
44     ElementClear, ElementClick, ElementSendKeys, ExecuteAsyncScript, ExecuteScript, Extension,
45     FindElement, FindElementElement, FindElementElements, FindElements, FullscreenWindow, Get,
46     GetActiveElement, GetAlertText, GetCSSValue, GetCookies, GetCurrentUrl, GetElementAttribute,
47     GetElementProperty, GetElementRect, GetElementTagName, GetElementText, GetNamedCookie,
48     GetPageSource, GetShadowRoot, GetTimeouts, GetTitle, GetWindowHandle, GetWindowHandles,
49     GetWindowRect, GoBack, GoForward, IsDisplayed, IsEnabled, IsSelected, MaximizeWindow,
50     MinimizeWindow, NewSession, NewWindow, PerformActions, Print, Refresh, ReleaseActions,
51     SendAlertText, SetTimeouts, SetWindowRect, Status, SwitchToFrame, SwitchToParentFrame,
52     SwitchToWindow, TakeElementScreenshot, TakeScreenshot,
53 };
54 use webdriver::command::{
55     ActionsParameters, AddCookieParameters, GetNamedCookieParameters, GetParameters,
56     JavascriptCommandParameters, LocatorParameters, NewSessionParameters, NewWindowParameters,
57     PrintMargins, PrintOrientation, PrintPage, PrintParameters, SendKeysParameters,
58     SwitchToFrameParameters, SwitchToWindowParameters, TimeoutsParameters, WindowRectParameters,
59 };
60 use webdriver::command::{WebDriverCommand, WebDriverMessage};
61 use webdriver::common::{
62     Cookie, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, ELEMENT_KEY, FRAME_KEY,
63     SHADOW_KEY, WINDOW_KEY,
64 };
65 use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
66 use webdriver::response::{
67     CloseWindowResponse, CookieResponse, CookiesResponse, ElementRectResponse, NewSessionResponse,
68     NewWindowResponse, TimeoutsResponse, ValueResponse, WebDriverResponse, WindowRectResponse,
69 };
70 use webdriver::server::{Session, WebDriverHandler};
71 use webdriver::{capabilities::CapabilitiesMatching, server::SessionTeardownKind};
72 
73 #[derive(Debug, PartialEq, Deserialize)]
74 struct MarionetteHandshake {
75     #[serde(rename = "marionetteProtocol")]
76     protocol: u16,
77     #[serde(rename = "applicationType")]
78     application_type: String,
79 }
80 
81 #[derive(Default)]
82 pub(crate) struct MarionetteSettings {
83     pub(crate) binary: Option<PathBuf>,
84     pub(crate) connect_existing: bool,
85     pub(crate) host: String,
86     pub(crate) port: Option<u16>,
87     pub(crate) websocket_port: u16,
88     pub(crate) allow_hosts: Vec<Host>,
89     pub(crate) allow_origins: Vec<Url>,
90 
91     /// Brings up the Browser Toolbox when starting Firefox,
92     /// letting you debug internals.
93     pub(crate) jsdebugger: bool,
94 
95     pub(crate) android_storage: AndroidStorageInput,
96 }
97 
98 #[derive(Default)]
99 pub(crate) struct MarionetteHandler {
100     connection: Mutex<Option<MarionetteConnection>>,
101     settings: MarionetteSettings,
102 }
103 
104 impl MarionetteHandler {
new(settings: MarionetteSettings) -> MarionetteHandler105     pub(crate) fn new(settings: MarionetteSettings) -> MarionetteHandler {
106         MarionetteHandler {
107             connection: Mutex::new(None),
108             settings,
109         }
110     }
111 
create_connection( &self, session_id: Option<String>, new_session_parameters: &NewSessionParameters, ) -> WebDriverResult<MarionetteConnection>112     fn create_connection(
113         &self,
114         session_id: Option<String>,
115         new_session_parameters: &NewSessionParameters,
116     ) -> WebDriverResult<MarionetteConnection> {
117         let mut fx_capabilities = FirefoxCapabilities::new(self.settings.binary.as_ref());
118         let (capabilities, options) = {
119             let mut capabilities = new_session_parameters
120                 .match_browser(&mut fx_capabilities)?
121                 .ok_or_else(|| {
122                     WebDriverError::new(
123                         ErrorStatus::SessionNotCreated,
124                         "Unable to find a matching set of capabilities",
125                     )
126                 })?;
127 
128             let options = FirefoxOptions::from_capabilities(
129                 fx_capabilities.chosen_binary.clone(),
130                 &self.settings,
131                 &mut capabilities,
132             )?;
133             (capabilities, options)
134         };
135 
136         if let Some(l) = options.log.level {
137             logging::set_max_level(l);
138         }
139 
140         let marionette_host = self.settings.host.to_owned();
141         let marionette_port = match self.settings.port {
142             Some(port) => port,
143             None => {
144                 // If we're launching Firefox Desktop version 95 or later, and there's no port
145                 // specified, we can pass 0 as the port and later read it back from
146                 // the profile.
147                 let can_use_profile: bool = options.android.is_none()
148                     && options.profile != ProfileType::Named
149                     && !self.settings.connect_existing
150                     && fx_capabilities
151                         .browser_version(&capabilities)
152                         .map(|opt_v| {
153                             opt_v
154                                 .map(|v| {
155                                     fx_capabilities
156                                         .compare_browser_version(&v, ">=95")
157                                         .unwrap_or(false)
158                                 })
159                                 .unwrap_or(false)
160                         })
161                         .unwrap_or(false);
162                 if can_use_profile {
163                     0
164                 } else {
165                     get_free_port(&marionette_host)?
166                 }
167             }
168         };
169 
170         let websocket_port = if options.use_websocket {
171             Some(self.settings.websocket_port)
172         } else {
173             None
174         };
175 
176         let browser = if options.android.is_some() {
177             // TODO: support connecting to running Apps.  There's no real obstruction here,
178             // just some details about port forwarding to work through.  We can't follow
179             // `chromedriver` here since it uses an abstract socket rather than a TCP socket:
180             // see bug 1240830 for thoughts on doing that for Marionette.
181             if self.settings.connect_existing {
182                 return Err(WebDriverError::new(
183                     ErrorStatus::SessionNotCreated,
184                     "Cannot connect to an existing Android App yet",
185                 ));
186             }
187             Browser::Remote(RemoteBrowser::new(
188                 options,
189                 marionette_port,
190                 websocket_port,
191                 self.settings.allow_hosts.to_owned(),
192                 self.settings.allow_origins.to_owned(),
193             )?)
194         } else if !self.settings.connect_existing {
195             Browser::Local(LocalBrowser::new(
196                 options,
197                 marionette_port,
198                 self.settings.allow_hosts.to_owned(),
199                 self.settings.allow_origins.to_owned(),
200                 self.settings.jsdebugger,
201             )?)
202         } else {
203             Browser::Existing(marionette_port)
204         };
205         let session = MarionetteSession::new(session_id, capabilities);
206         MarionetteConnection::new(marionette_host, browser, session)
207     }
208 
close_connection(&mut self, wait_for_shutdown: bool)209     fn close_connection(&mut self, wait_for_shutdown: bool) {
210         if let Ok(connection) = self.connection.get_mut() {
211             if let Some(conn) = connection.take() {
212                 if let Err(e) = conn.close(wait_for_shutdown) {
213                     error!("Failed to close browser connection: {}", e)
214                 }
215             }
216         }
217     }
218 }
219 
220 impl WebDriverHandler<GeckoExtensionRoute> for MarionetteHandler {
handle_command( &mut self, _: &Option<Session>, msg: WebDriverMessage<GeckoExtensionRoute>, ) -> WebDriverResult<WebDriverResponse>221     fn handle_command(
222         &mut self,
223         _: &Option<Session>,
224         msg: WebDriverMessage<GeckoExtensionRoute>,
225     ) -> WebDriverResult<WebDriverResponse> {
226         // First handle the status message which doesn't actually require a marionette
227         // connection or message
228         if let Status = msg.command {
229             let (ready, message) = self
230                 .connection
231                 .get_mut()
232                 .map(|ref connection| {
233                     connection
234                         .as_ref()
235                         .map(|_| (false, "Session already started"))
236                         .unwrap_or((true, ""))
237                 })
238                 .unwrap_or((false, "geckodriver internal error"));
239             let mut value = Map::new();
240             value.insert("ready".to_string(), Value::Bool(ready));
241             value.insert("message".to_string(), Value::String(message.into()));
242             return Ok(WebDriverResponse::Generic(ValueResponse(Value::Object(
243                 value,
244             ))));
245         }
246 
247         match self.connection.lock() {
248             Ok(mut connection) => {
249                 if connection.is_none() {
250                     if let NewSession(ref capabilities) = msg.command {
251                         let conn = self.create_connection(msg.session_id.clone(), capabilities)?;
252                         *connection = Some(conn);
253                     } else {
254                         return Err(WebDriverError::new(
255                             ErrorStatus::InvalidSessionId,
256                             "Tried to run command without establishing a connection",
257                         ));
258                     }
259                 }
260                 let conn = connection.as_mut().expect("Missing connection");
261                 conn.send_command(&msg).map_err(|mut err| {
262                     // Shutdown the browser if no session can
263                     // be established due to errors.
264                     if let NewSession(_) = msg.command {
265                         err.delete_session = true;
266                     }
267                     err
268                 })
269             }
270             Err(_) => Err(WebDriverError::new(
271                 ErrorStatus::UnknownError,
272                 "Failed to aquire Marionette connection",
273             )),
274         }
275     }
276 
teardown_session(&mut self, kind: SessionTeardownKind)277     fn teardown_session(&mut self, kind: SessionTeardownKind) {
278         let wait_for_shutdown = match kind {
279             SessionTeardownKind::Deleted => true,
280             SessionTeardownKind::NotDeleted => false,
281         };
282         self.close_connection(wait_for_shutdown);
283     }
284 }
285 
286 impl Drop for MarionetteHandler {
drop(&mut self)287     fn drop(&mut self) {
288         self.close_connection(false);
289     }
290 }
291 
292 struct MarionetteSession {
293     session_id: String,
294     capabilities: Map<String, Value>,
295     command_id: MessageId,
296 }
297 
298 impl MarionetteSession {
new(session_id: Option<String>, capabilities: Map<String, Value>) -> MarionetteSession299     fn new(session_id: Option<String>, capabilities: Map<String, Value>) -> MarionetteSession {
300         let initital_id = session_id.unwrap_or_else(|| "".to_string());
301         MarionetteSession {
302             session_id: initital_id,
303             capabilities,
304             command_id: 0,
305         }
306     }
307 
update( &mut self, msg: &WebDriverMessage<GeckoExtensionRoute>, resp: &MarionetteResponse, ) -> WebDriverResult<()>308     fn update(
309         &mut self,
310         msg: &WebDriverMessage<GeckoExtensionRoute>,
311         resp: &MarionetteResponse,
312     ) -> WebDriverResult<()> {
313         if let NewSession(_) = msg.command {
314             let session_id = try_opt!(
315                 try_opt!(
316                     resp.result.get("sessionId"),
317                     ErrorStatus::SessionNotCreated,
318                     "Unable to get session id"
319                 )
320                 .as_str(),
321                 ErrorStatus::SessionNotCreated,
322                 "Unable to convert session id to string"
323             );
324             self.session_id = session_id.to_string();
325         };
326         Ok(())
327     }
328 
329     /// Converts a Marionette JSON response into a `WebElement`.
330     ///
331     /// Note that it currently coerces all chrome elements, web frames, and web
332     /// windows also into web elements.  This will change at a later point.
to_web_element(&self, json_data: &Value) -> WebDriverResult<WebElement>333     fn to_web_element(&self, json_data: &Value) -> WebDriverResult<WebElement> {
334         let data = try_opt!(
335             json_data.as_object(),
336             ErrorStatus::UnknownError,
337             "Failed to convert data to an object"
338         );
339 
340         let chrome_element = data.get(CHROME_ELEMENT_KEY);
341         let element = data.get(ELEMENT_KEY);
342         let frame = data.get(FRAME_KEY);
343         let window = data.get(WINDOW_KEY);
344 
345         let value = try_opt!(
346             element.or(chrome_element).or(frame).or(window),
347             ErrorStatus::UnknownError,
348             "Failed to extract web element from Marionette response"
349         );
350         let id = try_opt!(
351             value.as_str(),
352             ErrorStatus::UnknownError,
353             "Failed to convert web element reference value to string"
354         )
355         .to_string();
356         Ok(WebElement(id))
357     }
358 
359     /// Converts a Marionette JSON response into a `ShadowRoot`.
to_shadow_root(&self, json_data: &Value) -> WebDriverResult<ShadowRoot>360     fn to_shadow_root(&self, json_data: &Value) -> WebDriverResult<ShadowRoot> {
361         let data = try_opt!(
362             json_data.as_object(),
363             ErrorStatus::UnknownError,
364             "Failed to convert data to an object"
365         );
366 
367         let shadow_root = data.get(SHADOW_KEY);
368 
369         let value = try_opt!(
370             shadow_root,
371             ErrorStatus::UnknownError,
372             "Failed to extract shadow root from Marionette response"
373         );
374         let id = try_opt!(
375             value.as_str(),
376             ErrorStatus::UnknownError,
377             "Failed to convert shadow root reference value to string"
378         )
379         .to_string();
380         Ok(ShadowRoot(id))
381     }
382 
next_command_id(&mut self) -> MessageId383     fn next_command_id(&mut self) -> MessageId {
384         self.command_id += 1;
385         self.command_id
386     }
387 
response( &mut self, msg: &WebDriverMessage<GeckoExtensionRoute>, resp: MarionetteResponse, ) -> WebDriverResult<WebDriverResponse>388     fn response(
389         &mut self,
390         msg: &WebDriverMessage<GeckoExtensionRoute>,
391         resp: MarionetteResponse,
392     ) -> WebDriverResult<WebDriverResponse> {
393         use self::GeckoExtensionCommand::*;
394 
395         if resp.id != self.command_id {
396             return Err(WebDriverError::new(
397                 ErrorStatus::UnknownError,
398                 format!(
399                     "Marionette responses arrived out of sequence, expected {}, got {}",
400                     self.command_id, resp.id
401                 ),
402             ));
403         }
404 
405         if let Some(error) = resp.error {
406             return Err(error.into());
407         }
408 
409         self.update(msg, &resp)?;
410 
411         Ok(match msg.command {
412             // Everything that doesn't have a response value
413             Get(_)
414             | GoBack
415             | GoForward
416             | Refresh
417             | SetTimeouts(_)
418             | SwitchToWindow(_)
419             | SwitchToFrame(_)
420             | SwitchToParentFrame
421             | AddCookie(_)
422             | DeleteCookies
423             | DeleteCookie(_)
424             | DismissAlert
425             | AcceptAlert
426             | SendAlertText(_)
427             | ElementClick(_)
428             | ElementClear(_)
429             | ElementSendKeys(_, _)
430             | PerformActions(_)
431             | ReleaseActions => WebDriverResponse::Void,
432             // Things that simply return the contents of the marionette "value" property
433             GetCurrentUrl
434             | GetTitle
435             | GetPageSource
436             | GetWindowHandle
437             | IsDisplayed(_)
438             | IsSelected(_)
439             | GetElementAttribute(_, _)
440             | GetElementProperty(_, _)
441             | GetCSSValue(_, _)
442             | GetElementText(_)
443             | GetElementTagName(_)
444             | IsEnabled(_)
445             | ExecuteScript(_)
446             | ExecuteAsyncScript(_)
447             | GetAlertText
448             | TakeScreenshot
449             | Print(_)
450             | TakeElementScreenshot(_) => {
451                 WebDriverResponse::Generic(resp.into_value_response(true)?)
452             }
453             GetTimeouts => {
454                 let script = match try_opt!(
455                     resp.result.get("script"),
456                     ErrorStatus::UnknownError,
457                     "Missing field: script"
458                 ) {
459                     Value::Null => None,
460                     n => try_opt!(
461                         Some(n.as_u64()),
462                         ErrorStatus::UnknownError,
463                         "Failed to interpret script timeout duration as u64"
464                     ),
465                 };
466                 let page_load = try_opt!(
467                     try_opt!(
468                         resp.result.get("pageLoad"),
469                         ErrorStatus::UnknownError,
470                         "Missing field: pageLoad"
471                     )
472                     .as_u64(),
473                     ErrorStatus::UnknownError,
474                     "Failed to interpret page load duration as u64"
475                 );
476                 let implicit = try_opt!(
477                     try_opt!(
478                         resp.result.get("implicit"),
479                         ErrorStatus::UnknownError,
480                         "Missing field: implicit"
481                     )
482                     .as_u64(),
483                     ErrorStatus::UnknownError,
484                     "Failed to interpret implicit search duration as u64"
485                 );
486 
487                 WebDriverResponse::Timeouts(TimeoutsResponse {
488                     script,
489                     page_load,
490                     implicit,
491                 })
492             }
493             Status => panic!("Got status command that should already have been handled"),
494             GetWindowHandles => WebDriverResponse::Generic(resp.into_value_response(false)?),
495             NewWindow(_) => {
496                 let handle: String = try_opt!(
497                     try_opt!(
498                         resp.result.get("handle"),
499                         ErrorStatus::UnknownError,
500                         "Failed to find handle field"
501                     )
502                     .as_str(),
503                     ErrorStatus::UnknownError,
504                     "Failed to interpret handle as string"
505                 )
506                 .into();
507                 let typ: String = try_opt!(
508                     try_opt!(
509                         resp.result.get("type"),
510                         ErrorStatus::UnknownError,
511                         "Failed to find type field"
512                     )
513                     .as_str(),
514                     ErrorStatus::UnknownError,
515                     "Failed to interpret type as string"
516                 )
517                 .into();
518 
519                 WebDriverResponse::NewWindow(NewWindowResponse { handle, typ })
520             }
521             CloseWindow => {
522                 let data = try_opt!(
523                     resp.result.as_array(),
524                     ErrorStatus::UnknownError,
525                     "Failed to interpret value as array"
526                 );
527                 let handles = data
528                     .iter()
529                     .map(|x| {
530                         Ok(try_opt!(
531                             x.as_str(),
532                             ErrorStatus::UnknownError,
533                             "Failed to interpret window handle as string"
534                         )
535                         .to_owned())
536                     })
537                     .collect::<Result<Vec<_>, _>>()?;
538                 WebDriverResponse::CloseWindow(CloseWindowResponse(handles))
539             }
540             GetElementRect(_) => {
541                 let x = try_opt!(
542                     try_opt!(
543                         resp.result.get("x"),
544                         ErrorStatus::UnknownError,
545                         "Failed to find x field"
546                     )
547                     .as_f64(),
548                     ErrorStatus::UnknownError,
549                     "Failed to interpret x as float"
550                 );
551 
552                 let y = try_opt!(
553                     try_opt!(
554                         resp.result.get("y"),
555                         ErrorStatus::UnknownError,
556                         "Failed to find y field"
557                     )
558                     .as_f64(),
559                     ErrorStatus::UnknownError,
560                     "Failed to interpret y as float"
561                 );
562 
563                 let width = try_opt!(
564                     try_opt!(
565                         resp.result.get("width"),
566                         ErrorStatus::UnknownError,
567                         "Failed to find width field"
568                     )
569                     .as_f64(),
570                     ErrorStatus::UnknownError,
571                     "Failed to interpret width as float"
572                 );
573 
574                 let height = try_opt!(
575                     try_opt!(
576                         resp.result.get("height"),
577                         ErrorStatus::UnknownError,
578                         "Failed to find height field"
579                     )
580                     .as_f64(),
581                     ErrorStatus::UnknownError,
582                     "Failed to interpret width as float"
583                 );
584 
585                 let rect = ElementRectResponse {
586                     x,
587                     y,
588                     width,
589                     height,
590                 };
591                 WebDriverResponse::ElementRect(rect)
592             }
593             FullscreenWindow | MinimizeWindow | MaximizeWindow | GetWindowRect
594             | SetWindowRect(_) => {
595                 let width = try_opt!(
596                     try_opt!(
597                         resp.result.get("width"),
598                         ErrorStatus::UnknownError,
599                         "Failed to find width field"
600                     )
601                     .as_u64(),
602                     ErrorStatus::UnknownError,
603                     "Failed to interpret width as positive integer"
604                 );
605 
606                 let height = try_opt!(
607                     try_opt!(
608                         resp.result.get("height"),
609                         ErrorStatus::UnknownError,
610                         "Failed to find heigenht field"
611                     )
612                     .as_u64(),
613                     ErrorStatus::UnknownError,
614                     "Failed to interpret height as positive integer"
615                 );
616 
617                 let x = try_opt!(
618                     try_opt!(
619                         resp.result.get("x"),
620                         ErrorStatus::UnknownError,
621                         "Failed to find x field"
622                     )
623                     .as_i64(),
624                     ErrorStatus::UnknownError,
625                     "Failed to interpret x as integer"
626                 );
627 
628                 let y = try_opt!(
629                     try_opt!(
630                         resp.result.get("y"),
631                         ErrorStatus::UnknownError,
632                         "Failed to find y field"
633                     )
634                     .as_i64(),
635                     ErrorStatus::UnknownError,
636                     "Failed to interpret y as integer"
637                 );
638 
639                 let rect = WindowRectResponse {
640                     x: x as i32,
641                     y: y as i32,
642                     width: width as i32,
643                     height: height as i32,
644                 };
645                 WebDriverResponse::WindowRect(rect)
646             }
647             GetCookies => {
648                 let cookies: Vec<Cookie> = serde_json::from_value(resp.result)?;
649                 WebDriverResponse::Cookies(CookiesResponse(cookies))
650             }
651             GetNamedCookie(ref name) => {
652                 let mut cookies: Vec<Cookie> = serde_json::from_value(resp.result)?;
653                 cookies.retain(|x| x.name == *name);
654                 let cookie = try_opt!(
655                     cookies.pop(),
656                     ErrorStatus::NoSuchCookie,
657                     format!("No cookie with name {}", name)
658                 );
659                 WebDriverResponse::Cookie(CookieResponse(cookie))
660             }
661             FindElement(_) | FindElementElement(_, _) => {
662                 let element = self.to_web_element(try_opt!(
663                     resp.result.get("value"),
664                     ErrorStatus::UnknownError,
665                     "Failed to find value field"
666                 ))?;
667                 WebDriverResponse::Generic(ValueResponse(serde_json::to_value(element)?))
668             }
669             FindElements(_) | FindElementElements(_, _) => {
670                 let element_vec = try_opt!(
671                     resp.result.as_array(),
672                     ErrorStatus::UnknownError,
673                     "Failed to interpret value as array"
674                 );
675                 let elements = element_vec
676                     .iter()
677                     .map(|x| self.to_web_element(x))
678                     .collect::<Result<Vec<_>, _>>()?;
679 
680                 // TODO(Henrik): How to remove unwrap?
681                 WebDriverResponse::Generic(ValueResponse(Value::Array(
682                     elements
683                         .iter()
684                         .map(|x| serde_json::to_value(x).unwrap())
685                         .collect(),
686                 )))
687             }
688             GetShadowRoot(_) => {
689                 let shadow_root = self.to_shadow_root(try_opt!(
690                     resp.result.get("value"),
691                     ErrorStatus::UnknownError,
692                     "Failed to find value field"
693                 ))?;
694                 WebDriverResponse::Generic(ValueResponse(serde_json::to_value(shadow_root)?))
695             }
696             GetActiveElement => {
697                 let element = self.to_web_element(try_opt!(
698                     resp.result.get("value"),
699                     ErrorStatus::UnknownError,
700                     "Failed to find value field"
701                 ))?;
702                 WebDriverResponse::Generic(ValueResponse(serde_json::to_value(element)?))
703             }
704             NewSession(_) => {
705                 let session_id = try_opt!(
706                     try_opt!(
707                         resp.result.get("sessionId"),
708                         ErrorStatus::InvalidSessionId,
709                         "Failed to find sessionId field"
710                     )
711                     .as_str(),
712                     ErrorStatus::InvalidSessionId,
713                     "sessionId is not a string"
714                 );
715 
716                 let mut capabilities = try_opt!(
717                     try_opt!(
718                         resp.result.get("capabilities"),
719                         ErrorStatus::UnknownError,
720                         "Failed to find capabilities field"
721                     )
722                     .as_object(),
723                     ErrorStatus::UnknownError,
724                     "capabilities field is not an object"
725                 )
726                 .clone();
727 
728                 capabilities.insert("moz:geckodriverVersion".into(), build::build_info().into());
729 
730                 WebDriverResponse::NewSession(NewSessionResponse::new(
731                     session_id.to_string(),
732                     Value::Object(capabilities),
733                 ))
734             }
735             DeleteSession => WebDriverResponse::DeleteSession,
736             Extension(ref extension) => match extension {
737                 GetContext => WebDriverResponse::Generic(resp.into_value_response(true)?),
738                 SetContext(_) => WebDriverResponse::Void,
739                 InstallAddon(_) => WebDriverResponse::Generic(resp.into_value_response(true)?),
740                 UninstallAddon(_) => WebDriverResponse::Void,
741                 TakeFullScreenshot => WebDriverResponse::Generic(resp.into_value_response(true)?),
742             },
743         })
744     }
745 }
746 
try_convert_to_marionette_message( msg: &WebDriverMessage<GeckoExtensionRoute>, browser: &Browser, ) -> WebDriverResult<Option<Command>>747 fn try_convert_to_marionette_message(
748     msg: &WebDriverMessage<GeckoExtensionRoute>,
749     browser: &Browser,
750 ) -> WebDriverResult<Option<Command>> {
751     use self::GeckoExtensionCommand::*;
752     use self::WebDriverCommand::*;
753 
754     Ok(match msg.command {
755         AcceptAlert => Some(Command::WebDriver(MarionetteWebDriverCommand::AcceptAlert)),
756         AddCookie(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::AddCookie(
757             x.to_marionette()?,
758         ))),
759         CloseWindow => Some(Command::WebDriver(MarionetteWebDriverCommand::CloseWindow)),
760         DeleteCookie(ref x) => Some(Command::WebDriver(
761             MarionetteWebDriverCommand::DeleteCookie(x.clone()),
762         )),
763         DeleteCookies => Some(Command::WebDriver(
764             MarionetteWebDriverCommand::DeleteCookies,
765         )),
766         DeleteSession => match browser {
767             Browser::Local(_) | Browser::Remote(_) => Some(Command::Marionette(
768                 marionette_rs::marionette::Command::DeleteSession {
769                     flags: vec![AppStatus::eForceQuit],
770                 },
771             )),
772             Browser::Existing(_) => Some(Command::WebDriver(
773                 MarionetteWebDriverCommand::DeleteSession,
774             )),
775         },
776         DismissAlert => Some(Command::WebDriver(MarionetteWebDriverCommand::DismissAlert)),
777         ElementClear(ref e) => Some(Command::WebDriver(
778             MarionetteWebDriverCommand::ElementClear(e.to_marionette()?),
779         )),
780         ElementClick(ref e) => Some(Command::WebDriver(
781             MarionetteWebDriverCommand::ElementClick(e.to_marionette()?),
782         )),
783         ElementSendKeys(ref e, ref x) => {
784             let keys = x.to_marionette()?;
785             Some(Command::WebDriver(
786                 MarionetteWebDriverCommand::ElementSendKeys {
787                     id: e.clone().to_string(),
788                     text: keys.text.clone(),
789                     value: keys.value,
790                 },
791             ))
792         }
793         ExecuteAsyncScript(ref x) => Some(Command::WebDriver(
794             MarionetteWebDriverCommand::ExecuteAsyncScript(x.to_marionette()?),
795         )),
796         ExecuteScript(ref x) => Some(Command::WebDriver(
797             MarionetteWebDriverCommand::ExecuteScript(x.to_marionette()?),
798         )),
799         FindElement(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::FindElement(
800             x.to_marionette()?,
801         ))),
802         FindElements(ref x) => Some(Command::WebDriver(
803             MarionetteWebDriverCommand::FindElements(x.to_marionette()?),
804         )),
805         FindElementElement(ref e, ref x) => {
806             let locator = x.to_marionette()?;
807             Some(Command::WebDriver(
808                 MarionetteWebDriverCommand::FindElementElement {
809                     element: e.clone().to_string(),
810                     using: locator.using.clone(),
811                     value: locator.value,
812                 },
813             ))
814         }
815         FindElementElements(ref e, ref x) => {
816             let locator = x.to_marionette()?;
817             Some(Command::WebDriver(
818                 MarionetteWebDriverCommand::FindElementElements {
819                     element: e.clone().to_string(),
820                     using: locator.using.clone(),
821                     value: locator.value,
822                 },
823             ))
824         }
825         FullscreenWindow => Some(Command::WebDriver(
826             MarionetteWebDriverCommand::FullscreenWindow,
827         )),
828         Get(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::Get(
829             x.to_marionette()?,
830         ))),
831         GetActiveElement => Some(Command::WebDriver(
832             MarionetteWebDriverCommand::GetActiveElement,
833         )),
834         GetAlertText => Some(Command::WebDriver(MarionetteWebDriverCommand::GetAlertText)),
835         GetCookies | GetNamedCookie(_) => {
836             Some(Command::WebDriver(MarionetteWebDriverCommand::GetCookies))
837         }
838         GetCSSValue(ref e, ref x) => Some(Command::WebDriver(
839             MarionetteWebDriverCommand::GetCSSValue {
840                 id: e.clone().to_string(),
841                 property: x.clone(),
842             },
843         )),
844         GetCurrentUrl => Some(Command::WebDriver(
845             MarionetteWebDriverCommand::GetCurrentUrl,
846         )),
847         GetElementAttribute(ref e, ref x) => Some(Command::WebDriver(
848             MarionetteWebDriverCommand::GetElementAttribute {
849                 id: e.clone().to_string(),
850                 name: x.clone(),
851             },
852         )),
853         GetElementProperty(ref e, ref x) => Some(Command::WebDriver(
854             MarionetteWebDriverCommand::GetElementProperty {
855                 id: e.clone().to_string(),
856                 name: x.clone(),
857             },
858         )),
859         GetElementRect(ref x) => Some(Command::WebDriver(
860             MarionetteWebDriverCommand::GetElementRect(x.to_marionette()?),
861         )),
862         GetElementTagName(ref x) => Some(Command::WebDriver(
863             MarionetteWebDriverCommand::GetElementTagName(x.to_marionette()?),
864         )),
865         GetElementText(ref x) => Some(Command::WebDriver(
866             MarionetteWebDriverCommand::GetElementText(x.to_marionette()?),
867         )),
868         GetPageSource => Some(Command::WebDriver(
869             MarionetteWebDriverCommand::GetPageSource,
870         )),
871         GetShadowRoot(ref e) => Some(Command::WebDriver(
872             MarionetteWebDriverCommand::GetShadowRoot {
873                 id: e.clone().to_string(),
874             },
875         )),
876         GetTitle => Some(Command::WebDriver(MarionetteWebDriverCommand::GetTitle)),
877         GetWindowHandle => Some(Command::WebDriver(
878             MarionetteWebDriverCommand::GetWindowHandle,
879         )),
880         GetWindowHandles => Some(Command::WebDriver(
881             MarionetteWebDriverCommand::GetWindowHandles,
882         )),
883         GetWindowRect => Some(Command::WebDriver(
884             MarionetteWebDriverCommand::GetWindowRect,
885         )),
886         GetTimeouts => Some(Command::WebDriver(MarionetteWebDriverCommand::GetTimeouts)),
887         GoBack => Some(Command::WebDriver(MarionetteWebDriverCommand::GoBack)),
888         GoForward => Some(Command::WebDriver(MarionetteWebDriverCommand::GoForward)),
889         IsDisplayed(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::IsDisplayed(
890             x.to_marionette()?,
891         ))),
892         IsEnabled(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::IsEnabled(
893             x.to_marionette()?,
894         ))),
895         IsSelected(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::IsSelected(
896             x.to_marionette()?,
897         ))),
898         MaximizeWindow => Some(Command::WebDriver(
899             MarionetteWebDriverCommand::MaximizeWindow,
900         )),
901         MinimizeWindow => Some(Command::WebDriver(
902             MarionetteWebDriverCommand::MinimizeWindow,
903         )),
904         NewWindow(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::NewWindow(
905             x.to_marionette()?,
906         ))),
907         Print(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::Print(
908             x.to_marionette()?,
909         ))),
910         Refresh => Some(Command::WebDriver(MarionetteWebDriverCommand::Refresh)),
911         ReleaseActions => Some(Command::WebDriver(
912             MarionetteWebDriverCommand::ReleaseActions,
913         )),
914         SendAlertText(ref x) => Some(Command::WebDriver(
915             MarionetteWebDriverCommand::SendAlertText(x.to_marionette()?),
916         )),
917         SetTimeouts(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::SetTimeouts(
918             x.to_marionette()?,
919         ))),
920         SetWindowRect(ref x) => Some(Command::WebDriver(
921             MarionetteWebDriverCommand::SetWindowRect(x.to_marionette()?),
922         )),
923         SwitchToFrame(ref x) => Some(Command::WebDriver(
924             MarionetteWebDriverCommand::SwitchToFrame(x.to_marionette()?),
925         )),
926         SwitchToParentFrame => Some(Command::WebDriver(
927             MarionetteWebDriverCommand::SwitchToParentFrame,
928         )),
929         SwitchToWindow(ref x) => Some(Command::WebDriver(
930             MarionetteWebDriverCommand::SwitchToWindow(x.to_marionette()?),
931         )),
932         TakeElementScreenshot(ref e) => {
933             let screenshot = ScreenshotOptions {
934                 id: Some(e.clone().to_string()),
935                 highlights: vec![],
936                 full: false,
937             };
938             Some(Command::WebDriver(
939                 MarionetteWebDriverCommand::TakeElementScreenshot(screenshot),
940             ))
941         }
942         TakeScreenshot => {
943             let screenshot = ScreenshotOptions {
944                 id: None,
945                 highlights: vec![],
946                 full: false,
947             };
948             Some(Command::WebDriver(
949                 MarionetteWebDriverCommand::TakeScreenshot(screenshot),
950             ))
951         }
952         Extension(TakeFullScreenshot) => {
953             let screenshot = ScreenshotOptions {
954                 id: None,
955                 highlights: vec![],
956                 full: true,
957             };
958             Some(Command::WebDriver(
959                 MarionetteWebDriverCommand::TakeFullScreenshot(screenshot),
960             ))
961         }
962         _ => None,
963     })
964 }
965 
966 #[derive(Debug, PartialEq)]
967 struct MarionetteCommand {
968     id: MessageId,
969     name: String,
970     params: Map<String, Value>,
971 }
972 
973 impl Serialize for MarionetteCommand {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,974     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
975     where
976         S: Serializer,
977     {
978         let data = (&0, &self.id, &self.name, &self.params);
979         data.serialize(serializer)
980     }
981 }
982 
983 impl MarionetteCommand {
new(id: MessageId, name: String, params: Map<String, Value>) -> MarionetteCommand984     fn new(id: MessageId, name: String, params: Map<String, Value>) -> MarionetteCommand {
985         MarionetteCommand { id, name, params }
986     }
987 
encode_msg<T>(msg: T) -> WebDriverResult<String> where T: serde::Serialize,988     fn encode_msg<T>(msg: T) -> WebDriverResult<String>
989     where
990         T: serde::Serialize,
991     {
992         let data = serde_json::to_string(&msg)?;
993 
994         Ok(format!("{}:{}", data.len(), data))
995     }
996 
from_webdriver_message( id: MessageId, capabilities: &Map<String, Value>, browser: &Browser, msg: &WebDriverMessage<GeckoExtensionRoute>, ) -> WebDriverResult<String>997     fn from_webdriver_message(
998         id: MessageId,
999         capabilities: &Map<String, Value>,
1000         browser: &Browser,
1001         msg: &WebDriverMessage<GeckoExtensionRoute>,
1002     ) -> WebDriverResult<String> {
1003         use self::GeckoExtensionCommand::*;
1004 
1005         if let Some(cmd) = try_convert_to_marionette_message(msg, browser)? {
1006             let req = Message::Incoming(Request(id, cmd));
1007             MarionetteCommand::encode_msg(req)
1008         } else {
1009             let (opt_name, opt_parameters) = match msg.command {
1010                 Status => panic!("Got status command that should already have been handled"),
1011                 NewSession(_) => {
1012                     let mut data = Map::new();
1013                     for (k, v) in capabilities.iter() {
1014                         data.insert(k.to_string(), serde_json::to_value(v)?);
1015                     }
1016 
1017                     (Some("WebDriver:NewSession"), Some(Ok(data)))
1018                 }
1019                 PerformActions(ref x) => {
1020                     (Some("WebDriver:PerformActions"), Some(x.to_marionette()))
1021                 }
1022                 Extension(ref extension) => match extension {
1023                     GetContext => (Some("Marionette:GetContext"), None),
1024                     InstallAddon(x) => (Some("Addon:Install"), Some(x.to_marionette())),
1025                     SetContext(x) => (Some("Marionette:SetContext"), Some(x.to_marionette())),
1026                     UninstallAddon(x) => (Some("Addon:Uninstall"), Some(x.to_marionette())),
1027                     _ => (None, None),
1028                 },
1029                 _ => (None, None),
1030             };
1031 
1032             let name = try_opt!(
1033                 opt_name,
1034                 ErrorStatus::UnsupportedOperation,
1035                 "Operation not supported"
1036             );
1037             let parameters = opt_parameters.unwrap_or_else(|| Ok(Map::new()))?;
1038 
1039             let req = MarionetteCommand::new(id, name.into(), parameters);
1040             MarionetteCommand::encode_msg(req)
1041         }
1042     }
1043 }
1044 
1045 #[derive(Debug, PartialEq)]
1046 struct MarionetteResponse {
1047     id: MessageId,
1048     error: Option<MarionetteError>,
1049     result: Value,
1050 }
1051 
1052 impl<'de> Deserialize<'de> for MarionetteResponse {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,1053     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1054     where
1055         D: Deserializer<'de>,
1056     {
1057         #[derive(Deserialize)]
1058         struct ResponseWrapper {
1059             msg_type: u64,
1060             id: MessageId,
1061             error: Option<MarionetteError>,
1062             result: Value,
1063         }
1064 
1065         let wrapper: ResponseWrapper = Deserialize::deserialize(deserializer)?;
1066 
1067         if wrapper.msg_type != 1 {
1068             return Err(de::Error::custom(
1069                 "Expected '1' in first element of response",
1070             ));
1071         };
1072 
1073         Ok(MarionetteResponse {
1074             id: wrapper.id,
1075             error: wrapper.error,
1076             result: wrapper.result,
1077         })
1078     }
1079 }
1080 
1081 impl MarionetteResponse {
into_value_response(self, value_required: bool) -> WebDriverResult<ValueResponse>1082     fn into_value_response(self, value_required: bool) -> WebDriverResult<ValueResponse> {
1083         let value: &Value = if value_required {
1084             try_opt!(
1085                 self.result.get("value"),
1086                 ErrorStatus::UnknownError,
1087                 "Failed to find value field"
1088             )
1089         } else {
1090             &self.result
1091         };
1092 
1093         Ok(ValueResponse(value.clone()))
1094     }
1095 }
1096 
1097 #[derive(Debug, PartialEq, Serialize, Deserialize)]
1098 struct MarionetteError {
1099     #[serde(rename = "error")]
1100     code: String,
1101     message: String,
1102     stacktrace: Option<String>,
1103 }
1104 
1105 impl From<MarionetteError> for WebDriverError {
from(error: MarionetteError) -> WebDriverError1106     fn from(error: MarionetteError) -> WebDriverError {
1107         let status = ErrorStatus::from(error.code);
1108         let message = error.message;
1109 
1110         if let Some(stack) = error.stacktrace {
1111             WebDriverError::new_with_stack(status, message, stack)
1112         } else {
1113             WebDriverError::new(status, message)
1114         }
1115     }
1116 }
1117 
get_free_port(host: &str) -> IoResult<u16>1118 fn get_free_port(host: &str) -> IoResult<u16> {
1119     TcpListener::bind((host, 0))
1120         .and_then(|stream| stream.local_addr())
1121         .map(|x| x.port())
1122 }
1123 
1124 struct MarionetteConnection {
1125     browser: Browser,
1126     session: MarionetteSession,
1127     stream: TcpStream,
1128 }
1129 
1130 impl MarionetteConnection {
new( host: String, mut browser: Browser, session: MarionetteSession, ) -> WebDriverResult<MarionetteConnection>1131     fn new(
1132         host: String,
1133         mut browser: Browser,
1134         session: MarionetteSession,
1135     ) -> WebDriverResult<MarionetteConnection> {
1136         let stream = match MarionetteConnection::connect(&host, &mut browser) {
1137             Ok(stream) => stream,
1138             Err(e) => {
1139                 if let Err(e) = browser.close(true) {
1140                     error!("Failed to stop browser: {:?}", e);
1141                 }
1142                 return Err(e);
1143             }
1144         };
1145         Ok(MarionetteConnection {
1146             browser,
1147             session,
1148             stream,
1149         })
1150     }
1151 
connect(host: &str, browser: &mut Browser) -> WebDriverResult<TcpStream>1152     fn connect(host: &str, browser: &mut Browser) -> WebDriverResult<TcpStream> {
1153         let timeout = time::Duration::from_secs(60);
1154         let poll_interval = time::Duration::from_millis(100);
1155         let now = time::Instant::now();
1156 
1157         debug!(
1158             "Waiting {}s to connect to browser on {}",
1159             timeout.as_secs(),
1160             host,
1161         );
1162 
1163         loop {
1164             // immediately abort connection attempts if process disappears
1165             if let Browser::Local(browser) = browser {
1166                 if let Some(status) = browser.check_status() {
1167                     return Err(WebDriverError::new(
1168                         ErrorStatus::UnknownError,
1169                         format!("Process unexpectedly closed with status {}", status),
1170                     ));
1171                 }
1172             }
1173 
1174             let last_err;
1175 
1176             if let Some(port) = browser.marionette_port()? {
1177                 match MarionetteConnection::try_connect(host, port) {
1178                     Ok(stream) => {
1179                         debug!("Connection to Marionette established on {}:{}.", host, port);
1180                         return Ok(stream);
1181                     }
1182                     Err(e) => {
1183                         let err_str = e.to_string();
1184                         last_err = Some(err_str);
1185                     }
1186                 }
1187             } else {
1188                 last_err = Some("Failed to read marionette port".into());
1189             }
1190             if now.elapsed() < timeout {
1191                 trace!("Retrying in {:?}", poll_interval);
1192                 thread::sleep(poll_interval);
1193             } else {
1194                 return Err(WebDriverError::new(
1195                     ErrorStatus::Timeout,
1196                     last_err.unwrap_or_else(|| "Unknown error".into()),
1197                 ));
1198             }
1199         }
1200     }
1201 
try_connect(host: &str, port: u16) -> WebDriverResult<TcpStream>1202     fn try_connect(host: &str, port: u16) -> WebDriverResult<TcpStream> {
1203         let mut stream = TcpStream::connect((host, port))?;
1204         MarionetteConnection::handshake(&mut stream)?;
1205         Ok(stream)
1206     }
1207 
handshake(stream: &mut TcpStream) -> WebDriverResult<MarionetteHandshake>1208     fn handshake(stream: &mut TcpStream) -> WebDriverResult<MarionetteHandshake> {
1209         let resp = (match stream.read_timeout() {
1210             Ok(timeout) => {
1211                 // If platform supports changing the read timeout of the stream,
1212                 // use a short one only for the handshake with Marionette. Don't
1213                 // make it shorter as 1000ms to not fail on slow connections.
1214                 stream
1215                     .set_read_timeout(Some(time::Duration::from_millis(1000)))
1216                     .ok();
1217                 let data = MarionetteConnection::read_resp(stream);
1218                 stream.set_read_timeout(timeout).ok();
1219 
1220                 data
1221             }
1222             _ => MarionetteConnection::read_resp(stream),
1223         })
1224         .map_err(|e| {
1225             WebDriverError::new(
1226                 ErrorStatus::UnknownError,
1227                 format!("Socket timeout reading Marionette handshake data: {}", e),
1228             )
1229         })?;
1230 
1231         let data = serde_json::from_str::<MarionetteHandshake>(&resp)?;
1232 
1233         if data.application_type != "gecko" {
1234             return Err(WebDriverError::new(
1235                 ErrorStatus::UnknownError,
1236                 format!("Unrecognized application type {}", data.application_type),
1237             ));
1238         }
1239 
1240         if data.protocol != 3 {
1241             return Err(WebDriverError::new(
1242                 ErrorStatus::UnknownError,
1243                 format!(
1244                     "Unsupported Marionette protocol version {}, required 3",
1245                     data.protocol
1246                 ),
1247             ));
1248         }
1249 
1250         Ok(data)
1251     }
1252 
close(self, wait_for_shutdown: bool) -> WebDriverResult<()>1253     fn close(self, wait_for_shutdown: bool) -> WebDriverResult<()> {
1254         self.stream.shutdown(Shutdown::Both)?;
1255         self.browser.close(wait_for_shutdown)?;
1256         Ok(())
1257     }
1258 
send_command( &mut self, msg: &WebDriverMessage<GeckoExtensionRoute>, ) -> WebDriverResult<WebDriverResponse>1259     fn send_command(
1260         &mut self,
1261         msg: &WebDriverMessage<GeckoExtensionRoute>,
1262     ) -> WebDriverResult<WebDriverResponse> {
1263         let id = self.session.next_command_id();
1264         let enc_cmd = MarionetteCommand::from_webdriver_message(
1265             id,
1266             &self.session.capabilities,
1267             &self.browser,
1268             msg,
1269         )?;
1270         let resp_data = self.send(enc_cmd)?;
1271         let data: MarionetteResponse = serde_json::from_str(&resp_data)?;
1272 
1273         self.session.response(msg, data)
1274     }
1275 
send(&mut self, data: String) -> WebDriverResult<String>1276     fn send(&mut self, data: String) -> WebDriverResult<String> {
1277         if self.stream.write(&*data.as_bytes()).is_err() {
1278             let mut err = WebDriverError::new(
1279                 ErrorStatus::UnknownError,
1280                 "Failed to write request to stream",
1281             );
1282             err.delete_session = true;
1283             return Err(err);
1284         }
1285 
1286         match MarionetteConnection::read_resp(&mut self.stream) {
1287             Ok(resp) => Ok(resp),
1288             Err(_) => {
1289                 let mut err = WebDriverError::new(
1290                     ErrorStatus::UnknownError,
1291                     "Failed to decode response from marionette",
1292                 );
1293                 err.delete_session = true;
1294                 Err(err)
1295             }
1296         }
1297     }
1298 
read_resp(stream: &mut TcpStream) -> IoResult<String>1299     fn read_resp(stream: &mut TcpStream) -> IoResult<String> {
1300         let mut bytes = 0usize;
1301 
1302         loop {
1303             let buf = &mut [0u8];
1304             let num_read = stream.read(buf)?;
1305             let byte = match num_read {
1306                 0 => {
1307                     return Err(IoError::new(
1308                         ErrorKind::Other,
1309                         "EOF reading marionette message",
1310                     ))
1311                 }
1312                 1 => buf[0],
1313                 _ => panic!("Expected one byte got more"),
1314             } as char;
1315             match byte {
1316                 '0'..='9' => {
1317                     bytes *= 10;
1318                     bytes += byte as usize - '0' as usize;
1319                 }
1320                 ':' => break,
1321                 _ => {}
1322             }
1323         }
1324 
1325         let buf = &mut [0u8; 8192];
1326         let mut payload = Vec::with_capacity(bytes);
1327         let mut total_read = 0;
1328         while total_read < bytes {
1329             let num_read = stream.read(buf)?;
1330             if num_read == 0 {
1331                 return Err(IoError::new(
1332                     ErrorKind::Other,
1333                     "EOF reading marionette message",
1334                 ));
1335             }
1336             total_read += num_read;
1337             for x in &buf[..num_read] {
1338                 payload.push(*x);
1339             }
1340         }
1341 
1342         // TODO(jgraham): Need to handle the error here
1343         Ok(String::from_utf8(payload).unwrap())
1344     }
1345 }
1346 
1347 trait ToMarionette<T> {
to_marionette(&self) -> WebDriverResult<T>1348     fn to_marionette(&self) -> WebDriverResult<T>;
1349 }
1350 
1351 impl ToMarionette<Map<String, Value>> for AddonInstallParameters {
to_marionette(&self) -> WebDriverResult<Map<String, Value>>1352     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
1353         let mut data = Map::new();
1354         data.insert("path".to_string(), serde_json::to_value(&self.path)?);
1355         if self.temporary.is_some() {
1356             data.insert(
1357                 "temporary".to_string(),
1358                 serde_json::to_value(&self.temporary)?,
1359             );
1360         }
1361         Ok(data)
1362     }
1363 }
1364 
1365 impl ToMarionette<Map<String, Value>> for AddonUninstallParameters {
to_marionette(&self) -> WebDriverResult<Map<String, Value>>1366     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
1367         let mut data = Map::new();
1368         data.insert("id".to_string(), Value::String(self.id.clone()));
1369         Ok(data)
1370     }
1371 }
1372 
1373 impl ToMarionette<Map<String, Value>> for GeckoContextParameters {
to_marionette(&self) -> WebDriverResult<Map<String, Value>>1374     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
1375         let mut data = Map::new();
1376         data.insert(
1377             "value".to_owned(),
1378             serde_json::to_value(self.context.clone())?,
1379         );
1380         Ok(data)
1381     }
1382 }
1383 
1384 impl ToMarionette<MarionettePrintParameters> for PrintParameters {
to_marionette(&self) -> WebDriverResult<MarionettePrintParameters>1385     fn to_marionette(&self) -> WebDriverResult<MarionettePrintParameters> {
1386         Ok(MarionettePrintParameters {
1387             orientation: self.orientation.to_marionette()?,
1388             scale: self.scale,
1389             background: self.background,
1390             page: self.page.to_marionette()?,
1391             margin: self.margin.to_marionette()?,
1392             page_ranges: self.page_ranges.clone(),
1393             shrink_to_fit: self.shrink_to_fit,
1394         })
1395     }
1396 }
1397 
1398 impl ToMarionette<MarionettePrintOrientation> for PrintOrientation {
to_marionette(&self) -> WebDriverResult<MarionettePrintOrientation>1399     fn to_marionette(&self) -> WebDriverResult<MarionettePrintOrientation> {
1400         Ok(match self {
1401             PrintOrientation::Landscape => MarionettePrintOrientation::Landscape,
1402             PrintOrientation::Portrait => MarionettePrintOrientation::Portrait,
1403         })
1404     }
1405 }
1406 
1407 impl ToMarionette<MarionettePrintPage> for PrintPage {
to_marionette(&self) -> WebDriverResult<MarionettePrintPage>1408     fn to_marionette(&self) -> WebDriverResult<MarionettePrintPage> {
1409         Ok(MarionettePrintPage {
1410             width: self.width,
1411             height: self.height,
1412         })
1413     }
1414 }
1415 
1416 impl ToMarionette<MarionettePrintMargins> for PrintMargins {
to_marionette(&self) -> WebDriverResult<MarionettePrintMargins>1417     fn to_marionette(&self) -> WebDriverResult<MarionettePrintMargins> {
1418         Ok(MarionettePrintMargins {
1419             top: self.top,
1420             bottom: self.bottom,
1421             left: self.left,
1422             right: self.right,
1423         })
1424     }
1425 }
1426 
1427 impl ToMarionette<Map<String, Value>> for ActionsParameters {
to_marionette(&self) -> WebDriverResult<Map<String, Value>>1428     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
1429         Ok(try_opt!(
1430             serde_json::to_value(self)?.as_object(),
1431             ErrorStatus::UnknownError,
1432             "Expected an object"
1433         )
1434         .clone())
1435     }
1436 }
1437 
1438 impl ToMarionette<MarionetteCookie> for AddCookieParameters {
to_marionette(&self) -> WebDriverResult<MarionetteCookie>1439     fn to_marionette(&self) -> WebDriverResult<MarionetteCookie> {
1440         Ok(MarionetteCookie {
1441             name: self.name.clone(),
1442             value: self.value.clone(),
1443             path: self.path.clone(),
1444             domain: self.domain.clone(),
1445             secure: self.secure,
1446             http_only: self.httpOnly,
1447             expiry: match &self.expiry {
1448                 Some(date) => Some(date.to_marionette()?),
1449                 None => None,
1450             },
1451             same_site: self.sameSite.clone(),
1452         })
1453     }
1454 }
1455 
1456 impl ToMarionette<MarionetteDate> for Date {
to_marionette(&self) -> WebDriverResult<MarionetteDate>1457     fn to_marionette(&self) -> WebDriverResult<MarionetteDate> {
1458         Ok(MarionetteDate(self.0))
1459     }
1460 }
1461 
1462 impl ToMarionette<Map<String, Value>> for GetNamedCookieParameters {
to_marionette(&self) -> WebDriverResult<Map<String, Value>>1463     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
1464         Ok(try_opt!(
1465             serde_json::to_value(self)?.as_object(),
1466             ErrorStatus::UnknownError,
1467             "Expected an object"
1468         )
1469         .clone())
1470     }
1471 }
1472 
1473 impl ToMarionette<MarionetteUrl> for GetParameters {
to_marionette(&self) -> WebDriverResult<MarionetteUrl>1474     fn to_marionette(&self) -> WebDriverResult<MarionetteUrl> {
1475         Ok(MarionetteUrl {
1476             url: self.url.clone(),
1477         })
1478     }
1479 }
1480 
1481 impl ToMarionette<MarionetteScript> for JavascriptCommandParameters {
to_marionette(&self) -> WebDriverResult<MarionetteScript>1482     fn to_marionette(&self) -> WebDriverResult<MarionetteScript> {
1483         Ok(MarionetteScript {
1484             script: self.script.clone(),
1485             args: self.args.clone(),
1486         })
1487     }
1488 }
1489 
1490 impl ToMarionette<MarionetteLocator> for LocatorParameters {
to_marionette(&self) -> WebDriverResult<MarionetteLocator>1491     fn to_marionette(&self) -> WebDriverResult<MarionetteLocator> {
1492         Ok(MarionetteLocator {
1493             using: self.using.to_marionette()?,
1494             value: self.value.clone(),
1495         })
1496     }
1497 }
1498 
1499 impl ToMarionette<MarionetteSelector> for LocatorStrategy {
to_marionette(&self) -> WebDriverResult<MarionetteSelector>1500     fn to_marionette(&self) -> WebDriverResult<MarionetteSelector> {
1501         use self::LocatorStrategy::*;
1502         match self {
1503             CSSSelector => Ok(MarionetteSelector::Css),
1504             LinkText => Ok(MarionetteSelector::LinkText),
1505             PartialLinkText => Ok(MarionetteSelector::PartialLinkText),
1506             TagName => Ok(MarionetteSelector::TagName),
1507             XPath => Ok(MarionetteSelector::XPath),
1508         }
1509     }
1510 }
1511 
1512 impl ToMarionette<MarionetteNewWindow> for NewWindowParameters {
to_marionette(&self) -> WebDriverResult<MarionetteNewWindow>1513     fn to_marionette(&self) -> WebDriverResult<MarionetteNewWindow> {
1514         Ok(MarionetteNewWindow {
1515             type_hint: self.type_hint.clone(),
1516         })
1517     }
1518 }
1519 
1520 impl ToMarionette<MarionetteKeys> for SendKeysParameters {
to_marionette(&self) -> WebDriverResult<MarionetteKeys>1521     fn to_marionette(&self) -> WebDriverResult<MarionetteKeys> {
1522         Ok(MarionetteKeys {
1523             text: self.text.clone(),
1524             value: self
1525                 .text
1526                 .chars()
1527                 .map(|x| x.to_string())
1528                 .collect::<Vec<String>>(),
1529         })
1530     }
1531 }
1532 
1533 impl ToMarionette<MarionetteFrame> for SwitchToFrameParameters {
to_marionette(&self) -> WebDriverResult<MarionetteFrame>1534     fn to_marionette(&self) -> WebDriverResult<MarionetteFrame> {
1535         Ok(match &self.id {
1536             Some(x) => match x {
1537                 FrameId::Short(n) => MarionetteFrame::Index(*n),
1538                 FrameId::Element(el) => MarionetteFrame::Element(el.0.clone()),
1539             },
1540             None => MarionetteFrame::Parent,
1541         })
1542     }
1543 }
1544 
1545 impl ToMarionette<Window> for SwitchToWindowParameters {
to_marionette(&self) -> WebDriverResult<Window>1546     fn to_marionette(&self) -> WebDriverResult<Window> {
1547         Ok(Window {
1548             handle: self.handle.clone(),
1549         })
1550     }
1551 }
1552 
1553 impl ToMarionette<MarionetteTimeouts> for TimeoutsParameters {
to_marionette(&self) -> WebDriverResult<MarionetteTimeouts>1554     fn to_marionette(&self) -> WebDriverResult<MarionetteTimeouts> {
1555         Ok(MarionetteTimeouts {
1556             implicit: self.implicit,
1557             page_load: self.page_load,
1558             script: self.script,
1559         })
1560     }
1561 }
1562 
1563 impl ToMarionette<LegacyWebElement> for WebElement {
to_marionette(&self) -> WebDriverResult<LegacyWebElement>1564     fn to_marionette(&self) -> WebDriverResult<LegacyWebElement> {
1565         Ok(LegacyWebElement {
1566             id: self.to_string(),
1567         })
1568     }
1569 }
1570 
1571 impl ToMarionette<MarionetteWebElement> for WebElement {
to_marionette(&self) -> WebDriverResult<MarionetteWebElement>1572     fn to_marionette(&self) -> WebDriverResult<MarionetteWebElement> {
1573         Ok(MarionetteWebElement {
1574             element: self.to_string(),
1575         })
1576     }
1577 }
1578 
1579 impl ToMarionette<MarionetteWindowRect> for WindowRectParameters {
to_marionette(&self) -> WebDriverResult<MarionetteWindowRect>1580     fn to_marionette(&self) -> WebDriverResult<MarionetteWindowRect> {
1581         Ok(MarionetteWindowRect {
1582             x: self.x,
1583             y: self.y,
1584             width: self.width,
1585             height: self.height,
1586         })
1587     }
1588 }
1589