Bug 1856663 - Add more chunks for Android mochitest-plain. r=jmaher,taskgraph-reviewe...
[gecko.git] / layout / base / OverflowChangedTracker.h
blobf79962e82f1d38ab42c843d1fce1914a8f0dc39c
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/. */
7 #ifndef mozilla_OverflowChangedTracker_h
8 #define mozilla_OverflowChangedTracker_h
10 #include "nsIFrame.h"
11 #include "nsContainerFrame.h"
12 #include "mozilla/SplayTree.h"
14 namespace mozilla {
16 /**
17 * Helper class that collects a list of frames that need
18 * UpdateOverflow() called on them, and coalesces them
19 * to avoid walking up the same ancestor tree multiple times.
21 class OverflowChangedTracker {
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.
29 TRANSFORM_CHANGED,
30 /**
31 * The overflow areas of children have changed
32 * and we need to call UpdateOverflow on the frame.
34 CHILDREN_CHANGED,
37 OverflowChangedTracker() : mSubtreeRoot(nullptr) {}
39 ~OverflowChangedTracker() {
40 NS_ASSERTION(mEntryList.empty(), "Need to flush before destroying!");
43 /**
44 * Add a frame that has had a style change, and needs its
45 * overflow updated.
47 * If there are pre-transform overflow areas stored for this
48 * frame, then we will call FinishAndStoreOverflow with those
49 * areas instead of UpdateOverflow().
51 * If the overflow area changes, then UpdateOverflow will also
52 * be called on the parent.
54 void AddFrame(nsIFrame* aFrame, ChangeKind aChangeKind) {
55 MOZ_ASSERT(
56 aFrame->FrameMaintainsOverflow(),
57 "Why add a frame that doesn't maintain overflow to the tracker?");
58 uint32_t depth = aFrame->GetDepthInFrameTree();
59 Entry* entry = nullptr;
60 if (!mEntryList.empty()) {
61 entry = mEntryList.find(Entry(aFrame, depth));
63 if (entry == nullptr) {
64 // Add new entry.
65 mEntryList.insert(new Entry(aFrame, depth, aChangeKind));
66 } else {
67 // Update the existing entry if the new value is stronger.
68 entry->mChangeKind = std::max(entry->mChangeKind, aChangeKind);
72 /**
73 * Remove a frame.
75 void RemoveFrame(nsIFrame* aFrame) {
76 if (mEntryList.empty()) {
77 return;
80 uint32_t depth = aFrame->GetDepthInFrameTree();
81 if (mEntryList.find(Entry(aFrame, depth))) {
82 delete mEntryList.remove(Entry(aFrame, depth));
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.
91 void SetSubtreeRoot(const nsIFrame* aSubtreeRoot) {
92 mSubtreeRoot = aSubtreeRoot;
95 /**
96 * Update the overflow of all added frames, and clear the entry list.
98 * Start from those deepest in the frame tree and works upwards. This stops
99 * us from processing the same frame twice.
101 void Flush() {
102 while (!mEntryList.empty()) {
103 Entry* entry = mEntryList.removeMin();
104 nsIFrame* frame = entry->mFrame;
106 bool overflowChanged = false;
107 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 overflowChanged = frame->UpdateOverflow();
111 } else {
112 // Take a faster path that doesn't require unioning the overflow areas
113 // of our children.
115 NS_ASSERTION(
116 frame->GetProperty(nsIFrame::DebugInitialOverflowPropertyApplied()),
117 "InitialOverflowProperty must be set first.");
119 OverflowAreas* overflow =
120 frame->GetProperty(nsIFrame::InitialOverflowProperty());
121 if (overflow) {
122 // FinishAndStoreOverflow will change the overflow areas passed in,
123 // so make a copy.
124 OverflowAreas overflowCopy = *overflow;
125 frame->FinishAndStoreOverflow(overflowCopy, frame->GetSize());
126 } else {
127 nsRect bounds(nsPoint(0, 0), frame->GetSize());
128 OverflowAreas boundsOverflow;
129 boundsOverflow.SetAllTo(bounds);
130 frame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
133 // We can't tell if the overflow changed, so be conservative
134 overflowChanged = true;
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 if (overflowChanged) {
141 nsIFrame* parent = frame->GetParent();
143 // It's possible that the parent is already in a nondisplay context,
144 // should not add it to the list if that's true.
145 if (parent && parent != mSubtreeRoot &&
146 parent->FrameMaintainsOverflow()) {
147 Entry* parentEntry =
148 mEntryList.find(Entry(parent, entry->mDepth - 1));
149 if (parentEntry) {
150 parentEntry->mChangeKind =
151 std::max(parentEntry->mChangeKind, CHILDREN_CHANGED);
152 } else {
153 mEntryList.insert(
154 new Entry(parent, entry->mDepth - 1, CHILDREN_CHANGED));
158 delete entry;
162 private:
163 struct Entry : SplayTreeNode<Entry> {
164 Entry(nsIFrame* aFrame, uint32_t aDepth,
165 ChangeKind aChangeKind = CHILDREN_CHANGED)
166 : mFrame(aFrame), mDepth(aDepth), mChangeKind(aChangeKind) {}
168 bool operator==(const Entry& aOther) const {
169 return mFrame == aOther.mFrame;
173 * Sort by *reverse* depth in the tree, and break ties with
174 * the frame pointer.
176 bool operator<(const Entry& aOther) const {
177 if (mDepth == aOther.mDepth) {
178 return mFrame < aOther.mFrame;
180 return mDepth > aOther.mDepth; /* reverse, want "min" to be deepest */
183 static int compare(const Entry& aOne, const Entry& aTwo) {
184 if (aOne == aTwo) {
185 return 0;
186 } else if (aOne < aTwo) {
187 return -1;
188 } else {
189 return 1;
193 nsIFrame* mFrame;
194 /* Depth in the frame tree */
195 uint32_t mDepth;
196 ChangeKind mChangeKind;
199 /* A list of frames to process, sorted by their depth in the frame tree */
200 SplayTree<Entry, Entry> mEntryList;
202 /* Don't update overflow of this frame or its ancestors. */
203 const nsIFrame* mSubtreeRoot;
206 } // namespace mozilla
208 #endif