mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-01 08:48:25 +02:00
Added documents on how things work internally. Also updated the "Developing Rust Components" section. Added info on threading issues and removed info covered by the internals docs. Differential Revision: https://phabricator.services.mozilla.com/D252244
50 lines
3 KiB
Markdown
50 lines
3 KiB
Markdown
# JavaScript Callback Interfaces
|
|
|
|
## Background
|
|
|
|
* [UniFFI general documentation](https://mozilla.github.io/uniffi-rs/latest/internals/foreign_calls.html)
|
|
* [Threading concerns](../developing-rust-components/threading.md)
|
|
* [Lifting and Lowering](./lifting-and-lowering.md)
|
|
|
|
## JavaScript layer
|
|
|
|
The generated JavaScript bindings create a [UniFFICallbackHandler](https://searchfox.org/mozilla-central/rev/b5ab48b8c33faf53817cb2ef64c8523a469ef695/dom/chrome-webidl/UniFFI.webidl#75) for each callback interface.
|
|
This stores the callback interface implementations that are manually written by Firefox engineers.
|
|
These are stored in a map, where the key is an integer handle for the callback interface.
|
|
|
|
`UniFFICallbackHandler.callAsync` is used by the C++ layer to invoke callback interface methods.
|
|
See below for why we only currently have `callAsync()`.
|
|
`UniFFICallbackHandler.callAsync()` inputs:
|
|
|
|
* The object handle
|
|
* The method index
|
|
* Each argument for the callback method, after being lowered by JavaScript.
|
|
|
|
`UniFFICallbackHandler.callAsync()` returns a `UniFFIScaffoldingCallResult`.
|
|
Like with [Rust calls](./rust-calls.md), this is a `UniffiCallStatus` combined with a return value.
|
|
|
|
For each callback interface, the JavaScript layer calls `UniFFIScaffolding.registerCallbackHandler()` with the `UniFFICallbackHandler` for that interface.
|
|
Like with Rust calls, the bindings code generates a unique ID to identify each callback interface.
|
|
|
|
## C++ layer
|
|
|
|
The C++ layer acts as a bridge between the generated Rust code and the generated JavaScript code.
|
|
It registers a vtable with the Rust code where each field points to a generated C function that:
|
|
|
|
* Looks up the `UniFFICallbackHandler` registered with `UniFFIScaffolding.registerCallbackHandler()`
|
|
* Lifts all passed arguments and passes them to the `UniFFICallbackHandler`.
|
|
* For fire-and-forget calls:
|
|
* Calls `UniFFICallbackHandler.callAsync()` with the lifted arguments then discards the returned Promise.
|
|
* Note: sync calls are currently always wrapped to be "fire-and-forget" callbacks
|
|
* For async calls:
|
|
* Calls `UniFFICallbackHandler.callAsync()` with the lifted arguments getting back a Promise object.
|
|
* Appends a [PromiseNativeHandler](https://searchfox.org/mozilla-central/source/dom/promise/PromiseNativeHandler.h) to promise object.
|
|
* The `PromiseNativeHandler` completes the promise by calling the complete callback as described in the [UniFFI FFI internals doc](https://mozilla.github.io/uniffi-rs/latest/internals/async-ffi.html#completing-async-methods-with-complete_func).
|
|
* The `PromiseNativeHandler` also has code to handle a rejected promise by calling the complete callback with `RustCallStatusCode::UnexpectedError`.
|
|
|
|
## Freeing Callback Interface Objects
|
|
|
|
Each VTable also has a `uniffi_free` method.
|
|
When the Rust code drops the callback interface object, the generated UniFFI code arranges for `uniffi_free` to be called.
|
|
When this happens, the C++ generated function calls `UniFFICallbackHandler.destroy()`.
|
|
The generated JavaScript handles that by removing the entry from the callback interface map.
|