Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / fetch / ImageResource.cpp
blobce4e585e38b9b7938aa4b929ce09a15064808ffb
1 /*
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
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.
8 This library is free software; you can redistribute it and/or
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.
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.
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.
24 #include "config.h"
25 #include "core/fetch/ImageResource.h"
27 #include "core/fetch/ImageResourceClient.h"
28 #include "core/fetch/MemoryCache.h"
29 #include "core/fetch/ResourceClient.h"
30 #include "core/fetch/ResourceClientWalker.h"
31 #include "core/fetch/ResourceFetcher.h"
32 #include "core/fetch/ResourceLoader.h"
33 #include "core/html/HTMLImageElement.h"
34 #include "core/layout/LayoutObject.h"
35 #include "core/svg/graphics/SVGImage.h"
36 #include "core/svg/graphics/SVGImageForContainer.h"
37 #include "platform/Logging.h"
38 #include "platform/RuntimeEnabledFeatures.h"
39 #include "platform/SharedBuffer.h"
40 #include "platform/TraceEvent.h"
41 #include "platform/graphics/BitmapImage.h"
42 #include "public/platform/Platform.h"
43 #include "wtf/CurrentTime.h"
44 #include "wtf/StdLibExtras.h"
46 namespace blink {
48 ResourcePtr<ImageResource> ImageResource::fetch(FetchRequest& request, ResourceFetcher* fetcher)
50 if (request.resourceRequest().requestContext() == WebURLRequest::RequestContextUnspecified)
51 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage);
52 if (fetcher->context().pageDismissalEventBeingDispatched()) {
53 KURL requestURL = request.resourceRequest().url();
54 if (requestURL.isValid() && fetcher->context().canRequest(Resource::Image, request.resourceRequest(), requestURL, request.options(), request.forPreload(), request.originRestriction()))
55 fetcher->context().sendImagePing(requestURL);
56 return 0;
59 if (fetcher->clientDefersImage(request.resourceRequest().url()))
60 request.setDefer(FetchRequest::DeferredByClient);
62 return toImageResource(fetcher->requestResource(request, ImageResourceFactory()));
65 ImageResource::ImageResource(const ResourceRequest& resourceRequest)
66 : Resource(resourceRequest, Image)
67 , m_devicePixelRatioHeaderValue(1.0)
68 , m_image(nullptr)
69 , m_hasDevicePixelRatioHeaderValue(false)
71 WTF_LOG(Timers, "new ImageResource(ResourceRequest) %p", this);
72 setStatus(Unknown);
73 setCustomAcceptHeader();
76 ImageResource::ImageResource(blink::Image* image)
77 : Resource(ResourceRequest(""), Image)
78 , m_devicePixelRatioHeaderValue(1.0)
79 , m_image(image)
80 , m_hasDevicePixelRatioHeaderValue(false)
82 WTF_LOG(Timers, "new ImageResource(Image) %p", this);
83 setStatus(Cached);
84 setLoading(false);
85 setCustomAcceptHeader();
88 ImageResource::ImageResource(const ResourceRequest& resourceRequest, blink::Image* image)
89 : Resource(resourceRequest, Image)
90 , m_image(image)
92 WTF_LOG(Timers, "new ImageResource(ResourceRequest, Image) %p", this);
93 setStatus(Cached);
94 setLoading(false);
95 setCustomAcceptHeader();
98 ImageResource::~ImageResource()
100 WTF_LOG(Timers, "~ImageResource %p", this);
101 clearImage();
104 void ImageResource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)
106 if (!fetcher || fetcher->autoLoadImages())
107 Resource::load(fetcher, options);
108 else
109 setLoading(false);
112 void ImageResource::didAddClient(ResourceClient* c)
114 if (m_data && !m_image && !errorOccurred()) {
115 createImage();
116 m_image->setData(m_data, true);
119 ASSERT(c->resourceClientType() == ImageResourceClient::expectedType());
120 if (m_image && !m_image->isNull())
121 static_cast<ImageResourceClient*>(c)->imageChanged(this);
123 Resource::didAddClient(c);
126 void ImageResource::didRemoveClient(ResourceClient* c)
128 ASSERT(c);
129 ASSERT(c->resourceClientType() == ImageResourceClient::expectedType());
130 if (m_imageForContainerMap)
131 m_imageForContainerMap->remove(static_cast<ImageResourceClient*>(c));
133 Resource::didRemoveClient(c);
136 bool ImageResource::isSafeToUnlock() const
138 // Note that |m_image| holds a reference to |m_data| in addition to the one held by the Resource parent class.
139 return !m_image || (m_image->hasOneRef() && m_data->refCount() == 2);
142 void ImageResource::destroyDecodedDataIfPossible()
144 if (!hasClients() && !isLoading() && (!m_image || (m_image->hasOneRef() && m_image->isBitmapImage()))) {
145 m_image = nullptr;
146 setDecodedSize(0);
147 } else if (m_image && !errorOccurred()) {
148 m_image->destroyDecodedData(true);
152 void ImageResource::allClientsRemoved()
154 if (m_image && !errorOccurred())
155 m_image->resetAnimation();
156 Resource::allClientsRemoved();
159 pair<blink::Image*, float> ImageResource::brokenImage(float deviceScaleFactor)
161 if (deviceScaleFactor >= 2) {
162 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, (blink::Image::loadPlatformResource("missingImage@2x")));
163 return std::make_pair(brokenImageHiRes, 2);
166 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, (blink::Image::loadPlatformResource("missingImage")));
167 return std::make_pair(brokenImageLoRes, 1);
170 bool ImageResource::willPaintBrokenImage() const
172 return errorOccurred();
175 blink::Image* ImageResource::image()
177 ASSERT(!isPurgeable());
179 if (errorOccurred()) {
180 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
181 // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage()
182 // when they need the real, deviceScaleFactor-appropriate broken image icon.
183 return brokenImage(1).first;
186 if (m_image)
187 return m_image.get();
189 return blink::Image::nullImage();
192 blink::Image* ImageResource::imageForLayoutObject(const LayoutObject* layoutObject)
194 ASSERT(!isPurgeable());
196 if (errorOccurred()) {
197 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
198 // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage()
199 // when they need the real, deviceScaleFactor-appropriate broken image icon.
200 return brokenImage(1).first;
203 if (!m_image)
204 return blink::Image::nullImage();
206 if (m_image->isSVGImage()) {
207 blink::Image* image = svgImageForLayoutObject(layoutObject);
208 if (image != blink::Image::nullImage())
209 return image;
212 return m_image.get();
215 void ImageResource::setContainerSizeForLayoutObject(const ImageResourceClient* layoutObject, const IntSize& containerSize, float containerZoom)
217 if (containerSize.isEmpty())
218 return;
219 ASSERT(layoutObject);
220 ASSERT(containerZoom);
221 if (!m_image)
222 return;
223 if (!m_image->isSVGImage()) {
224 m_image->setContainerSize(containerSize);
225 return;
228 FloatSize containerSizeWithoutZoom(containerSize);
229 containerSizeWithoutZoom.scale(1 / containerZoom);
230 m_imageForContainerMap->set(layoutObject, SVGImageForContainer::create(toSVGImage(m_image.get()), containerSizeWithoutZoom, containerZoom));
233 bool ImageResource::usesImageContainerSize() const
235 if (m_image)
236 return m_image->usesContainerSize();
238 return false;
241 bool ImageResource::imageHasRelativeWidth() const
243 if (m_image)
244 return m_image->hasRelativeWidth();
246 return false;
249 bool ImageResource::imageHasRelativeHeight() const
251 if (m_image)
252 return m_image->hasRelativeHeight();
254 return false;
257 LayoutSize ImageResource::imageSizeForLayoutObject(const LayoutObject* layoutObject, float multiplier, SizeType sizeType)
259 ASSERT(!isPurgeable());
261 if (!m_image)
262 return LayoutSize();
264 LayoutSize imageSize;
266 if (m_image->isBitmapImage() && (layoutObject && layoutObject->shouldRespectImageOrientation() == RespectImageOrientation))
267 imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation());
268 else if (m_image->isSVGImage() && sizeType == NormalSize)
269 imageSize = LayoutSize(svgImageSizeForLayoutObject(layoutObject));
270 else
271 imageSize = LayoutSize(m_image->size());
273 if (sizeType == IntrinsicCorrectedToDPR && m_hasDevicePixelRatioHeaderValue && m_devicePixelRatioHeaderValue > 0)
274 multiplier = 1.0 / m_devicePixelRatioHeaderValue;
276 if (multiplier == 1.0f)
277 return imageSize;
279 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
280 float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
281 float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
282 LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
283 imageSize.scale(widthScale, heightScale);
284 imageSize.clampToMinimumSize(minimumSize);
285 ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
286 return imageSize;
289 void ImageResource::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
291 if (m_image)
292 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
295 void ImageResource::notifyObservers(const IntRect* changeRect)
297 ResourceClientWalker<ImageResourceClient> w(m_clients);
298 while (ImageResourceClient* c = w.next())
299 c->imageChanged(this, changeRect);
302 void ImageResource::clear()
304 prune();
305 clearImage();
306 setEncodedSize(0);
309 void ImageResource::setCustomAcceptHeader()
311 DEFINE_STATIC_LOCAL(const AtomicString, acceptImages, ("image/webp,image/*,*/*;q=0.8", AtomicString::ConstructFromLiteral));
312 setAccept(acceptImages);
315 inline void ImageResource::createImage()
317 // Create the image if it doesn't yet exist.
318 if (m_image)
319 return;
321 if (m_response.mimeType() == "image/svg+xml") {
322 m_image = SVGImage::create(this);
323 m_imageForContainerMap = adoptPtr(new ImageForContainerMap);
324 } else {
325 m_image = BitmapImage::create(this);
329 inline void ImageResource::clearImage()
331 // If our Image has an observer, it's always us so we need to clear the back pointer
332 // before dropping our reference.
333 if (m_image)
334 m_image->setImageObserver(nullptr);
335 m_image.clear();
338 void ImageResource::appendData(const char* data, unsigned length)
340 Resource::appendData(data, length);
341 if (!loadingMultipartContent())
342 updateImage(false);
345 void ImageResource::updateImage(bool allDataReceived)
347 TRACE_EVENT0("blink", "ImageResource::updateImage");
349 if (m_data)
350 createImage();
352 bool sizeAvailable = false;
354 // Have the image update its data from its internal buffer.
355 // It will not do anything now, but will delay decoding until
356 // queried for info (like size or specific image frames).
357 if (m_image)
358 sizeAvailable = m_image->setData(m_data, allDataReceived);
360 // Go ahead and tell our observers to try to draw if we have either
361 // received all the data or the size is known. Each chunk from the
362 // network causes observers to repaint, which will force that chunk
363 // to decode.
364 if (sizeAvailable || allDataReceived) {
365 if (!m_image || m_image->isNull()) {
366 error(errorOccurred() ? status() : DecodeError);
367 if (memoryCache()->contains(this))
368 memoryCache()->remove(this);
369 return;
372 // It would be nice to only redraw the decoded band of the image, but with the current design
373 // (decoding delayed until painting) that seems hard.
374 notifyObservers();
378 void ImageResource::finishOnePart()
380 if (loadingMultipartContent())
381 clear();
382 updateImage(true);
383 if (loadingMultipartContent())
384 m_data.clear();
385 Resource::finishOnePart();
388 void ImageResource::error(Resource::Status status)
390 clear();
391 Resource::error(status);
392 notifyObservers();
395 void ImageResource::responseReceived(const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle)
397 if (loadingMultipartContent() && m_data)
398 finishOnePart();
399 if (RuntimeEnabledFeatures::clientHintsEnabled()) {
400 m_devicePixelRatioHeaderValue = response.httpHeaderField("content-dpr").toFloat(&m_hasDevicePixelRatioHeaderValue);
401 if (!m_hasDevicePixelRatioHeaderValue || m_devicePixelRatioHeaderValue <= 0.0) {
402 m_devicePixelRatioHeaderValue = 1.0;
403 m_hasDevicePixelRatioHeaderValue = false;
407 Resource::responseReceived(response, handle);
410 void ImageResource::decodedSizeChanged(const blink::Image* image, int delta)
412 if (!image || image != m_image)
413 return;
415 setDecodedSize(decodedSize() + delta);
418 void ImageResource::didDraw(const blink::Image* image)
420 if (!image || image != m_image)
421 return;
422 // decodedSize() == 0 indicates that the image is decoded into DiscardableMemory,
423 // not in MemoryCache. So we don't need to call Resource::didAccessDecodedData()
424 // to update MemoryCache.
425 if (decodedSize() != 0)
426 Resource::didAccessDecodedData();
429 bool ImageResource::shouldPauseAnimation(const blink::Image* image)
431 if (!image || image != m_image)
432 return false;
434 ResourceClientWalker<ImageResourceClient> w(m_clients);
435 while (ImageResourceClient* c = w.next()) {
436 if (c->willRenderImage(this))
437 return false;
440 return true;
443 void ImageResource::animationAdvanced(const blink::Image* image)
445 if (!image || image != m_image)
446 return;
447 notifyObservers();
450 void ImageResource::updateImageAnimationPolicy()
452 if (!m_image)
453 return;
455 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed;
456 ResourceClientWalker<ImageResourceClient> w(m_clients);
457 while (ImageResourceClient* c = w.next()) {
458 if (c->getImageAnimationPolicy(this, newPolicy))
459 break;
462 if (m_image->animationPolicy() != newPolicy) {
463 m_image->resetAnimation();
464 m_image->setAnimationPolicy(newPolicy);
468 void ImageResource::changedInRect(const blink::Image* image, const IntRect& rect)
470 if (!image || image != m_image)
471 return;
472 notifyObservers(&rect);
475 bool ImageResource::currentFrameKnownToBeOpaque(const LayoutObject* layoutObject)
477 blink::Image* image = imageForLayoutObject(layoutObject);
478 if (image->isBitmapImage()) {
479 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", InspectorPaintImageEvent::data(layoutObject, *this));
480 // BitmapImage::currentFrameKnownToBeOpaque() conservatively returns true for uncached
481 // frames. To get an accurate answer, we pre-cache the current frame metadata.
482 image->imageForCurrentFrame();
484 return image->currentFrameKnownToBeOpaque();
487 bool ImageResource::isAccessAllowed(SecurityOrigin* securityOrigin)
489 if (response().wasFetchedViaServiceWorker())
490 return response().serviceWorkerResponseType() != WebServiceWorkerResponseTypeOpaque;
491 if (!image()->currentFrameHasSingleSecurityOrigin())
492 return false;
493 if (passesAccessControlCheck(securityOrigin))
494 return true;
495 return !securityOrigin->taintsCanvas(response().url());
498 IntSize ImageResource::svgImageSizeForLayoutObject(const LayoutObject* layoutObject) const
500 IntSize imageSize = m_image->size();
501 if (!layoutObject)
502 return imageSize;
504 ImageForContainerMap::const_iterator it = m_imageForContainerMap->find(layoutObject);
505 if (it == m_imageForContainerMap->end())
506 return imageSize;
508 RefPtr<SVGImageForContainer> imageForContainer = it->value;
509 ASSERT(!imageForContainer->size().isEmpty());
510 return imageForContainer->size();
513 // FIXME: This doesn't take into account the animation timeline so animations will not
514 // restart on page load, nor will two animations in different pages have different timelines.
515 Image* ImageResource::svgImageForLayoutObject(const LayoutObject* layoutObject)
517 if (!layoutObject)
518 return Image::nullImage();
520 ImageForContainerMap::iterator it = m_imageForContainerMap->find(layoutObject);
521 if (it == m_imageForContainerMap->end())
522 return Image::nullImage();
524 RefPtr<SVGImageForContainer> imageForContainer = it->value;
525 ASSERT(!imageForContainer->size().isEmpty());
527 Node* node = layoutObject->node();
528 if (node && isHTMLImageElement(node)) {
529 const AtomicString& urlString = toHTMLImageElement(node)->imageSourceURL();
530 KURL url = node->document().completeURL(urlString);
531 imageForContainer->setURL(url);
534 return imageForContainer.get();
537 bool ImageResource::loadingMultipartContent() const
539 return m_loader && m_loader->loadingMultipartContent();
542 } // namespace blink