Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 2012 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/css/CSSImageSetValue.h"
     28 
     29 #include "core/FetchInitiatorTypeNames.h"
     30 #include "core/css/CSSImageValue.h"
     31 #include "core/css/CSSPrimitiveValue.h"
     32 #include "core/dom/Document.h"
     33 #include "core/fetch/FetchRequest.h"
     34 #include "core/fetch/ImageResource.h"
     35 #include "core/fetch/ResourceFetcher.h"
     36 #include "core/rendering/style/StyleFetchedImageSet.h"
     37 #include "core/rendering/style/StylePendingImage.h"
     38 #include "wtf/text/StringBuilder.h"
     39 
     40 namespace WebCore {
     41 
     42 CSSImageSetValue::CSSImageSetValue()
     43     : CSSValueList(ImageSetClass, CommaSeparator)
     44     , m_accessedBestFitImage(false)
     45     , m_scaleFactor(1)
     46 {
     47 }
     48 
     49 CSSImageSetValue::~CSSImageSetValue()
     50 {
     51     if (m_imageSet && m_imageSet->isImageResourceSet())
     52         toStyleFetchedImageSet(m_imageSet)->clearImageSetValue();
     53 }
     54 
     55 void CSSImageSetValue::fillImageSet()
     56 {
     57     size_t length = this->length();
     58     size_t i = 0;
     59     while (i < length) {
     60         CSSImageValue* imageValue = toCSSImageValue(item(i));
     61         String imageURL = imageValue->url();
     62 
     63         ++i;
     64         ASSERT_WITH_SECURITY_IMPLICATION(i < length);
     65         CSSValue* scaleFactorValue = item(i);
     66         float scaleFactor = toCSSPrimitiveValue(scaleFactorValue)->getFloatValue();
     67 
     68         ImageWithScale image;
     69         image.imageURL = imageURL;
     70         image.referrer = imageValue->referrer();
     71         image.scaleFactor = scaleFactor;
     72         m_imagesInSet.append(image);
     73         ++i;
     74     }
     75 
     76     // Sort the images so that they are stored in order from lowest resolution to highest.
     77     std::sort(m_imagesInSet.begin(), m_imagesInSet.end(), CSSImageSetValue::compareByScaleFactor);
     78 }
     79 
     80 CSSImageSetValue::ImageWithScale CSSImageSetValue::bestImageForScaleFactor()
     81 {
     82     ImageWithScale image;
     83     size_t numberOfImages = m_imagesInSet.size();
     84     for (size_t i = 0; i < numberOfImages; ++i) {
     85         image = m_imagesInSet.at(i);
     86         if (image.scaleFactor >= m_scaleFactor)
     87             return image;
     88     }
     89     return image;
     90 }
     91 
     92 StyleFetchedImageSet* CSSImageSetValue::cachedImageSet(ResourceFetcher* loader, float deviceScaleFactor, const ResourceLoaderOptions& options)
     93 {
     94     ASSERT(loader);
     95 
     96     m_scaleFactor = deviceScaleFactor;
     97 
     98     if (!m_imagesInSet.size())
     99         fillImageSet();
    100 
    101     if (!m_accessedBestFitImage) {
    102         // FIXME: In the future, we want to take much more than deviceScaleFactor into acount here.
    103         // All forms of scale should be included: Page::pageScaleFactor(), LocalFrame::pageZoomFactor(),
    104         // and any CSS transforms. https://bugs.webkit.org/show_bug.cgi?id=81698
    105         ImageWithScale image = bestImageForScaleFactor();
    106         if (Document* document = loader->document()) {
    107             FetchRequest request(ResourceRequest(document->completeURL(image.imageURL)), FetchInitiatorTypeNames::css, options);
    108             request.mutableResourceRequest().setHTTPReferrer(image.referrer);
    109 
    110             if (options.corsEnabled == IsCORSEnabled)
    111                 request.setCrossOriginAccessControl(loader->document()->securityOrigin(), options.allowCredentials, options.credentialsRequested);
    112 
    113             if (ResourcePtr<ImageResource> cachedImage = loader->fetchImage(request)) {
    114                 m_imageSet = StyleFetchedImageSet::create(cachedImage.get(), image.scaleFactor, this);
    115                 m_accessedBestFitImage = true;
    116             }
    117         }
    118     }
    119 
    120     return (m_imageSet && m_imageSet->isImageResourceSet()) ? toStyleFetchedImageSet(m_imageSet) : 0;
    121 }
    122 
    123 StyleFetchedImageSet* CSSImageSetValue::cachedImageSet(ResourceFetcher* fetcher, float deviceScaleFactor)
    124 {
    125     return cachedImageSet(fetcher, deviceScaleFactor, ResourceFetcher::defaultResourceOptions());
    126 }
    127 
    128 StyleImage* CSSImageSetValue::cachedOrPendingImageSet(float deviceScaleFactor)
    129 {
    130     if (!m_imageSet) {
    131         m_imageSet = StylePendingImage::create(this);
    132     } else if (!m_imageSet->isPendingImage()) {
    133         // If the deviceScaleFactor has changed, we may not have the best image loaded, so we have to re-assess.
    134         if (deviceScaleFactor != m_scaleFactor) {
    135             m_accessedBestFitImage = false;
    136             m_imageSet = StylePendingImage::create(this);
    137         }
    138     }
    139 
    140     return m_imageSet.get();
    141 }
    142 
    143 String CSSImageSetValue::customCSSText() const
    144 {
    145     StringBuilder result;
    146     result.append("-webkit-image-set(");
    147 
    148     size_t length = this->length();
    149     size_t i = 0;
    150     while (i < length) {
    151         if (i > 0)
    152             result.append(", ");
    153 
    154         const CSSValue* imageValue = item(i);
    155         result.append(imageValue->cssText());
    156         result.append(' ');
    157 
    158         ++i;
    159         ASSERT_WITH_SECURITY_IMPLICATION(i < length);
    160         const CSSValue* scaleFactorValue = item(i);
    161         result.append(scaleFactorValue->cssText());
    162         // FIXME: Eventually the scale factor should contain it's own unit http://wkb.ug/100120.
    163         // For now 'x' is hard-coded in the parser, so we hard-code it here too.
    164         result.append('x');
    165 
    166         ++i;
    167     }
    168 
    169     result.append(")");
    170     return result.toString();
    171 }
    172 
    173 bool CSSImageSetValue::hasFailedOrCanceledSubresources() const
    174 {
    175     if (!m_imageSet || !m_imageSet->isImageResourceSet())
    176         return false;
    177     if (Resource* cachedResource = toStyleFetchedImageSet(m_imageSet)->cachedImage())
    178         return cachedResource->loadFailedOrCanceled();
    179     return true;
    180 }
    181 
    182 CSSImageSetValue::CSSImageSetValue(const CSSImageSetValue& cloneFrom)
    183     : CSSValueList(cloneFrom)
    184     , m_accessedBestFitImage(false)
    185     , m_scaleFactor(1)
    186 {
    187     // Non-CSSValueList data is not accessible through CSS OM, no need to clone.
    188 }
    189 
    190 PassRefPtrWillBeRawPtr<CSSImageSetValue> CSSImageSetValue::cloneForCSSOM() const
    191 {
    192     return adoptRefWillBeNoop(new CSSImageSetValue(*this));
    193 }
    194 
    195 } // namespace WebCore
    196