1/* 2 * Copyright (C) 2012, 2013 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 "CSSImageSetValue.h" 28 29#if ENABLE(CSS_IMAGE_SET) 30 31#include "CSSImageValue.h" 32#include "CSSPrimitiveValue.h" 33#include "CachedImage.h" 34#include "CachedResourceLoader.h" 35#include "CachedResourceRequest.h" 36#include "CachedResourceRequestInitiators.h" 37#include "CrossOriginAccessControl.h" 38#include "Document.h" 39#include "Page.h" 40#include "StyleCachedImageSet.h" 41#include "StylePendingImage.h" 42#include <wtf/text/StringBuilder.h> 43 44namespace WebCore { 45 46CSSImageSetValue::CSSImageSetValue() 47 : CSSValueList(ImageSetClass, CommaSeparator) 48 , m_accessedBestFitImage(false) 49 , m_scaleFactor(1) 50{ 51} 52 53inline void CSSImageSetValue::detachPendingImage() 54{ 55 if (m_imageSet && m_imageSet->isPendingImage()) 56 toStylePendingImage(*m_imageSet).detachFromCSSValue(); 57} 58 59CSSImageSetValue::~CSSImageSetValue() 60{ 61 detachPendingImage(); 62 63 if (m_imageSet && m_imageSet->isCachedImageSet()) 64 toStyleCachedImageSet(*m_imageSet).clearImageSetValue(); 65} 66 67void CSSImageSetValue::fillImageSet() 68{ 69 size_t length = this->length(); 70 size_t i = 0; 71 while (i < length) { 72 CSSValue* imageValue = item(i); 73 String imageURL = toCSSImageValue(imageValue)->url(); 74 75 ++i; 76 ASSERT_WITH_SECURITY_IMPLICATION(i < length); 77 CSSValue* scaleFactorValue = item(i); 78 ASSERT_WITH_SECURITY_IMPLICATION(scaleFactorValue->isPrimitiveValue()); 79 float scaleFactor = toCSSPrimitiveValue(scaleFactorValue)->getFloatValue(); 80 81 ImageWithScale image; 82 image.imageURL = imageURL; 83 image.scaleFactor = scaleFactor; 84 m_imagesInSet.append(image); 85 ++i; 86 } 87 88 // Sort the images so that they are stored in order from lowest resolution to highest. 89 std::sort(m_imagesInSet.begin(), m_imagesInSet.end(), CSSImageSetValue::compareByScaleFactor); 90} 91 92CSSImageSetValue::ImageWithScale CSSImageSetValue::bestImageForScaleFactor() 93{ 94 ImageWithScale image; 95 size_t numberOfImages = m_imagesInSet.size(); 96 for (size_t i = 0; i < numberOfImages; ++i) { 97 image = m_imagesInSet.at(i); 98 if (image.scaleFactor >= m_scaleFactor) 99 return image; 100 } 101 return image; 102} 103 104StyleCachedImageSet* CSSImageSetValue::cachedImageSet(CachedResourceLoader* loader, const ResourceLoaderOptions& options) 105{ 106 ASSERT(loader); 107 108 Document* document = loader->document(); 109 if (Page* page = document->page()) 110 m_scaleFactor = page->deviceScaleFactor(); 111 else 112 m_scaleFactor = 1; 113 114 if (!m_imagesInSet.size()) 115 fillImageSet(); 116 117 if (!m_accessedBestFitImage) { 118 // FIXME: In the future, we want to take much more than deviceScaleFactor into acount here. 119 // All forms of scale should be included: Page::pageScaleFactor(), Frame::pageZoomFactor(), 120 // and any CSS transforms. https://bugs.webkit.org/show_bug.cgi?id=81698 121 ImageWithScale image = bestImageForScaleFactor(); 122 CachedResourceRequest request(ResourceRequest(document->completeURL(image.imageURL)), options); 123 request.setInitiator(cachedResourceRequestInitiators().css); 124 if (options.requestOriginPolicy() == PotentiallyCrossOriginEnabled) 125 updateRequestForAccessControl(request.mutableResourceRequest(), document->securityOrigin(), options.allowCredentials()); 126 if (CachedResourceHandle<CachedImage> cachedImage = loader->requestImage(request)) { 127 detachPendingImage(); 128 m_imageSet = StyleCachedImageSet::create(cachedImage.get(), image.scaleFactor, this); 129 m_accessedBestFitImage = true; 130 } 131 } 132 133 return (m_imageSet && m_imageSet->isCachedImageSet()) ? toStyleCachedImageSet(m_imageSet.get()) : nullptr; 134} 135 136StyleCachedImageSet* CSSImageSetValue::cachedImageSet(CachedResourceLoader* loader) 137{ 138 return cachedImageSet(loader, CachedResourceLoader::defaultCachedResourceOptions()); 139} 140 141StyleImage* CSSImageSetValue::cachedOrPendingImageSet(Document& document) 142{ 143 if (!m_imageSet) 144 m_imageSet = StylePendingImage::create(this); 145 else if (!m_imageSet->isPendingImage()) { 146 float deviceScaleFactor = 1; 147 if (Page* page = document.page()) 148 deviceScaleFactor = page->deviceScaleFactor(); 149 150 // If the deviceScaleFactor has changed, we may not have the best image loaded, so we have to re-assess. 151 if (deviceScaleFactor != m_scaleFactor) { 152 m_accessedBestFitImage = false; 153 m_imageSet = StylePendingImage::create(this); 154 } 155 } 156 157 return m_imageSet.get(); 158} 159 160String CSSImageSetValue::customCSSText() const 161{ 162 StringBuilder result; 163 result.appendLiteral("-webkit-image-set("); 164 165 size_t length = this->length(); 166 size_t i = 0; 167 while (i < length) { 168 if (i > 0) 169 result.appendLiteral(", "); 170 171 const CSSValue* imageValue = item(i); 172 result.append(imageValue->cssText()); 173 result.append(' '); 174 175 ++i; 176 ASSERT_WITH_SECURITY_IMPLICATION(i < length); 177 const CSSValue* scaleFactorValue = item(i); 178 result.append(scaleFactorValue->cssText()); 179 // FIXME: Eventually the scale factor should contain it's own unit http://wkb.ug/100120. 180 // For now 'x' is hard-coded in the parser, so we hard-code it here too. 181 result.append('x'); 182 183 ++i; 184 } 185 186 result.append(')'); 187 return result.toString(); 188} 189 190bool CSSImageSetValue::hasFailedOrCanceledSubresources() const 191{ 192 if (!m_imageSet || !m_imageSet->isCachedImageSet()) 193 return false; 194 CachedResource* cachedResource = toStyleCachedImageSet(*m_imageSet).cachedImage(); 195 if (!cachedResource) 196 return true; 197 return cachedResource->loadFailedOrCanceled(); 198} 199 200CSSImageSetValue::CSSImageSetValue(const CSSImageSetValue& cloneFrom) 201 : CSSValueList(cloneFrom) 202 , m_accessedBestFitImage(false) 203 , m_scaleFactor(1) 204{ 205 // Non-CSSValueList data is not accessible through CSS OM, no need to clone. 206} 207 208PassRefPtr<CSSImageSetValue> CSSImageSetValue::cloneForCSSOM() const 209{ 210 return adoptRef(new CSSImageSetValue(*this)); 211} 212 213} // namespace WebCore 214 215#endif // ENABLE(CSS_IMAGE_SET) 216