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

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: Addressed comments and fixed bugs 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;
Nate Chapin 2016/10/18 18:23:17 Instead of stashing the FetchRequest here and addi
sclittle 2016/10/18 23:53:03 I'm hesitant to just detect the range header, sinc
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 (resource &&
91 request.placeholderImageRequestType() != FetchRequest::AllowPlaceholder &&
92 resource->m_isPlaceholder) {
93 // If the image is a placeholder, but this fetch doesn't allow a
94 // placeholder, then load the original image. Note that the cache is not
95 // bypassed here - it should be fine to use a cached copy if possible.
96 resource->reloadIfLoFiOrPlaceholder(fetcher, false);
97 }
98 return resource;
68 } 99 }
69 100
70 ImageResource::ImageResource(const ResourceRequest& resourceRequest, 101 ImageResource::ImageResource(const ResourceRequest& resourceRequest,
71 const ResourceLoaderOptions& options) 102 const ResourceLoaderOptions& options,
103 bool isPlaceholder)
72 : Resource(resourceRequest, Image, options), 104 : Resource(resourceRequest, Image, options),
73 m_devicePixelRatioHeaderValue(1.0), 105 m_devicePixelRatioHeaderValue(1.0),
74 m_image(nullptr), 106 m_image(nullptr),
75 m_hasDevicePixelRatioHeaderValue(false), 107 m_hasDevicePixelRatioHeaderValue(false),
76 m_isSchedulingReload(false) { 108 m_isSchedulingReload(false),
109 m_isPlaceholder(isPlaceholder) {
77 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; 110 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this;
78 } 111 }
79 112
80 ImageResource::ImageResource(blink::Image* image, 113 ImageResource::ImageResource(blink::Image* image,
81 const ResourceLoaderOptions& options) 114 const ResourceLoaderOptions& options)
82 : Resource(ResourceRequest(""), Image, options), 115 : Resource(ResourceRequest(""), Image, options),
83 m_devicePixelRatioHeaderValue(1.0), 116 m_devicePixelRatioHeaderValue(1.0),
84 m_image(image), 117 m_image(image),
85 m_hasDevicePixelRatioHeaderValue(false), 118 m_hasDevicePixelRatioHeaderValue(false),
86 m_isSchedulingReload(false) { 119 m_isSchedulingReload(false),
120 m_isPlaceholder(false) {
87 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this; 121 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this;
88 setStatus(Cached); 122 setStatus(Cached);
89 } 123 }
90 124
91 ImageResource::~ImageResource() { 125 ImageResource::~ImageResource() {
92 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; 126 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this;
93 clearImage(); 127 clearImage();
94 } 128 }
95 129
96 DEFINE_TRACE(ImageResource) { 130 DEFINE_TRACE(ImageResource) {
97 visitor->trace(m_multipartParser); 131 visitor->trace(m_multipartParser);
98 Resource::trace(visitor); 132 Resource::trace(visitor);
99 ImageObserver::trace(visitor); 133 ImageObserver::trace(visitor);
100 MultipartImageResourceParser::Client::trace(visitor); 134 MultipartImageResourceParser::Client::trace(visitor);
101 } 135 }
102 136
103 void ImageResource::checkNotify() { 137 void ImageResource::checkNotify() {
104 // Don't notify observers and clients of completion if this ImageResource is 138 // Don't notify observers and clients of completion if this ImageResource is
105 // about to be reloaded. 139 // about to be reloaded.
106 if (m_isSchedulingReload) 140 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder())
107 return; 141 return;
108 142
109 notifyObserversInternal(MarkFinishedOption::ShouldMarkFinished); 143 notifyObserversInternal(MarkFinishedOption::ShouldMarkFinished);
110 Resource::checkNotify(); 144 Resource::checkNotify();
111 } 145 }
112 146
113 void ImageResource::notifyObserversInternal( 147 void ImageResource::notifyObserversInternal(
114 MarkFinishedOption markFinishedOption) { 148 MarkFinishedOption markFinishedOption) {
115 if (isLoading()) 149 if (isLoading())
116 return; 150 return;
(...skipping 12 matching lines...) Expand all
129 m_finishedObservers.add(observer); 163 m_finishedObservers.add(observer);
130 m_observers.remove(observer); 164 m_observers.remove(observer);
131 } 165 }
132 } 166 }
133 167
134 void ImageResource::didAddClient(ResourceClient* client) { 168 void ImageResource::didAddClient(ResourceClient* client) {
135 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); 169 DCHECK((m_multipartParser && isLoading()) || !data() || m_image);
136 170
137 // Don't notify observers and clients of completion if this ImageResource is 171 // Don't notify observers and clients of completion if this ImageResource is
138 // about to be reloaded. 172 // about to be reloaded.
139 if (m_isSchedulingReload) 173 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder())
140 return; 174 return;
141 175
142 Resource::didAddClient(client); 176 Resource::didAddClient(client);
143 } 177 }
144 178
145 void ImageResource::addObserver(ImageResourceObserver* observer) { 179 void ImageResource::addObserver(ImageResourceObserver* observer) {
146 willAddClientOrObserver(MarkAsReferenced); 180 willAddClientOrObserver(MarkAsReferenced);
147 181
148 m_observers.add(observer); 182 m_observers.add(observer);
149 183
150 if (isCacheValidator()) 184 if (isCacheValidator())
151 return; 185 return;
152 186
153 // When the response is not multipart, if |data()| exists, |m_image| must be 187 // When the response is not multipart, if |data()| exists, |m_image| must be
154 // created. This is assured that |updateImage()| is called when |appendData()| 188 // created. This is assured that |updateImage()| is called when |appendData()|
155 // is called. 189 // is called.
156 // 190 //
157 // On the other hand, when the response is multipart, |updateImage()| is not 191 // 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 192 // 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 193 // when |data()| exists. This is intentional since creating a |m_image| on
160 // receiving data might destroy an existing image in a previous part. 194 // receiving data might destroy an existing image in a previous part.
161 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); 195 DCHECK((m_multipartParser && isLoading()) || !data() || m_image);
162 196
163 if (m_image && !m_image->isNull()) { 197 if (m_image && !m_image->isNull()) {
164 observer->imageChanged(this); 198 observer->imageChanged(this);
165 } 199 }
166 200
167 if (isLoaded() && !m_isSchedulingReload) { 201 if (isLoaded() && !m_isSchedulingReload && !shouldReloadBrokenPlaceholder()) {
168 markObserverFinished(observer); 202 markObserverFinished(observer);
169 observer->imageNotifyFinished(this); 203 observer->imageNotifyFinished(this);
170 } 204 }
171 } 205 }
172 206
173 void ImageResource::removeObserver(ImageResourceObserver* observer) { 207 void ImageResource::removeObserver(ImageResourceObserver* observer) {
174 DCHECK(observer); 208 DCHECK(observer);
175 209
176 if (m_observers.contains(observer)) 210 if (m_observers.contains(observer))
177 m_observers.remove(observer); 211 m_observers.remove(observer);
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 if (data()) { 429 if (data()) {
396 DCHECK(m_image); 430 DCHECK(m_image);
397 sizeAvailable = m_image->setData(data(), allDataReceived); 431 sizeAvailable = m_image->setData(data(), allDataReceived);
398 } 432 }
399 433
400 // Go ahead and tell our observers to try to draw if we have either received 434 // 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 435 // 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. 436 // observers to repaint, which will force that chunk to decode.
403 if (sizeAvailable == Image::SizeUnavailable && !allDataReceived) 437 if (sizeAvailable == Image::SizeUnavailable && !allDataReceived)
404 return; 438 return;
439
440 if (m_isPlaceholder && allDataReceived && m_image && !m_image->isNull()) {
441 if (sizeAvailable == Image::SizeAvailable) {
442 // TODO(sclittle): Show the original image if the response consists of the
443 // entire image, such as if the entire image response body is smaller than
444 // the requested range.
445 IntSize dimensions = m_image->size();
446 clearImage();
447 m_image = PlaceholderImage::create(this, dimensions);
448 } else {
449 // Clear the image so that it gets treated like a decoding error, since
450 // the attempt to build a placeholder image failed.
451 clearImage();
452 }
453 }
454
405 if (!m_image || m_image->isNull()) { 455 if (!m_image || m_image->isNull()) {
406 size_t size = encodedSize(); 456 size_t size = encodedSize();
407 clear(); 457 clear();
408 if (!errorOccurred()) 458 if (!errorOccurred())
409 setStatus(DecodeError); 459 setStatus(DecodeError);
410 if (!allDataReceived && loader()) 460 if (!allDataReceived && loader())
411 loader()->didFinishLoading(nullptr, monotonicallyIncreasingTime(), size); 461 loader()->didFinishLoading(nullptr, monotonicallyIncreasingTime(), size);
412 memoryCache()->remove(this); 462 memoryCache()->remove(this);
413 } 463 }
414 464
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 observer->getImageAnimationPolicy(newPolicy)) 578 observer->getImageAnimationPolicy(newPolicy))
529 break; 579 break;
530 } 580 }
531 581
532 if (m_image->animationPolicy() != newPolicy) { 582 if (m_image->animationPolicy() != newPolicy) {
533 m_image->resetAnimation(); 583 m_image->resetAnimation();
534 m_image->setAnimationPolicy(newPolicy); 584 m_image->setAnimationPolicy(newPolicy);
535 } 585 }
536 } 586 }
537 587
538 void ImageResource::reloadIfLoFi(ResourceFetcher* fetcher) { 588 void ImageResource::reloadIfLoFiOrPlaceholder(ResourceFetcher* fetcher,
539 if (resourceRequest().loFiState() != WebURLRequest::LoFiOn) 589 bool bypassCache) {
Nate Chapin 2016/10/18 18:23:17 bypassCache should probably be an enum.
sclittle 2016/10/18 23:53:03 Done.
590 if (!m_isPlaceholder &&
Nate Chapin 2016/10/18 18:23:17 This if-statement is sufficiently complex that it
sclittle 2016/10/18 23:53:03 Done.
591 (resourceRequest().loFiState() != WebURLRequest::LoFiOn ||
592 (isLoaded() &&
593 !response().httpHeaderField("chrome-proxy").contains("q=low")))) {
540 return; 594 return;
541 if (isLoaded() && 595 }
542 !response().httpHeaderField("chrome-proxy").contains("q=low"))
543 return;
544 596
545 // Prevent clients and observers from being notified of completion while the 597 // 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 598 // 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 599 // progress doesn't cause clients and observers to be notified of completion
548 // prematurely. 600 // prematurely.
549 DCHECK(!m_isSchedulingReload); 601 DCHECK(!m_isSchedulingReload);
550 m_isSchedulingReload = true; 602 m_isSchedulingReload = true;
551 603
552 setCachePolicyBypassingCache(); 604 if (bypassCache)
605 setCachePolicyBypassingCache();
553 setLoFiStateOff(); 606 setLoFiStateOff();
607
608 if (m_isPlaceholder) {
609 m_isPlaceholder = false;
610 clearRangeRequestHeader();
611 }
612
554 if (isLoading()) { 613 if (isLoading()) {
555 loader()->cancel(); 614 loader()->cancel();
556 // Canceling the loader causes error() to be called, which in turn calls 615 // 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 616 // clear() and notifyObservers(), so there's no need to call these again
558 // here. 617 // here.
559 } else { 618 } else {
560 clear(); 619 clear();
561 notifyObservers(); 620 notifyObservers();
562 } 621 }
563 622
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 WebServiceWorkerResponseTypeOpaque; 672 WebServiceWorkerResponseTypeOpaque;
614 } 673 }
615 if (!getImage()->currentFrameHasSingleSecurityOrigin()) 674 if (!getImage()->currentFrameHasSingleSecurityOrigin())
616 return false; 675 return false;
617 if (passesAccessControlCheck(securityOrigin)) 676 if (passesAccessControlCheck(securityOrigin))
618 return true; 677 return true;
619 return !securityOrigin->taintsCanvas(response().url()); 678 return !securityOrigin->taintsCanvas(response().url());
620 } 679 }
621 680
622 } // namespace blink 681 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698