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