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 "FrameLoaderClient.h" 33#include "FrameLoaderTypes.h" 34#include "FrameView.h" 35#include "MemoryCache.h" 36#include "Page.h" 37#include "RenderObject.h" 38#include "ResourceBuffer.h" 39#include "Settings.h" 40#include "SubresourceLoader.h" 41#include <wtf/CurrentTime.h> 42#include <wtf/StdLibExtras.h> 43#include <wtf/Vector.h> 44 45#if USE(CG) 46#include "PDFDocumentImage.h" 47#endif 48 49#if ENABLE(SVG) 50#include "SVGImage.h" 51#endif 52 53using std::max; 54 55namespace WebCore { 56 57CachedImage::CachedImage(const ResourceRequest& resourceRequest) 58 : CachedResource(resourceRequest, ImageResource) 59 , m_image(0) 60 , m_shouldPaintBrokenImage(true) 61{ 62 setStatus(Unknown); 63} 64 65CachedImage::CachedImage(Image* image) 66 : CachedResource(ResourceRequest(), ImageResource) 67 , m_image(image) 68 , m_shouldPaintBrokenImage(true) 69{ 70 setStatus(Cached); 71 setLoading(false); 72} 73 74CachedImage::~CachedImage() 75{ 76 clearImage(); 77} 78 79void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options) 80{ 81 if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages()) 82 CachedResource::load(cachedResourceLoader, options); 83 else 84 setLoading(false); 85} 86 87void CachedImage::didAddClient(CachedResourceClient* c) 88{ 89 if (m_data && !m_image && !errorOccurred()) { 90 createImage(); 91 m_image->setData(m_data->sharedBuffer(), true); 92 } 93 94 ASSERT(c->resourceClientType() == CachedImageClient::expectedType()); 95 if (m_image && !m_image->isNull()) 96 static_cast<CachedImageClient*>(c)->imageChanged(this); 97 98 CachedResource::didAddClient(c); 99} 100 101void CachedImage::didRemoveClient(CachedResourceClient* c) 102{ 103 ASSERT(c); 104 ASSERT(c->resourceClientType() == CachedImageClient::expectedType()); 105 106 m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(c)); 107#if ENABLE(SVG) 108 if (m_svgImageCache) 109 m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c)); 110#endif 111 112 CachedResource::didRemoveClient(c); 113} 114 115void CachedImage::switchClientsToRevalidatedResource() 116{ 117 ASSERT(resourceToRevalidate()); 118 ASSERT(resourceToRevalidate()->isImage()); 119 // Pending container size requests need to be transferred to the revalidated resource. 120 if (!m_pendingContainerSizeRequests.isEmpty()) { 121 // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce(). 122 ContainerSizeRequests switchContainerSizeRequests; 123 for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) 124 switchContainerSizeRequests.set(it->key, it->value); 125 CachedResource::switchClientsToRevalidatedResource(); 126 CachedImage* revalidatedCachedImage = static_cast<CachedImage*>(resourceToRevalidate()); 127 for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it) 128 revalidatedCachedImage->setContainerSizeForRenderer(it->key, it->value.first, it->value.second); 129 return; 130 } 131 132 CachedResource::switchClientsToRevalidatedResource(); 133} 134 135void CachedImage::allClientsRemoved() 136{ 137 m_pendingContainerSizeRequests.clear(); 138 if (m_image && !errorOccurred()) 139 m_image->resetAnimation(); 140} 141 142pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const 143{ 144 if (deviceScaleFactor >= 2) { 145 DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef())); 146 return std::make_pair(brokenImageHiRes, 2); 147 } 148 149 DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef())); 150 return std::make_pair(brokenImageLoRes, 1); 151} 152 153bool CachedImage::willPaintBrokenImage() const 154{ 155 return errorOccurred() && m_shouldPaintBrokenImage; 156} 157 158Image* CachedImage::image() 159{ 160 ASSERT(!isPurgeable()); 161 162 if (errorOccurred() && m_shouldPaintBrokenImage) { 163 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate 164 // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 165 // when they need the real, deviceScaleFactor-appropriate broken image icon. 166 return brokenImage(1).first; 167 } 168 169 if (m_image) 170 return m_image.get(); 171 172 return Image::nullImage(); 173} 174 175Image* CachedImage::imageForRenderer(const RenderObject* renderer) 176{ 177 ASSERT(!isPurgeable()); 178 179 if (errorOccurred() && m_shouldPaintBrokenImage) { 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 CachedImage::brokenImage() 182 // when they need the real, deviceScaleFactor-appropriate broken image icon. 183 return brokenImage(1).first; 184 } 185 186 if (!m_image) 187 return Image::nullImage(); 188 189#if ENABLE(SVG) 190 if (m_image->isSVGImage()) { 191 Image* image = m_svgImageCache->imageForRenderer(renderer); 192 if (image != Image::nullImage()) 193 return image; 194 } 195#else 196 UNUSED_PARAM(renderer); 197#endif 198 return m_image.get(); 199} 200 201void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const IntSize& containerSize, float containerZoom) 202{ 203 if (containerSize.isEmpty()) 204 return; 205 ASSERT(renderer); 206 ASSERT(containerZoom); 207 if (!m_image) { 208 m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom)); 209 return; 210 } 211#if ENABLE(SVG) 212 if (!m_image->isSVGImage()) { 213 m_image->setContainerSize(containerSize); 214 return; 215 } 216 217 m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom); 218#else 219 UNUSED_PARAM(containerZoom); 220 m_image->setContainerSize(containerSize); 221#endif 222} 223 224bool CachedImage::usesImageContainerSize() const 225{ 226 if (m_image) 227 return m_image->usesContainerSize(); 228 229 return false; 230} 231 232bool CachedImage::imageHasRelativeWidth() const 233{ 234 if (m_image) 235 return m_image->hasRelativeWidth(); 236 237 return false; 238} 239 240bool CachedImage::imageHasRelativeHeight() const 241{ 242 if (m_image) 243 return m_image->hasRelativeHeight(); 244 245 return false; 246} 247 248LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier) 249{ 250 ASSERT(!isPurgeable()); 251 252 if (!m_image) 253 return IntSize(); 254 255 LayoutSize imageSize; 256 257 if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)) 258 imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation(); 259#if ENABLE(SVG) 260 else if (m_image->isSVGImage()) { 261 imageSize = m_svgImageCache->imageSizeForRenderer(renderer); 262 } 263#endif 264 else 265 imageSize = m_image->size(); 266 267 if (multiplier == 1.0f) 268 return imageSize; 269 270 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. 271 float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; 272 float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; 273 LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); 274 imageSize.scale(widthScale, heightScale); 275 imageSize.clampToMinimumSize(minimumSize); 276 ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); 277 return imageSize; 278} 279 280void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) 281{ 282 if (m_image) 283 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); 284} 285 286void CachedImage::notifyObservers(const IntRect* changeRect) 287{ 288 CachedResourceClientWalker<CachedImageClient> w(m_clients); 289 while (CachedImageClient* c = w.next()) 290 c->imageChanged(this, changeRect); 291} 292 293void CachedImage::checkShouldPaintBrokenImage() 294{ 295 if (!m_loader || m_loader->reachedTerminalState()) 296 return; 297 298 m_shouldPaintBrokenImage = m_loader->frameLoader()->client()->shouldPaintBrokenImage(m_resourceRequest.url()); 299} 300 301void CachedImage::clear() 302{ 303 destroyDecodedData(); 304 clearImage(); 305 m_pendingContainerSizeRequests.clear(); 306 setEncodedSize(0); 307} 308 309inline void CachedImage::createImage() 310{ 311 // Create the image if it doesn't yet exist. 312 if (m_image) 313 return; 314#if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS) 315 else if (m_response.mimeType() == "application/pdf") 316 m_image = PDFDocumentImage::create(this); 317#endif 318#if ENABLE(SVG) 319 else if (m_response.mimeType() == "image/svg+xml") { 320 RefPtr<SVGImage> svgImage = SVGImage::create(this); 321 m_svgImageCache = SVGImageCache::create(svgImage.get()); 322 m_image = svgImage.release(); 323 } 324#endif 325 else 326 m_image = BitmapImage::create(this); 327 328 if (m_image) { 329 // Send queued container size requests. 330 if (m_image->usesContainerSize()) { 331 for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) 332 setContainerSizeForRenderer(it->key, it->value.first, it->value.second); 333 } 334 m_pendingContainerSizeRequests.clear(); 335 } 336} 337 338inline void CachedImage::clearImage() 339{ 340 // If our Image has an observer, it's always us so we need to clear the back pointer 341 // before dropping our reference. 342 if (m_image) 343 m_image->setImageObserver(0); 344 m_image.clear(); 345} 346 347bool CachedImage::canBeDrawn() const 348{ 349 if (!m_image || m_image->isNull()) 350 return false; 351 352 if (!m_loader || m_loader->reachedTerminalState()) 353 return true; 354 355 Settings* settings = m_loader->frameLoader()->frame()->settings(); 356 if (!settings) 357 return true; 358 359 size_t estimatedDecodedImageSize = m_image->width() * m_image->height() * 4; // no overflow check 360 return estimatedDecodedImageSize <= settings->maximumDecodedImageSize(); 361} 362 363void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data) 364{ 365 m_data = data; 366 if (!data) 367 return; 368 369 createImage(); 370 371 // Have the image update its data from its internal buffer. 372 // It will not do anything now, but will delay decoding until 373 // queried for info (like size or specific image frames). 374 bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false); 375 if (!sizeAvailable) 376 return; 377 378 if (!canBeDrawn()) { 379 // There's no image to draw or its decoded size is bigger than the maximum allowed. 380 error(errorOccurred() ? status() : DecodeError); 381 if (inCache()) 382 memoryCache()->remove(this); 383 return; 384 } 385 386 // Go ahead and tell our observers to try to draw. 387 // Each chunk from the network causes observers to repaint, which will 388 // force that chunk to decode. 389 // It would be nice to only redraw the decoded band of the image, but with the current design 390 // (decoding delayed until painting) that seems hard. 391 notifyObservers(); 392 393 setEncodedSize(m_image->data() ? m_image->data()->size() : 0); 394} 395 396void CachedImage::addDataBuffer(ResourceBuffer* data) 397{ 398 ASSERT(m_options.dataBufferingPolicy == BufferData); 399 addIncrementalDataBuffer(data); 400} 401 402void CachedImage::addData(const char* data, unsigned length) 403{ 404 ASSERT(m_options.dataBufferingPolicy == DoNotBufferData); 405 addIncrementalDataBuffer(ResourceBuffer::create(data, length).get()); 406} 407 408void CachedImage::finishLoading(ResourceBuffer* data) 409{ 410 m_data = data; 411 if (!m_image && data) 412 createImage(); 413 414 if (m_image) 415 m_image->setData(m_data->sharedBuffer(), true); 416 417 if (!canBeDrawn()) { 418 // There's no image to draw or its decoded size is bigger than the maximum allowed. 419 error(errorOccurred() ? status() : DecodeError); 420 if (inCache()) 421 memoryCache()->remove(this); 422 return; 423 } 424 425 notifyObservers(); 426 if (m_image) 427 setEncodedSize(m_image->data() ? m_image->data()->size() : 0); 428 CachedResource::finishLoading(data); 429} 430 431void CachedImage::error(CachedResource::Status status) 432{ 433 checkShouldPaintBrokenImage(); 434 clear(); 435 CachedResource::error(status); 436 notifyObservers(); 437} 438 439void CachedImage::responseReceived(const ResourceResponse& response) 440{ 441 if (!m_response.isNull()) 442 clear(); 443 CachedResource::responseReceived(response); 444} 445 446void CachedImage::destroyDecodedData() 447{ 448 bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); 449 if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) { 450 // Image refs the data buffer so we should not make it purgeable while the image is alive. 451 // Invoking addClient() will reconstruct the image object. 452 m_image = 0; 453 setDecodedSize(0); 454 if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) 455 makePurgeable(true); 456 } else if (m_image && !errorOccurred()) 457 m_image->destroyDecodedData(); 458} 459 460void CachedImage::decodedSizeChanged(const Image* image, int delta) 461{ 462 if (!image || image != m_image) 463 return; 464 465 setDecodedSize(decodedSize() + delta); 466} 467 468void CachedImage::didDraw(const Image* image) 469{ 470 if (!image || image != m_image) 471 return; 472 473 double timeStamp = FrameView::currentPaintTimeStamp(); 474 if (!timeStamp) // If didDraw is called outside of a Frame paint. 475 timeStamp = currentTime(); 476 477 CachedResource::didAccessDecodedData(timeStamp); 478} 479 480bool CachedImage::shouldPauseAnimation(const Image* image) 481{ 482 if (!image || image != m_image) 483 return false; 484 485 CachedResourceClientWalker<CachedImageClient> w(m_clients); 486 while (CachedImageClient* c = w.next()) { 487 if (c->willRenderImage(this)) 488 return false; 489 } 490 491 return true; 492} 493 494void CachedImage::animationAdvanced(const Image* image) 495{ 496 if (!image || image != m_image) 497 return; 498 notifyObservers(); 499} 500 501void CachedImage::changedInRect(const Image* image, const IntRect& rect) 502{ 503 if (!image || image != m_image) 504 return; 505 notifyObservers(&rect); 506} 507 508void CachedImage::resumeAnimatingImagesForLoader(CachedResourceLoader* loader) 509{ 510 const CachedResourceLoader::DocumentResourceMap& resources = loader->allCachedResources(); 511 512 for (CachedResourceLoader::DocumentResourceMap::const_iterator it = resources.begin(), end = resources.end(); it != end; ++it) { 513 const CachedResourceHandle<CachedResource>& resource = it->value; 514 if (!resource || !resource->isImage()) 515 continue; 516 CachedImage* cachedImage = static_cast<CachedImage*>(resource.get()); 517 if (!cachedImage->hasImage()) 518 continue; 519 Image* image = cachedImage->image(); 520 if (!image->isBitmapImage()) 521 continue; 522 BitmapImage* bitmapImage = static_cast<BitmapImage*>(image); 523 if (!bitmapImage->canAnimate()) 524 continue; 525 cachedImage->animationAdvanced(bitmapImage); 526 } 527} 528 529bool CachedImage::currentFrameKnownToBeOpaque(const RenderObject* renderer) 530{ 531 Image* image = imageForRenderer(renderer); 532 if (image->isBitmapImage()) 533 image->nativeImageForCurrentFrame(); // force decode 534 return image->currentFrameKnownToBeOpaque(); 535} 536 537} // namespace WebCore 538