From f804fb5b4e8e4dc4700c00bab07b2a204c1d0433 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Thu, 18 Dec 2025 12:57:15 -0800 Subject: [PATCH] Add explicit `--cross-origin-isolation` flag to `flutter run`. (#180026) Fixes https://github.com/flutter/flutter/issues/163479 This adds a flag, `--cross-origin-isolation`/`--no-cross-origin-isolation` that allows the user to explicitly control whether `flutter run`/`drive`/`test` serves files with COOP/COEP headers. If the user doesn't specify, it uses cross origin isolation when wasm is enabled and no cross origin isolation when wasm is disabled. --- .../flutter_tools/lib/src/commands/run.dart | 5 + .../flutter_tools/lib/src/commands/test.dart | 13 ++ packages/flutter_tools/lib/src/device.dart | 11 ++ .../lib/src/isolated/devfs_web.dart | 3 + .../src/isolated/release_asset_server.dart | 4 +- .../lib/src/isolated/resident_web_runner.dart | 1 + .../lib/src/isolated/web_asset_server.dart | 10 +- .../lib/src/runner/flutter_command.dart | 10 ++ .../lib/src/test/flutter_web_platform.dart | 14 +- .../flutter_tools/lib/src/test/runner.dart | 1 + .../lib/src/web/web_constants.dart | 2 +- .../hermetic/flutter_web_platform_test.dart | 2 + .../web/devfs_web_ddc_modules_test.dart | 8 ++ .../general.shard/web/devfs_web_test.dart | 9 ++ .../web/web_asset_server_test.dart | 124 +++++++++++++++++- 15 files changed, 207 insertions(+), 10 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 31704fe4f53..0590dd88257 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -291,6 +291,9 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment ? stringsArg(FlutterOptions.kWebBrowserFlag) : const []; + final bool? webCrossOriginIsolation = argResults!.wasParsed('cross-origin-isolation') + ? boolArg('cross-origin-isolation') + : null; if (buildInfo.mode.isRelease) { return DebuggingOptions.disabled( buildInfo, @@ -306,6 +309,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment webRunHeadless: featureFlags.isWebEnabled && boolArg('web-run-headless'), webBrowserDebugPort: webBrowserDebugPort, webBrowserFlags: webBrowserFlags, + webCrossOriginIsolation: webCrossOriginIsolation, webRenderer: webRenderer, webUseWasm: useWasm, enableImpeller: enableImpeller, @@ -362,6 +366,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment webEnableExpressionEvaluation: featureFlags.isWebEnabled && boolArg('web-enable-expression-evaluation'), webLaunchUrl: featureFlags.isWebEnabled ? stringArg('web-launch-url') : null, + webCrossOriginIsolation: webCrossOriginIsolation, webRenderer: webRenderer, webUseWasm: useWasm, vmserviceOutFile: stringArg('vmservice-out-file'), diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index 57562f6ac79..eaac1ade1a3 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -285,6 +285,16 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { FlutterOptions.kWebWasmFlag, help: 'Compile to WebAssembly rather than JavaScript.\n$kWasmMoreInfo', negatable: false, + ) + ..addFlag( + 'cross-origin-isolation', + help: + 'Adds the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy ' + 'headers to the web server. These headers are required for using APIs like ' + 'SharedArrayBuffer. This is on by default for the "skwasm" web renderer, ' + 'and this flag can be used to override the default. To disable this for the ' + 'skwasm renderer, use "--no-cross-origin-isolation".', + hide: !verboseHelp, ); addDdsOptions(verboseHelp: verboseHelp); @@ -462,6 +472,9 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { enableFlutterGpu: (argResults!['enable-flutter-gpu'] as bool?) ?? false, debugLogsDirectoryPath: debugLogsDirectoryPath, webRenderer: webRenderer, + webCrossOriginIsolation: argResults!.wasParsed('cross-origin-isolation') + ? boolArg('cross-origin-isolation') + : null, printDtd: boolArg(FlutterGlobalOptions.kPrintDtd, global: true), webUseWasm: useWasm, ); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index cce79aeb867..055c573f0d8 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -987,6 +987,7 @@ class DebuggingOptions { this.webBrowserFlags = const [], this.webEnableExpressionEvaluation = false, this.webLaunchUrl, + bool? webCrossOriginIsolation, WebRendererMode? webRenderer, this.webUseWasm = false, this.vmserviceOutFile, @@ -1006,6 +1007,7 @@ class DebuggingOptions { this.printDtd = false, this.webDevServerConfig, }) : debuggingEnabled = true, + webCrossOriginIsolation = webCrossOriginIsolation ?? webUseWasm, webRenderer = webRenderer ?? WebRendererMode.getDefault(useWasm: webUseWasm); DebuggingOptions.disabled( @@ -1019,6 +1021,7 @@ class DebuggingOptions { this.webBrowserDebugPort, this.webBrowserFlags = const [], this.webLaunchUrl, + bool? webCrossOriginIsolation, WebRendererMode? webRenderer, this.webUseWasm = false, this.traceAllowlist, @@ -1056,6 +1059,7 @@ class DebuggingOptions { devToolsServerAddress = null, vmserviceOutFile = null, webEnableExpressionEvaluation = false, + webCrossOriginIsolation = webCrossOriginIsolation ?? webUseWasm, nativeNullAssertions = false, enableDevTools = false, ipv6 = false, @@ -1098,6 +1102,7 @@ class DebuggingOptions { required this.webBrowserFlags, required this.webEnableExpressionEvaluation, required this.webLaunchUrl, + required this.webCrossOriginIsolation, required this.webRenderer, required this.webUseWasm, required this.vmserviceOutFile, @@ -1186,6 +1191,10 @@ class DebuggingOptions { /// Allow developers to customize the browser's launch URL final String? webLaunchUrl; + /// Whether to enable cross-origin isolation. This is on by default for the + /// skwasm renderer. + final bool webCrossOriginIsolation; + /// Which web renderer to use for the debugging session final WebRendererMode webRenderer; @@ -1287,6 +1296,7 @@ class DebuggingOptions { 'webBrowserFlags': webBrowserFlags, 'webEnableExpressionEvaluation': webEnableExpressionEvaluation, 'webLaunchUrl': webLaunchUrl, + 'webCrossOriginIsolation': webCrossOriginIsolation, 'webHeaders': webDevServerConfig?.headers ?? {}, 'webRenderer': webRenderer.name, 'webUseWasm': webUseWasm, @@ -1354,6 +1364,7 @@ class DebuggingOptions { webBrowserFlags: (json['webBrowserFlags']! as List).cast(), webEnableExpressionEvaluation: json['webEnableExpressionEvaluation']! as bool, webLaunchUrl: json['webLaunchUrl'] as String?, + webCrossOriginIsolation: json['webCrossOriginIsolation']! as bool, webRenderer: WebRendererMode.values.byName(json['webRenderer']! as String), webUseWasm: json['webUseWasm']! as bool, vmserviceOutFile: json['vmserviceOutFile'] as String?, diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart index c9a13f6024e..57a437092ad 100644 --- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart +++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart @@ -80,6 +80,7 @@ class WebDevFS implements DevFS { required this.useLocalCanvasKit, required this.rootDirectory, this.useDwdsWebSocketConnection = false, + required this.webCrossOriginIsolation, required this.fileSystem, required this.logger, required this.platform, @@ -112,6 +113,7 @@ class WebDevFS implements DevFS { final bool useLocalCanvasKit; final WebDevServerConfig webDevServerConfig; final bool useDwdsWebSocketConnection; + final bool webCrossOriginIsolation; final FileSystem fileSystem; final Logger logger; final Platform platform; @@ -220,6 +222,7 @@ class WebDevFS implements DevFS { fileSystem: fileSystem, logger: logger, platform: platform, + crossOriginIsolation: webCrossOriginIsolation, shouldEnableMiddleware: shouldEnableMiddleware, ); return baseUri; diff --git a/packages/flutter_tools/lib/src/isolated/release_asset_server.dart b/packages/flutter_tools/lib/src/isolated/release_asset_server.dart index 6698f96dd84..606ca9a983c 100644 --- a/packages/flutter_tools/lib/src/isolated/release_asset_server.dart +++ b/packages/flutter_tools/lib/src/isolated/release_asset_server.dart @@ -92,7 +92,7 @@ class ReleaseAssetServer { 'Cross-Origin-Resource-Policy': 'cross-origin', 'Access-Control-Allow-Origin': '*', if (_needsCoopCoep && _fileSystem.path.extension(file.path) == '.html') - ...kMultiThreadedHeaders, + ...kCrossOriginIsolationHeaders, }, ); } @@ -102,7 +102,7 @@ class ReleaseAssetServer { file.readAsBytesSync(), headers: { 'Content-Type': 'text/html', - if (_needsCoopCoep) ...kMultiThreadedHeaders, + if (_needsCoopCoep) ...kCrossOriginIsolationHeaders, }, ); } diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index 2f844681b87..2ab61ec2911 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -313,6 +313,7 @@ class ResidentWebRunner extends ResidentRunner { useLocalCanvasKit: debuggingOptions.buildInfo.useLocalCanvasKit, rootDirectory: fileSystem.directory(projectRootPath), useDwdsWebSocketConnection: useDwdsWebSocketConnection, + webCrossOriginIsolation: debuggingOptions.webCrossOriginIsolation, fileSystem: fileSystem, logger: logger, platform: _platform, diff --git a/packages/flutter_tools/lib/src/isolated/web_asset_server.dart b/packages/flutter_tools/lib/src/isolated/web_asset_server.dart index fa896b80c4b..ca817281046 100644 --- a/packages/flutter_tools/lib/src/isolated/web_asset_server.dart +++ b/packages/flutter_tools/lib/src/isolated/web_asset_server.dart @@ -32,6 +32,7 @@ import '../web/devfs_config.dart'; import '../web/devfs_proxy.dart'; import '../web/memory_fs.dart'; import '../web/module_metadata.dart'; +import '../web/web_constants.dart'; import '../web_template.dart'; import 'proxy_middleware.dart'; import 'release_asset_server.dart'; @@ -180,6 +181,7 @@ class WebAssetServer implements AssetReader { DartDevelopmentServiceConfiguration ddsConfig, Uri entrypoint, ExpressionCompiler? expressionCompiler, { + required bool crossOriginIsolation, required WebDevServerConfig webDevServerConfig, required WebRendererMode webRenderer, required bool isWasm, @@ -237,6 +239,12 @@ class WebAssetServer implements AssetReader { // Allow rendering in a iframe. httpServer!.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN'); + if (crossOriginIsolation) { + for (final MapEntry header in kCrossOriginIsolationHeaders.entries) { + httpServer.defaultResponseHeaders.add(header.key, header.value); + } + } + for (final MapEntry header in extraHeaders.entries) { httpServer.defaultResponseHeaders.add(header.key, header.value); } @@ -279,7 +287,7 @@ class WebAssetServer implements AssetReader { flutterRoot: Cache.flutterRoot, webBuildDirectory: getWebBuildDirectory(), basePath: server.basePath, - needsCoopCoep: webRenderer == WebRendererMode.skwasm, + needsCoopCoep: crossOriginIsolation, ); runZonedGuarded( () { diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 784d5327b5e..5dd9e971ad1 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -391,6 +391,16 @@ abstract class FlutterCommand extends Command { valueHelp: '--foo=bar', hide: !verboseHelp, ); + argParser.addFlag( + 'cross-origin-isolation', + help: + 'Adds the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy ' + 'headers to the web server. These headers are required for using APIs like ' + 'SharedArrayBuffer. This is on by default for the "skwasm" web renderer, ' + 'and this flag can be used to override the default. To disable this for the ' + 'skwasm renderer, use "--no-cross-origin-isolation".', + hide: !verboseHelp, + ); } void usesTargetOption() { diff --git a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart index dba635d5a49..e6627334514 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -58,7 +58,7 @@ shelf.Handler createDirectoryHandler(Directory directory, {required bool crossOr file.openRead(), headers: { 'Content-Type': ?contentType, - if (needsCrossOriginIsolated) ...kMultiThreadedHeaders, + if (needsCrossOriginIsolated) ...kCrossOriginIsolationHeaders, }, ); }; @@ -84,6 +84,7 @@ class FlutterWebPlatform extends PlatformPlugin { required ProcessManager processManager, required this.webRenderer, required this.useWasm, + required this.crossOriginIsolation, TestTimeRecorder? testTimeRecorder, }) : _fileSystem = fileSystem, _buildDirectory = buildDirectory, @@ -99,7 +100,7 @@ class FlutterWebPlatform extends PlatformPlugin { fileSystem.directory( fileSystem.path.join(Cache.flutterRoot!, 'packages', 'flutter_tools'), ), - crossOriginIsolated: webRenderer == WebRendererMode.skwasm, + crossOriginIsolated: crossOriginIsolation, ), ) .add(_handleStaticArtifact) @@ -110,7 +111,7 @@ class FlutterWebPlatform extends PlatformPlugin { .add( createDirectoryHandler( fileSystem.directory(fileSystem.path.join(fileSystem.currentDirectory.path, 'test')), - crossOriginIsolated: webRenderer == WebRendererMode.skwasm, + crossOriginIsolated: crossOriginIsolation, ), ) .add(_packageFilesHandler); @@ -145,6 +146,7 @@ class FlutterWebPlatform extends PlatformPlugin { final String _root; final WebRendererMode webRenderer; final bool useWasm; + final bool crossOriginIsolation; /// Allows only one test suite (typically one test file) to be loaded and run /// at any given point in time. Loading more than one file at a time is known @@ -174,6 +176,7 @@ class FlutterWebPlatform extends PlatformPlugin { required ProcessManager processManager, required WebRendererMode webRenderer, required bool useWasm, + required bool crossOriginIsolation, TestTimeRecorder? testTimeRecorder, Uri? testPackageUri, Future Function() serverFactory = defaultServerFactory, @@ -215,6 +218,7 @@ class FlutterWebPlatform extends PlatformPlugin { processManager: processManager, webRenderer: webRenderer, useWasm: useWasm, + crossOriginIsolation: crossOriginIsolation, testTimeRecorder: testTimeRecorder, ); } @@ -419,7 +423,7 @@ class FlutterWebPlatform extends PlatformPlugin { final String basename = _fileSystem.path.basename(fileUri.toFilePath()); final shelf.Handler handler = createDirectoryHandler( _fileSystem.directory(dirname), - crossOriginIsolated: webRenderer == WebRendererMode.skwasm, + crossOriginIsolated: crossOriginIsolation, ); final modifiedRequest = shelf.Request( request.method, @@ -543,7 +547,7 @@ class FlutterWebPlatform extends PlatformPlugin { ''', headers: { 'Content-Type': 'text/html', - if (webRenderer == WebRendererMode.skwasm) ...kMultiThreadedHeaders, + if (webRenderer == WebRendererMode.skwasm) ...kCrossOriginIsolationHeaders, }, ); } diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index 67517dac453..3f8b8216eeb 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -143,6 +143,7 @@ interface class FlutterTestRunner { testTimeRecorder: testTimeRecorder, webRenderer: debuggingOptions.webRenderer, useWasm: debuggingOptions.webUseWasm, + crossOriginIsolation: debuggingOptions.webCrossOriginIsolation, ); }); await testWrapper.main(testArgs); diff --git a/packages/flutter_tools/lib/src/web/web_constants.dart b/packages/flutter_tools/lib/src/web/web_constants.dart index 672a2907a02..94ec3ab1b19 100644 --- a/packages/flutter_tools/lib/src/web/web_constants.dart +++ b/packages/flutter_tools/lib/src/web/web_constants.dart @@ -8,7 +8,7 @@ const kWasmMoreInfo = 'See https://flutter.dev/to/wasm for more information.'; /// /// See https://developer.chrome.com/blog/coep-credentialless-origin-trial /// for more information. -const kMultiThreadedHeaders = { +const kCrossOriginIsolationHeaders = { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'credentialless', }; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart index b4ae514ceca..8ddbe2e54e1 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart @@ -94,6 +94,7 @@ void main() { useWasm: false, serverFactory: () async => server, testPackageUri: Uri.parse('test'), + crossOriginIsolation: false, ); final shelf.Handler? handler = server.mountedHandler; expect(handler, isNotNull); @@ -147,6 +148,7 @@ void main() { useWasm: false, serverFactory: () async => server, testPackageUri: Uri.parse('test'), + crossOriginIsolation: false, ); final shelf.Handler? handler = server.mountedHandler; expect(handler, isNotNull); diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart index 63d2566c477..3a0a1184d21 100644 --- a/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart @@ -814,6 +814,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.ddcModuleLoaderJS.createSync(recursive: true); webDevFS.flutterJs.createSync(recursive: true); @@ -920,6 +921,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.ddcModuleLoaderJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -993,6 +995,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.ddcModuleLoaderJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1041,6 +1044,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.ddcModuleLoaderJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1092,6 +1096,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.ddcModuleLoaderJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1132,6 +1137,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + crossOriginIsolation: false, ); expect(webAssetServer.defaultResponseHeaders['x-frame-options'], null); @@ -1171,6 +1177,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + crossOriginIsolation: false, ); expect(webAssetServer.defaultResponseHeaders[extraHeaderKey], [extraHeaderValue]); @@ -1258,6 +1265,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.ddcModuleLoaderJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart index a88680e6e16..2bace5e9c4c 100644 --- a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart @@ -940,6 +940,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.flutterJs.createSync(recursive: true); @@ -1047,6 +1048,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.flutterJs.createSync(recursive: true); @@ -1159,6 +1161,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1234,6 +1237,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); @@ -1282,6 +1286,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1336,6 +1341,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); @@ -1380,6 +1386,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + crossOriginIsolation: false, ); expect(webAssetServer.defaultResponseHeaders['x-frame-options'], null); @@ -1419,6 +1426,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + crossOriginIsolation: false, ); expect(webAssetServer.defaultResponseHeaders[extraHeaderKey], [extraHeaderValue]); @@ -1513,6 +1521,7 @@ void main() { fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + webCrossOriginIsolation: false, ); webDevFS.requireJS.createSync(recursive: true); webDevFS.stackTraceMapper.createSync(recursive: true); diff --git a/packages/flutter_tools/test/general.shard/web/web_asset_server_test.dart b/packages/flutter_tools/test/general.shard/web/web_asset_server_test.dart index cc54ab4e772..be6510ea770 100644 --- a/packages/flutter_tools/test/general.shard/web/web_asset_server_test.dart +++ b/packages/flutter_tools/test/general.shard/web/web_asset_server_test.dart @@ -2,11 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:dwds/dwds.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/isolated/release_asset_server.dart'; +import 'package:flutter_tools/src/isolated/web_asset_server.dart'; +import 'package:flutter_tools/src/web/compile.dart'; +import 'package:flutter_tools/src/web/devfs_config.dart'; import 'package:flutter_tools/src/web/web_constants.dart'; import 'package:shelf/shelf.dart'; @@ -237,7 +243,7 @@ void main() { expect(response.statusCode, HttpStatus.ok); final Map headers = response.headers; - for (final MapEntry entry in kMultiThreadedHeaders.entries) { + for (final MapEntry entry in kCrossOriginIsolationHeaders.entries) { expect(headers, containsPair(entry.key, entry.value)); } }, @@ -267,4 +273,120 @@ void main() { expect(headers.containsKey('Cross-Origin-Embedder-Policy'), false); }, ); + + group('WebAssetServer', () { + testWithoutContext('serves with COOP/COEP headers when crossOriginIsolation is true', () async { + final WebAssetServer server = await WebAssetServer.start( + null, + null, + false, + false, + false, + BuildInfo.debug, + false, + const DartDevelopmentServiceConfiguration(enable: false), + Uri.base, + null, + crossOriginIsolation: true, + webDevServerConfig: const WebDevServerConfig(host: 'localhost'), + webRenderer: WebRendererMode.canvaskit, + isWasm: false, + useLocalCanvasKit: false, + testMode: true, + fileSystem: fileSystem, + logger: BufferLogger.test(), + platform: platform, + ); + + expect(server.defaultResponseHeaders['Cross-Origin-Opener-Policy'], ['same-origin']); + expect(server.defaultResponseHeaders['Cross-Origin-Embedder-Policy'], ['credentialless']); + }); + + testWithoutContext( + 'serves without COOP/COEP headers when crossOriginIsolation is false', + () async { + final WebAssetServer server = await WebAssetServer.start( + null, + null, + false, + false, + false, + BuildInfo.debug, + false, + const DartDevelopmentServiceConfiguration(enable: false), + Uri.base, + null, + crossOriginIsolation: false, + webDevServerConfig: const WebDevServerConfig(host: 'localhost'), + webRenderer: WebRendererMode.canvaskit, + isWasm: false, + useLocalCanvasKit: false, + testMode: true, + fileSystem: fileSystem, + logger: BufferLogger.test(), + platform: platform, + ); + + expect(server.defaultResponseHeaders['Cross-Origin-Opener-Policy'], isNull); + expect(server.defaultResponseHeaders['Cross-Origin-Embedder-Policy'], isNull); + }, + ); + + testWithoutContext('serves with COOP/COEP headers when web renderer is skwasm', () async { + final WebAssetServer server = await WebAssetServer.start( + null, + null, + false, + false, + false, + BuildInfo.debug, + false, + const DartDevelopmentServiceConfiguration(enable: false), + Uri.base, + null, + crossOriginIsolation: true, + webDevServerConfig: const WebDevServerConfig(host: 'localhost'), + webRenderer: WebRendererMode.skwasm, + isWasm: false, + useLocalCanvasKit: false, + testMode: true, + fileSystem: fileSystem, + logger: BufferLogger.test(), + platform: platform, + ); + + expect(server.defaultResponseHeaders['Cross-Origin-Opener-Policy'], ['same-origin']); + expect(server.defaultResponseHeaders['Cross-Origin-Embedder-Policy'], ['credentialless']); + }); + + testWithoutContext( + 'serves without COOP/COEP headers when web renderer is not skwasm', + () async { + final WebAssetServer server = await WebAssetServer.start( + null, + null, + false, + false, + false, + BuildInfo.debug, + false, + const DartDevelopmentServiceConfiguration(enable: false), + Uri.base, + null, + crossOriginIsolation: false, + webDevServerConfig: const WebDevServerConfig(host: 'localhost'), + webRenderer: WebRendererMode.canvaskit, + isWasm: false, + useLocalCanvasKit: false, + testMode: true, + fileSystem: fileSystem, + logger: BufferLogger.test(), + platform: platform, + ); + + expect(server.defaultResponseHeaders['Cross-Origin-Opener-Policy'], isNull); + expect(server.defaultResponseHeaders['Cross-Origin-Embedder-Policy'], isNull); + }, + ); + }); }