diff --git a/ci/builders/linux_web_engine.json b/ci/builders/linux_web_engine.json index 9dc023b4458b4..138e42565327b 100644 --- a/ci/builders/linux_web_engine.json +++ b/ci/builders/linux_web_engine.json @@ -179,7 +179,7 @@ } }, { - "name": "web_tests/test_bundles/dart2js-skwasm-skwasm_stub", + "name": "web_tests/test_bundles/dart2wasm-html-engine", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -187,11 +187,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2js-skwasm-skwasm_stub", + "name": "compile bundle dart2wasm-html-engine", "parameters": [ "test", "--compile", - "--bundle=dart2js-skwasm-skwasm_stub" + "--bundle=dart2wasm-html-engine" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -201,7 +201,7 @@ } }, { - "name": "web_tests/test_bundles/dart2wasm-html-engine", + "name": "web_tests/test_bundles/dart2wasm-html-html", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -209,11 +209,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2wasm-html-engine", + "name": "compile bundle dart2wasm-html-html", "parameters": [ "test", "--compile", - "--bundle=dart2wasm-html-engine" + "--bundle=dart2wasm-html-html" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -223,7 +223,7 @@ } }, { - "name": "web_tests/test_bundles/dart2wasm-html-html", + "name": "web_tests/test_bundles/dart2wasm-html-ui", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -231,11 +231,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2wasm-html-html", + "name": "compile bundle dart2wasm-html-ui", "parameters": [ "test", "--compile", - "--bundle=dart2wasm-html-html" + "--bundle=dart2wasm-html-ui" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -245,7 +245,7 @@ } }, { - "name": "web_tests/test_bundles/dart2wasm-html-ui", + "name": "web_tests/test_bundles/dart2wasm-canvaskit-canvaskit", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -253,11 +253,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2wasm-html-ui", + "name": "compile bundle dart2wasm-canvaskit-canvaskit", "parameters": [ "test", "--compile", - "--bundle=dart2wasm-html-ui" + "--bundle=dart2wasm-canvaskit-canvaskit" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -267,7 +267,7 @@ } }, { - "name": "web_tests/test_bundles/dart2wasm-canvaskit-canvaskit", + "name": "web_tests/test_bundles/dart2wasm-canvaskit-ui", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -275,11 +275,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2wasm-canvaskit-canvaskit", + "name": "compile bundle dart2wasm-canvaskit-ui", "parameters": [ "test", "--compile", - "--bundle=dart2wasm-canvaskit-canvaskit" + "--bundle=dart2wasm-canvaskit-ui" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -289,7 +289,7 @@ } }, { - "name": "web_tests/test_bundles/dart2wasm-canvaskit-ui", + "name": "web_tests/test_bundles/dart2wasm-skwasm-ui", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -297,11 +297,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2wasm-canvaskit-ui", + "name": "compile bundle dart2wasm-skwasm-ui", "parameters": [ "test", "--compile", - "--bundle=dart2wasm-canvaskit-ui" + "--bundle=dart2wasm-skwasm-ui" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -311,7 +311,7 @@ } }, { - "name": "web_tests/test_bundles/dart2wasm-skwasm-ui", + "name": "web_tests/test_bundles/fallbacks", "drone_dimensions": [ "device_type=none", "os=Linux" @@ -319,11 +319,11 @@ "generators": { "tasks": [ { - "name": "compile bundle dart2wasm-skwasm-ui", + "name": "compile bundle fallbacks", "parameters": [ "test", "--compile", - "--bundle=dart2wasm-skwasm-ui" + "--bundle=fallbacks" ], "scripts": [ "flutter/lib/web_ui/dev/felt" @@ -514,42 +514,6 @@ } ] }, - { - "name": "Linux run chrome-dart2js-skwasm-skwasm_stub suite", - "recipe": "engine_v2/tester_engine", - "drone_dimensions": [ - "device_type=none", - "os=Linux" - ], - "gclient_variables": { - "download_android_deps": false - }, - "dependencies": [ - "web_tests/artifacts", - "web_tests/test_bundles/dart2js-skwasm-skwasm_stub" - ], - "test_dependencies": [ - { - "dependency": "goldctl", - "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" - }, - { - "dependency": "chrome_and_driver", - "version": "119.0.6045.9" - } - ], - "tasks": [ - { - "name": "run suite chrome-dart2js-skwasm-skwasm_stub", - "parameters": [ - "test", - "--run", - "--suite=chrome-dart2js-skwasm-skwasm_stub" - ], - "script": "flutter/lib/web_ui/dev/felt" - } - ] - }, { "name": "Linux run chrome-full-dart2js-canvaskit-canvaskit suite", "recipe": "engine_v2/tester_engine", @@ -1090,6 +1054,78 @@ } ] }, + { + "name": "Linux run chrome-fallbacks suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Linux" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/fallbacks" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-fallbacks", + "parameters": [ + "test", + "--run", + "--suite=chrome-fallbacks" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Linux run firefox-fallbacks suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Linux" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/fallbacks" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "firefox", + "version": "version:106.0" + } + ], + "tasks": [ + { + "name": "run suite firefox-fallbacks", + "parameters": [ + "test", + "--run", + "--suite=firefox-fallbacks" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, { "name": "Mac run safari-dart2js-html-engine suite", "recipe": "engine_v2/tester_engine", @@ -1255,6 +1291,39 @@ } ] }, + { + "name": "Mac run safari-fallbacks suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Mac-13", + "cpu=arm64" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/fallbacks" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + } + ], + "tasks": [ + { + "name": "run suite safari-fallbacks", + "parameters": [ + "test", + "--run", + "--suite=safari-fallbacks" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, { "name": "Windows run chrome-dart2js-html-engine suite", "recipe": "engine_v2/tester_engine", @@ -1436,7 +1505,7 @@ ] }, { - "name": "Windows run chrome-dart2js-skwasm-skwasm_stub suite", + "name": "Windows run chrome-full-dart2js-canvaskit-canvaskit suite", "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", @@ -1447,7 +1516,7 @@ }, "dependencies": [ "web_tests/artifacts", - "web_tests/test_bundles/dart2js-skwasm-skwasm_stub" + "web_tests/test_bundles/dart2js-canvaskit-canvaskit" ], "test_dependencies": [ { @@ -1461,18 +1530,18 @@ ], "tasks": [ { - "name": "run suite chrome-dart2js-skwasm-skwasm_stub", + "name": "run suite chrome-full-dart2js-canvaskit-canvaskit", "parameters": [ "test", "--run", - "--suite=chrome-dart2js-skwasm-skwasm_stub" + "--suite=chrome-full-dart2js-canvaskit-canvaskit" ], "script": "flutter/lib/web_ui/dev/felt" } ] }, { - "name": "Windows run chrome-full-dart2js-canvaskit-canvaskit suite", + "name": "Windows run chrome-full-dart2js-canvaskit-ui suite", "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", @@ -1483,7 +1552,7 @@ }, "dependencies": [ "web_tests/artifacts", - "web_tests/test_bundles/dart2js-canvaskit-canvaskit" + "web_tests/test_bundles/dart2js-canvaskit-ui" ], "test_dependencies": [ { @@ -1497,18 +1566,18 @@ ], "tasks": [ { - "name": "run suite chrome-full-dart2js-canvaskit-canvaskit", + "name": "run suite chrome-full-dart2js-canvaskit-ui", "parameters": [ "test", "--run", - "--suite=chrome-full-dart2js-canvaskit-canvaskit" + "--suite=chrome-full-dart2js-canvaskit-ui" ], "script": "flutter/lib/web_ui/dev/felt" } ] }, { - "name": "Windows run chrome-full-dart2js-canvaskit-ui suite", + "name": "Windows run chrome-dart2wasm-html-engine suite", "recipe": "engine_v2/tester_engine", "drone_dimensions": [ "device_type=none", @@ -1519,7 +1588,7 @@ }, "dependencies": [ "web_tests/artifacts", - "web_tests/test_bundles/dart2js-canvaskit-ui" + "web_tests/test_bundles/dart2wasm-html-engine" ], "test_dependencies": [ { @@ -1533,11 +1602,299 @@ ], "tasks": [ { - "name": "run suite chrome-full-dart2js-canvaskit-ui", + "name": "run suite chrome-dart2wasm-html-engine", "parameters": [ "test", "--run", - "--suite=chrome-full-dart2js-canvaskit-ui" + "--suite=chrome-dart2wasm-html-engine" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-dart2wasm-html-html suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-html-html" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-dart2wasm-html-html", + "parameters": [ + "test", + "--run", + "--suite=chrome-dart2wasm-html-html" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-dart2wasm-html-ui suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-html-ui" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-dart2wasm-html-ui", + "parameters": [ + "test", + "--run", + "--suite=chrome-dart2wasm-html-ui" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-dart2wasm-canvaskit-canvaskit suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-canvaskit-canvaskit" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-dart2wasm-canvaskit-canvaskit", + "parameters": [ + "test", + "--run", + "--suite=chrome-dart2wasm-canvaskit-canvaskit" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-dart2wasm-canvaskit-ui suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-canvaskit-ui" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-dart2wasm-canvaskit-ui", + "parameters": [ + "test", + "--run", + "--suite=chrome-dart2wasm-canvaskit-ui" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-dart2wasm-skwasm-ui suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-skwasm-ui" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-dart2wasm-skwasm-ui", + "parameters": [ + "test", + "--run", + "--suite=chrome-dart2wasm-skwasm-ui" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-full-dart2wasm-canvaskit-canvaskit suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-canvaskit-canvaskit" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-full-dart2wasm-canvaskit-canvaskit", + "parameters": [ + "test", + "--run", + "--suite=chrome-full-dart2wasm-canvaskit-canvaskit" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-full-dart2wasm-canvaskit-ui suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/dart2wasm-canvaskit-ui" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-full-dart2wasm-canvaskit-ui", + "parameters": [ + "test", + "--run", + "--suite=chrome-full-dart2wasm-canvaskit-ui" + ], + "script": "flutter/lib/web_ui/dev/felt" + } + ] + }, + { + "name": "Windows run chrome-fallbacks suite", + "recipe": "engine_v2/tester_engine", + "drone_dimensions": [ + "device_type=none", + "os=Windows" + ], + "gclient_variables": { + "download_android_deps": false + }, + "dependencies": [ + "web_tests/artifacts", + "web_tests/test_bundles/fallbacks" + ], + "test_dependencies": [ + { + "dependency": "goldctl", + "version": "git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd" + }, + { + "dependency": "chrome_and_driver", + "version": "119.0.6045.9" + } + ], + "tasks": [ + { + "name": "run suite chrome-fallbacks", + "parameters": [ + "test", + "--run", + "--suite=chrome-fallbacks" ], "script": "flutter/lib/web_ui/dev/felt" } diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9a00e8054cbed..6cf87b1e6a704 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5872,7 +5872,16 @@ ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc + ../../ ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/base_uri.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/canvaskit_loader.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/entrypoint_loader.js + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/flutter.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/instantiate_wasm.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/loader.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/service_worker_loader.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/skwasm_loader.js + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/trusted_types.js + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/annotations.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/canvas.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/channel_buffers.dart + ../../../flutter/LICENSE @@ -8692,7 +8701,17 @@ FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc FILE: ../../../flutter/lib/ui/window/viewport_metrics.h +FILE: ../../../flutter/lib/web_ui/flutter_js/src/base_uri.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/canvaskit_loader.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/entrypoint_loader.js FILE: ../../../flutter/lib/web_ui/flutter_js/src/flutter.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/instantiate_wasm.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/loader.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/service_worker_loader.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/skwasm_loader.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/trusted_types.js +FILE: ../../../flutter/lib/web_ui/flutter_js/src/types.d.ts FILE: ../../../flutter/lib/web_ui/lib/annotations.dart FILE: ../../../flutter/lib/web_ui/lib/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/channel_buffers.dart diff --git a/lib/web_ui/dev/chrome.dart b/lib/web_ui/dev/chrome.dart index 374d4d61e1646..b209ddfb96dff 100644 --- a/lib/web_ui/dev/chrome.dart +++ b/lib/web_ui/dev/chrome.dart @@ -23,14 +23,11 @@ import 'package_lock.dart'; /// Provides an environment for desktop Chrome. class ChromeEnvironment implements BrowserEnvironment { ChromeEnvironment({ - required bool enableWasmGC, required bool useDwarf, - }) : _enableWasmGC = enableWasmGC, - _useDwarf = useDwarf; + }) : _useDwarf = useDwarf; late final BrowserInstallation _installation; - final bool _enableWasmGC; final bool _useDwarf; @override @@ -42,7 +39,6 @@ class ChromeEnvironment implements BrowserEnvironment { url, _installation, debug: debug, - enableWasmGC: _enableWasmGC, useDwarf: _useDwarf ); } @@ -83,7 +79,6 @@ class Chrome extends Browser { Uri url, BrowserInstallation installation, { required bool debug, - required bool enableWasmGC, required bool useDwarf, }) { final Completer remoteDebuggerCompleter = Completer.sync(); @@ -101,13 +96,7 @@ class Chrome extends Browser { final bool isChromeNoSandbox = Platform.environment['CHROME_NO_SANDBOX'] == 'true'; final String dir = await generateUserDirectory(installation, useDwarf); - final String jsFlags = enableWasmGC ? [ - '--experimental-wasm-gc', - '--experimental-wasm-stack-switching', - '--experimental-wasm-type-reflection', - ].join(' ') : ''; final List args = [ - if (jsFlags.isNotEmpty) '--js-flags=$jsFlags', '--user-data-dir=$dir', url.toString(), if (!debug) diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 301b9bc05ae81..0eabc3b62eeed 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -240,12 +240,11 @@ const List kAllBrowserNames = [ /// The [browserName] matches the browser name passed as the `--browser` option. BrowserEnvironment getBrowserEnvironment( BrowserName browserName, { - required bool enableWasmGC, required bool useDwarf, }) { switch (browserName) { case BrowserName.chrome: - return ChromeEnvironment(enableWasmGC: enableWasmGC, useDwarf: useDwarf); + return ChromeEnvironment(useDwarf: useDwarf); case BrowserName.edge: return EdgeEnvironment(); case BrowserName.firefox: diff --git a/lib/web_ui/dev/felt_config.dart b/lib/web_ui/dev/felt_config.dart index 6e42e500d2fb1..31f873938a830 100644 --- a/lib/web_ui/dev/felt_config.dart +++ b/lib/web_ui/dev/felt_config.dart @@ -32,11 +32,11 @@ class TestSet { } class TestBundle { - TestBundle(this.name, this.testSet, this.compileConfig); + TestBundle(this.name, this.testSet, this.compileConfigs); final String name; final TestSet testSet; - final CompileConfiguration compileConfig; + final List compileConfigs; } enum CanvasKitVariant { @@ -157,12 +157,16 @@ class FeltConfig { if (testSet == null) { throw AssertionError('Test set not found with name: `$testSetName` (referenced by test bundle: `$name`)'); } - final String compileConfigName = testBundleYaml['compile-config'] as String; - final CompileConfiguration? compileConfig = compileConfigsByName[compileConfigName]; - if (compileConfig == null) { - throw AssertionError('Compile config not found with name: `$compileConfigName` (referenced by test bundle: `$name`)'); + final dynamic compileConfigsValue = testBundleYaml['compile-configs']; + final List compileConfigs; + if (compileConfigsValue is String) { + compileConfigs = [compileConfigsByName[compileConfigsValue]!]; + } else { + compileConfigs = (compileConfigsValue as List).map( + (dynamic configName) => compileConfigsByName[configName as String]! + ).toList(); } - final TestBundle bundle = TestBundle(name, testSet, compileConfig); + final TestBundle bundle = TestBundle(name, testSet, compileConfigs); testBundles.add(bundle); if (testBundlesByName.containsKey(name)) { throw AssertionError('Duplicate test bundle name: $name'); @@ -202,11 +206,6 @@ class FeltConfig { if (runConfig == null) { throw AssertionError('Run config not found with name: `$runConfigName` (referenced by test suite: `$name`)'); } - if (bundle.compileConfig.renderer == Renderer.canvaskit && runConfig.variant == null) { - throw AssertionError( - 'Run config `$runConfigName` was used with a CanvasKit test bundle `$testBundleName` ' - 'but did not specify a CanvasKit variant (referenced by test suite: `$name`)'); - } bool canvasKit = false; bool canvasKitChromium = false; bool skwasm = false; diff --git a/lib/web_ui/dev/generate_builder_json.dart b/lib/web_ui/dev/generate_builder_json.dart index fe1e6f664a9aa..b2d9a5adc6bd5 100644 --- a/lib/web_ui/dev/generate_builder_json.dart +++ b/lib/web_ui/dev/generate_builder_json.dart @@ -120,11 +120,7 @@ Iterable _getAllTestSteps(List suites) { suite.runConfig.browser == BrowserName.safari ), ..._getTestStepsForPlatform(suites, 'Windows', (TestSuite suite) => - suite.runConfig.browser == BrowserName.chrome && - - // TODO(jacksongardner): Enable dart2wasm tests on Windows - // https://github.com/flutter/flutter/issues/124082 - suite.testBundle.compileConfig.compiler != Compiler.dart2wasm + suite.runConfig.browser == BrowserName.chrome ), ]; } diff --git a/lib/web_ui/dev/steps/compile_bundle_step.dart b/lib/web_ui/dev/steps/compile_bundle_step.dart index 6601e2fe49e20..b1d86ce8eeb2c 100644 --- a/lib/web_ui/dev/steps/compile_bundle_step.dart +++ b/lib/web_ui/dev/steps/compile_bundle_step.dart @@ -61,20 +61,20 @@ class CompileBundleStep implements PipelineStep { .toList(); } - TestCompiler _createCompiler() { - switch (bundle.compileConfig.compiler) { + TestCompiler _createCompiler(CompileConfiguration config) { + switch (config.compiler) { case Compiler.dart2js: return Dart2JSCompiler( testSetDirectory, outputBundleDirectory, - renderer: bundle.compileConfig.renderer, + renderer: config.renderer, isVerbose: isVerbose, ); case Compiler.dart2wasm: return Dart2WasmCompiler( testSetDirectory, outputBundleDirectory, - renderer: bundle.compileConfig.renderer, + renderer: config.renderer, isVerbose: isVerbose, ); } @@ -84,7 +84,9 @@ class CompileBundleStep implements PipelineStep { Future run() async { print('Compiling test bundle ${bundle.name.ansiMagenta}...'); final List allTests = _findTestFiles(); - final TestCompiler compiler = _createCompiler(); + final List compilers = bundle.compileConfigs.map( + (CompileConfiguration config) => _createCompiler(config) + ).toList(); final Stopwatch stopwatch = Stopwatch()..start(); final String testSetDirectoryPath = testSetDirectory.path; @@ -94,26 +96,28 @@ class CompileBundleStep implements PipelineStep { } final List>> pendingResults = >>[]; - for (final FilePath testFile in allTests) { - final String relativePath = pathlib.relative( - testFile.absolute, - from: testSetDirectoryPath); - final Future> result = compilePool.withResource(() async { - if (testFiles != null && !testFiles!.contains(testFile)) { - return MapEntry(relativePath, CompileResult.filtered); - } - final bool success = await compiler.compileTest(testFile); - const int maxTestNameLength = 80; - final String truncatedPath = relativePath.length > maxTestNameLength - ? relativePath.replaceRange(maxTestNameLength - 3, relativePath.length, '...') - : relativePath; - final String expandedPath = truncatedPath.padRight(maxTestNameLength); - io.stdout.write('\r ${success ? expandedPath.ansiGreen : expandedPath.ansiRed}'); - return success - ? MapEntry(relativePath, CompileResult.success) - : MapEntry(relativePath, CompileResult.compilationFailure); - }); - pendingResults.add(result); + for (final TestCompiler compiler in compilers) { + for (final FilePath testFile in allTests) { + final String relativePath = pathlib.relative( + testFile.absolute, + from: testSetDirectoryPath); + final Future> result = compilePool.withResource(() async { + if (testFiles != null && !testFiles!.contains(testFile)) { + return MapEntry(relativePath, CompileResult.filtered); + } + final bool success = await compiler.compileTest(testFile); + const int maxTestNameLength = 80; + final String truncatedPath = relativePath.length > maxTestNameLength + ? relativePath.replaceRange(maxTestNameLength - 3, relativePath.length, '...') + : relativePath; + final String expandedPath = truncatedPath.padRight(maxTestNameLength); + io.stdout.write('\r ${success ? expandedPath.ansiGreen : expandedPath.ansiRed}'); + return success + ? MapEntry(relativePath, CompileResult.success) + : MapEntry(relativePath, CompileResult.compilationFailure); + }); + pendingResults.add(result); + } } final Map results = Map.fromEntries(await Future.wait(pendingResults)); stopwatch.stop(); @@ -121,8 +125,11 @@ class CompileBundleStep implements PipelineStep { final String resultsJson = const JsonEncoder.withIndent(' ').convert({ 'name': bundle.name, 'directory': bundle.testSet.directory, - 'compiler': bundle.compileConfig.compiler.name, - 'renderer': bundle.compileConfig.renderer.name, + 'builds': bundle.compileConfigs.map( + (CompileConfiguration config) => { + 'compiler': config.compiler.name, + 'renderer': config.renderer.name, + }).toList(), 'compileTimeInMs': stopwatch.elapsedMilliseconds, 'results': results.map((String k, CompileResult v) => MapEntry(k, v.name)), }); diff --git a/lib/web_ui/dev/steps/copy_artifacts_step.dart b/lib/web_ui/dev/steps/copy_artifacts_step.dart index fa63d91761032..b02f6f49e4c8b 100644 --- a/lib/web_ui/dev/steps/copy_artifacts_step.dart +++ b/lib/web_ui/dev/steps/copy_artifacts_step.dart @@ -33,7 +33,6 @@ class CopyArtifactsStep implements PipelineStep { @override Future run() async { await environment.webTestsArtifactsDir.create(recursive: true); - await copyTestBootstrapScripts(); await buildHostPage(); await copyTestFonts(); await copySkiaTestImages(); @@ -52,23 +51,6 @@ class CopyArtifactsStep implements PipelineStep { } } - Future copyTestBootstrapScripts() async { - for (final String filename in [ - 'test_dart2js.js', - 'test_dart2wasm.js', - ]) { - final io.File sourceFile = io.File(pathlib.join( - environment.webUiDevDir.path, - filename, - )); - final io.File targetFile = io.File(pathlib.join( - environment.webTestsArtifactsDir.path, - filename, - )); - await sourceFile.copy(targetFile.path); - } - } - Future copyTestFonts() async { const Map testFonts = { 'Ahem': 'ahem.ttf', diff --git a/lib/web_ui/dev/steps/run_suite_step.dart b/lib/web_ui/dev/steps/run_suite_step.dart index adb9a12d8bb4b..a19abdc5302c9 100644 --- a/lib/web_ui/dev/steps/run_suite_step.dart +++ b/lib/web_ui/dev/steps/run_suite_step.dart @@ -49,8 +49,6 @@ class RunSuiteStep implements PipelineStep { /// Require Skia Gold to be available and reachable. final bool requireSkiaGold; - bool get isWasm => suite.testBundle.compileConfig.compiler == Compiler.dart2wasm; - @override String get description => 'run_suite'; @@ -65,7 +63,6 @@ class RunSuiteStep implements PipelineStep { _prepareTestResultsDirectory(); final BrowserEnvironment browserEnvironment = getBrowserEnvironment( suite.runConfig.browser, - enableWasmGC: isWasm, useDwarf: useDwarf, ); await browserEnvironment.prepare(); @@ -177,12 +174,18 @@ class RunSuiteStep implements PipelineStep { } Future _createSkiaClient() async { - final Renderer renderer = suite.testBundle.compileConfig.renderer; + if (suite.testBundle.compileConfigs.length > 1) { + // Multiple compile configs are only used for our fallback tests, which + // do not collect goldens. + return null; + } + final Renderer renderer = suite.testBundle.compileConfigs.first.renderer; final CanvasKitVariant? variant = suite.runConfig.variant; final io.Directory workDirectory = getSkiaGoldDirectoryForSuite(suite); if (workDirectory.existsSync()) { workDirectory.deleteSync(recursive: true); } + final bool isWasm = suite.testBundle.compileConfigs.first.compiler == Compiler.dart2wasm; final SkiaGoldClient skiaClient = SkiaGoldClient( workDirectory, dimensions: { diff --git a/lib/web_ui/dev/suite_filter.dart b/lib/web_ui/dev/suite_filter.dart index 25b566a1ff18d..91f26610bbb87 100644 --- a/lib/web_ui/dev/suite_filter.dart +++ b/lib/web_ui/dev/suite_filter.dart @@ -70,18 +70,28 @@ class FileFilter extends BundleNameFilter { } } -class CompilerFilter extends AllowListSuiteFilter { - CompilerFilter({required super.allowList}); +class CompilerFilter extends SuiteFilter { + CompilerFilter({required this.allowList}); + + final Set allowList; @override - Compiler getAttributeForSuite(TestSuite suite) => suite.testBundle.compileConfig.compiler; + SuiteFilterResult filterSuite(TestSuite suite) => suite.testBundle.compileConfigs.any( + (CompileConfiguration config) => allowList.contains(config.compiler) + ) ? SuiteFilterResult.accepted() + : SuiteFilterResult.rejected('Selected compilers not used in suite.'); } -class RendererFilter extends AllowListSuiteFilter { - RendererFilter({required super.allowList}); +class RendererFilter extends SuiteFilter { + RendererFilter({required this.allowList}); + + final Set allowList; @override - Renderer getAttributeForSuite(TestSuite suite) => suite.testBundle.compileConfig.renderer; + SuiteFilterResult filterSuite(TestSuite suite) => suite.testBundle.compileConfigs.any( + (CompileConfiguration config) => allowList.contains(config.renderer) + ) ? SuiteFilterResult.accepted() + : SuiteFilterResult.rejected('Selected renderers not used in suite.'); } class CanvasKitVariantFilter extends AllowListSuiteFilter { diff --git a/lib/web_ui/dev/test_dart2js.js b/lib/web_ui/dev/test_dart2js.js deleted file mode 100644 index 22f55b522bb03..0000000000000 --- a/lib/web_ui/dev/test_dart2js.js +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -// This script runs in HTML files and loads and instantiates unit tests -// for the flutter web engine that are compiled to dart2js. It is based -// off of the `test/dart.js` script from the `test` dart package. - -export const runTest = (configuration) => { - // Sends an error message to the server indicating that the script failed to - // load. - // - // This mimics a MultiChannel-formatted message. - var sendLoadException = function (message) { - window.parent.postMessage({ - "href": window.location.href, - "data": [0, { "type": "loadException", "message": message }], - "exception": true, - }, window.location.origin); - } - - // Listen for dartLoadException events and forward to the server. - window.addEventListener('dartLoadException', function (e) { - sendLoadException(e.detail); - }); - - // The basename of the current page. - var name = window.location.href.replace(/.*\//, '').replace(/#.*/, ''); - - // Find . - var links = document.getElementsByTagName("link"); - var testLinks = []; - var length = links.length; - for (var i = 0; i < length; ++i) { - if (links[i].rel == "x-dart-test") testLinks.push(links[i]); - } - - if (testLinks.length != 1) { - sendLoadException( - 'Expected exactly 1 in ' + name + ', found ' + - testLinks.length + '.'); - return; - } - - var link = testLinks[0]; - - if (link.href == '') { - sendLoadException( - 'Expected in ' + name + ' to have an "href" ' + - 'attribute.'); - return; - } - - try { - window._flutter.loader.loadEntrypoint({ - entrypointUrl: link.href + '.browser_test.dart.js', - onEntrypointLoaded: function(engineInitializer) { - engineInitializer.initializeEngine(configuration).then(function(appRunner) { - appRunner.runApp(); - }); - } - }); - } catch (exception) { - const message = `Failed to bootstrap unit test: ${exception}`; - sendLoadException(message); - } -}; diff --git a/lib/web_ui/dev/test_dart2wasm.js b/lib/web_ui/dev/test_dart2wasm.js deleted file mode 100644 index e1d1c32c164f9..0000000000000 --- a/lib/web_ui/dev/test_dart2wasm.js +++ /dev/null @@ -1,93 +0,0 @@ -// 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. - -// This script runs in HTML files and loads and instantiates dart unit tests -// that are compiled to WebAssembly. It is based off of the `test/dart.js` -// script from the `test` dart package. - -window.onload = async function () { - // Sends an error message to the server indicating that the script failed to - // load. - // - // This mimics a MultiChannel-formatted message. - var sendLoadException = function (message) { - window.parent.postMessage({ - "href": window.location.href, - "data": [0, { "type": "loadException", "message": message }], - "exception": true, - }, window.location.origin); - } - - // Listen for dartLoadException events and forward to the server. - window.addEventListener('dartLoadException', function (e) { - sendLoadException(e.detail); - }); - - // The basename of the current page. - var name = window.location.href.replace(/.*\//, '').replace(/#.*/, ''); - - // Find . - var links = document.getElementsByTagName("link"); - var testLinks = []; - var length = links.length; - for (var i = 0; i < length; ++i) { - if (links[i].rel == "x-dart-test") testLinks.push(links[i]); - } - - if (testLinks.length != 1) { - sendLoadException( - 'Expected exactly 1 in ' + name + ', found ' + - testLinks.length + '.'); - return; - } - - var link = testLinks[0]; - - if (link.href == '') { - sendLoadException( - 'Expected in ' + name + ' to have an "href" ' + - 'attribute.'); - return; - } - - let dart2wasm_runtime; - let moduleInstance; - try { - const isSkwasm = link.hasAttribute('skwasm'); - const imports = isSkwasm ? new Promise((resolve) => { - const skwasmScript = document.createElement('script'); - skwasmScript.src = '/canvaskit/skwasm.js'; - - document.body.appendChild(skwasmScript); - skwasmScript.addEventListener('load', async () => { - const skwasmInstance = await skwasm(); - window._flutter_skwasmInstance = skwasmInstance; - resolve({ - "skwasm": skwasmInstance.wasmExports, - "skwasmWrapper": skwasmInstance, - "ffi": { - "memory": skwasmInstance.wasmMemory, - } - }); - }); - }) : {}; - - let baseName = link.href + '.browser_test.dart'; - dart2wasm_runtime = await import(baseName + '.mjs'); - const dartModulePromise = WebAssembly.compileStreaming(fetch(baseName + '.wasm')); - moduleInstance = await dart2wasm_runtime.instantiate(dartModulePromise, imports); - } catch (exception) { - const message = `Failed to fetch and instantiate wasm module: ${exception}`; - sendLoadException(message); - } - - if (moduleInstance) { - try { - await dart2wasm_runtime.invoke(moduleInstance); - } catch (exception) { - const message = `Exception while invoking test: ${exception}`; - sendLoadException(message); - } - } -}; diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 90d3172560ca6..b91c35408393d 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -155,8 +155,9 @@ class BrowserPlatform extends PlatformPlugin { /// The URL for this server. Uri get url => server.url.resolve('/'); - bool get isWasm => suite.testBundle.compileConfig.compiler == Compiler.dart2wasm; - bool get needsCrossOriginIsolated => isWasm && suite.testBundle.compileConfig.renderer == Renderer.skwasm; + bool get needsCrossOriginIsolated => suite.testBundle.compileConfigs.any( + (CompileConfiguration config) => config.renderer == Renderer.skwasm + ); /// A [OneOffHandler] for servicing WebSocket connections for /// [BrowserManager]s. @@ -526,47 +527,58 @@ class BrowserPlatform extends PlatformPlugin { } } + String _makeBuildConfigString(String scriptBase, CompileConfiguration config) { + return config.compiler == Compiler.dart2wasm ? ''' + { + compileTarget: "${config.compiler.name}", + renderer: "${config.renderer.name}", + mainWasmPath: "$scriptBase.browser_test.dart.wasm", + jsSupportRuntimePath: "$scriptBase.browser_test.dart.mjs", + } +''' : ''' + { + compileTarget: "${config.compiler.name}", + renderer: "${config.renderer.name}", + mainJsPath: "$scriptBase.browser_test.dart.js", + } +'''; + } + /// Serves the HTML file that bootstraps the test. shelf.Response _testBootstrapHandler(shelf.Request request) { final String path = p.fromUri(request.url); if (path.endsWith('.html')) { final String test = '${p.withoutExtension(path)}.dart'; - - final bool linkSkwasm = suite.testBundle.compileConfig.renderer == Renderer.skwasm; - // Link to the Dart wrapper. final String scriptBase = htmlEscape.convert(p.basename(test)); - final String link = ''; - final String bootstrapScript = isWasm ? ''' + final String buildConfigsString = suite.testBundle.compileConfigs.map( + (CompileConfiguration config) => _makeBuildConfigString(scriptBase, config) + ).join(',\n'); + final String bootstrapScript = ''' + - - ''' : ''' - - '''; + return shelf.Response.ok(''' - $link $bootstrapScript @@ -643,13 +655,16 @@ class BrowserPlatform extends PlatformPlugin { 'debug': isDebug.toString() }); + final bool hasSourceMaps = suite.testBundle.compileConfigs.any( + (CompileConfiguration config) => config.compiler == Compiler.dart2js + ); final Future future = BrowserManager.start( browserEnvironment: browserEnvironment, url: hostUrl, future: completer.future, packageConfig: packageConfig, debug: isDebug, - sourceMapDirectory: isWasm ? null : getBundleBuildDirectory(suite.testBundle), + sourceMapDirectory: hasSourceMaps ? getBundleBuildDirectory(suite.testBundle) : null, ); // Store null values for browsers that error out so we know not to load them diff --git a/lib/web_ui/flutter_js/BUILD.gn b/lib/web_ui/flutter_js/BUILD.gn index c49893fd87891..255cd2c513fe3 100644 --- a/lib/web_ui/flutter_js/BUILD.gn +++ b/lib/web_ui/flutter_js/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//flutter/build/esbuild/esbuild.gni") +import("//flutter/shell/version/version.gni") import("sources.gni") group("flutter_js") { diff --git a/lib/web_ui/flutter_js/sources.gni b/lib/web_ui/flutter_js/sources.gni index ee00c55efeb7b..11da02b200455 100644 --- a/lib/web_ui/flutter_js/sources.gni +++ b/lib/web_ui/flutter_js/sources.gni @@ -2,4 +2,17 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -flutter_js_source_list = [ "src/flutter.js" ] +flutter_js_source_list = [ + "src/base_uri.js", + "src/browser_environment.js", + "src/canvaskit_loader.js", + "src/entrypoint_loader.js", + "src/flutter.js", + "src/instantiate_wasm.js", + "src/loader.js", + "src/service_worker_loader.js", + "src/skwasm_loader.js", + "src/trusted_types.js", + + "src/types.d.ts", +] diff --git a/lib/web_ui/flutter_js/src/base_uri.js b/lib/web_ui/flutter_js/src/base_uri.js new file mode 100644 index 0000000000000..bfca8d341bde7 --- /dev/null +++ b/lib/web_ui/flutter_js/src/base_uri.js @@ -0,0 +1,17 @@ +// 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. + +export const baseUri = ensureTrailingSlash(getBaseURI()); + +function getBaseURI() { + const base = document.querySelector("base"); + return (base && base.getAttribute("href")) || ""; +} + +function ensureTrailingSlash(uri) { + if (uri === "") { + return uri; + } + return uri.endsWith("/") ? uri : `${uri}/`; +} diff --git a/lib/web_ui/flutter_js/src/browser_environment.js b/lib/web_ui/flutter_js/src/browser_environment.js new file mode 100644 index 0000000000000..64baabc024270 --- /dev/null +++ b/lib/web_ui/flutter_js/src/browser_environment.js @@ -0,0 +1,46 @@ +// 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. + +const isBlink = () => { + return (navigator.vendor === 'Google Inc.') || + (navigator.agent === 'Edg/'); +} + +const hasImageCodecs = () => { + if (typeof ImageDecoder === 'undefined') { + return false; + } + // TODO(yjbanov): https://github.com/flutter/flutter/issues/122761 + // Frequently, when a browser launches an API that other browsers already + // support, there are subtle incompatibilities that may cause apps to crash if, + // we blindly adopt the new implementation. This check prevents us from picking + // up potentially incompatible implementations of ImagdeDecoder API. Instead, + // when a new browser engine launches the API, we'll evaluate it and enable it + // explicitly. + return isBlink(); +} + +const hasChromiumBreakIterators = () => { + return (typeof Intl.v8BreakIterator !== "undefined") && + (typeof Intl.Segmenter !== "undefined"); +} + +const supportsWasmGC = () => { + // This attempts to instantiate a wasm module that only will validate if the + // final WasmGC spec is implemented in the browser. + // + // Copied from https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/gc/index.js + const bytes = [0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 95, 1, 120, 0]; + return WebAssembly.validate(new Uint8Array(bytes)); +} + +/** + * @returns {import("./types").BrowserEnvironment} + */ +export const browserEnvironment = { + hasImageCodecs: hasImageCodecs(), + hasChromiumBreakIterators: hasChromiumBreakIterators(), + supportsWasmGC: supportsWasmGC(), + crossOriginIsolated: window.crossOriginIsolated, +}; diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js new file mode 100644 index 0000000000000..cf88c3c7ea53f --- /dev/null +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -0,0 +1,47 @@ +// 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 { createWasmInstantiator } from "./instantiate_wasm.js"; + +export const loadCanvasKit = (deps, config, browserEnvironment, engineRevision) => { + if (window.flutterCanvasKit) { + // The user has set this global variable ahead of time, so we just return that. + return Promise.resolve(window.flutterCanvasKit); + } + window.flutterCanvasKitLoaded = new Promise((resolve, reject) => { + const supportsChromiumCanvasKit = browserEnvironment.hasChromiumBreakIterators && browserEnvironment.hasImageCodecs; + if (!supportsChromiumCanvasKit && config.canvasKitVariant == "chromium") { + throw "Chromium CanvasKit variant specifically requested, but unsupported in this browser"; + } + const useChromiumCanvasKit = supportsChromiumCanvasKit && (config.canvasKitVariant !== "full"); + let baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineRevision}/`; + if (useChromiumCanvasKit) { + baseUrl = `${baseUrl}/chromium/`; + } + let canvasKitUrl = `${baseUrl}canvaskit.js`; + if (deps.flutterTT.policy) { + canvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl); + } + const wasmInstantiator = createWasmInstantiator(`${baseUrl}canvaskit.wasm`); + const script = document.createElement("script"); + script.src = canvasKitUrl; + if (config.nonce) { + script.nonce = config.nonce; + } + script.addEventListener('load', async () => { + try { + const canvasKit = await CanvasKitInit({ + instantiateWasm: wasmInstantiator, + }); + window.flutterCanvasKit = canvasKit; + resolve(canvasKit); + } catch (e) { + reject(e); + } + }); + script.addEventListener('error', reject); + document.head.appendChild(script); + }); + return window.flutterCanvasKitLoaded; +} diff --git a/lib/web_ui/flutter_js/src/entrypoint_loader.js b/lib/web_ui/flutter_js/src/entrypoint_loader.js new file mode 100644 index 0000000000000..db8b33159dc48 --- /dev/null +++ b/lib/web_ui/flutter_js/src/entrypoint_loader.js @@ -0,0 +1,198 @@ +// 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 { baseUri } from "./base_uri.js"; + +/** + * Handles injecting the main Flutter web entrypoint (main.dart.js), and notifying + * the user when Flutter is ready, through `didCreateEngineInitializer`. + * + * @see https://docs.flutter.dev/development/platform-integration/web/initialization + */ +export class FlutterEntrypointLoader { + /** + * Creates a FlutterEntrypointLoader. + */ + constructor() { + // Watchdog to prevent injecting the main entrypoint multiple times. + this._scriptLoaded = false; + } + /** + * Injects a TrustedTypesPolicy (or undefined if the feature is not supported). + * @param {TrustedTypesPolicy | undefined} policy + */ + setTrustedTypesPolicy(policy) { + this._ttPolicy = policy; + } + /** + * @deprecated + * Loads flutter main entrypoint, specified by `entrypointUrl`, and calls a + * user-specified `onEntrypointLoaded` callback with an EngineInitializer + * object when it's done. + * + * @param {*} options + * @returns {Promise | undefined} that will eventually resolve with an + * EngineInitializer, or will be rejected with the error caused by the loader. + * Returns undefined when an `onEntrypointLoaded` callback is supplied in `options`. + */ + async loadEntrypoint(options) { + const { entrypointUrl = `${baseUri}main.dart.js`, onEntrypointLoaded, nonce } = + options || {}; + return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); + } + + /** + * Loads the entry point for a flutter application. + * @param {import("./types").ApplicationBuild} build + * Information about the specific build that is to be loaded + * @param {*} deps + * External dependencies that may be needed to load the app. + * @param {import("./types").FlutterConfiguration} config + * The application configuration. If no callback is specified, this will be + * passed along to engine when initializing it. + * @param {string} nonce + * A nonce to apply to the main application script tag, if necessary. + * @param {import("./types").OnEntrypointLoadedCallback?} onEntrypointLoaded + * An optional callback to invoke when the entrypoint is loaded. If no + * callback is supplied, the engine initializer and app runner will be + * automatically invoked on load, passing along the supplied flutter + * configuration. + */ + async load(build, deps, config, nonce, onEntrypointLoaded) { + onEntrypointLoaded ??= (engineInitializer) => { + engineInitializer.initializeEngine(config).then((appRunner) => appRunner.runApp()) + }; + if (build.compileTarget === "dart2wasm") { + return this._loadWasmEntrypoint(build, deps, onEntrypointLoaded); + } else { + const mainPath = build.mainJsPath ?? "main.dart.js"; + const entrypointUrl = `${baseUri}${mainPath}`; + return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); + } + } + + /** + * Resolves the promise created by loadEntrypoint, and calls the `onEntrypointLoaded` + * function supplied by the user (if needed). + * + * Called by Flutter through `_flutter.loader.didCreateEngineInitializer` method, + * which is bound to the correct instance of the FlutterEntrypointLoader by + * the FlutterLoader object. + * + * @param {Function} engineInitializer @see https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/js_interop/js_loader.dart#L42 + */ + didCreateEngineInitializer(engineInitializer) { + if (typeof this._didCreateEngineInitializerResolve === "function") { + this._didCreateEngineInitializerResolve(engineInitializer); + // Remove the resolver after the first time, so Flutter Web can hot restart. + this._didCreateEngineInitializerResolve = null; + // Make the engine revert to "auto" initialization on hot restart. + delete _flutter.loader.didCreateEngineInitializer; + } + if (typeof this._onEntrypointLoaded === "function") { + this._onEntrypointLoaded(engineInitializer); + } + } + /** + * Injects a script tag into the DOM, and configures this loader to be able to + * handle the "entrypoint loaded" notifications received from Flutter web. + * + * @param {string} entrypointUrl the URL of the script that will initialize + * Flutter. + * @param {Function} onEntrypointLoaded a callback that will be called when + * Flutter web notifies this object that the entrypoint is + * loaded. + * @returns {Promise | undefined} a Promise that resolves when the entrypoint + * is loaded, or undefined if `onEntrypointLoaded` + * is a function. + */ + _loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce) { + const useCallback = typeof onEntrypointLoaded === "function"; + if (!this._scriptLoaded) { + this._scriptLoaded = true; + const scriptTag = this._createScriptTag(entrypointUrl, nonce); + if (useCallback) { + // Just inject the script tag, and return nothing; Flutter will call + // `didCreateEngineInitializer` when it's done. + console.debug("Injecting