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
56namespace WebCore {
57
58CachedImage::CachedImage(CachedResourceRequest&& request, const PAL::SessionID& sessionID, const CookieJar* cookieJar)
59 : CachedResource(WTFMove(request), Type::ImageResource, sessionID, cookieJar)
60{
61 setStatus(Unknown);
62}
63
64CachedImage::CachedImage(Image* image, const PAL::SessionID& sessionID, const CookieJar* cookieJar)
65 : CachedResource(URL(), Type::ImageResource, sessionID, cookieJar)
66 , m_image(image)
67{
68}
69
70CachedImage::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
82CachedImage::~CachedImage()
83{
84 clearImage();
85}
86
87void CachedImage::load(CachedResourceLoader& loader)
88{
89 if (loader.shouldPerformImageLoad(url()))
90 CachedResource::load(loader);
91 else
92 setLoading(false);
93}
94
95void 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
111void 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
128void 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
143bool CachedImage::isClientWaitingForAsyncDecoding(CachedImageClient& client) const
144{
145 return m_clientsWaitingForAsyncDecoding.contains(&client);
146}
147
148void 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
167void 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
177void 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
196void CachedImage::allClientsRemoved()
197{
198 m_pendingContainerContextRequests.clear();
199 m_clientsWaitingForAsyncDecoding.clear();
200 if (m_image && !errorOccurred())
201 m_image->resetAnimation();
202}
203
204std::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
220bool CachedImage::willPaintBrokenImage() const
221{
222 return errorOccurred() && m_shouldPaintBrokenImage;
223}
224
225Image* 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
240Image* 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
260void 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
278LayoutSize 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
305void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
306{
307 if (m_image)
308 m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
309}
310
311void 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
318void CachedImage::checkShouldPaintBrokenImage()
319{
320 if (!m_loader || m_loader->reachedTerminalState())
321 return;
322
323 m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url());
324}
325
326bool CachedImage::isPDFResource() const
327{
328 return Image::isPDFResource(m_response.mimeType(), url());
329}
330
331bool CachedImage::isPostScriptResource() const
332{
333 return Image::isPostScriptResource(m_response.mimeType(), url());
334}
335
336void CachedImage::clear()
337{
338 destroyDecodedData();
339 clearImage();
340 m_pendingContainerContextRequests.clear();
341 m_clientsWaitingForAsyncDecoding.clear();
342 setEncodedSize(0);
343}
344
345inline 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
369CachedImage::CachedImageObserver::CachedImageObserver(CachedImage& image)
370{
371 m_cachedImages.add(&image);
372}
373
374void CachedImage::CachedImageObserver::encodedDataStatusChanged(const Image& image, EncodedDataStatus status)
375{
376 for (auto cachedImage : m_cachedImages)
377 cachedImage->encodedDataStatusChanged(image, status);
378}
379
380void CachedImage::CachedImageObserver::decodedSizeChanged(const Image& image, long long delta)
381{
382 for (auto cachedImage : m_cachedImages)
383 cachedImage->decodedSizeChanged(image, delta);
384}
385
386void CachedImage::CachedImageObserver::didDraw(const Image& image)
387{
388 for (auto cachedImage : m_cachedImages)
389 cachedImage->didDraw(image);
390}
391
392bool 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
403void 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
409void CachedImage::CachedImageObserver::changedInRect(const Image& image, const IntRect* rect)
410{
411 for (auto cachedImage : m_cachedImages)
412 cachedImage->changedInRect(image, rect);
413}
414
415inline 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
436void 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
482bool 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
491RefPtr<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
504void CachedImage::didUpdateImageData()
505{
506 m_lastUpdateImageDataTime = MonotonicTime::now();
507 ASSERT(m_updateImageDataCount < std::numeric_limits<unsigned>::max());
508 ++m_updateImageDataCount;
509}
510
511EncodedDataStatus 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
520void CachedImage::updateBuffer(SharedBuffer& data)
521{
522 ASSERT(dataBufferingPolicy() == DataBufferingPolicy::BufferData);
523 updateBufferInternal(data);
524 CachedResource::updateBuffer(data);
525}
526
527void 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
534void 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
556void 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
566void CachedImage::error(CachedResource::Status status)
567{
568 checkShouldPaintBrokenImage();
569 clear();
570 CachedResource::error(status);
571 notifyObservers();
572}
573
574void CachedImage::responseReceived(const ResourceResponse& response)
575{
576 if (!m_response.isNull())
577 clear();
578 CachedResource::responseReceived(response);
579}
580
581void 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
591void CachedImage::encodedDataStatusChanged(const Image& image, EncodedDataStatus)
592{
593 if (&image != m_image)
594 return;
595
596 notifyObservers();
597}
598
599void 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
608void 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
620bool 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
634void 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
657void CachedImage::changedInRect(const Image& image, const IntRect* rect)
658{
659 if (&image != m_image)
660 return;
661 notifyObservers(rect);
662}
663
664bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
665{
666 Image* image = imageForRenderer(renderer);
667 return image->currentFrameKnownToBeOpaque();
668}
669
670bool 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
678CachedResource::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