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