summaryrefslogtreecommitdiffstats
path: root/layout/base/nsFrameManager.cpp
blob: 87328ccb80ca88208cb8fa1bb477757890621f8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* storage of the frame tree and information about it */

#include "nsFrameManager.h"

#include "nscore.h"
#include "nsCOMPtr.h"
#include "plhash.h"
#include "nsPlaceholderFrame.h"
#include "nsGkAtoms.h"
#include "nsILayoutHistoryState.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresState.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Document.h"

#include "nsError.h"
#include "nsAbsoluteContainingBlock.h"
#include "ChildIterator.h"

#include "GeckoProfiler.h"
#include "nsIStatefulFrame.h"
#include "nsContainerFrame.h"
#include "nsWindowSizes.h"

#include "mozilla/MemoryReporting.h"

// #define DEBUG_UNDISPLAYED_MAP
// #define DEBUG_DISPLAY_CONTENTS_MAP

using namespace mozilla;
using namespace mozilla::dom;

//----------------------------------------------------------------------

nsFrameManager::~nsFrameManager() {
  NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
}

void nsFrameManager::Destroy() {
  NS_ASSERTION(mPresShell, "Frame manager already shut down.");

  // Destroy the frame hierarchy.
  mPresShell->SetIgnoreFrameDestruction(true);

  if (mRootFrame) {
    FrameDestroyContext context(mPresShell);
    mRootFrame->Destroy(context);
    mRootFrame = nullptr;
  }

  mPresShell = nullptr;
}

//----------------------------------------------------------------------
void nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame,
                                  FrameChildListID aListID,
                                  nsFrameList&& aFrameList) {
  if (aParentFrame->IsAbsoluteContainer() &&
      aListID == aParentFrame->GetAbsoluteListID()) {
    aParentFrame->GetAbsoluteContainingBlock()->AppendFrames(
        aParentFrame, aListID, std::move(aFrameList));
  } else {
    aParentFrame->AppendFrames(aListID, std::move(aFrameList));
  }
}

void nsFrameManager::InsertFrames(nsContainerFrame* aParentFrame,
                                  FrameChildListID aListID,
                                  nsIFrame* aPrevFrame,
                                  nsFrameList&& aFrameList) {
  MOZ_ASSERT(
      !aPrevFrame ||
          (!aPrevFrame->GetNextContinuation() ||
           (aPrevFrame->GetNextContinuation()->HasAnyStateBits(
                NS_FRAME_IS_OVERFLOW_CONTAINER) &&
            !aPrevFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER))),
      "aPrevFrame must be the last continuation in its chain!");

  if (aParentFrame->IsAbsoluteContainer() &&
      aListID == aParentFrame->GetAbsoluteListID()) {
    aParentFrame->GetAbsoluteContainingBlock()->InsertFrames(
        aParentFrame, aListID, aPrevFrame, std::move(aFrameList));
  } else {
    aParentFrame->InsertFrames(aListID, aPrevFrame, nullptr,
                               std::move(aFrameList));
  }
}

void nsFrameManager::RemoveFrame(DestroyContext& aContext,
                                 FrameChildListID aListID,
                                 nsIFrame* aOldFrame) {
  // In case the reflow doesn't invalidate anything since it just leaves
  // a gap where the old frame was, we invalidate it here.  (This is
  // reasonably likely to happen when removing a last child in a way
  // that doesn't change the size of the parent.)
  // This has to sure to invalidate the entire overflow rect; this
  // is important in the presence of absolute positioning
  aOldFrame->InvalidateFrameForRemoval();

  NS_ASSERTION(!aOldFrame->GetPrevContinuation() ||
                   // exception for
                   // nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames
                   aOldFrame->IsTextFrame(),
               "Must remove first continuation.");
  NS_ASSERTION(!(aOldFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
                 aOldFrame->GetPlaceholderFrame()),
               "Must call RemoveFrame on placeholder for out-of-flows.");
  nsContainerFrame* parentFrame = aOldFrame->GetParent();
  if (parentFrame->IsAbsoluteContainer() &&
      aListID == parentFrame->GetAbsoluteListID()) {
    parentFrame->GetAbsoluteContainingBlock()->RemoveFrame(aContext, aListID,
                                                           aOldFrame);
  } else {
    parentFrame->RemoveFrame(aContext, aListID, aOldFrame);
  }
}

