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