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