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
11 #include "nsContainerFrame.h"
12 #include "mozilla/SplayTree.h"
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
{
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.
31 * The overflow areas of children have changed
32 * and we need to call UpdateOverflow on the frame.
37 OverflowChangedTracker() : mSubtreeRoot(nullptr) {}
39 ~OverflowChangedTracker() {
40 NS_ASSERTION(mEntryList
.empty(), "Need to flush before destroying!");
44 * Add a frame that has had a style change, and needs its
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
) {
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) {
65 mEntryList
.insert(new Entry(aFrame
, depth
, aChangeKind
));
67 // Update the existing entry if the new value is stronger.
68 entry
->mChangeKind
= std::max(entry
->mChangeKind
, aChangeKind
);
75 void RemoveFrame(nsIFrame
* aFrame
) {
76 if (mEntryList
.empty()) {
80 uint32_t depth
= aFrame
->GetDepthInFrameTree();
81 if (mEntryList
.find(Entry(aFrame
, depth
))) {
82 delete mEntryList
.remove(Entry(aFrame
, depth
));
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
;
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.
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();
112 // Take a faster path that doesn't require unioning the overflow areas
116 frame
->GetProperty(nsIFrame::DebugInitialOverflowPropertyApplied()),
117 "InitialOverflowProperty must be set first.");
119 OverflowAreas
* overflow
=
120 frame
->GetProperty(nsIFrame::InitialOverflowProperty());
122 // FinishAndStoreOverflow will change the overflow areas passed in,
124 OverflowAreas overflowCopy
= *overflow
;
125 frame
->FinishAndStoreOverflow(overflowCopy
, frame
->GetSize());
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
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()) {
148 mEntryList
.find(Entry(parent
, entry
->mDepth
- 1));
150 parentEntry
->mChangeKind
=
151 std::max(parentEntry
->mChangeKind
, CHILDREN_CHANGED
);
154 new Entry(parent
, entry
->mDepth
- 1, CHILDREN_CHANGED
));
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
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
) {
186 } else if (aOne
< aTwo
) {
194 /* Depth in the frame tree */
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