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