[web] Migrate Flutter Web DOM usage to JS static interop - 37. (#33373)
diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index 1870e9c..88dcd3e 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart
@@ -46,6 +46,7 @@ elt, if (pseudoElt != null) pseudoElt ]) as DomCSSStyleDeclaration; + external DomScreen? get screen; } @JS() @@ -1095,6 +1096,14 @@ @JS() @staticInterop +class DomCompositionEvent {} + +extension DomCompositionEventExtension on DomCompositionEvent { + external String? get data; +} + +@JS() +@staticInterop class DomHTMLInputElement extends DomHTMLElement {} extension DomHTMLInputElementExtension on DomHTMLInputElement { @@ -1218,6 +1227,35 @@ @staticInterop class DomCSSStyleSheet extends DomStyleSheet {} +extension DomCSSStyleSheetExtension on DomCSSStyleSheet { + List<DomCSSRule> get cssRules => + js_util.getProperty<List<Object?>>(this, 'cssRules').cast<DomCSSRule>(); + int insertRule(String rule, [int? index]) => js_util + .callMethod(this, 'insertRule', <Object>[rule, if (index != null) index]); +} + +@JS() +@staticInterop +class DomCSSRule {} + +@JS() +@staticInterop +class DomScreen {} + +extension DomScreenExtension on DomScreen { + external DomScreenOrientation? get orientation; +} + +@JS() +@staticInterop +class DomScreenOrientation extends DomEventTarget {} + +extension DomScreenOrientationExtension on DomScreenOrientation { + Future<dynamic> lock(String orientation) => js_util + .promiseToFuture(js_util.callMethod(this, 'lock', <String>[orientation])); + external void unlock(); +} + // A helper class for managing a subscription. On construction it will add an // event listener of the requested type to the target. Calling [cancel] will // remove the listener. Caller is still responsible for calling [allowInterop]
diff --git a/lib/web_ui/lib/src/engine/embedder.dart b/lib/web_ui/lib/src/engine/embedder.dart index c93c6e1..0aa7f84 100644 --- a/lib/web_ui/lib/src/engine/embedder.dart +++ b/lib/web_ui/lib/src/engine/embedder.dart
@@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'package:ui/ui.dart' as ui; @@ -52,22 +51,18 @@ // The tag name for the root view of the flutter app (glass-pane) static const String _glassPaneTagName = 'flt-glass-pane'; - /// Fires when browser language preferences change. - static const html.EventStreamProvider<html.Event> languageChangeEvent = - html.EventStreamProvider<html.Event>('languagechange'); - - /// Listens to window resize events. - StreamSubscription<html.Event>? _resizeSubscription; + /// Listens to window resize events + DomSubscription? _resizeSubscription; /// Listens to window locale events. - StreamSubscription<html.Event>? _localeSubscription; + DomSubscription? _localeSubscription; /// Contains Flutter-specific CSS rules, such as default margins and /// paddings. - html.StyleElement? _styleElement; + DomHTMLStyleElement? _styleElement; /// Configures the screen, such as scaling. - html.MetaElement? _viewportMeta; + DomHTMLMetaElement? _viewportMeta; /// The element that contains the [sceneElement]. /// @@ -76,12 +71,12 @@ /// /// This element is inserted after the [semanticsHostElement] so that /// platform views take precedence in DOM event handling. - html.Element? get sceneHostElement => _sceneHostElement; - html.Element? _sceneHostElement; + DomElement? get sceneHostElement => _sceneHostElement; + DomElement? _sceneHostElement; /// A child element of body outside the shadowroot that hosts /// global resources such svg filters and clip paths when using webkit. - html.Element? _resourcesHost; + DomElement? _resourcesHost; /// The element that contains the semantics tree. /// @@ -96,18 +91,18 @@ /// /// This element is inserted before the [semanticsHostElement] so that /// platform views take precedence in DOM event handling. - html.Element? get semanticsHostElement => _semanticsHostElement; - html.Element? _semanticsHostElement; + DomElement? get semanticsHostElement => _semanticsHostElement; + DomElement? _semanticsHostElement; /// The last scene element rendered by the [render] method. - html.Element? get sceneElement => _sceneElement; - html.Element? _sceneElement; + DomElement? get sceneElement => _sceneElement; + DomElement? _sceneElement; /// This is state persistent across hot restarts that indicates what /// to clear. Delay removal of old visible state to make the /// transition appear smooth. static const String _staleHotRestartStore = '__flutter_state'; - List<html.Element?>? _staleHotRestartState; + List<DomElement?>? _staleHotRestartState; /// Creates a container for DOM elements that need to be cleaned up between /// hot restarts. @@ -115,11 +110,11 @@ /// If a contains already exists, reuses the existing one. void _setupHotRestart() { // This persists across hot restarts to clear stale DOM. - _staleHotRestartState = getJsProperty<List<html.Element?>?>(html.window, _staleHotRestartStore); + _staleHotRestartState = getJsProperty<List<DomElement?>?>(domWindow, _staleHotRestartStore); if (_staleHotRestartState == null) { - _staleHotRestartState = <html.Element?>[]; + _staleHotRestartState = <DomElement?>[]; setJsProperty( - html.window, _staleHotRestartStore, _staleHotRestartState); + domWindow, _staleHotRestartStore, _staleHotRestartState); } } @@ -130,7 +125,7 @@ registerHotRestartListener(() { _resizeSubscription?.cancel(); _localeSubscription?.cancel(); - _staleHotRestartState!.addAll(<html.Element?>[ + _staleHotRestartState!.addAll(<DomElement?>[ _glassPaneElement, _styleElement, _viewportMeta, @@ -140,7 +135,7 @@ void _clearOnHotRestart() { if (_staleHotRestartState!.isNotEmpty) { - for (final html.Element? element in _staleHotRestartState!) { + for (final DomElement? element in _staleHotRestartState!) { element?.remove(); } _staleHotRestartState!.clear(); @@ -151,7 +146,7 @@ /// already in the right place, skip DOM mutation. This is both faster and /// more correct, because moving DOM nodes loses internal state, such as /// text selection. - void addSceneToSceneHost(html.Element? sceneElement) { + void addSceneToSceneHost(DomElement? sceneElement) { if (sceneElement != _sceneElement) { _sceneElement?.remove(); _sceneElement = sceneElement; @@ -169,14 +164,14 @@ /// which captures semantics input events. The semantics DOM tree must be a /// child of the glass pane element so that events bubble up to the glass pane /// if they are not handled by semantics. - html.Element? get glassPaneElement => _glassPaneElement; - html.Element? _glassPaneElement; + DomElement? get glassPaneElement => _glassPaneElement; + DomElement? _glassPaneElement; /// The [HostNode] of the [glassPaneElement], which contains the whole Flutter app. HostNode? get glassPaneShadow => _glassPaneShadow; HostNode? _glassPaneShadow; - final html.Element rootElement = html.document.body!; + final DomElement rootElement = domDocument.body!; static const String defaultFontStyle = 'normal'; static const String defaultFontWeight = 'normal'; @@ -189,11 +184,11 @@ final bool isWebKit = browserEngine == BrowserEngine.webkit; _styleElement?.remove(); - _styleElement = html.StyleElement(); + _styleElement = createDomHTMLStyleElement(); _resourcesHost?.remove(); _resourcesHost = null; - html.document.head!.append(_styleElement!); - final html.CssStyleSheet sheet = _styleElement!.sheet! as html.CssStyleSheet; + domDocument.head!.append(_styleElement!); + final DomCSSStyleSheet sheet = _styleElement!.sheet! as DomCSSStyleSheet; applyGlobalCssRulesToSheet( sheet, browserEngine: browserEngine, @@ -238,8 +233,8 @@ // engine are complete. bodyElement.spellcheck = false; - for (final html.Element viewportMeta - in html.document.head!.querySelectorAll('meta[name="viewport"]')) { + for (final DomElement viewportMeta + in domDocument.head!.querySelectorAll('meta[name="viewport"]')) { if (assertionsEnabled) { // Filter out the meta tag that the engine placed on the page. This is // to avoid UI flicker during hot restart. Hot restart will clean up the @@ -260,18 +255,18 @@ // variables, so this will be null upon hot restart. Instead, this tag is // removed by _clearOnHotRestart. _viewportMeta?.remove(); - _viewportMeta = html.MetaElement() + _viewportMeta = createDomHTMLMetaElement() ..setAttribute('flt-viewport', '') ..name = 'viewport' ..content = 'width=device-width, initial-scale=1.0, ' 'maximum-scale=1.0, user-scalable=no'; - html.document.head!.append(_viewportMeta!); + domDocument.head!.append(_viewportMeta!); // IMPORTANT: the glass pane element must come after the scene element in the DOM node list so // it can intercept input events. _glassPaneElement?.remove(); final DomElement glassPaneElement = domDocument.createElement(_glassPaneTagName); - _glassPaneElement = glassPaneElement as html.Element; + _glassPaneElement = glassPaneElement; glassPaneElement.style ..position = 'absolute' ..top = '0' @@ -285,12 +280,11 @@ // Create a [HostNode] under the glass pane element, and attach everything // there, instead of directly underneath the glass panel. - final HostNode glassPaneElementHostNode = _createHostNode(glassPaneElement - as html.Element); + final HostNode glassPaneElementHostNode = _createHostNode(glassPaneElement); _glassPaneShadow = glassPaneElementHostNode; // Don't allow the scene to receive pointer events. - _sceneHostElement = html.document.createElement('flt-scene-host') + _sceneHostElement = domDocument.createElement('flt-scene-host') ..style.pointerEvents = 'none'; /// CanvasKit uses a static scene element that never gets replaced, so it's @@ -298,24 +292,24 @@ /// system is reset due to hot restart or in a test. if (useCanvasKit) { skiaSceneHost = createDomElement('flt-scene'); - addSceneToSceneHost(skiaSceneHost as html.Element?); + addSceneToSceneHost(skiaSceneHost); } - final html.Element semanticsHostElement = - html.document.createElement('flt-semantics-host'); + final DomElement semanticsHostElement = + domDocument.createElement('flt-semantics-host'); semanticsHostElement.style ..position = 'absolute' ..transformOrigin = '0 0 0'; _semanticsHostElement = semanticsHostElement; updateSemanticsScreenProperties(); - final html.Element _accessibilityPlaceholder = EngineSemanticsOwner + final DomElement _accessibilityPlaceholder = EngineSemanticsOwner .instance.semanticsHelper - .prepareAccessibilityPlaceholder() as html.Element; + .prepareAccessibilityPlaceholder(); glassPaneElementHostNode.appendAll(<DomNode>[ - _accessibilityPlaceholder as DomNode, - _sceneHostElement! as DomNode, + _accessibilityPlaceholder, + _sceneHostElement!, // The semantic host goes last because hit-test order-wise it must be // first. If semantics goes under the scene host, platform views will @@ -327,7 +321,7 @@ // elements transparent. This way, if a platform view appears among other // interactive Flutter widgets, as long as those widgets do not intersect // with the platform view, the platform view will be reachable. - semanticsHostElement as DomNode, + semanticsHostElement, ]); // When debugging semantics, make the scene semi-transparent so that the @@ -339,7 +333,7 @@ PointerBinding.initInstance(glassPaneElement); KeyboardBinding.initInstance(glassPaneElement); - if (html.window.visualViewport == null && isWebKit) { + if (domWindow.visualViewport == null && isWebKit) { // Older Safari versions sometimes give us bogus innerWidth/innerHeight // values when the page loads. When it changes the values to correct ones // it does not notify of the change via `onResize`. As a workaround, we @@ -353,13 +347,13 @@ // Firefox returns correct values for innerHeight, innerWidth. // Firefox also triggers html.window.onResize therefore this timer does // not need to be set up for Firefox. - final int initialInnerWidth = html.window.innerWidth!; + final int initialInnerWidth = domWindow.innerWidth!; // Counts how many times screen size was checked. It is checked up to 5 // times. int checkCount = 0; Timer.periodic(const Duration(milliseconds: 100), (Timer t) { checkCount += 1; - if (initialInnerWidth != html.window.innerWidth) { + if (initialInnerWidth != domWindow.innerWidth) { // Window size changed. Notify. t.cancel(); _metricsDidChange(null); @@ -370,24 +364,25 @@ }); } - if (html.window.visualViewport != null) { - _resizeSubscription = - html.window.visualViewport!.onResize.listen(_metricsDidChange); + if (domWindow.visualViewport != null) { + _resizeSubscription = DomSubscription(domWindow.visualViewport!, 'resize', + allowInterop(_metricsDidChange)); } else { - _resizeSubscription = html.window.onResize.listen(_metricsDidChange); + _resizeSubscription = DomSubscription(domWindow, 'resize', + allowInterop(_metricsDidChange)); } - _localeSubscription = - languageChangeEvent.forTarget(html.window).listen(_languageDidChange); + _localeSubscription = DomSubscription(domWindow, 'languagechange', + allowInterop(_languageDidChange)); EnginePlatformDispatcher.instance.updateLocales(); } - // Creates a [HostNode] into a `root` [html.Element]. - HostNode _createHostNode(html.Element root) { + // Creates a [HostNode] into a `root` [DomElement]. + HostNode _createHostNode(DomElement root) { if (getJsProperty<Object?>(root, 'attachShadow') != null) { - return ShadowDomHostNode(root as DomElement); + return ShadowDomHostNode(root); } else { // attachShadow not available, fall back to ElementHostNode. - return ElementHostNode(root as DomElement); + return ElementHostNode(root); } } @@ -395,8 +390,8 @@ /// logical pixels. To compensate, an inverse scale is injected at the root /// level. void updateSemanticsScreenProperties() { - _semanticsHostElement!.style.transform = - 'scale(${1 / html.window.devicePixelRatio})'; + _semanticsHostElement!.style.setProperty('transform', + 'scale(${1 / domWindow.devicePixelRatio})'); } /// Called immediately after browser window metrics change. @@ -408,7 +403,7 @@ /// /// Note: always check for rotations for a mobile device. Update the physical /// size if the change is caused by a rotation. - void _metricsDidChange(html.Event? event) { + void _metricsDidChange(DomEvent? event) { updateSemanticsScreenProperties(); if (isMobile && !window.isRotation() && textEditing.isEditing) { window.computeOnScreenKeyboardInsets(true); @@ -422,7 +417,7 @@ } /// Called immediately after browser window language change. - void _languageDidChange(html.Event event) { + void _languageDidChange(DomEvent event) { EnginePlatformDispatcher.instance.updateLocales(); if (ui.window.onLocaleChanged != null) { ui.window.onLocaleChanged!(); @@ -451,9 +446,9 @@ /// /// See w3c screen api: https://www.w3.org/TR/screen-orientation/ Future<bool> setPreferredOrientation(List<dynamic> orientations) { - final html.Screen screen = html.window.screen!; + final DomScreen screen = domWindow.screen!; if (!unsafeIsNull(screen)) { - final html.ScreenOrientation? screenOrientation = screen.orientation; + final DomScreenOrientation? screenOrientation = screen.orientation; if (!unsafeIsNull(screenOrientation)) { if (orientations.isEmpty) { screenOrientation!.unlock(); @@ -500,9 +495,9 @@ } /// The element corresponding to the only child of the root surface. - html.Element? get _rootApplicationElement { - final html.Element lastElement = rootElement.children.last; - for (final html.Element child in lastElement.children) { + DomElement? get _rootApplicationElement { + final DomElement lastElement = rootElement.children.last; + for (final DomElement child in lastElement.children) { if (child.tagName == 'FLT-SCENE') { return child; } @@ -516,37 +511,37 @@ /// place it as first element of body(webkit), or as a child of /// glass pane element for other browsers to make sure url resolution /// works correctly when content is inside a shadow root. - void addResource(html.Element element) { + void addResource(DomElement element) { final bool isWebKit = browserEngine == BrowserEngine.webkit; if (_resourcesHost == null) { - _resourcesHost = html.DivElement() + _resourcesHost = createDomHTMLDivElement() ..style.visibility = 'hidden'; if (isWebKit) { - final html.Node bodyNode = html.document.body!; + final DomNode bodyNode = domDocument.body!; bodyNode.insertBefore(_resourcesHost!, bodyNode.firstChild); } else { _glassPaneShadow!.node.insertBefore( - _resourcesHost! as DomNode, _glassPaneShadow!.node.firstChild); + _resourcesHost!, _glassPaneShadow!.node.firstChild); } } _resourcesHost!.append(element); } /// Removes a global resource element. - void removeResource(html.Element? element) { + void removeResource(DomElement? element) { if (element == null) { return; } - assert(element.parent == _resourcesHost); + assert(element.parentNode == _resourcesHost); element.remove(); } - String get currentHtml => _rootApplicationElement?.outerHtml ?? ''; + String get currentHtml => _rootApplicationElement?.outerHTML ?? ''; } // Applies the required global CSS to an incoming [html.CssStyleSheet] `sheet`. void applyGlobalCssRulesToSheet( - html.CssStyleSheet sheet, { + DomCSSStyleSheet sheet, { required BrowserEngine browserEngine, required bool hasAutofillOverlay, String glassPaneTagName = FlutterViewEmbedder._glassPaneTagName,
diff --git a/lib/web_ui/lib/src/engine/host_node.dart b/lib/web_ui/lib/src/engine/host_node.dart index fe48e9d..dee8ffb 100644 --- a/lib/web_ui/lib/src/engine/host_node.dart +++ b/lib/web_ui/lib/src/engine/host_node.dart
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html' as html; - import 'browser_detection.dart'; import 'dom.dart'; import 'embedder.dart'; @@ -111,7 +109,7 @@ // TODO(dit): Apply only rules for the shadow root applyGlobalCssRulesToSheet( - shadowRootStyleElement.sheet! as html.CssStyleSheet, + shadowRootStyleElement.sheet! as DomCSSStyleSheet, browserEngine: browserEngine, hasAutofillOverlay: browserHasAutofillOverlay(), );
diff --git a/lib/web_ui/lib/src/engine/html/color_filter.dart b/lib/web_ui/lib/src/engine/html/color_filter.dart index 71beada..0858956 100644 --- a/lib/web_ui/lib/src/engine/html/color_filter.dart +++ b/lib/web_ui/lib/src/engine/html/color_filter.dart
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html' as html; - import 'package:ui/ui.dart' as ui; import '../browser_detection.dart'; @@ -54,7 +52,7 @@ @override void discard() { super.discard(); - flutterViewEmbedder.removeResource(_filterElement as html.Element?); + flutterViewEmbedder.removeResource(_filterElement); // Do not detach the child container from the root. It is permanently // attached. The elements are reused together and are detached from the DOM // together. @@ -73,7 +71,7 @@ @override void apply() { - flutterViewEmbedder.removeResource(_filterElement as html.Element?); + flutterViewEmbedder.removeResource(_filterElement); _filterElement = null; final EngineColorFilter? engineValue = filter as EngineColorFilter?; if (engineValue == null) { @@ -139,7 +137,7 @@ // Use SVG filter for blend mode. final SvgFilter svgFilter = svgFilterFromBlendMode(filterColor, colorFilterBlendMode); _filterElement = svgFilter.element; - flutterViewEmbedder.addResource(_filterElement! as html.Element); + flutterViewEmbedder.addResource(_filterElement!); style.filter = 'url(#${svgFilter.id})'; if (colorFilterBlendMode == ui.BlendMode.saturation || colorFilterBlendMode == ui.BlendMode.multiply || @@ -151,7 +149,7 @@ void _applyMatrixColorFilter(CkMatrixColorFilter colorFilter) { final SvgFilter svgFilter = svgFilterFromColorMatrix(colorFilter.matrix); _filterElement = svgFilter.element; - flutterViewEmbedder.addResource(_filterElement! as html.Element); + flutterViewEmbedder.addResource(_filterElement!); childContainer!.style.filter = 'url(#${svgFilter.id})'; }
diff --git a/lib/web_ui/lib/src/engine/html/shader_mask.dart b/lib/web_ui/lib/src/engine/html/shader_mask.dart index 526bf88..d748bc1 100644 --- a/lib/web_ui/lib/src/engine/html/shader_mask.dart +++ b/lib/web_ui/lib/src/engine/html/shader_mask.dart
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html' as html; - import 'package:ui/ui.dart' as ui; import '../browser_detection.dart'; @@ -40,7 +38,7 @@ final ui.Rect maskRect; final ui.BlendMode blendMode; final ui.FilterQuality filterQuality; - html.Element? _shaderElement; + DomElement? _shaderElement; final bool isWebKit = browserEngine == BrowserEngine.webkit; @override @@ -162,7 +160,7 @@ final SvgFilter svgFilter = svgMaskFilterFromImageAndBlendMode( imageUrl, blendModeTemp, maskRect.width, maskRect.height); - _shaderElement = svgFilter.element as html.Element; + _shaderElement = svgFilter.element; if (isWebKit) { _childContainer!.style.filter = 'url(#${svgFilter.id})'; } else {
diff --git a/lib/web_ui/lib/src/engine/mouse_cursor.dart b/lib/web_ui/lib/src/engine/mouse_cursor.dart index 1d05a24..3e5979b 100644 --- a/lib/web_ui/lib/src/engine/mouse_cursor.dart +++ b/lib/web_ui/lib/src/engine/mouse_cursor.dart
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dom.dart'; import 'embedder.dart'; import 'util.dart'; @@ -68,7 +67,7 @@ void activateSystemCursor(String? kind) { setElementStyle( - flutterViewEmbedder.glassPaneElement! as DomElement, + flutterViewEmbedder.glassPaneElement!, 'cursor', _mapKindToCssValue(kind), );
diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 39da68a..f5c03c0 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart
@@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:html' as html; import 'dart:typed_data'; import 'package:meta/meta.dart'; @@ -485,7 +484,7 @@ contentManager: platformViewManager, contentHandler: (DomElement content) { // Remove cast to [html.Element] after migration. - flutterViewEmbedder.glassPaneElement!.append(content as html.Element); + flutterViewEmbedder.glassPaneElement!.append(content); }, ); _platformViewMessageHandler!.handlePlatformViewCall(data, callback!); @@ -608,7 +607,7 @@ rasterizer!.draw(layerScene.layerTree); } else { final SurfaceScene surfaceScene = scene as SurfaceScene; - flutterViewEmbedder.addSceneToSceneHost(surfaceScene.webOnlyRootElement as html.Element?); + flutterViewEmbedder.addSceneToSceneHost(surfaceScene.webOnlyRootElement); } frameTimingsOnRasterFinish(); }
diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 4ac038f..9745699 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html' as html; import 'dart:math' as math; import 'dart:typed_data'; @@ -1787,7 +1786,7 @@ if (_rootSemanticsElement == null) { final SemanticsObject root = _semanticsTree[0]!; _rootSemanticsElement = root.element; - flutterViewEmbedder.semanticsHostElement!.append(root.element as html.Node); + flutterViewEmbedder.semanticsHostElement!.append(root.element); } _finalizeTree();
diff --git a/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart b/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart index fdab6ad..7ba45ad 100644 --- a/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart +++ b/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html' as html; - +import '../dom.dart'; +import '../safe_browser_api.dart'; import 'text_editing.dart'; /// Provides default functionality for listening to HTML composition events. @@ -29,9 +29,12 @@ /// The name of the browser composition event type that triggers on ending a composition. static const String _kCompositionEnd = 'compositionend'; - late final html.EventListener _compositionStartListener = _handleCompositionStart; - late final html.EventListener _compositionUpdateListener = _handleCompositionUpdate; - late final html.EventListener _compositionEndListener = _handleCompositionEnd; + late final DomEventListener _compositionStartListener = + allowInterop(_handleCompositionStart); + late final DomEventListener _compositionUpdateListener = + allowInterop(_handleCompositionUpdate); + late final DomEventListener _compositionEndListener = + allowInterop(_handleCompositionEnd); /// The currently composing text in the `domElement`. /// @@ -40,29 +43,29 @@ /// so it is safe to reference it to get the current composingText. String? composingText; - void addCompositionEventHandlers(html.HtmlElement domElement) { + void addCompositionEventHandlers(DomHTMLElement domElement) { domElement.addEventListener(_kCompositionStart, _compositionStartListener); domElement.addEventListener(_kCompositionUpdate, _compositionUpdateListener); domElement.addEventListener(_kCompositionEnd, _compositionEndListener); } - void removeCompositionEventHandlers(html.HtmlElement domElement) { + void removeCompositionEventHandlers(DomHTMLElement domElement) { domElement.removeEventListener(_kCompositionStart, _compositionStartListener); domElement.removeEventListener(_kCompositionUpdate, _compositionUpdateListener); domElement.removeEventListener(_kCompositionEnd, _compositionEndListener); } - void _handleCompositionStart(html.Event event) { + void _handleCompositionStart(DomEvent event) { composingText = null; } - void _handleCompositionUpdate(html.Event event) { - if (event is html.CompositionEvent) { - composingText = event.data; + void _handleCompositionUpdate(DomEvent event) { + if (domInstanceOfString(event, 'CompositionEvent')) { + composingText = (event as DomCompositionEvent).data; } } - void _handleCompositionEnd(html.Event event) { + void _handleCompositionEnd(DomEvent event) { composingText = null; }
diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 658c864..3e1a6e2 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart
@@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'dart:math' as math; import 'dart:typed_data'; @@ -1218,7 +1217,7 @@ activeDomElement.addEventListener('beforeinput', allowInterop(handleBeforeInput)); - addCompositionEventHandlers(activeDomElement as html.HtmlElement); + addCompositionEventHandlers(activeDomElement); // Refocus on the activeDomElement after blur, so that user can keep editing the // text field. @@ -1258,7 +1257,7 @@ subscriptions[i].cancel(); } subscriptions.clear(); - removeCompositionEventHandlers(activeDomElement as html.HtmlElement); + removeCompositionEventHandlers(activeDomElement); // If focused element is a part of a form, it needs to stay on the DOM // until the autofill context of the form is finalized. @@ -1508,7 +1507,7 @@ activeDomElement.addEventListener('beforeinput', allowInterop(handleBeforeInput)); - addCompositionEventHandlers(activeDomElement as html.HtmlElement); + addCompositionEventHandlers(activeDomElement); // Position the DOM element after it is focused. subscriptions.add(DomSubscription(activeDomElement, 'focus', @@ -1661,7 +1660,7 @@ activeDomElement.addEventListener('beforeinput', allowInterop(handleBeforeInput)); - addCompositionEventHandlers(activeDomElement as html.HtmlElement); + addCompositionEventHandlers(activeDomElement); subscriptions.add( DomSubscription(activeDomElement, 'blur', @@ -1723,7 +1722,7 @@ activeDomElement.addEventListener('beforeinput', allowInterop(handleBeforeInput)); - addCompositionEventHandlers(activeDomElement as html.HtmlElement); + addCompositionEventHandlers(activeDomElement); // Detects changes in text selection. //
diff --git a/lib/web_ui/test/canvaskit/embedded_views_test.dart b/lib/web_ui/test/canvaskit/embedded_views_test.dart index d9b2cf4..945a0d8 100644 --- a/lib/web_ui/test/canvaskit/embedded_views_test.dart +++ b/lib/web_ui/test/canvaskit/embedded_views_test.dart
@@ -42,9 +42,11 @@ // as a child of the glassPane, and the slot lives in the glassPane // shadow root. The slot is the one that has pointer events auto. final html.Element contents = - flutterViewEmbedder.glassPaneElement!.querySelector('#view-0')!; + flutterViewEmbedder.glassPaneElement!.querySelector('#view-0')! as + html.Element; final html.Element slot = - flutterViewEmbedder.sceneElement!.querySelector('slot')!; + flutterViewEmbedder.sceneElement!.querySelector('slot')! as + html.Element; final html.Element contentsHost = contents.parent!; final html.Element slotHost = slot.parent!; @@ -119,7 +121,8 @@ // Transformations happen on the slot element. final html.Element slotHost = - flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')!; + flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')! + as html.Element; expect( slotHost.style.transform, @@ -144,7 +147,8 @@ dispatcher.rasterizer!.draw(sb.build().layerTree); final html.Element slotHost = - flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')!; + flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')! + as html.Element; final html.CssStyleDeclaration style = slotHost.style; expect(style.transform, 'matrix(1, 0, 0, 1, 3, 4)'); @@ -189,7 +193,8 @@ // Transformations happen on the slot element. html.Element slotHost = - flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')!; + flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')! + as html.Element; expect( getTransformChain(slotHost), @@ -210,7 +215,8 @@ // Transformations happen on the slot element. slotHost = - flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')!; + flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')! + as html.Element; expect( getTransformChain(slotHost), @@ -241,7 +247,8 @@ // Transformations happen on the slot element. final html.Element slotHost = - flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')!; + flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')! + as html.Element; expect( getTransformChain(slotHost), @@ -270,7 +277,8 @@ // Transformations happen on the slot element. final html.Element slotHost = - flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')!; + flutterViewEmbedder.sceneElement!.querySelector('flt-platform-view-slot')! + as html.Element; expect( getTransformChain(slotHost), @@ -561,7 +569,8 @@ } final html.Node skPathDefs = - flutterViewEmbedder.sceneElement!.querySelector('#sk_path_defs')!; + flutterViewEmbedder.sceneElement!.querySelector('#sk_path_defs')! as + html.Node; expect(skPathDefs.childNodes, hasLength(0));
diff --git a/lib/web_ui/test/canvaskit/h5vcc_test.dart b/lib/web_ui/test/canvaskit/h5vcc_test.dart index b5b4608..d18ee25 100644 --- a/lib/web_ui/test/canvaskit/h5vcc_test.dart +++ b/lib/web_ui/test/canvaskit/h5vcc_test.dart
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html' as html; - import 'package:js/js.dart'; import 'package:js/js_util.dart' as js_util; import 'package:test/bootstrap/browser.dart'; @@ -59,7 +57,7 @@ // No <canvas> element should be created. expect( - flutterViewEmbedder.glassPaneElement!.querySelectorAll<html.Element>('canvas'), + flutterViewEmbedder.glassPaneElement!.querySelectorAll('canvas'), isEmpty, ); });
diff --git a/lib/web_ui/test/composition_test.dart b/lib/web_ui/test/composition_test.dart index 54c543d..ed4f463 100644 --- a/lib/web_ui/test/composition_test.dart +++ b/lib/web_ui/test/composition_test.dart
@@ -9,6 +9,7 @@ import 'package:test/test.dart'; 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/text_editing/composition_aware_mixin.dart'; import 'package:ui/src/engine/text_editing/input_type.dart'; @@ -68,7 +69,8 @@ final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); mockWithCompositionAwareMixin.composingText = fakeComposingText; - mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement); + mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement + as DomHTMLElement); _inputElement.dispatchEvent(html.Event(_MockWithCompositionAwareMixin._kCompositionEnd)); @@ -81,7 +83,8 @@ final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); mockWithCompositionAwareMixin.composingText = fakeComposingText; - mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement); + mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement + as DomHTMLElement); _inputElement.dispatchEvent(html.Event(_MockWithCompositionAwareMixin._kCompositionStart)); @@ -95,7 +98,8 @@ final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); mockWithCompositionAwareMixin.composingText = fakeComposingText; - mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement); + mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement + as DomHTMLElement); _inputElement.dispatchEvent(html.CompositionEvent( _MockWithCompositionAwareMixin._kCompositionUpdate,
diff --git a/lib/web_ui/test/embedder_test.dart b/lib/web_ui/test/embedder_test.dart index 528b93f..d227b0f 100644 --- a/lib/web_ui/test/embedder_test.dart +++ b/lib/web_ui/test/embedder_test.dart
@@ -80,11 +80,11 @@ test('should add/remove global resource', () { final FlutterViewEmbedder embedder = FlutterViewEmbedder(); final html.DivElement resource = html.DivElement(); - embedder.addResource(resource); + embedder.addResource(resource as DomHTMLDivElement); final html.Element? resourceRoot = resource.parent; expect(resourceRoot, isNotNull); expect(resourceRoot!.childNodes.length, 1); - embedder.removeResource(resource); + embedder.removeResource(resource as DomHTMLDivElement); expect(resourceRoot.childNodes.length, 0); }); @@ -92,7 +92,7 @@ final FlutterViewEmbedder embedder = FlutterViewEmbedder(); final html.InputElement regularTextField = html.InputElement(); regularTextField.placeholder = 'Now you see me'; - embedder.addResource(regularTextField); + embedder.addResource(regularTextField as DomHTMLInputElement); regularTextField.focus(); html.CssStyleDeclaration? style = domWindow.getComputedStyle( @@ -104,7 +104,7 @@ final html.InputElement textField = html.InputElement(); textField.placeholder = 'Now you dont'; textField.classes.add('flt-text-editing'); - embedder.addResource(textField); + embedder.addResource(textField as DomHTMLInputElement); textField.focus(); style = domWindow.getComputedStyle(
diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index d5b6742..f2f4611 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart
@@ -46,7 +46,8 @@ void testMain() { ensureFlutterViewEmbedderInitialized(); - final html.Element glassPane = flutterViewEmbedder.glassPaneElement!; + final html.Element glassPane = flutterViewEmbedder.glassPaneElement! as + html.Element; double dpi = 1.0; setUp(() {
diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index 701c157..972d24e 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart
@@ -1888,7 +1888,9 @@ expect(child3Rect.right, 20); expect(child3Rect.bottom, 60); - final html.Element platformViewElement = flutterViewEmbedder.glassPaneElement!.querySelector('#view-0')!; + final html.Element platformViewElement = + flutterViewEmbedder.glassPaneElement!.querySelector('#view-0')! as + html.Element; final html.Rectangle<num> platformViewRect = platformViewElement.getBoundingClientRect(); expect(platformViewRect.left, 0); expect(platformViewRect.top, 15);
diff --git a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart index 049611f..6b3567f 100644 --- a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart +++ b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart
@@ -39,7 +39,7 @@ setUp(() async { SurfaceSceneBuilder.debugForgetFrameScene(); for (final html.Node scene in - flutterViewEmbedder.sceneHostElement!.querySelectorAll('flt-scene')) { + flutterViewEmbedder.sceneHostElement!.querySelectorAll('flt-scene').cast<html.Node>()) { scene.remove(); } initWebGl(); @@ -163,8 +163,7 @@ builder.addPicture(Offset.zero, circles2); builder.pop(); - flutterViewEmbedder.sceneHostElement!.append(builder.build().webOnlyRootElement! - as html.Element); + flutterViewEmbedder.sceneHostElement!.append(builder.build().webOnlyRootElement!); } Picture _drawTestPictureWithText( @@ -220,6 +219,5 @@ builder.addPicture(Offset.zero, textPicture2); builder.pop(); - flutterViewEmbedder.sceneHostElement!.append(builder.build().webOnlyRootElement! - as html.Element); + flutterViewEmbedder.sceneHostElement!.append(builder.build().webOnlyRootElement!); }
diff --git a/lib/web_ui/test/matchers.dart b/lib/web_ui/test/matchers.dart index be7310c..4cf4622 100644 --- a/lib/web_ui/test/matchers.dart +++ b/lib/web_ui/test/matchers.dart
@@ -461,7 +461,7 @@ /// Currently rendered HTML DOM as an HTML string. String get currentHtml { - return flutterViewEmbedder.sceneElement?.outerHtml ?? ''; + return flutterViewEmbedder.sceneElement?.outerHTML ?? ''; } class SceneTester {