|
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 |