[web] Migrate EventListener's to JS types. (flutter/engine#40566)

This commit is contained in:
joshualitt 2023-04-04 09:53:13 -07:00 committed by GitHub
parent cfb2ed4e06
commit bee9f91790
27 changed files with 146 additions and 178 deletions

View File

@ -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);

View File

@ -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';
@ -67,7 +68,7 @@ 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.
@ -75,7 +76,7 @@ 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)? _cachedContextRestoredListener;
DomEventListener? _cachedContextRestoredListener;
SkGrContext? _grContext;
int? _glContext;
@ -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 '
@ -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;
@ -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,

View File

@ -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
@ -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);
@ -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
@ -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()
@ -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);
}

View File

@ -65,7 +65,8 @@ class DebugCanvasReuseOverlay {
..append(
createDomHTMLButtonElement()
..text = 'Reset'
..addEventListener('click', (_) => _reset()),
..addEventListener('click',
createDomEventListener((_) => _reset())),
),
),
);

View File

@ -87,7 +87,7 @@ 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);
}
@ -95,7 +95,7 @@ class HtmlCodec implements ui.Codec {
completer.completeError(event);
});
imgElement.addEventListener('error', errorListener);
loadListener = allowInterop((DomEvent event) {
loadListener = createDomEventListener((DomEvent event) {
if (chunkCallback != null) {
chunkCallback!(100, 100);
}

View File

@ -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;
@ -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();
@ -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.
@ -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);

View File

@ -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);
@ -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();

View File

@ -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);
}

View File

@ -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].
@ -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,

View File

@ -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;
@ -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].
@ -1029,7 +1029,7 @@ 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() {
@ -1037,12 +1037,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
? 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);
}

View File

