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 | 8 #include "core/fetch/ImageResourceInfo.h" |
26 #include "core/fetch/ImageResourceObserver.h" | 9 #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" | 10 #include "core/svg/graphics/SVGImage.h" |
33 #include "platform/Histogram.h" | 11 #include "platform/Histogram.h" |
34 #include "platform/RuntimeEnabledFeatures.h" | 12 #include "platform/RuntimeEnabledFeatures.h" |
35 #include "platform/SharedBuffer.h" | 13 #include "platform/SharedBuffer.h" |
36 #include "platform/geometry/IntSize.h" | 14 #include "platform/geometry/IntSize.h" |
37 #include "platform/graphics/BitmapImage.h" | 15 #include "platform/graphics/BitmapImage.h" |
38 #include "platform/graphics/PlaceholderImage.h" | 16 #include "platform/graphics/PlaceholderImage.h" |
39 #include "platform/tracing/TraceEvent.h" | 17 #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" | |
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 DoesCurrentFrameHaveSingleSecurityOrigin) const override { |
| 50 return true; |
| 51 } |
| 52 bool hasCacheControlNoStoreHeader() const override { return false; } |
| 53 const ResourceError& resourceError() const override { return m_error; } |
| 54 |
| 55 void decodeError(bool allDataReceived) override {} |
| 56 void setDecodedSize(size_t) override {} |
| 57 void willAddClientOrObserver() override {} |
| 58 void didRemoveClientOrObserver() override {} |
| 59 void emulateLoadStartedForInspector( |
| 60 ResourceFetcher*, |
| 61 const KURL&, |
| 62 const AtomicString& initiatorName) override {} |
| 63 |
| 64 const KURL m_url; |
| 65 const ResourceResponse m_response; |
| 66 const ResourceError m_error; |
| 67 }; |
| 68 |
55 } // namespace | 69 } // namespace |
56 | 70 |
57 class ImageResource::ImageResourceFactory : public ResourceFactory { | 71 ImageResourceContent::ImageResourceContent(PassRefPtr<blink::Image> image) |
58 STACK_ALLOCATED(); | 72 : m_image(image), m_isRefetchableDataFromDiskCache(true) { |
59 | 73 DEFINE_STATIC_LOCAL(NullImageResourceInfo, nullInfo, |
60 public: | 74 (new NullImageResourceInfo())); |
61 ImageResourceFactory(const FetchRequest& fetchRequest) | 75 m_info = &nullInfo; |
62 : ResourceFactory(Resource::Image), m_fetchRequest(&fetchRequest) {} | |
63 | |
64 Resource* create(const ResourceRequest& request, | |
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 ResourceRequestBlockedReason blockReason = fetcher->context().canRequest( | |
88 Resource::Image, request.resourceRequest(), requestURL, | |
89 request.options(), request.forPreload(), | |
90 request.getOriginRestriction()); | |
91 if (blockReason == ResourceRequestBlockedReason::None) | |
92 fetcher->context().sendImagePing(requestURL); | |
93 } | |
94 return nullptr; | |
95 } | |
96 | |
97 ImageResource* resource = toImageResource( | |
98 fetcher->requestResource(request, ImageResourceFactory(request))); | |
99 if (resource && | |
100 request.placeholderImageRequestType() != FetchRequest::AllowPlaceholder && | |
101 resource->m_isPlaceholder) { | |
102 // If the image is a placeholder, but this fetch doesn't allow a | |
103 // placeholder, then load the original image. Note that the cache is not | |
104 // bypassed here - it should be fine to use a cached copy if possible. | |
105 resource->reloadIfLoFiOrPlaceholder(fetcher, | |
106 ReloadCachePolicy::UseExistingPolicy); | |
107 } | |
108 return resource; | |
109 } | 76 } |
110 | 77 |
111 ImageResource::ImageResource(const ResourceRequest& resourceRequest, | 78 ImageResourceContent* ImageResourceContent::fetch(FetchRequest& request, |
112 const ResourceLoaderOptions& options, | 79 ResourceFetcher* fetcher) { |
113 bool isPlaceholder) | 80 // TODO(hiroshige): Remove direct references to ImageResource by making |
114 : Resource(resourceRequest, Image, options), | 81 // the dependencies around ImageResource and ImageResourceContent cleaner. |
115 m_devicePixelRatioHeaderValue(1.0), | 82 ImageResource* resource = ImageResource::fetch(request, fetcher); |
116 m_image(nullptr), | 83 if (!resource) |
117 m_hasDevicePixelRatioHeaderValue(false), | 84 return nullptr; |
118 m_isSchedulingReload(false), | 85 return resource->getContent(); |
119 m_isPlaceholder(isPlaceholder), | |
120 m_flushTimer(this, &ImageResource::flushImageIfNeeded), | |
121 m_isRefetchableDataFromDiskCache(true) { | |
122 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; | |
123 } | 86 } |
124 | 87 |
125 ImageResource::ImageResource(blink::Image* image, | 88 void ImageResourceContent::setImageResourceInfo(ImageResourceInfo* info) { |
126 const ResourceLoaderOptions& options) | 89 m_info = info; |
127 : Resource(ResourceRequest(""), Image, options), | |
128 m_devicePixelRatioHeaderValue(1.0), | |
129 m_image(image), | |
130 m_hasDevicePixelRatioHeaderValue(false), | |
131 m_isSchedulingReload(false), | |
132 m_isPlaceholder(false), | |
133 m_flushTimer(this, &ImageResource::flushImageIfNeeded), | |
134 m_isRefetchableDataFromDiskCache(true) { | |
135 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this; | |
136 setStatus(Cached); | |
137 } | 90 } |
138 | 91 |
139 ImageResource::~ImageResource() { | 92 DEFINE_TRACE(ImageResourceContent) { |
140 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; | 93 visitor->trace(m_info); |
141 clearImage(); | 94 ImageObserver::trace(visitor); |
142 } | 95 } |
143 | 96 |
144 DEFINE_TRACE(ImageResource) { | 97 void ImageResourceContent::markObserverFinished( |
145 visitor->trace(m_multipartParser); | 98 ImageResourceObserver* observer) { |
146 Resource::trace(visitor); | |
147 ImageObserver::trace(visitor); | |
148 MultipartImageResourceParser::Client::trace(visitor); | |
149 } | |
150 | |
151 void ImageResource::checkNotify() { | |
152 // Don't notify clients of completion if this ImageResource is | |
153 // about to be reloaded. | |
154 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder()) | |
155 return; | |
156 | |
157 Resource::checkNotify(); | |
158 } | |
159 | |
160 void ImageResource::markObserverFinished(ImageResourceObserver* observer) { | |
161 auto it = m_observers.find(observer); | 99 auto it = m_observers.find(observer); |
162 if (it == m_observers.end()) | 100 if (it == m_observers.end()) |
163 return; | 101 return; |
164 m_observers.remove(it); | 102 m_observers.remove(it); |
165 m_finishedObservers.add(observer); | 103 m_finishedObservers.add(observer); |
166 } | 104 } |
167 | 105 |
168 void ImageResource::didAddClient(ResourceClient* client) { | 106 void ImageResourceContent::addObserver(ImageResourceObserver* observer) { |
169 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); | 107 m_info->willAddClientOrObserver(); |
170 | |
171 // Don't notify observers and clients of completion if this ImageResource is | |
172 // about to be reloaded. | |
173 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder()) | |
174 return; | |
175 | |
176 Resource::didAddClient(client); | |
177 } | |
178 | |
179 void ImageResource::addObserver(ImageResourceObserver* observer) { | |
180 willAddClientOrObserver(MarkAsReferenced); | |
181 | 108 |
182 m_observers.add(observer); | 109 m_observers.add(observer); |
183 | 110 |
184 if (isCacheValidator()) | 111 if (m_info->isCacheValidator()) |
185 return; | 112 return; |
186 | 113 |
187 // When the response is not multipart, if |data()| exists, |m_image| must be | |
188 // created. This is assured that |updateImage()| is called when |appendData()| | |
189 // is called. | |
190 // | |
191 // On the other hand, when the response is multipart, |updateImage()| is not | |
192 // called in |appendData()|, which means |m_image| might not be created even | |
193 // when |data()| exists. This is intentional since creating a |m_image| on | |
194 // receiving data might destroy an existing image in a previous part. | |
195 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); | |
196 | |
197 if (m_image && !m_image->isNull()) { | 114 if (m_image && !m_image->isNull()) { |
198 observer->imageChanged(this); | 115 observer->imageChanged(this); |
199 } | 116 } |
200 | 117 |
201 if (isLoaded() && m_observers.contains(observer) && !m_isSchedulingReload && | 118 if (isLoaded() && m_observers.contains(observer) && |
202 !shouldReloadBrokenPlaceholder()) { | 119 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { |
203 markObserverFinished(observer); | 120 markObserverFinished(observer); |
204 observer->imageNotifyFinished(this); | 121 observer->imageNotifyFinished(this); |
205 } | 122 } |
206 } | 123 } |
207 | 124 |
208 void ImageResource::removeObserver(ImageResourceObserver* observer) { | 125 void ImageResourceContent::removeObserver(ImageResourceObserver* observer) { |
209 DCHECK(observer); | 126 DCHECK(observer); |
210 | 127 |
211 auto it = m_observers.find(observer); | 128 auto it = m_observers.find(observer); |
212 if (it != m_observers.end()) { | 129 if (it != m_observers.end()) { |
213 m_observers.remove(it); | 130 m_observers.remove(it); |
214 } else { | 131 } else { |
215 it = m_finishedObservers.find(observer); | 132 it = m_finishedObservers.find(observer); |
216 DCHECK(it != m_finishedObservers.end()); | 133 DCHECK(it != m_finishedObservers.end()); |
217 m_finishedObservers.remove(it); | 134 m_finishedObservers.remove(it); |
218 } | 135 } |
219 didRemoveClientOrObserver(); | 136 m_info->didRemoveClientOrObserver(); |
220 } | 137 } |
221 | 138 |
222 static void priorityFromObserver(const ImageResourceObserver* observer, | 139 static void priorityFromObserver(const ImageResourceObserver* observer, |
223 ResourcePriority& priority) { | 140 ResourcePriority& priority) { |
224 ResourcePriority nextPriority = observer->computeResourcePriority(); | 141 ResourcePriority nextPriority = observer->computeResourcePriority(); |
225 if (nextPriority.visibility == ResourcePriority::NotVisible) | 142 if (nextPriority.visibility == ResourcePriority::NotVisible) |
226 return; | 143 return; |
227 priority.visibility = ResourcePriority::Visible; | 144 priority.visibility = ResourcePriority::Visible; |
228 priority.intraPriorityValue += nextPriority.intraPriorityValue; | 145 priority.intraPriorityValue += nextPriority.intraPriorityValue; |
229 } | 146 } |
230 | 147 |
231 ResourcePriority ImageResource::priorityFromObservers() { | 148 ResourcePriority ImageResourceContent::priorityFromObservers() const { |
232 ResourcePriority priority; | 149 ResourcePriority priority; |
233 | 150 |
234 for (const auto& it : m_finishedObservers) | 151 for (const auto& it : m_finishedObservers) |
235 priorityFromObserver(it.key, priority); | 152 priorityFromObserver(it.key, priority); |
236 for (const auto& it : m_observers) | 153 for (const auto& it : m_observers) |
237 priorityFromObserver(it.key, priority); | 154 priorityFromObserver(it.key, priority); |
238 | 155 |
239 return priority; | 156 return priority; |
240 } | 157 } |
241 | 158 |
242 void ImageResource::destroyDecodedDataForFailedRevalidation() { | 159 void ImageResourceContent::destroyDecodedData() { |
243 clearImage(); | |
244 setDecodedSize(0); | |
245 } | |
246 | |
247 void ImageResource::destroyDecodedDataIfPossible() { | |
248 if (!m_image) | 160 if (!m_image) |
249 return; | 161 return; |
250 CHECK(!errorOccurred()); | 162 CHECK(!errorOccurred()); |
251 m_image->destroyDecodedData(); | 163 m_image->destroyDecodedData(); |
252 if (!isPreloaded() && m_isRefetchableDataFromDiskCache) { | |
253 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer.EstimatedDroppableEncodedSize", | |
254 encodedSize() / 1024); | |
255 } | |
256 } | 164 } |
257 | 165 |
258 void ImageResource::doResetAnimation() { | 166 void ImageResourceContent::doResetAnimation() { |
259 if (m_image) | 167 if (m_image) |
260 m_image->resetAnimation(); | 168 m_image->resetAnimation(); |
261 } | 169 } |
262 | 170 |
263 void ImageResource::allClientsAndObserversRemoved() { | 171 PassRefPtr<const SharedBuffer> ImageResourceContent::resourceBuffer() const { |
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) | 172 if (m_image) |
286 return m_image->data(); | 173 return m_image->data(); |
287 return nullptr; | 174 return nullptr; |
288 } | 175 } |
289 | 176 |
290 void ImageResource::appendData(const char* data, size_t length) { | 177 bool ImageResourceContent::shouldUpdateImageImmediately() const { |
291 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length); | 178 // If we don't have the size available yet, then update immediately since |
292 if (m_multipartParser) { | 179 // we need to know the image size as soon as possible. Likewise for |
293 m_multipartParser->appendData(data, length); | 180 // animated images, update right away since we shouldn't throttle animated |
294 } else { | 181 // images. |
295 Resource::appendData(data, length); | 182 return m_sizeAvailable == Image::SizeUnavailable || |
296 | 183 (m_image && m_image->maybeAnimated()); |
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 } | 184 } |
325 | 185 |
326 void ImageResource::flushImageIfNeeded(TimerBase*) { | 186 std::pair<blink::Image*, float> ImageResourceContent::brokenImage( |
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) { | 187 float deviceScaleFactor) { |
337 if (deviceScaleFactor >= 2) { | 188 if (deviceScaleFactor >= 2) { |
338 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, | 189 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, |
339 (blink::Image::loadPlatformResource("missingImage@2x"))); | 190 (blink::Image::loadPlatformResource("missingImage@2x"))); |
340 return std::make_pair(brokenImageHiRes, 2); | 191 return std::make_pair(brokenImageHiRes, 2); |
341 } | 192 } |
342 | 193 |
343 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, | 194 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, |
344 (blink::Image::loadPlatformResource("missingImage"))); | 195 (blink::Image::loadPlatformResource("missingImage"))); |
345 return std::make_pair(brokenImageLoRes, 1); | 196 return std::make_pair(brokenImageLoRes, 1); |
346 } | 197 } |
347 | 198 |
348 bool ImageResource::willPaintBrokenImage() const { | 199 blink::Image* ImageResourceContent::getImage() { |
349 return errorOccurred(); | |
350 } | |
351 | |
352 blink::Image* ImageResource::getImage() { | |
353 if (errorOccurred()) { | 200 if (errorOccurred()) { |
354 // Returning the 1x broken image is non-ideal, but we cannot reliably access | 201 // Returning the 1x broken image is non-ideal, but we cannot reliably access |
355 // the appropriate deviceScaleFactor from here. It is critical that callers | 202 // the appropriate deviceScaleFactor from here. It is critical that callers |
356 // use ImageResource::brokenImage() when they need the real, | 203 // use ImageResourceContent::brokenImage() when they need the real, |
357 // deviceScaleFactor-appropriate broken image icon. | 204 // deviceScaleFactor-appropriate broken image icon. |
358 return brokenImage(1).first; | 205 return brokenImage(1).first; |
359 } | 206 } |
360 | 207 |
361 if (m_image) | 208 if (m_image) |
362 return m_image.get(); | 209 return m_image.get(); |
363 | 210 |
364 return blink::Image::nullImage(); | 211 return blink::Image::nullImage(); |
365 } | 212 } |
366 | 213 |
367 bool ImageResource::usesImageContainerSize() const { | 214 bool ImageResourceContent::usesImageContainerSize() const { |
368 if (m_image) | 215 if (m_image) |
369 return m_image->usesContainerSize(); | 216 return m_image->usesContainerSize(); |
370 | 217 |
371 return false; | 218 return false; |
372 } | 219 } |
373 | 220 |
374 bool ImageResource::imageHasRelativeSize() const { | 221 bool ImageResourceContent::imageHasRelativeSize() const { |
375 if (m_image) | 222 if (m_image) |
376 return m_image->hasRelativeSize(); | 223 return m_image->hasRelativeSize(); |
377 | 224 |
378 return false; | 225 return false; |
379 } | 226 } |
380 | 227 |
381 LayoutSize ImageResource::imageSize( | 228 LayoutSize ImageResourceContent::imageSize( |
382 RespectImageOrientationEnum shouldRespectImageOrientation, | 229 RespectImageOrientationEnum shouldRespectImageOrientation, |
383 float multiplier, | 230 float multiplier, |
384 SizeType sizeType) { | 231 SizeType sizeType) { |
385 if (!m_image) | 232 if (!m_image) |
386 return LayoutSize(); | 233 return LayoutSize(); |
387 | 234 |
388 LayoutSize size; | 235 LayoutSize size; |
389 | 236 |
390 if (m_image->isBitmapImage() && | 237 if (m_image->isBitmapImage() && |
391 shouldRespectImageOrientation == RespectImageOrientation) { | 238 shouldRespectImageOrientation == RespectImageOrientation) { |
392 size = | 239 size = |
393 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); | 240 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); |
394 } else { | 241 } else { |
395 size = LayoutSize(m_image->size()); | 242 size = LayoutSize(m_image->size()); |
396 } | 243 } |
397 | 244 |
398 if (sizeType == IntrinsicCorrectedToDPR && m_hasDevicePixelRatioHeaderValue && | 245 if (sizeType == IntrinsicCorrectedToDPR && hasDevicePixelRatioHeaderValue() && |
399 m_devicePixelRatioHeaderValue > 0) | 246 devicePixelRatioHeaderValue() > 0) |
400 multiplier = 1 / m_devicePixelRatioHeaderValue; | 247 multiplier = 1 / devicePixelRatioHeaderValue(); |
401 | 248 |
402 if (multiplier == 1 || m_image->hasRelativeSize()) | 249 if (multiplier == 1 || m_image->hasRelativeSize()) |
403 return size; | 250 return size; |
404 | 251 |
405 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. | 252 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. |
406 LayoutSize minimumSize( | 253 LayoutSize minimumSize( |
407 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), | 254 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), |
408 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); | 255 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); |
409 size.scale(multiplier); | 256 size.scale(multiplier); |
410 size.clampToMinimumSize(minimumSize); | 257 size.clampToMinimumSize(minimumSize); |
411 return size; | 258 return size; |
412 } | 259 } |
413 | 260 |
414 void ImageResource::notifyObservers(NotifyFinishOption notifyingFinishOption, | 261 void ImageResourceContent::notifyObservers( |
415 const IntRect* changeRect) { | 262 NotifyFinishOption notifyingFinishOption, |
| 263 const IntRect* changeRect) { |
416 for (auto* observer : m_finishedObservers.asVector()) { | 264 for (auto* observer : m_finishedObservers.asVector()) { |
417 if (m_finishedObservers.contains(observer)) | 265 if (m_finishedObservers.contains(observer)) |
418 observer->imageChanged(this, changeRect); | 266 observer->imageChanged(this, changeRect); |
419 } | 267 } |
420 for (auto* observer : m_observers.asVector()) { | 268 for (auto* observer : m_observers.asVector()) { |
421 if (m_observers.contains(observer)) { | 269 if (m_observers.contains(observer)) { |
422 observer->imageChanged(this, changeRect); | 270 observer->imageChanged(this, changeRect); |
423 if (notifyingFinishOption == ShouldNotifyFinish && | 271 if (notifyingFinishOption == ShouldNotifyFinish && |
424 m_observers.contains(observer) && !m_isSchedulingReload && | 272 m_observers.contains(observer) && |
425 !shouldReloadBrokenPlaceholder()) { | 273 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { |
426 markObserverFinished(observer); | 274 markObserverFinished(observer); |
427 observer->imageNotifyFinished(this); | 275 observer->imageNotifyFinished(this); |
428 } | 276 } |
429 } | 277 } |
430 } | 278 } |
431 } | 279 } |
432 | 280 |
433 void ImageResource::clear() { | 281 PassRefPtr<Image> ImageResourceContent::createImage() { |
434 clearImage(); | 282 if (m_info->response().mimeType() == "image/svg+xml") |
435 clearData(); | 283 return SVGImage::create(this); |
436 setEncodedSize(0); | 284 return BitmapImage::create(this); |
437 } | 285 } |
438 | 286 |
439 inline void ImageResource::createImage() { | 287 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) | 288 if (!m_image) |
453 return; | 289 return; |
454 int64_t length = m_image->data() ? m_image->data()->size() : 0; | 290 int64_t length = m_image->data() ? m_image->data()->size() : 0; |
455 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); | 291 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); |
456 | 292 |
457 // If our Image has an observer, it's always us so we need to clear the back | 293 // If our Image has an observer, it's always us so we need to clear the back |
458 // pointer before dropping our reference. | 294 // pointer before dropping our reference. |
459 m_image->clearImageObserver(); | 295 m_image->clearImageObserver(); |
460 m_image.clear(); | 296 m_image.clear(); |
461 m_sizeAvailable = Image::SizeUnavailable; | 297 m_sizeAvailable = Image::SizeUnavailable; |
462 } | 298 } |
463 | 299 |
464 void ImageResource::updateImage(bool allDataReceived) { | 300 void ImageResourceContent::clearImageAndNotifyObservers( |
465 TRACE_EVENT0("blink", "ImageResource::updateImage"); | 301 NotifyFinishOption notifyingFinishOption) { |
| 302 clearImage(); |
| 303 notifyObservers(notifyingFinishOption); |
| 304 } |
466 | 305 |
467 if (data()) | 306 void ImageResourceContent::updateImage(PassRefPtr<SharedBuffer> data, |
468 createImage(); | 307 ClearImageOption clearImageOption, |
| 308 bool allDataReceived) { |
| 309 TRACE_EVENT0("blink", "ImageResourceContent::updateImage"); |
| 310 |
| 311 if (clearImageOption == ImageResourceContent::ClearExistingImage) { |
| 312 clearImage(); |
| 313 } |
469 | 314 |
470 // Have the image update its data from its internal buffer. It will not do | 315 // 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 | 316 // anything now, but will delay decoding until queried for info (like size or |
472 // specific image frames). | 317 // specific image frames). |
473 if (data()) { | 318 if (data) { |
| 319 if (!m_image) |
| 320 m_image = createImage(); |
474 DCHECK(m_image); | 321 DCHECK(m_image); |
475 m_sizeAvailable = m_image->setData(data(), allDataReceived); | 322 m_sizeAvailable = m_image->setData(std::move(data), allDataReceived); |
476 } | 323 } |
477 | 324 |
478 // Go ahead and tell our observers to try to draw if we have either received | 325 // 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 | 326 // 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. | 327 // observers to repaint, which will force that chunk to decode. |
481 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) | 328 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) |
482 return; | 329 return; |
483 | 330 |
484 if (m_isPlaceholder && allDataReceived && m_image && !m_image->isNull()) { | 331 if (m_info->isPlaceholder() && allDataReceived && m_image && |
| 332 !m_image->isNull()) { |
485 if (m_sizeAvailable == Image::SizeAvailable) { | 333 if (m_sizeAvailable == Image::SizeAvailable) { |
486 // TODO(sclittle): Show the original image if the response consists of the | 334 // 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 | 335 // entire image, such as if the entire image response body is smaller than |
488 // the requested range. | 336 // the requested range. |
489 IntSize dimensions = m_image->size(); | 337 IntSize dimensions = m_image->size(); |
| 338 |
490 clearImage(); | 339 clearImage(); |
491 m_image = PlaceholderImage::create(this, dimensions); | 340 m_image = PlaceholderImage::create(this, dimensions); |
492 } else { | 341 } else { |
493 // Clear the image so that it gets treated like a decoding error, since | 342 // Clear the image so that it gets treated like a decoding error, since |
494 // the attempt to build a placeholder image failed. | 343 // the attempt to build a placeholder image failed. |
495 clearImage(); | 344 clearImage(); |
496 } | 345 } |
497 } | 346 } |
498 | 347 |
499 if (!m_image || m_image->isNull()) { | 348 if (!m_image || m_image->isNull()) { |
500 size_t size = encodedSize(); | 349 clearImage(); |
501 clear(); | 350 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 } | 351 } |
509 | 352 |
510 // It would be nice to only redraw the decoded band of the image, but with the | 353 // 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. | 354 // current design (decoding delayed until painting) that seems hard. |
512 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); | 355 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); |
513 } | 356 } |
514 | 357 |
515 void ImageResource::updateImageAndClearBuffer() { | 358 void ImageResourceContent::decodedSizeChangedTo(const blink::Image* image, |
516 clearImage(); | 359 size_t newSize) { |
517 updateImage(true); | |
518 clearData(); | |
519 } | |
520 | |
521 void ImageResource::finish(double loadFinishTime) { | |
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) | 360 if (!image || image != m_image) |
573 return; | 361 return; |
574 | 362 |
575 setDecodedSize(newSize); | 363 m_info->setDecodedSize(newSize); |
576 } | 364 } |
577 | 365 |
578 bool ImageResource::shouldPauseAnimation(const blink::Image* image) { | 366 bool ImageResourceContent::shouldPauseAnimation(const blink::Image* image) { |
579 if (!image || image != m_image) | 367 if (!image || image != m_image) |
580 return false; | 368 return false; |
581 | 369 |
582 for (const auto& it : m_finishedObservers) | 370 for (const auto& it : m_finishedObservers) |
583 if (it.key->willRenderImage()) | 371 if (it.key->willRenderImage()) |
584 return false; | 372 return false; |
585 | 373 |
586 for (const auto& it : m_observers) | 374 for (const auto& it : m_observers) |
587 if (it.key->willRenderImage()) | 375 if (it.key->willRenderImage()) |
588 return false; | 376 return false; |
589 | 377 |
590 return true; | 378 return true; |
591 } | 379 } |
592 | 380 |
593 void ImageResource::animationAdvanced(const blink::Image* image) { | 381 void ImageResourceContent::animationAdvanced(const blink::Image* image) { |
594 if (!image || image != m_image) | 382 if (!image || image != m_image) |
595 return; | 383 return; |
596 notifyObservers(DoNotNotifyFinish); | 384 notifyObservers(DoNotNotifyFinish); |
597 } | 385 } |
598 | 386 |
599 void ImageResource::updateImageAnimationPolicy() { | 387 void ImageResourceContent::updateImageAnimationPolicy() { |
600 if (!m_image) | 388 if (!m_image) |
601 return; | 389 return; |
602 | 390 |
603 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; | 391 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; |
604 for (const auto& it : m_finishedObservers) { | 392 for (const auto& it : m_finishedObservers) { |
605 if (it.key->getImageAnimationPolicy(newPolicy)) | 393 if (it.key->getImageAnimationPolicy(newPolicy)) |
606 break; | 394 break; |
607 } | 395 } |
608 for (const auto& it : m_observers) { | 396 for (const auto& it : m_observers) { |
609 if (it.key->getImageAnimationPolicy(newPolicy)) | 397 if (it.key->getImageAnimationPolicy(newPolicy)) |
610 break; | 398 break; |
611 } | 399 } |
612 | 400 |
613 if (m_image->animationPolicy() != newPolicy) { | 401 if (m_image->animationPolicy() != newPolicy) { |
614 m_image->resetAnimation(); | 402 m_image->resetAnimation(); |
615 m_image->setAnimationPolicy(newPolicy); | 403 m_image->setAnimationPolicy(newPolicy); |
616 } | 404 } |
617 } | 405 } |
618 | 406 |
619 static bool isLoFiImage(const ImageResource& resource) { | 407 void ImageResourceContent::changedInRect(const blink::Image* image, |
620 if (resource.resourceRequest().loFiState() != WebURLRequest::LoFiOn) | 408 const IntRect& rect) { |
621 return false; | |
622 return !resource.isLoaded() || | |
623 resource.response() | |
624 .httpHeaderField("chrome-proxy-content-transform") | |
625 .contains("empty-image"); | |
626 } | |
627 | |
628 void ImageResource::reloadIfLoFiOrPlaceholder( | |
629 ResourceFetcher* fetcher, | |
630 ReloadCachePolicy reloadCachePolicy) { | |
631 if (!m_isPlaceholder && !isLoFiImage(*this)) | |
632 return; | |
633 | |
634 // Prevent clients and observers from being notified of completion while the | |
635 // reload is being scheduled, so that e.g. canceling an existing load in | |
636 // progress doesn't cause clients and observers to be notified of completion | |
637 // prematurely. | |
638 DCHECK(!m_isSchedulingReload); | |
639 m_isSchedulingReload = true; | |
640 | |
641 if (reloadCachePolicy == ReloadCachePolicy::BypassCache) | |
642 setCachePolicyBypassingCache(); | |
643 setLoFiStateOff(); | |
644 | |
645 if (m_isPlaceholder) { | |
646 m_isPlaceholder = false; | |
647 clearRangeRequestHeader(); | |
648 } | |
649 | |
650 if (isLoading()) { | |
651 loader()->cancel(); | |
652 // Canceling the loader causes error() to be called, which in turn calls | |
653 // clear() and notifyObservers(), so there's no need to call these again | |
654 // here. | |
655 } else { | |
656 clear(); | |
657 notifyObservers(DoNotNotifyFinish); | |
658 } | |
659 | |
660 setStatus(NotStarted); | |
661 | |
662 DCHECK(m_isSchedulingReload); | |
663 m_isSchedulingReload = false; | |
664 | |
665 fetcher->startLoad(this); | |
666 } | |
667 | |
668 void ImageResource::changedInRect(const blink::Image* image, | |
669 const IntRect& rect) { | |
670 if (!image || image != m_image) | 409 if (!image || image != m_image) |
671 return; | 410 return; |
672 notifyObservers(DoNotNotifyFinish, &rect); | 411 notifyObservers(DoNotNotifyFinish, &rect); |
673 } | 412 } |
674 | 413 |
675 void ImageResource::onePartInMultipartReceived( | 414 bool ImageResourceContent::isAccessAllowed(SecurityOrigin* securityOrigin) { |
676 const ResourceResponse& response) { | 415 return m_info->isAccessAllowed( |
677 DCHECK(m_multipartParser); | 416 securityOrigin, getImage()->currentFrameHasSingleSecurityOrigin() |
678 | 417 ? ImageResourceInfo::HasSingleSecurityOrigin |
679 setResponse(response); | 418 : ImageResourceInfo::HasMultipleSecurityOrigin); |
680 if (m_multipartParsingState == MultipartParsingState::WaitingForFirstPart) { | |
681 // We have nothing to do because we don't have any data. | |
682 m_multipartParsingState = MultipartParsingState::ParsingFirstPart; | |
683 return; | |
684 } | |
685 updateImageAndClearBuffer(); | |
686 | |
687 if (m_multipartParsingState == MultipartParsingState::ParsingFirstPart) { | |
688 m_multipartParsingState = MultipartParsingState::FinishedParsingFirstPart; | |
689 // Notify finished when the first part ends. | |
690 if (!errorOccurred()) | |
691 setStatus(Cached); | |
692 // We notify clients and observers of finish in checkNotify() and | |
693 // updateImageAndClearBuffer(), respectively, and they will not be | |
694 // notified again in Resource::finish()/error(). | |
695 checkNotify(); | |
696 if (loader()) | |
697 loader()->didFinishLoadingFirstPartInMultipart(); | |
698 } | |
699 } | 419 } |
700 | 420 |
701 void ImageResource::multipartDataReceived(const char* bytes, size_t size) { | 421 void ImageResourceContent::emulateLoadStartedForInspector( |
702 DCHECK(m_multipartParser); | 422 ResourceFetcher* fetcher, |
703 Resource::appendData(bytes, size); | 423 const KURL& url, |
| 424 const AtomicString& initiatorName) { |
| 425 m_info->emulateLoadStartedForInspector(fetcher, url, initiatorName); |
704 } | 426 } |
705 | 427 |
706 bool ImageResource::isAccessAllowed(SecurityOrigin* securityOrigin) { | 428 // TODO(hiroshige): Consider removing the following methods, or stoping |
707 if (response().wasFetchedViaServiceWorker()) { | 429 // redirecting to ImageResource. |
708 return response().serviceWorkerResponseType() != | 430 bool ImageResourceContent::isLoaded() const { |
709 WebServiceWorkerResponseTypeOpaque; | 431 return getStatus() > ResourceStatus::Pending; |
710 } | 432 } |
711 if (!getImage()->currentFrameHasSingleSecurityOrigin()) | 433 |
712 return false; | 434 bool ImageResourceContent::isLoading() const { |
713 if (passesAccessControlCheck(securityOrigin)) | 435 return getStatus() == ResourceStatus::Pending; |
714 return true; | 436 } |
715 return !securityOrigin->taintsCanvas(response().url()); | 437 |
| 438 bool ImageResourceContent::errorOccurred() const { |
| 439 return getStatus() == ResourceStatus::LoadError || |
| 440 getStatus() == ResourceStatus::DecodeError; |
| 441 } |
| 442 |
| 443 bool ImageResourceContent::loadFailedOrCanceled() const { |
| 444 return getStatus() == ResourceStatus::LoadError; |
| 445 } |
| 446 |
| 447 ResourceStatus ImageResourceContent::getStatus() const { |
| 448 return m_info->getStatus(); |
| 449 } |
| 450 |
| 451 const KURL& ImageResourceContent::url() const { |
| 452 return m_info->url(); |
| 453 } |
| 454 |
| 455 bool ImageResourceContent::hasCacheControlNoStoreHeader() const { |
| 456 return m_info->hasCacheControlNoStoreHeader(); |
| 457 } |
| 458 |
| 459 float ImageResourceContent::devicePixelRatioHeaderValue() const { |
| 460 return m_info->devicePixelRatioHeaderValue(); |
| 461 } |
| 462 |
| 463 bool ImageResourceContent::hasDevicePixelRatioHeaderValue() const { |
| 464 return m_info->hasDevicePixelRatioHeaderValue(); |
| 465 } |
| 466 |
| 467 const ResourceResponse& ImageResourceContent::response() const { |
| 468 return m_info->response(); |
| 469 } |
| 470 |
| 471 const ResourceError& ImageResourceContent::resourceError() const { |
| 472 return m_info->resourceError(); |
716 } | 473 } |
717 | 474 |
718 } // namespace blink | 475 } // namespace blink |
OLD | NEW |