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