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 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