When navigating, discard decoded image data that is only live due to page cache.
[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 "MemoryCache.h"
38 #include "Page.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 #include <wtf/Vector.h>
49
50 #if PLATFORM(IOS)
51 #include "SystemMemory.h"
52 #endif
53
54 #if USE(CG)
55 #include "PDFDocumentImage.h"
56 #endif
57
58 namespace WebCore {
59
60 CachedImage::CachedImage(const ResourceRequest& resourceRequest, SessionID sessionID)
61     : CachedResource(resourceRequest, ImageResource, sessionID)
62     , m_image(nullptr)
63     , m_isManuallyCached(false)
64     , m_shouldPaintBrokenImage(true)
65 {
66     setStatus(Unknown);
67 }
68
69 CachedImage::CachedImage(Image* image, SessionID sessionID)
70     : CachedResource(ResourceRequest(), ImageResource, sessionID)
71     , m_image(image)
72     , m_isManuallyCached(false)
73     , m_shouldPaintBrokenImage(true)
74 {
75     setStatus(Cached);
76     setLoading(false);
77 }
78
79 CachedImage::CachedImage(const URL& url, Image* image, SessionID sessionID)
80     : CachedResource(ResourceRequest(url), ImageResource, sessionID)
81     , m_image(image)
82     , m_isManuallyCached(false)
83     , m_shouldPaintBrokenImage(true)
84 {
85     setStatus(Cached);
86     setLoading(false);
87 }
88
89 CachedImage::CachedImage(const URL& url, Image* image, CachedImage::CacheBehaviorType type, SessionID sessionID)
90     : CachedResource(ResourceRequest(url), ImageResource, sessionID)
91     , m_image(image)
92     , m_isManuallyCached(type == CachedImage::ManuallyCached)
93     , m_shouldPaintBrokenImage(true)
94 {
95     setStatus(Cached);
96     setLoading(false);
97     if (UNLIKELY(isManuallyCached())) {
98         // Use the incoming URL in the response field. This ensures that code
99         // using the response directly, such as origin checks for security,
100         // actually see something.
101         m_response.setURL(url);
102     }
103 }
104
105 CachedImage::~CachedImage()
106 {
107     clearImage();
108 }
109
110 void CachedImage::load(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
111 {
112     if (cachedResourceLoader.shouldPerformImageLoad(url()))
113         CachedResource::load(cachedResourceLoader, options);
114     else
115         setLoading(false);
116 }
117
118 void CachedImage::didAddClient(CachedResourceClient* client)
119 {
120     if (m_data && !m_image && !errorOccurred()) {
121         createImage();
122         m_image->setData(m_data.copyRef(), true);
123     }
124     
125     ASSERT(client->resourceClientType() == CachedImageClient::expectedType());
126     if (m_image && !m_image->isNull())
127         static_cast<CachedImageClient*>(client)->imageChanged(this);
128
129     CachedResource::didAddClient(client);
130 }
131
132 void CachedImage::didRemoveClient(CachedResourceClient* client)
133 {
134     ASSERT(client);
135     ASSERT(client->resourceClientType() == CachedImageClient::expectedType());
136
137     m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(client));
138
139     if (m_svgImageCache)
140         m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(client));
141
142     CachedResource::didRemoveClient(client);
143 }
144
145 void CachedImage::switchClientsToRevalidatedResource()
146 {
147     ASSERT(is<CachedImage>(resourceToRevalidate()));
148     // Pending container size requests need to be transferred to the revalidated resource.
149     if (!m_pendingContainerSizeRequests.isEmpty()) {
150         // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce().
151         ContainerSizeRequests switchContainerSizeRequests;
152         for (auto& request : m_pendingContainerSizeRequests)
153             switchContainerSizeRequests.set(request.key, request.value);
154         CachedResource::switchClientsToRevalidatedResource();
155         CachedImage& revalidatedCachedImage = downcast<CachedImage>(*resourceToRevalidate());
156         for (auto& request : switchContainerSizeRequests)
157             revalidatedCachedImage.setContainerSizeForRenderer(request.key, request.value.first, request.value.second);
158         return;
159     }
160
161     CachedResource::switchClientsToRevalidatedResource();
162 }
163
164 void CachedImage::allClientsRemoved()
165 {
166     m_pendingContainerSizeRequests.clear();
167     if (m_image && !errorOccurred())
168         m_image->resetAnimation();
169 }
170
171 std::pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
172 {
173     if (deviceScaleFactor >= 3) {
174         static NeverDestroyed<Image*> brokenImageVeryHiRes(Image::loadPlatformResource("missingImage@3x").leakRef());
175         return std::make_pair(brokenImageVeryHiRes, 3);
176     }
177
178     if (deviceScaleFactor >= 2) {
179         static NeverDestroyed<Image*> brokenImageHiRes(Image::loadPlatformResource("missingImage@2x").leakRef());
180         return std::make_pair(brokenImageHiRes, 2);
181     }
182
183     static NeverDestroyed<Image*> brokenImageLoRes(Image::loadPlatformResource("missingImage").leakRef());
184     return std::make_pair(brokenImageLoRes, 1);
185 }
186
187 bool CachedImage::willPaintBrokenImage() const
188 {
189     return errorOccurred() && m_shouldPaintBrokenImage;
190 }
191
192 Image* CachedImage::image()
193 {
194     if (errorOccurred() && m_shouldPaintBrokenImage) {
195         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
196         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
197         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
198         return brokenImage(1).first;
199     }
200
201     if (m_image)
202         return m_image.get();
203
204     return Image::nullImage();
205 }
206
207 Image* CachedImage::imageForRenderer(const RenderObject* renderer)
208 {
209     if (errorOccurred() && m_shouldPaintBrokenImage) {
210         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
211         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
212         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
213         return brokenImage(1).first;
214     }
215
216     if (!m_image)
217         return Image::nullImage();
218
219     if (m_image->isSVGImage()) {
220         Image* image = m_svgImageCache->imageForRenderer(renderer);
221         if (image != Image::nullImage())
222             return image;
223     }
224     return m_image.get();
225 }
226
227 void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const LayoutSize& containerSize, float containerZoom)
228 {
229     if (containerSize.isEmpty())
230         return;
231     ASSERT(renderer);
232     ASSERT(containerZoom);
233     if (!m_image) {
234         m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
235         return;
236     }
237
238     if (!m_image->isSVGImage()) {
239         m_image->setContainerSize(containerSize);
240         return;
241     }
242
243     m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
244 }
245
246 bool CachedImage::usesImageContainerSize() const
247 {
248     if (m_image)
249         return m_image->usesContainerSize();
250
251     return false;
252 }
253
254 bool CachedImage::imageHasRelativeWidth() const
255 {
256     if (m_image)
257         return m_image->hasRelativeWidth();
258
259     return false;
260 }
261
262 bool CachedImage::imageHasRelativeHeight() const
263 {
264     if (m_image)
265         return m_image->hasRelativeHeight();
266
267     return false;
268 }
269
270 LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType)
271 {
272     if (!m_image)
273         return LayoutSize();
274
275     LayoutSize imageSize;
276
277     if (is<BitmapImage>(*m_image) && renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation)
278         imageSize = LayoutSize(downcast<BitmapImage>(*m_image).sizeRespectingOrientation());
279     else if (is<SVGImage>(*m_image) && sizeType == UsedSize)
280         imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer));
281     else
282         imageSize = LayoutSize(m_image->size());
283
284     if (multiplier == 1.0f)
285         return imageSize;
286         
287     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
288     float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
289     float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
290     LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
291     imageSize.scale(widthScale, heightScale);
292     imageSize.clampToMinimumSize(minimumSize);
293     ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
294     return imageSize;
295 }
296
297 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
298 {
299     if (m_image)
300         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
301 }
302
303 void CachedImage::notifyObservers(const IntRect* changeRect)
304 {
305     CachedResourceClientWalker<CachedImageClient> w(m_clients);
306     while (CachedImageClient* c = w.next())
307         c->imageChanged(this, changeRect);
308 }
309
310 void CachedImage::checkShouldPaintBrokenImage()
311 {
312     if (!m_loader || m_loader->reachedTerminalState())
313         return;
314
315     m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(url());
316 }
317
318 void CachedImage::clear()
319 {
320     destroyDecodedData();
321     clearImage();
322     m_pendingContainerSizeRequests.clear();
323     setEncodedSize(0);
324 }
325
326 inline void CachedImage::createImage()
327 {
328     // Create the image if it doesn't yet exist.
329     if (m_image)
330         return;
331
332 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
333     else if (m_response.mimeType() == "application/pdf")
334         m_image = PDFDocumentImage::create(this);
335 #endif
336     else if (m_response.mimeType() == "image/svg+xml") {
337         RefPtr<SVGImage> svgImage = SVGImage::create(*this, url());
338         m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get());
339         m_image = svgImage.release();
340     } else {
341         m_image = BitmapImage::create(this);
342         downcast<BitmapImage>(*m_image).setAllowSubsampling(m_loader && m_loader->frameLoader()->frame().settings().imageSubsamplingEnabled());
343     }
344
345     if (m_image) {
346         // Send queued container size requests.
347         if (m_image->usesContainerSize()) {
348             for (auto& request : m_pendingContainerSizeRequests)
349                 setContainerSizeForRenderer(request.key, request.value.first, request.value.second);
350         }
351         m_pendingContainerSizeRequests.clear();
352     }
353 }
354
355 inline void CachedImage::clearImage()
356 {
357     // If our Image has an observer, it's always us so we need to clear the back pointer
358     // before dropping our reference.
359     if (m_image)
360         m_image->setImageObserver(nullptr);
361     m_image = nullptr;
362 }
363
364 void CachedImage::addIncrementalDataBuffer(SharedBuffer& data)
365 {
366     m_data = &data;
367
368     createImage();
369
370     // Have the image update its data from its internal buffer.
371     // It will not do anything now, but will delay decoding until
372     // queried for info (like size or specific image frames).
373     bool sizeAvailable = m_image->setData(&data, false);
374     if (!sizeAvailable)
375         return;
376
377     if (m_image->isNull()) {
378         // Image decoding failed. Either we need more image data or the image data is malformed.
379         error(errorOccurred() ? status() : DecodeError);
380         if (inCache())
381             MemoryCache::singleton().remove(*this);
382         return;
383     }
384
385     // Tell our observers to try to draw.
386     // Each chunk from the network causes observers to repaint, which will force that chunk to decode.
387     // It would be nice to only redraw the decoded band of the image, but with the current design
388     // (decoding delayed until painting) that seems hard.
389     notifyObservers();
390
391     setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
392 }
393
394 void CachedImage::addDataBuffer(SharedBuffer& data)
395 {
396     ASSERT(dataBufferingPolicy() == BufferData);
397     addIncrementalDataBuffer(data);
398     CachedResource::addDataBuffer(data);
399 }
400
401 void CachedImage::addData(const char* data, unsigned length)
402 {
403     ASSERT(dataBufferingPolicy() == DoNotBufferData);
404     addIncrementalDataBuffer(SharedBuffer::create(data, length));
405     CachedResource::addData(data, length);
406 }
407
408 void CachedImage::finishLoading(SharedBuffer* data)
409 {
410     m_data = data;
411     if (!m_image && data)
412         createImage();
413
414     if (m_image)
415         m_image->setData(data, true);
416
417     if (!m_image || m_image->isNull()) {
418         // Image decoding failed; the image data is malformed.
419         error(errorOccurred() ? status() : DecodeError);
420         if (inCache())
421             MemoryCache::singleton().remove(*this);
422         return;
423     }
424
425     notifyObservers();
426     if (m_image)
427         setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
428     CachedResource::finishLoading(data);
429 }
430
431 void CachedImage::didReplaceSharedBufferContents()
432 {
433     if (m_image) {
434         // Let the Image know that the SharedBuffer has been rejigged, so it can let go of any references to the heap-allocated resource buffer.
435         // 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.
436         m_image->destroyDecodedData(true);
437     }
438     CachedResource::didReplaceSharedBufferContents();
439 }
440
441 void CachedImage::error(CachedResource::Status status)
442 {
443     checkShouldPaintBrokenImage();
444     clear();
445     CachedResource::error(status);
446     notifyObservers();
447 }
448
449 void CachedImage::responseReceived(const ResourceResponse& response)
450 {
451     if (!m_response.isNull())
452         clear();
453     CachedResource::responseReceived(response);
454 }
455
456 void CachedImage::destroyDecodedData()
457 {
458     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
459     if (canDeleteImage && !isLoading() && !hasClients()) {
460         m_image = nullptr;
461         setDecodedSize(0);
462     } else if (m_image && !errorOccurred())
463         m_image->destroyDecodedData();
464 }
465
466 void CachedImage::decodedSizeChanged(const Image* image, int delta)
467 {
468     if (!image || image != m_image)
469         return;
470     
471     setDecodedSize(decodedSize() + delta);
472 }
473
474 void CachedImage::didDraw(const Image* image)
475 {
476     if (!image || image != m_image)
477         return;
478     
479     double timeStamp = FrameView::currentPaintTimeStamp();
480     if (!timeStamp) // If didDraw is called outside of a Frame paint.
481         timeStamp = monotonicallyIncreasingTime();
482     
483     CachedResource::didAccessDecodedData(timeStamp);
484 }
485
486 void CachedImage::animationAdvanced(const Image* image)
487 {
488     if (!image || image != m_image)
489         return;
490     CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients);
491     while (CachedImageClient* client = clientWalker.next())
492         client->newImageAnimationFrameAvailable(*this);
493 }
494
495 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
496 {
497     if (!image || image != m_image)
498         return;
499     notifyObservers(&rect);
500 }
501
502 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
503 {
504     Image* image = imageForRenderer(renderer);
505     return image->currentFrameKnownToBeOpaque();
506 }
507
508 bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
509 {
510     if (!image()->hasSingleSecurityOrigin())
511         return false;
512     if (passesAccessControlCheck(*securityOrigin))
513         return true;
514     return !securityOrigin->taintsCanvas(responseForSameOriginPolicyChecks().url());
515 }
516
517 CachedResource::RevalidationDecision CachedImage::makeRevalidationDecision(CachePolicy cachePolicy) const
518 {
519     if (UNLIKELY(isManuallyCached())) {
520         // Do not revalidate manually cached images. This mechanism is used as a
521         // way to efficiently share an image from the client to content and
522         // the URL for that image may not represent a resource that can be
523         // retrieved by standard means. If the manual caching SPI is used, it is
524         // incumbent on the client to only use valid resources.
525         return RevalidationDecision::No;
526     }
527     return CachedResource::makeRevalidationDecision(cachePolicy);
528 }
529
530 bool CachedImage::areAllClientsInPageCache() const
531 {
532     for (auto& entry : m_clients) {
533         auto& client = *entry.key;
534         if (client.resourceClientType() != CachedImageClient::expectedType())
535             continue;
536         if (!static_cast<CachedImageClient&>(client).inPageCache())
537             return false;
538     }
539     return true;
540 }
541
542 } // namespace WebCore