OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "core/fetch/ImageResourceContent.h" | |
6 | |
7 #include "core/fetch/ImageResource.h" | |
8 #include "core/fetch/ImageResourceInfo.h" | |
9 #include "core/fetch/ImageResourceObserver.h" | |
10 #include "core/svg/graphics/SVGImage.h" | |
11 #include "platform/Histogram.h" | |
12 #include "platform/RuntimeEnabledFeatures.h" | |
13 #include "platform/SharedBuffer.h" | |
14 #include "platform/geometry/IntSize.h" | |
15 #include "platform/graphics/BitmapImage.h" | |
16 #include "platform/graphics/PlaceholderImage.h" | |
17 #include "platform/tracing/TraceEvent.h" | |
18 #include "wtf/StdLibExtras.h" | |
19 #include "wtf/Vector.h" | |
20 #include <memory> | |
21 #include <v8.h> | |
22 | |
23 namespace blink { | |
24 namespace { | |
25 class NullImageResourceInfo final | |
26 : public GarbageCollectedFinalized<NullImageResourceInfo>, | |
27 public ImageResourceInfo { | |
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 | |
69 } // namespace | |
70 | |
71 ImageResourceContent::ImageResourceContent(PassRefPtr<blink::Image> image) | |
72 : m_image(image), m_isRefetchableDataFromDiskCache(true) { | |
73 DEFINE_STATIC_LOCAL(NullImageResourceInfo, nullInfo, | |
74 (new NullImageResourceInfo())); | |
75 m_info = &nullInfo; | |
76 } | |
77 | |
78 ImageResourceContent* ImageResourceContent::fetch(FetchRequest& request, | |
79 ResourceFetcher* fetcher) { | |
80 // TODO(hiroshige): Remove direct references to ImageResource by making | |
81 // the dependencies around ImageResource and ImageResourceContent cleaner. | |
82 ImageResource* resource = ImageResource::fetch(request, fetcher); | |
83 if (!resource) | |
84 return nullptr; | |
85 return resource->getContent(); | |
86 } | |
87 | |
88 void ImageResourceContent::setImageResourceInfo(ImageResourceInfo* info) { | |
89 m_info = info; | |
90 } | |
91 | |
92 DEFINE_TRACE(ImageResourceContent) { | |
93 visitor->trace(m_info); | |
94 ImageObserver::trace(visitor); | |
95 } | |
96 | |
97 void ImageResourceContent::markObserverFinished( | |
98 ImageResourceObserver* observer) { | |
99 auto it = m_observers.find(observer); | |
100 if (it == m_observers.end()) | |
101 return; | |
102 m_observers.remove(it); | |
103 m_finishedObservers.add(observer); | |
104 } | |
105 | |
106 void ImageResourceContent::addObserver(ImageResourceObserver* observer) { | |
107 m_info->willAddClientOrObserver(); | |
108 | |
109 m_observers.add(observer); | |
110 | |
111 if (m_info->isCacheValidator()) | |
112 return; | |
113 | |
114 if (m_image && !m_image->isNull()) { | |
115 observer->imageChanged(this); | |
116 } | |
117 | |
118 if (isLoaded() && m_observers.contains(observer) && | |
119 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { | |
120 markObserverFinished(observer); | |
121 observer->imageNotifyFinished(this); | |
122 } | |
123 } | |
124 | |
125 void ImageResourceContent::removeObserver(ImageResourceObserver* observer) { | |
126 DCHECK(observer); | |
127 | |
128 auto it = m_observers.find(observer); | |
129 if (it != m_observers.end()) { | |
130 m_observers.remove(it); | |
131 } else { | |
132 it = m_finishedObservers.find(observer); | |
133 DCHECK(it != m_finishedObservers.end()); | |
134 m_finishedObservers.remove(it); | |
135 } | |
136 m_info->didRemoveClientOrObserver(); | |
137 } | |
138 | |
139 static void priorityFromObserver(const ImageResourceObserver* observer, | |
140 ResourcePriority& priority) { | |
141 ResourcePriority nextPriority = observer->computeResourcePriority(); | |
142 if (nextPriority.visibility == ResourcePriority::NotVisible) | |
143 return; | |
144 priority.visibility = ResourcePriority::Visible; | |
145 priority.intraPriorityValue += nextPriority.intraPriorityValue; | |
146 } | |
147 | |
148 ResourcePriority ImageResourceContent::priorityFromObservers() const { | |
149 ResourcePriority priority; | |
150 | |
151 for (const auto& it : m_finishedObservers) | |
152 priorityFromObserver(it.key, priority); | |
153 for (const auto& it : m_observers) | |
154 priorityFromObserver(it.key, priority); | |
155 | |
156 return priority; | |
157 } | |
158 | |
159 void ImageResourceContent::destroyDecodedData() { | |
160 if (!m_image) | |
161 return; | |
162 CHECK(!errorOccurred()); | |
163 m_image->destroyDecodedData(); | |
164 } | |
165 | |
166 void ImageResourceContent::doResetAnimation() { | |
167 if (m_image) | |
168 m_image->resetAnimation(); | |
169 } | |
170 | |
171 PassRefPtr<const SharedBuffer> ImageResourceContent::resourceBuffer() const { | |
172 if (m_image) | |
173 return m_image->data(); | |
174 return nullptr; | |
175 } | |
176 | |
177 bool ImageResourceContent::shouldUpdateImageImmediately() const { | |
178 // If we don't have the size available yet, then update immediately since | |
179 // we need to know the image size as soon as possible. Likewise for | |
180 // animated images, update right away since we shouldn't throttle animated | |
181 // images. | |
182 return m_sizeAvailable == Image::SizeUnavailable || | |
183 (m_image && m_image->maybeAnimated()); | |
184 } | |
185 | |
186 std::pair<blink::Image*, float> ImageResourceContent::brokenImage( | |
187 float deviceScaleFactor) { | |
188 if (deviceScaleFactor >= 2) { | |
189 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, | |
190 (blink::Image::loadPlatformResource("missingImage@2x"))); | |
191 return std::make_pair(brokenImageHiRes, 2); | |
192 } | |
193 | |
194 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, | |
195 (blink::Image::loadPlatformResource("missingImage"))); | |
196 return std::make_pair(brokenImageLoRes, 1); | |
197 } | |
198 | |
199 blink::Image* ImageResourceContent::getImage() { | |
200 if (errorOccurred()) { | |
201 // Returning the 1x broken image is non-ideal, but we cannot reliably access | |
202 // the appropriate deviceScaleFactor from here. It is critical that callers | |
203 // use ImageResourceContent::brokenImage() when they need the real, | |
204 // deviceScaleFactor-appropriate broken image icon. | |
205 return brokenImage(1).first; | |
206 } | |
207 | |
208 if (m_image) | |
209 return m_image.get(); | |
210 | |
211 return blink::Image::nullImage(); | |
212 } | |
213 | |
214 bool ImageResourceContent::usesImageContainerSize() const { | |
215 if (m_image) | |
216 return m_image->usesContainerSize(); | |
217 | |
218 return false; | |
219 } | |
220 | |
221 bool ImageResourceContent::imageHasRelativeSize() const { | |
222 if (m_image) | |
223 return m_image->hasRelativeSize(); | |
224 | |
225 return false; | |
226 } | |
227 | |
228 LayoutSize ImageResourceContent::imageSize( | |
229 RespectImageOrientationEnum shouldRespectImageOrientation, | |
230 float multiplier, | |
231 SizeType sizeType) { | |
232 if (!m_image) | |
233 return LayoutSize(); | |
234 | |
235 LayoutSize size; | |
236 | |
237 if (m_image->isBitmapImage() && | |
238 shouldRespectImageOrientation == RespectImageOrientation) { | |
239 size = | |
240 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); | |
241 } else { | |
242 size = LayoutSize(m_image->size()); | |
243 } | |
244 | |
245 if (sizeType == IntrinsicCorrectedToDPR && hasDevicePixelRatioHeaderValue() && | |
246 devicePixelRatioHeaderValue() > 0) | |
247 multiplier = 1 / devicePixelRatioHeaderValue(); | |
248 | |
249 if (multiplier == 1 || m_image->hasRelativeSize()) | |
250 return size; | |
251 | |
252 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. | |
253 LayoutSize minimumSize( | |
254 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), | |
255 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); | |
256 size.scale(multiplier); | |
257 size.clampToMinimumSize(minimumSize); | |
258 return size; | |
259 } | |
260 | |
261 void ImageResourceContent::notifyObservers( | |
262 NotifyFinishOption notifyingFinishOption, | |
263 const IntRect* changeRect) { | |
264 for (auto* observer : m_finishedObservers.asVector()) { | |
265 if (m_finishedObservers.contains(observer)) | |
266 observer->imageChanged(this, changeRect); | |
267 } | |
268 for (auto* observer : m_observers.asVector()) { | |
269 if (m_observers.contains(observer)) { | |
270 observer->imageChanged(this, changeRect); | |
271 if (notifyingFinishOption == ShouldNotifyFinish && | |
272 m_observers.contains(observer) && | |
273 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { | |
274 markObserverFinished(observer); | |
275 observer->imageNotifyFinished(this); | |
276 } | |
277 } | |
278 } | |
279 } | |
280 | |
281 PassRefPtr<Image> ImageResourceContent::createImage() { | |
282 if (m_info->response().mimeType() == "image/svg+xml") | |
283 return SVGImage::create(this); | |
284 return BitmapImage::create(this); | |
285 } | |
286 | |
287 void ImageResourceContent::clearImage() { | |
288 if (!m_image) | |
289 return; | |
290 int64_t length = m_image->data() ? m_image->data()->size() : 0; | |
291 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); | |
292 | |
293 // If our Image has an observer, it's always us so we need to clear the back | |
294 // pointer before dropping our reference. | |
295 m_image->clearImageObserver(); | |
296 m_image.clear(); | |
297 m_sizeAvailable = Image::SizeUnavailable; | |
298 } | |
299 | |
300 void ImageResourceContent::clearImageAndNotifyObservers( | |
301 NotifyFinishOption notifyingFinishOption) { | |
302 clearImage(); | |
303 notifyObservers(notifyingFinishOption); | |
304 } | |
305 | |
306 void ImageResourceContent::updateImage(PassRefPtr<SharedBuffer> data, | |
307 ClearImageOption clearImageOption, | |
308 bool allDataReceived) { | |
309 TRACE_EVENT0("blink", "ImageResourceContent::updateImage"); | |
310 | |
311 if (clearImageOption == ImageResourceContent::ClearExistingImage) { | |
312 clearImage(); | |
313 } | |
314 | |
315 // Have the image update its data from its internal buffer. It will not do | |
316 // anything now, but will delay decoding until queried for info (like size or | |
317 // specific image frames). | |
318 if (data) { | |
319 if (!m_image) | |
320 m_image = createImage(); | |
321 DCHECK(m_image); | |
322 m_sizeAvailable = m_image->setData(std::move(data), allDataReceived); | |
323 } | |
324 | |
325 // Go ahead and tell our observers to try to draw if we have either received | |
326 // all the data or the size is known. Each chunk from the network causes | |
327 // observers to repaint, which will force that chunk to decode. | |
328 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) | |
329 return; | |
330 | |
331 if (m_info->isPlaceholder() && allDataReceived && m_image && | |
332 !m_image->isNull()) { | |
333 if (m_sizeAvailable == Image::SizeAvailable) { | |
334 // TODO(sclittle): Show the original image if the response consists of the | |
335 // entire image, such as if the entire image response body is smaller than | |
336 // the requested range. | |
337 IntSize dimensions = m_image->size(); | |
338 | |
339 clearImage(); | |
340 m_image = PlaceholderImage::create(this, dimensions); | |
341 } else { | |
342 // Clear the image so that it gets treated like a decoding error, since | |
343 // the attempt to build a placeholder image failed. | |
344 clearImage(); | |
345 } | |
346 } | |
347 | |
348 if (!m_image || m_image->isNull()) { | |
349 clearImage(); | |
350 m_info->decodeError(allDataReceived); | |
351 } | |
352 | |
353 // It would be nice to only redraw the decoded band of the image, but with the | |
354 // current design (decoding delayed until painting) that seems hard. | |
355 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); | |
356 } | |
357 | |
358 void ImageResourceContent::decodedSizeChangedTo(const blink::Image* image, | |
359 size_t newSize) { | |
360 if (!image || image != m_image) | |
361 return; | |
362 | |
363 m_info->setDecodedSize(newSize); | |
364 } | |
365 | |
366 bool ImageResourceContent::shouldPauseAnimation(const blink::Image* image) { | |
367 if (!image || image != m_image) | |
368 return false; | |
369 | |
370 for (const auto& it : m_finishedObservers) | |
371 if (it.key->willRenderImage()) | |
372 return false; | |
373 | |
374 for (const auto& it : m_observers) | |
375 if (it.key->willRenderImage()) | |
376 return false; | |
377 | |
378 return true; | |
379 } | |
380 | |
381 void ImageResourceContent::animationAdvanced(const blink::Image* image) { | |
382 if (!image || image != m_image) | |
383 return; | |
384 notifyObservers(DoNotNotifyFinish); | |
385 } | |
386 | |
387 void ImageResourceContent::updateImageAnimationPolicy() { | |
388 if (!m_image) | |
389 return; | |
390 | |
391 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; | |
392 for (const auto& it : m_finishedObservers) { | |
393 if (it.key->getImageAnimationPolicy(newPolicy)) | |
394 break; | |
395 } | |
396 for (const auto& it : m_observers) { | |
397 if (it.key->getImageAnimationPolicy(newPolicy)) | |
398 break; | |
399 } | |
400 | |
401 if (m_image->animationPolicy() != newPolicy) { | |
402 m_image->resetAnimation(); | |
403 m_image->setAnimationPolicy(newPolicy); | |
404 } | |
405 } | |
406 | |
407 void ImageResourceContent::changedInRect(const blink::Image* image, | |
408 const IntRect& rect) { | |
409 if (!image || image != m_image) | |
410 return; | |
411 notifyObservers(DoNotNotifyFinish, &rect); | |
412 } | |
413 | |
414 bool ImageResourceContent::isAccessAllowed(SecurityOrigin* securityOrigin) { | |
415 return m_info->isAccessAllowed( | |
416 securityOrigin, getImage()->currentFrameHasSingleSecurityOrigin() | |
417 ? ImageResourceInfo::HasSingleSecurityOrigin | |
418 : ImageResourceInfo::HasMultipleSecurityOrigin); | |
419 } | |
420 | |
421 void ImageResourceContent::emulateLoadStartedForInspector( | |
422 ResourceFetcher* fetcher, | |
423 const KURL& url, | |
424 const AtomicString& initiatorName) { | |
425 m_info->emulateLoadStartedForInspector(fetcher, url, initiatorName); | |
426 } | |
427 | |
428 // TODO(hiroshige): Consider removing the following methods, or stoping | |
429 // redirecting to ImageResource. | |
430 bool ImageResourceContent::isLoaded() const { | |
431 return getStatus() > ResourceStatus::Pending; | |
432 } | |
433 | |
434 bool ImageResourceContent::isLoading() const { | |
435 return getStatus() == ResourceStatus::Pending; | |
436 } | |
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(); | |
473 } | |
474 | |
475 } // namespace blink | |
OLD | NEW |