tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

OverflowChangedTracker.h (7432B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_OverflowChangedTracker_h
      8 #define mozilla_OverflowChangedTracker_h
      9 
     10 #include "mozilla/HashTable.h"
     11 #include "nsContainerFrame.h"
     12 #include "nsIFrame.h"
     13 #include "nsTArray.h"
     14 
     15 namespace mozilla {
     16 
     17 /**
     18 * Helper class that collects a list of frames that need
     19 * UpdateOverflow() called on them, and coalesces them
     20 * to avoid walking up the same ancestor tree multiple times.
     21 */
     22 class OverflowChangedTracker {
     23 public:
     24  enum ChangeKind {
     25    /**
     26     * The frame was explicitly added as a result of
     27     * nsChangeHint_UpdatePostTransformOverflow and hence may have had a style
     28     * change that changes its geometry relative to parent, without reflowing.
     29     */
     30    TRANSFORM_CHANGED,
     31    /**
     32     * The overflow areas of children have changed
     33     * and we need to call UpdateOverflow on the frame.
     34     */
     35    CHILDREN_CHANGED,
     36  };
     37 
     38  OverflowChangedTracker() : mSubtreeRoot(nullptr) {}
     39 
     40  ~OverflowChangedTracker() {
     41    NS_ASSERTION(mEntries.empty(), "Need to flush before destroying!");
     42  }
     43 
     44  /**
     45   * Add a frame that has had a style change, and needs its
     46   * overflow updated.
     47   *
     48   * If there are pre-transform overflow areas stored for this
     49   * frame, then we will call FinishAndStoreOverflow with those
     50   * areas instead of UpdateOverflow().
     51   *
     52   * If the overflow area changes, then UpdateOverflow will also
     53   * be called on the parent.
     54   */
     55  void AddFrame(nsIFrame* aFrame, ChangeKind aChangeKind) {
     56    MOZ_ASSERT(
     57        aFrame->FrameMaintainsOverflow(),
     58        "Why add a frame that doesn't maintain overflow to the tracker?");
     59    if (auto p = mEntries.lookupForAdd(aFrame)) {
     60      p->value() = std::max(p->value(), aChangeKind);
     61    } else {
     62      // Ignore failure to add an entry; this could result in cosmetic
     63      // errors but is non-fatal.
     64      (void)mEntries.add(p, aFrame, aChangeKind);
     65    }
     66  }
     67 
     68  /**
     69   * Remove a frame.
     70   */
     71  void RemoveFrame(nsIFrame* aFrame) {
     72    if (!mEntries.empty()) {
     73      mEntries.remove(aFrame);
     74    }
     75  }
     76 
     77  /**
     78   * Set the subtree root to limit overflow updates. This must be set if and
     79   * only if currently reflowing aSubtreeRoot, to ensure overflow changes will
     80   * still propagate correctly.
     81   */
     82  void SetSubtreeRoot(const nsIFrame* aSubtreeRoot) {
     83    mSubtreeRoot = aSubtreeRoot;
     84  }
     85 
     86  /**
     87   * Update the overflow of all added frames, and clear the entry list.
     88   *
     89   * Start from those deepest in the frame tree and works upwards. This stops
     90   * us from processing the same frame twice.
     91   */
     92  void Flush() {
     93    // Collect all the entries into an array, sort by depth, and process them
     94    // from the deepest upwards.
     95 
     96    AutoTArray<Entry, 8> sortedEntries;
     97    // We use fallible allocations to avoid crashing on OOM; in the event that
     98    // of allocation failure, we'll effectively ignore the entries that weren't
     99    // added to the array, which could result in painting glitches but should
    100    // be otherwise harmless.
    101    (void)sortedEntries.SetCapacity(mEntries.count(), fallible);
    102    for (auto iter = mEntries.iter(); !iter.done(); iter.next()) {
    103      nsIFrame* frame = iter.get().key();
    104      uint32_t depth = frame->GetDepthInFrameTree();
    105      ChangeKind kind = iter.get().value();
    106      if (!sortedEntries.AppendElement(Entry(frame, depth, kind), fallible)) {
    107        break;
    108      }
    109    }
    110    mEntries.clearAndCompact();
    111    sortedEntries.Sort();
    112 
    113    while (!sortedEntries.IsEmpty()) {
    114      Entry entry = sortedEntries.PopLastElement();
    115      nsIFrame* frame = entry.mFrame;
    116 
    117      bool overflowChanged = false;
    118      if (entry.mChangeKind == CHILDREN_CHANGED) {
    119        // Need to union the overflow areas of the children.
    120        // Only update the parent if the overflow changes.
    121        overflowChanged = frame->UpdateOverflow();
    122      } else {
    123        // Take a faster path that doesn't require unioning the overflow areas
    124        // of our children.
    125 
    126        NS_ASSERTION(
    127            frame->GetProperty(nsIFrame::DebugInitialOverflowPropertyApplied()),
    128            "InitialOverflowProperty must be set first.");
    129 
    130        OverflowAreas* overflow =
    131            frame->GetProperty(nsIFrame::InitialOverflowProperty());
    132        if (overflow) {
    133          // FinishAndStoreOverflow will change the overflow areas passed in,
    134          // so make a copy.
    135          OverflowAreas overflowCopy = *overflow;
    136          frame->FinishAndStoreOverflow(overflowCopy, frame->GetSize());
    137        } else {
    138          nsRect bounds(nsPoint(0, 0), frame->GetSize());
    139          OverflowAreas boundsOverflow;
    140          boundsOverflow.SetAllTo(bounds);
    141          frame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
    142        }
    143 
    144        // We can't tell if the overflow changed, so be conservative
    145        overflowChanged = true;
    146      }
    147 
    148      // If the frame style changed (e.g. positioning offsets)
    149      // then we need to update the parent with the overflow areas of its
    150      // children.
    151      if (overflowChanged) {
    152        nsIFrame* parent = frame->GetParent();
    153 
    154        // It's possible that the parent is already in a nondisplay context,
    155        // should not add it to the list if that's true.
    156        if (parent && parent != mSubtreeRoot &&
    157            parent->FrameMaintainsOverflow()) {
    158          Entry parentEntry(parent, entry.mDepth - 1, CHILDREN_CHANGED);
    159          auto index = sortedEntries.IndexOfFirstElementGt(parentEntry);
    160          if (index > 0 && sortedEntries[index - 1] == parentEntry) {
    161            // Update the existing entry if the new value is stronger.
    162            Entry& existing = sortedEntries[index - 1];
    163            existing.mChangeKind =
    164                std::max(existing.mChangeKind, CHILDREN_CHANGED);
    165          } else {
    166            // Add new entry. We ignore failure here; we'll just potentially
    167            // miss some updates that ought to happen.
    168            // (Allocation failure seems unlikely here anyhow, as we just
    169            // popped an element off the array at the top of the loop.)
    170            (void)sortedEntries.InsertElementAt(index, parentEntry, fallible);
    171          }
    172        }
    173      }
    174    }
    175  }
    176 
    177 private:
    178  /* Entry type used for the sorted array in Flush(). */
    179  struct Entry {
    180    Entry(nsIFrame* aFrame, uint32_t aDepth,
    181          ChangeKind aChangeKind = CHILDREN_CHANGED)
    182        : mFrame(aFrame), mDepth(aDepth), mChangeKind(aChangeKind) {}
    183 
    184    /**
    185     * Note that "equality" tests only the frame pointer.
    186     */
    187    bool operator==(const Entry& aOther) const {
    188      return mFrame == aOther.mFrame;
    189    }
    190 
    191    /**
    192     * Sort by depth in the tree, and break ties with the frame pointer.
    193     */
    194    bool operator<(const Entry& aOther) const {
    195      if (mDepth == aOther.mDepth) {
    196        return mFrame < aOther.mFrame;
    197      }
    198      return mDepth < aOther.mDepth;
    199    }
    200 
    201    nsIFrame* mFrame;
    202    /* Depth in the frame tree */
    203    uint32_t mDepth;
    204    ChangeKind mChangeKind;
    205  };
    206 
    207  /* A collection of frames to be processed. */
    208  HashMap<nsIFrame*, ChangeKind> mEntries;
    209 
    210  /* Don't update overflow of this frame or its ancestors. */
    211  const nsIFrame* mSubtreeRoot;
    212 };
    213 
    214 }  // namespace mozilla
    215 
    216 #endif