Remove ENABLE(SVG_AS_IMAGE) since all major ports have it on by default
[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 "MemoryCache.h"
29 #include "CachedResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceLoader.h"
32 #include "CachedResourceRequest.h"
33 #include "Frame.h"
34 #include "FrameLoaderClient.h"
35 #include "FrameLoaderTypes.h"
36 #include "FrameView.h"
37 #include "Settings.h"
38 #include "SharedBuffer.h"
39 #include <wtf/CurrentTime.h>
40 #include <wtf/StdLibExtras.h>
41 #include <wtf/Vector.h>
42
43 #if USE(CG)
44 #include "PDFDocumentImage.h"
45 #endif
46
47 #if ENABLE(SVG)
48 #include "SVGImage.h"
49 #endif
50
51 using std::max;
52
53 namespace WebCore {
54
55 CachedImage::CachedImage(const ResourceRequest& resourceRequest)
56     : CachedResource(resourceRequest, ImageResource)
57     , m_image(0)
58     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
59     , m_shouldPaintBrokenImage(true)
60 {
61     setStatus(Unknown);
62 }
63
64 CachedImage::CachedImage(Image* image)
65     : CachedResource(ResourceRequest(), ImageResource)
66     , m_image(image)
67     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
68     , m_shouldPaintBrokenImage(true)
69 {
70     setStatus(Cached);
71     setLoading(false);
72 }
73
74 CachedImage::~CachedImage()
75 {
76 }
77
78 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
79 {
80     ASSERT(!hasClients());
81     destroyDecodedData();
82 }
83
84 void CachedImage::load(CachedResourceLoader* cachedResourceLoader)
85 {
86     if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
87         CachedResource::load(cachedResourceLoader, true, DoSecurityCheck);
88     else
89         setLoading(false);
90 }
91
92 void CachedImage::didAddClient(CachedResourceClient* c)
93 {
94     if (m_decodedDataDeletionTimer.isActive())
95         m_decodedDataDeletionTimer.stop();
96     
97     if (m_data && !m_image && !errorOccurred()) {
98         createImage();
99         m_image->setData(m_data, true);
100     }
101
102     if (m_image && !m_image->isNull())
103         c->imageChanged(this);
104
105     CachedResource::didAddClient(c);
106 }
107
108 void CachedImage::allClientsRemoved()
109 {
110     if (m_image && !errorOccurred())
111         m_image->resetAnimation();
112     if (double interval = memoryCache()->deadDecodedDataDeletionInterval())
113         m_decodedDataDeletionTimer.startOneShot(interval);
114 }
115
116 pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
117 {
118     if (deviceScaleFactor >= 2) {
119         DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef()));
120         return make_pair(brokenImageHiRes, 2);
121     }
122
123     DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef()));
124     return make_pair(brokenImageLoRes, 1);
125 }
126
127 bool CachedImage::willPaintBrokenImage() const
128 {
129     return errorOccurred() && m_shouldPaintBrokenImage;
130 }
131
132 Image* CachedImage::image() const
133 {
134     ASSERT(!isPurgeable());
135
136     if (errorOccurred() && m_shouldPaintBrokenImage) {
137         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
138         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
139         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
140         return brokenImage(1).first;
141     }
142
143     if (m_image)
144         return m_image.get();
145
146     return Image::nullImage();
147 }
148
149 void CachedImage::setImageContainerSize(const IntSize& containerSize)
150 {
151     if (m_image)
152         m_image->setContainerSize(containerSize);
153 }
154
155 bool CachedImage::usesImageContainerSize() const
156 {
157     if (m_image)
158         return m_image->usesContainerSize();
159
160     return false;
161 }
162
163 bool CachedImage::imageHasRelativeWidth() const
164 {
165     if (m_image)
166         return m_image->hasRelativeWidth();
167
168     return false;
169 }
170
171 bool CachedImage::imageHasRelativeHeight() const
172 {
173     if (m_image)
174         return m_image->hasRelativeHeight();
175
176     return false;
177 }
178
179 IntSize CachedImage::imageSize(float multiplier) const
180 {
181     ASSERT(!isPurgeable());
182
183     if (!m_image)
184         return IntSize();
185     if (multiplier == 1.0f)
186         return m_image->size();
187         
188     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
189     bool hasWidth = m_image->size().width() > 0;
190     bool hasHeight = m_image->size().height() > 0;
191     int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
192     int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
193     if (hasWidth)
194         width = max(1, width);
195     if (hasHeight)
196         height = max(1, height);
197     return IntSize(width, height);
198 }
199
200 IntRect CachedImage::imageRect(float multiplier) const
201 {
202     ASSERT(!isPurgeable());
203
204     if (!m_image)
205         return IntRect();
206     if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight()))
207         return m_image->rect();
208
209     float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier);
210     float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier);
211
212     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
213     bool hasWidth = m_image->rect().width() > 0;
214     bool hasHeight = m_image->rect().height() > 0;
215
216     int width = static_cast<int>(m_image->rect().width() * widthMultiplier);
217     int height = static_cast<int>(m_image->rect().height() * heightMultiplier);
218     if (hasWidth)
219         width = max(1, width);
220     if (hasHeight)
221         height = max(1, height);
222
223     int x = static_cast<int>(m_image->rect().x() * widthMultiplier);
224     int y = static_cast<int>(m_image->rect().y() * heightMultiplier);
225
226     return IntRect(x, y, width, height);
227 }
228
229 void CachedImage::notifyObservers(const IntRect* changeRect)
230 {
231     CachedResourceClientWalker w(m_clients);
232     while (CachedResourceClient* c = w.next())
233         c->imageChanged(this, changeRect);
234 }
235
236 void CachedImage::checkShouldPaintBrokenImage()
237 {
238     Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0;
239     if (!frame)
240         return;
241
242     m_shouldPaintBrokenImage = frame->loader()->client()->shouldPaintBrokenImage(m_resourceRequest.url());
243 }
244
245 void CachedImage::clear()
246 {
247     destroyDecodedData();
248     m_image = 0;
249     setEncodedSize(0);
250 }
251
252 inline void CachedImage::createImage()
253 {
254     // Create the image if it doesn't yet exist.
255     if (m_image)
256         return;
257 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
258     if (m_response.mimeType() == "application/pdf") {
259         m_image = PDFDocumentImage::create();
260         return;
261     }
262 #endif
263 #if ENABLE(SVG)
264     if (m_response.mimeType() == "image/svg+xml") {
265         m_image = SVGImage::create(this);
266         return;
267     }
268 #endif
269     m_image = BitmapImage::create(this);
270 }
271
272 size_t CachedImage::maximumDecodedImageSize()
273 {
274     Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0;
275     if (!frame)
276         return 0;
277     Settings* settings = frame->settings();
278     return settings ? settings->maximumDecodedImageSize() : 0;
279 }
280
281 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
282 {
283     m_data = data;
284
285     createImage();
286
287     bool sizeAvailable = false;
288
289     // Have the image update its data from its internal buffer.
290     // It will not do anything now, but will delay decoding until 
291     // queried for info (like size or specific image frames).
292     sizeAvailable = m_image->setData(m_data, allDataReceived);
293
294     // Go ahead and tell our observers to try to draw if we have either
295     // received all the data or the size is known.  Each chunk from the
296     // network causes observers to repaint, which will force that chunk
297     // to decode.
298     if (sizeAvailable || allDataReceived) {
299         size_t maxDecodedImageSize = maximumDecodedImageSize();
300         IntSize s = imageSize(1.0f);
301         size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
302         if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
303             error(errorOccurred() ? status() : DecodeError);
304             if (inCache())
305                 memoryCache()->remove(this);
306             return;
307         }
308         
309         // It would be nice to only redraw the decoded band of the image, but with the current design
310         // (decoding delayed until painting) that seems hard.
311         notifyObservers();
312
313         if (m_image)
314             setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
315     }
316     
317     if (allDataReceived) {
318         setLoading(false);
319         checkNotify();
320     }
321 }
322
323 void CachedImage::error(CachedResource::Status status)
324 {
325     checkShouldPaintBrokenImage();
326     clear();
327     setStatus(status);
328     ASSERT(errorOccurred());
329     m_data.clear();
330     notifyObservers();
331     setLoading(false);
332     checkNotify();
333 }
334
335 void CachedImage::destroyDecodedData()
336 {
337     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
338     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
339         // Image refs the data buffer so we should not make it purgeable while the image is alive. 
340         // Invoking addClient() will reconstruct the image object.
341         m_image = 0;
342         setDecodedSize(0);
343         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
344             makePurgeable(true);
345     } else if (m_image && !errorOccurred())
346         m_image->destroyDecodedData();
347 }
348
349 void CachedImage::decodedSizeChanged(const Image* image, int delta)
350 {
351     if (image != m_image)
352         return;
353     
354     setDecodedSize(decodedSize() + delta);
355 }
356
357 void CachedImage::didDraw(const Image* image)
358 {
359     if (image != m_image)
360         return;
361     
362     double timeStamp = FrameView::currentPaintTimeStamp();
363     if (!timeStamp) // If didDraw is called outside of a Frame paint.
364         timeStamp = currentTime();
365     
366     CachedResource::didAccessDecodedData(timeStamp);
367 }
368
369 bool CachedImage::shouldPauseAnimation(const Image* image)
370 {
371     if (image != m_image)
372         return false;
373     
374     CachedResourceClientWalker w(m_clients);
375     while (CachedResourceClient* c = w.next()) {
376         if (c->willRenderImage(this))
377             return false;
378     }
379
380     return true;
381 }
382
383 void CachedImage::animationAdvanced(const Image* image)
384 {
385     if (image == m_image)
386         notifyObservers();
387 }
388
389 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
390 {
391     if (image == m_image)
392         notifyObservers(&rect);
393 }
394
395 } //namespace WebCore