Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Output .js files as ES6 modules. #52023

Merged
merged 11 commits into from
Jul 1, 2024
Merged
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ allowed_hosts = [
]

deps = {
'src': 'https://github.com/flutter/buildroot.git' + '@' + '8c2d66fa4e6298894425f5bdd0591bc5b1154c53',
'src': 'https://github.com/flutter/buildroot.git' + '@' + '6c01dbca494b78e32f9e4aa704514faabfba74e8',

'src/flutter/third_party/depot_tools':
Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '580b4ff3f5cd0dcaa2eacda28cefe0f45320e8f7',
Expand Down
33 changes: 10 additions & 23 deletions lib/web_ui/flutter_js/src/canvaskit_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { createWasmInstantiator } from "./instantiate_wasm.js";
import { joinPathSegments } from "./utils.js";

export const loadCanvasKit = (deps, config, browserEnvironment, canvasKitBaseUrl) => {
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) => {
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";
Expand All @@ -25,24 +25,11 @@ export const loadCanvasKit = (deps, config, browserEnvironment, canvasKitBaseUrl
canvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl);
}
const wasmInstantiator = createWasmInstantiator(joinPathSegments(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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is beautiful!

window.flutterCanvasKit = await canvasKitModule.default({
instantiateWasm: wasmInstantiator,
});
script.addEventListener("error", reject);
document.head.appendChild(script);
});
return window.flutterCanvasKit;
})();
return window.flutterCanvasKitLoaded;
}
59 changes: 22 additions & 37 deletions lib/web_ui/flutter_js/src/skwasm_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,28 @@
import { createWasmInstantiator } from "./instantiate_wasm.js";
import { joinPathSegments } from "./utils.js";

export const loadSkwasm = (deps, config, browserEnvironment, baseUrl) => {
return new Promise((resolve, reject) => {
let skwasmUrl = joinPathSegments(baseUrl, "skwasm.js");
if (deps.flutterTT.policy) {
skwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl);
}
const wasmInstantiator = createWasmInstantiator(joinPathSegments(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, baseUrl) => {
let skwasmUrl = joinPathSegments(baseUrl, "skwasm.js");
if (deps.flutterTT.policy) {
skwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl);
}
const wasmInstantiator = createWasmInstantiator(joinPathSegments(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;
}
});
}
56 changes: 17 additions & 39 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,13 @@ extension CanvasKitExtension on CanvasKit {
);
}

@JS('window.CanvasKitInit')
external JSAny _CanvasKitInit(CanvasKitInitOptions options);
@JS()
@staticInterop
class CanvasKitModule {}

Future<CanvasKit> CanvasKitInit(CanvasKitInitOptions options) {
return js_util.promiseToFuture<CanvasKit>(
_CanvasKitInit(options).toObjectShallow);
extension CanvasKitModuleExtension on CanvasKitModule {
@JS('default')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔪 💔

external JSPromise<JSAny> defaultExport(CanvasKitInitOptions options);
}

typedef LocateFileCallback = String Function(String file, String unusedBase);
Expand Down Expand Up @@ -3661,11 +3662,11 @@ String canvasKitWasmModuleUrl(String file, String canvasKitBase) =>
/// Downloads the CanvasKit JavaScript, then calls `CanvasKitInit` to download
/// and intialize the CanvasKit wasm.
Future<CanvasKit> 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(
Expand All @@ -3681,10 +3682,12 @@ Future<CanvasKit> downloadCanvasKit() async {
/// downloads it.
///
/// If none of the URLs can be downloaded, throws an [Exception].
Future<void> _downloadOneOf(Iterable<String> urls) async {
Future<CanvasKitModule> _downloadOneOf(Iterable<String> urls) async {
for (final String url in urls) {
if (await _downloadCanvasKitJs(url)) {
return;
try {
return await _downloadCanvasKitJs(url);
} catch (_) {
continue;
}
}

Expand All @@ -3698,32 +3701,7 @@ Future<void> _downloadOneOf(Iterable<String> urls) async {
///
/// Returns a [Future] that completes with `true` if the CanvasKit JavaScript
/// file was successfully downloaded, or `false` if it failed.
Future<bool> _downloadCanvasKitJs(String url) {
final DomHTMLScriptElement canvasKitScript =
createDomHTMLScriptElement(configuration.nonce);
canvasKitScript.src = createTrustedScriptUrl(url);

final Completer<bool> canvasKitLoadCompleter = Completer<bool>();

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<CanvasKitModule> _downloadCanvasKitJs(String url) async {
final JSAny scriptUrl = createTrustedScriptUrl(url);
return (await importModule(scriptUrl).toDart) as CanvasKitModule;
}
6 changes: 3 additions & 3 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3407,16 +3407,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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down