LCOV - code coverage report
Current view: top level - layout/base - RestyleTracker.h (source / functions) Hit Total Coverage
Test: output.info Lines: 84 89 94.4 %
Date: 2017-07-14 16:53:18 Functions: 15 16 93.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /**
       7             :  * A class which manages pending restyles.  This handles keeping track
       8             :  * of what nodes restyles need to happen on and so forth.
       9             :  */
      10             : 
      11             : #ifndef mozilla_RestyleTracker_h
      12             : #define mozilla_RestyleTracker_h
      13             : 
      14             : #include "mozilla/dom/Element.h"
      15             : #include "mozilla/OverflowChangedTracker.h"
      16             : #include "nsAutoPtr.h"
      17             : #include "nsClassHashtable.h"
      18             : #include "nsContainerFrame.h"
      19             : #include "nsIContentInlines.h"
      20             : #include "mozilla/SplayTree.h"
      21             : #include "mozilla/RestyleLogging.h"
      22             : #include "GeckoProfiler.h"
      23             : #include "mozilla/Maybe.h"
      24             : 
      25             : namespace mozilla {
      26             : 
      27             : class ElementRestyler;
      28             : class GeckoRestyleManager;
      29             : 
      30           5 : class RestyleTracker {
      31             : public:
      32             :   typedef mozilla::dom::Element Element;
      33             : 
      34             :   friend class ElementRestyler; // for AddPendingRestyleToTable
      35             : 
      36          29 :   explicit RestyleTracker(Element::FlagsType aRestyleBits)
      37          29 :     : mRestyleBits(aRestyleBits)
      38             :     , mHaveLaterSiblingRestyles(false)
      39          29 :     , mHaveSelectors(false)
      40             :   {
      41          29 :     NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0,
      42             :                     "Why do we have these bits set?");
      43          29 :     NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != 0,
      44             :                     "Must have a restyle flag");
      45          29 :     NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) !=
      46             :                       ELEMENT_PENDING_RESTYLE_FLAGS,
      47             :                     "Shouldn't have both restyle flags set");
      48          29 :     NS_PRECONDITION((mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS) != 0,
      49             :                     "Must have root flag");
      50          29 :     NS_PRECONDITION((mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS) !=
      51             :                     ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS,
      52             :                     "Shouldn't have both root flags");
      53          29 :   }
      54             : 
      55          29 :   void Init(GeckoRestyleManager* aRestyleManager) {
      56          29 :     mRestyleManager = aRestyleManager;
      57          29 :   }
      58             : 
      59         267 :   uint32_t Count() const {
      60         267 :     return mPendingRestyles.Count();
      61             :   }
      62             : 
      63             :   /**
      64             :    * Add a restyle for the given element to the tracker.  Returns true
      65             :    * if the element already had eRestyle_LaterSiblings set on it.
      66             :    *
      67             :    * aRestyleRoot is the closest restyle root for aElement.  If the caller
      68             :    * does not know what the closest restyle root is, Nothing should be
      69             :    * passed.  A Some(nullptr) restyle root can be passed if there is no
      70             :    * ancestor element that is a restyle root.
      71             :    */
      72             :   bool AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint,
      73             :                          nsChangeHint aMinChangeHint,
      74             :                          const RestyleHintData* aRestyleHintData = nullptr,
      75             :                          const mozilla::Maybe<Element*>& aRestyleRoot =
      76             :                            mozilla::Nothing());
      77             : 
      78             :   Element* FindClosestRestyleRoot(Element* aElement);
      79             : 
      80             :   /**
      81             :    * Process the restyles we've been tracking.
      82             :    */
      83             :   void DoProcessRestyles();
      84             : 
      85             :   // Return our ELEMENT_HAS_PENDING_(ANIMATION_)RESTYLE bit
      86        2226 :   uint32_t RestyleBit() const {
      87        2226 :     return mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS;
      88             :   }
      89             : 
      90             :   // Return our ELEMENT_IS_POTENTIAL_(ANIMATION_)RESTYLE_ROOT bit
      91        3665 :   Element::FlagsType RootBit() const {
      92        3665 :     return mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS;
      93             :   }
      94             : 
      95             :   // Return our ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR bit if present,
      96             :   // or 0 if it is not.
      97         100 :   Element::FlagsType ConditionalDescendantsBit() const {
      98         100 :     return mRestyleBits & ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR;
      99             :   }
     100             : 
     101         360 :   struct Hints {
     102             :     nsRestyleHint mRestyleHint;        // What we want to restyle
     103             :     nsChangeHint mChangeHint;          // The minimal change hint for "self"
     104             :     RestyleHintData mRestyleHintData;  // Data associated with mRestyleHint
     105             :   };
     106             : 
     107         161 :   struct RestyleData : Hints {
     108           0 :     RestyleData() {
     109           0 :       mRestyleHint = nsRestyleHint(0);
     110           0 :       mChangeHint = nsChangeHint(0);
     111           0 :     }
     112             : 
     113         161 :     RestyleData(nsRestyleHint aRestyleHint, nsChangeHint aChangeHint,
     114         161 :                 const RestyleHintData* aRestyleHintData) {
     115         161 :       mRestyleHint = aRestyleHint;
     116         161 :       mChangeHint = aChangeHint;
     117         161 :       if (aRestyleHintData) {
     118          95 :         mRestyleHintData = *aRestyleHintData;
     119             :       }
     120         161 :     }
     121             : 
     122             :     // Descendant elements we must check that we ended up restyling, ordered
     123             :     // with the same invariant as mRestyleRoots.  The elements here are those
     124             :     // that we called AddPendingRestyle for and found the element this is
     125             :     // the RestyleData for as its nearest restyle root.
     126             :     nsTArray<RefPtr<Element>> mDescendants;
     127             :     UniqueProfilerBacktrace mBacktrace;
     128             :   };
     129             : 
     130             :   /**
     131             :    * If the given Element has a restyle pending for it, return the
     132             :    * relevant restyle data.  This function will clear everything other
     133             :    * than a possible eRestyle_LaterSiblings hint for aElement out of
     134             :    * our hashtable.  The returned aData will never have an
     135             :    * eRestyle_LaterSiblings hint in it.
     136             :    *
     137             :    * The return value indicates whether any restyle data was found for
     138             :    * the element.  aData is set to nullptr iff false is returned.
     139             :    */
     140             :   bool GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData);
     141             : 
     142             :   /**
     143             :    * Returns whether there is a RestyleData entry in mPendingRestyles
     144             :    * for the given element.
     145             :    */
     146         276 :   bool HasRestyleData(Element* aElement) {
     147         276 :     return mPendingRestyles.Contains(aElement);
     148             :   }
     149             : 
     150             :   /**
     151             :    * For each element in aElements, appends it to mRestyleRoots if it
     152             :    * has its restyle bit set.  This is used to ensure we restyle elements
     153             :    * that we did not add as restyle roots initially (due to there being
     154             :    * an ancestor with the restyle root bit set), but which we might
     155             :    * not have got around to restyling due to the restyle process
     156             :    * terminating early with RestyleResul::eStop (see ElementRestyler::Restyle).
     157             :    *
     158             :    * This function must be called with elements in order such that
     159             :    * appending them to mRestyleRoots maintains its ordering invariant that
     160             :    * ancestors appear after descendants.
     161             :    */
     162             :   void AddRestyleRootsIfAwaitingRestyle(
     163             :                                   const nsTArray<RefPtr<Element>>& aElements);
     164             : 
     165             :   /**
     166             :    * Converts any eRestyle_SomeDescendants restyle hints in the pending restyle
     167             :    * table into eRestyle_Subtree hints and clears out the associated arrays of
     168             :    * nsCSSSelector pointers.  This is called in response to a style sheet change
     169             :    * that might have cause an nsCSSSelector to be destroyed.
     170             :    */
     171             :   void ClearSelectors();
     172             : 
     173             :   /**
     174             :    * The document we're associated with.
     175             :    */
     176             :   inline nsIDocument* Document() const;
     177             : 
     178             : #ifdef RESTYLE_LOGGING
     179             :   // Defined in RestyleTrackerInlines.h.
     180             :   inline bool ShouldLogRestyle();
     181             :   inline int32_t& LoggingDepth();
     182             : #endif
     183             : 
     184             : private:
     185             :   bool AddPendingRestyleToTable(Element* aElement, nsRestyleHint aRestyleHint,
     186             :                                 nsChangeHint aMinChangeHint,
     187             :                                 const RestyleHintData* aRestyleHintData = nullptr);
     188             : 
     189             :   /**
     190             :    * Handle a single mPendingRestyles entry.  aRestyleHint must not
     191             :    * include eRestyle_LaterSiblings; that needs to be dealt with
     192             :    * before calling this function.
     193             :    */
     194             :   inline void ProcessOneRestyle(Element* aElement,
     195             :                                 nsRestyleHint aRestyleHint,
     196             :                                 nsChangeHint aChangeHint,
     197             :                                 const RestyleHintData& aRestyleHintData);
     198             : 
     199             :   typedef nsClassHashtable<nsISupportsHashKey, RestyleData> PendingRestyleTable;
     200             :   typedef AutoTArray< RefPtr<Element>, 32> RestyleRootArray;
     201             :   // Our restyle bits.  These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and
     202             :   // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS, one flag
     203             :   // from ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS, and might also include
     204             :   // ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR.
     205             :   Element::FlagsType mRestyleBits;
     206             :   GeckoRestyleManager* mRestyleManager; // Owns us
     207             :   // A hashtable that maps elements to pointers to RestyleData structs.  The
     208             :   // values only make sense if the element's current document is our
     209             :   // document and it has our RestyleBit() flag set.  In particular,
     210             :   // said bit might not be set if the element had a restyle posted and
     211             :   // then was moved around in the DOM.
     212             :   PendingRestyleTable mPendingRestyles;
     213             :   // An array that keeps track of our possible restyle roots.  This
     214             :   // maintains the invariant that if A and B are both restyle roots
     215             :   // and A is an ancestor of B then A will come after B in the array.
     216             :   // We maintain this invariant by checking whether an element has an
     217             :   // ancestor with the restyle root bit set before appending it to the
     218             :   // array.
     219             :   RestyleRootArray mRestyleRoots;
     220             :   // True if we have some entries with the eRestyle_LaterSiblings
     221             :   // flag.  We need this to avoid enumerating the hashtable looking
     222             :   // for such entries when we can't possibly have any.
     223             :   bool mHaveLaterSiblingRestyles;
     224             :   // True if we have some entries with selectors in the restyle hint data.
     225             :   // We use this to skip iterating over mPendingRestyles in ClearSelectors.
     226             :   bool mHaveSelectors;
     227             : };
     228             : 
     229             : inline bool
     230         228 : RestyleTracker::AddPendingRestyleToTable(Element* aElement,
     231             :                                          nsRestyleHint aRestyleHint,
     232             :                                          nsChangeHint aMinChangeHint,
     233             :                                          const RestyleHintData* aRestyleHintData)
     234             : {
     235             :   RestyleData* existingData;
     236             : 
     237         365 :   if (aRestyleHintData &&
     238         137 :       !aRestyleHintData->mSelectorsForDescendants.IsEmpty()) {
     239          41 :     mHaveSelectors = true;
     240             :   }
     241             : 
     242             :   // Check the RestyleBit() flag before doing the hashtable Get, since
     243             :   // it's possible that the data in the hashtable isn't actually
     244             :   // relevant anymore (if the flag is not set).
     245         228 :   if (aElement->HasFlag(RestyleBit())) {
     246          67 :     mPendingRestyles.Get(aElement, &existingData);
     247             :   } else {
     248         161 :     aElement->SetFlags(RestyleBit());
     249         161 :     existingData = nullptr;
     250             :   }
     251             : 
     252         228 :   if (aRestyleHint & eRestyle_SomeDescendants) {
     253          43 :     NS_ASSERTION(ConditionalDescendantsBit(),
     254             :                  "why are we getting eRestyle_SomeDescendants in an "
     255             :                  "animation-only restyle?");
     256          43 :     aElement->SetFlags(ConditionalDescendantsBit());
     257             :   }
     258             : 
     259         228 :   if (!existingData) {
     260             :     RestyleData* rd =
     261         161 :       new RestyleData(aRestyleHint, aMinChangeHint, aRestyleHintData);
     262         161 :     if (profiler_feature_active(ProfilerFeature::Restyle)) {
     263           0 :       rd->mBacktrace = profiler_get_backtrace();
     264             :     }
     265         161 :     mPendingRestyles.Put(aElement, rd);
     266         161 :     return false;
     267             :   }
     268             : 
     269             :   bool hadRestyleLaterSiblings =
     270          67 :     (existingData->mRestyleHint & eRestyle_LaterSiblings) != 0;
     271         134 :   existingData->mRestyleHint =
     272          67 :     nsRestyleHint(existingData->mRestyleHint | aRestyleHint);
     273          67 :   existingData->mChangeHint |= aMinChangeHint;
     274          67 :   if (aRestyleHintData) {
     275             :     existingData->mRestyleHintData.mSelectorsForDescendants
     276          42 :       .AppendElements(aRestyleHintData->mSelectorsForDescendants);
     277             :   }
     278             : 
     279          67 :   return hadRestyleLaterSiblings;
     280             : }
     281             : 
     282             : inline mozilla::dom::Element*
     283         216 : RestyleTracker::FindClosestRestyleRoot(Element* aElement)
     284             : {
     285         216 :   Element* cur = aElement;
     286        2318 :   while (!cur->HasFlag(RootBit())) {
     287        1159 :     nsIContent* parent = cur->GetFlattenedTreeParent();
     288             :     // Stop if we have no parent or the parent is not an element or
     289             :     // we're part of the viewport scrollbars (because those are not
     290             :     // frametree descendants of the primary frame of the root
     291             :     // element).
     292             :     // XXXbz maybe the primary frame of the root should be the root scrollframe?
     293        2322 :     if (!parent || !parent->IsElement() ||
     294             :         // If we've hit the root via a native anonymous kid and that
     295             :         // this native anonymous kid is not obviously a descendant
     296             :         // of the root's primary frame, assume we're under the root
     297             :         // scrollbars.  Since those don't get reresolved when
     298             :         // reresolving the root, we need to make sure to add the
     299             :         // element to mRestyleRoots.
     300        1071 :         (cur->IsInNativeAnonymousSubtree() && !parent->GetParent() &&
     301           8 :          cur->GetPrimaryFrame() &&
     302           4 :          cur->GetPrimaryFrame()->GetParent() != parent->GetPrimaryFrame())) {
     303         108 :       return nullptr;
     304             :     }
     305        1051 :     cur = parent->AsElement();
     306             :   }
     307         108 :   return cur;
     308             : }
     309             : 
     310             : inline bool
     311         228 : RestyleTracker::AddPendingRestyle(Element* aElement,
     312             :                                   nsRestyleHint aRestyleHint,
     313             :                                   nsChangeHint aMinChangeHint,
     314             :                                   const RestyleHintData* aRestyleHintData,
     315             :                                   const mozilla::Maybe<Element*>& aRestyleRoot)
     316             : {
     317             :   bool hadRestyleLaterSiblings =
     318         228 :     AddPendingRestyleToTable(aElement, aRestyleHint, aMinChangeHint,
     319         228 :                              aRestyleHintData);
     320             : 
     321             :   // We can only treat this element as a restyle root if we would
     322             :   // actually restyle its descendants (so either call
     323             :   // ElementRestyler::Restyle on it or just reframe it).
     324         265 :   if ((aRestyleHint & ~eRestyle_LaterSiblings) ||
     325          37 :       (aMinChangeHint & nsChangeHint_ReconstructFrame)) {
     326             :     Element* cur =
     327         196 :       aRestyleRoot ? *aRestyleRoot : FindClosestRestyleRoot(aElement);
     328         196 :     if (!cur) {
     329          84 :       mRestyleRoots.AppendElement(aElement);
     330          84 :       cur = aElement;
     331             :     }
     332             :     // At this point some ancestor of aElement (possibly aElement
     333             :     // itself) is in mRestyleRoots.  Set the root bit on aElement, to
     334             :     // speed up searching for an existing root on its descendants.
     335         196 :     aElement->SetFlags(RootBit());
     336         196 :     if (cur != aElement) {
     337             :       // We are already going to restyle cur, one of aElement's ancestors,
     338             :       // but we might not end up restyling all the way down to aElement.
     339             :       // Record it in the RestyleData so we can ensure it does get restyled
     340             :       // after we deal with cur.
     341             :       //
     342             :       // As with the mRestyleRoots array, mDescendants maintains the
     343             :       // invariant that if two elements appear in the array and one
     344             :       // is an ancestor of the other, that the ancestor appears after
     345             :       // the descendant.
     346             :       RestyleData* curData;
     347          46 :       mPendingRestyles.Get(cur, &curData);
     348             : 
     349             :       // Even if cur has a ForceDescendants restyle hint, we're not guaranteed
     350             :       // to reach aElement in the case the PresShell posts a restyle event from
     351             :       // PostRecreateFramesFor, so we need to track it here.
     352          46 :       MOZ_ASSERT(curData, "expected to find a RestyleData for cur");
     353          46 :       if (curData) {
     354          46 :         curData->mDescendants.AppendElement(aElement);
     355             :       }
     356             :     }
     357             :   }
     358             : 
     359         228 :   mHaveLaterSiblingRestyles =
     360         228 :     mHaveLaterSiblingRestyles || (aRestyleHint & eRestyle_LaterSiblings) != 0;
     361         228 :   return hadRestyleLaterSiblings;
     362             : }
     363             : 
     364             : } // namespace mozilla
     365             : 
     366             : #endif /* mozilla_RestyleTracker_h */

Generated by: LCOV version 1.13