Bug 21781: WebCore::Settings should have a maximum decoded image size setting
[WebKit-https.git] / WebCore / loader / 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 "Cache.h"
29 #include "CachedResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "DocLoader.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "Request.h"
35 #include "Settings.h"
36 #include "SystemTime.h"
37 #include <wtf/Vector.h>
38
39 #if PLATFORM(CG)
40 #include "PDFDocumentImage.h"
41 #endif
42
43 #if ENABLE(SVG_AS_IMAGE)
44 #include "SVGImage.h"
45 #endif
46
47 using std::max;
48
49 namespace WebCore {
50
51 CachedImage::CachedImage(const String& url)
52     : CachedResource(url, ImageResource)
53     , m_image(0)
54     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
55 {
56     m_status = Unknown;
57 }
58
59 CachedImage::CachedImage(Image* image)
60     : CachedResource(String(), ImageResource)
61     , m_image(image)
62     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
63 {
64     m_status = Cached;
65     m_loading = false;
66 }
67
68 CachedImage::~CachedImage()
69 {
70 }
71
72 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
73 {
74     ASSERT(!hasClients());
75     destroyDecodedData();
76 }
77
78 void CachedImage::load(DocLoader* docLoader)
79 {
80     if (!docLoader || docLoader->autoLoadImages())
81         CachedResource::load(docLoader, true, false, true);
82     else
83         m_loading = false;
84 }
85
86 void CachedImage::addClient(CachedResourceClient* c)
87 {
88     CachedResource::addClient(c);
89
90     if (m_decodedDataDeletionTimer.isActive())
91         m_decodedDataDeletionTimer.stop();
92
93     if (m_image && !m_image->rect().isEmpty())
94         c->imageChanged(this);
95
96     if (!m_loading)
97         c->notifyFinished(this);
98 }
99
100 void CachedImage::allClientsRemoved()
101 {
102     if (m_image && !m_errorOccurred)
103         m_image->resetAnimation();
104     if (double interval = cache()->deadDecodedDataDeletionInterval())
105         m_decodedDataDeletionTimer.startOneShot(interval);
106 }
107
108 static Image* brokenImage()
109 {
110     static RefPtr<Image> brokenImage;
111     if (!brokenImage)
112         brokenImage = Image::loadPlatformResource("missingImage");
113     return brokenImage.get();
114 }
115
116 static Image* nullImage()
117 {
118     static RefPtr<BitmapImage> nullImage = BitmapImage::create();
119     return nullImage.get();
120 }
121
122 Image* CachedImage::image() const
123 {
124     if (m_errorOccurred)
125         return brokenImage();
126
127     if (m_image)
128         return m_image.get();
129
130     return nullImage();
131 }
132
133 void CachedImage::setImageContainerSize(const IntSize& containerSize)
134 {
135     if (m_image)
136         m_image->setContainerSize(containerSize);
137 }
138
139 bool CachedImage::usesImageContainerSize() const
140 {
141     if (m_image)
142         return m_image->usesContainerSize();
143
144     return false;
145 }
146
147 bool CachedImage::imageHasRelativeWidth() const
148 {
149     if (m_image)
150         return m_image->hasRelativeWidth();
151
152     return false;
153 }
154
155 bool CachedImage::imageHasRelativeHeight() const
156 {
157     if (m_image)
158         return m_image->hasRelativeHeight();
159
160     return false;
161 }
162
163 IntSize CachedImage::imageSize(float multiplier) const
164 {
165     if (!m_image)
166         return IntSize();
167     if (multiplier == 1.0f)
168         return m_image->size();
169         
170     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
171     bool hasWidth = m_image->size().width() > 0;
172     bool hasHeight = m_image->size().height() > 0;
173     int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
174     int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
175     if (hasWidth)
176         width = max(1, width);
177     if (hasHeight)
178         height = max(1, height);
179     return IntSize(width, height);
180 }
181
182 IntRect CachedImage::imageRect(float multiplier) const
183 {
184     if (!m_image)
185         return IntRect();
186     if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight()))
187         return m_image->rect();
188
189     float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier);
190     float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier);
191
192     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
193     bool hasWidth = m_image->rect().width() > 0;
194     bool hasHeight = m_image->rect().height() > 0;
195
196     int width = static_cast<int>(m_image->rect().width() * widthMultiplier);
197     int height = static_cast<int>(m_image->rect().height() * heightMultiplier);
198     if (hasWidth)
199         width = max(1, width);
200     if (hasHeight)
201         height = max(1, height);
202
203     int x = static_cast<int>(m_image->rect().x() * widthMultiplier);
204     int y = static_cast<int>(m_image->rect().y() * heightMultiplier);
205
206     return IntRect(x, y, width, height);
207 }
208
209 void CachedImage::notifyObservers()
210 {
211     CachedResourceClientWalker w(m_clients);
212     while (CachedResourceClient* c = w.next())
213         c->imageChanged(this);
214 }
215
216 void CachedImage::clear()
217 {
218     destroyDecodedData();
219     m_image = 0;
220     setEncodedSize(0);
221 }
222
223 inline void CachedImage::createImage()
224 {
225     // Create the image if it doesn't yet exist.
226     if (m_image)
227         return;
228 #if PLATFORM(CG)
229     if (m_response.mimeType() == "application/pdf") {
230         m_image = PDFDocumentImage::create();
231         return;
232     }
233 #endif
234 #if ENABLE(SVG_AS_IMAGE)
235     if (m_response.mimeType() == "image/svg+xml") {
236         m_image = SVGImage::create(this);
237         return;
238     }
239 #endif
240     m_image = BitmapImage::create(this);
241 }
242
243 size_t CachedImage::maximumDecodedImageSize()
244 {
245     Frame* frame = m_request ? m_request->docLoader()->frame() : 0;
246     if (!frame)
247         return 0;
248     Settings* settings = frame->settings();
249     return settings ? settings->maximumDecodedImageSize() : 0;
250 }
251
252 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
253 {
254     m_data = data;
255
256     createImage();
257
258     bool sizeAvailable = false;
259
260     // Have the image update its data from its internal buffer.
261     // It will not do anything now, but will delay decoding until 
262     // queried for info (like size or specific image frames).
263     sizeAvailable = m_image->setData(m_data, allDataReceived);
264
265     // Go ahead and tell our observers to try to draw if we have either
266     // received all the data or the size is known.  Each chunk from the
267     // network causes observers to repaint, which will force that chunk
268     // to decode.
269     if (sizeAvailable || allDataReceived) {
270         size_t maxDecodedImageSize = maximumDecodedImageSize();
271         IntSize s = imageSize(1.0f);
272         size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
273         if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
274             error();
275             if (inCache())
276                 cache()->remove(this);
277             return;
278         }
279         
280         notifyObservers();
281
282         if (m_image)
283             setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
284     }
285     
286     if (allDataReceived) {
287         m_loading = false;
288         checkNotify();
289     }
290 }
291
292 void CachedImage::error()
293 {
294     clear();
295     m_errorOccurred = true;
296     notifyObservers();
297     m_loading = false;
298     checkNotify();
299 }
300
301 void CachedImage::checkNotify()
302 {
303     if (m_loading)
304         return;
305
306     CachedResourceClientWalker w(m_clients);
307     while (CachedResourceClient* c = w.next())
308         c->notifyFinished(this);
309 }
310
311 void CachedImage::destroyDecodedData()
312 {
313     if (m_image && !m_errorOccurred)
314         m_image->destroyDecodedData();
315 }
316
317 void CachedImage::decodedSizeChanged(const Image* image, int delta)
318 {
319     if (image != m_image)
320         return;
321     
322     setDecodedSize(decodedSize() + delta);
323 }
324
325 void CachedImage::didDraw(const Image* image)
326 {
327     if (image != m_image)
328         return;
329     
330     double timeStamp = FrameView::currentPaintTimeStamp();
331     if (!timeStamp) // If didDraw is called outside of a Frame paint.
332         timeStamp = currentTime();
333     
334     CachedResource::didAccessDecodedData(timeStamp);
335 }
336
337 bool CachedImage::shouldPauseAnimation(const Image* image)
338 {
339     if (image != m_image)
340         return false;
341     
342     CachedResourceClientWalker w(m_clients);
343     while (CachedResourceClient* c = w.next()) {
344         if (c->willRenderImage(this))
345             return false;
346     }
347
348     return true;
349 }
350
351 void CachedImage::animationAdvanced(const Image* image)
352 {
353     if (image == m_image)
354         notifyObservers();
355 }
356
357 } //namespace WebCore