Skip to content

Commit e5d5c01

Browse files
authored
[web] Upgrade Chrome to 141 (for engine tests) (flutter#177743)
- Update Chrome to 141 for web engine tests. - Improve image codec tests so they exercise all frames. - Skip the frames of certain images that are known to cause problems in Chrome. Chrome bug for the problematic images: https://issues.chromium.org/456445108 Fixes flutter#168686
1 parent a913d39 commit e5d5c01

File tree

5 files changed

+85
-56
lines changed

5 files changed

+85
-56
lines changed

engine/src/flutter/ci/builders/linux_web_engine_test.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@
179179
},
180180
{
181181
"dependency": "chrome_and_driver",
182-
"version": "125.0.6422.141"
182+
"version": "141.0.7390.76"
183183
}
184184
],
185185
"tasks": [

engine/src/flutter/lib/web_ui/dev/package_lock.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# Please refer to the "Upgrade Browser Version" section in the README.md for
22
# more details on how to update browser version numbers.
33
chrome:
4-
version: '133.0.6943.53'
4+
# Latest version can be found here:
5+
# https://googlechromelabs.github.io/chrome-for-testing/
6+
version: '141.0.7390.76'
57

68
firefox:
9+
# Latest version can be found here:
10+
# https://www.firefox.com/en-US/releases/
711
version: '143.0'
812

913
edge:

engine/src/flutter/lib/web_ui/lib/src/engine/image_decoder.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,16 @@ abstract class BrowserImageDecoder implements ui.Codec {
134134
}
135135

136136
final DecodeResult result = await webDecoder
137-
.decode(DecodeOptions(frameIndex: _nextFrameIndex))
137+
// Using `completeFramesOnly: false` to get frames even from partially decoded images.
138+
// Typically, this wouldn't work well in Flutter because Flutter doesn't support progressive
139+
// image rendering. So this could result in frames being rendered at lower quality than
140+
// expected.
141+
//
142+
// However, since we wait for the entire image to be decoded using [webDecoder.completed],
143+
// this ends up being a non-issue in practice.
144+
//
145+
// For more details, see: https://issues.chromium.org/issues/456445108
146+
.decode(DecodeOptions(frameIndex: _nextFrameIndex, completeFramesOnly: false))
138147
.toDart;
139148
final VideoFrame frame = result.image;
140149
_nextFrameIndex = (_nextFrameIndex + 1) % frameCount;

engine/src/flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ extension type DecodeResult(JSObject _) implements JSObject {
152152
///
153153
/// * https://www.w3.org/TR/webcodecs/#dictdef-imagedecodeoptions
154154
extension type DecodeOptions._(JSObject _) implements JSObject {
155-
external DecodeOptions({required int frameIndex});
155+
external DecodeOptions({required int frameIndex, required bool completeFramesOnly});
156156
}
157157

158158
/// The only frame in a static image, or one of the frames in an animated one.

engine/src/flutter/lib/web_ui/test/ui/codecs_test.dart

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,19 @@ void main() {
1818
}
1919

2020
abstract class TestCodec {
21-
TestCodec({required this.description});
22-
final String description;
23-
24-
ui.Codec? _cachedCodec;
25-
26-
Future<ui.Codec> getCodec() async => _cachedCodec ??= await createCodec();
27-
28-
Future<ui.Codec> createCodec();
29-
30-
void dispose() {
31-
_cachedCodec?.dispose();
32-
_cachedCodec = null;
33-
}
34-
}
35-
36-
abstract class TestFileCodec extends TestCodec {
37-
TestFileCodec.fromTestFile(this.testFile, {required super.description});
21+
TestCodec.fromTestFile(this.testFile, {required this.description});
3822

3923
final String testFile;
24+
final String description;
4025

4126
Future<ui.Codec> createCodecFromTestFile(String testFile);
4227

43-
@override
4428
Future<ui.Codec> createCodec() {
4529
return createCodecFromTestFile(testFile);
4630
}
4731
}
4832

49-
class UrlTestCodec extends TestFileCodec {
33+
class UrlTestCodec extends TestCodec {
5034
UrlTestCodec(super.testFile, this.codecFactory, String function)
5135
: super.fromTestFile(description: 'created with $function("$testFile")');
5236

@@ -58,7 +42,7 @@ class UrlTestCodec extends TestFileCodec {
5842
}
5943
}
6044

61-
class FetchTestCodec extends TestFileCodec {
45+
class FetchTestCodec extends TestCodec {
6246
FetchTestCodec(super.testFile, this.codecFactory, String function)
6347
: super.fromTestFile(
6448
description:
@@ -70,7 +54,7 @@ class FetchTestCodec extends TestFileCodec {
7054

7155
@override
7256
Future<ui.Codec> createCodecFromTestFile(String testFile) async {
73-
final HttpFetchResponse response = await httpFetch(testFile);
57+
final HttpFetchResponse response = await httpFetch('/test_images/$testFile');
7458

7559
if (!response.hasPayload) {
7660
throw Exception('Unable to fetch() image test file "$testFile"');
@@ -81,7 +65,7 @@ class FetchTestCodec extends TestFileCodec {
8165
}
8266
}
8367

84-
class BitmapTestCodec extends TestFileCodec {
68+
class BitmapTestCodec extends TestCodec {
8569
BitmapTestCodec(super.testFile, this.codecFactory, String function)
8670
: super.fromTestFile(
8771
description:
@@ -94,7 +78,7 @@ class BitmapTestCodec extends TestFileCodec {
9478
@override
9579
Future<ui.Codec> createCodecFromTestFile(String testFile) async {
9680
final DomHTMLImageElement imageElement = createDomHTMLImageElement();
97-
imageElement.src = testFile;
81+
imageElement.src = '/test_images/$testFile';
9882
imageElement.decoding = 'async';
9983

10084
await imageElement.decode();
@@ -168,26 +152,25 @@ Future<void> testMain() async {
168152
);
169153
testCodecs.add(
170154
FetchTestCodec(
171-
'/test_images/$testFile',
155+
testFile,
172156
(Uint8List bytes) => renderer.instantiateImageCodec(bytes),
173157
'renderer.instantiateImageCodec',
174158
),
175159
);
176160
testCodecs.add(
177161
FetchTestCodec(
178-
'/test_images/$testFile',
162+
testFile,
179163
(Uint8List bytes) => renderer.instantiateImageCodec(
180164
bytes,
181165
targetWidth: testTargetWidth,
182166
targetHeight: testTargetHeight,
183167
),
184-
'renderer.instantiateImageCodec '
185-
'($testTargetWidth x $testTargetHeight)',
168+
'renderer.instantiateImageCodec ($testTargetWidth x $testTargetHeight)',
186169
),
187170
);
188171
testCodecs.add(
189172
BitmapTestCodec(
190-
'test_images/$testFile',
173+
testFile,
191174
(DomImageBitmap bitmap) async => renderer.createImageFromImageBitmap(bitmap),
192175
'renderer.createImageFromImageBitmap',
193176
),
@@ -204,33 +187,47 @@ Future<void> testMain() async {
204187
mockHttpFetchResponseFactory = null;
205188
});
206189

207-
group('Codecs', () {
208-
final List<TestCodec> testCodecs = createTestCodecs();
209-
for (final TestCodec testCodec in testCodecs) {
210-
test('${testCodec.description} can create an image', () async {
211-
try {
212-
final ui.Codec codec = await testCodec.getCodec();
213-
final ui.FrameInfo frameInfo = await codec.getNextFrame();
214-
final ui.Image image = frameInfo.image;
215-
expect(image, isNotNull);
216-
expect(image.width, isNonZero);
217-
expect(image.height, isNonZero);
218-
expect(image.colorSpace, isNotNull);
219-
} catch (e) {
220-
throw TestFailure('Failed to get image for ${testCodec.description}: $e');
190+
void runCodecTest(TestCodec testCodec) {
191+
const problematicChromeImages = <String, Set<int>>{
192+
// Frame 2 cause Chrome to crash.
193+
// https://issues.chromium.org/456445108
194+
'crbug445556737.png': {2},
195+
// Frames 2 and 3 cause Chrome to crash.
196+
// https://issues.chromium.org/456445108
197+
'interlaced-multiframe-with-blending.png': {2, 3},
198+
};
199+
200+
test('${testCodec.description} can create an image and convert it to byte array', () async {
201+
final ui.Codec codec = await testCodec.createCodec();
202+
203+
final Set<int> problematicFrames;
204+
if (isChromium && problematicChromeImages.containsKey(testCodec.testFile)) {
205+
// Encountered an image with known problematic frames on Chromium.
206+
problematicFrames = problematicChromeImages[testCodec.testFile]!;
207+
} else {
208+
problematicFrames = <int>{};
209+
}
210+
211+
for (int i = 0; i < codec.frameCount; i++) {
212+
if (problematicFrames.contains(i)) {
213+
printWarning(
214+
'Skipping frame $i of ${testCodec.description} due to known Chromium crash bug.',
215+
);
216+
continue;
221217
}
222-
});
223218

224-
test('${testCodec.description} can be decoded with toByteData', () async {
225-
ui.Image image;
219+
final ui.Image image;
226220
try {
227-
final ui.Codec codec = await testCodec.getCodec();
228221
final ui.FrameInfo frameInfo = await codec.getNextFrame();
229222
image = frameInfo.image;
230223
} catch (e) {
231-
throw TestFailure('Failed to get image for ${testCodec.description}: $e');
224+
codec.dispose();
225+
throw TestFailure('Failed to get image at frame $i for ${testCodec.description}: $e');
232226
}
233227

228+
expect(image.width, isNonZero);
229+
expect(image.height, isNonZero);
230+
234231
final ByteData? byteData = await image.toByteData();
235232
expect(
236233
byteData,
@@ -249,12 +246,31 @@ Future<void> testMain() async {
249246
'${testCodec.description} toByteData() should '
250247
'contain nonzero value',
251248
);
252-
});
253-
}
254-
for (final testCodec in testCodecs) {
255-
testCodec.dispose();
256-
}
249+
}
250+
251+
// After all frames are decoded and tested, dispose the codec.
252+
codec.dispose();
253+
});
254+
}
255+
256+
group('Codecs (default browserSupportsImageDecoder)', () {
257+
createTestCodecs().forEach(runCodecTest);
257258
});
259+
260+
if (browserSupportsImageDecoder) {
261+
// For the sake of completeness, test codec fallback logic on browsers that support
262+
// `ImageDecoder`.
263+
group('Codecs (browserSupportsImageDecoder=false)', () {
264+
setUpAll(() {
265+
browserSupportsImageDecoder = false;
266+
});
267+
tearDownAll(() {
268+
debugResetBrowserSupportsImageDecoder();
269+
});
270+
271+
createTestCodecs().forEach(runCodecTest);
272+
});
273+
}
258274
});
259275

260276
test('crossOrigin requests cause an error', () async {

0 commit comments

Comments
 (0)