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 "core/loader/cache/ImageResource.h" 26 27 #include "core/loader/cache/ImageResourceClient.h" 28 #include "core/loader/cache/MemoryCache.h" 29 #include "core/loader/cache/ResourceClient.h" 30 #include "core/loader/cache/ResourceClientWalker.h" 31 #include "core/loader/cache/ResourceFetcher.h" 32 #include "core/page/FrameView.h" 33 #include "core/platform/SharedBuffer.h" 34 #include "core/platform/graphics/BitmapImage.h" 35 #include "core/rendering/RenderObject.h" 36 #include "core/svg/graphics/SVGImage.h" 37 #include "wtf/CurrentTime.h" 38 #include "wtf/StdLibExtras.h" 39 #include "wtf/Vector.h" 40 41 using namespace std; 42 43 namespace WebCore { 44 45 ImageResource::ImageResource(const ResourceRequest& resourceRequest) 46 : Resource(resourceRequest, Image) 47 , m_image(0) 48 , m_loadingMultipartContent(false) 49 { 50 setStatus(Unknown); 51 setCustomAcceptHeader(); 52 } 53 54 ImageResource::ImageResource(WebCore::Image* image) 55 : Resource(ResourceRequest(), Image) 56 , m_image(image) 57 { 58 setStatus(Cached); 59 setLoading(false); 60 setCustomAcceptHeader(); 61 } 62 63 ImageResource::~ImageResource() 64 { 65 clearImage(); 66 } 67 68 void ImageResource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options) 69 { 70 if (!fetcher || fetcher->autoLoadImages()) 71 Resource::load(fetcher, options); 72 else 73 setLoading(false); 74 } 75 76 void ImageResource::didAddClient(ResourceClient* c) 77 { 78 if (m_data && !m_image && !errorOccurred()) { 79 createImage(); 80 m_image->setData(m_data, true); 81 } 82 83 ASSERT(c->resourceClientType() == ImageResourceClient::expectedType()); 84 if (m_image && !m_image->isNull()) 85 static_cast<ImageResourceClient*>(c)->imageChanged(this); 86 87 Resource::didAddClient(c); 88 } 89 90 void ImageResource::didRemoveClient(ResourceClient* c) 91 { 92 ASSERT(c); 93 ASSERT(c->resourceClientType() == ImageResourceClient::expectedType()); 94 95 m_pendingContainerSizeRequests.remove(static_cast<ImageResourceClient*>(c)); 96 if (m_svgImageCache) 97 m_svgImageCache->removeClientFromCache(static_cast<ImageResourceClient*>(c)); 98 99 Resource::didRemoveClient(c); 100 } 101 102 void ImageResource::switchClientsToRevalidatedResource() 103 { 104 ASSERT(resourceToRevalidate()); 105 ASSERT(resourceToRevalidate()->isImage()); 106 // Pending container size requests need to be transferred to the revalidated resource. 107 if (!m_pendingContainerSizeRequests.isEmpty()) { 108 // A copy of pending size requests is needed as they are deleted during Resource::switchClientsToRevalidateResouce(). 109 ContainerSizeRequests switchContainerSizeRequests; 110 for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) 111 switchContainerSizeRequests.set(it->key, it->value); 112 Resource::switchClientsToRevalidatedResource(); 113 ImageResource* revalidatedImageResource = static_cast<ImageResource*>(resourceToRevalidate()); 114 for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it) 115 revalidatedImageResource->setContainerSizeForRenderer(it->key, it->value.first, it->value.second); 116 return; 117 } 118 119 Resource::switchClientsToRevalidatedResource(); 120 } 121 122 void ImageResource::allClientsRemoved() 123 { 124 m_pendingContainerSizeRequests.clear(); 125 if (m_image && !errorOccurred()) 126 m_image->resetAnimation(); 127 Resource::allClientsRemoved(); 128 } 129 130 pair<WebCore::Image*, float> ImageResource::brokenImage(float deviceScaleFactor) const 131 { 132 if (deviceScaleFactor >= 2) { 133 DEFINE_STATIC_LOCAL(WebCore::Image*, brokenImageHiRes, (WebCore::Image::loadPlatformResource("missingImage@2x").leakRef())); 134 return std::make_pair(brokenImageHiRes, 2); 135 } 136 137 DEFINE_STATIC_LOCAL(WebCore::Image*, brokenImageLoRes, (WebCore::Image::loadPlatformResource("missingImage").leakRef())); 138 return std::make_pair(brokenImageLoRes, 1); 139 } 140 141 bool ImageResource::willPaintBrokenImage() const 142 { 143 return errorOccurred(); 144 } 145 146 WebCore::Image* ImageResource::image() 147 { 148 ASSERT(!isPurgeable()); 149 150 if (errorOccurred()) { 151 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate 152 // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage() 153 // when they need the real, deviceScaleFactor-appropriate broken image icon. 154 return brokenImage(1).first; 155 } 156 157 if (m_image) 158 return m_image.get(); 159 160 return WebCore::Image::nullImage(); 161 } 162 163 WebCore::Image* ImageResource::imageForRenderer(const RenderObject* renderer) 164 { 165 ASSERT(!isPurgeable()); 166 167 if (errorOccurred()) { 168 // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate 169 // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage() 170 // when they need the real, deviceScaleFactor-appropriate broken image icon. 171 return brokenImage(1).first; 172 } 173 174 if (!m_image) 175 return WebCore::Image::nullImage(); 176 177 if (m_image->isSVGImage()) { 178 WebCore::Image* image = m_svgImageCache->imageForRenderer(renderer); 179 if (image != WebCore::Image::nullImage()) 180 return image; 181 } 182 183 return m_image.get(); 184 } 185 186 void ImageResource::setContainerSizeForRenderer(const ImageResourceClient* renderer, const IntSize& containerSize, float containerZoom) 187 { 188 if (containerSize.isEmpty()) 189 return; 190 ASSERT(renderer); 191 ASSERT(containerZoom); 192 if (!m_image) { 193 m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom)); 194 return; 195 } 196 if (!m_image->isSVGImage()) { 197 m_image->setContainerSize(containerSize); 198 return; 199 } 200 201 m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom); 202 } 203 204 bool ImageResource::usesImageContainerSize() const 205 { 206 if (m_image) 207 return m_image->usesContainerSize(); 208 209 return false; 210 } 211 212 bool ImageResource::imageHasRelativeWidth() const 213 { 214 if (m_image) 215 return m_image->hasRelativeWidth(); 216 217 return false; 218 } 219 220 bool ImageResource::imageHasRelativeHeight() const 221 { 222 if (m_image) 223 return m_image->hasRelativeHeight(); 224 225 return false; 226 } 227 228 LayoutSize ImageResource::imageSizeForRenderer(const RenderObject* renderer, float multiplier) 229 { 230 ASSERT(!isPurgeable()); 231 232 if (!m_image) 233 return IntSize(); 234 235 LayoutSize imageSize; 236 237 if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)) 238 imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation(); 239 else if (m_image->isSVGImage()) 240 imageSize = m_svgImageCache->imageSizeForRenderer(renderer); 241 else 242 imageSize = m_image->size(); 243 244 if (multiplier == 1.0f) 245 return imageSize; 246 247 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. 248 float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; 249 float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; 250 LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); 251 imageSize.scale(widthScale, heightScale); 252 imageSize.clampToMinimumSize(minimumSize); 253 ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); 254 return imageSize; 255 } 256 257 void ImageResource::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) 258 { 259 if (m_image) 260 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); 261 } 262 263 void ImageResource::notifyObservers(const IntRect* changeRect) 264 { 265 ResourceClientWalker<ImageResourceClient> w(m_clients); 266 while (ImageResourceClient* c = w.next()) 267 c->imageChanged(this, changeRect); 268 } 269 270 void ImageResource::clear() 271 { 272 destroyDecodedData(); 273 clearImage(); 274 m_pendingContainerSizeRequests.clear(); 275 setEncodedSize(0); 276 } 277 278 void ImageResource::setCustomAcceptHeader() 279 { 280 DEFINE_STATIC_LOCAL(const AtomicString, acceptWebP, ("image/webp,*/*;q=0.8", AtomicString::ConstructFromLiteral)); 281 setAccept(acceptWebP); 282 } 283 284 inline void ImageResource::createImage() 285 { 286 // Create the image if it doesn't yet exist. 287 if (m_image) 288 return; 289 290 if (m_response.mimeType() == "image/svg+xml") { 291 RefPtr<SVGImage> svgImage = SVGImage::create(this); 292 m_svgImageCache = SVGImageCache::create(svgImage.get()); 293 m_image = svgImage.release(); 294 } else { 295 m_image = BitmapImage::create(this); 296 } 297 298 if (m_image) { 299 // Send queued container size requests. 300 if (m_image->usesContainerSize()) { 301 for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it) 302 setContainerSizeForRenderer(it->key, it->value.first, it->value.second); 303 } 304 m_pendingContainerSizeRequests.clear(); 305 } 306 } 307 308 inline void ImageResource::clearImage() 309 { 310 // If our Image has an observer, it's always us so we need to clear the back pointer 311 // before dropping our reference. 312 if (m_image) 313 m_image->setImageObserver(0); 314 m_image.clear(); 315 } 316 317 void ImageResource::appendData(const char* data, int length) 318 { 319 Resource::appendData(data, length); 320 if (!m_loadingMultipartContent) 321 updateImage(false); 322 } 323 324 void ImageResource::updateImage(bool allDataReceived) 325 { 326 if (m_data) 327 createImage(); 328 329 bool sizeAvailable = false; 330 331 // Have the image update its data from its internal buffer. 332 // It will not do anything now, but will delay decoding until 333 // queried for info (like size or specific image frames). 334 if (m_image) 335 sizeAvailable = m_image->setData(m_data, allDataReceived); 336 337 // Go ahead and tell our observers to try to draw if we have either 338 // received all the data or the size is known. Each chunk from the 339 // network causes observers to repaint, which will force that chunk 340 // to decode. 341 if (sizeAvailable || allDataReceived) { 342 if (!m_image || m_image->isNull()) { 343 error(errorOccurred() ? status() : DecodeError); 344 if (inCache()) 345 memoryCache()->remove(this); 346 return; 347 } 348 349 // It would be nice to only redraw the decoded band of the image, but with the current design 350 // (decoding delayed until painting) that seems hard. 351 notifyObservers(); 352 } 353 } 354 355 void ImageResource::finishOnePart() 356 { 357 if (m_loadingMultipartContent) 358 clear(); 359 updateImage(true); 360 if (m_loadingMultipartContent) 361 m_data.clear(); 362 Resource::finishOnePart(); 363 } 364 365 void ImageResource::error(Resource::Status status) 366 { 367 clear(); 368 Resource::error(status); 369 notifyObservers(); 370 } 371 372 void ImageResource::responseReceived(const ResourceResponse& response) 373 { 374 if (m_loadingMultipartContent && m_data) 375 finishOnePart(); 376 else if (response.isMultipart()) 377 m_loadingMultipartContent = true; 378 Resource::responseReceived(response); 379 } 380 381 void ImageResource::destroyDecodedData() 382 { 383 bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); 384 if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) { 385 // Image refs the data buffer so we should not make it purgeable while the image is alive. 386 // Invoking addClient() will reconstruct the image object. 387 m_image = 0; 388 setDecodedSize(0); 389 makePurgeable(true); 390 } else if (m_image && !errorOccurred()) { 391 m_image->destroyDecodedData(true); 392 } 393 } 394 395 void ImageResource::decodedSizeChanged(const WebCore::Image* image, int delta) 396 { 397 if (!image || image != m_image) 398 return; 399 400 setDecodedSize(decodedSize() + delta); 401 } 402 403 void ImageResource::didDraw(const WebCore::Image* image) 404 { 405 if (!image || image != m_image) 406 return; 407 408 double timeStamp = FrameView::currentPaintTimeStamp(); 409 if (!timeStamp) // If didDraw is called outside of a Frame paint. 410 timeStamp = currentTime(); 411 412 Resource::didAccessDecodedData(timeStamp); 413 } 414 415 bool ImageResource::shouldPauseAnimation(const WebCore::Image* image) 416 { 417 if (!image || image != m_image) 418 return false; 419 420 ResourceClientWalker<ImageResourceClient> w(m_clients); 421 while (ImageResourceClient* c = w.next()) { 422 if (c->willRenderImage(this)) 423 return false; 424 } 425 426 return true; 427 } 428 429 void ImageResource::animationAdvanced(const WebCore::Image* image) 430 { 431 if (!image || image != m_image) 432 return; 433 notifyObservers(); 434 } 435 436 void ImageResource::changedInRect(const WebCore::Image* image, const IntRect& rect) 437 { 438 if (!image || image != m_image) 439 return; 440 notifyObservers(&rect); 441 } 442 443 bool ImageResource::currentFrameKnownToBeOpaque(const RenderObject* renderer) 444 { 445 WebCore::Image* image = imageForRenderer(renderer); 446 if (image->isBitmapImage()) 447 image->nativeImageForCurrentFrame(); // force decode 448 return image->currentFrameKnownToBeOpaque(); 449 } 450 451 } // namespace WebCore 452