Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsSplittableFrame.cpp
blob7e018322b94a0971893ef9d5a6ce2136c39093f4
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/. */
6 /*
7 * base class for rendering objects that can be split across lines,
8 * columns, or pages
9 */
11 #include "nsSplittableFrame.h"
12 #include "nsContainerFrame.h"
13 #include "nsIFrameInlines.h"
15 using namespace mozilla;
17 NS_IMPL_FRAMEARENA_HELPERS(nsSplittableFrame)
19 void
20 nsSplittableFrame::Init(nsIContent* aContent,
21 nsContainerFrame* aParent,
22 nsIFrame* aPrevInFlow)
24 nsFrame::Init(aContent, aParent, aPrevInFlow);
26 if (aPrevInFlow) {
27 // Hook the frame into the flow
28 SetPrevInFlow(aPrevInFlow);
29 aPrevInFlow->SetNextInFlow(this);
33 void
34 nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot)
36 // Disconnect from the flow list
37 if (mPrevContinuation || mNextContinuation) {
38 RemoveFromFlow(this);
41 // Let the base class destroy the frame
42 nsFrame::DestroyFrom(aDestructRoot);
45 nsSplittableType
46 nsSplittableFrame::GetSplittableType() const
48 return NS_FRAME_SPLITTABLE;
51 nsIFrame* nsSplittableFrame::GetPrevContinuation() const
53 return mPrevContinuation;
56 void
57 nsSplittableFrame::SetPrevContinuation(nsIFrame* aFrame)
59 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev continuation with incorrect type!");
60 NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
61 mPrevContinuation = aFrame;
62 RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
65 nsIFrame* nsSplittableFrame::GetNextContinuation() const
67 return mNextContinuation;
70 void
71 nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame)
73 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next continuation with incorrect type!");
74 NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
75 mNextContinuation = aFrame;
76 if (aFrame)
77 aFrame->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
80 nsIFrame*
81 nsSplittableFrame::FirstContinuation() const
83 nsSplittableFrame* firstContinuation = const_cast<nsSplittableFrame*>(this);
84 while (firstContinuation->mPrevContinuation) {
85 firstContinuation = static_cast<nsSplittableFrame*>(firstContinuation->mPrevContinuation);
87 MOZ_ASSERT(firstContinuation, "post-condition failed");
88 return firstContinuation;
91 nsIFrame*
92 nsSplittableFrame::LastContinuation() const
94 nsSplittableFrame* lastContinuation = const_cast<nsSplittableFrame*>(this);
95 while (lastContinuation->mNextContinuation) {
96 lastContinuation = static_cast<nsSplittableFrame*>(lastContinuation->mNextContinuation);
98 MOZ_ASSERT(lastContinuation, "post-condition failed");
99 return lastContinuation;
102 #ifdef DEBUG
103 bool nsSplittableFrame::IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
105 int32_t iterations = 0;
106 while (aFrame1 && iterations < 10) {
107 // Bail out after 10 iterations so we don't bog down debug builds too much
108 if (aFrame1 == aFrame2)
109 return true;
110 aFrame1 = aFrame1->GetPrevContinuation();
111 ++iterations;
113 return false;
116 bool nsSplittableFrame::IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
118 int32_t iterations = 0;
119 while (aFrame1 && iterations < 10) {
120 // Bail out after 10 iterations so we don't bog down debug builds too much
121 if (aFrame1 == aFrame2)
122 return true;
123 aFrame1 = aFrame1->GetNextContinuation();
124 ++iterations;
126 return false;
128 #endif
130 nsIFrame* nsSplittableFrame::GetPrevInFlow() const
132 return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
135 void
136 nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame)
138 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev in flow with incorrect type!");
139 NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
140 mPrevContinuation = aFrame;
141 AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
144 nsIFrame* nsSplittableFrame::GetNextInFlow() const
146 return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
147 mNextContinuation : nullptr;
150 void
151 nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame)
153 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next in flow with incorrect type!");
154 NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
155 mNextContinuation = aFrame;
156 if (aFrame)
157 aFrame->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
160 nsIFrame*
161 nsSplittableFrame::FirstInFlow() const
163 nsSplittableFrame* firstInFlow = const_cast<nsSplittableFrame*>(this);
164 while (nsIFrame* prev = firstInFlow->GetPrevInFlow()) {
165 firstInFlow = static_cast<nsSplittableFrame*>(prev);
167 MOZ_ASSERT(firstInFlow, "post-condition failed");
168 return firstInFlow;
171 nsIFrame*
172 nsSplittableFrame::LastInFlow() const
174 nsSplittableFrame* lastInFlow = const_cast<nsSplittableFrame*>(this);
175 while (nsIFrame* next = lastInFlow->GetNextInFlow()) {
176 lastInFlow = static_cast<nsSplittableFrame*>(next);
178 MOZ_ASSERT(lastInFlow, "post-condition failed");
179 return lastInFlow;
182 // Remove this frame from the flow. Connects prev in flow and next in flow
183 void
184 nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame)
186 nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
187 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
189 // The new continuation is fluid only if the continuation on both sides
190 // of the removed frame was fluid
191 if (aFrame->GetPrevInFlow() && aFrame->GetNextInFlow()) {
192 if (prevContinuation) {
193 prevContinuation->SetNextInFlow(nextContinuation);
195 if (nextContinuation) {
196 nextContinuation->SetPrevInFlow(prevContinuation);
198 } else {
199 if (prevContinuation) {
200 prevContinuation->SetNextContinuation(nextContinuation);
202 if (nextContinuation) {
203 nextContinuation->SetPrevContinuation(prevContinuation);
207 aFrame->SetPrevInFlow(nullptr);
208 aFrame->SetNextInFlow(nullptr);
211 nscoord
212 nsSplittableFrame::GetConsumedBSize() const
214 nscoord height = 0;
215 for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
216 height += prev->GetContentRectRelativeToSelf().height;
218 return height;
221 nscoord
222 nsSplittableFrame::GetEffectiveComputedBSize(const nsHTMLReflowState& aReflowState,
223 nscoord aConsumedBSize) const
225 nscoord bSize = aReflowState.ComputedBSize();
226 if (bSize == NS_INTRINSICSIZE) {
227 return NS_INTRINSICSIZE;
230 if (aConsumedBSize == NS_INTRINSICSIZE) {
231 aConsumedBSize = GetConsumedBSize();
234 bSize -= aConsumedBSize;
236 // We may have stretched the frame beyond its computed height. Oh well.
237 return std::max(0, bSize);
240 nsIFrame::LogicalSides
241 nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
243 if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
244 return LogicalSides(eLogicalSideBitsBBoth);
247 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
248 NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
249 return LogicalSides();
252 LogicalSides skip;
253 if (GetPrevInFlow()) {
254 skip |= eLogicalSideBitsBStart;
257 if (aReflowState) {
258 // We're in the midst of reflow right now, so it's possible that we haven't
259 // created a nif yet. If our content height is going to exceed our available
260 // height, though, then we're going to need a next-in-flow, it just hasn't
261 // been created yet.
263 if (NS_UNCONSTRAINEDSIZE != aReflowState->AvailableBSize()) {
264 nscoord effectiveCH = this->GetEffectiveComputedBSize(*aReflowState);
265 if (effectiveCH != NS_INTRINSICSIZE &&
266 effectiveCH > aReflowState->AvailableBSize()) {
267 // Our content height is going to exceed our available height, so we're
268 // going to need a next-in-flow.
269 skip |= eLogicalSideBitsBEnd;
272 } else {
273 nsIFrame* nif = GetNextInFlow();
274 if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
275 skip |= eLogicalSideBitsBEnd;
279 return skip;
282 #ifdef DEBUG
283 void
284 nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
286 nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent);
287 if (nullptr != mNextContinuation) {
288 IndentBy(out, aIndent);
289 fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation);
291 if (nullptr != mPrevContinuation) {
292 IndentBy(out, aIndent);
293 fprintf(out, "<prev-continuation va=\"%p\"/>\n", (void*)mPrevContinuation);
297 #endif