Skip to content

Commit

Permalink
Reverts "Reland "Output .js files as ES6 modules. (flutter#52023)" (f…
Browse files Browse the repository at this point in the history
…lutter#53688)" (flutter#53709)

Reverts: flutter#53688
Initiated by: jiahaog
Reason for reverting: canvaskit.js cannot be loaded in an internal end to end test - see b/350885206
Original PR Author: eyebrowsoffire

Reviewed By: {ditman}

This change reverts the following previous change:
This is an attempt to reland flutter#52023. The issue previously is that if it was not specified by the user, the default CanvasKit base URL did not have a leading slash, which does not work when doing dynamic imports.
  • Loading branch information
auto-submit[bot] authored Jul 3, 2024
1 parent 44e1d0b commit 8274f54
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 61 deletions.
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' + '@' + 'e265c359126b24351f534080fb22edaa159f2215',
'src': 'https://github.com/flutter/buildroot.git' + '@' + '8c2d66fa4e6298894425f5bdd0591bc5b1154c53',

'src/flutter/third_party/depot_tools':
Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '580b4ff3f5cd0dcaa2eacda28cefe0f45320e8f7',
Expand Down
33 changes: 23 additions & 10 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) => {
window.flutterCanvasKitLoaded = (async () => {
if (window.flutterCanvasKit) {
// The user has set this global variable ahead of time, so we just return that.
return window.flutterCanvasKit;
}
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";
Expand All @@ -25,11 +25,24 @@ export const loadCanvasKit = (deps, config, browserEnvironment, canvasKitBaseUrl
canvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl);
}
const wasmInstantiator = createWasmInstantiator(joinPathSegments(baseUrl, "canvaskit.wasm"));
const canvasKitModule = await import(canvasKitUrl);
window.flutterCanvasKit = await canvasKitModule.default({
instantiateWasm: wasmInstantiator,
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);
}
});
return window.flutterCanvasKit;
})();
script.addEventListener("error", reject);
document.head.appendChild(script);
});
return window.flutterCanvasKitLoaded;
}
59 changes: 37 additions & 22 deletions lib/web_ui/flutter_js/src/skwasm_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,43 @@
import { createWasmInstantiator } from "./instantiate_wasm.js";
import { joinPathSegments } from "./utils.js";

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' }));
}
return url;
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);
}
});
script.addEventListener("error", reject);
document.head.appendChild(script);
});
}
56 changes: 39 additions & 17 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,12 @@ extension CanvasKitExtension on CanvasKit {
);
}

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

extension CanvasKitModuleExtension on CanvasKitModule {
@JS('default')
external JSPromise<JSAny> defaultExport(CanvasKitInitOptions options);
Future<CanvasKit> CanvasKitInit(CanvasKitInitOptions options) {
return js_util.promiseToFuture<CanvasKit>(
_CanvasKitInit(options).toObjectShallow);
}

typedef LocateFileCallback = String Function(String file, String unusedBase);
Expand Down Expand Up @@ -3662,11 +3661,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 {
final CanvasKitModule canvasKitModule = await _downloadOneOf(_canvasKitJsUrls);
await _downloadOneOf(_canvasKitJsUrls);

final CanvasKit canvasKit = (await canvasKitModule.defaultExport(CanvasKitInitOptions(
final CanvasKit canvasKit = await CanvasKitInit(CanvasKitInitOptions(
locateFile: createLocateFileCallback(canvasKitWasmModuleUrl),
)).toDart) as CanvasKit;
));

if (canvasKit.ParagraphBuilder.RequiresClientICU() && !browserSupportsCanvaskitChromium) {
throw Exception(
Expand All @@ -3682,12 +3681,10 @@ Future<CanvasKit> downloadCanvasKit() async {
/// downloads it.
///
/// If none of the URLs can be downloaded, throws an [Exception].
Future<CanvasKitModule> _downloadOneOf(Iterable<String> urls) async {
Future<void> _downloadOneOf(Iterable<String> urls) async {
for (final String url in urls) {
try {
return await _downloadCanvasKitJs(url);
} catch (_) {
continue;
if (await _downloadCanvasKitJs(url)) {
return;
}
}

Expand All @@ -3701,7 +3698,32 @@ Future<CanvasKitModule> _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<CanvasKitModule> _downloadCanvasKitJs(String url) async {
final JSAny scriptUrl = createTrustedScriptUrl(url);
return (await importModule(scriptUrl).toDart) as CanvasKitModule;
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;
}
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class FlutterConfiguration {
_configuration?.canvasKitBaseUrl ?? _defaultCanvasKitBaseUrl;
static const String _defaultCanvasKitBaseUrl = String.fromEnvironment(
'FLUTTER_WEB_CANVASKIT_URL',
defaultValue: '/canvaskit/',
defaultValue: 'canvaskit/',
);

/// The variant of CanvasKit to download.
Expand Down
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 @@ -3383,16 +3383,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`.
JSAny createTrustedScriptUrl(String url) {
Object 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 as JSAny;
return trustedUrl;
}
return url.toJS;
return url;
}

DomMessageChannel createDomMessageChannel() => DomMessageChannel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ 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
14 changes: 7 additions & 7 deletions lib/web_ui/test/engine/configuration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,30 @@ void testMain() {
test('initializes with null', () async {
final FlutterConfiguration config = FlutterConfiguration.legacy(null);

expect(config.canvasKitBaseUrl, '/canvaskit/'); // _defaultCanvasKitBaseUrl
expect(config.canvasKitBaseUrl, 'canvaskit/'); // _defaultCanvasKitBaseUrl
});

test('legacy constructor initializes with a Js Object', () async {
final FlutterConfiguration config = FlutterConfiguration.legacy(
js_util.jsify(<String, Object?>{
'canvasKitBaseUrl': '/some_other_url/',
'canvasKitBaseUrl': 'some_other_url/',
}) as JsFlutterConfiguration);

expect(config.canvasKitBaseUrl, '/some_other_url/');
expect(config.canvasKitBaseUrl, 'some_other_url/');
});
});

group('setUserConfiguration', () {
test('throws assertion error if already initialized from JS', () async {
final FlutterConfiguration config = FlutterConfiguration.legacy(
js_util.jsify(<String, Object?>{
'canvasKitBaseUrl': '/some_other_url/',
'canvasKitBaseUrl': 'some_other_url/',
}) as JsFlutterConfiguration);

expect(() {
config.setUserConfiguration(
js_util.jsify(<String, Object?>{
'canvasKitBaseUrl': '/yet_another_url/',
'canvasKitBaseUrl': 'yet_another_url/',
}) as JsFlutterConfiguration);
}, throwsAssertionError);
});
Expand All @@ -55,10 +55,10 @@ void testMain() {

config.setUserConfiguration(
js_util.jsify(<String, Object?>{
'canvasKitBaseUrl': '/one_more_url/',
'canvasKitBaseUrl': 'one_more_url/',
}) as JsFlutterConfiguration);

expect(config.canvasKitBaseUrl, '/one_more_url/');
expect(config.canvasKitBaseUrl, 'one_more_url/');
});

test('can receive non-existing properties without crashing', () async {
Expand Down

0 comments on commit 8274f54

Please sign in to comment.