| 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 "Frame.h" |
| 33 | #include "FrameLoader.h" |
| 34 | #include "FrameLoaderClient.h" |
| 35 | #include "FrameLoaderTypes.h" |
| 36 | #include "FrameView.h" |
| 37 | #include "MIMETypeRegistry.h" |
| 38 | #include "MemoryCache.h" |
| 39 | #include "RenderElement.h" |
| 40 | #include "SVGImage.h" |
| 41 | #include "SecurityOrigin.h" |
| 42 | #include "Settings.h" |
| 43 | #include "SharedBuffer.h" |
| 44 | #include "SubresourceLoader.h" |
| 45 | #include <wtf/NeverDestroyed.h> |
| 46 | #include <wtf/StdLibExtras.h> |
| 47 | |
| 48 | #if PLATFORM(IOS_FAMILY) |
| 49 | #include "SystemMemory.h" |
| 50 | #endif |
| 51 | |
| 52 | #if USE(CG) |
| 53 | #include "PDFDocumentImage.h" |
| 54 | #endif |
| 55 | |
| 56 | namespace WebCore { |
| 57 | |
| 58 | CachedImage::CachedImage(CachedResourceRequest&& request, const PAL::SessionID& sessionID, const CookieJar* cookieJar) |
| 59 | : CachedResource(WTFMove(request), Type::ImageResource, sessionID, cookieJar) |
| 60 | { |
| 61 | setStatus(Unknown); |
| 62 | } |
| 63 | |
| 64 | CachedImage::CachedImage(Image* image, const PAL::SessionID& sessionID, const CookieJar* cookieJar) |
| 65 | : CachedResource(URL(), Type::ImageResource, sessionID, cookieJar) |
| 66 | , m_image(image) |
| 67 | { |
| 68 | } |
| 69 | |
| 70 | CachedImage::CachedImage(const URL& url, Image* image, const PAL::SessionID& sessionID, const CookieJar* cookieJar, const String& domainForCachePartition) |
| 71 | : CachedResource(url, Type::ImageResource, sessionID, cookieJar) |
| 72 | , m_image(image) |
| 73 | , m_isManuallyCached(true) |
| 74 | { |
| 75 | m_resourceRequest.setDomainForCachePartition(domainForCachePartition); |
| 76 | |
| 77 | // Use the incoming URL in the response field. This ensures that code using the response directly, |
| 78 | // such as origin checks for security, actually see something. |
| 79 | m_response.setURL(url); |
| 80 | } |
| 81 | |
| 82 | CachedImage::~CachedImage() |
| 83 | { |
| 84 | clearImage(); |
| 85 | } |
| 86 | |
| 87 | void CachedImage::load(CachedResourceLoader& loader) |
| 88 | { |
| 89 | if (loader.shouldPerformImageLoad(url())) |
| 90 | CachedResource::load(loader); |
| 91 | else |
| 92 | setLoading(false); |
| 93 | } |
| 94 | |
| 95 | void CachedImage::setBodyDataFrom(const CachedResource& resource) |
| 96 | { |
| 97 | ASSERT(resource.type() == type()); |
| 98 | const CachedImage& image = static_cast<const CachedImage&>(resource); |
| 99 | |
| 100 | CachedResource::setBodyDataFrom(resource); |
| 101 | |
| 102 | m_image = image.m_image; |
| 103 | m_imageObserver = image.m_imageObserver; |
| 104 | if (m_imageObserver) |
| 105 | m_imageObserver->cachedImages().add(this); |
| 106 | |
| 107 | if (m_image && is<SVGImage>(*m_image)) |
| 108 | m_svgImageCache = std::make_unique<SVGImageCache>(&downcast<SVGImage>(*m_image)); |
| 109 | } |
| 110 | |
| 111 | void CachedImage::didAddClient(CachedResourceClient& client) |
| 112 | { |
| 113 | if (m_data && !m_image && !errorOccurred()) { |
| 114 | createImage(); |
| 115 | m_image->setData(m_data.copyRef(), true); |
| 116 | } |
| 117 | |
| 118 | ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); |
| 119 | if (m_image && !m_image->isNull()) |
| 120 | static_cast<CachedImageClient&>(client).imageChanged(this); |
| 121 | |
| 122 | if (m_image) |
| 123 | m_image->startAnimationAsynchronously(); |
| 124 | |
| 125 | CachedResource::didAddClient(client); |
| 126 | } |
| 127 | |
| 128 | void CachedImage::didRemoveClient(CachedResourceClient& client) |
| 129 | { |
| 130 | ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); |
| 131 | |
| 132 | m_pendingContainerContextRequests.remove(&static_cast<CachedImageClient&>(client)); |
| 133 | m_clientsWaitingForAsyncDecoding.remove(&static_cast<CachedImageClient&>(client)); |
| 134 | |
| 135 | if (m_svgImageCache) |
| 136 | m_svgImageCache->removeClientFromCache(&static_cast<CachedImageClient&>(client)); |
| 137 | |
| 138 | CachedResource::didRemoveClient(client); |
| 139 | |
| 140 | static_cast<CachedImageClient&>(client).didRemoveCachedImageClient(*this); |
| 141 | } |
| 142 | |
| 143 | bool CachedImage::isClientWaitingForAsyncDecoding(CachedImageClient& client) const |
| 144 | { |
| 145 | return m_clientsWaitingForAsyncDecoding.contains(&client); |
| 146 | } |
| 147 | |
| 148 | void CachedImage::addClientWaitingForAsyncDecoding(CachedImageClient& client) |
| 149 | { |
| 150 | ASSERT(client.resourceClientType() == CachedImageClient::expectedType()); |
| 151 | if (m_clientsWaitingForAsyncDecoding.contains(&client)) |
| 152 | return; |
| 153 | if (!m_clients.contains(&client)) { |
| 154 | // If the <html> element does not have its own background specified, painting the root box |
| 155 | // renderer uses the style of the <body> element, see RenderView::rendererForRootBackground(). |
| 156 | // In this case, the client we are asked to add is the root box renderer. Since we can't add |
| 157 | // a client to m_clientsWaitingForAsyncDecoding unless it is one of the m_clients, we are going |
| 158 | // to cancel the repaint optimization we do in CachedImage::imageFrameAvailable() by adding |
| 159 | // all the m_clients to m_clientsWaitingForAsyncDecoding. |
| 160 | CachedResourceClientWalker<CachedImageClient> walker(m_clients); |
| 161 | while (auto* client = walker.next()) |
| 162 | m_clientsWaitingForAsyncDecoding.add(client); |
| 163 | } else |
| 164 | m_clientsWaitingForAsyncDecoding.add(&client); |
| 165 | } |
| 166 | |
| 167 | void CachedImage::removeAllClientsWaitingForAsyncDecoding() |
| 168 | { |
| 169 | if (m_clientsWaitingForAsyncDecoding.isEmpty() || !hasImage() || !is<BitmapImage>(image())) |
| 170 | return; |
| 171 | downcast<BitmapImage>(image())->stopAsyncDecodingQueue(); |
| 172 | for (auto* client : m_clientsWaitingForAsyncDecoding) |
| 173 | client->imageChanged(this); |
| 174 | m_clientsWaitingForAsyncDecoding.clear(); |
| 175 | } |
| 176 | |
| 177 | void CachedImage::switchClientsToRevalidatedResource() |
| 178 | { |
| 179 | ASSERT(is<CachedImage>(resourceToRevalidate())); |
| 180 | // Pending container size requests need to be transferred to the revalidated resource. |
| 181 | if (!m_pendingContainerContextRequests.isEmpty()) { |
| 182 | // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce(). |
| 183 | ContainerContextRequests switchContainerContextRequests; |
| 184 | for (auto& request : m_pendingContainerContextRequests) |
| 185 | switchContainerContextRequests.set(request.key, request.value); |
| 186 | CachedResource::switchClientsToRevalidatedResource(); |
| 187 | CachedImage& revalidatedCachedImage = downcast<CachedImage>(*resourceToRevalidate()); |
| 188 | for (auto& request : switchContainerContextRequests) |
| 189 | revalidatedCachedImage.setContainerContextForClient(*request.key, request.value.containerSize, request.value.containerZoom, request.value.imageURL); |
| 190 | return; |
| 191 | } |
| 192 | |
| 193 | CachedResource::switchClientsToRevalidatedResource(); |
| 194 | } |
| 195 | |
| 196 | void CachedImage::allClientsRemoved() |
| 197 | { |
| 198 | m_pendingContainerContextRequests.clear(); |
| 199 | m_clientsWaitingForAsyncDecoding.clear(); |
| 200 | if (m_image && !errorOccurred()) |
| 201 | m_image->resetAnimation(); |
| 202 | } |
| 203 | |
| 204 | std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const |
| 205 | { |
| 206 | if (deviceScaleFactor >= 3) { |
| 207 | static NeverDestroyed<Image*> brokenImageVeryHiRes(&Image::loadPlatformResource("missingImage@3x" ).leakRef()); |
| 208 | return std::make_pair(brokenImageVeryHiRes, 3); |
| 209 | } |
| 210 | |
| 211 | if (deviceScaleFactor >= 2) { |
| 212 | static NeverDestroyed<Image*> brokenImageHiRes(&Image::loadPlatformResource("missingImage@2x" ).leakRef()); |
| 213 | return std::make_pair(brokenImageHiRes, 2); |
| 214 | } |
| 215 | |
| 216 | static NeverDestroyed<Image*> brokenImageLoRes(&Image::loadPlatformResource("missingImage" ).leakRef()); |
| 217 | return std::make_pair(brokenImageLoRes, 1); |
| 218 | } |
| 219 | |
| 220 | bool CachedImage::willPaintBrokenImage() const |
| 221 | { |
| 222 | return errorOccurred() && m_shouldPaintBrokenImage; |
| 223 | } |
| 224 | |
| 225 | Image* CachedImage::image() |
| 226 | { |
| 227 | if (errorOccurred() && m_shouldPaintBrokenImage) { |
| 228 | // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate |
| 229 | // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() |
| 230 | // when they need the real, deviceScaleFactor-appropriate broken image icon. |
| 231 | return brokenImage(1).first; |
| 232 | } |
| 233 | |
| 234 | if (m_image) |
| 235 | return m_image.get(); |
| 236 | |
| 237 | return &Image::nullImage(); |
| 238 | } |
| 239 | |
| 240 | Image* CachedImage::imageForRenderer(const RenderObject* renderer) |
| 241 | { |
| 242 | if (errorOccurred() && m_shouldPaintBrokenImage) { |
| 243 | // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate |
| 244 | // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() |
| 245 | // when they need the real, deviceScaleFactor-appropriate broken image icon. |
| 246 | return brokenImage(1).first; |
| 247 | } |
| 248 | |
| 249 | if (!m_image) |
| 250 | return &Image::nullImage(); |
| 251 | |
| 252 | if (m_image->isSVGImage()) { |
| 253 | Image* image = m_svgImageCache->imageForRenderer(renderer); |
| 254 | if (image != &Image::nullImage()) |
| 255 | return image; |
| 256 | } |
| 257 | return m_image.get(); |
| 258 | } |
| 259 | |
| 260 | void CachedImage::setContainerContextForClient(const CachedImageClient& client, const LayoutSize& containerSize, float containerZoom, const URL& imageURL) |
| 261 | { |
| 262 | if (containerSize.isEmpty()) |
| 263 | return; |
| 264 | ASSERT(containerZoom); |
| 265 | if (!m_image) { |
| 266 | m_pendingContainerContextRequests.set(&client, ContainerContext { containerSize, containerZoom, imageURL }); |
| 267 | return; |
| 268 | } |
| 269 | |
| 270 | if (!m_image->isSVGImage()) { |
| 271 | m_image->setContainerSize(containerSize); |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | m_svgImageCache->setContainerContextForClient(client, containerSize, containerZoom, imageURL); |
| 276 | } |
| 277 | |
| 278 | LayoutSize CachedImage::imageSizeForRenderer(const RenderElement* renderer, float multiplier, SizeType sizeType) |
| 279 | { |
| 280 | if (!m_image) |
| 281 | return LayoutSize(); |
| 282 | |
| 283 | LayoutSize imageSize; |
| 284 | |
| 285 | if (is<BitmapImage>(*m_image) && renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation) |
| 286 | imageSize = LayoutSize(downcast<BitmapImage>(*m_image).sizeRespectingOrientation()); |
| 287 | else if (is<SVGImage>(*m_image) && sizeType == UsedSize) |
| 288 | imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer)); |
| 289 | else |
| 290 | imageSize = LayoutSize(m_image->size()); |
| 291 | |
| 292 | if (multiplier == 1.0f) |
| 293 | return imageSize; |
| 294 | |
| 295 | // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. |
| 296 | float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier; |
| 297 | float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier; |
| 298 | LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0); |
| 299 | imageSize.scale(widthScale, heightScale); |
| 300 | imageSize.clampToMinimumSize(minimumSize); |
| 301 | ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f)); |
| 302 | return imageSize; |
| 303 | } |
| 304 | |
| 305 | void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) |
| 306 | { |
| 307 | if (m_image) |
| 308 | m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); |
| 309 | } |
| 310 | |
| 311 | void CachedImage::notifyObservers(const IntRect* changeRect) |
| 312 | { |
| 313 | CachedResourceClientWalker<CachedImageClient> w(m_clients); |
| 314 | while (CachedImageClient* c = w.next()) |
| 315 | c->imageChanged(this, changeRect); |
| 316 | } |
| 317 | |
| 318 | void CachedImage::checkShouldPaintBrokenImage() |
| 319 | { |
| 320 | if (!m_loader || m_loader->reachedTerminalState()) |
| 321 | return; |
| 322 | |
| 323 | m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url()); |
| 324 | } |
| 325 | |
| 326 | bool CachedImage::isPDFResource() const |
| 327 | { |
| 328 | return Image::isPDFResource(m_response.mimeType(), url()); |
| 329 | } |
| 330 | |
| 331 | bool CachedImage::isPostScriptResource() const |
| 332 | { |
| 333 | return Image::isPostScriptResource(m_response.mimeType(), url()); |
| 334 | } |
| 335 | |
| 336 | void CachedImage::clear() |
| 337 | { |
| 338 | destroyDecodedData(); |
| 339 | clearImage(); |
| 340 | m_pendingContainerContextRequests.clear(); |
| 341 | m_clientsWaitingForAsyncDecoding.clear(); |
| 342 | setEncodedSize(0); |
| 343 | } |
| 344 | |
| 345 | inline void CachedImage::createImage() |
| 346 | { |
| 347 | // Create the image if it doesn't yet exist. |
| 348 | if (m_image) |
| 349 | return; |
| 350 | |
| 351 | m_imageObserver = CachedImageObserver::create(*this); |
| 352 | |
| 353 | m_image = Image::create(*m_imageObserver); |
| 354 | |
| 355 | if (m_image) { |
| 356 | if (is<SVGImage>(*m_image)) |
| 357 | m_svgImageCache = std::make_unique<SVGImageCache>(&downcast<SVGImage>(*m_image)); |
| 358 | |
| 359 | // Send queued container size requests. |
| 360 | if (m_image->usesContainerSize()) { |
| 361 | for (auto& request : m_pendingContainerContextRequests) |
| 362 | setContainerContextForClient(*request.key, request.value.containerSize, request.value.containerZoom, request.value.imageURL); |
| 363 | } |
| 364 | m_pendingContainerContextRequests.clear(); |
| 365 | m_clientsWaitingForAsyncDecoding.clear(); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | CachedImage::CachedImageObserver::CachedImageObserver(CachedImage& image) |
| 370 | { |
| 371 | m_cachedImages.add(&image); |
| 372 | } |
| 373 | |
| 374 | void CachedImage::CachedImageObserver::encodedDataStatusChanged(const Image& image, EncodedDataStatus status) |
| 375 | { |
| 376 | for (auto cachedImage : m_cachedImages) |
| 377 | cachedImage->encodedDataStatusChanged(image, status); |
| 378 | } |
| 379 | |
| 380 | void CachedImage::CachedImageObserver::decodedSizeChanged(const Image& image, long long delta) |
| 381 | { |
| 382 | for (auto cachedImage : m_cachedImages) |
| 383 | cachedImage->decodedSizeChanged(image, delta); |
| 384 | } |
| 385 | |
| 386 | void CachedImage::CachedImageObserver::didDraw(const Image& image) |
| 387 | { |
| 388 | for (auto cachedImage : m_cachedImages) |
| 389 | cachedImage->didDraw(image); |
| 390 | } |
| 391 | |
| 392 | bool CachedImage::CachedImageObserver::canDestroyDecodedData(const Image& image) |
| 393 | { |
| 394 | for (auto cachedImage : m_cachedImages) { |
| 395 | if (&image != cachedImage->image()) |
| 396 | continue; |
| 397 | if (!cachedImage->canDestroyDecodedData(image)) |
| 398 | return false; |
| 399 | } |
| 400 | return true; |
| 401 | } |
| 402 | |
| 403 | void CachedImage::CachedImageObserver::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect, DecodingStatus decodingStatus) |
| 404 | { |
| 405 | for (auto cachedImage : m_cachedImages) |
| 406 | cachedImage->imageFrameAvailable(image, animatingState, changeRect, decodingStatus); |
| 407 | } |
| 408 | |
| 409 | void CachedImage::CachedImageObserver::changedInRect(const Image& image, const IntRect* rect) |
| 410 | { |
| 411 | for (auto cachedImage : m_cachedImages) |
| 412 | cachedImage->changedInRect(image, rect); |
| 413 | } |
| 414 | |
| 415 | inline void CachedImage::clearImage() |
| 416 | { |
| 417 | if (!m_image) |
| 418 | return; |
| 419 | |
| 420 | if (m_imageObserver) { |
| 421 | m_imageObserver->cachedImages().remove(this); |
| 422 | |
| 423 | if (m_imageObserver->cachedImages().isEmpty()) { |
| 424 | ASSERT(m_imageObserver->hasOneRef()); |
| 425 | m_image->setImageObserver(nullptr); |
| 426 | } |
| 427 | |
| 428 | m_imageObserver = nullptr; |
| 429 | } |
| 430 | |
| 431 | m_image = nullptr; |
| 432 | m_lastUpdateImageDataTime = { }; |
| 433 | m_updateImageDataCount = 0; |
| 434 | } |
| 435 | |
| 436 | void CachedImage::updateBufferInternal(SharedBuffer& data) |
| 437 | { |
| 438 | m_data = &data; |
| 439 | setEncodedSize(m_data->size()); |
| 440 | createImage(); |
| 441 | |
| 442 | // Don't update the image with the new buffer very often. Changing the decoder |
| 443 | // internal data and repainting the observers sometimes are very expensive operations. |
| 444 | if (!m_forceUpdateImageDataEnabledForTesting && shouldDeferUpdateImageData()) |
| 445 | return; |
| 446 | |
| 447 | EncodedDataStatus encodedDataStatus = EncodedDataStatus::Unknown; |
| 448 | |
| 449 | if (isPostScriptResource()) { |
| 450 | #if PLATFORM(MAC) && !USE(WEBKIT_IMAGE_DECODERS) |
| 451 | // Delay updating the image with the PostScript data till all the data |
| 452 | // is received so it can be converted to PDF data. |
| 453 | return; |
| 454 | #else |
| 455 | // Set the encodedDataStatus to Error so loading this image will be canceled. |
| 456 | encodedDataStatus = EncodedDataStatus::Error; |
| 457 | #endif |
| 458 | } else { |
| 459 | // Have the image update its data from its internal buffer. Decoding the image data |
| 460 | // will be delayed until info (like size or specific image frames) are queried which |
| 461 | // usually happens when the observers are repainted. |
| 462 | encodedDataStatus = updateImageData(false); |
| 463 | } |
| 464 | |
| 465 | if (encodedDataStatus > EncodedDataStatus::Error && encodedDataStatus < EncodedDataStatus::SizeAvailable) |
| 466 | return; |
| 467 | |
| 468 | if (encodedDataStatus == EncodedDataStatus::Error || m_image->isNull()) { |
| 469 | // Image decoding failed. Either we need more image data or the image data is malformed. |
| 470 | error(errorOccurred() ? status() : DecodeError); |
| 471 | if (m_loader && encodedDataStatus == EncodedDataStatus::Error) |
| 472 | m_loader->cancel(); |
| 473 | if (inCache()) |
| 474 | MemoryCache::singleton().remove(*this); |
| 475 | return; |
| 476 | } |
| 477 | |
| 478 | // Tell our observers to try to draw. |
| 479 | notifyObservers(); |
| 480 | } |
| 481 | |
| 482 | bool CachedImage::shouldDeferUpdateImageData() const |
| 483 | { |
| 484 | static const double updateImageDataBackoffIntervals[] = { 0, 1, 3, 6, 15 }; |
| 485 | unsigned interval = std::min<unsigned>(m_updateImageDataCount, 4); |
| 486 | |
| 487 | // The first time through, the chunk time will be 0 and the image will get an update. |
| 488 | return (MonotonicTime::now() - m_lastUpdateImageDataTime).seconds() < updateImageDataBackoffIntervals[interval]; |
| 489 | } |
| 490 | |
| 491 | RefPtr<SharedBuffer> CachedImage::convertedDataIfNeeded(SharedBuffer* data) const |
| 492 | { |
| 493 | if (!data || !isPostScriptResource()) |
| 494 | return data; |
| 495 | #if PLATFORM(MAC) && !USE(WEBKIT_IMAGE_DECODERS) |
| 496 | return SharedBuffer::create(PDFDocumentImage::convertPostScriptDataToPDF(data->createCFData()).get()); |
| 497 | #else |
| 498 | // Loading the image should have been canceled if the system does not support converting PostScript to PDF. |
| 499 | ASSERT_NOT_REACHED(); |
| 500 | return nullptr; |
| 501 | #endif |
| 502 | } |
| 503 | |
| 504 | void CachedImage::didUpdateImageData() |
| 505 | { |
| 506 | m_lastUpdateImageDataTime = MonotonicTime::now(); |
| 507 | ASSERT(m_updateImageDataCount < std::numeric_limits<unsigned>::max()); |
| 508 | ++m_updateImageDataCount; |
| 509 | } |
| 510 | |
| 511 | EncodedDataStatus CachedImage::updateImageData(bool allDataReceived) |
| 512 | { |
| 513 | if (!m_image || !m_data) |
| 514 | return EncodedDataStatus::Error; |
| 515 | EncodedDataStatus result = m_image->setData(m_data.get(), allDataReceived); |
| 516 | didUpdateImageData(); |
| 517 | return result; |
| 518 | } |
| 519 | |
| 520 | void CachedImage::updateBuffer(SharedBuffer& data) |
| 521 | { |
| 522 | ASSERT(dataBufferingPolicy() == DataBufferingPolicy::BufferData); |
| 523 | updateBufferInternal(data); |
| 524 | CachedResource::updateBuffer(data); |
| 525 | } |
| 526 | |
| 527 | void CachedImage::updateData(const char* data, unsigned length) |
| 528 | { |
| 529 | ASSERT(dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData); |
| 530 | updateBufferInternal(SharedBuffer::create(data, length)); |
| 531 | CachedResource::updateData(data, length); |
| 532 | } |
| 533 | |
| 534 | void CachedImage::finishLoading(SharedBuffer* data) |
| 535 | { |
| 536 | m_data = convertedDataIfNeeded(data); |
| 537 | if (m_data) { |
| 538 | setEncodedSize(m_data->size()); |
| 539 | createImage(); |
| 540 | } |
| 541 | |
| 542 | EncodedDataStatus encodedDataStatus = updateImageData(true); |
| 543 | |
| 544 | if (encodedDataStatus == EncodedDataStatus::Error || m_image->isNull()) { |
| 545 | // Image decoding failed; the image data is malformed. |
| 546 | error(errorOccurred() ? status() : DecodeError); |
| 547 | if (inCache()) |
| 548 | MemoryCache::singleton().remove(*this); |
| 549 | return; |
| 550 | } |
| 551 | |
| 552 | notifyObservers(); |
| 553 | CachedResource::finishLoading(data); |
| 554 | } |
| 555 | |
| 556 | void CachedImage::didReplaceSharedBufferContents() |
| 557 | { |
| 558 | if (m_image) { |
| 559 | // Let the Image know that the SharedBuffer has been rejigged, so it can let go of any references to the heap-allocated resource buffer. |
| 560 | // FIXME(rdar://problem/24275617): It would be better if we could somehow tell the Image's decoder to swap in the new contents without destroying anything. |
| 561 | m_image->destroyDecodedData(true); |
| 562 | } |
| 563 | CachedResource::didReplaceSharedBufferContents(); |
| 564 | } |
| 565 | |
| 566 | void CachedImage::error(CachedResource::Status status) |
| 567 | { |
| 568 | checkShouldPaintBrokenImage(); |
| 569 | clear(); |
| 570 | CachedResource::error(status); |
| 571 | notifyObservers(); |
| 572 | } |
| 573 | |
| 574 | void CachedImage::responseReceived(const ResourceResponse& response) |
| 575 | { |
| 576 | if (!m_response.isNull()) |
| 577 | clear(); |
| 578 | CachedResource::responseReceived(response); |
| 579 | } |
| 580 | |
| 581 | void CachedImage::destroyDecodedData() |
| 582 | { |
| 583 | bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); |
| 584 | if (canDeleteImage && !isLoading() && !hasClients()) { |
| 585 | m_image = nullptr; |
| 586 | setDecodedSize(0); |
| 587 | } else if (m_image && !errorOccurred()) |
| 588 | m_image->destroyDecodedData(); |
| 589 | } |
| 590 | |
| 591 | void CachedImage::encodedDataStatusChanged(const Image& image, EncodedDataStatus) |
| 592 | { |
| 593 | if (&image != m_image) |
| 594 | return; |
| 595 | |
| 596 | notifyObservers(); |
| 597 | } |
| 598 | |
| 599 | void CachedImage::decodedSizeChanged(const Image& image, long long delta) |
| 600 | { |
| 601 | if (&image != m_image) |
| 602 | return; |
| 603 | |
| 604 | ASSERT(delta >= 0 || decodedSize() + delta >= 0); |
| 605 | setDecodedSize(static_cast<unsigned>(decodedSize() + delta)); |
| 606 | } |
| 607 | |
| 608 | void CachedImage::didDraw(const Image& image) |
| 609 | { |
| 610 | if (&image != m_image) |
| 611 | return; |
| 612 | |
| 613 | MonotonicTime timeStamp = FrameView::currentPaintTimeStamp(); |
| 614 | if (!timeStamp) // If didDraw is called outside of a Frame paint. |
| 615 | timeStamp = MonotonicTime::now(); |
| 616 | |
| 617 | CachedResource::didAccessDecodedData(timeStamp); |
| 618 | } |
| 619 | |
| 620 | bool CachedImage::canDestroyDecodedData(const Image& image) |
| 621 | { |
| 622 | if (&image != m_image) |
| 623 | return false; |
| 624 | |
| 625 | CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); |
| 626 | while (CachedImageClient* client = clientWalker.next()) { |
| 627 | if (!client->canDestroyDecodedData()) |
| 628 | return false; |
| 629 | } |
| 630 | |
| 631 | return true; |
| 632 | } |
| 633 | |
| 634 | void CachedImage::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect, DecodingStatus decodingStatus) |
| 635 | { |
| 636 | if (&image != m_image) |
| 637 | return; |
| 638 | |
| 639 | CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); |
| 640 | VisibleInViewportState visibleState = VisibleInViewportState::No; |
| 641 | |
| 642 | while (CachedImageClient* client = clientWalker.next()) { |
| 643 | // All the clients of animated images have to be notified. The new frame has to be drawn in all of them. |
| 644 | if (animatingState == ImageAnimatingState::No && !m_clientsWaitingForAsyncDecoding.contains(client)) |
| 645 | continue; |
| 646 | if (client->imageFrameAvailable(*this, animatingState, changeRect) == VisibleInViewportState::Yes) |
| 647 | visibleState = VisibleInViewportState::Yes; |
| 648 | } |
| 649 | |
| 650 | if (visibleState == VisibleInViewportState::No && animatingState == ImageAnimatingState::Yes) |
| 651 | m_image->stopAnimation(); |
| 652 | |
| 653 | if (decodingStatus != DecodingStatus::Partial) |
| 654 | m_clientsWaitingForAsyncDecoding.clear(); |
| 655 | } |
| 656 | |
| 657 | void CachedImage::changedInRect(const Image& image, const IntRect* rect) |
| 658 | { |
| 659 | if (&image != m_image) |
| 660 | return; |
| 661 | notifyObservers(rect); |
| 662 | } |
| 663 | |
| 664 | bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer) |
| 665 | { |
| 666 | Image* image = imageForRenderer(renderer); |
| 667 | return image->currentFrameKnownToBeOpaque(); |
| 668 | } |
| 669 | |
| 670 | bool CachedImage::isOriginClean(SecurityOrigin* origin) |
| 671 | { |
| 672 | ASSERT_UNUSED(origin, origin); |
| 673 | ASSERT(this->origin()); |
| 674 | ASSERT(origin->toString() == this->origin()->toString()); |
| 675 | return !loadFailedOrCanceled() && isCORSSameOrigin(); |
| 676 | } |
| 677 | |
| 678 | CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const |
| 679 | { |
| 680 | if (UNLIKELY(isManuallyCached())) { |
| 681 | // Do not revalidate manually cached images. This mechanism is used as a |
| 682 | // way to efficiently share an image from the client to content and |
| 683 | // the URL for that image may not represent a resource that can be |
| 684 | // retrieved by standard means. If the manual caching SPI is used, it is |
| 685 | // incumbent on the client to only use valid resources. |
| 686 | return RevalidationDecision::No; |
| 687 | } |
| 688 | return CachedResource::makeRevalidationDecision(cachePolicy); |
| 689 | } |
| 690 | |
| 691 | } // namespace WebCore |
| 692 | |