Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: third_party/WebKit/Source/core/fetch/ImageResource.cpp

Issue 2423683002: Add Blink support for showing image placeholders using range requests. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
(...skipping 14 matching lines...) Expand all
25 25
26 #include "core/fetch/ImageResourceObserver.h" 26 #include "core/fetch/ImageResourceObserver.h"
27 #include "core/fetch/MemoryCache.h" 27 #include "core/fetch/MemoryCache.h"
28 #include "core/fetch/ResourceClient.h" 28 #include "core/fetch/ResourceClient.h"
29 #include "core/fetch/ResourceFetcher.h" 29 #include "core/fetch/ResourceFetcher.h"
30 #include "core/fetch/ResourceLoader.h" 30 #include "core/fetch/ResourceLoader.h"
31 #include "core/fetch/ResourceLoadingLog.h" 31 #include "core/fetch/ResourceLoadingLog.h"
32 #include "core/svg/graphics/SVGImage.h" 32 #include "core/svg/graphics/SVGImage.h"
33 #include "platform/RuntimeEnabledFeatures.h" 33 #include "platform/RuntimeEnabledFeatures.h"
34 #include "platform/SharedBuffer.h" 34 #include "platform/SharedBuffer.h"
35 #include "platform/geometry/IntSize.h"
35 #include "platform/graphics/BitmapImage.h" 36 #include "platform/graphics/BitmapImage.h"
37 #include "platform/graphics/PlaceholderImage.h"
36 #include "platform/tracing/TraceEvent.h" 38 #include "platform/tracing/TraceEvent.h"
37 #include "public/platform/Platform.h" 39 #include "public/platform/Platform.h"
38 #include "public/platform/WebCachePolicy.h" 40 #include "public/platform/WebCachePolicy.h"
39 #include "wtf/CurrentTime.h" 41 #include "wtf/CurrentTime.h"
40 #include "wtf/HashCountedSet.h" 42 #include "wtf/HashCountedSet.h"
41 #include "wtf/StdLibExtras.h" 43 #include "wtf/StdLibExtras.h"
42 #include "wtf/Vector.h" 44 #include "wtf/Vector.h"
43 #include <memory> 45 #include <memory>
44 #include <v8.h> 46 #include <v8.h>
45 47
46 namespace blink { 48 namespace blink {
47 49
50 class ImageResource::ImageResourceFactory : public ResourceFactory {
51 STACK_ALLOCATED();
52
53 public:
54 ImageResourceFactory(const FetchRequest& fetchRequest)
55 : ResourceFactory(Resource::Image), m_fetchRequest(&fetchRequest) {}
56
57 Resource* create(const ResourceRequest& request,
58 const ResourceLoaderOptions& options,
59 const String&) const override {
60 return new ImageResource(request, options,
61 m_fetchRequest->placeholderImageRequestType() ==
62 FetchRequest::AllowPlaceholder);
63 }
64
65 private:
66 // Weak, unowned pointer. Must outlive |this|.
67 const FetchRequest* m_fetchRequest;
68 };
69
48 ImageResource* ImageResource::fetch(FetchRequest& request, 70 ImageResource* ImageResource::fetch(FetchRequest& request,
49 ResourceFetcher* fetcher) { 71 ResourceFetcher* fetcher) {
50 if (request.resourceRequest().requestContext() == 72 if (request.resourceRequest().requestContext() ==
51 WebURLRequest::RequestContextUnspecified) { 73 WebURLRequest::RequestContextUnspecified) {
52 request.mutableResourceRequest().setRequestContext( 74 request.mutableResourceRequest().setRequestContext(
53 WebURLRequest::RequestContextImage); 75 WebURLRequest::RequestContextImage);
54 } 76 }
55 if (fetcher->context().pageDismissalEventBeingDispatched()) { 77 if (fetcher->context().pageDismissalEventBeingDispatched()) {
56 KURL requestURL = request.resourceRequest().url(); 78 KURL requestURL = request.resourceRequest().url();
57 if (requestURL.isValid() && 79 if (requestURL.isValid() &&
58 fetcher->context().canRequest(Resource::Image, 80 fetcher->context().canRequest(Resource::Image,
59 request.resourceRequest(), requestURL, 81 request.resourceRequest(), requestURL,
60 request.options(), request.forPreload(), 82 request.options(), request.forPreload(),
61 request.getOriginRestriction())) 83 request.getOriginRestriction()))
62 fetcher->context().sendImagePing(requestURL); 84 fetcher->context().sendImagePing(requestURL);
63 return nullptr; 85 return nullptr;
64 } 86 }
65 87
66 return toImageResource( 88 ImageResource* resource = toImageResource(
67 fetcher->requestResource(request, ImageResourceFactory())); 89 fetcher->requestResource(request, ImageResourceFactory(request)));
90 if (request.placeholderImageRequestType() != FetchRequest::AllowPlaceholder &&
91 resource->m_isPlaceholder) {
92 // If the image is a placeholder, but this fetch doesn't allow a
93 // placeholder, then load the original image. Note that the cache is not
94 // bypassed here - it should be fine to use a cached copy if possible.
95 resource->reloadIfLoFi(fetcher, false);
96 }
97 return resource;
68 } 98 }
69 99
70 ImageResource::ImageResource(const ResourceRequest& resourceRequest, 100 ImageResource::ImageResource(const ResourceRequest& resourceRequest,
71 const ResourceLoaderOptions& options) 101 const ResourceLoaderOptions& options,
102 bool isPlaceholder)
72 : Resource(resourceRequest, Image, options), 103 : Resource(resourceRequest, Image, options),
73 m_devicePixelRatioHeaderValue(1.0), 104 m_devicePixelRatioHeaderValue(1.0),
74 m_image(nullptr), 105 m_image(nullptr),
75 m_hasDevicePixelRatioHeaderValue(false), 106 m_hasDevicePixelRatioHeaderValue(false),
76 m_isSchedulingReload(false) { 107 m_isSchedulingReload(false),
108 m_isPlaceholder(isPlaceholder) {
77 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; 109 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this;
78 } 110 }
79 111
80 ImageResource::ImageResource(blink::Image* image, 112 ImageResource::ImageResource(blink::Image* image,
81 const ResourceLoaderOptions& options) 113 const ResourceLoaderOptions& options)
82 : Resource(ResourceRequest(""), Image, options), 114 : Resource(ResourceRequest(""), Image, options),
83 m_devicePixelRatioHeaderValue(1.0), 115 m_devicePixelRatioHeaderValue(1.0),
84 m_image(image), 116 m_image(image),
85 m_hasDevicePixelRatioHeaderValue(false), 117 m_hasDevicePixelRatioHeaderValue(false),
86 m_isSchedulingReload(false) { 118 m_isSchedulingReload(false),
119 m_isPlaceholder(false) {
87 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this; 120 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this;
88 setStatus(Cached); 121 setStatus(Cached);
89 } 122 }
90 123
91 ImageResource::~ImageResource() { 124 ImageResource::~ImageResource() {
92 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; 125 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this;
93 clearImage(); 126 clearImage();
94 } 127 }
95 128
96 DEFINE_TRACE(ImageResource) { 129 DEFINE_TRACE(ImageResource) {
97 visitor->trace(m_multipartParser); 130 visitor->trace(m_multipartParser);
98 Resource::trace(visitor); 131 Resource::trace(visitor);
99 ImageObserver::trace(visitor); 132 ImageObserver::trace(visitor);
100 MultipartImageResourceParser::Client::trace(visitor); 133 MultipartImageResourceParser::Client::trace(visitor);
101 } 134 }
102 135
103 void ImageResource::checkNotify() { 136 void ImageResource::checkNotify() {
104 // Don't notify observers and clients of completion if this ImageResource is 137 // Don't notify observers and clients of completion if this ImageResource is
105 // about to be reloaded. 138 // about to be reloaded.
106 if (m_isSchedulingReload) 139 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder())
107 return; 140 return;
108 141
109 notifyObserversInternal(MarkFinishedOption::ShouldMarkFinished); 142 notifyObserversInternal(MarkFinishedOption::ShouldMarkFinished);
110 Resource::checkNotify(); 143 Resource::checkNotify();
111 } 144 }
112 145
113 void ImageResource::notifyObserversInternal( 146 void ImageResource::notifyObserversInternal(
114 MarkFinishedOption markFinishedOption) { 147 MarkFinishedOption markFinishedOption) {
115 if (isLoading()) 148 if (isLoading())
116 return; 149 return;
(...skipping 12 matching lines...) Expand all
129 m_finishedObservers.add(observer); 162 m_finishedObservers.add(observer);
130 m_observers.remove(observer); 163 m_observers.remove(observer);
131 } 164 }
132 } 165 }
133 166
134 void ImageResource::didAddClient(ResourceClient* client) { 167 void ImageResource::didAddClient(ResourceClient* client) {
135 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); 168 DCHECK((m_multipartParser && isLoading()) || !data() || m_image);
136 169
137 // Don't notify observers and clients of completion if this ImageResource is 170 // Don't notify observers and clients of completion if this ImageResource is
138 // about to be reloaded. 171 // about to be reloaded.
139 if (m_isSchedulingReload) 172 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder())
140 return; 173 return;
141 174
142 Resource::didAddClient(client); 175 Resource::didAddClient(client);
143 } 176 }
144 177
145 void ImageResource::addObserver(ImageResourceObserver* observer) { 178 void ImageResource::addObserver(ImageResourceObserver* observer) {
146 willAddClientOrObserver(MarkAsReferenced); 179 willAddClientOrObserver(MarkAsReferenced);
147 180
148 m_observers.add(observer); 181 m_observers.add(observer);
149 182
150 if (isCacheValidator()) 183 if (isCacheValidator())
151 return; 184 return;
152 185
153 // When the response is not multipart, if |data()| exists, |m_image| must be 186 // When the response is not multipart, if |data()| exists, |m_image| must be
154 // created. This is assured that |updateImage()| is called when |appendData()| 187 // created. This is assured that |updateImage()| is called when |appendData()|
155 // is called. 188 // is called.
156 // 189 //
157 // On the other hand, when the response is multipart, |updateImage()| is not 190 // On the other hand, when the response is multipart, |updateImage()| is not
158 // called in |appendData()|, which means |m_image| might not be created even 191 // called in |appendData()|, which means |m_image| might not be created even
159 // when |data()| exists. This is intentional since creating a |m_image| on 192 // when |data()| exists. This is intentional since creating a |m_image| on
160 // receiving data might destroy an existing image in a previous part. 193 // receiving data might destroy an existing image in a previous part.
161 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); 194 DCHECK((m_multipartParser && isLoading()) || !data() || m_image);
162 195
163 if (m_image && !m_image->isNull()) { 196 if (m_image && !m_image->isNull()) {
164 observer->imageChanged(this); 197 observer->imageChanged(this);
165 } 198 }
166 199
167 if (isLoaded() && !m_isSchedulingReload) { 200 if (isLoaded() && !m_isSchedulingReload && !shouldReloadBrokenPlaceholder()) {
168 markObserverFinished(observer); 201 markObserverFinished(observer);
169 observer->imageNotifyFinished(this); 202 observer->imageNotifyFinished(this);
170 } 203 }
171 } 204 }
172 205
173 void ImageResource::removeObserver(ImageResourceObserver* observer) { 206 void ImageResource::removeObserver(ImageResourceObserver* observer) {
174 DCHECK(observer); 207 DCHECK(observer);
175 208
176 if (m_observers.contains(observer)) 209 if (m_observers.contains(observer))
177 m_observers.remove(observer); 210 m_observers.remove(observer);
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 if (data()) { 428 if (data()) {
396 DCHECK(m_image); 429 DCHECK(m_image);
397 sizeAvailable = m_image->setData(data(), allDataReceived); 430 sizeAvailable = m_image->setData(data(), allDataReceived);
398 } 431 }
399 432
400 // Go ahead and tell our observers to try to draw if we have either received 433 // Go ahead and tell our observers to try to draw if we have either received
401 // all the data or the size is known. Each chunk from the network causes 434 // all the data or the size is known. Each chunk from the network causes
402 // observers to repaint, which will force that chunk to decode. 435 // observers to repaint, which will force that chunk to decode.
403 if (sizeAvailable == Image::SizeUnavailable && !allDataReceived) 436 if (sizeAvailable == Image::SizeUnavailable && !allDataReceived)
404 return; 437 return;
438
439 if (m_isPlaceholder && allDataReceived && m_image && !m_image->isNull()) {
440 if (sizeAvailable == Image::SizeAvailable) {
441 // TODO(sclittle): Show the original image if the response consists of the
442 // entire image, such as if the entire image response body is smaller than
443 // the requested range.
444 IntSize dimensions = m_image->size();
445 clearImage();
446 m_image = PlaceholderImage::create(this, dimensions);
447 } else {
448 // Clear the image so that it gets treated like a decoding error, since
449 // the attempt to build a placeholder image failed.
450 clearImage();
451 }
452 }
453
405 if (!m_image || m_image->isNull()) { 454 if (!m_image || m_image->isNull()) {
406 size_t size = encodedSize(); 455 size_t size = encodedSize();
407 clear(); 456 clear();
408 if (!errorOccurred()) 457 if (!errorOccurred())
409 setStatus(DecodeError); 458 setStatus(DecodeError);
410 if (!allDataReceived && loader()) 459 if (!allDataReceived && loader())
411 loader()->didFinishLoading(nullptr, monotonicallyIncreasingTime(), size); 460 loader()->didFinishLoading(nullptr, monotonicallyIncreasingTime(), size);
412 memoryCache()->remove(this); 461 memoryCache()->remove(this);
413 } 462 }
414 463
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 observer->getImageAnimationPolicy(newPolicy)) 577 observer->getImageAnimationPolicy(newPolicy))
529 break; 578 break;
530 } 579 }
531 580
532 if (m_image->animationPolicy() != newPolicy) { 581 if (m_image->animationPolicy() != newPolicy) {
533 m_image->resetAnimation(); 582 m_image->resetAnimation();
534 m_image->setAnimationPolicy(newPolicy); 583 m_image->setAnimationPolicy(newPolicy);
535 } 584 }
536 } 585 }
537 586
538 void ImageResource::reloadIfLoFi(ResourceFetcher* fetcher) { 587 void ImageResource::reloadIfLoFi(ResourceFetcher* fetcher, bool bypassCache) {
539 if (resourceRequest().loFiState() != WebURLRequest::LoFiOn) 588 if (!m_isPlaceholder &&
589 (resourceRequest().loFiState() != WebURLRequest::LoFiOn ||
590 (isLoaded() &&
591 !response().httpHeaderField("chrome-proxy").contains("q=low")))) {
540 return; 592 return;
541 if (isLoaded() && 593 }
542 !response().httpHeaderField("chrome-proxy").contains("q=low"))
543 return;
544 594
545 // Prevent clients and observers from being notified of completion while the 595 // Prevent clients and observers from being notified of completion while the
546 // reload is being scheduled, so that e.g. canceling an existing load in 596 // reload is being scheduled, so that e.g. canceling an existing load in
547 // progress doesn't cause clients and observers to be notified of completion 597 // progress doesn't cause clients and observers to be notified of completion
548 // prematurely. 598 // prematurely.
549 DCHECK(!m_isSchedulingReload); 599 DCHECK(!m_isSchedulingReload);
550 m_isSchedulingReload = true; 600 m_isSchedulingReload = true;
551 601
552 setCachePolicyBypassingCache(); 602 if (bypassCache)
603 setCachePolicyBypassingCache();
553 setLoFiStateOff(); 604 setLoFiStateOff();
Stephen Chennney 2016/10/17 18:09:17 It concerns me a little that if you reload the pla
sclittle 2016/10/18 00:54:11 We don't want to load a Lo-Fi image if a placehold
605
606 if (m_isPlaceholder) {
607 m_isPlaceholder = false;
608 clearRangeRequestHeader();
609 }
610
554 if (isLoading()) { 611 if (isLoading()) {
555 loader()->cancel(); 612 loader()->cancel();
556 // Canceling the loader causes error() to be called, which in turn calls 613 // Canceling the loader causes error() to be called, which in turn calls
557 // clear() and notifyObservers(), so there's no need to call these again 614 // clear() and notifyObservers(), so there's no need to call these again
558 // here. 615 // here.
559 } else { 616 } else {
560 clear(); 617 clear();
561 notifyObservers(); 618 notifyObservers();
562 } 619 }
563 620
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 WebServiceWorkerResponseTypeOpaque; 670 WebServiceWorkerResponseTypeOpaque;
614 } 671 }
615 if (!getImage()->currentFrameHasSingleSecurityOrigin()) 672 if (!getImage()->currentFrameHasSingleSecurityOrigin())
616 return false; 673 return false;
617 if (passesAccessControlCheck(securityOrigin)) 674 if (passesAccessControlCheck(securityOrigin))
618 return true; 675 return true;
619 return !securityOrigin->taintsCanvas(response().url()); 676 return !securityOrigin->taintsCanvas(response().url());
620 } 677 }
621 678
622 } // namespace blink 679 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698