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