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