summaryrefslogtreecommitdiffstats
path: root/docs/writing-rust-code/xpcom.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/writing-rust-code/xpcom.md')
-rw-r--r--docs/writing-rust-code/xpcom.md123
1 files changed, 123 insertions, 0 deletions
diff --git a/docs/writing-rust-code/xpcom.md b/docs/writing-rust-code/xpcom.md
new file mode 100644
index 0000000000..bf900e03f8
--- /dev/null
+++ b/docs/writing-rust-code/xpcom.md
@@ -0,0 +1,123 @@
+# XPCOM components in Rust
+
+XPCOM components can be written in Rust.
+
+## A tiny example
+
+The following example shows a new type that implements `nsIObserver`.
+
+First, create a new empty crate (e.g. with `cargo init --lib`), and add the
+following dependencies in its `Cargo.toml` file.
+
+```toml
+[dependencies]
+libc = "0.2"
+nserror = { path = "../../../xpcom/rust/nserror" }
+nsstring = { path = "../../../xpcom/rust/nsstring" }
+xpcom = { path = "../../../xpcom/rust/xpcom" }
+```
+
+(The number of `../` occurrences will depend on the depth of the crate in the
+file hierarchy.)
+
+Next hook it into the build system according to the [build
+documentation](../build/buildsystem/rust.html).
+
+The Rust code will need to import some basic types. `xpcom::interfaces`
+contains all the usual `nsI` interfaces.
+
+```rust
+use libc::c_char;
+use nserror::nsresult;
+use std::sync::atomic::{AtomicBool, Ordering};
+use xpcom::{interfaces::nsISupports, RefPtr};
+```
+
+The next part declares the implementation.
+
+```rust
+#[derive(xpcom)]
+#[xpimplements(nsIObserver)]
+#[refcnt = "atomic"]
+struct InitMyObserver {
+ ran: AtomicBool,
+}
+```
+
+It defines an initializer struct, prefixed with `Init`, with three attributes.
+- Some `derive` magic.
+- An `xpimplements` declaration naming the interface(s) being implemented.
+- The reference count type.
+
+Next, all interface methods are declared in the `impl` block as `unsafe` methods.
+
+```rust
+impl MyObserver {
+ #[allow(non_snake_case)]
+ unsafe fn Observe(
+ &self,
+ _subject: *const nsISupports,
+ _topic: *const c_char,
+ _data: *const i16,
+ ) -> nsresult {
+ self.ran.store(true, Ordering::SeqCst);
+ nserror::NS_OK
+ }
+}
+```
+
+These methods always take `&self`, not `&mut self`, so we need to use interior
+mutability: `AtomicBool`, `RefCell`, `Cell`, etc.
+
+XPCOM methods are unsafe by default, but the
+[xpcom_method!](https://searchfox.org/mozilla-central/source/xpcom/rust/xpcom/src/method.rs)
+macro can be used to clean this up. It also takes care of null-checking and
+hiding pointers behind references, lets you return a `Result` instead of an
+`nsresult,` and so on.
+
+To use this type within Rust code, do something like the following.
+
+```rust
+let observer = MyObserver::allocate(InitMyObserver {
+ ran: AtomicBool::new(false),
+});
+let rv = unsafe {
+ observer.Observe(x.coerce(),
+ cstr!("some-topic").as_ptr(),
+ ptr::null())
+};
+assert!(rv.succeeded());
+```
+
+The implementation has an (auto-generated) `allocate` method that takes in an
+initialization struct, and returns a `RefPtr` to the instance.
+
+`coerce` casts any XPCOM object to one of its base interfaces; in this case,
+the base interface is `nsISupports`. In C++, this would be handled
+automatically through inheritance, but Rust doesn’t have inheritance, so the
+conversion must be explicit.
+
+## Bigger examples
+
+The following XPCOM components are written in Rust.
+- [kvstore](https://searchfox.org/mozilla-central/source/toolkit/components/kvstore),
+ which exposes the LMDB key-value store (via the [Rkv
+ library](https://docs.rs/rkv)) The API is asynchronous, using `moz_task` to
+ schedule all I/O on a background thread, and supports getting, setting, and
+ iterating over keys.
+- [cert_storage](https://searchfox.org/mozilla-central/source/security/manager/ssl/cert_storage),
+ which stores lists of [revoked intermediate certificates](https://blog.mozilla.org/security/2015/03/03/revoking-intermediate-certificates-introducing-onecrl/).
+- [bookmark_sync](https://searchfox.org/mozilla-central/source/toolkit/components/places/bookmark_sync),
+ which [merges](https://mozilla.github.io/dogear) bookmarks from Firefox Sync
+ with bookmarks in the Places database.
+- [webext_storage_bridge](https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage/webext_storage_bridge),
+ which powers the WebExtension storage.sync API. It's a self-contained example
+ that pulls in a crate from application-services for the heavy lifting, wraps
+ that up in a Rust XPCOM component, and then wraps the component in a JS
+ interface. There's also some boilerplate there around adding a
+ `components.conf` file, and a dummy C++ header that declares the component
+ constructor.
+- [firefox-accounts-bridge](https://searchfox.org/mozilla-central/source/services/fxaccounts/rust-bridge/firefox-accounts-bridge),
+ which wraps the Rust Firefox Accounts client with which we eventually want to
+ replace our creaky JS implementation. It has a lot of the same patterns as
+ webext_storage_bridge.