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. 7 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. 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 24#include "config.h" 25#include "CachedImage.h" 26 27#include "BitmapImage.h" 28#include "CachedImageClient.h" 29#include "CachedResourceClient.h" 30#include "CachedResourceClientWalker.h" 31#include "CachedResourceLoader.h" 32#include "Frame.h" 33#include "FrameLoader.h" 34#include "FrameLoaderClient.h" 35#include "FrameLoaderTypes.h" 36#include "FrameView.h" 37#include "MemoryCache.h" 38#include "Page.h" 39#include "RenderElement.h" 40#include "ResourceBuffer.h" 41#include "SVGImage.h" 42#include "SecurityOrigin.h" 43#include "Settings.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> 49 50#if PLATFORM(IOS) 51#include "SystemMemory.h" 52#endif 53 54#if USE(CG) 55#include "PDFDocumentImage.h" 56#endif 57 58#if ENABLE(DISK_IMAGE_CACHE) 59#include "DiskImageCacheIOS.h" 60#endif 61 62namespace WebCore { 63 64CachedImage::CachedImage(const ResourceRequest& resourceRequest, SessionID sessionID) 65 : CachedResource(resourceRequest, ImageResource, sessionID) 66 , m_image(0) 67 , m_isManuallyCached(false) 68 , m_shouldPaintBrokenImage(true) 69{ 70 setStatus(Unknown); 71} 72 73CachedImage::CachedImage(Image* image, SessionID sessionID) 74 : CachedResource(ResourceRequest(), ImageResource, sessionID) 75 , m_image(image) 76 , m_isManuallyCached(false) 77 , m_shouldPaintBrokenImage(true) 78{ 79 setStatus(Cached); 80 setLoading(false); 81} 82 83CachedImage::CachedImage(const URL& url, Image* image, SessionID sessionID) 84 : CachedResource(ResourceRequest(url), ImageResource, sessionID) 85 , m_image(image) 86 , m_isManuallyCached(false) 87 , m_shouldPaintBrokenImage(true) 88{ 89 setStatus(Cached); 90 setLoading(false); 91} 92 93CachedImage::CachedImage(const URL& url, Image* image, CachedImage::CacheBehaviorType type, SessionID sessionID) 94 : CachedResource(ResourceRequest(url), ImageResource, sessionID) 95 , m_image(image) 96 , m_isManuallyCached(type == CachedImage::ManuallyCached) 97 , m_shouldPaintBrokenImage(true) 98{ 99 setStatus(Cached); 100 setLoading(false); 101 if (UNLIKELY(isManuallyCached())) { 102 // Use the incoming URL in the response field. This ensures that code 103 // using the response directly, such as origin checks for security, 104 // actually see something. 105 m_response.setURL(url); 106 } 107} 108 109CachedImage::~CachedImage() 110{ 111 clearImage(); 112} 113 114void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options) 115{ 116 if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages()) 117 CachedResource::load(cachedResourceLoader, options); 118 else 119 setLoading(false); 120} 121 122void CachedImage::didAddClient(CachedResourceClient* c) 123{ 124 if (m_data && !m_image && !errorOccurred()) { 125 createImage(); 126 m_image->setData(m_data->sharedBuffer(), true); 127 } 128 129 ASSERT(c->resourceClientType() == CachedImageClient::expectedType()); 130 if (m_image && !m_image->isNull()) 131 static_cast<CachedImageClient*>(c)->imageChanged(this); 132 133 CachedResource::didAddClient(c); 134} 135 136void CachedImage::didRemoveClient(CachedResourceClient* c) 137{ 138 ASSERT(c); 139 ASSERT(c->resourceClientType() == CachedImageClient::expectedType()); 140 141 m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(c)); 142 143 if (m_svgImageCache) 144 m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c)); 145 146 CachedResource::didRemoveClient(c); 147} 148 149void CachedImage::switchClientsToRevalidatedResource() 150{ 151 ASSERT(resourceToRevalidate()); 152 ASSERT(resourceToRevalidate()->isImage()); 153 // Pending container size requests need to be transferred to the revalidated resource. 154 if (!m_pendingContainerSizeRequests.isEmpty()) { 155 // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce(). 156 ContainerSizeRequests switchContainerSizeRequests; 157 for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) 158 switchContainerSizeRequests.set(it->key, it->value); 159 CachedResource::switchClientsToRevalidatedResource(); 160 CachedImage* revalidatedCachedImage = toCachedImage(resourceToRevalidate()); 161 for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it) 162 revalidatedCachedImage->setContainerSizeForRenderer(it->key, it->value.first, it->value.second); 163 return; 164 } 165 166 CachedResource::switchClientsToRevalidatedResource(); 167} 168 169void CachedImage::allClientsRemoved() 170{ 171 m_pendingContainerSizeRequests.clear(); 172 if (m_image && !errorOccurred()) 173 m_image->resetAnimation(); 174} 175 176std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const 177{ 178 if (deviceScaleFactor >= 2) { 179 static NeverDestroyed<Image*> brokenImageHiRes(Image::loadPlatformResource("missingImage@2x").leakRef()); 180 return std::make_pair(brokenImageHiRes, 2); 181 } 182 183 static NeverDestroyed<Image*> brokenImageLoRes(Image::loadPlatformResource("missingImage").leakRef()); 184 return std::make_pair(brokenImageLoRes, 1); 185} 186 187bool CachedImage::willPaintBrokenImage() const 188{ 189 return errorOccurred() && m_shouldPaintBrokenImage; 190} 191 192Image* CachedImage::image() 193{ 194 ASSERT(!isPurgeable()); 195 196 if (errorOccurred() && m_shouldPaintBrokenImage) { 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 CachedImage::brokenImage() 199 // when they need the real, deviceScaleFactor-appropriate broken image icon. 200 return brokenImage(1).first; 201 } 202 203 if (m_image) 204 return m_image.get(); 205 206 return Image::nullImage(); 207} 208 209Image* CachedImage::imageForRenderer(const RenderObject* renderer) 210{ 211 ASSERT(!isPurgeable()); 212 213 if (errorOccurred() && m_shouldPaintBrokenImage) { 214 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate 215 // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 216 // when they need the real, deviceScaleFactor-appropriate broken image icon. 217 return brokenImage(1).first; 218 } 219 220 if (!m_image) 221 return Image::nullImage(); 222 223 if (m_image->isSVGImage()) { 224 Image* image = m_svgImageCache->imageForRenderer(renderer); 225 if (image != Image::nullImage()) 226 return image; 227 } 228 return m_image.get(); 229} 230 231void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const LayoutSize& containerSize, float containerZoom) 232{ 233 if (containerSize.isEmpty()) 234 return; 235 ASSERT(renderer); 236 ASSERT(containerZoom); 237 if (!m_image) { 238 m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom)); 239 return; 240 } 241 242 if (!m_image->isSVGImage()) { 243 m_image->setContainerSize(containerSize); 244 return; 245 } 246 247 m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom); 248} 249 250bool CachedImage::usesImageContainerSize() const 251{ 252 if (m_image) 253 return m_image->usesContainerSize(); 254 255 return false; 256} 257 258bool CachedImage::imageHasRelativeWidth() const 259{ 260 if (m_image) 261 return m_image->hasRelativeWidth(); 262 263 return false; 264} 265 266bool CachedImage::imageHasRelativeHeight() const 267{ 268 if (m_image) 269 return m_image->hasRelativeHeight(); 270 271 return false; 272} 273 274LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType) 275{ 276 ASSERT(!isPurgeable()); 277 278 if (!m_image) 279 return LayoutSize(); 280 281 LayoutSize imageSize(m_image->size()); 282 283#if ENABLE(CSS_IMAGE_ORIENTATION) 284 if (renderer && m_image->isBitmapImage()) { 285 ImageOrientationDescription orientationDescription(renderer->shouldRespectImageOrientation(), renderer->style().imageOrientation()); 286 if (orientationDescription.respectImageOrientation() == RespectImageOrientation) 287 imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation(orientationDescription)); 288 } 289#else 290 if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)) 291#if !PLATFORM(IOS) 292 imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); 293#else 294 { 295 // On iOS, the image may have been subsampled to accommodate our size restrictions. However 296 // we should tell the renderer what the original size was. 297 imageSize = LayoutSize(toBitmapImage(m_image.get())->originalSizeRespectingOrientation()); 298 } else if (m_image->isBitmapImage()) 299 imageSize = LayoutSize(toBitmapImage(m_image.get())->originalSize()); 300#endif // !PLATFORM(IOS) 301#endif // ENABLE(CSS_IMAGE_ORIENTATION) 302 303 else if (m_image->isSVGImage() && sizeType == UsedSize) { 304 imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer)); 305 } 306 307 if (multiplier == 1.0f) 308 return imageSize; 309 310 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. 311 float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; 312 float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; 313 LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); 314 imageSize.scale(widthScale, heightScale); 315 imageSize.clampToMinimumSize(minimumSize); 316 ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); 317 return imageSize; 318} 319 320void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) 321{ 322 if (m_image) 323 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); 324} 325 326void CachedImage::notifyObservers(const IntRect* changeRect) 327{ 328 CachedResourceClientWalker<CachedImageClient> w(m_clients); 329 while (CachedImageClient* c = w.next()) 330 c->imageChanged(this, changeRect); 331} 332 333void CachedImage::checkShouldPaintBrokenImage() 334{ 335 if (!m_loader || m_loader->reachedTerminalState()) 336 return; 337 338 m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(m_resourceRequest.url()); 339} 340 341void CachedImage::clear() 342{ 343 destroyDecodedData(); 344 clearImage(); 345 m_pendingContainerSizeRequests.clear(); 346 setEncodedSize(0); 347} 348 349inline void CachedImage::createImage() 350{ 351 // Create the image if it doesn't yet exist. 352 if (m_image) 353 return; 354#if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS) 355 else if (m_response.mimeType() == "application/pdf") 356 m_image = PDFDocumentImage::create(this); 357#endif 358 else if (m_response.mimeType() == "image/svg+xml") { 359 RefPtr<SVGImage> svgImage = SVGImage::create(this); 360 m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get()); 361 m_image = svgImage.release(); 362 } else 363 m_image = BitmapImage::create(this); 364 365 if (m_image) { 366 // Send queued container size requests. 367 if (m_image->usesContainerSize()) { 368 for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) 369 setContainerSizeForRenderer(it->key, it->value.first, it->value.second); 370 } 371 m_pendingContainerSizeRequests.clear(); 372 } 373} 374 375inline void CachedImage::clearImage() 376{ 377 // If our Image has an observer, it's always us so we need to clear the back pointer 378 // before dropping our reference. 379 if (m_image) 380 m_image->setImageObserver(0); 381 m_image.clear(); 382} 383 384void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data) 385{ 386 m_data = data; 387 if (!data) 388 return; 389 390 createImage(); 391 392 // Have the image update its data from its internal buffer. 393 // It will not do anything now, but will delay decoding until 394 // queried for info (like size or specific image frames). 395 bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false); 396 if (!sizeAvailable) 397 return; 398 399 if (m_image->isNull()) { 400 // Image decoding failed. Either we need more image data or the image data is malformed. 401 error(errorOccurred() ? status() : DecodeError); 402 if (inCache()) 403 memoryCache()->remove(this); 404 return; 405 } 406 407 // Go ahead and tell our observers to try to draw. 408 // Each chunk from the network causes observers to repaint, which will 409 // force that chunk to decode. 410 // It would be nice to only redraw the decoded band of the image, but with the current design 411 // (decoding delayed until painting) that seems hard. 412 notifyObservers(); 413 414 setEncodedSize(m_image->data() ? m_image->data()->size() : 0); 415} 416 417void CachedImage::addDataBuffer(ResourceBuffer* data) 418{ 419 ASSERT(m_options.dataBufferingPolicy() == BufferData); 420 addIncrementalDataBuffer(data); 421} 422 423void CachedImage::addData(const char* data, unsigned length) 424{ 425 ASSERT(m_options.dataBufferingPolicy() == DoNotBufferData); 426 addIncrementalDataBuffer(ResourceBuffer::create(data, length).get()); 427} 428 429void CachedImage::finishLoading(ResourceBuffer* data) 430{ 431 m_data = data; 432 if (!m_image && data) 433 createImage(); 434 435 if (m_image) 436 m_image->setData(m_data->sharedBuffer(), true); 437 438 if (!m_image || m_image->isNull()) { 439 // Image decoding failed; the image data is malformed. 440 error(errorOccurred() ? status() : DecodeError); 441 if (inCache()) 442 memoryCache()->remove(this); 443 return; 444 } 445 446 notifyObservers(); 447 if (m_image) 448 setEncodedSize(m_image->data() ? m_image->data()->size() : 0); 449 CachedResource::finishLoading(data); 450} 451 452void CachedImage::error(CachedResource::Status status) 453{ 454 checkShouldPaintBrokenImage(); 455 clear(); 456 CachedResource::error(status); 457 notifyObservers(); 458} 459 460void CachedImage::responseReceived(const ResourceResponse& response) 461{ 462 if (!m_response.isNull()) 463 clear(); 464 CachedResource::responseReceived(response); 465} 466 467void CachedImage::destroyDecodedData() 468{ 469 bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); 470 if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) { 471 // Image refs the data buffer so we should not make it purgeable while the image is alive. 472 // Invoking addClient() will reconstruct the image object. 473 m_image = 0; 474 setDecodedSize(0); 475 if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) 476 makePurgeable(true); 477 } else if (m_image && !errorOccurred()) 478 m_image->destroyDecodedData(); 479} 480 481void CachedImage::decodedSizeChanged(const Image* image, int delta) 482{ 483 if (!image || image != m_image) 484 return; 485 486 setDecodedSize(decodedSize() + delta); 487} 488 489void CachedImage::didDraw(const Image* image) 490{ 491 if (!image || image != m_image) 492 return; 493 494 double timeStamp = FrameView::currentPaintTimeStamp(); 495 if (!timeStamp) // If didDraw is called outside of a Frame paint. 496 timeStamp = monotonicallyIncreasingTime(); 497 498 CachedResource::didAccessDecodedData(timeStamp); 499} 500 501void CachedImage::animationAdvanced(const Image* image) 502{ 503 if (!image || image != m_image) 504 return; 505 CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); 506 while (CachedImageClient* client = clientWalker.next()) 507 client->newImageAnimationFrameAvailable(*this); 508} 509 510void CachedImage::changedInRect(const Image* image, const IntRect& rect) 511{ 512 if (!image || image != m_image) 513 return; 514 notifyObservers(&rect); 515} 516 517bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer) 518{ 519 Image* image = imageForRenderer(renderer); 520 if (image->isBitmapImage()) 521 image->nativeImageForCurrentFrame(); // force decode 522 return image->currentFrameKnownToBeOpaque(); 523} 524 525#if ENABLE(DISK_IMAGE_CACHE) 526bool CachedImage::canUseDiskImageCache() const 527{ 528 if (isLoading() || errorOccurred()) 529 return false; 530 531 if (!m_data) 532 return false; 533 534 if (isPurgeable()) 535 return false; 536 537 if (m_data->size() < diskImageCache().minimumImageSize()) 538 return false; 539 540 // "Cache-Control: no-store" resources may be marked as such because they may 541 // contain sensitive information. We should not write these resources to disk. 542 if (m_response.cacheControlContainsNoStore()) 543 return false; 544 545 // Testing shows that PDF images did not work when memory mapped. 546 // However, SVG images and Bitmap images were fine. See: 547 // <rdar://problem/8591834> Disk Image Cache should support PDF Images 548 if (m_response.mimeType() == "application/pdf") 549 return false; 550 551 return true; 552} 553 554void CachedImage::useDiskImageCache() 555{ 556 ASSERT(canUseDiskImageCache()); 557 ASSERT(!isUsingDiskImageCache()); 558 m_data->sharedBuffer()->allowToBeMemoryMapped(); 559} 560#endif 561 562bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin) 563{ 564 if (!image()->hasSingleSecurityOrigin()) 565 return false; 566 if (passesAccessControlCheck(securityOrigin)) 567 return true; 568 return !securityOrigin->taintsCanvas(response().url()); 569} 570 571bool CachedImage::mustRevalidateDueToCacheHeaders(CachePolicy policy) const 572{ 573 if (UNLIKELY(isManuallyCached())) { 574 // Do not revalidate manually cached images. This mechanism is used as a 575 // way to efficiently share an image from the client to content and 576 // the URL for that image may not represent a resource that can be 577 // retrieved by standard means. If the manual caching SPI is used, it is 578 // incumbent on the client to only use valid resources. 579 return false; 580 } 581 return CachedResource::mustRevalidateDueToCacheHeaders(policy); 582} 583 584} // namespace WebCore 585