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
|