//----------------------------------------------------------------------

// Capture state for a given frame.
// Accept a content id here, in some cases we may not have content (scroll
// position)
void nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
                                          nsILayoutHistoryState* aState) {
  if (!aFrame || !aState) {
    NS_WARNING("null frame, or state");
    return;
  }

  // Only capture state for stateful frames
  nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
  if (!statefulFrame) {
    return;
  }

  // Capture the state, exit early if we get null (nothing to save)
  UniquePtr<PresState> frameState = statefulFrame->SaveState();
  if (!frameState) {
    return;
  }

  // Generate the hash key to store the state under
  // Exit early if we get empty key
  nsAutoCString stateKey;
  nsIContent* content = aFrame->GetContent();
  Document* doc = content ? content->GetUncomposedDoc() : nullptr;
  statefulFrame->GenerateStateKey(content, doc, stateKey);
  if (stateKey.IsEmpty()) {
    return;
  }

  // Store the state. aState owns frameState now.
  aState->AddState(stateKey, std::move(frameState));
}

void nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
                                       nsILayoutHistoryState* aState) {
  MOZ_ASSERT(nullptr != aFrame && nullptr != aState,
             "null parameters passed in");

  CaptureFrameStateFor(aFrame, aState);

  // Now capture state recursively for the frame hierarchy rooted at aFrame
  for (const auto& childList : aFrame->ChildLists()) {
    for (nsIFrame* child : childList.mList) {
      if (child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
        // We'll pick it up when we get to its placeholder
        continue;
      }
      // Make sure to walk through placeholders as needed, so that we
      // save state for out-of-flows which may not be our descendants
      // themselves but whose placeholders are our descendants.
      CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState);
    }
  }
}

// Restore state for a given frame.
// Accept a content id here, in some cases we may not have content (scroll
// position)
void nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
                                          nsILayoutHistoryState* aState) {
  if (!aFrame || !aState) {
    NS_WARNING("null frame or state");
    return;
  }

  // Only restore state for stateful frames
  nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
  if (!statefulFrame) {
    return;
  }

  // Generate the hash key the state was stored under
  // Exit early if we get empty key
  nsIContent* content = aFrame->GetContent();
  // If we don't have content, we can't generate a hash
  // key and there's probably no state information for us.
  if (!content) {
    return;
  }

  nsAutoCString stateKey;
  Document* doc = content->GetUncomposedDoc();
  statefulFrame->GenerateStateKey(content, doc, stateKey);
  if (stateKey.IsEmpty()) {
    return;
  }

  // Get the state from the hash
  PresState* frameState = aState->GetState(stateKey);
  if (!frameState) {
    return;
  }

  // Restore it
  nsresult rv = statefulFrame->RestoreState(frameState);
  if (NS_FAILED(rv)) {
    return;
  }

  // If we restore ok, remove the state from the state table
  aState->RemoveState(stateKey);
}

void nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
                                       nsILayoutHistoryState* aState) {
  MOZ_ASSERT(nullptr != aFrame && nullptr != aState,
             "null parameters passed in");

  RestoreFrameStateFor(aFrame, aState);

  // Now restore state recursively for the frame hierarchy rooted at aFrame
  for (const auto& childList : aFrame->ChildLists()) {
    for (nsIFrame* child : childList.mList) {
      RestoreFrameState(child, aState);
    }
  }
}

void nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
  aSizes.mLayoutPresShellSize += aSizes.mState.mMallocSizeOf(this);
}