Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mozilla/geckodriver.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Tolfsen <ato@mozilla.com>2017-06-03 15:21:18 +0300
committerAndreas Tolfsen <ato@mozilla.com>2017-06-03 15:21:18 +0300
commit4030c529d6b10b0d9e00cd08b5fb43579fe8a290 (patch)
tree66b5a3bd37a8d3aafba0c7cc7e6a5313e697e828
parent60e7a430dc38a9e1921928c0cd996bb5fc00198b (diff)
move canonical repo to mozilla-central
The canonical repository for geckodriver is henceforth in mozilla-central's testing/geckodriver directory. Please submit any future patches to the Testing :: geckodriver component in Bugzilla.
-rw-r--r--CHANGES.md327
-rw-r--r--CONTRIBUTING.md97
-rw-r--r--Cargo.lock775
-rw-r--r--Cargo.toml31
-rw-r--r--LICENSE373
-rw-r--r--README.md16
-rw-r--r--build.sh169
-rw-r--r--ci.sh21
-rw-r--r--i686-trusty/Dockerfile4
-rw-r--r--src/capabilities.rs516
-rw-r--r--src/logging.rs158
-rw-r--r--src/main.rs177
-rw-r--r--src/marionette.rs1579
-rw-r--r--src/prefs.rs226
-rw-r--r--src/tests/profile.zipbin444 -> 0 bytes
15 files changed, 15 insertions, 4454 deletions
diff --git a/CHANGES.md b/CHANGES.md
deleted file mode 100644
index cd39073..0000000
--- a/CHANGES.md
+++ /dev/null
@@ -1,327 +0,0 @@
-# Change log
-
-All notable changes to this program is documented in this file.
-
-## 0.16.1 (2016-04-26)
-
-### Fixed
-- Read Firefox version number from stdout when failing to look for the application .ini file (fixes [Selenium #3884](https://github.com/SeleniumHQ/selenium/issues/3884))
-- Session is now ended when closing the last Firefox window (fixes [#613](https://github.com/mozilla/geckodriver/issues/613))
-
-## 0.16.0 (2016-04-21)
-
-Note that geckodriver v0.16.0 is only compatible with Selenium 3.4 and greater.
-
-### Added
-- Support for WebDriver-conforming [New Session](https://w3c.github.io/webdriver/webdriver-spec.html#dfn-new-session) negotiation, with `desiredCapabilities`/`requiredCapabilities` negotiation as fallback
-- Added two new endpoints:
- - GET `/session/{session id}/window/rect` for [Get Window Rect](https://w3c.github.io/webdriver/webdriver-spec.html#get-window-rect)
- - POST `/session/{session id}/window/rect` for [Set Window Rect](https://w3c.github.io/webdriver/webdriver-spec.html#set-window-rect)
-- Align errors with the [WebDriver errors](https://w3c.github.io/webdriver/webdriver-spec.html#handling-errors):
- - Introduces new errors [`ElementClickIntercepted`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.ElementClickIntercepted), [`ElementNotInteractable`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.ElementNotInteractable), [`InvalidCoordinates`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.InvalidCoordinates), [`NoSuchCookie`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.NoSuchCookie), [`UnableToCaptureScreen`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.UnableToCaptureScreen), and [`UnknownCommand`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.UnknownCommand)
- - Removes `ElementNotVisible` and `InvalidElementCoordinates` errors
-
-### Removed
-- Removed following list of unused endpoints:
- - GET `/session/{session id}/alert_text`
- - POST `/session/{session id}/alert_text`
- - POST `/session/{session id}/accept_alert`
- - POST `/session/{session id}/dismiss_alert`
- - GET `/session/{session id}/window_handle`
- - DELETE `/session/{session id}/window_handle`
- - POST `/session/{session id}/execute_async`
- - POST `/session/{session id}/execute`
-
-### Changed
-- [`SendKeysParameters`](https://docs.rs/webdriver/0.25.0/webdriver/command/struct.SendKeysParameters.html), which is used for the [Element Send Keys](https://w3c.github.io/webdriver/webdriver-spec.html#element-send-keys) and [Send Alert Text](https://w3c.github.io/webdriver/webdriver-spec.html#send-alert-text) commands, has been updated to take a string `text` field
-- [`CookieResponse`](https://docs.rs/webdriver/0.25.0/webdriver/response/struct.CookieResponse.html) and [`CloseWindowResponse`](https://docs.rs/webdriver/0.25.0/webdriver/response/struct.CloseWindowResponse.html) fixed to be properly wrapped in a `value` field, like other responses
-- Allow negative numbers for `x` and `y` fields in `pointerMove` action
-- Disable Flash and the plugin container in Firefox by default, which should help mitigate the “Plugin Container for Firefox has stopped wroking” problems [many users were reporting](https://github.com/mozilla/geckodriver/issues/225) when deleting a session
-- Preferences passed in a profile now take precedence over set of default preferences defined by geckodriver (fixed by [@DrMarcII](https://github.com/DrMarcII))
- - The exceptions are the `marionette.port` and `marionette.log.level` preferences and their fallbacks, which are set unconditionally and cannot be overriden
-- Remove default preference that disables unsafe CPOW checks
-- WebDriver library updated to 0.25.2
-
-### Fixed
-- Fix for the “corrupt deflate stream” exception that sometimes occured when trying to write an empty profile by [@kirhgoph](https://github.com/kirhgoph)
-- Recognise `sslProxy` and `sslProxyPort` entries in the proxy configuration object (fixed by [@juangj](https://github.com/juangj))
-- Fix “`httpProxyPort` was not an integer” error (fixed by [@juangj](https://github.com/juangj))
-- Fix broken unmarshaling of _Get Timeouts_ response format from Firefox 52 and earlier (fixed by [@juangj](https://github.com/juangj))
-- Allow preferences in `moz:firefoxOptions` to be both positive- and negative integers (fixed by [@juangj](https://github.com/juangj))
-- Allow IPv6 hostnames in the proxy configuration object
-- i686-unknown-linux-musl (Linux 32-bit) build fixed
-- Log messages from other Rust modules are now ignored
-- Improved log messages to the HTTPD
-
-## 0.15.0 (2017-03-08)
-
-### Added
-- Added routing and parsing for the [Get Timeouts](https://w3c.github.io/webdriver/webdriver-spec.html#dfn-get-timeouts) command
-
-### Changed
-- All HTTP responses are now wrapped in `{value: …}` objects per the WebDriver specification; this may likely require you to update your client library
-- Pointer move action’s `element` key changed to `origin`, which lets pointer actions originate within the context of the viewport, the pointer’s current position, or from an element
-- Now uses about:blank as the new tab document; this was previously disabled due to [bug 1333736](https://bugzilla.mozilla.org/show_bug.cgi?id=1333736) in Marionette
-- WebDriver libary updated to 0.23.0
-
-### Fixed
-- Aligned the data structure accepted by the [Set Timeouts](https://w3c.github.io/webdriver/webdriver-spec.html#set-timeouts) command with the WebDriver specification
-
-## 0.14.0 (2017-01-31)
-
-### Changed
-- Firefox process is now terminated and session ended when the last window is closed
-- WebDriver library updated to version 0.20.0
-
-### Fixed
-- Stacktraces are now included when the error originates from within the Rust stack
-- HTTPD now returns correct response headers for `Content-Type` and `Cache-Control` thanks to @jugglinmike
-
-## 0.13.0 (2017-01-06)
-
-### Changed
-- When navigating to a document with an insecure- or otherwise invalid TLS certificate, an [insecure certificate](https://w3c.github.io/webdriver/webdriver-spec.html#dfn-insecure-certificate) error will be returned
-- On macOS, deducing Firefox’ location on the system will look for _firefox-bin_ on the system path (`PATH` environmental variable) before looking in the applications folder
-- Window position coordinates are allowed to be negative numbers, to cater for maximised window positioning on Windows
-- WebDriver library updated to version 0.18.0
-
-### Fixed
-- Check for single-character key codes in action sequences now counts characters instead of bytes
-
-## 0.12.0 (2017-01-03)
-
-### Added
-- Added [_Take Element Screenshot_](https://w3c.github.io/webdriver/webdriver-spec.html#take-element-screenshot) command
-- Added new [_Status_](https://w3c.github.io/webdriver/webdriver-spec.html#status) command
-- Added routing for the [_Get Timeouts_](https://w3c.github.io/webdriver/webdriver-spec.html#get-timeouts) command, but it is not yet implemented in Marionette, and will return an _unsupported operation_ error until it is
-- Implemented routing for [new actions API](https://w3c.github.io/webdriver/webdriver-spec.html#actions), but it too is not yet fully implemented in Marionette
-
-### Changed
-- [Synced Firefox preferences](https://github.com/mozilla/geckodriver/commit/2bfdc3ec8151c427a6a75a6ba3ad203459540495) with those used in Mozilla automation
-- Default log level for debug builds of Firefox, which used to be `DEBUG`, changed to `INFO`-level
-- WebDriver library dependency upgraded to 0.17.1
-- Using _session not created_ error when failing to start session
-- geckodriver will exit with exit code 69 to indicate that the port is unavailable
-
-### Fixed
-- Improved logging when starting Firefox
-- Reverted to synchronous logging, which should address cases of inconsistent output when failing to bind to port
-- Clarified in README that geckodriver is not supported on Windows XP
-- Added documentation of supported capabilities to [README](https://github.com/mozilla/geckodriver/blob/master/README.md)
-- Included capabilities example in [README](https://github.com/mozilla/geckodriver/blob/master/README.md)
-
-## 0.11.1 (2016-10-10)
-
-### Fixed
-- Version number in binary now reflects the release version.
-
-## 0.11.0 (2016-10-10)
-
-### Added
-- Introduced continous integration builds for Linux- and Windows 32-bit binaries
-- Added commands for setting- and getting the window position
-- Added new extension commands for finding an element’s anonymous children and querying its attributes; accessible through the `/session/{sessionId}/moz/xbl/{elementId}/anonymous_children` to return all anonymous children and `/session/{sessionId}/moz/xbl/{elementId}/anonymous_by_attribute` to return an anonymous element by a name and attribute query
-- Introduced a `moz:firefoxOptions` capability to customise a Firefox session:
- - The `binary`, `args`, and `profile` entries on this dictionary is equivalent to the old `firefox_binary`, `firefox_args`, and `firefox_profile` capabilities, which have now all been removed
- - The `log` capability takes a dictionary such as `{log: "trace"}` to enable trace level verbosity in Gecko
- - The `prefs` capability lets you define Firefox preferences through capabilities
-- Re-introduced the `--webdriver-port` argument as a hidden alias to `--port`
-
-### Changed
-- `firefox_binary`, `firefox_args`, and `firefox_profile` capabilities removed in favour of the `moz:firefoxOptions` dictionary detailed above and in the README
-- Removed `--no-e10s` flag, and geckodriver will from now rely on the Firefox default multiprocessing settings (override using preferences)
-- Disable pop-up blocker in the default profile by @juangj
-- Changed Rust compiler version to 1.12 (beta) temporarily because of [trouble linking Musl binaries](https://github.com/rust-lang/rust/issues/34978)
-- Replaced _env_logger_ logging facility with the _slog_ package, causing the `RUST_LOG` environment variable to no longer have any affect
-- Updated the WebDriver Rust library to version 0.15.
-
-### Fixed
-- Corrected link to repository in Cargo metadata
-- Verbosity shorthand flag `-v[v]` now works again, following the replacement of the argument parsing library in the previous release
-- When the HTTPD fails to start, errors are propagated to the user
-- Disabled the additional welcome URL (`startup.homepage_welcome_url.additional`) so that officially branded Firefox builds do not start with two open tabs in fresh profiles
-- Disabled homepage override URL redirection on milestone upgrades, which means a tab with an upgrade notice is not displayed when launching a new Firefox version
-
-## 0.10.0 (2016-08-02)
-
-### Changed
-- Use multi-process Firefox (e10s) by default, added flag `--no-e10s` to disable it and removed `--e10s` flag
-- Disable autofilling of forms by default by @mythsunwind
-- Replace _argparse_ with _clap_ for arguments parsing
-
-### Fixed
-- Attempt to deploy a single file from Travis when making a release
-- Grammar fix in README
-
-
-## 0.9.0 (2016-06-30)
-
-### Added
-- Add ability to use `firefox_binary` capability to define location of Firefox to use
-- Automatically detect the default Firefox path if one is not given
-- Cross-compile to Windows and ARMv7 (HF) in CI
-- Add Musl C library-backed static binaries in CI
-- Add `-v`, `-vv`, and `--log LEVEL` flags to increase Gecko verbosity
-- Add Get Element Property endpoint
-- Add new `--version` flag showing copying information and a link to the repository
-
-### Changed
-- Now connects to a Marionette on a random port by default
-- Update webdriver-rust library dependency
-- Migrated to use Travis to deploy new releases
-- Reduced amount of logging
-- Introduced a changelog (this)
-
-
-## 0.8.0 (2016-06-07)
-
-### Added
-- Allow specifying array of arguments to the Firefox binary through the `firefox_args` capability
-- Pass parameters with New Session command
-
-### Changed
-- Change product name to _geckodriver_
-- Make README more exhaustive
-- Quit Firefox when deleting a session
-- Update webdriver-rust library
-- Update dependencies
-
-### Fixed
-- Fix tests
-- FIx typo in error message for parsing errors
-
-
-## 0.7.1 (2016-04-27)
-
-### Added
-- Add command line flag for using e10s enabeld Firefox by @martionsideofthemoon
-- Allow providing custom profiels
-
-### Changed
-- Allow binding to an IPv6 address by @juangj
-- By default, connect to host-agnostic localhost by @juangj
-- Make `GeckoContextParameters` public
-- Update dependencies
-
-### Fixed
-- Squash rustc 1.6 warnings by using `std::thread::sleep(dur: Duration)`
-
-
-## 0.6.2 (2016-01-20)
-
-### Added
-- Add LICENSE file from @joshbruning
-- Schedule builds in CI on pushes and pull requests
-
-### Changed
-- Enable CPOWs in Marionette
-
-
-## 0.6.0 (2016-01-12)
-
-### Added
-- Add Get Page Source endpoint
-
-### Changed
-- Handle arrays being sent from Marionette
-- Correct build steps in README
-- Update what properties are read from errors sent by Marionette
-- Update dependencies
-
-
-## 0.5.0 (2015-12-10)
-
-### Changed
-- Update argparse dependency to use Cargo
-- Update to the latest version of the Marionette wire protocol
-- Update to latest webdriver-rust library
-- Update dependencies
-
-
-## 0.4.2 (2015-10-02)
-
-### Changed
-- Skip compiling optional items in hyper
-
-
-## 0.4.1 (2015-10-02)
-
-### Changed
-- Update webdriver-rust library
-- Update dependencies
-
-
-## 0.4.0 (2015-09-28)
-
-### Added
-- Add command extensions for switching between content- and chrome contexts
-- Add more documentation from @vladikoff
-
-### Changed
-- Update Cargo.lock with new dependencies for building
-- Update for protocol updates that flatten commands
-- Update to new protocol error handling
-- Update for Marionette protocol version 3 changes
-- Strip any leading and trailing `{}` from the `sessionId` Marionette returns
-- Update dependencies
-
-### Fixed
-- Fix `GetCSSValue` message to send correct key `propertyName`
-- Fix example in documentation from @vladikoff
-
-
-## 0.3.0 (2015-08-17)
-
-### Added
-- Add support for finding elements in subtrees
-
-
-## 0.2.0 (2015-05-20)
-
-### Added
-- Extra debug messages
-- Add ability to set WebDriver port
-- Add support for getting the active element
-- Add support for `GetCookies` and `DeleteCookie`/`DeleteCookies`
-- Add preferences that switch off certain features not required for WebDriver tests
-
-### Changed
-- Make failing to communicate with Firefox a fatal error that closes the session
-- Shut down session only when loosing connection
-- Better handling of missing command line flags
-- Poll for connection every 100ms rather than every 100s
-- Switch to string-based error codes
-- Switch webdriver-rust library dependency to be pulled from git
-- Update dependencies
-
-### Fixed
-- Handle null id for switching to frame more correctly
-
-
-## 0.1.0 (2015-04-09)
-
-### Added
-- Add proxy for converting WebDriver HTTP protocol to Marionette protocol
-- Add endpoints for modal dialogue support
-- Allow connecting to a running Firefox instance
-- Add explicit Cargo.lock file
-- Start Firefox when we get a New Session command
-- Add flag parsing and address parsing
-- Add basic error handling
-
-### Changed
-- Update for Rust beta
-- Switch to new IO libraries
-- Pin webdriver-rust commit so we can upgrade rustc versions independently
-- Set preferences when starting Firefox
-- Improve some error messages
-- Re-enable environment variable based logging
-
-### Fixed
-- Fix Get Element Rect command to return floats instead of integers
-- Fix passing of web elements to Switch To Frame command
-- Fix serialisation of script commands
-- Fix assorted bugs found by the Selenium test suite
-- Fix conversion of Find Element/Find Elements responses from Marionette to WebDriver
-- Fixed build by updating Cargo.lock with new dependencies for building
-- Squash compile warnings
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index ca22acb..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,97 +0,0 @@
-# Contributing to geckodriver
-
-The geckodriver project welcomes contributions from everyone. There are a
-number of ways you can help:
-
-## Issue Reports
-
-When opening new issues or commenting on existing issues please make
-sure discussions are related to concrete technical issues with the
-geckodriver or Marionette software.
-
-For issue reports to be actionable, it must be clear exactly what the
-observed and expected behaviours are, and how to set up the state required
-to observe the erroneous behaviour. The most useful thing to provide is a
-minimal HTML file which allows the problem to be reproduced, plus a
-trace-level log from geckodriver showing the wire-protocol calls used to set
-up the problem. Please provide [concise reproducible test
-cases](http://sscce.org/) and describe what results you are seeing and what
-results you expect. Because of the wide variety of client bindings for
-WebDriver, clients scripts and logs are typically not very useful if the
-verbose geckodriver logs are available. Issues relating to a specific client
-should be filed in the issue tracker of that project.
-
-## Code Contributions
-
-If you're looking for easy bugs, have a look at
-[issues labelled E-easy](https://github.com/mozilla/geckodriver/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aeasy+)
-on Github.
-
-This document will guide you through the contribution process.
-
-### Step 1: Fork
-
-Fork the project [on Github](https://github.com/mozilla/geckodriver)
-and check out your copy locally.
-
-```text
-% git clone git@github.com:username/geckodriver.git
-% cd geckodriver
-% git remote add upstream git://github.com/mozilla/geckodriver.git
-```
-
-### Step 2: Branch
-
-Create a feature branch and start hacking:
-
-```text
-% git checkout -b my-feature-branch
-```
-
-We practice HEAD-based development, which means all changes are applied
-directly on top of master.
-
-### Step 3: Commit
-
-First make sure git knows your name and email address:
-
-```text
-% git config --global user.name 'Santa Claus'
-% git config --global user.email 'santa@example.com'
-```
-
-The first line must be meaningful as it's what people see when they
-run `git shortlog` or `git log --oneline`.
-
-### Step 4: Rebase
-
-Use `git rebase` (not `git merge`) to sync your work from time to time.
-
-```text
-% git fetch upstream
-% git rebase upstream/master
-```
-
-### Step 5: Push
-
-```text
-% git push my-feature-branch
-```
-
-Go to https://github.com/yourusername/geckodriver and press the _Pull
-Request_ and fill out the form.
-
-Pull requests are usually reviewed within a few days. Reviews will be done
-through [Reviewable](https://reviewable.io/reviews/mozilla/geckodriver)
-
-### Step 6: Integration
-
-When code review is complete, a committer will take your PR and
-integrate it on geckodriver's master branch. Because we like to keep a
-linear history on the master branch, we will normally squash and rebase
-your branch history.
-
-## Communication
-
-geckodriver contributors frequent the `#ateam` channel on
-[`irc.mozilla.org`](http://chat.mibbit.com/?server=irc.mozilla.org:#ateam).
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index 258e639..0000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,775 +0,0 @@
-[root]
-name = "geckodriver"
-version = "0.16.1"
-dependencies = [
- "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
- "clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozprofile 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozrunner 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozversion 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog-atomic 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog-stdlog 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog-stream 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
- "webdriver 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "advapi32-sys"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "backtrace"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "backtrace-sys"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "bitflags"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "bzip2"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bzip2-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "bzip2-sys"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "cfg-if"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "chrono"
-version = "0.2.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "clap"
-version = "2.23.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "cookie"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "crossbeam"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "dbghelp-sys"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "flate2"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "miniz-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "gcc"
-version = "0.3.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "httparse"
-version = "1.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "hyper"
-version = "0.10.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "httparse 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "mime 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
- "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "idna"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "isatty"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "kernel32-sys"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "ktmw32-sys"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "language-tags"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "lazy_static"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "lazy_static"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "libc"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "log"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "matches"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "memchr"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "mime"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "miniz-sys"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "mozprofile"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "mozrunner"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "mozprofile 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "winreg 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "mozversion"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rust-ini 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "msdos_time"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "num"
-version = "0.1.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "num-iter"
-version = "0.1.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.1.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "num_cpus"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "podio"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "rand"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "regex"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "rust-ini"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "rustc-serialize"
-version = "0.3.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "rustc_version"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "semver"
-version = "0.1.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "semver"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "slog"
-version = "1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "slog-atomic"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "slog-extra"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "slog-stdlog"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog-term 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "slog-stream"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog-extra 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "slog-term"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
- "isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "slog-stream 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "strsim"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "tempdir"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "term_size"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "thread-id"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "thread-id"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "thread_local"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "thread_local"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "time"
-version = "0.1.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "traitobject"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "typeable"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "unicase"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "unicode-bidi"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "unreachable"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "url"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "idna 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "utf8-ranges"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "uuid"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "vec_map"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "webdriver"
-version = "0.25.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cookie 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "winapi"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "winreg"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "ktmw32-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "zip"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[metadata]
-"checksum advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "307c92332867e586720c0222ee9d890bbe8431711efed8a1b06bc5b40fc66bd7"
-"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
-"checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80"
-"checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842"
-"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
-"checksum bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3eafc42c44e0d827de6b1c131175098fe7fb53b8ce8a47e65cb3ea94688be24"
-"checksum bzip2-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "98ce3fff84d4e90011f464bbdf48e3428f04270439f703868fd489d2aaedfc30"
-"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
-"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
-"checksum clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f57e9b63057a545ad2ecd773ea61e49422ed1b1d63d74d5da5ecaee55b3396cd"
-"checksum cookie 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce776927cd64cbe74ebd1d9b375edb9d1b6bfa808618ddf9548645e019ebdfbb"
-"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
-"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
-"checksum flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)" = "36df0166e856739905cd3d7e0b210fe818592211a008862599845e012d8d304c"
-"checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae"
-"checksum httparse 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77f756bed9ee3a83ce98774f4155b42a31b787029013f3a7d83eca714e500e21"
-"checksum hyper 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)" = "94da93321c171e26481afeebe8288757b0501901b7c5492648163d8ec4942ec5"
-"checksum idna 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac85ec3f80c8e4e99d9325521337e14ec7555c458a14e377d189659a427f375"
-"checksum isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fa500db770a99afe2a0f2229be2a3d09c7ed9d7e4e8440bf71253141994e240f"
-"checksum kernel32-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e014dab1082fd9d80ea1fa6fcb261b47ed3eb511612a14198bb507701add083e"
-"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum ktmw32-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f9313a869ff779ae08dd990b75a92dc06aa16d771f41305f7286649cd39a0ee"
-"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
-"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
-"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
-"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
-"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
-"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
-"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
-"checksum mime 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5514f038123342d01ee5f95129e4ef1e0470c93bc29edf058a46f9ee3ba6737e"
-"checksum miniz-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "28eaee17666671fa872e567547e8428e83308ebe5808cdf6a0e28397dbe2c726"
-"checksum mozprofile 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4981de229fba7d949465a2f564e2b46906c6e876516040416956a54021a84165"
-"checksum mozrunner 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "91c54a746e7430326d2bc48a5b10ba7e9cd2294306773cb9f232835abe791f02"
-"checksum mozversion 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76acea204db8c94a612821e0ddc5e4513e46af21fdbd8c15b8efe13b343062f0"
-"checksum msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958"
-"checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40"
-"checksum num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1a4bf6f9174aa5783a9b4cc892cacd11aebad6c69ad027a0b65c6ca5f8aa37"
-"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e"
-"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99"
-"checksum num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca313f1862c7ec3e0dfe8ace9fa91b1d9cb5c84ace3d00f5ec4216238e93c167"
-"checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
-"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
-"checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b"
-"checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
-"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
-"checksum rust-ini 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06d4e8b0b50e7e7f827d609fa9746e1cf6371a1fa15404a1a0a86152a801079f"
-"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
-"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
-"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
-"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
-"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
-"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-"checksum slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bab9d589681f7d6b9ca4ed5cc861779a392bca7beaae2f69f2341617415a78dc"
-"checksum slog-atomic 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6f5a4e4908d6818fe553b6126ba5377801556ab885c65ebf960b722a6778864"
-"checksum slog-extra 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "511581f4dd1dc90e4eca99b60be8a692d9c975e8757558aa774f16007d27492a"
-"checksum slog-stdlog 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56cc08f40c45e0ab41dcfde0a19a22c5b7176d3827fc7d078450ebfdc080a37c"
-"checksum slog-stream 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f0fee00b80a7a44f82c5cf44ba03b6dc2712f9c14469a62ad90ea0911635c5"
-"checksum slog-term 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb53c0bae0745898fd5a7b75b1c389507333470ac4c645ae431890c0f828b6ca"
-"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
-"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
-"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
-"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
-"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a"
-"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
-"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
-"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3"
-"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
-"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
-"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764"
-"checksum unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a078ebdd62c0e71a709c3d53d2af693fe09fe93fbff8344aebe289b78f9032"
-"checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff"
-"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
-"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
-"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
-"checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e"
-"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
-"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f"
-"checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897"
-"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-"checksum webdriver 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d161bc62ed766ddc0838af89f1b339ed3c8b5c3dbe8776b59731dfae7b1a6c7"
-"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-"checksum winreg 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e63857fb213f619b4c4fff86b158285c76766aac7e7474967e92fb6dbbfeefe9"
-"checksum zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c0deac03fc7d43abcf19f2c2db6bd9289f9ea3d31f350e26eb0ed8b4117983c1"
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644
index e3bc8d4..0000000
--- a/Cargo.toml
+++ /dev/null
@@ -1,31 +0,0 @@
-[package]
-name = "geckodriver"
-version = "0.16.1"
-authors = [
- "James Graham <james@hoppipolla.co.uk>",
- "Andreas Tolfsen <ato@mozilla.com>",
-]
-description = "Proxy for using WebDriver clients to interact with Gecko-based browsers."
-keywords = ["webdriver", "w3c", "httpd", "mozilla", "firefox"]
-repository = "https://github.com/mozilla/geckodriver"
-readme = "README.md"
-license = "MPL-2.0"
-
-[dependencies]
-chrono = "^0.2"
-clap = {version = "^2.19", default-features = false, features = ["suggestions", "wrap_help"]}
-hyper = "0.10"
-lazy_static = "0.1"
-log = "0.3"
-mozprofile = "0.2"
-mozrunner = "0.3"
-mozversion = "0.1"
-regex = "0.2"
-rustc-serialize = "0.3"
-slog = "1"
-slog-atomic = "0.4"
-slog-stdlog = "1"
-slog-stream = "1"
-uuid = "0.1.18"
-webdriver = "0.25"
-zip = "0.1"
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 14e2f77..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,373 +0,0 @@
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
- means each individual or legal entity that creates, contributes to
- the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
- means the combination of the Contributions of others (if any) used
- by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
- means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
- means Source Code Form to which the initial Contributor has attached
- the notice in Exhibit A, the Executable Form of such Source Code
- Form, and Modifications of such Source Code Form, in each case
- including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
- means
-
- (a) that the initial Contributor has attached the notice described
- in Exhibit B to the Covered Software; or
-
- (b) that the Covered Software was made available under the terms of
- version 1.1 or earlier of the License, but not also under the
- terms of a Secondary License.
-
-1.6. "Executable Form"
- means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
- a separate file or files, that is not Covered Software.
-
-1.8. "License"
- means this document.
-
-1.9. "Licensable"
- means having the right to grant, to the maximum extent possible,
- whether at the time of the initial grant or subsequently, any and
- all of the rights conveyed by this License.
-
-1.10. "Modifications"
- means any of the following:
-
- (a) any file in Source Code Form that results from an addition to,
- deletion from, or modification of the contents of Covered
- Software; or
-
- (b) any new file in Source Code Form that contains any Covered
- Software.
-
-1.11. "Patent Claims" of a Contributor
- means any patent claim(s), including without limitation, method,
- process, and apparatus claims, in any patent Licensable by such
- Contributor that would be infringed, but for the grant of the
- License, by the making, using, selling, offering for sale, having
- made, import, or transfer of either its Contributions or its
- Contributor Version.
-
-1.12. "Secondary License"
- means either the GNU General Public License, Version 2.0, the GNU
- Lesser General Public License, Version 2.1, the GNU Affero General
- Public License, Version 3.0, or any later versions of those
- licenses.
-
-1.13. "Source Code Form"
- means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
- means an individual or a legal entity exercising rights under this
- License. For legal entities, "You" includes any entity that
- controls, is controlled by, or is under common control with You. For
- purposes of this definition, "control" means (a) the power, direct
- or indirect, to cause the direction or management of such entity,
- whether by contract or otherwise, or (b) ownership of more than
- fifty percent (50%) of the outstanding shares or beneficial
- ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or
- as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
- for sale, have made, import, and otherwise transfer either its
- Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
- or
-
-(b) for infringements caused by: (i) Your and any other third party's
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
- its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
- Form, as described in Section 3.1, and You must inform recipients of
- the Executable Form how they can obtain a copy of such Source Code
- Form by reasonable means in a timely manner, at a charge no more
- than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
- License, or sublicense it under different terms, provided that the
- license for the Executable Form does not attempt to limit or alter
- the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-* *
-* 6. Disclaimer of Warranty *
-* ------------------------- *
-* *
-* Covered Software is provided under this License on an "as is" *
-* basis, without warranty of any kind, either expressed, implied, or *
-* statutory, including, without limitation, warranties that the *
-* Covered Software is free of defects, merchantable, fit for a *
-* particular purpose or non-infringing. The entire risk as to the *
-* quality and performance of the Covered Software is with You. *
-* Should any Covered Software prove defective in any respect, You *
-* (not any Contributor) assume the cost of any necessary servicing, *
-* repair, or correction. This disclaimer of warranty constitutes an *
-* essential part of this License. No use of any Covered Software is *
-* authorized under this License except under this disclaimer. *
-* *
-************************************************************************
-
-************************************************************************
-* *
-* 7. Limitation of Liability *
-* -------------------------- *
-* *
-* Under no circumstances and under no legal theory, whether tort *
-* (including negligence), contract, or otherwise, shall any *
-* Contributor, or anyone who distributes Covered Software as *
-* permitted above, be liable to You for any direct, indirect, *
-* special, incidental, or consequential damages of any character *
-* including, without limitation, damages for lost profits, loss of *
-* goodwill, work stoppage, computer failure or malfunction, or any *
-* and all other commercial damages or losses, even if such party *
-* shall have been informed of the possibility of such damages. This *
-* limitation of liability shall not apply to liability for death or *
-* personal injury resulting from such party's negligence to the *
-* extent applicable law prohibits such limitation. Some *
-* jurisdictions do not allow the exclusion or limitation of *
-* incidental or consequential damages, so this exclusion and *
-* limitation may not apply to You. *
-* *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
- 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/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
- This Source Code Form is "Incompatible With Secondary Licenses", as
- defined by the Mozilla Public License, v. 2.0.
diff --git a/README.md b/README.md
index cb4977b..92bebe9 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# geckodriver [![Build Status](https://travis-ci.org/mozilla/geckodriver.svg?branch=master)](https://travis-ci.org/mozilla/geckodriver)
+# geckodriver
Proxy for using W3C WebDriver-compatible clients
to interact with Gecko-based browsers.
@@ -533,3 +533,17 @@ Or if you want a non-optimised binary for debugging:
[WebDriver]: https://w3c.github.io/webdriver/webdriver-spec.html
[Marionette]: http://searchfox.org/mozilla-central/source/testing/marionette/README
[Rust compiler toolchain]: https://rustup.rs/
+
+## Contributing
+
+The canonical source code repository for geckodriver
+now lives in [mozilla-central] under [testing/geckodriver].
+You can read more about [working with Mozilla source code] on MDN.
+This means we do no longer accept pull requests on GitHub.
+Patches should be uploaded to a bug
+in the [Testing :: GeckoDriver] component.
+
+[mozilla-central]: https://hg.mozilla.org/mozilla-central/
+[testing/geckodriver]: https://hg.mozilla.org/mozilla-central/file/tip/testing/geckodriver
+[working with Mozilla source code]: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Source_Code
+[Testing :: geckodriver]: https://bugzilla.mozilla.org/buglist.cgi?product=Testing&component=geckodriver&resolution=---&list_id=13613952
diff --git a/build.sh b/build.sh
deleted file mode 100644
index 8bf54e9..0000000
--- a/build.sh
+++ /dev/null
@@ -1,169 +0,0 @@
-set -ex
-
-print_versions() {
- rustc -V
- cargo -V
- cc --version
-}
-
-rustup_install() {
- export PATH="$PATH:$HOME/.cargo/bin"
- curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain=$1
-}
-
-# Add provided target to current Rust toolchain if it is not already
-# the default or installed.
-rustup_target_add() {
- if ! rustup target list | grep -E "$1 \((default|installed)\)"
- then
- rustup target add $1
- fi
-}
-
-setup_docker() {
- apt-get -qq -y install zip
- cd /mnt/host
-}
-
-mingw_i686_install() {
- curl https://static.rust-lang.org/dist/rust-mingw-nightly-i686-pc-windows-gnu.tar.gz \
- | tar xzvf - -C /tmp
- /tmp/rust-mingw-nightly-i686-pc-windows-gnu/install.sh --prefix=`rustc --print sysroot`
-}
-
-# Configure rustc target for cross compilation. Provided with a build
-# target, this will determine which linker to use for cross compilation.
-cargo_config() {
- local prefix
-
- case "$TARGET" in
- aarch64-unknown-linux-gnu)
- prefix=aarch64-linux-gnu
- ;;
- arm*-unknown-linux-gnueabihf)
- prefix=arm-linux-gnueabihf
- ;;
- arm-unknown-linux-gnueabi)
- prefix=arm-linux-gnueabi
- ;;
- mipsel-unknown-linux-musl)
- prefix=mipsel-openwrt-linux
- ;;
- x86_64-pc-windows-gnu)
- prefix=x86_64-w64-mingw32
- ;;
- i686-pc-windows-gnu)
- prefix=i686-w64-mingw32
- ;;
- *)
- return
- ;;
- esac
-
- mkdir -p ~/.cargo
- cat >~/.cargo/config <<EOF
-[target.$TARGET]
-linker = "$prefix-gcc"
-EOF
-
- cat ~/.cargo/config
-}
-
-# Build current crate for given target and print file type information.
-# If the second argument is set, a release build will be made.
-cargo_build() {
- local mode
- if [ -z "$2" ]
- then
- mode=debug
- else
- mode=release
- fi
-
- local modeflag
- if [ "$mode" == "release" ]
- then
- modeflag=--release
- fi
-
- cargo build --target $1 $modeflag
-
- file $(get_binary $1 $mode)
-}
-
-# Run current crate's tests if the current system supports it.
-cargo_test() {
- if echo "$1" | grep -E "(i686|x86_64)-unknown-linux-(gnu|musl)|darwin"
- then
- cargo test --target $1
- fi
-}
-
-# Returns relative path to binary
-# based on build target and type ("release"/"debug").
-get_binary() {
- local ext
- if [[ "$1" =~ "windows" ]]
- then
- ext=".exe"
- fi
- echo "target/$1/$2/geckodriver$ext"
-}
-
-# Create a compressed archive of the binary
-# for the given given git tag, build target, and build type.
-package_binary() {
- local bin
- bin=$(get_binary $2 $4)
- cp $bin .
-
- if [[ "$2" =~ "windows" ]]
- then
- filename="geckodriver-$1-$3.zip"
- zip "$filename" geckodriver.exe
- file "$filename"
- else
- filename="geckodriver-$1-$3.tar.gz"
- tar zcvf "$filename" geckodriver
- file "$filename"
- fi
- if [ ! -z "$DOCKER_IMAGE" ]
- then
- chown "$USER_ID:$GROUP_ID" "$filename"
- fi
-}
-
-main() {
- TOOLCHAIN=${TOOLCHAIN:=stable}
-
- if [ ! -z "$DOCKER_IMAGE" ]
- then
- setup_docker
- fi
-
- rustup_install $TOOLCHAIN
- print_versions
- rustup_target_add $TARGET
-
- # custom mingw component required
- # when compiling on 32-bit windows
- # see https://github.com/mozilla/geckodriver/pull/138#issuecomment-232139097
- if [[ $TARGET == "i686-pc-windows-gnu" ]]
- then
- mingw_i686_install
- fi
-
- cargo_config $TARGET
- cargo_build $TARGET
- cargo_test $TARGET
-
- # when something is tagged,
- # also create a release build and package it
- if [ ! -z "$TRAVIS_TAG" ]
- then
- cargo_build $TARGET 1
- package_binary $TRAVIS_TAG $TARGET $NAME "release"
- fi
-}
-
-main
diff --git a/ci.sh b/ci.sh
deleted file mode 100644
index 7a43e34..0000000
--- a/ci.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-set -ex
-
-if [ ! -z "$DOCKER_IMAGE" ]
-then
- tag="$DOCKER_IMAGE/latest"
- docker build $DOCKER_IMAGE -t $tag
- docker run \
- -e USER="$USER" \
- -e TARGET="$TARGET" \
- -e TOOLCHAIN="$TOOLCHAIN" \
- -e DOCKER_IMAGE="$DOCKER_IMAGE" \
- -e NAME="$NAME" \
- -e TRAVIS_TAG="$TRAVIS_TAG" \
- -e USER_ID=$(id -u) \
- -e GROUP_ID=$(id -g) \
- -v $PWD:/mnt/host \
- -i $tag \
- bash -s -- < build.sh
-else
- bash build.sh
-fi
diff --git a/i686-trusty/Dockerfile b/i686-trusty/Dockerfile
deleted file mode 100644
index 4d2480f..0000000
--- a/i686-trusty/Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM ubuntu:15.10
-
-RUN apt-get -y update
-RUN apt-get -y install curl file gcc gcc-mingw-w64-i686 zip
diff --git a/src/capabilities.rs b/src/capabilities.rs
deleted file mode 100644
index ce7a1bc..0000000
--- a/src/capabilities.rs
+++ /dev/null
@@ -1,516 +0,0 @@
-use logging::LogLevel;
-use marionette::LogOptions;
-use mozprofile::preferences::Pref;
-use mozprofile::profile::Profile;
-use mozrunner::runner::platform::firefox_default_path;
-use mozversion::{self, firefox_version, Version};
-use regex::bytes::Regex;
-use rustc_serialize::base64::FromBase64;
-use rustc_serialize::json::Json;
-use std::collections::BTreeMap;
-use std::default::Default;
-use std::error::Error;
-use std::fs;
-use std::io::BufWriter;
-use std::io::Cursor;
-use std::io;
-use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
-use std::str::{self, FromStr};
-use webdriver::capabilities::{BrowserCapabilities, Capabilities};
-use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
-use zip;
-
-/// Provides matching of `moz:firefoxOptions` and resolution of which Firefox
-/// binary to use.
-///
-/// `FirefoxCapabilities` is constructed with the fallback binary, should
-/// `moz:firefoxOptions` not contain a binary entry. This may either be the
-/// system Firefox installation or an override, for example given to the
-/// `--binary` flag of geckodriver.
-pub struct FirefoxCapabilities<'a> {
- pub chosen_binary: Option<PathBuf>,
- fallback_binary: Option<&'a PathBuf>,
- version_cache: BTreeMap<PathBuf, String>,
-}
-
-
-impl<'a> FirefoxCapabilities<'a> {
- pub fn new(fallback_binary: Option<&'a PathBuf>) -> FirefoxCapabilities<'a> {
- FirefoxCapabilities {
- chosen_binary: None,
- fallback_binary: fallback_binary,
- version_cache: BTreeMap::new(),
- }
- }
-
- fn set_binary(&mut self, capabilities: &BTreeMap<String, Json>) {
- self.chosen_binary = capabilities
- .get("moz:firefoxOptions")
- .and_then(|x| x.find("binary"))
- .and_then(|x| x.as_string())
- .map(|x| PathBuf::from(x))
- .or_else(|| self.fallback_binary.map(|x| x.clone()))
- .or_else(|| firefox_default_path())
- .and_then(|x| x.canonicalize().ok())
- }
-
- fn version(&mut self) -> Option<String> {
- if let Some(ref binary) = self.chosen_binary {
- if let Some(value) = self.version_cache.get(binary) {
- return Some((*value).clone());
- }
- debug!("Trying to read firefox version from ini files");
- let rv = firefox_version(&*binary)
- .ok()
- .and_then(|x| x.version_string)
- .or_else(|| {
- debug!("Trying to read firefox version from binary");
- self.version_from_binary(binary)
- });
- if let Some(ref version) = rv {
- debug!("Found version {}", version);
- self.version_cache
- .insert(binary.clone(), version.clone());
- } else {
- debug!("Failed to get binary version");
- }
- rv
- } else {
- None
- }
- }
-
- fn version_from_binary(&self, binary: &PathBuf) -> Option<String> {
- let version_regexp = Regex::new(r#"\d+\.\d+(?:[a-z]\d+)?"#).expect("Error parsing version regexp");
- let output = Command::new(binary)
- .args(&["-version"])
- .stdout(Stdio::piped())
- .spawn()
- .and_then(|child| child.wait_with_output())
- .ok();
-
- if let Some(x) = output {
- version_regexp.captures(&*x.stdout)
- .and_then(|captures| captures.get(0))
- .and_then(|m| str::from_utf8(m.as_bytes()).ok())
- .map(|x| x.into())
- } else {
- None
- }
- }
-}
-
-// TODO: put this in webdriver-rust
-fn convert_version_error(err: mozversion::Error) -> WebDriverError {
- WebDriverError::new(
- ErrorStatus::SessionNotCreated,
- err.description().to_string())
-}
-
-impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
- fn init(&mut self, capabilities: &Capabilities) {
- self.set_binary(capabilities);
- }
-
- fn browser_name(&mut self, _: &Capabilities) -> WebDriverResult<Option<String>> {
- Ok(Some("firefox".into()))
- }
-
- fn browser_version(&mut self, _: &Capabilities) -> WebDriverResult<Option<String>> {
- Ok(self.version())
- }
-
- fn platform_name(&mut self, _: &Capabilities) -> WebDriverResult<Option<String>> {
- Ok(if cfg!(target_os = "windows") {
- Some("windows".into())
- } else if cfg!(target_os = "macos") {
- Some("mac".into())
- } else if cfg!(target_os = "linux") {
- Some("linux".into())
- } else {
- None
- })
- }
-
- fn accept_insecure_certs(&mut self, _: &Capabilities) -> WebDriverResult<bool> {
- let version_str = self.version();
- if let Some(x) = version_str {
- Ok(try!(Version::from_str(&*x).or_else(|x| Err(convert_version_error(x)))).major >= 52)
- } else {
- Ok(false)
- }
- }
-
- fn compare_browser_version(&mut self,
- version: &str,
- comparison: &str)
- -> WebDriverResult<bool> {
- try!(Version::from_str(version).or_else(|x| Err(convert_version_error(x))))
- .matches(comparison)
- .or_else(|x| Err(convert_version_error(x)))
- }
-
- fn accept_proxy(&mut self, _: &Capabilities, _: &Capabilities) -> WebDriverResult<bool> {
- Ok(true)
- }
-
- fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()> {
- if !name.starts_with("moz:") {
- return Ok(())
- }
- match name {
- "moz:firefoxOptions" => {
- let data = try_opt!(value.as_object(),
- ErrorStatus::InvalidArgument,
- "moz:firefoxOptions is not an object");
- for (key, value) in data.iter() {
- match &**key {
- "binary" => {
- if !value.is_string() {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "binary path is not a string"));
- }
- },
- "args" => {
- if !try_opt!(value.as_array(),
- ErrorStatus::InvalidArgument,
- "args is not an array")
- .iter()
- .all(|value| value.is_string()) {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "args entry is not a string"));
- }
- },
- "profile" => {
- if !value.is_string() {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "profile is not a string"));
- }
- },
- "log" => {
- let log_data = try_opt!(value.as_object(),
- ErrorStatus::InvalidArgument,
- "log value is not an object");
- for (log_key, log_value) in log_data.iter() {
- match &**log_key {
- "level" => {
- let level = try_opt!(log_value.as_string(),
- ErrorStatus::InvalidArgument,
- "log level is not a string");
- if LogLevel::from_str(level).is_err() {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- format!("{} is not a valid log level",
- level)))
- }
- }
- x => return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- format!("Invalid log field {}", x)))
- }
- }
- },
- "prefs" => {
- let prefs_data = try_opt!(value.as_object(),
- ErrorStatus::InvalidArgument,
- "prefs value is not an object");
- if !prefs_data.values()
- .all(|x| x.is_string() || x.is_i64() || x.is_u64() || x.is_boolean()) {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Preference values not all string or integer or boolean"));
- }
- }
- x => return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- format!("Invalid moz:firefoxOptions field {}", x)))
- }
- }
- }
- _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- format!("Unrecognised option {}", name)))
- }
- Ok(())
- }
-
- fn accept_custom(&mut self, _: &str, _: &Json, _: &Capabilities) -> WebDriverResult<bool> {
- Ok(true)
- }
-}
-
-/// Rust representation of `moz:firefoxOptions`.
-///
-/// Calling `FirefoxOptions::from_capabilities(binary, capabilities)` causes
-/// the encoded profile, the binary arguments, log settings, and additional
-/// preferences to be checked and unmarshaled from the `moz:firefoxOptions`
-/// JSON Object into a Rust representation.
-#[derive(Default)]
-pub struct FirefoxOptions {
- pub binary: Option<PathBuf>,
- pub profile: Option<Profile>,
- pub args: Option<Vec<String>>,
- pub log: LogOptions,
- pub prefs: Vec<(String, Pref)>,
-}
-
-impl FirefoxOptions {
- pub fn new() -> FirefoxOptions {
- Default::default()
- }
-
- pub fn from_capabilities(binary_path: Option<PathBuf>,
- matched: &mut Capabilities)
- -> WebDriverResult<FirefoxOptions> {
- let mut rv = FirefoxOptions::new();
- rv.binary = binary_path;
-
- if let Some(json) = matched.remove("moz:firefoxOptions") {
- let options = try!(json.as_object()
- .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "'moz:firefoxOptions' \
- capability is not an object")));
-
- rv.profile = try!(FirefoxOptions::load_profile(&options));
- rv.args = try!(FirefoxOptions::load_args(&options));
- rv.log = try!(FirefoxOptions::load_log(&options));
- rv.prefs = try!(FirefoxOptions::load_prefs(&options));
- }
-
- Ok(rv)
- }
-
- fn load_profile(options: &Capabilities) -> WebDriverResult<Option<Profile>> {
- if let Some(profile_json) = options.get("profile") {
- let profile_base64 =
- try!(profile_json
- .as_string()
- .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
- "Profile is not a string")));
- let profile_zip = &*try!(profile_base64.from_base64());
-
- // Create an emtpy profile directory
- let profile = try!(Profile::new(None));
- try!(unzip_buffer(profile_zip,
- profile
- .temp_dir
- .as_ref()
- .expect("Profile doesn't have a path")
- .path()));
-
- Ok(Some(profile))
- } else {
- Ok(None)
- }
- }
-
- fn load_args(options: &Capabilities) -> WebDriverResult<Option<Vec<String>>> {
- if let Some(args_json) = options.get("args") {
- let args_array = try!(args_json
- .as_array()
- .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
- "Arguments were not an \
- array")));
- let args = try!(args_array
- .iter()
- .map(|x| x.as_string().map(|x| x.to_owned()))
- .collect::<Option<Vec<String>>>()
- .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
- "Arguments entries were not all \
- strings")));
- Ok(Some(args))
- } else {
- Ok(None)
- }
- }
-
- fn load_log(options: &Capabilities) -> WebDriverResult<LogOptions> {
- if let Some(json) = options.get("log") {
- let log = try!(json.as_object()
- .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Log section is not an object")));
-
- let level = match log.get("level") {
- Some(json) => {
- let s = try!(json.as_string()
- .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Log level is not a string")));
- Some(try!(LogLevel::from_str(s)
- .ok()
- .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Log level is unknown"))))
- }
- None => None,
- };
-
- Ok(LogOptions { level: level })
-
- } else {
- Ok(Default::default())
- }
- }
-
- pub fn load_prefs(options: &Capabilities) -> WebDriverResult<Vec<(String, Pref)>> {
- if let Some(prefs_data) = options.get("prefs") {
- let prefs = try!(prefs_data
- .as_object()
- .ok_or(WebDriverError::new(ErrorStatus::UnknownError,
- "Prefs were not an object")));
- let mut rv = Vec::with_capacity(prefs.len());
- for (key, value) in prefs.iter() {
- rv.push((key.clone(), try!(pref_from_json(value))));
- }
- Ok(rv)
- } else {
- Ok(vec![])
- }
- }
-}
-
-fn pref_from_json(value: &Json) -> WebDriverResult<Pref> {
- match value {
- &Json::String(ref x) => Ok(Pref::new(x.clone())),
- &Json::I64(x) => Ok(Pref::new(x)),
- &Json::U64(x) => Ok(Pref::new(x as i64)),
- &Json::Boolean(x) => Ok(Pref::new(x)),
- _ => Err(WebDriverError::new(ErrorStatus::UnknownError,
- "Could not convert pref value to string, boolean, or integer"))
- }
-}
-
-fn unzip_buffer(buf: &[u8], dest_dir: &Path) -> WebDriverResult<()> {
- let reader = Cursor::new(buf);
- let mut zip = try!(zip::ZipArchive::new(reader).map_err(|_| {
- WebDriverError::new(ErrorStatus::UnknownError, "Failed to unzip profile")
- }));
-
- for i in 0..zip.len() {
- let mut file = try!(zip.by_index(i).map_err(|_| {
- WebDriverError::new(ErrorStatus::UnknownError, "Processing profile zip file failed")
- }));
- let unzip_path = {
- let name = file.name();
- let is_dir = name.ends_with("/");
- let rel_path = Path::new(name);
- let dest_path = dest_dir.join(rel_path);
-
- {
- let create_dir = if is_dir {
- Some(dest_path.as_path())
- } else {
- dest_path.parent()
- };
- if let Some(dir) = create_dir {
- if !dir.exists() {
- debug!("Creating profile directory tree {}", dir.to_string_lossy());
- try!(fs::create_dir_all(dir));
- }
- }
- }
-
- if is_dir {
- None
- } else {
- Some(dest_path)
- }
- };
-
- if let Some(unzip_path) = unzip_path {
- debug!("Extracting profile to {}", unzip_path.to_string_lossy());
- let dest = try!(fs::File::create(unzip_path));
- if file.size() > 0 {
- let mut writer = BufWriter::new(dest);
- try!(io::copy(&mut file, &mut writer));
- }
- }
- }
-
- Ok(())
-}
-
-#[cfg(test)]
-mod tests {
- extern crate mozprofile;
- extern crate rustc_serialize;
-
- use self::mozprofile::preferences::Pref;
- use self::rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
- use self::rustc_serialize::json::Json;
- use super::FirefoxOptions;
- use marionette::MarionetteHandler;
- use std::collections::BTreeMap;
- use std::default::Default;
- use std::fs::File;
- use std::io::Read;
-
- use webdriver::capabilities::Capabilities;
-
- fn example_profile() -> Json {
- let mut profile_data = Vec::with_capacity(1024);
- let mut profile = File::open("src/tests/profile.zip").unwrap();
- profile.read_to_end(&mut profile_data).unwrap();
- let base64_config = Config {
- char_set: CharacterSet::Standard,
- newline: Newline::LF,
- pad: true,
- line_length: None,
- };
- Json::String(profile_data.to_base64(base64_config))
- }
-
- fn make_options(firefox_opts: Capabilities) -> FirefoxOptions {
- let mut caps = Capabilities::new();
- caps.insert("moz:firefoxOptions".into(), Json::Object(firefox_opts));
- let binary = None;
- FirefoxOptions::from_capabilities(binary, &mut caps).unwrap()
- }
-
- #[test]
- fn test_profile() {
- let encoded_profile = example_profile();
- let mut firefox_opts = Capabilities::new();
- firefox_opts.insert("profile".into(), encoded_profile);
-
- let opts = make_options(firefox_opts);
- let mut profile = opts.profile.unwrap();
- let prefs = profile.user_prefs().unwrap();
-
- println!("{:#?}", prefs.prefs);
-
- assert_eq!(prefs.get("startup.homepage_welcome_url"),
- Some(&Pref::new("data:text/html,PASS")));
- }
-
- #[test]
- fn test_prefs() {
- let encoded_profile = example_profile();
- let mut prefs: BTreeMap<String, Json> = BTreeMap::new();
- prefs.insert("browser.display.background_color".into(),
- Json::String("#00ff00".into()));
-
- let mut firefox_opts = Capabilities::new();
- firefox_opts.insert("profile".into(), encoded_profile);
- firefox_opts.insert("prefs".into(), Json::Object(prefs));
-
- let opts = make_options(firefox_opts);
- let mut profile = opts.profile.unwrap();
-
- let handler = MarionetteHandler::new(Default::default());
- handler
- .set_prefs(2828, &mut profile, true, opts.prefs)
- .unwrap();
-
- let prefs_set = profile.user_prefs().unwrap();
- println!("{:#?}", prefs_set.prefs);
-
- assert_eq!(prefs_set.get("startup.homepage_welcome_url"),
- Some(&Pref::new("data:text/html,PASS")));
- assert_eq!(prefs_set.get("browser.display.background_color"),
- Some(&Pref::new("#00ff00")));
- assert_eq!(prefs_set.get("marionette.defaultPrefs.port"),
- Some(&Pref::new(2828)));
- }
-}
diff --git a/src/logging.rs b/src/logging.rs
deleted file mode 100644
index 2cbff7e..0000000
--- a/src/logging.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use std::fmt;
-use std::io;
-use std::str::FromStr;
-use std::sync::atomic::AtomicBool;
-use std::sync::atomic::Ordering::SeqCst;
-
-use chrono::{DateTime, Local};
-use slog;
-use slog::DrainExt;
-use slog_atomic::{AtomicSwitch, AtomicSwitchCtrl};
-use slog_stream::{stream, Format, Streamer};
-use slog::Level as SlogLevel;
-use slog::{LevelFilter, Logger};
-use slog::{OwnedKeyValueList, Record};
-use slog_stdlog;
-
-lazy_static! {
- static ref ATOMIC_DRAIN: AtomicSwitchCtrl<io::Error> = AtomicSwitch::new(
- slog::Discard.map_err(|_| io::Error::new(io::ErrorKind::Other, "should not happen"))
- ).ctrl();
- static ref FIRST_RUN: AtomicBool = AtomicBool::new(true);
-}
-
-static DEFAULT_LEVEL: &'static LogLevel = &LogLevel::Info;
-
-/// Logger levels from [Log.jsm]
-/// (https://developer.mozilla.org/en/docs/Mozilla/JavaScript_code_modules/Log.jsm).
-#[derive(Debug, Clone)]
-pub enum LogLevel {
- Fatal,
- Error,
- Warn,
- Info,
- Config,
- Debug,
- Trace,
-}
-
-impl fmt::Display for LogLevel {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let s = match *self {
- LogLevel::Fatal => "FATAL",
- LogLevel::Error => "ERROR",
- LogLevel::Warn => "WARN",
- LogLevel::Info => "INFO",
- LogLevel::Config => "CONFIG",
- LogLevel::Debug => "DEBUG",
- LogLevel::Trace => "TRACE",
- };
- write!(f, "{}", s)
- }
-}
-
-impl FromStr for LogLevel {
- type Err = ();
-
- fn from_str(s: &str) -> Result<LogLevel, ()> {
- match s {
- "fatal" => Ok(LogLevel::Fatal),
- "error" => Ok(LogLevel::Error),
- "warn" => Ok(LogLevel::Warn),
- "info" => Ok(LogLevel::Info),
- "config" => Ok(LogLevel::Config),
- "debug" => Ok(LogLevel::Debug),
- "trace" => Ok(LogLevel::Trace),
- _ => Err(()),
- }
- }
-}
-
-trait ToSlogLevel {
- fn to_slog(&self) -> SlogLevel;
-}
-
-impl ToSlogLevel for LogLevel {
- fn to_slog(&self) -> SlogLevel {
- match *self {
- LogLevel::Fatal => SlogLevel::Critical,
- LogLevel::Error => SlogLevel::Error,
- LogLevel::Warn => SlogLevel::Warning,
- LogLevel::Info => SlogLevel::Info,
- LogLevel::Config | LogLevel::Debug => SlogLevel::Debug,
- LogLevel::Trace => SlogLevel::Trace,
- }
- }
-}
-
-trait ToGeckoLevel {
- fn to_gecko(&self) -> LogLevel;
-}
-
-impl ToGeckoLevel for SlogLevel {
- fn to_gecko(&self) -> LogLevel {
- match *self {
- SlogLevel::Critical => LogLevel::Fatal,
- SlogLevel::Error => LogLevel::Error,
- SlogLevel::Warning => LogLevel::Warn,
- SlogLevel::Info => LogLevel::Info,
- SlogLevel::Debug => LogLevel::Debug,
- SlogLevel::Trace => LogLevel::Trace,
- }
- }
-}
-
-/// Initialise logger if it has not been already. The provided `level`
-/// filters out log records below this granularity.
-pub fn init(level: &Option<LogLevel>) {
- let effective_level = level.as_ref().unwrap_or(DEFAULT_LEVEL);
-
- let drain = filtered_gecko_log(&effective_level);
- ATOMIC_DRAIN.set(drain);
-
- let first_run = FIRST_RUN.load(SeqCst);
- FIRST_RUN.store(false, SeqCst);
- if first_run {
- let log = Logger::root(ATOMIC_DRAIN.drain().fuse(), o!());
- slog_stdlog::set_logger(log.clone()).unwrap();
- }
-}
-
-fn filtered_gecko_log(level: &LogLevel) -> LevelFilter<Streamer<io::Stderr, GeckoFormat>> {
- let io = stream(io::stderr(), GeckoFormat {});
- slog::level_filter(level.to_slog(), io)
-}
-
-struct GeckoFormat;
-
-impl Format for GeckoFormat {
- fn format(&self, io: &mut io::Write, record: &Record, _: &OwnedKeyValueList) -> io::Result<()> {
- // TODO(ato): Quite sure this is the wrong way to filter records with slog,
- // but I do not comprehend how slog works.
- let module = record.module();
- if module.starts_with("geckodriver") || module.starts_with("webdriver") {
- let ts = format_ts(Local::now());
- let level = record.level().to_gecko();
- let _ = try!(write!(io, "{}\t{}\t{}\t{}\n", ts, module, level, record.msg()));
- }
- Ok(())
- }
-}
-
-/// Produces a 13-digit Unix Epoch timestamp similar to Gecko.
-fn format_ts(ts: DateTime<Local>) -> String {
- format!("{}{:03}", ts.timestamp(), ts.timestamp_subsec_millis())
-}
-
-#[cfg(test)]
-mod tests {
- use chrono::Local;
- use super::format_ts;
-
- #[test]
- fn test_format_ts() {
- let ts = Local::now();
- let s = format_ts(ts);
- assert_eq!(s.len(), 13);
- }
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 518b50e..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-extern crate chrono;
-#[macro_use]
-extern crate clap;
-#[macro_use]
-extern crate lazy_static;
-extern crate hyper;
-extern crate mozprofile;
-extern crate mozrunner;
-extern crate mozversion;
-extern crate regex;
-extern crate rustc_serialize;
-#[macro_use]
-extern crate slog;
-extern crate slog_atomic;
-extern crate slog_stdlog;
-extern crate slog_stream;
-extern crate zip;
-extern crate webdriver;
-
-#[macro_use]
-extern crate log;
-
-use std::borrow::ToOwned;
-use std::io::Write;
-use std::net::{SocketAddr, IpAddr};
-use std::path::PathBuf;
-use std::str::FromStr;
-
-use clap::{App, Arg};
-
-macro_rules! try_opt {
- ($expr:expr, $err_type:expr, $err_msg:expr) => ({
- match $expr {
- Some(x) => x,
- None => return Err(WebDriverError::new($err_type, $err_msg))
- }
- })
-}
-
-mod logging;
-mod prefs;
-mod marionette;
-mod capabilities;
-
-use logging::LogLevel;
-use marionette::{MarionetteHandler, MarionetteSettings, extension_routes};
-
-type ProgramResult = std::result::Result<(), (ExitCode, String)>;
-
-enum ExitCode {
- Ok = 0,
- Usage = 64,
- Unavailable = 69,
-}
-
-fn app<'a, 'b>() -> App<'a, 'b> {
- App::new(format!("geckodriver {}", crate_version!()))
- .about("WebDriver implementation for Firefox.")
- .arg(Arg::with_name("webdriver_host")
- .long("host")
- .value_name("HOST")
- .help("Host ip to use for WebDriver server (default: 127.0.0.1)")
- .takes_value(true))
- .arg(Arg::with_name("webdriver_port")
- .short("p")
- .long("port")
- .value_name("PORT")
- .help("Port to use for WebDriver server (default: 4444)")
- .takes_value(true)
- .alias("webdriver-port"))
- .arg(Arg::with_name("binary")
- .short("b")
- .long("binary")
- .value_name("BINARY")
- .help("Path to the Firefox binary")
- .takes_value(true))
- .arg(Arg::with_name("marionette_port")
- .long("marionette-port")
- .value_name("PORT")
- .help("Port to use to connect to Gecko (default: random free port)")
- .takes_value(true))
- .arg(Arg::with_name("connect_existing")
- .long("connect-existing")
- .requires("marionette_port")
- .help("Connect to an existing Firefox instance"))
- .arg(Arg::with_name("verbosity")
- .short("v")
- .multiple(true)
- .conflicts_with("log_level")
- .help("Log level verbosity (-v for debug and -vv for trace level)"))
- .arg(Arg::with_name("log_level")
- .long("log")
- .takes_value(true)
- .value_name("LEVEL")
- .possible_values(&["fatal", "error", "warn", "info", "config", "debug", "trace"])
- .help("Set Gecko log level"))
- .arg(Arg::with_name("version")
- .short("V")
- .long("version")
- .help("Prints version and copying information"))
-}
-
-fn run() -> ProgramResult {
- let matches = app().get_matches();
-
- if matches.is_present("version") {
- println!("geckodriver {}\n\n{}", crate_version!(),
-"The source code of this program is available at
-https://github.com/mozilla/geckodriver.
-
-This program is subject to the terms of the Mozilla Public License 2.0.
-You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.");
- return Ok(())
- }
-
- let host = matches.value_of("webdriver_host").unwrap_or("127.0.0.1");
- let port = match u16::from_str(matches.value_of("webdriver_port")
- .or(matches.value_of("webdriver_port_alias"))
- .unwrap_or("4444")) {
- Ok(x) => x,
- Err(_) => return Err((ExitCode::Usage, "invalid WebDriver port".to_owned())),
- };
- let addr = match IpAddr::from_str(host) {
- Ok(addr) => SocketAddr::new(addr, port),
- Err(_) => return Err((ExitCode::Usage, "invalid host address".to_owned())),
- };
-
- let binary = matches.value_of("binary").map(|x| PathBuf::from(x));
-
- let marionette_port = match matches.value_of("marionette_port") {
- Some(x) => match u16::from_str(x) {
- Ok(x) => Some(x),
- Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".to_owned())),
- },
- None => None
- };
-
- // overrides defaults in Gecko
- // which are info for optimised builds
- // and debug for debug builds
- let log_level = if matches.is_present("log_level") {
- LogLevel::from_str(matches.value_of("log_level").unwrap()).ok()
- } else {
- match matches.occurrences_of("verbosity") {
- 0 => Some(LogLevel::Info),
- 1 => Some(LogLevel::Debug),
- _ => Some(LogLevel::Trace),
- }
- };
- logging::init(&log_level);
-
- let settings = MarionetteSettings {
- port: marionette_port,
- binary: binary,
- connect_existing: matches.is_present("connect_existing"),
- log_level: log_level,
- };
-
- let handler = MarionetteHandler::new(settings);
- let listening = try!(webdriver::server::start(addr, handler, &extension_routes()[..])
- .map_err(|err| (ExitCode::Unavailable, err.to_string())));
- info!("Listening on {}", listening.socket);
- Ok(())
-}
-
-fn main() {
- let exit_code = match run() {
- Ok(_) => ExitCode::Ok,
- Err((exit_code, reason)) => {
- error!("{}", reason);
- exit_code
- },
- };
-
- std::io::stdout().flush().unwrap();
- std::process::exit(exit_code as i32);
-}
diff --git a/src/marionette.rs b/src/marionette.rs
deleted file mode 100644
index 1039795..0000000
--- a/src/marionette.rs
+++ /dev/null
@@ -1,1579 +0,0 @@
-use hyper::method::Method;
-use logging;
-use logging::LogLevel;
-use mozprofile::preferences::Pref;
-use mozprofile::profile::Profile;
-use mozrunner::runner::{Runner, FirefoxRunner};
-use regex::Captures;
-use rustc_serialize::json;
-use rustc_serialize::json::{Json, ToJson};
-use std::collections::BTreeMap;
-use std::error::Error;
-use std::io::Error as IoError;
-use std::io::ErrorKind;
-use std::io::prelude::*;
-use std::path::PathBuf;
-use std::io::Result as IoResult;
-use std::net::{TcpListener, TcpStream};
-use std::sync::Mutex;
-use std::thread::sleep;
-use std::time::Duration;
-use webdriver::capabilities::CapabilitiesMatching;
-use webdriver::command::{WebDriverCommand, WebDriverMessage, Parameters,
- WebDriverExtensionCommand};
-use webdriver::command::WebDriverCommand::{
- NewSession, DeleteSession, Status, Get, GetCurrentUrl,
- GoBack, GoForward, Refresh, GetTitle, GetPageSource, GetWindowHandle,
- GetWindowHandles, CloseWindow, SetWindowRect,
- GetWindowRect, MaximizeWindow, SwitchToWindow, SwitchToFrame,
- SwitchToParentFrame, FindElement, FindElements,
- FindElementElement, FindElementElements, GetActiveElement,
- IsDisplayed, IsSelected, GetElementAttribute, GetElementProperty, GetCSSValue,
- GetElementText, GetElementTagName, GetElementRect, IsEnabled,
- ElementClick, ElementTap, ElementClear, ElementSendKeys,
- ExecuteScript, ExecuteAsyncScript, GetCookies, GetNamedCookie, AddCookie,
- DeleteCookies, DeleteCookie, GetTimeouts, SetTimeouts, DismissAlert,
- AcceptAlert, GetAlertText, SendAlertText, TakeScreenshot, TakeElementScreenshot,
- Extension, PerformActions, ReleaseActions};
-use webdriver::command::{
- NewSessionParameters, GetParameters, WindowRectParameters, SwitchToWindowParameters,
- SwitchToFrameParameters, LocatorParameters, JavascriptCommandParameters,
- GetNamedCookieParameters, AddCookieParameters, TimeoutsParameters,
- ActionsParameters, TakeScreenshotParameters};
-use webdriver::response::{CloseWindowResponse, Cookie, CookieResponse, ElementRectResponse,
- NewSessionResponse, TimeoutsResponse, ValueResponse, WebDriverResponse,
- WindowRectResponse};
-use webdriver::common::{
- Date, Nullable, WebElement, FrameId, ELEMENT_KEY};
-use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
-use webdriver::server::{WebDriverHandler, Session};
-use webdriver::httpapi::{WebDriverExtensionRoute};
-
-use capabilities::{FirefoxCapabilities, FirefoxOptions};
-use prefs;
-
-const DEFAULT_HOST: &'static str = "localhost";
-
-pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> {
- return vec![(Method::Get, "/session/{sessionId}/moz/context", GeckoExtensionRoute::GetContext),
- (Method::Post, "/session/{sessionId}/moz/context", GeckoExtensionRoute::SetContext),
- (Method::Post,
- "/session/{sessionId}/moz/xbl/{elementId}/anonymous_children",
- GeckoExtensionRoute::XblAnonymousChildren),
- (Method::Post,
- "/session/{sessionId}/moz/xbl/{elementId}/anonymous_by_attribute",
- GeckoExtensionRoute::XblAnonymousByAttribute),
- (Method::Post, "/session/{sessionId}/moz/addon/install",
- GeckoExtensionRoute::InstallAddon),
- (Method::Post, "/session/{sessionId}/moz/addon/uninstall",
- GeckoExtensionRoute::UninstallAddon)];
-}
-
-#[derive(Clone, PartialEq)]
-pub enum GeckoExtensionRoute {
- GetContext,
- SetContext,
- XblAnonymousChildren,
- XblAnonymousByAttribute,
- InstallAddon,
- UninstallAddon,
-}
-
-impl WebDriverExtensionRoute for GeckoExtensionRoute {
- type Command = GeckoExtensionCommand;
-
- fn command(&self,
- captures: &Captures,
- body_data: &Json)
- -> WebDriverResult<WebDriverCommand<GeckoExtensionCommand>> {
- let command = match self {
- &GeckoExtensionRoute::GetContext => GeckoExtensionCommand::GetContext,
- &GeckoExtensionRoute::SetContext => {
- let parameters: GeckoContextParameters = try!(Parameters::from_json(&body_data));
- GeckoExtensionCommand::SetContext(parameters)
- }
- &GeckoExtensionRoute::XblAnonymousChildren => {
- let element_id = try!(captures.name("elementId")
- .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Missing elementId parameter")));
- GeckoExtensionCommand::XblAnonymousChildren(element_id.as_str().into())
- }
- &GeckoExtensionRoute::XblAnonymousByAttribute => {
- let element_id = try!(captures.name("elementId")
- .ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Missing elementId parameter")));
- let parameters: AttributeParameters = try!(Parameters::from_json(&body_data));
- GeckoExtensionCommand::XblAnonymousByAttribute(element_id.as_str().into(),
- parameters)
- }
- &GeckoExtensionRoute::InstallAddon => {
- let parameters: AddonInstallParameters = try!(Parameters::from_json(&body_data));
- GeckoExtensionCommand::InstallAddon(parameters)
- }
- &GeckoExtensionRoute::UninstallAddon => {
- let parameters: AddonUninstallParameters = try!(Parameters::from_json(&body_data));
- GeckoExtensionCommand::UninstallAddon(parameters)
- }
- };
- Ok(WebDriverCommand::Extension(command))
- }
-}
-
-#[derive(Clone, PartialEq)]
-pub enum GeckoExtensionCommand {
- GetContext,
- SetContext(GeckoContextParameters),
- XblAnonymousChildren(WebElement),
- XblAnonymousByAttribute(WebElement, AttributeParameters),
- InstallAddon(AddonInstallParameters),
- UninstallAddon(AddonUninstallParameters)
-}
-
-impl WebDriverExtensionCommand for GeckoExtensionCommand {
- fn parameters_json(&self) -> Option<Json> {
- match self {
- &GeckoExtensionCommand::GetContext => None,
- &GeckoExtensionCommand::SetContext(ref x) => Some(x.to_json()),
- &GeckoExtensionCommand::XblAnonymousChildren(_) => None,
- &GeckoExtensionCommand::XblAnonymousByAttribute(_, ref x) => Some(x.to_json()),
- &GeckoExtensionCommand::InstallAddon(ref x) => Some(x.to_json()),
- &GeckoExtensionCommand::UninstallAddon(ref x) => Some(x.to_json()),
- }
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-enum GeckoContext {
- Content,
- Chrome,
-}
-
-impl ToJson for GeckoContext {
- fn to_json(&self) -> Json {
- match self {
- &GeckoContext::Content => Json::String("content".to_owned()),
- &GeckoContext::Chrome => Json::String("chrome".to_owned()),
- }
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct GeckoContextParameters {
- context: GeckoContext
-}
-
-impl Parameters for GeckoContextParameters {
- fn from_json(body: &Json) -> WebDriverResult<GeckoContextParameters> {
- let data = try!(body.as_object().ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Message body was not an object")));
- let context_value = try!(data.get("context").ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Missing context key")));
- let value = try!(context_value.as_string().ok_or(
- WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "context was not a string")));
- let context = try!(match value {
- "chrome" => Ok(GeckoContext::Chrome),
- "content" => Ok(GeckoContext::Content),
- _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- format!("{} is not a valid context",
- value)))
- });
- Ok(GeckoContextParameters {
- context: context
- })
- }
-}
-
-impl ToMarionette for GeckoContextParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- data.insert("value".to_owned(), self.context.to_json());
- Ok(data)
- }
-}
-
-impl ToJson for GeckoContextParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("context".to_owned(), self.context.to_json());
- Json::Object(data)
- }
-}
-
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct AttributeParameters {
- name: String,
- value: String
-}
-
-impl Parameters for AttributeParameters {
- fn from_json(body: &Json) -> WebDriverResult<AttributeParameters> {
- let data = try!(body.as_object().ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Message body was not an object")));
- let name = try!(try!(data.get("name").ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Missing 'name' parameter"))).as_string().
- ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "'name' parameter is not a string")));
- let value = try!(try!(data.get("value").ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Missing 'value' parameter"))).as_string().
- ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
- "'value' parameter is not a string")));
- Ok(AttributeParameters {
- name: name.to_owned(),
- value: value.to_owned(),
- })
- }
-}
-
-impl ToJson for AttributeParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("name".to_owned(), self.name.to_json());
- data.insert("value".to_owned(), self.value.to_json());
- Json::Object(data)
- }
-}
-
-impl ToMarionette for AttributeParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- data.insert("using".to_owned(), "anon attribute".to_json());
- let mut value = BTreeMap::new();
- value.insert(self.name.to_owned(), self.value.to_json());
- data.insert("value".to_owned(), Json::Object(value));
- Ok(data)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct AddonInstallParameters {
- pub path: String,
- pub temporary: bool
-}
-
-impl Parameters for AddonInstallParameters {
- fn from_json(body: &Json) -> WebDriverResult<AddonInstallParameters> {
- let data = try!(body.as_object().ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Message body was not an object")));
-
- let path = try_opt!(
- try_opt!(data.get("path"),
- ErrorStatus::InvalidArgument,
- "Missing 'path' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "'path' is not a string").to_string();
-
- let temporary = match data.get("temporary") {
- Some(x) => try_opt!(x.as_boolean(),
- ErrorStatus::InvalidArgument,
- "Failed to convert 'temporary' to boolean"),
- None => false
- };
-
- return Ok(AddonInstallParameters {
- path: path,
- temporary: temporary,
- })
- }
-}
-
-impl ToJson for AddonInstallParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("path".to_string(), self.path.to_json());
- data.insert("temporary".to_string(), self.temporary.to_json());
- Json::Object(data)
- }
-}
-
-impl ToMarionette for AddonInstallParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- data.insert("path".to_string(), self.path.to_json());
- data.insert("temporary".to_string(), self.temporary.to_json());
- Ok(data)
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct AddonUninstallParameters {
- pub id: String
-}
-
-impl Parameters for AddonUninstallParameters {
- fn from_json(body: &Json) -> WebDriverResult<AddonUninstallParameters> {
- let data = try!(body.as_object().ok_or(
- WebDriverError::new(ErrorStatus::InvalidArgument,
- "Message body was not an object")));
-
- let id = try_opt!(
- try_opt!(data.get("id"),
- ErrorStatus::InvalidArgument,
- "Missing 'id' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "'id' is not a string").to_string();
-
- return Ok(AddonUninstallParameters {id: id})
- }
-}
-
-impl ToJson for AddonUninstallParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), self.id.to_json());
- Json::Object(data)
- }
-}
-
-impl ToMarionette for AddonUninstallParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), self.id.to_json());
- Ok(data)
- }
-}
-
-#[derive(Default)]
-pub struct LogOptions {
- pub level: Option<LogLevel>,
-}
-
-#[derive(Default)]
-pub struct MarionetteSettings {
- pub port: Option<u16>,
- pub binary: Option<PathBuf>,
- pub connect_existing: bool,
-
- /// Optionally increase Marionette's verbosity by providing a log
- /// level. The Gecko default is LogLevel::Info for optimised
- /// builds and LogLevel::Debug for debug builds.
- pub log_level: Option<LogLevel>,
-}
-
-pub struct MarionetteHandler {
- connection: Mutex<Option<MarionetteConnection>>,
- settings: MarionetteSettings,
- browser: Option<FirefoxRunner>,
- current_log_level: Option<LogLevel>,
-}
-
-impl MarionetteHandler {
- pub fn new(settings: MarionetteSettings) -> MarionetteHandler {
- MarionetteHandler {
- connection: Mutex::new(None),
- settings: settings,
- browser: None,
- current_log_level: None,
- }
- }
-
- fn create_connection(&mut self,
- session_id: &Option<String>,
- new_session_parameters: &NewSessionParameters)
- -> WebDriverResult<BTreeMap<String, Json>> {
- let (options, capabilities) = {
- let mut fx_capabilities = FirefoxCapabilities::new(self.settings.binary.as_ref());
- let mut capabilities = try!(
- try!(new_session_parameters
- .match_browser(&mut fx_capabilities))
- .ok_or(WebDriverError::new(
- ErrorStatus::SessionNotCreated,
- "Unable to find a matching set of capabilities")));
-
- let options = try!(FirefoxOptions::from_capabilities(fx_capabilities.chosen_binary,
- &mut capabilities));
- (options, capabilities)
- };
-
- self.current_log_level = options.log.level.clone().or(self.settings.log_level.clone());
- logging::init(&self.current_log_level);
-
- let port = self.settings.port.unwrap_or(try!(get_free_port()));
- if !self.settings.connect_existing {
- try!(self.start_browser(port, options));
- }
-
- let mut connection = MarionetteConnection::new(port, session_id.clone());
- try!(connection.connect());
- self.connection = Mutex::new(Some(connection));
-
- Ok(capabilities)
- }
-
- fn start_browser(&mut self, port: u16, mut options: FirefoxOptions) -> WebDriverResult<()> {
- let binary = try!(options.binary
- .ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated,
- "Expected browser binary location, but unable to find \
- binary in default location, no \
- 'moz:firefoxOptions.binary' capability provided, and \
- no binary flag set on the command line")));
-
- let custom_profile = options.profile.is_some();
-
- let mut runner = try!(FirefoxRunner::new(&binary, options.profile.take())
- .map_err(|e| WebDriverError::new(ErrorStatus::SessionNotCreated,
- e.description().to_owned())));
-
- // double-dashed flags are not accepted on Windows systems
- runner.args().push("-marionette".to_owned());
-
- if let Some(args) = options.args.take() {
- runner.args().extend(args);
- };
-
- try!(self.set_prefs(port, &mut runner.profile, custom_profile, options.prefs)
- .map_err(|e| {
- WebDriverError::new(ErrorStatus::SessionNotCreated,
- format!("Failed to set preferences: {}", e))
- }));
-
- info!("Starting browser {} with args {:?}", binary.display(), runner.args());
- try!(runner.start()
- .map_err(|e| {
- WebDriverError::new(ErrorStatus::SessionNotCreated,
- format!("Failed to start browser {}: {}",
- binary.display(), e))
- }));
- self.browser = Some(runner);
-
- Ok(())
- }
-
- pub fn set_prefs(&self, port: u16, profile: &mut Profile, custom_profile: bool,
- extra_prefs: Vec<(String, Pref)>)
- -> WebDriverResult<()> {
- let prefs = try!(profile.user_prefs()
- .map_err(|_| WebDriverError::new(ErrorStatus::UnknownError,
- "Unable to read profile preferences file")));
-
- for &(ref name, ref value) in prefs::DEFAULT.iter() {
- if !custom_profile || !prefs.contains_key(name) {
- prefs.insert((*name).clone(), (*value).clone());
- }
- }
-
- prefs.insert_slice(&extra_prefs[..]);
-
- // fallbacks can be removed when Firefox 54 becomes stable
- if let Some(ref level) = self.current_log_level {
- prefs.insert("marionette.log.level", Pref::new(level.to_string()));
- prefs.insert("marionette.logging", Pref::new(level.to_string())); // fallback
- };
- prefs.insert("marionette.port", Pref::new(port as i64));
- prefs.insert("marionette.defaultPrefs.port", Pref::new(port as i64)); // fallback
-
- prefs.write().map_err(|_| WebDriverError::new(ErrorStatus::UnknownError,
- "Unable to write Firefox profile"))
- }
-}
-
-impl WebDriverHandler<GeckoExtensionRoute> for MarionetteHandler {
- fn handle_command(&mut self, _: &Option<Session>,
- msg: WebDriverMessage<GeckoExtensionRoute>) -> WebDriverResult<WebDriverResponse> {
- let mut resolved_capabilities = None;
- {
- let mut capabilities_options = None;
- // First handle the status message which doesn't actually require a marionette
- // connection or message
- if msg.command == Status {
- let (ready, message) = self.connection.lock()
- .map(|ref connection| connection
- .as_ref()
- .map(|_| (false, "Session already started"))
- .unwrap_or((true, "")))
- .unwrap_or((false, "geckodriver internal error"));
- let mut value = BTreeMap::new();
- value.insert("ready".to_string(), Json::Boolean(ready));
- value.insert("message".to_string(), Json::String(message.into()));
- return Ok(WebDriverResponse::Generic(ValueResponse::new(Json::Object(value))));
- }
- match self.connection.lock() {
- Ok(ref connection) => {
- if connection.is_none() {
- match msg.command {
- NewSession(ref capabilities) => {
- capabilities_options = Some(capabilities);
- },
- _ => {
- return Err(WebDriverError::new(
- ErrorStatus::SessionNotCreated,
- "Tried to run command without establishing a connection"));
- }
- }
- }
- },
- Err(_) => {
- return Err(WebDriverError::new(
- ErrorStatus::UnknownError,
- "Failed to aquire Marionette connection"))
- }
- }
- if let Some(capabilities) = capabilities_options {
- resolved_capabilities = Some(try!(
- self.create_connection(&msg.session_id, &capabilities)));
- }
- }
-
- match self.connection.lock() {
- Ok(ref mut connection) => {
- match connection.as_mut() {
- Some(conn) => conn.send_command(resolved_capabilities, &msg),
- None => panic!("Connection missing")
- }
- },
- Err(_) => {
- Err(WebDriverError::new(
- ErrorStatus::UnknownError,
- "Failed to aquire Marionette connection"))
- }
- }
- }
-
- fn delete_session(&mut self, _: &Option<Session>) {
- if let Ok(connection) = self.connection.lock() {
- if let Some(ref conn) = *connection {
- conn.close();
- }
- }
- if let Some(ref mut runner) = self.browser {
- debug!("Stopping browser process");
- if runner.stop().is_err() {
- error!("Failed to kill browser process");
- };
- }
- self.connection = Mutex::new(None);
- self.browser = None;
- }
-}
-
-pub struct MarionetteSession {
- pub session_id: String,
- protocol: Option<String>,
- application_type: Option<String>,
- command_id: u64
-}
-
-impl MarionetteSession {
- pub fn new(session_id: Option<String>) -> MarionetteSession {
- let initital_id = session_id.unwrap_or("".to_string());
- MarionetteSession {
- session_id: initital_id,
- protocol: None,
- application_type: None,
- command_id: 0
- }
- }
-
- pub fn update(&mut self, msg: &WebDriverMessage<GeckoExtensionRoute>,
- resp: &MarionetteResponse) -> WebDriverResult<()> {
- match msg.command {
- NewSession(_) => {
- let session_id = try_opt!(
- try_opt!(resp.result.find("sessionId"),
- ErrorStatus::SessionNotCreated,
- "Unable to get session id").as_string(),
- ErrorStatus::SessionNotCreated,
- "Unable to convert session id to string");
- self.session_id = session_id.to_string().clone();
- },
- _ => {}
- }
- Ok(())
- }
-
- fn to_web_element(&self, json_data: &Json) -> WebDriverResult<WebElement> {
- let data = try_opt!(json_data.as_object(),
- ErrorStatus::UnknownError,
- "Failed to convert data to an object");
- let id = try_opt!(
- try_opt!(
- match data.get("ELEMENT") {
- Some(id) => Some(id),
- None => {
- match data.get(ELEMENT_KEY) {
- Some(id) => Some(id),
- None => None
- }
- }
- },
- ErrorStatus::UnknownError,
- "Failed to extract Web Element from response").as_string(),
- ErrorStatus::UnknownError,
- "Failed to convert id value to string"
- ).to_string();
- Ok(WebElement::new(id))
- }
-
- pub fn next_command_id(&mut self) -> u64 {
- self.command_id = self.command_id + 1;
- self.command_id
- }
-
- pub fn response(&mut self, msg: &WebDriverMessage<GeckoExtensionRoute>,
- resp: MarionetteResponse) -> WebDriverResult<WebDriverResponse> {
-
- if resp.id != self.command_id {
- return Err(WebDriverError::new(ErrorStatus::UnknownError,
- format!("Marionette responses arrived out of sequence, expected {}, got {}",
- self.command_id, resp.id)));
- }
-
- if let Some(error) = resp.error {
- let status = self.error_from_string(&error.status);
-
- return Err(WebDriverError::new(status, error.message));
- }
-
- try!(self.update(msg, &resp));
-
- Ok(match msg.command {
- // Everything that doesn't have a response value
- Get(_) | GoBack | GoForward | Refresh | SetTimeouts(_) |
- SetWindowRect(_) | MaximizeWindow | SwitchToWindow(_) | SwitchToFrame(_) |
- SwitchToParentFrame | AddCookie(_) | DeleteCookies | DeleteCookie(_) |
- DismissAlert | AcceptAlert | SendAlertText(_) | ElementClick(_) |
- ElementTap(_) | ElementClear(_) | ElementSendKeys(_, _) |
- PerformActions(_) | ReleaseActions => {
- WebDriverResponse::Void
- },
- // Things that simply return the contents of the marionette "value" property
- GetCurrentUrl | GetTitle | GetPageSource | GetWindowHandle | IsDisplayed(_) |
- IsSelected(_) | GetElementAttribute(_, _) | GetElementProperty(_, _) |
- GetCSSValue(_, _) | GetElementText(_) |
- GetElementTagName(_) | IsEnabled(_) | ExecuteScript(_) | ExecuteAsyncScript(_) |
- GetAlertText | TakeScreenshot | TakeElementScreenshot(_) => {
- let value = try_opt!(resp.result.find("value"),
- ErrorStatus::UnknownError,
- "Failed to find value field");
- //TODO: Convert webelement keys
- WebDriverResponse::Generic(ValueResponse::new(value.clone()))
- },
- GetTimeouts => {
- let script = try_opt!(try_opt!(resp.result
- .find("script"),
- ErrorStatus::UnknownError,
- "Missing field: script")
- .as_u64(),
- ErrorStatus::UnknownError,
- "Failed to interpret script timeout duration as u64");
- // Check for the spec-compliant "pageLoad", but also for "page load",
- // which was sent by Firefox 52 and earlier.
- let page_load = try_opt!(try_opt!(resp.result.find("pageLoad")
- .or(resp.result.find("page load")),
- ErrorStatus::UnknownError,
- "Missing field: pageLoad")
- .as_u64(),
- ErrorStatus::UnknownError,
- "Failed to interpret page load duration as u64");
- let implicit = try_opt!(try_opt!(resp.result
- .find("implicit"),
- ErrorStatus::UnknownError,
- "Missing field: implicit")
- .as_u64(),
- ErrorStatus::UnknownError,
- "Failed to interpret implicit search duration as u64");
-
- WebDriverResponse::Timeouts(TimeoutsResponse {
- script: script,
- pageLoad: page_load,
- implicit: implicit,
- })
- },
- Status => panic!("Got status command that should already have been handled"),
- GetWindowHandles => {
- WebDriverResponse::Generic(ValueResponse::new(resp.result.clone()))
- },
- CloseWindow => {
- let data = try_opt!(resp.result.as_array(),
- ErrorStatus::UnknownError,
- "Failed to interpret value as array");
- let handles = try!(data.iter()
- .map(|x| {
- Ok(try_opt!(x.as_string(),
- ErrorStatus::UnknownError,
- "Failed to interpret window handle as string")
- .to_owned())
- })
- .collect());
- WebDriverResponse::CloseWindow(CloseWindowResponse { window_handles: handles })
- }
- GetWindowRect => {
- let width = try_opt!(
- try_opt!(resp.result.find("width"),
- ErrorStatus::UnknownError,
- "Failed to find width field").as_u64(),
- ErrorStatus::UnknownError,
- "Failed to interpret width as integer");
-
- let height = try_opt!(
- try_opt!(resp.result.find("height"),
- ErrorStatus::UnknownError,
- "Failed to find height field").as_u64(),
- ErrorStatus::UnknownError,
- "Failed to interpret width as integer");
-
- let x = try_opt!(
- try_opt!(resp.result.find("x"),
- ErrorStatus::UnknownError,
- "Failed to find x field").as_i64(),
- ErrorStatus::UnknownError,
- "Failed to interpret x as integer");
-
- let y = try_opt!(
- try_opt!(resp.result.find("y"),
- ErrorStatus::UnknownError,
- "Failed to find y field").as_i64(),
- ErrorStatus::UnknownError,
- "Failed to interpret y as integer");
-
- WebDriverResponse::WindowRect(WindowRectResponse {x: x,
- y: y,
- width: width,
- height: height})
- },
- GetElementRect(_) => {
- let x = try_opt!(
- try_opt!(resp.result.find("x"),
- ErrorStatus::UnknownError,
- "Failed to find x field").as_f64(),
- ErrorStatus::UnknownError,
- "Failed to interpret x as float");
-
- let y = try_opt!(
- try_opt!(resp.result.find("y"),
- ErrorStatus::UnknownError,
- "Failed to find y field").as_f64(),
- ErrorStatus::UnknownError,
- "Failed to interpret y as float");
-
- let width = try_opt!(
- try_opt!(resp.result.find("width"),
- ErrorStatus::UnknownError,
- "Failed to find width field").as_f64(),
- ErrorStatus::UnknownError,
- "Failed to interpret width as float");
-
- let height = try_opt!(
- try_opt!(resp.result.find("height"),
- ErrorStatus::UnknownError,
- "Failed to find height field").as_f64(),
- ErrorStatus::UnknownError,
- "Failed to interpret width as float");
-
- WebDriverResponse::ElementRect(ElementRectResponse::new(x, y, width, height))
- },
- GetCookies => {
- let cookies = try!(self.process_cookies(&resp.result));
- WebDriverResponse::Cookie(CookieResponse::new(cookies))
- },
- GetNamedCookie(ref name) => {
- let mut cookies = try!(self.process_cookies(&resp.result));
- cookies.retain(|x| x.name == *name);
- WebDriverResponse::Cookie(CookieResponse::new(cookies))
- }
- FindElement(_) | FindElementElement(_, _) => {
- let element = try!(self.to_web_element(
- try_opt!(resp.result.find("value"),
- ErrorStatus::UnknownError,
- "Failed to find value field")));
- WebDriverResponse::Generic(ValueResponse::new(element.to_json()))
- },
- FindElements(_) | FindElementElements(_, _) => {
- let element_vec = try_opt!(resp.result.as_array(),
- ErrorStatus::UnknownError,
- "Failed to interpret value as array");
- let elements = try!(element_vec.iter().map(
- |x| {
- self.to_web_element(x)
- }).collect::<Result<Vec<_>, _>>());
- WebDriverResponse::Generic(ValueResponse::new(
- Json::Array(elements.iter().map(|x| {x.to_json()}).collect())))
- },
- GetActiveElement => {
- let element = try!(self.to_web_element(
- try_opt!(resp.result.find("value"),
- ErrorStatus::UnknownError,
- "Failed to find value field")));
- WebDriverResponse::Generic(ValueResponse::new(element.to_json()))
- },
- NewSession(_) => {
- let mut session_id = try_opt!(
- try_opt!(resp.result.find("sessionId"),
- ErrorStatus::InvalidSessionId,
- "Failed to find sessionId field").as_string(),
- ErrorStatus::InvalidSessionId,
- "sessionId was not a string");
-
- if session_id.starts_with("{") && session_id.ends_with("}") {
- session_id = &session_id[1..session_id.len()-1];
- }
-
- let capabilities = try_opt!(
- try_opt!(resp.result.find("capabilities"),
- ErrorStatus::UnknownError,
- "Failed to find capabilities field").as_object(),
- ErrorStatus::UnknownError,
- "capabiltites field was not an Object");
-
- WebDriverResponse::NewSession(NewSessionResponse::new(
- session_id.to_string(), Json::Object(capabilities.clone())))
- },
- DeleteSession => {
- WebDriverResponse::DeleteSession
- },
- Extension(ref extension) => {
- match extension {
- &GeckoExtensionCommand::GetContext => {
- let value = try_opt!(resp.result.find("value"),
- ErrorStatus::UnknownError,
- "Failed to find value field");
- WebDriverResponse::Generic(ValueResponse::new(value.clone()))
- },
- &GeckoExtensionCommand::SetContext(_) => WebDriverResponse::Void,
- &GeckoExtensionCommand::XblAnonymousChildren(_) => {
- let els_vec = try_opt!(resp.result.as_array(),
- ErrorStatus::UnknownError, "Failed to interpret body as array");
- let els = try!(els_vec.iter().map(|x| self.to_web_element(x))
- .collect::<Result<Vec<_>, _>>());
- WebDriverResponse::Generic(ValueResponse::new(
- Json::Array(els.iter().map(|el| el.to_json()).collect())))
- },
- &GeckoExtensionCommand::XblAnonymousByAttribute(_, _) => {
- let el = try!(self.to_web_element(try_opt!(resp.result.find("value"),
- ErrorStatus::UnknownError, "Failed to find value field")));
- WebDriverResponse::Generic(ValueResponse::new(el.to_json()))
- },
- &GeckoExtensionCommand::InstallAddon(_) => {
- let value = try_opt!(resp.result.find("value"),
- ErrorStatus::UnknownError,
- "Failed to find value field");
- WebDriverResponse::Generic(ValueResponse::new(value.clone()))
- },
- &GeckoExtensionCommand::UninstallAddon(_) => WebDriverResponse::Void
- }
- }
- })
- }
-
- fn process_cookies(&self, json_data: &Json) -> WebDriverResult<Vec<Cookie>> {
- let value = try_opt!(json_data.as_array(),
- ErrorStatus::UnknownError,
- "Failed to interpret value as array");
- value.iter().map(|x| {
- let name = try_opt!(
- try_opt!(x.find("name"),
- ErrorStatus::UnknownError,
- "Failed to find name field").as_string(),
- ErrorStatus::UnknownError,
- "Failed to interpret name as string").to_string();
- let value = try_opt!(
- try_opt!(x.find("value"),
- ErrorStatus::UnknownError,
- "Failed to find value field").as_string(),
- ErrorStatus::UnknownError,
- "Failed to interpret value as string").to_string();
- let path = try!(
- Nullable::from_json(x.find("path").unwrap_or(&Json::Null),
- |x| {
- Ok((try_opt!(x.as_string(),
- ErrorStatus::UnknownError,
- "Failed to interpret path as String")).to_string())
- }));
- let domain = try!(
- Nullable::from_json(x.find("domain").unwrap_or(&Json::Null),
- |x| {
- Ok((try_opt!(x.as_string(),
- ErrorStatus::UnknownError,
- "Failed to interpret domain as String")).to_string())
- }));
- let expiry = try!(
- Nullable::from_json(x.find("expiry").unwrap_or(&Json::Null),
- |x| {
- Ok(Date::new((try_opt!(
- x.as_u64(),
- ErrorStatus::UnknownError,
- "Failed to interpret expiry as u64"))))
- }));
- let secure = try_opt!(
- x.find("secure").map_or(Some(false), |x| x.as_boolean()),
- ErrorStatus::UnknownError,
- "Failed to interpret secure as boolean");
- let http_only = try_opt!(
- x.find("httpOnly").map_or(Some(false), |x| x.as_boolean()),
- ErrorStatus::UnknownError,
- "Failed to interpret httpOnly as boolean");
- Ok(Cookie::new(name, value, path, domain, expiry, secure, http_only))
- }).collect::<Result<Vec<_>, _>>()
- }
-
- pub fn error_from_string(&self, error_code: &str) -> ErrorStatus {
- match error_code {
- "element click intercepted" => ErrorStatus::ElementClickIntercepted,
- "element not interactable" | "element not visible" => ErrorStatus::ElementNotInteractable,
- "element not selectable" => ErrorStatus::ElementNotSelectable,
- "insecure certificate" => ErrorStatus::InsecureCertificate,
- "invalid argument" => ErrorStatus::InvalidArgument,
- "invalid cookie domain" => ErrorStatus::InvalidCookieDomain,
- "invalid coordinates" | "invalid element coordinates" => ErrorStatus::InvalidCoordinates,
- "invalid element state" => ErrorStatus::InvalidElementState,
- "invalid selector" => ErrorStatus::InvalidSelector,
- "invalid session id" => ErrorStatus::InvalidSessionId,
- "javascript error" => ErrorStatus::JavascriptError,
- "move target out of bounds" => ErrorStatus::MoveTargetOutOfBounds,
- "no such alert" => ErrorStatus::NoSuchAlert,
- "no such element" => ErrorStatus::NoSuchElement,
- "no such frame" => ErrorStatus::NoSuchFrame,
- "no such window" => ErrorStatus::NoSuchWindow,
- "script timeout" => ErrorStatus::ScriptTimeout,
- "session not created" => ErrorStatus::SessionNotCreated,
- "stale element reference" => ErrorStatus::StaleElementReference,
- "timeout" => ErrorStatus::Timeout,
- "unable to capture screen" => ErrorStatus::UnableToCaptureScreen,
- "unable to set cookie" => ErrorStatus::UnableToSetCookie,
- "unexpected alert open" => ErrorStatus::UnexpectedAlertOpen,
- "unknown command" => ErrorStatus::UnknownCommand,
- "unknown error" => ErrorStatus::UnknownError,
- "unsupported operation" => ErrorStatus::UnsupportedOperation,
- _ => ErrorStatus::UnknownError,
- }
- }
-}
-
-pub struct MarionetteCommand {
- pub id: u64,
- pub name: String,
- pub params: BTreeMap<String, Json>
-}
-
-impl MarionetteCommand {
- fn new(id: u64, name: String, params: BTreeMap<String, Json>) -> MarionetteCommand {
- MarionetteCommand {
- id: id,
- name: name,
- params: params,
- }
- }
-
- fn from_webdriver_message(id: u64,
- capabilities: Option<BTreeMap<String, Json>>,
- msg: &WebDriverMessage<GeckoExtensionRoute>)
- -> WebDriverResult<MarionetteCommand> {
- let (opt_name, opt_parameters) = match msg.command {
- NewSession(_) => {
- let caps = capabilities.expect("Tried to create new session without processing capabilities");
-
- let mut data = BTreeMap::new();
- for (k, v) in caps.iter() {
- data.insert(k.to_string(), v.to_json());
- }
-
- // duplicate in capabilities.desiredCapabilities for legacy compat
- let mut legacy_caps = BTreeMap::new();
- legacy_caps.insert("desiredCapabilities".to_string(), caps.to_json());
- data.insert("capabilities".to_string(), legacy_caps.to_json());
-
- (Some("newSession"), Some(Ok(data)))
- },
- DeleteSession => {
- let mut body = BTreeMap::new();
- body.insert("flags".to_owned(), vec!["eForceQuit".to_json()].to_json());
- (Some("quitApplication"), Some(Ok(body)))
- },
- Status => panic!("Got status command that should already have been handled"),
- Get(ref x) => (Some("get"), Some(x.to_marionette())),
- GetCurrentUrl => (Some("getCurrentUrl"), None),
- GoBack => (Some("goBack"), None),
- GoForward => (Some("goForward"), None),
- Refresh => (Some("refresh"), None),
- GetTitle => (Some("getTitle"), None),
- GetPageSource => (Some("getPageSource"), None),
- GetWindowHandle => (Some("getWindowHandle"), None),
- GetWindowHandles => (Some("getWindowHandles"), None),
- CloseWindow => (Some("close"), None),
- GetTimeouts => (Some("getTimeouts"), None),
- SetTimeouts(ref x) => (Some("timeouts"), Some(x.to_marionette())),
- SetWindowRect(ref x) => (Some("setWindowRect"), Some(x.to_marionette())),
- GetWindowRect => (Some("getWindowRect"), None),
- MaximizeWindow => (Some("maximizeWindow"), None),
- SwitchToWindow(ref x) => (Some("switchToWindow"), Some(x.to_marionette())),
- SwitchToFrame(ref x) => (Some("switchToFrame"), Some(x.to_marionette())),
- SwitchToParentFrame => (Some("switchToParentFrame"), None),
- FindElement(ref x) => (Some("findElement"), Some(x.to_marionette())),
- FindElements(ref x) => (Some("findElements"), Some(x.to_marionette())),
- FindElementElement(ref e, ref x) => {
- let mut data = try!(x.to_marionette());
- data.insert("element".to_string(), e.id.to_json());
- (Some("findElement"), Some(Ok(data)))
- },
- FindElementElements(ref e, ref x) => {
- let mut data = try!(x.to_marionette());
- data.insert("element".to_string(), e.id.to_json());
- (Some("findElements"), Some(Ok(data)))
- },
- GetActiveElement => (Some("getActiveElement"), None),
- IsDisplayed(ref x) => (Some("isElementDisplayed"), Some(x.to_marionette())),
- IsSelected(ref x) => (Some("isElementSelected"), Some(x.to_marionette())),
- GetElementAttribute(ref e, ref x) => {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), e.id.to_json());
- data.insert("name".to_string(), x.to_json());
- (Some("getElementAttribute"), Some(Ok(data)))
- },
- GetElementProperty(ref e, ref x) => {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), e.id.to_json());
- data.insert("name".to_string(), x.to_json());
- (Some("getElementProperty"), Some(Ok(data)))
- },
- GetCSSValue(ref e, ref x) => {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), e.id.to_json());
- data.insert("propertyName".to_string(), x.to_json());
- (Some("getElementValueOfCssProperty"), Some(Ok(data)))
- },
- GetElementText(ref x) => (Some("getElementText"), Some(x.to_marionette())),
- GetElementTagName(ref x) => (Some("getElementTagName"), Some(x.to_marionette())),
- GetElementRect(ref x) => (Some("getElementRect"), Some(x.to_marionette())),
- IsEnabled(ref x) => (Some("isElementEnabled"), Some(x.to_marionette())),
- PerformActions(ref x) => (Some("performActions"), Some(x.to_marionette())),
- ReleaseActions => (Some("releaseActions"), None),
- ElementClick(ref x) => (Some("clickElement"), Some(x.to_marionette())),
- ElementTap(ref x) => (Some("singleTap"), Some(x.to_marionette())),
- ElementClear(ref x) => (Some("clearElement"), Some(x.to_marionette())),
- ElementSendKeys(ref e, ref x) => {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), e.id.to_json());
- data.insert("text".to_string(), x.text.to_json());
- data.insert("value".to_string(),
- x.text
- .chars()
- .map(|x| x.to_string())
- .collect::<Vec<String>>()
- .to_json());
- (Some("sendKeysToElement"), Some(Ok(data)))
- },
- ExecuteScript(ref x) => (Some("executeScript"), Some(x.to_marionette())),
- ExecuteAsyncScript(ref x) => (Some("executeAsyncScript"), Some(x.to_marionette())),
- GetCookies | GetNamedCookie(_) => (Some("getCookies"), None),
- DeleteCookies => (Some("deleteAllCookies"), None),
- DeleteCookie(ref x) => {
- let mut data = BTreeMap::new();
- data.insert("name".to_string(), x.to_json());
- (Some("deleteCookie"), Some(Ok(data)))
- },
- AddCookie(ref x) => (Some("addCookie"), Some(x.to_marionette())),
- DismissAlert => (Some("dismissDialog"), None),
- AcceptAlert => (Some("acceptDialog"), None),
- GetAlertText => (Some("getTextFromDialog"), None),
- SendAlertText(ref x) => {
- let mut data = BTreeMap::new();
- data.insert("text".to_string(), x.text.to_json());
- data.insert("value".to_string(),
- x.text
- .chars()
- .map(|x| x.to_string())
- .collect::<Vec<String>>()
- .to_json());
- (Some("sendKeysToDialog"), Some(Ok(data)))
- },
- TakeScreenshot => {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), Json::Null);
- data.insert("highlights".to_string(), Json::Array(vec![]));
- data.insert("full".to_string(), Json::Boolean(false));
- (Some("takeScreenshot"), Some(Ok(data)))
- },
- TakeElementScreenshot(ref e) => {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), e.id.to_json());
- data.insert("highlights".to_string(), Json::Array(vec![]));
- data.insert("full".to_string(), Json::Boolean(false));
- (Some("takeScreenshot"), Some(Ok(data)))
- },
- Extension(ref extension) => {
- match extension {
- &GeckoExtensionCommand::GetContext => (Some("getContext"), None),
- &GeckoExtensionCommand::SetContext(ref x) => {
- (Some("setContext"), Some(x.to_marionette()))
- },
- &GeckoExtensionCommand::XblAnonymousChildren(ref e) => {
- let mut data = BTreeMap::new();
- data.insert("using".to_owned(), "anon".to_json());
- data.insert("value".to_owned(), Json::Null);
- data.insert("element".to_string(), e.id.to_json());
- (Some("findElements"), Some(Ok(data)))
- },
- &GeckoExtensionCommand::XblAnonymousByAttribute(ref e, ref x) => {
- let mut data = try!(x.to_marionette());
- data.insert("element".to_string(), e.id.to_json());
- (Some("findElement"), Some(Ok(data)))
- },
- &GeckoExtensionCommand::InstallAddon(ref x) => {
- (Some("addon:install"), Some(x.to_marionette()))
- },
- &GeckoExtensionCommand::UninstallAddon(ref x) => {
- (Some("addon:uninstall"), Some(x.to_marionette()))
- }
- }
- }
- };
-
- let name = try_opt!(opt_name,
- ErrorStatus::UnsupportedOperation,
- "Operation not supported");
- let parameters = try!(opt_parameters.unwrap_or(Ok(BTreeMap::new())));
-
- Ok(MarionetteCommand::new(id, name.into(), parameters))
- }
-}
-
-impl ToJson for MarionetteCommand {
- fn to_json(&self) -> Json {
- Json::Array(vec![Json::U64(0), self.id.to_json(), self.name.to_json(),
- self.params.to_json()])
- }
-}
-
-pub struct MarionetteResponse {
- pub id: u64,
- pub error: Option<MarionetteError>,
- pub result: Json,
-}
-
-impl MarionetteResponse {
- fn from_json(data: &Json) -> WebDriverResult<MarionetteResponse> {
- let data_array = try_opt!(data.as_array(),
- ErrorStatus::UnknownError,
- "Expected a json array");
-
- if data_array.len() != 4 {
- return Err(WebDriverError::new(
- ErrorStatus::UnknownError,
- "Expected an array of length 4"));
- }
-
- if data_array[0].as_u64() != Some(1) {
- return Err(WebDriverError::new(ErrorStatus::UnknownError,
- "Expected 1 in first element of response"));
- };
- let id = try_opt!(data[1].as_u64(),
- ErrorStatus::UnknownError,
- "Expected an integer id");
- let error = if data[2].is_object() {
- Some(try!(MarionetteError::from_json(&data[2])))
- } else if data[2].is_null() {
- None
- } else {
- return Err(WebDriverError::new(ErrorStatus::UnknownError,
- "Expected object or null error"));
- };
-
- let result = if data[3].is_null() || data[3].is_object() || data[3].is_array() {
- data[3].clone()
- } else {
- return Err(WebDriverError::new(ErrorStatus::UnknownError,
- "Expected object params"));
- };
-
- Ok(MarionetteResponse {id: id,
- error: error,
- result: result})
- }
-}
-
-impl ToJson for MarionetteResponse {
- fn to_json(&self) -> Json {
- Json::Array(vec![Json::U64(1), self.id.to_json(), self.error.to_json(),
- self.result.clone()])
- }
-}
-
-#[derive(RustcEncodable, RustcDecodable)]
-pub struct MarionetteError {
- pub status: String,
- pub message: String,
- pub stacktrace: Option<String>
-}
-
-impl MarionetteError {
- fn new(status: String, message: String, stacktrace: Option<String>) -> MarionetteError {
- MarionetteError {
- status: status,
- message: message,
- stacktrace: stacktrace
- }
- }
-
- fn from_json(data: &Json) -> WebDriverResult<MarionetteError> {
- if !data.is_object() {
- return Err(WebDriverError::new(ErrorStatus::UnknownError,
- "Expected an error object"));
- }
- let status = try_opt!(
- try_opt!(data.find("error"),
- ErrorStatus::UnknownError,
- "Error value has no status").as_string(),
- ErrorStatus::UnknownError,
- "Error status was not a string").into();
-
- let message = try_opt!(
- try_opt!(data.find("message"),
- ErrorStatus::UnknownError,
- "Error value has no message").as_string(),
- ErrorStatus::UnknownError,
- "Error message was not a string").into();
-
- let stacktrace = match data.find("stacktrace") {
- None | Some(&Json::Null) => None,
- Some(x) => Some(try_opt!(x.as_string(),
- ErrorStatus::UnknownError,
- "Error message was not a string").into()),
- };
- Ok(MarionetteError::new(status, message, stacktrace))
- }
-}
-
-impl ToJson for MarionetteError {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("status".into(), self.status.to_json());
- data.insert("message".into(), self.message.to_json());
- data.insert("stacktrace".into(), self.stacktrace.to_json());
- Json::Object(data)
- }
-}
-
-fn get_free_port() -> IoResult<u16> {
- TcpListener::bind(&("localhost", 0))
- .and_then(|stream| stream.local_addr())
- .map(|x| x.port())
-}
-
-pub struct MarionetteConnection {
- port: u16,
- stream: Option<TcpStream>,
- pub session: MarionetteSession
-}
-
-impl MarionetteConnection {
- pub fn new(port: u16, session_id: Option<String>) -> MarionetteConnection {
- MarionetteConnection {
- port: port,
- stream: None,
- session: MarionetteSession::new(session_id)
- }
- }
-
- pub fn connect(&mut self) -> WebDriverResult<()> {
- let timeout = 60 * 1000; // ms
- let poll_interval = 100; // ms
- let poll_attempts = timeout / poll_interval;
- let mut poll_attempt = 0;
-
- loop {
- match TcpStream::connect(&(DEFAULT_HOST, self.port)) {
- Ok(stream) => {
- self.stream = Some(stream);
- break
- },
- Err(e) => {
- trace!(" connection attempt {}/{}", poll_attempt, poll_attempts);
- if poll_attempt <= poll_attempts {
- poll_attempt += 1;
- sleep(Duration::from_millis(poll_interval));
- } else {
- return Err(WebDriverError::new(
- ErrorStatus::UnknownError, e.description().to_owned()));
- }
- }
- }
- };
-
- debug!("Connected to Marionette on {}:{}", DEFAULT_HOST, self.port);
-
- try!(self.handshake());
- Ok(())
- }
-
- fn handshake(&mut self) -> WebDriverResult<()> {
- let resp = try!(self.read_resp());
- let handshake_data = try!(Json::from_str(&*resp));
-
- let data = try_opt!(handshake_data.as_object(),
- ErrorStatus::UnknownError,
- "Expected a json object in handshake");
-
- self.session.protocol = Some(try_opt!(data.get("marionetteProtocol"),
- ErrorStatus::UnknownError,
- "Missing 'marionetteProtocol' field in handshake").to_string());
-
- self.session.application_type = Some(try_opt!(data.get("applicationType"),
- ErrorStatus::UnknownError,
- "Missing 'applicationType' field in handshake").to_string());
-
- if self.session.protocol != Some("3".into()) {
- return Err(WebDriverError::new(
- ErrorStatus::UnknownError,
- format!("Unsupported Marionette protocol version {}, required 3",
- self.session.protocol.as_ref().unwrap_or(&"<unknown>".into()))));
- }
-
- Ok(())
- }
-
- pub fn close(&self) {
- }
-
- fn encode_msg(&self, msg:Json) -> String {
- let data = json::encode(&msg).unwrap();
- format!("{}:{}", data.len(), data)
- }
-
- pub fn send_command(&mut self,
- capabilities: Option<BTreeMap<String, Json>>,
- msg: &WebDriverMessage<GeckoExtensionRoute>)
- -> WebDriverResult<WebDriverResponse> {
- let id = self.session.next_command_id();
- let command = try!(MarionetteCommand::from_webdriver_message(id, capabilities, msg));
-
- let resp_data = try!(self.send(command.to_json()));
- let json_data: Json = try!(Json::from_str(&*resp_data));
-
- self.session.response(msg, try!(MarionetteResponse::from_json(&json_data)))
- }
-
- fn send(&mut self, msg: Json) -> WebDriverResult<String> {
- let data = self.encode_msg(msg);
- trace!("-> {}", data);
-
- match self.stream {
- Some(ref mut stream) => {
- if stream.write(&*data.as_bytes()).is_err() {
- let mut err = WebDriverError::new(ErrorStatus::UnknownError,
- "Failed to write response to stream");
- err.delete_session = true;
- return Err(err);
- }
- }
- None => {
- let mut err = WebDriverError::new(ErrorStatus::UnknownError,
- "Tried to write before opening stream");
- err.delete_session = true;
- return Err(err);
- }
- }
- match self.read_resp() {
- Ok(resp) => Ok(resp),
- Err(_) => {
- let mut err = WebDriverError::new(ErrorStatus::UnknownError,
- "Failed to decode response from marionette");
- err.delete_session = true;
- Err(err)
- }
- }
- }
-
- fn read_resp(&mut self) -> IoResult<String> {
- let mut bytes = 0usize;
-
- // TODO(jgraham): Check before we unwrap?
- let mut stream = self.stream.as_mut().unwrap();
- loop {
- let mut buf = &mut [0 as u8];
- let num_read = try!(stream.read(buf));
- let byte = match num_read {
- 0 => {
- return Err(IoError::new(ErrorKind::Other,
- "EOF reading marionette message"))
- },
- 1 => buf[0] as char,
- _ => panic!("Expected one byte got more")
- };
- match byte {
- '0'...'9' => {
- bytes = bytes * 10;
- bytes += byte as usize - '0' as usize;
- },
- ':' => {
- break
- }
- _ => {}
- }
- }
-
- let mut buf = &mut [0 as u8; 8192];
- let mut payload = Vec::with_capacity(bytes);
- let mut total_read = 0;
- while total_read < bytes {
- let num_read = try!(stream.read(buf));
- if num_read == 0 {
- return Err(IoError::new(ErrorKind::Other,
- "EOF reading marionette message"))
- }
- total_read += num_read;
- for x in &buf[..num_read] {
- payload.push(*x);
- }
- }
-
- // TODO(jgraham): Need to handle the error here
- let data = String::from_utf8(payload).unwrap();
- trace!("<- {}", data);
-
- Ok(data)
- }
-}
-
-trait ToMarionette {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>>;
-}
-
-impl ToMarionette for GetParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
- }
-}
-
-impl ToMarionette for TimeoutsParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
- }
-}
-
-impl ToMarionette for WindowRectParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
- }
-}
-
-impl ToMarionette for SwitchToWindowParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- data.insert("name".to_string(), self.handle.to_json());
- Ok(data)
- }
-}
-
-impl ToMarionette for LocatorParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- Ok(try_opt!(self.to_json().as_object(),
- ErrorStatus::UnknownError,
- "Expected an object")
- .clone())
- }
-}
-
-impl ToMarionette for SwitchToFrameParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- let key = match self.id {
- FrameId::Null => None,
- FrameId::Short(_) => Some("id"),
- FrameId::Element(_) => Some("element"),
- };
- if let Some(x) = key {
- data.insert(x.to_string(), self.id.to_json());
- }
- Ok(data)
- }
-}
-
-impl ToMarionette for JavascriptCommandParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = self.to_json().as_object().unwrap().clone();
- data.insert("newSandbox".to_string(), false.to_json());
- data.insert("specialPowers".to_string(), false.to_json());
- data.insert("scriptTimeout".to_string(), Json::Null);
- Ok(data)
- }
-}
-
-impl ToMarionette for ActionsParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- Ok(try_opt!(self.to_json().as_object(),
- ErrorStatus::UnknownError,
- "Expected an object")
- .clone())
- }
-}
-
-impl ToMarionette for GetNamedCookieParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- Ok(try_opt!(self.to_json().as_object(),
- ErrorStatus::UnknownError,
- "Expected an object")
- .clone())
- }
-}
-
-impl ToMarionette for AddCookieParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut cookie = BTreeMap::new();
- cookie.insert("name".to_string(), self.name.to_json());
- cookie.insert("value".to_string(), self.value.to_json());
- if self.path.is_value() {
- cookie.insert("path".to_string(), self.path.to_json());
- }
- if self.domain.is_value() {
- cookie.insert("domain".to_string(), self.domain.to_json());
- }
- if self.expiry.is_value() {
- cookie.insert("expiry".to_string(), self.expiry.to_json());
- }
- cookie.insert("secure".to_string(), self.secure.to_json());
- cookie.insert("httpOnly".to_string(), self.httpOnly.to_json());
- let mut data = BTreeMap::new();
- data.insert("cookie".to_string(), Json::Object(cookie));
- Ok(data)
- }
-}
-
-impl ToMarionette for TakeScreenshotParameters {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- let element = match self.element {
- Nullable::Null => Json::Null,
- Nullable::Value(ref x) => Json::Object(try!(x.to_marionette()))
- };
- data.insert("element".to_string(), element);
- Ok(data)
- }
-}
-
-impl ToMarionette for WebElement {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), self.id.to_json());
- Ok(data)
- }
-}
-
-impl<T: ToJson> ToMarionette for Nullable<T> {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- //Note this is a terrible hack. We don't want Nullable<T: ToJson+ToMarionette>
- //so in cases where ToJson != ToMarionette you have to deal with the Nullable
- //explicitly. This kind of suggests that the whole design is wrong.
- Ok(try_opt!(self.to_json().as_object(), ErrorStatus::UnknownError, "Expected an object").clone())
- }
-}
-
-impl ToMarionette for FrameId {
- fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
- let mut data = BTreeMap::new();
- match *self {
- FrameId::Short(x) => data.insert("id".to_string(), x.to_json()),
- FrameId::Element(ref x) => data.insert("element".to_string(),
- Json::Object(try!(x.to_marionette()))),
- FrameId::Null => None
- };
- Ok(data)
- }
-}
diff --git a/src/prefs.rs b/src/prefs.rs
deleted file mode 100644
index 3e82850..0000000
--- a/src/prefs.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-use mozprofile::preferences::Pref;
-
-lazy_static! {
- pub static ref DEFAULT: [(&'static str, Pref); 78] = [
- // Disable automatic downloading of new releases
- ("app.update.auto", Pref::new(false)),
-
- // Disable automatically upgrading Firefox
- ("app.update.enabled", Pref::new(false)),
-
- // Increase the APZ content response timeout in tests to 1
- // minute. This is to accommodate the fact that test environments
- // tends to be slower than production environments (with the
- // b2g emulator being the slowest of them all), resulting in the
- // production timeout value sometimes being exceeded and causing
- // false-positive test failures.
- //
- // (bug 1176798, bug 1177018, bug 1210465)
- ("apz.content_response_timeout", Pref::new(60000)),
-
- // Enable the dump function, which sends messages to the system
- // console
- ("browser.dom.window.dump.enabled", Pref::new(true)),
-
- // Indicate that the download panel has been shown once so
- // that whichever download test runs first does not show the popup
- // inconsistently
- ("browser.download.panel.shown", Pref::new(true)),
-
- // Implicitly accept license
- ("browser.EULA.override", Pref::new(true)),
-
- // use about:blank as new tab page
- ("browser.newtabpage.enabled", Pref::new(false)),
-
- // Assume the about:newtab pages intro panels have been shown
- // to not depend on which test runs first and happens to open
- // about:newtab
- ("browser.newtabpage.introShown", Pref::new(true)),
-
- // Never start the browser in offline mode
- ("browser.offline", Pref::new(false)),
-
- // Background thumbnails in particular cause grief, and disabling
- // thumbnails in general cannot hurt
- ("browser.pagethumbnails.capturing_disabled", Pref::new(true)),
-
- // Avoid performing Reader Mode intros during tests
- ("browser.reader.detectedFirstArticle", Pref::new(true)),
-
- // Disable safebrowsing components
- ("browser.safebrowsing.blockedURIs.enabled", Pref::new(false)),
- ("browser.safebrowsing.downloads.enabled", Pref::new(false)),
- ("browser.safebrowsing.enabled", Pref::new(false)),
- ("browser.safebrowsing.forbiddenURIs.enabled", Pref::new(false)),
- ("browser.safebrowsing.malware.enabled", Pref::new(false)),
- ("browser.safebrowsing.phishing.enabled", Pref::new(false)),
-
- // Disable updates to search engines
- ("browser.search.update", Pref::new(false)),
-
- // Do not restore the last open set of tabs if the browser crashed
- ("browser.sessionstore.resume_from_crash", Pref::new(false)),
-
- // Skip check for default browser on startup
- ("browser.shell.checkDefaultBrowser", Pref::new(false)),
-
- // Do not warn when quitting with multiple tabs
- ("browser.showQuitWarning", Pref::new(false)),
-
- // Disable Android snippets
- ("browser.snippets.enabled", Pref::new(false)),
- ("browser.snippets.syncPromo.enabled", Pref::new(false)),
- ("browser.snippets.firstrunHomepage.enabled", Pref::new(false)),
-
- // Do not redirect user when a milestone upgrade of Firefox
- // is detected
- ("browser.startup.homepage_override.mstone", Pref::new("ignore")),
-
- // Start with a blank page (about:blank)
- ("browser.startup.page", Pref::new(0)),
-
- // Disable tab animation
- ("browser.tabs.animate", Pref::new(false)),
-
- // Do not warn when quitting a window with multiple tabs
- ("browser.tabs.closeWindowWithLastTab", Pref::new(false)),
-
- // Do not allow background tabs to be zombified, otherwise for
- // tests that open additional tabs, the test harness tab itself
- // might get unloaded
- ("browser.tabs.disableBackgroundZombification", Pref::new(false)),
-
- // Do not warn on exit when multiple tabs are open
- ("browser.tabs.warnOnClose", Pref::new(false)),
-
- // Do not warn when closing all other open tabs
- ("browser.tabs.warnOnCloseOtherTabs", Pref::new(false)),
-
- // Do not warn when multiple tabs will be opened
- ("browser.tabs.warnOnOpen", Pref::new(false)),
-
- // Disable first run splash page on Windows 10
- ("browser.usedOnWindows10.introURL", Pref::new("")),
-
- // Disable the UI tour
- ("browser.uitour.enabled", Pref::new(false)),
-
- // Do not warn on quitting Firefox
- ("browser.warnOnQuit", Pref::new(false)),
-
- // Do not show datareporting policy notifications which can
- // interfere with tests
- ("datareporting.healthreport.about.reportUrl", Pref::new("http://%(server)s/dummy/abouthealthreport/")),
- ("datareporting.healthreport.documentServerURI", Pref::new("http://%(server)s/dummy/healthreport/")),
- ("datareporting.healthreport.logging.consoleEnabled", Pref::new(false)),
- ("datareporting.healthreport.service.enabled", Pref::new(false)),
- ("datareporting.healthreport.service.firstRun", Pref::new(false)),
- ("datareporting.healthreport.uploadEnabled", Pref::new(false)),
- ("datareporting.policy.dataSubmissionEnabled", Pref::new(false)),
- ("datareporting.policy.dataSubmissionPolicyAccepted", Pref::new(false)),
- ("datareporting.policy.dataSubmissionPolicyBypassNotification", Pref::new(true)),
-
- // Disable popup-blocker
- ("dom.disable_open_during_load", Pref::new(false)),
-
- // Disable the ProcessHangMonitor
- ("dom.ipc.reportProcessHangs", Pref::new(false)),
-
- // Disable slow script dialogues
- ("dom.max_chrome_script_run_time", Pref::new(0)),
- ("dom.max_script_run_time", Pref::new(0)),
-
- // Only load extensions from the application and user profile
- // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
- ("extensions.autoDisableScopes", Pref::new(0)),
- ("extensions.enabledScopes", Pref::new(5)),
-
- // don't block add-ons for e10s
- ("extensions.e10sBlocksEnabling", Pref::new(false)),
-
- // Disable metadata caching for installed add-ons by default
- ("extensions.getAddons.cache.enabled", Pref::new(false)),
-
- // Disable intalling any distribution extensions or add-ons
- ("extensions.installDistroAddons", Pref::new(false)),
- ("extensions.showMismatchUI", Pref::new(false)),
-
- // Turn off extension updates so they do not bother tests
- ("extensions.update.enabled", Pref::new(false)),
- ("extensions.update.notifyUser", Pref::new(false)),
-
- // Make sure opening about:addons will not hit the network
- ("extensions.webservice.discoverURL", Pref::new("http://%(server)s/dummy/discoveryURL")),
-
- // Allow the application to have focus even it runs in the
- // background
- ("focusmanager.testmode", Pref::new(true)),
-
- // Disable useragent updates
- ("general.useragent.updates.enabled", Pref::new(false)),
-
- // Always use network provider for geolocation tests so we bypass
- // the macOS dialog raised by the corelocation provider
- ("geo.provider.testing", Pref::new(true)),
-
- // Do not scan wi-fi
- ("geo.wifi.scan", Pref::new(false)),
-
- // No hang monitor
- ("hangmonitor.timeout", Pref::new(0)),
-
- // Show chrome errors and warnings in the error console
- ("javascript.options.showInConsole", Pref::new(true)),
-
- // Make sure the disk cache does not get auto disabled
- ("network.http.bypass-cachelock-threshold", Pref::new(200000)),
-
- // Do not prompt for temporary redirects
- ("network.http.prompt-temp-redirect", Pref::new(false)),
-
- // Disable speculative connections so they are not reported as
- // leaking when they are hanging around
- ("network.http.speculative-parallel-limit", Pref::new(0)),
-
- // Do not automatically switch between offline and online
- ("network.manage-offline-status", Pref::new(false)),
-
- // Make sure SNTP requests do not hit the network
- ("network.sntp.pools", Pref::new("%(server)s")),
-
- // Disable Flash. The plugin container it is run in is
- // causing problems when quitting Firefox from geckodriver,
- // c.f. https://github.com/mozilla/geckodriver/issues/225.
- ("plugin.state.flash", Pref::new(0)),
-
- // Local documents have access to all other local docments,
- // including directory listings.
- ("security.fileuri.strict_origin_policy", Pref::new(false)),
-
- // Tests don't wait for the notification button security delay
- ("security.notification_enable_delay", Pref::new(0)),
-
- // Ensure blocklist updates don't hit the network
- ("services.settings.server", Pref::new("http://%(server)s/dummy/blocklist/")),
-
- // Do not automatically fill sign-in forms with known usernames
- // and passwords
- ("signon.autofillForms", Pref::new(false)),
-
- // Disable password capture, so that tests that include forms
- // are not influenced by the presence of the persistent doorhanger
- // notification
- ("signon.rememberSignons", Pref::new(false)),
-
- // Disable first run pages
- ("startup.homepage_welcome_url", Pref::new("about:blank")),
- ("startup.homepage_welcome_url.additional", Pref::new("")),
-
- // Prevent starting into safe mode after application crashes
- ("toolkit.startup.max_resumed_crashes", Pref::new(-1)),
-
- // We want to collect telemetry, but we don't want to send in the results
- ("toolkit.telemetry.server", Pref::new("https://%(server)s/dummy/telemetry/")),
- ];
-}
diff --git a/src/tests/profile.zip b/src/tests/profile.zip
deleted file mode 100644
index 286b118..0000000
--- a/src/tests/profile.zip
+++ /dev/null
Binary files differ