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