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