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 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 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 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 53 void StyleInvalidator::clearInvalidation(Node& node) 54 { 55 if (node.isElementNode() && node.needsStyleInvalidation()) 56 m_pendingInvalidationMap.remove(toElement(&node)); 57 } 58 59 void StyleInvalidator::clearPendingInvalidations() 60 { 61 m_pendingInvalidationMap.clear(); 62 } 63 64 StyleInvalidator::StyleInvalidator() 65 { 66 } 67 68 StyleInvalidator::~StyleInvalidator() 69 { 70 } 71 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 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 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 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 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 163 void StyleInvalidator::trace(Visitor* visitor) 164 { 165 #if ENABLE(OILPAN) 166 visitor->trace(m_pendingInvalidationMap); 167 #endif 168 } 169 170 } // namespace blink 171