diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /remote/startup | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'remote/startup')
-rw-r--r-- | remote/startup/RemoteAgentHandler.cpp | 36 | ||||
-rw-r--r-- | remote/startup/RemoteAgentHandler.h | 12 | ||||
-rw-r--r-- | remote/startup/handler.rs | 242 | ||||
-rw-r--r-- | remote/startup/mod.rs | 5 | ||||
-rw-r--r-- | remote/startup/moz.build | 7 |
5 files changed, 302 insertions, 0 deletions
diff --git a/remote/startup/RemoteAgentHandler.cpp b/remote/startup/RemoteAgentHandler.cpp new file mode 100644 index 0000000000..45cf6ed941 --- /dev/null +++ b/remote/startup/RemoteAgentHandler.cpp @@ -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(); +} diff --git a/remote/startup/RemoteAgentHandler.h b/remote/startup/RemoteAgentHandler.h new file mode 100644 index 0000000000..dff28c4b9e --- /dev/null +++ b/remote/startup/RemoteAgentHandler.h @@ -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 diff --git a/remote/startup/handler.rs b/remote/startup/handler.rs new file mode 100644 index 0000000000..f441b6100f --- /dev/null +++ b/remote/startup/handler.rs @@ -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 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-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()) +} diff --git a/remote/startup/mod.rs b/remote/startup/mod.rs new file mode 100644 index 0000000000..30f7fee742 --- /dev/null +++ b/remote/startup/mod.rs @@ -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; diff --git a/remote/startup/moz.build b/remote/startup/moz.build new file mode 100644 index 0000000000..040851d67d --- /dev/null +++ b/remote/startup/moz.build @@ -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" |