Bug 1693004 - [geckodriver] Add support for the "webSocketUrl" capability. r=webdriver-reviewers,jgraham

The "webSocketUrl" capability offers an opt-in
mechanism for WebDriver HTTP implementations to
make use of WebDriver BiDi, the bi-directional
protocol based on a WebSocket connection.

If the used version of Firefox has support for
WebDriver BiDi enabled, and the capability is
set to "true", it will be returned as part of
the "New Session" capabilities and contains
the host and port of the WebSocket server.

Differential Revision: https://phabricator.services.mozilla.com/D116689
This commit is contained in:
Henrik Skupin 2021-06-09 18:15:11 +00:00
Родитель 1e01a194c0
Коммит b72f22094f
3 изменённых файлов: 99 добавлений и 11 удалений

Просмотреть файл

@ -229,6 +229,7 @@ fn set_prefs(
// Deprecated with geckodriver 0.30.0, but left for backward compatibility. // Deprecated with geckodriver 0.30.0, but left for backward compatibility.
prefs.insert("marionette.log.level", logging::max_level().into()); prefs.insert("marionette.log.level", logging::max_level().into());
prefs.insert("remote.log.level", logging::max_level().into());
prefs.write().map_err(|e| { prefs.write().map_err(|e| {
WebDriverError::new( WebDriverError::new(

Просмотреть файл

@ -154,6 +154,10 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
Ok(true) Ok(true)
} }
fn accept_proxy(&mut self, _: &Capabilities, _: &Capabilities) -> WebDriverResult<bool> {
Ok(true)
}
fn set_window_rect(&mut self, _: &Capabilities) -> WebDriverResult<bool> { fn set_window_rect(&mut self, _: &Capabilities) -> WebDriverResult<bool> {
Ok(true) Ok(true)
} }
@ -173,8 +177,10 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
Ok(true) Ok(true)
} }
fn accept_proxy(&mut self, _: &Capabilities, _: &Capabilities) -> WebDriverResult<bool> { fn web_socket_url(&mut self, caps: &Capabilities) -> WebDriverResult<bool> {
Ok(true) self.browser_version(caps)?
.map(|v| self.compare_browser_version(&v, ">=90"))
.unwrap_or(Ok(false))
} }
fn validate_custom(&mut self, name: &str, value: &Value) -> WebDriverResult<()> { fn validate_custom(&mut self, name: &str, value: &Value) -> WebDriverResult<()> {
@ -410,12 +416,7 @@ impl FirefoxOptions {
} }
if let Some(args) = rv.args.as_ref() { if let Some(args) = rv.args.as_ref() {
let os_args = parse_args( let os_args = parse_args(args.iter().map(OsString::from).collect::<Vec<_>>().iter());
args.iter()
.map(OsString::from)
.collect::<Vec<_>>()
.iter(),
);
if let Some(path) = get_arg_value(os_args.iter(), Arg::Profile) { if let Some(path) = get_arg_value(os_args.iter(), Arg::Profile) {
if rv.profile.is_some() { if rv.profile.is_some() {
return Err(WebDriverError::new( return Err(WebDriverError::new(
@ -462,6 +463,27 @@ impl FirefoxOptions {
} }
} }
if let Some(json) = matched.get("webSocketUrl") {
let use_web_socket = json.as_bool().ok_or_else(|| {
WebDriverError::new(
ErrorStatus::InvalidArgument,
"webSocketUrl is not a boolean",
)
})?;
if use_web_socket {
let mut remote_args = Vec::new();
remote_args.push("--remote-debugging-port".to_owned());
remote_args.push("0".to_owned());
if let Some(ref mut args) = rv.args {
args.append(&mut remote_args);
} else {
rv.args = Some(remote_args);
}
}
}
Ok(rv) Ok(rv)
} }
@ -844,6 +866,52 @@ mod tests {
assert_eq!(opts.prefs, vec![]); assert_eq!(opts.prefs, vec![]);
} }
#[test]
fn fx_options_from_capabilities_with_websocket_url_not_set() {
let mut caps = Capabilities::new();
let opts = FirefoxOptions::from_capabilities(None, AndroidStorageInput::Auto, &mut caps)
.expect("Valid Firefox options");
assert!(
opts.args.is_none(),
"CLI arguments for Firefox unexpectedly found"
);
}
#[test]
fn fx_options_from_capabilities_with_websocket_url_false() {
let mut caps = Capabilities::new();
caps.insert("webSocketUrl".into(), json!(false));
let opts = FirefoxOptions::from_capabilities(None, AndroidStorageInput::Auto, &mut caps)
.expect("Valid Firefox options");
assert!(
opts.args.is_none(),
"CLI arguments for Firefox unexpectedly found"
);
}
#[test]
fn fx_options_from_capabilities_with_websocket_url_true() {
let mut caps = Capabilities::new();
caps.insert("webSocketUrl".into(), json!(true));
let opts = FirefoxOptions::from_capabilities(None, AndroidStorageInput::Auto, &mut caps)
.expect("Valid Firefox options");
if let Some(args) = opts.args {
let mut iter = args.iter();
assert!(iter
.find(|&arg| arg == &"--remote-debugging-port".to_owned())
.is_some());
assert_eq!(iter.next(), Some(&"0".to_owned()));
} else {
assert!(false, "CLI arguments for Firefox not found");
}
}
#[test] #[test]
fn fx_options_from_capabilities_with_debugger_address_not_set() { fn fx_options_from_capabilities_with_debugger_address_not_set() {
let mut caps = Capabilities::new(); let mut caps = Capabilities::new();
@ -867,7 +935,7 @@ mod tests {
assert!( assert!(
opts.args.is_none(), opts.args.is_none(),
"CLI arguments for remote protocol unexpectedly found" "CLI arguments for Firefox unexpectedly found"
); );
} }
@ -886,7 +954,7 @@ mod tests {
.is_some()); .is_some());
assert_eq!(iter.next(), Some(&"0".to_owned())); assert_eq!(iter.next(), Some(&"0".to_owned()));
} else { } else {
assert!(false, "CLI arguments for remote protocol not found"); assert!(false, "CLI arguments for Firefox not found");
} }
assert!(opts assert!(opts

Просмотреть файл

@ -50,6 +50,9 @@ pub trait BrowserCapabilities {
/// Indicates that interactability checks will be applied to `<input type=file>`. /// Indicates that interactability checks will be applied to `<input type=file>`.
fn strict_file_interactability(&mut self, _: &Capabilities) -> WebDriverResult<bool>; fn strict_file_interactability(&mut self, _: &Capabilities) -> WebDriverResult<bool>;
/// Whether a WebSocket URL for the created session has to be returned
fn web_socket_url(&mut self, _: &Capabilities) -> WebDriverResult<bool>;
fn accept_proxy( fn accept_proxy(
&mut self, &mut self,
proxy_settings: &Map<String, Value>, proxy_settings: &Map<String, Value>,
@ -132,7 +135,8 @@ impl SpecNewSessionParameters {
match &**key { match &**key {
x @ "acceptInsecureCerts" x @ "acceptInsecureCerts"
| x @ "setWindowRect" | x @ "setWindowRect"
| x @ "strictFileInteractability" => { | x @ "strictFileInteractability"
| x @ "webSocketUrl" => {
if !value.is_boolean() { if !value.is_boolean() {
return Err(WebDriverError::new( return Err(WebDriverError::new(
ErrorStatus::InvalidArgument, ErrorStatus::InvalidArgument,
@ -169,6 +173,12 @@ impl SpecNewSessionParameters {
} }
} }
} }
// With a value of `false` the capability needs to be removed.
if let Some(Value::Bool(false)) = capabilities.get(&"webSocketUrl".to_string()) {
capabilities.remove(&"webSocketUrl".to_string());
}
Ok(capabilities) Ok(capabilities)
} }
@ -516,6 +526,15 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
return false; return false;
} }
} }
"webSocketUrl" => {
if value.as_bool().unwrap_or(false)
&& !browser_capabilities
.web_socket_url(merged)
.unwrap_or(false)
{
return false;
}
}
name => { name => {
if name.contains(':') { if name.contains(':') {
if !browser_capabilities if !browser_capabilities