Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ vars = {
# Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS.
# You can use //tools/dart/create_updated_flutter_deps.py to produce
# updated revision list of existing dependencies.
'dart_revision': '2607b01bec99f324e45b00fde76591f244f65a4e',
'dart_revision': 'e5dd92c3ca766810e0e5a02d8725b1cebc19f564',

# WARNING: DO NOT EDIT MANUALLY
# The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py
Expand Down
4 changes: 3 additions & 1 deletion ci/licenses_golden/licenses_third_party
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Signature: 8d4521c3fe82b7be926a7af5f8b15557
Signature: 65f332e775d31fee5a51ee0a303354c3

UNUSED LICENSES:

Expand Down Expand Up @@ -9750,9 +9750,11 @@ FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/ffi/arena.dart
FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/ffi/dylib_utils.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/rti.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart
FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
FILE: ../../../third_party/dart/sdk/lib/ffi/annotations.dart
FILE: ../../../third_party/dart/sdk/lib/ffi/dynamic_library.dart
FILE: ../../../third_party/dart/sdk/lib/ffi/ffi.dart
Expand Down
4 changes: 3 additions & 1 deletion lib/snapshot/libraries.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@
"ffi": {
"uri": "../../../third_party/dart/sdk/lib/ffi/ffi.dart",
"patches": [
"../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart",
"../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart",
"../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart",
"../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart",
"../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart"
"../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart"
]
},
"wasm": {
Expand Down
4 changes: 3 additions & 1 deletion lib/snapshot/libraries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@ flutter:
ffi:
uri: "../../../third_party/dart/sdk/lib/ffi/ffi.dart"
patches:
- "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart"
- "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart"
- "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart"
- "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart"
- "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart"
- "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart"

wasm:
uri: "../../../third_party/dart/sdk/lib/wasm/wasm.dart"
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/dev/goldens_lock.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
repository: https://github.com/flutter/goldens.git
revision: b85f9093e6bc6d4e7cbb7f97491667c143c4a360
revision: 99caeb1bcb8b7a856a78bd8d55816cc97db56112
81 changes: 49 additions & 32 deletions lib/web_ui/dev/test_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ class TestCommand extends Command<bool> with ArgUtils {
'.dart_tool/goldens. Use this option to bulk-update all screenshots, '
'for example, when a new browser version affects pixels.',
)
..addFlag(
'fetch-goldens-repo',
defaultsTo: true,
negatable: true,
help:
'Whether to fetch the goldens repo. Set this to false to iterate '
'on golden tests without fearing that the fetcher will overwrite '
'your local changes.',
)
..addOption(
'browser',
defaultsTo: 'chrome',
Expand Down Expand Up @@ -165,39 +174,41 @@ class TestCommand extends Command<bool> with ArgUtils {
final FilePath dir = FilePath.fromWebUi('');
print('');
print('Initial test run is done!');
print('Watching ${dir.relativeToCwd}/lib and ${dir.relativeToCwd}/test to re-run tests');
print(
'Watching ${dir.relativeToCwd}/lib and ${dir.relativeToCwd}/test to re-run tests');
print('');
PipelineWatcher(
dir: dir.absolute,
pipeline: testPipeline,
ignore: (event) {
// Ignore font files that are copied whenever tests run.
if (event.path.endsWith('.ttf')) {
return true;
}
dir: dir.absolute,
pipeline: testPipeline,
ignore: (event) {
// Ignore font files that are copied whenever tests run.
if (event.path.endsWith('.ttf')) {
return true;
}

// Ignore auto-generated JS files.
// The reason we are using `.contains()` instead of `.endsWith()` is
// because the auto-generated files could end with any of the
// following:
//
// - browser_test.dart.js
// - browser_test.dart.js.map
// - browser_test.dart.js.deps
if (event.path.contains('browser_test.dart.js')) {
return true;
}
// Ignore auto-generated JS files.
// The reason we are using `.contains()` instead of `.endsWith()` is
// because the auto-generated files could end with any of the
// following:
//
// - browser_test.dart.js
// - browser_test.dart.js.map
// - browser_test.dart.js.deps
if (event.path.contains('browser_test.dart.js')) {
return true;
}

// React to changes in lib/ and test/ folders.
final String relativePath = path.relative(event.path, from: dir.absolute);
if (relativePath.startsWith('lib/') || relativePath.startsWith('test/')) {
return false;
}
// React to changes in lib/ and test/ folders.
final String relativePath =
path.relative(event.path, from: dir.absolute);
if (relativePath.startsWith('lib/') ||
relativePath.startsWith('test/')) {
return false;
}

// Ignore anything else.
return true;
}
).start();
// Ignore anything else.
return true;
}).start();
// Return a never-ending future.
return Completer<bool>().future;
} else {
Expand All @@ -217,15 +228,17 @@ class TestCommand extends Command<bool> with ArgUtils {
bool unitTestResult = await runUnitTests();
bool integrationTestResult = await runIntegrationTests();
if (integrationTestResult != unitTestResult) {
print('Tests run. Integration tests passed: $integrationTestResult '
print(
'Tests run. Integration tests passed: $integrationTestResult '
'unit tests passed: $unitTestResult');
}
return integrationTestResult && unitTestResult;
} else {
return await runUnitTests();
}
}
throw UnimplementedError('Unknown test type requested: $testTypesRequested');
throw UnimplementedError(
'Unknown test type requested: $testTypesRequested');
} on TestFailureException {
return true;
}
Expand Down Expand Up @@ -272,9 +285,9 @@ class TestCommand extends Command<bool> with ArgUtils {
environment.webUiTestResultsDirectory.createSync(recursive: true);

// If screenshot tests are available, fetch the screenshot goldens.
if (isScreenshotTestsAvailable) {
if (isScreenshotTestsAvailable && doFetchGoldensRepo) {
if (isVerboseLoggingEnabled) {
print('INFO: Screenshot tests available');
print('INFO: Fetching goldens repo');
}
final GoldensRepoFetcher goldensRepoFetcher = GoldensRepoFetcher(
environment.webUiGoldensRepositoryDirectory,
Expand Down Expand Up @@ -483,6 +496,9 @@ class TestCommand extends Command<bool> with ArgUtils {
/// ".dart_tool/goldens".
bool get doUpdateScreenshotGoldens => boolArg('update-screenshot-goldens');

/// Whether to fetch the goldens repo prior to running tests.
bool get doFetchGoldensRepo => boolArg('fetch-goldens-repo');

/// Runs all tests specified in [targets].
///
/// Unlike [_runAllTestsForCurrentPlatform], this does not filter targets
Expand Down Expand Up @@ -774,6 +790,7 @@ const List<String> _kTestFonts = <String>[
'ahem.ttf',
'Roboto-Regular.ttf',
'NotoNaskhArabic-Regular.ttf',
'NotoColorEmoji.ttf',
];

void _copyTestFontsIntoWebUi() {
Expand Down
11 changes: 10 additions & 1 deletion lib/web_ui/lib/src/engine/assets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,16 @@ class AssetManagerException implements Exception {
class WebOnlyMockAssetManager implements AssetManager {
String defaultAssetsDir = '';
String defaultAssetManifest = '{}';
String defaultFontManifest = '[]';
String defaultFontManifest = '''[
{
"family":"$_robotoFontFamily",
"fonts":[{"asset":"$_robotoTestFontUrl"}]
},
{
"family":"$_ahemFontFamily",
"fonts":[{"asset":"$_ahemFontUrl"}]
}
]''';

@override
String get assetsDir => defaultAssetsDir;
Expand Down
24 changes: 19 additions & 5 deletions lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -359,14 +359,17 @@ class HtmlViewEmbedder {
final Set<int> unusedViews = Set<int>.from(_activeCompositionOrder);
_activeCompositionOrder.clear();

List<int>? debugInvalidViewIds;
for (int i = 0; i < _compositionOrder.length; i++) {
int viewId = _compositionOrder[i];

assert(
_views.containsKey(viewId),
'Cannot render platform view $viewId. '
'It has not been created, or it has been deleted.',
);
if (assertionsEnabled) {
if (!_views.containsKey(viewId)) {
debugInvalidViewIds ??= <int>[];
debugInvalidViewIds.add(viewId);
continue;
}
}

unusedViews.remove(viewId);
html.Element platformViewRoot = _rootViews[viewId]!;
Expand All @@ -381,6 +384,16 @@ class HtmlViewEmbedder {

for (final int unusedViewId in unusedViews) {
_releaseOverlay(unusedViewId);
_rootViews[unusedViewId]?.remove();
}

if (assertionsEnabled) {
if (debugInvalidViewIds != null && debugInvalidViewIds.isNotEmpty) {
throw AssertionError(
'Cannot render platform views: ${debugInvalidViewIds.join(', ')}. '
'These views have not been created, or they have been deleted.',
);
}
}
}

Expand Down Expand Up @@ -476,6 +489,7 @@ class OverlayCache {
for (final Surface overlay in _cache) {
overlay.dispose();
}
_cache.clear();
}
}

Expand Down
68 changes: 59 additions & 9 deletions lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -211,15 +211,19 @@ Future<void> _registerSymbolsAndEmoji() async {
String? symbolsFontUrl = extractUrlFromCss(symbolsCss);
String? emojiFontUrl = extractUrlFromCss(emojiCss);

if (symbolsFontUrl == null || emojiFontUrl == null) {
html.window.console
.warn('Error parsing CSS for Noto Emoji and Symbols font.');
if (symbolsFontUrl != null) {
notoDownloadQueue.add(_ResolvedNotoSubset(
symbolsFontUrl, 'Noto Sans Symbols', const <CodeunitRange>[]));
} else {
html.window.console.warn('Error parsing CSS for Noto Symbols font.');
}

notoDownloadQueue.add(_ResolvedNotoSubset(
symbolsFontUrl!, 'Noto Sans Symbols', const <CodeunitRange>[]));
notoDownloadQueue.add(_ResolvedNotoSubset(
emojiFontUrl!, 'Noto Color Emoji Compat', const <CodeunitRange>[]));
if (emojiFontUrl != null) {
notoDownloadQueue.add(_ResolvedNotoSubset(
emojiFontUrl, 'Noto Color Emoji Compat', const <CodeunitRange>[]));
} else {
html.window.console.warn('Error parsing CSS for Noto Emoji font.');
}
}

/// Finds the minimum set of fonts which covers all of the [codeunits].
Expand Down Expand Up @@ -388,6 +392,9 @@ class _ResolvedNotoSubset {
final List<CodeunitRange> ranges;

_ResolvedNotoSubset(this.url, this.family, this.ranges);

@override
String toString() => '_ResolvedNotoSubset($family, $url)';
}

_NotoFont _notoSansSC = _NotoFont('Noto Sans SC', <CodeunitRange>[
Expand Down Expand Up @@ -660,21 +667,64 @@ class FallbackFontDownloadQueue {
}

class NotoDownloader {
int _debugActiveDownloadCount = 0;

/// Returns a future that resolves when there are no pending downloads.
///
/// Useful in tests to make sure that fonts are loaded before working with
/// text.
Future<void> debugWhenIdle() async {
if (assertionsEnabled) {
// Some downloads begin asynchronously in a microtask or in a Timer.run.
// Let those run before waiting for downloads to finish.
await Future<void>.delayed(Duration.zero);
while (_debugActiveDownloadCount > 0) {
await Future<void>.delayed(const Duration(milliseconds: 100));
// If we started with a non-zero count and hit zero while waiting, wait a
// little more to make sure another download doesn't get chained after
// the last one (e.g. font file download after font CSS download).
if (_debugActiveDownloadCount == 0) {
await Future<void>.delayed(const Duration(milliseconds: 100));
}
}
} else {
throw UnimplementedError();
}
}

/// Downloads the [url] and returns it as a [ByteBuffer].
///
/// Override this for testing.
Future<ByteBuffer> downloadAsBytes(String url) {
return html.window.fetch(url).then((dynamic fetchResult) => fetchResult
if (assertionsEnabled) {
_debugActiveDownloadCount += 1;
}
final Future<ByteBuffer> result = html.window.fetch(url).then((dynamic fetchResult) => fetchResult
.arrayBuffer()
.then<ByteBuffer>((dynamic x) => x as ByteBuffer));
if (assertionsEnabled) {
result.whenComplete(() {
_debugActiveDownloadCount -= 1;
});
}
return result;
}

/// Downloads the [url] and returns is as a [String].
///
/// Override this for testing.
Future<String> downloadAsString(String url) {
return html.window.fetch(url).then((dynamic response) =>
if (assertionsEnabled) {
_debugActiveDownloadCount += 1;
}
final Future<String> result = html.window.fetch(url).then((dynamic response) =>
response.text().then<String>((dynamic x) => x as String));
if (assertionsEnabled) {
result.whenComplete(() {
_debugActiveDownloadCount -= 1;
});
}
return result;
}
}

Expand Down
Loading