• 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 "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_AS_IMAGE)
48 #include "SVGImage.h"
49 #endif
50 
51 using std::max;
52 
53 namespace WebCore {
54 
CachedImage(const String & url)55 CachedImage::CachedImage(const String& url)
56     : CachedResource(url, ImageResource)
57     , m_image(0)
58     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
59     , m_shouldPaintBrokenImage(true)
60     , m_autoLoadWasPreventedBySettings(false)
61 {
62     setStatus(Unknown);
63 }
64 
CachedImage(Image * image)65 CachedImage::CachedImage(Image* image)
66     : CachedResource(String(), ImageResource)
67     , m_image(image)
68     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
69     , m_shouldPaintBrokenImage(true)
70     , m_autoLoadWasPreventedBySettings(false)
71 {
72     setStatus(Cached);
73     setLoading(false);
74 }
75 
~CachedImage()76 CachedImage::~CachedImage()
77 {
78 }
79 
decodedDataDeletionTimerFired(Timer<CachedImage> *)80 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
81 {
82     ASSERT(!hasClients());
83     destroyDecodedData();
84 }
85 
load(CachedResourceLoader * cachedResourceLoader)86 void CachedImage::load(CachedResourceLoader* cachedResourceLoader)
87 {
88 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
89     if (!cachedResourceLoader || (cachedResourceLoader->autoLoadImages() && !cachedResourceLoader->shouldBlockNetworkImage(m_url)))
90 #else
91     if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
92 #endif
93         CachedResource::load(cachedResourceLoader, true, DoSecurityCheck, true);
94     else
95         setLoading(false);
96 }
97 
didAddClient(CachedResourceClient * c)98 void CachedImage::didAddClient(CachedResourceClient* c)
99 {
100     if (m_decodedDataDeletionTimer.isActive())
101         m_decodedDataDeletionTimer.stop();
102 
103     if (m_data && !m_image && !errorOccurred()) {
104         createImage();
105         m_image->setData(m_data, true);
106     }
107 
108     if (m_image && !m_image->isNull())
109         c->imageChanged(this);
110 
111     CachedResource::didAddClient(c);
112 }
113 
allClientsRemoved()114 void CachedImage::allClientsRemoved()
115 {
116     if (m_image && !errorOccurred())
117         m_image->resetAnimation();
118     if (double interval = memoryCache()->deadDecodedDataDeletionInterval())
119         m_decodedDataDeletionTimer.startOneShot(interval);
120 }
121 
brokenImage()122 static Image* brokenImage()
123 {
124     DEFINE_STATIC_LOCAL(RefPtr<Image>, brokenImage, (Image::loadPlatformResource("missingImage")));
125     return brokenImage.get();
126 }
127 
image() const128 Image* CachedImage::image() const
129 {
130     ASSERT(!isPurgeable());
131 
132     if (errorOccurred() && m_shouldPaintBrokenImage)
133         return brokenImage();
134 
135     if (m_image)
136         return m_image.get();
137 
138     return Image::nullImage();
139 }
140 
setImageContainerSize(const IntSize & containerSize)141 void CachedImage::setImageContainerSize(const IntSize& containerSize)
142 {
143     if (m_image)
144         m_image->setContainerSize(containerSize);
145 }
146 
usesImageContainerSize() const147 bool CachedImage::usesImageContainerSize() const
148 {
149     if (m_image)
150         return m_image->usesContainerSize();
151 
152     return false;
153 }
154 
imageHasRelativeWidth() const155 bool CachedImage::imageHasRelativeWidth() const
156 {
157     if (m_image)
158         return m_image->hasRelativeWidth();
159 
160     return false;
161 }
162 
imageHasRelativeHeight() const163 bool CachedImage::imageHasRelativeHeight() const
164 {
165     if (m_image)
166         return m_image->hasRelativeHeight();
167 
168     return false;
169 }
170 
imageSize(float multiplier) const171 IntSize CachedImage::imageSize(float multiplier) const
172 {
173     ASSERT(!isPurgeable());
174 
175     if (!m_image)
176         return IntSize();
177     if (multiplier == 1.0f)
178         return m_image->size();
179 
180     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
181     bool hasWidth = m_image->size().width() > 0;
182     bool hasHeight = m_image->size().height() > 0;
183     int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
184     int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
185     if (hasWidth)
186         width = max(1, width);
187     if (hasHeight)
188         height = max(1, height);
189     return IntSize(width, height);
190 }
191 
imageRect(float multiplier) const192 IntRect CachedImage::imageRect(float multiplier) const
193 {
194     ASSERT(!isPurgeable());
195 
196     if (!m_image)
197         return IntRect();
198     if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight()))
199         return m_image->rect();
200 
201     float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier);
202     float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier);
203 
204     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
205     bool hasWidth = m_image->rect().width() > 0;
206     bool hasHeight = m_image->rect().height() > 0;
207 
208     int width = static_cast<int>(m_image->rect().width() * widthMultiplier);
209     int height = static_cast<int>(m_image->rect().height() * heightMultiplier);
210     if (hasWidth)
211         width = max(1, width);
212     if (hasHeight)
213         height = max(1, height);
214 
215     int x = static_cast<int>(m_image->rect().x() * widthMultiplier);
216     int y = static_cast<int>(m_image->rect().y() * heightMultiplier);
217 
218     return IntRect(x, y, width, height);
219 }
220 
notifyObservers(const IntRect * changeRect)221 void CachedImage::notifyObservers(const IntRect* changeRect)
222 {
223     CachedResourceClientWalker w(m_clients);
224     while (CachedResourceClient* c = w.next())
225         c->imageChanged(this, changeRect);
226 }
227 
checkShouldPaintBrokenImage()228 void CachedImage::checkShouldPaintBrokenImage()
229 {
230     Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0;
231     if (!frame)
232         return;
233 
234     m_shouldPaintBrokenImage = frame->loader()->client()->shouldPaintBrokenImage(KURL(ParsedURLString, m_url));
235 }
236 
clear()237 void CachedImage::clear()
238 {
239     destroyDecodedData();
240     m_image = 0;
241     setEncodedSize(0);
242 }
243 
createImage()244 inline void CachedImage::createImage()
245 {
246     // Create the image if it doesn't yet exist.
247     if (m_image)
248         return;
249 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
250     if (m_response.mimeType() == "application/pdf") {
251         m_image = PDFDocumentImage::create();
252         return;
253     }
254 #endif
255 #if ENABLE(SVG_AS_IMAGE)
256     if (m_response.mimeType() == "image/svg+xml") {
257         m_image = SVGImage::create(this);
258         return;
259     }
260 #endif
261     m_image = BitmapImage::create(this);
262 #if PLATFORM(ANDROID)
263     m_image->setURL(url());
264 #endif
265 }
266 
maximumDecodedImageSize()267 size_t CachedImage::maximumDecodedImageSize()
268 {
269     Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0;
270     if (!frame)
271         return 0;
272     Settings* settings = frame->settings();
273     return settings ? settings->maximumDecodedImageSize() : 0;
274 }
275 
data(PassRefPtr<SharedBuffer> data,bool allDataReceived)276 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
277 {
278     m_data = data;
279 
280     createImage();
281 
282     bool sizeAvailable = false;
283 
284     // Have the image update its data from its internal buffer.
285     // It will not do anything now, but will delay decoding until
286     // queried for info (like size or specific image frames).
287     sizeAvailable = m_image->setData(m_data, allDataReceived);
288 
289     // Go ahead and tell our observers to try to draw if we have either
290     // received all the data or the size is known.  Each chunk from the
291     // network causes observers to repaint, which will force that chunk
292     // to decode.
293     if (sizeAvailable || allDataReceived) {
294         size_t maxDecodedImageSize = maximumDecodedImageSize();
295         IntSize s = imageSize(1.0f);
296         size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
297         if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
298             error(errorOccurred() ? status() : DecodeError);
299             if (inCache())
300                 memoryCache()->remove(this);
301             return;
302         }
303 
304         // It would be nice to only redraw the decoded band of the image, but with the current design
305         // (decoding delayed until painting) that seems hard.
306         notifyObservers();
307 
308         if (m_image)
309             setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
310     }
311 
312     if (allDataReceived) {
313         setLoading(false);
314         checkNotify();
315     }
316 }
317 
error(CachedResource::Status status)318 void CachedImage::error(CachedResource::Status status)
319 {
320     checkShouldPaintBrokenImage();
321     clear();
322     setStatus(status);
323     ASSERT(errorOccurred());
324     m_data.clear();
325     notifyObservers();
326     setLoading(false);
327     checkNotify();
328 }
329 
destroyDecodedData()330 void CachedImage::destroyDecodedData()
331 {
332     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
333     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
334         // Image refs the data buffer so we should not make it purgeable while the image is alive.
335         // Invoking addClient() will reconstruct the image object.
336         m_image = 0;
337         setDecodedSize(0);
338         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
339             makePurgeable(true);
340     } else if (m_image && !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