1/*
2 * Copyright (C) 2008, 2011, 2012, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "CSSImageGeneratorValue.h"
29
30#include "CSSCanvasValue.h"
31#include "CSSCrossfadeValue.h"
32#include "CSSFilterImageValue.h"
33#include "CSSGradientValue.h"
34#include "CSSImageValue.h"
35#include "CSSNamedImageValue.h"
36#include "CSSPaintImageValue.h"
37#include "GeneratedImage.h"
38#include "HTMLCanvasElement.h"
39#include "InspectorInstrumentation.h"
40#include "RenderElement.h"
41
42namespace WebCore {
43
44static const Seconds timeToKeepCachedGeneratedImages { 3_s };
45
46class CSSImageGeneratorValue::CachedGeneratedImage {
47 WTF_MAKE_FAST_ALLOCATED;
48public:
49 CachedGeneratedImage(CSSImageGeneratorValue&, FloatSize, GeneratedImage&);
50 GeneratedImage& image() const { return m_image; }
51 void puntEvictionTimer() { m_evictionTimer.restart(); }
52
53private:
54 void evictionTimerFired();
55
56 CSSImageGeneratorValue& m_owner;
57 const FloatSize m_size;
58 const Ref<GeneratedImage> m_image;
59 DeferrableOneShotTimer m_evictionTimer;
60};
61
62CSSImageGeneratorValue::CSSImageGeneratorValue(ClassType classType)
63 : CSSValue(classType)
64{
65}
66
67CSSImageGeneratorValue::~CSSImageGeneratorValue() = default;
68
69void CSSImageGeneratorValue::addClient(RenderElement& renderer)
70{
71 if (m_clients.isEmpty())
72 ref();
73
74 m_clients.add(&renderer);
75
76 if (is<CSSCanvasValue>(this)) {
77 if (HTMLCanvasElement* canvasElement = downcast<CSSCanvasValue>(this)->element())
78 InspectorInstrumentation::didChangeCSSCanvasClientNodes(*canvasElement);
79 }
80}
81
82void CSSImageGeneratorValue::removeClient(RenderElement& renderer)
83{
84 ASSERT(m_clients.contains(&renderer));
85 if (!m_clients.remove(&renderer))
86 return;
87
88 if (is<CSSCanvasValue>(this)) {
89 if (HTMLCanvasElement* canvasElement = downcast<CSSCanvasValue>(this)->element())
90 InspectorInstrumentation::didChangeCSSCanvasClientNodes(*canvasElement);
91 }
92
93 if (m_clients.isEmpty())
94 deref();
95}
96
97GeneratedImage* CSSImageGeneratorValue::cachedImageForSize(FloatSize size)
98{
99 if (size.isEmpty())
100 return nullptr;
101
102 auto* cachedGeneratedImage = m_images.get(size);
103 if (!cachedGeneratedImage)
104 return nullptr;
105
106 cachedGeneratedImage->puntEvictionTimer();
107 return &cachedGeneratedImage->image();
108}
109
110void CSSImageGeneratorValue::saveCachedImageForSize(FloatSize size, GeneratedImage& image)
111{
112 ASSERT(!m_images.contains(size));
113 m_images.add(size, std::make_unique<CachedGeneratedImage>(*this, size, image));
114}
115
116void CSSImageGeneratorValue::evictCachedGeneratedImage(FloatSize size)
117{
118 ASSERT(m_images.contains(size));
119 m_images.remove(size);
120}
121
122inline CSSImageGeneratorValue::CachedGeneratedImage::CachedGeneratedImage(CSSImageGeneratorValue& owner, FloatSize size, GeneratedImage& image)
123 : m_owner(owner)
124 , m_size(size)
125 , m_image(image)
126 , m_evictionTimer(*this, &CSSImageGeneratorValue::CachedGeneratedImage::evictionTimerFired, timeToKeepCachedGeneratedImages)
127{
128 m_evictionTimer.restart();
129}
130
131void CSSImageGeneratorValue::CachedGeneratedImage::evictionTimerFired()
132{
133 // NOTE: This is essentially a "delete this", the object is no longer valid after this line.
134 m_owner.evictCachedGeneratedImage(m_size);
135}
136
137RefPtr<Image> CSSImageGeneratorValue::image(RenderElement& renderer, const FloatSize& size)
138{
139 switch (classType()) {
140 case CanvasClass:
141 return downcast<CSSCanvasValue>(*this).image(&renderer, size);
142 case NamedImageClass:
143 return downcast<CSSNamedImageValue>(*this).image(&renderer, size);
144 case CrossfadeClass:
145 return downcast<CSSCrossfadeValue>(*this).image(renderer, size);
146 case FilterImageClass:
147 return downcast<CSSFilterImageValue>(*this).image(&renderer, size);
148 case LinearGradientClass:
149 return downcast<CSSLinearGradientValue>(*this).image(renderer, size);
150 case RadialGradientClass:
151 return downcast<CSSRadialGradientValue>(*this).image(renderer, size);
152 case ConicGradientClass:
153 return downcast<CSSConicGradientValue>(*this).image(renderer, size);
154#if ENABLE(CSS_PAINTING_API)
155 case PaintImageClass:
156 return downcast<CSSPaintImageValue>(*this).image(renderer, size);
157#endif
158 default:
159 ASSERT_NOT_REACHED();
160 }
161 return nullptr;
162}
163
164bool CSSImageGeneratorValue::isFixedSize() const
165{
166 switch (classType()) {
167 case CanvasClass:
168 return downcast<CSSCanvasValue>(*this).isFixedSize();
169 case NamedImageClass:
170 return downcast<CSSNamedImageValue>(*this).isFixedSize();
171 case CrossfadeClass:
172 return downcast<CSSCrossfadeValue>(*this).isFixedSize();
173 case FilterImageClass:
174 return downcast<CSSFilterImageValue>(*this).isFixedSize();
175 case LinearGradientClass:
176 return downcast<CSSLinearGradientValue>(*this).isFixedSize();
177 case RadialGradientClass:
178 return downcast<CSSRadialGradientValue>(*this).isFixedSize();
179 case ConicGradientClass:
180 return downcast<CSSConicGradientValue>(*this).isFixedSize();
181#if ENABLE(CSS_PAINTING_API)
182 case PaintImageClass:
183 return downcast<CSSPaintImageValue>(*this).isFixedSize();
184#endif
185 default:
186 ASSERT_NOT_REACHED();
187 }
188 return false;
189}
190
191FloatSize CSSImageGeneratorValue::fixedSize(const RenderElement& renderer)
192{
193 switch (classType()) {
194 case CanvasClass:
195 return downcast<CSSCanvasValue>(*this).fixedSize(&renderer);
196 case CrossfadeClass:
197 return downcast<CSSCrossfadeValue>(*this).fixedSize(renderer);
198 case FilterImageClass:
199 return downcast<CSSFilterImageValue>(*this).fixedSize(&renderer);
200 case LinearGradientClass:
201 return downcast<CSSLinearGradientValue>(*this).fixedSize(renderer);
202 case RadialGradientClass:
203 return downcast<CSSRadialGradientValue>(*this).fixedSize(renderer);
204 case ConicGradientClass:
205 return downcast<CSSConicGradientValue>(*this).fixedSize(renderer);
206#if ENABLE(CSS_PAINTING_API)
207 case PaintImageClass:
208 return downcast<CSSPaintImageValue>(*this).fixedSize(renderer);
209#endif
210 default:
211 ASSERT_NOT_REACHED();
212 }
213 return FloatSize();
214}
215
216bool CSSImageGeneratorValue::isPending() const
217{
218 switch (classType()) {
219 case CrossfadeClass:
220 return downcast<CSSCrossfadeValue>(*this).isPending();
221 case CanvasClass:
222 return downcast<CSSCanvasValue>(*this).isPending();
223 case NamedImageClass:
224 return downcast<CSSNamedImageValue>(*this).isPending();
225 case FilterImageClass:
226 return downcast<CSSFilterImageValue>(*this).isPending();
227 case LinearGradientClass:
228 return downcast<CSSLinearGradientValue>(*this).isPending();
229 case RadialGradientClass:
230 return downcast<CSSRadialGradientValue>(*this).isPending();
231 case ConicGradientClass:
232 return downcast<CSSConicGradientValue>(*this).isPending();
233#if ENABLE(CSS_PAINTING_API)
234 case PaintImageClass:
235 return downcast<CSSPaintImageValue>(*this).isPending();
236#endif
237 default:
238 ASSERT_NOT_REACHED();
239 }
240 return false;
241}
242
243bool CSSImageGeneratorValue::knownToBeOpaque(const RenderElement& renderer) const
244{
245 switch (classType()) {
246 case CrossfadeClass:
247 return downcast<CSSCrossfadeValue>(*this).knownToBeOpaque(renderer);
248 case CanvasClass:
249 return false;
250 case NamedImageClass:
251 return false;
252 case FilterImageClass:
253 return downcast<CSSFilterImageValue>(*this).knownToBeOpaque(renderer);
254 case LinearGradientClass:
255 return downcast<CSSLinearGradientValue>(*this).knownToBeOpaque(renderer);
256 case RadialGradientClass:
257 return downcast<CSSRadialGradientValue>(*this).knownToBeOpaque(renderer);
258 case ConicGradientClass:
259 return downcast<CSSConicGradientValue>(*this).knownToBeOpaque(renderer);
260#if ENABLE(CSS_PAINTING_API)
261 case PaintImageClass:
262 return downcast<CSSPaintImageValue>(*this).knownToBeOpaque(renderer);
263#endif
264 default:
265 ASSERT_NOT_REACHED();
266 }
267 return false;
268}
269
270void CSSImageGeneratorValue::loadSubimages(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
271{
272 switch (classType()) {
273 case CrossfadeClass:
274 downcast<CSSCrossfadeValue>(*this).loadSubimages(cachedResourceLoader, options);
275 break;
276 case CanvasClass:
277 downcast<CSSCanvasValue>(*this).loadSubimages(cachedResourceLoader, options);
278 break;
279 case FilterImageClass:
280 downcast<CSSFilterImageValue>(*this).loadSubimages(cachedResourceLoader, options);
281 break;
282 case LinearGradientClass:
283 downcast<CSSLinearGradientValue>(*this).loadSubimages(cachedResourceLoader, options);
284 break;
285 case RadialGradientClass:
286 downcast<CSSRadialGradientValue>(*this).loadSubimages(cachedResourceLoader, options);
287 break;
288 case ConicGradientClass:
289 downcast<CSSConicGradientValue>(*this).loadSubimages(cachedResourceLoader, options);
290 break;
291#if ENABLE(CSS_PAINTING_API)
292 case PaintImageClass:
293 downcast<CSSPaintImageValue>(*this).loadSubimages(cachedResourceLoader, options);
294 break;
295#endif
296 default:
297 ASSERT_NOT_REACHED();
298 }
299}
300
301bool CSSImageGeneratorValue::subimageIsPending(const CSSValue& value)
302{
303 if (is<CSSImageValue>(value))
304 return downcast<CSSImageValue>(value).isPending();
305
306 if (is<CSSImageGeneratorValue>(value))
307 return downcast<CSSImageGeneratorValue>(value).isPending();
308
309 if (is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
310 return false;
311
312 ASSERT_NOT_REACHED();
313 return false;
314}
315
316CachedImage* CSSImageGeneratorValue::cachedImageForCSSValue(CSSValue& value, CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
317{
318 if (is<CSSImageValue>(value)) {
319 auto& imageValue = downcast<CSSImageValue>(value);
320 return imageValue.loadImage(cachedResourceLoader, options);
321 }
322
323 if (is<CSSImageGeneratorValue>(value)) {
324 downcast<CSSImageGeneratorValue>(value).loadSubimages(cachedResourceLoader, options);
325 // FIXME: Handle CSSImageGeneratorValue (and thus cross-fades with gradients and canvas).
326 return nullptr;
327 }
328
329 if (is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
330 return nullptr;
331
332 ASSERT_NOT_REACHED();
333 return nullptr;
334}
335
336} // namespace WebCore
337