From d7c13885e87c65ac82e3160fa14c10eb61c639d6 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Tue, 14 Nov 2023 09:38:05 -0800 Subject: [PATCH 01/33] Modularize flutter.js source. --- lib/web_ui/flutter_js/sources.gni | 8 +- .../flutter_js/src/entrypoint_loader.js | 135 +++++++ lib/web_ui/flutter_js/src/flutter.js | 382 +----------------- lib/web_ui/flutter_js/src/loader.js | 45 +++ .../flutter_js/src/service_worker_loader.js | 150 +++++++ lib/web_ui/flutter_js/src/trusted_types.js | 35 ++ 6 files changed, 378 insertions(+), 377 deletions(-) create mode 100644 lib/web_ui/flutter_js/src/entrypoint_loader.js create mode 100644 lib/web_ui/flutter_js/src/loader.js create mode 100644 lib/web_ui/flutter_js/src/service_worker_loader.js create mode 100644 lib/web_ui/flutter_js/src/trusted_types.js diff --git a/lib/web_ui/flutter_js/sources.gni b/lib/web_ui/flutter_js/sources.gni index ee00c55efeb7b..02dab6e3e5e16 100644 --- a/lib/web_ui/flutter_js/sources.gni +++ b/lib/web_ui/flutter_js/sources.gni @@ -2,4 +2,10 @@ # 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/entrypoint_loader.js", + "src/flutter.js", + "src/loader.js", + "src/service_worker_loader.js", + "src/trusted_types.js", +] 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..b5a29918b4a31 --- /dev/null +++ b/lib/web_ui/flutter_js/src/entrypoint_loader.js @@ -0,0 +1,135 @@ +// 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 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}/`; +} + +/** + * 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; + } + /** + * 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._loadEntrypoint(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. + */ + _loadEntrypoint(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 - - ''' : ''' - - '''; + return shelf.Response.ok(''' - $link $bootstrapScript diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index d6c0030edb1b0..749872087456d 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -26,7 +26,7 @@ export const loadCanvasKit = (deps, options, browserEnvironment) => { script.addEventListener('error', (e) => { reject(e); }); - document.body.appendChild(script); + 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 index b5a29918b4a31..ec53bc1838a2d 100644 --- a/lib/web_ui/flutter_js/src/entrypoint_loader.js +++ b/lib/web_ui/flutter_js/src/entrypoint_loader.js @@ -50,8 +50,25 @@ export class FlutterEntrypointLoader { async loadEntrypoint(options) { const { entrypointUrl = `${baseUri}main.dart.js`, onEntrypointLoaded, nonce } = options || {}; - return this._loadEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); + return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); } + + async load(build, options) { + let { onEntrypointLoaded, nonce } = options; + onEntrypointLoaded ??= (engineInitializer) => { + engineInitializer.initializeEngine(options).then((appRunner) => appRunner.runApp()) + }; + console.log(`TARGET: ${build.compileTarget}`); + if (build.compileTarget == "dart2wasm") { + return this._loadWasmEntrypoint(build, onEntrypointLoaded); + } else { + const mainPath = build.mainJsPath ?? "main.dart.js"; + const entrypointUrl = `${baseUri}${mainPath}`; + console.log(`ENTRYPOINT URL: ${entrypointUrl}`); + return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); + } + } + /** * Resolves the promise created by loadEntrypoint, and calls the `onEntrypointLoaded` * function supplied by the user (if needed). @@ -87,7 +104,7 @@ export class FlutterEntrypointLoader { * is loaded, or undefined if `onEntrypointLoaded` * is a function. */ - _loadEntrypoint(entrypointUrl, onEntrypointLoaded, nonce) { + _loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce) { const useCallback = typeof onEntrypointLoaded === "function"; if (!this._scriptLoaded) { this._scriptLoaded = true; @@ -97,7 +114,7 @@ export class FlutterEntrypointLoader { // `didCreateEngineInitializer` when it's done. console.debug("Injecting '''; diff --git a/lib/web_ui/flutter_js/src/browser_environment.js b/lib/web_ui/flutter_js/src/browser_environment.js index 8c891d0945457..185c8e05731f2 100644 --- a/lib/web_ui/flutter_js/src/browser_environment.js +++ b/lib/web_ui/flutter_js/src/browser_environment.js @@ -21,7 +21,6 @@ const isBlink = () => { const hasImageCodecs = () => { if (typeof ImageDecoder === 'undefined') { - console.log("no image decoder"); return false; } // TODO(yjbanov): https://github.com/flutter/flutter/issues/122761 @@ -32,7 +31,6 @@ const hasImageCodecs = () => { // when a new browser engine launches the API, we'll evaluate it and enable it // explicitly. if (!isBlink()) { - console.log("not blink"); return false; } return true; diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index 749872087456d..c1d8ce2ce093b 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export const loadCanvasKit = (deps, options, browserEnvironment) => { +export const loadCanvasKit = (deps, config, browserEnvironment) => { window.flutterCanvasKitLoaded = new Promise((resolve, reject) => { const supportsChromiumCanvasKit = browserEnvironment.hasChromiumBreakIterators && browserEnvironment.hasImageCodecs; - if (!supportsChromiumCanvasKit && options.canvasKitVariant == "chromium") { + if (!supportsChromiumCanvasKit && config.canvasKitVariant == "chromium") { throw "Chromium CanvasKit variant specifically requested, but unsupported in this browser"; } - const useChromiumCanvasKit = supportsChromiumCanvasKit && (options.canvasKitVariant !== "full"); - const baseUrl = options.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; + const useChromiumCanvasKit = supportsChromiumCanvasKit && (config.canvasKitVariant !== "full"); + const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; const canvasKitUrl = useChromiumCanvasKit ? `${baseUrl}/canvaskit.js` : `${baseUrl}/chromium/canvaskit.js`; const trustedCanvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl); const script = document.createElement("script"); diff --git a/lib/web_ui/flutter_js/src/entrypoint_loader.js b/lib/web_ui/flutter_js/src/entrypoint_loader.js index ec53bc1838a2d..e8ab0dc7c17a7 100644 --- a/lib/web_ui/flutter_js/src/entrypoint_loader.js +++ b/lib/web_ui/flutter_js/src/entrypoint_loader.js @@ -53,19 +53,16 @@ export class FlutterEntrypointLoader { return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); } - async load(build, options) { - let { onEntrypointLoaded, nonce } = options; + async load(build, deps, config, onEntrypointLoaded) { onEntrypointLoaded ??= (engineInitializer) => { - engineInitializer.initializeEngine(options).then((appRunner) => appRunner.runApp()) + engineInitializer.initializeEngine(config).then((appRunner) => appRunner.runApp()) }; - console.log(`TARGET: ${build.compileTarget}`); if (build.compileTarget == "dart2wasm") { - return this._loadWasmEntrypoint(build, onEntrypointLoaded); + return this._loadWasmEntrypoint(build, deps, onEntrypointLoaded); } else { const mainPath = build.mainJsPath ?? "main.dart.js"; const entrypointUrl = `${baseUri}${mainPath}`; - console.log(`ENTRYPOINT URL: ${entrypointUrl}`); - return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce); + return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded); } } @@ -131,10 +128,8 @@ export class FlutterEntrypointLoader { } } - async _loadWasmEntrypoint(build, onEntrypointLoaded) { - console.log("load wasm entrypoint"); + async _loadWasmEntrypoint(build, deps, onEntrypointLoaded) { if (!this._scriptLoaded) { - console.log("script not yet loaded"); this._scriptLoaded = true; this._onEntrypointLoaded = onEntrypointLoaded; @@ -146,11 +141,25 @@ export class FlutterEntrypointLoader { } const dartModulePromise = WebAssembly.compileStreaming(fetch(moduleUri)); - console.log("about to load support runtime"); const jsSupportRuntime = await import(jsSupportRuntimeUri); - console.log("about to instantiate module"); - const moduleInstance = await jsSupportRuntime.instantiate(dartModulePromise, {}); - console.log("invoking module!"); + + let imports; + if (build.renderer == "skwasm") { + imports = (async () => { + const skwasmInstance = await deps.skwasm; + window._flutter_skwasmInstance = skwasmInstance; + return { + skwasm: skwasmInstance.wasmExports, + skwasmWrapper: skwasmInstance, + ffi: { + memory: skwasmInstance.wasmMemory, + }, + }; + })(); + } else { + imports = {}; + } + const moduleInstance = await jsSupportRuntime.instantiate(dartModulePromise, imports); await jsSupportRuntime.invoke(moduleInstance); } } diff --git a/lib/web_ui/flutter_js/src/loader.js b/lib/web_ui/flutter_js/src/loader.js index d36e27c47e3fa..eb54e023e304c 100644 --- a/lib/web_ui/flutter_js/src/loader.js +++ b/lib/web_ui/flutter_js/src/loader.js @@ -49,9 +49,9 @@ export class FlutterLoader { async load({ serviceWorkerSettings, onEntrypointLoaded, - options, + config, } = {}) { - options ??= {}; + config ??= {}; const browserEnvironment = detectBrowserEnvironment(); const buildConfig = _flutter.buildConfig; if (!buildConfig) { @@ -74,7 +74,7 @@ export class FlutterLoader { if (build.compileTarget == "dart2wasm" && !browserEnvironment.supportsWasmGC) { return false; } - if (options.renderer && options.renderer != build.renderer) { + if (config.renderer && config.renderer != build.renderer) { return false; } return rendererIsCompatible(build.renderer); @@ -93,9 +93,9 @@ export class FlutterLoader { console.warn("Exception while loading service worker:", e); }); if (build.renderer == "canvaskit") { - deps.canvasKit = loadCanvasKit(deps, options, browserEnvironment); + deps.canvasKit = loadCanvasKit(deps, config, browserEnvironment); } else if (build.renderer == "skwasm") { - deps.skwasm = loadSkwasm(); + deps.skwasm = loadSkwasm(deps, config, browserEnvironment); } // The FlutterEntrypointLoader instance could be injected as a dependency @@ -105,6 +105,6 @@ export class FlutterLoader { // Install the `didCreateEngineInitializer` listener where Flutter web expects it to be. this.didCreateEngineInitializer = entrypointLoader.didCreateEngineInitializer.bind(entrypointLoader); - return entrypointLoader.load(build, onEntrypointLoaded); + return entrypointLoader.load(build, deps, config, onEntrypointLoaded); } } diff --git a/lib/web_ui/flutter_js/src/skwasm_loader.js b/lib/web_ui/flutter_js/src/skwasm_loader.js index afa771509782d..b826d06ab44e1 100644 --- a/lib/web_ui/flutter_js/src/skwasm_loader.js +++ b/lib/web_ui/flutter_js/src/skwasm_loader.js @@ -2,23 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export const loadSkwasm = (deps, options, browserEnvironment) => { - return new Promise((resolve) => { - const supportsChromiumCanvasKit = browserEnvironment.hasChromiumBreakIterators && browserEnvironment.hasImageCodecs; - if (!supportsChromiumCanvasKit && options.canvasKitVariant == "chromium") { - throw "Chromium CanvasKit variant specifically requested, but unsupported in this browser"; - } - const useChromiumCanvasKit = supportsChromiumCanvasKit && (options.canvasKitVariant !== "full"); - const baseUrl = options.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; - const canvasKitUrl = useChromiumCanvasKit ? `${baseUrl}/skwasm.js` : `${baseUrl}/chromium/skwasm.js`; - const trustedCanvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl); +export const loadSkwasm = (deps, config, browserEnvironment) => { + return new Promise((resolve, reject) => { + const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; + const skwasmUrl = `${baseUrl}/skwasm.js`; + const trustedSkwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl); const script = document.createElement("script"); - script.src = trustedCanvasKitUrl; + script.src = trustedSkwasmUrl; script.addEventListener('load', async () => { try { - const canvasKit = await CanvasKitInit(); - window.flutterCanvasKit = canvasKit; - resolve(canvasKit); + const skwasmInstance = await skwasm(); + resolve(skwasmInstance); } catch (e) { reject(e); } @@ -26,8 +20,7 @@ export const loadSkwasm = (deps, options, browserEnvironment) => { script.addEventListener('error', (e) => { reject(e); }); - document.body.appendChild(script); + document.head.appendChild(script); }); - return window.flutterCanvasKitLoaded; } \ No newline at end of file diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 15a658b9646ea..0830496ae4d02 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -64,14 +64,11 @@ class CanvasKitRenderer implements Renderer { Future initialize() async { _initialized ??= () async { if (windowFlutterCanvasKit != null) { - print('ALREADY LOADED'); canvasKit = windowFlutterCanvasKit!; } else if (windowFlutterCanvasKitLoaded != null) { - print('PRELOAD'); // CanvasKit is being preloaded by flutter.js. Wait for it to complete. canvasKit = await promiseToFuture(windowFlutterCanvasKitLoaded!); } else { - print('LOAD OURSELVES'); canvasKit = await downloadCanvasKit(); windowFlutterCanvasKit = canvasKit; } diff --git a/lib/web_ui/test/canvaskit/hot_restart_test.dart b/lib/web_ui/test/canvaskit/hot_restart_test.dart index b032f532e31b7..3ee6903857c44 100644 --- a/lib/web_ui/test/canvaskit/hot_restart_test.dart +++ b/lib/web_ui/test/canvaskit/hot_restart_test.dart @@ -12,8 +12,6 @@ void main() { void testMain() { test('CanvasKit reuses the instance already set on `window`', () async { - expect(windowFlutterCanvasKit, isNull); - // First initialization should make CanvasKit available through `window`. await renderer.initialize(); expect(windowFlutterCanvasKit, isNotNull); diff --git a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart index 0cfd55a178a11..709b88aff9a92 100644 --- a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart +++ b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart @@ -16,7 +16,6 @@ void testMain() { test('services are initalized separately from UI', () async { final JsFlutterConfiguration? config = await bootstrapAndExtractConfig(); expect(scheduleFrameCallback, isNull); - expect(windowFlutterCanvasKit, isNull); expect(findGlassPane(), isNull); expect(RawKeyboard.instance, isNull); From 4267ee2aa3ff5a9b0c5f427c74b3cf63dccb0f09 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 09:13:50 -0800 Subject: [PATCH 07/33] Remove hack. --- lib/web_ui/dev/test_platform.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 1b437b7374efb..209546d0f0876 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -156,7 +156,7 @@ class BrowserPlatform extends PlatformPlugin { Uri get url => server.url.resolve('/'); bool get isWasm => suite.testBundle.compileConfig.compiler == Compiler.dart2wasm; - bool get needsCrossOriginIsolated => true; //isWasm && suite.testBundle.compileConfig.renderer == Renderer.skwasm; + bool get needsCrossOriginIsolated => isWasm && suite.testBundle.compileConfig.renderer == Renderer.skwasm; /// A [OneOffHandler] for servicing WebSocket connections for /// [BrowserManager]s. From 0103f5ef1ea4c24b0a4ffaf0dae4bcdf3170ed94 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 09:44:27 -0800 Subject: [PATCH 08/33] Fix browsers without Trusted Types. --- lib/web_ui/flutter_js/src/canvaskit_loader.js | 8 +++++--- lib/web_ui/flutter_js/src/skwasm_loader.js | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index c1d8ce2ce093b..012254c089893 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -10,10 +10,12 @@ export const loadCanvasKit = (deps, config, browserEnvironment) => { } const useChromiumCanvasKit = supportsChromiumCanvasKit && (config.canvasKitVariant !== "full"); const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; - const canvasKitUrl = useChromiumCanvasKit ? `${baseUrl}/canvaskit.js` : `${baseUrl}/chromium/canvaskit.js`; - const trustedCanvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl); + let canvasKitUrl = useChromiumCanvasKit ? `${baseUrl}/chromium/canvaskit.js` : `${baseUrl}/canvaskit.js`; + if (deps.flutterTT.policy) { + canvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl); + } const script = document.createElement("script"); - script.src = trustedCanvasKitUrl; + script.src = canvasKitUrl; script.addEventListener('load', async () => { try { const canvasKit = await CanvasKitInit(); diff --git a/lib/web_ui/flutter_js/src/skwasm_loader.js b/lib/web_ui/flutter_js/src/skwasm_loader.js index b826d06ab44e1..af20d9374b830 100644 --- a/lib/web_ui/flutter_js/src/skwasm_loader.js +++ b/lib/web_ui/flutter_js/src/skwasm_loader.js @@ -5,10 +5,12 @@ export const loadSkwasm = (deps, config, browserEnvironment) => { return new Promise((resolve, reject) => { const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; - const skwasmUrl = `${baseUrl}/skwasm.js`; - const trustedSkwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl); + let skwasmUrl = `${baseUrl}/skwasm.js`; + if (deps.flutterTT.policy) { + skwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl); + } const script = document.createElement("script"); - script.src = trustedSkwasmUrl; + script.src = skwasmUrl; script.addEventListener('load', async () => { try { const skwasmInstance = await skwasm(); From 9abd0443dab9d796adb93304cbd8bacab32cf100 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 10:08:48 -0800 Subject: [PATCH 09/33] Update licenses golden. --- ci/licenses_golden/licenses_flutter | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 611f58c269881..417377f94196a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5787,7 +5787,14 @@ 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/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/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 @@ -8600,7 +8607,15 @@ 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/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/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 From 1898e0e9e5aec1c8616b14d2a621aa171d03e179 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 10:15:20 -0800 Subject: [PATCH 10/33] Remove old test harnesses. --- lib/web_ui/dev/test_dart2js.js | 76 -------------------------- lib/web_ui/dev/test_dart2wasm.js | 93 -------------------------------- 2 files changed, 169 deletions(-) delete mode 100644 lib/web_ui/dev/test_dart2js.js delete mode 100644 lib/web_ui/dev/test_dart2wasm.js diff --git a/lib/web_ui/dev/test_dart2js.js b/lib/web_ui/dev/test_dart2js.js deleted file mode 100644 index 6f24a42a18cba..0000000000000 --- a/lib/web_ui/dev/test_dart2js.js +++ /dev/null @@ -1,76 +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; - } - - _flutter.buildConfig = { - builds: [{ - compileTarget: "dart2js", - renderer: "canvaskit", - url: link.href + '.browser_test.dart.js', - }] - }; - - try { - window._flutter.loader.load({ - options: configuration, - 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); - } - } -}; From 406cd6b3c0a9fccc968989c66bda594c4f04d305 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 11:00:53 -0800 Subject: [PATCH 11/33] Inject engine revision into flutter.js. --- lib/web_ui/flutter_js/BUILD.gn | 18 +++++++++++++++++- lib/web_ui/flutter_js/src/canvaskit_loader.js | 4 +++- lib/web_ui/flutter_js/src/skwasm_loader.js | 4 +++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/web_ui/flutter_js/BUILD.gn b/lib/web_ui/flutter_js/BUILD.gn index c49893fd87891..4b84d594643bf 100644 --- a/lib/web_ui/flutter_js/BUILD.gn +++ b/lib/web_ui/flutter_js/BUILD.gn @@ -3,11 +3,13 @@ # found in the LICENSE file. import("//flutter/build/esbuild/esbuild.gni") +import("//flutter/shell/version/version.gni") import("sources.gni") group("flutter_js") { public_deps = [ ":flutter_js_bundle", + ":flutter_js_engine_version", ":flutter_js_sources", ] } @@ -18,6 +20,17 @@ copy("flutter_js_sources") { [ "$root_out_dir/flutter_web_sdk/flutter_js/{{source_target_relative}}" ] } +generated_file("flutter_js_engine_version") { + outputs = [ "$root_out_dir/flutter_web_sdk/flutter_js/src/revision.js" ] + contents = [ + "// 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 engineRevision = \"$engine_version\";", + ] +} + esbuild("flutter_js_bundle") { entry_point = "$root_out_dir/flutter_web_sdk/flutter_js/src/flutter.js" output_bundle = "$root_out_dir/flutter_web_sdk/flutter_js" @@ -26,5 +39,8 @@ esbuild("flutter_js_bundle") { minify = true sourcemap = true - public_deps = [ ":flutter_js_sources" ] + public_deps = [ + ":flutter_js_engine_version", + ":flutter_js_sources", + ] } diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index 012254c089893..847f3593e8a73 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import { engineRevision } from "./revision.js"; + export const loadCanvasKit = (deps, config, browserEnvironment) => { window.flutterCanvasKitLoaded = new Promise((resolve, reject) => { const supportsChromiumCanvasKit = browserEnvironment.hasChromiumBreakIterators && browserEnvironment.hasImageCodecs; @@ -9,7 +11,7 @@ export const loadCanvasKit = (deps, config, browserEnvironment) => { throw "Chromium CanvasKit variant specifically requested, but unsupported in this browser"; } const useChromiumCanvasKit = supportsChromiumCanvasKit && (config.canvasKitVariant !== "full"); - const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; + const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineRevision}`; let canvasKitUrl = useChromiumCanvasKit ? `${baseUrl}/chromium/canvaskit.js` : `${baseUrl}/canvaskit.js`; if (deps.flutterTT.policy) { canvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl); diff --git a/lib/web_ui/flutter_js/src/skwasm_loader.js b/lib/web_ui/flutter_js/src/skwasm_loader.js index af20d9374b830..2bfb8e3f0803b 100644 --- a/lib/web_ui/flutter_js/src/skwasm_loader.js +++ b/lib/web_ui/flutter_js/src/skwasm_loader.js @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import { engineRevision } from "./revision.js"; + export const loadSkwasm = (deps, config, browserEnvironment) => { return new Promise((resolve, reject) => { - const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineVersion}`; + const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineRevision}`; let skwasmUrl = `${baseUrl}/skwasm.js`; if (deps.flutterTT.policy) { skwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl); From 7142f19f758e4fac0d0386b07d1c0580542acff5 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 15:13:02 -0800 Subject: [PATCH 12/33] Don't copy nonexistent bootstrap scripts anymore. --- lib/web_ui/dev/steps/copy_artifacts_step.dart | 18 ------------------ 1 file changed, 18 deletions(-) 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', From 770a89daa11e16c9126124b75c8a2276880036e0 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 14 Dec 2023 17:17:18 -0800 Subject: [PATCH 13/33] Remove skwasm_stub tests. No longer pertinent. --- lib/web_ui/test/felt_config.yaml | 13 ---------- lib/web_ui/test/skwasm_stub/smoke_test.dart | 28 --------------------- 2 files changed, 41 deletions(-) delete mode 100644 lib/web_ui/test/skwasm_stub/smoke_test.dart diff --git a/lib/web_ui/test/felt_config.yaml b/lib/web_ui/test/felt_config.yaml index 6cc145be6a3cc..1bf13914581e0 100644 --- a/lib/web_ui/test/felt_config.yaml +++ b/lib/web_ui/test/felt_config.yaml @@ -42,11 +42,6 @@ test-sets: - name: ui directory: ui - # This just has a single test that makes sure the skwasm stub renderer is - # included when compiling to JS - - name: skwasm_stub - directory: skwasm_stub - test-bundles: - name: dart2js-html-engine test-set: engine @@ -68,10 +63,6 @@ test-bundles: test-set: ui compile-config: dart2js-canvaskit - - name: dart2js-skwasm-skwasm_stub - test-set: skwasm_stub - compile-config: dart2js-skwasm - - name: dart2wasm-html-engine test-set: engine compile-config: dart2wasm-html @@ -144,10 +135,6 @@ test-suites: run-config: chrome artifact-deps: [ canvaskit_chromium ] - - name: chrome-dart2js-skwasm-skwasm_stub - test-bundle: dart2js-skwasm-skwasm_stub - run-config: chrome - - name: chrome-full-dart2js-canvaskit-canvaskit test-bundle: dart2js-canvaskit-canvaskit run-config: chrome-full diff --git a/lib/web_ui/test/skwasm_stub/smoke_test.dart b/lib/web_ui/test/skwasm_stub/smoke_test.dart deleted file mode 100644 index 7361d9c283c65..0000000000000 --- a/lib/web_ui/test/skwasm_stub/smoke_test.dart +++ /dev/null @@ -1,28 +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. - -@TestOn('chrome || safari || firefox') -library; - -import 'dart:async'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine/renderer.dart'; -import 'package:ui/src/engine/skwasm/skwasm_stub/renderer.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - group('Skwasm stub tests', () { - test('Skwasm stub renderer throws', () { - expect(renderer, isA()); - expect(() { - renderer.initialize(); - }, throwsUnimplementedError); - }); - }); -} From 111fdd915bb2bc6ed0eb288f0f67d97580287163 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 15 Dec 2023 08:41:26 -0800 Subject: [PATCH 14/33] Adjust builder json. --- ci/builders/linux_web_engine.json | 94 ------------------------------- 1 file changed, 94 deletions(-) diff --git a/ci/builders/linux_web_engine.json b/ci/builders/linux_web_engine.json index 9dc023b4458b4..b91e62411d45d 100644 --- a/ci/builders/linux_web_engine.json +++ b/ci/builders/linux_web_engine.json @@ -178,28 +178,6 @@ ] } }, - { - "name": "web_tests/test_bundles/dart2js-skwasm-skwasm_stub", - "drone_dimensions": [ - "device_type=none", - "os=Linux" - ], - "generators": { - "tasks": [ - { - "name": "compile bundle dart2js-skwasm-skwasm_stub", - "parameters": [ - "test", - "--compile", - "--bundle=dart2js-skwasm-skwasm_stub" - ], - "scripts": [ - "flutter/lib/web_ui/dev/felt" - ] - } - ] - } - }, { "name": "web_tests/test_bundles/dart2wasm-html-engine", "drone_dimensions": [ @@ -514,42 +492,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", @@ -1435,42 +1377,6 @@ } ] }, - { - "name": "Windows run chrome-dart2js-skwasm-skwasm_stub 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/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": "Windows run chrome-full-dart2js-canvaskit-canvaskit suite", "recipe": "engine_v2/tester_engine", From c249d05069446bca5ceaa892c0e3a70ac6367be2 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 15 Dec 2023 10:19:16 -0800 Subject: [PATCH 15/33] Allow for multiple compile configs. --- lib/web_ui/dev/chrome.dart | 13 +--- lib/web_ui/dev/common.dart | 3 +- lib/web_ui/dev/felt_config.dart | 23 ++++--- lib/web_ui/dev/generate_builder_json.dart | 6 +- lib/web_ui/dev/steps/compile_bundle_step.dart | 61 +++++++++++-------- lib/web_ui/dev/steps/run_suite_step.dart | 11 ++-- lib/web_ui/dev/suite_filter.dart | 22 +++++-- lib/web_ui/dev/test_platform.dart | 15 +++-- lib/web_ui/test/felt_config.yaml | 22 +++---- 9 files changed, 93 insertions(+), 83 deletions(-) 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/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_platform.dart b/lib/web_ui/dev/test_platform.dart index 209546d0f0876..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. @@ -551,12 +552,15 @@ class BrowserPlatform extends PlatformPlugin { final String test = '${p.withoutExtension(path)}.dart'; final String scriptBase = htmlEscape.convert(p.basename(test)); + final String buildConfigsString = suite.testBundle.compileConfigs.map( + (CompileConfiguration config) => _makeBuildConfigString(scriptBase, config) + ).join(',\n'); final String bootstrapScript = '''