зеркало из https://github.com/mozilla/gecko-dev.git
124 строки
4.5 KiB
Markdown
124 строки
4.5 KiB
Markdown
|
# 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.
|