@ -2,6 +2,7 @@
// 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 'dart:math' as math;
import 'package:meta/meta.dart';
@ -74,7 +75,7 @@ class SafariPointerEventWorkaround {
static SafariPointerEventWorkaround instance = SafariPointerEventWorkaround();
void workAroundMissingPointerEvents() {
domDocument.addEventListener('touchstart', allowInterop((DomEvent event) {}));
domDocument.addEventListener('touchstart', createDomEventListener((DomEvent event) {}));
}
}
@ -204,10 +205,10 @@ class _Listener {
factory _Listener.register({
required String event,
required DomEventTarget target,
required DomEventListener handler,
required DartDomEventListener handler,
bool capture = false,
}) {
final DomEventListener jsHandler = allowInterop((DomEvent event) => handler(event));
final DomEventListener jsHandler = createDomEventListener(handler);
final _Listener listener = _Listener._(
event: event,
target: target,
@ -223,15 +224,14 @@ class _Listener {
factory _Listener.registerNative({
required String event,
required DomEventTarget target,
required DomEventListener handler,
required DomEventListener jsHandler,
bool capture = false,
bool passive = false,
}) {
final Object eventOptions = createPlainJsObject(<String, Object?>{
final Map<String, Object> eventOptions = <String, Object>{
'capture': capture,
'passive': passive,
});
final DomEventListener jsHandler = allowInterop((DomEvent event) => handler(event));
};
final _Listener listener = _Listener._(
event: event,
target: target,
@ -239,7 +239,7 @@ class _Listener {
useCapture: capture,
isNative: true,
);
addJsEventListener(target, event, jsHandler, eventOptions);
target.addEventListenerWithOptions(event, jsHandler, eventOptions);
return listener;
}
@ -253,7 +253,7 @@ class _Listener {
void unregister() {
if (isNative) {
removeJsEventListener(target, event, handler, useCapture);
target.removeEventListener(event, handler, useCapture);
} else {
target.removeEventListener(event, handler, useCapture);
}
@ -306,10 +306,10 @@ abstract class _BaseAdapter {
void addEventListener(
DomEventTarget target,
String eventName,
DomEventListener handler, {
DartDomEventListener handler, {
bool useCapture = true,
}) {
dynamic loggedHandler(DomEvent event) {
JSVoid loggedHandler(DomEvent event) {
if (_debugLogPointerEvents) {
if (domInstanceOfString(event, 'PointerEvent')) {
final DomPointerEvent pointerEvent = event as DomPointerEvent;
@ -495,11 +495,11 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
return data;
}
void _addWheelEventListener(DomEventListener handler) {
void _addWheelEventListener(DartDomEventListener handler) {
_listeners.add(_Listener.registerNative(
event: 'wheel',
target: glassPaneElement,
handler: (DomEvent event) => handler(event),
jsHandler: createDomEventListener(handler),
));
}

View File

@ -9,18 +9,17 @@ import '../engine.dart' show registerHotRestartListener;
import 'dom.dart';
import 'keyboard_binding.dart';
import 'platform_dispatcher.dart';
import 'safe_browser_api.dart';
import 'services.dart';
/// Provides keyboard bindings, such as the `flutter/keyevent` channel.
class RawKeyboard {
RawKeyboard._(this._onMacOs) {
_keydownListener = allowInterop((DomEvent event) {
_keydownListener = createDomEventListener((DomEvent event) {
_handleHtmlEvent(event);
});
domWindow.addEventListener('keydown', _keydownListener);
_keyupListener = allowInterop((DomEvent event) {
_keyupListener = createDomEventListener((DomEvent event) {
_handleHtmlEvent(event);
});
domWindow.addEventListener('keyup', _keyupListener);

View File

@ -27,16 +27,6 @@ import 'vector_math.dart';
export 'package:js/js.dart' show allowInterop;
/// Creates JavaScript object populated with [properties].
///
/// This is equivalent to writing `{}` in plain JavaScript.
Object createPlainJsObject([Map<String, Object?>? properties]) {
if (properties != null) {
return js_util.jsify(properties) as Object;
} else {
return js_util.newObject<Object>();
}
}
/// Returns true if [object] has property [name], false otherwise.
///
@ -80,37 +70,6 @@ Future<T> promiseToFuture<T>(Object jsPromise) {
/// A function that receives a benchmark [value] labeleb by [name].
typedef OnBenchmark = void Function(String name, double value);
/// Adds an event [listener] of type [type] to the [target].
///
/// [eventOptions] supply additional configuration parameters.
///
/// This is different from [DomElement.addEventListener] in that the listener
/// is added as a plain JavaScript function, as opposed to a Dart function.
///
/// To remove the listener, call [removeJsEventListener].
void addJsEventListener(Object target, String type, Function listener, Object eventOptions) {
js_util.callMethod<void>(
target,
'addEventListener', <dynamic>[
type,
listener,
eventOptions,
]
);
}
/// Removes an event listener that was added using [addJsEventListener].
void removeJsEventListener(Object target, String type, Function listener, Object eventOptions) {
js_util.callMethod<void>(
target,
'removeEventListener', <dynamic>[
type,
listener,
eventOptions,
]
);
}
/// Parses a string [source] into a double.
///
/// Uses the JavaScript `parseFloat` function instead of Dart's [double.parse]
@ -1076,7 +1035,7 @@ class OffScreenCanvas {
if (offScreenCanvas != null) {
offScreenCanvas!.convertToBlob().then((DomBlob value) {
final DomFileReader fileReader = createDomFileReader();
fileReader.addEventListener('load', allowInterop((DomEvent event) {
fileReader.addEventListener('load', createDomEventListener((DomEvent event) {
completer.complete(
js_util.getProperty<String>(js_util.getProperty<Object>(event, 'target'), 'result'),
);

View File

@ -6,7 +6,6 @@ import 'package:ui/ui.dart' as ui;
import '../dom.dart';
import '../platform_dispatcher.dart';
import '../safe_browser_api.dart';
import 'semantics.dart';
/// Adds increment/decrement event handling to a semantics object.
@ -25,7 +24,7 @@ class Incrementable extends RoleManager {
_element.type = 'range';
_element.setAttribute('role', 'slider');
_element.addEventListener('change', allowInterop((_) {
_element.addEventListener('change', createDomEventListener((_) {
if (_element.disabled!) {
return;
}

View File

@ -121,7 +121,7 @@ class Scrollable extends RoleManager {
};
semanticsObject.owner.addGestureModeListener(_gestureModeListener);
_scrollListener = allowInterop((_) {
_scrollListener = createDomEventListener((_) {
_recomputeScrollPosition();
});
semanticsObject.element.addEventListener('scroll', _scrollListener);

View File

@ -8,7 +8,6 @@ import 'package:meta/meta.dart';
import '../browser_detection.dart';
import '../dom.dart';
import '../safe_browser_api.dart';
import 'semantics.dart';
/// The maximum [semanticsActivationAttempts] before we give up waiting for
@ -180,7 +179,7 @@ class DesktopSemanticsEnabler extends SemanticsEnabler {
// Only listen to "click" because other kinds of events are reported via
// PointerBinding.
placeholder.addEventListener('click', allowInterop((DomEvent event) {
placeholder.addEventListener('click', createDomEventListener((DomEvent event) {
tryEnableSemantics(event);
}), true);
@ -374,7 +373,7 @@ class MobileSemanticsEnabler extends SemanticsEnabler {
// Only listen to "click" because other kinds of events are reported via
// PointerBinding.
placeholder.addEventListener('click', allowInterop((DomEvent event) {
placeholder.addEventListener('click', createDomEventListener((DomEvent event) {
tryEnableSemantics(event);
}), true);

View File

@ -6,7 +6,6 @@ import 'package:ui/ui.dart' as ui;
import '../dom.dart';
import '../platform_dispatcher.dart';
import '../safe_browser_api.dart';
import 'semantics.dart';
/// Listens to HTML "click" gestures detected by the browser.
@ -44,7 +43,7 @@ class Tappable extends RoleManager {
if (semanticsObject.hasAction(ui.SemanticsAction.tap) &&
!semanticsObject.hasFlag(ui.SemanticsFlag.isTextField)) {
if (_clickListener == null) {
_clickListener = allowInterop((_) {
_clickListener = createDomEventListener((_) {
if (semanticsObject.owner.gestureMode !=
GestureMode.browserGestures) {
return;

View File

@ -9,7 +9,6 @@ import '../browser_detection.dart';
import '../dom.dart';
import '../embedder.dart';
import '../platform_dispatcher.dart';
import '../safe_browser_api.dart';
import '../text_editing/text_editing.dart';
import 'semantics.dart';
@ -139,13 +138,13 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy {
// Subscribe to text and selection changes.
subscriptions.add(
DomSubscription(activeDomElement, 'input', allowInterop(handleChange)));
DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'keydown',
allowInterop(maybeSendAction)));
maybeSendAction));
subscriptions.add(
DomSubscription(domDocument, 'selectionchange',
allowInterop(handleChange)));
handleChange));
preventDefaultForMouseEvents();
}
@ -298,7 +297,7 @@ class TextField extends RoleManager {
void _initializeForBlink() {
_initializeEditableElement();
activeEditableElement.addEventListener('focus',
allowInterop((DomEvent event) {
createDomEventListener((DomEvent event) {
if (semanticsObject.owner.gestureMode != GestureMode.browserGestures) {
return;
}
@ -339,14 +338,14 @@ class TextField extends RoleManager {
num? lastPointerDownOffsetY;
semanticsObject.element.addEventListener('pointerdown',
allowInterop((DomEvent event) {
createDomEventListener((DomEvent event) {
final DomPointerEvent pointerEvent = event as DomPointerEvent;
lastPointerDownOffsetX = pointerEvent.clientX;
lastPointerDownOffsetY = pointerEvent.clientY;
}), true);
semanticsObject.element.addEventListener('pointerup',
allowInterop((DomEvent event) {
createDomEventListener((DomEvent event) {
final DomPointerEvent pointerEvent = event as DomPointerEvent;
if (lastPointerDownOffsetX != null) {
@ -396,7 +395,7 @@ class TextField extends RoleManager {
semanticsObject.element.removeAttribute('role');
activeEditableElement.addEventListener('blur',
allowInterop((DomEvent event) {
createDomEventListener((DomEvent event) {
semanticsObject.element.setAttribute('role', 'textbox');
activeEditableElement.remove();
SemanticsTextEditingStrategy._instance?.deactivate(this);

View File

@ -2,8 +2,9 @@
// 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 '../dom.dart';
import '../safe_browser_api.dart';
import 'text_editing.dart';
/// Provides default functionality for listening to HTML composition events.
@ -30,11 +31,11 @@ mixin CompositionAwareMixin {
static const String _kCompositionEnd = 'compositionend';
late final DomEventListener _compositionStartListener =
allowInterop(_handleCompositionStart);
createDomEventListener(_handleCompositionStart);
late final DomEventListener _compositionUpdateListener =
allowInterop(_handleCompositionUpdate);
createDomEventListener(_handleCompositionUpdate);
late final DomEventListener _compositionEndListener =
allowInterop(_handleCompositionEnd);
createDomEventListener(_handleCompositionEnd);
/// The currently composing text in the `domElement`.
///
@ -55,17 +56,17 @@ mixin CompositionAwareMixin {
domElement.removeEventListener(_kCompositionEnd, _compositionEndListener);
}
void _handleCompositionStart(DomEvent event) {
JSVoid _handleCompositionStart(DomEvent event) {
composingText = null;
}
void _handleCompositionUpdate(DomEvent event) {
JSVoid _handleCompositionUpdate(DomEvent event) {
if (domInstanceOfString(event, 'CompositionEvent')) {
composingText = (event as DomCompositionEvent).data;
}
}
void _handleCompositionEnd(DomEvent event) {
JSVoid _handleCompositionEnd(DomEvent event) {
composingText = null;
}

View File

@ -194,7 +194,7 @@ class EngineAutofillForm {
formElement.noValidate = true;
formElement.method = 'post';
formElement.action = '#';
formElement.addEventListener('submit', allowInterop((DomEvent e) {
formElement.addEventListener('submit', createDomEventListener((DomEvent e) {
e.preventDefault();
}));
@ -304,7 +304,7 @@ class EngineAutofillForm {
final DomElement element = elements![key]!;
subscriptions.add(
DomSubscription(element, 'input',
allowInterop((DomEvent e) {
(DomEvent e) {
if (items![key] == null) {
throw StateError(
'AutofillInfo must have a valid uniqueIdentifier.');
@ -312,7 +312,7 @@ class EngineAutofillForm {
final AutofillInfo autofillInfo = items![key]!;
handleChange(element, autofillInfo);
}
})));
}));
}
keys.forEach(addSubscriptionForKey);
@ -1231,23 +1231,23 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input',
allowInterop(handleChange)));
handleChange));
subscriptions.add(DomSubscription(activeDomElement, 'keydown',
allowInterop(maybeSendAction)));
maybeSendAction));
subscriptions.add(DomSubscription(domDocument, 'selectionchange',
allowInterop(handleChange)));
handleChange));
activeDomElement.addEventListener('beforeinput',
allowInterop(handleBeforeInput));
createDomEventListener(handleBeforeInput));
addCompositionEventHandlers(activeDomElement);
// Refocus on the activeDomElement after blur, so that user can keep editing the
// text field.
subscriptions.add(DomSubscription(activeDomElement, 'blur',
allowInterop((_) { activeDomElement.focus(); })));
(_) { activeDomElement.focus(); }));
preventDefaultForMouseEvents();
}
@ -1420,19 +1420,19 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements
/// flickering during selection by mouse.
void preventDefaultForMouseEvents() {
subscriptions.add(
DomSubscription(activeDomElement, 'mousedown', allowInterop((_) {
DomSubscription(activeDomElement, 'mousedown', (_) {
_.preventDefault();
})));
}));
subscriptions.add(
DomSubscription(activeDomElement, 'mouseup', allowInterop((_) {
DomSubscription(activeDomElement, 'mouseup', (_) {
_.preventDefault();
})));
}));
subscriptions.add(
DomSubscription(activeDomElement, 'mousemove', allowInterop((_) {
DomSubscription(activeDomElement, 'mousemove', (_) {
_.preventDefault();
})));
}));
}
}
@ -1525,25 +1525,25 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// Subscribe to text and selection changes.
subscriptions.add(DomSubscription(activeDomElement, 'input',
allowInterop(handleChange)));
handleChange));
subscriptions.add(DomSubscription(activeDomElement, 'keydown',
allowInterop(maybeSendAction)));
maybeSendAction));
subscriptions.add(DomSubscription(domDocument, 'selectionchange',
allowInterop(handleChange)));
handleChange));
activeDomElement.addEventListener('beforeinput',
allowInterop(handleBeforeInput));
createDomEventListener(handleBeforeInput));
addCompositionEventHandlers(activeDomElement);
// Position the DOM element after it is focused.
subscriptions.add(DomSubscription(activeDomElement, 'focus',
allowInterop((_) {
(_) {
// Cancel previous timer if exists.
_schedulePlacement();
})));
}));
_addTapListener();
@ -1567,14 +1567,14 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// input field was activated. If the time is too short, we re-focus the
// input element.
subscriptions.add(DomSubscription(activeDomElement, 'blur',
allowInterop((_) {
(_) {
final bool isFastCallback = blurWatch.elapsed < _blurFastCallbackInterval;
if (windowHasFocus && isFastCallback) {
activeDomElement.focus();
} else {
owner.sendTextConnectionClosedToFrameworkIfAny();
}
})));
}));
}
@override
@ -1610,7 +1610,7 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
/// [_positionInputElementTimer] timer is restarted. The element will be
/// placed to its correct position after [_delayBeforePlacement].
void _addTapListener() {
subscriptions.add(DomSubscription(activeDomElement, 'click', allowInterop((_) {
subscriptions.add(DomSubscription(activeDomElement, 'click', (_) {
// Check if the element is already positioned. If not this does not fall
// under `The user was using the long press, now they want to enter text
// via keyboard` journey.
@ -1621,7 +1621,7 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// Re-configure the timer to place the element.
_schedulePlacement();
}
})));
}));
}
void _schedulePlacement() {
@ -1676,24 +1676,24 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// Subscribe to text and selection changes.
subscriptions.add(
DomSubscription(activeDomElement, 'input', allowInterop(handleChange)));
DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(activeDomElement, 'keydown',
allowInterop(maybeSendAction)));
maybeSendAction));
subscriptions.add(
DomSubscription(domDocument, 'selectionchange',
allowInterop(handleChange)));
handleChange));
activeDomElement.addEventListener('beforeinput',
allowInterop(handleBeforeInput));
createDomEventListener(handleBeforeInput));
addCompositionEventHandlers(activeDomElement);
subscriptions.add(
DomSubscription(activeDomElement, 'blur',
allowInterop((_) {
(_) {
if (windowHasFocus) {
// Chrome on Android will hide the onscreen keyboard when you tap outside
// the text box. Instead, we want the framework to tell us to hide the
@ -1703,7 +1703,7 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
} else {
owner.sendTextConnectionClosedToFrameworkIfAny();
}
})));
}));
}
@override
@ -1742,14 +1742,14 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// Subscribe to text and selection changes.
subscriptions.add(
DomSubscription(activeDomElement, 'input', allowInterop(handleChange)));
DomSubscription(activeDomElement, 'input', handleChange));
subscriptions.add(
DomSubscription(
activeDomElement, 'keydown', allowInterop(maybeSendAction)));
activeDomElement, 'keydown', maybeSendAction));
activeDomElement.addEventListener('beforeinput',
allowInterop(handleBeforeInput));
createDomEventListener(handleBeforeInput));
addCompositionEventHandlers(activeDomElement);
@ -1770,16 +1770,16 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
DomSubscription(
activeDomElement,
'keyup',
allowInterop((DomEvent event) {
(DomEvent event) {
handleChange(event);
})));
}));
// In Firefox the context menu item "Select All" does not work without
// listening to onSelect. On the other browsers onSelectionChange is
// enough for covering "Select All" functionality.
subscriptions.add(
DomSubscription(
activeDomElement, 'select', allowInterop(handleChange)));
activeDomElement, 'select', handleChange));
// Refocus on the activeDomElement after blur, so that user can keep editing the
// text field.
@ -1787,9 +1787,9 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
DomSubscription(
activeDomElement,
'blur',
allowInterop((_) {
(_) {
_postponeFocus();
})));
}));
preventDefaultForMouseEvents();
}

View File

@ -4,7 +4,6 @@
import 'dart:async';
import 'package:js/js.dart';
import 'package:ui/src/engine/browser_detection.dart';
import 'package:ui/src/engine/dom.dart';
import 'package:ui/src/engine/window.dart';
@ -33,7 +32,7 @@ class FullPageDimensionsProvider extends DimensionsProvider {
_domResizeSubscription = DomSubscription(
resizeEventTarget,
'resize',
allowInterop(_onVisualViewportResize),
_onVisualViewportResize,
);
}

View File

@ -5,7 +5,6 @@
import 'package:meta/meta.dart';
import 'package:ui/src/engine/dom.dart';
import 'package:ui/src/engine/safe_browser_api.dart';
import 'package:ui/src/engine/view_embedder/hot_restart_cache_handler.dart';
import 'custom_element_embedding_strategy.dart';
@ -65,7 +64,7 @@ mixin _ContextMenu {
/// Listener for contextmenu events that prevents the browser's context menu
/// from being shown.
final DomEventListener _disablingContextMenuListener = allowInterop((DomEvent event) {
final DomEventListener _disablingContextMenuListener = createDomEventListener((DomEvent event) {
event.preventDefault();
});

View File

@ -53,7 +53,7 @@ void testMain() {
final JsUrlStrategy jsUrlStrategy = JsUrlStrategy(
getPath: allowInterop(() => '/initial'),
getState: allowInterop(() => state),
addPopStateListener: allowInterop((DomEventListener listener) => allowInterop(() {})),
addPopStateListener: allowInterop((DartDomEventListener listener) => allowInterop(() {})),
prepareExternalUrl: allowInterop((String value) => ''),
pushState: allowInterop((Object? newState, String title, String url) {
expect(newState is Map, true);

View File

@ -13,7 +13,6 @@ import 'package:ui/src/engine.dart' show flutterViewEmbedder;
import 'package:ui/src/engine/browser_detection.dart';
import 'package:ui/src/engine/dom.dart';
import 'package:ui/src/engine/initialization.dart';
import 'package:ui/src/engine/safe_browser_api.dart';
import 'package:ui/src/engine/services.dart';
import 'package:ui/src/engine/text_editing/autofill_hint.dart';
import 'package:ui/src/engine/text_editing/input_type.dart';
@ -903,7 +902,7 @@ Future<void> testMain() async {
defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement;
final Completer<bool> submittedForm = Completer<bool>();
formElement.addEventListener(
'submit', allowInterop((DomEvent event) =>
'submit', createDomEventListener((DomEvent event) =>
submittedForm.complete(true)));
const MethodCall clearClient = MethodCall('TextInput.clearClient');
@ -956,7 +955,7 @@ Future<void> testMain() async {
defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement;
final Completer<bool> submittedForm = Completer<bool>();
formElement.addEventListener(
'submit', allowInterop((DomEvent event) =>
'submit', createDomEventListener((DomEvent event) =>
submittedForm.complete(true)));
// Clear client is not called. The used requested context to be finalized.

View File

@ -437,7 +437,7 @@ Future<void> testMain() async {
test('dispatches browser event on flutter/service_worker channel', () async {
final Completer<void> completer = Completer<void>();
domWindow.addEventListener('flutter-first-frame',
allowInterop((DomEvent e) => completer.complete()));
createDomEventListener((DomEvent e) => completer.complete()));
final Zone innerZone = Zone.current.fork();
innerZone.runGuarded(() {

View File

@ -397,7 +397,7 @@ Future<HtmlImage> createTestImage({int width = 50, int height = 40}) {
ctx.fill();
final DomHTMLImageElement imageElement = createDomHTMLImageElement();
final Completer<HtmlImage> completer = Completer<HtmlImage>();
imageElement.addEventListener('load', allowInterop((DomEvent event) {
imageElement.addEventListener('load', createDomEventListener((DomEvent event) {
completer.complete(HtmlImage(imageElement, width, height));
}));
imageElement.src = js_util.callMethod<String>(canvas, 'toDataURL', <dynamic>[]);

View File

@ -184,7 +184,7 @@ MultiChannel<dynamic> _connectToServer() {
final DomWebSocket webSocket = createDomWebSocket(_currentUrl.queryParameters['managerUrl']!);
final StreamChannelController<dynamic> controller = StreamChannelController<dynamic>(sync: true);
webSocket.addEventListener('message', allowInterop((DomEvent message) {
webSocket.addEventListener('message', createDomEventListener((DomEvent message) {
final String data = (message as DomMessageEvent).data as String;
controller.local.sink.add(jsonDecode(data));
}));
@ -221,7 +221,7 @@ StreamChannel<dynamic> _connectToIframe(String url, int id) {
_domSubscriptions[id] = domSubscriptions;
_streamSubscriptions[id] = streamSubscriptions;
domSubscriptions.add(DomSubscription(domWindow, 'message',
allowInterop((DomEvent event) {
(DomEvent event) {
final DomMessageEvent message = event as DomMessageEvent;
// A message on the Window can theoretically come from any website. It's
// very unlikely that a malicious site would care about hacking someone's
@ -249,13 +249,13 @@ StreamChannel<dynamic> _connectToIframe(String url, int id) {
// loading the test.
controller.local.sink.add(message.data['data']);
}
})));
}));
channel.port1.start();
domSubscriptions.add(DomSubscription(channel.port1, 'message',
allowInterop((DomEvent message) {
(DomEvent message) {
controller.local.sink.add((message as DomMessageEvent).data['data']);
})));
}));
streamSubscriptions.add(controller.local.stream.listen((dynamic message) async {
await readyCompleter.future;