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/. */
7 * base class for rendering objects that can be split across lines,
11 #include "nsSplittableFrame.h"
12 #include "nsContainerFrame.h"
13 #include "nsIFrameInlines.h"
15 using namespace mozilla
;
17 NS_IMPL_FRAMEARENA_HELPERS(nsSplittableFrame
)
20 nsSplittableFrame::Init(nsIContent
* aContent
,
21 nsContainerFrame
* aParent
,
22 nsIFrame
* aPrevInFlow
)
24 nsFrame::Init(aContent
, aParent
, aPrevInFlow
);
27 // Hook the frame into the flow
28 SetPrevInFlow(aPrevInFlow
);
29 aPrevInFlow
->SetNextInFlow(this);
34 nsSplittableFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
36 // Disconnect from the flow list
37 if (mPrevContinuation
|| mNextContinuation
) {
41 // Let the base class destroy the frame
42 nsFrame::DestroyFrom(aDestructRoot
);
46 nsSplittableFrame::GetSplittableType() const
48 return NS_FRAME_SPLITTABLE
;
51 nsIFrame
* nsSplittableFrame::GetPrevContinuation() const
53 return mPrevContinuation
;
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
;
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
;
77 aFrame
->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION
);
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
;
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
;
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
)
110 aFrame1
= aFrame1
->GetPrevContinuation();
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
)
123 aFrame1
= aFrame1
->GetNextContinuation();
130 nsIFrame
* nsSplittableFrame::GetPrevInFlow() const
132 return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION
) ? mPrevContinuation
: nullptr;
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;
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
;
157 aFrame
->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION
);
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");
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");
182 // Remove this frame from the flow. Connects prev in flow and next in flow
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
);
199 if (prevContinuation
) {
200 prevContinuation
->SetNextContinuation(nextContinuation
);
202 if (nextContinuation
) {
203 nextContinuation
->SetPrevContinuation(prevContinuation
);
207 aFrame
->SetPrevInFlow(nullptr);
208 aFrame
->SetNextInFlow(nullptr);
212 nsSplittableFrame::GetConsumedBSize() const
215 for (nsIFrame
* prev
= GetPrevInFlow(); prev
; prev
= prev
->GetPrevInFlow()) {
216 height
+= prev
->GetContentRectRelativeToSelf().height
;
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();
253 if (GetPrevInFlow()) {
254 skip
|= eLogicalSideBitsBStart
;
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
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
;
273 nsIFrame
* nif
= GetNextInFlow();
274 if (nif
&& !IS_TRUE_OVERFLOW_CONTAINER(nif
)) {
275 skip
|= eLogicalSideBitsBEnd
;
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
);