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