summaryrefslogtreecommitdiffstats
path: root/xpcom/rust/xpcom
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/rust/xpcom')
-rw-r--r--xpcom/rust/xpcom/Cargo.toml20
-rw-r--r--xpcom/rust/xpcom/src/base.rs59
-rw-r--r--xpcom/rust/xpcom/src/components.rs23
-rw-r--r--xpcom/rust/xpcom/src/interfaces/idl.rs12
-rw-r--r--xpcom/rust/xpcom/src/interfaces/mod.rs31
-rw-r--r--xpcom/rust/xpcom/src/interfaces/nonidl.rs180
-rw-r--r--xpcom/rust/xpcom/src/lib.rs43
-rw-r--r--xpcom/rust/xpcom/src/method.rs241
-rw-r--r--xpcom/rust/xpcom/src/promise.rs62
-rw-r--r--xpcom/rust/xpcom/src/reexports.rs52
-rw-r--r--xpcom/rust/xpcom/src/refptr.rs388
-rw-r--r--xpcom/rust/xpcom/src/statics.rs75
-rw-r--r--xpcom/rust/xpcom/xpcom_macros/Cargo.toml16
-rw-r--r--xpcom/rust/xpcom/xpcom_macros/src/lib.rs812
14 files changed, 2014 insertions, 0 deletions
diff --git a/xpcom/rust/xpcom/Cargo.toml b/xpcom/rust/xpcom/Cargo.toml
new file mode 100644
index 0000000000..9c8a1ec83b
--- /dev/null
+++ b/xpcom/rust/xpcom/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "xpcom"
+version = "0.1.0"
+authors = ["Nika Layzell <nika@thelayzells.com>"]
+edition = "2018"
+license = "MPL-2.0"
+
+[dependencies]
+cstr = "0.2"
+libc = "0.2"
+nsstring = { path = "../nsstring" }
+nserror = { path = "../nserror" }
+threadbound = "0.1"
+xpcom_macros = { path = "xpcom_macros" }
+thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
+mozbuild = "0.1"
+
+[features]
+thread_sanitizer = []
+gecko_refcount_logging = []
diff --git a/xpcom/rust/xpcom/src/base.rs b/xpcom/rust/xpcom/src/base.rs
new file mode 100644
index 0000000000..556768a179
--- /dev/null
+++ b/xpcom/rust/xpcom/src/base.rs
@@ -0,0 +1,59 @@
+/* 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 crate::interfaces::{nsIInterfaceRequestor, nsISupports};
+use crate::{GetterAddrefs, RefCounted, RefPtr};
+
+#[repr(C)]
+#[derive(Copy, Clone, Eq, PartialEq)]
+/// A "unique identifier". This is modeled after OSF DCE UUIDs.
+pub struct nsID(pub u32, pub u16, pub u16, pub [u8; 8]);
+
+/// Interface IDs
+pub type nsIID = nsID;
+/// Class IDs
+pub type nsCID = nsID;
+
+/// A type which implements XpCom must follow the following rules:
+///
+/// * It must be a legal XPCOM interface.
+/// * The result of a QueryInterface or similar call, passing IID, must return a
+/// valid reference to an object of the given type.
+/// * It must be valid to cast a &self reference to a &nsISupports reference.
+pub unsafe trait XpCom: RefCounted {
+ const IID: nsIID;
+
+ /// Perform a QueryInterface call on this object, attempting to dynamically
+ /// cast it to the requested interface type. Returns Some(RefPtr<T>) if the
+ /// cast succeeded, and None otherwise.
+ fn query_interface<T: XpCom>(&self) -> Option<RefPtr<T>> {
+ let mut ga = GetterAddrefs::<T>::new();
+ unsafe {
+ if (*(self as *const Self as *const nsISupports))
+ .QueryInterface(&T::IID, ga.void_ptr())
+ .succeeded()
+ {
+ ga.refptr()
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Perform a `GetInterface` call on this object, returning `None` if the
+ /// object doesn't implement `nsIInterfaceRequestor`, or can't access the
+ /// interface `T`.
+ fn get_interface<T: XpCom>(&self) -> Option<RefPtr<T>> {
+ let ireq = self.query_interface::<nsIInterfaceRequestor>()?;
+
+ let mut ga = GetterAddrefs::<T>::new();
+ unsafe {
+ if ireq.GetInterface(&T::IID, ga.void_ptr()).succeeded() {
+ ga.refptr()
+ } else {
+ None
+ }
+ }
+ }
+}
diff --git a/xpcom/rust/xpcom/src/components.rs b/xpcom/rust/xpcom/src/components.rs
new file mode 100644
index 0000000000..c83e2df705
--- /dev/null
+++ b/xpcom/rust/xpcom/src/components.rs
@@ -0,0 +1,23 @@
+/* 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/. */
+
+//! This module contains convenient accessors for static XPCOM components.
+//!
+//! The contents of this file are generated from
+//! `xpcom/components/gen_static_components.py`.
+
+extern "C" {
+ fn Gecko_GetServiceByModuleID(
+ id: ModuleID,
+ iid: &crate::nsIID,
+ result: *mut *mut libc::c_void,
+ ) -> nserror::nsresult;
+ fn Gecko_CreateInstanceByModuleID(
+ id: ModuleID,
+ iid: &crate::nsIID,
+ result: *mut *mut libc::c_void,
+ ) -> nserror::nsresult;
+}
+
+include!(mozbuild::objdir_path!("xpcom/components/components.rs"));
diff --git a/xpcom/rust/xpcom/src/interfaces/idl.rs b/xpcom/rust/xpcom/src/interfaces/idl.rs
new file mode 100644
index 0000000000..c8d62ce716
--- /dev/null
+++ b/xpcom/rust/xpcom/src/interfaces/idl.rs
@@ -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/. */
+
+#![allow(bad_style)]
+
+use crate::interfaces::*;
+use crate::*;
+
+// NOTE: This file contains a series of `include!()` invocations, defining all
+// idl interfaces directly within this module.
+include!(mozbuild::objdir_path!("dist/xpcrs/rt/all.rs"));
diff --git a/xpcom/rust/xpcom/src/interfaces/mod.rs b/xpcom/rust/xpcom/src/interfaces/mod.rs
new file mode 100644
index 0000000000..5b9de5cef5
--- /dev/null
+++ b/xpcom/rust/xpcom/src/interfaces/mod.rs
@@ -0,0 +1,31 @@
+/* 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/. */
+
+//! This module contains the xpcom interfaces exposed to rust code.
+//!
+//! The items in this module come in a few flavours:
+//!
+//! 1. `nsI*`: These are the types for XPCOM interfaces. They should always be
+//! passed behind a reference, pointer, or `RefPtr`. They may be coerced to
+//! their base interfaces using the `coerce` method.
+//!
+//! 2. `nsI*Coerce`: These traits provide the implementation mechanics for the
+//! `coerce` method, and can usually be ignored. *These traits are hidden in
+//! rustdoc*
+//!
+//! 3. `nsI*VTable`: These structs are the vtable definitions for each type.
+//! They contain the base interface's vtable, followed by pointers for each
+//! of the vtable's methods. If direct access is needed, a `*const nsI*` can
+//! be safely transmuted to a `*const nsI*VTable`. *These structs are hidden
+//! in rustdoc*
+//!
+//! 4. Typedefs used in idl file definitions.
+
+// Interfaces defined in .idl files
+mod idl;
+pub use self::idl::*;
+
+// Other interfaces which are needed to compile
+mod nonidl;
+pub use self::nonidl::*;
diff --git a/xpcom/rust/xpcom/src/interfaces/nonidl.rs b/xpcom/rust/xpcom/src/interfaces/nonidl.rs
new file mode 100644
index 0000000000..b9e3f1abe2
--- /dev/null
+++ b/xpcom/rust/xpcom/src/interfaces/nonidl.rs
@@ -0,0 +1,180 @@
+/* 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/. */
+
+//! This module contains definitions of interfaces which are used in idl files
+//! as forward declarations, but are not actually defined in an idl file.
+//!
+//! NOTE: The IIDs in these files must be kept in sync with the IDL definitions
+//! in the corresponding C++ files.
+
+use crate::nsID;
+
+// XXX: This macro should have an option for a custom base interface instead of
+// nsISupports, such that Document can have nsINode as a base, etc. For now,
+// query_interface should be sufficient.
+macro_rules! nonidl {
+ ($name:ident, $iid:expr) => {
+ /// This interface is referenced from idl files, but not defined in
+ /// them. It exports no methods to rust code.
+ #[repr(C)]
+ pub struct $name {
+ _vtable: *const $crate::interfaces::nsISupportsVTable,
+ }
+
+ unsafe impl $crate::XpCom for $name {
+ const IID: $crate::nsIID = $iid;
+ }
+
+ unsafe impl $crate::RefCounted for $name {
+ #[inline]
+ unsafe fn addref(&self) {
+ self.AddRef();
+ }
+ #[inline]
+ unsafe fn release(&self) {
+ self.Release();
+ }
+ }
+
+ impl ::std::ops::Deref for $name {
+ type Target = $crate::interfaces::nsISupports;
+ #[inline]
+ fn deref(&self) -> &$crate::interfaces::nsISupports {
+ unsafe { ::std::mem::transmute(self) }
+ }
+ }
+ };
+}
+
+// Must be kept in sync with Document.h
+nonidl!(
+ Document,
+ nsID(
+ 0xce1f7627,
+ 0x7109,
+ 0x4977,
+ [0xba, 0x77, 0x49, 0x0f, 0xfd, 0xe0, 0x7a, 0xaa]
+ )
+);
+
+// Must be kept in sync with nsINode.h
+nonidl!(
+ nsINode,
+ nsID(
+ 0x70ba4547,
+ 0x7699,
+ 0x44fc,
+ [0xb3, 0x20, 0x52, 0xdb, 0xe3, 0xd1, 0xf9, 0x0a]
+ )
+);
+
+// Must be kept in sync with nsIContent.h
+nonidl!(
+ nsIContent,
+ nsID(
+ 0x8e1bab9d,
+ 0x8815,
+ 0x4d2c,
+ [0xa2, 0x4d, 0x7a, 0xba, 0x52, 0x39, 0xdc, 0x22]
+ )
+);
+
+// Must be kept in sync with nsIConsoleReportCollector.h
+nonidl!(
+ nsIConsoleReportCollector,
+ nsID(
+ 0xdd98a481,
+ 0xd2c4,
+ 0x4203,
+ [0x8d, 0xfa, 0x85, 0xbf, 0xd7, 0xdc, 0xd7, 0x05]
+ )
+);
+
+// Must be kept in sync with nsIGlobalObject.h
+nonidl!(
+ nsIGlobalObject,
+ nsID(
+ 0x11afa8be,
+ 0xd997,
+ 0x4e07,
+ [0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f]
+ )
+);
+
+// Must be kept in sync with nsIScriptElement.h
+nonidl!(
+ nsIScriptElement,
+ nsID(
+ 0xe60fca9b,
+ 0x1b96,
+ 0x4e4e,
+ [0xa9, 0xb4, 0xdc, 0x98, 0x4f, 0x88, 0x3f, 0x9c]
+ )
+);
+
+// Must be kept in sync with nsPIDOMWindow.h
+nonidl!(
+ nsPIDOMWindowOuter,
+ nsID(
+ 0x769693d4,
+ 0xb009,
+ 0x4fe2,
+ [0xaf, 0x18, 0x7d, 0xc8, 0xdf, 0x74, 0x96, 0xdf]
+ )
+);
+
+// Must be kept in sync with nsPIDOMWindow.h
+nonidl!(
+ nsPIDOMWindowInner,
+ nsID(
+ 0x775dabc9,
+ 0x8f43,
+ 0x4277,
+ [0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb]
+ )
+);
+
+// Must be kept in sync with nsIScriptContext.h
+nonidl!(
+ nsIScriptContext,
+ nsID(
+ 0x54cbe9cf,
+ 0x7282,
+ 0x421a,
+ [0x91, 0x6f, 0xd0, 0x70, 0x73, 0xde, 0xb8, 0xc0]
+ )
+);
+
+// Must be kept in sync with nsIScriptGlobalObject.h
+nonidl!(
+ nsIScriptGlobalObject,
+ nsID(
+ 0x876f83bd,
+ 0x6314,
+ 0x460a,
+ [0xa0, 0x45, 0x1c, 0x8f, 0x46, 0x2f, 0xb8, 0xe1]
+ )
+);
+
+// Must be kept in sync with nsIScrollObserver.h
+nonidl!(
+ nsIScrollObserver,
+ nsID(
+ 0xaa5026eb,
+ 0x2f88,
+ 0x4026,
+ [0xa4, 0x6b, 0xf4, 0x59, 0x6b, 0x4e, 0xdf, 0x00]
+ )
+);
+
+// Must be kept in sync with nsIWidget.h
+nonidl!(
+ nsIWidget,
+ nsID(
+ 0x06396bf6,
+ 0x2dd8,
+ 0x45e5,
+ [0xac, 0x45, 0x75, 0x26, 0x53, 0xb1, 0xc9, 0x80]
+ )
+);
diff --git a/xpcom/rust/xpcom/src/lib.rs b/xpcom/rust/xpcom/src/lib.rs
new file mode 100644
index 0000000000..ac039ebe76
--- /dev/null
+++ b/xpcom/rust/xpcom/src/lib.rs
@@ -0,0 +1,43 @@
+/* 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/. */
+
+//! This crate contains the functionality required in order to both implement
+//! and call XPCOM methods from rust code.
+//!
+//! For documentation on how to implement XPCOM methods, see the documentation
+//! for the [`xpcom_macros`](../xpcom_macros/index.html) crate.
+
+#![allow(non_snake_case)]
+#![allow(non_camel_case_types)]
+
+// re-export the xpcom_macros macro
+pub use xpcom_macros::xpcom;
+
+// Helper functions and data structures are exported in the root of the crate.
+mod base;
+pub use base::*;
+
+// Declarative macro to generate XPCOM method stubs.
+mod method;
+pub use method::*;
+
+// dom::Promise resolving.
+mod promise;
+pub use promise::*;
+
+mod refptr;
+pub use refptr::*;
+
+mod statics;
+pub use statics::*;
+
+// XPCOM interface definitions.
+pub mod interfaces;
+
+// XPCOM component getters.
+pub mod components;
+
+// Implementation details of the xpcom_macros crate.
+#[doc(hidden)]
+pub mod reexports;
diff --git a/xpcom/rust/xpcom/src/method.rs b/xpcom/rust/xpcom/src/method.rs
new file mode 100644
index 0000000000..66c0510bd9
--- /dev/null
+++ b/xpcom/rust/xpcom/src/method.rs
@@ -0,0 +1,241 @@
+/* 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 nserror::{nsresult, NS_ERROR_NULL_POINTER};
+
+/// The xpcom_method macro generates a Rust XPCOM method stub that converts
+/// raw pointer arguments to references, calls a Rustic implementation
+/// of the method, writes its return value into the XPCOM method's outparameter,
+/// and returns an nsresult.
+///
+/// In other words, given an XPCOM method like:
+///
+/// ```ignore
+/// interface nsIFooBarBaz : nsISupports {
+/// nsIVariant foo(in AUTF8String bar, [optional] in bool baz);
+/// }
+/// ```
+///
+/// And a Rust implementation that uses #[xpcom] to implement it:
+///
+/// ```ignore
+/// #[xpcom(implement(nsIFooBarBaz), atomic)]
+/// struct FooBarBaz {
+/// // …
+/// }
+/// ```
+///
+/// With the appropriate extern crate and use declarations
+///
+/// ```ignore
+/// extern crate xpcom;
+/// use xpcom::xpcom_method;
+/// ```
+///
+/// Invoking the macro with the name of the XPCOM method, the name of its
+/// Rustic implementation, the set of its arguments, and its return value:
+///
+/// ```ignore
+/// impl FooBarBaz {
+/// xpcom_method!(
+/// foo => Foo(bar: *const nsACString, baz: bool) -> *const nsIVariant
+/// );
+/// }
+/// ```
+///
+/// Results in the macro generating an XPCOM stub like the following:
+///
+/// ```ignore
+/// unsafe fn Foo(&self, bar: *const nsACString, baz: bool, retval: *mut *const nsIVariant) -> nsresult {
+/// let bar = match Ensure::ensure(bar) {
+/// Ok(val) => val,
+/// Err(result) => return result,
+/// };
+/// let baz = match Ensure::ensure(baz) {
+/// Ok(val) => val,
+/// Err(result) => return result,
+/// };
+///
+/// match self.foo(bar, baz) {
+/// Ok(val) => {
+/// val.forget(&mut *retval);
+/// NS_OK
+/// }
+/// Err(error) => {
+/// error!("{}", error);
+/// error.into()
+/// }
+/// }
+/// }
+/// ```
+///
+/// Which calls a Rustic implementation (that you implement) like the following:
+///
+/// ```ignore
+/// impl FooBarBaz {
+/// fn foo(&self, bar: &nsACString, baz: bool) -> Result<RefPtr<nsIVariant>, nsresult> {
+/// // …
+/// }
+/// }
+/// ```
+///
+/// Notes:
+///
+/// On error, the Rustic implementation can return an Err(nsresult) or any
+/// other type that implements Into<nsresult>. So you can define and return
+/// a custom error type, which the XPCOM stub will convert to nsresult.
+///
+/// This macro assumes that all non-null pointer arguments are valid!
+/// It does ensure that they aren't null, using the `ensure_param` macro.
+/// But it doesn't otherwise check their validity. That makes the function
+/// unsafe, so callers must ensure that they only call it with valid pointer
+/// arguments.
+#[macro_export]
+macro_rules! xpcom_method {
+ // This rule is provided to ensure external modules don't need to import
+ // internal implementation details of xpcom_method.
+ // The @ensure_param rule converts raw pointer arguments to references,
+ // returning NS_ERROR_NULL_POINTER if the argument is_null().
+ //
+ // Notes:
+ //
+ // This rule can be called on a non-pointer copy parameter, but there's no
+ // benefit to doing so. The macro will just set the value of the parameter
+ // to itself. (This macro does this anyway due to limitations in declarative
+ // macros; it isn't currently possible to distinguish between pointer and
+ // copy types when processing a set of parameters.)
+ //
+ // The macro currently supports only in-parameters (*const nsIFoo); It
+ // doesn't (yet?) support out-parameters (*mut nsIFoo). The xpcom_method
+ // macro itself does, however, support the return value out-parameter.
+ (@ensure_param $name:ident) => {
+ let $name = match $crate::Ensure::ensure($name) {
+ Ok(val) => val,
+ Err(result) => return result,
+ };
+ };
+
+ // `#[allow(non_snake_case)]` is used for each method because `$xpcom_name`
+ // is almost always UpperCamelCase, and Rust gives a warning that it should
+ // be snake_case. It isn't reasonable to rename the XPCOM methods, so
+ // silence the warning.
+
+ // A method whose return value is a *mut *const nsISomething type.
+ // Example: foo => Foo(bar: *const nsACString, baz: bool) -> *const nsIVariant
+ ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> *const $retval:ty) => {
+ #[allow(non_snake_case)]
+ unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut *const $retval) -> nsresult {
+ $(xpcom_method!(@ensure_param $param_name);)*
+ match self.$rust_name($($param_name, )*) {
+ Ok(val) => {
+ val.forget(&mut *retval);
+ NS_OK
+ }
+ Err(error) => {
+ error.into()
+ }
+ }
+ }
+ };
+
+ // A method whose return value is a *mut nsAString type.
+ // Example: foo => Foo(bar: *const nsACString, baz: bool) -> nsAString
+ ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> nsAString) => {
+ #[allow(non_snake_case)]
+ unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut nsAString) -> nsresult {
+ $(xpcom_method!(@ensure_param $param_name);)*
+ match self.$rust_name($($param_name, )*) {
+ Ok(val) => {
+ (*retval).assign(&val);
+ NS_OK
+ }
+ Err(error) => {
+ error.into()
+ }
+ }
+ }
+ };
+
+ // A method whose return value is a *mut nsACString type.
+ // Example: foo => Foo(bar: *const nsACString, baz: bool) -> nsACString
+ ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> nsACString) => {
+ #[allow(non_snake_case)]
+ unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut nsACString) -> nsresult {
+ $(xpcom_method!(@ensure_param $param_name);)*
+ match self.$rust_name($($param_name, )*) {
+ Ok(val) => {
+ (*retval).assign(&val);
+ NS_OK
+ }
+ Err(error) => {
+ error.into()
+ }
+ }
+ }
+ };
+
+ // A method whose return value is a non-nsA[C]String *mut type.
+ // Example: foo => Foo(bar: *const nsACString, baz: bool) -> bool
+ ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> $retval:ty) => {
+ #[allow(non_snake_case)]
+ unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut $retval) -> nsresult {
+ $(xpcom_method!(@ensure_param $param_name);)*
+ match self.$rust_name($($param_name, )*) {
+ Ok(val) => {
+ *retval = val;
+ NS_OK
+ }
+ Err(error) => {
+ error.into()
+ }
+ }
+ }
+ };
+
+ // A method that doesn't have a return value.
+ // Example: foo => Foo(bar: *const nsACString, baz: bool)
+ ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*)) => {
+ #[allow(non_snake_case)]
+ unsafe fn $xpcom_name(&self, $($param_name: $param_type,)*) -> nsresult {
+ $(xpcom_method!(@ensure_param $param_name);)*
+ match self.$rust_name($($param_name, )*) {
+ Ok(_) => NS_OK,
+ Err(error) => {
+ error.into()
+ }
+ }
+ }
+ };
+}
+
+/// A trait that ensures that a raw pointer isn't null and converts it to
+/// a reference. Because of limitations in declarative macros, this includes an
+/// implementation for types that are Copy, which simply returns the value
+/// itself.
+#[doc(hidden)]
+pub trait Ensure<T> {
+ unsafe fn ensure(value: T) -> Self;
+}
+
+impl<'a, T: 'a> Ensure<*const T> for Result<&'a T, nsresult> {
+ unsafe fn ensure(ptr: *const T) -> Result<&'a T, nsresult> {
+ if ptr.is_null() {
+ Err(NS_ERROR_NULL_POINTER)
+ } else {
+ Ok(&*ptr)
+ }
+ }
+}
+
+impl<'a, T: 'a> Ensure<*const T> for Result<Option<&'a T>, nsresult> {
+ unsafe fn ensure(ptr: *const T) -> Result<Option<&'a T>, nsresult> {
+ Ok(if ptr.is_null() { None } else { Some(&*ptr) })
+ }
+}
+
+impl<T: Copy> Ensure<T> for Result<T, nsresult> {
+ unsafe fn ensure(copyable: T) -> Result<T, nsresult> {
+ Ok(copyable)
+ }
+}
diff --git a/xpcom/rust/xpcom/src/promise.rs b/xpcom/rust/xpcom/src/promise.rs
new file mode 100644
index 0000000000..0fdab9b6aa
--- /dev/null
+++ b/xpcom/rust/xpcom/src/promise.rs
@@ -0,0 +1,62 @@
+/* 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/. */
+
+use crate::{
+ create_instance,
+ interfaces::{nsIVariant, nsIWritableVariant},
+ RefCounted,
+};
+
+use cstr::*;
+
+mod ffi {
+ use super::*;
+
+ extern "C" {
+ // These are implemented in dom/promise/Promise.cpp
+ pub fn DomPromise_AddRef(promise: *const Promise);
+ pub fn DomPromise_Release(promise: *const Promise);
+ pub fn DomPromise_RejectWithVariant(promise: *const Promise, variant: *const nsIVariant);
+ pub fn DomPromise_ResolveWithVariant(promise: *const Promise, variant: *const nsIVariant);
+ }
+}
+
+#[repr(C)]
+pub struct Promise {
+ private: [u8; 0],
+
+ /// This field is a phantomdata to ensure that the Promise type and any
+ /// struct containing it is not safe to send across threads, as DOM is
+ /// generally not threadsafe.
+ __nosync: ::std::marker::PhantomData<::std::rc::Rc<u8>>,
+}
+
+impl Promise {
+ pub fn reject_with_undefined(&self) {
+ let variant = create_instance::<nsIWritableVariant>(cstr!("@mozilla.org/variant;1"))
+ .expect("Failed to create writable variant");
+ unsafe {
+ variant.SetAsVoid();
+ }
+ self.reject_with_variant(&variant);
+ }
+
+ pub fn reject_with_variant(&self, variant: &nsIVariant) {
+ unsafe { ffi::DomPromise_RejectWithVariant(self, variant) }
+ }
+
+ pub fn resolve_with_variant(&self, variant: &nsIVariant) {
+ unsafe { ffi::DomPromise_ResolveWithVariant(self, variant) }
+ }
+}
+
+unsafe impl RefCounted for Promise {
+ unsafe fn addref(&self) {
+ ffi::DomPromise_AddRef(self)
+ }
+
+ unsafe fn release(&self) {
+ ffi::DomPromise_Release(self)
+ }
+}
diff --git a/xpcom/rust/xpcom/src/reexports.rs b/xpcom/rust/xpcom/src/reexports.rs
new file mode 100644
index 0000000000..d198899497
--- /dev/null
+++ b/xpcom/rust/xpcom/src/reexports.rs
@@ -0,0 +1,52 @@
+/* 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/. */
+
+//! The automatically generated code from `xpcom_macros` depends on some types
+//! which are defined in other libraries which `xpcom` depends on, but which may
+//! not be `extern crate`-ed into the crate the macros are expanded into. This
+//! module re-exports those types from `xpcom` so that they can be used from the
+//! macro.
+// re-export libc so it can be used by the procedural macro.
+pub extern crate libc;
+
+pub use nsstring::{nsACString, nsAString, nsCString, nsString};
+
+pub use nserror::{nsresult, NS_ERROR_NO_INTERFACE, NS_OK};
+
+pub use std::ops::Deref;
+
+/// Helper method used by the xpcom codegen, it is not public API or meant for
+/// calling outside of that context.
+///
+/// Takes a reference to the `this` pointer received from XPIDL, and offsets and
+/// casts it to a reference to the concrete rust `struct` type, `U`.
+///
+/// `vtable_index` is the index, and therefore the offset in pointers, of the
+/// vtable for `T` in `U`.
+///
+/// A reference to `this` is taken, instead of taking `*const T` by value, to use
+/// as a lifetime bound, such that the returned `&U` reference has a bounded
+/// lifetime when used to call the implementation method.
+#[inline]
+pub unsafe fn transmute_from_vtable_ptr<'a, T, U>(
+ this: &'a *const T,
+ vtable_index: usize,
+) -> &'a U {
+ &*((*this as *const *const ()).sub(vtable_index) as *const U)
+}
+
+/// On some ABIs, extra information is included before the vtable's function
+/// pointers which are used to implement RTTI. We build Gecko with RTTI
+/// disabled, however these fields may still be present to support
+/// `dynamic_cast<void*>` on our rust VTables in case they are accessed.
+///
+/// Itanium ABI Layout: https://refspecs.linuxbase.org/cxxabi-1.83.html#vtable
+#[repr(C)]
+pub struct VTableExtra<T> {
+ #[cfg(not(windows))]
+ pub offset: isize,
+ #[cfg(not(windows))]
+ pub typeinfo: *const libc::c_void,
+ pub vtable: T,
+}
diff --git a/xpcom/rust/xpcom/src/refptr.rs b/xpcom/rust/xpcom/src/refptr.rs
new file mode 100644
index 0000000000..8549b6d2f0
--- /dev/null
+++ b/xpcom/rust/xpcom/src/refptr.rs
@@ -0,0 +1,388 @@
+/* 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 crate::interfaces::nsISupports;
+use libc;
+use nserror::{nsresult, NS_OK};
+use std::cell::Cell;
+use std::convert::TryInto;
+use std::fmt;
+use std::marker::PhantomData;
+use std::mem;
+use std::ops::Deref;
+use std::ptr::{self, NonNull};
+use std::sync::atomic::{self, AtomicUsize, Ordering};
+use threadbound::ThreadBound;
+
+// This should match the definition in mfbt/RefCountType.h, modulo the delicate
+// effort at maintaining binary compatibility with Microsoft COM on Windows.
+pub type MozExternalRefCountType = u32;
+
+/// A trait representing a type which can be reference counted invasively.
+/// The object is responsible for freeing its backing memory when its
+/// reference count reaches 0.
+pub unsafe trait RefCounted {
+ /// Increment the reference count.
+ unsafe fn addref(&self);
+ /// Decrement the reference count, potentially freeing backing memory.
+ unsafe fn release(&self);
+}
+
+/// A smart pointer holding a RefCounted object. The object itself manages its
+/// own memory. RefPtr will invoke the addref and release methods at the
+/// appropriate times to facilitate the bookkeeping.
+#[repr(transparent)]
+pub struct RefPtr<T: RefCounted + 'static> {
+ _ptr: NonNull<T>,
+ // Tell dropck that we own an instance of T.
+ _marker: PhantomData<T>,
+}
+
+impl<T: RefCounted + 'static> RefPtr<T> {
+ /// Construct a new RefPtr from a reference to the refcounted object.
+ #[inline]
+ pub fn new(p: &T) -> RefPtr<T> {
+ unsafe {
+ p.addref();
+ }
+ RefPtr {
+ _ptr: p.into(),
+ _marker: PhantomData,
+ }
+ }
+
+ /// Construct a RefPtr from a raw pointer, addrefing it.
+ #[inline]
+ pub unsafe fn from_raw(p: *const T) -> Option<RefPtr<T>> {
+ let ptr = NonNull::new(p as *mut T)?;
+ ptr.as_ref().addref();
+ Some(RefPtr {
+ _ptr: ptr,
+ _marker: PhantomData,
+ })
+ }
+
+ /// Construct a RefPtr from a raw pointer, without addrefing it.
+ #[inline]
+ pub unsafe fn from_raw_dont_addref(p: *const T) -> Option<RefPtr<T>> {
+ Some(RefPtr {
+ _ptr: NonNull::new(p as *mut T)?,
+ _marker: PhantomData,
+ })
+ }
+
+ /// Write this RefPtr's value into an outparameter.
+ #[inline]
+ pub fn forget(self, into: &mut *const T) {
+ *into = Self::forget_into_raw(self);
+ }
+
+ #[inline]
+ pub fn forget_into_raw(this: RefPtr<T>) -> *const T {
+ let into = &*this as *const T;
+ mem::forget(this);
+ into
+ }
+}
+
+impl<T: RefCounted + 'static> Deref for RefPtr<T> {
+ type Target = T;
+ #[inline]
+ fn deref(&self) -> &T {
+ unsafe { self._ptr.as_ref() }
+ }
+}
+
+impl<T: RefCounted + 'static> Drop for RefPtr<T> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ self._ptr.as_ref().release();
+ }
+ }
+}
+
+impl<T: RefCounted + 'static> Clone for RefPtr<T> {
+ #[inline]
+ fn clone(&self) -> RefPtr<T> {
+ RefPtr::new(self)
+ }
+}
+
+impl<T: RefCounted + 'static + fmt::Debug> fmt::Debug for RefPtr<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "RefPtr<{:?}>", self.deref())
+ }
+}
+
+// Both `Send` and `Sync` bounds are required for `RefPtr<T>` to implement
+// either, as sharing a `RefPtr<T>` also allows transferring ownership, and
+// vice-versa.
+unsafe impl<T: RefCounted + 'static + Send + Sync> Send for RefPtr<T> {}
+unsafe impl<T: RefCounted + 'static + Send + Sync> Sync for RefPtr<T> {}
+
+macro_rules! assert_layout_eq {
+ ($T:ty, $U:ty) => {
+ const _: [(); std::mem::size_of::<$T>()] = [(); std::mem::size_of::<$U>()];
+ const _: [(); std::mem::align_of::<$T>()] = [(); std::mem::align_of::<$U>()];
+ };
+}
+
+// Assert that `RefPtr<nsISupports>` has the correct memory layout.
+assert_layout_eq!(RefPtr<nsISupports>, *const nsISupports);
+// Assert that the null-pointer optimization applies to `RefPtr<nsISupports>`.
+assert_layout_eq!(RefPtr<nsISupports>, Option<RefPtr<nsISupports>>);
+
+/// A wrapper that binds a RefCounted value to its original thread,
+/// preventing retrieval from other threads and panicking if the value
+/// is dropped on a different thread.
+///
+/// These limitations enable values of this type to be Send + Sync, which is
+/// useful when creating a struct that holds a RefPtr<T> type while being
+/// Send + Sync. Such a struct can hold a ThreadBoundRefPtr<T> type instead.
+pub struct ThreadBoundRefPtr<T: RefCounted + 'static>(ThreadBound<*const T>);
+
+impl<T: RefCounted + 'static> ThreadBoundRefPtr<T> {
+ pub fn new(ptr: RefPtr<T>) -> Self {
+ let raw: *const T = &*ptr;
+ mem::forget(ptr);
+ ThreadBoundRefPtr(ThreadBound::new(raw))
+ }
+
+ pub fn get_ref(&self) -> Option<&T> {
+ self.0.get_ref().map(|raw| unsafe { &**raw })
+ }
+}
+
+impl<T: RefCounted + 'static> Drop for ThreadBoundRefPtr<T> {
+ fn drop(&mut self) {
+ unsafe {
+ RefPtr::from_raw_dont_addref(self.get_ref().expect("drop() called on wrong thread!"));
+ }
+ }
+}
+
+/// A helper struct for constructing `RefPtr<T>` from raw pointer outparameters.
+/// Holds a `*const T` internally which will be released if non null when
+/// destructed, and can be easily transformed into an `Option<RefPtr<T>>`.
+///
+/// It many cases it may be easier to use the `getter_addrefs` method.
+pub struct GetterAddrefs<T: RefCounted + 'static> {
+ _ptr: *const T,
+ _marker: PhantomData<T>,
+}
+
+impl<T: RefCounted + 'static> GetterAddrefs<T> {
+ /// Create a `GetterAddrefs`, initializing it with the null pointer.
+ #[inline]
+ pub fn new() -> GetterAddrefs<T> {
+ GetterAddrefs {
+ _ptr: ptr::null(),
+ _marker: PhantomData,
+ }
+ }
+
+ /// Get a reference to the internal `*const T`. This method is unsafe,
+ /// as the destructor of this class depends on the internal `*const T`
+ /// being either a valid reference to a value of type `T`, or null.
+ #[inline]
+ pub unsafe fn ptr(&mut self) -> &mut *const T {
+ &mut self._ptr
+ }
+
+ /// Get a reference to the internal `*const T` as a `*mut libc::c_void`.
+ /// This is useful to pass to functions like `GetInterface` which take a
+ /// void pointer outparameter.
+ #[inline]
+ pub unsafe fn void_ptr(&mut self) -> *mut *mut libc::c_void {
+ &mut self._ptr as *mut *const T as *mut *mut libc::c_void
+ }
+
+ /// Transform this `GetterAddrefs` into an `Option<RefPtr<T>>`, without
+ /// performing any addrefs or releases.
+ #[inline]
+ pub fn refptr(self) -> Option<RefPtr<T>> {
+ let p = self._ptr;
+ // Don't run the destructor because we don't want to release the stored
+ // pointer.
+ mem::forget(self);
+ unsafe { RefPtr::from_raw_dont_addref(p) }
+ }
+}
+
+impl<T: RefCounted + 'static> Drop for GetterAddrefs<T> {
+ #[inline]
+ fn drop(&mut self) {
+ if !self._ptr.is_null() {
+ unsafe {
+ (*self._ptr).release();
+ }
+ }
+ }
+}
+
+/// Helper method for calling XPCOM methods which return a reference counted
+/// value through an outparameter. Takes a lambda, which is called with a valid
+/// outparameter argument (`*mut *const T`), and returns a `nsresult`. Returns
+/// either a `RefPtr<T>` with the value returned from the outparameter, or a
+/// `nsresult`.
+///
+/// # NOTE:
+///
+/// Can return `Err(NS_OK)` if the call succeeded, but the outparameter was set
+/// to NULL.
+///
+/// # Usage
+///
+/// ```
+/// let x: Result<RefPtr<T>, nsresult> =
+/// getter_addrefs(|p| iosvc.NewURI(uri, ptr::null(), ptr::null(), p));
+/// ```
+#[inline]
+pub fn getter_addrefs<T: RefCounted, F>(f: F) -> Result<RefPtr<T>, nsresult>
+where
+ F: FnOnce(*mut *const T) -> nsresult,
+{
+ let mut ga = GetterAddrefs::<T>::new();
+ let rv = f(unsafe { ga.ptr() });
+ if rv.failed() {
+ return Err(rv);
+ }
+ ga.refptr().ok_or(NS_OK)
+}
+
+/// The type of the reference count type for xpcom structs.
+///
+/// `#[xpcom(nonatomic)]` will use this type for the `__refcnt` field.
+#[derive(Debug)]
+pub struct Refcnt(Cell<usize>);
+impl Refcnt {
+ /// Create a new reference count value. This is unsafe as manipulating
+ /// Refcnt values is an easy footgun.
+ pub unsafe fn new() -> Self {
+ Refcnt(Cell::new(0))
+ }
+
+ /// Increment the reference count. Returns the new reference count. This is
+ /// unsafe as modifying this value can cause a use-after-free.
+ pub unsafe fn inc(&self) -> MozExternalRefCountType {
+ // XXX: Checked add?
+ let new = self.0.get() + 1;
+ self.0.set(new);
+ new.try_into().unwrap()
+ }
+
+ /// Decrement the reference count. Returns the new reference count. This is
+ /// unsafe as modifying this value can cause a use-after-free.
+ pub unsafe fn dec(&self) -> MozExternalRefCountType {
+ // XXX: Checked sub?
+ let new = self.0.get() - 1;
+ self.0.set(new);
+ new.try_into().unwrap()
+ }
+
+ /// Get the current value of the reference count.
+ pub fn get(&self) -> usize {
+ self.0.get()
+ }
+}
+
+/// The type of the atomic reference count used for xpcom structs.
+///
+/// `#[xpcom(atomic)]` will use this type for the `__refcnt` field.
+///
+/// See `nsISupportsImpl.h`'s `ThreadSafeAutoRefCnt` class for reasoning behind
+/// memory ordering decisions.
+#[derive(Debug)]
+pub struct AtomicRefcnt(AtomicUsize);
+impl AtomicRefcnt {
+ /// Create a new reference count value. This is unsafe as manipulating
+ /// Refcnt values is an easy footgun.
+ pub unsafe fn new() -> Self {
+ AtomicRefcnt(AtomicUsize::new(0))
+ }
+
+ /// Increment the reference count. Returns the new reference count. This is
+ /// unsafe as modifying this value can cause a use-after-free.
+ pub unsafe fn inc(&self) -> MozExternalRefCountType {
+ let result = self.0.fetch_add(1, Ordering::Relaxed) + 1;
+ result.try_into().unwrap()
+ }
+
+ /// Decrement the reference count. Returns the new reference count. This is
+ /// unsafe as modifying this value can cause a use-after-free.
+ pub unsafe fn dec(&self) -> MozExternalRefCountType {
+ let result = self.0.fetch_sub(1, Ordering::Release) - 1;
+ if result == 0 {
+ // We're going to destroy the object on this thread, so we need
+ // acquire semantics to synchronize with the memory released by
+ // the last release on other threads, that is, to ensure that
+ // writes prior to that release are now visible on this thread.
+ if cfg!(feature = "thread_sanitizer") {
+ // TSan doesn't understand atomic::fence, so in order to avoid
+ // a false positive for every time a refcounted object is
+ // deleted, we replace the fence with an atomic operation.
+ self.0.load(Ordering::Acquire);
+ } else {
+ atomic::fence(Ordering::Acquire);
+ }
+ }
+ result.try_into().unwrap()
+ }
+
+ /// Get the current value of the reference count.
+ pub fn get(&self) -> usize {
+ self.0.load(Ordering::Acquire)
+ }
+}
+
+#[cfg(feature = "gecko_refcount_logging")]
+pub mod trace_refcnt {
+ extern "C" {
+ pub fn NS_LogCtor(aPtr: *mut libc::c_void, aTypeName: *const libc::c_char, aSize: u32);
+ pub fn NS_LogDtor(aPtr: *mut libc::c_void, aTypeName: *const libc::c_char, aSize: u32);
+ pub fn NS_LogAddRef(
+ aPtr: *mut libc::c_void,
+ aRefcnt: usize,
+ aClass: *const libc::c_char,
+ aClassSize: u32,
+ );
+ pub fn NS_LogRelease(
+ aPtr: *mut libc::c_void,
+ aRefcnt: usize,
+ aClass: *const libc::c_char,
+ aClassSize: u32,
+ );
+ }
+}
+
+// stub inline methods for the refcount logging functions for when the feature
+// is disabled.
+#[cfg(not(feature = "gecko_refcount_logging"))]
+pub mod trace_refcnt {
+ #[inline]
+ #[allow(non_snake_case)]
+ pub unsafe extern "C" fn NS_LogCtor(_: *mut libc::c_void, _: *const libc::c_char, _: u32) {}
+ #[inline]
+ #[allow(non_snake_case)]
+ pub unsafe extern "C" fn NS_LogDtor(_: *mut libc::c_void, _: *const libc::c_char, _: u32) {}
+ #[inline]
+ #[allow(non_snake_case)]
+ pub unsafe extern "C" fn NS_LogAddRef(
+ _: *mut libc::c_void,
+ _: usize,
+ _: *const libc::c_char,
+ _: u32,
+ ) {
+ }
+ #[inline]
+ #[allow(non_snake_case)]
+ pub unsafe extern "C" fn NS_LogRelease(
+ _: *mut libc::c_void,
+ _: usize,
+ _: *const libc::c_char,
+ _: u32,
+ ) {
+ }
+}
diff --git a/xpcom/rust/xpcom/src/statics.rs b/xpcom/rust/xpcom/src/statics.rs
new file mode 100644
index 0000000000..27f66fdb4b
--- /dev/null
+++ b/xpcom/rust/xpcom/src/statics.rs
@@ -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/. */
+
+use crate::interfaces::{nsIComponentManager, nsIComponentRegistrar, nsIServiceManager};
+use crate::{GetterAddrefs, RefPtr, XpCom};
+use std::ffi::CStr;
+
+/// Get a reference to the global `nsIComponentManager`.
+///
+/// Can return `None` during shutdown.
+#[inline]
+pub fn component_manager() -> Option<RefPtr<nsIComponentManager>> {
+ unsafe { RefPtr::from_raw(Gecko_GetComponentManager()) }
+}
+
+/// Get a reference to the global `nsIServiceManager`.
+///
+/// Can return `None` during shutdown.
+#[inline]
+pub fn service_manager() -> Option<RefPtr<nsIServiceManager>> {
+ unsafe { RefPtr::from_raw(Gecko_GetServiceManager()) }
+}
+
+/// Get a reference to the global `nsIComponentRegistrar`
+///
+/// Can return `None` during shutdown.
+#[inline]
+pub fn component_registrar() -> Option<RefPtr<nsIComponentRegistrar>> {
+ unsafe { RefPtr::from_raw(Gecko_GetComponentRegistrar()) }
+}
+
+/// Helper for calling `nsIComponentManager::CreateInstanceByContractID` on the
+/// global `nsIComponentRegistrar`.
+///
+/// This method is similar to `do_CreateInstance` in C++.
+#[inline]
+pub fn create_instance<T: XpCom>(id: &CStr) -> Option<RefPtr<T>> {
+ unsafe {
+ let mut ga = GetterAddrefs::<T>::new();
+ if component_manager()?
+ .CreateInstanceByContractID(id.as_ptr(), &T::IID, ga.void_ptr())
+ .succeeded()
+ {
+ ga.refptr()
+ } else {
+ None
+ }
+ }
+}
+
+/// Helper for calling `nsIServiceManager::GetServiceByContractID` on the global
+/// `nsIServiceManager`.
+///
+/// This method is similar to `do_GetService` in C++.
+#[inline]
+pub fn get_service<T: XpCom>(id: &CStr) -> Option<RefPtr<T>> {
+ unsafe {
+ let mut ga = GetterAddrefs::<T>::new();
+ if service_manager()?
+ .GetServiceByContractID(id.as_ptr(), &T::IID, ga.void_ptr())
+ .succeeded()
+ {
+ ga.refptr()
+ } else {
+ None
+ }
+ }
+}
+
+extern "C" {
+ fn Gecko_GetComponentManager() -> *const nsIComponentManager;
+ fn Gecko_GetServiceManager() -> *const nsIServiceManager;
+ fn Gecko_GetComponentRegistrar() -> *const nsIComponentRegistrar;
+}
diff --git a/xpcom/rust/xpcom/xpcom_macros/Cargo.toml b/xpcom/rust/xpcom/xpcom_macros/Cargo.toml
new file mode 100644
index 0000000000..78238ae6d1
--- /dev/null
+++ b/xpcom/rust/xpcom/xpcom_macros/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "xpcom_macros"
+version = "0.1.0"
+authors = ["Nika Layzell <nika@thelayzells.com>"]
+edition = "2018"
+license = "MPL-2.0"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "2", features = ["full"] }
+quote = "1"
+proc-macro2 = "1"
+lazy_static = "1.0"
+mozbuild = "0.1"
diff --git a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
new file mode 100644
index 0000000000..5c40f699aa
--- /dev/null
+++ b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
@@ -0,0 +1,812 @@
+/* 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/. */
+
+//! This crate provides the `#[xpcom]` custom attribute. This custom attribute
+//! is used in order to implement [`xpcom`] interfaces.
+//!
+//! # Usage
+//!
+//! The easiest way to explain this crate is probably with a usage example. I'll
+//! show you the example, and then we'll destructure it and walk through what
+//! each component is doing.
+//!
+//! ```ignore
+//! // Declaring an XPCOM Struct
+//! #[xpcom(implement(nsIRunnable), atomic)]
+//! struct ImplRunnable {
+//! i: i32,
+//! }
+//!
+//! // Implementing methods on an XPCOM Struct
+//! impl ImplRunnable {
+//! unsafe fn Run(&self) -> nsresult {
+//! println!("{}", self.i);
+//! NS_OK
+//! }
+//! }
+//! ```
+//!
+//! ## Declaring an XPCOM Struct
+//!
+//! ```ignore
+//! // This derive should be placed on the initialization struct in order to
+//! // trigger the procedural macro.
+//! #[xpcom(
+//! // The implement argument should be passed the names of the IDL
+//! // interfaces which you want to implement. These can be separated by
+//! // commas if you want to implement multiple interfaces.
+//! //
+//! // Some methods use types which we cannot bind to in rust. Interfaces
+//! // like those cannot be implemented, and a compile-time error will occur
+//! // if they are listed in this attribute.
+//! implement(nsIRunnable),
+//!
+//! // The refcount kind can be specified as one of the following values:
+//! // * `atomic` == atomic reference count
+//! // ~= NS_DECL_THREADSAFE_ISUPPORTS in C++
+//! // * `nonatomic` == non atomic reference count
+//! // ~= NS_DECL_ISUPPORTS in C++
+//! atomic,
+//! )]
+//!
+//! // It is a compile time error to put the `#[xpcom]` attribute on
+//! // an enum, union, or tuple struct.
+//! //
+//! // The macro will generate both the named struct, as well as a version with
+//! // its name prefixed with `Init` which can be used to initialize the type.
+//! struct ImplRunnable {
+//! i: i32,
+//! }
+//! ```
+//!
+//! The above example will generate `ImplRunnable` and `InitImplRunnable`
+//! structs. The `ImplRunnable` struct will implement the [`nsIRunnable`] XPCOM
+//! interface, and cannot be constructed directly.
+//!
+//! The following methods will be automatically implemented on it:
+//!
+//! ```ignore
+//! // Automatic nsISupports implementation
+//! unsafe fn AddRef(&self) -> MozExternalRefCountType;
+//! unsafe fn Release(&self) -> MozExternalRefCountType;
+//! unsafe fn QueryInterface(&self, uuid: &nsIID, result: *mut *mut libc::c_void) -> nsresult;
+//!
+//! // Allocates and initializes a new instance of this type. The values will
+//! // be moved from the `Init` struct which is passed in.
+//! fn allocate(init: InitImplRunnable) -> RefPtr<Self>;
+//!
+//! // Helper for performing the `query_interface` operation to case to a
+//! // specific interface.
+//! fn query_interface<T: XpCom>(&self) -> Option<RefPtr<T>>;
+//!
+//! // Coerce function for cheaply casting to our base interfaces.
+//! fn coerce<T: ImplRunnableCoerce>(&self) -> &T;
+//! ```
+//!
+//! The [`RefCounted`] interface will also be implemented, so that the type can
+//! be used within the [`RefPtr`] type.
+//!
+//! The `coerce` and `query_interface` methods are backed by the generated
+//! `*Coerce` trait. This trait is impl-ed for every interface implemented by
+//! the trait. For example:
+//!
+//! ```ignore
+//! pub trait ImplRunnableCoerce {
+//! fn coerce_from(x: &ImplRunnable) -> &Self;
+//! }
+//! impl ImplRunnableCoerce for nsIRunnable { .. }
+//! impl ImplRunnableCoerce for nsISupports { .. }
+//! ```
+//!
+//! ## Implementing methods on an XPCOM Struct
+//!
+//! ```ignore
+//! // Methods should be implemented directly on the generated struct. All
+//! // methods other than `AddRef`, `Release`, and `QueryInterface` must be
+//! // implemented manually.
+//! impl ImplRunnable {
+//! // The method should have the same name as the corresponding C++ method.
+//! unsafe fn Run(&self) -> nsresult {
+//! // Fields defined on the template struct will be directly on the
+//! // generated struct.
+//! println!("{}", self.i);
+//! NS_OK
+//! }
+//! }
+//! ```
+//!
+//! XPCOM methods implemented in Rust have signatures similar to methods
+//! implemented in C++.
+//!
+//! ```ignore
+//! // nsISupports foo(in long long bar, in AString baz);
+//! unsafe fn Foo(&self, bar: i64, baz: *const nsAString,
+//! _retval: *mut *const nsISupports) -> nsresult;
+//!
+//! // AString qux(in nsISupports ham);
+//! unsafe fn Qux(&self, ham: *const nsISupports,
+//! _retval: *mut nsAString) -> nsresult;
+//! ```
+//!
+//! This is a little tedious, so the `xpcom_method!` macro provides a convenient
+//! way to generate wrappers around more idiomatic Rust methods.
+//!
+//! [`xpcom`]: ../xpcom/index.html
+//! [`nsIRunnable`]: ../xpcom/struct.nsIRunnable.html
+//! [`RefCounted`]: ../xpcom/struct.RefCounted.html
+//! [`RefPtr`]: ../xpcom/struct.RefPtr.html
+
+use lazy_static::lazy_static;
+use proc_macro2::{Span, TokenStream};
+use quote::{format_ident, quote, ToTokens};
+use std::collections::{HashMap, HashSet};
+use syn::meta::ParseNestedMeta;
+use syn::punctuated::Punctuated;
+use syn::{parse_macro_input, parse_quote, Field, Fields, Ident, ItemStruct, Token, Type};
+
+macro_rules! bail {
+ (@($t:expr), $s:expr) => {
+ return Err(syn::Error::new_spanned(&$t, &$s[..]))
+ };
+ (@($t:expr), $f:expr, $($e:expr),*) => {
+ return Err(syn::Error::new_spanned(&$t, &format!($f, $($e),*)[..]))
+ };
+ ($s:expr) => {
+ return Err(syn::Error::new(Span::call_site(), &$s[..]))
+ };
+ ($f:expr, $($e:expr),*) => {
+ return Err(syn::Error::new(Span::call_site(), &format!($f, $($e),*)[..]))
+ };
+}
+
+/* These are the structs generated by the rust_macros.py script */
+
+/// A single parameter to an XPCOM method.
+#[derive(Debug)]
+struct Param {
+ name: &'static str,
+ ty: &'static str,
+}
+
+/// A single method on an XPCOM interface.
+#[derive(Debug)]
+struct Method {
+ name: &'static str,
+ params: &'static [Param],
+ ret: &'static str,
+}
+
+/// An XPCOM interface. `methods` will be `Err("reason")` if the interface
+/// cannot be implemented in rust code.
+#[derive(Debug)]
+struct Interface {
+ name: &'static str,
+ base: Option<&'static str>,
+ sync: bool,
+ methods: Result<&'static [Method], &'static str>,
+}
+
+impl Interface {
+ fn base(&self) -> Option<&'static Interface> {
+ Some(IFACES[self.base?])
+ }
+
+ fn methods(&self) -> Result<&'static [Method], syn::Error> {
+ match self.methods {
+ Ok(methods) => Ok(methods),
+ Err(reason) => Err(syn::Error::new(
+ Span::call_site(),
+ format!(
+ "Interface {} cannot be implemented in rust \
+ because {} is not supported yet",
+ self.name, reason
+ ),
+ )),
+ }
+ }
+}
+
+lazy_static! {
+ /// This item contains the information generated by the procedural macro in
+ /// the form of a `HashMap` from interface names to their descriptions.
+ static ref IFACES: HashMap<&'static str, &'static Interface> = {
+ let lists: &[&[Interface]] =
+ include!(mozbuild::objdir_path!("dist/xpcrs/bt/all.rs"));
+
+ let mut hm = HashMap::new();
+ for &list in lists {
+ for iface in list {
+ hm.insert(iface.name, iface);
+ }
+ }
+ hm
+ };
+}
+
+/// The type of the reference count to use for the struct.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+enum RefcntKind {
+ Atomic,
+ NonAtomic,
+}
+
+/// Produces the tokens for the type representation.
+impl ToTokens for RefcntKind {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match *self {
+ RefcntKind::NonAtomic => quote!(xpcom::Refcnt).to_tokens(tokens),
+ RefcntKind::Atomic => quote!(xpcom::AtomicRefcnt).to_tokens(tokens),
+ }
+ }
+}
+
+/// Extract the fields list from the input struct.
+fn get_fields(si: &ItemStruct) -> Result<&Punctuated<Field, Token![,]>, syn::Error> {
+ match si.fields {
+ Fields::Named(ref named) => Ok(&named.named),
+ _ => bail!(@(si), "The initializer struct must be a standard named \
+ value struct definition"),
+ }
+}
+
+/// Takes the template struct in, and generates `ItemStruct` for the "real" and
+/// "init" structs.
+fn gen_structs(
+ template: &ItemStruct,
+ bases: &[&Interface],
+ refcnt_ty: RefcntKind,
+) -> Result<(ItemStruct, ItemStruct), syn::Error> {
+ let real_ident = &template.ident;
+ let init_ident = format_ident!("Init{}", real_ident);
+ let vis = &template.vis;
+
+ let bases = bases.iter().map(|base| {
+ let ident = format_ident!("__base_{}", base.name);
+ let vtable = format_ident!("{}VTable", base.name);
+ quote!(#ident : &'static xpcom::interfaces::#vtable)
+ });
+
+ let fields = get_fields(template)?;
+ let (impl_generics, _, where_clause) = template.generics.split_for_impl();
+ Ok((
+ parse_quote! {
+ #[repr(C)]
+ #vis struct #real_ident #impl_generics #where_clause {
+ #(#bases,)*
+ __refcnt: #refcnt_ty,
+ #fields
+ }
+ },
+ parse_quote! {
+ #vis struct #init_ident #impl_generics #where_clause {
+ #fields
+ }
+ },
+ ))
+}
+
+/// Generates the `extern "system"` methods which are actually included in the
+/// VTable for the given interface.
+///
+/// `idx` must be the offset in pointers of the pointer to this vtable in the
+/// struct `real`. This is soundness-critical, as it will be used to offset
+/// pointers received from xpcom back to the concrete implementation.
+fn gen_vtable_methods(
+ real: &ItemStruct,
+ iface: &Interface,
+ vtable_index: usize,
+) -> Result<TokenStream, syn::Error> {
+ let base_ty = format_ident!("{}", iface.name);
+
+ let base_methods = if let Some(base) = iface.base() {
+ gen_vtable_methods(real, base, vtable_index)?
+ } else {
+ quote! {}
+ };
+
+ let ty_name = &real.ident;
+ let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl();
+
+ let mut method_defs = Vec::new();
+ for method in iface.methods()? {
+ let ret = syn::parse_str::<Type>(method.ret)?;
+
+ let mut params = Vec::new();
+ let mut args = Vec::new();
+ for param in method.params {
+ let name = format_ident!("{}", param.name);
+ let ty = syn::parse_str::<Type>(param.ty)?;
+
+ params.push(quote! {#name : #ty,});
+ args.push(quote! {#name,});
+ }
+
+ let name = format_ident!("{}", method.name);
+ method_defs.push(quote! {
+ unsafe extern "system" fn #name #impl_generics (
+ this: *const #base_ty, #(#params)*
+ ) -> #ret #where_clause {
+ let this: &#ty_name #ty_generics =
+ ::xpcom::reexports::transmute_from_vtable_ptr(&this, #vtable_index);
+ this.#name(#(#args)*)
+ }
+ });
+ }
+
+ Ok(quote! {
+ #base_methods
+ #(#method_defs)*
+ })
+}
+
+/// Generates the VTable for a given base interface. This assumes that the
+/// implementations of each of the `extern "system"` methods are in scope.
+fn gen_inner_vtable(real: &ItemStruct, iface: &Interface) -> Result<TokenStream, syn::Error> {
+ let vtable_ty = format_ident!("{}VTable", iface.name);
+
+ // Generate the vtable for the base interface.
+ let base_vtable = if let Some(base) = iface.base() {
+ let vt = gen_inner_vtable(real, base)?;
+ quote! {__base: #vt,}
+ } else {
+ quote! {}
+ };
+
+ // Include each of the method definitions for this interface.
+ let (_, ty_generics, _) = real.generics.split_for_impl();
+ let turbofish = ty_generics.as_turbofish();
+ let vtable_init = iface
+ .methods()?
+ .iter()
+ .map(|method| {
+ let name = format_ident!("{}", method.name);
+ quote! { #name : #name #turbofish, }
+ })
+ .collect::<Vec<_>>();
+
+ Ok(quote!(#vtable_ty {
+ #base_vtable
+ #(#vtable_init)*
+ }))
+}
+
+fn gen_root_vtable(
+ real: &ItemStruct,
+ base: &Interface,
+ idx: usize,
+) -> Result<TokenStream, syn::Error> {
+ let field = format_ident!("__base_{}", base.name);
+ let vtable_ty = format_ident!("{}VTable", base.name);
+
+ let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl();
+ let turbofish = ty_generics.as_turbofish();
+
+ let methods = gen_vtable_methods(real, base, idx)?;
+ let vtable = gen_inner_vtable(real, base)?;
+
+ // Define the `recover_self` method. This performs an offset calculation to
+ // recover a pointer to the original struct from a pointer to the given
+ // VTable field.
+ Ok(quote! {#field: {
+ // The method implementations which will be used to build the vtable.
+ #methods
+
+ // The actual VTable definition. This is in a separate method in order
+ // to allow it to be generic.
+ #[inline]
+ fn get_vtable #impl_generics () -> &'static ::xpcom::reexports::VTableExtra<#vtable_ty> #where_clause {
+ &::xpcom::reexports::VTableExtra {
+ #[cfg(not(windows))]
+ offset: {
+ // NOTE: workaround required to avoid depending on the
+ // unstable const expression feature `const {}`.
+ const OFFSET: isize = -((::std::mem::size_of::<usize>() * #idx) as isize);
+ OFFSET
+ },
+ #[cfg(not(windows))]
+ typeinfo: 0 as *const _,
+ vtable: #vtable,
+ }
+ }
+ &get_vtable #turbofish ().vtable
+ },})
+}
+
+/// Generate the cast implementations. This generates the implementation details
+/// for the `Coerce` trait, and the `QueryInterface` method. The first return
+/// value is the `QueryInterface` implementation, and the second is the `Coerce`
+/// implementation.
+fn gen_casts(
+ seen: &mut HashSet<&'static str>,
+ iface: &Interface,
+ real: &ItemStruct,
+ coerce_name: &Ident,
+ vtable_field: &Ident,
+) -> Result<(TokenStream, TokenStream), syn::Error> {
+ if !seen.insert(iface.name) {
+ return Ok((quote! {}, quote! {}));
+ }
+
+ // Generate the cast implementations for the base interfaces.
+ let (base_qi, base_coerce) = if let Some(base) = iface.base() {
+ gen_casts(seen, base, real, coerce_name, vtable_field)?
+ } else {
+ (quote! {}, quote! {})
+ };
+
+ // Add the if statment to QueryInterface for the base class.
+ let base_name = format_ident!("{}", iface.name);
+
+ let qi = quote! {
+ #base_qi
+ if *uuid == #base_name::IID {
+ // Implement QueryInterface in terms of coercions.
+ self.addref();
+ *result = self.coerce::<#base_name>()
+ as *const #base_name
+ as *const ::xpcom::reexports::libc::c_void
+ as *mut ::xpcom::reexports::libc::c_void;
+ return ::xpcom::reexports::NS_OK;
+ }
+ };
+
+ // Add an implementation of the `*Coerce` trait for the base interface.
+ let name = &real.ident;
+ let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl();
+ let coerce = quote! {
+ #base_coerce
+
+ impl #impl_generics #coerce_name #ty_generics for ::xpcom::interfaces::#base_name #where_clause {
+ fn coerce_from(v: &#name #ty_generics) -> &Self {
+ unsafe {
+ // Get the address of the VTable field. This should be a
+ // pointer to a pointer to a vtable, which we can then cast
+ // into a pointer to our interface.
+ &*(&(v.#vtable_field)
+ as *const &'static _
+ as *const ::xpcom::interfaces::#base_name)
+ }
+ }
+ }
+ };
+
+ Ok((qi, coerce))
+}
+
+fn check_generics(generics: &syn::Generics) -> Result<(), syn::Error> {
+ for param in &generics.params {
+ let tp = match param {
+ syn::GenericParam::Type(tp) => tp,
+ syn::GenericParam::Lifetime(lp) => bail!(
+ @(lp),
+ "Cannot use #[xpcom] on types with lifetime parameters. \
+ Implementors of XPCOM interfaces must not contain non-'static \
+ lifetimes.",
+ ),
+ // XXX: Once const generics become stable, it may be as simple as
+ // removing this bail! to support them.
+ syn::GenericParam::Const(cp) => {
+ bail!(@(cp), "Cannot use #[xpcom] on types with const generics.")
+ }
+ };
+
+ let mut static_lt = false;
+ for bound in &tp.bounds {
+ match bound {
+ syn::TypeParamBound::Lifetime(lt) if lt.ident == "static" => {
+ static_lt = true;
+ break;
+ }
+ _ => {}
+ }
+ }
+
+ if !static_lt {
+ bail!(
+ @(param),
+ "Every generic parameter for xpcom implementation must have a \
+ 'static lifetime bound declared in the generics. Implicit \
+ lifetime bounds or lifetime bounds in where clauses are not \
+ detected by the macro and will be ignored. \
+ Implementors of XPCOM interfaces must not contain non-'static \
+ lifetimes.",
+ );
+ }
+ }
+ Ok(())
+}
+
+#[derive(Default)]
+struct Options {
+ bases: Vec<&'static Interface>,
+ refcnt: Option<RefcntKind>,
+}
+
+impl Options {
+ fn parse(&mut self, meta: ParseNestedMeta) -> Result<(), syn::Error> {
+ if meta.path.is_ident("atomic") || meta.path.is_ident("nonatomic") {
+ if self.refcnt.is_some() {
+ bail!(@(meta.path), "Duplicate refcnt atomicity specifier");
+ }
+ self.refcnt = Some(if meta.path.is_ident("atomic") {
+ RefcntKind::Atomic
+ } else {
+ RefcntKind::NonAtomic
+ });
+ Ok(())
+ } else if meta.path.is_ident("implement") {
+ meta.parse_nested_meta(|meta| {
+ let ident = match meta.path.get_ident() {
+ Some(ref iface) => iface.to_string(),
+ _ => bail!(@(meta.path), "Interface name must be unqualified"),
+ };
+ if let Some(&iface) = IFACES.get(ident.as_str()) {
+ self.bases.push(iface);
+ } else {
+ bail!(@(meta.path), "Invalid base interface `{}`", ident);
+ }
+ Ok(())
+ })
+ } else {
+ bail!(@(meta.path), "Unexpected argument to #[xpcom]")
+ }
+ }
+
+ fn validate(self) -> Result<Self, syn::Error> {
+ if self.bases.is_empty() {
+ bail!(
+ "Types with #[xpcom(..)] must implement at least one \
+ interface. Interfaces can be implemented by adding an \
+ implements(nsIFoo, nsIBar) parameter to the #[xpcom] attribute"
+ );
+ }
+
+ if self.refcnt.is_none() {
+ bail!("Must specify refcnt kind in #[xpcom] attribute");
+ }
+
+ Ok(self)
+ }
+}
+
+/// The root xpcom procedural macro definition.
+fn xpcom_impl(options: Options, template: ItemStruct) -> Result<TokenStream, syn::Error> {
+ check_generics(&template.generics)?;
+
+ let bases = options.bases;
+
+ // Ensure that all our base interface methods have unique names.
+ let mut method_names = HashMap::new();
+ for base in &bases {
+ for method in base.methods()? {
+ if let Some(existing) = method_names.insert(method.name, base.name) {
+ bail!(
+ "The method `{0}` is declared on both `{1}` and `{2}`,
+ but a Rust type cannot implement two methods with the \
+ same name. You can add the `[binaryname(Renamed{0})]` \
+ XPIDL attribute to one of the declarations to rename it.",
+ method.name,
+ existing,
+ base.name
+ );
+ }
+ }
+ }
+
+ // Determine what reference count type to use, and generate the real struct.
+ let refcnt_ty = options.refcnt.unwrap();
+ let (real, init) = gen_structs(&template, &bases, refcnt_ty)?;
+
+ let name_init = &init.ident;
+ let name = &real.ident;
+ let coerce_name = format_ident!("{}Coerce", name);
+
+ // Generate a VTable for each of the base interfaces.
+ let mut vtables = Vec::new();
+ for (idx, base) in bases.iter().enumerate() {
+ vtables.push(gen_root_vtable(&real, base, idx)?);
+ }
+
+ // Generate the field initializers for the final struct, moving each field
+ // out of the original __init struct.
+ let inits = get_fields(&init)?.iter().map(|field| {
+ let id = &field.ident;
+ quote! { #id : __init.#id, }
+ });
+
+ let vis = &real.vis;
+
+ // Generate the implementation for QueryInterface and Coerce.
+ let mut seen = HashSet::new();
+ let mut qi_impl = Vec::new();
+ let mut coerce_impl = Vec::new();
+ for base in &bases {
+ let (qi, coerce) = gen_casts(
+ &mut seen,
+ base,
+ &real,
+ &coerce_name,
+ &format_ident!("__base_{}", base.name),
+ )?;
+ qi_impl.push(qi);
+ coerce_impl.push(coerce);
+ }
+
+ let assert_sync = if bases.iter().any(|iface| iface.sync) {
+ quote! {
+ // Helper for asserting that for all instantiations, this
+ // object implements Send + Sync.
+ fn xpcom_type_must_be_send_sync<T: Send + Sync>(t: &T) {}
+ xpcom_type_must_be_send_sync(&*boxed);
+ }
+ } else {
+ quote! {}
+ };
+
+ let size_for_logs = if real.generics.params.is_empty() {
+ quote!(::std::mem::size_of::<Self>() as u32)
+ } else {
+ // Refcount logging requires all types with the same name to have the
+ // same size, and generics aren't taken into account when creating our
+ // name string, so we need to make sure that all possible instantiations
+ // report the same size. To do that, we fake a size based on the number
+ // of vtable pointers and the known refcount field.
+ let fake_size_npointers = bases.len() + 1;
+ quote!((::std::mem::size_of::<usize>() * #fake_size_npointers) as u32)
+ };
+
+ let (impl_generics, ty_generics, where_clause) = real.generics.split_for_impl();
+ let name_for_logs = quote!(
+ concat!(module_path!(), "::", stringify!(#name #ty_generics), "\0").as_ptr()
+ as *const ::xpcom::reexports::libc::c_char
+ );
+ Ok(quote! {
+ #init
+
+ #real
+
+ impl #impl_generics #name #ty_generics #where_clause {
+ /// This method is used for
+ fn allocate(__init: #name_init #ty_generics) -> ::xpcom::RefPtr<Self> {
+ #[allow(unused_imports)]
+ use ::xpcom::*;
+ #[allow(unused_imports)]
+ use ::xpcom::interfaces::*;
+ #[allow(unused_imports)]
+ use ::xpcom::reexports::{
+ libc, nsACString, nsAString, nsCString, nsString, nsresult
+ };
+
+ // Helper for asserting that for all instantiations, this
+ // object has the 'static lifetime.
+ fn xpcom_types_must_be_static<T: 'static>(t: &T) {}
+
+ unsafe {
+ // NOTE: This is split into multiple lines to make the
+ // output more readable.
+ let value = #name {
+ #(#vtables)*
+ __refcnt: #refcnt_ty::new(),
+ #(#inits)*
+ };
+ let boxed = ::std::boxed::Box::new(value);
+ xpcom_types_must_be_static(&*boxed);
+ #assert_sync
+ let raw = ::std::boxed::Box::into_raw(boxed);
+ ::xpcom::RefPtr::from_raw(raw).unwrap()
+ }
+ }
+
+ /// Automatically generated implementation of AddRef for nsISupports.
+ #vis unsafe fn AddRef(&self) -> ::xpcom::MozExternalRefCountType {
+ let new = self.__refcnt.inc();
+ ::xpcom::trace_refcnt::NS_LogAddRef(
+ self as *const _ as *mut ::xpcom::reexports::libc::c_void,
+ new as usize,
+ #name_for_logs,
+ #size_for_logs,
+ );
+ new
+ }
+
+ /// Automatically generated implementation of Release for nsISupports.
+ #vis unsafe fn Release(&self) -> ::xpcom::MozExternalRefCountType {
+ let new = self.__refcnt.dec();
+ ::xpcom::trace_refcnt::NS_LogRelease(
+ self as *const _ as *mut ::xpcom::reexports::libc::c_void,
+ new as usize,
+ #name_for_logs,
+ #size_for_logs,
+ );
+ if new == 0 {
+ // dealloc
+ ::std::mem::drop(::std::boxed::Box::from_raw(self as *const Self as *mut Self));
+ }
+ new
+ }
+
+ /// Automatically generated implementation of QueryInterface for
+ /// nsISupports.
+ #vis unsafe fn QueryInterface(&self,
+ uuid: *const ::xpcom::nsIID,
+ result: *mut *mut ::xpcom::reexports::libc::c_void)
+ -> ::xpcom::reexports::nsresult {
+ #[allow(unused_imports)]
+ use ::xpcom::*;
+ #[allow(unused_imports)]
+ use ::xpcom::interfaces::*;
+
+ #(#qi_impl)*
+
+ ::xpcom::reexports::NS_ERROR_NO_INTERFACE
+ }
+
+ /// Perform a QueryInterface call on this object, attempting to
+ /// dynamically cast it to the requested interface type. Returns
+ /// Some(RefPtr<T>) if the cast succeeded, and None otherwise.
+ #vis fn query_interface<XPCOM_InterfaceType: ::xpcom::XpCom>(&self)
+ -> ::std::option::Option<::xpcom::RefPtr<XPCOM_InterfaceType>>
+ {
+ let mut ga = ::xpcom::GetterAddrefs::<XPCOM_InterfaceType>::new();
+ unsafe {
+ if self.QueryInterface(&XPCOM_InterfaceType::IID, ga.void_ptr()).succeeded() {
+ ga.refptr()
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Coerce this type safely to any of the interfaces which it
+ /// implements without `AddRef`ing it.
+ #vis fn coerce<XPCOM_InterfaceType: #coerce_name #ty_generics>(&self) -> &XPCOM_InterfaceType {
+ XPCOM_InterfaceType::coerce_from(self)
+ }
+ }
+
+ /// This trait is implemented on the interface types which this
+ /// `#[xpcom]` type can be safely ane cheaply coerced to using the
+ /// `coerce` method.
+ ///
+ /// The trait and its method should usually not be used directly, but
+ /// rather acts as a trait bound and implementation for the `coerce`
+ /// methods.
+ #[doc(hidden)]
+ #vis trait #coerce_name #impl_generics #where_clause {
+ /// Convert a value of the `#[xpcom]` type into the implementing
+ /// interface type.
+ fn coerce_from(v: &#name #ty_generics) -> &Self;
+ }
+
+ #(#coerce_impl)*
+
+ unsafe impl #impl_generics ::xpcom::RefCounted for #name #ty_generics #where_clause {
+ unsafe fn addref(&self) {
+ self.AddRef();
+ }
+
+ unsafe fn release(&self) {
+ self.Release();
+ }
+ }
+ })
+}
+
+#[proc_macro_attribute]
+pub fn xpcom(
+ args: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ let mut options = Options::default();
+ let xpcom_parser = syn::meta::parser(|meta| options.parse(meta));
+ parse_macro_input!(args with xpcom_parser);
+ let input = parse_macro_input!(input as ItemStruct);
+ match options
+ .validate()
+ .and_then(|options| xpcom_impl(options, input))
+ {
+ Ok(ts) => ts.into(),
+ Err(err) => err.to_compile_error().into(),
+ }
+}