Fixes a few issues in flutter_js (flutter/engine#53231)

This addresses a couple things:
https://github.com/flutter/flutter/issues/147610 (Treat `auto` renderer properly)
https://github.com/flutter/flutter/issues/149443 (Add an entrypoint base url config option)

This also adds a `useLocalCanvasKit` configuration option on the build config, which the flutter tool can use to fix https://github.com/flutter/flutter/issues/148713
This commit is contained in:
Jackson Gardner
2024-06-07 13:35:12 -07:00
committed by GitHub
parent bdd8fb686e
commit 24ca8a9440
11 changed files with 112 additions and 49 deletions

View File

@@ -42761,7 +42761,6 @@ ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc + ../../
ORIGIN: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/ui/window/viewport_metrics.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/base_uri.js + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/canvaskit_loader.js + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/entrypoint_loader.js + ../../../flutter/LICENSE
@@ -42771,6 +42770,7 @@ ORIGIN: ../../../flutter/lib/web_ui/flutter_js/src/loader.js + ../../../flutter/
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/flutter_js/src/utils.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
@@ -45632,7 +45632,6 @@ FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.cc
FILE: ../../../flutter/lib/ui/window/pointer_data_packet_converter.h
FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc
FILE: ../../../flutter/lib/ui/window/viewport_metrics.h
FILE: ../../../flutter/lib/web_ui/flutter_js/src/base_uri.js
FILE: ../../../flutter/lib/web_ui/flutter_js/src/browser_environment.js
FILE: ../../../flutter/lib/web_ui/flutter_js/src/canvaskit_loader.js
FILE: ../../../flutter/lib/web_ui/flutter_js/src/entrypoint_loader.js
@@ -45643,6 +45642,7 @@ 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/flutter_js/src/utils.js
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

View File

@@ -562,6 +562,7 @@ class BrowserPlatform extends PlatformPlugin {
</script>
<script>
_flutter.buildConfig = {
useLocalCanvaskit: true,
builds: [
$buildConfigsString
]
@@ -571,7 +572,6 @@ class BrowserPlatform extends PlatformPlugin {
<script>
_flutter.loader.load({
config: {
canvasKitBaseUrl: "/canvaskit/",
// Some of our tests rely on color emoji
useColorEmoji: true,
canvasKitVariant: "${getCanvasKitVariant()}",

View File

@@ -3,7 +3,6 @@
# found in the LICENSE file.
flutter_js_source_list = [
"src/base_uri.js",
"src/browser_environment.js",
"src/canvaskit_loader.js",
"src/entrypoint_loader.js",
@@ -13,6 +12,7 @@ flutter_js_source_list = [
"src/service_worker_loader.js",
"src/skwasm_loader.js",
"src/trusted_types.js",
"src/utils.js",
"src/types.d.ts",
]

View File

@@ -1,17 +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.
export const baseUri = ensureTrailingSlash(getBaseURI());
function getBaseURI() {
const base = document.querySelector("base");
return (base && base.getAttribute("href")) || "";
}
function ensureTrailingSlash(uri) {
if (uri === "") {
return uri;
}
return uri.endsWith("/") ? uri : `${uri}/`;
}

View File

@@ -3,8 +3,9 @@
// found in the LICENSE file.
import { createWasmInstantiator } from "./instantiate_wasm.js";
import { joinPathSegments } from "./utils.js";
export const loadCanvasKit = (deps, config, browserEnvironment, engineRevision) => {
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);
@@ -15,21 +16,21 @@ export const loadCanvasKit = (deps, config, browserEnvironment, engineRevision)
throw "Chromium CanvasKit variant specifically requested, but unsupported in this browser";
}
const useChromiumCanvasKit = supportsChromiumCanvasKit && (config.canvasKitVariant !== "full");
let baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineRevision}/`;
let baseUrl = canvasKitBaseUrl;
if (useChromiumCanvasKit) {
baseUrl = `${baseUrl}chromium/`;
baseUrl = joinPathSegments(baseUrl, "chromium");
}
let canvasKitUrl = `${baseUrl}canvaskit.js`;
let canvasKitUrl = joinPathSegments(baseUrl, "canvaskit.js");
if (deps.flutterTT.policy) {
canvasKitUrl = deps.flutterTT.policy.createScriptURL(canvasKitUrl);
}
const wasmInstantiator = createWasmInstantiator(`${baseUrl}canvaskit.wasm`);
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 () => {
script.addEventListener("load", async () => {
try {
const canvasKit = await CanvasKitInit({
instantiateWasm: wasmInstantiator,
@@ -40,7 +41,7 @@ export const loadCanvasKit = (deps, config, browserEnvironment, engineRevision)
reject(e);
}
});
script.addEventListener('error', reject);
script.addEventListener("error", reject);
document.head.appendChild(script);
});
return window.flutterCanvasKitLoaded;

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { baseUri } from "./base_uri.js";
import { baseUri, joinPathSegments } from "./utils.js";
/**
* Handles injecting the main Flutter web entrypoint (main.dart.js), and notifying
@@ -37,7 +37,7 @@ export class FlutterEntrypointLoader {
* Returns undefined when an `onEntrypointLoaded` callback is supplied in `options`.
*/
async loadEntrypoint(options) {
const { entrypointUrl = `${baseUri}main.dart.js`, onEntrypointLoaded, nonce } =
const { entrypointUrl = joinPathSegments(baseUri, "main.dart.js"), onEntrypointLoaded, nonce } =
options || {};
return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce);
}
@@ -63,11 +63,12 @@ export class FlutterEntrypointLoader {
onEntrypointLoaded ??= (engineInitializer) => {
engineInitializer.initializeEngine(config).then((appRunner) => appRunner.runApp())
};
const { entryPointBaseUrl } = config;
if (build.compileTarget === "dart2wasm") {
return this._loadWasmEntrypoint(build, deps, onEntrypointLoaded);
return this._loadWasmEntrypoint(build, deps, entryPointBaseUrl, onEntrypointLoaded);
} else {
const mainPath = build.mainJsPath ?? "main.dart.js";
const entrypointUrl = `${baseUri}${mainPath}`;
const entrypointUrl = joinPathSegments(baseUri, entryPointBaseUrl, mainPath);
return this._loadJSEntrypoint(entrypointUrl, onEntrypointLoaded, nonce);
}
}
@@ -138,16 +139,17 @@ export class FlutterEntrypointLoader {
*
* @param {import("./types").WasmApplicationBuild} build
* @param {*} deps
* @param {string} entryPointBaseUrl
* @param {import("./types").OnEntrypointLoadedCallback} onEntrypointLoaded
*/
async _loadWasmEntrypoint(build, deps, onEntrypointLoaded) {
async _loadWasmEntrypoint(build, deps, entrypointBaseUrl, onEntrypointLoaded) {
if (!this._scriptLoaded) {
this._scriptLoaded = true;
this._onEntrypointLoaded = onEntrypointLoaded;
const { mainWasmPath, jsSupportRuntimePath } = build;
const moduleUri = `${baseUri}${mainWasmPath}`;
let jsSupportRuntimeUri = `${baseUri}${jsSupportRuntimePath}`;
const moduleUri = joinPathSegments(baseUri, entrypointBaseUrl, mainWasmPath);
let jsSupportRuntimeUri = joinPathSegments(baseUri, entrypointBaseUrl, jsSupportRuntimePath);
if (this._ttPolicy != null) {
jsSupportRuntimeUri = this._ttPolicy.createScriptURL(jsSupportRuntimeUri);
}

View File

@@ -8,6 +8,7 @@ import { FlutterServiceWorkerLoader } from './service_worker_loader.js';
import { FlutterTrustedTypesPolicy } from './trusted_types.js';
import { loadCanvasKit } from './canvaskit_loader.js';
import { loadSkwasm } from './skwasm_loader.js';
import { getCanvaskitBaseUrl } from './utils.js';
/**
* The public interface of _flutter.loader. Exposes two methods:
@@ -87,11 +88,25 @@ export class FlutterLoader {
}
}
/**
* @param {import("./types").ApplicationBuild} build
* @param {import("./types").WebRenderer} renderer
**/
const buildContainsRenderer = (build, renderer) => {
switch (build.renderer) {
// The "auto" build contains both canvaskit and html renderers.
case "auto":
return renderer == "canvaskit" || renderer == "html";
default:
return build.renderer == renderer;
}
}
const buildIsCompatible = (build) => {
if (build.compileTarget === "dart2wasm" && !browserEnvironment.supportsWasmGC) {
return false;
}
if (config.renderer && config.renderer != build.renderer) {
if (config.renderer && !buildContainsRenderer(build, config.renderer)) {
return false;
}
return rendererIsCompatible(build.renderer);
@@ -112,10 +127,11 @@ export class FlutterLoader {
});
}
const canvasKitBaseUrl = getCanvaskitBaseUrl(config, buildConfig);
if (build.renderer === "canvaskit") {
deps.canvasKit = loadCanvasKit(deps, config, browserEnvironment, buildConfig.engineRevision);
deps.canvasKit = loadCanvasKit(deps, config, browserEnvironment, canvasKitBaseUrl);
} else if (build.renderer === "skwasm") {
deps.skwasm = loadSkwasm(deps, config, browserEnvironment, buildConfig.engineRevision);
deps.skwasm = loadSkwasm(deps, config, browserEnvironment, canvasKitBaseUrl);
}
// The FlutterEntrypointLoader instance could be injected as a dependency

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { baseUri } from "./base_uri.js";
import { baseUri, joinPathSegments } from "./utils.js";
/**
* Wraps `promise` in a timeout of the given `duration` in ms.
@@ -78,7 +78,7 @@ export class FlutterServiceWorkerLoader {
}
const {
serviceWorkerVersion,
serviceWorkerUrl = `${baseUri}flutter_service_worker.js?v=${serviceWorkerVersion}`,
serviceWorkerUrl = joinPathSegments(baseUri, `flutter_service_worker.js?v=${serviceWorkerVersion}`),
timeoutMillis = 4000,
} = settings;
// Apply the TrustedTypes policy, if present.

View File

@@ -3,21 +3,21 @@
// found in the LICENSE file.
import { createWasmInstantiator } from "./instantiate_wasm.js";
import { joinPathSegments } from "./utils.js";
export const loadSkwasm = (deps, config, browserEnvironment, engineRevision) => {
export const loadSkwasm = (deps, config, browserEnvironment, baseUrl) => {
return new Promise((resolve, reject) => {
const baseUrl = config.canvasKitBaseUrl ?? `https://www.gstatic.com/flutter-canvaskit/${engineRevision}/`;
let skwasmUrl = `${baseUrl}skwasm.js`;
let skwasmUrl = joinPathSegments(baseUrl, "skwasm.js");
if (deps.flutterTT.policy) {
skwasmUrl = deps.flutterTT.policy.createScriptURL(skwasmUrl);
}
const wasmInstantiator = createWasmInstantiator(`${baseUrl}skwasm.wasm`);
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 () => {
script.addEventListener("load", async () => {
try {
const skwasmInstance = await skwasm({
instantiateWasm: wasmInstantiator,
@@ -28,10 +28,10 @@ export const loadSkwasm = (deps, config, browserEnvironment, engineRevision) =>
// make sure that the worker JS can be loaded regardless of where
// it is hosted.
const url = scriptDirectory + fileName;
if (url.endsWith('.worker.js')) {
if (url.endsWith(".worker.js")) {
return URL.createObjectURL(new Blob(
[`importScripts('${url}');`],
{ 'type': 'application/javascript' }));
[`importScripts("${url}");`],
{ "type": "application/javascript" }));
}
return url;
}
@@ -41,7 +41,7 @@ export const loadSkwasm = (deps, config, browserEnvironment, engineRevision) =>
reject(e);
}
});
script.addEventListener('error', reject);
script.addEventListener("error", reject);
document.head.appendChild(script);
});
}

View File

@@ -8,6 +8,7 @@ type WasmCompileTarget = "dart2wasm";
export type CompileTarget = JSCompileTarget | WasmCompileTarget;
export type WebRenderer =
"auto" |
"html" |
"canvaskit" |
"skwasm";
@@ -32,6 +33,7 @@ export type ApplicationBuild = JSApplicationBuild | WasmApplicationBuild;
export interface BuildConfig {
serviceWorkerVersion: string;
engineRevision: string;
useLocalCanvasKit: bool?;
builds: ApplicationBuild[];
}
@@ -54,6 +56,7 @@ export interface FlutterConfiguration {
renderer: WebRenderer?;
hostElement: HtmlElement?;
fontFallbackBaseUrl: string?;
entryPointBaseUrl: string?;
}
export interface ServiceWorkerSettings {

View File

@@ -0,0 +1,58 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export const baseUri = getBaseURI();
function getBaseURI() {
const base = document.querySelector("base");
return (base && base.getAttribute("href")) || "";
}
export function joinPathSegments(...segments) {
return segments.filter((segment) => !!segment).map((segment, i) => {
if (i === 0) {
return stripRightSlashes(segment);
} else {
return stripLeftSlashes(stripRightSlashes(segment));
}
}).filter(x => x.length).join("/")
}
function stripLeftSlashes(s) {
let i = 0;
while (i < s.length) {
if (s.charCodeAt(i) !== "/") {
break;
}
i++;
}
return s.substring(i);
}
function stripRightSlashes(s) {
let i = s.length;
while (i > 0) {
if (s.charCodeAt(i - 1) !== "/") {
break;
}
i--;
}
return s.substring(0, i);
}
/**
* Calculates the proper base URL for CanvasKit/Skwasm assets.
*
* @param {import("./types").FlutterConfiguration} config
* @param {import("./types").BuildConfig} buildConfig
*/
export function getCanvaskitBaseUrl(config, buildConfig) {
if (config.canvasKitBaseUrl) {
return config.canvasKitBaseUrl;
}
if (buildConfig.engineRevision && !buildConfig.useLocalCanvasKit) {
return joinPathSegments("https://www.gstatic.com/flutter-canvaskit", buildConfig.engineRevision);
}
return "/canvaskit";
}