tizen beta release
[framework/web/webkit-efl.git] / Source / WebCore / loader / cache / CachedImage.cpp
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 "Frame.h"
33 #include "FrameLoaderClient.h"
34 #include "FrameLoaderTypes.h"
35 #include "FrameView.h"
36 #include "RenderObject.h"
37 #include "Settings.h"
38 #include "SharedBuffer.h"
39 #include "SubresourceLoader.h"
40 #include <wtf/CurrentTime.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/Vector.h>
43
44 #if USE(CG)
45 #include "PDFDocumentImage.h"
46 #endif
47
48 #if ENABLE(SVG)
49 #include "SVGImage.h"
50 #endif
51
52 using std::max;
53
54 namespace WebCore {
55
56 CachedImage::CachedImage(const ResourceRequest& resourceRequest)
57     : CachedResource(resourceRequest, ImageResource)
58     , m_image(0)
59     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
60     , m_shouldPaintBrokenImage(true)
61 {
62     setStatus(Unknown);
63 }
64
65 CachedImage::CachedImage(Image* image)
66     : CachedResource(ResourceRequest(), ImageResource)
67     , m_image(image)
68     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
69     , m_shouldPaintBrokenImage(true)
70 {
71     setStatus(Cached);
72     setLoading(false);
73 }
74
75 CachedImage::~CachedImage()
76 {
77 }
78
79 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
80 {
81     ASSERT(!hasClients());
82     destroyDecodedData();
83 }
84
85 void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
86 {
87     if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
88         CachedResource::load(cachedResourceLoader, options);
89     else
90         setLoading(false);
91 }
92
93 void CachedImage::removeClientForRenderer(RenderObject* renderer)
94 {
95 #if ENABLE(SVG)
96     if (m_svgImageCache)
97         m_svgImageCache->removeRendererFromCache(renderer);
98 #endif
99     removeClient(renderer);
100 }
101
102 void CachedImage::didAddClient(CachedResourceClient* c)
103 {
104     if (m_decodedDataDeletionTimer.isActive())
105         m_decodedDataDeletionTimer.stop();
106     
107     if (m_data && !m_image && !errorOccurred()) {
108         createImage();
109         m_image->setData(m_data, true);
110     }
111     
112     ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
113     if (m_image && !m_image->isNull())
114         static_cast<CachedImageClient*>(c)->imageChanged(this);
115
116     CachedResource::didAddClient(c);
117 }
118
119 void CachedImage::allClientsRemoved()
120 {
121     if (m_image && !errorOccurred())
122         m_image->resetAnimation();
123     if (double interval = memoryCache()->deadDecodedDataDeletionInterval())
124         m_decodedDataDeletionTimer.startOneShot(interval);
125 }
126
127 pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
128 {
129     if (deviceScaleFactor >= 2) {
130         DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef()));
131         return make_pair(brokenImageHiRes, 2);
132     }
133
134     DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef()));
135     return make_pair(brokenImageLoRes, 1);
136 }
137
138 bool CachedImage::willPaintBrokenImage() const
139 {
140     return errorOccurred() && m_shouldPaintBrokenImage;
141 }
142
143 #if ENABLE(SVG)
144 inline Image* CachedImage::lookupOrCreateImageForRenderer(const RenderObject* renderer)
145 {
146     if (!m_image)
147         return 0;
148     if (!m_image->isSVGImage())
149         return m_image.get();
150     Image* useImage = m_svgImageCache->lookupOrCreateBitmapImageForRenderer(renderer);
151     if (useImage == Image::nullImage())
152         return m_image.get();
153     return useImage;
154 }
155 #else
156 inline Image* CachedImage::lookupOrCreateImageForRenderer(const RenderObject*)
157 {
158     return m_image.get();
159 }
160 #endif
161
162 Image* CachedImage::image()
163 {
164     ASSERT(!isPurgeable());
165
166     if (errorOccurred() && m_shouldPaintBrokenImage) {
167         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
168         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
169         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
170         return brokenImage(1).first;
171     }
172
173     if (m_image)
174         return m_image.get();
175
176     return Image::nullImage();
177 }
178
179 Image* CachedImage::imageForRenderer(const RenderObject* renderer)
180 {
181     ASSERT(!isPurgeable());
182
183     if (errorOccurred() && m_shouldPaintBrokenImage) {
184         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
185         // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage() 
186         // when they need the real, deviceScaleFactor-appropriate broken image icon. 
187         return brokenImage(1).first;
188     }
189
190     if (m_image)
191         return lookupOrCreateImageForRenderer(renderer);
192
193     return Image::nullImage();
194 }
195
196 void CachedImage::setContainerSizeForRenderer(const RenderObject* renderer, const IntSize& containerSize, float containerZoom)
197 {
198     if (!m_image || containerSize.isEmpty())
199         return;
200 #if ENABLE(SVG)
201     if (!m_image->isSVGImage()) {
202         m_image->setContainerSize(containerSize);
203         return;
204     }
205     m_svgImageCache->setRequestedSizeAndZoom(renderer, SVGImageCache::SizeAndZoom(containerSize, containerZoom));
206 #else
207     UNUSED_PARAM(renderer);
208     m_image->setContainerSize(containerSize);
209 #endif
210 }
211
212 bool CachedImage::usesImageContainerSize() const
213 {
214     if (m_image)
215         return m_image->usesContainerSize();
216
217     return false;
218 }
219
220 bool CachedImage::imageHasRelativeWidth() const
221 {
222     if (m_image)
223         return m_image->hasRelativeWidth();
224
225     return false;
226 }
227
228 bool CachedImage::imageHasRelativeHeight() const
229 {
230     if (m_image)
231         return m_image->hasRelativeHeight();
232
233     return false;
234 }
235
236 IntSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier)
237 {
238     ASSERT(!isPurgeable());
239
240     if (!m_image)
241         return IntSize();
242 #if ENABLE(SVG)
243     if (m_image->isSVGImage()) {
244         // SVGImages already includes the zooming in its intrinsic size.
245         SVGImageCache::SizeAndZoom sizeAndZoom = m_svgImageCache->requestedSizeAndZoom(renderer);
246         if (sizeAndZoom.size.isEmpty())
247             return m_image->size();
248         if (sizeAndZoom.zoom == 1)
249             return sizeAndZoom.size;
250         if (multiplier == 1) {
251             // Consumer wants unscaled coordinates.
252             sizeAndZoom.size.setWidth(sizeAndZoom.size.width() / sizeAndZoom.zoom);
253             sizeAndZoom.size.setHeight(sizeAndZoom.size.height() / sizeAndZoom.zoom);
254             return sizeAndZoom.size;
255         }
256         return sizeAndZoom.size;
257     }
258 #endif
259
260     if (multiplier == 1.0f)
261         return m_image->size();
262         
263     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
264     bool hasWidth = m_image->size().width() > 0;
265     bool hasHeight = m_image->size().height() > 0;
266     int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
267     int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
268     if (hasWidth)
269         width = max(1, width);
270     if (hasHeight)
271         height = max(1, height);
272     return IntSize(width, height);
273 }
274
275 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
276 {
277     if (m_image)
278         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
279 }
280
281 void CachedImage::notifyObservers(const IntRect* changeRect)
282 {
283     CachedResourceClientWalker<CachedImageClient> w(m_clients);
284     while (CachedImageClient* c = w.next())
285         c->imageChanged(this, changeRect);
286 }
287
288 void CachedImage::checkShouldPaintBrokenImage()
289 {
290     if (!m_loader || m_loader->reachedTerminalState())
291         return;
292
293     m_shouldPaintBrokenImage = m_loader->frameLoader()->client()->shouldPaintBrokenImage(m_resourceRequest.url());
294 }
295
296 void CachedImage::clear()
297 {
298     destroyDecodedData();
299 #if ENABLE(SVG)
300     m_svgImageCache.clear();
301 #endif
302     m_image = 0;
303     setEncodedSize(0);
304 }
305
306 inline void CachedImage::createImage()
307 {
308     // Create the image if it doesn't yet exist.
309     if (m_image)
310         return;
311 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
312     if (m_response.mimeType() == "application/pdf") {
313         m_image = PDFDocumentImage::create();
314         return;
315     }
316 #endif
317 #if ENABLE(SVG)
318     if (m_response.mimeType() == "image/svg+xml") {
319         RefPtr<SVGImage> svgImage = SVGImage::create(this);
320         m_svgImageCache = SVGImageCache::create(svgImage.get());
321         m_image = svgImage.release();
322         return;
323     }
324 #endif
325     m_image = BitmapImage::create(this);
326 }
327
328 size_t CachedImage::maximumDecodedImageSize()
329 {
330     if (!m_loader || m_loader->reachedTerminalState())
331         return 0;
332     Settings* settings = m_loader->frameLoader()->frame()->settings();
333     return settings ? settings->maximumDecodedImageSize() : 0;
334 }
335
336 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
337 {
338     m_data = data;
339
340     createImage();
341
342     bool sizeAvailable = false;
343
344     // Have the image update its data from its internal buffer.
345     // It will not do anything now, but will delay decoding until 
346     // queried for info (like size or specific image frames).
347     sizeAvailable = m_image->setData(m_data, allDataReceived);
348
349     // Go ahead and tell our observers to try to draw if we have either
350     // received all the data or the size is known.  Each chunk from the
351     // network causes observers to repaint, which will force that chunk
352     // to decode.
353 #if ENABLE(TIZEN_PREVENT_INCREMENTAL_IMAGE_RENDERING_WORKAROUND)
354     if (/*sizeAvailable ||*/ allDataReceived) {
355 #else
356     if (sizeAvailable || allDataReceived) {
357 #endif
358         size_t maxDecodedImageSize = maximumDecodedImageSize();
359         IntSize s = m_image->size();
360         size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
361         if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
362             error(errorOccurred() ? status() : DecodeError);
363             if (inCache())
364                 memoryCache()->remove(this);
365             return;
366         }
367         
368         // It would be nice to only redraw the decoded band of the image, but with the current design
369         // (decoding delayed until painting) that seems hard.
370         notifyObservers();
371
372         if (m_image)
373             setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
374     }
375     
376     if (allDataReceived) {
377         setLoading(false);
378         checkNotify();
379     }
380 }
381
382 void CachedImage::error(CachedResource::Status status)
383 {
384     checkShouldPaintBrokenImage();
385     clear();
386     setStatus(status);
387     ASSERT(errorOccurred());
388     m_data.clear();
389     notifyObservers();
390     setLoading(false);
391     checkNotify();
392 }
393
394 void CachedImage::destroyDecodedData()
395 {
396     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
397     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
398         // Image refs the data buffer so we should not make it purgeable while the image is alive. 
399         // Invoking addClient() will reconstruct the image object.
400         m_image = 0;
401         setDecodedSize(0);
402         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
403             makePurgeable(true);
404     } else if (m_image && !errorOccurred())
405         m_image->destroyDecodedData();
406 }
407
408 void CachedImage::decodedSizeChanged(const Image* image, int delta)
409 {
410     if (!image || image != m_image)
411         return;
412     
413     setDecodedSize(decodedSize() + delta);
414 }
415
416 void CachedImage::didDraw(const Image* image)
417 {
418     if (!image || image != m_image)
419         return;
420     
421     double timeStamp = FrameView::currentPaintTimeStamp();
422     if (!timeStamp) // If didDraw is called outside of a Frame paint.
423         timeStamp = currentTime();
424     
425     CachedResource::didAccessDecodedData(timeStamp);
426 }
427
428 bool CachedImage::shouldPauseAnimation(const Image* image)
429 {
430     if (!image || image != m_image)
431         return false;
432     
433     CachedResourceClientWalker<CachedImageClient> w(m_clients);
434     while (CachedImageClient* c = w.next()) {
435         if (c->willRenderImage(this))
436             return false;
437     }
438
439     return true;
440 }
441
442 void CachedImage::animationAdvanced(const Image* image)
443 {
444     if (!image || image != m_image)
445         return;
446     notifyObservers();
447 }
448
449 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
450 {
451     if (!image || image != m_image)
452         return;
453 #if ENABLE(SVG)
454     // We have to update the cached ImageBuffers if the underlying content changed.
455     if (image->isSVGImage()) {
456         m_svgImageCache->imageContentChanged();
457         return;
458     }
459 #endif
460     notifyObservers(&rect);
461 }
462
463 } // namespace WebCore