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 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 } | 76 } |
108 | 77 |
109 ImageResource::ImageResource(const ResourceRequest& resourceRequest, | 78 ImageResourceContent* ImageResourceContent::fetch(FetchRequest& request, |
110 const ResourceLoaderOptions& options, | 79 ResourceFetcher* fetcher) { |
111 bool isPlaceholder) | 80 ImageResource* resource = ImageResource::fetch(request, fetcher); |
Nate Chapin
2016/12/07 20:27:28
This feels like a layering violation, and is (I th
hiroshige
2016/12/09 22:50:39
Done.
| |
112 : Resource(resourceRequest, Image, options), | 81 if (!resource) |
113 m_devicePixelRatioHeaderValue(1.0), | 82 return nullptr; |
114 m_image(nullptr), | 83 return resource->getContent(); |
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 } | 84 } |
122 | 85 |
123 ImageResource::ImageResource(blink::Image* image, | 86 void ImageResourceContent::setImageResourceInfo(ImageResourceInfo* info) { |
124 const ResourceLoaderOptions& options) | 87 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 } | 88 } |
136 | 89 |
137 ImageResource::~ImageResource() { | 90 DEFINE_TRACE(ImageResourceContent) { |
138 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; | 91 visitor->trace(m_info); |
139 clearImage(); | 92 ImageObserver::trace(visitor); |
140 } | 93 } |
141 | 94 |
142 DEFINE_TRACE(ImageResource) { | 95 void ImageResourceContent::markObserverFinished( |
143 visitor->trace(m_multipartParser); | 96 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)) { | 97 if (m_observers.contains(observer)) { |
160 m_finishedObservers.add(observer); | 98 m_finishedObservers.add(observer); |
161 m_observers.remove(observer); | 99 m_observers.remove(observer); |
162 } | 100 } |
163 } | 101 } |
164 | 102 |
165 void ImageResource::didAddClient(ResourceClient* client) { | 103 void ImageResourceContent::addObserver(ImageResourceObserver* observer) { |
166 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); | 104 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 | 105 |
179 m_observers.add(observer); | 106 m_observers.add(observer); |
180 | 107 |
181 if (isCacheValidator()) | 108 if (m_info->isCacheValidator()) |
182 return; | 109 return; |
183 | 110 |
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()) { | 111 if (m_image && !m_image->isNull()) { |
195 observer->imageChanged(this); | 112 observer->imageChanged(this); |
196 } | 113 } |
197 | 114 |
198 if (isLoaded() && m_observers.contains(observer) && !m_isSchedulingReload && | 115 if (isLoaded() && m_observers.contains(observer) && |
199 !shouldReloadBrokenPlaceholder()) { | 116 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { |
200 markObserverFinished(observer); | 117 markObserverFinished(observer); |
201 observer->imageNotifyFinished(this); | 118 observer->imageNotifyFinished(this); |
202 } | 119 } |
203 } | 120 } |
204 | 121 |
205 void ImageResource::removeObserver(ImageResourceObserver* observer) { | 122 void ImageResourceContent::removeObserver(ImageResourceObserver* observer) { |
206 DCHECK(observer); | 123 DCHECK(observer); |
207 | 124 |
208 if (m_observers.contains(observer)) | 125 if (m_observers.contains(observer)) |
209 m_observers.remove(observer); | 126 m_observers.remove(observer); |
210 else if (m_finishedObservers.contains(observer)) | 127 else if (m_finishedObservers.contains(observer)) |
211 m_finishedObservers.remove(observer); | 128 m_finishedObservers.remove(observer); |
212 else | 129 else |
213 NOTREACHED(); | 130 NOTREACHED(); |
214 | 131 |
215 didRemoveClientOrObserver(); | 132 m_info->didRemoveClientOrObserver(); |
216 } | 133 } |
217 | 134 |
218 static void priorityFromObserver(const ImageResourceObserver* observer, | 135 static void priorityFromObserver(const ImageResourceObserver* observer, |
219 ResourcePriority& priority) { | 136 ResourcePriority& priority) { |
220 ResourcePriority nextPriority = observer->computeResourcePriority(); | 137 ResourcePriority nextPriority = observer->computeResourcePriority(); |
221 if (nextPriority.visibility == ResourcePriority::NotVisible) | 138 if (nextPriority.visibility == ResourcePriority::NotVisible) |
222 return; | 139 return; |
223 priority.visibility = ResourcePriority::Visible; | 140 priority.visibility = ResourcePriority::Visible; |
224 priority.intraPriorityValue += nextPriority.intraPriorityValue; | 141 priority.intraPriorityValue += nextPriority.intraPriorityValue; |
225 } | 142 } |
226 | 143 |
227 ResourcePriority ImageResource::priorityFromObservers() { | 144 ResourcePriority ImageResourceContent::priorityFromObservers() const { |
228 ResourcePriority priority; | 145 ResourcePriority priority; |
229 | 146 |
230 for (auto* observer : m_finishedObservers.asVector()) { | 147 for (auto* observer : m_finishedObservers.asVector()) { |
231 if (m_finishedObservers.contains(observer)) | 148 if (m_finishedObservers.contains(observer)) |
232 priorityFromObserver(observer, priority); | 149 priorityFromObserver(observer, priority); |
233 } | 150 } |
234 for (auto* observer : m_observers.asVector()) { | 151 for (auto* observer : m_observers.asVector()) { |
235 if (m_observers.contains(observer)) | 152 if (m_observers.contains(observer)) |
236 priorityFromObserver(observer, priority); | 153 priorityFromObserver(observer, priority); |
237 } | 154 } |
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 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) { | 172 float deviceScaleFactor) { |
337 if (deviceScaleFactor >= 2) { | 173 if (deviceScaleFactor >= 2) { |
338 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, | 174 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, |
339 (blink::Image::loadPlatformResource("missingImage@2x"))); | 175 (blink::Image::loadPlatformResource("missingImage@2x"))); |
340 return std::make_pair(brokenImageHiRes, 2); | 176 return std::make_pair(brokenImageHiRes, 2); |
341 } | 177 } |
342 | 178 |
343 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, | 179 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, |
344 (blink::Image::loadPlatformResource("missingImage"))); | 180 (blink::Image::loadPlatformResource("missingImage"))); |
345 return std::make_pair(brokenImageLoRes, 1); | 181 return std::make_pair(brokenImageLoRes, 1); |
346 } | 182 } |
347 | 183 |
348 bool ImageResource::willPaintBrokenImage() const { | 184 blink::Image* ImageResourceContent::getImage() const { |
349 return errorOccurred(); | |
350 } | |
351 | |
352 blink::Image* ImageResource::getImage() { | |
353 if (errorOccurred()) { | 185 if (errorOccurred()) { |
354 // Returning the 1x broken image is non-ideal, but we cannot reliably access | 186 // Returning the 1x broken image is non-ideal, but we cannot reliably access |
355 // the appropriate deviceScaleFactor from here. It is critical that callers | 187 // the appropriate deviceScaleFactor from here. It is critical that callers |
356 // use ImageResource::brokenImage() when they need the real, | 188 // use ImageResourceContent::brokenImage() when they need the real, |
357 // deviceScaleFactor-appropriate broken image icon. | 189 // deviceScaleFactor-appropriate broken image icon. |
358 return brokenImage(1).first; | 190 return brokenImage(1).first; |
359 } | 191 } |
360 | 192 |
361 if (m_image) | 193 if (m_image) |
362 return m_image.get(); | 194 return m_image.get(); |
363 | 195 |
364 return blink::Image::nullImage(); | 196 return blink::Image::nullImage(); |
365 } | 197 } |
366 | 198 |
367 bool ImageResource::usesImageContainerSize() const { | 199 bool ImageResourceContent::usesImageContainerSize() const { |
368 if (m_image) | 200 if (m_image) |
369 return m_image->usesContainerSize(); | 201 return m_image->usesContainerSize(); |
370 | 202 |
371 return false; | 203 return false; |
372 } | 204 } |
373 | 205 |
374 bool ImageResource::imageHasRelativeSize() const { | 206 bool ImageResourceContent::imageHasRelativeSize() const { |
375 if (m_image) | 207 if (m_image) |
376 return m_image->hasRelativeSize(); | 208 return m_image->hasRelativeSize(); |
377 | 209 |
378 return false; | 210 return false; |
379 } | 211 } |
380 | 212 |
381 LayoutSize ImageResource::imageSize( | 213 LayoutSize ImageResourceContent::imageSize( |
382 RespectImageOrientationEnum shouldRespectImageOrientation, | 214 RespectImageOrientationEnum shouldRespectImageOrientation, |
383 float multiplier, | 215 float multiplier, |
384 SizeType sizeType) { | 216 SizeType sizeType) { |
385 if (!m_image) | 217 if (!m_image) |
386 return LayoutSize(); | 218 return LayoutSize(); |
387 | 219 |
388 LayoutSize size; | 220 LayoutSize size; |
389 | 221 |
390 if (m_image->isBitmapImage() && | 222 if (m_image->isBitmapImage() && |
391 shouldRespectImageOrientation == RespectImageOrientation) { | 223 shouldRespectImageOrientation == RespectImageOrientation) { |
392 size = | 224 size = |
393 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); | 225 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); |
394 } else { | 226 } else { |
395 size = LayoutSize(m_image->size()); | 227 size = LayoutSize(m_image->size()); |
396 } | 228 } |
397 | 229 |
398 if (sizeType == IntrinsicCorrectedToDPR && m_hasDevicePixelRatioHeaderValue && | 230 if (sizeType == IntrinsicCorrectedToDPR && hasDevicePixelRatioHeaderValue() && |
399 m_devicePixelRatioHeaderValue > 0) | 231 devicePixelRatioHeaderValue() > 0) |
400 multiplier = 1 / m_devicePixelRatioHeaderValue; | 232 multiplier = 1 / devicePixelRatioHeaderValue(); |
401 | 233 |
402 if (multiplier == 1 || m_image->hasRelativeSize()) | 234 if (multiplier == 1 || m_image->hasRelativeSize()) |
403 return size; | 235 return size; |
404 | 236 |
405 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. | 237 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. |
406 LayoutSize minimumSize( | 238 LayoutSize minimumSize( |
407 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), | 239 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), |
408 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); | 240 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); |
409 size.scale(multiplier); | 241 size.scale(multiplier); |
410 size.clampToMinimumSize(minimumSize); | 242 size.clampToMinimumSize(minimumSize); |
411 return size; | 243 return size; |
412 } | 244 } |
413 | 245 |
414 void ImageResource::notifyObservers(NotifyFinishOption notifyingFinishOption, | 246 void ImageResourceContent::notifyObservers( |
415 const IntRect* changeRect) { | 247 NotifyFinishOption notifyingFinishOption, |
248 const IntRect* changeRect) { | |
416 for (auto* observer : m_finishedObservers.asVector()) { | 249 for (auto* observer : m_finishedObservers.asVector()) { |
417 if (m_finishedObservers.contains(observer)) | 250 if (m_finishedObservers.contains(observer)) |
418 observer->imageChanged(this, changeRect); | 251 observer->imageChanged(this, changeRect); |
419 } | 252 } |
420 for (auto* observer : m_observers.asVector()) { | 253 for (auto* observer : m_observers.asVector()) { |
421 if (m_observers.contains(observer)) { | 254 if (m_observers.contains(observer)) { |
422 observer->imageChanged(this, changeRect); | 255 observer->imageChanged(this, changeRect); |
423 if (notifyingFinishOption == ShouldNotifyFinish && | 256 if (notifyingFinishOption == ShouldNotifyFinish && |
424 m_observers.contains(observer) && !m_isSchedulingReload && | 257 m_observers.contains(observer) && |
425 !shouldReloadBrokenPlaceholder()) { | 258 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { |
426 markObserverFinished(observer); | 259 markObserverFinished(observer); |
427 observer->imageNotifyFinished(this); | 260 observer->imageNotifyFinished(this); |
428 } | 261 } |
429 } | 262 } |
430 } | 263 } |
431 } | 264 } |
432 | 265 |
433 void ImageResource::clear() { | 266 inline PassRefPtr<Image> ImageResourceContent::createImage() { |
yhirano
2016/12/08 04:17:58
Is this inline needed? Ditto below.
hiroshige
2016/12/09 22:50:39
Removed.
| |
434 clearImage(); | 267 if (m_info->response().mimeType() == "image/svg+xml") |
435 clearData(); | 268 return SVGImage::create(this); |
436 setEncodedSize(0); | 269 return BitmapImage::create(this); |
437 } | 270 } |
438 | 271 |
439 inline void ImageResource::createImage() { | 272 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) | 273 if (!m_image) |
453 return; | 274 return; |
454 int64_t length = m_image->data() ? m_image->data()->size() : 0; | 275 int64_t length = m_image->data() ? m_image->data()->size() : 0; |
455 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); | 276 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); |
456 | 277 |
457 // If our Image has an observer, it's always us so we need to clear the back | 278 // If our Image has an observer, it's always us so we need to clear the back |
458 // pointer before dropping our reference. | 279 // pointer before dropping our reference. |
459 m_image->clearImageObserver(); | 280 m_image->clearImageObserver(); |
460 m_image.clear(); | 281 m_image.clear(); |
461 m_sizeAvailable = Image::SizeUnavailable; | 282 m_sizeAvailable = Image::SizeUnavailable; |
462 } | 283 } |
463 | 284 |
464 void ImageResource::updateImage(bool allDataReceived) { | 285 void ImageResourceContent::clearImageAndNotifyObservers( |
465 TRACE_EVENT0("blink", "ImageResource::updateImage"); | 286 NotifyFinishOption notifyingFinishOption) { |
287 clearImage(); | |
288 notifyObservers(notifyingFinishOption); | |
289 } | |
466 | 290 |
467 if (data()) | 291 void ImageResourceContent::updateImage(PassRefPtr<SharedBuffer> data, |
468 createImage(); | 292 ClearImageOption clearImageOption, |
293 bool allDataReceived) { | |
294 TRACE_EVENT0("blink", "ImageResourceContent::updateImage"); | |
295 | |
296 if (clearImageOption == ImageResourceContent::ClearExistingImage) { | |
297 clearImage(); | |
298 } | |
469 | 299 |
470 // Have the image update its data from its internal buffer. It will not do | 300 // 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 | 301 // anything now, but will delay decoding until queried for info (like size or |
472 // specific image frames). | 302 // specific image frames). |
473 if (data()) { | 303 if (data) { |
304 if (!m_image) | |
305 m_image = createImage(); | |
474 DCHECK(m_image); | 306 DCHECK(m_image); |
475 m_sizeAvailable = m_image->setData(data(), allDataReceived); | 307 m_sizeAvailable = m_image->setData(std::move(data), allDataReceived); |
476 } | 308 } |
477 | 309 |
478 // Go ahead and tell our observers to try to draw if we have either received | 310 // 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 | 311 // 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. | 312 // observers to repaint, which will force that chunk to decode. |
481 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) | 313 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) |
482 return; | 314 return; |
483 | 315 |
484 if (m_isPlaceholder && allDataReceived && m_image && !m_image->isNull()) { | 316 if (m_info->isPlaceholder() && allDataReceived && m_image && |
317 !m_image->isNull()) { | |
485 if (m_sizeAvailable == Image::SizeAvailable) { | 318 if (m_sizeAvailable == Image::SizeAvailable) { |
486 // TODO(sclittle): Show the original image if the response consists of the | 319 // 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 | 320 // entire image, such as if the entire image response body is smaller than |
488 // the requested range. | 321 // the requested range. |
489 IntSize dimensions = m_image->size(); | 322 IntSize dimensions = m_image->size(); |
323 | |
490 clearImage(); | 324 clearImage(); |
491 m_image = PlaceholderImage::create(this, dimensions); | 325 m_image = PlaceholderImage::create(this, dimensions); |
492 } else { | 326 } else { |
493 // Clear the image so that it gets treated like a decoding error, since | 327 // Clear the image so that it gets treated like a decoding error, since |
494 // the attempt to build a placeholder image failed. | 328 // the attempt to build a placeholder image failed. |
495 clearImage(); | 329 clearImage(); |
496 } | 330 } |
497 } | 331 } |
498 | 332 |
499 if (!m_image || m_image->isNull()) { | 333 if (!m_image || m_image->isNull()) { |
500 size_t size = encodedSize(); | 334 clearImage(); |
501 clear(); | 335 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 } | 336 } |
509 | 337 |
510 // It would be nice to only redraw the decoded band of the image, but with the | 338 // 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. | 339 // current design (decoding delayed until painting) that seems hard. |
512 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); | 340 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); |
513 } | 341 } |
514 | 342 |
515 void ImageResource::updateImageAndClearBuffer() { | 343 void ImageResourceContent::decodedSizeChangedTo(const blink::Image* image, |
516 clearImage(); | 344 size_t newSize) { |
517 updateImage(true); | 345 if (!image || image != m_image) |
518 clearData(); | 346 return; |
347 m_info->setDecodedSize(newSize); | |
519 } | 348 } |
520 | 349 |
521 void ImageResource::finish(double loadFinishTime) { | 350 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) | 351 if (!image || image != m_image) |
580 return false; | 352 return false; |
581 | 353 |
582 for (auto* observer : m_finishedObservers.asVector()) { | 354 for (auto* observer : m_finishedObservers.asVector()) { |
583 if (m_finishedObservers.contains(observer) && observer->willRenderImage()) | 355 if (m_finishedObservers.contains(observer) && observer->willRenderImage()) |
584 return false; | 356 return false; |
585 } | 357 } |
586 | 358 |
587 for (auto* observer : m_observers.asVector()) { | 359 for (auto* observer : m_observers.asVector()) { |
588 if (m_observers.contains(observer) && observer->willRenderImage()) | 360 if (m_observers.contains(observer) && observer->willRenderImage()) |
589 return false; | 361 return false; |
590 } | 362 } |
591 | 363 |
592 return true; | 364 return true; |
593 } | 365 } |
594 | 366 |
595 void ImageResource::animationAdvanced(const blink::Image* image) { | 367 void ImageResourceContent::animationAdvanced(const blink::Image* image) { |
596 if (!image || image != m_image) | 368 if (!image || image != m_image) |
597 return; | 369 return; |
598 notifyObservers(DoNotNotifyFinish); | 370 notifyObservers(DoNotNotifyFinish); |
599 } | 371 } |
600 | 372 |
601 void ImageResource::updateImageAnimationPolicy() { | 373 void ImageResourceContent::updateImageAnimationPolicy() { |
602 if (!m_image) | 374 if (!m_image) |
603 return; | 375 return; |
604 | 376 |
605 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; | 377 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; |
606 for (auto* observer : m_finishedObservers.asVector()) { | 378 for (auto* observer : m_finishedObservers.asVector()) { |
607 if (m_finishedObservers.contains(observer) && | 379 if (m_finishedObservers.contains(observer) && |
608 observer->getImageAnimationPolicy(newPolicy)) | 380 observer->getImageAnimationPolicy(newPolicy)) |
609 break; | 381 break; |
610 } | 382 } |
611 for (auto* observer : m_observers.asVector()) { | 383 for (auto* observer : m_observers.asVector()) { |
612 if (m_observers.contains(observer) && | 384 if (m_observers.contains(observer) && |
613 observer->getImageAnimationPolicy(newPolicy)) | 385 observer->getImageAnimationPolicy(newPolicy)) |
614 break; | 386 break; |
615 } | 387 } |
616 | 388 |
617 if (m_image->animationPolicy() != newPolicy) { | 389 if (m_image->animationPolicy() != newPolicy) { |
618 m_image->resetAnimation(); | 390 m_image->resetAnimation(); |
619 m_image->setAnimationPolicy(newPolicy); | 391 m_image->setAnimationPolicy(newPolicy); |
620 } | 392 } |
621 } | 393 } |
622 | 394 |
623 static bool isLoFiImage(const ImageResource& resource) { | 395 void ImageResourceContent::changedInRect(const blink::Image* image, |
624 if (resource.resourceRequest().loFiState() != WebURLRequest::LoFiOn) | 396 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) | 397 if (!image || image != m_image) |
675 return; | 398 return; |
676 notifyObservers(DoNotNotifyFinish, &rect); | 399 notifyObservers(DoNotNotifyFinish, &rect); |
677 } | 400 } |
678 | 401 |
679 void ImageResource::onePartInMultipartReceived( | 402 bool ImageResourceContent::isAccessAllowed( |
680 const ResourceResponse& response) { | 403 SecurityOrigin* securityOrigin) const { |
681 DCHECK(m_multipartParser); | 404 return m_info->isAccessAllowed( |
682 | 405 securityOrigin, getImage()->currentFrameHasSingleSecurityOrigin() |
683 setResponse(response); | 406 ? ImageResourceInfo::HasSingleSecurityOrigin |
684 if (m_multipartParsingState == MultipartParsingState::WaitingForFirstPart) { | 407 : ImageResourceInfo::HasMultipleSecurityOrigin); |
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 and observers of finish in checkNotify() and | |
697 // updateImageAndClearBuffer(), respectively, and they will not be | |
698 // notified again in Resource::finish()/error(). | |
699 checkNotify(); | |
700 if (loader()) | |
701 loader()->didFinishLoadingFirstPartInMultipart(); | |
702 } | |
703 } | 408 } |
704 | 409 |
705 void ImageResource::multipartDataReceived(const char* bytes, size_t size) { | 410 void ImageResourceContent::emulateLoadStartedForInspector( |
706 DCHECK(m_multipartParser); | 411 ResourceFetcher* fetcher, |
707 Resource::appendData(bytes, size); | 412 const KURL& url, |
413 const AtomicString& initiatorName) { | |
414 m_info->emulateLoadStartedForInspector(fetcher, url, initiatorName); | |
708 } | 415 } |
709 | 416 |
710 bool ImageResource::isAccessAllowed(SecurityOrigin* securityOrigin) { | 417 // TODO(hiroshige): Consider removing the following methods, or stoping |
711 if (response().wasFetchedViaServiceWorker()) { | 418 // redirecting to ImageResource. |
712 return response().serviceWorkerResponseType() != | 419 bool ImageResourceContent::isLoaded() const { |
713 WebServiceWorkerResponseTypeOpaque; | 420 return getStatus() > ResourceStatus::Pending; |
714 } | 421 } |
715 if (!getImage()->currentFrameHasSingleSecurityOrigin()) | 422 |
716 return false; | 423 bool ImageResourceContent::isLoading() const { |
717 if (passesAccessControlCheck(securityOrigin)) | 424 return getStatus() == ResourceStatus::Pending; |
718 return true; | 425 } |
719 return !securityOrigin->taintsCanvas(response().url()); | 426 |
427 bool ImageResourceContent::errorOccurred() const { | |
428 return getStatus() == ResourceStatus::LoadError || | |
429 getStatus() == ResourceStatus::DecodeError; | |
430 } | |
431 | |
432 bool ImageResourceContent::loadFailedOrCanceled() const { | |
433 return getStatus() == ResourceStatus::LoadError; | |
434 } | |
435 | |
436 ResourceStatus ImageResourceContent::getStatus() const { | |
437 return m_info->getStatus(); | |
438 } | |
439 | |
440 const KURL& ImageResourceContent::url() const { | |
441 return m_info->url(); | |
442 } | |
443 | |
444 bool ImageResourceContent::hasCacheControlNoStoreHeader() const { | |
445 return m_info->hasCacheControlNoStoreHeader(); | |
446 } | |
447 | |
448 float ImageResourceContent::devicePixelRatioHeaderValue() const { | |
449 return m_info->devicePixelRatioHeaderValue(); | |
450 } | |
451 | |
452 bool ImageResourceContent::hasDevicePixelRatioHeaderValue() const { | |
453 return m_info->hasDevicePixelRatioHeaderValue(); | |
454 } | |
455 | |
456 const ResourceResponse& ImageResourceContent::response() const { | |
457 return m_info->response(); | |
458 } | |
459 | |
460 const ResourceError& ImageResourceContent::resourceError() const { | |
461 return m_info->resourceError(); | |
720 } | 462 } |
721 | 463 |
722 } // namespace blink | 464 } // namespace blink |
OLD | NEW |