LCOV - code coverage report
Current view: top level - layout/base - OverflowChangedTracker.h (source / functions) Hit Total Coverage
Test: output.info Lines: 13 71 18.3 %
Date: 2017-07-14 16:53:18 Functions: 4 10 40.0 %
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             : #ifndef mozilla_OverflowChangedTracker_h
       7             : #define mozilla_OverflowChangedTracker_h
       8             : 
       9             : #include "nsIFrame.h"
      10             : #include "nsContainerFrame.h"
      11             : #include "mozilla/SplayTree.h"
      12             : 
      13             : namespace mozilla {
      14             : 
      15             : /**
      16             :  * Helper class that collects a list of frames that need
      17             :  * UpdateOverflow() called on them, and coalesces them
      18             :  * to avoid walking up the same ancestor tree multiple times.
      19             :  */
      20             : class OverflowChangedTracker
      21             : {
      22             : public:
      23             :   enum ChangeKind {
      24             :     /**
      25             :      * The frame was explicitly added as a result of
      26             :      * nsChangeHint_UpdatePostTransformOverflow and hence may have had a style
      27             :      * change that changes its geometry relative to parent, without reflowing.
      28             :      */
      29             :     TRANSFORM_CHANGED,
      30             :     /**
      31             :      * The overflow areas of children have changed
      32             :      * and we need to call UpdateOverflow on the frame.
      33             :      */
      34             :     CHILDREN_CHANGED,
      35             :   };
      36             : 
      37          28 :   OverflowChangedTracker() :
      38          28 :     mSubtreeRoot(nullptr)
      39          28 :   {}
      40             : 
      41           4 :   ~OverflowChangedTracker()
      42           4 :   {
      43           4 :     NS_ASSERTION(mEntryList.empty(), "Need to flush before destroying!");
      44           4 :   }
      45             : 
      46             :   /**
      47             :    * Add a frame that has had a style change, and needs its
      48             :    * overflow updated.
      49             :    *
      50             :    * If there are pre-transform overflow areas stored for this
      51             :    * frame, then we will call FinishAndStoreOverflow with those
      52             :    * areas instead of UpdateOverflow().
      53             :    *
      54             :    * If the overflow area changes, then UpdateOverflow will also
      55             :    * be called on the parent.
      56             :    */
      57           0 :   void AddFrame(nsIFrame* aFrame, ChangeKind aChangeKind) {
      58           0 :     uint32_t depth = aFrame->GetDepthInFrameTree();
      59           0 :     Entry *entry = nullptr;
      60           0 :     if (!mEntryList.empty()) {
      61           0 :       entry = mEntryList.find(Entry(aFrame, depth));
      62             :     }
      63           0 :     if (entry == nullptr) {
      64             :       // Add new entry.
      65           0 :       mEntryList.insert(new Entry(aFrame, depth, aChangeKind));
      66             :     } else {
      67             :       // Update the existing entry if the new value is stronger.
      68           0 :       entry->mChangeKind = std::max(entry->mChangeKind, aChangeKind);
      69             :     }
      70           0 :   }
      71             : 
      72             :   /**
      73             :    * Remove a frame.
      74             :    */
      75         126 :   void RemoveFrame(nsIFrame* aFrame) {
      76         126 :     if (mEntryList.empty()) {
      77         126 :       return;
      78             :     }
      79             : 
      80           0 :     uint32_t depth = aFrame->GetDepthInFrameTree();
      81           0 :     if (mEntryList.find(Entry(aFrame, depth))) {
      82           0 :       delete mEntryList.remove(Entry(aFrame, depth));
      83             :     }
      84             :   }
      85             : 
      86             :   /**
      87             :    * Set the subtree root to limit overflow updates. This must be set if and
      88             :    * only if currently reflowing aSubtreeRoot, to ensure overflow changes will
      89             :    * still propagate correctly.
      90             :    */
      91           0 :   void SetSubtreeRoot(const nsIFrame* aSubtreeRoot) {
      92           0 :     mSubtreeRoot = aSubtreeRoot;
      93           0 :   }
      94             : 
      95             :   /**
      96             :    * Update the overflow of all added frames, and clear the entry list.
      97             :    *
      98             :    * Start from those deepest in the frame tree and works upwards. This stops
      99             :    * us from processing the same frame twice.
     100             :    */
     101          25 :   void Flush() {
     102          25 :     while (!mEntryList.empty()) {
     103           0 :       Entry *entry = mEntryList.removeMin();
     104           0 :       nsIFrame *frame = entry->mFrame;
     105             : 
     106           0 :       bool overflowChanged = false;
     107           0 :       if (entry->mChangeKind == CHILDREN_CHANGED) {
     108             :         // Need to union the overflow areas of the children.
     109             :         // Only update the parent if the overflow changes.
     110           0 :         overflowChanged = frame->UpdateOverflow();
     111             :       } else {
     112             :         // Take a faster path that doesn't require unioning the overflow areas
     113             :         // of our children.
     114             : 
     115           0 :         NS_ASSERTION(frame->GetProperty(
     116             :                        nsIFrame::DebugInitialOverflowPropertyApplied()),
     117             :                      "InitialOverflowProperty must be set first.");
     118             : 
     119             :         nsOverflowAreas* overflow =
     120           0 :           frame->GetProperty(nsIFrame::InitialOverflowProperty());
     121           0 :         if (overflow) {
     122             :           // FinishAndStoreOverflow will change the overflow areas passed in,
     123             :           // so make a copy.
     124           0 :           nsOverflowAreas overflowCopy = *overflow;
     125           0 :           frame->FinishAndStoreOverflow(overflowCopy, frame->GetSize());
     126             :         } else {
     127           0 :           nsRect bounds(nsPoint(0, 0), frame->GetSize());
     128           0 :           nsOverflowAreas boundsOverflow;
     129           0 :           boundsOverflow.SetAllTo(bounds);
     130           0 :           frame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
     131             :         }
     132             : 
     133             :         // We can't tell if the overflow changed, so be conservative
     134           0 :         overflowChanged = true;
     135             :       }
     136             : 
     137             :       // If the frame style changed (e.g. positioning offsets)
     138             :       // then we need to update the parent with the overflow areas of its
     139             :       // children.
     140           0 :       if (overflowChanged) {
     141           0 :         nsIFrame *parent = frame->GetParent();
     142           0 :         while (parent &&
     143           0 :                parent != mSubtreeRoot &&
     144           0 :                parent->Combines3DTransformWithAncestors()) {
     145             :           // Passing frames in between the frame and the establisher of
     146             :           // 3D rendering context.
     147           0 :           parent = parent->GetParent();
     148           0 :           MOZ_ASSERT(parent, "Root frame should never return true for Combines3DTransformWithAncestors");
     149             :         }
     150           0 :         if (parent && parent != mSubtreeRoot) {
     151           0 :           Entry* parentEntry = mEntryList.find(Entry(parent, entry->mDepth - 1));
     152           0 :           if (parentEntry) {
     153           0 :             parentEntry->mChangeKind = std::max(parentEntry->mChangeKind, CHILDREN_CHANGED);
     154             :           } else {
     155           0 :             mEntryList.insert(new Entry(parent, entry->mDepth - 1, CHILDREN_CHANGED));
     156             :           }
     157             :         }
     158             :       }
     159             :       delete entry;
     160             :     }
     161          25 :   }
     162             : 
     163             : private:
     164             :   struct Entry : SplayTreeNode<Entry>
     165             :   {
     166           0 :     Entry(nsIFrame* aFrame, uint32_t aDepth, ChangeKind aChangeKind = CHILDREN_CHANGED)
     167           0 :       : mFrame(aFrame)
     168             :       , mDepth(aDepth)
     169           0 :       , mChangeKind(aChangeKind)
     170           0 :     {}
     171             : 
     172           0 :     bool operator==(const Entry& aOther) const
     173             :     {
     174           0 :       return mFrame == aOther.mFrame;
     175             :     }
     176             : 
     177             :     /**
     178             :      * Sort by *reverse* depth in the tree, and break ties with
     179             :      * the frame pointer.
     180             :      */
     181           0 :     bool operator<(const Entry& aOther) const
     182             :     {
     183           0 :       if (mDepth == aOther.mDepth) {
     184           0 :         return mFrame < aOther.mFrame;
     185             :       }
     186           0 :       return mDepth > aOther.mDepth; /* reverse, want "min" to be deepest */
     187             :     }
     188             : 
     189           0 :     static int compare(const Entry& aOne, const Entry& aTwo)
     190             :     {
     191           0 :       if (aOne == aTwo) {
     192           0 :         return 0;
     193           0 :       } else if (aOne < aTwo) {
     194           0 :         return -1;
     195             :       } else {
     196           0 :         return 1;
     197             :       }
     198             :     }
     199             : 
     200             :     nsIFrame* mFrame;
     201             :     /* Depth in the frame tree */
     202             :     uint32_t mDepth;
     203             :     ChangeKind mChangeKind;
     204             :   };
     205             : 
     206             :   /* A list of frames to process, sorted by their depth in the frame tree */
     207             :   SplayTree<Entry, Entry> mEntryList;
     208             : 
     209             :   /* Don't update overflow of this frame or its ancestors. */
     210             :   const nsIFrame* mSubtreeRoot;
     211             : };
     212             : 
     213             : } // namespace mozilla
     214             : 
     215             : #endif

Generated by: LCOV version 1.13