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