Unreviewed, rolling out r112201.
[WebKit-https.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     UNUSED_PARAM(containerZoom);
209     m_image->setContainerSize(containerSize);
210 #endif
211 }
212
213 bool CachedImage::usesImageContainerSize() const
214 {
215     if (m_image)
216         return m_image->usesContainerSize();
217
218     return false;
219 }
220
221 bool CachedImage::imageHasRelativeWidth() const
222 {
223     if (m_image)
224         return m_image->hasRelativeWidth();
225
226     return false;
227 }
228
229 bool CachedImage::imageHasRelativeHeight() const
230 {
231     if (m_image)
232         return m_image->hasRelativeHeight();
233
234     return false;
235 }
236
237 IntSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier)
238 {
239     ASSERT(!isPurgeable());
240
241     if (!m_image)
242         return IntSize();
243
244     IntSize imageSize = m_image->size();
245
246 #if ENABLE(SVG)
247     if (m_image->isSVGImage()) {
248         SVGImageCache::SizeAndZoom sizeAndZoom = m_svgImageCache->requestedSizeAndZoom(renderer);
249         if (!sizeAndZoom.size.isEmpty()) {
250             imageSize.setWidth(sizeAndZoom.size.width() / sizeAndZoom.zoom);
251             imageSize.setHeight(sizeAndZoom.size.height() / sizeAndZoom.zoom);
252         }
253     }
254 #else
255     UNUSED_PARAM(renderer);
256 #endif
257
258     if (multiplier == 1.0f)
259         return imageSize;
260         
261     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
262     bool hasWidth = imageSize.width() > 0;
263     bool hasHeight = imageSize.height() > 0;
264     int width = imageSize.width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
265     int height = imageSize.height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
266     if (hasWidth)
267         width = max(1, width);
268     if (hasHeight)
269         height = max(1, height);
270     return IntSize(width, height);
271 }
272
273 void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio, float scaleFactor)
274 {
275     if (m_image)
276         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio, scaleFactor);
277 }
278
279 void CachedImage::notifyObservers(const IntRect* changeRect)
280 {
281     CachedResourceClientWalker<CachedImageClient> w(m_clients);
282     while (CachedImageClient* c = w.next())
283         c->imageChanged(this, changeRect);
284 }
285
286 void CachedImage::checkShouldPaintBrokenImage()
287 {
288     if (!m_loader || m_loader->reachedTerminalState())
289         return;
290
291     m_shouldPaintBrokenImage = m_loader->frameLoader()->client()->shouldPaintBrokenImage(m_resourceRequest.url());
292 }
293
294 void CachedImage::clear()
295 {
296     destroyDecodedData();
297 #if ENABLE(SVG)
298     m_svgImageCache.clear();
299 #endif
300     m_image = 0;
301     setEncodedSize(0);
302 }
303
304 inline void CachedImage::createImage()
305 {
306     // Create the image if it doesn't yet exist.
307     if (m_image)
308         return;
309 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
310     if (m_response.mimeType() == "application/pdf") {
311         m_image = PDFDocumentImage::create();
312         return;
313     }
314 #endif
315 #if ENABLE(SVG)
316     if (m_response.mimeType() == "image/svg+xml") {
317         RefPtr<SVGImage> svgImage = SVGImage::create(this);
318         m_svgImageCache = SVGImageCache::create(svgImage.get());
319         m_image = svgImage.release();
320         return;
321     }
322 #endif
323     m_image = BitmapImage::create(this);
324 }
325
326 size_t CachedImage::maximumDecodedImageSize()
327 {
328     if (!m_loader || m_loader->reachedTerminalState())
329         return 0;
330     Settings* settings = m_loader->frameLoader()->frame()->settings();
331     return settings ? settings->maximumDecodedImageSize() : 0;
332 }
333
334 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
335 {
336     m_data = data;
337
338     createImage();
339
340     bool sizeAvailable = false;
341
342     // Have the image update its data from its internal buffer.
343     // It will not do anything now, but will delay decoding until 
344     // queried for info (like size or specific image frames).
345     sizeAvailable = m_image->setData(m_data, allDataReceived);
346
347     // Go ahead and tell our observers to try to draw if we have either
348     // received all the data or the size is known.  Each chunk from the
349     // network causes observers to repaint, which will force that chunk
350     // to decode.
351     if (sizeAvailable || allDataReceived) {
352         size_t maxDecodedImageSize = maximumDecodedImageSize();
353         IntSize s = m_image->size();
354         size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
355         if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
356             error(errorOccurred() ? status() : DecodeError);
357             if (inCache())
358                 memoryCache()->remove(this);
359             return;
360         }
361         
362         // It would be nice to only redraw the decoded band of the image, but with the current design
363         // (decoding delayed until painting) that seems hard.
364         notifyObservers();
365
366         if (m_image)
367             setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
368     }
369     
370     if (allDataReceived) {
371         setLoading(false);
372         checkNotify();
373     }
374 }
375
376 void CachedImage::error(CachedResource::Status status)
377 {
378     checkShouldPaintBrokenImage();
379     clear();
380     setStatus(status);
381     ASSERT(errorOccurred());
382     m_data.clear();
383     notifyObservers();
384     setLoading(false);
385     checkNotify();
386 }
387
388 void CachedImage::setResponse(const ResourceResponse& response)
389 {
390     if (!m_response.isNull())
391         clear();
392     CachedResource::setResponse(response);
393 }
394
395 void CachedImage::destroyDecodedData()
396 {
397     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
398     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
399         // Image refs the data buffer so we should not make it purgeable while the image is alive. 
400         // Invoking addClient() will reconstruct the image object.
401         m_image = 0;
402         setDecodedSize(0);
403         if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
404             makePurgeable(true);
405     } else if (m_image && !errorOccurred())
406         m_image->destroyDecodedData();
407 }
408
409 void CachedImage::decodedSizeChanged(const Image* image, int delta)
410 {
411     if (!image || image != m_image)
412         return;
413     
414     setDecodedSize(decodedSize() + delta);
415 }
416
417 void CachedImage::didDraw(const Image* image)
418 {
419     if (!image || image != m_image)
420         return;
421     
422     double timeStamp = FrameView::currentPaintTimeStamp();
423     if (!timeStamp) // If didDraw is called outside of a Frame paint.
424         timeStamp = currentTime();
425     
426     CachedResource::didAccessDecodedData(timeStamp);
427 }
428
429 bool CachedImage::shouldPauseAnimation(const Image* image)
430 {
431     if (!image || image != m_image)
432         return false;
433     
434     CachedResourceClientWalker<CachedImageClient> w(m_clients);
435     while (CachedImageClient* c = w.next()) {
436         if (c->willRenderImage(this))
437             return false;
438     }
439
440     return true;
441 }
442
443 void CachedImage::animationAdvanced(const Image* image)
444 {
445     if (!image || image != m_image)
446         return;
447     notifyObservers();
448 }
449
450 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
451 {
452     if (!image || image != m_image)
453         return;
454 #if ENABLE(SVG)
455     // We have to update the cached ImageBuffers if the underlying content changed.
456     if (image->isSVGImage()) {
457         m_svgImageCache->imageContentChanged();
458         return;
459     }
460 #endif
461     notifyObservers(&rect);
462 }
463
464 } // namespace WebCore