layout/generic/nsSplittableFrame.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:d9dd03e41945
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/. */
5
6 /*
7 * base class for rendering objects that can be split across lines,
8 * columns, or pages
9 */
10
11 #include "nsSplittableFrame.h"
12 #include "nsContainerFrame.h"
13 #include "nsIFrameInlines.h"
14
15 NS_IMPL_FRAMEARENA_HELPERS(nsSplittableFrame)
16
17 void
18 nsSplittableFrame::Init(nsIContent* aContent,
19 nsIFrame* aParent,
20 nsIFrame* aPrevInFlow)
21 {
22 nsFrame::Init(aContent, aParent, aPrevInFlow);
23
24 if (aPrevInFlow) {
25 // Hook the frame into the flow
26 SetPrevInFlow(aPrevInFlow);
27 aPrevInFlow->SetNextInFlow(this);
28 }
29 }
30
31 void
32 nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot)
33 {
34 // Disconnect from the flow list
35 if (mPrevContinuation || mNextContinuation) {
36 RemoveFromFlow(this);
37 }
38
39 // Let the base class destroy the frame
40 nsFrame::DestroyFrom(aDestructRoot);
41 }
42
43 nsSplittableType
44 nsSplittableFrame::GetSplittableType() const
45 {
46 return NS_FRAME_SPLITTABLE;
47 }
48
49 nsIFrame* nsSplittableFrame::GetPrevContinuation() const
50 {
51 return mPrevContinuation;
52 }
53
54 void
55 nsSplittableFrame::SetPrevContinuation(nsIFrame* aFrame)
56 {
57 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev continuation with incorrect type!");
58 NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
59 mPrevContinuation = aFrame;
60 RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
61 }
62
63 nsIFrame* nsSplittableFrame::GetNextContinuation() const
64 {
65 return mNextContinuation;
66 }
67
68 void
69 nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame)
70 {
71 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next continuation with incorrect type!");
72 NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
73 mNextContinuation = aFrame;
74 if (aFrame)
75 aFrame->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
76 }
77
78 nsIFrame*
79 nsSplittableFrame::FirstContinuation() const
80 {
81 nsSplittableFrame* firstContinuation = const_cast<nsSplittableFrame*>(this);
82 while (firstContinuation->mPrevContinuation) {
83 firstContinuation = static_cast<nsSplittableFrame*>(firstContinuation->mPrevContinuation);
84 }
85 MOZ_ASSERT(firstContinuation, "post-condition failed");
86 return firstContinuation;
87 }
88
89 nsIFrame*
90 nsSplittableFrame::LastContinuation() const
91 {
92 nsSplittableFrame* lastContinuation = const_cast<nsSplittableFrame*>(this);
93 while (lastContinuation->mNextContinuation) {
94 lastContinuation = static_cast<nsSplittableFrame*>(lastContinuation->mNextContinuation);
95 }
96 MOZ_ASSERT(lastContinuation, "post-condition failed");
97 return lastContinuation;
98 }
99
100 #ifdef DEBUG
101 bool nsSplittableFrame::IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
102 {
103 int32_t iterations = 0;
104 while (aFrame1 && iterations < 10) {
105 // Bail out after 10 iterations so we don't bog down debug builds too much
106 if (aFrame1 == aFrame2)
107 return true;
108 aFrame1 = aFrame1->GetPrevContinuation();
109 ++iterations;
110 }
111 return false;
112 }
113
114 bool nsSplittableFrame::IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
115 {
116 int32_t iterations = 0;
117 while (aFrame1 && iterations < 10) {
118 // Bail out after 10 iterations so we don't bog down debug builds too much
119 if (aFrame1 == aFrame2)
120 return true;
121 aFrame1 = aFrame1->GetNextContinuation();
122 ++iterations;
123 }
124 return false;
125 }
126 #endif
127
128 nsIFrame* nsSplittableFrame::GetPrevInFlow() const
129 {
130 return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
131 }
132
133 void
134 nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame)
135 {
136 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev in flow with incorrect type!");
137 NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
138 mPrevContinuation = aFrame;
139 AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
140 }
141
142 nsIFrame* nsSplittableFrame::GetNextInFlow() const
143 {
144 return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
145 mNextContinuation : nullptr;
146 }
147
148 void
149 nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame)
150 {
151 NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next in flow with incorrect type!");
152 NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
153 mNextContinuation = aFrame;
154 if (aFrame)
155 aFrame->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
156 }
157
158 nsIFrame*
159 nsSplittableFrame::FirstInFlow() const
160 {
161 nsSplittableFrame* firstInFlow = const_cast<nsSplittableFrame*>(this);
162 while (nsIFrame* prev = firstInFlow->GetPrevInFlow()) {
163 firstInFlow = static_cast<nsSplittableFrame*>(prev);
164 }
165 MOZ_ASSERT(firstInFlow, "post-condition failed");
166 return firstInFlow;
167 }
168
169 nsIFrame*
170 nsSplittableFrame::LastInFlow() const
171 {
172 nsSplittableFrame* lastInFlow = const_cast<nsSplittableFrame*>(this);
173 while (nsIFrame* next = lastInFlow->GetNextInFlow()) {
174 lastInFlow = static_cast<nsSplittableFrame*>(next);
175 }
176 MOZ_ASSERT(lastInFlow, "post-condition failed");
177 return lastInFlow;
178 }
179
180 // Remove this frame from the flow. Connects prev in flow and next in flow
181 void
182 nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame)
183 {
184 nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
185 nsIFrame* nextContinuation = aFrame->GetNextContinuation();
186
187 // The new continuation is fluid only if the continuation on both sides
188 // of the removed frame was fluid
189 if (aFrame->GetPrevInFlow() && aFrame->GetNextInFlow()) {
190 if (prevContinuation) {
191 prevContinuation->SetNextInFlow(nextContinuation);
192 }
193 if (nextContinuation) {
194 nextContinuation->SetPrevInFlow(prevContinuation);
195 }
196 } else {
197 if (prevContinuation) {
198 prevContinuation->SetNextContinuation(nextContinuation);
199 }
200 if (nextContinuation) {
201 nextContinuation->SetPrevContinuation(prevContinuation);
202 }
203 }
204
205 aFrame->SetPrevInFlow(nullptr);
206 aFrame->SetNextInFlow(nullptr);
207 }
208
209 nscoord
210 nsSplittableFrame::GetConsumedHeight() const
211 {
212 nscoord height = 0;
213
214 // Reduce the height by the computed height of prev-in-flows.
215 for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
216 height += prev->GetRect().height;
217 }
218
219 return height;
220 }
221
222 nscoord
223 nsSplittableFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState,
224 nscoord aConsumedHeight) const
225 {
226 nscoord height = aReflowState.ComputedHeight();
227 if (height == NS_INTRINSICSIZE) {
228 return NS_INTRINSICSIZE;
229 }
230
231 if (aConsumedHeight == NS_INTRINSICSIZE) {
232 aConsumedHeight = GetConsumedHeight();
233 }
234
235 height -= aConsumedHeight;
236
237 if (aConsumedHeight != 0 && aConsumedHeight != NS_INTRINSICSIZE) {
238 // We just subtracted our top-border padding, since it was included in the
239 // first frame's height. Add it back to get the content height.
240 height += aReflowState.ComputedPhysicalBorderPadding().top;
241 }
242
243 // We may have stretched the frame beyond its computed height. Oh well.
244 height = std::max(0, height);
245
246 return height;
247 }
248
249 int
250 nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
251 {
252 if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
253 return LOGICAL_SIDES_B_BOTH;
254 }
255
256 int skip = 0;
257
258 if (GetPrevInFlow()) {
259 skip |= LOGICAL_SIDE_B_START;
260 }
261
262 if (aReflowState) {
263 // We're in the midst of reflow right now, so it's possible that we haven't
264 // created a nif yet. If our content height is going to exceed our available
265 // height, though, then we're going to need a next-in-flow, it just hasn't
266 // been created yet.
267
268 if (NS_UNCONSTRAINEDSIZE != aReflowState->AvailableHeight()) {
269 nscoord effectiveCH = this->GetEffectiveComputedHeight(*aReflowState);
270 if (effectiveCH > aReflowState->AvailableHeight()) {
271 // Our content height is going to exceed our available height, so we're
272 // going to need a next-in-flow.
273 skip |= LOGICAL_SIDE_B_END;
274 }
275 }
276 } else {
277 nsIFrame* nif = GetNextInFlow();
278 if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
279 skip |= LOGICAL_SIDE_B_END;
280 }
281 }
282
283 return skip;
284 }
285
286 #ifdef DEBUG
287 void
288 nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
289 {
290 nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent);
291 if (nullptr != mNextContinuation) {
292 IndentBy(out, aIndent);
293 fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation);
294 }
295 if (nullptr != mPrevContinuation) {
296 IndentBy(out, aIndent);
297 fprintf(out, "<prev-continuation va=\"%p\"/>\n", (void*)mPrevContinuation);
298 }
299
300 }
301 #endif

mercurial