diff options
Diffstat (limited to '')
-rw-r--r-- | services/common/app_services_logger/AppServicesLoggerComponents.h | 38 | ||||
-rw-r--r-- | services/common/app_services_logger/Cargo.toml | 16 | ||||
-rw-r--r-- | services/common/app_services_logger/components.conf | 15 | ||||
-rw-r--r-- | services/common/app_services_logger/src/lib.rs | 139 |
4 files changed, 208 insertions, 0 deletions
diff --git a/services/common/app_services_logger/AppServicesLoggerComponents.h b/services/common/app_services_logger/AppServicesLoggerComponents.h new file mode 100644 index 0000000000..015eae230b --- /dev/null +++ b/services/common/app_services_logger/AppServicesLoggerComponents.h @@ -0,0 +1,38 @@ +/* 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_services_AppServicesLoggerComponents_h_ +#define mozilla_services_AppServicesLoggerComponents_h_ + +#include "mozIAppServicesLogger.h" +#include "nsCOMPtr.h" + +extern "C" { + +// Implemented in Rust, in the `app_services_logger` crate. +nsresult NS_NewAppServicesLogger(mozIAppServicesLogger** aResult); + +} // extern "C" + +namespace mozilla { +namespace appservices { + +// The C++ constructor for a `services.appServicesLogger` service. This wrapper +// exists because `components.conf` requires a component class constructor to +// return an `already_AddRefed<T>`, but Rust doesn't have such a type. So we +// call the Rust constructor using a `nsCOMPtr` (which is compatible with Rust's +// `xpcom::RefPtr`) out param, and return that. +already_AddRefed<mozIAppServicesLogger> NewLogService() { + nsCOMPtr<mozIAppServicesLogger> logger; + nsresult rv = NS_NewAppServicesLogger(getter_AddRefs(logger)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + return logger.forget(); +} + +} // namespace appservices +} // namespace mozilla + +#endif // mozilla_services_AppServicesLoggerComponents_h_ diff --git a/services/common/app_services_logger/Cargo.toml b/services/common/app_services_logger/Cargo.toml new file mode 100644 index 0000000000..5db41ddcdb --- /dev/null +++ b/services/common/app_services_logger/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "app_services_logger" +version = "0.1.0" +authors = ["lougeniac64 <lougeniaC64@users.noreply.github.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cstr = "0.2" +golden_gate = { path = "../../../services/sync/golden_gate" } +log = "0.4" +once_cell = "1.4.0" +nserror = { path = "../../../xpcom/rust/nserror" } +nsstring = { path = "../../../xpcom/rust/nsstring" } +xpcom = { path = "../../../xpcom/rust/xpcom" } diff --git a/services/common/app_services_logger/components.conf b/services/common/app_services_logger/components.conf new file mode 100644 index 0000000000..cdea60a04b --- /dev/null +++ b/services/common/app_services_logger/components.conf @@ -0,0 +1,15 @@ +# -*- 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': '{d2716568-f5fa-4989-91dd-e11599e932a1}', + 'contract_ids': ['@mozilla.org/appservices/logger;1'], + 'type': 'mozIAppServicesLogger', + 'headers': ['mozilla/appservices/AppServicesLoggerComponents.h'], + 'constructor': 'mozilla::appservices::NewLogService', + }, +] diff --git a/services/common/app_services_logger/src/lib.rs b/services/common/app_services_logger/src/lib.rs new file mode 100644 index 0000000000..18873e8ba9 --- /dev/null +++ b/services/common/app_services_logger/src/lib.rs @@ -0,0 +1,139 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! This provides a XPCOM service to send app services logs to the desktop + +#[macro_use] +extern crate cstr; + +#[macro_use] +extern crate xpcom; + +use golden_gate::log::LogSink; +use log; +use nserror::{nsresult, NS_OK}; +use nsstring::nsAString; +use once_cell::sync::Lazy; +use std::os::raw::c_char; +use std::{ + cmp, + collections::HashMap, + sync::{ + atomic::{AtomicBool, Ordering}, + RwLock, + }, +}; +use xpcom::{ + interfaces::{mozIAppServicesLogger, mozIServicesLogSink, nsISupports}, + RefPtr, +}; + +/// A flag that's set after we register our observer to clear the map of loggers +/// on shutdown. +static SHUTDOWN_OBSERVED: AtomicBool = AtomicBool::new(false); + +#[derive(xpcom)] +#[xpimplements(mozIAppServicesLogger)] +#[refcnt = "nonatomic"] +pub struct InitAppServicesLogger {} + +pub static LOGGERS_BY_TARGET: Lazy<RwLock<HashMap<String, LogSink>>> = Lazy::new(|| { + let h: HashMap<String, LogSink> = HashMap::new(); + let m = RwLock::new(h); + m +}); + +impl AppServicesLogger { + xpcom_method!(register => Register(target: *const nsAString, logger: *const mozIServicesLogSink)); + fn register(&self, target: &nsAString, logger: &mozIServicesLogSink) -> Result<(), nsresult> { + let log_sink_logger = LogSink::with_logger(Some(logger))?; + let max_level = cmp::max(log::max_level(), log_sink_logger.max_level); + + // Note: This will only work if the max_level is lower than the compile-time + // max_level_* filter. + log::set_max_level(max_level); + + ensure_observing_shutdown(); + + LOGGERS_BY_TARGET + .write() + .unwrap() + .insert(target.to_string(), log_sink_logger); + Ok(()) + } + + pub fn is_app_services_logger_registered(target: String) -> bool { + match LOGGERS_BY_TARGET.read() { + Ok(loggers_by_target) => loggers_by_target.contains_key(&target), + Err(_e) => false, + } + } +} + +// Import the `NS_IsMainThread` symbol from Gecko... +extern "C" { + fn NS_IsMainThread() -> bool; +} + +/// Registers an observer to clear the loggers map on `xpcom-shutdown`. This +/// function must be called from the main thread, because the observer service +//// is main thread-only. +fn ensure_observing_shutdown() { + assert!(unsafe { NS_IsMainThread() }); + // If we've already registered our observer, bail. Relaxed ordering is safe + // here and below, because we've asserted we're only called from the main + // thread, and only check the flag here. + if SHUTDOWN_OBSERVED.load(Ordering::Relaxed) { + return; + } + if let Some(service) = xpcom::services::get_ObserverService() { + let observer = ShutdownObserver::allocate(InitShutdownObserver {}); + let rv = unsafe { + service.AddObserver(observer.coerce(), cstr!("xpcom-shutdown").as_ptr(), false) + }; + // If we fail to register the observer now, or fail to get the observer + // service, the flag will remain `false`, and we'll try again on the + // next call to `ensure_observing_shutdown`. + SHUTDOWN_OBSERVED.store(rv.succeeded(), Ordering::Relaxed); + } +} + +#[derive(xpcom)] +#[xpimplements(nsIObserver)] +#[refcnt = "nonatomic"] +struct InitShutdownObserver {} + +impl ShutdownObserver { + xpcom_method!(observe => Observe(_subject: *const nsISupports, topic: *const c_char, _data: *const i16)); + /// Remove our shutdown observer and clear the map. + fn observe( + &self, + _subject: &nsISupports, + topic: *const c_char, + _data: *const i16, + ) -> Result<(), nsresult> { + LOGGERS_BY_TARGET.write().unwrap().clear(); + if let Some(service) = xpcom::services::get_ObserverService() { + // Ignore errors, since we're already shutting down. + let _ = unsafe { service.RemoveObserver(self.coerce(), topic) }; + } + Ok(()) + } +} + +/// The constructor for an `AppServicesLogger` service. This uses C linkage so that it +/// can be called from C++. See `AppServicesLoggerComponents.h` for the C++ +/// constructor that's passed to the component manager. +/// +/// # Safety +/// +/// This function is unsafe because it dereferences `result`. +#[no_mangle] +pub unsafe extern "C" fn NS_NewAppServicesLogger( + result: *mut *const mozIAppServicesLogger, +) -> nsresult { + let logger = AppServicesLogger::allocate(InitAppServicesLogger {}); + RefPtr::new(logger.coerce::<mozIAppServicesLogger>()).forget(&mut *result); + NS_OK +} |