Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f192cc9

Browse files
Remove some of our hacks around JSPromise now that we have better APIs. (#45591)
Our JSPromise hackery is causing some issues with the new dart roll. We should just use the `JSPromise` and `JSFunction` support to simplify this. Note that I still have to do *some* hackery to construct `JSPromise` objects and to invoke `JSFunction` objects, and eventually we'll probably be able to simplify this even more once those APIs are baked into `dart:js_interop`
1 parent 0774ddc commit f192cc9

File tree

7 files changed

+78
-97
lines changed

7 files changed

+78
-97
lines changed

lib/web_ui/lib/src/engine/app_bootstrap.dart

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'package:js/js_util.dart' show allowInterop;
6-
75
import 'configuration.dart';
86
import 'js_interop/js_loader.dart';
9-
import 'js_interop/js_promise.dart';
107

118
/// The type of a function that initializes an engine (in Dart).
129
typedef InitEngineFn = Future<void> Function([JsFlutterConfiguration? params]);
@@ -40,33 +37,26 @@ class AppBootstrap {
4037
// This is a convenience method that lets the programmer call "autoStart"
4138
// from JavaScript immediately after the main.dart.js has loaded.
4239
// Returns a promise that resolves to the Flutter app that was started.
43-
autoStart: allowInterop(() => futureToPromise(() async {
40+
autoStart: () async {
4441
await autoStart();
4542
// Return the App that was just started
4643
return _prepareFlutterApp();
47-
}())),
44+
},
4845
// Calls [_initEngine], and returns a JS Promise that resolves to an
4946
// app runner object.
50-
initializeEngine: allowInterop(([JsFlutterConfiguration? configuration]) => futureToPromise(() async {
47+
initializeEngine: ([JsFlutterConfiguration? configuration]) async {
5148
await _initializeEngine(configuration);
5249
return _prepareAppRunner();
53-
}()))
50+
}
5451
);
5552
}
5653

5754
/// Creates an appRunner that runs our encapsulated runApp function.
5855
FlutterAppRunner _prepareAppRunner() {
59-
return FlutterAppRunner(runApp: allowInterop(([RunAppFnParameters? params]) {
60-
// `params` coming from JS may be used to configure the run app method.
61-
return Promise<FlutterApp>(allowInterop((
62-
PromiseResolver<FlutterApp> resolve,
63-
PromiseRejecter _,
64-
) async {
65-
await _runApp();
66-
// Return the App that was just started
67-
resolve.resolve(_prepareFlutterApp());
68-
}));
69-
}));
56+
return FlutterAppRunner(runApp: ([RunAppFnParameters? params]) async {
57+
await _runApp();
58+
return _prepareFlutterApp();
59+
});
7060
}
7161

7262
/// Represents the App that was just started, and its JS API.

lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ Future<ByteBuffer> readVideoFramePixelsUnmodified(VideoFrame videoFrame) async {
142142
// In dart2wasm, Uint8List is not the same as a JS Uint8Array. So we
143143
// explicitly construct the JS object here.
144144
final JSUint8Array destination = createUint8ArrayFromLength(size);
145-
final JsPromise copyPromise = videoFrame.copyTo(destination);
145+
final JSPromise copyPromise = videoFrame.copyTo(destination);
146146
await promiseToFuture<void>(copyPromise);
147147

148148
// In dart2wasm, `toDart` incurs a copy here. On JS backends, this is a

lib/web_ui/lib/src/engine/js_interop/js_loader.dart

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ library js_loader;
88
import 'dart:js_interop';
99

1010
import 'package:js/js_util.dart' as js_util;
11-
12-
import '../configuration.dart';
13-
import 'js_promise.dart';
11+
import 'package:ui/src/engine.dart';
1412

1513
@JS()
1614
@staticInterop
@@ -34,6 +32,17 @@ extension FlutterLoaderExtension on FlutterLoader {
3432
bool get isAutoStart => !js_util.hasProperty(this, 'didCreateEngineInitializer');
3533
}
3634

35+
/// Typedef for the function that initializes the flutter engine.
36+
/// ///
37+
/// [JsFlutterConfiguration] comes from `../configuration.dart`. It is the same
38+
/// object that can be used to configure flutter "inline", through the
39+
/// (to be deprecated) `window.flutterConfiguration` object.
40+
typedef InitializeEngineFn = Future<FlutterAppRunner> Function([JsFlutterConfiguration?]);
41+
42+
/// Typedef for the `autoStart` function that can be called straight from an engine initializer instance.
43+
/// (Similar to [RunAppFn], but taking no specific "runApp" parameters).
44+
typedef ImmediateRunAppFn = Future<FlutterApp> Function();
45+
3746
// FlutterEngineInitializer
3847

3948
/// An object that allows the user to initialize the Engine of a Flutter App.
@@ -44,34 +53,34 @@ extension FlutterLoaderExtension on FlutterLoader {
4453
@anonymous
4554
@staticInterop
4655
abstract class FlutterEngineInitializer{
47-
external factory FlutterEngineInitializer({
56+
factory FlutterEngineInitializer({
4857
required InitializeEngineFn initializeEngine,
4958
required ImmediateRunAppFn autoStart,
59+
}) => FlutterEngineInitializer._(
60+
initializeEngine: (() => futureToPromise(initializeEngine())).toJS,
61+
autoStart: (() => futureToPromise(autoStart())).toJS,
62+
);
63+
external factory FlutterEngineInitializer._({
64+
required JSFunction initializeEngine,
65+
required JSFunction autoStart,
5066
});
5167
}
5268

53-
/// Typedef for the function that initializes the flutter engine.
54-
///
55-
/// [JsFlutterConfiguration] comes from `../configuration.dart`. It is the same
56-
/// object that can be used to configure flutter "inline", through the
57-
/// (to be deprecated) `window.flutterConfiguration` object.
58-
typedef InitializeEngineFn = Promise<FlutterAppRunner> Function([JsFlutterConfiguration?]);
59-
60-
/// Typedef for the `autoStart` function that can be called straight from an engine initializer instance.
61-
/// (Similar to [RunAppFn], but taking no specific "runApp" parameters).
62-
typedef ImmediateRunAppFn = Promise<FlutterApp> Function();
63-
6469
// FlutterAppRunner
6570

6671
/// A class that exposes a function that runs the Flutter app,
6772
/// and returns a promise of a FlutterAppCleaner.
6873
@JS()
6974
@anonymous
7075
@staticInterop
71-
abstract class FlutterAppRunner {
76+
abstract class FlutterAppRunner extends JSObject {
77+
factory FlutterAppRunner({required RunAppFn runApp,}) => FlutterAppRunner._(
78+
runApp: ((RunAppFnParameters args) => futureToPromise(runApp(args))).toJS
79+
);
80+
7281
/// Runs a flutter app
73-
external factory FlutterAppRunner({
74-
required RunAppFn runApp, // Returns an App
82+
external factory FlutterAppRunner._({
83+
required JSFunction runApp, // Returns an App
7584
});
7685
}
7786

@@ -81,18 +90,19 @@ abstract class FlutterAppRunner {
8190
@anonymous
8291
@staticInterop
8392
abstract class RunAppFnParameters {
93+
external factory RunAppFnParameters();
8494
}
8595

8696
/// Typedef for the function that runs the flutter app main entrypoint.
87-
typedef RunAppFn = Promise<FlutterApp> Function([RunAppFnParameters?]);
97+
typedef RunAppFn = Future<FlutterApp> Function([RunAppFnParameters?]);
8898

8999
// FlutterApp
90100

91101
/// A class that exposes the public API of a running Flutter Web App running.
92102
@JS()
93103
@anonymous
94104
@staticInterop
95-
abstract class FlutterApp {
105+
abstract class FlutterApp extends JSObject {
96106
/// Cleans a Flutter app
97107
external factory FlutterApp();
98108
}

lib/web_ui/lib/src/engine/js_interop/js_promise.dart

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,27 @@ import 'package:js/js_util.dart' as js_util;
1111

1212
import '../util.dart';
1313

14-
@JS()
15-
@staticInterop
16-
class PromiseResolver<T extends Object?> {}
17-
18-
extension PromiseResolverExtension<T extends Object?> on PromiseResolver<T> {
19-
void resolve(T result) => js_util.callMethod(this, 'call', <Object>[this, if (result != null) result]);
20-
}
21-
22-
@JS()
23-
@staticInterop
24-
class PromiseRejecter {}
25-
26-
extension PromiseRejecterExtension on PromiseRejecter {
27-
void reject(Object? error) => js_util.callMethod(this, 'call', <Object>[this, if (error != null) error]);
14+
extension CallExtension on JSFunction {
15+
external void call(JSAny? this_, JSAny? object);
2816
}
2917

30-
/// Type-safe JS Promises
3118
@JS('Promise')
32-
@staticInterop
33-
abstract class Promise<T extends Object?> {
34-
/// A constructor for a JS promise
35-
external factory Promise(PromiseExecutor<T> executor);
36-
}
19+
external JSAny get _promiseConstructor;
20+
21+
JSPromise createPromise(JSFunction executor) =>
22+
js_util.callConstructor(
23+
_promiseConstructor,
24+
<Object>[executor],
25+
);
3726

38-
/// The type of function that is used to create a Promise<T>
39-
typedef PromiseExecutor<T extends Object?> = void Function(PromiseResolver<T> resolve, PromiseRejecter reject);
4027

41-
Promise<T> futureToPromise<T extends Object>(Future<T> future) {
42-
return Promise<T>(js_util.allowInterop((PromiseResolver<T> resolver, PromiseRejecter rejecter) {
28+
JSPromise futureToPromise<T extends JSAny>(Future<T> future) {
29+
return createPromise((JSFunction resolver, JSFunction rejecter) {
4330
future.then(
44-
(T value) => resolver.resolve(value),
31+
(T value) => resolver.call(null, value),
4532
onError: (Object? error) {
4633
printWarning('Rejecting promise with error: $error');
47-
rejecter.reject(error);
34+
rejecter.call(null, null);
4835
});
49-
}));
36+
}.toJS);
5037
}

lib/web_ui/lib/src/engine/safe_browser_api.dart

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -196,20 +196,6 @@ bool get _defaultBrowserSupportsImageDecoder =>
196196
// enable it explicitly.
197197
bool get _isBrowserImageDecoderStable => browserEngine == BrowserEngine.blink;
198198

199-
/// The signature of the function passed to the constructor of JavaScript `Promise`.
200-
typedef JsPromiseCallback = void Function(void Function(Object? value) resolve, void Function(Object? error) reject);
201-
202-
/// Corresponds to JavaScript's `Promise`.
203-
///
204-
/// This type doesn't need any members. Instead, it should be first converted
205-
/// to Dart's [Future] using [promiseToFuture] then interacted with through the
206-
/// [Future] API.
207-
@JS('window.Promise')
208-
@staticInterop
209-
class JsPromise {
210-
external factory JsPromise(JsPromiseCallback callback);
211-
}
212-
213199
/// Corresponds to the browser's `ImageDecoder` type.
214200
///
215201
/// See also:
@@ -228,7 +214,7 @@ extension ImageDecoderExtension on ImageDecoder {
228214
external JSBoolean get _complete;
229215
bool get complete => _complete.toDart;
230216

231-
external JsPromise decode(DecodeOptions options);
217+
external JSPromise decode(DecodeOptions options);
232218
external JSVoid close();
233219
}
234220

@@ -302,8 +288,8 @@ extension VideoFrameExtension on VideoFrame {
302288
double allocationSize() => _allocationSize().toDartDouble;
303289

304290
@JS('copyTo')
305-
external JsPromise _copyTo(JSAny destination);
306-
JsPromise copyTo(Object destination) => _copyTo(destination.toJSAnyShallow);
291+
external JSPromise _copyTo(JSAny destination);
292+
JSPromise copyTo(Object destination) => _copyTo(destination.toJSAnyShallow);
307293

308294
@JS('format')
309295
external JSString? get _format;
@@ -344,7 +330,7 @@ extension VideoFrameExtension on VideoFrame {
344330
class ImageTrackList {}
345331

346332
extension ImageTrackListExtension on ImageTrackList {
347-
external JsPromise get ready;
333+
external JSPromise get ready;
348334
external ImageTrack? get selectedTrack;
349335
}
350336

lib/web_ui/test/engine/app_bootstrap_test.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,17 @@ void testMain() {
8888

8989
final FlutterEngineInitializer engineInitializer = bootstrap.prepareEngineInitializer();
9090

91-
final Object appInitializer = await promiseToFuture<Object>(callMethod<Object>(engineInitializer, 'initializeEngine', <Object?>[]));
92-
final Object maybeApp = await promiseToFuture<Object>(callMethod<Object>(appInitializer, 'runApp', <Object?>[]));
93-
91+
final Object appInitializer = await promiseToFuture<Object>(callMethod<Object>(
92+
engineInitializer,
93+
'initializeEngine',
94+
<Object?>[]
95+
));
96+
expect(appInitializer, isA<FlutterAppRunner>());
97+
final Object maybeApp = await promiseToFuture<Object>(callMethod<Object>(
98+
appInitializer,
99+
'runApp',
100+
<Object?>[RunAppFnParameters()]
101+
));
94102
expect(maybeApp, isA<FlutterApp>());
95103
expect(initCalled, 1, reason: 'initEngine should have been called.');
96104
expect(runCalled, 2, reason: 'runApp should have been called.');

lib/web_ui/test/engine/window_test.dart

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:js_interop';
67
import 'dart:js_util' as js_util;
78
import 'dart:typed_data';
89

@@ -331,19 +332,18 @@ Future<void> testMain() async {
331332
// The `orientation` property cannot be overridden, so this test overrides the entire `screen`.
332333
js_util.setProperty(domWindow, 'screen', js_util.jsify(<Object?, Object?>{
333334
'orientation': <Object?, Object?>{
334-
'lock': allowInterop((String lockType) {
335+
'lock': (String lockType) {
335336
lockCalls.add(lockType);
336-
return Promise<Object?>(allowInterop((PromiseResolver<Object?> resolve, PromiseRejecter reject) {
337-
if (!simulateError) {
338-
resolve.resolve(null);
339-
} else {
340-
reject.reject('Simulating error');
337+
return futureToPromise(() async {
338+
if (simulateError) {
339+
throw Error();
341340
}
342-
}));
343-
}),
344-
'unlock': allowInterop(() {
341+
return 0.toJS;
342+
}());
343+
}.toJS,
344+
'unlock': () {
345345
unlockCount += 1;
346-
}),
346+
}.toJS,
347347
},
348348
}));
349349

0 commit comments

Comments
 (0)