diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index 4073b82b0c0f2..431a81f304808 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import 'dom.dart'; +import 'safe_browser_api.dart'; // iOS 15 launched WebGL 2.0, but there's something broken about it, which // leads to apps failing to load. For now, we're forcing WebGL 1 on iOS. @@ -268,4 +269,5 @@ int _detectWebGLVersion() { } /// Whether the current browser supports the Chromium variant of CanvasKit. -final bool browserSupportsCanvaskitChromium = domIntl.v8BreakIterator != null; +bool get browserSupportsCanvaskitChromium => + browserSupportsImageDecoder && domIntl.v8BreakIterator != null; diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index dbd01511304d6..00c8662ed6422 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -29,6 +29,7 @@ import 'renderer.dart'; late CanvasKit canvasKit; // TODO(mdebbar): Turn this on when CanvasKit Chromium is ready. +// Set it to `browserSupportsCanvasKitChromium`. // https://github.com/flutter/flutter/issues/122329 const bool _enableCanvasKitChromiumInAutoMode = false; diff --git a/lib/web_ui/lib/src/engine/safe_browser_api.dart b/lib/web_ui/lib/src/engine/safe_browser_api.dart index 38c7f52c3db94..c908bf514575b 100644 --- a/lib/web_ui/lib/src/engine/safe_browser_api.dart +++ b/lib/web_ui/lib/src/engine/safe_browser_api.dart @@ -217,17 +217,27 @@ const bool _browserImageDecodingEnabled = bool.fromEnvironment( ); /// Whether the current browser supports `ImageDecoder`. -bool browserSupportsImageDecoder = - _browserImageDecodingEnabled && - _imageDecoderConstructor != null && - browserEngine == BrowserEngine.blink; +bool browserSupportsImageDecoder = _defaultBrowserSupportsImageDecoder; /// Sets the value of [browserSupportsImageDecoder] to its default value. void debugResetBrowserSupportsImageDecoder() { - browserSupportsImageDecoder = - _imageDecoderConstructor != null; + browserSupportsImageDecoder = _defaultBrowserSupportsImageDecoder; } +bool get _defaultBrowserSupportsImageDecoder => + _browserImageDecodingEnabled && + _imageDecoderConstructor != null && + _isBrowserImageDecoderStable; + +// TODO(yjbanov): https://github.com/flutter/flutter/issues/122761 +// Frequently, when a browser launches an API that other browsers already +// support, there are subtle incompatibilities that may cause apps to crash if, +// we blindly adopt the new implementation. This variable prevents us from +// picking up potentially incompatible implementations of ImagdeDecoder API. +// Instead, when a new browser engine launches the API, we'll evaluate it and +// enable it explicitly. +bool get _isBrowserImageDecoderStable => browserEngine == BrowserEngine.blink; + /// The signature of the function passed to the constructor of JavaScript `Promise`. typedef JsPromiseCallback = void Function(void Function(Object? value) resolve, void Function(Object? error) reject); diff --git a/lib/web_ui/test/browser_detect_test.dart b/lib/web_ui/test/browser_detect_test.dart index ae291d0cc5bf7..ff3af2fe233dc 100644 --- a/lib/web_ui/test/browser_detect_test.dart +++ b/lib/web_ui/test/browser_detect_test.dart @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:js/js.dart'; + import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/safe_browser_api.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -152,4 +155,50 @@ void testMain() { ); }); }); + + group('browserSupportsCanvasKitChromium', () { + late dynamic oldV8BreakIterator = v8BreakIterator; + setUp(() { + oldV8BreakIterator = v8BreakIterator; + }); + tearDown(() { + v8BreakIterator = oldV8BreakIterator; + debugResetBrowserSupportsImageDecoder(); + }); + + test('Detect browsers that support CanvasKit Chromium', () { + v8BreakIterator = Object(); // Any non-null value. + browserSupportsImageDecoder = true; + + expect(browserSupportsCanvaskitChromium, isTrue); + }); + + test('Detect browsers that do not support image codecs', () { + v8BreakIterator = Object(); // Any non-null value. + browserSupportsImageDecoder = false; + + expect(browserSupportsCanvaskitChromium, isFalse); + }); + + test('Detect browsers that do not support v8BreakIterator', () { + v8BreakIterator = null; + browserSupportsImageDecoder = true; + + expect(browserSupportsCanvaskitChromium, isFalse); + }); + + test('Detect browsers that support neither', () { + v8BreakIterator = null; + browserSupportsImageDecoder = false; + + expect(browserSupportsCanvaskitChromium, isFalse); + }); + }); } + + +@JS('window.Intl.v8BreakIterator') +external dynamic get v8BreakIterator; + +@JS('window.Intl.v8BreakIterator') +external set v8BreakIterator(dynamic x); diff --git a/third_party/canvaskit/BUILD.gn b/third_party/canvaskit/BUILD.gn index 188bfdff04692..fe7032bfd5b77 100644 --- a/third_party/canvaskit/BUILD.gn +++ b/third_party/canvaskit/BUILD.gn @@ -7,8 +7,14 @@ import("//build/toolchain/wasm.gni") # This toolchain is only to be used by the canvaskit target below. wasm_toolchain("canvaskit") { extra_toolchain_args = { + # Include ICU data. skia_use_icu = true skia_use_client_icu = false + + # Include image codecs. + skia_use_libjpeg_turbo_decode = true + skia_use_libpng_decode = true + skia_use_libwebp_decode = true } } @@ -20,9 +26,16 @@ group("canvaskit_group") { # This toolchain is only to be used by canvaskit_chromium_group below. wasm_toolchain("canvaskit_chromium") { extra_toolchain_args = { + # In Chromium browsers, we can use the browser's APIs to get the necessary + # ICU data. skia_use_icu = false skia_use_client_icu = true skia_icu_bidi_third_party_dir = "//flutter/third_party/canvaskit/icu_bidi" + + # In Chromium browsers, we can use the browser's built-in codecs. + skia_use_libjpeg_turbo_decode = false + skia_use_libpng_decode = false + skia_use_libwebp_decode = false } } diff --git a/tools/gn b/tools/gn index b2cadaf487e57..f3dabdec3ac5d 100755 --- a/tools/gn +++ b/tools/gn @@ -617,11 +617,10 @@ def to_gn_wasm_args(args, gn_args): gn_args['skia_use_vulkan'] = False gn_args['skia_use_webgpu'] = False gn_args['skia_use_libheif'] = False - gn_args['skia_use_libjpeg_turbo_decode'] = True gn_args['skia_use_libjpeg_turbo_encode'] = False - gn_args['skia_use_libpng_decode'] = True + # TODO(yjbanov): https://github.com/flutter/flutter/issues/122759 + # Remove this and implement it through Canvas2d. gn_args['skia_use_libpng_encode'] = True - gn_args['skia_use_libwebp_decode'] = True gn_args['skia_use_libwebp_encode'] = False gn_args['skia_use_lua'] = False gn_args['skia_use_wuffs'] = True