diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a1989101ea127..ffcb36dbe7f3f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1949,7 +1949,6 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart + ../.. ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/initialization.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_loader.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_promise.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/key_map.g.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart + ../../../flutter/LICENSE @@ -2002,8 +2001,8 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph. ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/js_functions.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart + ../../../flutter/LICENSE @@ -2011,10 +2010,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_pa ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart + ../../../flutter/LICENSE @@ -2064,15 +2060,12 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart + .. ORIGIN: ../../../flutter/lib/web_ui/lib/window.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/canvas.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/skwasm/data.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/export.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/skwasm/fonts.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/helpers.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/paint.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/path.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/picture.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/shaders.cpp + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/web_ui/skwasm/string.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/surface.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/wrappers.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/runtime/dart_isolate.cc + ../../../flutter/LICENSE @@ -4552,7 +4545,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/initialization.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_loader.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_promise.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/key_map.g.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart @@ -4605,8 +4597,8 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.da FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/js_functions.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart @@ -4614,10 +4606,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart @@ -4667,15 +4656,12 @@ FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart FILE: ../../../flutter/lib/web_ui/lib/window.dart FILE: ../../../flutter/lib/web_ui/skwasm/canvas.cpp FILE: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp -FILE: ../../../flutter/lib/web_ui/skwasm/data.cpp FILE: ../../../flutter/lib/web_ui/skwasm/export.h -FILE: ../../../flutter/lib/web_ui/skwasm/fonts.cpp FILE: ../../../flutter/lib/web_ui/skwasm/helpers.h FILE: ../../../flutter/lib/web_ui/skwasm/paint.cpp FILE: ../../../flutter/lib/web_ui/skwasm/path.cpp FILE: ../../../flutter/lib/web_ui/skwasm/picture.cpp FILE: ../../../flutter/lib/web_ui/skwasm/shaders.cpp -FILE: ../../../flutter/lib/web_ui/skwasm/string.cpp FILE: ../../../flutter/lib/web_ui/skwasm/surface.cpp FILE: ../../../flutter/lib/web_ui/skwasm/wrappers.h FILE: ../../../flutter/runtime/dart_isolate.cc diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index a6c832c188754..6c152e9512ad1 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -108,7 +108,6 @@ export 'engine/html_image_codec.dart'; export 'engine/initialization.dart'; export 'engine/js_interop/js_loader.dart'; export 'engine/js_interop/js_promise.dart'; -export 'engine/js_interop/js_typed_data.dart'; export 'engine/key_map.g.dart'; export 'engine/keyboard_binding.dart'; export 'engine/mouse_cursor.dart'; diff --git a/lib/web_ui/lib/src/engine/assets.dart b/lib/web_ui/lib/src/engine/assets.dart index 585f9d4f39dad..b200f824e4145 100644 --- a/lib/web_ui/lib/src/engine/assets.dart +++ b/lib/web_ui/lib/src/engine/assets.dart @@ -8,6 +8,20 @@ import 'dart:typed_data'; import 'dom.dart'; import 'util.dart'; +const String ahemFontFamily = 'Ahem'; +const String ahemFontUrl = '/assets/fonts/ahem.ttf'; +const String robotoFontFamily = 'Roboto'; +const String robotoTestFontUrl = '/assets/fonts/Roboto-Regular.ttf'; + +/// The list of test fonts, in the form of font family name - font file url pairs. +/// This list does not include embedded test fonts, which need to be loaded and +/// registered separately in [FontCollection.debugDownloadTestFonts]. +const Map testFontUrls = { + ahemFontFamily: ahemFontUrl, + robotoFontFamily: robotoTestFontUrl, + 'RobotoVariable': '/assets/fonts/RobotoSlab-VariableFont_wght.ttf', +}; + /// This class downloads assets over the network. /// /// Assets are resolved relative to [assetsDir] inside the absolute base @@ -96,3 +110,79 @@ class AssetManager { return (await response.payload.asByteBuffer()).asByteData(); } } + +/// An asset manager that gives fake empty responses for assets. +class WebOnlyMockAssetManager extends AssetManager { + /// Mock asset directory relative to base url. + String defaultAssetsDir = ''; + + /// Mock empty asset manifest. + String defaultAssetManifest = '{}'; + + /// Mock font manifest overridable for unit testing. + String defaultFontManifest = ''' + [ + { + "family":"$robotoFontFamily", + "fonts":[{"asset":"$robotoTestFontUrl"}] + }, + { + "family":"$ahemFontFamily", + "fonts":[{"asset":"$ahemFontUrl"}] + } + ]'''; + + @override + String get assetsDir => defaultAssetsDir; + + @override + String getAssetUrl(String asset) => asset; + + @override + Future loadAsset(String asset) async { + if (asset == getAssetUrl('AssetManifest.json')) { + return MockHttpFetchResponse( + url: asset, + status: 200, + payload: MockHttpFetchPayload( + byteBuffer: _toByteData(utf8.encode(defaultAssetManifest)).buffer, + ), + ); + } + if (asset == getAssetUrl('FontManifest.json')) { + return MockHttpFetchResponse( + url: asset, + status: 200, + payload: MockHttpFetchPayload( + byteBuffer: _toByteData(utf8.encode(defaultFontManifest)).buffer, + ), + ); + } + + return MockHttpFetchResponse( + url: asset, + status: 404, + ); + } + + @override + Future load(String asset) { + if (asset == getAssetUrl('AssetManifest.json')) { + return Future.value( + _toByteData(utf8.encode(defaultAssetManifest))); + } + if (asset == getAssetUrl('FontManifest.json')) { + return Future.value( + _toByteData(utf8.encode(defaultFontManifest))); + } + throw HttpFetchNoPayloadError(asset, status: 404); + } + + ByteData _toByteData(List bytes) { + final ByteData byteData = ByteData(bytes.length); + for (int i = 0; i < bytes.length; i++) { + byteData.setUint8(i, bytes[i]); + } + return byteData; + } +} diff --git a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart index 63827fcaeb1b6..66deb4940bd40 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart @@ -456,7 +456,7 @@ class FallbackFontDownloadQueue { final Uint8List bytes = downloadedData[url]!; FontFallbackData.instance.registerFallbackFont(font.name, bytes); if (pendingFonts.isEmpty) { - (renderer.fontCollection as SkiaFontCollection).registerDownloadedFonts(); + renderer.fontCollection.registerDownloadedFonts(); sendFontChangeMessage(); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart index 5f4fbabb0a723..86c5ab5e2edf7 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart @@ -3,9 +3,17 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; import 'dart:typed_data'; -import 'package:ui/src/engine.dart'; +import 'package:web_test_fonts/web_test_fonts.dart'; + +import '../assets.dart'; +import '../dom.dart'; +import '../fonts.dart'; +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'font_fallbacks.dart'; // This URL was found by using the Google Fonts Developer API to find the URL // for Roboto. The API warns that this URL is not stable. In order to update @@ -69,18 +77,15 @@ class SkiaFontCollection implements FlutterFontCollection { } @override - Future loadFontFromList(Uint8List list, {String? fontFamily}) async { + Future loadFontFromList(Uint8List list, {String? fontFamily}) async { if (fontFamily == null) { fontFamily = _readActualFamilyName(list); if (fontFamily == null) { printWarning('Failed to read font family name. Aborting font load.'); - return false; + return; } } - // Make sure CanvasKit is actually loaded - await renderer.initialize(); - final SkTypeface? typeface = canvasKit.Typeface.MakeFreeTypeFaceFromData(list.buffer); if (typeface != null) { @@ -88,65 +93,53 @@ class SkiaFontCollection implements FlutterFontCollection { _registerWithFontProvider(); } else { printWarning('Failed to parse font family "$fontFamily"'); - return false; + return; } - return true; } /// Loads fonts from `FontManifest.json`. @override - Future loadAssetFonts(FontManifest manifest) async { - final List> pendingDownloads = >[]; - bool loadedRoboto = false; - for (final FontFamily family in manifest.families) { - if (family.name == 'Roboto') { - loadedRoboto = true; - } - for (final FontAsset fontAsset in family.fontAssets) { - final String url = assetManager.getAssetUrl(fontAsset.asset); - pendingDownloads.add(_downloadFont(fontAsset.asset, url, family.name)); + Future downloadAssetFonts(AssetManager assetManager) async { + final HttpFetchResponse response = await assetManager.loadAsset('FontManifest.json'); + + if (!response.hasPayload) { + printWarning('Font manifest does not exist at `${response.url}` - ignoring.'); + return; + } + + final Uint8List data = await response.asUint8List(); + final List? fontManifest = json.decode(utf8.decode(data)) as List?; + if (fontManifest == null) { + throw AssertionError( + 'There was a problem trying to load FontManifest.json'); + } + + final List> pendingFonts = >[]; + + for (final Map fontFamily + in fontManifest.cast>()) { + final String family = fontFamily.readString('family'); + final List fontAssets = fontFamily.readList('fonts'); + for (final dynamic fontAssetItem in fontAssets) { + final Map fontAsset = fontAssetItem as Map; + final String asset = fontAsset.readString('asset'); + _downloadFont(pendingFonts, assetManager.getAssetUrl(asset), family); } } /// We need a default fallback font for CanvasKit, in order to /// avoid crashing while laying out text with an unregistered font. We chose /// Roboto to match Android. - if (!loadedRoboto) { + if (!_isFontFamilyDownloaded('Roboto')) { // Download Roboto and add it to the font buffers. - pendingDownloads.add(_downloadFont('Roboto', _robotoUrl, 'Roboto')); + _downloadFont(pendingFonts, _robotoUrl, 'Roboto'); } - final Map fontFailures = {}; - final List<(String, UnregisteredFont)> downloadedFonts = <(String, UnregisteredFont)>[]; - for (final FontDownloadResult result in await Future.wait(pendingDownloads)) { - if (result.font != null) { - downloadedFonts.add((result.assetName, result.font!)); - } else { - fontFailures[result.assetName] = result.error!; - } - } - - // Make sure CanvasKit is actually loaded - await renderer.initialize(); - - final List loadedFonts = []; - for (final (String assetName, UnregisteredFont unregisteredFont) in downloadedFonts) { - final Uint8List bytes = unregisteredFont.bytes.asUint8List(); - final SkTypeface? typeface = - canvasKit.Typeface.MakeFreeTypeFaceFromData(bytes.buffer); - if (typeface != null) { - loadedFonts.add(assetName); - _registeredFonts.add(RegisteredFont(bytes, unregisteredFont.family, typeface)); - } else { - printWarning('Failed to load font ${unregisteredFont.family} at ${unregisteredFont.url}'); - printWarning('Verify that ${unregisteredFont.url} contains a valid font.'); - fontFailures[assetName] = FontInvalidDataError(unregisteredFont.url); - } - } - registerDownloadedFonts(); - return AssetFontsResult(loadedFonts, fontFailures); + final List completedPendingFonts = await Future.wait(pendingFonts); + _unregisteredFonts.addAll(completedPendingFonts.whereType()); } + @override void registerDownloadedFonts() { RegisteredFont? makeRegisterFont(ByteBuffer buffer, String url, String family) { final Uint8List bytes = buffer.asUint8List(); @@ -176,30 +169,61 @@ class SkiaFontCollection implements FlutterFontCollection { _registerWithFontProvider(); } - Future _downloadFont( - String assetName, - String url, - String fontFamily - ) async { - final ByteBuffer fontData; - - // Try to get the font leniently. Do not crash the app when failing to - // fetch the font in the spirit of "gradual degradation of functionality". - try { - final HttpFetchResponse response = await httpFetch(url); - if (!response.hasPayload) { - printWarning('Font family $fontFamily not found (404) at $url'); - return FontDownloadResult.fromError(assetName, FontNotFoundError(url)); + /// Whether the [fontFamily] was registered and/or loaded. + bool _isFontFamilyDownloaded(String fontFamily) { + return _downloadedFontFamilies.contains(fontFamily); + } + + /// Loads the Ahem font, unless it's already been loaded using + /// `FontManifest.json` (see [downloadAssetFonts]). + /// + /// `FontManifest.json` has higher priority than the default test font URLs. + /// This allows customizing test environments where fonts are loaded from + /// different URLs. + @override + Future debugDownloadTestFonts() async { + final List> pendingFonts = >[]; + for (final MapEntry fontEntry in testFontUrls.entries) { + if (!_isFontFamilyDownloaded(fontEntry.key)) { + _downloadFont(pendingFonts, fontEntry.value, fontEntry.key); } + } + final List completedPendingFonts = await Future.wait(pendingFonts); + final List fonts = [ + UnregisteredFont( + EmbeddedTestFont.flutterTest.data.buffer, + '', + EmbeddedTestFont.flutterTest.fontFamily, + ), + ...completedPendingFonts.whereType(), + ]; + _unregisteredFonts.addAll(fonts); + + // Ahem must be added to font fallbacks list regardless of where it was + // downloaded from. + FontFallbackData.instance.globalFontFallbacks.add(ahemFontFamily); + } - fontData = await response.asByteBuffer(); - } catch (e) { - printWarning('Failed to load font $fontFamily at $url'); - printWarning(e.toString()); - return FontDownloadResult.fromError(assetName, FontDownloadError(url, e)); + void _downloadFont( + List> waitUnregisteredFonts, + String url, + String family + ) { + Future downloadFont() async { + // Try to get the font leniently. Do not crash the app when failing to + // fetch the font in the spirit of "gradual degradation of functionality". + try { + final ByteBuffer data = await httpFetchByteBuffer(url); + return UnregisteredFont(data, url, family); + } catch (e) { + printWarning('Failed to load font $family at $url'); + printWarning(e.toString()); + return null; + } } - _downloadedFontFamilies.add(fontFamily); - return FontDownloadResult.fromFont(assetName, UnregisteredFont(fontData, url, fontFamily)); + + _downloadedFontFamilies.add(family); + waitUnregisteredFonts.add(downloadFont()); } @@ -245,12 +269,3 @@ class UnregisteredFont { final String url; final String family; } - -class FontDownloadResult { - FontDownloadResult.fromFont(this.assetName, UnregisteredFont this.font) : error = null; - FontDownloadResult.fromError(this.assetName, FontLoadError this.error) : font = null; - - final String assetName; - final UnregisteredFont? font; - final FontLoadError? error; -} diff --git a/lib/web_ui/lib/src/engine/canvaskit/image.dart b/lib/web_ui/lib/src/engine/canvaskit/image.dart index d100f5a83c5c1..f527294bb4f99 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -3,12 +3,23 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:js_interop'; import 'dart:typed_data'; -import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../dom.dart'; +import '../html_image_codec.dart'; +import '../safe_browser_api.dart'; +import '../util.dart'; +import 'canvas.dart'; +import 'canvaskit_api.dart'; +import 'image_wasm_codecs.dart'; +import 'image_web_codecs.dart'; +import 'native_memory.dart'; +import 'painting.dart'; +import 'picture.dart'; +import 'picture_recorder.dart'; + /// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia. FutureOr skiaInstantiateImageCodec(Uint8List list, [int? targetWidth, int? targetHeight]) { @@ -203,16 +214,16 @@ Future fetchImage(String url, WebOnlyImageCodecChunkCallback? chunkCa /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API Future readChunked(HttpFetchPayload payload, int contentLength, WebOnlyImageCodecChunkCallback chunkCallback) async { - final JSUint8Array1 result = createUint8ArrayFromLength(contentLength); + final Uint8List result = Uint8List(contentLength); int position = 0; int cumulativeBytesLoaded = 0; - await payload.read((JSUint8Array1 chunk) { - cumulativeBytesLoaded += chunk.length.toDart.toInt(); + await payload.read((Uint8List chunk) { + cumulativeBytesLoaded += chunk.lengthInBytes; chunkCallback(cumulativeBytesLoaded, contentLength); - result.set(chunk, position.toJS); - position += chunk.length.toDart.toInt(); + result.setAll(position, chunk); + position += chunk.lengthInBytes; }); - return (result as JSUint8Array).toDart; + return result; } /// A [ui.Image] backed by an `SkImage` from Skia. diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart b/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart index bf96471ff1f8a..604ac004c8980 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart @@ -15,9 +15,15 @@ import 'dart:math' as math; import 'dart:typed_data'; import 'package:meta/meta.dart'; -import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../alarm_clock.dart'; +import '../dom.dart'; +import '../safe_browser_api.dart'; +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'image.dart'; + Duration _kDefaultWebDecoderExpireDuration = const Duration(seconds: 3); Duration _kWebDecoderExpireDuration = _kDefaultWebDecoderExpireDuration; @@ -433,18 +439,24 @@ bool _shouldReadPixelsUnmodified(VideoFrame videoFrame, ui.ImageByteFormat forma return format == ui.ImageByteFormat.rawRgba && isRgbFrame; } +@JS('Uint8Array') +@staticInterop +class _JSUint8Array { + external factory _JSUint8Array(JSNumber length); +} + Future readVideoFramePixelsUnmodified(VideoFrame videoFrame) async { final int size = videoFrame.allocationSize().toInt(); // In dart2wasm, Uint8List is not the same as a JS Uint8Array. So we // explicitly construct the JS object here. - final JSUint8Array1 destination = createUint8ArrayFromLength(size); + final JSUint8Array destination = _JSUint8Array(size.toJS) as JSUint8Array; final JsPromise copyPromise = videoFrame.copyTo(destination); await promiseToFuture(copyPromise); // In dart2wasm, `toDart` incurs a copy here. On JS backends, this is a // no-op. - return (destination as JSUint8Array).toDart.buffer; + return destination.toDart.buffer; } Future encodeVideoFrameAsPng(VideoFrame videoFrame) async { diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index cec642f014188..06db1bc9a2a61 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -48,8 +48,6 @@ class CanvasKitRenderer implements Renderer { static CanvasKitRenderer get instance => _instance; static late CanvasKitRenderer _instance; - Future? _initialized; - @override String get rendererTag => 'canvaskit'; @@ -68,16 +66,14 @@ class CanvasKitRenderer implements Renderer { @override Future initialize() async { - _initialized ??= () async { - if (windowFlutterCanvasKit != null) { - canvasKit = windowFlutterCanvasKit!; - } else { - canvasKit = await downloadCanvasKit(); - windowFlutterCanvasKit = canvasKit; - } - _instance = this; - }(); - return _initialized; + if (windowFlutterCanvasKit != null) { + canvasKit = windowFlutterCanvasKit!; + } else { + canvasKit = await downloadCanvasKit(); + windowFlutterCanvasKit = canvasKit; + } + + _instance = this; } @override diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index f427cb7d43880..ca7056d05672b 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -1397,7 +1397,7 @@ class DomXMLHttpRequestEventTarget extends DomEventTarget {} Future<_DomResponse> _rawHttpGet(String url) => js_util.promiseToFuture<_DomResponse>(domWindow._fetch1(url.toJS)); -typedef MockHttpFetchResponseFactory = Future Function( +typedef MockHttpFetchResponseFactory = Future Function( String url); MockHttpFetchResponseFactory? mockHttpFetchResponseFactory; @@ -1417,10 +1417,7 @@ MockHttpFetchResponseFactory? mockHttpFetchResponseFactory; /// [httpFetchText] instead. Future httpFetch(String url) async { if (mockHttpFetchResponseFactory != null) { - final MockHttpFetchResponse? response = await mockHttpFetchResponseFactory!(url); - if (response != null) { - return response; - } + return mockHttpFetchResponseFactory!(url); } try { final _DomResponse domResponse = await _rawHttpGet(url); @@ -1659,36 +1656,31 @@ typedef MockOnRead = Future Function(HttpFetchReader callback); class MockHttpFetchPayload implements HttpFetchPayload { MockHttpFetchPayload({ - required ByteBuffer byteBuffer, - int? chunkSize, + ByteBuffer? byteBuffer, + Object? json, + String? text, + MockOnRead? onRead, }) : _byteBuffer = byteBuffer, - _chunkSize = chunkSize ?? 64; + _json = json, + _text = text, + _onRead = onRead; - final ByteBuffer _byteBuffer; - final int _chunkSize; + final ByteBuffer? _byteBuffer; + final Object? _json; + final String? _text; + final MockOnRead? _onRead; @override - Future read(HttpFetchReader callback) async { - final int totalLength = _byteBuffer.lengthInBytes; - int currentIndex = 0; - while (currentIndex < totalLength) { - final int chunkSize = math.min(_chunkSize, totalLength - currentIndex); - final Uint8List chunk = Uint8List.sublistView( - _byteBuffer.asByteData(), currentIndex, currentIndex + chunkSize - ); - callback(chunk.toJS as T); - currentIndex += chunkSize; - } - } + Future read(HttpFetchReader callback) => _onRead!(callback); @override - Future asByteBuffer() async => _byteBuffer; + Future asByteBuffer() async => _byteBuffer!; @override - Future json() async => throw AssertionError('json not supported by mock'); + Future json() async => _json!; @override - Future text() async => throw AssertionError('text not supported by mock'); + Future text() async => _text!; } /// Indicates a missing HTTP payload when one was expected, such as when @@ -1802,7 +1794,9 @@ extension _DomStreamReaderExtension on _DomStreamReader { class _DomStreamChunk {} extension _DomStreamChunkExtension on _DomStreamChunk { - external JSAny? get value; + @JS('value') + external JSAny? get _value; + Object? get value => _value?.toObjectShallow; @JS('done') external JSBoolean get _done; @@ -1924,10 +1918,6 @@ extension DomFontFaceExtension on DomFontFace { @JS('weight') external JSString? get _weight; String? get weight => _weight?.toDart; - - @JS('status') - external JSString? get _status; - String? get status => _status?.toDart; } @JS() diff --git a/lib/web_ui/lib/src/engine/fonts.dart b/lib/web_ui/lib/src/engine/fonts.dart index 41dac23c68470..cf3b60f6e9319 100644 --- a/lib/web_ui/lib/src/engine/fonts.dart +++ b/lib/web_ui/lib/src/engine/fonts.dart @@ -3,126 +3,32 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; -import 'dart:js_interop'; import 'dart:typed_data'; -import 'package:ui/src/engine.dart'; - -class FontAsset { - FontAsset(this.asset, this.descriptors); - - final String asset; - final Map descriptors; -} - -class FontFamily { - FontFamily(this.name, this.fontAssets); - - final String name; - final List fontAssets; -} - -class FontManifest { - FontManifest(this.families); - - final List families; -} - -Future fetchFontManifest(AssetManager assetManager) async { - final HttpFetchResponse response = await assetManager.loadAsset('FontManifest.json'); - if (!response.hasPayload) { - printWarning('Font manifest does not exist at `${response.url}` - ignoring.'); - return FontManifest([]); - } - - final Converter, Object?> decoder = const Utf8Decoder().fuse(const JsonDecoder()); - Object? fontManifestJson; - final Sink> inputSink = decoder.startChunkedConversion( - ChunkedConversionSink.withCallback( - (List accumulated) { - if (accumulated.length != 1) { - throw AssertionError('There was a problem trying to load FontManifest.json'); - } - fontManifestJson = accumulated.first; - } - )); - await response.read((JSUint8Array chunk) => inputSink.add(chunk.toDart)); - inputSink.close(); - if (fontManifestJson == null) { - throw AssertionError('There was a problem trying to load FontManifest.json'); - } - final List families = (fontManifestJson! as List).map( - (dynamic fontFamilyJson) { - final Map fontFamily = fontFamilyJson as Map; - final String familyName = fontFamily.readString('family'); - final List fontAssets = fontFamily.readList('fonts'); - return FontFamily(familyName, fontAssets.map((dynamic fontAssetJson) { - String? asset; - final Map descriptors = {}; - for (final MapEntry descriptor in (fontAssetJson as Map).entries) { - if (descriptor.key == 'asset') { - asset = descriptor.value as String; - } else { - descriptors[descriptor.key] = descriptor.value as String; - } - } - if (asset == null) { - throw AssertionError("Invalid Font manifest, missing 'asset' key on font."); - } - return FontAsset(asset, descriptors); - }).toList()); - }).toList(); - return FontManifest(families); -} - -abstract class FontLoadError extends Error { - FontLoadError(this.url); - - String url; - String get message; -} - -class FontNotFoundError extends FontLoadError { - FontNotFoundError(super.url); - - @override - String get message => 'Font asset not found at url $url.'; -} - -class FontDownloadError extends FontLoadError { - FontDownloadError(super.url, this.error); - - dynamic error; - - @override - String get message => 'Failed to download font asset at url $url with error: $error.'; -} - -class FontInvalidDataError extends FontLoadError { - FontInvalidDataError(super.url); - - @override - String get message => 'Invalid data for font asset at url $url.'; -} - -class AssetFontsResult { - AssetFontsResult(this.loadedFonts, this.fontFailures); - - /// A list of asset keys for fonts that were successfully loaded. - final List loadedFonts; - - /// A map of the asset keys to failures for fonts that failed to load. - final Map fontFailures; -} +import 'assets.dart'; abstract class FlutterFontCollection { - /// Loads a font directly from font data. - Future loadFontFromList(Uint8List list, {String? fontFamily}); - - /// Completes when fonts from FontManifest.json have been loaded. - Future loadAssetFonts(FontManifest manifest); - // Unregisters all fonts. + /// Fonts loaded with [loadFontFromList] do not need to be registered + /// with [registerDownloadedFonts]. Fonts are both downloaded and registered + /// with [loadFontFromList] calls. + Future loadFontFromList(Uint8List list, {String? fontFamily}); + + /// Completes when fonts from FontManifest.json have been downloaded. + Future downloadAssetFonts(AssetManager assetManager); + + /// Registers both downloaded fonts and fallback fonts with the TypefaceFontProvider. + /// + /// Downloading of fonts happens separately from registering of fonts so that + /// the download step can happen concurrently with the initalization of the renderer. + /// + /// The correct order of calls to register downloaded fonts: + /// 1) [downloadAssetFonts] + /// 2) [registerDownloadedFonts] + /// + /// For fallbackFonts, call registerFallbackFont (see font_fallbacks.dart) + /// for each fallback font before calling [registerDownloadedFonts] + void registerDownloadedFonts(); + FutureOr debugDownloadTestFonts(); void clear(); } diff --git a/lib/web_ui/lib/src/engine/initialization.dart b/lib/web_ui/lib/src/engine/initialization.dart index 18cb6597f90d5..f55255c092e92 100644 --- a/lib/web_ui/lib/src/engine/initialization.dart +++ b/lib/web_ui/lib/src/engine/initialization.dart @@ -8,7 +8,6 @@ import 'dart:js_interop'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import 'package:web_test_fonts/web_test_fonts.dart'; /// The mode the app is running in. /// Keep these in sync with the same constants on the framework-side under foundation/constants.dart. @@ -202,6 +201,7 @@ Future initializeEngineServices({ Future initializeRendererCallback () async => renderer.initialize(); await Future.wait(>[initializeRendererCallback(), _downloadAssetFonts()]); + renderer.fontCollection.registerDownloadedFonts(); _initializationState = DebugEngineInitializationState.initializedServices; } @@ -248,17 +248,12 @@ void _setAssetManager(AssetManager assetManager) { Future _downloadAssetFonts() async { renderer.fontCollection.clear(); - if (ui.debugEmulateFlutterTesterEnvironment) { - // Load the embedded test font before loading fonts from the assets so that - // the embedded test font is the default (first) font. - await renderer.fontCollection.loadFontFromList( - EmbeddedTestFont.flutterTest.data, - fontFamily: EmbeddedTestFont.flutterTest.fontFamily - ); + if (_assetManager != null) { + await renderer.fontCollection.downloadAssetFonts(_assetManager!); } - if (_assetManager != null) { - await renderer.fontCollection.loadAssetFonts(await fetchFontManifest(assetManager)); + if (ui.debugEmulateFlutterTesterEnvironment) { + await renderer.fontCollection.debugDownloadTestFonts(); } } diff --git a/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart b/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart deleted file mode 100644 index a6bb9e2041485..0000000000000 --- a/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:js_interop'; - -@JS() -@staticInterop -class ArrayBuffer {} - -@JS() -@staticInterop -class TypedArray {} - -extension TypedArrayExtension on TypedArray { - external void set(JSUint8Array1 source, JSNumber start); - external JSNumber get length; -} - -// Due to some differences between wasm and JS backends, we can't use the -// JSUint8Array object provided by the dart sdk. So for now, we can define this -// as an opaque JS object. -@JS('Uint8Array') -@staticInterop -class JSUint8Array1 extends TypedArray { - external factory JSUint8Array1._(JSAny bufferOrLength); -} - -JSUint8Array1 createUint8ArrayFromBuffer(ArrayBuffer buffer) => JSUint8Array1._(buffer as JSObject); -JSUint8Array1 createUint8ArrayFromLength(int length) => JSUint8Array1._(length.toJS); diff --git a/lib/web_ui/lib/src/engine/renderer.dart b/lib/web_ui/lib/src/engine/renderer.dart index 5dbaaad432bfb..bdb1d0169b7ce 100644 --- a/lib/web_ui/lib/src/engine/renderer.dart +++ b/lib/web_ui/lib/src/engine/renderer.dart @@ -29,22 +29,21 @@ abstract class Renderer { factory Renderer._internal() { if (FlutterConfiguration.flutterWebUseSkwasm) { return SkwasmRenderer(); - } else { - bool useCanvasKit; - if (FlutterConfiguration.flutterWebAutoDetect) { - if (configuration.requestedRendererType != null) { - useCanvasKit = configuration.requestedRendererType == 'canvaskit'; - } else { - // If requestedRendererType is not specified, use CanvasKit for desktop and - // html for mobile. - useCanvasKit = isDesktop; - } + } + bool useCanvasKit; + if (FlutterConfiguration.flutterWebAutoDetect) { + if (configuration.requestedRendererType != null) { + useCanvasKit = configuration.requestedRendererType == 'canvaskit'; } else { - useCanvasKit = FlutterConfiguration.useSkia; + // If requestedRendererType is not specified, use CanvasKit for desktop and + // html for mobile. + useCanvasKit = isDesktop; } - - return useCanvasKit ? CanvasKitRenderer() : HtmlRenderer(); + } else { + useCanvasKit = FlutterConfiguration.useSkia; } + + return useCanvasKit ? CanvasKitRenderer() : HtmlRenderer(); } String get rendererTag; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart index 13d223e640888..9d489df3ab489 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart @@ -18,8 +18,8 @@ export 'skwasm_impl/paragraph.dart'; export 'skwasm_impl/path.dart'; export 'skwasm_impl/path_metrics.dart'; export 'skwasm_impl/picture.dart'; +export 'skwasm_impl/raw/js_functions.dart'; export 'skwasm_impl/raw/raw_canvas.dart'; -export 'skwasm_impl/raw/raw_fonts.dart'; export 'skwasm_impl/raw/raw_geometry.dart'; export 'skwasm_impl/raw/raw_memory.dart'; export 'skwasm_impl/raw/raw_paint.dart'; @@ -27,10 +27,7 @@ export 'skwasm_impl/raw/raw_path.dart'; export 'skwasm_impl/raw/raw_path_metrics.dart'; export 'skwasm_impl/raw/raw_picture.dart'; export 'skwasm_impl/raw/raw_shaders.dart'; -export 'skwasm_impl/raw/raw_skdata.dart'; -export 'skwasm_impl/raw/raw_skstring.dart'; export 'skwasm_impl/raw/raw_surface.dart'; -export 'skwasm_impl/raw/skwasm_module.dart'; export 'skwasm_impl/renderer.dart'; export 'skwasm_impl/scene_builder.dart'; export 'skwasm_impl/shaders.dart'; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart index f99c01f484e65..0d3a2612988ff 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart @@ -3,116 +3,34 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; -import 'dart:ffi'; -import 'dart:js_interop'; import 'dart:typed_data'; import 'package:ui/src/engine.dart'; -import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; class SkwasmFontCollection implements FlutterFontCollection { - SkwasmFontCollection() : _handle = fontCollectionCreate(); - - FontCollectionHandle _handle; - @override void clear() { - fontCollectionDispose(_handle); - _handle = fontCollectionCreate(); + // TODO(jacksongardner): implement clear } @override - Future loadAssetFonts(FontManifest manifest) async { - final List> fontFutures = >[]; - final List loadedFonts = []; - final Map fontFailures = {}; - - // We can't restore the pointers directly due to a bug in dart2wasm - // https://github.com/dart-lang/sdk/issues/52142 - final List familyHandles = []; - for (final FontFamily family in manifest.families) { - final List rawUtf8Bytes = utf8.encode(family.name); - final SkStringHandle stringHandle = skStringAllocate(rawUtf8Bytes.length); - final Pointer stringDataPointer = skStringGetData(stringHandle); - for (int i = 0; i < rawUtf8Bytes.length; i++) { - stringDataPointer[i] = rawUtf8Bytes[i]; - } - familyHandles.add(stringHandle.address); - for (final FontAsset fontAsset in family.fontAssets) { - fontFutures.add(() async { - final FontLoadError? error = await _downloadFontAsset(fontAsset, stringHandle); - if (error == null) { - loadedFonts.add(fontAsset.asset); - } else { - fontFailures[fontAsset.asset] = error; - } - }()); - } - } - await Future.wait(fontFutures); + FutureOr debugDownloadTestFonts() { + // TODO(jacksongardner): implement debugDownloadTestFonts + } - // Wait until all the downloading and registering is complete before - // freeing the handles to the family name strings. - familyHandles - .map((int address) => SkStringHandle.fromAddress(address)) - .forEach(skStringFree); - return AssetFontsResult(loadedFonts, fontFailures); + @override + Future downloadAssetFonts(AssetManager assetManager) async { + // TODO(jacksongardner): implement downloadAssetFonts } - Future _downloadFontAsset(FontAsset asset, SkStringHandle familyNameHandle) async { - final HttpFetchResponse response; - try { - response = await assetManager.loadAsset(asset.asset); - } catch (error) { - return FontDownloadError(assetManager.getAssetUrl(asset.asset), error); - } - if (!response.hasPayload) { - return FontNotFoundError(assetManager.getAssetUrl(asset.asset)); - } - int length = 0; - final List chunks = []; - await response.read((JSUint8Array1 chunk) { - length += chunk.length.toDart.toInt(); - chunks.add(chunk); - }); - final SkDataHandle fontData = skDataCreate(length); - int dataAddress = skDataGetPointer(fontData).cast().address; - final JSUint8Array1 wasmMemory = createUint8ArrayFromBuffer(skwasmInstance.wasmMemory.buffer); - for (final JSUint8Array1 chunk in chunks) { - wasmMemory.set(chunk, dataAddress.toJS); - dataAddress += chunk.length.toDart.toInt(); - } - final bool result = fontCollectionRegisterFont(_handle, fontData, familyNameHandle); - skDataDispose(fontData); - if (!result) { - return FontInvalidDataError(assetManager.getAssetUrl(asset.asset)); - } - return null; + @override + Future loadFontFromList(Uint8List list, {String? fontFamily}) async { + // TODO(jacksongardner): implement loadFontFromList } @override - Future loadFontFromList(Uint8List list, {String? fontFamily}) async { - final SkDataHandle dataHandle = skDataCreate(list.length); - final Pointer dataPointer = skDataGetPointer(dataHandle).cast(); - for (int i = 0; i < list.length; i++) { - dataPointer[i] = list[i]; - } - bool success; - if (fontFamily != null) { - final List rawUtf8Bytes = utf8.encode(fontFamily); - final SkStringHandle stringHandle = skStringAllocate(rawUtf8Bytes.length); - final Pointer stringDataPointer = skStringGetData(stringHandle); - for (int i = 0; i < rawUtf8Bytes.length; i++) { - stringDataPointer[i] = rawUtf8Bytes[i]; - } - success = fontCollectionRegisterFont(_handle, dataHandle, stringHandle); - skStringFree(stringHandle); - } else { - success = fontCollectionRegisterFont(_handle, dataHandle, nullptr); - } - skDataDispose(dataHandle); - return success; + void registerDownloadedFonts() { + // TODO(jacksongardner): implement registerDownloadedFonts } } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/js_functions.dart similarity index 68% rename from lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart rename to lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/js_functions.dart index e12b43ddcd0e9..54a42bfc6c31b 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/js_functions.dart @@ -4,16 +4,6 @@ import 'dart:js_interop'; -import 'package:ui/src/engine.dart'; - -@JS() -@staticInterop -class WebAssemblyMemory {} - -extension WebAssemblyMemoryExtension on WebAssemblyMemory { - external ArrayBuffer get buffer; -} - @JS() @staticInterop class SkwasmInstance {} @@ -21,7 +11,6 @@ class SkwasmInstance {} extension SkwasmInstanceExtension on SkwasmInstance { external JSNumber addFunction(JSFunction function, JSString signature); external void removeFunction(JSNumber functionPointer); - external WebAssemblyMemory get wasmMemory; } @JS('window._flutter_skwasmInstance') diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart deleted file mode 100644 index 5bbd928d1ce2e..0000000000000 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@DefaultAsset('skwasm') -library skwasm_impl; - -import 'dart:ffi'; - -import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; - -final class RawFontCollection extends Opaque {} -typedef FontCollectionHandle = Pointer; - -@Native(symbol: 'fontCollection_create', isLeaf: true) -external FontCollectionHandle fontCollectionCreate(); - -@Native(symbol: 'fontCollection_dispose', isLeaf: true) -external void fontCollectionDispose(FontCollectionHandle handle); - -@Native(symbol: 'fontCollection_registerFont', isLeaf: true) -external bool fontCollectionRegisterFont( - FontCollectionHandle handle, - SkDataHandle fontData, - SkStringHandle fontName, -); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart index a999ddad28b30..beadca256a28d 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart @@ -12,6 +12,12 @@ import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawShader extends Opaque {} typedef ShaderHandle = Pointer; +final class RawSkString extends Opaque {} +typedef SkStringHandle = Pointer; + +final class RawSkData extends Opaque {} +typedef SkDataHandle = Pointer; + final class RawRuntimeEffect extends Opaque {} typedef RuntimeEffectHandle = Pointer; @@ -100,6 +106,15 @@ external ShaderHandle shaderCreateSweepGradient( @Native(symbol: 'shader_dispose', isLeaf: true) external void shaderDispose(ShaderHandle handle); +@Native(symbol: 'shaderSource_allocate', isLeaf: true) +external SkStringHandle shaderSourceAllocate(int size); + +@Native Function(SkStringHandle)>(symbol: 'shaderSource_getData', isLeaf: true) +external Pointer shaderSourceGetData(SkStringHandle handle); + +@Native(symbol: 'shaderSource_free', isLeaf: true) +external void shaderSourceFree(SkStringHandle handle); + @Native(symbol: 'runtimeEffect_create', isLeaf: true) external RuntimeEffectHandle runtimeEffectCreate(SkStringHandle source); @@ -109,6 +124,15 @@ external void runtimeEffectDispose(RuntimeEffectHandle handle); @Native(symbol: 'runtimeEffect_getUniformSize', isLeaf: true) external int runtimeEffectGetUniformSize(RuntimeEffectHandle handle); +@Native(symbol: 'data_create', isLeaf: true) +external SkDataHandle dataCreate(int size); + +@Native Function(SkDataHandle)>(symbol: 'data_getPointer', isLeaf: true) +external Pointer dataGetPointer(SkDataHandle handle); + +@Native(symbol: 'data_dispose', isLeaf: true) +external void dataDispose(SkDataHandle handle); + @Native; - -@Native(symbol: 'skData_create', isLeaf: true) -external SkDataHandle skDataCreate(int size); - -@Native Function(SkDataHandle)>(symbol: 'skData_getPointer', isLeaf: true) -external Pointer skDataGetPointer(SkDataHandle handle); - -@Native(symbol: 'skData_dispose', isLeaf: true) -external void skDataDispose(SkDataHandle handle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart deleted file mode 100644 index 1b5af811542ad..0000000000000 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@DefaultAsset('skwasm') -library skwasm_impl; - -import 'dart:ffi'; - -final class RawSkString extends Opaque {} -typedef SkStringHandle = Pointer; - -@Native(symbol: 'skString_allocate', isLeaf: true) -external SkStringHandle skStringAllocate(int size); - -@Native Function(SkStringHandle)>(symbol: 'skString_getData', isLeaf: true) -external Pointer skStringGetData(SkStringHandle handle); - -@Native(symbol: 'skString_free', isLeaf: true) -external void skStringFree(SkStringHandle handle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index 73b8e4e21fc83..4740a2f4e9724 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -283,6 +283,9 @@ class SkwasmRenderer implements Renderer { } final SkwasmPicture picture = (scene as SkwasmScene).picture as SkwasmPicture; await surface.renderPicture(picture); + + // TODO(jacksongardner): Remove this hack. See https://github.com/flutter/flutter/issues/124616 + await Future.delayed(const Duration(milliseconds: 100)); } @override diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart index ef2bf5ce2f1c5..dc37b92666143 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart @@ -162,15 +162,15 @@ class SkwasmFragmentProgram implements ui.FragmentProgram { // TODO(jacksongardner): Can we avoid this copy? final List sourceData = utf8.encode(shaderData.source); - final SkStringHandle sourceString = skStringAllocate(sourceData.length); - final Pointer sourceBuffer = skStringGetData(sourceString); + final SkStringHandle sourceString = shaderSourceAllocate(sourceData.length); + final Pointer sourceBuffer = shaderSourceGetData(sourceString); int i = 0; for (final int byte in sourceData) { sourceBuffer[i] = byte; i++; } final RuntimeEffectHandle handle = runtimeEffectCreate(sourceString); - skStringFree(sourceString); + shaderSourceFree(sourceString); return SkwasmFragmentProgram._(name, handle); } @@ -193,7 +193,7 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader { SkwasmFragmentProgram program, { List? childShaders, }) : _program = program, - _uniformData = skDataCreate(program.uniformSize), + _uniformData = dataCreate(program.uniformSize), _childShaders = childShaders; @override @@ -234,7 +234,7 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader { shaderDispose(_handle); _handle = nullptr; } - final Pointer dataPointer = skDataGetPointer(_uniformData).cast(); + final Pointer dataPointer = dataGetPointer(_uniformData).cast(); dataPointer[index] = value; } @@ -247,7 +247,7 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader { void dispose() { super.dispose(); if (_uniformData != nullptr) { - skDataDispose(_uniformData); + dataDispose(_uniformData); _uniformData = nullptr; } } diff --git a/lib/web_ui/lib/src/engine/test_embedding.dart b/lib/web_ui/lib/src/engine/test_embedding.dart index 18febe3f233ff..103830680348e 100644 --- a/lib/web_ui/lib/src/engine/test_embedding.dart +++ b/lib/web_ui/lib/src/engine/test_embedding.dart @@ -10,6 +10,31 @@ import 'dart:async'; import 'package:ui/ui.dart' as ui; import 'package:ui/ui_web/src/ui_web.dart' as ui_web; +import '../engine.dart'; + +Future? _platformInitializedFuture; + +Future initializeTestFlutterViewEmbedder({double devicePixelRatio = 3.0}) { + // Force-initialize FlutterViewEmbedder so it doesn't overwrite test pixel ratio. + ensureFlutterViewEmbedderInitialized(); + + // The following parameters are hard-coded in Flutter's test embedder. Since + // we don't have an embedder yet this is the lowest-most layer we can put + // this stuff in. + window.debugOverrideDevicePixelRatio(devicePixelRatio); + window.webOnlyDebugPhysicalSizeOverride = + ui.Size(800 * devicePixelRatio, 600 * devicePixelRatio); + scheduleFrameCallback = () {}; + ui.debugEmulateFlutterTesterEnvironment = true; + + // Initialize platform once and reuse across all tests. + if (_platformInitializedFuture != null) { + return _platformInitializedFuture!; + } + return _platformInitializedFuture = + initializeEngine(assetManager: WebOnlyMockAssetManager()); +} + const bool _debugLogHistoryActions = false; class TestHistoryEntry { diff --git a/lib/web_ui/lib/src/engine/text/font_collection.dart b/lib/web_ui/lib/src/engine/text/font_collection.dart index c6666183a3fbf..5af290c6630de 100644 --- a/lib/web_ui/lib/src/engine/text/font_collection.dart +++ b/lib/web_ui/lib/src/engine/text/font_collection.dart @@ -3,9 +3,16 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; import 'dart:typed_data'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/fonts.dart'; +import 'package:web_test_fonts/web_test_fonts.dart'; + +import '../assets.dart'; +import '../dom.dart'; +import '../util.dart'; +import 'layout_service.dart'; /// This class is responsible for registering and loading fonts. /// @@ -14,48 +21,95 @@ import 'package:ui/src/engine.dart'; /// font manifest. If test fonts are enabled, then call /// [debugDownloadTestFonts] as well. class HtmlFontCollection implements FlutterFontCollection { + FontManager? _assetFontManager; + FontManager? _testFontManager; + /// Reads the font manifest using the [assetManager] and downloads all of the /// fonts declared within. @override - Future loadAssetFonts(FontManifest manifest) async { - final List> pendingFonts = >[]; - for (final FontFamily family in manifest.families) { - for (final FontAsset fontAsset in family.fontAssets) { - pendingFonts.add(() async { - return ( - fontAsset.asset, - await _loadFontAsset(family.name, fontAsset.asset, fontAsset.descriptors) - ); - }()); - } + Future downloadAssetFonts(AssetManager assetManager) async { + final HttpFetchResponse response = await assetManager.loadAsset('FontManifest.json'); + + if (!response.hasPayload) { + printWarning('Font manifest does not exist at `${response.url}` - ignoring.'); + return; + } + + final Uint8List data = await response.asUint8List(); + final List? fontManifest = json.decode(utf8.decode(data)) as List?; + if (fontManifest == null) { + throw AssertionError( + 'There was a problem trying to load FontManifest.json'); } - final List loadedFonts = []; - final Map fontFailures = {}; - for (final (String asset, FontLoadError? error) in await Future.wait(pendingFonts)) { - if (error == null) { - loadedFonts.add(asset); - } else { - fontFailures[asset] = error; + _assetFontManager = FontManager(); + + for (final Map fontFamily + in fontManifest.cast>()) { + final String? family = fontFamily.tryString('family'); + final List> fontAssets = fontFamily.castList>('fonts'); + + for (final Map fontAsset in fontAssets) { + final String asset = fontAsset.readString('asset'); + final Map descriptors = {}; + for (final String descriptor in fontAsset.keys) { + if (descriptor != 'asset') { + descriptors[descriptor] = '${fontAsset[descriptor]}'; + } + } + _assetFontManager!.downloadAsset( + family!, 'url(${assetManager.getAssetUrl(asset)})', descriptors); } } - return AssetFontsResult(loadedFonts, fontFailures); + await _assetFontManager!.downloadAllFonts(); } @override - Future loadFontFromList(Uint8List list, {String? fontFamily}) async { + Future loadFontFromList(Uint8List list, {String? fontFamily}) { if (fontFamily == null) { - printWarning('Font family must be provided to HtmlFontCollection.'); - return false; + throw AssertionError('Font family must be provided to HtmlFontCollection.'); } - return _loadFontFaceBytes(fontFamily, list); + return _assetFontManager!._loadFontFaceBytes(fontFamily, list); + } + + /// Downloads fonts that are used by tests. + @override + Future debugDownloadTestFonts() async { + final FontManager fontManager = _testFontManager = FontManager(); + fontManager._downloadedFonts.add(createDomFontFace( + EmbeddedTestFont.flutterTest.fontFamily, + EmbeddedTestFont.flutterTest.data, + )); + for (final MapEntry fontEntry in testFontUrls.entries) { + fontManager.downloadAsset(fontEntry.key, 'url(${fontEntry.value})', const {}); + } + await fontManager.downloadAllFonts(); + } + + @override + void registerDownloadedFonts() { + _assetFontManager?.registerDownloadedFonts(); + _testFontManager?.registerDownloadedFonts(); } /// Unregister all fonts that have been registered. @override void clear() { + _assetFontManager = null; + _testFontManager = null; domDocument.fonts!.clear(); } +} + +/// Manages a collection of fonts and ensures they are loaded. +class FontManager { + + /// Fonts that started the downloading process. Once the fonts have downloaded + /// without error, they are moved to [_downloadedFonts]. Those fonts + /// are subsequently registered by [registerDownloadedFonts]. + final List> _fontLoadingFutures = >[]; + + final List _downloadedFonts = []; // Regular expression to detect a string with no punctuations. // For example font family 'Ahem!' does not fall into this category @@ -98,77 +152,79 @@ class HtmlFontCollection implements FlutterFontCollection { /// /// * https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#Valid_family_names /// * https://drafts.csswg.org/css-fonts-3/#font-family-prop - Future _loadFontAsset( + void downloadAsset( String family, String asset, Map descriptors, - ) async { - final List fontFaces = []; - final List errors = []; - try { - if (startWithDigit.hasMatch(family) || - notPunctuation.stringMatch(family) != family) { - // Load a font family name with special characters once here wrapped in - // quotes. - fontFaces.add(await _loadFontFace("'$family'", asset, descriptors)); - } - } on FontLoadError catch (error) { - errors.add(error); + ) { + if (startWithDigit.hasMatch(family) || + notPunctuation.stringMatch(family) != family) { + // Load a font family name with special characters once here wrapped in + // quotes. + _loadFontFace("'$family'", asset, descriptors); } - try { - // Load all fonts, without quoted family names. - fontFaces.add(await _loadFontFace(family, asset, descriptors)); - } on FontLoadError catch (error) { - errors.add(error); - } - if (fontFaces.isEmpty) { - // We failed to load either font face. Return the first error. - return errors.first; - } - - try { - fontFaces.forEach(domDocument.fonts!.add); - } catch (e) { - return FontInvalidDataError(asset); - } - return null; + // Load all fonts, without quoted family names. + _loadFontFace(family, asset, descriptors); } - Future _loadFontFace( + void _loadFontFace( String family, String asset, Map descriptors, - ) async { + ) { + Future fontFaceLoad(DomFontFace fontFace) async { + try { + final DomFontFace loadedFontFace = await fontFace.load(); + return loadedFontFace; + } catch (e) { + printWarning('Error while trying to load font family "$family":\n$e'); + return null; + } + } // try/catch because `new FontFace` can crash with an improper font family. try { - final DomFontFace fontFace = createDomFontFace(family, 'url(${assetManager.getAssetUrl(asset)})', descriptors); - return await fontFace.load(); + final DomFontFace fontFace = createDomFontFace(family, asset, descriptors); + _fontLoadingFutures.add(fontFaceLoad(fontFace)); } catch (e) { printWarning('Error while loading font family "$family":\n$e'); - throw FontDownloadError(asset, e); } } + void registerDownloadedFonts() { + if (_downloadedFonts.isEmpty) { + return; + } + // Since we can't use tear-offs for interop members, this code is faster and + // easier to read with a for loop instead of forEach. + // ignore: prefer_foreach + for (final DomFontFace font in _downloadedFonts) { + domDocument.fonts!.add(font); + } + } + + + Future downloadAllFonts() async { + final List loadedFonts = await Future.wait(_fontLoadingFutures); + _downloadedFonts.addAll(loadedFonts.whereType()); + } + // Loads a font from bytes, surfacing errors through the future. - Future _loadFontFaceBytes(String family, Uint8List list) async { + Future _loadFontFaceBytes(String family, Uint8List list) { // Since these fonts are loaded by user code, surface the error // through the returned future. - try { - final DomFontFace fontFace = createDomFontFace(family, list); - if (fontFace.status == 'error') { - // Font failed to load. - return false; - } + final DomFontFace fontFace = createDomFontFace(family, list); + return fontFace.load().then((_) { domDocument.fonts!.add(fontFace); - // There might be paragraph measurements for this new font before it is // loaded. They were measured using fallback font, so we should clear the // cache. Spanometer.clearRulersCache(); - } catch (exception) { - // Failures here will throw an DomException. Return false. - return false; - } - return true; + }, onError: (dynamic exception) { + // Failures here will throw an DomException which confusingly + // does not implement Exception or Error. Rethrow an Exception so it can + // be caught in user code without depending on dart:html or requiring a + // catch block without "on". + throw Exception(exception.toString()); + }); } } diff --git a/lib/web_ui/skwasm/BUILD.gn b/lib/web_ui/skwasm/BUILD.gn index 7fb55ceab5bc9..7ad61d87fefcd 100644 --- a/lib/web_ui/skwasm/BUILD.gn +++ b/lib/web_ui/skwasm/BUILD.gn @@ -8,15 +8,12 @@ wasm_lib("skwasm") { sources = [ "canvas.cpp", "contour_measure.cpp", - "data.cpp", "export.h", - "fonts.cpp", "helpers.h", "paint.cpp", "path.cpp", "picture.cpp", "shaders.cpp", - "string.cpp", "surface.cpp", "wrappers.h", ] @@ -49,8 +46,5 @@ wasm_lib("skwasm") { ] } - deps = [ - "//third_party/skia", - "//third_party/skia/modules/skparagraph", - ] + deps = [ "//third_party/skia" ] } diff --git a/lib/web_ui/skwasm/canvas.cpp b/lib/web_ui/skwasm/canvas.cpp index 9a07aef09bba1..79cd4d2f06213 100644 --- a/lib/web_ui/skwasm/canvas.cpp +++ b/lib/web_ui/skwasm/canvas.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "export.h" #include "helpers.h" #include "wrappers.h" diff --git a/lib/web_ui/skwasm/contour_measure.cpp b/lib/web_ui/skwasm/contour_measure.cpp index 642d1fa36924a..fcc30ad407967 100644 --- a/lib/web_ui/skwasm/contour_measure.cpp +++ b/lib/web_ui/skwasm/contour_measure.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "export.h" #include "helpers.h" diff --git a/lib/web_ui/skwasm/data.cpp b/lib/web_ui/skwasm/data.cpp deleted file mode 100644 index 9c5e860d6d635..0000000000000 --- a/lib/web_ui/skwasm/data.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "export.h" - -#include "third_party/skia/include/core/SkData.h" - -SKWASM_EXPORT SkData* skData_create(size_t size) { - return SkData::MakeUninitialized(size).release(); -} - -SKWASM_EXPORT void* skData_getPointer(SkData* data) { - return data->writable_data(); -} - -SKWASM_EXPORT void skData_dispose(SkData* data) { - return data->unref(); -} diff --git a/lib/web_ui/skwasm/export.h b/lib/web_ui/skwasm/export.h index fe4711aac20f6..cb0172e0bf621 100644 --- a/lib/web_ui/skwasm/export.h +++ b/lib/web_ui/skwasm/export.h @@ -4,6 +4,4 @@ #pragma once -#include - #define SKWASM_EXPORT extern "C" EMSCRIPTEN_KEEPALIVE diff --git a/lib/web_ui/skwasm/fonts.cpp b/lib/web_ui/skwasm/fonts.cpp deleted file mode 100644 index 22b66419cf8e9..0000000000000 --- a/lib/web_ui/skwasm/fonts.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "export.h" -#include "third_party/skia/include/core/SkFontMgr.h" -#include "third_party/skia/modules/skparagraph/include/FontCollection.h" -#include "third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h" - -using namespace skia::textlayout; - -struct FlutterFontCollection { - sk_sp collection; - sk_sp provider; -}; - -SKWASM_EXPORT FlutterFontCollection* fontCollection_create() { - auto collection = sk_make_sp(); - auto provider = sk_make_sp(); - collection->enableFontFallback(); - collection->setDefaultFontManager(provider); - return new FlutterFontCollection{ - std::move(collection), - std::move(provider), - }; -} - -SKWASM_EXPORT void fontCollection_dispose(FlutterFontCollection* collection) { - delete collection; -} - -SKWASM_EXPORT bool fontCollection_registerFont( - FlutterFontCollection* collection, - SkData* fontData, - SkString* fontName) { - fontData->ref(); - auto typeFace = - SkFontMgr::RefDefault()->makeFromData(sk_sp(fontData)); - if (!typeFace) { - return false; - } - if (fontName) { - SkString alias = *fontName; - collection->provider->registerTypeface(std::move(typeFace), alias); - } else { - collection->provider->registerTypeface(std::move(typeFace)); - } - return true; -} diff --git a/lib/web_ui/skwasm/paint.cpp b/lib/web_ui/skwasm/paint.cpp index 026a7a4d80b5f..eb66fff6e292a 100644 --- a/lib/web_ui/skwasm/paint.cpp +++ b/lib/web_ui/skwasm/paint.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "export.h" #include "helpers.h" #include "third_party/skia/include/core/SkPaint.h" diff --git a/lib/web_ui/skwasm/path.cpp b/lib/web_ui/skwasm/path.cpp index 63690f1370108..f8fe6e1452e30 100644 --- a/lib/web_ui/skwasm/path.cpp +++ b/lib/web_ui/skwasm/path.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "export.h" #include "helpers.h" #include "third_party/skia/include/core/SkPath.h" diff --git a/lib/web_ui/skwasm/picture.cpp b/lib/web_ui/skwasm/picture.cpp index c12d7d42fc53b..2d80a985ab9bb 100644 --- a/lib/web_ui/skwasm/picture.cpp +++ b/lib/web_ui/skwasm/picture.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "export.h" #include "helpers.h" #include "third_party/skia/include/core/SkPictureRecorder.h" diff --git a/lib/web_ui/skwasm/shaders.cpp b/lib/web_ui/skwasm/shaders.cpp index 5a521f8184512..1c29c10e844fb 100644 --- a/lib/web_ui/skwasm/shaders.cpp +++ b/lib/web_ui/skwasm/shaders.cpp @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "export.h" #include "helpers.h" #include "third_party/skia/include/effects/SkGradientShader.h" @@ -101,6 +102,18 @@ SKWASM_EXPORT void shader_dispose(SkShader* shader) { shader->unref(); } +SKWASM_EXPORT SkString* shaderSource_allocate(size_t length) { + return new SkString(length); +} + +SKWASM_EXPORT char* shaderSource_getData(SkString* string) { + return string->data(); +} + +SKWASM_EXPORT void shaderSource_free(SkString* string) { + return delete string; +} + SKWASM_EXPORT SkRuntimeEffect* runtimeEffect_create(SkString* source) { auto result = SkRuntimeEffect::MakeForShader(*source); if (result.effect == nullptr) { @@ -120,6 +133,18 @@ SKWASM_EXPORT size_t runtimeEffect_getUniformSize(SkRuntimeEffect* effect) { return effect->uniformSize(); } +SKWASM_EXPORT SkData* data_create(size_t size) { + return SkData::MakeUninitialized(size).release(); +} + +SKWASM_EXPORT void* data_getPointer(SkData* data) { + return data->writable_data(); +} + +SKWASM_EXPORT void data_dispose(SkData* data) { + return data->unref(); +} + SKWASM_EXPORT SkShader* shader_createRuntimeEffectShader( SkRuntimeEffect* runtimeEffect, SkData* uniforms, diff --git a/lib/web_ui/skwasm/string.cpp b/lib/web_ui/skwasm/string.cpp deleted file mode 100644 index 77e66d705dcfc..0000000000000 --- a/lib/web_ui/skwasm/string.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "export.h" - -#include "third_party/skia/include/core/SkString.h" - -SKWASM_EXPORT SkString* skString_allocate(size_t length) { - return new SkString(length); -} - -SKWASM_EXPORT char* skString_getData(SkString* string) { - return string->data(); -} - -SKWASM_EXPORT void skString_free(SkString* string) { - return delete string; -} diff --git a/lib/web_ui/test/canvaskit/canvas_golden_test.dart b/lib/web_ui/test/canvaskit/canvas_golden_test.dart index f58485f1f0124..f581af87035d3 100644 --- a/lib/web_ui/test/canvaskit/canvas_golden_test.dart +++ b/lib/web_ui/test/canvaskit/canvas_golden_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:js_util' as js_util; import 'dart:math' as math; import 'dart:typed_data'; @@ -28,6 +29,15 @@ void testMain() { expect(notoDownloadQueue.downloader.debugActiveDownloadCount, 0); expect(notoDownloadQueue.isPending, isFalse); + // We render some color emojis in this test. + final FlutterConfiguration config = FlutterConfiguration() + ..setUserConfiguration( + js_util.jsify({ + 'useColorEmoji': true, + }) as JsFlutterConfiguration); + debugSetConfiguration(config); + + FontFallbackData.debugReset(); notoDownloadQueue.downloader.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/'; }); diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index b37d01076dc44..36db2c0003938 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -1527,6 +1527,11 @@ void _textStyleTests() { } void _paragraphTests() { + setUpAll(() async { + await CanvasKitRenderer.instance.fontCollection.debugDownloadTestFonts(); + CanvasKitRenderer.instance.fontCollection.registerDownloadedFonts(); + }); + // This test is just a kitchen sink that blasts CanvasKit with all paragraph // properties all at once, making sure CanvasKit doesn't choke on anything. // In particular, this tests that our JS bindings are correct, such as that diff --git a/lib/web_ui/test/canvaskit/common.dart b/lib/web_ui/test/canvaskit/common.dart index 842d3a4a70a92..407e0b4226609 100644 --- a/lib/web_ui/test/canvaskit/common.dart +++ b/lib/web_ui/test/canvaskit/common.dart @@ -11,21 +11,14 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; - const MethodCodec codec = StandardMethodCodec(); /// Common test setup for all CanvasKit unit-tests. void setUpCanvasKitTest() { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - - setUpAll(() { - // Ahem must be added to font fallbacks list regardless of where it was - // downloaded from. - FontFallbackData.instance.globalFontFallbacks.add('Ahem'); + setUpAll(() async { + expect(renderer, isA(), reason: 'This test must run in CanvasKit mode.'); + debugDisableFontFallbacks = false; + await initializeEngine(assetManager: WebOnlyMockAssetManager()); }); tearDown(() { diff --git a/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart b/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart index f8bff49380d7f..a55382a27b9a3 100644 --- a/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart +++ b/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:js_util' as js_util; import 'dart:math' as math; import 'dart:typed_data'; @@ -24,14 +25,18 @@ void testMain() { group('Font fallbacks', () { setUpCanvasKitTest(); - setUpAll(() { - debugDisableFontFallbacks = false; - }); - /// Used to save and restore [ui.window.onPlatformMessage] after each test. ui.PlatformMessageCallback? savedCallback; setUp(() { + // We render some color emojis in this test. + final FlutterConfiguration config = FlutterConfiguration() + ..setUserConfiguration( + js_util.jsify({ + 'useColorEmoji': true, + }) as JsFlutterConfiguration); + debugSetConfiguration(config); + FontFallbackData.debugReset(); notoDownloadQueue.downloader.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/'; savedCallback = ui.window.onPlatformMessage; diff --git a/lib/web_ui/test/canvaskit/skia_font_collection_test.dart b/lib/web_ui/test/canvaskit/skia_font_collection_test.dart index 1527e707fab91..75764a8c35f4f 100644 --- a/lib/web_ui/test/canvaskit/skia_font_collection_test.dart +++ b/lib/web_ui/test/canvaskit/skia_font_collection_test.dart @@ -9,22 +9,18 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; -import '../common/fake_asset_manager.dart'; -import '../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { group('$SkiaFontCollection', () { - setUpUnitTests(); - final List warnings = []; late void Function(String) oldPrintWarning; - late FakeAssetScope testAssetScope; setUpAll(() async { + ensureFlutterViewEmbedderInitialized(); + await renderer.initialize(); oldPrintWarning = printWarning; printWarning = (String warning) { warnings.add(warning); @@ -36,19 +32,20 @@ void testMain() { }); setUp(() { - testAssetScope = fakeAssetManager.pushAssetScope(); mockHttpFetchResponseFactory = null; warnings.clear(); }); tearDown(() { - fakeAssetManager.popAssetScope(testAssetScope); mockHttpFetchResponseFactory = null; }); test('logs no warnings with the default mock asset manager', () async { final SkiaFontCollection fontCollection = SkiaFontCollection(); - await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + await fontCollection.downloadAssetFonts(mockAssetManager); + fontCollection.registerDownloadedFonts(); expect(warnings, isEmpty); }); @@ -64,7 +61,9 @@ void testMain() { ); }; final SkiaFontCollection fontCollection = SkiaFontCollection(); - testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data(''' + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + mockAssetManager.defaultFontManifest = ''' [ { "family":"Roboto", @@ -75,9 +74,10 @@ void testMain() { "fonts":[{"asset":"packages/bogus/BrokenFont.ttf"}] } ] - ''')); + '''; // It should complete without error, but emit a warning about BrokenFont. - await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); + await fontCollection.downloadAssetFonts(mockAssetManager); + fontCollection.registerDownloadedFonts(); expect( warnings, containsAllInOrder( @@ -91,7 +91,9 @@ void testMain() { test('logs an HTTP warning if one of the registered fonts is missing (404 file not found)', () async { final SkiaFontCollection fontCollection = SkiaFontCollection(); - testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data(''' + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + mockAssetManager.defaultFontManifest = ''' [ { "family":"Roboto", @@ -102,32 +104,38 @@ void testMain() { "fonts":[{"asset":"packages/bogus/ThisFontDoesNotExist.ttf"}] } ] - ''')); + '''; // It should complete without error, but emit a warning about ThisFontDoesNotExist. - await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); + await fontCollection.downloadAssetFonts(mockAssetManager); + fontCollection.registerDownloadedFonts(); expect( warnings, containsAllInOrder([ - 'Font family ThisFontDoesNotExist not found (404) at packages/bogus/ThisFontDoesNotExist.ttf' + 'Failed to load font ThisFontDoesNotExist at packages/bogus/ThisFontDoesNotExist.ttf', + 'Flutter Web engine failed to fetch "packages/bogus/ThisFontDoesNotExist.ttf". HTTP request succeeded, but the server responded with HTTP status 404.', ]), ); }); test('prioritizes Ahem loaded via FontManifest.json', () async { final SkiaFontCollection fontCollection = SkiaFontCollection(); - testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data(''' + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + mockAssetManager.defaultFontManifest = ''' [ { "family":"Ahem", "fonts":[{"asset":"/assets/fonts/Roboto-Regular.ttf"}] } ] - '''.trim())); + '''.trim(); final ByteBuffer robotoData = await httpFetchByteBuffer('/assets/fonts/Roboto-Regular.ttf'); - await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); + await fontCollection.downloadAssetFonts(mockAssetManager); + await fontCollection.debugDownloadTestFonts(); + fontCollection.registerDownloadedFonts(); expect(warnings, isEmpty); // Use `singleWhere` to make sure only one version of 'Ahem' is loaded. @@ -140,10 +148,18 @@ void testMain() { }); test('falls back to default Ahem URL', () async { - final SkiaFontCollection fontCollection = renderer.fontCollection as SkiaFontCollection; + final SkiaFontCollection fontCollection = SkiaFontCollection(); + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + mockAssetManager.defaultFontManifest = '[]'; final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf'); + await fontCollection.downloadAssetFonts(mockAssetManager); + await fontCollection.debugDownloadTestFonts(); + fontCollection.registerDownloadedFonts(); + expect(warnings, isEmpty); + // Use `singleWhere` to make sure only one version of 'Ahem' is loaded. final RegisteredFont ahem = fontCollection.debugRegisteredFonts! .singleWhere((RegisteredFont font) => font.family == 'Ahem'); @@ -153,9 +169,24 @@ void testMain() { expect(ahem.bytes.length, ahemData.lengthInBytes); }); + test('download fonts separately from registering', () async { + final SkiaFontCollection fontCollection = SkiaFontCollection(); + + await fontCollection.debugDownloadTestFonts(); + /// Fonts should have been downloaded, but not yet registered + expect(fontCollection.debugRegisteredFonts, isEmpty); + + fontCollection.registerDownloadedFonts(); + /// Fonts should now be registered and _registeredFonts should be filled + expect(fontCollection.debugRegisteredFonts, isNotEmpty); + expect(warnings, isEmpty); + }); + test('FlutterTest is the default test font', () async { - final SkiaFontCollection fontCollection = renderer.fontCollection as SkiaFontCollection; + final SkiaFontCollection fontCollection = SkiaFontCollection(); + await fontCollection.debugDownloadTestFonts(); + fontCollection.registerDownloadedFonts(); expect(fontCollection.debugRegisteredFonts, isNotEmpty); expect(fontCollection.debugRegisteredFonts!.first.family, 'FlutterTest'); }); diff --git a/lib/web_ui/test/common/fake_asset_manager.dart b/lib/web_ui/test/common/fake_asset_manager.dart deleted file mode 100644 index 2aea72c30315d..0000000000000 --- a/lib/web_ui/test/common/fake_asset_manager.dart +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:ui/src/engine.dart'; - -class FakeAssetManager implements AssetManager { - FakeAssetManager(); - - @override - String get assetsDir => 'assets'; - - @override - String getAssetUrl(String asset) => asset; - - @override - Future load(String assetKey) async { - final ByteData? data = _assetMap[assetKey]; - if (data == null) { - throw HttpFetchNoPayloadError(assetKey, status: 404); - } - return data; - } - - @override - Future loadAsset(String asset) async { - final ByteData? assetData = await _currentScope?.getAssetData(asset); - if (assetData != null) { - return MockHttpFetchResponse( - url: asset, - status: 200, - payload: MockHttpFetchPayload( - byteBuffer: assetData.buffer, - ), - ); - } else { - return MockHttpFetchResponse( - url: asset, - status: 404, - ); - } - } - - FakeAssetScope pushAssetScope() { - final FakeAssetScope scope = FakeAssetScope._(_currentScope); - _currentScope = scope; - return scope; - } - - void popAssetScope(FakeAssetScope scope) { - assert(_currentScope == scope); - _currentScope = scope._parent; - } - - void setAsset(String assetKey, ByteData assetData) { - _assetMap[assetKey] = assetData; - } - - FakeAssetScope? _currentScope; - final Map _assetMap = {}; -} - -class FakeAssetScope { - FakeAssetScope._(this._parent); - - final FakeAssetScope? _parent; - final Map Function()> _assetFetcherMap = Function()>{}; - - void setAsset(String assetKey, ByteData assetData) { - _assetFetcherMap[assetKey] = () async => assetData; - } - - void setAssetPassthrough(String assetKey) { - _assetFetcherMap[assetKey] = () async { - return ByteData.view(await httpFetchByteBuffer(assetKey)); - }; - } - - Future? getAssetData(String assetKey) { - final Future Function()? fetcher = _assetFetcherMap[assetKey]; - if (fetcher != null) { - return fetcher(); - } - if (_parent != null) { - return _parent!.getAssetData(assetKey); - } - return null; - } -} - -FakeAssetManager fakeAssetManager = FakeAssetManager(); - -ByteData stringAsUtf8Data(String string) { - return ByteData.view(Uint8List.fromList(utf8.encode(string)).buffer); -} - -const String ahemFontFamily = 'Ahem'; -const String ahemFontUrl = '/assets/fonts/ahem.ttf'; -const String robotoFontFamily = 'Roboto'; -const String robotoTestFontUrl = '/assets/fonts/Roboto-Regular.ttf'; -const String robotoVariableFontFamily = 'RobotoVariable'; -const String robotoVariableFontUrl = '/assets/fonts/RobotoSlab-VariableFont_wght.ttf'; - -/// The list of test fonts, in the form of font family name - font file url pairs. -/// This list does not include embedded test fonts, which need to be loaded and -/// registered separately in [FontCollection.debugDownloadTestFonts]. -const Map testFontUrls = { - ahemFontFamily: ahemFontUrl, - robotoFontFamily: robotoTestFontUrl, - robotoVariableFontFamily: robotoVariableFontUrl, -}; - -FakeAssetScope configureDebugFontsAssetScope(FakeAssetManager manager) { - final FakeAssetScope scope = manager.pushAssetScope(); - scope.setAsset('AssetManifest.json', stringAsUtf8Data('{}')); - scope.setAsset('FontManifest.json', stringAsUtf8Data(''' - [ - { - "family":"$robotoFontFamily", - "fonts":[{"asset":"$robotoTestFontUrl"}] - }, - { - "family":"$ahemFontFamily", - "fonts":[{"asset":"$ahemFontUrl"}] - }, - { - "family":"$robotoVariableFontFamily", - "fonts":[{"asset":"$robotoVariableFontUrl"}] - } - ]''')); - scope.setAssetPassthrough(robotoTestFontUrl); - scope.setAssetPassthrough(ahemFontUrl); - scope.setAssetPassthrough(robotoVariableFontUrl); - return scope; -} diff --git a/lib/web_ui/test/common/test_initialization.dart b/lib/web_ui/test/common/test_initialization.dart deleted file mode 100644 index 812210624111e..0000000000000 --- a/lib/web_ui/test/common/test_initialization.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:js_util' as js_util; - -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' as engine; -import 'package:ui/ui.dart' as ui; - -import 'fake_asset_manager.dart'; - -void setUpUnitTests({ - bool emulateTesterEnvironment = true, - bool setUpTestViewDimensions = true, -}) { - late final FakeAssetScope debugFontsScope; - setUpAll(() async { - if (emulateTesterEnvironment) { - ui.debugEmulateFlutterTesterEnvironment = true; - } - - // Some of our tests rely on color emoji - final engine.FlutterConfiguration config = engine.FlutterConfiguration() - ..setUserConfiguration( - js_util.jsify({ - 'useColorEmoji': true, - }) as engine.JsFlutterConfiguration); - engine.debugSetConfiguration(config); - engine.notoDownloadQueue.downloader.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/'; - - debugFontsScope = configureDebugFontsAssetScope(fakeAssetManager); - await engine.initializeEngine(assetManager: fakeAssetManager); - - if (setUpTestViewDimensions) { - // Force-initialize FlutterViewEmbedder so it doesn't overwrite test pixel ratio. - engine.ensureFlutterViewEmbedderInitialized(); - - // The following parameters are hard-coded in Flutter's test embedder. Since - // we don't have an embedder yet this is the lowest-most layer we can put - // this stuff in. - const double devicePixelRatio = 3.0; - engine.window.debugOverrideDevicePixelRatio(devicePixelRatio); - engine.window.webOnlyDebugPhysicalSizeOverride = - const ui.Size(800 * devicePixelRatio, 600 * devicePixelRatio); - engine.scheduleFrameCallback = () {}; - } - }); - - tearDownAll(() async { - fakeAssetManager.popAssetScope(debugFontsScope); - }); -} diff --git a/lib/web_ui/test/engine/clipboard_test.dart b/lib/web_ui/test/engine/clipboard_test.dart index baeca91df6c7e..48578bf246b5b 100644 --- a/lib/web_ui/test/engine/clipboard_test.dart +++ b/lib/web_ui/test/engine/clipboard_test.dart @@ -9,14 +9,12 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; -import '../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); group('message handler', () { const String testText = 'test text'; diff --git a/lib/web_ui/test/engine/dom_http_fetch_test.dart b/lib/web_ui/test/engine/dom_http_fetch_test.dart index 606753ba4636b..1d1b8628ccdc5 100644 --- a/lib/web_ui/test/engine/dom_http_fetch_test.dart +++ b/lib/web_ui/test/engine/dom_http_fetch_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:js_interop'; import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; @@ -117,7 +116,7 @@ Future _testSuccessfulPayloads() async { expect(response.url, url); final List result = []; - await response.payload.read((JSUint8Array chunk) => result.addAll(chunk.toDart)); + await response.payload.read(result.addAll); expect(result, hasLength(length)); expect( result, diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart index 1e0381ee0ec5f..9017fde35d081 100644 --- a/lib/web_ui/test/engine/image/html_image_codec_test.dart +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -8,16 +8,15 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine/html_image_codec.dart'; +import 'package:ui/src/engine/test_embedding.dart'; import 'package:ui/ui.dart' as ui; -import '../../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); group('HtmCodec', () { test('supports raw images - RGBA8888', () async { final Completer completer = Completer(); diff --git a/lib/web_ui/test/engine/image_to_byte_data_test.dart b/lib/web_ui/test/engine/image_to_byte_data_test.dart index 306e50c560ec2..a9bb8a791f2ea 100644 --- a/lib/web_ui/test/engine/image_to_byte_data_test.dart +++ b/lib/web_ui/test/engine/image_to_byte_data_test.dart @@ -9,14 +9,16 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + setUpAll(() async { + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); Future createTestImageByColor(Color color) async { final EnginePictureRecorder recorder = EnginePictureRecorder(); diff --git a/lib/web_ui/test/engine/recording_canvas_test.dart b/lib/web_ui/test/engine/recording_canvas_test.dart index 6ff41a2bff902..72c63f4143fa6 100644 --- a/lib/web_ui/test/engine/recording_canvas_test.dart +++ b/lib/web_ui/test/engine/recording_canvas_test.dart @@ -8,16 +8,15 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; import '../common/mock_engine_canvas.dart'; -import '../common/test_initialization.dart'; +import '../html/screenshot.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + debugEmulateFlutterTesterEnvironment = true; + setUpStableTestFonts(); late RecordingCanvas underTest; late MockEngineCanvas mockCanvas; diff --git a/lib/web_ui/test/engine/text_editing_test.dart b/lib/web_ui/test/engine/text_editing_test.dart index c3c00a07c1c43..4e6a3763063a4 100644 --- a/lib/web_ui/test/engine/text_editing_test.dart +++ b/lib/web_ui/test/engine/text_editing_test.dart @@ -12,6 +12,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart' show flutterViewEmbedder; import 'package:ui/src/engine/browser_detection.dart'; import 'package:ui/src/engine/dom.dart'; +import 'package:ui/src/engine/initialization.dart'; import 'package:ui/src/engine/services.dart'; import 'package:ui/src/engine/text_editing/autofill_hint.dart'; import 'package:ui/src/engine/text_editing/input_type.dart'; @@ -20,7 +21,6 @@ import 'package:ui/src/engine/util.dart'; import 'package:ui/src/engine/vector_math.dart'; import '../common/spy.dart'; -import '../common/test_initialization.dart'; /// The `keyCode` of the "Enter" key. const int _kReturnKeyCode = 13; @@ -60,10 +60,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false - ); + await initializeEngine(); tearDown(() { lastEditingState = null; diff --git a/lib/web_ui/test/html/bitmap_canvas_golden_test.dart b/lib/web_ui/test/html/bitmap_canvas_golden_test.dart index 1ede2389e02ba..1852988f64029 100644 --- a/lib/web_ui/test/html/bitmap_canvas_golden_test.dart +++ b/lib/web_ui/test/html/bitmap_canvas_golden_test.dart @@ -10,8 +10,8 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; import 'paragraph/helper.dart'; +import 'screenshot.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -35,10 +35,7 @@ Future testMain() async { flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene-host')!.append(testScene); } - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); tearDown(() { flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene')?.remove(); diff --git a/lib/web_ui/test/html/canvas_clip_path_golden_test.dart b/lib/web_ui/test/html/canvas_clip_path_golden_test.dart index a0ea6e9c4adf4..7242600b361e6 100644 --- a/lib/web_ui/test/html/canvas_clip_path_golden_test.dart +++ b/lib/web_ui/test/html/canvas_clip_path_golden_test.dart @@ -7,10 +7,8 @@ import 'dart:js_util' as js_util; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart' as engine; -import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../common/test_initialization.dart'; import 'screenshot.dart'; void main() { @@ -18,9 +16,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await engine.renderer.fontCollection.debugDownloadTestFonts(); + engine.renderer.fontCollection.registerDownloadedFonts(); + }); // Regression test for https://github.com/flutter/flutter/issues/48683 // Should clip image with oval. diff --git a/lib/web_ui/test/html/canvas_context_golden_test.dart b/lib/web_ui/test/html/canvas_context_golden_test.dart index 282bd2590d712..53a9d30a2e201 100644 --- a/lib/web_ui/test/html/canvas_context_golden_test.dart +++ b/lib/web_ui/test/html/canvas_context_golden_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart' as engine; import 'package:ui/ui.dart' hide TextStyle; -import '../common/test_initialization.dart'; import 'screenshot.dart'; void main() { @@ -20,9 +19,12 @@ Future testMain() async { const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await engine.renderer.fontCollection.debugDownloadTestFonts(); + engine.renderer.fontCollection.registerDownloadedFonts(); + }); // Regression test for https://github.com/flutter/flutter/issues/49429 // Should clip with correct transform. diff --git a/lib/web_ui/test/html/canvas_reuse_golden_test.dart b/lib/web_ui/test/html/canvas_reuse_golden_test.dart index ded21b4350e42..0757f0851e879 100644 --- a/lib/web_ui/test/html/canvas_reuse_golden_test.dart +++ b/lib/web_ui/test/html/canvas_reuse_golden_test.dart @@ -9,8 +9,6 @@ import 'package:ui/ui.dart' hide TextStyle; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } @@ -24,7 +22,12 @@ Future testMain() async { ..strokeWidth = 2.0 ..color = const Color(0xFFFF00FF); - setUpUnitTests(); + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); // Regression test for https://github.com/flutter/flutter/issues/51514 test("Canvas is reused and z-index doesn't leak across paints", () async { diff --git a/lib/web_ui/test/html/clip_op_golden_test.dart b/lib/web_ui/test/html/clip_op_golden_test.dart index 9153643f944d3..2d2b34535dc61 100644 --- a/lib/web_ui/test/html/clip_op_golden_test.dart +++ b/lib/web_ui/test/html/clip_op_golden_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; import 'paragraph/helper.dart'; import 'screenshot.dart'; @@ -16,10 +15,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); /// Regression test for https://github.com/flutter/flutter/issues/64734. test('Clips using difference', () async { diff --git a/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart b/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart index dd7d329f2ac83..41743319b73ef 100644 --- a/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart @@ -9,17 +9,16 @@ import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpAll(() async { + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); setUp(() async { debugShowClipLayers = true; diff --git a/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart index 816219f879e7e..b1851aa4e51b6 100644 --- a/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -17,9 +16,13 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); test('Blend circles with difference and color', () async { final RecordingCanvas rc = diff --git a/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart index 69a92c88a0689..3b985102d4f4c 100644 --- a/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; import '../testimage.dart'; @@ -18,9 +17,12 @@ void main() { SurfacePaint makePaint() => Paint() as SurfacePaint; Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); const Color red = Color(0xFFFF0000); const Color green = Color(0xFF00FF00); diff --git a/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart index 2dda8c5d48009..03e8b8d2ccf5b 100644 --- a/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart @@ -10,7 +10,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -18,9 +17,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + ui.debugEmulateFlutterTesterEnvironment = true; + await ui.webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); tearDown(() { ContextStateHandle.debugEmulateWebKitMaskFilter = false; diff --git a/lib/web_ui/test/html/compositing/color_filter_golden_test.dart b/lib/web_ui/test/html/compositing/color_filter_golden_test.dart index faf6260620e75..a522d59684e40 100644 --- a/lib/web_ui/test/html/compositing/color_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/color_filter_golden_test.dart @@ -11,8 +11,6 @@ import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../../common/test_initialization.dart'; - const Rect region = Rect.fromLTWH(0, 0, 500, 500); void main() { @@ -20,10 +18,11 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpAll(() async { + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); setUp(() async { debugShowClipLayers = true; diff --git a/lib/web_ui/test/html/compositing/compositing_golden_test.dart b/lib/web_ui/test/html/compositing/compositing_golden_test.dart index 6881c47a2e996..bc86da523bf1d 100644 --- a/lib/web_ui/test/html/compositing/compositing_golden_test.dart +++ b/lib/web_ui/test/html/compositing/compositing_golden_test.dart @@ -11,7 +11,6 @@ import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; import '../../common/matchers.dart'; -import '../../common/test_initialization.dart'; const ui.Rect region = ui.Rect.fromLTWH(0, 0, 500, 100); @@ -20,10 +19,11 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpAll(() async { + await ui.webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); setUp(() async { // To debug test failures uncomment the following to visualize clipping diff --git a/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart b/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart index db41df3e346ad..bf7e63d20fb0e 100644 --- a/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart @@ -6,7 +6,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -14,9 +13,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); test('Should blur rectangles based on sigma.', () async { final RecordingCanvas rc = diff --git a/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart index 14be3d83c1914..8b26b7342f6c1 100644 --- a/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -15,14 +14,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - setUp(() async { debugShowClipLayers = true; SurfaceSceneBuilder.debugForgetFrameScene(); + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); }); tearDown(() { diff --git a/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart index a3bfcca3e633c..ad86f7ad77b9e 100644 --- a/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart @@ -14,7 +14,6 @@ import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -22,9 +21,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + }); + + setUpStableTestFonts(); test('Paints image', () async { final RecordingCanvas rc = diff --git a/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart index 6b6974b937ceb..a2cfffb29e7b2 100644 --- a/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; const Rect region = Rect.fromLTWH(0, 0, 500, 100); @@ -19,13 +18,11 @@ void main() { SurfacePaint makePaint() => Paint() as SurfacePaint; Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - setUpAll(() async { debugShowClipLayers = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); }); setUp(() async { diff --git a/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart b/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart index 94548a1745aad..4790ff9bd4e43 100644 --- a/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart +++ b/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart @@ -11,7 +11,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide ImageShader, TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -23,9 +22,12 @@ Future testMain() async { const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); setUp(() { GlContextCache.dispose(); diff --git a/lib/web_ui/test/html/paragraph/bidi_golden_test.dart b/lib/web_ui/test/html/paragraph/bidi_golden_test.dart index 07a7f91c49f1d..8f1c6f2060568 100644 --- a/lib/web_ui/test/html/paragraph/bidi_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/bidi_golden_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; const String _rtlWord1 = 'واحد'; @@ -18,10 +18,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); void paintBasicBidiStartingWithLtr( EngineCanvas canvas, diff --git a/lib/web_ui/test/html/paragraph/general_golden_test.dart b/lib/web_ui/test/html/paragraph/general_golden_test.dart index 34325eb7b7ab1..82d2e4da230f0 100644 --- a/lib/web_ui/test/html/paragraph/general_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/general_golden_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; const Rect bounds = Rect.fromLTWH(0, 0, 800, 600); @@ -20,10 +20,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); test('paints spans and lines correctly', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); diff --git a/lib/web_ui/test/html/paragraph/justify_golden_test.dart b/lib/web_ui/test/html/paragraph/justify_golden_test.dart index 148274ea4f01e..00a0033df3b0a 100644 --- a/lib/web_ui/test/html/paragraph/justify_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/justify_golden_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; void main() { @@ -17,10 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); void testJustifyWithMultipleSpans(EngineCanvas canvas) { void build(CanvasParagraphBuilder builder) { diff --git a/lib/web_ui/test/html/paragraph/overflow_golden_test.dart b/lib/web_ui/test/html/paragraph/overflow_golden_test.dart index ee787940ef557..eebfde0c1489e 100644 --- a/lib/web_ui/test/html/paragraph/overflow_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/overflow_golden_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; void main() { @@ -17,10 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); void testEllipsis(EngineCanvas canvas) { Offset offset = Offset.zero; diff --git a/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart b/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart index cf9cdc8372166..7f90cde8a3243 100644 --- a/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; const Rect bounds = Rect.fromLTWH(0, 0, 800, 600); @@ -19,10 +19,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); test('draws paragraphs with placeholders', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); diff --git a/lib/web_ui/test/html/paragraph/shadows_golden_test.dart b/lib/web_ui/test/html/paragraph/shadows_golden_test.dart index f5119e1c62209..3c6f30fec8fcd 100644 --- a/lib/web_ui/test/html/paragraph/shadows_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/shadows_golden_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; const Rect bounds = Rect.fromLTWH(0, 0, 800, 600); @@ -17,10 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); test('paints multiple shadows', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); diff --git a/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart b/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart index 3350c212c97a3..a4164b2dd6b35 100644 --- a/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart @@ -8,7 +8,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'text_scuba.dart'; typedef PaintTest = void Function(RecordingCanvas recordingCanvas); @@ -23,10 +23,7 @@ Future testMain() async { viewportSize: const Size(600, 600), ); - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); void paintTest(EngineCanvas canvas, PaintTest painter) { const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600); diff --git a/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart b/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart index 6d922396d99fe..a97bce79940f6 100644 --- a/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart @@ -8,7 +8,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide window; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'text_scuba.dart'; const String threeLines = 'First\nSecond\nThird'; @@ -25,10 +25,7 @@ Future testMain() async { viewportSize: const Size(800, 800), ); - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); testEachCanvas('maxLines clipping', (EngineCanvas canvas) { Offset offset = Offset.zero; diff --git a/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart b/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart index 15a961ced6343..4474bba0b9f33 100644 --- a/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart @@ -6,7 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../../common/test_initialization.dart'; +import '../screenshot.dart'; import 'helper.dart'; import 'text_scuba.dart'; @@ -19,10 +19,8 @@ Future testMain() async { viewportSize: const Size(600, 600), ); - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + + setUpStableTestFonts(); testEachCanvas('draws paragraphs with placeholders', (EngineCanvas canvas) { const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600); diff --git a/lib/web_ui/test/html/path_metrics_golden_test.dart b/lib/web_ui/test/html/path_metrics_golden_test.dart index 6ce6bd660da06..4a3ea96f7288d 100644 --- a/lib/web_ui/test/html/path_metrics_golden_test.dart +++ b/lib/web_ui/test/html/path_metrics_golden_test.dart @@ -8,7 +8,6 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; import '../common/matchers.dart'; -import '../common/test_initialization.dart'; import 'screenshot.dart'; void main() { @@ -23,9 +22,12 @@ Future testMain() async { const Color redAccentColor = Color(0xFFFF1744); const double kDashLength = 5.0; - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); test('Should calculate tangent on line', () async { final Path path = Path(); diff --git a/lib/web_ui/test/html/path_transform_golden_test.dart b/lib/web_ui/test/html/path_transform_golden_test.dart index 68a2b417c0a22..cb5b398169540 100644 --- a/lib/web_ui/test/html/path_transform_golden_test.dart +++ b/lib/web_ui/test/html/path_transform_golden_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../common/test_initialization.dart'; import 'screenshot.dart'; void main() { @@ -21,9 +20,12 @@ Future testMain() async { const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); test('Should draw transformed line.', () async { final RecordingCanvas rc = diff --git a/lib/web_ui/test/html/recording_canvas_golden_test.dart b/lib/web_ui/test/html/recording_canvas_golden_test.dart index 47fa8dabd0f5b..3a32e02830ed7 100644 --- a/lib/web_ui/test/html/recording_canvas_golden_test.dart +++ b/lib/web_ui/test/html/recording_canvas_golden_test.dart @@ -12,16 +12,18 @@ import 'package:ui/ui.dart' hide TextStyle; import 'package:web_engine_tester/golden_tester.dart'; import '../common/matchers.dart'; -import '../common/test_initialization.dart'; +import 'screenshot.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + }); + + setUpStableTestFonts(); const double screenWidth = 600.0; const double screenHeight = 800.0; diff --git a/lib/web_ui/test/html/screenshot.dart b/lib/web_ui/test/html/screenshot.dart index 445744c51cfb4..3e2226ae68803 100644 --- a/lib/web_ui/test/html/screenshot.dart +++ b/lib/web_ui/test/html/screenshot.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; @@ -69,3 +70,14 @@ Future sceneScreenshot(SurfaceSceneBuilder sceneBuilder, String fileName, sceneElement?.remove(); } } + + +/// Configures the test to use bundled Roboto and Ahem fonts to avoid golden +/// screenshot differences due to differences in the preinstalled system fonts. +void setUpStableTestFonts() { + setUpAll(() async { + await ui.webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); +} diff --git a/lib/web_ui/test/html/shaders/gradient_golden_test.dart b/lib/web_ui/test/html/shaders/gradient_golden_test.dart index 8dfa8fc66503f..5aebc78ef0070 100644 --- a/lib/web_ui/test/html/shaders/gradient_golden_test.dart +++ b/lib/web_ui/test/html/shaders/gradient_golden_test.dart @@ -10,7 +10,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; // TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. @@ -26,7 +25,11 @@ Future testMain() async { const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); const Rect region = Rect.fromLTWH(0, 0, 500, 240); - setUpUnitTests(); + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + }); + + setUpStableTestFonts(); test('Paints sweep gradient rectangles', () async { final RecordingCanvas canvas = diff --git a/lib/web_ui/test/html/shaders/image_shader_golden_test.dart b/lib/web_ui/test/html/shaders/image_shader_golden_test.dart index ba7d7fc52ab23..405250062d462 100644 --- a/lib/web_ui/test/html/shaders/image_shader_golden_test.dart +++ b/lib/web_ui/test/html/shaders/image_shader_golden_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; // TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. @@ -25,9 +24,12 @@ Future testMain() async { const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); final HtmlImage testImage = createTestImage(); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); void drawShapes(RecordingCanvas rc, SurfacePaint paint, Rect shaderRect) { /// Rect. diff --git a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart index bf1ab3752cebd..742067e4914e0 100644 --- a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart +++ b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart @@ -8,7 +8,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; // TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. @@ -19,9 +18,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); test('Should draw linear gradient using rectangle.', () async { final RecordingCanvas rc = diff --git a/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart index 27f64b3022a7a..51bfab70965a1 100644 --- a/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart +++ b/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart @@ -6,7 +6,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' hide TextStyle; -import '../../common/test_initialization.dart'; import '../screenshot.dart'; void main() { @@ -14,9 +13,13 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); Future testGradient(String fileName, Shader shader, {Rect paintRect = const Rect.fromLTRB(50, 50, 300, 300), diff --git a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart index b2fd11110fd59..6c11494b9f8f0 100644 --- a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart +++ b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart @@ -10,8 +10,6 @@ import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../../common/test_initialization.dart'; - /// To debug compositing failures on browsers, set this flag to true and run /// flutter run -d chrome --web-renderer=html /// test/golden_tests/engine/shader_mask_golden_test.dart --profile @@ -31,13 +29,9 @@ Future main() async { // https://github.com/flutter/flutter/issues/86623 Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); - setUpAll(() async { debugShowClipLayers = true; + await webOnlyInitializePlatform(); }); setUp(() async { @@ -47,6 +41,8 @@ Future testMain() async { scene.remove(); } initWebGl(); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); }); /// Should render the picture unmodified. diff --git a/lib/web_ui/test/html/shadow_golden_test.dart b/lib/web_ui/test/html/shadow_golden_test.dart index ca0b7ccee366b..7d9e1e04308e9 100644 --- a/lib/web_ui/test/html/shadow_golden_test.dart +++ b/lib/web_ui/test/html/shadow_golden_test.dart @@ -9,7 +9,7 @@ import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; +import 'screenshot.dart'; const Color _kShadowColor = Color.fromARGB(255, 0, 0, 0); @@ -22,10 +22,7 @@ Future testMain() async { late SurfaceSceneBuilder builder; - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpStableTestFonts(); setUp(() { builder = SurfaceSceneBuilder(); diff --git a/lib/web_ui/test/html/text_test.dart b/lib/web_ui/test/html/text_test.dart index 72a624a6335c0..2b7e629fb2917 100644 --- a/lib/web_ui/test/html/text_test.dart +++ b/lib/web_ui/test/html/text_test.dart @@ -7,11 +7,11 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; + import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; import '../common/matchers.dart'; -import '../common/test_initialization.dart'; import 'paragraph/helper.dart'; void main() { @@ -21,7 +21,7 @@ void main() { Future testMain() async { const double baselineRatio = 1.1662499904632568; - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); late String fallback; setUp(() { diff --git a/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart b/lib/web_ui/test/text/canvas_paragraph_builder_test.dart similarity index 99% rename from lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart rename to lib/web_ui/test/text/canvas_paragraph_builder_test.dart index 7d51f2b4964f6..0989d248bdb53 100644 --- a/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart +++ b/lib/web_ui/test/text/canvas_paragraph_builder_test.dart @@ -7,8 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../../common/test_initialization.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; /// Some text measurements are sensitive to browser implementations. Position /// info in the following tests only pass in Chrome, they are slightly different @@ -32,7 +31,7 @@ void main() { } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); test('empty paragraph', () { final CanvasParagraph paragraph1 = rich( diff --git a/lib/web_ui/test/html/text/canvas_paragraph_test.dart b/lib/web_ui/test/text/canvas_paragraph_test.dart similarity index 99% rename from lib/web_ui/test/html/text/canvas_paragraph_test.dart rename to lib/web_ui/test/text/canvas_paragraph_test.dart index dd652d32d82f7..c7dbbecabf059 100644 --- a/lib/web_ui/test/html/text/canvas_paragraph_test.dart +++ b/lib/web_ui/test/text/canvas_paragraph_test.dart @@ -7,15 +7,14 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../common/test_initialization.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); group('$CanvasParagraph.getBoxesForRange', () { test('return empty list for invalid ranges', () { diff --git a/lib/web_ui/test/html/text/font_collection_test.dart b/lib/web_ui/test/text/font_collection_test.dart similarity index 69% rename from lib/web_ui/test/html/text/font_collection_test.dart rename to lib/web_ui/test/text/font_collection_test.dart index 3f7485ff71c40..caf27b9c722cf 100644 --- a/lib/web_ui/test/html/text/font_collection_test.dart +++ b/lib/web_ui/test/text/font_collection_test.dart @@ -7,43 +7,32 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; -import '../../common/fake_asset_manager.dart'; -import '../../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { - group('$HtmlFontCollection', () { - setUpUnitTests(); - + group('$FontManager', () { + late FontManager fontManager; const String testFontUrl = '/assets/fonts/ahem.ttf'; - late FakeAssetScope testScope; setUp(() { - testScope = fakeAssetManager.pushAssetScope(); - testScope.setAssetPassthrough(testFontUrl); - - // Clear the fonts before the test begins to wipe out the fonts from the - // test initialization. - domDocument.fonts!.clear(); + fontManager = FontManager(); }); tearDown(() { - fakeAssetManager.popAssetScope(testScope); + domDocument.fonts!.clear(); }); group('regular special characters', () { test('Register Asset with no special characters', () async { const String testFontFamily = 'Ahem'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); @@ -57,12 +46,10 @@ void testMain() { const String testFontFamily = 'Ahem ahem ahem'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); @@ -78,12 +65,10 @@ void testMain() { const String testFontFamily = 'AhEm'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); @@ -96,15 +81,13 @@ void testMain() { test('Register Asset with descriptor', () async { const String testFontFamily = 'Ahem'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, { - 'weight': 'bold' - }) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const { + 'weight': 'bold', + }); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { expect(f.weight, 'bold'); @@ -122,12 +105,10 @@ void testMain() { const String testFontFamily = '/Ahem'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); @@ -149,12 +130,10 @@ void testMain() { const String testFontFamily = 'Ahem!!ahem'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); @@ -176,12 +155,10 @@ void testMain() { const String testFontFamily = 'Ahem ,ahem'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); @@ -204,12 +181,10 @@ void testMain() { const String testFontFamily = 'Ahem 1998'; final List fontFamilyList = []; - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); + fontManager.downloadAsset( + testFontFamily, 'url($testFontUrl)', const {}); + await fontManager.downloadAllFonts(); + fontManager.registerDownloadedFonts(); domDocument.fonts! .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); diff --git a/lib/web_ui/test/html/text/font_loading_test.dart b/lib/web_ui/test/text/font_loading_test.dart similarity index 93% rename from lib/web_ui/test/html/text/font_loading_test.dart rename to lib/web_ui/test/text/font_loading_test.dart index 702c70b66ff3d..d96ca6acb22f0 100644 --- a/lib/web_ui/test/html/text/font_loading_test.dart +++ b/lib/web_ui/test/text/font_loading_test.dart @@ -11,14 +11,12 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../common/test_initialization.dart'; - void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); group('loadFontFromList', () { const String testFontUrl = '/assets/fonts/ahem.ttf'; @@ -26,11 +24,10 @@ Future testMain() async { domDocument.fonts!.clear(); }); - test('returns normally from invalid font buffer', () async { + test('surfaces error from invalid font buffer', () async { await expectLater( - () async => ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'), - returnsNormally - ); + ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'), + throwsA(const TypeMatcher())); }, // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 skip: browserEngine == BrowserEngine.webkit); diff --git a/lib/web_ui/test/html/text/layout_fragmenter_test.dart b/lib/web_ui/test/text/layout_fragmenter_test.dart similarity index 99% rename from lib/web_ui/test/html/text/layout_fragmenter_test.dart rename to lib/web_ui/test/text/layout_fragmenter_test.dart index fe8a8ca5a2852..a36dc89433db7 100644 --- a/lib/web_ui/test/html/text/layout_fragmenter_test.dart +++ b/lib/web_ui/test/text/layout_fragmenter_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; final EngineTextStyle defaultStyle = EngineTextStyle.only( color: const Color(0xFFFF0000), diff --git a/lib/web_ui/test/html/text/layout_service_helper.dart b/lib/web_ui/test/text/layout_service_helper.dart similarity index 100% rename from lib/web_ui/test/html/text/layout_service_helper.dart rename to lib/web_ui/test/text/layout_service_helper.dart diff --git a/lib/web_ui/test/html/text/layout_service_plain_test.dart b/lib/web_ui/test/text/layout_service_plain_test.dart similarity index 99% rename from lib/web_ui/test/html/text/layout_service_plain_test.dart rename to lib/web_ui/test/text/layout_service_plain_test.dart index d08d88b15e493..96c3e047cecd3 100644 --- a/lib/web_ui/test/html/text/layout_service_plain_test.dart +++ b/lib/web_ui/test/text/layout_service_plain_test.dart @@ -7,8 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../common/test_initialization.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; import 'layout_service_helper.dart'; const bool skipWordSpacing = true; @@ -18,7 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); test('no text', () { final CanvasParagraph paragraph = CanvasParagraphBuilder(ahemStyle).build(); diff --git a/lib/web_ui/test/html/text/layout_service_rich_test.dart b/lib/web_ui/test/text/layout_service_rich_test.dart similarity index 98% rename from lib/web_ui/test/html/text/layout_service_rich_test.dart rename to lib/web_ui/test/text/layout_service_rich_test.dart index 2d6173caa0ad1..381da5e228183 100644 --- a/lib/web_ui/test/html/text/layout_service_rich_test.dart +++ b/lib/web_ui/test/text/layout_service_rich_test.dart @@ -8,8 +8,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -import '../../common/test_initialization.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; import 'layout_service_helper.dart'; void main() { @@ -17,7 +16,7 @@ void main() { } Future testMain() async { - setUpUnitTests(); + await initializeTestFlutterViewEmbedder(); test('does not crash on empty spans', () { final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { diff --git a/lib/web_ui/test/html/text/line_breaker_test.dart b/lib/web_ui/test/text/line_breaker_test.dart similarity index 99% rename from lib/web_ui/test/html/text/line_breaker_test.dart rename to lib/web_ui/test/text/line_breaker_test.dart index 2bd99fcc20e4d..275c4de47b60f 100644 --- a/lib/web_ui/test/html/text/line_breaker_test.dart +++ b/lib/web_ui/test/text/line_breaker_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; import 'line_breaker_test_helper.dart'; import 'line_breaker_test_raw_data.dart'; diff --git a/lib/web_ui/test/html/text/line_breaker_test_helper.dart b/lib/web_ui/test/text/line_breaker_test_helper.dart similarity index 100% rename from lib/web_ui/test/html/text/line_breaker_test_helper.dart rename to lib/web_ui/test/text/line_breaker_test_helper.dart diff --git a/lib/web_ui/test/html/text/line_breaker_test_raw_data.dart b/lib/web_ui/test/text/line_breaker_test_raw_data.dart similarity index 100% rename from lib/web_ui/test/html/text/line_breaker_test_raw_data.dart rename to lib/web_ui/test/text/line_breaker_test_raw_data.dart diff --git a/lib/web_ui/test/html/text/text_direction_test.dart b/lib/web_ui/test/text/text_direction_test.dart similarity index 99% rename from lib/web_ui/test/html/text/text_direction_test.dart rename to lib/web_ui/test/text/text_direction_test.dart index 9049c0948ef54..3c39220c0159e 100644 --- a/lib/web_ui/test/html/text/text_direction_test.dart +++ b/lib/web_ui/test/text/text_direction_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; -import '../paragraph/helper.dart'; +import '../html/paragraph/helper.dart'; void main() { internalBootstrapBrowserTest(() => testMain); diff --git a/lib/web_ui/test/html/text/word_breaker_test.dart b/lib/web_ui/test/text/word_breaker_test.dart similarity index 100% rename from lib/web_ui/test/html/text/word_breaker_test.dart rename to lib/web_ui/test/text/word_breaker_test.dart diff --git a/lib/web_ui/test/ui/README.md b/lib/web_ui/test/ui/README.md index 9ce5ea6d15f44..acd49655295e0 100644 --- a/lib/web_ui/test/ui/README.md +++ b/lib/web_ui/test/ui/README.md @@ -8,5 +8,5 @@ In practice, this means these tests should only use `dart:ui` APIs or ## Notes -These tests should call `setUpUnitTests()` at the top level to initialize the +These tests should call `setUpUiTest()` at the top level to initialize the renderer they are expected to run. diff --git a/lib/web_ui/test/ui/canvas_curves_golden_test.dart b/lib/web_ui/test/ui/canvas_curves_golden_test.dart index f96de710c13ba..e067e9f944b8f 100644 --- a/lib/web_ui/test/ui/canvas_curves_golden_test.dart +++ b/lib/web_ui/test/ui/canvas_curves_golden_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -17,9 +16,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUiTest(); const Rect region = Rect.fromLTWH(0, 0, 300, 300); diff --git a/lib/web_ui/test/ui/canvas_lines_golden_test.dart b/lib/web_ui/test/ui/canvas_lines_golden_test.dart index 58c7abe6d7689..df3c00bee99a0 100644 --- a/lib/web_ui/test/ui/canvas_lines_golden_test.dart +++ b/lib/web_ui/test/ui/canvas_lines_golden_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -15,9 +14,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUiTest(); const Rect region = Rect.fromLTWH(0, 0, 300, 300); diff --git a/lib/web_ui/test/ui/canvas_test.dart b/lib/web_ui/test/ui/canvas_test.dart index 484a40f1be63b..1cb3db0ac3a99 100644 --- a/lib/web_ui/test/ui/canvas_test.dart +++ b/lib/web_ui/test/ui/canvas_test.dart @@ -11,16 +11,14 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; import '../common/matchers.dart'; -import '../common/test_initialization.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUiTest(); final bool deviceClipRoundsOut = renderer is! HtmlRenderer; runCanvasTests(deviceClipRoundsOut: deviceClipRoundsOut); diff --git a/lib/web_ui/test/ui/color_test.dart b/lib/web_ui/test/ui/color_test.dart index 2ac2df7595d02..7236628b1f6cf 100644 --- a/lib/web_ui/test/ui/color_test.dart +++ b/lib/web_ui/test/ui/color_test.dart @@ -6,7 +6,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -17,7 +17,7 @@ class NotAColor extends Color { } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); test('color accessors should work', () { const Color foo = Color(0x12345678); diff --git a/lib/web_ui/test/ui/font_collection_test.dart b/lib/web_ui/test/ui/font_collection_test.dart deleted file mode 100644 index a999bedd15b4d..0000000000000 --- a/lib/web_ui/test/ui/font_collection_test.dart +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; - -import '../common/fake_asset_manager.dart'; -import '../common/test_initialization.dart'; -import 'utils.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -Future testMain() async { - setUpUnitTests(); - - late FakeAssetScope testScope; - setUp(() { - mockHttpFetchResponseFactory = null; - testScope = fakeAssetManager.pushAssetScope(); - }); - - tearDown(() { - fakeAssetManager.popAssetScope(testScope); - }); - - test('Loading valid font from data succeeds without family name (except in HTML renderer)', () async { - final FlutterFontCollection collection = renderer.fontCollection; - final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf'); - expect( - await collection.loadFontFromList(ahemData.asUint8List()), - !isHtml, // HtmlFontCollection requires family name - ); - }); - - test('Loading valid font from data succeeds with family name', () async { - final FlutterFontCollection collection = renderer.fontCollection; - final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf'); - expect( - await collection.loadFontFromList(ahemData.asUint8List(), fontFamily: 'FamilyName'), - true - ); - }); - - test('Loading invalid font from data returns false', () async { - final FlutterFontCollection collection = renderer.fontCollection; - final List invalidFontData = utf8.encode('This is not valid font data'); - expect( - await collection.loadFontFromList(Uint8List.fromList(invalidFontData), fontFamily: 'FamilyName'), - false - ); - }); - - test('Loading valid asset fonts succeds', () async { - testScope.setAssetPassthrough(robotoVariableFontUrl); - testScope.setAssetPassthrough(robotoTestFontUrl); - testScope.setAssetPassthrough(ahemFontUrl); - - final FlutterFontCollection collection = renderer.fontCollection; - final AssetFontsResult result = await collection.loadAssetFonts(FontManifest([ - FontFamily(robotoFontFamily, [ - FontAsset(robotoVariableFontUrl, {}), - FontAsset(robotoTestFontUrl, {'weight': 'bold'}), - ]), - FontFamily(ahemFontFamily, [ - FontAsset(ahemFontUrl, {}) - ]), - ])); - expect(result.loadedFonts, [ - robotoVariableFontUrl, - robotoTestFontUrl, - ahemFontUrl, - ]); - expect(result.fontFailures, isEmpty); - }); - - test('Loading asset fonts reports when font not found', () async { - testScope.setAssetPassthrough(robotoVariableFontUrl); - testScope.setAssetPassthrough(robotoTestFontUrl); - - const String invalidFontUrl = 'assets/invalid_font_url.ttf'; - - final FlutterFontCollection collection = renderer.fontCollection; - final AssetFontsResult result = await collection.loadAssetFonts(FontManifest([ - FontFamily(robotoFontFamily, [ - FontAsset(robotoVariableFontUrl, {}), - FontAsset(robotoTestFontUrl, {'weight': 'bold'}), - ]), - FontFamily(ahemFontFamily, [ - FontAsset(invalidFontUrl, {}) - ]), - ])); - expect(result.loadedFonts, [ - robotoVariableFontUrl, - robotoTestFontUrl, - ]); - expect(result.fontFailures, hasLength(1)); - if (isHtml) { - // The HTML renderer doesn't have a way to differentiate 404's from other - // download errors. - expect(result.fontFailures[invalidFontUrl], isA()); - } else { - expect(result.fontFailures[invalidFontUrl], isA()); - } - }); - - test('Loading asset fonts reports when a font has invalid data', () async { - const String invalidFontUrl = 'assets/invalid_font_data.ttf'; - - testScope.setAssetPassthrough(robotoVariableFontUrl); - testScope.setAssetPassthrough(robotoTestFontUrl); - testScope.setAssetPassthrough(invalidFontUrl); - - mockHttpFetchResponseFactory = (String url) async { - if (url == invalidFontUrl) { - return MockHttpFetchResponse( - url: url, - status: 200, - payload: MockHttpFetchPayload( - byteBuffer: stringAsUtf8Data('this is invalid data').buffer - ), - ); - } - return null; - }; - - final FlutterFontCollection collection = renderer.fontCollection; - final AssetFontsResult result = await collection.loadAssetFonts(FontManifest([ - FontFamily(robotoFontFamily, [ - FontAsset(robotoVariableFontUrl, {}), - FontAsset(robotoTestFontUrl, {'weight': 'bold'}), - ]), - FontFamily(ahemFontFamily, [ - FontAsset(invalidFontUrl, {}) - ]), - ])); - expect(result.loadedFonts, [ - robotoVariableFontUrl, - robotoTestFontUrl, - ]); - expect(result.fontFailures, hasLength(1)); - if (isHtml) { - // The HTML renderer doesn't have a way to differentiate invalid data - // from other download errors. - expect(result.fontFailures[invalidFontUrl], isA()); - } else { - expect(result.fontFailures[invalidFontUrl], isA()); - } - }); -} diff --git a/lib/web_ui/test/ui/fragment_shader_test.dart b/lib/web_ui/test/ui/fragment_shader_test.dart index da69653d0a237..1fef73e8e0f55 100644 --- a/lib/web_ui/test/ui/fragment_shader_test.dart +++ b/lib/web_ui/test/ui/fragment_shader_test.dart @@ -11,8 +11,6 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/fake_asset_manager.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -42,9 +40,7 @@ const String kVoronoiShaderSksl = r''' '''; Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUiTest(); const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300); diff --git a/lib/web_ui/test/ui/gradient_golden_test.dart b/lib/web_ui/test/ui/gradient_golden_test.dart index f678f4d4dbb82..96ba0f7d7fd20 100644 --- a/lib/web_ui/test/ui/gradient_golden_test.dart +++ b/lib/web_ui/test/ui/gradient_golden_test.dart @@ -10,7 +10,6 @@ import 'package:ui/src/engine/browser_detection.dart'; import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -18,9 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUiTest(); const Rect region = Rect.fromLTWH(0, 0, 300, 300); diff --git a/lib/web_ui/test/ui/gradient_test.dart b/lib/web_ui/test/ui/gradient_test.dart index ae13bb05a9355..69d0ba9d7aa08 100644 --- a/lib/web_ui/test/ui/gradient_test.dart +++ b/lib/web_ui/test/ui/gradient_test.dart @@ -7,14 +7,14 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); test('Gradient.radial with no focal point', () { expect( @@ -26,7 +26,7 @@ Future testMain() async { TileMode.mirror), isNotNull, ); - }); + }, skip: isSkwasm); // this is just a radial gradient, focal point is discarded. test('radial center and focal == Offset.zero and focalRadius == 0.0 is ok', @@ -42,7 +42,7 @@ Future testMain() async { Offset.zero, ), isNotNull); - }); + }, skip: isSkwasm); test('radial center != focal and focalRadius == 0.0 is ok', () { expect( @@ -56,7 +56,7 @@ Future testMain() async { const Offset(2.0, 2.0), ), isNotNull); - }); + }, skip: isSkwasm); // this would result in div/0 on skia side. test('radial center and focal == Offset.zero and focalRadius != 0.0 assert', @@ -74,5 +74,5 @@ Future testMain() async { ), throwsA(const TypeMatcher()), ); - }); + }, skip: isSkwasm); } diff --git a/lib/web_ui/test/ui/paragraph_builder_test.dart b/lib/web_ui/test/ui/paragraph_builder_test.dart index fe31eed072861..51db06b23e719 100644 --- a/lib/web_ui/test/ui/paragraph_builder_test.dart +++ b/lib/web_ui/test/ui/paragraph_builder_test.dart @@ -6,7 +6,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -14,10 +13,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUiTest(); test('Should be able to build and layout a paragraph', () { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); diff --git a/lib/web_ui/test/ui/path_metrics_test.dart b/lib/web_ui/test/ui/path_metrics_test.dart index ba820f7590c59..f211b8a6b3219 100644 --- a/lib/web_ui/test/ui/path_metrics_test.dart +++ b/lib/web_ui/test/ui/path_metrics_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart'; import '../common/matchers.dart'; -import '../common/test_initialization.dart'; +import 'utils.dart'; const double kTolerance = 0.1; @@ -18,7 +18,7 @@ void main() { } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); group('PathMetric length', () { test('empty path', () { final Path path = Path(); diff --git a/lib/web_ui/test/ui/path_test.dart b/lib/web_ui/test/ui/path_test.dart index 94094a03b50ba..5b78add83b697 100644 --- a/lib/web_ui/test/ui/path_test.dart +++ b/lib/web_ui/test/ui/path_test.dart @@ -8,7 +8,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -16,7 +15,7 @@ void main() { } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); test('path getBounds', () { const Rect r = Rect.fromLTRB(1.0, 3.0, 5.0, 7.0); final Path p = Path()..addRect(r); diff --git a/lib/web_ui/test/ui/picture_test.dart b/lib/web_ui/test/ui/picture_test.dart index 297d8b50f403a..daa9f9d8075c9 100644 --- a/lib/web_ui/test/ui/picture_test.dart +++ b/lib/web_ui/test/ui/picture_test.dart @@ -6,14 +6,14 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart' as ui; -import '../common/test_initialization.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); test('Picture construction invokes onCreate once', () async { int onCreateInvokedCount = 0; diff --git a/lib/web_ui/test/ui/rect_test.dart b/lib/web_ui/test/ui/rect_test.dart index 3fa4eb0d14d01..d25c7af56fa82 100644 --- a/lib/web_ui/test/ui/rect_test.dart +++ b/lib/web_ui/test/ui/rect_test.dart @@ -6,14 +6,14 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); test('rect accessors', () { const Rect r = Rect.fromLTRB(1.0, 3.0, 5.0, 7.0); expect(r.left, equals(1.0)); diff --git a/lib/web_ui/test/ui/rrect_test.dart b/lib/web_ui/test/ui/rrect_test.dart index 958c7aa137cfa..00f8750e6a048 100644 --- a/lib/web_ui/test/ui/rrect_test.dart +++ b/lib/web_ui/test/ui/rrect_test.dart @@ -6,14 +6,14 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; import 'package:ui/ui.dart'; -import '../common/test_initialization.dart'; +import 'utils.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } Future testMain() async { - setUpUnitTests(); + setUpUiTest(); test('RRect.contains()', () { final RRect rrect = RRect.fromRectAndCorners( const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), diff --git a/lib/web_ui/test/ui/scene_builder_test.dart b/lib/web_ui/test/ui/scene_builder_test.dart index b8f59b67f3522..18bb6a4ab7076 100644 --- a/lib/web_ui/test/ui/scene_builder_test.dart +++ b/lib/web_ui/test/ui/scene_builder_test.dart @@ -10,7 +10,6 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -18,10 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUiTest(); group('${ui.SceneBuilder}', () { const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300); @@ -37,6 +33,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_centered_circle.png', region: region); }); @@ -61,6 +58,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_rotated_rounded_square.png', region: region); }); @@ -76,6 +74,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_circle_clip_rect.png', region: region); }); @@ -94,6 +93,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_circle_clip_rrect.png', region: region); }); @@ -110,6 +110,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_rectangle_clip_circular_path.png', region: region); }); @@ -138,6 +139,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_opacity_circles_on_square.png', region: region); }); @@ -178,6 +180,7 @@ Future testMain() async { })); await renderer.renderScene(sceneBuilder.build()); + await awaitNextFrame(); await matchGoldenFile('scene_builder_shader_mask.png', region: region); }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 }); diff --git a/lib/web_ui/test/ui/shadow_test.dart b/lib/web_ui/test/ui/shadow_test.dart index eb6b364a76947..b1bb65ad0c924 100644 --- a/lib/web_ui/test/ui/shadow_test.dart +++ b/lib/web_ui/test/ui/shadow_test.dart @@ -7,7 +7,6 @@ import 'package:test/test.dart'; import 'package:ui/ui.dart' as ui; import 'package:web_engine_tester/golden_tester.dart'; -import '../common/test_initialization.dart'; import 'utils.dart'; void main() { @@ -17,10 +16,7 @@ void main() { Future testMain() async { const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300); - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUiTest(); test('Test drawing a shadow of an opaque object', () async { final ui.Picture picture = drawPicture((ui.Canvas canvas) { diff --git a/lib/web_ui/test/ui/utils.dart b/lib/web_ui/test/ui/utils.dart index 38f976ad91f3f..01b42125383bb 100644 --- a/lib/web_ui/test/ui/utils.dart +++ b/lib/web_ui/test/ui/utils.dart @@ -3,11 +3,57 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:js_interop'; +import 'dart:typed_data'; +import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_stub.dart' if (dart.library.ffi) 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart'; +class FakeAssetManager implements AssetManager { + FakeAssetManager(this._parent); + + @override + String get assetsDir => 'assets'; + + @override + String getAssetUrl(String asset) => asset; + + @override + Future load(String assetKey) async { + final ByteData? data = _assetMap[assetKey]; + if (data == null) { + return _parent.load(assetKey); + } + return data; + } + + @override + Future loadAsset(String asset) { + return _parent.loadAsset(asset); + } + + void setAsset(String assetKey, ByteData assetData) { + _assetMap[assetKey] = assetData; + } + + final Map _assetMap = {}; + final AssetManager _parent; +} + +FakeAssetManager fakeAssetManager = FakeAssetManager(WebOnlyMockAssetManager()); + +/// Initializes the renderer for this test. +void setUpUiTest() { + setUpAll(() async { + debugEmulateFlutterTesterEnvironment = true; + await initializeEngine(assetManager: fakeAssetManager); + await renderer.fontCollection.debugDownloadTestFonts(); + renderer.fontCollection.registerDownloadedFonts(); + }); +} + Picture drawPicture(void Function(Canvas) drawCommands) { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); @@ -21,6 +67,13 @@ Future drawPictureUsingCurrentRenderer(Picture picture) async { sb.pushOffset(0, 0); sb.addPicture(Offset.zero, picture); await renderer.renderScene(sb.build()); + await awaitNextFrame(); +} + +Future awaitNextFrame() { + final Completer completer = Completer(); + domWindow.requestAnimationFrame((JSNumber time) => completer.complete()); + return completer.future; } /// Returns [true] if this test is running in the CanvasKit renderer. diff --git a/third_party/canvaskit/BUILD.gn b/third_party/canvaskit/BUILD.gn index af6b617bd8624..062aa10433ac3 100644 --- a/third_party/canvaskit/BUILD.gn +++ b/third_party/canvaskit/BUILD.gn @@ -71,15 +71,7 @@ copy("canvaskit_chromium_group") { # This toolchain is only to be used by skwasm_group below. wasm_toolchain("skwasm") { 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" - - skia_use_libjpeg_turbo_decode = false - skia_use_libpng_decode = false - skia_use_libwebp_decode = false + skia_enable_skparagraph = false # skwasm is multithreaded wasm_use_pthreads = true diff --git a/web_sdk/web_engine_tester/lib/golden_tester.dart b/web_sdk/web_engine_tester/lib/golden_tester.dart index 5e743b4ab7fc6..904a7eb91ed28 100644 --- a/web_sdk/web_engine_tester/lib/golden_tester.dart +++ b/web_sdk/web_engine_tester/lib/golden_tester.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:js_interop'; import 'package:test/test.dart'; // ignore: implementation_imports @@ -50,14 +49,6 @@ enum PixelComparison { /// [pixelComparison] determines the algorithm used to compare pixels. Uses /// fuzzy comparison by default. Future matchGoldenFile(String filename, {Rect? region}) async { - // It is difficult to deterministically tell when rendered content is actually - // visible to the user, so we pump 15 frames to make sure that the content is - // has reached the screen. This is at the recommendation of the Chrome team, - // and they use this same thing in their screenshot unit tests. - for (int i = 0; i < 15; i++) { - await awaitNextFrame(); - } - if (!filename.endsWith('.png')) { throw ArgumentError('Filename must end in .png or SkiaGold will ignore it.'); } @@ -85,10 +76,3 @@ Future matchGoldenFile(String filename, {Rect? region}) async { } fail(response); } - -/// Waits for one frame to complete rendering -Future awaitNextFrame() { - final Completer completer = Completer(); - domWindow.requestAnimationFrame((JSNumber time) => completer.complete()); - return completer.future; -}