diff --git a/CHANGES.md b/CHANGES.md index eb7902c4..ed684cf5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,32 @@ Change log All notable changes to this program are documented in this file. +0.29.0 (2021-01-14, `cf6956a5ec8e`) +-------------------- + +### Known problems + +- _macOS 10.15 (Catalina):_ + + Due to the requirement from Apple that all programs must be + notarized, geckodriver will not work on Catalina if you manually + download it through another notarized program, such as Firefox. + + Whilst we are working on a repackaging fix for this problem, you can + find more details on how to work around this issue in the [macOS + notarization] section of the documentation. + +### Added + +- Introduced the new boolean capability `moz:debuggerAddress` that can be used + to opt-in to the experimental Chrome DevTools Protocol (CDP) implementation. + A string capability with the same name will be returned by [`NewSession`], + which contains the `host:port` combination of the HTTP server that can be + used to query for websockets of available targets. + + Note: For this experimental feature the site-isolation support of + Firefox aka [Fission] will be not available. + 0.28.0 (2020-11-03, `c00d2b6acd3f`) -------------------- @@ -21,8 +47,8 @@ All notable changes to this program are documented in this file. ### Added - The command line flag `--android-storage` has been added, to allow geckodriver -to also control Firefox on root-less Android devices. See the [documentation][Flags] -for available values. + to also control Firefox on root-less Android devices. + See the [documentation][Flags] for available values. ### Fixed @@ -1368,10 +1394,10 @@ and greater. [Browser Toolbox]: https://developer.mozilla.org/en-US/docs/Tools/Browser_Toolbox [WebDriver conformance]: https://wpt.fyi/results/webdriver/tests?label=experimental [`moz:firefoxOptions`]: https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions +[`moz:debuggerAddress`]: https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-debuggeraddress [Microsoft Visual Studio redistributable runtime]: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads [GeckoView]: https://wiki.mozilla.org/Mobile/GeckoView -[Firefox Preview]: https://play.google.com/store/apps/details?id=org.mozilla.fenix -[Firefox Reality]: https://play.google.com/store/apps/details?id=org.mozilla.vrbrowser +[Fission]: https://wiki.mozilla.org/Project_Fission [Capabilities]: https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html [Flags]: https://firefox-source-docs.mozilla.org/testing/geckodriver/Flags.html [enable remote debugging on the Android device]: https://developers.google.com/web/tools/chrome-devtools/remote-debugging diff --git a/Cargo.toml b/Cargo.toml index a5d4a274..06ea304d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "geckodriver" -version = "0.28.0" +version = "0.29.0" description = "Proxy for using WebDriver clients to interact with Gecko-based browsers." keywords = ["webdriver", "w3c", "httpd", "mozilla", "firefox"] repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/geckodriver" @@ -17,17 +17,17 @@ hyper = "0.13" lazy_static = "1.0" log = { version = "0.4", features = ["std"] } marionette = { path = "./marionette" } -mozdevice = "0.3.0" -mozprofile = "0.7.0" -mozrunner = "0.12.0" -mozversion = "0.4.0" +mozdevice = "0.3.1" +mozprofile = "0.7.1" +mozrunner = "0.12.1" +mozversion = "0.4.1" regex = { version="1.0", default-features = false, features = ["perf", "std"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" serde_yaml = "0.8" uuid = { version = "0.8", features = ["v4"] } -webdriver = "0.42.0" +webdriver = "0.43.0" zip = { version = "0.4", default-features = false, features = ["deflate"] } [[bin]] diff --git a/build.rs b/build.rs index 58d2726a..2fca20dd 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + // Writes build information to ${OUT_DIR}/build-info.rs which is included in // the program during compilation: // @@ -42,13 +46,11 @@ fn get_build_info(dir: &Path) -> Box { Box::new(Hg {}) } else if Path::exists(&dir.join(".git")) { Box::new(Git {}) + } else if let Some(parent) = dir.parent() { + get_build_info(parent) } else { - if let Some(parent) = dir.parent() { - get_build_info(parent) - } else { - eprintln!("unable to detect vcs"); - Box::new(Noop {}) - } + eprintln!("unable to detect vcs"); + Box::new(Noop {}) } } diff --git a/doc/Capabilities.md b/doc/Capabilities.md index 383a362c..feec2cbe 100644 --- a/doc/Capabilities.md +++ b/doc/Capabilities.md @@ -8,6 +8,52 @@ We additionally have some capabilities that largely are implementation concerns that normal users should not care about: +`moz:debuggerAddress` +-------------------- + +A boolean value to indicate if Firefox has to be started with the +[Remote Protocol] enabled, which is a low-level debugging interface that +implements a subset of the [Chrome DevTools Protocol] (CDP). + +When enabled the returned `moz:debuggerAddress` capability of the `New Session` +command is the `host:port` combination of a server that supports the following +HTTP endpoints: + +### GET /json/version + +The browser version metadata: + + { + "Browser": "Firefox/84.0a1", + "Protocol-Version": "1.0", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:84.0) Gecko/20100101 Firefox/84.0", + "V8-Version": "1.0", + "WebKit-Version": "1.0", + "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/fe507083-2960-a442-bbd7-7dfe1f111c05" + } + +### GET /json/list + +A list of all available websocket targets: + + [ { + "description": "", + "devtoolsFrontendUrl": null, + "faviconUrl": "", + "id": "ecbf9028-676a-1b40-8596-a5edc0e2875b", + "type": "page", + "url": "https://www.mozilla.org/en-US/", + "browsingContextId": 29, + "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/ecbf9028-676a-1b40-8596-a5edc0e2875b" + } ] + +The contained `webSocketDebuggerUrl` entries can be used to connect to the +websocket and interact with the browser by using the CDP protocol. + +[Remote Protocol]: /testing/remote/doc/ +[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/ + + `moz:useNonSpecCompliantPointerOrigin` -------------------------------------- diff --git a/doc/Flags.md b/doc/Flags.md index 8889e8b5..1fc6285a 100644 --- a/doc/Flags.md +++ b/doc/Flags.md @@ -1,7 +1,7 @@ Flags ===== -#### --android-storage ANDROID_STORAGE +#### --android-storage ANDROID_STORAGE Selects the test data location on the Android device, eg. the Firefox profile. By default `auto` is used. @@ -47,7 +47,7 @@ By default `auto` is used. -#### -b BINARY / --binary BINARY +#### -b BINARY / --binary BINARY Path to the Firefox binary to use. By default geckodriver tries to find and use the system installation of Firefox, but that behaviour @@ -74,7 +74,7 @@ scanning the Windows registry. [whereis(1)]: http://www.manpagez.com/man/1/whereis/ -#### --connect-existing +#### --connect-existing Connect geckodriver to an existing Firefox instance. This means geckodriver will abstain from the default of starting a new Firefox @@ -85,29 +85,29 @@ To enable the remote protocol in Firefox, you can pass the `-marionette` flag. Unless the `marionette.port` preference has been user-set, Marionette will listen on port 2828. So when using `--connect-existing` it is likely you will also have to use -[`--marionette-port`] to set the correct port. +`--marionette-port` to set the correct port. -[`--marionette-port`]: #marionette-port +[`--marionette-port`]: #marionette-port -#### --host HOST +#### --host HOST Host to use for the WebDriver server. Defaults to 127.0.0.1. -#### --log LEVEL +#### --log LEVEL Set the Gecko and geckodriver log level. Possible values are `fatal`, `error`, `warn`, `info`, `config`, `debug`, and `trace`. -#### --marionette-host HOST +#### --marionette-host HOST Selects the host for geckodriver’s connection to the [Marionette] remote protocol. Defaults to 127.0.0.1. -#### --marionette-port PORT +#### --marionette-port PORT Selects the port for geckodriver’s connection to the [Marionette] remote protocol. @@ -116,13 +116,13 @@ In the default mode where geckodriver starts and manages the Firefox process, it will pick a free port assigned by the system and set the `marionette.port` preference in the profile. -When [`--connect-existing`] is used and the Firefox process is not +When `--connect-existing` is used and the Firefox process is not under geckodriver’s control, it will simply connect to PORT. [`--connect-existing`]: #connect-existing -#### -p PORT / --port PORT +#### -p PORT / --port PORT Port to use for the WebDriver server. Defaults to 4444. @@ -130,7 +130,7 @@ A helpful trick is that it is possible to bind to 0 to get the system to atomically assign a free port. -#### --jsdebugger +#### --jsdebugger Attach [browser toolbox] debugger when Firefox starts. This is useful for debugging [Marionette] internals. diff --git a/doc/Support.md b/doc/Support.md index a7478d4c..94afa547 100644 --- a/doc/Support.md +++ b/doc/Support.md @@ -23,6 +23,11 @@ and required versions of Selenium and Firefox: + + 0.29.0 + ≥ 3.11 (3.14 Python) + 60 + n/a 0.28.0 ≥ 3.11 (3.14 Python) diff --git a/doc/Testing.md b/doc/Testing.md index f8dfae08..f14aff6e 100644 --- a/doc/Testing.md +++ b/doc/Testing.md @@ -14,9 +14,10 @@ tell mach to build these by adding the following line to your [mozconfig]: ac_add_options --enable-rust-tests -Tests can then be run like this: +Tests can then be run by using `cargo test` in the specific source folder: - % ./mach test testing/geckodriver + % cd testing/geckodriver/src + % cargo test To run the more extensive WPT tests you can use mach, but first make sure you have built Firefox: diff --git a/mach_commands.py b/mach_commands.py index beb4b93b..bf5fe594 100644 --- a/mach_commands.py +++ b/mach_commands.py @@ -125,29 +125,3 @@ def run(self, binary, params, debug, debugger, debugger_args): args = [self.debuggerInfo.path] + self.debuggerInfo.args + args return self.run_process(args=args, ensure_exit_code=False, pass_thru=True) - - -@CommandProvider -class GeckoDriverTest(MachCommandBase): - @Command( - "geckodriver-test", - category="post-build", - description="Run geckodriver unit tests.", - ) - @CommandArgument( - "-v", - "--verbose", - action="store_true", - help="Verbose output for what" " commands the build is running.", - ) - def test(self, verbose=False, **kwargs): - from mozbuild.controller.building import BuildDriver - - self.log_manager.enable_all_structured_loggers() - - driver = self._spawn(BuildDriver) - return driver.build( - what=["testing/geckodriver/check"], - verbose=verbose, - mach_context=self._mach_context, - ) diff --git a/marionette/src/common.rs b/marionette/src/common.rs index b051e66e..78bf3afd 100644 --- a/marionette/src/common.rs +++ b/marionette/src/common.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use serde::ser::SerializeMap; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; diff --git a/marionette/src/error.rs b/marionette/src/error.rs index 512aae6a..5db502eb 100644 --- a/marionette/src/error.rs +++ b/marionette/src/error.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use std::error; use std::fmt; diff --git a/marionette/src/lib.rs b/marionette/src/lib.rs index 30ef48d2..80817c5f 100644 --- a/marionette/src/lib.rs +++ b/marionette/src/lib.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + pub mod error; pub mod common; diff --git a/marionette/src/marionette.rs b/marionette/src/marionette.rs index 35d0fc04..c06e2d60 100644 --- a/marionette/src/marionette.rs +++ b/marionette/src/marionette.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use serde::{Deserialize, Serialize}; use crate::common::BoolValue; diff --git a/marionette/src/message.rs b/marionette/src/message.rs index 8ccb816a..33741ca4 100644 --- a/marionette/src/message.rs +++ b/marionette/src/message.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use serde::de::{self, SeqAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{Map, Value}; diff --git a/marionette/src/result.rs b/marionette/src/result.rs index 0f102698..95817c15 100644 --- a/marionette/src/result.rs +++ b/marionette/src/result.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use serde::de; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; diff --git a/marionette/src/test.rs b/marionette/src/test.rs index b6309a26..3b20bb09 100644 --- a/marionette/src/test.rs +++ b/marionette/src/test.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + pub static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf"; pub fn assert_ser_de(data: &T, json: serde_json::Value) diff --git a/marionette/src/webdriver.rs b/marionette/src/webdriver.rs index a15e1cf8..b1069ed4 100644 --- a/marionette/src/webdriver.rs +++ b/marionette/src/webdriver.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/src/build.rs b/src/build.rs index c9590334..7ba31447 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use serde_json::Value; use std::fmt; diff --git a/src/capabilities.rs b/src/capabilities.rs index ab4ed687..e21651ea 100644 --- a/src/capabilities.rs +++ b/src/capabilities.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use crate::command::LogOptions; use crate::logging::Level; use base64; @@ -313,6 +317,14 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { )); } } + "moz:debuggerAddress" => { + if !value.is_boolean() { + return Err(WebDriverError::new( + ErrorStatus::InvalidArgument, + "moz:debuggerAddress is not a boolean", + )); + } + } _ => { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, @@ -397,6 +409,35 @@ impl FirefoxOptions { rv.profile = FirefoxOptions::load_profile(&options)?; } + if let Some(json) = matched.remove("moz:debuggerAddress") { + let use_web_socket = json.as_bool().ok_or_else(|| { + WebDriverError::new( + ErrorStatus::InvalidArgument, + "moz:debuggerAddress is not a boolean", + ) + })?; + + if use_web_socket { + let mut remote_args = Vec::new(); + remote_args.push("--remote-debugging-port".to_owned()); + remote_args.push("0".to_owned()); + + if let Some(ref mut args) = rv.args { + args.append(&mut remote_args); + } else { + rv.args = Some(remote_args); + } + + // Force Fission disabled until Remote Agent is compatible, + // and preference hasn't been already set + let has_fission_pref = rv.prefs.iter().find(|&x| x.0 == "fission.autostart"); + if has_fission_pref.is_none() { + rv.prefs + .push(("fission.autostart".to_owned(), Pref::new(false))); + } + } + } + Ok(rv) } @@ -754,6 +795,57 @@ mod tests { assert_eq!(opts.prefs, vec![]); } + #[test] + fn fx_options_from_capabilities_with_debugger_address_not_set() { + let mut caps = Capabilities::new(); + + let opts = FirefoxOptions::from_capabilities(None, AndroidStorageInput::Auto, &mut caps) + .expect("Valid Firefox options"); + + assert!( + opts.args.is_none(), + "CLI arguments for Firefox unexpectedly found" + ); + } + + #[test] + fn fx_options_from_capabilities_with_debugger_address_false() { + let mut caps = Capabilities::new(); + caps.insert("moz:debuggerAddress".into(), json!(false)); + + let opts = FirefoxOptions::from_capabilities(None, AndroidStorageInput::Auto, &mut caps) + .expect("Valid Firefox options"); + + assert!( + opts.args.is_none(), + "CLI arguments for remote protocol unexpectedly found" + ); + } + + #[test] + fn fx_options_from_capabilities_with_debugger_address_true() { + let mut caps = Capabilities::new(); + caps.insert("moz:debuggerAddress".into(), json!(true)); + + let opts = FirefoxOptions::from_capabilities(None, AndroidStorageInput::Auto, &mut caps) + .expect("Valid Firefox options"); + + if let Some(args) = opts.args { + let mut iter = args.iter(); + assert!(iter + .find(|&arg| arg == &"--remote-debugging-port".to_owned()) + .is_some()); + assert_eq!(iter.next(), Some(&"0".to_owned())); + } else { + assert!(false, "CLI arguments for remote protocol not found"); + } + + assert!(opts + .prefs + .iter() + .any(|pref| pref == &("fission.autostart".to_owned(), Pref::new(false)))); + } + #[test] fn fx_options_from_capabilities_with_invalid_caps() { let mut caps = Capabilities::new(); diff --git a/src/command.rs b/src/command.rs index 5e3a6f3d..f5bc27ad 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use crate::logging; use base64; use hyper::Method; diff --git a/src/logging.rs b/src/logging.rs index 7c0117dc..7721bb77 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + //! Gecko-esque logger implementation for the [`log`] crate. //! //! The [`log`] crate provides a single logging API that abstracts over the diff --git a/src/marionette.rs b/src/marionette.rs index 7074cf35..99a82f3a 100644 --- a/src/marionette.rs +++ b/src/marionette.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use crate::android::AndroidHandler; use crate::command::{ AddonInstallParameters, AddonUninstallParameters, GeckoContextParameters, diff --git a/src/prefs.rs b/src/prefs.rs index 17e882c5..075d0c68 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + use mozprofile::preferences::Pref; // ALL CHANGES TO THIS FILE MUST HAVE REVIEW FROM A GECKODRIVER PEER! diff --git a/src/test.rs b/src/test.rs index 71caceed..e664aadf 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + pub fn assert_de(data: &T, json: serde_json::Value) where T: std::fmt::Debug,