1
2 // Copyright 2014 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "config.h"
7
8 #include "core/css/invalidation/StyleInvalidator.h"
9
10 #include "core/css/invalidation/DescendantInvalidationSet.h"
11 #include "core/dom/Document.h"
12 #include "core/dom/Element.h"
13 #include "core/dom/ElementTraversal.h"
14 #include "core/dom/shadow/ElementShadow.h"
15 #include "core/dom/shadow/ShadowRoot.h"
16 #include "core/rendering/RenderObject.h"
17
18 namespace blink {
19
invalidate(Document & document)20 void StyleInvalidator::invalidate(Document& document)
21 {
22 RecursionData recursionData;
23 if (Element* documentElement = document.documentElement())
24 invalidate(*documentElement, recursionData);
25 document.clearChildNeedsStyleInvalidation();
26 document.clearNeedsStyleInvalidation();
27 clearPendingInvalidations();
28 }
29
scheduleInvalidation(PassRefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet,Element & element)30 void StyleInvalidator::scheduleInvalidation(PassRefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet, Element& element)
31 {
32 ASSERT(element.inActiveDocument());
33 ASSERT(element.styleChangeType() < SubtreeStyleChange);
34 InvalidationList& list = ensurePendingInvalidationList(element);
35 // If we're already going to invalidate the whole subtree we don't need to store any new sets.
36 if (!list.isEmpty() && list.last()->wholeSubtreeInvalid())
37 return;
38 // If this set would invalidate the whole subtree we can discard all existing sets.
39 if (invalidationSet->wholeSubtreeInvalid())
40 list.clear();
41 list.append(invalidationSet);
42 element.setNeedsStyleInvalidation();
43 }
44
ensurePendingInvalidationList(Element & element)45 StyleInvalidator::InvalidationList& StyleInvalidator::ensurePendingInvalidationList(Element& element)
46 {
47 PendingInvalidationMap::AddResult addResult = m_pendingInvalidationMap.add(&element, nullptr);
48 if (addResult.isNewEntry)
49 addResult.storedValue->value = adoptPtrWillBeNoop(new InvalidationList);
50 return *addResult.storedValue->value;
51 }
52
clearInvalidation(Node & node)53 void StyleInvalidator::clearInvalidation(Node& node)
54 {
55 if (node.isElementNode() && node.needsStyleInvalidation())
56 m_pendingInvalidationMap.remove(toElement(&node));
57 }
58
clearPendingInvalidations()59 void StyleInvalidator::clearPendingInvalidations()
60 {
61 m_pendingInvalidationMap.clear();
62 }
63
StyleInvalidator()64 StyleInvalidator::StyleInvalidator()
65 {
66 }
67
~StyleInvalidator()68 StyleInvalidator::~StyleInvalidator()
69 {
70 }
71
pushInvalidationSet(const DescendantInvalidationSet & invalidationSet)72 void StyleInvalidator::RecursionData::pushInvalidationSet(const DescendantInvalidationSet& invalidationSet)
73 {
74 ASSERT(!m_wholeSubtreeInvalid);
75 if (invalidationSet.treeBoundaryCrossing())
76 m_treeBoundaryCrossing = true;
77 if (invalidationSet.wholeSubtreeInvalid()) {
78 m_wholeSubtreeInvalid = true;
79 return;
80 }
81 m_invalidationSets.append(&invalidationSet);
82 m_invalidateCustomPseudo = invalidationSet.customPseudoInvalid();
83 }
84
matchesCurrentInvalidationSets(Element & element)85 bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& element)
86 {
87 ASSERT(!m_wholeSubtreeInvalid);
88
89 if (m_invalidateCustomPseudo && element.shadowPseudoId() != nullAtom)
90 return true;
91
92 for (InvalidationSets::iterator it = m_invalidationSets.begin(); it != m_invalidationSets.end(); ++it) {
93 if ((*it)->invalidatesElement(element))
94 return true;
95 }
96
97 return false;
98 }
99
checkInvalidationSetsAgainstElement(Element & element,StyleInvalidator::RecursionData & recursionData)100 bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, StyleInvalidator::RecursionData& recursionData)
101 {
102 if (element.styleChangeType() >= SubtreeStyleChange || recursionData.wholeSubtreeInvalid()) {
103 recursionData.setWholeSubtreeInvalid();
104 return false;
105 }
106 if (element.needsStyleInvalidation()) {
107 if (InvalidationList* invalidationList = m_pendingInvalidationMap.get(&element)) {
108 for (InvalidationList::const_iterator it = invalidationList->begin(); it != invalidationList->end(); ++it)
109 recursionData.pushInvalidationSet(**it);
110 // FIXME: It's really only necessary to clone the render style for this element, not full style recalc.
111 return true;
112 }
113 }
114 return recursionData.matchesCurrentInvalidationSets(element);
115 }
116
invalidateChildren(Element & element,StyleInvalidator::RecursionData & recursionData)117 bool StyleInvalidator::invalidateChildren(Element& element, StyleInvalidator::RecursionData& recursionData)
118 {
119 bool someChildrenNeedStyleRecalc = false;
120 for (ShadowRoot* root = element.youngestShadowRoot(); root; root = root->olderShadowRoot()) {
121 if (!recursionData.treeBoundaryCrossing() && !root->childNeedsStyleInvalidation() && !root->needsStyleInvalidation())
122 continue;
123 for (Element* child = ElementTraversal::firstChild(*root); child; child = ElementTraversal::nextSibling(*child)) {
124 bool childRecalced = invalidate(*child, recursionData);
125 someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced;
126 }
127 root->clearChildNeedsStyleInvalidation();
128 root->clearNeedsStyleInvalidation();
129 }
130 for (Element* child = ElementTraversal::firstChild(element); child; child = ElementTraversal::nextSibling(*child)) {
131 bool childRecalced = invalidate(*child, recursionData);
132 someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced;
133 }
134 return someChildrenNeedStyleRecalc;
135 }
136
invalidate(Element & element,StyleInvalidator::RecursionData & recursionData)137 bool StyleInvalidator::invalidate(Element& element, StyleInvalidator::RecursionData& recursionData)
138 {
139 RecursionCheckpoint checkpoint(&recursionData);
140
141 bool thisElementNeedsStyleRecalc = checkInvalidationSetsAgainstElement(element, recursionData);
142
143 bool someChildrenNeedStyleRecalc = false;
144 if (recursionData.hasInvalidationSets() || element.childNeedsStyleInvalidation())
145 someChildrenNeedStyleRecalc = invalidateChildren(element, recursionData);
146
147 if (thisElementNeedsStyleRecalc) {
148 element.setNeedsStyleRecalc(recursionData.wholeSubtreeInvalid() ? SubtreeStyleChange : LocalStyleChange);
149 } else if (recursionData.hasInvalidationSets() && someChildrenNeedStyleRecalc) {
150 // Clone the RenderStyle in order to preserve correct style sharing, if possible. Otherwise recalc style.
151 if (RenderObject* renderer = element.renderer())
152 renderer->setStyleInternal(RenderStyle::clone(renderer->style()));
153 else
154 element.setNeedsStyleRecalc(LocalStyleChange);
155 }
156
157 element.clearChildNeedsStyleInvalidation();
158 element.clearNeedsStyleInvalidation();
159
160 return thisElementNeedsStyleRecalc;
161 }
162
trace(Visitor * visitor)163 void StyleInvalidator::trace(Visitor* visitor)
164 {
165 #if ENABLE(OILPAN)
166 visitor->trace(m_pendingInvalidationMap);
167 #endif
168 }
169
170 } // namespace blink
171