diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart index 55ffc907f9..a63f14e9a2 100644 --- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart +++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart @@ -41,6 +41,7 @@ import '../web/chrome.dart'; import '../web/compile.dart'; import '../web/memory_fs.dart'; import '../web/module_metadata.dart'; +import '../web/web_constants.dart'; import '../web_template.dart'; typedef DwdsLauncher = @@ -1315,11 +1316,8 @@ class ReleaseAssetServer { 'Content-Type': mimeType, 'Cross-Origin-Resource-Policy': 'cross-origin', 'Access-Control-Allow-Origin': '*', - if (_needsCoopCoep && - _fileSystem.path.extension(file.path) == '.html') ...{ - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'credentialless', - }, + if (_needsCoopCoep && _fileSystem.path.extension(file.path) == '.html') + ...kMultiThreadedHeaders, }, ); } @@ -1329,10 +1327,7 @@ class ReleaseAssetServer { file.readAsBytesSync(), headers: { 'Content-Type': 'text/html', - if (_needsCoopCoep) ...{ - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'credentialless', - }, + if (_needsCoopCoep) ...kMultiThreadedHeaders, }, ); } 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 4dc403beea..7886003834 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -32,6 +32,7 @@ import '../web/bootstrap.dart'; import '../web/chrome.dart'; import '../web/compile.dart'; import '../web/memory_fs.dart'; +import '../web/web_constants.dart'; import 'test_compiler.dart'; import 'test_golden_comparator.dart'; import 'test_time_recorder.dart'; @@ -57,10 +58,7 @@ shelf.Handler createDirectoryHandler(Directory directory, {required bool crossOr file.openRead(), headers: { if (contentType != null) 'Content-Type': contentType, - if (needsCrossOriginIsolated) ...{ - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'credentialless', - }, + if (needsCrossOriginIsolated) ...kMultiThreadedHeaders, }, ); }; @@ -551,10 +549,7 @@ class FlutterWebPlatform extends PlatformPlugin { ''', headers: { 'Content-Type': 'text/html', - if (webRenderer == WebRendererMode.skwasm) ...{ - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'credentialless', - }, + if (webRenderer == WebRendererMode.skwasm) ...kMultiThreadedHeaders, }, ); } diff --git a/packages/flutter_tools/lib/src/web/web_constants.dart b/packages/flutter_tools/lib/src/web/web_constants.dart index 770d3df1ae..f87742e7e7 100644 --- a/packages/flutter_tools/lib/src/web/web_constants.dart +++ b/packages/flutter_tools/lib/src/web/web_constants.dart @@ -3,3 +3,12 @@ // found in the LICENSE file. const String kWasmMoreInfo = 'See https://flutter.dev/to/wasm for more information.'; + +/// Headers required to run Wasm-compiled applications with multi-threading. +/// +/// See https://developer.chrome.com/blog/coep-credentialless-origin-trial +/// for more information. +const Map kMultiThreadedHeaders = { + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'credentialless', +}; 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 14c420d1ed..0d9c658aed 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 @@ -7,6 +7,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/isolated/devfs_web.dart'; +import 'package:flutter_tools/src/web/web_constants.dart'; import 'package:shelf/shelf.dart'; import '../../src/common.dart'; @@ -236,8 +237,9 @@ void main() { expect(response.statusCode, HttpStatus.ok); final Map headers = response.headers; - expect(headers['Cross-Origin-Opener-Policy'], 'same-origin'); - expect(headers['Cross-Origin-Embedder-Policy'], 'credentialless'); + for (final MapEntry entry in kMultiThreadedHeaders.entries) { + expect(headers, containsPair(entry.key, entry.value)); + } }, );