1 /*
2 * Copyright (C) 2011 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 COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/core/css/css_crossfade_value.h"
27
28 #include "third_party/blink/renderer/core/css/css_image_value.h"
29 #include "third_party/blink/renderer/core/layout/layout_object.h"
30 #include "third_party/blink/renderer/core/style/style_fetched_image.h"
31 #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
32 #include "third_party/blink/renderer/platform/graphics/crossfade_generated_image.h"
33 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
34
35 namespace blink {
36 namespace cssvalue {
37
SubimageIsPending(const CSSValue & value)38 static bool SubimageIsPending(const CSSValue& value) {
39 if (auto* image_value = DynamicTo<CSSImageValue>(value))
40 return image_value->IsCachePending();
41
42 if (auto* image_generator_value = DynamicTo<CSSImageGeneratorValue>(value))
43 return image_generator_value->IsPending();
44
45 NOTREACHED();
46
47 return false;
48 }
49
SubimageKnownToBeOpaque(const CSSValue & value,const Document & document,const ComputedStyle & style)50 static bool SubimageKnownToBeOpaque(const CSSValue& value,
51 const Document& document,
52 const ComputedStyle& style) {
53 if (auto* image_value = DynamicTo<CSSImageValue>(value))
54 return image_value->KnownToBeOpaque(document, style);
55
56 if (auto* img_generator_value = DynamicTo<CSSImageGeneratorValue>(value))
57 return img_generator_value->KnownToBeOpaque(document, style);
58
59 NOTREACHED();
60
61 return false;
62 }
63
CachedImageForCSSValue(CSSValue * value,const Document & document)64 static ImageResourceContent* CachedImageForCSSValue(CSSValue* value,
65 const Document& document) {
66 if (!value)
67 return nullptr;
68
69 if (auto* image_value = DynamicTo<CSSImageValue>(value)) {
70 StyleImage* style_image_resource =
71 image_value->CacheImage(document, FetchParameters::kNone);
72 if (!style_image_resource)
73 return nullptr;
74
75 return style_image_resource->CachedImage();
76 }
77
78 if (auto* img_generator_value = DynamicTo<CSSImageGeneratorValue>(value)) {
79 img_generator_value->LoadSubimages(document);
80 // FIXME: Handle CSSImageGeneratorValue (and thus cross-fades with gradients
81 // and canvas).
82 return nullptr;
83 }
84
85 NOTREACHED();
86
87 return nullptr;
88 }
89
RenderableImageForCSSValue(CSSValue * value,const Document & document)90 static Image* RenderableImageForCSSValue(CSSValue* value,
91 const Document& document) {
92 ImageResourceContent* cached_image = CachedImageForCSSValue(value, document);
93
94 if (!cached_image || cached_image->ErrorOccurred() ||
95 cached_image->GetImage()->IsNull())
96 return nullptr;
97
98 return cached_image->GetImage();
99 }
100
UrlForCSSValue(const CSSValue & value)101 static KURL UrlForCSSValue(const CSSValue& value) {
102 auto* image_value = DynamicTo<CSSImageValue>(value);
103 if (!image_value)
104 return KURL();
105
106 return KURL(image_value->Url());
107 }
108
CSSCrossfadeValue(CSSValue * from_value,CSSValue * to_value,CSSPrimitiveValue * percentage_value)109 CSSCrossfadeValue::CSSCrossfadeValue(CSSValue* from_value,
110 CSSValue* to_value,
111 CSSPrimitiveValue* percentage_value)
112 : CSSImageGeneratorValue(kCrossfadeClass),
113 from_value_(from_value),
114 to_value_(to_value),
115 percentage_value_(percentage_value),
116 cached_from_image_(nullptr),
117 cached_to_image_(nullptr),
118 crossfade_subimage_observer_(this) {}
119
120 CSSCrossfadeValue::~CSSCrossfadeValue() = default;
121
Dispose()122 void CSSCrossfadeValue::Dispose() {
123 if (cached_from_image_) {
124 cached_from_image_->RemoveObserver(&crossfade_subimage_observer_);
125 cached_from_image_ = nullptr;
126 }
127 if (cached_to_image_) {
128 cached_to_image_->RemoveObserver(&crossfade_subimage_observer_);
129 cached_to_image_ = nullptr;
130 }
131 }
132
CustomCSSText() const133 String CSSCrossfadeValue::CustomCSSText() const {
134 StringBuilder result;
135 result.Append("-webkit-cross-fade(");
136 result.Append(from_value_->CssText());
137 result.Append(", ");
138 result.Append(to_value_->CssText());
139 result.Append(", ");
140 result.Append(percentage_value_->CssText());
141 result.Append(')');
142 return result.ToString();
143 }
144
ComputedCSSValue(const ComputedStyle & style,bool allow_visited_style)145 CSSCrossfadeValue* CSSCrossfadeValue::ComputedCSSValue(
146 const ComputedStyle& style,
147 bool allow_visited_style) {
148 CSSValue* from_value = from_value_;
149 if (auto* from_image_value = DynamicTo<CSSImageValue>(from_value_.Get())) {
150 from_value = from_image_value->ValueWithURLMadeAbsolute();
151 } else if (auto* from_generator_value =
152 DynamicTo<CSSImageGeneratorValue>(from_value_.Get())) {
153 from_value =
154 from_generator_value->ComputedCSSValue(style, allow_visited_style);
155 }
156 CSSValue* to_value = to_value_;
157 if (auto* to_image_value = DynamicTo<CSSImageValue>(to_value_.Get())) {
158 to_value = to_image_value->ValueWithURLMadeAbsolute();
159 } else if (auto* to_generator_value =
160 DynamicTo<CSSImageGeneratorValue>(to_value_.Get())) {
161 to_value = to_generator_value->ComputedCSSValue(style, allow_visited_style);
162 }
163 return MakeGarbageCollected<CSSCrossfadeValue>(from_value, to_value,
164 percentage_value_);
165 }
166
FixedSize(const Document & document,const FloatSize & default_object_size) const167 FloatSize CSSCrossfadeValue::FixedSize(
168 const Document& document,
169 const FloatSize& default_object_size) const {
170 Image* from_image = RenderableImageForCSSValue(from_value_.Get(), document);
171 Image* to_image = RenderableImageForCSSValue(to_value_.Get(), document);
172
173 if (!from_image || !to_image)
174 return FloatSize();
175
176 FloatSize from_image_size(from_image->Size());
177 FloatSize to_image_size(to_image->Size());
178
179 if (auto* from_svg_image = DynamicTo<SVGImage>(from_image)) {
180 from_image_size = from_svg_image->ConcreteObjectSize(default_object_size);
181 }
182
183 if (auto* to_svg_image = DynamicTo<SVGImage>(to_image)) {
184 to_image_size = to_svg_image->ConcreteObjectSize(default_object_size);
185 }
186
187 // Rounding issues can cause transitions between images of equal size to
188 // return a different fixed size; avoid performing the interpolation if the
189 // images are the same size.
190 if (from_image_size == to_image_size)
191 return from_image_size;
192
193 float percentage = percentage_value_->GetFloatValue();
194 float inverse_percentage = 1 - percentage;
195
196 return FloatSize(from_image_size.Width() * inverse_percentage +
197 to_image_size.Width() * percentage,
198 from_image_size.Height() * inverse_percentage +
199 to_image_size.Height() * percentage);
200 }
201
IsPending() const202 bool CSSCrossfadeValue::IsPending() const {
203 return SubimageIsPending(*from_value_) || SubimageIsPending(*to_value_);
204 }
205
KnownToBeOpaque(const Document & document,const ComputedStyle & style) const206 bool CSSCrossfadeValue::KnownToBeOpaque(const Document& document,
207 const ComputedStyle& style) const {
208 return SubimageKnownToBeOpaque(*from_value_, document, style) &&
209 SubimageKnownToBeOpaque(*to_value_, document, style);
210 }
211
LoadSubimages(const Document & document)212 void CSSCrossfadeValue::LoadSubimages(const Document& document) {
213 ImageResourceContent* old_cached_from_image = cached_from_image_;
214 ImageResourceContent* old_cached_to_image = cached_to_image_;
215
216 cached_from_image_ = CachedImageForCSSValue(from_value_.Get(), document);
217 cached_to_image_ = CachedImageForCSSValue(to_value_.Get(), document);
218
219 if (cached_from_image_ != old_cached_from_image) {
220 if (old_cached_from_image)
221 old_cached_from_image->RemoveObserver(&crossfade_subimage_observer_);
222 if (cached_from_image_)
223 cached_from_image_->AddObserver(&crossfade_subimage_observer_);
224 }
225
226 if (cached_to_image_ != old_cached_to_image) {
227 if (old_cached_to_image)
228 old_cached_to_image->RemoveObserver(&crossfade_subimage_observer_);
229 if (cached_to_image_)
230 cached_to_image_->AddObserver(&crossfade_subimage_observer_);
231 }
232
233 crossfade_subimage_observer_.SetReady(true);
234 }
235
GetImage(const ImageResourceObserver & client,const Document & document,const ComputedStyle &,const FloatSize & size) const236 scoped_refptr<Image> CSSCrossfadeValue::GetImage(
237 const ImageResourceObserver& client,
238 const Document& document,
239 const ComputedStyle&,
240 const FloatSize& size) const {
241 if (size.IsEmpty())
242 return nullptr;
243
244 Image* from_image = RenderableImageForCSSValue(from_value_.Get(), document);
245 Image* to_image = RenderableImageForCSSValue(to_value_.Get(), document);
246
247 if (!from_image || !to_image)
248 return Image::NullImage();
249
250 scoped_refptr<Image> from_image_ref(from_image);
251 scoped_refptr<Image> to_image_ref(to_image);
252
253 if (auto* from_svg_image = DynamicTo<SVGImage>(from_image)) {
254 from_image_ref = SVGImageForContainer::Create(from_svg_image, size, 1,
255 UrlForCSSValue(*from_value_));
256 }
257
258 if (auto* to_svg_image = DynamicTo<SVGImage>(to_image)) {
259 to_image_ref = SVGImageForContainer::Create(to_svg_image, size, 1,
260 UrlForCSSValue(*to_value_));
261 }
262
263 return CrossfadeGeneratedImage::Create(from_image_ref, to_image_ref,
264 percentage_value_->GetFloatValue(),
265 FixedSize(document, size), size);
266 }
267
CrossfadeChanged(ImageResourceObserver::CanDeferInvalidation defer)268 void CSSCrossfadeValue::CrossfadeChanged(
269 ImageResourceObserver::CanDeferInvalidation defer) {
270 for (const auto& curr : Clients()) {
271 ImageResourceObserver* client =
272 const_cast<ImageResourceObserver*>(curr.key);
273 client->ImageChanged(static_cast<WrappedImagePtr>(this), defer);
274 }
275 }
276
WillRenderImage() const277 bool CSSCrossfadeValue::WillRenderImage() const {
278 for (const auto& curr : Clients()) {
279 if (const_cast<ImageResourceObserver*>(curr.key)->WillRenderImage())
280 return true;
281 }
282 return false;
283 }
284
ImageChanged(ImageResourceContent *,CanDeferInvalidation defer)285 void CSSCrossfadeValue::CrossfadeSubimageObserverProxy::ImageChanged(
286 ImageResourceContent*,
287 CanDeferInvalidation defer) {
288 if (ready_)
289 owner_value_->CrossfadeChanged(defer);
290 }
291
WillRenderImage()292 bool CSSCrossfadeValue::CrossfadeSubimageObserverProxy::WillRenderImage() {
293 // If the images are not ready/loaded we won't paint them. If the images
294 // are ready then ask the clients.
295 return ready_ && owner_value_->WillRenderImage();
296 }
297
HasFailedOrCanceledSubresources() const298 bool CSSCrossfadeValue::HasFailedOrCanceledSubresources() const {
299 if (cached_from_image_ && cached_from_image_->LoadFailedOrCanceled())
300 return true;
301 if (cached_to_image_ && cached_to_image_->LoadFailedOrCanceled())
302 return true;
303 return false;
304 }
305
Equals(const CSSCrossfadeValue & other) const306 bool CSSCrossfadeValue::Equals(const CSSCrossfadeValue& other) const {
307 return DataEquivalent(from_value_, other.from_value_) &&
308 DataEquivalent(to_value_, other.to_value_) &&
309 DataEquivalent(percentage_value_, other.percentage_value_);
310 }
311
TraceAfterDispatch(blink::Visitor * visitor) const312 void CSSCrossfadeValue::TraceAfterDispatch(blink::Visitor* visitor) const {
313 visitor->Trace(from_value_);
314 visitor->Trace(to_value_);
315 visitor->Trace(percentage_value_);
316 visitor->Trace(cached_from_image_);
317 visitor->Trace(cached_to_image_);
318 visitor->Trace(crossfade_subimage_observer_);
319 CSSImageGeneratorValue::TraceAfterDispatch(visitor);
320 }
321
322 } // namespace cssvalue
323 } // namespace blink
324