From 2adf22179862a8be8d0eeacc34250d0cddc3e964 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Wed, 10 Apr 2024 10:36:38 -0700 Subject: [PATCH 1/8] Output .js files as ES6 modules. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 930ba359d3321..94685d4d1ed2d 100644 --- a/DEPS +++ b/DEPS @@ -288,7 +288,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'dcd71b5b237e1e58f2ad8a7e51bead0c2a3755a9', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '0e548968c7e1f8e162912e65d7108aa5ed30eb76', 'src/flutter/third_party/depot_tools': Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '580b4ff3f5cd0dcaa2eacda28cefe0f45320e8f7', From 8d2232f6cf6c40deee97e14dd1ea666e76d17401 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Wed, 10 Apr 2024 14:35:35 -0700 Subject: [PATCH 2/8] Change loading logic to use dynamic imports instead. --- lib/web_ui/flutter_js/src/canvaskit_loader.js | 35 ++++------- lib/web_ui/flutter_js/src/skwasm_loader.js | 61 +++++++------------ .../src/engine/canvaskit/canvaskit_api.dart | 56 ++++++----------- 3 files changed, 51 insertions(+), 101 deletions(-) diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index 39a696a6a8686..f467285bc231c 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -4,12 +4,12 @@ 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) => { +export const loadCanvasKit = async (deps, config, browserEnvironment, engineRevision) => { + window.flutterCanvasKitLoaded = (async () => { + if (window.flutterCanvasKit) { + // The user has set this global variable ahead of time, so we just return that. + return window.flutterCanvasKit; + } const supportsChromiumCanvasKit = browserEnvironment.hasChromiumBreakIterators && browserEnvironment.hasImageCodecs; if (!supportsChromiumCanvasKit && config.canvasKitVariant == "chromium") { throw "Chromium CanvasKit variant specifically requested, but unsupported in this browser"; @@ -24,24 +24,11 @@ export const loadCanvasKit = (deps, config, browserEnvironment, engineRevision) 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); - } + const canvasKitModule = await import(canvasKitUrl); + window.flutterCanvasKit = await canvasKitModule.default({ + instantiateWasm: wasmInstantiator, }); - script.addEventListener('error', reject); - document.head.appendChild(script); - }); + return window.flutterCanvasKit; + })(); return window.flutterCanvasKitLoaded; } diff --git a/lib/web_ui/flutter_js/src/skwasm_loader.js b/lib/web_ui/flutter_js/src/skwasm_loader.js index 86e1b529784d3..d923d44690857 100644 --- a/lib/web_ui/flutter_js/src/skwasm_loader.js +++ b/lib/web_ui/flutter_js/src/skwasm_loader.js @@ -4,44 +4,29 @@ import { createWasmInstantiator } from "./instantiate_wasm.js"; -export const loadSkwasm = (deps, config, browserEnvironment, engineRevision) => { - return new Promise((resolve, reject) => { - 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); - } - const wasmInstantiator = createWasmInstantiator(`${baseUrl}skwasm.wasm`); - const script = document.createElement("script"); - script.src = skwasmUrl; - if (config.nonce) { - script.nonce = config.nonce; - } - script.addEventListener('load', async () => { - try { - const skwasmInstance = await skwasm({ - instantiateWasm: wasmInstantiator, - locateFile: (fileName, scriptDirectory) => { - // When hosted via a CDN or some other url that is not the same - // origin as the main script of the page, we will fail to create - // a web worker with the .worker.js script. This workaround will - // make sure that the worker JS can be loaded regardless of where - // it is hosted. - const url = scriptDirectory + fileName; - if (url.endsWith('.worker.js')) { - return URL.createObjectURL(new Blob( - [`importScripts('${url}');`], - { 'type': 'application/javascript' })); - } - return url; - } - }); - resolve(skwasmInstance); - } catch (e) { - reject(e); +export const loadSkwasm = async (deps, config, browserEnvironment, engineRevision) => { + 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); + } + const wasmInstantiator = createWasmInstantiator(`${baseUrl}skwasm.wasm`); + const skwasm = await import(skwasmUrl); + return await skwasm.default({ + instantiateWasm: wasmInstantiator, + locateFile: (fileName, scriptDirectory) => { + // When hosted via a CDN or some other url that is not the same + // origin as the main script of the page, we will fail to create + // a web worker with the .worker.js script. This workaround will + // make sure that the worker JS can be loaded regardless of where + // it is hosted. + const url = scriptDirectory + fileName; + if (url.endsWith('.worker.js')) { + return URL.createObjectURL(new Blob( + [`importScripts('${url}');`], + { 'type': 'application/javascript' })); } - }); - script.addEventListener('error', reject); - document.head.appendChild(script); + return url; + } }); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index 39608d26cc652..6190fa77f22d6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -259,12 +259,13 @@ extension CanvasKitExtension on CanvasKit { ); } -@JS('window.CanvasKitInit') -external JSAny _CanvasKitInit(CanvasKitInitOptions options); +@JS() +@staticInterop +class CanvasKitModule {} -Future CanvasKitInit(CanvasKitInitOptions options) { - return js_util.promiseToFuture( - _CanvasKitInit(options).toObjectShallow); +extension CanvasKitModuleExtension on CanvasKitModule { + @JS('default') + external JSPromise defaultExport(CanvasKitInitOptions options); } typedef LocateFileCallback = String Function(String file, String unusedBase); @@ -3649,11 +3650,11 @@ String canvasKitWasmModuleUrl(String file, String canvasKitBase) => /// Downloads the CanvasKit JavaScript, then calls `CanvasKitInit` to download /// and intialize the CanvasKit wasm. Future downloadCanvasKit() async { - await _downloadOneOf(_canvasKitJsUrls); + final CanvasKitModule canvasKitModule = await _downloadOneOf(_canvasKitJsUrls); - final CanvasKit canvasKit = await CanvasKitInit(CanvasKitInitOptions( + final CanvasKit canvasKit = (await canvasKitModule.defaultExport(CanvasKitInitOptions( locateFile: createLocateFileCallback(canvasKitWasmModuleUrl), - )); + )).toDart) as CanvasKit; if (canvasKit.ParagraphBuilder.RequiresClientICU() && !browserSupportsCanvaskitChromium) { throw Exception( @@ -3669,10 +3670,12 @@ Future downloadCanvasKit() async { /// downloads it. /// /// If none of the URLs can be downloaded, throws an [Exception]. -Future _downloadOneOf(Iterable urls) async { +Future _downloadOneOf(Iterable urls) async { for (final String url in urls) { - if (await _downloadCanvasKitJs(url)) { - return; + try { + return await _downloadCanvasKitJs(url); + } catch (_) { + continue; } } @@ -3686,32 +3689,7 @@ Future _downloadOneOf(Iterable urls) async { /// /// Returns a [Future] that completes with `true` if the CanvasKit JavaScript /// file was successfully downloaded, or `false` if it failed. -Future _downloadCanvasKitJs(String url) { - final DomHTMLScriptElement canvasKitScript = - createDomHTMLScriptElement(configuration.nonce); - canvasKitScript.src = createTrustedScriptUrl(url); - - final Completer canvasKitLoadCompleter = Completer(); - - late final DomEventListener loadCallback; - late final DomEventListener errorCallback; - - void loadEventHandler(DomEvent _) { - canvasKitScript.remove(); - canvasKitLoadCompleter.complete(true); - } - void errorEventHandler(DomEvent errorEvent) { - canvasKitScript.remove(); - canvasKitLoadCompleter.complete(false); - } - - loadCallback = createDomEventListener(loadEventHandler); - errorCallback = createDomEventListener(errorEventHandler); - - canvasKitScript.addEventListener('load', loadCallback); - canvasKitScript.addEventListener('error', errorCallback); - - domDocument.head!.appendChild(canvasKitScript); - - return canvasKitLoadCompleter.future; +Future _downloadCanvasKitJs(String url) async { + // final Object scriptUrl = createTrustedScriptUrl(url); + return (await importModule(url).toDart) as CanvasKitModule; } From 8e2144540fbc6b57e161df75e0a64d946376baf6 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Wed, 10 Apr 2024 15:10:25 -0700 Subject: [PATCH 3/8] `CanvasKitInit` isn't in the global scope anymore. --- .../initialization/does_not_mock_module_exports_test.dart | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart b/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart index b6676f2af4fcf..96e0c8a6d14d5 100644 --- a/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart +++ b/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart @@ -18,13 +18,6 @@ void testMain() { // Initialize CanvasKit... await bootstrapAndRunApp(); - // CanvasKitInit should be defined... - expect( - js_util.hasProperty(domWindow, 'CanvasKitInit'), - isTrue, - reason: 'CanvasKitInit should be defined on Window', - ); - // window.exports and window.module should be undefined! expect( js_util.hasProperty(domWindow, 'exports'), From fb88f55202192ef0437623c69fd22b2f228fd403 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 27 Jun 2024 10:01:49 -0700 Subject: [PATCH 4/8] Fix buildroot hash. --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 4a56fbca927ef..2df075354d9ab 100644 --- a/DEPS +++ b/DEPS @@ -277,7 +277,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'ef1bb419537f7bb16d4b7df0ed7a5e6f57a8d1fes', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'ef1bb419537f7bb16d4b7df0ed7a5e6f57a8d1fe', 'src/flutter/third_party/depot_tools': Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '580b4ff3f5cd0dcaa2eacda28cefe0f45320e8f7', From b6e339d393f4f8b6d7a57b4371d7ce6864965617 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 27 Jun 2024 10:41:53 -0700 Subject: [PATCH 5/8] Use trusted script URLs when dynamically importing. --- lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index ac42694d5a76f..f2c7c594d049f 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -3702,6 +3702,6 @@ Future _downloadOneOf(Iterable urls) async { /// Returns a [Future] that completes with `true` if the CanvasKit JavaScript /// file was successfully downloaded, or `false` if it failed. Future _downloadCanvasKitJs(String url) async { - // final Object scriptUrl = createTrustedScriptUrl(url); - return (await importModule(url).toDart) as CanvasKitModule; + final Object scriptUrl = createTrustedScriptUrl(url); + return (await importModule(scriptUrl).toDart) as CanvasKitModule; } From 911e7388ffc6b13bd70da70adef8770c806bb21e Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 27 Jun 2024 11:01:10 -0700 Subject: [PATCH 6/8] Fix createTrustedScriptUrl. --- lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart | 2 +- lib/web_ui/lib/src/engine/dom.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index f2c7c594d049f..2e6e8dfc62a6e 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -3702,6 +3702,6 @@ Future _downloadOneOf(Iterable urls) async { /// Returns a [Future] that completes with `true` if the CanvasKit JavaScript /// file was successfully downloaded, or `false` if it failed. Future _downloadCanvasKitJs(String url) async { - final Object scriptUrl = createTrustedScriptUrl(url); + final JSAny scriptUrl = createTrustedScriptUrl(url); return (await importModule(scriptUrl).toDart) as CanvasKitModule; } diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index 622cf3f2a2dd1..6cf0141a69e34 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -3378,16 +3378,16 @@ final DomTrustedTypePolicy _ttPolicy = domWindow.trustedTypes!.createPolicy( /// Converts a String `url` into a [DomTrustedScriptURL] object when the /// Trusted Types API is available, else returns the unmodified `url`. -Object createTrustedScriptUrl(String url) { +JSAny createTrustedScriptUrl(String url) { if (domWindow.trustedTypes != null) { // Pass `url` through Flutter Engine's TrustedType policy. final DomTrustedScriptURL trustedUrl = _ttPolicy.createScriptURL(url); assert(trustedUrl.url != '', 'URL: $url rejected by TrustedTypePolicy'); - return trustedUrl; + return trustedUrl as JSAny; } - return url; + return url.toJS; } DomMessageChannel createDomMessageChannel() => DomMessageChannel(); From 777d955ce2d6ef603bcfeb288423a9fca8701453 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 27 Jun 2024 13:45:11 -0700 Subject: [PATCH 7/8] Remove unintentionally added newline. --- lib/web_ui/flutter_js/src/canvaskit_loader.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index df2f427d561f9..d28b329a3bd3e 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -1,4 +1,3 @@ - // 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. From 312e6764f4884727aaf4d1366450aa2a2f36a76d Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Mon, 1 Jul 2024 10:54:45 -0700 Subject: [PATCH 8/8] Change as per David's comment. --- lib/web_ui/flutter_js/src/canvaskit_loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/flutter_js/src/canvaskit_loader.js b/lib/web_ui/flutter_js/src/canvaskit_loader.js index d28b329a3bd3e..4c8e91c7bc175 100644 --- a/lib/web_ui/flutter_js/src/canvaskit_loader.js +++ b/lib/web_ui/flutter_js/src/canvaskit_loader.js @@ -5,7 +5,7 @@ import { createWasmInstantiator } from "./instantiate_wasm.js"; import { joinPathSegments } from "./utils.js"; -export const loadCanvasKit = async (deps, config, browserEnvironment, canvasKitBaseUrl) => { +export const loadCanvasKit = (deps, config, browserEnvironment, canvasKitBaseUrl) => { window.flutterCanvasKitLoaded = (async () => { if (window.flutterCanvasKit) { // The user has set this global variable ahead of time, so we just return that.