Add `compiler=js` escape hatch to revert to dart2js compilation (#9406)
diff --git a/packages/devtools_app/benchmark/test_infra/common.dart b/packages/devtools_app/benchmark/test_infra/common.dart index 2915822..32ab643 100644 --- a/packages/devtools_app/benchmark/test_infra/common.dart +++ b/packages/devtools_app/benchmark/test_infra/common.dart
@@ -10,7 +10,7 @@ /// found" in DevTools. const _benchmarkInitialPage = ''; -const _wasmQueryParameters = {'wasm': 'true'}; +const _wasmQueryParameters = {'compiler': 'wasm'}; String benchmarkPath({required bool useWasm}) => Uri( path: _benchmarkInitialPage,
diff --git a/packages/devtools_app/lib/src/shared/preferences/preferences.dart b/packages/devtools_app/lib/src/shared/preferences/preferences.dart index 637124d..cfc9230 100644 --- a/packages/devtools_app/lib/src/shared/preferences/preferences.dart +++ b/packages/devtools_app/lib/src/shared/preferences/preferences.dart
@@ -171,6 +171,16 @@ Future<void> _initWasmEnabled() async { wasmEnabled.value = kIsWasm; + + // If the user forced the dart2js-compiled DevTools via query parameter, + // then set the storage value to match. This will persist across multiple + // sessions of DevTools. + if (DevToolsQueryParams.load().useJs) { + safeUnawaited( + storage.setValue(_ExperimentPreferences.wasm.storageKey, 'false'), + ); + } + addAutoDisposeListener(wasmEnabled, () async { final enabled = wasmEnabled.value; _log.fine('preference update (wasmEnabled = $enabled)'); @@ -188,8 +198,8 @@ 'Reloading DevTools for Wasm preference update (enabled = $enabled)', ); updateQueryParameter( - DevToolsQueryParams.wasmKey, - enabled ? 'true' : null, + DevToolsQueryParams.compilerKey, + enabled ? 'wasm' : null, reload: true, ); } @@ -207,7 +217,7 @@ // back to JS. We know this because the flutter_bootstrap.js logic always // sets the 'wasm' query parameter to 'true' when attempting to load // DevTools with wasm. Remove the wasm query parameter and return early. - updateQueryParameter(DevToolsQueryParams.wasmKey, null); + updateQueryParameter(DevToolsQueryParams.compilerKey, null); ga.impression(gac.devToolsMain, gac.jsFallback); // Do not show the JS fallback notification when embedded in VS Code
diff --git a/packages/devtools_app/lib/src/shared/primitives/query_parameters.dart b/packages/devtools_app/lib/src/shared/primitives/query_parameters.dart index 2ca2a62..7bd99f3 100644 --- a/packages/devtools_app/lib/src/shared/primitives/query_parameters.dart +++ b/packages/devtools_app/lib/src/shared/primitives/query_parameters.dart
@@ -64,7 +64,14 @@ /// Whether DevTools should be loaded using dart2wasm + skwasm instead of /// dart2js + canvaskit. - bool get useWasm => params[wasmKey] == 'true'; + bool get useWasm => params[compilerKey] == 'wasm'; + + /// Whether DevTools should be loaded using dart2js + canvaskit instead of + /// dart2wasm + skwasm. + /// + /// This should only ever be explicitly set by the user if their app fails to + /// load using wasm. + bool get useJs => params[compilerKey] == 'js'; static const vmServiceUriKey = 'uri'; static const hideScreensKey = 'hide'; @@ -75,10 +82,14 @@ static const ideKey = 'ide'; static const ideFeatureKey = 'ideFeature'; - // This query parameter must match the String value in the Flutter bootstrap - // logic that is used to select a web renderer. See - // devtools/packages/devtools_app/web/flutter_bootstrap.js. - static const wasmKey = 'wasm'; + /// Query parameter key to determine whether to use dart2wasm or dart2js. + /// + /// This query parameter must match the String value in the Flutter bootstrap + /// logic that is used to select a web renderer. See + /// devtools/packages/devtools_app/web/flutter_bootstrap.js. + /// + /// Valid values are "js" or "wasm". + static const compilerKey = 'compiler'; // TODO(kenz): remove legacy value in May of 2025 when all IDEs are not using // these and 12 months have passed to allow users ample upgrade time.
diff --git a/packages/devtools_app/web/flutter_bootstrap.js b/packages/devtools_app/web/flutter_bootstrap.js index 2836e26..59b0b9e 100644 --- a/packages/devtools_app/web/flutter_bootstrap.js +++ b/packages/devtools_app/web/flutter_bootstrap.js
@@ -22,9 +22,15 @@ } // This query parameter must match the String value specified by -// `DevToolsQueryParameters.wasmKey`. See +// `DevToolsQueryParameters.compilerKey`. See // devtools/packages/devtools_app/lib/src/shared/query_parameters.dart -const wasmQueryParameterKey = 'wasm'; +const compilerQueryParameterKey = 'compiler'; + +// Returns the value for the given search param. +function getSearchParam(searchParamKey) { + const searchParams = new URLSearchParams(window.location.search); + return searchParams.get(searchParamKey); +} // Calls the DevTools server API to read the user's wasm preference. async function getDevToolsWasmPreference() { @@ -49,12 +55,20 @@ } } +// The query parameter compiler=js gives us an escape hatch we can offer users if their +// dart2wasm app fails to load. +const forceUseJs = () => getSearchParam(compilerQueryParameterKey) === 'js'; + // Returns whether DevTools should be loaded with the skwasm renderer based on the // value of the 'wasm' query parameter or the wasm setting from the DevTools // preference file. async function shouldUseSkwasm() { - const searchParams = new URLSearchParams(window.location.search); - const wasmEnabledFromQueryParameter = searchParams.get(wasmQueryParameterKey) === 'true'; + // If dart2js has specifically been requested via query parameter, then do not try to + // use skwasm (even if the local setting is for wasm). + if (forceUseJs()) { + return false; + } + const wasmEnabledFromQueryParameter = getSearchParam(compilerQueryParameterKey) === 'wasm'; const wasmEnabledFromDevToolsPreference = await getDevToolsWasmPreference(); return wasmEnabledFromQueryParameter === true || wasmEnabledFromDevToolsPreference === true; } @@ -64,9 +78,9 @@ function updateWasmQueryParameter(useSkwasm) { const url = new URL(window.location.href); if (useSkwasm) { - url.searchParams.set(wasmQueryParameterKey, 'true'); + url.searchParams.set(compilerQueryParameterKey, 'wasm'); } else { - url.searchParams.delete(wasmQueryParameterKey); + url.searchParams.delete(compilerQueryParameterKey); } // Update the browser's history without reloading. This is a no-op if the wasm // query parameter does not actually need to be updated. @@ -77,9 +91,11 @@ async function bootstrapAppFor3P() { const useSkwasm = await shouldUseSkwasm(); - // Ensure the 'wasm' query parameter in the URL is accurate for the renderer - // DevTools will be loaded with. - updateWasmQueryParameter(useSkwasm); + if (!forceUseJs()) { + // Ensure the 'wasm' query parameter in the URL is accurate for the renderer + // DevTools will be loaded with. + updateWasmQueryParameter(useSkwasm); + } const rendererForLog = useSkwasm ? 'skwasm' : 'canvaskit'; console.log('Attempting to load DevTools with ' + rendererForLog + ' renderer.');