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.
25 #include "CachedImage.h"
27 #include "BitmapImage.h"
28 #include "CachedImageClient.h"
29 #include "CachedResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceLoader.h"
33 #include "FrameLoader.h"
34 #include "FrameLoaderClient.h"
35 #include "FrameLoaderTypes.h"
36 #include "FrameView.h"
37 #include "MemoryCache.h"
39 #include "RenderElement.h"
41 #include "SecurityOrigin.h"
43 #include "SharedBuffer.h"
44 #include "SubresourceLoader.h"
45 #include <wtf/CurrentTime.h>
46 #include <wtf/NeverDestroyed.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/Vector.h>
51 #include "SystemMemory.h"
55 #include "PDFDocumentImage.h"
60 CachedImage::CachedImage(const ResourceRequest& resourceRequest, SessionID sessionID)
61 : CachedResource(resourceRequest, ImageResource, sessionID)
63 , m_isManuallyCached(false)
64 , m_shouldPaintBrokenImage(true)
69 CachedImage::CachedImage(Image* image, SessionID sessionID)
70 : CachedResource(ResourceRequest(), ImageResource, sessionID)
72 , m_isManuallyCached(false)
73 , m_shouldPaintBrokenImage(true)
79 CachedImage::CachedImage(const URL& url, Image* image, SessionID sessionID)
80 : CachedResource(ResourceRequest(url), ImageResource, sessionID)
82 , m_isManuallyCached(false)
83 , m_shouldPaintBrokenImage(true)
89 CachedImage::CachedImage(const URL& url, Image* image, CachedImage::CacheBehaviorType type, SessionID sessionID)
90 : CachedResource(ResourceRequest(url), ImageResource, sessionID)
92 , m_isManuallyCached(type == CachedImage::ManuallyCached)
93 , m_shouldPaintBrokenImage(true)
97 if (UNLIKELY(isManuallyCached())) {
98 // Use the incoming URL in the response field. This ensures that code
99 // using the response directly, such as origin checks for security,
100 // actually see something.
101 m_response.setURL(url);
105 CachedImage::~CachedImage()
110 void CachedImage::load(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
112 if (cachedResourceLoader.shouldPerformImageLoad(url()))
113 CachedResource::load(cachedResourceLoader, options);
118 void CachedImage::didAddClient(CachedResourceClient* client)
120 if (m_data && !m_image && !errorOccurred()) {
122 m_image->setData(m_data.copyRef(), true);
125 ASSERT(client->resourceClientType() == CachedImageClient::expectedType());
126 if (m_image && !m_image->isNull())
127 static_cast<CachedImageClient*>(client)->imageChanged(this);
129 CachedResource::didAddClient(client);
132 void CachedImage::didRemoveClient(CachedResourceClient* client)
135 ASSERT(client->resourceClientType() == CachedImageClient::expectedType());
137 m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(client));
140 m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(client));
142 CachedResource::didRemoveClient(client);
145 void CachedImage::switchClientsToRevalidatedResource()
147 ASSERT(is<CachedImage>(resourceToRevalidate()));
148 // Pending container size requests need to be transferred to the revalidated resource.
149 if (!m_pendingContainerSizeRequests.isEmpty()) {
150 // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce().
151 ContainerSizeRequests switchContainerSizeRequests;
152 for (auto& request : m_pendingContainerSizeRequests)
153 switchContainerSizeRequests.set(request.key, request.value);
154 CachedResource::switchClientsToRevalidatedResource();
155 CachedImage& revalidatedCachedImage = downcast<CachedImage>(*resourceToRevalidate());
156 for (auto& request : switchContainerSizeRequests)
157 revalidatedCachedImage.setContainerSizeForRenderer(request.key, request.value.first, request.value.second);
161 CachedResource::switchClientsToRevalidatedResource();
164 void CachedImage::allClientsRemoved()
166 m_pendingContainerSizeRequests.clear();
167 if (m_image && !errorOccurred())
168 m_image->resetAnimation();
171 std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
173 if (deviceScaleFactor >= 3) {
174 static NeverDestroyed<Image*> brokenImageVeryHiRes(Image::loadPlatformResource("missingImage@3x").leakRef());
175 return std::make_pair(brokenImageVeryHiRes, 3);
178 if (deviceScaleFactor >= 2) {
179 static NeverDestroyed<Image*> brokenImageHiRes(Image::loadPlatformResource("missingImage@2x").leakRef());
180 return std::make_pair(brokenImageHiRes, 2);
183 static NeverDestroyed<Image*> brokenImageLoRes(Image::loadPlatformResource("missingImage").leakRef());
184 return std::make_pair(brokenImageLoRes, 1);
187 bool CachedImage::willPaintBrokenImage() const
189 return errorOccurred() && m_shouldPaintBrokenImage;
192 Image* CachedImage::image()
194 if (errorOccurred() && m_shouldPaintBrokenImage) {
195 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
196 // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
197 // when they need the real, deviceScaleFactor-appropriate broken image icon.
198 return brokenImage(1).first;
202 return m_image.get();
204 return Image::nullImage();
207 Image* CachedImage::imageForRenderer(const RenderObject* renderer)
209 if (errorOccurred() && m_shouldPaintBrokenImage) {
210 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
211 // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
212 // when they need the real, deviceScaleFactor-appropriate broken image icon.
213 return brokenImage(1).first;
217 return Image::nullImage();
219 if (m_image->isSVGImage()) {
220 Image* image = m_svgImageCache->imageForRenderer(renderer);
221 if (image != Image::nullImage())
224 return m_image.get();
227 void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const LayoutSize& containerSize, float containerZoom)
229 if (containerSize.isEmpty())
232 ASSERT(containerZoom);
234 m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
238 if (!m_image->isSVGImage()) {
239 m_image->setContainerSize(containerSize);
243 m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
246 bool CachedImage::usesImageContainerSize() const
249 return m_image->usesContainerSize();
254 bool CachedImage::imageHasRelativeWidth() const
257 return m_image->hasRelativeWidth();
262 bool CachedImage::imageHasRelativeHeight() const
265 return m_image->hasRelativeHeight();
270 LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType)
275 LayoutSize imageSize;
277 if (is<BitmapImage>(*m_image) && renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)
278 imageSize = LayoutSize(downcast<BitmapImage>(*m_image).sizeRespectingOrientation());
279 else if (is<SVGImage>(*m_image) && sizeType == UsedSize)
280 imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer));
282 imageSize = LayoutSize(m_image->size());
284 if (multiplier == 1.0f)
287 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
288 float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
289 float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
290 LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
291 imageSize.scale(widthScale, heightScale);
292 imageSize.clampToMinimumSize(minimumSize);
293 ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
297 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
300 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
303 void CachedImage::notifyObservers(const IntRect* changeRect)
305 CachedResourceClientWalker<CachedImageClient> w(m_clients);
306 while (CachedImageClient* c = w.next())
307 c->imageChanged(this, changeRect);
310 void CachedImage::checkShouldPaintBrokenImage()
312 if (!m_loader || m_loader->reachedTerminalState())
315 m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url());
318 void CachedImage::clear()
320 destroyDecodedData();
322 m_pendingContainerSizeRequests.clear();
326 inline void CachedImage::createImage()
328 // Create the image if it doesn't yet exist.
332 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
333 else if (m_response.mimeType() == "application/pdf")
334 m_image = PDFDocumentImage::create(this);
336 else if (m_response.mimeType() == "image/svg+xml") {
337 RefPtr<SVGImage> svgImage = SVGImage::create(*this, url());
338 m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get());
339 m_image = svgImage.release();
341 m_image = BitmapImage::create(this);
342 downcast<BitmapImage>(*m_image).setAllowSubsampling(m_loader && m_loader->frameLoader()->frame().settings().imageSubsamplingEnabled());
346 // Send queued container size requests.
347 if (m_image->usesContainerSize()) {
348 for (auto& request : m_pendingContainerSizeRequests)
349 setContainerSizeForRenderer(request.key, request.value.first, request.value.second);
351 m_pendingContainerSizeRequests.clear();
355 inline void CachedImage::clearImage()
357 // If our Image has an observer, it's always us so we need to clear the back pointer
358 // before dropping our reference.
360 m_image->setImageObserver(nullptr);
364 void CachedImage::addIncrementalDataBuffer(SharedBuffer& data)
370 // Have the image update its data from its internal buffer.
371 // It will not do anything now, but will delay decoding until
372 // queried for info (like size or specific image frames).
373 bool sizeAvailable = m_image->setData(&data, false);
377 if (m_image->isNull()) {
378 // Image decoding failed. Either we need more image data or the image data is malformed.
379 error(errorOccurred() ? status() : DecodeError);
381 MemoryCache::singleton().remove(*this);
385 // Tell our observers to try to draw.
386 // Each chunk from the network causes observers to repaint, which will force that chunk to decode.
387 // It would be nice to only redraw the decoded band of the image, but with the current design
388 // (decoding delayed until painting) that seems hard.
391 setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
394 void CachedImage::addDataBuffer(SharedBuffer& data)
396 ASSERT(dataBufferingPolicy() == BufferData);
397 addIncrementalDataBuffer(data);
398 CachedResource::addDataBuffer(data);
401 void CachedImage::addData(const char* data, unsigned length)
403 ASSERT(dataBufferingPolicy() == DoNotBufferData);
404 addIncrementalDataBuffer(SharedBuffer::create(data, length));
405 CachedResource::addData(data, length);
408 void CachedImage::finishLoading(SharedBuffer* data)
411 if (!m_image && data)
415 m_image->setData(data, true);
417 if (!m_image || m_image->isNull()) {
418 // Image decoding failed; the image data is malformed.
419 error(errorOccurred() ? status() : DecodeError);
421 MemoryCache::singleton().remove(*this);
427 setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
428 CachedResource::finishLoading(data);
431 void CachedImage::didReplaceSharedBufferContents()
434 // Let the Image know that the SharedBuffer has been rejigged, so it can let go of any references to the heap-allocated resource buffer.
435 // FIXME(rdar://problem/24275617): It would be better if we could somehow tell the Image's decoder to swap in the new contents without destroying anything.
436 m_image->destroyDecodedData(true);
438 CachedResource::didReplaceSharedBufferContents();
441 void CachedImage::error(CachedResource::Status status)
443 checkShouldPaintBrokenImage();
445 CachedResource::error(status);
449 void CachedImage::responseReceived(const ResourceResponse& response)
451 if (!m_response.isNull())
453 CachedResource::responseReceived(response);
456 void CachedImage::destroyDecodedData()
458 bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
459 if (canDeleteImage && !isLoading() && !hasClients()) {
462 } else if (m_image && !errorOccurred())
463 m_image->destroyDecodedData();
466 void CachedImage::decodedSizeChanged(const Image* image, int delta)
468 if (!image || image != m_image)
471 setDecodedSize(decodedSize() + delta);
474 void CachedImage::didDraw(const Image* image)
476 if (!image || image != m_image)
479 double timeStamp = FrameView::currentPaintTimeStamp();
480 if (!timeStamp) // If didDraw is called outside of a Frame paint.
481 timeStamp = monotonicallyIncreasingTime();
483 CachedResource::didAccessDecodedData(timeStamp);
486 void CachedImage::animationAdvanced(const Image* image)
488 if (!image || image != m_image)
490 CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients);
491 while (CachedImageClient* client = clientWalker.next())
492 client->newImageAnimationFrameAvailable(*this);
495 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
497 if (!image || image != m_image)
499 notifyObservers(&rect);
502 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
504 Image* image = imageForRenderer(renderer);
505 return image->currentFrameKnownToBeOpaque();
508 bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
510 if (!image()->hasSingleSecurityOrigin())
512 if (passesAccessControlCheck(*securityOrigin))
514 return !securityOrigin->taintsCanvas(responseForSameOriginPolicyChecks().url());
517 CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const
519 if (UNLIKELY(isManuallyCached())) {
520 // Do not revalidate manually cached images. This mechanism is used as a
521 // way to efficiently share an image from the client to content and
522 // the URL for that image may not represent a resource that can be
523 // retrieved by standard means. If the manual caching SPI is used, it is
524 // incumbent on the client to only use valid resources.
525 return RevalidationDecision::No;
527 return CachedResource::makeRevalidationDecision(cachePolicy);
530 bool CachedImage::areAllClientsInPageCache() const
532 for (auto& entry : m_clients) {
533 auto& client = *entry.key;
534 if (client.resourceClientType() != CachedImageClient::expectedType())
536 if (!static_cast<CachedImageClient&>(client).inPageCache())
542 } // namespace WebCore