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/fetch/ImageResource.h" | 24 #include "core/fetch/ImageResource.h" |
25 | 25 |
26 #include "core/fetch/ImageResourceContent.h" | |
27 #include "core/fetch/ImageResourceInfo.h" | |
26 #include "core/fetch/ImageResourceObserver.h" | 28 #include "core/fetch/ImageResourceObserver.h" |
yhirano
2016/12/05 09:02:03
Is this needed?
hiroshige
2016/12/06 09:32:50
Done.
| |
27 #include "core/fetch/MemoryCache.h" | 29 #include "core/fetch/MemoryCache.h" |
28 #include "core/fetch/ResourceClient.h" | 30 #include "core/fetch/ResourceClient.h" |
29 #include "core/fetch/ResourceFetcher.h" | 31 #include "core/fetch/ResourceFetcher.h" |
30 #include "core/fetch/ResourceLoader.h" | 32 #include "core/fetch/ResourceLoader.h" |
31 #include "core/fetch/ResourceLoadingLog.h" | 33 #include "core/fetch/ResourceLoadingLog.h" |
32 #include "core/svg/graphics/SVGImage.h" | |
33 #include "platform/Histogram.h" | 34 #include "platform/Histogram.h" |
34 #include "platform/RuntimeEnabledFeatures.h" | 35 #include "platform/RuntimeEnabledFeatures.h" |
35 #include "platform/SharedBuffer.h" | 36 #include "platform/SharedBuffer.h" |
36 #include "platform/geometry/IntSize.h" | |
37 #include "platform/graphics/BitmapImage.h" | |
38 #include "platform/graphics/PlaceholderImage.h" | |
39 #include "platform/tracing/TraceEvent.h" | 37 #include "platform/tracing/TraceEvent.h" |
40 #include "public/platform/Platform.h" | 38 #include "public/platform/Platform.h" |
41 #include "public/platform/WebCachePolicy.h" | |
42 #include "wtf/CurrentTime.h" | 39 #include "wtf/CurrentTime.h" |
43 #include "wtf/HashCountedSet.h" | 40 #include "wtf/HashCountedSet.h" |
yhirano
2016/12/05 09:02:03
Not needed?
hiroshige
2016/12/06 09:32:50
Done.
| |
44 #include "wtf/StdLibExtras.h" | 41 #include "wtf/StdLibExtras.h" |
45 #include "wtf/Vector.h" | 42 #include "wtf/Vector.h" |
yhirano
2016/12/05 09:02:03
Not needed?
hiroshige
2016/12/06 09:32:50
Done.
| |
46 #include <memory> | 43 #include <memory> |
47 #include <v8.h> | 44 #include <v8.h> |
48 | 45 |
49 namespace blink { | 46 namespace blink { |
50 namespace { | 47 namespace { |
51 // The amount of time to wait before informing the clients that the image has | 48 // The amount of time to wait before informing the clients that the image has |
52 // been updated (in seconds). This effectively throttles invalidations that | 49 // been updated (in seconds). This effectively throttles invalidations that |
53 // result from new data arriving for this image. | 50 // result from new data arriving for this image. |
54 constexpr double kFlushDelaySeconds = 1.; | 51 constexpr double kFlushDelaySeconds = 1.; |
55 } // namespace | 52 } // namespace |
56 | 53 |
54 class ImageResource::ImageResourceInfoImpl final | |
55 : public GarbageCollectedFinalized<ImageResourceInfoImpl>, | |
56 public ImageResourceInfo { | |
57 USING_GARBAGE_COLLECTED_MIXIN(ImageResourceInfoImpl); | |
58 | |
59 public: | |
60 ImageResourceInfoImpl(ImageResource* resource) : m_resource(resource) { | |
61 DCHECK(m_resource); | |
62 } | |
63 DEFINE_INLINE_VIRTUAL_TRACE() { | |
64 visitor->trace(m_resource); | |
65 ImageResourceInfo::trace(visitor); | |
66 } | |
67 | |
68 private: | |
69 const KURL& url() const override { return m_resource->url(); } | |
70 bool isSchedulingReload() const override { | |
71 return m_resource->m_isSchedulingReload; | |
72 } | |
73 bool hasDevicePixelRatioHeaderValue() const override { | |
74 return m_resource->m_hasDevicePixelRatioHeaderValue; | |
75 } | |
76 float devicePixelRatioHeaderValue() const override { | |
77 return m_resource->m_devicePixelRatioHeaderValue; | |
78 } | |
79 const ResourceResponse& response() const override { | |
80 return m_resource->response(); | |
81 } | |
82 Resource::Status getStatus() const override { | |
83 return m_resource->getStatus(); | |
84 } | |
85 bool isPlaceholder() const override { return m_resource->isPlaceholder(); } | |
86 bool isCacheValidator() const override { | |
87 return m_resource->isCacheValidator(); | |
88 } | |
89 bool schedulingReloadOrShouldReloadBrokenPlaceholder() const override { | |
90 return m_resource->m_isSchedulingReload || | |
91 m_resource->shouldReloadBrokenPlaceholder(); | |
92 } | |
93 bool isAccessAllowed( | |
94 SecurityOrigin* securityOrigin, | |
95 bool doesCurrentFrameHasSingleSecurityOrigin) const override { | |
96 return m_resource->isAccessAllowed(securityOrigin, | |
97 doesCurrentFrameHasSingleSecurityOrigin); | |
98 } | |
99 bool hasCacheControlNoStoreHeader() const override { | |
100 return m_resource->hasCacheControlNoStoreHeader(); | |
101 } | |
102 const ResourceError& resourceError() const override { | |
103 return m_resource->resourceError(); | |
104 } | |
105 | |
106 void decodeError(bool allDataReceived) override { | |
107 m_resource->decodeError(allDataReceived); | |
108 } | |
109 void setDecodedSize(size_t size) override { | |
110 m_resource->setDecodedSize(size); | |
111 } | |
112 void willAddClientOrObserver() override { | |
113 m_resource->willAddClientOrObserver(Resource::MarkAsReferenced); | |
114 } | |
115 void didRemoveClientOrObserver() override { | |
116 m_resource->didRemoveClientOrObserver(); | |
117 } | |
118 void emulateLoadStartedForInspector( | |
119 ResourceFetcher* fetcher, | |
120 const KURL& url, | |
121 const AtomicString& initiatorName) override { | |
122 fetcher->emulateLoadStartedForInspector(m_resource.get(), url, | |
123 WebURLRequest::RequestContextImage, | |
124 initiatorName); | |
125 } | |
126 | |
127 Member<ImageResource> m_resource; | |
yhirano
2016/12/05 09:02:03
[optional] const Member<ImageResource> m_resource;
hiroshige
2016/12/06 09:32:50
Done.
| |
128 }; | |
129 | |
57 class ImageResource::ImageResourceFactory : public ResourceFactory { | 130 class ImageResource::ImageResourceFactory : public ResourceFactory { |
58 STACK_ALLOCATED(); | 131 STACK_ALLOCATED(); |
59 | 132 |
60 public: | 133 public: |
61 ImageResourceFactory(const FetchRequest& fetchRequest) | 134 ImageResourceFactory(const FetchRequest& fetchRequest) |
62 : ResourceFactory(Resource::Image), m_fetchRequest(&fetchRequest) {} | 135 : ResourceFactory(Resource::Image), m_fetchRequest(&fetchRequest) {} |
63 | 136 |
64 Resource* create(const ResourceRequest& request, | 137 Resource* create(const ResourceRequest& request, |
65 const ResourceLoaderOptions& options, | 138 const ResourceLoaderOptions& options, |
66 const String&) const override { | 139 const String&) const override { |
67 return new ImageResource(request, options, | 140 return new ImageResource(request, options, ImageResourceContent::create(), |
68 m_fetchRequest->placeholderImageRequestType() == | 141 m_fetchRequest->placeholderImageRequestType() == |
69 FetchRequest::AllowPlaceholder); | 142 FetchRequest::AllowPlaceholder); |
70 } | 143 } |
71 | 144 |
72 private: | 145 private: |
73 // Weak, unowned pointer. Must outlive |this|. | 146 // Weak, unowned pointer. Must outlive |this|. |
74 const FetchRequest* m_fetchRequest; | 147 const FetchRequest* m_fetchRequest; |
75 }; | 148 }; |
76 | 149 |
77 ImageResource* ImageResource::fetch(FetchRequest& request, | 150 ImageResource* ImageResource::fetch(FetchRequest& request, |
(...skipping 21 matching lines...) Expand all Loading... | |
99 resource->m_isPlaceholder) { | 172 resource->m_isPlaceholder) { |
100 // If the image is a placeholder, but this fetch doesn't allow a | 173 // If the image is a placeholder, but this fetch doesn't allow a |
101 // placeholder, then load the original image. Note that the cache is not | 174 // placeholder, then load the original image. Note that the cache is not |
102 // bypassed here - it should be fine to use a cached copy if possible. | 175 // bypassed here - it should be fine to use a cached copy if possible. |
103 resource->reloadIfLoFiOrPlaceholder(fetcher, | 176 resource->reloadIfLoFiOrPlaceholder(fetcher, |
104 ReloadCachePolicy::UseExistingPolicy); | 177 ReloadCachePolicy::UseExistingPolicy); |
105 } | 178 } |
106 return resource; | 179 return resource; |
107 } | 180 } |
108 | 181 |
182 ImageResource* ImageResource::create(const ResourceRequest& request) { | |
183 return new ImageResource(request, ResourceLoaderOptions(), | |
184 ImageResourceContent::create(), false); | |
185 } | |
186 | |
109 ImageResource::ImageResource(const ResourceRequest& resourceRequest, | 187 ImageResource::ImageResource(const ResourceRequest& resourceRequest, |
110 const ResourceLoaderOptions& options, | 188 const ResourceLoaderOptions& options, |
189 ImageResourceContent* content, | |
111 bool isPlaceholder) | 190 bool isPlaceholder) |
112 : Resource(resourceRequest, Image, options), | 191 : Resource(resourceRequest, Image, options), |
192 m_content(content), | |
113 m_devicePixelRatioHeaderValue(1.0), | 193 m_devicePixelRatioHeaderValue(1.0), |
114 m_image(nullptr), | |
115 m_hasDevicePixelRatioHeaderValue(false), | 194 m_hasDevicePixelRatioHeaderValue(false), |
116 m_isSchedulingReload(false), | 195 m_isSchedulingReload(false), |
117 m_isPlaceholder(isPlaceholder), | 196 m_isPlaceholder(isPlaceholder), |
118 m_flushTimer(this, &ImageResource::flushImageIfNeeded), | 197 m_flushTimer(this, &ImageResource::flushImageIfNeeded) { |
119 m_isRefetchableDataFromDiskCache(true) { | 198 DCHECK(getContent()); |
120 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; | 199 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; |
121 } | 200 getContent()->setImageResourceInfo(new ImageResourceInfoImpl(this)); |
122 | |
123 ImageResource::ImageResource(blink::Image* image, | |
124 const ResourceLoaderOptions& options) | |
125 : Resource(ResourceRequest(""), Image, options), | |
126 m_devicePixelRatioHeaderValue(1.0), | |
127 m_image(image), | |
128 m_hasDevicePixelRatioHeaderValue(false), | |
129 m_isSchedulingReload(false), | |
130 m_isPlaceholder(false), | |
131 m_flushTimer(this, &ImageResource::flushImageIfNeeded), | |
132 m_isRefetchableDataFromDiskCache(true) { | |
133 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this; | |
134 setStatus(Cached); | |
135 } | 201 } |
136 | 202 |
137 ImageResource::~ImageResource() { | 203 ImageResource::~ImageResource() { |
138 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; | 204 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; |
139 clearImage(); | |
140 } | 205 } |
141 | 206 |
142 DEFINE_TRACE(ImageResource) { | 207 DEFINE_TRACE(ImageResource) { |
143 visitor->trace(m_multipartParser); | 208 visitor->trace(m_multipartParser); |
209 visitor->trace(m_content); | |
144 Resource::trace(visitor); | 210 Resource::trace(visitor); |
145 ImageObserver::trace(visitor); | |
146 MultipartImageResourceParser::Client::trace(visitor); | 211 MultipartImageResourceParser::Client::trace(visitor); |
147 } | 212 } |
148 | 213 |
149 void ImageResource::checkNotify() { | 214 void ImageResource::checkNotify() { |
150 // Don't notify clients of completion if this ImageResource is | 215 // Don't notify clients of completion if this ImageResource is |
151 // about to be reloaded. | 216 // about to be reloaded. |
152 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder()) | 217 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder()) |
153 return; | 218 return; |
154 | 219 |
155 Resource::checkNotify(); | 220 Resource::checkNotify(); |
156 } | 221 } |
157 | 222 |
158 void ImageResource::markObserverFinished(ImageResourceObserver* observer) { | 223 bool ImageResource::hasClientsOrObservers() const { |
159 if (m_observers.contains(observer)) { | 224 return Resource::hasClientsOrObservers() || getContent()->hasObservers(); |
160 m_finishedObservers.add(observer); | |
161 m_observers.remove(observer); | |
162 } | |
163 } | 225 } |
164 | 226 |
165 void ImageResource::didAddClient(ResourceClient* client) { | 227 void ImageResource::didAddClient(ResourceClient* client) { |
166 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); | 228 DCHECK((m_multipartParser && isLoading()) || !data() || |
229 getContent()->hasImage()); | |
167 | 230 |
168 // Don't notify observers and clients of completion if this ImageResource is | 231 // Don't notify observers and clients of completion if this ImageResource is |
169 // about to be reloaded. | 232 // about to be reloaded. |
170 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder()) | 233 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder()) |
171 return; | 234 return; |
172 | 235 |
173 Resource::didAddClient(client); | 236 Resource::didAddClient(client); |
174 } | 237 } |
175 | 238 |
176 void ImageResource::addObserver(ImageResourceObserver* observer) { | |
177 willAddClientOrObserver(MarkAsReferenced); | |
178 | |
179 m_observers.add(observer); | |
180 | |
181 if (isCacheValidator()) | |
182 return; | |
183 | |
184 // When the response is not multipart, if |data()| exists, |m_image| must be | |
185 // created. This is assured that |updateImage()| is called when |appendData()| | |
186 // is called. | |
187 // | |
188 // On the other hand, when the response is multipart, |updateImage()| is not | |
189 // called in |appendData()|, which means |m_image| might not be created even | |
190 // when |data()| exists. This is intentional since creating a |m_image| on | |
191 // receiving data might destroy an existing image in a previous part. | |
192 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); | |
193 | |
194 if (m_image && !m_image->isNull()) { | |
195 observer->imageChanged(this); | |
196 } | |
197 | |
198 if (isLoaded() && m_observers.contains(observer) && !m_isSchedulingReload && | |
199 !shouldReloadBrokenPlaceholder()) { | |
200 markObserverFinished(observer); | |
201 observer->imageNotifyFinished(this); | |
202 } | |
203 } | |
204 | |
205 void ImageResource::removeObserver(ImageResourceObserver* observer) { | |
206 DCHECK(observer); | |
207 | |
208 if (m_observers.contains(observer)) | |
209 m_observers.remove(observer); | |
210 else if (m_finishedObservers.contains(observer)) | |
211 m_finishedObservers.remove(observer); | |
212 else | |
213 NOTREACHED(); | |
214 | |
215 didRemoveClientOrObserver(); | |
216 } | |
217 | |
218 static void priorityFromObserver(const ImageResourceObserver* observer, | |
219 ResourcePriority& priority) { | |
220 ResourcePriority nextPriority = observer->computeResourcePriority(); | |
221 if (nextPriority.visibility == ResourcePriority::NotVisible) | |
222 return; | |
223 priority.visibility = ResourcePriority::Visible; | |
224 priority.intraPriorityValue += nextPriority.intraPriorityValue; | |
225 } | |
226 | |
227 ResourcePriority ImageResource::priorityFromObservers() { | |
228 ResourcePriority priority; | |
229 | |
230 for (auto* observer : m_finishedObservers.asVector()) { | |
231 if (m_finishedObservers.contains(observer)) | |
232 priorityFromObserver(observer, priority); | |
233 } | |
234 for (auto* observer : m_observers.asVector()) { | |
235 if (m_observers.contains(observer)) | |
236 priorityFromObserver(observer, priority); | |
237 } | |
238 | |
239 return priority; | |
240 } | |
241 | |
242 void ImageResource::destroyDecodedDataForFailedRevalidation() { | 239 void ImageResource::destroyDecodedDataForFailedRevalidation() { |
243 clearImage(); | 240 getContent()->updateImage(nullptr, ImageResourceContent::ClearExistingImage, |
241 false); | |
244 setDecodedSize(0); | 242 setDecodedSize(0); |
245 } | 243 } |
246 | 244 |
247 void ImageResource::destroyDecodedDataIfPossible() { | 245 void ImageResource::destroyDecodedDataIfPossible() { |
248 if (!m_image) | 246 getContent()->destroyDecodedData(); |
249 return; | 247 if (getContent()->hasImage() && !isPreloaded() && |
250 CHECK(!errorOccurred()); | 248 getContent()->isRefetchableDataFromDiskCache()) { |
251 m_image->destroyDecodedData(); | |
252 if (!isPreloaded() && m_isRefetchableDataFromDiskCache) { | |
253 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer.EstimatedDroppableEncodedSize", | 249 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer.EstimatedDroppableEncodedSize", |
254 encodedSize() / 1024); | 250 encodedSize() / 1024); |
255 } | 251 } |
256 } | 252 } |
257 | 253 |
258 void ImageResource::doResetAnimation() { | |
259 if (m_image) | |
260 m_image->resetAnimation(); | |
261 } | |
262 | |
263 void ImageResource::allClientsAndObserversRemoved() { | 254 void ImageResource::allClientsAndObserversRemoved() { |
264 if (m_image) { | 255 CHECK(!getContent()->hasImage() || !errorOccurred()); |
265 CHECK(!errorOccurred()); | 256 // If possible, delay the resetting until back at the event loop. Doing so |
266 // If possible, delay the resetting until back at the event loop. Doing so | 257 // after a conservative GC prevents resetAnimation() from upsetting ongoing |
267 // after a conservative GC prevents resetAnimation() from upsetting ongoing | 258 // animation updates (crbug.com/613709) |
268 // animation updates (crbug.com/613709) | 259 if (!ThreadHeap::willObjectBeLazilySwept(this)) { |
269 if (!ThreadHeap::willObjectBeLazilySwept(this)) { | 260 Platform::current()->currentThread()->getWebTaskRunner()->postTask( |
270 Platform::current()->currentThread()->getWebTaskRunner()->postTask( | 261 BLINK_FROM_HERE, WTF::bind(&ImageResourceContent::doResetAnimation, |
271 BLINK_FROM_HERE, WTF::bind(&ImageResource::doResetAnimation, | 262 wrapWeakPersistent(getContent()))); |
272 wrapWeakPersistent(this))); | 263 } else { |
273 } else { | 264 getContent()->doResetAnimation(); |
274 m_image->resetAnimation(); | |
275 } | |
276 } | 265 } |
277 if (m_multipartParser) | 266 if (m_multipartParser) |
278 m_multipartParser->cancel(); | 267 m_multipartParser->cancel(); |
279 Resource::allClientsAndObserversRemoved(); | 268 Resource::allClientsAndObserversRemoved(); |
280 } | 269 } |
281 | 270 |
282 PassRefPtr<const SharedBuffer> ImageResource::resourceBuffer() const { | 271 PassRefPtr<const SharedBuffer> ImageResource::resourceBuffer() const { |
283 if (data()) | 272 if (data()) |
284 return data(); | 273 return data(); |
285 if (m_image) | 274 if (getContent()->hasImage()) |
286 return m_image->data(); | 275 return getContent()->getImage()->data(); |
287 return nullptr; | 276 return nullptr; |
288 } | 277 } |
289 | 278 |
290 void ImageResource::appendData(const char* data, size_t length) { | 279 void ImageResource::appendData(const char* data, size_t length) { |
291 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length); | 280 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length); |
292 if (m_multipartParser) { | 281 if (m_multipartParser) { |
293 m_multipartParser->appendData(data, length); | 282 m_multipartParser->appendData(data, length); |
294 } else { | 283 } else { |
295 Resource::appendData(data, length); | 284 Resource::appendData(data, length); |
296 | 285 |
297 // If we don't have the size available yet, then update immediately since | 286 // If we don't have the size available yet, then update immediately since |
298 // we need to know the image size as soon as possible. Likewise for | 287 // we need to know the image size as soon as possible. Likewise for |
299 // animated images, update right away since we shouldn't throttle animated | 288 // animated images, update right away since we shouldn't throttle animated |
300 // images. | 289 // images. |
301 if (m_sizeAvailable == Image::SizeUnavailable || | 290 if (!getContent()->isSizeAvailable() || |
302 (m_image && m_image->maybeAnimated())) { | 291 (getContent()->hasImage() && |
303 updateImage(false); | 292 getContent()->getImage()->maybeAnimated())) { |
293 getContent()->updateImage(this->data(), | |
294 ImageResourceContent::KeepExistingImage, false); | |
304 return; | 295 return; |
305 } | 296 } |
306 | 297 |
307 // For other cases, only update at |kFlushDelaySeconds| intervals. This | 298 // For other cases, only update at |kFlushDelaySeconds| intervals. This |
308 // throttles how frequently we update |m_image| and how frequently we | 299 // throttles how frequently we update |m_image| and how frequently we |
309 // inform the clients which causes an invalidation of this image. In other | 300 // inform the clients which causes an invalidation of this image. In other |
310 // words, we only invalidate this image every |kFlushDelaySeconds| seconds | 301 // words, we only invalidate this image every |kFlushDelaySeconds| seconds |
311 // while loading. | 302 // while loading. |
312 if (!m_flushTimer.isActive()) { | 303 if (!m_flushTimer.isActive()) { |
313 double now = WTF::monotonicallyIncreasingTime(); | 304 double now = WTF::monotonicallyIncreasingTime(); |
314 if (!m_lastFlushTime) | 305 if (!m_lastFlushTime) |
315 m_lastFlushTime = now; | 306 m_lastFlushTime = now; |
316 | 307 |
317 DCHECK_LE(m_lastFlushTime, now); | 308 DCHECK_LE(m_lastFlushTime, now); |
318 double flushDelay = m_lastFlushTime - now + kFlushDelaySeconds; | 309 double flushDelay = m_lastFlushTime - now + kFlushDelaySeconds; |
319 if (flushDelay < 0.) | 310 if (flushDelay < 0.) |
320 flushDelay = 0.; | 311 flushDelay = 0.; |
321 m_flushTimer.startOneShot(flushDelay, BLINK_FROM_HERE); | 312 m_flushTimer.startOneShot(flushDelay, BLINK_FROM_HERE); |
322 } | 313 } |
323 } | 314 } |
324 } | 315 } |
325 | 316 |
326 void ImageResource::flushImageIfNeeded(TimerBase*) { | 317 void ImageResource::flushImageIfNeeded(TimerBase*) { |
327 // We might have already loaded the image fully, in which case we don't need | 318 // We might have already loaded the image fully, in which case we don't need |
328 // to call |updateImage()|. | 319 // to call |updateImage()|. |
329 if (isLoading()) { | 320 if (isLoading()) { |
330 m_lastFlushTime = WTF::monotonicallyIncreasingTime(); | 321 m_lastFlushTime = WTF::monotonicallyIncreasingTime(); |
331 updateImage(false); | 322 getContent()->updateImage(this->data(), |
323 ImageResourceContent::KeepExistingImage, false); | |
332 } | 324 } |
333 } | 325 } |
334 | 326 |
335 std::pair<blink::Image*, float> ImageResource::brokenImage( | |
336 float deviceScaleFactor) { | |
337 if (deviceScaleFactor >= 2) { | |
338 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, | |
339 (blink::Image::loadPlatformResource("missingImage@2x"))); | |
340 return std::make_pair(brokenImageHiRes, 2); | |
341 } | |
342 | |
343 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, | |
344 (blink::Image::loadPlatformResource("missingImage"))); | |
345 return std::make_pair(brokenImageLoRes, 1); | |
346 } | |
347 | |
348 bool ImageResource::willPaintBrokenImage() const { | 327 bool ImageResource::willPaintBrokenImage() const { |
349 return errorOccurred(); | 328 return errorOccurred(); |
350 } | 329 } |
351 | 330 |
352 blink::Image* ImageResource::getImage() { | 331 void ImageResource::decodeError(bool allDataReceived) { |
353 if (errorOccurred()) { | 332 size_t size = encodedSize(); |
354 // Returning the 1x broken image is non-ideal, but we cannot reliably access | 333 |
355 // the appropriate deviceScaleFactor from here. It is critical that callers | 334 clearData(); |
356 // use ImageResource::brokenImage() when they need the real, | 335 setEncodedSize(0); |
357 // deviceScaleFactor-appropriate broken image icon. | 336 if (!errorOccurred()) |
358 return brokenImage(1).first; | 337 setStatus(DecodeError); |
338 | |
339 if (!allDataReceived && loader()) { | |
340 // TODO(hiroshige): Do not call didFinishLoading() directly. | |
341 loader()->didFinishLoading(monotonicallyIncreasingTime(), size, size); | |
359 } | 342 } |
360 | 343 |
361 if (m_image) | 344 memoryCache()->remove(this); |
362 return m_image.get(); | |
363 | |
364 return blink::Image::nullImage(); | |
365 } | |
366 | |
367 bool ImageResource::usesImageContainerSize() const { | |
368 if (m_image) | |
369 return m_image->usesContainerSize(); | |
370 | |
371 return false; | |
372 } | |
373 | |
374 bool ImageResource::imageHasRelativeSize() const { | |
375 if (m_image) | |
376 return m_image->hasRelativeSize(); | |
377 | |
378 return false; | |
379 } | |
380 | |
381 LayoutSize ImageResource::imageSize( | |
382 RespectImageOrientationEnum shouldRespectImageOrientation, | |
383 float multiplier, | |
384 SizeType sizeType) { | |
385 if (!m_image) | |
386 return LayoutSize(); | |
387 | |
388 LayoutSize size; | |
389 | |
390 if (m_image->isBitmapImage() && | |
391 shouldRespectImageOrientation == RespectImageOrientation) { | |
392 size = | |
393 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); | |
394 } else { | |
395 size = LayoutSize(m_image->size()); | |
396 } | |
397 | |
398 if (sizeType == IntrinsicCorrectedToDPR && m_hasDevicePixelRatioHeaderValue && | |
399 m_devicePixelRatioHeaderValue > 0) | |
400 multiplier = 1 / m_devicePixelRatioHeaderValue; | |
401 | |
402 if (multiplier == 1 || m_image->hasRelativeSize()) | |
403 return size; | |
404 | |
405 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. | |
406 LayoutSize minimumSize( | |
407 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), | |
408 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); | |
409 size.scale(multiplier); | |
410 size.clampToMinimumSize(minimumSize); | |
411 return size; | |
412 } | |
413 | |
414 void ImageResource::notifyObservers(NotifyFinishOption notifyingFinishOption, | |
415 const IntRect* changeRect) { | |
416 for (auto* observer : m_finishedObservers.asVector()) { | |
417 if (m_finishedObservers.contains(observer)) | |
418 observer->imageChanged(this, changeRect); | |
419 } | |
420 for (auto* observer : m_observers.asVector()) { | |
421 if (m_observers.contains(observer)) { | |
422 observer->imageChanged(this, changeRect); | |
423 if (notifyingFinishOption == ShouldNotifyFinish && | |
424 m_observers.contains(observer) && !m_isSchedulingReload && | |
425 !shouldReloadBrokenPlaceholder()) { | |
426 markObserverFinished(observer); | |
427 observer->imageNotifyFinished(this); | |
428 } | |
429 } | |
430 } | |
431 } | |
432 | |
433 void ImageResource::clear() { | |
434 clearImage(); | |
435 clearData(); | |
436 setEncodedSize(0); | |
437 } | |
438 | |
439 inline void ImageResource::createImage() { | |
440 // Create the image if it doesn't yet exist. | |
441 if (m_image) | |
442 return; | |
443 | |
444 if (response().mimeType() == "image/svg+xml") { | |
445 m_image = SVGImage::create(this); | |
446 } else { | |
447 m_image = BitmapImage::create(this); | |
448 } | |
449 } | |
450 | |
451 inline void ImageResource::clearImage() { | |
452 if (!m_image) | |
453 return; | |
454 int64_t length = m_image->data() ? m_image->data()->size() : 0; | |
455 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); | |
456 | |
457 // If our Image has an observer, it's always us so we need to clear the back | |
458 // pointer before dropping our reference. | |
459 m_image->clearImageObserver(); | |
460 m_image.clear(); | |
461 m_sizeAvailable = Image::SizeUnavailable; | |
462 } | |
463 | |
464 void ImageResource::updateImage(bool allDataReceived) { | |
465 TRACE_EVENT0("blink", "ImageResource::updateImage"); | |
466 | |
467 if (data()) | |
468 createImage(); | |
469 | |
470 // Have the image update its data from its internal buffer. It will not do | |
471 // anything now, but will delay decoding until queried for info (like size or | |
472 // specific image frames). | |
473 if (data()) { | |
474 DCHECK(m_image); | |
475 m_sizeAvailable = m_image->setData(data(), allDataReceived); | |
476 } | |
477 | |
478 // Go ahead and tell our observers to try to draw if we have either received | |
479 // all the data or the size is known. Each chunk from the network causes | |
480 // observers to repaint, which will force that chunk to decode. | |
481 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) | |
482 return; | |
483 | |
484 if (m_isPlaceholder && allDataReceived && m_image && !m_image->isNull()) { | |
485 if (m_sizeAvailable == Image::SizeAvailable) { | |
486 // TODO(sclittle): Show the original image if the response consists of the | |
487 // entire image, such as if the entire image response body is smaller than | |
488 // the requested range. | |
489 IntSize dimensions = m_image->size(); | |
490 clearImage(); | |
491 m_image = PlaceholderImage::create(this, dimensions); | |
492 } else { | |
493 // Clear the image so that it gets treated like a decoding error, since | |
494 // the attempt to build a placeholder image failed. | |
495 clearImage(); | |
496 } | |
497 } | |
498 | |
499 if (!m_image || m_image->isNull()) { | |
500 size_t size = encodedSize(); | |
501 clear(); | |
502 if (!errorOccurred()) | |
503 setStatus(DecodeError); | |
504 if (!allDataReceived && loader()) { | |
505 loader()->didFinishLoading(monotonicallyIncreasingTime(), size, size); | |
506 } | |
507 memoryCache()->remove(this); | |
508 } | |
509 | |
510 // It would be nice to only redraw the decoded band of the image, but with the | |
511 // current design (decoding delayed until painting) that seems hard. | |
512 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); | |
513 } | 345 } |
514 | 346 |
515 void ImageResource::updateImageAndClearBuffer() { | 347 void ImageResource::updateImageAndClearBuffer() { |
516 clearImage(); | 348 getContent()->updateImage(data(), ImageResourceContent::ClearExistingImage, |
517 updateImage(true); | 349 true); |
518 clearData(); | 350 clearData(); |
519 } | 351 } |
520 | 352 |
521 void ImageResource::finish(double loadFinishTime) { | 353 void ImageResource::finish(double loadFinishTime) { |
522 if (m_multipartParser) { | 354 if (m_multipartParser) { |
523 m_multipartParser->finish(); | 355 m_multipartParser->finish(); |
524 if (data()) | 356 if (data()) |
525 updateImageAndClearBuffer(); | 357 updateImageAndClearBuffer(); |
526 } else { | 358 } else { |
527 updateImage(true); | 359 getContent()->updateImage(data(), ImageResourceContent::KeepExistingImage, |
360 true); | |
528 // As encoded image data can be created from m_image (see | 361 // As encoded image data can be created from m_image (see |
529 // ImageResource::resourceBuffer(), we don't have to keep m_data. Let's | 362 // ImageResource::resourceBuffer(), we don't have to keep m_data. Let's |
530 // clear this. As for the lifetimes of m_image and m_data, see this | 363 // clear this. As for the lifetimes of m_image and m_data, see this |
531 // document: | 364 // document: |
532 // https://docs.google.com/document/d/1v0yTAZ6wkqX2U_M6BNIGUJpM1s0TIw1Vsqpxo L7aciY/edit?usp=sharing | 365 // https://docs.google.com/document/d/1v0yTAZ6wkqX2U_M6BNIGUJpM1s0TIw1Vsqpxo L7aciY/edit?usp=sharing |
533 clearData(); | 366 clearData(); |
534 } | 367 } |
535 Resource::finish(loadFinishTime); | 368 Resource::finish(loadFinishTime); |
536 } | 369 } |
537 | 370 |
538 void ImageResource::error(const ResourceError& error) { | 371 void ImageResource::error(const ResourceError& error) { |
539 if (m_multipartParser) | 372 if (m_multipartParser) |
540 m_multipartParser->cancel(); | 373 m_multipartParser->cancel(); |
541 clear(); | 374 clearData(); |
yhirano
2016/12/05 09:02:03
Resource::error calls clearData().
yhirano
2016/12/05 09:02:03
+setEncodedSize(0)?
hiroshige
2016/12/06 09:32:50
Done to keep the current behavior.
But I'm not so
hiroshige
2016/12/06 09:32:50
Done.
| |
542 Resource::error(error); | 375 Resource::error(error); |
543 notifyObservers(ShouldNotifyFinish); | 376 getContent()->updateImage(nullptr, ImageResourceContent::ClearExistingImage, |
377 true); | |
544 } | 378 } |
545 | 379 |
546 void ImageResource::responseReceived( | 380 void ImageResource::responseReceived( |
547 const ResourceResponse& response, | 381 const ResourceResponse& response, |
548 std::unique_ptr<WebDataConsumerHandle> handle) { | 382 std::unique_ptr<WebDataConsumerHandle> handle) { |
549 DCHECK(!handle); | 383 DCHECK(!handle); |
550 DCHECK(!m_multipartParser); | 384 DCHECK(!m_multipartParser); |
551 // If there's no boundary, just handle the request normally. | 385 // If there's no boundary, just handle the request normally. |
552 if (response.isMultipart() && !response.multipartBoundary().isEmpty()) { | 386 if (response.isMultipart() && !response.multipartBoundary().isEmpty()) { |
553 m_multipartParser = new MultipartImageResourceParser( | 387 m_multipartParser = new MultipartImageResourceParser( |
554 response, response.multipartBoundary(), this); | 388 response, response.multipartBoundary(), this); |
555 } | 389 } |
556 Resource::responseReceived(response, std::move(handle)); | 390 Resource::responseReceived(response, std::move(handle)); |
557 if (RuntimeEnabledFeatures::clientHintsEnabled()) { | 391 if (RuntimeEnabledFeatures::clientHintsEnabled()) { |
558 m_devicePixelRatioHeaderValue = | 392 m_devicePixelRatioHeaderValue = |
559 this->response() | 393 this->response() |
560 .httpHeaderField(HTTPNames::Content_DPR) | 394 .httpHeaderField(HTTPNames::Content_DPR) |
561 .toFloat(&m_hasDevicePixelRatioHeaderValue); | 395 .toFloat(&m_hasDevicePixelRatioHeaderValue); |
562 if (!m_hasDevicePixelRatioHeaderValue || | 396 if (!m_hasDevicePixelRatioHeaderValue || |
563 m_devicePixelRatioHeaderValue <= 0.0) { | 397 m_devicePixelRatioHeaderValue <= 0.0) { |
564 m_devicePixelRatioHeaderValue = 1.0; | 398 m_devicePixelRatioHeaderValue = 1.0; |
565 m_hasDevicePixelRatioHeaderValue = false; | 399 m_hasDevicePixelRatioHeaderValue = false; |
566 } | 400 } |
567 } | 401 } |
568 } | 402 } |
569 | 403 |
570 void ImageResource::decodedSizeChangedTo(const blink::Image* image, | |
571 size_t newSize) { | |
572 if (!image || image != m_image) | |
573 return; | |
574 | |
575 setDecodedSize(newSize); | |
576 } | |
577 | |
578 bool ImageResource::shouldPauseAnimation(const blink::Image* image) { | |
579 if (!image || image != m_image) | |
580 return false; | |
581 | |
582 for (auto* observer : m_finishedObservers.asVector()) { | |
583 if (m_finishedObservers.contains(observer) && observer->willRenderImage()) | |
584 return false; | |
585 } | |
586 | |
587 for (auto* observer : m_observers.asVector()) { | |
588 if (m_observers.contains(observer) && observer->willRenderImage()) | |
589 return false; | |
590 } | |
591 | |
592 return true; | |
593 } | |
594 | |
595 void ImageResource::animationAdvanced(const blink::Image* image) { | |
596 if (!image || image != m_image) | |
597 return; | |
598 notifyObservers(DoNotNotifyFinish); | |
599 } | |
600 | |
601 void ImageResource::updateImageAnimationPolicy() { | |
602 if (!m_image) | |
603 return; | |
604 | |
605 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; | |
606 for (auto* observer : m_finishedObservers.asVector()) { | |
607 if (m_finishedObservers.contains(observer) && | |
608 observer->getImageAnimationPolicy(newPolicy)) | |
609 break; | |
610 } | |
611 for (auto* observer : m_observers.asVector()) { | |
612 if (m_observers.contains(observer) && | |
613 observer->getImageAnimationPolicy(newPolicy)) | |
614 break; | |
615 } | |
616 | |
617 if (m_image->animationPolicy() != newPolicy) { | |
618 m_image->resetAnimation(); | |
619 m_image->setAnimationPolicy(newPolicy); | |
620 } | |
621 } | |
622 | |
623 static bool isLoFiImage(const ImageResource& resource) { | 404 static bool isLoFiImage(const ImageResource& resource) { |
624 if (resource.resourceRequest().loFiState() != WebURLRequest::LoFiOn) | 405 if (resource.resourceRequest().loFiState() != WebURLRequest::LoFiOn) |
625 return false; | 406 return false; |
626 return !resource.isLoaded() || | 407 return !resource.isLoaded() || |
627 resource.response() | 408 resource.response() |
628 .httpHeaderField("chrome-proxy-content-transform") | 409 .httpHeaderField("chrome-proxy-content-transform") |
629 .contains("empty-image"); | 410 .contains("empty-image"); |
630 } | 411 } |
631 | 412 |
632 void ImageResource::reloadIfLoFiOrPlaceholder( | 413 void ImageResource::reloadIfLoFiOrPlaceholder( |
(...skipping 17 matching lines...) Expand all Loading... | |
650 m_isPlaceholder = false; | 431 m_isPlaceholder = false; |
651 clearRangeRequestHeader(); | 432 clearRangeRequestHeader(); |
652 } | 433 } |
653 | 434 |
654 if (isLoading()) { | 435 if (isLoading()) { |
655 loader()->cancel(); | 436 loader()->cancel(); |
656 // Canceling the loader causes error() to be called, which in turn calls | 437 // Canceling the loader causes error() to be called, which in turn calls |
657 // clear() and notifyObservers(), so there's no need to call these again | 438 // clear() and notifyObservers(), so there's no need to call these again |
658 // here. | 439 // here. |
659 } else { | 440 } else { |
660 clear(); | 441 clearData(); |
661 notifyObservers(DoNotNotifyFinish); | 442 setEncodedSize(0); |
443 getContent()->updateImage(nullptr, ImageResourceContent::ClearExistingImage, | |
444 false); | |
662 } | 445 } |
663 | 446 |
664 setStatus(NotStarted); | 447 setStatus(NotStarted); |
665 | 448 |
666 DCHECK(m_isSchedulingReload); | 449 DCHECK(m_isSchedulingReload); |
667 m_isSchedulingReload = false; | 450 m_isSchedulingReload = false; |
668 | 451 |
669 fetcher->startLoad(this); | 452 fetcher->startLoad(this); |
670 } | 453 } |
671 | 454 |
672 void ImageResource::changedInRect(const blink::Image* image, | |
673 const IntRect& rect) { | |
674 if (!image || image != m_image) | |
675 return; | |
676 notifyObservers(DoNotNotifyFinish, &rect); | |
677 } | |
678 | |
679 void ImageResource::onePartInMultipartReceived( | 455 void ImageResource::onePartInMultipartReceived( |
680 const ResourceResponse& response) { | 456 const ResourceResponse& response) { |
681 DCHECK(m_multipartParser); | 457 DCHECK(m_multipartParser); |
682 | 458 |
683 setResponse(response); | 459 setResponse(response); |
684 if (m_multipartParsingState == MultipartParsingState::WaitingForFirstPart) { | 460 if (m_multipartParsingState == MultipartParsingState::WaitingForFirstPart) { |
685 // We have nothing to do because we don't have any data. | 461 // We have nothing to do because we don't have any data. |
686 m_multipartParsingState = MultipartParsingState::ParsingFirstPart; | 462 m_multipartParsingState = MultipartParsingState::ParsingFirstPart; |
687 return; | 463 return; |
688 } | 464 } |
(...skipping 11 matching lines...) Expand all Loading... | |
700 if (loader()) | 476 if (loader()) |
701 loader()->didFinishLoadingFirstPartInMultipart(); | 477 loader()->didFinishLoadingFirstPartInMultipart(); |
702 } | 478 } |
703 } | 479 } |
704 | 480 |
705 void ImageResource::multipartDataReceived(const char* bytes, size_t size) { | 481 void ImageResource::multipartDataReceived(const char* bytes, size_t size) { |
706 DCHECK(m_multipartParser); | 482 DCHECK(m_multipartParser); |
707 Resource::appendData(bytes, size); | 483 Resource::appendData(bytes, size); |
708 } | 484 } |
709 | 485 |
710 bool ImageResource::isAccessAllowed(SecurityOrigin* securityOrigin) { | 486 bool ImageResource::isAccessAllowed( |
487 SecurityOrigin* securityOrigin, | |
488 bool doesCurrentFrameHasSingleSecurityOrigin) const { | |
711 if (response().wasFetchedViaServiceWorker()) { | 489 if (response().wasFetchedViaServiceWorker()) { |
712 return response().serviceWorkerResponseType() != | 490 return response().serviceWorkerResponseType() != |
713 WebServiceWorkerResponseTypeOpaque; | 491 WebServiceWorkerResponseTypeOpaque; |
714 } | 492 } |
715 if (!getImage()->currentFrameHasSingleSecurityOrigin()) | 493 if (!doesCurrentFrameHasSingleSecurityOrigin) |
716 return false; | 494 return false; |
717 if (passesAccessControlCheck(securityOrigin)) | 495 if (passesAccessControlCheck(securityOrigin)) |
718 return true; | 496 return true; |
719 return !securityOrigin->taintsCanvas(response().url()); | 497 return !securityOrigin->taintsCanvas(response().url()); |
720 } | 498 } |
721 | 499 |
500 ImageResourceContent* ImageResource::getContent() const { | |
501 return m_content; | |
502 } | |
503 | |
504 ResourcePriority ImageResource::priorityFromObservers() { | |
505 return getContent()->priorityFromObservers(); | |
506 } | |
507 | |
722 } // namespace blink | 508 } // namespace blink |
OLD | NEW |