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