From 6eaecd2b97715cdedb20072501d7797b15932c8a Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:14 -0800 Subject: [PATCH 1/3] Moves the ?compiler= query param to the expected place for legacy URLs --- .../lib/src/shared/primitives/url_utils.dart | 13 +++++++ .../shared/primitives/url_utils_test.dart | 38 +++++++++++++++++++ .../devtools_app/web/flutter_bootstrap.js | 3 ++ 3 files changed, 54 insertions(+) diff --git a/packages/devtools_app/lib/src/shared/primitives/url_utils.dart b/packages/devtools_app/lib/src/shared/primitives/url_utils.dart index 632f487fb80..024d1a51913 100644 --- a/packages/devtools_app/lib/src/shared/primitives/url_utils.dart +++ b/packages/devtools_app/lib/src/shared/primitives/url_utils.dart @@ -14,6 +14,9 @@ String extractCurrentPageFromUrl(String url) { : uri.path.substring(1); } +const _jsCompilerParam = '?compiler=js'; +const _wasmCompilerParam = '?compiler=wasm'; + /// Maps DevTools URLs in the original fragment format onto the equivalent URLs /// in the new URL format. /// @@ -25,9 +28,13 @@ String? mapLegacyUrl(String url) { // http://localhost:123/#/?page=inspector&uri=ws://... final isRootRequest = uri.path == '/' || uri.path.endsWith('/devtools/'); if (isRootRequest && uri.fragment.isNotEmpty) { + final hasJsParam = url.contains(_jsCompilerParam); + final hasWasmParam = url.contains(_wasmCompilerParam) && !hasJsParam; final basePath = uri.path; // Convert the URL by removing the fragment separator. final newUrl = url + .replaceAll(_wasmCompilerParam, '') + .replaceAll(_jsCompilerParam, '') // Handle localhost:123/#/inspector?uri=xxx .replaceFirst('/#/', '/') // Handle localhost:123/#?page=inspector&uri=xxx @@ -35,6 +42,12 @@ String? mapLegacyUrl(String url) { // Move page names from the querystring into the path. var newUri = Uri.parse(newUrl); + final queryParams = { + ...newUri.queryParameters, + if (hasWasmParam) 'compiler': 'wasm', + if (hasJsParam) 'compiler': 'js', + }; + newUri = newUri.replace(queryParameters: queryParams); final page = newUri.queryParameters['page']; if (newUri.path == basePath && page != null) { final newParams = {...newUri.queryParameters}..remove('page'); diff --git a/packages/devtools_app/test/shared/primitives/url_utils_test.dart b/packages/devtools_app/test/shared/primitives/url_utils_test.dart index ba1ee16ca42..9f60e33eb79 100644 --- a/packages/devtools_app/test/shared/primitives/url_utils_test.dart +++ b/packages/devtools_app/test/shared/primitives/url_utils_test.dart @@ -65,6 +65,44 @@ void main() { test('maps legacy URIs with no page names', () { expect(mapLegacyUrl('$prefix/#/?foo=bar'), '$prefix/?foo=bar'); }); + + for (final compilerValue in ['js', 'wasm']) { + group('with ?compiler=$compilerValue query param', () { + test( + 'moves ?compiler=$compilerValue from before hash to after path', + () { + final newUrl = '$prefix/inspector?compiler=$compilerValue'; + expect( + mapLegacyUrl('$prefix/?compiler=$compilerValue#/inspector'), + newUrl, + ); + expect( + mapLegacyUrl( + '$prefix/?compiler=$compilerValue#/?page=inspector', + ), + newUrl, + ); + }, + ); + + test('handles additional query parameters', () { + final newUrl = + '$prefix/inspector?foo=bar&compiler=$compilerValue'; + expect( + mapLegacyUrl( + '$prefix/?compiler=$compilerValue#/inspector?foo=bar', + ), + newUrl, + ); + expect( + mapLegacyUrl( + '$prefix/?compiler=$compilerValue#/?page=inspector&foo=bar', + ), + newUrl, + ); + }); + }); + } }); } }); diff --git a/packages/devtools_app/web/flutter_bootstrap.js b/packages/devtools_app/web/flutter_bootstrap.js index dc563c3b782..6e2feadd520 100644 --- a/packages/devtools_app/web/flutter_bootstrap.js +++ b/packages/devtools_app/web/flutter_bootstrap.js @@ -84,6 +84,9 @@ async function shouldUseSkwasm() { // Sets or removes the 'wasm' query parameter based on whether DevTools should // be loaded with the skwasm renderer. +// +// Note: In the case of the legacy-formatted URL, this adds the query parameter +// in the wrong place. We fix this in the Dart mapLegacyUrl function. function updateWasmQueryParameter(useSkwasm) { const url = new URL(window.location.href); if (useSkwasm) { From 1f210138faacb2de719d593600e9296640ff6095 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:46:58 -0800 Subject: [PATCH 2/3] Clean up and notes --- .../lib/src/shared/primitives/url_utils.dart | 7 ++- .../shared/primitives/url_utils_test.dart | 63 ++++++++++--------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/packages/devtools_app/lib/src/shared/primitives/url_utils.dart b/packages/devtools_app/lib/src/shared/primitives/url_utils.dart index 024d1a51913..933706bb87e 100644 --- a/packages/devtools_app/lib/src/shared/primitives/url_utils.dart +++ b/packages/devtools_app/lib/src/shared/primitives/url_utils.dart @@ -28,13 +28,16 @@ String? mapLegacyUrl(String url) { // http://localhost:123/#/?page=inspector&uri=ws://... final isRootRequest = uri.path == '/' || uri.path.endsWith('/devtools/'); if (isRootRequest && uri.fragment.isNotEmpty) { + // Note: If there is a ?compiler= query parameter, we remove it from before + // the hash then add it back in as a query parameter at the end. + // See https://github.com/flutter/devtools/issues/9612 for details. final hasJsParam = url.contains(_jsCompilerParam); final hasWasmParam = url.contains(_wasmCompilerParam) && !hasJsParam; final basePath = uri.path; // Convert the URL by removing the fragment separator. final newUrl = url - .replaceAll(_wasmCompilerParam, '') .replaceAll(_jsCompilerParam, '') + .replaceAll(_wasmCompilerParam, '') // Handle localhost:123/#/inspector?uri=xxx .replaceFirst('/#/', '/') // Handle localhost:123/#?page=inspector&uri=xxx @@ -44,8 +47,8 @@ String? mapLegacyUrl(String url) { var newUri = Uri.parse(newUrl); final queryParams = { ...newUri.queryParameters, - if (hasWasmParam) 'compiler': 'wasm', if (hasJsParam) 'compiler': 'js', + if (hasWasmParam) 'compiler': 'wasm', }; newUri = newUri.replace(queryParameters: queryParams); final page = newUri.queryParameters['page']; diff --git a/packages/devtools_app/test/shared/primitives/url_utils_test.dart b/packages/devtools_app/test/shared/primitives/url_utils_test.dart index 9f60e33eb79..16494d1312e 100644 --- a/packages/devtools_app/test/shared/primitives/url_utils_test.dart +++ b/packages/devtools_app/test/shared/primitives/url_utils_test.dart @@ -66,43 +66,48 @@ void main() { expect(mapLegacyUrl('$prefix/#/?foo=bar'), '$prefix/?foo=bar'); }); - for (final compilerValue in ['js', 'wasm']) { - group('with ?compiler=$compilerValue query param', () { - test( - 'moves ?compiler=$compilerValue from before hash to after path', - () { - final newUrl = '$prefix/inspector?compiler=$compilerValue'; + group( + 'with "compiler" query param (https://github.com/flutter/devtools/issues/9612)', + () { + for (final compilerValue in ['js', 'wasm']) { + test( + 'moves ?compiler=$compilerValue from before hash to after path', + () { + final newUrl = '$prefix/inspector?compiler=$compilerValue'; + expect( + mapLegacyUrl( + '$prefix/?compiler=$compilerValue#/inspector', + ), + newUrl, + ); + expect( + mapLegacyUrl( + '$prefix/?compiler=$compilerValue#/?page=inspector', + ), + newUrl, + ); + }, + ); + + test('handles additional query parameters', () { + final newUrl = + '$prefix/inspector?foo=bar&compiler=$compilerValue'; expect( - mapLegacyUrl('$prefix/?compiler=$compilerValue#/inspector'), + mapLegacyUrl( + '$prefix/?compiler=$compilerValue#/inspector?foo=bar', + ), newUrl, ); expect( mapLegacyUrl( - '$prefix/?compiler=$compilerValue#/?page=inspector', + '$prefix/?compiler=$compilerValue#/?page=inspector&foo=bar', ), newUrl, ); - }, - ); - - test('handles additional query parameters', () { - final newUrl = - '$prefix/inspector?foo=bar&compiler=$compilerValue'; - expect( - mapLegacyUrl( - '$prefix/?compiler=$compilerValue#/inspector?foo=bar', - ), - newUrl, - ); - expect( - mapLegacyUrl( - '$prefix/?compiler=$compilerValue#/?page=inspector&foo=bar', - ), - newUrl, - ); - }); - }); - } + }); + } + }, + ); }); } }); From 8c60a07df139990a69862de9ca19b4e4aa02e41a Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:48:49 -0800 Subject: [PATCH 3/3] Add note to flutter_bootstrap as well --- packages/devtools_app/web/flutter_bootstrap.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/devtools_app/web/flutter_bootstrap.js b/packages/devtools_app/web/flutter_bootstrap.js index 6e2feadd520..ee6a54d68db 100644 --- a/packages/devtools_app/web/flutter_bootstrap.js +++ b/packages/devtools_app/web/flutter_bootstrap.js @@ -86,7 +86,8 @@ async function shouldUseSkwasm() { // be loaded with the skwasm renderer. // // Note: In the case of the legacy-formatted URL, this adds the query parameter -// in the wrong place. We fix this in the Dart mapLegacyUrl function. +// in the wrong place. We fix this in the Dart mapLegacyUrl function. Details: +// https://github.com/flutter/devtools/issues/9612 function updateWasmQueryParameter(useSkwasm) { const url = new URL(window.location.href); if (useSkwasm) {