From 647d4fca2b2b5e5122006cfbc0ea6fed79416a4f Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 29 Jun 2023 15:52:48 -0400 Subject: [PATCH] [web] Move web-only initialization APIs to `dart:ui_web` (flutter/engine#43111) | Old API in `dart:ui` | New API in `dart:ui_web` | |-|-| | ~`webOnlyInitializePlatform`~ | ~`ui_web.initializePlatform`~ | | `webOnlyWarmupEngine` | `ui_web.bootstrapEngine` | | `debugEmulateFlutterTesterEnvironment` | `ui_web.debugEmulateFlutterTesterEnvironment` | | `webOnlySetPluginHandler` | `ui_web.setPluginHandler` | Part of https://github.com/flutter/flutter/issues/126831 --- .../ci/licenses_golden/licenses_flutter | 6 + .../lib/web_ui/lib/initialization.dart | 131 +++++++----------- .../web_ui/lib/src/engine/canvaskit/text.dart | 7 +- .../lib/src/engine/html/scene_builder.dart | 5 +- .../web_ui/lib/src/engine/initialization.dart | 2 +- .../lib/src/engine/platform_dispatcher.dart | 2 +- .../lib/web_ui/lib/src/engine/plugins.dart | 4 +- .../lib/src/engine/semantics/semantics.dart | 3 +- .../web_ui/lib/src/engine/text/paragraph.dart | 5 +- .../lib/web_ui/lib/src/engine/text/ruler.dart | 3 +- .../lib/web_ui/lib/ui_web/src/ui_web.dart | 3 + .../lib/ui_web/src/ui_web/initialization.dart | 57 ++++++++ .../src/ui_web/navigation/url_strategy.dart | 3 +- .../web_ui/lib/ui_web/src/ui_web/plugins.dart | 14 ++ .../web_ui/lib/ui_web/src/ui_web/testing.dart | 27 ++++ .../flutter_tester_emulation_golden_test.dart | 3 +- .../lib/web_ui/test/canvaskit/text_test.dart | 7 +- .../test/common/test_initialization.dart | 3 +- .../test/engine/dom_http_fetch_test.dart | 1 - .../test/engine/initialization_test.dart | 10 +- ...application_switcher_description_test.dart | 5 +- .../system_ui_overlay_style_test.dart | 3 +- .../canvas_draw_image_golden_test.dart | 3 +- .../text/canvas_paragraph_builder_test.dart | 5 +- .../html/text/layout_service_plain_test.dart | 5 +- .../lib/web_ui/test/html/text_test.dart | 19 +-- 26 files changed, 215 insertions(+), 121 deletions(-) create mode 100644 engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart create mode 100644 engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/plugins.dart create mode 100644 engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/testing.dart diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index d57b461954..c4e70c380b 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -2110,9 +2110,12 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/tile_mode.dart + ../../../flutter/LICENS ORIGIN: ../../../flutter/lib/web_ui/lib/ui.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/platform_location.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/plugins.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/testing.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/window.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/canvas.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp + ../../../flutter/LICENSE @@ -4791,9 +4794,12 @@ FILE: ../../../flutter/lib/web_ui/lib/tile_mode.dart FILE: ../../../flutter/lib/web_ui/lib/ui.dart FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web.dart FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart +FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/platform_location.dart FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart +FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/plugins.dart +FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/testing.dart FILE: ../../../flutter/lib/web_ui/lib/window.dart FILE: ../../../flutter/lib/web_ui/skwasm/canvas.cpp FILE: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp diff --git a/engine/src/flutter/lib/web_ui/lib/initialization.dart b/engine/src/flutter/lib/web_ui/lib/initialization.dart index bf6b5f2993..f3f0208a09 100644 --- a/engine/src/flutter/lib/web_ui/lib/initialization.dart +++ b/engine/src/flutter/lib/web_ui/lib/initialization.dart @@ -28,89 +28,59 @@ part of ui; /// /// This is only available on the Web, as native Flutter configures the /// environment in the native embedder. +// TODO(mdebbar): Deprecate this and remove it. +// https://github.com/flutter/flutter/issues/127395 Future webOnlyInitializePlatform() async { await engine.initializeEngine(); } -/// Initializes essential bits of the engine before it fully initializes. -/// When [didCreateEngineInitializer] is set, it delegates engine initialization -/// and app startup to the programmer. -/// Else, it immediately triggers the full engine + app bootstrap. -/// -/// This method is called by the flutter_tools package, from the entrypoint that -/// it generates around the main method provided by the programmer. See: -/// * https://github.com/flutter/flutter/blob/2bd3e0d914854aa8c12e933f25c5fd8532ae5571/packages/flutter_tools/lib/src/build_system/targets/web.dart#L135-L163 -/// * https://github.com/flutter/flutter/blob/61fb2de52c7bdac19b7f2f74eaf3f11237e1e91d/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart#L460-L485 -/// -/// This function first calls [engine.initializeEngineServices] so the engine -/// can prepare the js-interop layer that is used by web apps (instead of the -/// old `ui.webOnlyFoo` methods/getters). -/// -/// It then creates a JsObject that is passed to the [didCreateEngineInitializer] -/// JS callback, to delegate bootstrapping the app to the programmer. -/// -/// If said callback is not defined, this assumes that the Flutter Web app is -/// initializing "automatically", as was normal before this feature was -/// introduced. This will immediately run the initEngine and runApp methods -/// (via [engine.AppBootstrap.now]). -/// -/// This is the only bit of `dart:ui` that should be directly called by Flutter -/// web apps. Everything else should go through the JS-interop layer created in -/// `engine.warmup`. -/// -/// This method should NOT trigger the download of any additional resources -/// (except when the app is in "autoStart" mode). +// TODO(mdebbar): Deprecate this and remove it. +// https://github.com/flutter/flutter/issues/127395 Future webOnlyWarmupEngine({ - Function? registerPlugins, - Function? runApp, -}) async { - // Create the object that knows how to bootstrap an app from JS and Dart. - final engine.AppBootstrap bootstrap = engine.AppBootstrap( - initializeEngine: ([engine.JsFlutterConfiguration? configuration]) async { - await engine.initializeEngineServices(jsConfiguration: configuration); - }, runApp: () async { - if (registerPlugins != null) { - registerPlugins(); - } - await engine.initializeEngineUi(); - if (runApp != null) { - runApp(); - } - }, + VoidCallback? registerPlugins, + VoidCallback? runApp, +}) { + assert(() { + engine.printWarning( + 'The webOnlyWarmupEngine API is deprecated and will be removed in a ' + 'future release. Please use `bootstrapEngine` from `dart:ui_web` instead.', + ); + return true; + }()); + return ui_web.bootstrapEngine( + registerPlugins: registerPlugins, + runApp: runApp, ); - - final engine.FlutterLoader? loader = engine.flutter?.loader; - if (loader == null || loader.isAutoStart) { - // The user does not want control of the app, bootstrap immediately. - engine.domWindow.console.debug('Flutter Web Bootstrap: Auto.'); - await bootstrap.autoStart(); - } else { - // Yield control of the bootstrap procedure to the user. - engine.domWindow.console.debug('Flutter Web Bootstrap: Programmatic.'); - loader.didCreateEngineInitializer(bootstrap.prepareEngineInitializer()); - } } -/// Emulates the `flutter test` environment. -/// -/// When set to true, the engine will emulate a specific screen size, and always -/// use the "Ahem" font to reduce test flakiness and dependence on the test -/// environment. -bool get debugEmulateFlutterTesterEnvironment => - _debugEmulateFlutterTesterEnvironment; +// TODO(mdebbar): Deprecate this and remove it. +// https://github.com/flutter/flutter/issues/127395 +bool get debugEmulateFlutterTesterEnvironment { + assert(() { + engine.printWarning( + 'The debugEmulateFlutterTesterEnvironment getter is deprecated and will ' + 'be removed in a future release. Please use ' + '`debugEmulateFlutterTesterEnvironment` from `dart:ui_web` instead.', + ); + return true; + }()); + return ui_web.debugEmulateFlutterTesterEnvironment; +} + +// TODO(mdebbar): Deprecate this and remove it. +// https://github.com/flutter/flutter/issues/127395 set debugEmulateFlutterTesterEnvironment(bool value) { - _debugEmulateFlutterTesterEnvironment = value; - if (_debugEmulateFlutterTesterEnvironment) { - const Size logicalSize = Size(800.0, 600.0); - engine.window.webOnlyDebugPhysicalSizeOverride = - logicalSize * window.devicePixelRatio; - } - engine.debugDisableFontFallbacks = value; + assert(() { + engine.printWarning( + 'The debugEmulateFlutterTesterEnvironment setter is deprecated and will ' + 'be removed in a future release. Please use ' + '`debugEmulateFlutterTesterEnvironment` from `dart:ui_web` instead.', + ); + return true; + }()); + ui_web.debugEmulateFlutterTesterEnvironment = value; } -bool _debugEmulateFlutterTesterEnvironment = false; - -/// Provides the asset manager. // TODO(mdebbar): Deprecate this and remove it. // https://github.com/flutter/flutter/issues/127395 ui_web.AssetManager get webOnlyAssetManager { @@ -124,12 +94,17 @@ ui_web.AssetManager get webOnlyAssetManager { return ui_web.assetManager; } -/// Sets the handler that forwards platform messages to web plugins. -/// -/// This function exists because unlike mobile, on the web plugins are also -/// implemented using Dart code, and that code needs a way to receive messages. -void webOnlySetPluginHandler(Future Function(String, ByteData?, PlatformMessageResponseCallback?) handler) { - engine.pluginMessageCallHandler = handler; +// TODO(mdebbar): Deprecate this and remove it. +// https://github.com/flutter/flutter/issues/127395 +void webOnlySetPluginHandler(PlatformMessageCallback handler) { + assert(() { + engine.printWarning( + 'The webOnlySetPluginHandler API is deprecated and will be removed in a ' + 'future release. Please use `setPluginHandler` from `dart:ui_web` instead.', + ); + return true; + }()); + ui_web.setPluginHandler(handler); } // TODO(mdebbar): Deprecate this and remove it. diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart index f177031d6a..6fe5a29a48 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -7,12 +7,13 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; final bool _ckRequiresClientICU = canvasKit.ParagraphBuilder.RequiresClientICU(); final List _testFonts = ['FlutterTest', 'Ahem']; String? _effectiveFontFamily(String? fontFamily) { - return ui.debugEmulateFlutterTesterEnvironment && !_testFonts.contains(fontFamily) + return ui_web.debugEmulateFlutterTesterEnvironment && !_testFonts.contains(fontFamily) ? _testFonts.first : fontFamily; } @@ -231,7 +232,7 @@ class CkTextStyle implements ui.TextStyle { fontStyle, textBaseline, _effectiveFontFamily(fontFamily), - ui.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, + ui_web.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, fontSize, letterSpacing, wordSpacing, @@ -481,7 +482,7 @@ class CkStrutStyle implements ui.StrutStyle { ui.FontStyle? fontStyle, bool? forceStrutHeight, }) : _fontFamily = _effectiveFontFamily(fontFamily), - _fontFamilyFallback = ui.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, + _fontFamilyFallback = ui_web.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, _fontSize = fontSize, _height = height, _leadingDistribution = leadingDistribution, diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart index d8e8bb3d24..e77e7d4792 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../../engine.dart' show kProfileApplyFrame, kProfilePrerollFrame; import '../dom.dart'; @@ -111,7 +112,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { // Top level transform contains view configuration to scale // scene to devicepixelratio. Use identity instead since CSS uses // logical device pixels. - if (!ui.debugEmulateFlutterTesterEnvironment) { + if (!ui_web.debugEmulateFlutterTesterEnvironment) { assert(matrix4[0] == window.devicePixelRatio && matrix4[5] == window.devicePixelRatio); } @@ -383,7 +384,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { void _addTexture(double dx, double dy, double width, double height, int textureId, ui.FilterQuality filterQuality) { // In test mode, allow this to be a no-op. - if (!ui.debugEmulateFlutterTesterEnvironment) { + if (!ui_web.debugEmulateFlutterTesterEnvironment) { throw UnimplementedError('Textures are not supported in Flutter Web'); } } 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 7d265c424c..94340b6d22 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 @@ -244,7 +244,7 @@ void _setAssetManager(ui_web.AssetManager assetManager) { Future _downloadAssetFonts() async { renderer.fontCollection.clear(); - if (ui.debugEmulateFlutterTesterEnvironment) { + if (ui_web.debugEmulateFlutterTesterEnvironment) { // Load the embedded test font before loading fonts from the assets so that // the embedded test font is the default (first) font. await renderer.fontCollection.loadFontFromList( diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 40b8a0ed4f..4a42c98838 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -472,7 +472,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // In widget tests we want to bypass processing of platform messages. bool returnImmediately = false; assert(() { - if (ui.debugEmulateFlutterTesterEnvironment) { + if (ui_web.debugEmulateFlutterTesterEnvironment) { returnImmediately = true; } return true; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/plugins.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/plugins.dart index 083816b1c2..151935f9fd 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/plugins.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/plugins.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:typed_data'; - import 'package:ui/ui.dart' as ui; -Future Function(String, ByteData?, ui.PlatformMessageResponseCallback?)? pluginMessageCallHandler; +ui.PlatformMessageCallback? pluginMessageCallHandler; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart index c1f99639aa..8e3971b6df 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -7,6 +7,7 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../../engine.dart' show registerHotRestartListener; import '../alarm_clock.dart'; @@ -2116,7 +2117,7 @@ class EngineSemanticsOwner { /// Updates the semantics tree from data in the [uiUpdate]. void updateSemantics(ui.SemanticsUpdate uiUpdate) { if (!_semanticsEnabled) { - if (ui.debugEmulateFlutterTesterEnvironment) { + if (ui_web.debugEmulateFlutterTesterEnvironment) { // Running Flutter widget tests in a fake environment. Don't enable // engine semantics. Test semantics trees violate invariants in ways // production implementation isn't built to handle. For example, tests diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/text/paragraph.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/text/paragraph.dart index 5e17a5a5b5..a6ccc5c4ad 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -5,6 +5,7 @@ import 'dart:math' as math; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../browser_detection.dart'; import '../dom.dart'; @@ -473,7 +474,7 @@ class EngineTextStyle implements ui.TextStyle { // This makes widget tests predictable and less flaky. String result = fontFamily; assert(() { - if (ui.debugEmulateFlutterTesterEnvironment && !_testFonts.contains(fontFamily)) { + if (ui_web.debugEmulateFlutterTesterEnvironment && !_testFonts.contains(fontFamily)) { result = _testFonts.first; } return true; @@ -820,7 +821,7 @@ void applyTextStyleToElement({ } // For test environment use effectiveFontFamily since we need to // consistently use the correct test font. - if (ui.debugEmulateFlutterTesterEnvironment) { + if (ui_web.debugEmulateFlutterTesterEnvironment) { cssStyle.fontFamily = canonicalizeFontFamily(style.effectiveFontFamily)!; } else { cssStyle.fontFamily = canonicalizeFontFamily(style.fontFamily)!; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/text/ruler.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/text/ruler.dart index 23c1cb0644..c3032705fb 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/text/ruler.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../browser_detection.dart'; import '../dom.dart'; @@ -144,7 +145,7 @@ class TextDimensions { if (browserEngine == BrowserEngine.firefox && // In the flutter tester environment, we use a predictable-size for font // measurement tests. - !ui.debugEmulateFlutterTesterEnvironment) { + !ui_web.debugEmulateFlutterTesterEnvironment) { // See subpixel rounding bug : // https://bugzilla.mozilla.org/show_bug.cgi?id=442139 // This causes bottom of letters such as 'y' to be cutoff and diff --git a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web.dart b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web.dart index 7327584344..f8d21ca377 100644 --- a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web.dart +++ b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web.dart @@ -9,6 +9,9 @@ library ui_web; export 'ui_web/asset_manager.dart'; +export 'ui_web/initialization.dart'; export 'ui_web/navigation/platform_location.dart'; export 'ui_web/navigation/url_strategy.dart'; export 'ui_web/platform_view_registry.dart'; +export 'ui_web/plugins.dart'; +export 'ui_web/testing.dart'; diff --git a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart new file mode 100644 index 0000000000..5411f01d21 --- /dev/null +++ b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +/// Bootstraps the Flutter Web engine and app. +/// +/// If the app uses plugins, then the [registerPlugins] callback can be provided +/// to register those plugins. This is done typically by calling +/// `registerPlugins` from the auto-generated `web_plugin_registrant.dart` file. +/// +/// The [runApp] callback is invoked to run the app after the engine is fully +/// initialized. +/// +/// For more information, see what the `flutter_tools` does in the entrypoint +/// that it generates around the app's main method: +/// +/// * https://github.com/flutter/flutter/blob/95be76ab7e3dca2def54454313e97f94f4ac4582/packages/flutter_tools/lib/src/web/file_generators/main_dart.dart#L14-L43 +/// +/// By default, engine initialization and app startup occur immediately and back +/// to back. They can be programmatically controlled by setting +/// `FlutterLoader.didCreateEngineInitializer`. For more information, see how +/// `flutter.js` does it: +/// +/// * https://github.com/flutter/flutter/blob/95be76ab7e3dca2def54454313e97f94f4ac4582/packages/flutter_tools/lib/src/web/file_generators/js/flutter.js +Future bootstrapEngine({ + ui.VoidCallback? registerPlugins, + ui.VoidCallback? runApp, +}) async { + // Create the object that knows how to bootstrap an app from JS and Dart. + final AppBootstrap bootstrap = AppBootstrap( + initializeEngine: ([JsFlutterConfiguration? configuration]) async { + await initializeEngineServices(jsConfiguration: configuration); + }, runApp: () async { + if (registerPlugins != null) { + registerPlugins(); + } + await initializeEngineUi(); + if (runApp != null) { + runApp(); + } + }, + ); + + final FlutterLoader? loader = flutter?.loader; + if (loader == null || loader.isAutoStart) { + // The user does not want control of the app, bootstrap immediately. + domWindow.console.debug('Flutter Web Bootstrap: Auto.'); + await bootstrap.autoStart(); + } else { + // Yield control of the bootstrap procedure to the user. + domWindow.console.debug('Flutter Web Bootstrap: Programmatic.'); + loader.didCreateEngineInitializer(bootstrap.prepareEngineInitializer()); + } +} diff --git a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart index 7932221eb3..9760f005bf 100644 --- a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart +++ b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart @@ -8,9 +8,10 @@ import 'package:meta/meta.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../testing.dart'; import 'platform_location.dart'; -UrlStrategy _realDefaultUrlStrategy = ui.debugEmulateFlutterTesterEnvironment +UrlStrategy _realDefaultUrlStrategy = debugEmulateFlutterTesterEnvironment ? TestUrlStrategy.fromEntry(const TestHistoryEntry('default', null, '/')) : const HashUrlStrategy(); diff --git a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/plugins.dart b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/plugins.dart new file mode 100644 index 0000000000..c3a5dcc45b --- /dev/null +++ b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/plugins.dart @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +/// Sets the handler that forwards platform messages to web plugins. +/// +/// This function exists because unlike mobile, on the web plugins are also +/// implemented using Dart code, and that code needs a way to receive messages. +void setPluginHandler(ui.PlatformMessageCallback handler) { + pluginMessageCallHandler = handler; +} diff --git a/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/testing.dart b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/testing.dart new file mode 100644 index 0000000000..ae5b6e88e3 --- /dev/null +++ b/engine/src/flutter/lib/web_ui/lib/ui_web/src/ui_web/testing.dart @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +/// Whether the Flutter engine is running in `flutter test` emulation mode. +/// +/// When true, the engine will emulate a specific screen size, and always +/// use the "Ahem" font to reduce test flakiness and dependence on the test +/// environment. +bool get debugEmulateFlutterTesterEnvironment => + _debugEmulateFlutterTesterEnvironment; + +/// Sets whether the Flutter engine is running in `flutter test` emulation mode. +set debugEmulateFlutterTesterEnvironment(bool value) { + _debugEmulateFlutterTesterEnvironment = value; + if (_debugEmulateFlutterTesterEnvironment) { + const ui.Size logicalSize = ui.Size(800.0, 600.0); + window.webOnlyDebugPhysicalSizeOverride = + logicalSize * window.devicePixelRatio; + } + debugDisableFontFallbacks = value; +} + +bool _debugEmulateFlutterTesterEnvironment = false; diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart index 81a2c340ff..d26c8dda16 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import 'common.dart'; @@ -16,7 +17,7 @@ void main() { const ui.Rect kDefaultRegion = ui.Rect.fromLTRB(0, 0, 500, 250); void testMain() { - ui.debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; group('flutter_tester emulation', () { setUpCanvasKitTest(); diff --git a/engine/src/flutter/lib/web_ui/test/canvaskit/text_test.dart b/engine/src/flutter/lib/web_ui/test/canvaskit/text_test.dart index d14ad7b75e..8d4af67648 100644 --- a/engine/src/flutter/lib/web_ui/test/canvaskit/text_test.dart +++ b/engine/src/flutter/lib/web_ui/test/canvaskit/text_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import 'common.dart'; @@ -94,9 +95,9 @@ void testMain() { }); group('test fonts in flutterTester environment', () { - final bool resetValue = ui.debugEmulateFlutterTesterEnvironment; - ui.debugEmulateFlutterTesterEnvironment = true; - tearDownAll(() => ui.debugEmulateFlutterTesterEnvironment = resetValue); + final bool resetValue = ui_web.debugEmulateFlutterTesterEnvironment; + ui_web.debugEmulateFlutterTesterEnvironment = true; + tearDownAll(() => ui_web.debugEmulateFlutterTesterEnvironment = resetValue); const List testFonts = ['FlutterTest', 'Ahem']; test('The default test font is used when a non-test fontFamily is specified', () { diff --git a/engine/src/flutter/lib/web_ui/test/common/test_initialization.dart b/engine/src/flutter/lib/web_ui/test/common/test_initialization.dart index 427d47dcd3..2b1124ea1e 100644 --- a/engine/src/flutter/lib/web_ui/test/common/test_initialization.dart +++ b/engine/src/flutter/lib/web_ui/test/common/test_initialization.dart @@ -7,6 +7,7 @@ import 'dart:js_util' as js_util; import 'package:test/test.dart'; import 'package:ui/src/engine.dart' as engine; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import 'fake_asset_manager.dart'; @@ -17,7 +18,7 @@ void setUpUnitTests({ late final FakeAssetScope debugFontsScope; setUpAll(() async { if (emulateTesterEnvironment) { - ui.debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; } // Some of our tests rely on color emoji diff --git a/engine/src/flutter/lib/web_ui/test/engine/dom_http_fetch_test.dart b/engine/src/flutter/lib/web_ui/test/engine/dom_http_fetch_test.dart index 606753ba46..2433d67058 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/dom_http_fetch_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/dom_http_fetch_test.dart @@ -8,7 +8,6 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; - import 'package:ui/ui.dart' as ui; void main() { diff --git a/engine/src/flutter/lib/web_ui/test/engine/initialization_test.dart b/engine/src/flutter/lib/web_ui/test/engine/initialization_test.dart index 2a10f1bc50..0068628b88 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/initialization_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/initialization_test.dart @@ -8,7 +8,7 @@ import 'package:js/js_util.dart' as js_util; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart' as engine; -import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; @JS('_flutter') external set _loader(JSAny? loader); @@ -28,7 +28,7 @@ void main() { } void testMain() { - test('webOnlyWarmupEngine calls _flutter.loader.didCreateEngineInitializer callback', () async { + test('bootstrapEngine calls _flutter.loader.didCreateEngineInitializer callback', () async { Object? engineInitializer; void didCreateEngineInitializerMock(Object? obj) { @@ -41,7 +41,7 @@ void testMain() { // Reset the engine engine.debugResetEngineInitializationState(); - await ui.webOnlyWarmupEngine( + await ui_web.bootstrapEngine( registerPlugins: () {}, runApp: () {}, ); @@ -52,7 +52,7 @@ void testMain() { expect(js_util.hasProperty(engineInitializer!, 'autoStart'), isTrue, reason: 'Missing FlutterEngineInitializer method: autoStart.'); }); - test('webOnlyWarmupEngine does auto-start when _flutter.loader.didCreateEngineInitializer does not exist', () async { + test('bootstrapEngine does auto-start when _flutter.loader.didCreateEngineInitializer does not exist', () async { loader = null; bool pluginsRegistered = false; @@ -67,7 +67,7 @@ void testMain() { // Reset the engine engine.debugResetEngineInitializationState(); - await ui.webOnlyWarmupEngine( + await ui_web.bootstrapEngine( registerPlugins: registerPluginsMock, runApp: runAppMock, ); diff --git a/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart b/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart index 1dd6902e6a..140f990f35 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; void main() { internalBootstrapBrowserTest(() => testMain); @@ -25,7 +26,7 @@ Future testMain() async { group('Title and Primary Color/Theme meta', () { test('is set on the document by platform message', () { // Run the unit test without emulating Flutter tester environment. - ui.debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; // TODO(yjbanov): https://github.com/flutter/flutter/issues/39159 domDocument.title = ''; @@ -69,7 +70,7 @@ Future testMain() async { test('supports null title and primaryColor', () { // Run the unit test without emulating Flutter tester environment. - ui.debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; const ui.Color expectedNullColor = ui.Color(0xFF000000); // TODO(yjbanov): https://github.com/flutter/flutter/issues/39159 diff --git a/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart b/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart index 8e040bfc43..25e248d66a 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; void main() { internalBootstrapBrowserTest(() => testMain); @@ -38,7 +39,7 @@ void testMain() { group('SystemUIOverlayStyle', () { test('theme color is set / removed by platform message', () { // Run the unit test without emulating Flutter tester environment. - ui.debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; expect(getCssThemeColor(), null); diff --git a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart index a3bfcca3e6..6c6de54d98 100644 --- a/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart +++ b/engine/src/flutter/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart @@ -11,6 +11,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import 'package:web_engine_tester/golden_tester.dart'; @@ -318,7 +319,7 @@ Future testMain() async { // Cyan text should be above everything. test('Paints text above and below image', () async { // Use a non-Ahem font so that text is visible. - debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); rc.save(); diff --git a/engine/src/flutter/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart b/engine/src/flutter/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart index 5fc4142f0d..6342f6d87d 100644 --- a/engine/src/flutter/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart +++ b/engine/src/flutter/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../../common/test_initialization.dart'; import '../paragraph/helper.dart'; @@ -414,7 +415,7 @@ Future testMain() async { // Paragraphs and spans force the FlutterTest font in test mode. We need to // trick them into thinking they are not in test mode, so they use the // provided font family. - debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 12.0, fontFamily: 'first'); final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); @@ -454,7 +455,7 @@ Future testMain() async { // measurements. ignorePositions: true, ); - debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; }); // Regression test for https://github.com/flutter/flutter/issues/108431. diff --git a/engine/src/flutter/lib/web_ui/test/html/text/layout_service_plain_test.dart b/engine/src/flutter/lib/web_ui/test/html/text/layout_service_plain_test.dart index d08d88b15e..affdae3bd8 100644 --- a/engine/src/flutter/lib/web_ui/test/html/text/layout_service_plain_test.dart +++ b/engine/src/flutter/lib/web_ui/test/html/text/layout_service_plain_test.dart @@ -6,6 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../../common/test_initialization.dart'; import '../paragraph/helper.dart'; @@ -714,7 +715,7 @@ Future testMain() async { test('does not leak styles across spanometers', () { // This prevents the Ahem font from being forced in all paragraphs. - ui.debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; final CanvasParagraph p1 = plain( EngineParagraphStyle( @@ -750,6 +751,6 @@ Future testMain() async { expect(textContext.font, contains('40px')); expect(textContext.font, contains('FontFamily2')); - ui.debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; }); } diff --git a/engine/src/flutter/lib/web_ui/test/html/text_test.dart b/engine/src/flutter/lib/web_ui/test/html/text_test.dart index 72a624a633..4bb4595681 100644 --- a/engine/src/flutter/lib/web_ui/test/html/text_test.dart +++ b/engine/src/flutter/lib/web_ui/test/html/text_test.dart @@ -9,6 +9,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../common/matchers.dart'; import '../common/test_initialization.dart'; @@ -236,7 +237,7 @@ Future testMain() async { test('adds Arial and sans-serif as fallback fonts', () { // Set this to false so it doesn't default to the test font. - debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; final CanvasParagraph paragraph = plain(EngineParagraphStyle( fontFamily: 'SomeFont', @@ -247,14 +248,14 @@ Future testMain() async { expect(paragraph.toDomElement().children.single.style.fontFamily, 'SomeFont, $fallback, sans-serif'); - debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; }, // TODO(mdebbar): https://github.com/flutter/flutter/issues/46638 skip: browserEngine == BrowserEngine.firefox); test('does not add fallback fonts to generic families', () { // Set this to false so it doesn't default to the default test font. - debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; final CanvasParagraph paragraph = plain(EngineParagraphStyle( fontFamily: 'serif', @@ -264,12 +265,12 @@ Future testMain() async { paragraph.layout(constrain(double.infinity)); expect(paragraph.toDomElement().children.single.style.fontFamily, 'serif'); - debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; }); test('can set font families that need to be quoted', () { // Set this to false so it doesn't default to the default test font. - debugEmulateFlutterTesterEnvironment = false; + ui_web.debugEmulateFlutterTesterEnvironment = false; final CanvasParagraph paragraph = plain(EngineParagraphStyle( fontFamily: 'MyFont 2000', @@ -280,7 +281,7 @@ Future testMain() async { expect(paragraph.toDomElement().children.single.style.fontFamily, '"MyFont 2000", $fallback, sans-serif'); - debugEmulateFlutterTesterEnvironment = true; + ui_web.debugEmulateFlutterTesterEnvironment = true; }); group('TextRange', () { @@ -360,9 +361,9 @@ Future testMain() async { }); group('test fonts in flutterTester environment', () { - final bool resetValue = debugEmulateFlutterTesterEnvironment; - debugEmulateFlutterTesterEnvironment = true; - tearDownAll(() => debugEmulateFlutterTesterEnvironment = resetValue); + final bool resetValue = ui_web.debugEmulateFlutterTesterEnvironment; + ui_web.debugEmulateFlutterTesterEnvironment = true; + tearDownAll(() => ui_web.debugEmulateFlutterTesterEnvironment = resetValue); const List testFonts = ['FlutterTest', 'Ahem']; test('The default test font is used when a non-test fontFamily is specified, or fontFamily is not specified', () {