Backed out 2 changesets (bug 1720676) for remote fails on browser_agent.js. CLOSED TREE

Backed out changeset 93a9c870d3d8 (bug 1720676)
Backed out changeset f915c04b9c35 (bug 1720676)
This commit is contained in:
Csoregi Natalia 2021-07-29 21:30:57 +03:00
Родитель b1bfbf5738
Коммит e8cf092efc
28 изменённых файлов: 610 добавлений и 194 удалений

14
Cargo.lock сгенерированный
Просмотреть файл

@ -2018,6 +2018,7 @@ dependencies = [
"processtools",
"profiler_helper",
"qcms",
"remote",
"rlbox_lucet_sandbox",
"rsdparsa_capi",
"rusqlite",
@ -4189,6 +4190,19 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "remote"
version = "0.1.0"
dependencies = [
"http",
"libc",
"log",
"nserror",
"nsstring",
"thiserror",
"xpcom",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"

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

@ -197,6 +197,8 @@
; WebDriver (Marionette, Remote Agent) remote protocols
#ifdef ENABLE_WEBDRIVER
@RESPATH@/components/marionette.manifest
@RESPATH@/components/marionette.js
@RESPATH@/chrome/remote@JAREXT@
@RESPATH@/chrome/remote.manifest
#endif

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

@ -208,6 +208,8 @@
; WebDriver (Marionette, Remote Agent) remote protocols
#ifdef ENABLE_WEBDRIVER
@BINPATH@/components/marionette.manifest
@BINPATH@/components/marionette.js
@BINPATH@/chrome/remote@JAREXT@
@BINPATH@/chrome/remote.manifest
#endif

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

@ -11,6 +11,8 @@ const { XPCOMUtils } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
JSONHandler: "chrome://remote/content/cdp/JSONHandler.jsm",
RecommendedPreferences:
"chrome://remote/content/shared/RecommendedPreferences.jsm",
@ -83,7 +85,11 @@ class CDP {
await this.targetList.watchForTargets();
Cu.printStderr(`DevTools listening on ${this.address}\n`);
Services.obs.notifyObservers(
null,
"remote-listening",
`DevTools listening on ${this.address}`
);
}
/**

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

@ -8,7 +8,7 @@ const { Preferences } = ChromeUtils.import(
);
// To fully test the Remote Agent's capabilities an instance of the interface
// also needs to be used.
// needs to be used. This refers to the Rust specific implementation.
const remoteAgentInstance = Cc["@mozilla.org/remote/agent;1"].createInstance(
Ci.nsIRemoteAgent
);
@ -49,23 +49,28 @@ add_agent_task(async function listening() {
is(remoteAgentInstance.listening, true, "Agent is listening");
});
add_agent_task(async function remoteListeningNotification() {
let active;
add_agent_task(async function listen() {
const port = getNonAtomicFreePort();
let boundURL;
function observer(subject, topic, data) {
const prefix = "DevTools listening on ";
if (!data.startsWith(prefix)) {
return;
}
Services.obs.removeObserver(observer, topic);
active = data;
boundURL = Services.io.newURI(data.split(prefix)[1]);
}
Services.obs.addObserver(observer, "remote-listening");
await RemoteAgent.listen("http://localhost:" + port);
is(active, "true", "remote-listening observer notified enabled state");
Services.obs.addObserver(observer, "remote-listening");
await RemoteAgent.close();
is(active, "false", "remote-listening observer notified disabled state");
isnot(boundURL, undefined, "remote-listening observer notified");
is(
boundURL.port,
port,
`expected default port ${port}, got ${boundURL.port}`
);
});
// TODO(ato): https://bugzil.la/1590829

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

@ -42,9 +42,11 @@ const add_plain_task = add_task.bind(this);
// Start RemoteAgent lazily and reuse it for all the tests in the suite.
// Starting and stopping RemoteAgent for every test would trigger race conditions
// in httpd.js. See Bug 1609162.
let remoteAgentStarted = false;
async function startRemoteAgent() {
if (!RemoteAgent.listening) {
if (!remoteAgentStarted) {
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
remoteAgentStarted = true;
info("Remote agent started");
}
}

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

@ -34,28 +34,27 @@ XPCOMUtils.defineLazyGetter(this, "activeProtocols", () => {
const WEBDRIVER_BIDI_ACTIVE = 0x1;
const CDP_ACTIVE = 0x2;
const DEFAULT_PORT = 9222;
// By default force local connections only
const LOOPBACKS = ["localhost", "127.0.0.1", "[::1]"];
const PREF_FORCE_LOCAL = "remote.force-local";
class RemoteAgentClass {
constructor() {
this.classID = Components.ID("{8f685a9d-8181-46d6-a71d-869289099c6d}");
this.helpInfo = ` --remote-debugging-port [<port>] Start the Firefox remote agent,
which is a low-level debugging interface based on the
CDP protocol. Defaults to listen on localhost:9222.\n`;
this.server = null;
this._enabled = false;
this._port = DEFAULT_PORT;
this._server = null;
if ((activeProtocols & WEBDRIVER_BIDI_ACTIVE) === WEBDRIVER_BIDI_ACTIVE) {
this.webDriverBiDi = new WebDriverBiDi(this);
logger.debug("WebDriver BiDi enabled");
} else {
this.webDriverBiDi = null;
}
this._cdp = null;
this._webDriverBiDi = null;
}
get cdp() {
return this._cdp;
if ((activeProtocols & CDP_ACTIVE) === CDP_ACTIVE) {
this.cdp = new CDP(this);
logger.debug("CDP enabled");
} else {
this.cdp = null;
}
}
get debuggerAddress() {
@ -66,10 +65,6 @@ class RemoteAgentClass {
return `${this.host}:${this.port}`;
}
get enabled() {
return this._enabled;
}
get host() {
// Bug 1675471: When using the nsIRemoteAgent interface the HTTPd server's
// primary identity ("this.server.identity.primaryHost") is lazily set.
@ -90,15 +85,7 @@ class RemoteAgentClass {
return this.server?.identity.primaryScheme;
}
get server() {
return this._server;
}
get webDriverBiDi() {
return this._webDriverBiDi;
}
async listen(url) {
listen(url) {
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
throw Components.Exception(
"May only be instantiated in parent process",
@ -107,7 +94,7 @@ class RemoteAgentClass {
}
if (this.listening) {
return;
return Promise.resolve();
}
if (!(url instanceof Ci.nsIURI)) {
@ -127,11 +114,14 @@ class RemoteAgentClass {
port = -1;
}
try {
this._server = new HttpServer();
this.server._start(port, host);
this.server = new HttpServer();
Services.obs.notifyObservers(null, "remote-listening", true);
return this.asyncListen(host, port);
}
async asyncListen(host, port) {
try {
this.server._start(port, host);
await this.cdp?.start();
await this.webDriverBiDi?.start();
@ -141,134 +131,30 @@ class RemoteAgentClass {
}
}
async close() {
if (!this.listening) {
return;
}
close() {
try {
// Stop the CDP support before stopping the server.
// Otherwise the HTTP server will fail to stop.
this.cdp?.stop();
this.webDriverBiDi?.stop();
await this.server.stop();
this._server = null;
Services.obs.notifyObservers(null, "remote-listening");
if (this.listening) {
return this.server.stop();
}
} catch (e) {
// this function must never fail
logger.error("unable to stop listener", e);
}
}
/**
* Handle the --remote-debugging-port command line argument.
*
* @param {nsICommandLine} cmdLine
* Instance of the command line interface.
*
* @return {boolean}
* Return `true` if the command line argument has been found.
*/
handleRemoteDebuggingPortFlag(cmdLine) {
let enabled = false;
try {
// Catch cases when the argument, and a port have been specified.
const port = cmdLine.handleFlagWithParam("remote-debugging-port", false);
if (port !== null) {
enabled = true;
// In case of an invalid port keep the default port
const parsed = Number(port);
if (!isNaN(parsed)) {
this._port = parsed;
}
}
} catch (e) {
// If no port has been given check for the existence of the argument.
enabled = cmdLine.handleFlag("remote-debugging-port", false);
} finally {
this.server = null;
}
return enabled;
}
async observe(subject, topic) {
if (this.enabled) {
logger.trace(`Received observer notification ${topic}`);
}
switch (topic) {
case "profile-after-change":
Services.obs.addObserver(this, "command-line-startup");
break;
case "command-line-startup":
Services.obs.removeObserver(this, topic);
this._enabled = this.handleRemoteDebuggingPortFlag(subject);
if (this.enabled) {
Services.obs.addObserver(this, "remote-startup-requested");
}
// Ideally we should only enable the Remote Agent when the command
// line argument has been specified. But to allow Browser Chrome tests
// to run the Remote Agent and the supported protocols also need to be
// initialized.
//
// With Bug 1717899 we will extend the lifetime of the Remote Agent to
// the whole Firefox session, which will be identical to Marionette. For
// now prevent logging if the component is not enabled during startup.
if (
(activeProtocols & WEBDRIVER_BIDI_ACTIVE) ===
WEBDRIVER_BIDI_ACTIVE
) {
this._webDriverBiDi = new WebDriverBiDi(this);
if (this.enabled) {
logger.debug("WebDriver BiDi enabled");
}
}
if ((activeProtocols & CDP_ACTIVE) === CDP_ACTIVE) {
this._cdp = new CDP(this);
if (this.enabled) {
logger.debug("CDP enabled");
}
}
break;
case "remote-startup-requested":
Services.obs.removeObserver(this, topic);
// Listen for application shutdown to also shutdown the Remote Agent
Services.obs.addObserver(this, "quit-application");
try {
let address = Services.io.newURI(`http://localhost:${this._port}`);
await this.listen(address);
} catch (e) {
throw Error(`Unable to start remote agent: ${e}`);
}
break;
case "quit-application":
Services.obs.removeObserver(this, "quit-application");
this.close();
break;
}
return Promise.resolve();
}
// XPCOM
get QueryInterface() {
return ChromeUtils.generateQI([
"nsICommandLineHandler",
"nsIObserver",
"nsIRemoteAgent",
]);
return ChromeUtils.generateQI(["nsIRemoteAgent"]);
}
}

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

@ -3,27 +3,17 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
Classes = [
# Remote Agent
{
"cid": "{8f685a9d-8181-46d6-a71d-869289099c6d}",
"contract_ids": ["@mozilla.org/remote/agent;1"],
"categories": {
"command-line-handler": "m-remote",
"profile-after-change": "RemoteAgent",
},
"jsm": "chrome://remote/content/components/RemoteAgent.jsm",
"constructor": "RemoteAgentFactory",
},
# Marionette
{
"cid": "{786a1369-dca5-4adc-8486-33d23c88010a}",
"contract_ids": ["@mozilla.org/remote/marionette;1"],
"categories": {
"command-line-handler": "m-marionette",
"profile-after-change": "Marionette",
},
"jsm": "chrome://remote/content/components/Marionette.jsm",
"constructor": "MarionetteFactory",
"cid": "{0d1bb02e-ac91-4904-b61d-97da83ebf6fb}",
"contract_ids": ["@mozilla.org/commandlinehandler/general-startup;1?type=remote"],
"categories": {"command-line-handler": "m-remote"},
"headers": ["RemoteAgentHandler.h"],
"constructor": "GetRemoteAgentHandler",
},
]

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

@ -4,14 +4,14 @@
"use strict";
var EXPORTED_SYMBOLS = ["Marionette", "MarionetteFactory"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
EnvironmentPrefs: "chrome://remote/content/marionette/prefs.js",
Log: "chrome://remote/content/shared/Log.jsm",
MarionettePrefs: "chrome://remote/content/marionette/prefs.js",
@ -69,9 +69,6 @@ class MarionetteParentProcess {
constructor() {
this.server = null;
this.classID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
this.helpInfo = " --marionette Enable remote control server.\n";
// holds reference to ChromeWindow
// used to run GFX sanity tests on Windows
this.gfxWindow = null;
@ -303,10 +300,6 @@ class MarionetteParentProcess {
}
class MarionetteContentProcess {
constructor() {
this.classID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
}
get running() {
let reply = Services.cpmm.sendSyncMessage("Marionette:IsRunning");
if (reply.length == 0) {
@ -321,14 +314,37 @@ class MarionetteContentProcess {
}
}
var Marionette;
if (isRemote) {
Marionette = new MarionetteContentProcess();
} else {
Marionette = new MarionetteParentProcess();
}
const MarionetteFactory = {
instance_: null,
// This is used by the XPCOM codepath which expects a constructor
const MarionetteFactory = function() {
return Marionette;
createInstance(outer, iid) {
if (outer) {
throw Components.Exception("", Cr.NS_ERROR_NO_AGGREGATION);
}
if (!this.instance_) {
if (isRemote) {
this.instance_ = new MarionetteContentProcess();
} else {
this.instance_ = new MarionetteParentProcess();
}
}
return this.instance_.QueryInterface(iid);
},
};
function Marionette() {}
Marionette.prototype = {
classDescription: "Marionette component",
classID: Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}"),
contractID: "@mozilla.org/remote/marionette;1",
/* eslint-disable-next-line camelcase */
_xpcom_factory: MarionetteFactory,
helpInfo: " --marionette Enable remote control server.\n",
};
this.NSGetFactory = ComponentUtils.generateNSGetFactory([Marionette]);

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

@ -0,0 +1,4 @@
component {786a1369-dca5-4adc-8486-33d23c88010a} marionette.js
contract @mozilla.org/remote/marionette;1 {786a1369-dca5-4adc-8486-33d23c88010a}
category command-line-handler b-marionette @mozilla.org/remote/marionette;1
category profile-after-change Marionette @mozilla.org/remote/marionette;1

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

@ -2,6 +2,15 @@
# 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/.
DIRS += [
"rust",
]
EXTRA_COMPONENTS += [
"marionette.js",
"marionette.manifest",
]
XPIDL_MODULE = "remote"
XPIDL_SOURCES += [
@ -11,7 +20,7 @@ XPIDL_SOURCES += [
XPCOM_MANIFESTS += ["components.conf"]
with Files("Marionette.jsm"):
with Files("marionette.*"):
BUG_COMPONENT = ("Testing", "Marionette")
with Files("nsIMarionette.idl"):
BUG_COMPONENT = ("Testing", "Marionette")

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

@ -0,0 +1,12 @@
[package]
name = "remote"
version = "0.1.0"
[dependencies]
http = "0.2"
libc = "0.2"
log = "0.4"
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
thiserror = "1"
xpcom = { path = "../../../xpcom/rust/xpcom" }

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

@ -0,0 +1,36 @@
/* 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/. */
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsICommandLineHandler.h"
#include "nsISupportsUtils.h"
#include "RemoteAgentHandler.h"
// anonymous namespace prevents outside C++ code
// from improperly accessing these implementation details
namespace {
extern "C" {
// implemented in Rust, see handler.rs
void new_remote_agent_handler(nsICommandLineHandler** result);
}
static mozilla::StaticRefPtr<nsICommandLineHandler> sHandler;
} // namespace
already_AddRefed<nsICommandLineHandler> GetRemoteAgentHandler() {
nsCOMPtr<nsICommandLineHandler> handler;
if (sHandler) {
handler = sHandler;
} else {
new_remote_agent_handler(getter_AddRefs(handler));
sHandler = handler;
mozilla::ClearOnShutdown(&sHandler);
}
return handler.forget();
}

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

@ -0,0 +1,12 @@
/* 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/. */
#ifndef mozilla_remote_startup_RemoteAgentHandler_h
#define mozilla_remote_startup_RemoteAgentHandler_h
#include "nsICommandLineHandler.h"
already_AddRefed<nsICommandLineHandler> GetRemoteAgentHandler();
#endif // mozilla_remote_startup_RemoteAgentHandler_h

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

@ -0,0 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += ["RemoteAgentHandler.h"]
UNIFIED_SOURCES += ["RemoteAgentHandler.cpp"]
FINAL_LIBRARY = "xul"

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

@ -0,0 +1,61 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::num;
use http;
use nserror::{
nsresult, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_INVALID_ARG, NS_ERROR_LAUNCHED_CHILD_PROCESS,
NS_ERROR_NOT_AVAILABLE,
};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RemoteAgentError {
#[error("expected address syntax [<host>]:<port>: {0}")]
AddressSpec(#[from] http::uri::InvalidUri),
#[error("may only be instantiated in parent process")]
ChildProcess,
#[error("invalid port: {0}")]
InvalidPort(#[from] num::ParseIntError),
#[error("listener restricted to loopback devices")]
LoopbackRestricted,
#[error("missing port number")]
MissingPort,
#[error("unavailable")]
Unavailable,
#[error("error result {0}")]
XpCom(#[source] nsresult),
}
impl From<RemoteAgentError> for nsresult {
fn from(err: RemoteAgentError) -> nsresult {
use RemoteAgentError::*;
match err {
AddressSpec(_) | InvalidPort(_) => NS_ERROR_INVALID_ARG,
ChildProcess => NS_ERROR_LAUNCHED_CHILD_PROCESS,
LoopbackRestricted => NS_ERROR_ILLEGAL_VALUE,
MissingPort => NS_ERROR_INVALID_ARG,
Unavailable => NS_ERROR_NOT_AVAILABLE,
XpCom(result) => result,
}
}
}
impl From<nsresult> for RemoteAgentError {
fn from(result: nsresult) -> Self {
use RemoteAgentError::*;
match result {
NS_ERROR_NOT_AVAILABLE => Unavailable,
NS_ERROR_LAUNCHED_CHILD_PROCESS => ChildProcess,
x => RemoteAgentError::XpCom(x),
}
}
}

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

@ -0,0 +1,242 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::cell::RefCell;
use std::ffi::{CStr, CString, NulError};
use std::slice;
use libc::c_char;
use log::*;
use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_INVALID_ARG, NS_OK};
use nsstring::{nsACString, nsCString, nsString};
use xpcom::interfaces::{nsICommandLine, nsICommandLineHandler, nsIObserverService, nsISupports};
use xpcom::{xpcom, xpcom_method, RefPtr};
use crate::{
RemoteAgent,
RemoteAgentError::{self, *},
RemoteAgentResult, DEFAULT_HOST, DEFAULT_PORT,
};
macro_rules! fatalln {
($($arg:tt)*) => ({
let p = prog().unwrap_or("gecko".to_string());
eprintln!("{}: {}", p, format_args!($($arg)*));
panic!();
})
}
#[no_mangle]
pub unsafe extern "C" fn new_remote_agent_handler(result: *mut *const nsICommandLineHandler) {
if let Ok(handler) = RemoteAgentHandler::new() {
RefPtr::new(handler.coerce::<nsICommandLineHandler>()).forget(&mut *result);
} else {
*result = std::ptr::null();
}
}
#[derive(xpcom)]
#[xpimplements(nsICommandLineHandler)]
#[xpimplements(nsIObserver)]
#[refcnt = "atomic"]
struct InitRemoteAgentHandler {
agent: RemoteAgent,
observer: RefPtr<nsIObserverService>,
address: RefCell<String>,
}
impl RemoteAgentHandler {
pub fn new() -> Result<RefPtr<Self>, RemoteAgentError> {
let agent = RemoteAgent::get()?;
let observer = xpcom::services::get_ObserverService().ok_or(Unavailable)?;
Ok(Self::allocate(InitRemoteAgentHandler {
agent,
observer,
address: RefCell::new(String::new()),
}))
}
xpcom_method!(handle => Handle(command_line: *const nsICommandLine));
fn handle(&self, command_line: &nsICommandLine) -> Result<(), nsresult> {
match self.handle_inner(&command_line) {
Ok(_) => Ok(()),
Err(err) => fatalln!("{}", err),
}
}
fn handle_inner(&self, command_line: &nsICommandLine) -> RemoteAgentResult<()> {
let flags = CommandLine::new(command_line);
let remote_debugging_port = if flags.present("remote-debugging-port") {
Some(flags.opt_u16("remote-debugging-port")?)
} else {
None
};
let addr = match remote_debugging_port {
Some(Some(port)) => format!("{}:{}", DEFAULT_HOST, port),
Some(None) => format!("{}:{}", DEFAULT_HOST, DEFAULT_PORT),
None => return Ok(()),
};
*self.address.borrow_mut() = addr.to_string();
// When remote-startup-requested fires, it takes care of
// asking the remote agent to listen for incoming connections.
// Because the remote agent starts asynchronously, we wait
// until we receive remote-listening before we declare to the
// world that we are ready to accept connections.
self.add_observer("remote-listening")?;
self.add_observer("remote-startup-requested")?;
Ok(())
}
fn add_observer(&self, topic: &str) -> RemoteAgentResult<()> {
let topic = CString::new(topic).unwrap();
unsafe {
self.observer
.AddObserver(self.coerce(), topic.as_ptr(), false)
}
.to_result()?;
Ok(())
}
xpcom_method!(help_info => GetHelpInfo() -> nsACString);
fn help_info(&self) -> Result<nsCString, nsresult> {
let help = format!(
r#" --remote-debugging-port [<port>] Start the Firefox remote agent,
which is a low-level debugging interface based on the CDP protocol.
Defaults to listen on {}:{}.
"#,
DEFAULT_HOST, DEFAULT_PORT
);
Ok(nsCString::from(help))
}
xpcom_method!(observe => Observe(_subject: *const nsISupports, topic: string, data: wstring));
fn observe(
&self,
_subject: *const nsISupports,
topic: string,
data: wstring,
) -> Result<(), nsresult> {
let topic = unsafe { CStr::from_ptr(topic) }.to_str().unwrap();
match topic {
"remote-startup-requested" => {
if let Err(err) = self.agent.listen(&self.address.borrow()) {
fatalln!("unable to start remote agent: {}", err);
}
}
"remote-listening" => {
let output = unsafe { wstring_to_cstring(data) }.map_err(|_| NS_ERROR_FAILURE)?;
eprintln!("{}", output.to_string_lossy());
}
s => warn!("unknown system notification: {}", s),
}
Ok(())
}
}
// Rust wrapper for nsICommandLine.
struct CommandLine<'a> {
inner: &'a nsICommandLine,
}
impl<'a> CommandLine<'a> {
const CASE_SENSITIVE: bool = true;
fn new(inner: &'a nsICommandLine) -> Self {
Self { inner }
}
fn position(&self, name: &str) -> i32 {
let flag = nsString::from(name);
let mut result: i32 = 0;
unsafe {
self.inner
.FindFlag(&*flag, Self::CASE_SENSITIVE, &mut result)
}
.to_result()
.map_err(|err| error!("FindFlag: {}", err))
.unwrap();
result
}
fn present(&self, name: &str) -> bool {
self.position(name) >= 0
}
// nsICommandLine.handleFlagWithParam has the following possible return values:
//
// - an AString value representing the argument value if it exists
// - NS_ERROR_INVALID_ARG if the flag was defined, but without a value
// - a null pointer if the flag was not defined
// - possibly any other NS exception
//
// This means we need to treat NS_ERROR_INVALID_ARG with special care
// because --remote-debugging-port can be used both with and without a value.
fn opt_str(&self, name: &str) -> RemoteAgentResult<Option<String>> {
if self.present(name) {
let flag = nsString::from(name);
let mut val = nsString::new();
let result = unsafe {
self.inner
.HandleFlagWithParam(&*flag, Self::CASE_SENSITIVE, &mut *val)
}
.to_result();
match result {
Ok(_) => Ok(Some(val.to_string())),
Err(NS_ERROR_INVALID_ARG) => Ok(None),
Err(err) => Err(RemoteAgentError::XpCom(err)),
}
} else {
Err(RemoteAgentError::XpCom(NS_ERROR_ILLEGAL_VALUE))
}
}
fn opt_u16(&self, name: &str) -> RemoteAgentResult<Option<u16>> {
Ok(if let Some(s) = self.opt_str(name)? {
Some(s.parse()?)
} else {
None
})
}
}
fn prog() -> Option<String> {
std::env::current_exe()
.ok()?
.file_name()?
.to_str()?
.to_owned()
.into()
}
// Arcane XPIDL types for raw character pointers
// to ASCII (7-bit) and UTF-16 strings, respectively.
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Guide/Internal_strings#IDL
#[allow(non_camel_case_types)]
type string = *const c_char;
#[allow(non_camel_case_types)]
type wstring = *const i16;
// Convert wstring to a CString (via nsCString's UTF-16 to UTF-8 conversion).
// But first, say three Hail Marys.
unsafe fn wstring_to_cstring(ws: wstring) -> Result<CString, NulError> {
let mut len: usize = 0;
while (*(ws.offset(len as isize))) != 0 {
len += 1;
}
let ss = slice::from_raw_parts(ws as *const u16, len);
let mut s = nsCString::new();
s.assign_utf16_to_utf8(ss);
CString::new(s.as_str_unchecked())
}

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

@ -0,0 +1,18 @@
// 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/.
extern crate http;
extern crate libc;
extern crate log;
extern crate nserror;
extern crate nsstring;
extern crate thiserror;
extern crate xpcom;
mod error;
mod handler;
mod remote_agent;
pub use crate::error::RemoteAgentError;
pub use crate::remote_agent::{RemoteAgent, RemoteAgentResult, DEFAULT_HOST, DEFAULT_PORT};

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

@ -0,0 +1,75 @@
// 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/.
//! Rust interface for the Gecko remote agent.
use std::str::FromStr;
use log::*;
use nserror::NS_ERROR_ILLEGAL_VALUE;
use nsstring::{nsAString, nsString};
use xpcom::interfaces::nsIRemoteAgent;
use xpcom::RefPtr;
use crate::error::RemoteAgentError::{self, *};
pub const DEFAULT_HOST: &'static str = "localhost";
pub const DEFAULT_PORT: u16 = 9222;
pub type RemoteAgentResult<T> = Result<T, RemoteAgentError>;
pub struct RemoteAgent {
inner: RefPtr<nsIRemoteAgent>,
}
impl RemoteAgent {
pub fn get() -> RemoteAgentResult<RemoteAgent> {
let inner = xpcom::services::get_RemoteAgent().ok_or(Unavailable)?;
Ok(RemoteAgent { inner })
}
pub fn listen(&self, spec: &str) -> RemoteAgentResult<()> {
let addr = http::uri::Authority::from_str(spec)?;
let host = if addr.host().is_empty() {
DEFAULT_HOST
} else {
addr.host()
}
.to_string();
let port = addr.port_u16().unwrap_or(DEFAULT_PORT);
let url = nsString::from(&format!("http://{}:{}/", host, port));
unsafe { self.inner.Listen(&*url as &nsAString) }
.to_result()
.map_err(|err| {
// TODO(ato): https://bugzil.la/1600139
match err {
NS_ERROR_ILLEGAL_VALUE => RemoteAgentError::LoopbackRestricted,
nsresult => RemoteAgentError::from(nsresult),
}
})?;
Ok(())
}
pub fn listening(&self) -> RemoteAgentResult<bool> {
let mut listening = false;
unsafe { self.inner.GetListening(&mut listening) }.to_result()?;
Ok(listening)
}
pub fn close(&self) -> RemoteAgentResult<()> {
unsafe { self.inner.Close() }.to_result()?;
Ok(())
}
}
impl Drop for RemoteAgent {
fn drop(&mut self) {
// it should always be safe to call nsIRemoteAgent.close()
if let Err(e) = self.close() {
error!("unable to close remote agent listener: {}", e);
}
}
}

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

@ -27,6 +27,8 @@ Component files include the likes of components.conf,
RemoteAgent.manifest, moz.build files, and jar.mn.
All the JS modules (files ending with `.jsm`) are symlinked into
the build and can be changed without rebuilding.
The Remote Agents startup code found under remote/components/rust/
is written in Rust and requires rebuilds when changed.
You may also opt out of building all the WebDriver specific components
([Marionette], and the Remote Agent) by setting the following flag in

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

@ -4,7 +4,6 @@
remote.jar:
% content remote %content/
content/components/Marionette.jsm (components/Marionette.jsm)
content/components/RemoteAgent.jsm (components/RemoteAgent.jsm)
# transport layer (http / websocket)

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

@ -168,7 +168,7 @@ The practical details of working on the Marionette code is outlined
in [Contributing.md], but generally you do not have to re-build
Firefox when changing code. Any change to remote/marionette/*.js
will be picked up on restarting Firefox. The only notable exception
is remote/components/Marionette.jsm, which does require
is remote/components/marionette.js, which does require
a re-build.
[XPCOM]: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM

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

@ -11,6 +11,8 @@ const { XPCOMUtils } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
error: "chrome://remote/content/shared/webdriver/Errors.jsm",
Log: "chrome://remote/content/shared/Log.jsm",
WebDriverNewSessionHandler:
@ -149,7 +151,11 @@ class WebDriverBiDi {
new WebDriverNewSessionHandler(this)
);
Cu.printStderr(`WebDriver BiDi listening on ${this.address}\n`);
Services.obs.notifyObservers(
null,
"remote-listening",
`WebDriver BiDi listening on ${this.address}`
);
}
/**

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

@ -29,6 +29,7 @@ libfuzzer = ["gkrust-shared/libfuzzer", "gecko-fuzz-targets"]
webrtc = ["gkrust-shared/webrtc"]
wasm_library_sandboxing = ["gkrust-shared/wasm_library_sandboxing"]
webgpu = ["gkrust-shared/webgpu"]
remote_agent = ["gkrust-shared/remote"]
glean_disable_upload = ["gkrust-shared/glean_disable_upload"]
glean_with_gecko = ["gkrust-shared/glean_with_gecko"]
with_dbus = ["gkrust-shared/with_dbus"]

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

@ -29,6 +29,7 @@ libfuzzer = ["gkrust-shared/libfuzzer"]
webrtc = ["gkrust-shared/webrtc"]
wasm_library_sandboxing = ["gkrust-shared/wasm_library_sandboxing"]
webgpu = ["gkrust-shared/webgpu"]
remote_agent = ["gkrust-shared/remote"]
glean_disable_upload = ["gkrust-shared/glean_disable_upload"]
glean_with_gecko = ["gkrust-shared/glean_with_gecko"]
with_dbus = ["gkrust-shared/with_dbus"]

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

@ -71,6 +71,9 @@ if CONFIG['LIBFUZZER']:
if CONFIG['MOZ_WEBRTC']:
gkrust_features += ['webrtc']
if CONFIG['ENABLE_WEBDRIVER']:
gkrust_features += ['remote_agent']
# We need to tell Glean it is being built with Gecko.
gkrust_features += ['glean_with_gecko']

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

@ -46,6 +46,7 @@ neqo_glue = { path = "../../../../netwerk/socket/neqo_glue" }
rlbox_lucet_sandbox = { version = "0.1.0", optional = true }
wgpu_bindings = { path = "../../../../gfx/wgpu_bindings", optional = true }
mapped_hyph = { git = "https://github.com/jfkthame/mapped_hyph.git", rev = "746743227485a83123784df0c53227ab466612ed" }
remote = { path = "../../../../remote/components/rust", optional = true }
fog_control = { path = "../../../components/glean" }
app_services_logger = { path = "../../../../services/common/app_services_logger" }
http_sfv = { path = "../../../../netwerk/base/http-sfv" }
@ -98,6 +99,7 @@ libfuzzer = []
webrtc = ["mdns_service"]
wasm_library_sandboxing = ["rlbox_lucet_sandbox"]
webgpu = ["wgpu_bindings"]
remote_agent = ["remote"]
glean_disable_upload = ["fog_control/disable_upload"]
glean_with_gecko = ["fog_control/with_gecko"]
oxidized_breakpad = ["rust_minidump_writer_linux"]

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

@ -80,6 +80,9 @@ extern crate l10nregistry;
#[cfg(not(target_os = "android"))]
extern crate viaduct;
#[cfg(feature = "remote")]
extern crate remote;
extern crate gecko_logger;
#[cfg(feature = "oxidized_breakpad")]