Clean up image subsampling code, make it less iOS-specific
[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         imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation());
293 #endif // ENABLE(CSS_IMAGE_ORIENTATION)
294
295     else if (m_image->isSVGImage() && sizeType == UsedSize) {
296         imageSize = LayoutSize(m_svgImageCache->imageSizeForRenderer(renderer));
297     }
298
299     if (multiplier == 1.0f)
300         return imageSize;
301         
302     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
303     float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
304     float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
305     LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
306     imageSize.scale(widthScale, heightScale);
307     imageSize.clampToMinimumSize(minimumSize);
308     ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
309     return imageSize;
310 }
311
312 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
313 {
314     if (m_image)
315         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
316 }
317
318 void CachedImage::notifyObservers(const IntRect* changeRect)
319 {
320     CachedResourceClientWalker<CachedImageClient> w(m_clients);
321     while (CachedImageClient* c = w.next())
322         c->imageChanged(this, changeRect);
323 }
324
325 void CachedImage::checkShouldPaintBrokenImage()
326 {
327     if (!m_loader || m_loader->reachedTerminalState())
328         return;
329
330     m_shouldPaintBrokenImage = m_loader->frameLoader()->client().shouldPaintBrokenImage(m_resourceRequest.url());
331 }
332
333 void CachedImage::clear()
334 {
335     destroyDecodedData();
336     clearImage();
337     m_pendingContainerSizeRequests.clear();
338     setEncodedSize(0);
339 }
340
341 inline void CachedImage::createImage()
342 {
343     // Create the image if it doesn't yet exist.
344     if (m_image)
345         return;
346
347 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
348     else if (m_response.mimeType() == "application/pdf")
349         m_image = PDFDocumentImage::create(this);
350 #endif
351     else if (m_response.mimeType() == "image/svg+xml") {
352         RefPtr<SVGImage> svgImage = SVGImage::create(this);
353         m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get());
354         m_image = svgImage.release();
355     } else {
356         m_image = BitmapImage::create(this);
357         toBitmapImage(m_image.get())->setAllowSubsampling(m_loader && m_loader->frameLoader()->frame().settings().imageSubsamplingEnabled());
358     }
359
360     if (m_image) {
361         // Send queued container size requests.
362         if (m_image->usesContainerSize()) {
363             for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
364                 setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
365         }
366         m_pendingContainerSizeRequests.clear();
367     }
368 }
369
370 inline void CachedImage::clearImage()
371 {
372     // If our Image has an observer, it's always us so we need to clear the back pointer
373     // before dropping our reference.
374     if (m_image)
375         m_image->setImageObserver(0);
376     m_image.clear();
377 }
378
379 void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data)
380 {
381     m_data = data;
382     if (!data)
383         return;
384
385     createImage();
386
387     // Have the image update its data from its internal buffer.
388     // It will not do anything now, but will delay decoding until
389     // queried for info (like size or specific image frames).
390     bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false);
391     if (!sizeAvailable)
392         return;
393
394     if (m_image->isNull()) {
395         // Image decoding failed. Either we need more image data or the image data is malformed.
396         error(errorOccurred() ? status() : DecodeError);
397         if (inCache())
398             memoryCache()->remove(this);
399         return;
400     }
401
402     // Go ahead and tell our observers to try to draw.
403     // Each chunk from the network causes observers to repaint, which will
404     // force that chunk to decode.
405     // It would be nice to only redraw the decoded band of the image, but with the current design
406     // (decoding delayed until painting) that seems hard.
407     notifyObservers();
408
409     setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
410 }
411
412 void CachedImage::addDataBuffer(ResourceBuffer* data)
413 {
414     ASSERT(m_options.dataBufferingPolicy() == BufferData);
415     addIncrementalDataBuffer(data);
416 }
417
418 void CachedImage::addData(const char* data, unsigned length)
419 {
420     ASSERT(m_options.dataBufferingPolicy() == DoNotBufferData);
421     addIncrementalDataBuffer(ResourceBuffer::create(data, length).get());
422 }
423
424 void CachedImage::finishLoading(ResourceBuffer* data)
425 {
426     m_data = data;
427     if (!m_image && data)
428         createImage();
429
430     if (m_image)
431         m_image->setData(m_data->sharedBuffer(), true);
432
433     if (!m_image || m_image->isNull()) {
434         // Image decoding failed; the image data is malformed.
435         error(errorOccurred() ? status() : DecodeError);
436         if (inCache())
437             memoryCache()->remove(this);
438         return;
439     }
440
441     notifyObservers();
442     if (m_image)
443         setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
444     CachedResource::finishLoading(data);
445 }
446
447 void CachedImage::error(CachedResource::Status status)
448 {
449     checkShouldPaintBrokenImage();
450     clear();
451     CachedResource::error(status);
452     notifyObservers();
453 }
454
455 void CachedImage::responseReceived(const ResourceResponse& response)
456 {
457     if (!m_response.isNull())
458         clear();
459     CachedResource::responseReceived(response);
460 }
461
462 void CachedImage::destroyDecodedData()
463 {
464     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
465     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
466         // Image refs the data buffer so we should not make it purgeable while the image is alive. 
467         // Invoking addClient() will reconstruct the image object.
468         m_image = 0;
469         setDecodedSize(0);
470         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
471             makePurgeable(true);
472     } else if (m_image && !errorOccurred())
473         m_image->destroyDecodedData();
474 }
475
476 void CachedImage::decodedSizeChanged(const Image* image, int delta)
477 {
478     if (!image || image != m_image)
479         return;
480     
481     setDecodedSize(decodedSize() + delta);
482 }
483
484 void CachedImage::didDraw(const Image* image)
485 {
486     if (!image || image != m_image)
487         return;
488     
489     double timeStamp = FrameView::currentPaintTimeStamp();
490     if (!timeStamp) // If didDraw is called outside of a Frame paint.
491         timeStamp = monotonicallyIncreasingTime();
492     
493     CachedResource::didAccessDecodedData(timeStamp);
494 }
495
496 void CachedImage::animationAdvanced(const Image* image)
497 {
498     if (!image || image != m_image)
499         return;
500     CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients);
501     while (CachedImageClient* client = clientWalker.next())
502         client->newImageAnimationFrameAvailable(*this);
503 }
504
505 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
506 {
507     if (!image || image != m_image)
508         return;
509     notifyObservers(&rect);
510 }
511
512 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
513 {
514     Image* image = imageForRenderer(renderer);
515     return image->currentFrameKnownToBeOpaque();
516 }
517
518 #if ENABLE(DISK_IMAGE_CACHE)
519 bool CachedImage::canUseDiskImageCache() const
520 {
521     if (isLoading() || errorOccurred())
522         return false;
523
524     if (!m_data)
525         return false;
526
527     if (isPurgeable())
528         return false;
529
530     if (m_data->size() < diskImageCache().minimumImageSize())
531         return false;
532
533     // "Cache-Control: no-store" resources may be marked as such because they may
534     // contain sensitive information. We should not write these resources to disk.
535     if (m_response.cacheControlContainsNoStore())
536         return false;
537
538     // Testing shows that PDF images did not work when memory mapped.
539     // However, SVG images and Bitmap images were fine. See:
540     // <rdar://problem/8591834> Disk Image Cache should support PDF Images
541     if (m_response.mimeType() == "application/pdf")
542         return false;
543
544     return true;
545 }
546
547 void CachedImage::useDiskImageCache()
548 {
549     ASSERT(canUseDiskImageCache());
550     ASSERT(!isUsingDiskImageCache());
551     m_data->sharedBuffer()->allowToBeMemoryMapped();
552 }
553 #endif
554
555 bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
556 {
557     if (!image()->hasSingleSecurityOrigin())
558         return false;
559     if (passesAccessControlCheck(securityOrigin))
560         return true;
561     return !securityOrigin->taintsCanvas(response().url());
562 }
563
564 bool CachedImage::mustRevalidateDueToCacheHeaders(CachePolicy policy) const
565 {
566     if (UNLIKELY(isManuallyCached())) {
567         // Do not revalidate manually cached images. This mechanism is used as a
568         // way to efficiently share an image from the client to content and
569         // the URL for that image may not represent a resource that can be
570         // retrieved by standard means. If the manual caching SPI is used, it is
571         // incumbent on the client to only use valid resources.
572         return false;
573     }
574     return CachedResource::mustRevalidateDueToCacheHeaders(policy);
575 }
576
577 } // namespace WebCore