Home | History | Annotate | Download | only in invalidation
      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