OLD | NEW |
1 /* | 1 /* |
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) |
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) |
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) |
5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) | 5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) |
6 Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. | 6 Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
7 | 7 |
8 This library is free software; you can redistribute it and/or | 8 This library is free software; you can redistribute it and/or |
9 modify it under the terms of the GNU Library General Public | 9 modify it under the terms of the GNU Library General Public |
10 License as published by the Free Software Foundation; either | 10 License as published by the Free Software Foundation; either |
11 version 2 of the License, or (at your option) any later version. | 11 version 2 of the License, or (at your option) any later version. |
12 | 12 |
13 This library is distributed in the hope that it will be useful, | 13 This library is distributed in the hope that it will be useful, |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 Library General Public License for more details. | 16 Library General Public License for more details. |
17 | 17 |
18 You should have received a copy of the GNU Library General Public License | 18 You should have received a copy of the GNU Library General Public License |
19 along with this library; see the file COPYING.LIB. If not, write to | 19 along with this library; see the file COPYING.LIB. If not, write to |
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 Boston, MA 02110-1301, USA. | 21 Boston, MA 02110-1301, USA. |
22 */ | 22 */ |
23 | 23 |
24 #include "core/loader/resource/ImageResource.h" | 24 #include "core/loader/resource/ImageResource.h" |
25 | 25 |
| 26 #include <stdint.h> |
| 27 #include <v8.h> |
26 #include <memory> | 28 #include <memory> |
27 | 29 |
28 #include "core/loader/resource/ImageResourceContent.h" | 30 #include "core/loader/resource/ImageResourceContent.h" |
29 #include "core/loader/resource/ImageResourceInfo.h" | 31 #include "core/loader/resource/ImageResourceInfo.h" |
30 #include "platform/Histogram.h" | 32 #include "platform/Histogram.h" |
31 #include "platform/RuntimeEnabledFeatures.h" | 33 #include "platform/RuntimeEnabledFeatures.h" |
32 #include "platform/SharedBuffer.h" | 34 #include "platform/SharedBuffer.h" |
33 #include "platform/instrumentation/tracing/TraceEvent.h" | 35 #include "platform/instrumentation/tracing/TraceEvent.h" |
34 #include "platform/loader/fetch/MemoryCache.h" | 36 #include "platform/loader/fetch/MemoryCache.h" |
35 #include "platform/loader/fetch/ResourceClient.h" | 37 #include "platform/loader/fetch/ResourceClient.h" |
36 #include "platform/loader/fetch/ResourceFetcher.h" | 38 #include "platform/loader/fetch/ResourceFetcher.h" |
37 #include "platform/loader/fetch/ResourceLoader.h" | 39 #include "platform/loader/fetch/ResourceLoader.h" |
38 #include "platform/loader/fetch/ResourceLoadingLog.h" | 40 #include "platform/loader/fetch/ResourceLoadingLog.h" |
| 41 #include "platform/network/HTTPParsers.h" |
39 #include "platform/weborigin/SecurityViolationReportingPolicy.h" | 42 #include "platform/weborigin/SecurityViolationReportingPolicy.h" |
40 #include "public/platform/Platform.h" | 43 #include "public/platform/Platform.h" |
41 #include "v8/include/v8.h" | 44 #include "v8/include/v8.h" |
42 #include "wtf/CurrentTime.h" | 45 #include "wtf/CurrentTime.h" |
43 #include "wtf/StdLibExtras.h" | 46 #include "wtf/StdLibExtras.h" |
44 | 47 |
45 namespace blink { | 48 namespace blink { |
46 namespace { | 49 namespace { |
47 // The amount of time to wait before informing the clients that the image has | 50 // The amount of time to wait before informing the clients that the image has |
48 // been updated (in seconds). This effectively throttles invalidations that | 51 // been updated (in seconds). This effectively throttles invalidations that |
(...skipping 23 matching lines...) Expand all Loading... |
72 bool hasDevicePixelRatioHeaderValue() const override { | 75 bool hasDevicePixelRatioHeaderValue() const override { |
73 return m_resource->m_hasDevicePixelRatioHeaderValue; | 76 return m_resource->m_hasDevicePixelRatioHeaderValue; |
74 } | 77 } |
75 float devicePixelRatioHeaderValue() const override { | 78 float devicePixelRatioHeaderValue() const override { |
76 return m_resource->m_devicePixelRatioHeaderValue; | 79 return m_resource->m_devicePixelRatioHeaderValue; |
77 } | 80 } |
78 const ResourceResponse& response() const override { | 81 const ResourceResponse& response() const override { |
79 return m_resource->response(); | 82 return m_resource->response(); |
80 } | 83 } |
81 ResourceStatus getStatus() const override { return m_resource->getStatus(); } | 84 ResourceStatus getStatus() const override { return m_resource->getStatus(); } |
82 bool isPlaceholder() const override { return m_resource->isPlaceholder(); } | 85 bool shouldShowPlaceholder() const override { |
| 86 return m_resource->shouldShowPlaceholder(); |
| 87 } |
83 bool isCacheValidator() const override { | 88 bool isCacheValidator() const override { |
84 return m_resource->isCacheValidator(); | 89 return m_resource->isCacheValidator(); |
85 } | 90 } |
86 bool schedulingReloadOrShouldReloadBrokenPlaceholder() const override { | 91 bool schedulingReloadOrShouldReloadBrokenPlaceholder() const override { |
87 return m_resource->m_isSchedulingReload || | 92 return m_resource->m_isSchedulingReload || |
88 m_resource->shouldReloadBrokenPlaceholder(); | 93 m_resource->shouldReloadBrokenPlaceholder(); |
89 } | 94 } |
90 bool isAccessAllowed( | 95 bool isAccessAllowed( |
91 SecurityOrigin* securityOrigin, | 96 SecurityOrigin* securityOrigin, |
92 DoesCurrentFrameHaveSingleSecurityOrigin | 97 DoesCurrentFrameHaveSingleSecurityOrigin |
93 doesCurrentFrameHasSingleSecurityOrigin) const override { | 98 doesCurrentFrameHasSingleSecurityOrigin) const override { |
94 return m_resource->isAccessAllowed(securityOrigin, | 99 return m_resource->isAccessAllowed(securityOrigin, |
95 doesCurrentFrameHasSingleSecurityOrigin); | 100 doesCurrentFrameHasSingleSecurityOrigin); |
96 } | 101 } |
97 bool hasCacheControlNoStoreHeader() const override { | 102 bool hasCacheControlNoStoreHeader() const override { |
98 return m_resource->hasCacheControlNoStoreHeader(); | 103 return m_resource->hasCacheControlNoStoreHeader(); |
99 } | 104 } |
100 const ResourceError& resourceError() const override { | 105 const ResourceError& resourceError() const override { |
101 return m_resource->resourceError(); | 106 return m_resource->resourceError(); |
102 } | 107 } |
103 | 108 |
104 void setIsPlaceholder(bool isPlaceholder) override { | |
105 m_resource->m_isPlaceholder = isPlaceholder; | |
106 } | |
107 void setDecodedSize(size_t size) override { | 109 void setDecodedSize(size_t size) override { |
108 m_resource->setDecodedSize(size); | 110 m_resource->setDecodedSize(size); |
109 } | 111 } |
110 void willAddClientOrObserver() override { | 112 void willAddClientOrObserver() override { |
111 m_resource->willAddClientOrObserver(Resource::MarkAsReferenced); | 113 m_resource->willAddClientOrObserver(Resource::MarkAsReferenced); |
112 } | 114 } |
113 void didRemoveClientOrObserver() override { | 115 void didRemoveClientOrObserver() override { |
114 m_resource->didRemoveClientOrObserver(); | 116 m_resource->didRemoveClientOrObserver(); |
115 } | 117 } |
116 void emulateLoadStartedForInspector( | 118 void emulateLoadStartedForInspector( |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 if (blockReason == ResourceRequestBlockedReason::None) | 168 if (blockReason == ResourceRequestBlockedReason::None) |
167 fetcher->context().sendImagePing(requestURL); | 169 fetcher->context().sendImagePing(requestURL); |
168 } | 170 } |
169 return nullptr; | 171 return nullptr; |
170 } | 172 } |
171 | 173 |
172 ImageResource* resource = toImageResource( | 174 ImageResource* resource = toImageResource( |
173 fetcher->requestResource(request, ImageResourceFactory(request))); | 175 fetcher->requestResource(request, ImageResourceFactory(request))); |
174 if (resource && | 176 if (resource && |
175 request.placeholderImageRequestType() != FetchRequest::AllowPlaceholder && | 177 request.placeholderImageRequestType() != FetchRequest::AllowPlaceholder && |
176 resource->m_isPlaceholder) { | 178 resource->shouldShowPlaceholder()) { |
177 // If the image is a placeholder, but this fetch doesn't allow a | 179 // If the image is a placeholder, but this fetch doesn't allow a |
178 // placeholder, then load the original image. Note that the cache is not | 180 // placeholder, then load the original image. Note that the cache is not |
179 // bypassed here - it should be fine to use a cached copy if possible. | 181 // bypassed here - it should be fine to use a cached copy if possible. |
180 resource->reloadIfLoFiOrPlaceholderImage( | 182 resource->reloadIfLoFiOrPlaceholderImage( |
181 fetcher, kReloadAlwaysWithExistingCachePolicy); | 183 fetcher, kReloadAlwaysWithExistingCachePolicy); |
182 } | 184 } |
183 return resource; | 185 return resource; |
184 } | 186 } |
185 | 187 |
186 ImageResource* ImageResource::create(const ResourceRequest& request) { | 188 ImageResource* ImageResource::create(const ResourceRequest& request) { |
187 return new ImageResource(request, ResourceLoaderOptions(), | 189 return new ImageResource(request, ResourceLoaderOptions(), |
188 ImageResourceContent::create(), false); | 190 ImageResourceContent::create(), false); |
189 } | 191 } |
190 | 192 |
191 ImageResource::ImageResource(const ResourceRequest& resourceRequest, | 193 ImageResource::ImageResource(const ResourceRequest& resourceRequest, |
192 const ResourceLoaderOptions& options, | 194 const ResourceLoaderOptions& options, |
193 ImageResourceContent* content, | 195 ImageResourceContent* content, |
194 bool isPlaceholder) | 196 bool isPlaceholder) |
195 : Resource(resourceRequest, Image, options), | 197 : Resource(resourceRequest, Image, options), |
196 m_content(content), | 198 m_content(content), |
197 m_devicePixelRatioHeaderValue(1.0), | 199 m_devicePixelRatioHeaderValue(1.0), |
198 m_hasDevicePixelRatioHeaderValue(false), | 200 m_hasDevicePixelRatioHeaderValue(false), |
199 m_isSchedulingReload(false), | 201 m_isSchedulingReload(false), |
200 m_isPlaceholder(isPlaceholder), | 202 m_placeholderOption( |
| 203 isPlaceholder ? PlaceholderOption::ShowAndReloadPlaceholderAlways |
| 204 : PlaceholderOption::DoNotReloadPlaceholder), |
201 m_flushTimer(this, &ImageResource::flushImageIfNeeded) { | 205 m_flushTimer(this, &ImageResource::flushImageIfNeeded) { |
202 DCHECK(getContent()); | 206 DCHECK(getContent()); |
203 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; | 207 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; |
204 getContent()->setImageResourceInfo(new ImageResourceInfoImpl(this)); | 208 getContent()->setImageResourceInfo(new ImageResourceInfoImpl(this)); |
205 } | 209 } |
206 | 210 |
207 ImageResource::~ImageResource() { | 211 ImageResource::~ImageResource() { |
208 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; | 212 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; |
209 } | 213 } |
210 | 214 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 | 317 |
314 void ImageResource::flushImageIfNeeded(TimerBase*) { | 318 void ImageResource::flushImageIfNeeded(TimerBase*) { |
315 // We might have already loaded the image fully, in which case we don't need | 319 // We might have already loaded the image fully, in which case we don't need |
316 // to call |updateImage()|. | 320 // to call |updateImage()|. |
317 if (isLoading()) { | 321 if (isLoading()) { |
318 m_lastFlushTime = WTF::monotonicallyIncreasingTime(); | 322 m_lastFlushTime = WTF::monotonicallyIncreasingTime(); |
319 updateImage(this->data(), ImageResourceContent::UpdateImage, false); | 323 updateImage(this->data(), ImageResourceContent::UpdateImage, false); |
320 } | 324 } |
321 } | 325 } |
322 | 326 |
323 bool ImageResource::willPaintBrokenImage() const { | |
324 return errorOccurred(); | |
325 } | |
326 | |
327 void ImageResource::decodeError(bool allDataReceived) { | 327 void ImageResource::decodeError(bool allDataReceived) { |
328 size_t size = encodedSize(); | 328 size_t size = encodedSize(); |
329 | 329 |
330 clearData(); | 330 clearData(); |
331 setEncodedSize(0); | 331 setEncodedSize(0); |
332 if (!errorOccurred()) | 332 if (!errorOccurred()) |
333 setStatus(ResourceStatus::DecodeError); | 333 setStatus(ResourceStatus::DecodeError); |
334 | 334 |
335 // Finishes loading if needed, and notifies observers. | 335 // Finishes loading if needed, and notifies observers. |
336 if (!allDataReceived && loader()) { | 336 if (!allDataReceived && loader()) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
373 if (m_multipartParser) | 373 if (m_multipartParser) |
374 m_multipartParser->cancel(); | 374 m_multipartParser->cancel(); |
375 // TODO(hiroshige): Move setEncodedSize() call to Resource::error() if it | 375 // TODO(hiroshige): Move setEncodedSize() call to Resource::error() if it |
376 // is really needed, or remove it otherwise. | 376 // is really needed, or remove it otherwise. |
377 setEncodedSize(0); | 377 setEncodedSize(0); |
378 Resource::error(error); | 378 Resource::error(error); |
379 updateImage(nullptr, ImageResourceContent::ClearImageAndNotifyObservers, | 379 updateImage(nullptr, ImageResourceContent::ClearImageAndNotifyObservers, |
380 true); | 380 true); |
381 } | 381 } |
382 | 382 |
| 383 // Determines if |response| likely contains the entire resource for the purposes |
| 384 // of determining whether or not to show a placeholder, e.g. if the server |
| 385 // responded with a full 200 response or if the full image is smaller than the |
| 386 // requested range. |
| 387 static bool isEntireResource(const ResourceResponse& response) { |
| 388 if (response.httpStatusCode() != 206) |
| 389 return true; |
| 390 |
| 391 int64_t firstBytePosition = -1, lastBytePosition = -1, instanceLength = -1; |
| 392 return parseContentRangeHeaderFor206( |
| 393 response.httpHeaderField("Content-Range"), &firstBytePosition, |
| 394 &lastBytePosition, &instanceLength) && |
| 395 firstBytePosition == 0 && lastBytePosition + 1 == instanceLength; |
| 396 } |
| 397 |
383 void ImageResource::responseReceived( | 398 void ImageResource::responseReceived( |
384 const ResourceResponse& response, | 399 const ResourceResponse& response, |
385 std::unique_ptr<WebDataConsumerHandle> handle) { | 400 std::unique_ptr<WebDataConsumerHandle> handle) { |
386 DCHECK(!handle); | 401 DCHECK(!handle); |
387 DCHECK(!m_multipartParser); | 402 DCHECK(!m_multipartParser); |
388 // If there's no boundary, just handle the request normally. | 403 // If there's no boundary, just handle the request normally. |
389 if (response.isMultipart() && !response.multipartBoundary().isEmpty()) { | 404 if (response.isMultipart() && !response.multipartBoundary().isEmpty()) { |
390 m_multipartParser = new MultipartImageResourceParser( | 405 m_multipartParser = new MultipartImageResourceParser( |
391 response, response.multipartBoundary(), this); | 406 response, response.multipartBoundary(), this); |
392 } | 407 } |
393 Resource::responseReceived(response, std::move(handle)); | 408 Resource::responseReceived(response, std::move(handle)); |
394 if (RuntimeEnabledFeatures::clientHintsEnabled()) { | 409 if (RuntimeEnabledFeatures::clientHintsEnabled()) { |
395 m_devicePixelRatioHeaderValue = | 410 m_devicePixelRatioHeaderValue = |
396 this->response() | 411 this->response() |
397 .httpHeaderField(HTTPNames::Content_DPR) | 412 .httpHeaderField(HTTPNames::Content_DPR) |
398 .toFloat(&m_hasDevicePixelRatioHeaderValue); | 413 .toFloat(&m_hasDevicePixelRatioHeaderValue); |
399 if (!m_hasDevicePixelRatioHeaderValue || | 414 if (!m_hasDevicePixelRatioHeaderValue || |
400 m_devicePixelRatioHeaderValue <= 0.0) { | 415 m_devicePixelRatioHeaderValue <= 0.0) { |
401 m_devicePixelRatioHeaderValue = 1.0; | 416 m_devicePixelRatioHeaderValue = 1.0; |
402 m_hasDevicePixelRatioHeaderValue = false; | 417 m_hasDevicePixelRatioHeaderValue = false; |
403 } | 418 } |
404 } | 419 } |
| 420 |
| 421 if (m_placeholderOption == |
| 422 PlaceholderOption::ShowAndReloadPlaceholderAlways && |
| 423 isEntireResource(this->response())) { |
| 424 if (this->response().httpStatusCode() < 400 || |
| 425 this->response().httpStatusCode() >= 600) { |
| 426 // Don't treat a complete and broken image as a placeholder if the |
| 427 // response code is something other than a 4xx or 5xx error. |
| 428 // This is done to prevent reissuing the request in cases like |
| 429 // "204 No Content" responses to tracking requests triggered by <img> |
| 430 // tags, and <img> tags used to preload non-image resources. |
| 431 m_placeholderOption = PlaceholderOption::DoNotReloadPlaceholder; |
| 432 } else { |
| 433 m_placeholderOption = PlaceholderOption::ReloadPlaceholderOnDecodeError; |
| 434 } |
| 435 } |
405 } | 436 } |
406 | 437 |
| 438 bool ImageResource::shouldShowPlaceholder() const { |
| 439 switch (m_placeholderOption) { |
| 440 case PlaceholderOption::ShowAndReloadPlaceholderAlways: |
| 441 return true; |
| 442 case PlaceholderOption::ReloadPlaceholderOnDecodeError: |
| 443 case PlaceholderOption::DoNotReloadPlaceholder: |
| 444 return false; |
| 445 } |
| 446 NOTREACHED(); |
| 447 return false; |
| 448 } |
| 449 |
| 450 bool ImageResource::shouldReloadBrokenPlaceholder() const { |
| 451 switch (m_placeholderOption) { |
| 452 case PlaceholderOption::ShowAndReloadPlaceholderAlways: |
| 453 return errorOccurred(); |
| 454 case PlaceholderOption::ReloadPlaceholderOnDecodeError: |
| 455 return getStatus() == ResourceStatus::DecodeError; |
| 456 case PlaceholderOption::DoNotReloadPlaceholder: |
| 457 return false; |
| 458 } |
| 459 NOTREACHED(); |
| 460 return false; |
| 461 } |
| 462 |
407 static bool isLoFiImage(const ImageResource& resource) { | 463 static bool isLoFiImage(const ImageResource& resource) { |
408 if (!(resource.resourceRequest().previewsState() & | 464 if (!(resource.resourceRequest().previewsState() & |
409 WebURLRequest::ServerLoFiOn)) { | 465 WebURLRequest::ServerLoFiOn)) { |
410 return false; | 466 return false; |
411 } | 467 } |
412 return !resource.isLoaded() || | 468 return !resource.isLoaded() || |
413 resource.response() | 469 resource.response() |
414 .httpHeaderField("chrome-proxy-content-transform") | 470 .httpHeaderField("chrome-proxy-content-transform") |
415 .contains("empty-image"); | 471 .contains("empty-image"); |
416 } | 472 } |
417 | 473 |
418 void ImageResource::reloadIfLoFiOrPlaceholderImage( | 474 void ImageResource::reloadIfLoFiOrPlaceholderImage( |
419 ResourceFetcher* fetcher, | 475 ResourceFetcher* fetcher, |
420 ReloadLoFiOrPlaceholderPolicy policy) { | 476 ReloadLoFiOrPlaceholderPolicy policy) { |
421 if (policy == kReloadIfNeeded && !shouldReloadBrokenPlaceholder()) | 477 if (policy == kReloadIfNeeded && !shouldReloadBrokenPlaceholder()) |
422 return; | 478 return; |
423 | 479 |
424 if (!m_isPlaceholder && !isLoFiImage(*this)) | 480 if (m_placeholderOption == PlaceholderOption::DoNotReloadPlaceholder && |
| 481 !isLoFiImage(*this)) |
425 return; | 482 return; |
426 | 483 |
427 // Prevent clients and observers from being notified of completion while the | 484 // Prevent clients and observers from being notified of completion while the |
428 // reload is being scheduled, so that e.g. canceling an existing load in | 485 // reload is being scheduled, so that e.g. canceling an existing load in |
429 // progress doesn't cause clients and observers to be notified of completion | 486 // progress doesn't cause clients and observers to be notified of completion |
430 // prematurely. | 487 // prematurely. |
431 DCHECK(!m_isSchedulingReload); | 488 DCHECK(!m_isSchedulingReload); |
432 m_isSchedulingReload = true; | 489 m_isSchedulingReload = true; |
433 | 490 |
434 if (policy != kReloadAlwaysWithExistingCachePolicy) | 491 if (policy != kReloadAlwaysWithExistingCachePolicy) |
435 setCachePolicyBypassingCache(); | 492 setCachePolicyBypassingCache(); |
436 | 493 |
437 setPreviewsStateNoTransform(); | 494 setPreviewsStateNoTransform(); |
438 | 495 |
439 if (m_isPlaceholder) { | 496 if (m_placeholderOption != PlaceholderOption::DoNotReloadPlaceholder) |
440 m_isPlaceholder = false; | |
441 clearRangeRequestHeader(); | 497 clearRangeRequestHeader(); |
442 } | 498 m_placeholderOption = PlaceholderOption::DoNotReloadPlaceholder; |
443 | 499 |
444 if (isLoading()) { | 500 if (isLoading()) { |
445 loader()->cancel(); | 501 loader()->cancel(); |
446 // Canceling the loader causes error() to be called, which in turn calls | 502 // Canceling the loader causes error() to be called, which in turn calls |
447 // clear() and notifyObservers(), so there's no need to call these again | 503 // clear() and notifyObservers(), so there's no need to call these again |
448 // here. | 504 // here. |
449 } else { | 505 } else { |
450 clearData(); | 506 clearData(); |
451 setEncodedSize(0); | 507 setEncodedSize(0); |
452 updateImage(nullptr, ImageResourceContent::ClearImageAndNotifyObservers, | 508 updateImage(nullptr, ImageResourceContent::ClearImageAndNotifyObservers, |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 // reloading in Step 3 due to notifyObservers()'s | 595 // reloading in Step 3 due to notifyObservers()'s |
540 // schedulingReloadOrShouldReloadBrokenPlaceholder() check. | 596 // schedulingReloadOrShouldReloadBrokenPlaceholder() check. |
541 // 3. reloadIfLoFiOrPlaceholderImage() is called via ResourceFetcher | 597 // 3. reloadIfLoFiOrPlaceholderImage() is called via ResourceFetcher |
542 // (a) via didFinishLoading() called in decodeError(), or | 598 // (a) via didFinishLoading() called in decodeError(), or |
543 // (b) after returning ImageResource::updateImage(). | 599 // (b) after returning ImageResource::updateImage(). |
544 decodeError(allDataReceived); | 600 decodeError(allDataReceived); |
545 } | 601 } |
546 } | 602 } |
547 | 603 |
548 } // namespace blink | 604 } // namespace blink |
OLD | NEW |