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