• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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