@@ -18,9 +18,9 @@ import 'image_stream.dart';
18
18
/// used for testing purposes.
19
19
typedef HttpRequestFactory = web.XMLHttpRequest Function ();
20
20
21
- /// The type for an overridable factory function for creating <img> elements,
22
- /// used for testing purposes.
23
- typedef ImgElementFactory = web.HTMLImageElement Function ();
21
+ /// The type for an overridable factory function for creating HTML elements to
22
+ /// display images, used for testing purposes.
23
+ typedef HtmlElementFactory = web.HTMLImageElement Function ();
24
24
25
25
// Method signature for _loadAsync decode callbacks.
26
26
typedef _SimpleDecoderCallback = Future <ui.Codec > Function (ui.ImmutableBuffer buffer);
@@ -40,17 +40,17 @@ void debugRestoreHttpRequestFactory() {
40
40
httpRequestFactory = _httpClient;
41
41
}
42
42
43
- /// The default <img> element factory.
43
+ /// The default HTML element factory.
44
44
web.HTMLImageElement _imgElementFactory () {
45
45
return web.document.createElement ('img' ) as web.HTMLImageElement ;
46
46
}
47
47
48
- /// The factory function that creates <img> elements, can be overridden for
48
+ /// The factory function that creates HTML elements, can be overridden for
49
49
/// tests.
50
50
@visibleForTesting
51
- ImgElementFactory imgElementFactory = _imgElementFactory;
51
+ HtmlElementFactory imgElementFactory = _imgElementFactory;
52
52
53
- /// Restores the default <img> element factory.
53
+ /// Restores the default HTML element factory.
54
54
@visibleForTesting
55
55
void debugRestoreImgElementFactory () {
56
56
imgElementFactory = _imgElementFactory;
@@ -63,7 +63,12 @@ void debugRestoreImgElementFactory() {
63
63
class NetworkImage extends image_provider.ImageProvider <image_provider.NetworkImage >
64
64
implements image_provider.NetworkImage {
65
65
/// Creates an object that fetches the image at the given URL.
66
- const NetworkImage (this .url, {this .scale = 1.0 , this .headers});
66
+ const NetworkImage (
67
+ this .url, {
68
+ this .scale = 1.0 ,
69
+ this .headers,
70
+ this .webHtmlElementStrategy = image_provider.WebHtmlElementStrategy .never,
71
+ });
67
72
68
73
@override
69
74
final String url;
@@ -74,6 +79,9 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
74
79
@override
75
80
final Map <String , String >? headers;
76
81
82
+ @override
83
+ final image_provider.WebHtmlElementStrategy webHtmlElementStrategy;
84
+
77
85
@override
78
86
Future <NetworkImage > obtainKey (image_provider.ImageConfiguration configuration) {
79
87
return SynchronousFuture <NetworkImage >(this );
@@ -136,19 +144,7 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
136
144
) async {
137
145
assert (key == this );
138
146
139
- final Uri resolved = Uri .base .resolve (key.url);
140
-
141
- final bool containsNetworkImageHeaders = key.headers? .isNotEmpty ?? false ;
142
-
143
- // We use a different method when headers are set because the
144
- // `ui_web.createImageCodecFromUrl` method is not capable of handling headers.
145
- if (containsNetworkImageHeaders) {
146
- // It is not possible to load an <img> element and pass the headers with
147
- // the request to fetch the image. Since the user has provided headers,
148
- // this function should assume the headers are required to resolve to
149
- // the correct resource and should not attempt to load the image in an
150
- // <img> tag without the headers.
151
-
147
+ Future <ImageStreamCompleter > loadViaDecode () async {
152
148
// Resolve the Codec before passing it to
153
149
// [MultiFrameImageStreamCompleter] so any errors aren't reported
154
150
// twice (once from the MultiFrameImageStreamCompleter and again
@@ -161,34 +157,38 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
161
157
debugLabel: key.url,
162
158
informationCollector: _imageStreamInformationCollector (key),
163
159
);
164
- } else if (isSkiaWeb) {
165
- try {
166
- // Resolve the Codec before passing it to
167
- // [MultiFrameImageStreamCompleter] so any errors aren't reported
168
- // twice (once from the MultiFrameImageStreamCompleter and again
169
- // from the wrapping [ForwardingImageStreamCompleter]).
170
- final ui.Codec codec = await _fetchImageBytes (decode);
171
- return MultiFrameImageStreamCompleter (
172
- chunkEvents: chunkEvents.stream,
173
- codec: Future <ui.Codec >.value (codec),
174
- scale: key.scale,
175
- debugLabel: key.url,
176
- informationCollector: _imageStreamInformationCollector (key),
177
- );
178
- } catch (e) {
179
- // If we failed to fetch the bytes, try to load the image in an <img>
180
- // element instead.
181
- final web.HTMLImageElement imageElement = imgElementFactory ();
182
- imageElement.src = key.url;
183
- // Decode the <img> element before creating the ImageStreamCompleter
184
- // to avoid double reporting the error.
185
- await imageElement.decode ().toDart;
186
- return OneFrameImageStreamCompleter (
187
- Future <ImageInfo >.value (WebImageInfo (imageElement, debugLabel: key.url)),
188
- informationCollector: _imageStreamInformationCollector (key),
189
- )..debugLabel = key.url;
190
- }
191
- } else {
160
+ }
161
+
162
+ Future <ImageStreamCompleter > loadViaImgElement () async {
163
+ // If we failed to fetch the bytes, try to load the image in an <img>
164
+ // element instead.
165
+ final web.HTMLImageElement imageElement = imgElementFactory ();
166
+ imageElement.src = key.url;
167
+ // Decode the <img> element before creating the ImageStreamCompleter
168
+ // to avoid double reporting the error.
169
+ await imageElement.decode ().toDart;
170
+ return OneFrameImageStreamCompleter (
171
+ Future <ImageInfo >.value (WebImageInfo (imageElement, debugLabel: key.url)),
172
+ informationCollector: _imageStreamInformationCollector (key),
173
+ )..debugLabel = key.url;
174
+ }
175
+
176
+ final bool containsNetworkImageHeaders = key.headers? .isNotEmpty ?? false ;
177
+ // When headers are set, the image can only be loaded by decoding.
178
+ //
179
+ // For the HTML renderer, `ui_web.createImageCodecFromUrl` method is not
180
+ // capable of handling headers.
181
+ //
182
+ // For CanvasKit and Skwasm, it is not possible to load an <img> element and
183
+ // pass the headers with the request to fetch the image. Since the user has
184
+ // provided headers, this function should assume the headers are required to
185
+ // resolve to the correct resource and should not attempt to load the image
186
+ // in an <img> tag without the headers.
187
+ if (containsNetworkImageHeaders) {
188
+ return loadViaDecode ();
189
+ }
190
+
191
+ if (! isSkiaWeb) {
192
192
// This branch is only hit by the HTML renderer, which is deprecated. The
193
193
// HTML renderer supports loading images with CORS restrictions, so we
194
194
// don't need to catch errors and try loading the image in an <img> tag
@@ -198,6 +198,7 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
198
198
// [MultiFrameImageStreamCompleter] so any errors aren't reported
199
199
// twice (once from the MultiFrameImageStreamCompleter) and again
200
200
// from the wrapping [ForwardingImageStreamCompleter].
201
+ final Uri resolved = Uri .base .resolve (key.url);
201
202
final ui.Codec codec = await ui_web.createImageCodecFromUrl (
202
203
resolved,
203
204
chunkCallback: (int bytes, int total) {
@@ -212,6 +213,21 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
212
213
informationCollector: _imageStreamInformationCollector (key),
213
214
);
214
215
}
216
+
217
+ switch (webHtmlElementStrategy) {
218
+ case image_provider.WebHtmlElementStrategy .never:
219
+ return loadViaDecode ();
220
+ case image_provider.WebHtmlElementStrategy .prefer:
221
+ return loadViaImgElement ();
222
+ case image_provider.WebHtmlElementStrategy .fallback:
223
+ try {
224
+ // Await here so that errors occurred during the asynchronous process
225
+ // of `loadViaDecode` are caught and triggers `loadViaImgElement`.
226
+ return await loadViaDecode ();
227
+ } catch (e) {
228
+ return loadViaImgElement ();
229
+ }
230
+ }
215
231
}
216
232
217
233
Future <ui.Codec > _fetchImageBytes (_SimpleDecoderCallback decode) async {
0 commit comments