Other desktop-specific concerns

Releasing resources

Firefox Desktop does not allow “late writes” – i.e. writes that happen during shutdown. This is a particular concern to any component that holds an SQLite database. If they are garbage collected at shutdown with the database still open, then SQLite will write to disk when it closes its connection, triggering a late write error.

The typical way to avoid this is to have a shutdown method, which closes all resources. The JS code then adds a profile-before-change shutdown blocker, which calls shutdown(). See the ContentRelevancyManager.sys.mjs for an example.

64-bit integers

The JS number type can only store 53 bit integer values safely. If you have a u64 or i64 Rust value, make sure to only use values between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER. Values outside the range will result in JavaScript exceptions when they’re attempted to be passed/returned across the FFI.

Callback/trait interfaces and reference cycles

Callback interfaces should avoid storing UniFFI objects, since this can create a reference cycle that will never be broken. For example, component A stores reference to a callback interface B and B stores a reference to A. In this case there’s no way for the JavaScript garbage collector to detect the cycle, since it goes through FFI code.. This also applies to trait interfaces that have the with_foreign attribute.

There are several ways avoid this, listed in order of preference:

  • Prefer callback interfaces that store no properties at all.

  • Avoid properties that store Rust objects.

  • Use a WeakRef to store Rust objects.

  • As a last-resort, use callback interfaces that store Rust objects but make sure you have a system to manually break the reference cycles.

Note: Reference cycles can also be formed in Rust-only code, using Arcs so watch out for those too. However, cycles that go across the FFI using callback interfaces are particularly hard to spot.

Implementing trait interfaces in Rust

JavaScript callback interfaces can sometimes be avoided if UniFFI trait interfaces are used. These work similarly to callback interfaces, but can be implemented in either JavaScript or Rust.

Firefox Desktop has some support for using Rust code, for example [Rust code can access XPCOM components](https://firefox-source-docs.mozilla.org/writing-rust-code/xpcom.html. You may be able to leverage this to avoid JavaScript altogether and implementing the trait in Firefox Desktop Rust code instead.