Reland "Skwasm Font Loading (flutter/engine#41756)
This relands https://github.com/flutter/engine/pull/41246, which had to be reverted due to some issues parsing the font manifest.
This commit is contained in:
@@ -1949,6 +1949,7 @@ 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
|
||||
@@ -2001,8 +2002,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
|
||||
@@ -2010,7 +2011,10 @@ 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
|
||||
@@ -2060,12 +2064,15 @@ 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
|
||||
@@ -4545,6 +4552,7 @@ 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
|
||||
@@ -4597,8 +4605,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
|
||||
@@ -4606,7 +4614,10 @@ 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
|
||||
@@ -4656,12 +4667,15 @@ 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
|
||||
|
||||
@@ -108,6 +108,7 @@ 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';
|
||||
|
||||
@@ -8,20 +8,6 @@ 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<String, String> testFontUrls = <String, String>{
|
||||
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
|
||||
@@ -110,79 +96,3 @@ 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<HttpFetchResponse> 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<ByteData> load(String asset) {
|
||||
if (asset == getAssetUrl('AssetManifest.json')) {
|
||||
return Future<ByteData>.value(
|
||||
_toByteData(utf8.encode(defaultAssetManifest)));
|
||||
}
|
||||
if (asset == getAssetUrl('FontManifest.json')) {
|
||||
return Future<ByteData>.value(
|
||||
_toByteData(utf8.encode(defaultFontManifest)));
|
||||
}
|
||||
throw HttpFetchNoPayloadError(asset, status: 404);
|
||||
}
|
||||
|
||||
ByteData _toByteData(List<int> bytes) {
|
||||
final ByteData byteData = ByteData(bytes.length);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
byteData.setUint8(i, bytes[i]);
|
||||
}
|
||||
return byteData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ class FallbackFontDownloadQueue {
|
||||
final Uint8List bytes = downloadedData[url]!;
|
||||
FontFallbackData.instance.registerFallbackFont(font.name, bytes);
|
||||
if (pendingFonts.isEmpty) {
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
(renderer.fontCollection as SkiaFontCollection).registerDownloadedFonts();
|
||||
sendFontChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,9 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
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';
|
||||
import 'package:ui/src/engine.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
|
||||
@@ -77,15 +69,18 @@ class SkiaFontCollection implements FlutterFontCollection {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadFontFromList(Uint8List list, {String? fontFamily}) async {
|
||||
Future<bool> 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;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure CanvasKit is actually loaded
|
||||
await renderer.initialize();
|
||||
|
||||
final SkTypeface? typeface =
|
||||
canvasKit.Typeface.MakeFreeTypeFaceFromData(list.buffer);
|
||||
if (typeface != null) {
|
||||
@@ -93,53 +88,65 @@ class SkiaFontCollection implements FlutterFontCollection {
|
||||
_registerWithFontProvider();
|
||||
} else {
|
||||
printWarning('Failed to parse font family "$fontFamily"');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Loads fonts from `FontManifest.json`.
|
||||
@override
|
||||
Future<void> 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<dynamic>? fontManifest = json.decode(utf8.decode(data)) as List<dynamic>?;
|
||||
if (fontManifest == null) {
|
||||
throw AssertionError(
|
||||
'There was a problem trying to load FontManifest.json');
|
||||
}
|
||||
|
||||
final List<Future<UnregisteredFont?>> pendingFonts = <Future<UnregisteredFont?>>[];
|
||||
|
||||
for (final Map<String, dynamic> fontFamily
|
||||
in fontManifest.cast<Map<String, dynamic>>()) {
|
||||
final String family = fontFamily.readString('family');
|
||||
final List<dynamic> fontAssets = fontFamily.readList('fonts');
|
||||
for (final dynamic fontAssetItem in fontAssets) {
|
||||
final Map<String, dynamic> fontAsset = fontAssetItem as Map<String, dynamic>;
|
||||
final String asset = fontAsset.readString('asset');
|
||||
_downloadFont(pendingFonts, assetManager.getAssetUrl(asset), family);
|
||||
Future<AssetFontsResult> loadAssetFonts(FontManifest manifest) async {
|
||||
final List<Future<FontDownloadResult>> pendingDownloads = <Future<FontDownloadResult>>[];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 (!_isFontFamilyDownloaded('Roboto')) {
|
||||
if (!loadedRoboto) {
|
||||
// Download Roboto and add it to the font buffers.
|
||||
_downloadFont(pendingFonts, _robotoUrl, 'Roboto');
|
||||
pendingDownloads.add(_downloadFont('Roboto', _robotoUrl, 'Roboto'));
|
||||
}
|
||||
|
||||
final List<UnregisteredFont?> completedPendingFonts = await Future.wait(pendingFonts);
|
||||
_unregisteredFonts.addAll(completedPendingFonts.whereType<UnregisteredFont>());
|
||||
final Map<String, FontLoadError> fontFailures = <String, FontLoadError>{};
|
||||
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<String> loadedFonts = <String>[];
|
||||
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);
|
||||
}
|
||||
|
||||
@override
|
||||
void registerDownloadedFonts() {
|
||||
RegisteredFont? makeRegisterFont(ByteBuffer buffer, String url, String family) {
|
||||
final Uint8List bytes = buffer.asUint8List();
|
||||
@@ -169,61 +176,30 @@ class SkiaFontCollection implements FlutterFontCollection {
|
||||
_registerWithFontProvider();
|
||||
}
|
||||
|
||||
/// 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<void> debugDownloadTestFonts() async {
|
||||
final List<Future<UnregisteredFont?>> pendingFonts = <Future<UnregisteredFont?>>[];
|
||||
for (final MapEntry<String, String> fontEntry in testFontUrls.entries) {
|
||||
if (!_isFontFamilyDownloaded(fontEntry.key)) {
|
||||
_downloadFont(pendingFonts, fontEntry.value, fontEntry.key);
|
||||
}
|
||||
}
|
||||
final List<UnregisteredFont?> completedPendingFonts = await Future.wait(pendingFonts);
|
||||
final List<UnregisteredFont> fonts = <UnregisteredFont>[
|
||||
UnregisteredFont(
|
||||
EmbeddedTestFont.flutterTest.data.buffer,
|
||||
'<embedded>',
|
||||
EmbeddedTestFont.flutterTest.fontFamily,
|
||||
),
|
||||
...completedPendingFonts.whereType<UnregisteredFont>(),
|
||||
];
|
||||
_unregisteredFonts.addAll(fonts);
|
||||
|
||||
// Ahem must be added to font fallbacks list regardless of where it was
|
||||
// downloaded from.
|
||||
FontFallbackData.instance.globalFontFallbacks.add(ahemFontFamily);
|
||||
}
|
||||
|
||||
void _downloadFont(
|
||||
List<Future<UnregisteredFont?>> waitUnregisteredFonts,
|
||||
Future<FontDownloadResult> _downloadFont(
|
||||
String assetName,
|
||||
String url,
|
||||
String family
|
||||
) {
|
||||
Future<UnregisteredFont?> 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;
|
||||
}
|
||||
}
|
||||
String fontFamily
|
||||
) async {
|
||||
final ByteBuffer fontData;
|
||||
|
||||
_downloadedFontFamilies.add(family);
|
||||
waitUnregisteredFonts.add(downloadFont());
|
||||
// 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));
|
||||
}
|
||||
|
||||
fontData = await response.asByteBuffer();
|
||||
} catch (e) {
|
||||
printWarning('Failed to load font $fontFamily at $url');
|
||||
printWarning(e.toString());
|
||||
return FontDownloadResult.fromError(assetName, FontDownloadError(url, e));
|
||||
}
|
||||
_downloadedFontFamilies.add(fontFamily);
|
||||
return FontDownloadResult.fromFont(assetName, UnregisteredFont(fontData, url, fontFamily));
|
||||
}
|
||||
|
||||
|
||||
@@ -269,3 +245,12 @@ 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;
|
||||
}
|
||||
|
||||
@@ -3,23 +3,12 @@
|
||||
// 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<ui.Codec> skiaInstantiateImageCodec(Uint8List list,
|
||||
[int? targetWidth, int? targetHeight]) {
|
||||
@@ -214,16 +203,16 @@ Future<Uint8List> fetchImage(String url, WebOnlyImageCodecChunkCallback? chunkCa
|
||||
///
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API
|
||||
Future<Uint8List> readChunked(HttpFetchPayload payload, int contentLength, WebOnlyImageCodecChunkCallback chunkCallback) async {
|
||||
final Uint8List result = Uint8List(contentLength);
|
||||
final JSUint8Array1 result = createUint8ArrayFromLength(contentLength);
|
||||
int position = 0;
|
||||
int cumulativeBytesLoaded = 0;
|
||||
await payload.read<Uint8List>((Uint8List chunk) {
|
||||
cumulativeBytesLoaded += chunk.lengthInBytes;
|
||||
await payload.read<JSUint8Array1>((JSUint8Array1 chunk) {
|
||||
cumulativeBytesLoaded += chunk.length.toDart.toInt();
|
||||
chunkCallback(cumulativeBytesLoaded, contentLength);
|
||||
result.setAll(position, chunk);
|
||||
position += chunk.lengthInBytes;
|
||||
result.set(chunk, position.toJS);
|
||||
position += chunk.length.toDart.toInt();
|
||||
});
|
||||
return result;
|
||||
return (result as JSUint8Array).toDart;
|
||||
}
|
||||
|
||||
/// A [ui.Image] backed by an `SkImage` from Skia.
|
||||
|
||||
@@ -15,15 +15,9 @@ 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;
|
||||
|
||||
@@ -439,24 +433,18 @@ bool _shouldReadPixelsUnmodified(VideoFrame videoFrame, ui.ImageByteFormat forma
|
||||
return format == ui.ImageByteFormat.rawRgba && isRgbFrame;
|
||||
}
|
||||
|
||||
@JS('Uint8Array')
|
||||
@staticInterop
|
||||
class _JSUint8Array {
|
||||
external factory _JSUint8Array(JSNumber length);
|
||||
}
|
||||
|
||||
Future<ByteBuffer> 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 JSUint8Array destination = _JSUint8Array(size.toJS) as JSUint8Array;
|
||||
final JSUint8Array1 destination = createUint8ArrayFromLength(size);
|
||||
final JsPromise copyPromise = videoFrame.copyTo(destination);
|
||||
await promiseToFuture<void>(copyPromise);
|
||||
|
||||
// In dart2wasm, `toDart` incurs a copy here. On JS backends, this is a
|
||||
// no-op.
|
||||
return destination.toDart.buffer;
|
||||
return (destination as JSUint8Array).toDart.buffer;
|
||||
}
|
||||
|
||||
Future<Uint8List> encodeVideoFrameAsPng(VideoFrame videoFrame) async {
|
||||
|
||||
@@ -48,6 +48,8 @@ class CanvasKitRenderer implements Renderer {
|
||||
static CanvasKitRenderer get instance => _instance;
|
||||
static late CanvasKitRenderer _instance;
|
||||
|
||||
Future<void>? _initialized;
|
||||
|
||||
@override
|
||||
String get rendererTag => 'canvaskit';
|
||||
|
||||
@@ -66,14 +68,16 @@ class CanvasKitRenderer implements Renderer {
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
if (windowFlutterCanvasKit != null) {
|
||||
canvasKit = windowFlutterCanvasKit!;
|
||||
} else {
|
||||
canvasKit = await downloadCanvasKit();
|
||||
windowFlutterCanvasKit = canvasKit;
|
||||
}
|
||||
|
||||
_instance = this;
|
||||
_initialized ??= () async {
|
||||
if (windowFlutterCanvasKit != null) {
|
||||
canvasKit = windowFlutterCanvasKit!;
|
||||
} else {
|
||||
canvasKit = await downloadCanvasKit();
|
||||
windowFlutterCanvasKit = canvasKit;
|
||||
}
|
||||
_instance = this;
|
||||
}();
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1397,7 +1397,7 @@ class DomXMLHttpRequestEventTarget extends DomEventTarget {}
|
||||
Future<_DomResponse> _rawHttpGet(String url) =>
|
||||
js_util.promiseToFuture<_DomResponse>(domWindow._fetch1(url.toJS));
|
||||
|
||||
typedef MockHttpFetchResponseFactory = Future<MockHttpFetchResponse> Function(
|
||||
typedef MockHttpFetchResponseFactory = Future<MockHttpFetchResponse?> Function(
|
||||
String url);
|
||||
|
||||
MockHttpFetchResponseFactory? mockHttpFetchResponseFactory;
|
||||
@@ -1417,7 +1417,10 @@ MockHttpFetchResponseFactory? mockHttpFetchResponseFactory;
|
||||
/// [httpFetchText] instead.
|
||||
Future<HttpFetchResponse> httpFetch(String url) async {
|
||||
if (mockHttpFetchResponseFactory != null) {
|
||||
return mockHttpFetchResponseFactory!(url);
|
||||
final MockHttpFetchResponse? response = await mockHttpFetchResponseFactory!(url);
|
||||
if (response != null) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
try {
|
||||
final _DomResponse domResponse = await _rawHttpGet(url);
|
||||
@@ -1656,31 +1659,36 @@ typedef MockOnRead = Future<void> Function<T>(HttpFetchReader<T> callback);
|
||||
|
||||
class MockHttpFetchPayload implements HttpFetchPayload {
|
||||
MockHttpFetchPayload({
|
||||
ByteBuffer? byteBuffer,
|
||||
Object? json,
|
||||
String? text,
|
||||
MockOnRead? onRead,
|
||||
required ByteBuffer byteBuffer,
|
||||
int? chunkSize,
|
||||
}) : _byteBuffer = byteBuffer,
|
||||
_json = json,
|
||||
_text = text,
|
||||
_onRead = onRead;
|
||||
_chunkSize = chunkSize ?? 64;
|
||||
|
||||
final ByteBuffer? _byteBuffer;
|
||||
final Object? _json;
|
||||
final String? _text;
|
||||
final MockOnRead? _onRead;
|
||||
final ByteBuffer _byteBuffer;
|
||||
final int _chunkSize;
|
||||
|
||||
@override
|
||||
Future<void> read<T>(HttpFetchReader<T> callback) => _onRead!(callback);
|
||||
Future<void> read<T>(HttpFetchReader<T> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ByteBuffer> asByteBuffer() async => _byteBuffer!;
|
||||
Future<ByteBuffer> asByteBuffer() async => _byteBuffer;
|
||||
|
||||
@override
|
||||
Future<dynamic> json() async => _json!;
|
||||
Future<dynamic> json() async => throw AssertionError('json not supported by mock');
|
||||
|
||||
@override
|
||||
Future<String> text() async => _text!;
|
||||
Future<String> text() async => throw AssertionError('text not supported by mock');
|
||||
}
|
||||
|
||||
/// Indicates a missing HTTP payload when one was expected, such as when
|
||||
@@ -1794,9 +1802,7 @@ extension _DomStreamReaderExtension on _DomStreamReader {
|
||||
class _DomStreamChunk {}
|
||||
|
||||
extension _DomStreamChunkExtension on _DomStreamChunk {
|
||||
@JS('value')
|
||||
external JSAny? get _value;
|
||||
Object? get value => _value?.toObjectShallow;
|
||||
external JSAny? get value;
|
||||
|
||||
@JS('done')
|
||||
external JSBoolean get _done;
|
||||
@@ -1918,6 +1924,10 @@ 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()
|
||||
|
||||
@@ -3,32 +3,127 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:js_interop';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'assets.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
class FontAsset {
|
||||
FontAsset(this.asset, this.descriptors);
|
||||
|
||||
final String asset;
|
||||
final Map<String, String> descriptors;
|
||||
}
|
||||
|
||||
class FontFamily {
|
||||
FontFamily(this.name, this.fontAssets);
|
||||
|
||||
final String name;
|
||||
final List<FontAsset> fontAssets;
|
||||
}
|
||||
|
||||
class FontManifest {
|
||||
FontManifest(this.families);
|
||||
|
||||
final List<FontFamily> families;
|
||||
}
|
||||
|
||||
Future<FontManifest> 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(<FontFamily>[]);
|
||||
}
|
||||
|
||||
final Converter<List<int>, Object?> decoder = const Utf8Decoder().fuse(const JsonDecoder());
|
||||
Object? fontManifestJson;
|
||||
final Sink<List<int>> inputSink = decoder.startChunkedConversion(
|
||||
ChunkedConversionSink<Object?>.withCallback(
|
||||
(List<Object?> 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<FontFamily> families = (fontManifestJson! as List<dynamic>).map(
|
||||
(dynamic fontFamilyJson) {
|
||||
final Map<String, dynamic> fontFamily = fontFamilyJson as Map<String, dynamic>;
|
||||
final String familyName = fontFamily.readString('family');
|
||||
final List<dynamic> fontAssets = fontFamily.readList('fonts');
|
||||
return FontFamily(familyName, fontAssets.map((dynamic fontAssetJson) {
|
||||
String? asset;
|
||||
final Map<String, String> descriptors = <String, String>{};
|
||||
for (final MapEntry<String, dynamic> descriptor in (fontAssetJson as Map<String, dynamic>).entries) {
|
||||
if (descriptor.key == 'asset') {
|
||||
asset = descriptor.value as String;
|
||||
} else {
|
||||
// Sometimes these descriptors are strings, and sometimes numbers, so we stringify them here.
|
||||
descriptors[descriptor.key] = '${descriptor.value}';
|
||||
}
|
||||
}
|
||||
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<String> loadedFonts;
|
||||
|
||||
/// A map of the asset keys to failures for fonts that failed to load.
|
||||
final Map<String, FontLoadError> fontFailures;
|
||||
}
|
||||
|
||||
abstract class FlutterFontCollection {
|
||||
/// Loads a font directly from font data.
|
||||
Future<bool> loadFontFromList(Uint8List list, {String? fontFamily});
|
||||
|
||||
/// Fonts loaded with [loadFontFromList] do not need to be registered
|
||||
/// with [registerDownloadedFonts]. Fonts are both downloaded and registered
|
||||
/// with [loadFontFromList] calls.
|
||||
Future<void> loadFontFromList(Uint8List list, {String? fontFamily});
|
||||
/// Completes when fonts from FontManifest.json have been loaded.
|
||||
Future<AssetFontsResult> loadAssetFonts(FontManifest manifest);
|
||||
|
||||
/// Completes when fonts from FontManifest.json have been downloaded.
|
||||
Future<void> 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<void> debugDownloadTestFonts();
|
||||
// Unregisters all fonts.
|
||||
void clear();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.
|
||||
@@ -201,7 +202,6 @@ Future<void> initializeEngineServices({
|
||||
|
||||
Future<void> initializeRendererCallback () async => renderer.initialize();
|
||||
await Future.wait<void>(<Future<void>>[initializeRendererCallback(), _downloadAssetFonts()]);
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
_initializationState = DebugEngineInitializationState.initializedServices;
|
||||
}
|
||||
|
||||
@@ -248,12 +248,17 @@ void _setAssetManager(AssetManager assetManager) {
|
||||
Future<void> _downloadAssetFonts() async {
|
||||
renderer.fontCollection.clear();
|
||||
|
||||
if (_assetManager != null) {
|
||||
await renderer.fontCollection.downloadAssetFonts(_assetManager!);
|
||||
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 (ui.debugEmulateFlutterTesterEnvironment) {
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
if (_assetManager != null) {
|
||||
await renderer.fontCollection.loadAssetFonts(await fetchFontManifest(assetManager));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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);
|
||||
@@ -29,21 +29,22 @@ abstract class Renderer {
|
||||
factory Renderer._internal() {
|
||||
if (FlutterConfiguration.flutterWebUseSkwasm) {
|
||||
return SkwasmRenderer();
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
useCanvasKit = FlutterConfiguration.useSkia;
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
useCanvasKit = FlutterConfiguration.useSkia;
|
||||
}
|
||||
|
||||
return useCanvasKit ? CanvasKitRenderer() : HtmlRenderer();
|
||||
return useCanvasKit ? CanvasKitRenderer() : HtmlRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
String get rendererTag;
|
||||
|
||||
@@ -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,7 +27,10 @@ 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';
|
||||
|
||||
@@ -3,34 +3,116 @@
|
||||
// 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() {
|
||||
// TODO(jacksongardner): implement clear
|
||||
fontCollectionDispose(_handle);
|
||||
_handle = fontCollectionCreate();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> debugDownloadTestFonts() {
|
||||
// TODO(jacksongardner): implement debugDownloadTestFonts
|
||||
Future<AssetFontsResult> loadAssetFonts(FontManifest manifest) async {
|
||||
final List<Future<void>> fontFutures = <Future<void>>[];
|
||||
final List<String> loadedFonts = <String>[];
|
||||
final Map<String, FontLoadError> fontFailures = <String, FontLoadError>{};
|
||||
|
||||
// We can't restore the pointers directly due to a bug in dart2wasm
|
||||
// https://github.com/dart-lang/sdk/issues/52142
|
||||
final List<int> familyHandles = <int>[];
|
||||
for (final FontFamily family in manifest.families) {
|
||||
final List<int> rawUtf8Bytes = utf8.encode(family.name);
|
||||
final SkStringHandle stringHandle = skStringAllocate(rawUtf8Bytes.length);
|
||||
final Pointer<Int8> 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
Future<FontLoadError?> _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<JSUint8Array1> chunks = <JSUint8Array1>[];
|
||||
await response.read((JSUint8Array1 chunk) {
|
||||
length += chunk.length.toDart.toInt();
|
||||
chunks.add(chunk);
|
||||
});
|
||||
final SkDataHandle fontData = skDataCreate(length);
|
||||
int dataAddress = skDataGetPointer(fontData).cast<Int8>().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<void> downloadAssetFonts(AssetManager assetManager) async {
|
||||
// TODO(jacksongardner): implement downloadAssetFonts
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadFontFromList(Uint8List list, {String? fontFamily}) async {
|
||||
// TODO(jacksongardner): implement loadFontFromList
|
||||
}
|
||||
|
||||
@override
|
||||
void registerDownloadedFonts() {
|
||||
// TODO(jacksongardner): implement registerDownloadedFonts
|
||||
Future<bool> loadFontFromList(Uint8List list, {String? fontFamily}) async {
|
||||
final SkDataHandle dataHandle = skDataCreate(list.length);
|
||||
final Pointer<Int8> dataPointer = skDataGetPointer(dataHandle).cast<Int8>();
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
dataPointer[i] = list[i];
|
||||
}
|
||||
bool success;
|
||||
if (fontFamily != null) {
|
||||
final List<int> rawUtf8Bytes = utf8.encode(fontFamily);
|
||||
final SkStringHandle stringHandle = skStringAllocate(rawUtf8Bytes.length);
|
||||
final Pointer<Int8> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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<RawFontCollection>;
|
||||
|
||||
@Native<FontCollectionHandle Function()>(symbol: 'fontCollection_create', isLeaf: true)
|
||||
external FontCollectionHandle fontCollectionCreate();
|
||||
|
||||
@Native<Void Function(FontCollectionHandle)>(symbol: 'fontCollection_dispose', isLeaf: true)
|
||||
external void fontCollectionDispose(FontCollectionHandle handle);
|
||||
|
||||
@Native<Bool Function(
|
||||
FontCollectionHandle,
|
||||
SkDataHandle,
|
||||
SkStringHandle,
|
||||
)>(symbol: 'fontCollection_registerFont', isLeaf: true)
|
||||
external bool fontCollectionRegisterFont(
|
||||
FontCollectionHandle handle,
|
||||
SkDataHandle fontData,
|
||||
SkStringHandle fontName,
|
||||
);
|
||||
@@ -12,12 +12,6 @@ import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
final class RawShader extends Opaque {}
|
||||
typedef ShaderHandle = Pointer<RawShader>;
|
||||
|
||||
final class RawSkString extends Opaque {}
|
||||
typedef SkStringHandle = Pointer<RawSkString>;
|
||||
|
||||
final class RawSkData extends Opaque {}
|
||||
typedef SkDataHandle = Pointer<RawSkData>;
|
||||
|
||||
final class RawRuntimeEffect extends Opaque {}
|
||||
typedef RuntimeEffectHandle = Pointer<RawRuntimeEffect>;
|
||||
|
||||
@@ -106,15 +100,6 @@ external ShaderHandle shaderCreateSweepGradient(
|
||||
@Native<Void Function(ShaderHandle)>(symbol: 'shader_dispose', isLeaf: true)
|
||||
external void shaderDispose(ShaderHandle handle);
|
||||
|
||||
@Native<SkStringHandle Function(Size)>(symbol: 'shaderSource_allocate', isLeaf: true)
|
||||
external SkStringHandle shaderSourceAllocate(int size);
|
||||
|
||||
@Native<Pointer<Int8> Function(SkStringHandle)>(symbol: 'shaderSource_getData', isLeaf: true)
|
||||
external Pointer<Int8> shaderSourceGetData(SkStringHandle handle);
|
||||
|
||||
@Native<Void Function(SkStringHandle)>(symbol: 'shaderSource_free', isLeaf: true)
|
||||
external void shaderSourceFree(SkStringHandle handle);
|
||||
|
||||
@Native<RuntimeEffectHandle Function(SkStringHandle)>(symbol: 'runtimeEffect_create', isLeaf: true)
|
||||
external RuntimeEffectHandle runtimeEffectCreate(SkStringHandle source);
|
||||
|
||||
@@ -124,15 +109,6 @@ external void runtimeEffectDispose(RuntimeEffectHandle handle);
|
||||
@Native<Size Function(RuntimeEffectHandle)>(symbol: 'runtimeEffect_getUniformSize', isLeaf: true)
|
||||
external int runtimeEffectGetUniformSize(RuntimeEffectHandle handle);
|
||||
|
||||
@Native<SkDataHandle Function(Size)>(symbol: 'data_create', isLeaf: true)
|
||||
external SkDataHandle dataCreate(int size);
|
||||
|
||||
@Native<Pointer<Void> Function(SkDataHandle)>(symbol: 'data_getPointer', isLeaf: true)
|
||||
external Pointer<Void> dataGetPointer(SkDataHandle handle);
|
||||
|
||||
@Native<Void Function(SkDataHandle)>(symbol: 'data_dispose', isLeaf: true)
|
||||
external void dataDispose(SkDataHandle handle);
|
||||
|
||||
@Native<ShaderHandle Function(
|
||||
RuntimeEffectHandle,
|
||||
SkDataHandle,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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 RawSkData extends Opaque {}
|
||||
typedef SkDataHandle = Pointer<RawSkData>;
|
||||
|
||||
@Native<SkDataHandle Function(Size)>(symbol: 'skData_create', isLeaf: true)
|
||||
external SkDataHandle skDataCreate(int size);
|
||||
|
||||
@Native<Pointer<Void> Function(SkDataHandle)>(symbol: 'skData_getPointer', isLeaf: true)
|
||||
external Pointer<Void> skDataGetPointer(SkDataHandle handle);
|
||||
|
||||
@Native<Void Function(SkDataHandle)>(symbol: 'skData_dispose', isLeaf: true)
|
||||
external void skDataDispose(SkDataHandle handle);
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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<RawSkString>;
|
||||
|
||||
@Native<SkStringHandle Function(Size)>(symbol: 'skString_allocate', isLeaf: true)
|
||||
external SkStringHandle skStringAllocate(int size);
|
||||
|
||||
@Native<Pointer<Int8> Function(SkStringHandle)>(symbol: 'skString_getData', isLeaf: true)
|
||||
external Pointer<Int8> skStringGetData(SkStringHandle handle);
|
||||
|
||||
@Native<Void Function(SkStringHandle)>(symbol: 'skString_free', isLeaf: true)
|
||||
external void skStringFree(SkStringHandle handle);
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
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 {}
|
||||
@@ -11,6 +21,7 @@ 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')
|
||||
@@ -283,9 +283,6 @@ 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<void>.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -162,15 +162,15 @@ class SkwasmFragmentProgram implements ui.FragmentProgram {
|
||||
|
||||
// TODO(jacksongardner): Can we avoid this copy?
|
||||
final List<int> sourceData = utf8.encode(shaderData.source);
|
||||
final SkStringHandle sourceString = shaderSourceAllocate(sourceData.length);
|
||||
final Pointer<Int8> sourceBuffer = shaderSourceGetData(sourceString);
|
||||
final SkStringHandle sourceString = skStringAllocate(sourceData.length);
|
||||
final Pointer<Int8> sourceBuffer = skStringGetData(sourceString);
|
||||
int i = 0;
|
||||
for (final int byte in sourceData) {
|
||||
sourceBuffer[i] = byte;
|
||||
i++;
|
||||
}
|
||||
final RuntimeEffectHandle handle = runtimeEffectCreate(sourceString);
|
||||
shaderSourceFree(sourceString);
|
||||
skStringFree(sourceString);
|
||||
return SkwasmFragmentProgram._(name, handle);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader {
|
||||
SkwasmFragmentProgram program, {
|
||||
List<SkwasmShader>? childShaders,
|
||||
}) : _program = program,
|
||||
_uniformData = dataCreate(program.uniformSize),
|
||||
_uniformData = skDataCreate(program.uniformSize),
|
||||
_childShaders = childShaders;
|
||||
|
||||
@override
|
||||
@@ -234,7 +234,7 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader {
|
||||
shaderDispose(_handle);
|
||||
_handle = nullptr;
|
||||
}
|
||||
final Pointer<Float> dataPointer = dataGetPointer(_uniformData).cast<Float>();
|
||||
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData).cast<Float>();
|
||||
dataPointer[index] = value;
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader {
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
if (_uniformData != nullptr) {
|
||||
dataDispose(_uniformData);
|
||||
skDataDispose(_uniformData);
|
||||
_uniformData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,31 +10,6 @@ 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<void>? _platformInitializedFuture;
|
||||
|
||||
Future<void> 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 {
|
||||
|
||||
@@ -3,16 +3,9 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
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';
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
/// This class is responsible for registering and loading fonts.
|
||||
///
|
||||
@@ -21,95 +14,48 @@ import 'layout_service.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<void> 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<dynamic>? fontManifest = json.decode(utf8.decode(data)) as List<dynamic>?;
|
||||
if (fontManifest == null) {
|
||||
throw AssertionError(
|
||||
'There was a problem trying to load FontManifest.json');
|
||||
}
|
||||
|
||||
_assetFontManager = FontManager();
|
||||
|
||||
for (final Map<String, dynamic> fontFamily
|
||||
in fontManifest.cast<Map<String, dynamic>>()) {
|
||||
final String? family = fontFamily.tryString('family');
|
||||
final List<Map<String, dynamic>> fontAssets = fontFamily.castList<Map<String, dynamic>>('fonts');
|
||||
|
||||
for (final Map<String, dynamic> fontAsset in fontAssets) {
|
||||
final String asset = fontAsset.readString('asset');
|
||||
final Map<String, String> descriptors = <String, String>{};
|
||||
for (final String descriptor in fontAsset.keys) {
|
||||
if (descriptor != 'asset') {
|
||||
descriptors[descriptor] = '${fontAsset[descriptor]}';
|
||||
}
|
||||
}
|
||||
_assetFontManager!.downloadAsset(
|
||||
family!, 'url(${assetManager.getAssetUrl(asset)})', descriptors);
|
||||
Future<AssetFontsResult> loadAssetFonts(FontManifest manifest) async {
|
||||
final List<Future<(String, FontLoadError?)>> pendingFonts = <Future<(String, FontLoadError?)>>[];
|
||||
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)
|
||||
);
|
||||
}());
|
||||
}
|
||||
}
|
||||
await _assetFontManager!.downloadAllFonts();
|
||||
|
||||
final List<String> loadedFonts = <String>[];
|
||||
final Map<String, FontLoadError> fontFailures = <String, FontLoadError>{};
|
||||
for (final (String asset, FontLoadError? error) in await Future.wait(pendingFonts)) {
|
||||
if (error == null) {
|
||||
loadedFonts.add(asset);
|
||||
} else {
|
||||
fontFailures[asset] = error;
|
||||
}
|
||||
}
|
||||
return AssetFontsResult(loadedFonts, fontFailures);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadFontFromList(Uint8List list, {String? fontFamily}) {
|
||||
Future<bool> loadFontFromList(Uint8List list, {String? fontFamily}) async {
|
||||
if (fontFamily == null) {
|
||||
throw AssertionError('Font family must be provided to HtmlFontCollection.');
|
||||
printWarning('Font family must be provided to HtmlFontCollection.');
|
||||
return false;
|
||||
}
|
||||
return _assetFontManager!._loadFontFaceBytes(fontFamily, list);
|
||||
}
|
||||
|
||||
/// Downloads fonts that are used by tests.
|
||||
@override
|
||||
Future<void> debugDownloadTestFonts() async {
|
||||
final FontManager fontManager = _testFontManager = FontManager();
|
||||
fontManager._downloadedFonts.add(createDomFontFace(
|
||||
EmbeddedTestFont.flutterTest.fontFamily,
|
||||
EmbeddedTestFont.flutterTest.data,
|
||||
));
|
||||
for (final MapEntry<String, String> fontEntry in testFontUrls.entries) {
|
||||
fontManager.downloadAsset(fontEntry.key, 'url(${fontEntry.value})', const <String, String>{});
|
||||
}
|
||||
await fontManager.downloadAllFonts();
|
||||
}
|
||||
|
||||
@override
|
||||
void registerDownloadedFonts() {
|
||||
_assetFontManager?.registerDownloadedFonts();
|
||||
_testFontManager?.registerDownloadedFonts();
|
||||
return _loadFontFaceBytes(fontFamily, list);
|
||||
}
|
||||
|
||||
/// 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<Future<DomFontFace?>> _fontLoadingFutures = <Future<DomFontFace?>>[];
|
||||
|
||||
final List<DomFontFace> _downloadedFonts = <DomFontFace>[];
|
||||
|
||||
// Regular expression to detect a string with no punctuations.
|
||||
// For example font family 'Ahem!' does not fall into this category
|
||||
@@ -152,79 +98,77 @@ class FontManager {
|
||||
///
|
||||
/// * https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#Valid_family_names
|
||||
/// * https://drafts.csswg.org/css-fonts-3/#font-family-prop
|
||||
void downloadAsset(
|
||||
Future<FontLoadError?> _loadFontAsset(
|
||||
String family,
|
||||
String asset,
|
||||
Map<String, String> descriptors,
|
||||
) {
|
||||
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);
|
||||
) async {
|
||||
final List<DomFontFace> fontFaces = <DomFontFace>[];
|
||||
final List<FontLoadError> errors = <FontLoadError>[];
|
||||
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);
|
||||
}
|
||||
// Load all fonts, without quoted family names.
|
||||
_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;
|
||||
}
|
||||
|
||||
void _loadFontFace(
|
||||
Future<DomFontFace> _loadFontFace(
|
||||
String family,
|
||||
String asset,
|
||||
Map<String, String> descriptors,
|
||||
) {
|
||||
Future<DomFontFace?> 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;
|
||||
}
|
||||
}
|
||||
) async {
|
||||
// try/catch because `new FontFace` can crash with an improper font family.
|
||||
try {
|
||||
final DomFontFace fontFace = createDomFontFace(family, asset, descriptors);
|
||||
_fontLoadingFutures.add(fontFaceLoad(fontFace));
|
||||
final DomFontFace fontFace = createDomFontFace(family, 'url(${assetManager.getAssetUrl(asset)})', descriptors);
|
||||
return await fontFace.load();
|
||||
} 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<void> downloadAllFonts() async {
|
||||
final List<DomFontFace?> loadedFonts = await Future.wait(_fontLoadingFutures);
|
||||
_downloadedFonts.addAll(loadedFonts.whereType<DomFontFace>());
|
||||
}
|
||||
|
||||
// Loads a font from bytes, surfacing errors through the future.
|
||||
Future<void> _loadFontFaceBytes(String family, Uint8List list) {
|
||||
Future<bool> _loadFontFaceBytes(String family, Uint8List list) async {
|
||||
// Since these fonts are loaded by user code, surface the error
|
||||
// through the returned future.
|
||||
final DomFontFace fontFace = createDomFontFace(family, list);
|
||||
return fontFace.load().then((_) {
|
||||
try {
|
||||
final DomFontFace fontFace = createDomFontFace(family, list);
|
||||
if (fontFace.status == 'error') {
|
||||
// Font failed to load.
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
}, 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());
|
||||
});
|
||||
} catch (exception) {
|
||||
// Failures here will throw an DomException. Return false.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ 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",
|
||||
]
|
||||
@@ -46,5 +49,8 @@ wasm_lib("skwasm") {
|
||||
]
|
||||
}
|
||||
|
||||
deps = [ "//third_party/skia" ]
|
||||
deps = [
|
||||
"//third_party/skia",
|
||||
"//third_party/skia/modules/skparagraph",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "export.h"
|
||||
#include "helpers.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "export.h"
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
19
engine/src/flutter/lib/web_ui/skwasm/data.cpp
Normal file
19
engine/src/flutter/lib/web_ui/skwasm/data.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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();
|
||||
}
|
||||
@@ -4,4 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <emscripten.h>
|
||||
|
||||
#define SKWASM_EXPORT extern "C" EMSCRIPTEN_KEEPALIVE
|
||||
|
||||
49
engine/src/flutter/lib/web_ui/skwasm/fonts.cpp
Normal file
49
engine/src/flutter/lib/web_ui/skwasm/fonts.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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<FontCollection> collection;
|
||||
sk_sp<TypefaceFontProvider> provider;
|
||||
};
|
||||
|
||||
SKWASM_EXPORT FlutterFontCollection* fontCollection_create() {
|
||||
auto collection = sk_make_sp<FontCollection>();
|
||||
auto provider = sk_make_sp<TypefaceFontProvider>();
|
||||
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<SkData>(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;
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "export.h"
|
||||
#include "helpers.h"
|
||||
#include "third_party/skia/include/core/SkPaint.h"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "export.h"
|
||||
#include "helpers.h"
|
||||
#include "third_party/skia/include/core/SkPath.h"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "export.h"
|
||||
#include "helpers.h"
|
||||
#include "third_party/skia/include/core/SkPictureRecorder.h"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "export.h"
|
||||
#include "helpers.h"
|
||||
#include "third_party/skia/include/effects/SkGradientShader.h"
|
||||
@@ -102,18 +101,6 @@ 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) {
|
||||
@@ -133,18 +120,6 @@ 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,
|
||||
|
||||
19
engine/src/flutter/lib/web_ui/skwasm/string.cpp
Normal file
19
engine/src/flutter/lib/web_ui/skwasm/string.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -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_util' as js_util;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
@@ -29,15 +28,6 @@ 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(<String, Object?>{
|
||||
'useColorEmoji': true,
|
||||
}) as JsFlutterConfiguration);
|
||||
debugSetConfiguration(config);
|
||||
|
||||
|
||||
FontFallbackData.debugReset();
|
||||
notoDownloadQueue.downloader.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/';
|
||||
});
|
||||
|
||||
@@ -1527,11 +1527,6 @@ 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
|
||||
|
||||
@@ -11,14 +11,21 @@ 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() {
|
||||
setUpAll(() async {
|
||||
expect(renderer, isA<CanvasKitRenderer>(), reason: 'This test must run in CanvasKit mode.');
|
||||
debugDisableFontFallbacks = false;
|
||||
await initializeEngine(assetManager: WebOnlyMockAssetManager());
|
||||
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');
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:js_util' as js_util;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
@@ -25,18 +24,14 @@ 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(<String, Object?>{
|
||||
'useColorEmoji': true,
|
||||
}) as JsFlutterConfiguration);
|
||||
debugSetConfiguration(config);
|
||||
|
||||
FontFallbackData.debugReset();
|
||||
notoDownloadQueue.downloader.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/';
|
||||
savedCallback = ui.window.onPlatformMessage;
|
||||
|
||||
@@ -9,18 +9,22 @@ 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<String> warnings = <String>[];
|
||||
late void Function(String) oldPrintWarning;
|
||||
late FakeAssetScope testAssetScope;
|
||||
|
||||
setUpAll(() async {
|
||||
ensureFlutterViewEmbedderInitialized();
|
||||
await renderer.initialize();
|
||||
oldPrintWarning = printWarning;
|
||||
printWarning = (String warning) {
|
||||
warnings.add(warning);
|
||||
@@ -32,20 +36,19 @@ 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();
|
||||
final WebOnlyMockAssetManager mockAssetManager =
|
||||
WebOnlyMockAssetManager();
|
||||
await fontCollection.downloadAssetFonts(mockAssetManager);
|
||||
fontCollection.registerDownloadedFonts();
|
||||
await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
|
||||
|
||||
expect(warnings, isEmpty);
|
||||
});
|
||||
@@ -61,9 +64,7 @@ void testMain() {
|
||||
);
|
||||
};
|
||||
final SkiaFontCollection fontCollection = SkiaFontCollection();
|
||||
final WebOnlyMockAssetManager mockAssetManager =
|
||||
WebOnlyMockAssetManager();
|
||||
mockAssetManager.defaultFontManifest = '''
|
||||
testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data('''
|
||||
[
|
||||
{
|
||||
"family":"Roboto",
|
||||
@@ -74,10 +75,9 @@ void testMain() {
|
||||
"fonts":[{"asset":"packages/bogus/BrokenFont.ttf"}]
|
||||
}
|
||||
]
|
||||
''';
|
||||
'''));
|
||||
// It should complete without error, but emit a warning about BrokenFont.
|
||||
await fontCollection.downloadAssetFonts(mockAssetManager);
|
||||
fontCollection.registerDownloadedFonts();
|
||||
await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
|
||||
expect(
|
||||
warnings,
|
||||
containsAllInOrder(
|
||||
@@ -91,9 +91,7 @@ void testMain() {
|
||||
|
||||
test('logs an HTTP warning if one of the registered fonts is missing (404 file not found)', () async {
|
||||
final SkiaFontCollection fontCollection = SkiaFontCollection();
|
||||
final WebOnlyMockAssetManager mockAssetManager =
|
||||
WebOnlyMockAssetManager();
|
||||
mockAssetManager.defaultFontManifest = '''
|
||||
testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data('''
|
||||
[
|
||||
{
|
||||
"family":"Roboto",
|
||||
@@ -104,38 +102,32 @@ void testMain() {
|
||||
"fonts":[{"asset":"packages/bogus/ThisFontDoesNotExist.ttf"}]
|
||||
}
|
||||
]
|
||||
''';
|
||||
'''));
|
||||
|
||||
// It should complete without error, but emit a warning about ThisFontDoesNotExist.
|
||||
await fontCollection.downloadAssetFonts(mockAssetManager);
|
||||
fontCollection.registerDownloadedFonts();
|
||||
await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
|
||||
expect(
|
||||
warnings,
|
||||
containsAllInOrder(<String>[
|
||||
'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.',
|
||||
'Font family ThisFontDoesNotExist not found (404) at packages/bogus/ThisFontDoesNotExist.ttf'
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('prioritizes Ahem loaded via FontManifest.json', () async {
|
||||
final SkiaFontCollection fontCollection = SkiaFontCollection();
|
||||
final WebOnlyMockAssetManager mockAssetManager =
|
||||
WebOnlyMockAssetManager();
|
||||
mockAssetManager.defaultFontManifest = '''
|
||||
testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data('''
|
||||
[
|
||||
{
|
||||
"family":"Ahem",
|
||||
"fonts":[{"asset":"/assets/fonts/Roboto-Regular.ttf"}]
|
||||
}
|
||||
]
|
||||
'''.trim();
|
||||
'''.trim()));
|
||||
|
||||
final ByteBuffer robotoData = await httpFetchByteBuffer('/assets/fonts/Roboto-Regular.ttf');
|
||||
|
||||
await fontCollection.downloadAssetFonts(mockAssetManager);
|
||||
await fontCollection.debugDownloadTestFonts();
|
||||
fontCollection.registerDownloadedFonts();
|
||||
await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
|
||||
expect(warnings, isEmpty);
|
||||
|
||||
// Use `singleWhere` to make sure only one version of 'Ahem' is loaded.
|
||||
@@ -148,18 +140,10 @@ void testMain() {
|
||||
});
|
||||
|
||||
test('falls back to default Ahem URL', () async {
|
||||
final SkiaFontCollection fontCollection = SkiaFontCollection();
|
||||
final WebOnlyMockAssetManager mockAssetManager =
|
||||
WebOnlyMockAssetManager();
|
||||
mockAssetManager.defaultFontManifest = '[]';
|
||||
final SkiaFontCollection fontCollection = renderer.fontCollection as SkiaFontCollection;
|
||||
|
||||
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');
|
||||
@@ -169,24 +153,9 @@ 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 = SkiaFontCollection();
|
||||
final SkiaFontCollection fontCollection = renderer.fontCollection as SkiaFontCollection;
|
||||
|
||||
await fontCollection.debugDownloadTestFonts();
|
||||
fontCollection.registerDownloadedFonts();
|
||||
expect(fontCollection.debugRegisteredFonts, isNotEmpty);
|
||||
expect(fontCollection.debugRegisteredFonts!.first.family, 'FlutterTest');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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<ByteData> load(String assetKey) async {
|
||||
final ByteData? data = _assetMap[assetKey];
|
||||
if (data == null) {
|
||||
throw HttpFetchNoPayloadError(assetKey, status: 404);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<HttpFetchResponse> 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<String, ByteData> _assetMap = <String, ByteData>{};
|
||||
}
|
||||
|
||||
class FakeAssetScope {
|
||||
FakeAssetScope._(this._parent);
|
||||
|
||||
final FakeAssetScope? _parent;
|
||||
final Map<String, Future<ByteData> Function()> _assetFetcherMap = <String, Future<ByteData> 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<ByteData>? getAssetData(String assetKey) {
|
||||
final Future<ByteData> 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<String, String> testFontUrls = <String, String>{
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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(<String, Object?>{
|
||||
'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);
|
||||
});
|
||||
}
|
||||
@@ -9,12 +9,14 @@ 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<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
group('message handler', () {
|
||||
const String testText = 'test text';
|
||||
|
||||
|
||||
@@ -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_interop';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
@@ -116,7 +117,7 @@ Future<void> _testSuccessfulPayloads() async {
|
||||
expect(response.url, url);
|
||||
|
||||
final List<int> result = <int>[];
|
||||
await response.payload.read<Uint8List>(result.addAll);
|
||||
await response.payload.read<JSUint8Array>((JSUint8Array chunk) => result.addAll(chunk.toDart));
|
||||
expect(result, hasLength(length));
|
||||
expect(
|
||||
result,
|
||||
|
||||
@@ -8,15 +8,16 @@ 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<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
group('HtmCodec', () {
|
||||
test('supports raw images - RGBA8888', () async {
|
||||
final Completer<ui.Image> completer = Completer<ui.Image>();
|
||||
|
||||
@@ -9,16 +9,14 @@ 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<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests();
|
||||
|
||||
Future<Image> createTestImageByColor(Color color) async {
|
||||
final EnginePictureRecorder recorder = EnginePictureRecorder();
|
||||
|
||||
@@ -8,15 +8,16 @@ import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../common/mock_engine_canvas.dart';
|
||||
import '../html/screenshot.dart';
|
||||
import '../common/test_initialization.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
}
|
||||
|
||||
void testMain() {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
late RecordingCanvas underTest;
|
||||
late MockEngineCanvas mockCanvas;
|
||||
|
||||
@@ -12,7 +12,6 @@ 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';
|
||||
@@ -21,6 +20,7 @@ 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,7 +60,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
await initializeEngine();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false
|
||||
);
|
||||
|
||||
tearDown(() {
|
||||
lastEditingState = null;
|
||||
|
||||
@@ -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,7 +35,10 @@ Future<void> testMain() async {
|
||||
flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene-host')!.append(testScene);
|
||||
}
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
tearDown(() {
|
||||
flutterViewEmbedder.glassPaneShadow.querySelector('flt-scene')?.remove();
|
||||
|
||||
@@ -7,8 +7,10 @@ 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() {
|
||||
@@ -16,12 +18,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await engine.renderer.fontCollection.debugDownloadTestFonts();
|
||||
engine.renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/48683
|
||||
// Should clip image with oval.
|
||||
|
||||
@@ -7,6 +7,7 @@ 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() {
|
||||
@@ -19,12 +20,9 @@ Future<void> testMain() async {
|
||||
const double screenHeight = 800.0;
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight);
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await engine.renderer.fontCollection.debugDownloadTestFonts();
|
||||
engine.renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/49429
|
||||
// Should clip with correct transform.
|
||||
|
||||
@@ -9,6 +9,8 @@ import 'package:ui/ui.dart' hide TextStyle;
|
||||
|
||||
import 'package:web_engine_tester/golden_tester.dart';
|
||||
|
||||
import '../common/test_initialization.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
}
|
||||
@@ -22,12 +24,7 @@ Future<void> testMain() async {
|
||||
..strokeWidth = 2.0
|
||||
..color = const Color(0xFFFF00FF);
|
||||
|
||||
setUp(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests();
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/51514
|
||||
test("Canvas is reused and z-index doesn't leak across paints", () async {
|
||||
|
||||
@@ -7,6 +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 'screenshot.dart';
|
||||
|
||||
@@ -15,7 +16,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
/// Regression test for https://github.com/flutter/flutter/issues/64734.
|
||||
test('Clips using difference', () async {
|
||||
|
||||
@@ -9,16 +9,17 @@ import 'package:ui/ui.dart';
|
||||
|
||||
import 'package:web_engine_tester/golden_tester.dart';
|
||||
|
||||
import '../../common/test_initialization.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
debugShowClipLayers = true;
|
||||
|
||||
@@ -9,6 +9,7 @@ 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() {
|
||||
@@ -16,13 +17,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('Blend circles with difference and color', () async {
|
||||
final RecordingCanvas rc =
|
||||
|
||||
@@ -7,6 +7,7 @@ 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';
|
||||
|
||||
@@ -17,12 +18,9 @@ void main() {
|
||||
SurfacePaint makePaint() => Paint() as SurfacePaint;
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
const Color red = Color(0xFFFF0000);
|
||||
const Color green = Color(0xFF00FF00);
|
||||
|
||||
@@ -10,6 +10,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 '../screenshot.dart';
|
||||
|
||||
void main() {
|
||||
@@ -17,12 +18,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
ui.debugEmulateFlutterTesterEnvironment = true;
|
||||
await ui.webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
tearDown(() {
|
||||
ContextStateHandle.debugEmulateWebKitMaskFilter = false;
|
||||
|
||||
@@ -11,6 +11,8 @@ 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() {
|
||||
@@ -18,11 +20,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
debugShowClipLayers = true;
|
||||
|
||||
@@ -11,6 +11,7 @@ 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);
|
||||
|
||||
@@ -19,11 +20,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
await ui.webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
// To debug test failures uncomment the following to visualize clipping
|
||||
|
||||
@@ -6,6 +6,7 @@ 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() {
|
||||
@@ -13,12 +14,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUp(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('Should blur rectangles based on sigma.', () async {
|
||||
final RecordingCanvas rc =
|
||||
|
||||
@@ -7,6 +7,7 @@ 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() {
|
||||
@@ -14,12 +15,14 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
debugShowClipLayers = true;
|
||||
SurfaceSceneBuilder.debugForgetFrameScene();
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:ui/ui.dart';
|
||||
|
||||
import 'package:web_engine_tester/golden_tester.dart';
|
||||
|
||||
import '../../common/test_initialization.dart';
|
||||
import '../screenshot.dart';
|
||||
|
||||
void main() {
|
||||
@@ -21,12 +22,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
|
||||
setUp(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
});
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('Paints image', () async {
|
||||
final RecordingCanvas rc =
|
||||
|
||||
@@ -7,6 +7,7 @@ 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);
|
||||
@@ -18,11 +19,13 @@ void main() {
|
||||
SurfacePaint makePaint() => Paint() as SurfacePaint;
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUpAll(() async {
|
||||
debugShowClipLayers = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
|
||||
@@ -11,6 +11,7 @@ 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() {
|
||||
@@ -22,12 +23,9 @@ Future<void> testMain() async {
|
||||
const double screenHeight = 800.0;
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight);
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
GlContextCache.dispose();
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
|
||||
const String _rtlWord1 = 'واحد';
|
||||
@@ -18,7 +18,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
void paintBasicBidiStartingWithLtr(
|
||||
EngineCanvas canvas,
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
|
||||
const Rect bounds = Rect.fromLTWH(0, 0, 800, 600);
|
||||
@@ -20,7 +20,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('paints spans and lines correctly', () {
|
||||
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
|
||||
void main() {
|
||||
@@ -17,7 +17,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
void testJustifyWithMultipleSpans(EngineCanvas canvas) {
|
||||
void build(CanvasParagraphBuilder builder) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
|
||||
void main() {
|
||||
@@ -17,7 +17,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
void testEllipsis(EngineCanvas canvas) {
|
||||
Offset offset = Offset.zero;
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
|
||||
const Rect bounds = Rect.fromLTWH(0, 0, 800, 600);
|
||||
@@ -19,7 +19,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('draws paragraphs with placeholders', () {
|
||||
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
|
||||
const Rect bounds = Rect.fromLTWH(0, 0, 800, 600);
|
||||
@@ -17,7 +17,10 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('paints multiple shadows', () {
|
||||
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'text_scuba.dart';
|
||||
|
||||
typedef PaintTest = void Function(RecordingCanvas recordingCanvas);
|
||||
@@ -23,7 +23,10 @@ Future<void> testMain() async {
|
||||
viewportSize: const Size(600, 600),
|
||||
);
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
void paintTest(EngineCanvas canvas, PaintTest painter) {
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600);
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' hide window;
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'text_scuba.dart';
|
||||
|
||||
const String threeLines = 'First\nSecond\nThird';
|
||||
@@ -25,7 +25,10 @@ Future<void> testMain() async {
|
||||
viewportSize: const Size(800, 800),
|
||||
);
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
testEachCanvas('maxLines clipping', (EngineCanvas canvas) {
|
||||
Offset offset = Offset.zero;
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../screenshot.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import 'helper.dart';
|
||||
import 'text_scuba.dart';
|
||||
|
||||
@@ -19,8 +19,10 @@ Future<void> testMain() async {
|
||||
viewportSize: const Size(600, 600),
|
||||
);
|
||||
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
testEachCanvas('draws paragraphs with placeholders', (EngineCanvas canvas) {
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600);
|
||||
|
||||
@@ -8,6 +8,7 @@ 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() {
|
||||
@@ -22,12 +23,9 @@ Future<void> testMain() async {
|
||||
const Color redAccentColor = Color(0xFFFF1744);
|
||||
const double kDashLength = 5.0;
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('Should calculate tangent on line', () async {
|
||||
final Path path = Path();
|
||||
|
||||
@@ -9,6 +9,7 @@ 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() {
|
||||
@@ -20,12 +21,9 @@ Future<void> testMain() async {
|
||||
const double screenHeight = 800.0;
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight);
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('Should draw transformed line.', () async {
|
||||
final RecordingCanvas rc =
|
||||
|
||||
@@ -12,18 +12,16 @@ import 'package:ui/ui.dart' hide TextStyle;
|
||||
import 'package:web_engine_tester/golden_tester.dart';
|
||||
|
||||
import '../common/matchers.dart';
|
||||
import 'screenshot.dart';
|
||||
import '../common/test_initialization.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
});
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
const double screenWidth = 600.0;
|
||||
const double screenHeight = 800.0;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// 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';
|
||||
@@ -70,14 +69,3 @@ Future<void> 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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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.
|
||||
@@ -25,11 +26,7 @@ Future<void> testMain() async {
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight);
|
||||
const Rect region = Rect.fromLTWH(0, 0, 500, 240);
|
||||
|
||||
setUp(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
});
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests();
|
||||
|
||||
test('Paints sweep gradient rectangles', () async {
|
||||
final RecordingCanvas canvas =
|
||||
|
||||
@@ -9,6 +9,7 @@ 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.
|
||||
@@ -24,12 +25,9 @@ Future<void> testMain() async {
|
||||
const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight);
|
||||
final HtmlImage testImage = createTestImage();
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
void drawShapes(RecordingCanvas rc, SurfacePaint paint, Rect shaderRect) {
|
||||
/// Rect.
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.
|
||||
@@ -18,12 +19,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
test('Should draw linear gradient using rectangle.', () async {
|
||||
final RecordingCanvas rc =
|
||||
|
||||
@@ -6,6 +6,7 @@ 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() {
|
||||
@@ -13,13 +14,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
|
||||
setUpAll(() async {
|
||||
debugEmulateFlutterTesterEnvironment = true;
|
||||
await webOnlyInitializePlatform();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
Future<void> testGradient(String fileName, Shader shader,
|
||||
{Rect paintRect = const Rect.fromLTRB(50, 50, 300, 300),
|
||||
|
||||
@@ -10,6 +10,8 @@ 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
|
||||
@@ -29,9 +31,13 @@ Future<void> main() async {
|
||||
// https://github.com/flutter/flutter/issues/86623
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUpAll(() async {
|
||||
debugShowClipLayers = true;
|
||||
await webOnlyInitializePlatform();
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
@@ -41,8 +47,6 @@ Future<void> testMain() async {
|
||||
scene.remove();
|
||||
}
|
||||
initWebGl();
|
||||
await renderer.fontCollection.debugDownloadTestFonts();
|
||||
renderer.fontCollection.registerDownloadedFonts();
|
||||
});
|
||||
|
||||
/// Should render the picture unmodified.
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:ui/ui.dart';
|
||||
|
||||
import 'package:web_engine_tester/golden_tester.dart';
|
||||
|
||||
import 'screenshot.dart';
|
||||
import '../common/test_initialization.dart';
|
||||
|
||||
const Color _kShadowColor = Color.fromARGB(255, 0, 0, 0);
|
||||
|
||||
@@ -22,7 +22,10 @@ Future<void> testMain() async {
|
||||
|
||||
late SurfaceSceneBuilder builder;
|
||||
|
||||
setUpStableTestFonts();
|
||||
setUpUnitTests(
|
||||
emulateTesterEnvironment: false,
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
builder = SurfaceSceneBuilder();
|
||||
|
||||
@@ -7,7 +7,8 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import '../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
|
||||
@@ -31,7 +32,7 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
|
||||
test('empty paragraph', () {
|
||||
final CanvasParagraph paragraph1 = rich(
|
||||
@@ -7,14 +7,15 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import '../paragraph/helper.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
|
||||
group('$CanvasParagraph.getBoxesForRange', () {
|
||||
test('return empty list for invalid ranges', () {
|
||||
@@ -7,32 +7,43 @@ 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('$FontManager', () {
|
||||
late FontManager fontManager;
|
||||
group('$HtmlFontCollection', () {
|
||||
setUpUnitTests();
|
||||
|
||||
const String testFontUrl = '/assets/fonts/ahem.ttf';
|
||||
|
||||
late FakeAssetScope testScope;
|
||||
setUp(() {
|
||||
fontManager = FontManager();
|
||||
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();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
domDocument.fonts!.clear();
|
||||
fakeAssetManager.popAssetScope(testScope);
|
||||
});
|
||||
|
||||
group('regular special characters', () {
|
||||
test('Register Asset with no special characters', () async {
|
||||
const String testFontFamily = 'Ahem';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -46,10 +57,12 @@ void testMain() {
|
||||
const String testFontFamily = 'Ahem ahem ahem';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -65,10 +78,12 @@ void testMain() {
|
||||
const String testFontFamily = 'AhEm';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -81,13 +96,15 @@ void testMain() {
|
||||
test('Register Asset with descriptor', () async {
|
||||
const String testFontFamily = 'Ahem';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{
|
||||
'weight': 'bold'
|
||||
})
|
||||
])
|
||||
]));
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{
|
||||
'weight': 'bold',
|
||||
});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
expect(f.weight, 'bold');
|
||||
@@ -105,10 +122,12 @@ void testMain() {
|
||||
const String testFontFamily = '/Ahem';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -130,10 +149,12 @@ void testMain() {
|
||||
const String testFontFamily = 'Ahem!!ahem';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -155,10 +176,12 @@ void testMain() {
|
||||
const String testFontFamily = 'Ahem ,ahem';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -181,10 +204,12 @@ void testMain() {
|
||||
const String testFontFamily = 'Ahem 1998';
|
||||
final List<String> fontFamilyList = <String>[];
|
||||
|
||||
fontManager.downloadAsset(
|
||||
testFontFamily, 'url($testFontUrl)', const <String, String>{});
|
||||
await fontManager.downloadAllFonts();
|
||||
fontManager.registerDownloadedFonts();
|
||||
final HtmlFontCollection collection = HtmlFontCollection();
|
||||
await collection.loadAssetFonts(FontManifest(<FontFamily>[
|
||||
FontFamily(testFontFamily, <FontAsset>[
|
||||
FontAsset(testFontUrl, <String, String>{})
|
||||
])
|
||||
]));
|
||||
domDocument.fonts!
|
||||
.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) {
|
||||
fontFamilyList.add(f.family!);
|
||||
@@ -11,12 +11,14 @@ 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<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
group('loadFontFromList', () {
|
||||
const String testFontUrl = '/assets/fonts/ahem.ttf';
|
||||
|
||||
@@ -24,10 +26,11 @@ Future<void> testMain() async {
|
||||
domDocument.fonts!.clear();
|
||||
});
|
||||
|
||||
test('surfaces error from invalid font buffer', () async {
|
||||
test('returns normally from invalid font buffer', () async {
|
||||
await expectLater(
|
||||
ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'),
|
||||
throwsA(const TypeMatcher<Exception>()));
|
||||
() async => ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'),
|
||||
returnsNormally
|
||||
);
|
||||
},
|
||||
// TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702
|
||||
skip: browserEngine == BrowserEngine.webkit);
|
||||
@@ -7,7 +7,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../paragraph/helper.dart';
|
||||
|
||||
final EngineTextStyle defaultStyle = EngineTextStyle.only(
|
||||
color: const Color(0xFFFF0000),
|
||||
@@ -7,7 +7,8 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import '../paragraph/helper.dart';
|
||||
import 'layout_service_helper.dart';
|
||||
|
||||
const bool skipWordSpacing = true;
|
||||
@@ -17,7 +18,7 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
|
||||
test('no text', () {
|
||||
final CanvasParagraph paragraph = CanvasParagraphBuilder(ahemStyle).build();
|
||||
@@ -8,7 +8,8 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../../common/test_initialization.dart';
|
||||
import '../paragraph/helper.dart';
|
||||
import 'layout_service_helper.dart';
|
||||
|
||||
void main() {
|
||||
@@ -16,7 +17,7 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
|
||||
test('does not crash on empty spans', () {
|
||||
final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) {
|
||||
@@ -8,7 +8,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../paragraph/helper.dart';
|
||||
import 'line_breaker_test_helper.dart';
|
||||
import 'line_breaker_test_raw_data.dart';
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart';
|
||||
|
||||
import '../html/paragraph/helper.dart';
|
||||
import '../paragraph/helper.dart';
|
||||
|
||||
void main() {
|
||||
internalBootstrapBrowserTest(() => testMain);
|
||||
@@ -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<void> testMain() async {
|
||||
const double baselineRatio = 1.1662499904632568;
|
||||
|
||||
await initializeTestFlutterViewEmbedder();
|
||||
setUpUnitTests();
|
||||
|
||||
late String fallback;
|
||||
setUp(() {
|
||||
|
||||
@@ -8,5 +8,5 @@ In practice, this means these tests should only use `dart:ui` APIs or
|
||||
|
||||
## Notes
|
||||
|
||||
These tests should call `setUpUiTest()` at the top level to initialize the
|
||||
These tests should call `setUpUnitTests()` at the top level to initialize the
|
||||
renderer they are expected to run.
|
||||
|
||||
@@ -9,6 +9,7 @@ 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() {
|
||||
@@ -16,7 +17,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpUiTest();
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
const Rect region = Rect.fromLTWH(0, 0, 300, 300);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ 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() {
|
||||
@@ -14,7 +15,9 @@ void main() {
|
||||
}
|
||||
|
||||
Future<void> testMain() async {
|
||||
setUpUiTest();
|
||||
setUpUnitTests(
|
||||
setUpTestViewDimensions: false,
|
||||
);
|
||||
|
||||
const Rect region = Rect.fromLTWH(0, 0, 300, 300);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user