Skip to content

Commit

Permalink
Merge pull request #468 from cunarist/fix-web-load
Browse files Browse the repository at this point in the history
Fix JavaScript bindings on the web
  • Loading branch information
temeddix authored Oct 23, 2024
2 parents 924db8c + 51382db commit f8f154f
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 34 deletions.
4 changes: 2 additions & 2 deletions flutter_package/example/native/hub/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ crate-type = ["lib", "cdylib", "staticlib"]
rinf = "7.0.4"
prost = "0.13.0"
tokio = { version = "1", features = ["rt", "sync", "time", "macros"] }
tokio_with_wasm = { version = "0.7.1", features = [
tokio_with_wasm = { version = "0.7.2", features = [
"rt",
"sync",
"time",
"macros",
] }
wasm-bindgen = "0.2.93"
wasm-bindgen = "0.2.95"
messages = "0.3.1"
anyhow = "1.0.89"
sample_crate = { path = "../sample_crate" }
2 changes: 1 addition & 1 deletion flutter_package/example/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<meta name="description" content="A new Flutter project.">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
Expand Down
13 changes: 5 additions & 8 deletions flutter_package/lib/src/interface_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import 'load_web.dart';
import 'dart:typed_data';
import 'dart:js' as js;
import 'interface.dart';
import 'dart:async';
import 'dart:convert';
Expand All @@ -16,11 +15,11 @@ void setCompiledLibPathReal(String path) {
Future<void> prepareInterfaceReal(
AssignRustSignal assignRustSignal,
) async {
// Load the JavaScript module.
await loadJsFile();

// Listen to Rust via JavaScript
final jsObject = js.context['rinf'] as js.JsObject;
jsObject['send_rust_signal_extern'] = (
// Listen to Rust via JavaScript.
rinfBindingsObject['send_rust_signal_extern'] = (
int messageId,
Uint8List messageBytes,
Uint8List binary,
Expand All @@ -39,8 +38,7 @@ void startRustLogicReal() {
if (wasAlreadyLoaded) {
return;
}
final jsObject = js.context['rinf'] as js.JsObject;
jsObject.callMethod('start_rust_logic_extern', []);
wasmBindingsObject.callMethod('start_rust_logic_extern', []);
}

void stopRustLogicReal() {
Expand All @@ -52,8 +50,7 @@ void sendDartSignalReal(
Uint8List messageBytes,
Uint8List binary,
) {
final jsObject = js.context['rinf'] as js.JsObject;
jsObject.callMethod('send_dart_signal_extern', [
wasmBindingsObject.callMethod('send_dart_signal_extern', [
messageId,
messageBytes,
binary,
Expand Down
29 changes: 20 additions & 9 deletions flutter_package/lib/src/load_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,33 @@ import 'dart:async';

String? jsLibPath;

// When Dart performs hot restart,
// the `rinf` object is already defined
// as a global JavaScript variable.
final wasAlreadyLoaded = js.context.hasProperty('rinf');

void setJsLibPath(String path) {
jsLibPath = path;
}

bool wasAlreadyLoaded = false;
js.JsObject rinfBindingsObject = js.context['rinfBindings'];
js.JsObject wasmBindingsObject = js.context['wasmBindings'];

Future<void> loadJsFile() async {
// When Dart performs hot restart,
// the `rinfBindings` JavaScript object is already defined
// as a global JavaScript variable.
wasAlreadyLoaded = js.context.hasProperty('rinfBindings');

// Stop loading if it already has been done.
if (wasAlreadyLoaded) {
return;
}

// Create the namespace JavaScript object.
// This namespace object is used by Rust
// to call functions defined in Dart.
js.context['rinfBindings'] = js.JsObject.jsify({});

// Prepare to await the module load.
final loadCompleter = Completer<void>();
js.context['completeRinfLoad'] = loadCompleter.complete;
rinfBindingsObject['completeRinfLoad'] = loadCompleter.complete;

// Flutter app doesn't always have the top-level path of the domain.
// Sometimes, the flutter app might be placed in a lower path.
Expand All @@ -36,10 +47,10 @@ Future<void> loadJsFile() async {
scriptElement.type = 'module';
scriptElement.innerHtml = '''
import init, * as wasmBindings from "$fullUrl";
globalThis.wasmBindings = wasmBindings;
await init();
window.rinf = { ...wasmBindings };
completeRinfLoad();
delete window.completeRinfLoad;
rinfBindings.completeRinfLoad();
delete rinfBindings.completeRinfLoad;
''';
document.head!.append(scriptElement);

Expand Down
4 changes: 2 additions & 2 deletions flutter_package/template/native/hub/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ prost = "0.13.0"
tokio = { version = "1", features = ["rt", "macros"] }

# Uncomment below to target the web.
# tokio_with_wasm = { version = "0.7.1", features = ["rt", "macros"] }
# wasm-bindgen = "0.2.93"
# tokio_with_wasm = { version = "0.7.2", features = ["rt", "macros"] }
# wasm-bindgen = "0.2.95"
4 changes: 2 additions & 2 deletions rust_crate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ backtrace = { version = "0.3.69", optional = true }

[target.'cfg(target_family = "wasm")'.dependencies]
js-sys = "0.3.70"
wasm-bindgen = "0.2.93"
wasm-bindgen-futures = "0.4.43"
wasm-bindgen = "0.2.95"
wasm-bindgen-futures = "0.4.45"

[lints.clippy]
unwrap_used = "deny"
Expand Down
4 changes: 4 additions & 0 deletions rust_crate/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub enum RinfError {
NoDartIsolate,
CannotDecodeMessage,
NoSignalHandler,
NoBindings,
}

impl fmt::Display for RinfError {
Expand All @@ -20,6 +21,9 @@ impl fmt::Display for RinfError {
Self::NoSignalHandler => {
write!(f, "Could not find the handler for Dart signal")
}
Self::NoBindings => {
write!(f, "Rinf bindings are not ready")
}
}
}
}
Expand Down
21 changes: 11 additions & 10 deletions rust_crate/src/interface_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,29 @@ where

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = rinf, catch)]
// The reason this extern function is marked `catch`
// and returns a `Result` is that the
// `rinfBindings` JavaScript object created by Dart
// does not exist in web workers; it is only available
// in the main JavaScript thread. Loading the function
// fails in web workers.
#[wasm_bindgen(js_namespace = rinfBindings, catch)]
pub fn send_rust_signal_extern(
resource: i32,
message_bytes: Uint8Array,
binary: Uint8Array,
) -> Result<(), JsValue>; // catch the JS exception
) -> Result<(), JsValue>;
}

pub fn send_rust_signal_real(
message_id: i32,
message_bytes: Vec<u8>,
binary: Vec<u8>,
) -> Result<(), RinfError> {
match send_rust_signal_extern(
let result = send_rust_signal_extern(
message_id,
js_sys::Uint8Array::from(message_bytes.as_slice()),
js_sys::Uint8Array::from(binary.as_slice()),
) {
Ok(_) => Ok(()),
Err(e) => {
crate::debug_print!("An error occured during the launch: {e:?}");
Err(RinfError::NoSignalHandler)
}
}
);
result.map_err(|_| RinfError::NoBindings)
}

0 comments on commit f8f154f

Please sign in to comment.