1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
4 * All rights reserved.
5 * Copyright (C) 2013 Google Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24 #include "third_party/blink/renderer/core/css/resolver/element_style_resources.h"
25
26 #include "third_party/blink/renderer/core/css/css_gradient_value.h"
27 #include "third_party/blink/renderer/core/css/css_image_value.h"
28 #include "third_party/blink/renderer/core/css/css_property_names.h"
29 #include "third_party/blink/renderer/core/css/css_uri_value.h"
30 #include "third_party/blink/renderer/core/dom/document.h"
31 #include "third_party/blink/renderer/core/dom/element.h"
32 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
33 #include "third_party/blink/renderer/core/dom/tree_scope.h"
34 #include "third_party/blink/renderer/core/frame/local_frame.h"
35 #include "third_party/blink/renderer/core/frame/settings.h"
36 #include "third_party/blink/renderer/core/loader/lazy_image_helper.h"
37 #include "third_party/blink/renderer/core/style/computed_style.h"
38 #include "third_party/blink/renderer/core/style/content_data.h"
39 #include "third_party/blink/renderer/core/style/cursor_data.h"
40 #include "third_party/blink/renderer/core/style/fill_layer.h"
41 #include "third_party/blink/renderer/core/style/filter_operation.h"
42 #include "third_party/blink/renderer/core/style/style_fetched_image.h"
43 #include "third_party/blink/renderer/core/style/style_fetched_image_set.h"
44 #include "third_party/blink/renderer/core/style/style_generated_image.h"
45 #include "third_party/blink/renderer/core/style/style_image.h"
46 #include "third_party/blink/renderer/core/style/style_pending_image.h"
47 #include "third_party/blink/renderer/core/svg/svg_resource.h"
48 #include "third_party/blink/renderer/core/svg/svg_tree_scope_resources.h"
49 #include "third_party/blink/renderer/platform/geometry/length.h"
50 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
51 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
52
53 namespace blink {
54
ElementStyleResources(Element & element,float device_scale_factor,PseudoElement * pseudo_element)55 ElementStyleResources::ElementStyleResources(Element& element,
56 float device_scale_factor,
57 PseudoElement* pseudo_element)
58 : element_(&element),
59 device_scale_factor_(device_scale_factor),
60 pseudo_element_(pseudo_element) {}
61
GetStyleImage(CSSPropertyID property,const CSSValue & value)62 StyleImage* ElementStyleResources::GetStyleImage(CSSPropertyID property,
63 const CSSValue& value) {
64 if (auto* img_value = DynamicTo<CSSImageValue>(value))
65 return CachedOrPendingFromValue(property, *img_value);
66
67 if (auto* img_generator_value = DynamicTo<CSSImageGeneratorValue>(value))
68 return GeneratedOrPendingFromValue(property, *img_generator_value);
69
70 if (auto* img_set_value = DynamicTo<CSSImageSetValue>(value))
71 return SetOrPendingFromValue(property, *img_set_value);
72
73 return nullptr;
74 }
75
GeneratedOrPendingFromValue(CSSPropertyID property,const CSSImageGeneratorValue & value)76 StyleImage* ElementStyleResources::GeneratedOrPendingFromValue(
77 CSSPropertyID property,
78 const CSSImageGeneratorValue& value) {
79 if (value.IsPending()) {
80 pending_image_properties_.insert(property);
81 return MakeGarbageCollected<StylePendingImage>(value);
82 }
83 return MakeGarbageCollected<StyleGeneratedImage>(value);
84 }
85
SetOrPendingFromValue(CSSPropertyID property,const CSSImageSetValue & value)86 StyleImage* ElementStyleResources::SetOrPendingFromValue(
87 CSSPropertyID property,
88 const CSSImageSetValue& value) {
89 if (value.IsCachePending(device_scale_factor_)) {
90 pending_image_properties_.insert(property);
91 return MakeGarbageCollected<StylePendingImage>(value);
92 }
93 return value.CachedImage(device_scale_factor_);
94 }
95
CachedOrPendingFromValue(CSSPropertyID property,const CSSImageValue & value)96 StyleImage* ElementStyleResources::CachedOrPendingFromValue(
97 CSSPropertyID property,
98 const CSSImageValue& value) {
99 if (value.IsCachePending()) {
100 pending_image_properties_.insert(property);
101 return MakeGarbageCollected<StylePendingImage>(value);
102 }
103 value.RestoreCachedResourceIfNeeded(element_->GetDocument());
104 return value.CachedImage();
105 }
106
GetSVGResourceFromValue(TreeScope & tree_scope,const cssvalue::CSSURIValue & value,AllowExternal allow_external) const107 SVGResource* ElementStyleResources::GetSVGResourceFromValue(
108 TreeScope& tree_scope,
109 const cssvalue::CSSURIValue& value,
110 AllowExternal allow_external) const {
111 if (value.IsLocal(element_->GetDocument())) {
112 SVGTreeScopeResources& tree_scope_resources =
113 tree_scope.EnsureSVGTreeScopedResources();
114 AtomicString decoded_fragment(DecodeURLEscapeSequences(
115 value.FragmentIdentifier(), DecodeURLMode::kUTF8OrIsomorphic));
116 return tree_scope_resources.ResourceForId(decoded_fragment);
117 }
118 if (allow_external == kAllowExternalResource)
119 return value.EnsureResourceReference();
120 return nullptr;
121 }
122
LoadPendingSVGResources(ComputedStyle & style)123 void ElementStyleResources::LoadPendingSVGResources(ComputedStyle& style) {
124 if (!style.HasFilter())
125 return;
126 FilterOperations::FilterOperationVector& filter_operations =
127 style.MutableFilter().Operations();
128 for (const auto& filter_operation : filter_operations) {
129 auto* reference_operation =
130 DynamicTo<ReferenceFilterOperation>(filter_operation.Get());
131 if (!reference_operation)
132 continue;
133 if (SVGResource* resource = reference_operation->Resource())
134 resource->Load(element_->GetDocument());
135 }
136 }
137
BackgroundLayerMayBeSprite(const FillLayer & background_layer)138 static bool BackgroundLayerMayBeSprite(const FillLayer& background_layer) {
139 // Simple heuristic to guess if a CSS background image layer is used to
140 // create CSS sprites. For a legit background image it's very likely the X
141 // and the Y position will not be explicitly specifed. For CSS sprite image,
142 // background X or Y position will probably be specified.
143 DCHECK(background_layer.GetImage());
144 return background_layer.PositionX().IsFixed() ||
145 background_layer.PositionY().IsFixed();
146 }
147
LoadPendingImage(ComputedStyle & style,StylePendingImage * pending_image,FetchParameters::ImageRequestBehavior image_request_behavior,CrossOriginAttributeValue cross_origin)148 StyleImage* ElementStyleResources::LoadPendingImage(
149 ComputedStyle& style,
150 StylePendingImage* pending_image,
151 FetchParameters::ImageRequestBehavior image_request_behavior,
152 CrossOriginAttributeValue cross_origin) {
153 if (CSSImageValue* image_value = pending_image->CssImageValue()) {
154 return image_value->CacheImage(element_->GetDocument(),
155 image_request_behavior, cross_origin);
156 }
157
158 if (CSSPaintValue* paint_value = pending_image->CssPaintValue()) {
159 auto* image = MakeGarbageCollected<StyleGeneratedImage>(*paint_value);
160 style.AddPaintImage(image);
161 return image;
162 }
163
164 if (CSSImageGeneratorValue* image_generator_value =
165 pending_image->CssImageGeneratorValue()) {
166 image_generator_value->LoadSubimages(element_->GetDocument());
167 return MakeGarbageCollected<StyleGeneratedImage>(*image_generator_value);
168 }
169
170 if (CSSImageSetValue* image_set_value = pending_image->CssImageSetValue()) {
171 return image_set_value->CacheImage(element_->GetDocument(),
172 device_scale_factor_,
173 image_request_behavior, cross_origin);
174 }
175
176 NOTREACHED();
177 return nullptr;
178 }
179
LoadPendingImages(ComputedStyle & style)180 void ElementStyleResources::LoadPendingImages(ComputedStyle& style) {
181 // We must loop over the properties and then look at the style to see if
182 // a pending image exists, and only load that image. For example:
183 //
184 // <style>
185 // div { background-image: url(a.png); }
186 // div { background-image: url(b.png); }
187 // div { background-image: none; }
188 // </style>
189 // <div></div>
190 //
191 // We call styleImage() for both a.png and b.png adding the
192 // CSSPropertyID::kBackgroundImage property to the pending_image_properties_
193 // set, then we null out the background image because of the "none".
194 //
195 // If we eagerly loaded the images we'd fetch a.png, even though it's not
196 // used. If we didn't null check below we'd crash since the none actually
197 // removed all background images.
198
199 for (CSSPropertyID property : pending_image_properties_) {
200 switch (property) {
201 case CSSPropertyID::kBackgroundImage: {
202 for (FillLayer* background_layer = &style.AccessBackgroundLayers();
203 background_layer; background_layer = background_layer->Next()) {
204 StyleImage* background_image = background_layer->GetImage();
205 if (background_image && background_image->IsPendingImage()) {
206 FetchParameters::ImageRequestBehavior image_request_behavior =
207 FetchParameters::kNone;
208 if (!BackgroundLayerMayBeSprite(*background_layer)) {
209 if (element_->GetDocument()
210 .GetFrame()
211 ->GetLazyLoadImageSetting() ==
212 LocalFrame::LazyLoadImageSetting::kEnabledAutomatic) {
213 image_request_behavior = FetchParameters::kDeferImageLoad;
214 }
215 }
216 StyleImage* new_image =
217 LoadPendingImage(style, To<StylePendingImage>(background_image),
218 image_request_behavior);
219 if (new_image && new_image->IsLazyloadPossiblyDeferred()) {
220 LazyImageHelper::StartMonitoring(pseudo_element_ ? pseudo_element_
221 : element_);
222 }
223 background_layer->SetImage(new_image);
224 }
225 }
226 break;
227 }
228 case CSSPropertyID::kContent: {
229 for (ContentData* content_data =
230 const_cast<ContentData*>(style.GetContentData());
231 content_data; content_data = content_data->Next()) {
232 if (content_data->IsImage()) {
233 StyleImage* image = To<ImageContentData>(content_data)->GetImage();
234 if (image->IsPendingImage()) {
235 To<ImageContentData>(content_data)
236 ->SetImage(LoadPendingImage(style,
237 To<StylePendingImage>(image),
238 FetchParameters::kNone));
239 }
240 }
241 }
242 break;
243 }
244 case CSSPropertyID::kCursor: {
245 if (CursorList* cursor_list = style.Cursors()) {
246 for (wtf_size_t i = 0; i < cursor_list->size(); ++i) {
247 CursorData& current_cursor = cursor_list->at(i);
248 if (StyleImage* image = current_cursor.GetImage()) {
249 if (image->IsPendingImage()) {
250 current_cursor.SetImage(
251 LoadPendingImage(style, To<StylePendingImage>(image),
252 FetchParameters::kNone));
253 }
254 }
255 }
256 }
257 break;
258 }
259 case CSSPropertyID::kListStyleImage: {
260 if (style.ListStyleImage() &&
261 style.ListStyleImage()->IsPendingImage()) {
262 style.SetListStyleImage(LoadPendingImage(
263 style, To<StylePendingImage>(style.ListStyleImage()),
264 FetchParameters::kNone));
265 }
266 break;
267 }
268 case CSSPropertyID::kBorderImageSource: {
269 if (style.BorderImageSource() &&
270 style.BorderImageSource()->IsPendingImage()) {
271 style.SetBorderImageSource(LoadPendingImage(
272 style, To<StylePendingImage>(style.BorderImageSource()),
273 FetchParameters::kNone));
274 }
275 break;
276 }
277 case CSSPropertyID::kWebkitBoxReflect: {
278 if (StyleReflection* reflection = style.BoxReflect()) {
279 const NinePieceImage& mask_image = reflection->Mask();
280 if (mask_image.GetImage() &&
281 mask_image.GetImage()->IsPendingImage()) {
282 StyleImage* loaded_image = LoadPendingImage(
283 style, To<StylePendingImage>(mask_image.GetImage()),
284 FetchParameters::kNone);
285 reflection->SetMask(NinePieceImage(
286 loaded_image, mask_image.ImageSlices(), mask_image.Fill(),
287 mask_image.BorderSlices(), mask_image.Outset(),
288 mask_image.HorizontalRule(), mask_image.VerticalRule()));
289 }
290 }
291 break;
292 }
293 case CSSPropertyID::kWebkitMaskBoxImageSource: {
294 if (style.MaskBoxImageSource() &&
295 style.MaskBoxImageSource()->IsPendingImage()) {
296 style.SetMaskBoxImageSource(LoadPendingImage(
297 style, To<StylePendingImage>(style.MaskBoxImageSource()),
298 FetchParameters::kNone));
299 }
300 break;
301 }
302 case CSSPropertyID::kWebkitMaskImage: {
303 for (FillLayer* mask_layer = &style.AccessMaskLayers(); mask_layer;
304 mask_layer = mask_layer->Next()) {
305 if (mask_layer->GetImage() &&
306 mask_layer->GetImage()->IsPendingImage()) {
307 mask_layer->SetImage(LoadPendingImage(
308 style, To<StylePendingImage>(mask_layer->GetImage()),
309 FetchParameters::kNone, kCrossOriginAttributeAnonymous));
310 }
311 }
312 break;
313 }
314 case CSSPropertyID::kShapeOutside:
315 if (style.ShapeOutside() && style.ShapeOutside()->GetImage() &&
316 style.ShapeOutside()->GetImage()->IsPendingImage()) {
317 style.ShapeOutside()->SetImage(LoadPendingImage(
318 style, To<StylePendingImage>(style.ShapeOutside()->GetImage()),
319 FetchParameters::kNone, kCrossOriginAttributeAnonymous));
320 }
321 break;
322 default:
323 NOTREACHED();
324 }
325 }
326 }
327
LoadPendingResources(ComputedStyle & computed_style)328 void ElementStyleResources::LoadPendingResources(
329 ComputedStyle& computed_style) {
330 LoadPendingImages(computed_style);
331 LoadPendingSVGResources(computed_style);
332 }
333
334 } // namespace blink
335