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 /* storage of the frame tree and information about it */
9 #include "nsFrameManager.h"
14 #include "nsPlaceholderFrame.h"
15 #include "nsGkAtoms.h"
16 #include "nsILayoutHistoryState.h"
17 #include "mozilla/PresShell.h"
18 #include "mozilla/PresState.h"
19 #include "mozilla/ComputedStyle.h"
20 #include "mozilla/dom/Element.h"
21 #include "mozilla/dom/Document.h"
24 #include "nsAbsoluteContainingBlock.h"
25 #include "ChildIterator.h"
27 #include "GeckoProfiler.h"
28 #include "nsIStatefulFrame.h"
29 #include "nsContainerFrame.h"
30 #include "nsWindowSizes.h"
32 #include "mozilla/MemoryReporting.h"
34 // #define DEBUG_UNDISPLAYED_MAP
35 // #define DEBUG_DISPLAY_CONTENTS_MAP
37 using namespace mozilla
;
38 using namespace mozilla::dom
;
40 //----------------------------------------------------------------------
42 nsFrameManager::~nsFrameManager() {
43 NS_ASSERTION(!mPresShell
, "nsFrameManager::Destroy never called");
46 void nsFrameManager::Destroy() {
47 NS_ASSERTION(mPresShell
, "Frame manager already shut down.");
49 // Destroy the frame hierarchy.
50 mPresShell
->SetIgnoreFrameDestruction(true);
53 mRootFrame
->Destroy();
60 //----------------------------------------------------------------------
61 void nsFrameManager::AppendFrames(nsContainerFrame
* aParentFrame
,
62 FrameChildListID aListID
,
63 nsFrameList
&& aFrameList
) {
64 if (aParentFrame
->IsAbsoluteContainer() &&
65 aListID
== aParentFrame
->GetAbsoluteListID()) {
66 aParentFrame
->GetAbsoluteContainingBlock()->AppendFrames(
67 aParentFrame
, aListID
, std::move(aFrameList
));
69 aParentFrame
->AppendFrames(aListID
, std::move(aFrameList
));
73 void nsFrameManager::InsertFrames(nsContainerFrame
* aParentFrame
,
74 FrameChildListID aListID
,
76 nsFrameList
&& aFrameList
) {
79 (!aPrevFrame
->GetNextContinuation() ||
80 (aPrevFrame
->GetNextContinuation()->HasAnyStateBits(
81 NS_FRAME_IS_OVERFLOW_CONTAINER
) &&
82 !aPrevFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
))),
83 "aPrevFrame must be the last continuation in its chain!");
85 if (aParentFrame
->IsAbsoluteContainer() &&
86 aListID
== aParentFrame
->GetAbsoluteListID()) {
87 aParentFrame
->GetAbsoluteContainingBlock()->InsertFrames(
88 aParentFrame
, aListID
, aPrevFrame
, std::move(aFrameList
));
90 aParentFrame
->InsertFrames(aListID
, aPrevFrame
, nullptr,
91 std::move(aFrameList
));
95 void nsFrameManager::RemoveFrame(FrameChildListID aListID
,
96 nsIFrame
* aOldFrame
) {
97 // In case the reflow doesn't invalidate anything since it just leaves
98 // a gap where the old frame was, we invalidate it here. (This is
99 // reasonably likely to happen when removing a last child in a way
100 // that doesn't change the size of the parent.)
101 // This has to sure to invalidate the entire overflow rect; this
102 // is important in the presence of absolute positioning
103 aOldFrame
->InvalidateFrameForRemoval();
105 NS_ASSERTION(!aOldFrame
->GetPrevContinuation() ||
107 // nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames
108 aOldFrame
->IsTextFrame(),
109 "Must remove first continuation.");
110 NS_ASSERTION(!(aOldFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) &&
111 aOldFrame
->GetPlaceholderFrame()),
112 "Must call RemoveFrame on placeholder for out-of-flows.");
113 nsContainerFrame
* parentFrame
= aOldFrame
->GetParent();
114 if (parentFrame
->IsAbsoluteContainer() &&
115 aListID
== parentFrame
->GetAbsoluteListID()) {
116 parentFrame
->GetAbsoluteContainingBlock()->RemoveFrame(parentFrame
, aListID
,
119 parentFrame
->RemoveFrame(aListID
, aOldFrame
);
123 //----------------------------------------------------------------------
125 // Capture state for a given frame.
126 // Accept a content id here, in some cases we may not have content (scroll
128 void nsFrameManager::CaptureFrameStateFor(nsIFrame
* aFrame
,
129 nsILayoutHistoryState
* aState
) {
130 if (!aFrame
|| !aState
) {
131 NS_WARNING("null frame, or state");
135 // Only capture state for stateful frames
136 nsIStatefulFrame
* statefulFrame
= do_QueryFrame(aFrame
);
137 if (!statefulFrame
) {
141 // Capture the state, exit early if we get null (nothing to save)
142 UniquePtr
<PresState
> frameState
= statefulFrame
->SaveState();
147 // Generate the hash key to store the state under
148 // Exit early if we get empty key
149 nsAutoCString stateKey
;
150 nsIContent
* content
= aFrame
->GetContent();
151 Document
* doc
= content
? content
->GetUncomposedDoc() : nullptr;
152 statefulFrame
->GenerateStateKey(content
, doc
, stateKey
);
153 if (stateKey
.IsEmpty()) {
157 // Store the state. aState owns frameState now.
158 aState
->AddState(stateKey
, std::move(frameState
));
161 void nsFrameManager::CaptureFrameState(nsIFrame
* aFrame
,
162 nsILayoutHistoryState
* aState
) {
163 MOZ_ASSERT(nullptr != aFrame
&& nullptr != aState
,
164 "null parameters passed in");
166 CaptureFrameStateFor(aFrame
, aState
);
168 // Now capture state recursively for the frame hierarchy rooted at aFrame
169 for (const auto& childList
: aFrame
->ChildLists()) {
170 for (nsIFrame
* child
: childList
.mList
) {
171 if (child
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
)) {
172 // We'll pick it up when we get to its placeholder
175 // Make sure to walk through placeholders as needed, so that we
176 // save state for out-of-flows which may not be our descendants
177 // themselves but whose placeholders are our descendants.
178 CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child
), aState
);
183 // Restore state for a given frame.
184 // Accept a content id here, in some cases we may not have content (scroll
186 void nsFrameManager::RestoreFrameStateFor(nsIFrame
* aFrame
,
187 nsILayoutHistoryState
* aState
) {
188 if (!aFrame
|| !aState
) {
189 NS_WARNING("null frame or state");
193 // Only restore state for stateful frames
194 nsIStatefulFrame
* statefulFrame
= do_QueryFrame(aFrame
);
195 if (!statefulFrame
) {
199 // Generate the hash key the state was stored under
200 // Exit early if we get empty key
201 nsIContent
* content
= aFrame
->GetContent();
202 // If we don't have content, we can't generate a hash
203 // key and there's probably no state information for us.
208 nsAutoCString stateKey
;
209 Document
* doc
= content
->GetUncomposedDoc();
210 statefulFrame
->GenerateStateKey(content
, doc
, stateKey
);
211 if (stateKey
.IsEmpty()) {
215 // Get the state from the hash
216 PresState
* frameState
= aState
->GetState(stateKey
);
222 nsresult rv
= statefulFrame
->RestoreState(frameState
);
227 // If we restore ok, remove the state from the state table
228 aState
->RemoveState(stateKey
);
231 void nsFrameManager::RestoreFrameState(nsIFrame
* aFrame
,
232 nsILayoutHistoryState
* aState
) {
233 MOZ_ASSERT(nullptr != aFrame
&& nullptr != aState
,
234 "null parameters passed in");
236 RestoreFrameStateFor(aFrame
, aState
);
238 // Now restore state recursively for the frame hierarchy rooted at aFrame
239 for (const auto& childList
: aFrame
->ChildLists()) {
240 for (nsIFrame
* child
: childList
.mList
) {
241 RestoreFrameState(child
, aState
);
246 void nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes
& aSizes
) const {
247 aSizes
.mLayoutPresShellSize
+= aSizes
.mState
.mMallocSizeOf(this);