зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1720676 - [remote] Remove Remote Agent's Rust implementation. r=webdriver-reviewers,jdescottes
By implementing the command line handling and the printing to stderr in the JS module of the Remote Agent, the Rust implementation is no longer necessary. This change allows the JS component to hold each detail about the Remote Agent's status. Also it doesn't require a full build anymore when changes are needed. Differential Revision: https://phabricator.services.mozilla.com/D121096
This commit is contained in:
Родитель
38d311bf45
Коммит
0fd845f828
|
@ -2018,7 +2018,6 @@ dependencies = [
|
|||
"processtools",
|
||||
"profiler_helper",
|
||||
"qcms",
|
||||
"remote",
|
||||
"rlbox_lucet_sandbox",
|
||||
"rsdparsa_capi",
|
||||
"rusqlite",
|
||||
|
@ -4190,19 +4189,6 @@ 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"
|
||||
|
|
|
@ -11,8 +11,6 @@ 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",
|
||||
|
@ -85,11 +83,7 @@ class CDP {
|
|||
|
||||
await this.targetList.watchForTargets();
|
||||
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"remote-listening",
|
||||
`DevTools listening on ${this.address}`
|
||||
);
|
||||
Cu.printStderr(`DevTools listening on ${this.address}\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@ const { Preferences } = ChromeUtils.import(
|
|||
);
|
||||
|
||||
// To fully test the Remote Agent's capabilities an instance of the interface
|
||||
// needs to be used. This refers to the Rust specific implementation.
|
||||
// also needs to be used.
|
||||
const remoteAgentInstance = Cc["@mozilla.org/remote/agent;1"].createInstance(
|
||||
Ci.nsIRemoteAgent
|
||||
);
|
||||
|
@ -49,28 +49,23 @@ add_agent_task(async function listening() {
|
|||
is(remoteAgentInstance.listening, true, "Agent is listening");
|
||||
});
|
||||
|
||||
add_agent_task(async function listen() {
|
||||
add_agent_task(async function remoteListeningNotification() {
|
||||
let active;
|
||||
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);
|
||||
boundURL = Services.io.newURI(data.split(prefix)[1]);
|
||||
}
|
||||
Services.obs.addObserver(observer, "remote-listening");
|
||||
|
||||
active = data;
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observer, "remote-listening");
|
||||
await RemoteAgent.listen("http://localhost:" + port);
|
||||
isnot(boundURL, undefined, "remote-listening observer notified");
|
||||
is(
|
||||
boundURL.port,
|
||||
port,
|
||||
`expected default port ${port}, got ${boundURL.port}`
|
||||
);
|
||||
is(active, "true", "remote-listening observer notified enabled state");
|
||||
|
||||
Services.obs.addObserver(observer, "remote-listening");
|
||||
await RemoteAgent.close();
|
||||
is(active, null, "remote-listening observer notified disabled state");
|
||||
});
|
||||
|
||||
// TODO(ato): https://bugzil.la/1590829
|
||||
|
|
|
@ -42,11 +42,9 @@ 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 (!remoteAgentStarted) {
|
||||
if (!RemoteAgent.listening) {
|
||||
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
|
||||
remoteAgentStarted = true;
|
||||
info("Remote agent started");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,27 +34,28 @@ 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.server = null;
|
||||
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`;
|
||||
|
||||
if ((activeProtocols & WEBDRIVER_BIDI_ACTIVE) === WEBDRIVER_BIDI_ACTIVE) {
|
||||
this.webDriverBiDi = new WebDriverBiDi(this);
|
||||
logger.debug("WebDriver BiDi enabled");
|
||||
} else {
|
||||
this.webDriverBiDi = null;
|
||||
}
|
||||
this._enabled = false;
|
||||
this._port = DEFAULT_PORT;
|
||||
this._server = null;
|
||||
|
||||
if ((activeProtocols & CDP_ACTIVE) === CDP_ACTIVE) {
|
||||
this.cdp = new CDP(this);
|
||||
logger.debug("CDP enabled");
|
||||
} else {
|
||||
this.cdp = null;
|
||||
}
|
||||
this._cdp = null;
|
||||
this._webDriverBiDi = null;
|
||||
}
|
||||
|
||||
get cdp() {
|
||||
return this._cdp;
|
||||
}
|
||||
|
||||
get debuggerAddress() {
|
||||
|
@ -65,6 +66,10 @@ 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.
|
||||
|
@ -85,7 +90,15 @@ class RemoteAgentClass {
|
|||
return this.server?.identity.primaryScheme;
|
||||
}
|
||||
|
||||
listen(url) {
|
||||
get server() {
|
||||
return this._server;
|
||||
}
|
||||
|
||||
get webDriverBiDi() {
|
||||
return this._webDriverBiDi;
|
||||
}
|
||||
|
||||
async listen(url) {
|
||||
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
||||
throw Components.Exception(
|
||||
"May only be instantiated in parent process",
|
||||
|
@ -94,7 +107,7 @@ class RemoteAgentClass {
|
|||
}
|
||||
|
||||
if (this.listening) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(url instanceof Ci.nsIURI)) {
|
||||
|
@ -114,15 +127,12 @@ class RemoteAgentClass {
|
|||
port = -1;
|
||||
}
|
||||
|
||||
this.server = new HttpServer();
|
||||
|
||||
return this.asyncListen(host, port);
|
||||
}
|
||||
|
||||
async asyncListen(host, port) {
|
||||
try {
|
||||
this._server = new HttpServer();
|
||||
this.server._start(port, host);
|
||||
|
||||
Services.obs.notifyObservers(null, "remote-listening", true);
|
||||
|
||||
await this.cdp?.start();
|
||||
await this.webDriverBiDi?.start();
|
||||
} catch (e) {
|
||||
|
@ -131,30 +141,134 @@ class RemoteAgentClass {
|
|||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
async close() {
|
||||
if (!this.listening) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Stop the CDP support before stopping the server.
|
||||
// Otherwise the HTTP server will fail to stop.
|
||||
this.cdp?.stop();
|
||||
this.webDriverBiDi?.stop();
|
||||
|
||||
if (this.listening) {
|
||||
return this.server.stop();
|
||||
}
|
||||
await this.server.stop();
|
||||
this._server = null;
|
||||
Services.obs.notifyObservers(null, "remote-listening");
|
||||
} catch (e) {
|
||||
// this function must never fail
|
||||
logger.error("unable to stop listener", e);
|
||||
} finally {
|
||||
this.server = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// XPCOM
|
||||
|
||||
get QueryInterface() {
|
||||
return ChromeUtils.generateQI(["nsIRemoteAgent"]);
|
||||
return ChromeUtils.generateQI([
|
||||
"nsICommandLineHandler",
|
||||
"nsIObserver",
|
||||
"nsIRemoteAgent",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,13 @@ Classes = [
|
|||
{
|
||||
"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",
|
||||
},
|
||||
{
|
||||
"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",
|
||||
},
|
||||
|
||||
# Marionette
|
||||
{
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
# 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",
|
||||
]
|
||||
|
||||
XPIDL_MODULE = "remote"
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
[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" }
|
|
@ -1,36 +0,0 @@
|
|||
/* 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();
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* 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
|
|
@ -1,7 +0,0 @@
|
|||
# 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"
|
|
@ -1,61 +0,0 @@
|
|||
// 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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
// 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())
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// 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};
|
|
@ -1,75 +0,0 @@
|
|||
// 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,8 +27,6 @@ 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 Agent’s 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
|
||||
|
|
|
@ -11,8 +11,6 @@ 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:
|
||||
|
@ -151,11 +149,7 @@ class WebDriverBiDi {
|
|||
new WebDriverNewSessionHandler(this)
|
||||
);
|
||||
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"remote-listening",
|
||||
`WebDriver BiDi listening on ${this.address}`
|
||||
);
|
||||
Cu.printStderr(`WebDriver BiDi listening on ${this.address}\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,7 +29,6 @@ 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,7 +29,6 @@ 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,9 +71,6 @@ 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,7 +46,6 @@ 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" }
|
||||
|
@ -99,7 +98,6 @@ 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,9 +80,6 @@ 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")]
|
||||
|
|
Загрузка…
Ссылка в новой задаче