layout/base/RestyleTracker.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     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  */
    11 #ifndef mozilla_RestyleTracker_h
    12 #define mozilla_RestyleTracker_h
    14 #include "mozilla/dom/Element.h"
    15 #include "nsDataHashtable.h"
    16 #include "nsIFrame.h"
    17 #include "mozilla/SplayTree.h"
    19 namespace mozilla {
    21 class RestyleManager;
    23 /** 
    24  * Helper class that collects a list of frames that need
    25  * UpdateOverflow() called on them, and coalesces them
    26  * to avoid walking up the same ancestor tree multiple times.
    27  */
    28 class OverflowChangedTracker
    29 {
    30 public:
    31   enum ChangeKind {
    32     /**
    33      * The frame was explicitly added as a result of
    34      * nsChangeHint_UpdatePostTransformOverflow and hence may have had a style
    35      * change that changes its geometry relative to parent, without reflowing.
    36      */
    37     TRANSFORM_CHANGED,
    38     /**
    39      * The overflow areas of children have changed
    40      * and we need to call UpdateOverflow on the frame.
    41      */
    42     CHILDREN_CHANGED,
    43     /**
    44      * The overflow areas of children have changed
    45      * and we need to call UpdateOverflow on the frame.
    46      * Also call UpdateOverflow on the parent even if the
    47      * overflow areas of the frame does not change.
    48      */
    49     CHILDREN_AND_PARENT_CHANGED
    50   };
    52   OverflowChangedTracker() :
    53     mSubtreeRoot(nullptr)
    54   {}
    56   ~OverflowChangedTracker()
    57   {
    58     NS_ASSERTION(mEntryList.empty(), "Need to flush before destroying!");
    59   }
    61   /**
    62    * Add a frame that has had a style change, and needs its
    63    * overflow updated.
    64    *
    65    * If there are pre-transform overflow areas stored for this
    66    * frame, then we will call FinishAndStoreOverflow with those
    67    * areas instead of UpdateOverflow().
    68    *
    69    * If the overflow area changes, then UpdateOverflow will also
    70    * be called on the parent.
    71    */
    72   void AddFrame(nsIFrame* aFrame, ChangeKind aChangeKind) {
    73     uint32_t depth = aFrame->GetDepthInFrameTree();
    74     Entry *entry = nullptr;
    75     if (!mEntryList.empty()) {
    76       entry = mEntryList.find(Entry(aFrame, depth));
    77     }
    78     if (entry == nullptr) {
    79       // Add new entry.
    80       mEntryList.insert(new Entry(aFrame, depth, aChangeKind));
    81     } else {
    82       // Update the existing entry if the new value is stronger.
    83       entry->mChangeKind = std::max(entry->mChangeKind, aChangeKind);
    84     }
    85   }
    87   /**
    88    * Remove a frame.
    89    */
    90   void RemoveFrame(nsIFrame* aFrame) {
    91     if (mEntryList.empty()) {
    92       return;
    93     }
    95     uint32_t depth = aFrame->GetDepthInFrameTree();
    96     if (mEntryList.find(Entry(aFrame, depth))) {
    97       delete mEntryList.remove(Entry(aFrame, depth));
    98     }
    99   }
   101   /**
   102    * Set the subtree root to limit overflow updates. This must be set if and
   103    * only if currently reflowing aSubtreeRoot, to ensure overflow changes will
   104    * still propagate correctly.
   105    */
   106   void SetSubtreeRoot(const nsIFrame* aSubtreeRoot) {
   107     mSubtreeRoot = aSubtreeRoot;
   108   }
   110   /**
   111    * Update the overflow of all added frames, and clear the entry list.
   112    *
   113    * Start from those deepest in the frame tree and works upwards. This stops 
   114    * us from processing the same frame twice.
   115    */
   116   void Flush() {
   117     while (!mEntryList.empty()) {
   118       Entry *entry = mEntryList.removeMin();
   119       nsIFrame *frame = entry->mFrame;
   121       bool overflowChanged = false;
   122       if (entry->mChangeKind == CHILDREN_AND_PARENT_CHANGED) {
   123         // Need to union the overflow areas of the children.
   124         // Always update the parent, even if the overflow does not change.
   125         frame->UpdateOverflow();
   126         overflowChanged = true;
   127       } else if (entry->mChangeKind == CHILDREN_CHANGED) {
   128         // Need to union the overflow areas of the children.
   129         // Only update the parent if the overflow changes.
   130         overflowChanged = frame->UpdateOverflow();
   131       } else {
   132         // Take a faster path that doesn't require unioning the overflow areas
   133         // of our children.
   135 #ifdef DEBUG
   136         bool hasInitialOverflowPropertyApplied = false;
   137         frame->Properties().Get(nsIFrame::DebugInitialOverflowPropertyApplied(),
   138                                  &hasInitialOverflowPropertyApplied);
   139         NS_ASSERTION(hasInitialOverflowPropertyApplied,
   140                      "InitialOverflowProperty must be set first.");
   141 #endif
   143         nsOverflowAreas* overflow = 
   144           static_cast<nsOverflowAreas*>(frame->Properties().Get(nsIFrame::InitialOverflowProperty()));
   145         if (overflow) {
   146           // FinishAndStoreOverflow will change the overflow areas passed in,
   147           // so make a copy.
   148           nsOverflowAreas overflowCopy = *overflow;
   149           frame->FinishAndStoreOverflow(overflowCopy, frame->GetSize());
   150         } else {
   151           nsRect bounds(nsPoint(0, 0), frame->GetSize());
   152           nsOverflowAreas boundsOverflow;
   153           boundsOverflow.SetAllTo(bounds);
   154           frame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
   155         }
   157         // We can't tell if the overflow changed, so be conservative
   158         overflowChanged = true;
   159       }
   161       // If the frame style changed (e.g. positioning offsets)
   162       // then we need to update the parent with the overflow areas of its
   163       // children.
   164       if (overflowChanged) {
   165         nsIFrame *parent = frame->GetParent();
   166         if (parent && parent != mSubtreeRoot) {
   167           Entry* parentEntry = mEntryList.find(Entry(parent, entry->mDepth - 1));
   168           if (parentEntry) {
   169             parentEntry->mChangeKind = std::max(parentEntry->mChangeKind, CHILDREN_CHANGED);
   170           } else {
   171             mEntryList.insert(new Entry(parent, entry->mDepth - 1, CHILDREN_CHANGED));
   172           }
   173         }
   174       }
   175       delete entry;
   176     }
   177   }
   179 private:
   180   struct Entry : SplayTreeNode<Entry>
   181   {
   182     Entry(nsIFrame* aFrame, uint32_t aDepth, ChangeKind aChangeKind = CHILDREN_CHANGED)
   183       : mFrame(aFrame)
   184       , mDepth(aDepth)
   185       , mChangeKind(aChangeKind)
   186     {}
   188     bool operator==(const Entry& aOther) const
   189     {
   190       return mFrame == aOther.mFrame;
   191     }
   193     /**
   194      * Sort by *reverse* depth in the tree, and break ties with
   195      * the frame pointer.
   196      */
   197     bool operator<(const Entry& aOther) const
   198     {
   199       if (mDepth == aOther.mDepth) {
   200         return mFrame < aOther.mFrame;
   201       }
   202       return mDepth > aOther.mDepth; /* reverse, want "min" to be deepest */
   203     }
   205     static int compare(const Entry& aOne, const Entry& aTwo)
   206     {
   207       if (aOne == aTwo) {
   208         return 0;
   209       } else if (aOne < aTwo) {
   210         return -1;
   211       } else {
   212         return 1;
   213       }
   214     }
   216     nsIFrame* mFrame;
   217     /* Depth in the frame tree */
   218     uint32_t mDepth;
   219     ChangeKind mChangeKind;
   220   };
   222   /* A list of frames to process, sorted by their depth in the frame tree */
   223   SplayTree<Entry, Entry> mEntryList;
   225   /* Don't update overflow of this frame or its ancestors. */
   226   const nsIFrame* mSubtreeRoot;
   227 };
   229 class RestyleTracker {
   230 public:
   231   typedef mozilla::dom::Element Element;
   233   RestyleTracker(uint32_t aRestyleBits) :
   234     mRestyleBits(aRestyleBits),
   235     mHaveLaterSiblingRestyles(false)
   236   {
   237     NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0,
   238                     "Why do we have these bits set?");
   239     NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != 0,
   240                     "Must have a restyle flag");
   241     NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) !=
   242                       ELEMENT_PENDING_RESTYLE_FLAGS,
   243                     "Shouldn't have both restyle flags set");
   244     NS_PRECONDITION((mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS) != 0,
   245                     "Must have root flag");
   246     NS_PRECONDITION((mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS) !=
   247                     (ELEMENT_ALL_RESTYLE_FLAGS & ~ELEMENT_PENDING_RESTYLE_FLAGS),
   248                     "Shouldn't have both root flags");
   249   }
   251   void Init(RestyleManager* aRestyleManager) {
   252     mRestyleManager = aRestyleManager;
   253   }
   255   uint32_t Count() const {
   256     return mPendingRestyles.Count();
   257   }
   259   /**
   260    * Add a restyle for the given element to the tracker.  Returns true
   261    * if the element already had eRestyle_LaterSiblings set on it.
   262    */
   263   bool AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint,
   264                            nsChangeHint aMinChangeHint);
   266   /**
   267    * Process the restyles we've been tracking.
   268    */
   269   void ProcessRestyles() {
   270     // Fast-path the common case (esp. for the animation restyle
   271     // tracker) of not having anything to do.
   272     if (mPendingRestyles.Count()) {
   273       DoProcessRestyles();
   274     }
   275   }
   277   // Return our ELEMENT_HAS_PENDING_(ANIMATION_)RESTYLE bit
   278   uint32_t RestyleBit() const {
   279     return mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS;
   280   }
   282   // Return our ELEMENT_IS_POTENTIAL_(ANIMATION_)RESTYLE_ROOT bit
   283   uint32_t RootBit() const {
   284     return mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS;
   285   }
   287   struct RestyleData {
   288     nsRestyleHint mRestyleHint;  // What we want to restyle
   289     nsChangeHint  mChangeHint;   // The minimal change hint for "self"
   290   };
   292   /**
   293    * If the given Element has a restyle pending for it, return the
   294    * relevant restyle data.  This function will clear everything other
   295    * than a possible eRestyle_LaterSiblings hint for aElement out of
   296    * our hashtable.  The returned aData will never have an
   297    * eRestyle_LaterSiblings hint in it.
   298    *
   299    * The return value indicates whether any restyle data was found for
   300    * the element.  If false is returned, then the state of *aData is
   301    * undefined.
   302    */
   303   bool GetRestyleData(Element* aElement, RestyleData* aData);
   305   /**
   306    * The document we're associated with.
   307    */
   308   inline nsIDocument* Document() const;
   310   struct RestyleEnumerateData : public RestyleData {
   311     nsRefPtr<Element> mElement;
   312   };
   314 private:
   315   /**
   316    * Handle a single mPendingRestyles entry.  aRestyleHint must not
   317    * include eRestyle_LaterSiblings; that needs to be dealt with
   318    * before calling this function.
   319    */
   320   inline void ProcessOneRestyle(Element* aElement,
   321                                 nsRestyleHint aRestyleHint,
   322                                 nsChangeHint aChangeHint);
   324   /**
   325    * The guts of our restyle processing.
   326    */
   327   void DoProcessRestyles();
   329   typedef nsDataHashtable<nsISupportsHashKey, RestyleData> PendingRestyleTable;
   330   typedef nsAutoTArray< nsRefPtr<Element>, 32> RestyleRootArray;
   331   // Our restyle bits.  These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and
   332   // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS and one flag
   333   // that's not in ELEMENT_PENDING_RESTYLE_FLAGS.
   334   uint32_t mRestyleBits;
   335   RestyleManager* mRestyleManager; // Owns us
   336   // A hashtable that maps elements to RestyleData structs.  The
   337   // values only make sense if the element's current document is our
   338   // document and it has our RestyleBit() flag set.  In particular,
   339   // said bit might not be set if the element had a restyle posted and
   340   // then was moved around in the DOM.
   341   PendingRestyleTable mPendingRestyles;
   342   // An array that keeps track of our possible restyle roots.  This
   343   // maintains the invariant that if A and B are both restyle roots
   344   // and A is an ancestor of B then A will come after B in the array.
   345   // We maintain this invariant by checking whether an element has an
   346   // ancestor with the restyle root bit set before appending it to the
   347   // array.
   348   RestyleRootArray mRestyleRoots;
   349   // True if we have some entries with the eRestyle_LaterSiblings
   350   // flag.  We need this to avoid enumerating the hashtable looking
   351   // for such entries when we can't possibly have any.
   352   bool mHaveLaterSiblingRestyles;
   353 };
   355 inline bool RestyleTracker::AddPendingRestyle(Element* aElement,
   356                                                 nsRestyleHint aRestyleHint,
   357                                                 nsChangeHint aMinChangeHint)
   358 {
   359   RestyleData existingData;
   360   existingData.mRestyleHint = nsRestyleHint(0);
   361   existingData.mChangeHint = NS_STYLE_HINT_NONE;
   363   // Check the RestyleBit() flag before doing the hashtable Get, since
   364   // it's possible that the data in the hashtable isn't actually
   365   // relevant anymore (if the flag is not set).
   366   if (aElement->HasFlag(RestyleBit())) {
   367     mPendingRestyles.Get(aElement, &existingData);
   368   } else {
   369     aElement->SetFlags(RestyleBit());
   370   }
   372   bool hadRestyleLaterSiblings =
   373     (existingData.mRestyleHint & eRestyle_LaterSiblings) != 0;
   374   existingData.mRestyleHint =
   375     nsRestyleHint(existingData.mRestyleHint | aRestyleHint);
   376   NS_UpdateHint(existingData.mChangeHint, aMinChangeHint);
   378   mPendingRestyles.Put(aElement, existingData);
   380   // We can only treat this element as a restyle root if we would
   381   // actually restyle its descendants (so either call
   382   // ReResolveStyleContext on it or just reframe it).
   383   if ((aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) ||
   384       (aMinChangeHint & nsChangeHint_ReconstructFrame)) {
   385     for (const Element* cur = aElement; !cur->HasFlag(RootBit()); ) {
   386       nsIContent* parent = cur->GetFlattenedTreeParent();
   387       // Stop if we have no parent or the parent is not an element or
   388       // we're part of the viewport scrollbars (because those are not
   389       // frametree descendants of the primary frame of the root
   390       // element).
   391       // XXXbz maybe the primary frame of the root should be the root scrollframe?
   392       if (!parent || !parent->IsElement() ||
   393           // If we've hit the root via a native anonymous kid and that
   394           // this native anonymous kid is not obviously a descendant
   395           // of the root's primary frame, assume we're under the root
   396           // scrollbars.  Since those don't get reresolved when
   397           // reresolving the root, we need to make sure to add the
   398           // element to mRestyleRoots.
   399           (cur->IsInNativeAnonymousSubtree() && !parent->GetParent() &&
   400            cur->GetPrimaryFrame() &&
   401            cur->GetPrimaryFrame()->GetParent() != parent->GetPrimaryFrame())) {
   402         mRestyleRoots.AppendElement(aElement);
   403         break;
   404       }
   405       cur = parent->AsElement();
   406     }
   407     // At this point some ancestor of aElement (possibly aElement
   408     // itself) is in mRestyleRoots.  Set the root bit on aElement, to
   409     // speed up searching for an existing root on its descendants.
   410     aElement->SetFlags(RootBit());
   411   }
   413   mHaveLaterSiblingRestyles =
   414     mHaveLaterSiblingRestyles || (aRestyleHint & eRestyle_LaterSiblings) != 0;
   415   return hadRestyleLaterSiblings;
   416 }
   418 } // namespace mozilla
   420 #endif /* mozilla_RestyleTracker_h */

mercurial