WebCore/loader/CachedImage.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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 "SharedBuffer.h"
       
    38 #include <wtf/CurrentTime.h>
       
    39 #include <wtf/StdLibExtras.h>
       
    40 #include <wtf/Vector.h>
       
    41 
       
    42 #if PLATFORM(CG)
       
    43 #include "PDFDocumentImage.h"
       
    44 #endif
       
    45 
       
    46 #if ENABLE(SVG_AS_IMAGE)
       
    47 #include "SVGImage.h"
       
    48 #endif
       
    49 
       
    50 using std::max;
       
    51 
       
    52 namespace WebCore {
       
    53 
       
    54 CachedImage::CachedImage(const String& url)
       
    55     : CachedResource(url, ImageResource)
       
    56     , m_image(0)
       
    57     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
       
    58     , m_httpStatusCodeErrorOccurred(false)
       
    59 {
       
    60     setStatus(Unknown);
       
    61 }
       
    62 
       
    63 CachedImage::CachedImage(Image* image)
       
    64     : CachedResource(String(), ImageResource)
       
    65     , m_image(image)
       
    66     , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
       
    67     , m_httpStatusCodeErrorOccurred(false)
       
    68 {
       
    69     setStatus(Cached);
       
    70     setLoading(false);
       
    71 }
       
    72 
       
    73 CachedImage::~CachedImage()
       
    74 {
       
    75 }
       
    76 
       
    77 void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
       
    78 {
       
    79     ASSERT(!hasClients());
       
    80     destroyDecodedData();
       
    81 }
       
    82 
       
    83 void CachedImage::load(DocLoader* docLoader)
       
    84 {
       
    85     if (!docLoader || docLoader->autoLoadImages())
       
    86         CachedResource::load(docLoader, true, DoSecurityCheck, true);
       
    87     else
       
    88         setLoading(false);
       
    89 }
       
    90 
       
    91 void CachedImage::didAddClient(CachedResourceClient* c)
       
    92 {
       
    93     if (m_decodedDataDeletionTimer.isActive())
       
    94         m_decodedDataDeletionTimer.stop();
       
    95     
       
    96     if (m_data && !m_image && !errorOccurred()) {
       
    97         createImage();
       
    98         m_image->setData(m_data, true);
       
    99     }
       
   100 
       
   101     if (m_image && !m_image->isNull())
       
   102         c->imageChanged(this);
       
   103 
       
   104     if (!isLoading())
       
   105         c->notifyFinished(this);
       
   106 }
       
   107 
       
   108 void CachedImage::allClientsRemoved()
       
   109 {
       
   110     if (m_image && !errorOccurred())
       
   111         m_image->resetAnimation();
       
   112     if (double interval = cache()->deadDecodedDataDeletionInterval())
       
   113         m_decodedDataDeletionTimer.startOneShot(interval);
       
   114 }
       
   115 
       
   116 static Image* brokenImage()
       
   117 {
       
   118     DEFINE_STATIC_LOCAL(RefPtr<Image>, brokenImage, (Image::loadPlatformResource("missingImage")));
       
   119     return brokenImage.get();
       
   120 }
       
   121 
       
   122 static Image* nullImage()
       
   123 {
       
   124     DEFINE_STATIC_LOCAL(RefPtr<BitmapImage>, nullImage, (BitmapImage::create()));
       
   125     return nullImage.get();
       
   126 }
       
   127 
       
   128 Image* CachedImage::image() const
       
   129 {
       
   130     ASSERT(!isPurgeable());
       
   131 
       
   132     if (errorOccurred())
       
   133         return brokenImage();
       
   134 
       
   135     if (m_image)
       
   136         return m_image.get();
       
   137 
       
   138     return nullImage();
       
   139 }
       
   140 
       
   141 void CachedImage::setImageContainerSize(const IntSize& containerSize)
       
   142 {
       
   143     if (m_image)
       
   144         m_image->setContainerSize(containerSize);
       
   145 }
       
   146 
       
   147 bool CachedImage::usesImageContainerSize() const
       
   148 {
       
   149     if (m_image)
       
   150         return m_image->usesContainerSize();
       
   151 
       
   152     return false;
       
   153 }
       
   154 
       
   155 bool CachedImage::imageHasRelativeWidth() const
       
   156 {
       
   157     if (m_image)
       
   158         return m_image->hasRelativeWidth();
       
   159 
       
   160     return false;
       
   161 }
       
   162 
       
   163 bool CachedImage::imageHasRelativeHeight() const
       
   164 {
       
   165     if (m_image)
       
   166         return m_image->hasRelativeHeight();
       
   167 
       
   168     return false;
       
   169 }
       
   170 
       
   171 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 
       
   192 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 
       
   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 
       
   228 void CachedImage::clear()
       
   229 {
       
   230     destroyDecodedData();
       
   231     m_image = 0;
       
   232     setEncodedSize(0);
       
   233 }
       
   234 
       
   235 inline void CachedImage::createImage()
       
   236 {
       
   237     // Create the image if it doesn't yet exist.
       
   238     if (m_image)
       
   239         return;
       
   240 #if PLATFORM(CG)
       
   241     if (m_response.mimeType() == "application/pdf") {
       
   242         m_image = PDFDocumentImage::create();
       
   243         return;
       
   244     }
       
   245 #endif
       
   246 #if ENABLE(SVG_AS_IMAGE)
       
   247     if (m_response.mimeType() == "image/svg+xml") {
       
   248         m_image = SVGImage::create(this);
       
   249         return;
       
   250     }
       
   251 #endif
       
   252     m_image = BitmapImage::create(this);
       
   253 }
       
   254 
       
   255 size_t CachedImage::maximumDecodedImageSize()
       
   256 {
       
   257     Frame* frame = m_request ? m_request->docLoader()->frame() : 0;
       
   258     if (!frame)
       
   259         return 0;
       
   260     Settings* settings = frame->settings();
       
   261     return settings ? settings->maximumDecodedImageSize() : 0;
       
   262 }
       
   263 
       
   264 void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
       
   265 {
       
   266     m_data = data;
       
   267 
       
   268     createImage();
       
   269 
       
   270     bool sizeAvailable = false;
       
   271 
       
   272     // Have the image update its data from its internal buffer.
       
   273     // It will not do anything now, but will delay decoding until 
       
   274     // queried for info (like size or specific image frames).
       
   275     sizeAvailable = m_image->setData(m_data, allDataReceived);
       
   276 
       
   277     // Go ahead and tell our observers to try to draw if we have either
       
   278     // received all the data or the size is known.  Each chunk from the
       
   279     // network causes observers to repaint, which will force that chunk
       
   280     // to decode.
       
   281     if (sizeAvailable || allDataReceived) {
       
   282         size_t maxDecodedImageSize = maximumDecodedImageSize();
       
   283         IntSize s = imageSize(1.0f);
       
   284         size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
       
   285         if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
       
   286             error();
       
   287             if (inCache())
       
   288                 cache()->remove(this);
       
   289             return;
       
   290         }
       
   291         
       
   292         // It would be nice to only redraw the decoded band of the image, but with the current design
       
   293         // (decoding delayed until painting) that seems hard.
       
   294         notifyObservers();
       
   295 
       
   296         if (m_image)
       
   297             setEncodedSize(m_image->data() ? m_image->data()->size() : 0);
       
   298     }
       
   299     
       
   300     if (allDataReceived) {
       
   301         setLoading(false);
       
   302         checkNotify();
       
   303     }
       
   304 }
       
   305 
       
   306 void CachedImage::error()
       
   307 {
       
   308     clear();
       
   309     setErrorOccurred(true);
       
   310     m_data.clear();
       
   311     notifyObservers();
       
   312     setLoading(false);
       
   313     checkNotify();
       
   314 }
       
   315 
       
   316 void CachedImage::checkNotify()
       
   317 {
       
   318     if (isLoading())
       
   319         return;
       
   320 
       
   321     CachedResourceClientWalker w(m_clients);
       
   322     while (CachedResourceClient* c = w.next())
       
   323         c->notifyFinished(this);
       
   324 }
       
   325 
       
   326 void CachedImage::destroyDecodedData()
       
   327 {
       
   328     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
       
   329     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
       
   330         // Image refs the data buffer so we should not make it purgeable while the image is alive. 
       
   331         // Invoking addClient() will reconstruct the image object.
       
   332         m_image = 0;
       
   333         setDecodedSize(0);
       
   334         makePurgeable(true);
       
   335     } else if (m_image && !errorOccurred())
       
   336         m_image->destroyDecodedData();
       
   337 }
       
   338 
       
   339 void CachedImage::decodedSizeChanged(const Image* image, int delta)
       
   340 {
       
   341     if (image != m_image)
       
   342         return;
       
   343     
       
   344     setDecodedSize(decodedSize() + delta);
       
   345 }
       
   346 
       
   347 void CachedImage::didDraw(const Image* image)
       
   348 {
       
   349     if (image != m_image)
       
   350         return;
       
   351     
       
   352     double timeStamp = FrameView::currentPaintTimeStamp();
       
   353     if (!timeStamp) // If didDraw is called outside of a Frame paint.
       
   354         timeStamp = currentTime();
       
   355     
       
   356     CachedResource::didAccessDecodedData(timeStamp);
       
   357 }
       
   358 
       
   359 bool CachedImage::shouldPauseAnimation(const Image* image)
       
   360 {
       
   361     if (image != m_image)
       
   362         return false;
       
   363     
       
   364     CachedResourceClientWalker w(m_clients);
       
   365     while (CachedResourceClient* c = w.next()) {
       
   366         if (c->willRenderImage(this))
       
   367             return false;
       
   368     }
       
   369 
       
   370     return true;
       
   371 }
       
   372 
       
   373 void CachedImage::animationAdvanced(const Image* image)
       
   374 {
       
   375     if (image == m_image)
       
   376         notifyObservers();
       
   377 }
       
   378 
       
   379 void CachedImage::changedInRect(const Image* image, const IntRect& rect)
       
   380 {
       
   381     if (image == m_image)
       
   382         notifyObservers(&rect);
       
   383 }
       
   384 
       
   385 } //namespace WebCore