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