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