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) 2012 Google Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 * DAMAGE.
27 */
28
29 #include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
30
31 #include "third_party/blink/renderer/core/animation/document_timeline.h"
32 #include "third_party/blink/renderer/core/css/css_font_selector.h"
33 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
34 #include "third_party/blink/renderer/core/css/font_face.h"
35 #include "third_party/blink/renderer/core/css/page_rule_collector.h"
36 #include "third_party/blink/renderer/core/css/part_names.h"
37 #include "third_party/blink/renderer/core/css/resolver/match_request.h"
38 #include "third_party/blink/renderer/core/css/rule_feature_set.h"
39 #include "third_party/blink/renderer/core/css/style_change_reason.h"
40 #include "third_party/blink/renderer/core/css/style_engine.h"
41 #include "third_party/blink/renderer/core/css/style_rule.h"
42 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
43 #include "third_party/blink/renderer/core/dom/document.h"
44 #include "third_party/blink/renderer/core/dom/shadow_root.h"
45 #include "third_party/blink/renderer/core/html/html_style_element.h"
46 #include "third_party/blink/renderer/core/html_names.h"
47 #include "third_party/blink/renderer/core/svg/svg_style_element.h"
48
49 namespace blink {
50
Parent() const51 ScopedStyleResolver* ScopedStyleResolver::Parent() const {
52 for (TreeScope* scope = GetTreeScope().ParentTreeScope(); scope;
53 scope = scope->ParentTreeScope()) {
54 if (ScopedStyleResolver* resolver = scope->GetScopedStyleResolver())
55 return resolver;
56 }
57 return nullptr;
58 }
59
AddKeyframeRules(const RuleSet & rule_set)60 void ScopedStyleResolver::AddKeyframeRules(const RuleSet& rule_set) {
61 if (RuntimeEnabledFeatures::CSSKeyframesMemoryReductionEnabled())
62 return;
63
64 const HeapVector<Member<StyleRuleKeyframes>> keyframes_rules =
65 rule_set.KeyframesRules();
66 for (auto rule : keyframes_rules)
67 AddKeyframeStyle(rule);
68 }
69
AddFontFaceRules(const RuleSet & rule_set)70 void ScopedStyleResolver::AddFontFaceRules(const RuleSet& rule_set) {
71 // FIXME(BUG 72461): We don't add @font-face rules of scoped style sheets for
72 // the moment.
73 if (!GetTreeScope().RootNode().IsDocumentNode())
74 return;
75
76 Document& document = GetTreeScope().GetDocument();
77 CSSFontSelector* css_font_selector =
78 document.GetStyleEngine().GetFontSelector();
79 const HeapVector<Member<StyleRuleFontFace>> font_face_rules =
80 rule_set.FontFaceRules();
81 for (auto& font_face_rule : font_face_rules) {
82 if (FontFace* font_face = FontFace::Create(&document, font_face_rule))
83 css_font_selector->GetFontFaceCache()->Add(font_face_rule, font_face);
84 }
85 if (font_face_rules.size())
86 document.GetStyleResolver().InvalidateMatchedPropertiesCache();
87 }
88
AppendActiveStyleSheets(unsigned index,const ActiveStyleSheetVector & active_sheets)89 void ScopedStyleResolver::AppendActiveStyleSheets(
90 unsigned index,
91 const ActiveStyleSheetVector& active_sheets) {
92 for (auto* active_iterator = active_sheets.begin() + index;
93 active_iterator != active_sheets.end(); active_iterator++) {
94 CSSStyleSheet* sheet = active_iterator->first;
95 viewport_dependent_media_query_results_.AppendVector(
96 sheet->ViewportDependentMediaQueryResults());
97 device_dependent_media_query_results_.AppendVector(
98 sheet->DeviceDependentMediaQueryResults());
99 if (!active_iterator->second)
100 continue;
101 const RuleSet& rule_set = *active_iterator->second;
102 author_style_sheets_.push_back(sheet);
103 AddKeyframeRules(rule_set);
104 AddFontFaceRules(rule_set);
105 AddTreeBoundaryCrossingRules(rule_set, sheet, index);
106 AddSlottedRules(rule_set, sheet, index++);
107 }
108 }
109
CollectFeaturesTo(RuleFeatureSet & features,HeapHashSet<Member<const StyleSheetContents>> & visited_shared_style_sheet_contents) const110 void ScopedStyleResolver::CollectFeaturesTo(
111 RuleFeatureSet& features,
112 HeapHashSet<Member<const StyleSheetContents>>&
113 visited_shared_style_sheet_contents) const {
114 features.ViewportDependentMediaQueryResults().AppendVector(
115 viewport_dependent_media_query_results_);
116 features.DeviceDependentMediaQueryResults().AppendVector(
117 device_dependent_media_query_results_);
118
119 for (auto sheet : author_style_sheets_) {
120 DCHECK(sheet->ownerNode() || sheet->IsConstructed());
121 StyleSheetContents* contents = sheet->Contents();
122 if (contents->HasOneClient() ||
123 visited_shared_style_sheet_contents.insert(contents).is_new_entry)
124 features.Add(contents->GetRuleSet().Features());
125 }
126
127 if (tree_boundary_crossing_rule_set_) {
128 for (const auto& rules : *tree_boundary_crossing_rule_set_)
129 features.Add(rules->rule_set_->Features());
130 }
131 if (slotted_rule_set_) {
132 for (const auto& rules : *slotted_rule_set_)
133 features.Add(rules->rule_set_->Features());
134 }
135 }
136
ResetAuthorStyle()137 void ScopedStyleResolver::ResetAuthorStyle() {
138 author_style_sheets_.clear();
139 viewport_dependent_media_query_results_.clear();
140 device_dependent_media_query_results_.clear();
141 keyframes_rule_map_.clear();
142 tree_boundary_crossing_rule_set_ = nullptr;
143 slotted_rule_set_ = nullptr;
144 has_deep_or_shadow_selector_ = false;
145 needs_append_all_sheets_ = false;
146 }
147
ActiveAuthorStyleSheets()148 const ActiveStyleSheetVector& ScopedStyleResolver::ActiveAuthorStyleSheets() {
149 StyleSheetCollection* collection =
150 GetTreeScope().GetDocument().GetStyleEngine().StyleSheetCollectionFor(
151 *scope_);
152 DCHECK(collection);
153 return collection->ActiveAuthorStyleSheets();
154 }
155
156 // static
157 StyleRuleKeyframes*
KeyframeStylesForAnimationFromActiveSheets(const AtomicString & name,const ActiveStyleSheetVector & sheets)158 ScopedStyleResolver::KeyframeStylesForAnimationFromActiveSheets(
159 const AtomicString& name,
160 const ActiveStyleSheetVector& sheets) {
161 // We prefer non-vendor-prefixed over vendor-prefixed rules.
162 StyleRuleKeyframes* vendor_prefixed_result = nullptr;
163 for (auto sheet = sheets.rbegin(); sheet != sheets.rend(); ++sheet) {
164 RuleSet* rule_set = sheet->second;
165 if (!rule_set)
166 continue;
167 if (StyleRuleKeyframes* rule = rule_set->KeyframeStylesForAnimation(name)) {
168 if (!rule->IsVendorPrefixed())
169 return rule;
170 if (!vendor_prefixed_result)
171 vendor_prefixed_result = rule;
172 }
173 }
174 return vendor_prefixed_result;
175 }
176
KeyframeStylesForAnimation(const AtomicString & animation_name)177 StyleRuleKeyframes* ScopedStyleResolver::KeyframeStylesForAnimation(
178 const AtomicString& animation_name) {
179 if (RuntimeEnabledFeatures::CSSKeyframesMemoryReductionEnabled()) {
180 return KeyframeStylesForAnimationFromActiveSheets(
181 animation_name, ActiveAuthorStyleSheets());
182 }
183
184 if (keyframes_rule_map_.IsEmpty())
185 return nullptr;
186
187 KeyframesRuleMap::iterator it = keyframes_rule_map_.find(animation_name);
188 if (it == keyframes_rule_map_.end())
189 return nullptr;
190
191 return it->value.Get();
192 }
193
AddKeyframeStyle(StyleRuleKeyframes * rule)194 void ScopedStyleResolver::AddKeyframeStyle(StyleRuleKeyframes* rule) {
195 DCHECK(!RuntimeEnabledFeatures::CSSKeyframesMemoryReductionEnabled());
196 AtomicString name = rule->GetName();
197
198 if (rule->IsVendorPrefixed()) {
199 KeyframesRuleMap::iterator it = keyframes_rule_map_.find(name);
200 if (it == keyframes_rule_map_.end())
201 keyframes_rule_map_.Set(name, rule);
202 else if (it->value->IsVendorPrefixed())
203 keyframes_rule_map_.Set(name, rule);
204 } else {
205 keyframes_rule_map_.Set(name, rule);
206 }
207 }
208
InvalidationRootForTreeScope(const TreeScope & tree_scope)209 Element& ScopedStyleResolver::InvalidationRootForTreeScope(
210 const TreeScope& tree_scope) {
211 DCHECK(tree_scope.GetDocument().documentElement());
212 if (tree_scope.RootNode() == tree_scope.GetDocument())
213 return *tree_scope.GetDocument().documentElement();
214 return To<ShadowRoot>(tree_scope.RootNode()).host();
215 }
216
KeyframesRulesAdded(const TreeScope & tree_scope)217 void ScopedStyleResolver::KeyframesRulesAdded(const TreeScope& tree_scope) {
218 // Called when @keyframes rules are about to be added/removed from a
219 // TreeScope. @keyframes rules may apply to animations on elements in the
220 // same TreeScope as the stylesheet, or the host element in the parent
221 // TreeScope if the TreeScope is a shadow tree.
222 if (!tree_scope.GetDocument().documentElement())
223 return;
224
225 ScopedStyleResolver* resolver = tree_scope.GetScopedStyleResolver();
226 ScopedStyleResolver* parent_resolver =
227 tree_scope.ParentTreeScope()
228 ? tree_scope.ParentTreeScope()->GetScopedStyleResolver()
229 : nullptr;
230
231 bool had_unresolved_keyframes = false;
232 if (resolver && resolver->has_unresolved_keyframes_rule_) {
233 resolver->has_unresolved_keyframes_rule_ = false;
234 had_unresolved_keyframes = true;
235 }
236 if (parent_resolver && parent_resolver->has_unresolved_keyframes_rule_) {
237 parent_resolver->has_unresolved_keyframes_rule_ = false;
238 had_unresolved_keyframes = true;
239 }
240
241 if (had_unresolved_keyframes) {
242 // If an animation ended up not being started because no @keyframes
243 // rules were found for the animation-name, we need to recalculate style
244 // for the elements in the scope, including its shadow host if
245 // applicable.
246 InvalidationRootForTreeScope(tree_scope)
247 .SetNeedsStyleRecalc(kSubtreeStyleChange,
248 StyleChangeReasonForTracing::Create(
249 style_change_reason::kStyleSheetChange));
250 return;
251 }
252
253 // If we have animations running, added/removed @keyframes may affect these.
254 tree_scope.GetDocument().Timeline().InvalidateKeyframeEffects(tree_scope);
255 }
256
CollectMatchingAuthorRules(ElementRuleCollector & collector,ShadowV0CascadeOrder cascade_order)257 void ScopedStyleResolver::CollectMatchingAuthorRules(
258 ElementRuleCollector& collector,
259 ShadowV0CascadeOrder cascade_order) {
260 wtf_size_t sheet_index = 0;
261 for (auto sheet : author_style_sheets_) {
262 DCHECK(sheet->ownerNode() || sheet->IsConstructed());
263 MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
264 &scope_->RootNode(), sheet, sheet_index++);
265 collector.CollectMatchingRules(match_request, cascade_order);
266 }
267 }
268
CollectMatchingShadowHostRules(ElementRuleCollector & collector,ShadowV0CascadeOrder cascade_order)269 void ScopedStyleResolver::CollectMatchingShadowHostRules(
270 ElementRuleCollector& collector,
271 ShadowV0CascadeOrder cascade_order) {
272 wtf_size_t sheet_index = 0;
273 for (auto sheet : author_style_sheets_) {
274 DCHECK(sheet->ownerNode() || sheet->IsConstructed());
275 MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
276 &scope_->RootNode(), sheet, sheet_index++);
277 collector.CollectMatchingShadowHostRules(match_request, cascade_order);
278 }
279 }
280
CollectMatchingSlottedRules(ElementRuleCollector & collector,ShadowV0CascadeOrder cascade_order)281 void ScopedStyleResolver::CollectMatchingSlottedRules(
282 ElementRuleCollector& collector,
283 ShadowV0CascadeOrder cascade_order) {
284 if (!slotted_rule_set_)
285 return;
286
287 for (const auto& rules : *slotted_rule_set_) {
288 MatchRequest request(rules->rule_set_.Get(), &GetTreeScope().RootNode(),
289 rules->parent_style_sheet_, rules->parent_index_);
290 collector.CollectMatchingRules(request, cascade_order, true);
291 }
292 }
293
CollectMatchingTreeBoundaryCrossingRules(ElementRuleCollector & collector,ShadowV0CascadeOrder cascade_order)294 void ScopedStyleResolver::CollectMatchingTreeBoundaryCrossingRules(
295 ElementRuleCollector& collector,
296 ShadowV0CascadeOrder cascade_order) {
297 if (!tree_boundary_crossing_rule_set_)
298 return;
299
300 for (const auto& rules : *tree_boundary_crossing_rule_set_) {
301 MatchRequest request(rules->rule_set_.Get(), &GetTreeScope().RootNode(),
302 rules->parent_style_sheet_, rules->parent_index_);
303 collector.CollectMatchingRules(request, cascade_order, true);
304 }
305 }
306
CollectMatchingPartPseudoRules(ElementRuleCollector & collector,PartNames & part_names,ShadowV0CascadeOrder cascade_order)307 void ScopedStyleResolver::CollectMatchingPartPseudoRules(
308 ElementRuleCollector& collector,
309 PartNames& part_names,
310 ShadowV0CascadeOrder cascade_order) {
311 wtf_size_t sheet_index = 0;
312 for (auto sheet : author_style_sheets_) {
313 DCHECK(sheet->ownerNode() || sheet->IsConstructed());
314 MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
315 &scope_->RootNode(), sheet, sheet_index++);
316 collector.CollectMatchingPartPseudoRules(match_request, part_names,
317 cascade_order);
318 }
319 }
320
MatchPageRules(PageRuleCollector & collector)321 void ScopedStyleResolver::MatchPageRules(PageRuleCollector& collector) {
322 // Only consider the global author RuleSet for @page rules, as per the HTML5
323 // spec.
324 DCHECK(scope_->RootNode().IsDocumentNode());
325 for (auto sheet : author_style_sheets_)
326 collector.MatchPageRules(&sheet->Contents()->GetRuleSet());
327 }
328
Trace(Visitor * visitor) const329 void ScopedStyleResolver::Trace(Visitor* visitor) const {
330 visitor->Trace(scope_);
331 visitor->Trace(author_style_sheets_);
332 visitor->Trace(keyframes_rule_map_);
333 visitor->Trace(tree_boundary_crossing_rule_set_);
334 visitor->Trace(slotted_rule_set_);
335 }
336
AddRules(RuleSet * rule_set,const HeapVector<MinimalRuleData> & rules)337 static void AddRules(RuleSet* rule_set,
338 const HeapVector<MinimalRuleData>& rules) {
339 for (const auto& info : rules)
340 rule_set->AddRule(info.rule_, info.selector_index_, info.flags_);
341 }
342
AddTreeBoundaryCrossingRules(const RuleSet & author_rules,CSSStyleSheet * parent_style_sheet,unsigned sheet_index)343 void ScopedStyleResolver::AddTreeBoundaryCrossingRules(
344 const RuleSet& author_rules,
345 CSSStyleSheet* parent_style_sheet,
346 unsigned sheet_index) {
347 bool is_document_scope = GetTreeScope().RootNode().IsDocumentNode();
348 if (author_rules.DeepCombinatorOrShadowPseudoRules().IsEmpty() &&
349 (is_document_scope ||
350 (author_rules.ContentPseudoElementRules().IsEmpty())))
351 return;
352
353 if (!author_rules.DeepCombinatorOrShadowPseudoRules().IsEmpty())
354 has_deep_or_shadow_selector_ = true;
355
356 auto* rule_set_for_scope = MakeGarbageCollected<RuleSet>();
357 AddRules(rule_set_for_scope,
358 author_rules.DeepCombinatorOrShadowPseudoRules());
359
360 if (!is_document_scope)
361 AddRules(rule_set_for_scope, author_rules.ContentPseudoElementRules());
362
363 if (!tree_boundary_crossing_rule_set_) {
364 tree_boundary_crossing_rule_set_ =
365 MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
366 GetTreeScope().GetDocument().GetStyleEngine().AddTreeBoundaryCrossingScope(
367 GetTreeScope());
368 }
369
370 tree_boundary_crossing_rule_set_->push_back(MakeGarbageCollected<RuleSubSet>(
371 parent_style_sheet, sheet_index, rule_set_for_scope));
372 }
373
V0ShadowAddedOnV1Document()374 void ScopedStyleResolver::V0ShadowAddedOnV1Document() {
375 // See the comment in AddSlottedRules().
376 if (!slotted_rule_set_)
377 return;
378
379 if (!tree_boundary_crossing_rule_set_) {
380 tree_boundary_crossing_rule_set_ =
381 MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
382 GetTreeScope().GetDocument().GetStyleEngine().AddTreeBoundaryCrossingScope(
383 GetTreeScope());
384 }
385 tree_boundary_crossing_rule_set_->AppendVector(*slotted_rule_set_);
386 slotted_rule_set_ = nullptr;
387 }
388
AddSlottedRules(const RuleSet & author_rules,CSSStyleSheet * parent_style_sheet,unsigned sheet_index)389 void ScopedStyleResolver::AddSlottedRules(const RuleSet& author_rules,
390 CSSStyleSheet* parent_style_sheet,
391 unsigned sheet_index) {
392 bool is_document_scope = GetTreeScope().RootNode().IsDocumentNode();
393 if (is_document_scope || author_rules.SlottedPseudoElementRules().IsEmpty())
394 return;
395
396 auto* slotted_rule_set = MakeGarbageCollected<RuleSet>();
397 AddRules(slotted_rule_set, author_rules.SlottedPseudoElementRules());
398
399 // In case ::slotted rule is used in V0/V1 mixed document, put ::slotted
400 // rules in tree boundary crossing rules as the pure v1 fast path in
401 // StyleResolver misses them.
402 // Adding this tree scope to tree boundary crossing scopes may end up in
403 // O(N^2) where N is number of scopes which has ::slotted() rules.
404 // Once the document-wide cascade order flag downgrades from V1 to V0,
405 // these slotted rules have to be moved back to tree boundary crossing
406 // rule sets. See V0ShadowAddedOnV1Document().
407 if (GetTreeScope().GetDocument().MayContainV0Shadow()) {
408 if (!tree_boundary_crossing_rule_set_) {
409 tree_boundary_crossing_rule_set_ =
410 MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
411 GetTreeScope()
412 .GetDocument()
413 .GetStyleEngine()
414 .AddTreeBoundaryCrossingScope(GetTreeScope());
415 }
416 tree_boundary_crossing_rule_set_->push_back(
417 MakeGarbageCollected<RuleSubSet>(parent_style_sheet, sheet_index,
418 slotted_rule_set));
419 return;
420 }
421 if (!slotted_rule_set_)
422 slotted_rule_set_ = MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
423 slotted_rule_set_->push_back(MakeGarbageCollected<RuleSubSet>(
424 parent_style_sheet, sheet_index, slotted_rule_set));
425 }
426
Trace(Visitor * visitor) const427 void ScopedStyleResolver::RuleSubSet::Trace(Visitor* visitor) const {
428 visitor->Trace(parent_style_sheet_);
429 visitor->Trace(rule_set_);
430 }
431
432 } // namespace blink
433