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