Home | History | Annotate | Download | only in cache
      1 /*
      2     Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de)
      3     Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org)
      4     Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org)
      5     Copyright (C) 2006 Samuel Weinig (sam.weinig (at) 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 "core/loader/cache/ImageResource.h"
     26 
     27 #include "core/loader/cache/ImageResourceClient.h"
     28 #include "core/loader/cache/MemoryCache.h"
     29 #include "core/loader/cache/ResourceClient.h"
     30 #include "core/loader/cache/ResourceClientWalker.h"
     31 #include "core/loader/cache/ResourceFetcher.h"
     32 #include "core/page/FrameView.h"
     33 #include "core/platform/SharedBuffer.h"
     34 #include "core/platform/graphics/BitmapImage.h"
     35 #include "core/rendering/RenderObject.h"
     36 #include "core/svg/graphics/SVGImage.h"
     37 #include "wtf/CurrentTime.h"
     38 #include "wtf/StdLibExtras.h"
     39 #include "wtf/Vector.h"
     40 
     41 using namespace std;
     42 
     43 namespace WebCore {
     44 
     45 ImageResource::ImageResource(const ResourceRequest& resourceRequest)
     46     : Resource(resourceRequest, Image)
     47     , m_image(0)
     48     , m_loadingMultipartContent(false)
     49 {
     50     setStatus(Unknown);
     51     setCustomAcceptHeader();
     52 }
     53 
     54 ImageResource::ImageResource(WebCore::Image* image)
     55     : Resource(ResourceRequest(), Image)
     56     , m_image(image)
     57 {
     58     setStatus(Cached);
     59     setLoading(false);
     60     setCustomAcceptHeader();
     61 }
     62 
     63 ImageResource::~ImageResource()
     64 {
     65     clearImage();
     66 }
     67 
     68 void ImageResource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)
     69 {
     70     if (!fetcher || fetcher->autoLoadImages())
     71         Resource::load(fetcher, options);
     72     else
     73         setLoading(false);
     74 }
     75 
     76 void ImageResource::didAddClient(ResourceClient* c)
     77 {
     78     if (m_data && !m_image && !errorOccurred()) {
     79         createImage();
     80         m_image->setData(m_data, true);
     81     }
     82 
     83     ASSERT(c->resourceClientType() == ImageResourceClient::expectedType());
     84     if (m_image && !m_image->isNull())
     85         static_cast<ImageResourceClient*>(c)->imageChanged(this);
     86 
     87     Resource::didAddClient(c);
     88 }
     89 
     90 void ImageResource::didRemoveClient(ResourceClient* c)
     91 {
     92     ASSERT(c);
     93     ASSERT(c->resourceClientType() == ImageResourceClient::expectedType());
     94 
     95     m_pendingContainerSizeRequests.remove(static_cast<ImageResourceClient*>(c));
     96     if (m_svgImageCache)
     97         m_svgImageCache->removeClientFromCache(static_cast<ImageResourceClient*>(c));
     98 
     99     Resource::didRemoveClient(c);
    100 }
    101 
    102 void ImageResource::switchClientsToRevalidatedResource()
    103 {
    104     ASSERT(resourceToRevalidate());
    105     ASSERT(resourceToRevalidate()->isImage());
    106     // Pending container size requests need to be transferred to the revalidated resource.
    107     if (!m_pendingContainerSizeRequests.isEmpty()) {
    108         // A copy of pending size requests is needed as they are deleted during Resource::switchClientsToRevalidateResouce().
    109         ContainerSizeRequests switchContainerSizeRequests;
    110         for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
    111             switchContainerSizeRequests.set(it->key, it->value);
    112         Resource::switchClientsToRevalidatedResource();
    113         ImageResource* revalidatedImageResource = static_cast<ImageResource*>(resourceToRevalidate());
    114         for (ContainerSizeRequests::iterator it = switchContainerSizeRequests.begin(); it != switchContainerSizeRequests.end(); ++it)
    115             revalidatedImageResource->setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
    116         return;
    117     }
    118 
    119     Resource::switchClientsToRevalidatedResource();
    120 }
    121 
    122 void ImageResource::allClientsRemoved()
    123 {
    124     m_pendingContainerSizeRequests.clear();
    125     if (m_image && !errorOccurred())
    126         m_image->resetAnimation();
    127     Resource::allClientsRemoved();
    128 }
    129 
    130 pair<WebCore::Image*, float> ImageResource::brokenImage(float deviceScaleFactor) const
    131 {
    132     if (deviceScaleFactor >= 2) {
    133         DEFINE_STATIC_LOCAL(WebCore::Image*, brokenImageHiRes, (WebCore::Image::loadPlatformResource("missingImage@2x").leakRef()));
    134         return std::make_pair(brokenImageHiRes, 2);
    135     }
    136 
    137     DEFINE_STATIC_LOCAL(WebCore::Image*, brokenImageLoRes, (WebCore::Image::loadPlatformResource("missingImage").leakRef()));
    138     return std::make_pair(brokenImageLoRes, 1);
    139 }
    140 
    141 bool ImageResource::willPaintBrokenImage() const
    142 {
    143     return errorOccurred();
    144 }
    145 
    146 WebCore::Image* ImageResource::image()
    147 {
    148     ASSERT(!isPurgeable());
    149 
    150     if (errorOccurred()) {
    151         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
    152         // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage()
    153         // when they need the real, deviceScaleFactor-appropriate broken image icon.
    154         return brokenImage(1).first;
    155     }
    156 
    157     if (m_image)
    158         return m_image.get();
    159 
    160     return WebCore::Image::nullImage();
    161 }
    162 
    163 WebCore::Image* ImageResource::imageForRenderer(const RenderObject* renderer)
    164 {
    165     ASSERT(!isPurgeable());
    166 
    167     if (errorOccurred()) {
    168         // Returning the 1x broken image is non-ideal, but we cannot reliably access the appropriate
    169         // deviceScaleFactor from here. It is critical that callers use ImageResource::brokenImage()
    170         // when they need the real, deviceScaleFactor-appropriate broken image icon.
    171         return brokenImage(1).first;
    172     }
    173 
    174     if (!m_image)
    175         return WebCore::Image::nullImage();
    176 
    177     if (m_image->isSVGImage()) {
    178         WebCore::Image* image = m_svgImageCache->imageForRenderer(renderer);
    179         if (image != WebCore::Image::nullImage())
    180             return image;
    181     }
    182 
    183     return m_image.get();
    184 }
    185 
    186 void ImageResource::setContainerSizeForRenderer(const ImageResourceClient* renderer, const IntSize& containerSize, float containerZoom)
    187 {
    188     if (containerSize.isEmpty())
    189         return;
    190     ASSERT(renderer);
    191     ASSERT(containerZoom);
    192     if (!m_image) {
    193         m_pendingContainerSizeRequests.set(renderer, SizeAndZoom(containerSize, containerZoom));
    194         return;
    195     }
    196     if (!m_image->isSVGImage()) {
    197         m_image->setContainerSize(containerSize);
    198         return;
    199     }
    200 
    201     m_svgImageCache->setContainerSizeForRenderer(renderer, containerSize, containerZoom);
    202 }
    203 
    204 bool ImageResource::usesImageContainerSize() const
    205 {
    206     if (m_image)
    207         return m_image->usesContainerSize();
    208 
    209     return false;
    210 }
    211 
    212 bool ImageResource::imageHasRelativeWidth() const
    213 {
    214     if (m_image)
    215         return m_image->hasRelativeWidth();
    216 
    217     return false;
    218 }
    219 
    220 bool ImageResource::imageHasRelativeHeight() const
    221 {
    222     if (m_image)
    223         return m_image->hasRelativeHeight();
    224 
    225     return false;
    226 }
    227 
    228 LayoutSize ImageResource::imageSizeForRenderer(const RenderObject* renderer, float multiplier)
    229 {
    230     ASSERT(!isPurgeable());
    231 
    232     if (!m_image)
    233         return IntSize();
    234 
    235     LayoutSize imageSize;
    236 
    237     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
    238         imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation();
    239     else if (m_image->isSVGImage())
    240         imageSize = m_svgImageCache->imageSizeForRenderer(renderer);
    241     else
    242         imageSize = m_image->size();
    243 
    244     if (multiplier == 1.0f)
    245         return imageSize;
    246 
    247     // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
    248     float widthScale = m_image->hasRelativeWidth() ? 1.0f : multiplier;
    249     float heightScale = m_image->hasRelativeHeight() ? 1.0f : multiplier;
    250     LayoutSize minimumSize(imageSize.width() > 0 ? 1 : 0, imageSize.height() > 0 ? 1 : 0);
    251     imageSize.scale(widthScale, heightScale);
    252     imageSize.clampToMinimumSize(minimumSize);
    253     ASSERT(multiplier != 1.0f || (imageSize.width().fraction() == 0.0f && imageSize.height().fraction() == 0.0f));
    254     return imageSize;
    255 }
    256 
    257 void ImageResource::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
    258 {
    259     if (m_image)
    260         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
    261 }
    262 
    263 void ImageResource::notifyObservers(const IntRect* changeRect)
    264 {
    265     ResourceClientWalker<ImageResourceClient> w(m_clients);
    266     while (ImageResourceClient* c = w.next())
    267         c->imageChanged(this, changeRect);
    268 }
    269 
    270 void ImageResource::clear()
    271 {
    272     destroyDecodedData();
    273     clearImage();
    274     m_pendingContainerSizeRequests.clear();
    275     setEncodedSize(0);
    276 }
    277 
    278 void ImageResource::setCustomAcceptHeader()
    279 {
    280     DEFINE_STATIC_LOCAL(const AtomicString, acceptWebP, ("image/webp,*/*;q=0.8", AtomicString::ConstructFromLiteral));
    281     setAccept(acceptWebP);
    282 }
    283 
    284 inline void ImageResource::createImage()
    285 {
    286     // Create the image if it doesn't yet exist.
    287     if (m_image)
    288         return;
    289 
    290     if (m_response.mimeType() == "image/svg+xml") {
    291         RefPtr<SVGImage> svgImage = SVGImage::create(this);
    292         m_svgImageCache = SVGImageCache::create(svgImage.get());
    293         m_image = svgImage.release();
    294     } else {
    295         m_image = BitmapImage::create(this);
    296     }
    297 
    298     if (m_image) {
    299         // Send queued container size requests.
    300         if (m_image->usesContainerSize()) {
    301             for (ContainerSizeRequests::iterator it = m_pendingContainerSizeRequests.begin(); it != m_pendingContainerSizeRequests.end(); ++it)
    302                 setContainerSizeForRenderer(it->key, it->value.first, it->value.second);
    303         }
    304         m_pendingContainerSizeRequests.clear();
    305     }
    306 }
    307 
    308 inline void ImageResource::clearImage()
    309 {
    310     // If our Image has an observer, it's always us so we need to clear the back pointer
    311     // before dropping our reference.
    312     if (m_image)
    313         m_image->setImageObserver(0);
    314     m_image.clear();
    315 }
    316 
    317 void ImageResource::appendData(const char* data, int length)
    318 {
    319     Resource::appendData(data, length);
    320     if (!m_loadingMultipartContent)
    321         updateImage(false);
    322 }
    323 
    324 void ImageResource::updateImage(bool allDataReceived)
    325 {
    326     if (m_data)
    327         createImage();
    328 
    329     bool sizeAvailable = false;
    330 
    331     // Have the image update its data from its internal buffer.
    332     // It will not do anything now, but will delay decoding until
    333     // queried for info (like size or specific image frames).
    334     if (m_image)
    335         sizeAvailable = m_image->setData(m_data, allDataReceived);
    336 
    337     // Go ahead and tell our observers to try to draw if we have either
    338     // received all the data or the size is known. Each chunk from the
    339     // network causes observers to repaint, which will force that chunk
    340     // to decode.
    341     if (sizeAvailable || allDataReceived) {
    342         if (!m_image || m_image->isNull()) {
    343             error(errorOccurred() ? status() : DecodeError);
    344             if (inCache())
    345                 memoryCache()->remove(this);
    346             return;
    347         }
    348 
    349         // It would be nice to only redraw the decoded band of the image, but with the current design
    350         // (decoding delayed until painting) that seems hard.
    351         notifyObservers();
    352     }
    353 }
    354 
    355 void ImageResource::finishOnePart()
    356 {
    357     if (m_loadingMultipartContent)
    358         clear();
    359     updateImage(true);
    360     if (m_loadingMultipartContent)
    361         m_data.clear();
    362     Resource::finishOnePart();
    363 }
    364 
    365 void ImageResource::error(Resource::Status status)
    366 {
    367     clear();
    368     Resource::error(status);
    369     notifyObservers();
    370 }
    371 
    372 void ImageResource::responseReceived(const ResourceResponse& response)
    373 {
    374     if (m_loadingMultipartContent && m_data)
    375         finishOnePart();
    376     else if (response.isMultipart())
    377         m_loadingMultipartContent = true;
    378     Resource::responseReceived(response);
    379 }
    380 
    381 void ImageResource::destroyDecodedData()
    382 {
    383     bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage());
    384     if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) {
    385         // Image refs the data buffer so we should not make it purgeable while the image is alive.
    386         // Invoking addClient() will reconstruct the image object.
    387         m_image = 0;
    388         setDecodedSize(0);
    389         makePurgeable(true);
    390     } else if (m_image && !errorOccurred()) {
    391         m_image->destroyDecodedData(true);
    392     }
    393 }
    394 
    395 void ImageResource::decodedSizeChanged(const WebCore::Image* image, int delta)
    396 {
    397     if (!image || image != m_image)
    398         return;
    399 
    400     setDecodedSize(decodedSize() + delta);
    401 }
    402 
    403 void ImageResource::didDraw(const WebCore::Image* image)
    404 {
    405     if (!image || image != m_image)
    406         return;
    407 
    408     double timeStamp = FrameView::currentPaintTimeStamp();
    409     if (!timeStamp) // If didDraw is called outside of a Frame paint.
    410         timeStamp = currentTime();
    411 
    412     Resource::didAccessDecodedData(timeStamp);
    413 }
    414 
    415 bool ImageResource::shouldPauseAnimation(const WebCore::Image* image)
    416 {
    417     if (!image || image != m_image)
    418         return false;
    419 
    420     ResourceClientWalker<ImageResourceClient> w(m_clients);
    421     while (ImageResourceClient* c = w.next()) {
    422         if (c->willRenderImage(this))
    423             return false;
    424     }
    425 
    426     return true;
    427 }
    428 
    429 void ImageResource::animationAdvanced(const WebCore::Image* image)
    430 {
    431     if (!image || image != m_image)
    432         return;
    433     notifyObservers();
    434 }
    435 
    436 void ImageResource::changedInRect(const WebCore::Image* image, const IntRect& rect)
    437 {
    438     if (!image || image != m_image)
    439         return;
    440     notifyObservers(&rect);
    441 }
    442 
    443 bool ImageResource::currentFrameKnownToBeOpaque(const RenderObject* renderer)
    444 {
    445     WebCore::Image* image = imageForRenderer(renderer);
    446     if (image->isBitmapImage())
    447         image->nativeImageForCurrentFrame(); // force decode
    448     return image->currentFrameKnownToBeOpaque();
    449 }
    450 
    451 } // namespace WebCore
    452