Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 45bbbd7

Browse files
authored
Recover when browser throws on ImageElement.decode due to too many images (#15160)
1 parent f9a2fab commit 45bbbd7

File tree

1 file changed

+33
-26
lines changed

1 file changed

+33
-26
lines changed

lib/web_ui/lib/src/engine/html_image_codec.dart

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// found in the LICENSE file.
44

55
part of engine;
6-
76
final bool _supportsDecode = js_util.getProperty(
87
js_util.getProperty(
98
js_util.getProperty(html.window, 'Image'), 'prototype'),
@@ -23,46 +22,54 @@ class HtmlCodec implements ui.Codec {
2322

2423
@override
2524
Future<ui.FrameInfo> getNextFrame() async {
26-
StreamSubscription<html.Event> loadSubscription;
27-
StreamSubscription<html.Event> errorSubscription;
2825
final Completer<ui.FrameInfo> completer = Completer<ui.FrameInfo>();
29-
final html.ImageElement imgElement = html.ImageElement();
30-
// If the browser doesn't support asynchronous decoding of an image,
31-
// then use the `onload` event to decide when it's ready to paint to the
32-
// DOM. Unfortunately, this will case the image to be decoded synchronously
33-
// on the main thread, and may cause dropped framed.
34-
if (!_supportsDecode) {
35-
loadSubscription = imgElement.onLoad.listen((html.Event event) {
36-
loadSubscription.cancel();
37-
errorSubscription.cancel();
26+
if (_supportsDecode) {
27+
final html.ImageElement imgElement = html.ImageElement();
28+
imgElement.src = src;
29+
js_util.setProperty(imgElement, 'decoding', 'async');
30+
imgElement.decode().then((dynamic _) {
3831
final HtmlImage image = HtmlImage(
3932
imgElement,
4033
imgElement.naturalWidth,
4134
imgElement.naturalHeight,
4235
);
4336
completer.complete(SingleFrameInfo(image));
37+
}).catchError((e) {
38+
// This code path is hit on Chrome 80.0.3987.16 when too many
39+
// images are on the page (~1000).
40+
// Fallback here is to load using onLoad instead.
41+
_decodeUsingOnLoad(completer);
4442
});
43+
} else {
44+
_decodeUsingOnLoad(completer);
4545
}
46+
return completer.future;
47+
}
48+
49+
void _decodeUsingOnLoad(Completer completer) {
50+
StreamSubscription<html.Event> loadSubscription;
51+
StreamSubscription<html.Event> errorSubscription;
52+
final html.ImageElement imgElement = html.ImageElement();
53+
// If the browser doesn't support asynchronous decoding of an image,
54+
// then use the `onload` event to decide when it's ready to paint to the
55+
// DOM. Unfortunately, this will cause the image to be decoded synchronously
56+
// on the main thread, and may cause dropped framed.
4657
errorSubscription = imgElement.onError.listen((html.Event event) {
4758
loadSubscription?.cancel();
4859
errorSubscription.cancel();
4960
completer.completeError(event);
5061
});
62+
loadSubscription = imgElement.onLoad.listen((html.Event event) {
63+
loadSubscription.cancel();
64+
errorSubscription.cancel();
65+
final HtmlImage image = HtmlImage(
66+
imgElement,
67+
imgElement.naturalWidth,
68+
imgElement.naturalHeight,
69+
);
70+
completer.complete(SingleFrameInfo(image));
71+
});
5172
imgElement.src = src;
52-
// If the browser supports asynchronous image decoding, use that instead
53-
// of `onload`.
54-
if (_supportsDecode) {
55-
imgElement.decode().then((dynamic _) {
56-
errorSubscription.cancel();
57-
final HtmlImage image = HtmlImage(
58-
imgElement,
59-
imgElement.naturalWidth,
60-
imgElement.naturalHeight,
61-
);
62-
completer.complete(SingleFrameInfo(image));
63-
});
64-
}
65-
return completer.future;
6673
}
6774

6875
@override

0 commit comments

Comments
 (0)