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