1 | /* |
2 | * Copyright (C) 2012-2017 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 | #include "CSSImageValue.h" |
30 | #include "CSSPrimitiveValue.h" |
31 | #include "CachedImage.h" |
32 | #include "CachedResourceLoader.h" |
33 | #include "CachedResourceRequest.h" |
34 | #include "CachedResourceRequestInitiators.h" |
35 | #include "Document.h" |
36 | #include "Page.h" |
37 | #include <wtf/text/StringBuilder.h> |
38 | |
39 | namespace WebCore { |
40 | |
41 | CSSImageSetValue::CSSImageSetValue(LoadedFromOpaqueSource loadedFromOpaqueSource) |
42 | : CSSValueList(ImageSetClass, CommaSeparator) |
43 | , m_loadedFromOpaqueSource(loadedFromOpaqueSource) |
44 | { |
45 | } |
46 | |
47 | CSSImageSetValue::~CSSImageSetValue() = default; |
48 | |
49 | void CSSImageSetValue::fillImageSet() |
50 | { |
51 | size_t length = this->length(); |
52 | size_t i = 0; |
53 | while (i < length) { |
54 | CSSValue* imageValue = item(i); |
55 | URL imageURL = downcast<CSSImageValue>(*imageValue).url(); |
56 | |
57 | ++i; |
58 | ASSERT_WITH_SECURITY_IMPLICATION(i < length); |
59 | CSSValue* scaleFactorValue = item(i); |
60 | float scaleFactor = downcast<CSSPrimitiveValue>(*scaleFactorValue).floatValue(); |
61 | |
62 | ImageWithScale image; |
63 | image.imageURL = imageURL; |
64 | image.scaleFactor = scaleFactor; |
65 | m_imagesInSet.append(image); |
66 | ++i; |
67 | } |
68 | |
69 | // Sort the images so that they are stored in order from lowest resolution to highest. |
70 | std::sort(m_imagesInSet.begin(), m_imagesInSet.end(), CSSImageSetValue::compareByScaleFactor); |
71 | } |
72 | |
73 | CSSImageSetValue::ImageWithScale CSSImageSetValue::bestImageForScaleFactor() |
74 | { |
75 | if (!m_imagesInSet.size()) |
76 | fillImageSet(); |
77 | |
78 | ImageWithScale image; |
79 | size_t numberOfImages = m_imagesInSet.size(); |
80 | for (size_t i = 0; i < numberOfImages; ++i) { |
81 | image = m_imagesInSet.at(i); |
82 | if (image.scaleFactor >= m_deviceScaleFactor) |
83 | return image; |
84 | } |
85 | return image; |
86 | } |
87 | |
88 | std::pair<CachedImage*, float> CSSImageSetValue::loadBestFitImage(CachedResourceLoader& loader, const ResourceLoaderOptions& options) |
89 | { |
90 | Document* document = loader.document(); |
91 | ASSERT(document); |
92 | |
93 | updateDeviceScaleFactor(*document); |
94 | |
95 | if (!m_accessedBestFitImage) { |
96 | m_accessedBestFitImage = true; |
97 | |
98 | // FIXME: In the future, we want to take much more than deviceScaleFactor into acount here. |
99 | // All forms of scale should be included: Page::pageScaleFactor(), Frame::pageZoomFactor(), |
100 | // and any CSS transforms. https://bugs.webkit.org/show_bug.cgi?id=81698 |
101 | ImageWithScale image = bestImageForScaleFactor(); |
102 | |
103 | ResourceLoaderOptions loadOptions = options; |
104 | loadOptions.loadedFromOpaqueSource = m_loadedFromOpaqueSource; |
105 | CachedResourceRequest request(ResourceRequest(document->completeURL(image.imageURL)), loadOptions); |
106 | request.setInitiator(cachedResourceRequestInitiators().css); |
107 | if (options.mode == FetchOptions::Mode::Cors) |
108 | request.updateForAccessControl(*document); |
109 | |
110 | m_cachedImage = loader.requestImage(WTFMove(request)).value_or(nullptr); |
111 | m_bestFitImageScaleFactor = image.scaleFactor; |
112 | } |
113 | return { m_cachedImage.get(), m_bestFitImageScaleFactor }; |
114 | } |
115 | |
116 | void CSSImageSetValue::updateDeviceScaleFactor(const Document& document) |
117 | { |
118 | float deviceScaleFactor = document.page() ? document.page()->deviceScaleFactor() : 1; |
119 | if (deviceScaleFactor == m_deviceScaleFactor) |
120 | return; |
121 | m_deviceScaleFactor = deviceScaleFactor; |
122 | m_accessedBestFitImage = false; |
123 | m_cachedImage = nullptr; |
124 | } |
125 | |
126 | String CSSImageSetValue::customCSSText() const |
127 | { |
128 | StringBuilder result; |
129 | result.appendLiteral("image-set(" ); |
130 | |
131 | size_t length = this->length(); |
132 | size_t i = 0; |
133 | while (i < length) { |
134 | if (i > 0) |
135 | result.appendLiteral(", " ); |
136 | |
137 | const CSSValue* imageValue = item(i); |
138 | result.append(imageValue->cssText()); |
139 | result.append(' '); |
140 | |
141 | ++i; |
142 | ASSERT_WITH_SECURITY_IMPLICATION(i < length); |
143 | const CSSValue* scaleFactorValue = item(i); |
144 | result.append(scaleFactorValue->cssText()); |
145 | // FIXME: Eventually the scale factor should contain it's own unit http://wkb.ug/100120. |
146 | // For now 'x' is hard-coded in the parser, so we hard-code it here too. |
147 | result.append('x'); |
148 | |
149 | ++i; |
150 | } |
151 | |
152 | result.append(')'); |
153 | return result.toString(); |
154 | } |
155 | |
156 | bool CSSImageSetValue::traverseSubresources(const WTF::Function<bool (const CachedResource&)>& handler) const |
157 | { |
158 | if (!m_cachedImage) |
159 | return false; |
160 | return handler(*m_cachedImage); |
161 | } |
162 | |
163 | } // namespace WebCore |
164 | |