/src/mozilla-central/layout/base/nsFrameManager.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /* storage of the frame tree and information about it */ |
8 | | |
9 | | #include "nsFrameManager.h" |
10 | | |
11 | | #include "nscore.h" |
12 | | #include "nsIPresShell.h" |
13 | | #include "nsCOMPtr.h" |
14 | | #include "plhash.h" |
15 | | #include "nsPlaceholderFrame.h" |
16 | | #include "nsGkAtoms.h" |
17 | | #include "nsILayoutHistoryState.h" |
18 | | #include "mozilla/PresState.h" |
19 | | #include "mozilla/ComputedStyle.h" |
20 | | #include "mozilla/dom/Element.h" |
21 | | #include "nsIDocument.h" |
22 | | |
23 | | #include "nsError.h" |
24 | | #include "nsAutoPtr.h" |
25 | | #include "nsAbsoluteContainingBlock.h" |
26 | | #include "ChildIterator.h" |
27 | | |
28 | | #include "GeckoProfiler.h" |
29 | | #include "nsIStatefulFrame.h" |
30 | | #include "nsContainerFrame.h" |
31 | | #include "nsWindowSizes.h" |
32 | | |
33 | | #include "mozilla/MemoryReporting.h" |
34 | | |
35 | | // #define DEBUG_UNDISPLAYED_MAP |
36 | | // #define DEBUG_DISPLAY_CONTENTS_MAP |
37 | | |
38 | | using namespace mozilla; |
39 | | using namespace mozilla::dom; |
40 | | |
41 | | //---------------------------------------------------------------------- |
42 | | |
43 | | nsFrameManager::~nsFrameManager() |
44 | 0 | { |
45 | 0 | NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called"); |
46 | 0 | } |
47 | | |
48 | | void |
49 | | nsFrameManager::Destroy() |
50 | 0 | { |
51 | 0 | NS_ASSERTION(mPresShell, "Frame manager already shut down."); |
52 | 0 |
|
53 | 0 | // Destroy the frame hierarchy. |
54 | 0 | mPresShell->SetIgnoreFrameDestruction(true); |
55 | 0 |
|
56 | 0 | if (mRootFrame) { |
57 | 0 | mRootFrame->Destroy(); |
58 | 0 | mRootFrame = nullptr; |
59 | 0 | } |
60 | 0 |
|
61 | 0 | mPresShell = nullptr; |
62 | 0 | } |
63 | | |
64 | | //---------------------------------------------------------------------- |
65 | | void |
66 | | nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame, |
67 | | ChildListID aListID, |
68 | | nsFrameList& aFrameList) |
69 | 0 | { |
70 | 0 | if (aParentFrame->IsAbsoluteContainer() && |
71 | 0 | aListID == aParentFrame->GetAbsoluteListID()) { |
72 | 0 | aParentFrame->GetAbsoluteContainingBlock()-> |
73 | 0 | AppendFrames(aParentFrame, aListID, aFrameList); |
74 | 0 | } else { |
75 | 0 | aParentFrame->AppendFrames(aListID, aFrameList); |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | | void |
80 | | nsFrameManager::InsertFrames(nsContainerFrame* aParentFrame, |
81 | | ChildListID aListID, |
82 | | nsIFrame* aPrevFrame, |
83 | | nsFrameList& aFrameList) |
84 | 0 | { |
85 | 0 | MOZ_ASSERT(!aPrevFrame || (!aPrevFrame->GetNextContinuation() |
86 | 0 | || (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) |
87 | 0 | && !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))), |
88 | 0 | "aPrevFrame must be the last continuation in its chain!"); |
89 | 0 |
|
90 | 0 | if (aParentFrame->IsAbsoluteContainer() && |
91 | 0 | aListID == aParentFrame->GetAbsoluteListID()) { |
92 | 0 | aParentFrame->GetAbsoluteContainingBlock()-> |
93 | 0 | InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList); |
94 | 0 | } else { |
95 | 0 | aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList); |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | void |
100 | | nsFrameManager::RemoveFrame(ChildListID aListID, |
101 | | nsIFrame* aOldFrame) |
102 | 0 | { |
103 | 0 | // In case the reflow doesn't invalidate anything since it just leaves |
104 | 0 | // a gap where the old frame was, we invalidate it here. (This is |
105 | 0 | // reasonably likely to happen when removing a last child in a way |
106 | 0 | // that doesn't change the size of the parent.) |
107 | 0 | // This has to sure to invalidate the entire overflow rect; this |
108 | 0 | // is important in the presence of absolute positioning |
109 | 0 | aOldFrame->InvalidateFrameForRemoval(); |
110 | 0 |
|
111 | 0 | NS_ASSERTION(!aOldFrame->GetPrevContinuation() || |
112 | 0 | // exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames |
113 | 0 | aOldFrame->IsTextFrame(), |
114 | 0 | "Must remove first continuation."); |
115 | 0 | NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW && |
116 | 0 | aOldFrame->GetPlaceholderFrame()), |
117 | 0 | "Must call RemoveFrame on placeholder for out-of-flows."); |
118 | 0 | nsContainerFrame* parentFrame = aOldFrame->GetParent(); |
119 | 0 | if (parentFrame->IsAbsoluteContainer() && |
120 | 0 | aListID == parentFrame->GetAbsoluteListID()) { |
121 | 0 | parentFrame->GetAbsoluteContainingBlock()-> |
122 | 0 | RemoveFrame(parentFrame, aListID, aOldFrame); |
123 | 0 | } else { |
124 | 0 | parentFrame->RemoveFrame(aListID, aOldFrame); |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | | //---------------------------------------------------------------------- |
129 | | |
130 | | // Capture state for a given frame. |
131 | | // Accept a content id here, in some cases we may not have content (scroll position) |
132 | | void |
133 | | nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame, |
134 | | nsILayoutHistoryState* aState) |
135 | 0 | { |
136 | 0 | if (!aFrame || !aState) { |
137 | 0 | NS_WARNING("null frame, or state"); |
138 | 0 | return; |
139 | 0 | } |
140 | 0 |
|
141 | 0 | // Only capture state for stateful frames |
142 | 0 | nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); |
143 | 0 | if (!statefulFrame) { |
144 | 0 | return; |
145 | 0 | } |
146 | 0 | |
147 | 0 | // Capture the state, exit early if we get null (nothing to save) |
148 | 0 | UniquePtr<PresState> frameState = statefulFrame->SaveState(); |
149 | 0 | if (!frameState) { |
150 | 0 | return; |
151 | 0 | } |
152 | 0 | |
153 | 0 | // Generate the hash key to store the state under |
154 | 0 | // Exit early if we get empty key |
155 | 0 | nsAutoCString stateKey; |
156 | 0 | nsIContent* content = aFrame->GetContent(); |
157 | 0 | nsIDocument* doc = content ? content->GetUncomposedDoc() : nullptr; |
158 | 0 | nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey); |
159 | 0 | if(NS_FAILED(rv) || stateKey.IsEmpty()) { |
160 | 0 | return; |
161 | 0 | } |
162 | 0 | |
163 | 0 | // Store the state. aState owns frameState now. |
164 | 0 | aState->AddState(stateKey, std::move(frameState)); |
165 | 0 | } |
166 | | |
167 | | void |
168 | | nsFrameManager::CaptureFrameState(nsIFrame* aFrame, |
169 | | nsILayoutHistoryState* aState) |
170 | 0 | { |
171 | 0 | MOZ_ASSERT(nullptr != aFrame && nullptr != aState, "null parameters passed in"); |
172 | 0 |
|
173 | 0 | CaptureFrameStateFor(aFrame, aState); |
174 | 0 |
|
175 | 0 | // Now capture state recursively for the frame hierarchy rooted at aFrame |
176 | 0 | nsIFrame::ChildListIterator lists(aFrame); |
177 | 0 | for (; !lists.IsDone(); lists.Next()) { |
178 | 0 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
179 | 0 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
180 | 0 | nsIFrame* child = childFrames.get(); |
181 | 0 | if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
182 | 0 | // We'll pick it up when we get to its placeholder |
183 | 0 | continue; |
184 | 0 | } |
185 | 0 | // Make sure to walk through placeholders as needed, so that we |
186 | 0 | // save state for out-of-flows which may not be our descendants |
187 | 0 | // themselves but whose placeholders are our descendants. |
188 | 0 | CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState); |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | // Restore state for a given frame. |
194 | | // Accept a content id here, in some cases we may not have content (scroll position) |
195 | | void |
196 | | nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame, |
197 | | nsILayoutHistoryState* aState) |
198 | 0 | { |
199 | 0 | if (!aFrame || !aState) { |
200 | 0 | NS_WARNING("null frame or state"); |
201 | 0 | return; |
202 | 0 | } |
203 | 0 |
|
204 | 0 | // Only restore state for stateful frames |
205 | 0 | nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); |
206 | 0 | if (!statefulFrame) { |
207 | 0 | return; |
208 | 0 | } |
209 | 0 | |
210 | 0 | // Generate the hash key the state was stored under |
211 | 0 | // Exit early if we get empty key |
212 | 0 | nsIContent* content = aFrame->GetContent(); |
213 | 0 | // If we don't have content, we can't generate a hash |
214 | 0 | // key and there's probably no state information for us. |
215 | 0 | if (!content) { |
216 | 0 | return; |
217 | 0 | } |
218 | 0 | |
219 | 0 | nsAutoCString stateKey; |
220 | 0 | nsIDocument* doc = content->GetUncomposedDoc(); |
221 | 0 | nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey); |
222 | 0 | if (NS_FAILED(rv) || stateKey.IsEmpty()) { |
223 | 0 | return; |
224 | 0 | } |
225 | 0 | |
226 | 0 | // Get the state from the hash |
227 | 0 | PresState* frameState = aState->GetState(stateKey); |
228 | 0 | if (!frameState) { |
229 | 0 | return; |
230 | 0 | } |
231 | 0 | |
232 | 0 | // Restore it |
233 | 0 | rv = statefulFrame->RestoreState(frameState); |
234 | 0 | if (NS_FAILED(rv)) { |
235 | 0 | return; |
236 | 0 | } |
237 | 0 | |
238 | 0 | // If we restore ok, remove the state from the state table |
239 | 0 | aState->RemoveState(stateKey); |
240 | 0 | } |
241 | | |
242 | | void |
243 | | nsFrameManager::RestoreFrameState(nsIFrame* aFrame, |
244 | | nsILayoutHistoryState* aState) |
245 | 0 | { |
246 | 0 | MOZ_ASSERT(nullptr != aFrame && nullptr != aState, "null parameters passed in"); |
247 | 0 |
|
248 | 0 | RestoreFrameStateFor(aFrame, aState); |
249 | 0 |
|
250 | 0 | // Now restore state recursively for the frame hierarchy rooted at aFrame |
251 | 0 | nsIFrame::ChildListIterator lists(aFrame); |
252 | 0 | for (; !lists.IsDone(); lists.Next()) { |
253 | 0 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
254 | 0 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
255 | 0 | RestoreFrameState(childFrames.get(), aState); |
256 | 0 | } |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | void |
261 | | nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const |
262 | 0 | { |
263 | 0 | aSizes.mLayoutPresShellSize += aSizes.mState.mMallocSizeOf(this); |
264 | 0 | } |