mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
bug 1543115: remote, toolkit: bootstrap from Rust; r=remote-protocol-reviewers,maja_zf
This bootstraps the remote agent from Rust so that we have access to write to stderr using the eprintln!() macro. There is a future intention to expand Rust usage in the remote agent by delegating CDP and WebDriver Bi-Di protocol schema validation to serde. The Rust port is faithful to the JS version in terms of functionality, and in some places improves on the original design by enforcing a strict division between flag handling code on one hand, and the remote agent server on the other. Differential Revision: https://phabricator.services.mozilla.com/D50289 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
4a8b5c26b8
commit
36f394af15
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -1460,6 +1460,7 @@ dependencies = [
|
||||
"nsstring 0.1.0",
|
||||
"prefs_parser 0.0.1",
|
||||
"profiler_helper 0.1.0",
|
||||
"remote 0.1.0",
|
||||
"rlbox_lucet_sandbox 0.1.0 (git+https://github.com/PLSysSec/rlbox_lucet_sandbox/?rev=997c648eb0eaeaaa7a00a9eee20431f750b4e190)",
|
||||
"rsdparsa_capi 0.1.0",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3052,6 +3053,19 @@ dependencies = [
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remote"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nserror 0.1.0",
|
||||
"nsstring 0.1.0",
|
||||
"xpcom 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.2"
|
||||
|
15
remote/Cargo.toml
Normal file
15
remote/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "remote"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
failure = { version = "0.1", default_features = false, features = ["derive"] }
|
||||
http = "0.1"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
nserror = { path = "../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../xpcom/rust/nsstring" }
|
||||
xpcom = { path = "../xpcom/rust/xpcom" }
|
@ -1,30 +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 strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["Observer"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
class Observer {
|
||||
static observe(type, observer) {
|
||||
Services.obs.addObserver(observer, type);
|
||||
}
|
||||
|
||||
static unobserve(type, observer) {
|
||||
Services.obs.removeObserver(observer, type);
|
||||
}
|
||||
|
||||
static once(type, observer = () => {}) {
|
||||
return new Promise(resolve => {
|
||||
const wrappedObserver = (first, ...rest) => {
|
||||
Observer.unobserve(type, wrappedObserver);
|
||||
observer.call(first, ...rest);
|
||||
resolve();
|
||||
};
|
||||
Observer.observe(type, wrappedObserver);
|
||||
});
|
||||
}
|
||||
}
|
@ -12,12 +12,9 @@ const { XPCOMUtils } = ChromeUtils.import(
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
FatalError: "chrome://remote/content/Error.jsm",
|
||||
HttpServer: "chrome://remote/content/server/HTTPD.jsm",
|
||||
JSONHandler: "chrome://remote/content/JSONHandler.jsm",
|
||||
Log: "chrome://remote/content/Log.jsm",
|
||||
NetUtil: "resource://gre/modules/NetUtil.jsm",
|
||||
Observer: "chrome://remote/content/Observer.jsm",
|
||||
Preferences: "resource://gre/modules/Preferences.jsm",
|
||||
RecommendedPreferences: "chrome://remote/content/RecommendedPreferences.jsm",
|
||||
Targets: "chrome://remote/content/targets/Targets.jsm",
|
||||
@ -27,8 +24,6 @@ XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
||||
const ENABLED = "remote.enabled";
|
||||
const FORCE_LOCAL = "remote.force-local";
|
||||
|
||||
const DEFAULT_HOST = "localhost";
|
||||
const DEFAULT_PORT = 9222;
|
||||
const LOOPBACKS = ["localhost", "127.0.0.1", "[::1]"];
|
||||
|
||||
class RemoteAgentClass {
|
||||
@ -90,7 +85,6 @@ class RemoteAgentClass {
|
||||
const mainTarget = this.targets.getMainProcessTarget();
|
||||
|
||||
this.server._start(port, host);
|
||||
dump(`DevTools listening on ${mainTarget.wsDebuggerURL}\n`);
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"remote-listening",
|
||||
@ -148,83 +142,10 @@ class RemoteAgentClass {
|
||||
return this.server.identity.primaryPort;
|
||||
}
|
||||
|
||||
// nsICommandLineHandler
|
||||
|
||||
async handle(cmdLine) {
|
||||
function flag(name) {
|
||||
const caseSensitive = true;
|
||||
try {
|
||||
return cmdLine.handleFlagWithParam(name, caseSensitive);
|
||||
} catch (e) {
|
||||
return cmdLine.handleFlag(name, caseSensitive);
|
||||
}
|
||||
}
|
||||
|
||||
const remoteDebugger = flag("remote-debugger");
|
||||
const remoteDebuggingPort = flag("remote-debugging-port");
|
||||
|
||||
if (remoteDebugger && remoteDebuggingPort) {
|
||||
log.fatal(
|
||||
"Conflicting flags --remote-debugger and --remote-debugging-port"
|
||||
);
|
||||
cmdLine.preventDefault = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!remoteDebugger && !remoteDebuggingPort) {
|
||||
return;
|
||||
}
|
||||
|
||||
let host, port;
|
||||
if (typeof remoteDebugger == "string") {
|
||||
[host, port] = remoteDebugger.split(":");
|
||||
} else if (typeof remoteDebuggingPort == "string") {
|
||||
port = remoteDebuggingPort;
|
||||
}
|
||||
|
||||
let addr;
|
||||
try {
|
||||
addr = NetUtil.newURI(
|
||||
`http://${host || DEFAULT_HOST}:${port || DEFAULT_PORT}/`
|
||||
);
|
||||
} catch (e) {
|
||||
log.fatal(
|
||||
`Expected address syntax [<host>]:<port>: ${remoteDebugger ||
|
||||
remoteDebuggingPort}`
|
||||
);
|
||||
cmdLine.preventDefault = true;
|
||||
return;
|
||||
}
|
||||
|
||||
await Observer.once("sessionstore-windows-restored");
|
||||
|
||||
try {
|
||||
this.listen(addr);
|
||||
} catch (e) {
|
||||
this.close();
|
||||
throw new FatalError(
|
||||
`Unable to start remote agent on ${addr.spec}: ${e.message}`,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get helpInfo() {
|
||||
return (
|
||||
" --remote-debugger [<host>][:<port>]\n" +
|
||||
" --remote-debugging-port <port> Start the Firefox remote agent, which is \n" +
|
||||
" a low-level debugging interface based on the CDP protocol.\n" +
|
||||
" Defaults to listen on localhost:9222.\n"
|
||||
);
|
||||
}
|
||||
|
||||
// XPCOM
|
||||
|
||||
get QueryInterface() {
|
||||
return ChromeUtils.generateQI([
|
||||
Ci.nsICommandLineHandler,
|
||||
Ci.nsIRemoteAgent,
|
||||
]);
|
||||
return ChromeUtils.generateQI([Ci.nsIRemoteAgent]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,19 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
Classes = [
|
||||
{
|
||||
'cid': '{8f685a9d-8181-46d6-a71d-869289099c6d}',
|
||||
'contract_ids': ['@mozilla.org/remote/agent'],
|
||||
'jsm': 'chrome://remote/content/RemoteAgent.jsm',
|
||||
'constructor': 'RemoteAgentFactory',
|
||||
'categories': {'command-line-handler': 'm-remote'},
|
||||
"cid": "{8f685a9d-8181-46d6-a71d-869289099c6d}",
|
||||
"contract_ids": ["@mozilla.org/remote/agent;1"],
|
||||
"jsm": "chrome://remote/content/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",
|
||||
},
|
||||
]
|
||||
|
60
remote/error.rs
Normal file
60
remote/error.rs
Normal file
@ -0,0 +1,60 @@
|
||||
// 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 failure::Fail;
|
||||
use http;
|
||||
use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_AVAILABLE};
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum RemoteAgentError {
|
||||
#[fail(display = "expected address syntax [<host>]:<port>: {}", _0)]
|
||||
AddressSpec(http::uri::InvalidUri),
|
||||
|
||||
#[fail(display = "conflicting flags --remote-debugger and --remote-debugging-port")]
|
||||
FlagConflict,
|
||||
|
||||
#[fail(display = "invalid port: {}", _0)]
|
||||
InvalidPort(num::ParseIntError),
|
||||
|
||||
#[fail(display = "missing port number")]
|
||||
MissingPort,
|
||||
|
||||
#[fail(display = "unavailable")]
|
||||
Unavailable,
|
||||
|
||||
#[fail(display = "error result {}", _0)]
|
||||
XpCom(nsresult),
|
||||
}
|
||||
|
||||
impl From<RemoteAgentError> for nsresult {
|
||||
fn from(err: RemoteAgentError) -> nsresult {
|
||||
use RemoteAgentError::*;
|
||||
match err {
|
||||
AddressSpec(_) | InvalidPort(_) => NS_ERROR_INVALID_ARG,
|
||||
MissingPort | FlagConflict => NS_ERROR_INVALID_ARG,
|
||||
Unavailable => NS_ERROR_NOT_AVAILABLE,
|
||||
XpCom(result) => result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<num::ParseIntError> for RemoteAgentError {
|
||||
fn from(err: num::ParseIntError) -> Self {
|
||||
RemoteAgentError::InvalidPort(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<http::uri::InvalidUri> for RemoteAgentError {
|
||||
fn from(err: http::uri::InvalidUri) -> Self {
|
||||
RemoteAgentError::AddressSpec(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nsresult> for RemoteAgentError {
|
||||
fn from(result: nsresult) -> Self {
|
||||
RemoteAgentError::XpCom(result)
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ remote.jar:
|
||||
content/Error.jsm (Error.jsm)
|
||||
content/JSONHandler.jsm (JSONHandler.jsm)
|
||||
content/Log.jsm (Log.jsm)
|
||||
content/Observer.jsm (Observer.jsm)
|
||||
content/Protocol.jsm (Protocol.jsm)
|
||||
content/RecommendedPreferences.jsm (RecommendedPreferences.jsm)
|
||||
content/Sync.jsm (Sync.jsm)
|
||||
|
18
remote/lib.rs
Normal file
18
remote/lib.rs
Normal file
@ -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 failure;
|
||||
extern crate http;
|
||||
extern crate libc;
|
||||
extern crate log;
|
||||
extern crate nserror;
|
||||
extern crate nsstring;
|
||||
extern crate xpcom;
|
||||
|
||||
mod error;
|
||||
mod remote_agent;
|
||||
mod startup;
|
||||
|
||||
pub use crate::error::RemoteAgentError;
|
||||
pub use crate::remote_agent::{RemoteAgent, RemoteAgentResult, DEFAULT_HOST, DEFAULT_PORT};
|
@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
"startup",
|
||||
"test",
|
||||
]
|
||||
|
||||
|
66
remote/remote_agent.rs
Normal file
66
remote/remote_agent.rs
Normal file
@ -0,0 +1,66 @@
|
||||
// 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 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()?;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
36
remote/startup/RemoteAgentHandler.cpp
Normal file
36
remote/startup/RemoteAgentHandler.cpp
Normal file
@ -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();
|
||||
}
|
12
remote/startup/RemoteAgentHandler.h
Normal file
12
remote/startup/RemoteAgentHandler.h
Normal file
@ -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
|
253
remote/startup/handler.rs
Normal file
253
remote/startup/handler.rs
Normal file
@ -0,0 +1,253 @@
|
||||
// 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) {
|
||||
let handler: RefPtr<RemoteAgentHandler> = RemoteAgentHandler::new().unwrap();
|
||||
RefPtr::new(handler.coerce::<nsICommandLineHandler>()).forget(&mut *result);
|
||||
}
|
||||
|
||||
#[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_debugger = if flags.present("remote-debugger") {
|
||||
Some(flags.opt_str("remote-debugger")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let remote_debugging_port = if flags.present("remote-debugging-port") {
|
||||
Some(flags.opt_u16("remote-debugging-port")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let addr = match (remote_debugger, remote_debugging_port) {
|
||||
(Some(_), Some(_)) => return Err(FlagConflict),
|
||||
(None, None) => return Ok(()),
|
||||
|
||||
// --remote-debugger [<host>][:<port>]
|
||||
(Some(Some(spec)), _) => spec,
|
||||
(Some(None), _) => format!("{}:{}", DEFAULT_HOST, DEFAULT_PORT),
|
||||
|
||||
// --remote-debugging-port <port>
|
||||
(None, Some(Some(port))) => format!("{}:{}", DEFAULT_HOST, port),
|
||||
(None, Some(None)) => return Err(MissingPort),
|
||||
};
|
||||
|
||||
*self.address.borrow_mut() = addr.to_string();
|
||||
|
||||
// Before we can start the remote agent we ensure the browser session
|
||||
// state has been restored. This guarantees we wait for all windows
|
||||
// and tabs to be resurrected.
|
||||
//
|
||||
// Once sessionstore-windows-restored 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("sessionstore-windows-restored")?;
|
||||
|
||||
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 = r#" --remote-debugger [<host>][:<port>]
|
||||
--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.
|
||||
"#;
|
||||
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 {
|
||||
"sessionstore-windows-restored" => {
|
||||
if let Err(err) = self.agent.listen(&self.address.borrow()) {
|
||||
fatalln!("unable to start remote agent: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
"remote-listening" => {
|
||||
let url = unsafe { wstring_to_cstring(data) }.map_err(|_| NS_ERROR_FAILURE)?;
|
||||
eprintln!("DevTools listening on {}", url.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-debugger 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())
|
||||
}
|
5
remote/startup/mod.rs
Normal file
5
remote/startup/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
// 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/.
|
||||
|
||||
mod handler;
|
7
remote/startup/moz.build
Normal file
7
remote/startup/moz.build
Normal file
@ -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"
|
@ -29,6 +29,7 @@ fuzzing_interfaces = ["gkrust-shared/fuzzing_interfaces", "gecko-fuzz-targets"]
|
||||
webrtc = ["gkrust-shared/webrtc"]
|
||||
wasm_library_sandboxing = ["gkrust-shared/wasm_library_sandboxing"]
|
||||
webgpu = ["gkrust-shared/webgpu"]
|
||||
remote_agent = ["gkrust-shared/remote"]
|
||||
|
||||
[dependencies]
|
||||
bench-collections-gtest = { path = "../../../../xpcom/rust/gtest/bench-collections" }
|
||||
|
@ -30,6 +30,7 @@ fuzzing_interfaces = ["gkrust-shared/fuzzing_interfaces"]
|
||||
webrtc = ["gkrust-shared/webrtc"]
|
||||
wasm_library_sandboxing = ["gkrust-shared/wasm_library_sandboxing"]
|
||||
webgpu = ["gkrust-shared/webgpu"]
|
||||
remote_agent = ["gkrust-shared/remote"]
|
||||
|
||||
[dependencies]
|
||||
gkrust-shared = { path = "shared" }
|
||||
|
@ -67,3 +67,6 @@ if CONFIG['FUZZING_INTERFACES']:
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
gkrust_features += ['webrtc']
|
||||
|
||||
if CONFIG['ENABLE_REMOTE_AGENT']:
|
||||
gkrust_features += ['remote_agent']
|
||||
|
@ -45,6 +45,7 @@ neqo_glue = { path = "../../../../netwerk/socket/neqo_glue" }
|
||||
rlbox_lucet_sandbox = { version = "0.1.0", optional = true }
|
||||
wgpu-remote = { path = "../../../../gfx/wgpu/wgpu-remote", optional = true }
|
||||
mapped_hyph = { git = "https://github.com/jfkthame/mapped_hyph.git", tag = "v0.3.0" }
|
||||
remote = { path = "../../../../remote", optional = true }
|
||||
|
||||
unic-langid = { version = "0.7.1", features = ["likelysubtags"] }
|
||||
unic-langid-ffi = { path = "../../../../intl/locale/rust/unic-langid-ffi" }
|
||||
@ -79,6 +80,7 @@ fuzzing_interfaces = []
|
||||
webrtc = ["mdns_service"]
|
||||
wasm_library_sandboxing = ["rlbox_lucet_sandbox"]
|
||||
webgpu = ["wgpu-remote"]
|
||||
remote_agent = ["remote"]
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
@ -64,6 +64,9 @@ extern crate unic_langid_ffi;
|
||||
extern crate fluent_langneg;
|
||||
extern crate fluent_langneg_ffi;
|
||||
|
||||
#[cfg(feature = "remote")]
|
||||
extern crate remote;
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::env;
|
||||
use std::ffi::{CStr, CString};
|
||||
|
Loading…
Reference in New Issue
Block a user