1 /* 2 Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de) 3 Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org) 5 Copyright (C) 2006 Samuel Weinig (sam.weinig (at) 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 "MemoryCache.h" 29 #include "CachedResourceClient.h" 30 #include "CachedResourceClientWalker.h" 31 #include "CachedResourceLoader.h" 32 #include "CachedResourceRequest.h" 33 #include "Frame.h" 34 #include "FrameLoaderClient.h" 35 #include "FrameLoaderTypes.h" 36 #include "FrameView.h" 37 #include "Settings.h" 38 #include "SharedBuffer.h" 39 #include <wtf/CurrentTime.h> 40 #include <wtf/StdLibExtras.h> 41 #include <wtf/Vector.h> 42 43 #if USE(CG) 44 #include "PDFDocumentImage.h" 45 #endif 46 47 #if ENABLE(SVG_AS_IMAGE) 48 #include "SVGImage.h" 49 #endif 50 51 using std::max; 52 53 namespace WebCore { 54 55 CachedImage::CachedImage(const String& url) 56 : CachedResource(url, ImageResource) 57 , m_image(0) 58 , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) 59 , m_shouldPaintBrokenImage(true) 60 , m_autoLoadWasPreventedBySettings(false) 61 { 62 setStatus(Unknown); 63 } 64 65 CachedImage::CachedImage(Image* image) 66 : CachedResource(String(), ImageResource) 67 , m_image(image) 68 , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) 69 , m_shouldPaintBrokenImage(true) 70 , m_autoLoadWasPreventedBySettings(false) 71 { 72 setStatus(Cached); 73 setLoading(false); 74 } 75 76 CachedImage::~CachedImage() 77 { 78 } 79 80 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*) 81 { 82 ASSERT(!hasClients()); 83 destroyDecodedData(); 84 } 85 86 void CachedImage::load(CachedResourceLoader* cachedResourceLoader) 87 { 88 #ifdef ANDROID_BLOCK_NETWORK_IMAGE 89 if (!cachedResourceLoader || (cachedResourceLoader->autoLoadImages() && !cachedResourceLoader->shouldBlockNetworkImage(m_url))) 90 #else 91 if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages()) 92 #endif 93 CachedResource::load(cachedResourceLoader, true, DoSecurityCheck, true); 94 else 95 setLoading(false); 96 } 97 98 void CachedImage::didAddClient(CachedResourceClient* c) 99 { 100 if (m_decodedDataDeletionTimer.isActive()) 101 m_decodedDataDeletionTimer.stop(); 102 103 if (m_data && !m_image && !errorOccurred()) { 104 createImage(); 105 m_image->setData(m_data, true); 106 } 107 108 if (m_image && !m_image->isNull()) 109 c->imageChanged(this); 110 111 CachedResource::didAddClient(c); 112 } 113 114 void CachedImage::allClientsRemoved() 115 { 116 if (m_image && !errorOccurred()) 117 m_image->resetAnimation(); 118 if (double interval = memoryCache()->deadDecodedDataDeletionInterval()) 119 m_decodedDataDeletionTimer.startOneShot(interval); 120 } 121 122 static Image* brokenImage() 123 { 124 DEFINE_STATIC_LOCAL(RefPtr<Image>, brokenImage, (Image::loadPlatformResource("missingImage"))); 125 return brokenImage.get(); 126 } 127 128 Image* CachedImage::image() const 129 { 130 ASSERT(!isPurgeable()); 131 132 if (errorOccurred() && m_shouldPaintBrokenImage) 133 return brokenImage(); 134 135 if (m_image) 136 return m_image.get(); 137 138 return Image::nullImage(); 139 } 140 141 void CachedImage::setImageContainerSize(const IntSize& containerSize) 142 { 143 if (m_image) 144 m_image->setContainerSize(containerSize); 145 } 146 147 bool CachedImage::usesImageContainerSize() const 148 { 149 if (m_image) 150 return m_image->usesContainerSize(); 151 152 return false; 153 } 154 155 bool CachedImage::imageHasRelativeWidth() const 156 { 157 if (m_image) 158 return m_image->hasRelativeWidth(); 159 160 return false; 161 } 162 163 bool CachedImage::imageHasRelativeHeight() const 164 { 165 if (m_image) 166 return m_image->hasRelativeHeight(); 167 168 return false; 169 } 170 171 IntSize CachedImage::imageSize(float multiplier) const 172 { 173 ASSERT(!isPurgeable()); 174 175 if (!m_image) 176 return IntSize(); 177 if (multiplier == 1.0f) 178 return m_image->size(); 179 180 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. 181 bool hasWidth = m_image->size().width() > 0; 182 bool hasHeight = m_image->size().height() > 0; 183 int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier); 184 int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier); 185 if (hasWidth) 186 width = max(1, width); 187 if (hasHeight) 188 height = max(1, height); 189 return IntSize(width, height); 190 } 191 192 IntRect CachedImage::imageRect(float multiplier) const 193 { 194 ASSERT(!isPurgeable()); 195 196 if (!m_image) 197 return IntRect(); 198 if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight())) 199 return m_image->rect(); 200 201 float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier); 202 float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier); 203 204 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. 205 bool hasWidth = m_image->rect().width() > 0; 206 bool hasHeight = m_image->rect().height() > 0; 207 208 int width = static_cast<int>(m_image->rect().width() * widthMultiplier); 209 int height = static_cast<int>(m_image->rect().height() * heightMultiplier); 210 if (hasWidth) 211 width = max(1, width); 212 if (hasHeight) 213 height = max(1, height); 214 215 int x = static_cast<int>(m_image->rect().x() * widthMultiplier); 216 int y = static_cast<int>(m_image->rect().y() * heightMultiplier); 217 218 return IntRect(x, y, width, height); 219 } 220 221 void CachedImage::notifyObservers(const IntRect* changeRect) 222 { 223 CachedResourceClientWalker w(m_clients); 224 while (CachedResourceClient* c = w.next()) 225 c->imageChanged(this, changeRect); 226 } 227 228 void CachedImage::checkShouldPaintBrokenImage() 229 { 230 Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0; 231 if (!frame) 232 return; 233 234 m_shouldPaintBrokenImage = frame->loader()->client()->shouldPaintBrokenImage(KURL(ParsedURLString, m_url)); 235 } 236 237 void CachedImage::clear() 238 { 239 destroyDecodedData(); 240 m_image = 0; 241 setEncodedSize(0); 242 } 243 244 inline void CachedImage::createImage() 245 { 246 // Create the image if it doesn't yet exist. 247 if (m_image) 248 return; 249 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS) 250 if (m_response.mimeType() == "application/pdf") { 251 m_image = PDFDocumentImage::create(); 252 return; 253 } 254 #endif 255 #if ENABLE(SVG_AS_IMAGE) 256 if (m_response.mimeType() == "image/svg+xml") { 257 m_image = SVGImage::create(this); 258 return; 259 } 260 #endif 261 m_image = BitmapImage::create(this); 262 #if PLATFORM(ANDROID) 263 m_image->setURL(url()); 264 #endif 265 } 266 267 size_t CachedImage::maximumDecodedImageSize() 268 { 269 Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0; 270 if (!frame) 271 return 0; 272 Settings* settings = frame->settings(); 273 return settings ? settings->maximumDecodedImageSize() : 0; 274 } 275 276 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) 277 { 278 m_data = data; 279 280 createImage(); 281 282 bool sizeAvailable = false; 283 284 // Have the image update its data from its internal buffer. 285 // It will not do anything now, but will delay decoding until 286 // queried for info (like size or specific image frames). 287 sizeAvailable = m_image->setData(m_data, allDataReceived); 288 289 // Go ahead and tell our observers to try to draw if we have either 290 // received all the data or the size is known. Each chunk from the 291 // network causes observers to repaint, which will force that chunk 292 // to decode. 293 if (sizeAvailable || allDataReceived) { 294 size_t maxDecodedImageSize = maximumDecodedImageSize(); 295 IntSize s = imageSize(1.0f); 296 size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check 297 if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) { 298 error(errorOccurred() ? status() : DecodeError); 299 if (inCache()) 300 memoryCache()->remove(this); 301 return; 302 } 303 304 // It would be nice to only redraw the decoded band of the image, but with the current design 305 // (decoding delayed until painting) that seems hard. 306 notifyObservers(); 307 308 if (m_image) 309 setEncodedSize(m_image->data() ? m_image->data()->size() : 0); 310 } 311 312 if (allDataReceived) { 313 setLoading(false); 314 checkNotify(); 315 } 316 } 317 318 void CachedImage::error(CachedResource::Status status) 319 { 320 checkShouldPaintBrokenImage(); 321 clear(); 322 setStatus(status); 323 ASSERT(errorOccurred()); 324 m_data.clear(); 325 notifyObservers(); 326 setLoading(false); 327 checkNotify(); 328 } 329 330 void CachedImage::destroyDecodedData() 331 { 332 bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); 333 if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) { 334 // Image refs the data buffer so we should not make it purgeable while the image is alive. 335 // Invoking addClient() will reconstruct the image object. 336 m_image = 0; 337 setDecodedSize(0); 338 if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) 339 makePurgeable(true); 340 } else if (m_image && !errorOccurred()) 341 m_image->destroyDecodedData(); 342 } 343 344 void CachedImage::decodedSizeChanged(const Image* image, int delta) 345 { 346 if (image != m_image) 347 return; 348 349 setDecodedSize(decodedSize() + delta); 350 } 351 352 void CachedImage::didDraw(const Image* image) 353 { 354 if (image != m_image) 355 return; 356 357 double timeStamp = FrameView::currentPaintTimeStamp(); 358 if (!timeStamp) // If didDraw is called outside of a Frame paint. 359 timeStamp = currentTime(); 360 361 CachedResource::didAccessDecodedData(timeStamp); 362 } 363 364 bool CachedImage::shouldPauseAnimation(const Image* image) 365 { 366 if (image != m_image) 367 return false; 368 369 CachedResourceClientWalker w(m_clients); 370 while (CachedResourceClient* c = w.next()) { 371 if (c->willRenderImage(this)) 372 return false; 373 } 374 375 return true; 376 } 377 378 void CachedImage::animationAdvanced(const Image* image) 379 { 380 if (image == m_image) 381 notifyObservers(); 382 } 383 384 void CachedImage::changedInRect(const Image* image, const IntRect& rect) 385 { 386 if (image == m_image) 387 notifyObservers(&rect); 388 } 389 390 } //namespace WebCore 391