From 15280cb88e996dfd65745c7da22e2e4cff693508 Mon Sep 17 00:00:00 2001 From: joshualitt Date: Thu, 2 Jun 2022 10:06:49 -0700 Subject: [PATCH] [web] Migrate Flutter Web DOM usage to JS static interop - 23. (flutter/engine#33351) --- .../lib/web_ui/lib/src/engine/dom.dart | 27 +++++++++++- .../lib/web_ui/lib/src/engine/embedder.dart | 2 +- .../lib/web_ui/lib/src/engine/keyboard.dart | 37 +++++++++-------- .../lib/src/engine/keyboard_binding.dart | 41 ++++++++++--------- 4 files changed, 67 insertions(+), 40 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart index 10179a82757..73d75c615dc 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart @@ -20,7 +20,7 @@ import 'package:js/js_util.dart' as js_util; @JS() @staticInterop -class DomWindow {} +class DomWindow extends DomEventTarget {} extension DomWindowExtension on DomWindow { external DomConsole get console; @@ -122,6 +122,8 @@ class DomEvent {} extension DomEventExtension on DomEvent { external DomEventTarget? get target; + external num? get timeStamp; + external String get type; external void preventDefault(); external void stopPropagation(); } @@ -315,7 +317,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { String get flexDirection => getPropertyValue('flex-direction'); String get alignItems => getPropertyValue('align-items'); String get margin => getPropertyValue('margin'); - String get background=> getPropertyValue('background'); + String get background => getPropertyValue('background'); external String getPropertyValue(String property); void setProperty(String propertyName, String value, [String? priority]) { @@ -670,6 +672,27 @@ extension DomResponseExtension on DomResponse { js_util.promiseToFuture(js_util.callMethod(this, 'text', [])); } +@JS() +@staticInterop +class DomUIEvent extends DomEvent {} + +@JS() +@staticInterop +class DomKeyboardEvent extends DomUIEvent {} + +extension DomKeyboardEventExtension on DomKeyboardEvent { + external bool get altKey; + external String? get code; + external bool get ctrlKey; + external String? get key; + external int get keyCode; + external int get location; + external bool get metaKey; + external bool? get repeat; + external bool get shiftKey; + external bool getModifierState(String keyArg); +} + Object? domGetConstructor(String constructorName) => js_util.getProperty(domWindow, constructorName); diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/embedder.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/embedder.dart index 0116e1a6685..955522bac41 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/embedder.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/embedder.dart @@ -337,7 +337,7 @@ class FlutterViewEmbedder { } PointerBinding.initInstance(glassPaneElement as html.Element); - KeyboardBinding.initInstance(glassPaneElement as html.Element); + KeyboardBinding.initInstance(glassPaneElement); if (html.window.visualViewport == null && isWebKit) { // Older Safari versions sometimes give us bogus innerWidth/innerHeight diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard.dart index 316101a21af..8e14ca66f22 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard.dart @@ -3,11 +3,12 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'dart:typed_data'; import '../engine.dart' show registerHotRestartListener; +import 'dom.dart'; import 'platform_dispatcher.dart'; +import 'safe_browser_api.dart'; import 'services.dart'; /// Provides keyboard bindings, such as the `flutter/keyevent` channel. @@ -29,19 +30,19 @@ class Keyboard { /// if no repeat events were received. final Map _keydownTimers = {}; - html.EventListener? _keydownListener; - html.EventListener? _keyupListener; + DomEventListener? _keydownListener; + DomEventListener? _keyupListener; Keyboard._(this._onMacOs) { - _keydownListener = (html.Event event) { + _keydownListener = allowInterop((DomEvent event) { _handleHtmlEvent(event); - }; - html.window.addEventListener('keydown', _keydownListener); + }); + domWindow.addEventListener('keydown', _keydownListener); - _keyupListener = (html.Event event) { + _keyupListener = allowInterop((DomEvent event) { _handleHtmlEvent(event); - }; - html.window.addEventListener('keyup', _keyupListener); + }); + domWindow.addEventListener('keyup', _keyupListener); registerHotRestartListener(() { dispose(); }); @@ -52,8 +53,8 @@ class Keyboard { /// After calling this method this object becomes unusable and [instance] /// becomes `null`. Call [initialize] again to initialize a new singleton. void dispose() { - html.window.removeEventListener('keydown', _keydownListener); - html.window.removeEventListener('keyup', _keyupListener); + domWindow.removeEventListener('keydown', _keydownListener); + domWindow.removeEventListener('keyup', _keyupListener); for (final String key in _keydownTimers.keys) { _keydownTimers[key]!.cancel(); @@ -86,12 +87,12 @@ class Keyboard { return _onMacOs; } - void _handleHtmlEvent(html.Event event) { - if (event is! html.KeyboardEvent) { + void _handleHtmlEvent(DomEvent event) { + if (!domInstanceOfString(event, 'KeyboardEvent')) { return; } - final html.KeyboardEvent keyboardEvent = event; + final DomKeyboardEvent keyboardEvent = event as DomKeyboardEvent; final String timerKey = keyboardEvent.code!; // Don't handle synthesizing a keyup event for modifier keys @@ -146,7 +147,7 @@ class Keyboard { ); } - void _synthesizeKeyup(html.KeyboardEvent event) { + void _synthesizeKeyup(DomKeyboardEvent event) { final Map eventData = { 'type': 'keyup', 'keymap': 'web', @@ -179,7 +180,7 @@ const int modifierCapsLock = 0x20; const int modifierScrollLock = 0x40; /// Creates a bitmask representing the meta state of the [event]. -int _getMetaState(html.KeyboardEvent event) { +int _getMetaState(DomKeyboardEvent event) { int metaState = _modifierNone; if (event.getModifierState('Shift')) { metaState |= _modifierShift; @@ -211,7 +212,7 @@ int _getMetaState(html.KeyboardEvent event) { /// /// Modifier keys are shift, alt, ctrl and meta/cmd/win. These are the keys used /// to perform keyboard shortcuts (e.g. `cmd+c`, `cmd+l`). -bool _isModifierKey(html.KeyboardEvent event) { +bool _isModifierKey(DomKeyboardEvent event) { final String key = event.key!; return key == 'Meta' || key == 'Shift' || key == 'Alt' || key == 'Control'; } @@ -219,7 +220,7 @@ bool _isModifierKey(html.KeyboardEvent event) { /// Returns true if the [event] is been affects by any of the modifiers key /// /// This is a strong indication that this key is been used for a shortcut -bool _isAffectedByModifiers(html.KeyboardEvent event) { +bool _isAffectedByModifiers(DomKeyboardEvent event) { return event.ctrlKey || event.shiftKey || event.altKey || event.metaKey; } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart index 01f00867c89..ff5b5aac1c1 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -8,8 +8,10 @@ import 'package:ui/ui.dart' as ui; import '../engine.dart' show registerHotRestartListener; import 'browser_detection.dart'; +import 'dom.dart'; import 'key_map.dart'; import 'platform_dispatcher.dart'; +import 'safe_browser_api.dart'; import 'semantics.dart'; typedef _VoidCallback = void Function(); @@ -92,7 +94,7 @@ class KeyboardBinding { static KeyboardBinding? get instance => _instance; static KeyboardBinding? _instance; - static void initInstance(html.Element glassPaneElement) { + static void initInstance(DomElement glassPaneElement) { if (_instance == null) { _instance = KeyboardBinding._(glassPaneElement); assert(() { @@ -106,30 +108,31 @@ class KeyboardBinding { _setup(); } - final html.Element glassPaneElement; + final DomElement glassPaneElement; late KeyboardConverter _converter; - final Map _listeners = {}; + final Map _listeners = {}; - void _addEventListener(String eventName, html.EventListener handler) { - dynamic loggedHandler(html.Event event) { + void _addEventListener(String eventName, DomEventListener handler) { + dynamic loggedHandler(DomEvent event) { if (_debugLogKeyEvents) { print(event.type); } - if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { + if (EngineSemanticsOwner.instance.receiveGlobalEvent(event as html.Event)) { return handler(event); } return null; } + final DomEventListener wrappedHandler = allowInterop(loggedHandler); assert(!_listeners.containsKey(eventName)); - _listeners[eventName] = loggedHandler; - html.window.addEventListener(eventName, loggedHandler, true); + _listeners[eventName] = wrappedHandler; + domWindow.addEventListener(eventName, wrappedHandler, true); } /// Remove all active event listeners. void _clearListeners() { - _listeners.forEach((String eventName, html.EventListener listener) { - html.window.removeEventListener(eventName, listener, true); + _listeners.forEach((String eventName, DomEventListener listener) { + domWindow.removeEventListener(eventName, listener, true); }); _listeners.clear(); } @@ -143,12 +146,12 @@ class KeyboardBinding { } void _setup() { - _addEventListener('keydown', (html.Event event) { - return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); - }); - _addEventListener('keyup', (html.Event event) { - return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); - }); + _addEventListener('keydown', allowInterop((DomEvent event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as DomKeyboardEvent)); + })); + _addEventListener('keyup', allowInterop((DomEvent event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as DomKeyboardEvent)); + })); _converter = KeyboardConverter(_onKeyData, onMacOs: operatingSystem == OperatingSystem.macOs); } @@ -168,12 +171,12 @@ class AsyncKeyboardDispatching { final _VoidCallback? callback; } -// A wrapper of [html.KeyboardEvent] with reduced methods delegated to the event +// A wrapper of [DomKeyboardEvent] with reduced methods delegated to the event // for the convenience of testing. class FlutterHtmlKeyboardEvent { FlutterHtmlKeyboardEvent(this._event); - final html.KeyboardEvent _event; + final DomKeyboardEvent _event; String get type => _event.type; String? get code => _event.code; @@ -190,7 +193,7 @@ class FlutterHtmlKeyboardEvent { void preventDefault() => _event.preventDefault(); } -// Reads [html.KeyboardEvent], then [dispatches ui.KeyData] accordingly. +// Reads [DomKeyboardEvent], then [dispatches ui.KeyData] accordingly. // // The events are read through [handleEvent], and dispatched through the // [dispatchKeyData] as given in the constructor. Some key data might be