1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_SELECTOR_FILTER_PARENT_SCOPE_H_
6 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_SELECTOR_FILTER_PARENT_SCOPE_H_
7
8 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
9 #include "third_party/blink/renderer/core/css/selector_filter.h"
10 #include "third_party/blink/renderer/core/dom/document.h"
11 #include "third_party/blink/renderer/core/dom/element.h"
12
13 namespace blink {
14
15 // Maintains the parent element stack (and bloom filter) inside RecalcStyle.
16 // SelectorFilterParentScope for the parent element is added to the stack before
17 // recalculating style for its children. The bloom filter is populated lazily by
18 // PushParentIfNeeded().
19 class CORE_EXPORT SelectorFilterParentScope {
20 STACK_ALLOCATED();
21
22 public:
SelectorFilterParentScope(Element & parent)23 explicit SelectorFilterParentScope(Element& parent)
24 : SelectorFilterParentScope(&parent, ScopeType::kParent) {
25 DCHECK(previous_);
26 DCHECK(previous_->scope_type_ == ScopeType::kRoot ||
27 (previous_->parent_ &&
28 &previous_->parent_->GetDocument() == &parent.GetDocument()));
29 }
30 ~SelectorFilterParentScope();
31
32 static void EnsureParentStackIsPushed();
33
34 protected:
35 enum class ScopeType { kParent, kRoot };
36 SelectorFilterParentScope(Element* parent, ScopeType scope);
37
38 private:
39 void PushParentIfNeeded();
40 void PushAncestors(Element&);
41 void PopAncestors(Element&);
42
43 Element* parent_;
44 bool pushed_ = false;
45 ScopeType scope_type_;
46 SelectorFilterParentScope* previous_;
47 StyleResolver* resolver_;
48
49 static SelectorFilterParentScope* current_scope_;
50 };
51
52 // When starting the style recalc, we push an object of this class onto the
53 // stack to establish a root scope for the SelectorFilter for a document to
54 // make the style recalc re-entrant. If we do a style recalc for a document
55 // inside a style recalc for another document (which can happen when
56 // synchronously loading an svg generated content image), the previous_ pointer
57 // for the root scope of the inner recalc will point to the current scope of the
58 // outer one, but the root scope will isolate the inner from trying to push any
59 // parent stacks in the outer document.
60 class CORE_EXPORT SelectorFilterRootScope final
61 : private SelectorFilterParentScope {
62 STACK_ALLOCATED();
63
64 public:
65 // |parent| is nullptr when the documentElement() is the style recalc root.
SelectorFilterRootScope(Element * parent)66 explicit SelectorFilterRootScope(Element* parent)
67 : SelectorFilterParentScope(parent, ScopeType::kRoot) {}
68 };
69
SelectorFilterParentScope(Element * parent,ScopeType scope_type)70 inline SelectorFilterParentScope::SelectorFilterParentScope(
71 Element* parent,
72 ScopeType scope_type)
73 : parent_(parent),
74 scope_type_(scope_type),
75 previous_(current_scope_),
76 resolver_(nullptr) {
77 DCHECK(scope_type != ScopeType::kRoot || !parent || !previous_ ||
78 !previous_->parent_ ||
79 &parent_->GetDocument() != &previous_->parent_->GetDocument());
80 if (parent) {
81 DCHECK(parent->GetDocument().InStyleRecalc());
82 resolver_ = &parent->GetDocument().GetStyleResolver();
83 }
84 current_scope_ = this;
85 }
86
~SelectorFilterParentScope()87 inline SelectorFilterParentScope::~SelectorFilterParentScope() {
88 current_scope_ = previous_;
89 if (!pushed_)
90 return;
91 DCHECK(resolver_);
92 DCHECK(parent_);
93 resolver_->GetSelectorFilter().PopParent(*parent_);
94 if (scope_type_ == ScopeType::kRoot)
95 PopAncestors(*parent_);
96 }
97
EnsureParentStackIsPushed()98 inline void SelectorFilterParentScope::EnsureParentStackIsPushed() {
99 if (current_scope_)
100 current_scope_->PushParentIfNeeded();
101 }
102
PushParentIfNeeded()103 inline void SelectorFilterParentScope::PushParentIfNeeded() {
104 if (pushed_)
105 return;
106 if (!parent_) {
107 DCHECK(scope_type_ == ScopeType::kRoot);
108 return;
109 }
110 if (scope_type_ == ScopeType::kRoot) {
111 PushAncestors(*parent_);
112 } else {
113 DCHECK(previous_);
114 previous_->PushParentIfNeeded();
115 }
116 resolver_->GetSelectorFilter().PushParent(*parent_);
117 pushed_ = true;
118 }
119
120 } // namespace blink
121
122 #endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_SELECTOR_FILTER_PARENT_SCOPE_H_
123