Skip to content

Commit

Permalink
[web] Migrate EventListener's to JS types. (flutter#40566)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshualitt authored Apr 4, 2023
1 parent e860fa8 commit 8edd3c0
Show file tree
Hide file tree
Showing 27 changed files with 146 additions and 178 deletions.
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3915,8 +3915,8 @@ Future<bool> _downloadCanvasKitJs(String url) {
canvasKitLoadCompleter.complete(false);
}

loadCallback = allowInterop(loadEventHandler);
errorCallback = allowInterop(errorEventHandler);
loadCallback = createDomEventListener(loadEventHandler);
errorCallback = createDomEventListener(errorEventHandler);

canvasKitScript.addEventListener('load', loadCallback);
canvasKitScript.addEventListener('error', errorCallback);
Expand Down
15 changes: 8 additions & 7 deletions lib/web_ui/lib/src/engine/canvaskit/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:js_interop';

import 'package:ui/ui.dart' as ui;

import '../browser_detection.dart';
import '../configuration.dart';
import '../dom.dart';
import '../platform_dispatcher.dart';
import '../safe_browser_api.dart';
import '../util.dart';
import '../window.dart';
import 'canvas.dart';
Expand Down Expand Up @@ -67,15 +68,15 @@ class Surface {
/// We must cache this function because each time we access the tear-off it
/// creates a new object, meaning we won't be able to remove this listener
/// later.
void Function(DomEvent)? _cachedContextLostListener;
DomEventListener? _cachedContextLostListener;

/// A cached copy of the most recently created `webglcontextrestored`
/// listener.
///
/// We must cache this function because each time we access the tear-off it
/// creates a new object, meaning we won't be able to remove this listener
/// later.
void Function(DomEvent)? _cachedContextRestoredListener;
DomEventListener? _cachedContextRestoredListener;

SkGrContext? _grContext;
int? _glContext;
Expand Down Expand Up @@ -268,7 +269,7 @@ class Surface {
htmlCanvas!.style.transform = 'translate(0, -${offset}px)';
}

void _contextRestoredListener(DomEvent event) {
JSVoid _contextRestoredListener(DomEvent event) {
assert(
_contextLost,
'Received "webglcontextrestored" event but never received '
Expand All @@ -280,7 +281,7 @@ class Surface {
event.preventDefault();
}

void _contextLostListener(DomEvent event) {
JSVoid _contextLostListener(DomEvent event) {
assert(event.target == htmlCanvas,
'Received a context lost event for a disposed canvas');
final SurfaceFactory factory = SurfaceFactory.instance;
Expand Down Expand Up @@ -344,8 +345,8 @@ class Surface {
// notification. When we receive this notification we force a new context.
//
// See also: https://www.khronos.org/webgl/wiki/HandlingContextLost
_cachedContextRestoredListener = allowInterop(_contextRestoredListener);
_cachedContextLostListener = allowInterop(_contextLostListener);
_cachedContextRestoredListener = createDomEventListener(_contextRestoredListener);
_cachedContextLostListener = createDomEventListener(_contextLostListener);
htmlCanvas.addEventListener(
'webglcontextlost',
_cachedContextLostListener,
Expand Down
28 changes: 22 additions & 6 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import 'browser_detection.dart';
/// `JSUndefined`.
extension ObjectToJSAnyExtension on Object {
JSAny get toJSAnyShallow {
if (isWasm) {
if (isWasm) {
return toJSAnyDeep;
} else {
// TODO(joshualitt): remove this cast when we reify JS types on JS
Expand Down Expand Up @@ -334,6 +334,13 @@ extension DomEventTargetExtension on DomEventTarget {
}
}

@JS('addEventListener')
external JSVoid _addEventListener3(
JSString type, DomEventListener listener, JSAny options);
void addEventListenerWithOptions(String type, DomEventListener listener,
Map<String, Object> options) =>
_addEventListener3(type.toJS, listener, options.toJSAnyDeep);

@JS('removeEventListener')
external JSVoid _removeEventListener1(
JSString type, DomEventListener listener);
Expand All @@ -356,7 +363,14 @@ extension DomEventTargetExtension on DomEventTarget {
bool dispatchEvent(DomEvent event) => _dispatchEvent(event).toDart;
}

typedef DomEventListener = void Function(DomEvent event);
typedef DartDomEventListener = JSVoid Function(DomEvent event);

@JS()
@staticInterop
class DomEventListener {}

DomEventListener createDomEventListener(DartDomEventListener listener) =>
listener.toJS as DomEventListener;

@JS()
@staticInterop
Expand Down Expand Up @@ -2260,10 +2274,10 @@ extension DomMediaQueryListExtension on DomMediaQueryList {
bool get matches => _matches.toDart;

@JS('addListener')
external JSVoid addListener(JSFunction? listener);
external JSVoid addListener(DomEventListener? listener);

@JS('removeListener')
external JSVoid removeListener(JSFunction? listener);
external JSVoid removeListener(DomEventListener? listener);
}

@JS()
Expand Down Expand Up @@ -2829,8 +2843,10 @@ extension DomScreenOrientationExtension on DomScreenOrientation {
// remove the listener. Caller is still responsible for calling [allowInterop]
// on the listener before creating the subscription.
class DomSubscription {
DomSubscription(this.target, String typeString, this.listener)
: type = typeString.toJS {
DomSubscription(
this.target, String typeString, DartDomEventListener dartListener)
: type = typeString.toJS,
listener = createDomEventListener(dartListener) {
target._addEventListener1(type, listener);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class DebugCanvasReuseOverlay {
..append(
createDomHTMLButtonElement()
..text = 'Reset'
..addEventListener('click', (_) => _reset()),
..addEventListener('click',
createDomEventListener((_) => _reset())),
),
),
);
Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/html_image_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ class HtmlCodec implements ui.Codec {
// on the main thread, and may cause dropped framed.
late DomEventListener errorListener;
DomEventListener? loadListener;
errorListener = allowInterop((DomEvent event) {
errorListener = createDomEventListener((DomEvent event) {
if (loadListener != null) {
imgElement.removeEventListener('load', loadListener);
}
imgElement.removeEventListener('error', errorListener);
completer.completeError(event);
});
imgElement.addEventListener('error', errorListener);
loadListener = allowInterop((DomEvent event) {
loadListener = createDomEventListener((DomEvent event) {
if (chunkCallback != null) {
chunkCallback!(100, 100);
}
Expand Down
24 changes: 12 additions & 12 deletions lib/web_ui/lib/src/engine/keyboard_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:js_interop';

import 'package:meta/meta.dart';
import 'package:ui/ui.dart' as ui;
import 'package:web_locale_keymap/web_locale_keymap.dart' as locale_keymap;
Expand All @@ -11,7 +13,6 @@ import 'browser_detection.dart';
import 'dom.dart';
import 'key_map.g.dart';
import 'platform_dispatcher.dart';
import 'safe_browser_api.dart';
import 'semantics.dart';

typedef _VoidCallback = void Function();
Expand Down Expand Up @@ -100,13 +101,13 @@ ValueGetter<T> _cached<T>(ValueGetter<T> body) {

class KeyboardBinding {
KeyboardBinding._() {
_addEventListener('keydown', allowInterop((DomEvent domEvent) {
_addEventListener('keydown', (DomEvent domEvent) {
final FlutterHtmlKeyboardEvent event = FlutterHtmlKeyboardEvent(domEvent as DomKeyboardEvent);
return _converter.handleEvent(event);
}));
_addEventListener('keyup', allowInterop((DomEvent event) {
return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as DomKeyboardEvent));
}));
_converter.handleEvent(event);
});
_addEventListener('keyup', (DomEvent event) {
_converter.handleEvent(FlutterHtmlKeyboardEvent(event as DomKeyboardEvent));
});
}

/// The singleton instance of this object.
Expand Down Expand Up @@ -138,18 +139,17 @@ class KeyboardBinding {
);
final Map<String, DomEventListener> _listeners = <String, DomEventListener>{};

void _addEventListener(String eventName, DomEventListener handler) {
dynamic loggedHandler(DomEvent event) {
void _addEventListener(String eventName, DartDomEventListener handler) {
JSVoid loggedHandler(DomEvent event) {
if (_debugLogKeyEvents) {
print(event.type);
}
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) {
return handler(event);
handler(event);
}
return null;
}

final DomEventListener wrappedHandler = allowInterop(loggedHandler);
final DomEventListener wrappedHandler = createDomEventListener(loggedHandler);
assert(!_listeners.containsKey(eventName));
_listeners[eventName] = wrappedHandler;
domWindow.addEventListener(eventName, wrappedHandler, true);
Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ typedef _PathGetter = String Function();

typedef _StateGetter = Object? Function();

typedef _AddPopStateListener = ui.VoidCallback Function(DomEventListener);
typedef _AddPopStateListener = ui.VoidCallback Function(DartDomEventListener);

typedef _StringToString = String Function(String);

Expand Down Expand Up @@ -47,7 +47,7 @@ abstract class JsUrlStrategy {
extension JsUrlStrategyExtension on JsUrlStrategy {
/// Adds a listener to the `popstate` event and returns a function that, when
/// invoked, removes the listener.
external ui.VoidCallback addPopStateListener(DomEventListener fn);
external ui.VoidCallback addPopStateListener(DartDomEventListener fn);

/// Returns the active path in the browser.
external String getPath();
Expand Down
3 changes: 1 addition & 2 deletions lib/web_ui/lib/src/engine/navigation/url_strategy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ class HashUrlStrategy extends ui_web.UrlStrategy {

@override
ui.VoidCallback addPopStateListener(ui_web.PopStateListener fn) {
final DomEventListener wrappedFn =
allowInterop((DomEvent event) => fn((event as DomPopStateEvent).state));
final DomEventListener wrappedFn = createDomEventListener(fn);
_platformLocation.addPopStateListener(wrappedFn);
return () => _platformLocation.removePopStateListener(wrappedFn);
}
Expand Down
5 changes: 2 additions & 3 deletions lib/web_ui/lib/src/engine/picture.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import 'dom.dart';
import 'html/bitmap_canvas.dart';
import 'html/recording_canvas.dart';
import 'html_image_codec.dart';
import 'safe_browser_api.dart';
import 'util.dart';

/// An implementation of [ui.PictureRecorder] backed by a [RecordingCanvas].
Expand Down Expand Up @@ -74,13 +73,13 @@ class EnginePicture implements ui.Picture {
// Ignoring the returned futures from onError and onLoad because we're
// communicating through the `onImageLoaded` completer.
late final DomEventListener errorListener;
errorListener = allowInterop((DomEvent event) {
errorListener = createDomEventListener((DomEvent event) {
onImageLoaded.completeError(event);
imageElement.removeEventListener('error', errorListener);
});
imageElement.addEventListener('error', errorListener);
late final DomEventListener loadListener;
loadListener = allowInterop((DomEvent event) {
loadListener = createDomEventListener((DomEvent event) {
onImageLoaded.complete(HtmlImage(
imageElement,
width,
Expand Down
14 changes: 7 additions & 7 deletions lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class HighContrastSupport {

/// Reference to css media query that indicates whether high contrast is on.
final DomMediaQueryList _highContrastMediaQuery = domWindow.matchMedia(_highContrastMediaQueryString);
late final JSFunction _onHighContrastChangeListener =
_onHighContrastChange.toJS;
late final DomEventListener _onHighContrastChangeListener =
createDomEventListener(_onHighContrastChange);

bool get isHighContrastEnabled => _highContrastMediaQuery.matches;

Expand Down Expand Up @@ -799,11 +799,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
}
updateLocales(); // First time, for good measure.
_onLocaleChangedSubscription =
DomSubscription(domWindow, 'languagechange', allowInterop((DomEvent _) {
DomSubscription(domWindow, 'languagechange', (DomEvent _) {
// Update internal config, then propagate the changes.
updateLocales();
invokeOnLocaleChanged();
}));
});
}

/// Removes the [_onLocaleChangedSubscription].
Expand Down Expand Up @@ -1029,20 +1029,20 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
/// A callback that is invoked whenever [_brightnessMediaQuery] changes value.
///
/// Updates the [_platformBrightness] with the new user preference.
JSFunction? _brightnessMediaQueryListener;
DomEventListener? _brightnessMediaQueryListener;

/// Set the callback function for listening changes in [_brightnessMediaQuery] value.
void _addBrightnessMediaQueryListener() {
_updatePlatformBrightness(_brightnessMediaQuery.matches
? ui.Brightness.dark
: ui.Brightness.light);

_brightnessMediaQueryListener = (DomEvent event) {
_brightnessMediaQueryListener = createDomEventListener((DomEvent event) {
final DomMediaQueryListEvent mqEvent =
event as DomMediaQueryListEvent;
_updatePlatformBrightness(
mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light);
}.toJS;
});
_brightnessMediaQuery.addListener(_brightnessMediaQueryListener);
}

Expand Down
Loading

0 comments on commit 8edd3c0

Please sign in to comment.