From df7a8214799a8c44fcc49bf832a225cdd69ef336 Mon Sep 17 00:00:00 2001 From: joshualitt Date: Mon, 27 Jun 2022 11:33:45 -0700 Subject: [PATCH] [web] Migrate Flutter Web DOM usage to JS static interop - 38. (flutter/engine#33374) --- .../lib/web_ui/lib/initialization.dart | 12 ++++--- .../lib/src/engine/browser_detection.dart | 2 +- .../lib/web_ui/lib/src/engine/dom.dart | 3 ++ .../lib/web_ui/lib/src/engine/embedder.dart | 4 +-- .../lib/src/engine/font_change_util.dart | 7 ++-- .../web_ui/lib/src/engine/initialization.dart | 7 ++-- .../lib/src/engine/safe_browser_api.dart | 2 +- engine/src/flutter/lib/web_ui/lib/ui.dart | 1 - .../test/canvaskit/embedded_views_test.dart | 36 +++++++++---------- .../test/canvaskit/image_golden_test.dart | 3 +- .../test/engine/semantics/semantics_test.dart | 2 +- .../engine/surface/platform_view_test.dart | 5 ++- 12 files changed, 44 insertions(+), 40 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/initialization.dart b/engine/src/flutter/lib/web_ui/lib/initialization.dart index 75f1f24e657..8ee067b5ef8 100644 --- a/engine/src/flutter/lib/web_ui/lib/initialization.dart +++ b/engine/src/flutter/lib/web_ui/lib/initialization.dart @@ -4,6 +4,8 @@ // TODO(yjbanov): rename this file to web_only_api.dart. // https://github.com/flutter/flutter/issues/100394 +// Rather than extending this file with new APIs, we +// should instead use js interop. // This file contains extra web-only API that non-web engines do not have. // @@ -129,13 +131,13 @@ void webOnlySetPluginHandler(Future Function(String, ByteData?, PlatformMe engine.pluginMessageCallHandler = handler; } -/// A function which takes a unique `id` and creates an HTML element. -typedef PlatformViewFactory = html.Element Function(int viewId); - /// A registry for factories that create platform views. class PlatformViewRegistry { - /// Register [viewTypeId] as being creating by the given [factory]. - bool registerViewFactory(String viewTypeId, PlatformViewFactory viewFactory, + /// Register [viewTypeId] as being creating by the given [viewFactory]. + /// [viewFactory] can be any function that takes an integer and returns an + /// `HTMLElement` DOM object. + bool registerViewFactory(String viewTypeId, + Object Function(int viewId) viewFactory, {bool isVisible = true}) { // TODO(web): Deprecate this once there's another way of calling `registerFactory` (js interop?) return engine.platformViewManager diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart index 59dc018b309..8718be3d115 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/browser_detection.dart @@ -185,7 +185,7 @@ OperatingSystem detectOperatingSystem({ return OperatingSystem.iOs; } else if (userAgent.contains('Android')) { // The Android OS reports itself as "Linux armv8l" in - // [html.window.navigator.platform]. So we have to check the user-agent to + // [domWindow.navigator.platform]. So we have to check the user-agent to // determine if the OS is Android or not. return OperatingSystem.android; } else if (platform.startsWith('Linux')) { 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 88dcd3e138f..4775a89d432 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 @@ -47,8 +47,11 @@ extension DomWindowExtension on DomWindow { if (pseudoElt != null) pseudoElt ]) as DomCSSStyleDeclaration; external DomScreen? get screen; + external int requestAnimationFrame(DomRequestAnimationFrameCallback callback); } +typedef DomRequestAnimationFrameCallback = void Function(num highResTime); + @JS() @staticInterop class DomConsole {} 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 0aa7f845431..a343133e412 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 @@ -345,7 +345,7 @@ class FlutterViewEmbedder { // // VisualViewport API is not enabled in Firefox as well. On the other hand // Firefox returns correct values for innerHeight, innerWidth. - // Firefox also triggers html.window.onResize therefore this timer does + // Firefox also triggers domWindow.onResize therefore this timer does // not need to be set up for Firefox. final int initialInnerWidth = domWindow.innerWidth!; // Counts how many times screen size was checked. It is checked up to 5 @@ -539,7 +539,7 @@ class FlutterViewEmbedder { String get currentHtml => _rootApplicationElement?.outerHTML ?? ''; } -// Applies the required global CSS to an incoming [html.CssStyleSheet] `sheet`. +// Applies the required global CSS to an incoming [DomCSSStyleSheet] `sheet`. void applyGlobalCssRulesToSheet( DomCSSStyleSheet sheet, { required BrowserEngine browserEngine, diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/font_change_util.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/font_change_util.dart index 14cfcf0a5bf..c4b876741f9 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/font_change_util.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/font_change_util.dart @@ -3,10 +3,11 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'dart:typed_data'; +import 'dom.dart'; import 'platform_dispatcher.dart'; +import 'safe_browser_api.dart'; import 'services.dart'; final ByteData? _fontChangeMessage = @@ -22,13 +23,13 @@ FutureOr sendFontChangeMessage() async { if (!_fontChangeScheduled) { _fontChangeScheduled = true; // Batch updates into next animationframe. - html.window.requestAnimationFrame((num _) { + domWindow.requestAnimationFrame(allowInterop((num _) { _fontChangeScheduled = false; EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/system', _fontChangeMessage, (_) {}, ); - }); + })); } } diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/initialization.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/initialization.dart index f1210d8110d..71da02a9c3b 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/initialization.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/initialization.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:developer' as developer; -import 'dart:html' as html; import 'package:ui/src/engine/assets.dart'; import 'package:ui/src/engine/browser_detection.dart'; @@ -22,6 +21,8 @@ import 'package:ui/src/engine/text/line_break_properties.dart'; import 'package:ui/src/engine/window.dart'; import 'package:ui/ui.dart' as ui; +import 'dom.dart'; + /// The mode the app is running in. /// Keep these in sync with the same constants on the framework-side under foundation/constants.dart. const bool kReleaseMode = @@ -178,7 +179,7 @@ Future initializeEngineServices({ // fires. if (!waitingForAnimation) { waitingForAnimation = true; - html.window.requestAnimationFrame((num highResTime) { + domWindow.requestAnimationFrame(allowInterop((num highResTime) { frameTimingsOnVsync(); // Reset immediately, because `frameHandler` can schedule more frames. @@ -210,7 +211,7 @@ Future initializeEngineServices({ // implement it properly. EnginePlatformDispatcher.instance.invokeOnDrawFrame(); } - }); + })); } }; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart index b2b29a4718a..3b918a17318 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart @@ -64,7 +64,7 @@ T setJsProperty(Object object, String name, T value) { 'Attempted to set property "$name" on a JavaScript object. This property ' 'has not been checked for safety. Possible solutions to this problem:\n' ' - Do not set this property.\n' - ' - Use a `dart:html` API that does the same thing.\n' + ' - Use a `js_util` API that does the same thing.\n' ' - Ensure that the property is safe then add it to _safeJsProperties set.', ); return js_util.setProperty(object, name, value); diff --git a/engine/src/flutter/lib/web_ui/lib/ui.dart b/engine/src/flutter/lib/web_ui/lib/ui.dart index baa2a5c2b7a..a01d53936b6 100644 --- a/engine/src/flutter/lib/web_ui/lib/ui.dart +++ b/engine/src/flutter/lib/web_ui/lib/ui.dart @@ -10,7 +10,6 @@ library ui; import 'dart:async'; import 'dart:collection' as collection; import 'dart:convert'; -import 'dart:html' as html; import 'dart:math' as math; import 'dart:typed_data'; diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/embedded_views_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/embedded_views_test.dart index 30b332f7c39..2a796d318c1 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/embedded_views_test.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/embedded_views_test.dart @@ -27,7 +27,7 @@ void testMain() { test('embeds interactive platform views', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -63,7 +63,7 @@ void testMain() { test('clips platform views with RRects', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -103,7 +103,7 @@ void testMain() { test('correctly transforms platform views', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -135,7 +135,7 @@ void testMain() { test('correctly offsets platform views', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -175,7 +175,7 @@ void testMain() { test('correctly offsets when clip chain length is changed', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -228,7 +228,7 @@ void testMain() { window.debugOverrideDevicePixelRatio(4); ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -255,7 +255,7 @@ void testMain() { window.debugOverrideDevicePixelRatio(4); ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -297,7 +297,7 @@ void testMain() { for (int i = 0; i < 16; i++) { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-$i', + (int viewId) => createDomHTMLDivElement()..id = 'view-$i', ); await createPlatformView(i, 'test-platform-view'); platformViewIds.add(i); @@ -453,7 +453,7 @@ void testMain() { for (int i = 0; i < 20; i++) { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-$i', + (int viewId) => createDomHTMLDivElement()..id = 'view-$i', ); await createPlatformView(i, 'test-platform-view'); platformViewIds.add(i); @@ -583,7 +583,7 @@ void testMain() { test('embeds and disposes of a platform view', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -626,7 +626,7 @@ void testMain() { test('removed the DOM node of an unrendered platform view', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -688,7 +688,7 @@ void testMain() { () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'test-view', + (int viewId) => createDomHTMLDivElement()..id = 'test-view', ); await createPlatformView(0, 'test-platform-view'); @@ -725,7 +725,7 @@ void testMain() { () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -748,7 +748,7 @@ void testMain() { HtmlViewEmbedder.debugDisableOverlays = true; ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); @@ -778,7 +778,7 @@ void testMain() { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); await createPlatformView(1, 'test-platform-view'); @@ -824,7 +824,7 @@ void testMain() { HtmlViewEmbedder.debugDisableOverlays = true; ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); await createPlatformView(1, 'test-platform-view'); @@ -863,11 +863,11 @@ void testMain() { ui.platformViewRegistry.registerViewFactory( 'test-visible-view', (int viewId) => - html.DivElement()..className = 'visible-platform-view'); + createDomHTMLDivElement()..className = 'visible-platform-view'); ui.platformViewRegistry.registerViewFactory( 'test-invisible-view', (int viewId) => - html.DivElement()..className = 'invisible-platform-view', + createDomHTMLDivElement()..className = 'invisible-platform-view', isVisible: false, ); await createPlatformView(0, 'test-visible-view'); diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/image_golden_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/image_golden_test.dart index ca4626fa1af..06cb0ff7709 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/image_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/image_golden_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'dart:typed_data'; import 'package:js/js.dart'; @@ -533,7 +532,7 @@ void _testForImageCodecs({required bool useBrowserImageDecoder}) { test('the same image can be rendered on difference surfaces', () async { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement()..id = 'view-0', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); await createPlatformView(0, 'test-platform-view'); diff --git a/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart b/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart index 972d24e4909..2a8eb8e4433 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -1806,7 +1806,7 @@ void _testPlatformView() { ui.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => html.DivElement() + (int viewId) => createDomHTMLDivElement() ..id = 'view-0' ..style.width = '100%' ..style.height = '100%', diff --git a/engine/src/flutter/lib/web_ui/test/engine/surface/platform_view_test.dart b/engine/src/flutter/lib/web_ui/test/engine/surface/platform_view_test.dart index 9c763a4fb69..86c506a8505 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/surface/platform_view_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/surface/platform_view_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -28,11 +27,11 @@ Future testMain() async { setUp(() async { platformViewRegistry.registerViewFactory( 'test-0', - (int viewId) => html.DivElement(), + (int viewId) => createDomHTMLDivElement(), ); platformViewRegistry.registerViewFactory( 'test-1', - (int viewId) => html.DivElement(), + (int viewId) => createDomHTMLDivElement(), ); // Ensure the views are created... await Future.wait(>[