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 "CachedImageClient.h"
29#include "CachedResourceClient.h"
30#include "CachedResourceClientWalker.h"
31#include "CachedResourceLoader.h"
32#include "FrameLoaderClient.h"
33#include "FrameLoaderTypes.h"
34#include "FrameView.h"
35#include "MemoryCache.h"
36#include "Page.h"
37#include "RenderObject.h"
38#include "ResourceBuffer.h"
39#include "Settings.h"
40#include "SubresourceLoader.h"
41#include <wtf/CurrentTime.h>
42#include <wtf/StdLibExtras.h>
43#include <wtf/Vector.h>
44
45#if USE(CG)
46#include "PDFDocumentImage.h"
47#endif
48
49#if ENABLE(SVG)
50#include "SVGImage.h"
51#endif
52
53using std::max;
54
55namespace WebCore {
56
57CachedImage::CachedImage(const ResourceRequest& resourceRequest)
58    : CachedResource(resourceRequest, ImageResource)
59    , m_image(0)
60    , m_shouldPaintBrokenImage(true)
61{
62    setStatus(Unknown);
63}
64
65CachedImage::CachedImage(Image* image)
66    : CachedResource(ResourceRequest(), ImageResource)
67    , m_image(image)
68    , m_shouldPaintBrokenImage(true)
69{
70    setStatus(Cached);
71    setLoading(false);
72}
73
74CachedImage::~CachedImage()
75{
76    clearImage();
77}
78
79void CachedImage::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
80{
81    if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages())
82        CachedResource::load(cachedResourceLoader, options);
83    else
84        setLoading(false);
85}
86
87void CachedImage::didAddClient(CachedResourceClient* c)
88{
89    if (m_data && !m_image && !errorOccurred()) {
90        createImage();
91        m_image->setData(m_data->sharedBuffer(), true);
92    }
93
94    ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
95    if (m_image && !m_image->isNull())
96        static_cast<CachedImageClient*>(c)->imageChanged(this);
97
98    CachedResource::didAddClient(c);
99}
100
101void CachedImage::didRemoveClient(CachedResourceClient* c)
102{
103    ASSERT(c);
104    ASSERT(c->resourceClientType() == CachedImageClient::expectedType());
105
106    m_pendingContainerSizeRequests.remove(static_cast<CachedImageClient*>(c));
107#if ENABLE(SVG)
108    if (m_svgImageCache)
109        m_svgImageCache->removeClientFromCache(static_cast<CachedImageClient*>(c));
110#endif
111
112    CachedResource::didRemoveClient(c);
113}
114
115void CachedImage::switchClientsToRevalidatedResource()
116{
117    ASSERT(resourceToRevalidate());
118    ASSERT(resourceToRevalidate()->isImage());
119    // Pending container size requests need to be transferred to the revalidated resource.
120    if (!m_pendingContainerSizeRequests.isEmpty()) {
121        // A copy of pending size requests is needed as they are deleted during CachedResource::switchClientsToRevalidateResouce().
122        ContainerSizeRequests switchContainerSizeRequests;
123        for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
124            switchContainerSizeRequests.set(it->key, it->value);
125        CachedResource::switchClientsToRevalidatedResource();
126        CachedImage* revalidatedCachedImage = static_cast<CachedImage*>(resourceToRevalidate());
127        for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it)
128            revalidatedCachedImage->setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
129        return;
130    }
131
132    CachedResource::switchClientsToRevalidatedResource();
133}
134
135void CachedImage::allClientsRemoved()
136{
137    m_pendingContainerSizeRequests.clear();
138    if (m_image && !errorOccurred())
139        m_image->resetAnimation();
140}
141
142pair<Image*, float> CachedImage::brokenImage(float deviceScaleFactor) const
143{
144    if (deviceScaleFactor >= 2) {
145        DEFINE_STATIC_LOCAL(Image*, brokenImageHiRes, (Image::loadPlatformResource("missingImage@2x").leakRef()));
146        return std::make_pair(brokenImageHiRes, 2);
147    }
148
149    DEFINE_STATIC_LOCAL(Image*, brokenImageLoRes, (Image::loadPlatformResource("missingImage").leakRef()));
150    return std::make_pair(brokenImageLoRes, 1);
151}
152
153bool CachedImage::willPaintBrokenImage() const
154{
155    return errorOccurred() && m_shouldPaintBrokenImage;
156}
157
158Image* CachedImage::image()
159{
160    ASSERT(!isPurgeable());
161
162    if (errorOccurred() && m_shouldPaintBrokenImage) {
163        // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
164        // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
165        // when they need the real, deviceScaleFactor-appropriate broken image icon.
166        return brokenImage(1).first;
167    }
168
169    if (m_image)
170        return m_image.get();
171
172    return Image::nullImage();
173}
174
175Image* CachedImage::imageForRenderer(const RenderObject* renderer)
176{
177    ASSERT(!isPurgeable());
178
179    if (errorOccurred() && m_shouldPaintBrokenImage) {
180        // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
181        // deviceScaleFactor from here. It is critical that callers use CachedImage::brokenImage()
182        // when they need the real, deviceScaleFactor-appropriate broken image icon.
183        return brokenImage(1).first;
184    }
185
186    if (!m_image)
187        return Image::nullImage();
188
189#if ENABLE(SVG)
190    if (m_image->isSVGImage()) {
191        Image* image = m_svgImageCache->imageForRenderer(renderer);
192        if (image != Image::nullImage())
193            return image;
194    }
195#else
196    UNUSED_PARAM(renderer);
197#endif
198    return m_image.get();
199}
200
201void CachedImage::setContainerSizeForRenderer(const CachedImageClient* renderer, const IntSize& containerSize, float containerZoom)
202{
203    if (containerSize.isEmpty())
204        return;
205    ASSERT(renderer);
206    ASSERT(containerZoom);
207    if (!m_image) {
208        m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
209        return;
210    }
211#if ENABLE(SVG)
212    if (!m_image->isSVGImage()) {
213        m_image->setContainerSize(containerSize);
214        return;
215    }
216
217    m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
218#else
219    UNUSED_PARAM(containerZoom);
220    m_image->setContainerSize(containerSize);
221#endif
222}
223
224bool CachedImage::usesImageContainerSize() const
225{
226    if (m_image)
227        return m_image->usesContainerSize();
228
229    return false;
230}
231
232bool CachedImage::imageHasRelativeWidth() const
233{
234    if (m_image)
235        return m_image->hasRelativeWidth();
236
237    return false;
238}
239
240bool CachedImage::imageHasRelativeHeight() const
241{
242    if (m_image)
243        return m_image->hasRelativeHeight();
244
245    return false;
246}
247
248LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier)
249{
250    ASSERT(!isPurgeable());
251
252    if (!m_image)
253        return IntSize();
254
255    LayoutSize imageSize;
256
257    if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
258        imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation();
259#if ENABLE(SVG)
260    else if (m_image->isSVGImage()) {
261        imageSize = m_svgImageCache->imageSizeForRenderer(renderer);
262    }
263#endif
264    else
265        imageSize = m_image->size();
266
267    if (multiplier == 1.0f)
268        return imageSize;
269
270    // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
271    float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
272    float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
273    LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
274    imageSize.scale(widthScale, heightScale);
275    imageSize.clampToMinimumSize(minimumSize);
276    ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
277    return imageSize;
278}
279
280void CachedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
281{
282    if (m_image)
283        m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
284}
285
286void CachedImage::notifyObservers(const IntRect* changeRect)
287{
288    CachedResourceClientWalker<CachedImageClient> w(m_clients);
289    while (CachedImageClient* c = w.next())
290        c->imageChanged(this, changeRect);
291}
292
293void CachedImage::checkShouldPaintBrokenImage()
294{
295    if (!m_loader || m_loader->reachedTerminalState())
296        return;
297
298    m_shouldPaintBrokenImage = m_loader->frameLoader()->client()->shouldPaintBrokenImage(m_resourceRequest.url());
299}
300
301void CachedImage::clear()
302{
303    destroyDecodedData();
304    clearImage();
305    m_pendingContainerSizeRequests.clear();
306    setEncodedSize(0);
307}
308
309inline void CachedImage::createImage()
310{
311    // Create the image if it doesn't yet exist.
312    if (m_image)
313        return;
314#if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
315    else if (m_response.mimeType() == "application/pdf")
316        m_image = PDFDocumentImage::create(this);
317#endif
318#if ENABLE(SVG)
319    else if (m_response.mimeType() == "image/svg+xml") {
320        RefPtr<SVGImage> svgImage = SVGImage::create(this);
321        m_svgImageCache = SVGImageCache::create(svgImage.get());
322        m_image = svgImage.release();
323    }
324#endif
325    else
326        m_image = BitmapImage::create(this);
327
328    if (m_image) {
329        // Send queued container size requests.
330        if (m_image->usesContainerSize()) {
331            for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
332                setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
333        }
334        m_pendingContainerSizeRequests.clear();
335    }
336}
337
338inline void CachedImage::clearImage()
339{
340    // If our Image has an observer, it's always us so we need to clear the back pointer
341    // before dropping our reference.
342    if (m_image)
343        m_image->setImageObserver(0);
344    m_image.clear();
345}
346
347bool CachedImage::canBeDrawn() const
348{
349    if (!m_image || m_image->isNull())
350        return false;
351
352    if (!m_loader || m_loader->reachedTerminalState())
353        return true;
354
355    Settings* settings = m_loader->frameLoader()->frame()->settings();
356    if (!settings)
357        return true;
358
359    size_t estimatedDecodedImageSize = m_image->width() * m_image->height() * 4; // no overflow check
360    return estimatedDecodedImageSize <= settings->maximumDecodedImageSize();
361}
362
363void CachedImage::addIncrementalDataBuffer(ResourceBuffer* data)
364{
365    m_data = data;
366    if (!data)
367        return;
368
369    createImage();
370
371    // Have the image update its data from its internal buffer.
372    // It will not do anything now, but will delay decoding until
373    // queried for info (like size or specific image frames).
374    bool sizeAvailable = m_image->setData(m_data->sharedBuffer(), false);
375    if (!sizeAvailable)
376        return;
377
378    if (!canBeDrawn()) {
379        // There's no image to draw or its decoded size is bigger than the maximum allowed.
380        error(errorOccurred() ? status() : DecodeError);
381        if (inCache())
382            memoryCache()->remove(this);
383        return;
384    }
385
386    // Go ahead and tell our observers to try to draw.
387    // Each chunk from the network causes observers to repaint, which will
388    // force that chunk to decode.
389    // It would be nice to only redraw the decoded band of the image, but with the current design
390    // (decoding delayed until painting) that seems hard.
391    notifyObservers();
392
393    setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
394}
395
396void CachedImage::addDataBuffer(ResourceBuffer* data)
397{
398    ASSERT(m_options.dataBufferingPolicy == BufferData);
399    addIncrementalDataBuffer(data);
400}
401
402void CachedImage::addData(const char* data, unsigned length)
403{
404    ASSERT(m_options.dataBufferingPolicy == DoNotBufferData);
405    addIncrementalDataBuffer(ResourceBuffer::create(data, length).get());
406}
407
408void CachedImage::finishLoading(ResourceBuffer* data)
409{
410    m_data = data;
411    if (!m_image && data)
412        createImage();
413
414    if (m_image)
415        m_image->setData(m_data->sharedBuffer(), true);
416
417    if (!canBeDrawn()) {
418        // There's no image to draw or its decoded size is bigger than the maximum allowed.
419        error(errorOccurred() ? status() : DecodeError);
420        if (inCache())
421            memoryCache()->remove(this);
422        return;
423    }
424
425    notifyObservers();
426    if (m_image)
427        setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
428    CachedResource::finishLoading(data);
429}
430
431void CachedImage::error(CachedResource::Status status)
432{
433    checkShouldPaintBrokenImage();
434    clear();
435    CachedResource::error(status);
436    notifyObservers();
437}
438
439void CachedImage::responseReceived(const ResourceResponse& response)
440{
441    if (!m_response.isNull())
442        clear();
443    CachedResource::responseReceived(response);
444}
445
446void CachedImage::destroyDecodedData()
447{
448    bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
449    if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
450        // Image refs the data buffer so we should not make it purgeable while the image is alive.
451        // Invoking addClient() will reconstruct the image object.
452        m_image = 0;
453        setDecodedSize(0);
454        if (!MemoryCache::shouldMakeResourcePurgeableOnEviction())
455            makePurgeable(true);
456    } else if (m_image && !errorOccurred())
457        m_image->destroyDecodedData();
458}
459
460void CachedImage::decodedSizeChanged(const Image* image, int delta)
461{
462    if (!image || image != m_image)
463        return;
464
465    setDecodedSize(decodedSize() + delta);
466}
467
468void CachedImage::didDraw(const Image* image)
469{
470    if (!image || image != m_image)
471        return;
472
473    double timeStamp = FrameView::currentPaintTimeStamp();
474    if (!timeStamp) // If didDraw is called outside of a Frame paint.
475        timeStamp = currentTime();
476
477    CachedResource::didAccessDecodedData(timeStamp);
478}
479
480bool CachedImage::shouldPauseAnimation(const Image* image)
481{
482    if (!image || image != m_image)
483        return false;
484
485    CachedResourceClientWalker<CachedImageClient> w(m_clients);
486    while (CachedImageClient* c = w.next()) {
487        if (c->willRenderImage(this))
488            return false;
489    }
490
491    return true;
492}
493
494void CachedImage::animationAdvanced(const Image* image)
495{
496    if (!image || image != m_image)
497        return;
498    notifyObservers();
499}
500
501void CachedImage::changedInRect(const Image* image, const IntRect& rect)
502{
503    if (!image || image != m_image)
504        return;
505    notifyObservers(&rect);
506}
507
508void CachedImage::resumeAnimatingImagesForLoader(CachedResourceLoader* loader)
509{
510    const CachedResourceLoader::DocumentResourceMap& resources = loader->allCachedResources();
511
512    for (CachedResourceLoader::DocumentResourceMap::const_iterator it = resources.begin(), end = resources.end(); it != end; ++it) {
513        const CachedResourceHandle<CachedResource>& resource = it->value;
514        if (!resource || !resource->isImage())
515            continue;
516        CachedImage* cachedImage = static_cast<CachedImage*>(resource.get());
517        if (!cachedImage->hasImage())
518            continue;
519        Image* image = cachedImage->image();
520        if (!image->isBitmapImage())
521            continue;
522        BitmapImage* bitmapImage = static_cast<BitmapImage*>(image);
523        if (!bitmapImage->canAnimate())
524            continue;
525        cachedImage->animationAdvanced(bitmapImage);
526    }
527}
528
529bool CachedImage::currentFrameKnownToBeOpaque(const RenderObject* renderer)
530{
531    Image* image = imageForRenderer(renderer);
532    if (image->isBitmapImage())
533        image->nativeImageForCurrentFrame(); // force decode
534    return image->currentFrameKnownToBeOpaque();
535}
536
537} // namespace WebCore
538