Line data Source code
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 : using namespace mozilla;
16 :
17 : void
18 448 : nsSplittableFrame::Init(nsIContent* aContent,
19 : nsContainerFrame* aParent,
20 : nsIFrame* aPrevInFlow)
21 : {
22 448 : nsFrame::Init(aContent, aParent, aPrevInFlow);
23 :
24 448 : if (aPrevInFlow) {
25 : // Hook the frame into the flow
26 0 : SetPrevInFlow(aPrevInFlow);
27 0 : aPrevInFlow->SetNextInFlow(this);
28 : }
29 448 : }
30 :
31 : void
32 76 : nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot)
33 : {
34 : // Disconnect from the flow list
35 76 : if (mPrevContinuation || mNextContinuation) {
36 0 : RemoveFromFlow(this);
37 : }
38 :
39 : // Let the base class destroy the frame
40 76 : nsFrame::DestroyFrom(aDestructRoot);
41 76 : }
42 :
43 : nsSplittableType
44 0 : nsSplittableFrame::GetSplittableType() const
45 : {
46 0 : return NS_FRAME_SPLITTABLE;
47 : }
48 :
49 3428 : nsIFrame* nsSplittableFrame::GetPrevContinuation() const
50 : {
51 3428 : return mPrevContinuation;
52 : }
53 :
54 : void
55 0 : nsSplittableFrame::SetPrevContinuation(nsIFrame* aFrame)
56 : {
57 0 : NS_ASSERTION(!aFrame || Type() == aFrame->Type(),
58 : "setting a prev continuation with incorrect type!");
59 0 : NS_ASSERTION(!IsInPrevContinuationChain(aFrame, this),
60 : "creating a loop in continuation chain!");
61 0 : mPrevContinuation = aFrame;
62 0 : RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
63 0 : }
64 :
65 2753 : nsIFrame* nsSplittableFrame::GetNextContinuation() const
66 : {
67 2753 : return mNextContinuation;
68 : }
69 :
70 : void
71 0 : nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame)
72 : {
73 0 : NS_ASSERTION(!aFrame || Type() == aFrame->Type(),
74 : "setting a next continuation with incorrect type!");
75 0 : NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
76 0 : mNextContinuation = aFrame;
77 0 : if (aFrame)
78 0 : aFrame->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
79 0 : }
80 :
81 : nsIFrame*
82 578 : nsSplittableFrame::FirstContinuation() const
83 : {
84 578 : nsSplittableFrame* firstContinuation = const_cast<nsSplittableFrame*>(this);
85 578 : while (firstContinuation->mPrevContinuation) {
86 0 : firstContinuation = static_cast<nsSplittableFrame*>(firstContinuation->mPrevContinuation);
87 : }
88 578 : MOZ_ASSERT(firstContinuation, "post-condition failed");
89 578 : return firstContinuation;
90 : }
91 :
92 : nsIFrame*
93 58 : nsSplittableFrame::LastContinuation() const
94 : {
95 58 : nsSplittableFrame* lastContinuation = const_cast<nsSplittableFrame*>(this);
96 58 : while (lastContinuation->mNextContinuation) {
97 0 : lastContinuation = static_cast<nsSplittableFrame*>(lastContinuation->mNextContinuation);
98 : }
99 58 : MOZ_ASSERT(lastContinuation, "post-condition failed");
100 58 : return lastContinuation;
101 : }
102 :
103 : #ifdef DEBUG
104 0 : bool nsSplittableFrame::IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
105 : {
106 0 : int32_t iterations = 0;
107 0 : while (aFrame1 && iterations < 10) {
108 : // Bail out after 10 iterations so we don't bog down debug builds too much
109 0 : if (aFrame1 == aFrame2)
110 0 : return true;
111 0 : aFrame1 = aFrame1->GetPrevContinuation();
112 0 : ++iterations;
113 : }
114 0 : return false;
115 : }
116 :
117 0 : bool nsSplittableFrame::IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
118 : {
119 0 : int32_t iterations = 0;
120 0 : while (aFrame1 && iterations < 10) {
121 : // Bail out after 10 iterations so we don't bog down debug builds too much
122 0 : if (aFrame1 == aFrame2)
123 0 : return true;
124 0 : aFrame1 = aFrame1->GetNextContinuation();
125 0 : ++iterations;
126 : }
127 0 : return false;
128 : }
129 : #endif
130 :
131 4136 : nsIFrame* nsSplittableFrame::GetPrevInFlow() const
132 : {
133 4136 : return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
134 : }
135 :
136 : void
137 0 : nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame)
138 : {
139 0 : NS_ASSERTION(!aFrame || Type() == aFrame->Type(),
140 : "setting a prev in flow with incorrect type!");
141 0 : NS_ASSERTION(!IsInPrevContinuationChain(aFrame, this),
142 : "creating a loop in continuation chain!");
143 0 : mPrevContinuation = aFrame;
144 0 : AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
145 0 : }
146 :
147 3147 : nsIFrame* nsSplittableFrame::GetNextInFlow() const
148 : {
149 3147 : return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
150 3147 : mNextContinuation : nullptr;
151 : }
152 :
153 : void
154 0 : nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame)
155 : {
156 0 : NS_ASSERTION(!aFrame || Type() == aFrame->Type(),
157 : "setting a next in flow with incorrect type!");
158 0 : NS_ASSERTION(!IsInNextContinuationChain(aFrame, this),
159 : "creating a loop in continuation chain!");
160 0 : mNextContinuation = aFrame;
161 0 : if (aFrame)
162 0 : aFrame->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
163 0 : }
164 :
165 : nsIFrame*
166 457 : nsSplittableFrame::FirstInFlow() const
167 : {
168 457 : nsSplittableFrame* firstInFlow = const_cast<nsSplittableFrame*>(this);
169 457 : while (nsIFrame* prev = firstInFlow->GetPrevInFlow()) {
170 0 : firstInFlow = static_cast<nsSplittableFrame*>(prev);
171 0 : }
172 457 : MOZ_ASSERT(firstInFlow, "post-condition failed");
173 457 : return firstInFlow;
174 : }
175 :
176 : nsIFrame*
177 6 : nsSplittableFrame::LastInFlow() const
178 : {
179 6 : nsSplittableFrame* lastInFlow = const_cast<nsSplittableFrame*>(this);
180 6 : while (nsIFrame* next = lastInFlow->GetNextInFlow()) {
181 0 : lastInFlow = static_cast<nsSplittableFrame*>(next);
182 0 : }
183 6 : MOZ_ASSERT(lastInFlow, "post-condition failed");
184 6 : return lastInFlow;
185 : }
186 :
187 : // Remove this frame from the flow. Connects prev in flow and next in flow
188 : void
189 0 : nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame)
190 : {
191 0 : nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
192 0 : nsIFrame* nextContinuation = aFrame->GetNextContinuation();
193 :
194 : // The new continuation is fluid only if the continuation on both sides
195 : // of the removed frame was fluid
196 0 : if (aFrame->GetPrevInFlow() && aFrame->GetNextInFlow()) {
197 0 : if (prevContinuation) {
198 0 : prevContinuation->SetNextInFlow(nextContinuation);
199 : }
200 0 : if (nextContinuation) {
201 0 : nextContinuation->SetPrevInFlow(prevContinuation);
202 : }
203 : } else {
204 0 : if (prevContinuation) {
205 0 : prevContinuation->SetNextContinuation(nextContinuation);
206 : }
207 0 : if (nextContinuation) {
208 0 : nextContinuation->SetPrevContinuation(prevContinuation);
209 : }
210 : }
211 :
212 0 : aFrame->SetPrevInFlow(nullptr);
213 0 : aFrame->SetNextInFlow(nullptr);
214 0 : }
215 :
216 : nscoord
217 162 : nsSplittableFrame::ConsumedBSize(WritingMode aWM) const
218 : {
219 162 : nscoord bSize = 0;
220 162 : for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
221 0 : bSize += prev->ContentBSize(aWM);
222 : }
223 162 : return bSize;
224 : }
225 :
226 : nscoord
227 206 : nsSplittableFrame::GetEffectiveComputedBSize(const ReflowInput& aReflowInput,
228 : nscoord aConsumedBSize) const
229 : {
230 206 : nscoord bSize = aReflowInput.ComputedBSize();
231 206 : if (bSize == NS_INTRINSICSIZE) {
232 118 : return NS_INTRINSICSIZE;
233 : }
234 :
235 88 : if (aConsumedBSize == NS_INTRINSICSIZE) {
236 0 : aConsumedBSize = ConsumedBSize(aReflowInput.GetWritingMode());
237 : }
238 :
239 88 : bSize -= aConsumedBSize;
240 :
241 : // We may have stretched the frame beyond its computed height. Oh well.
242 88 : return std::max(0, bSize);
243 : }
244 :
245 : nsIFrame::LogicalSides
246 1904 : nsSplittableFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
247 : {
248 1904 : if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
249 0 : return LogicalSides(eLogicalSideBitsBBoth);
250 : }
251 :
252 1904 : if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
253 : StyleBoxDecorationBreak::Clone)) {
254 0 : return LogicalSides();
255 : }
256 :
257 1904 : LogicalSides skip;
258 1904 : if (GetPrevInFlow()) {
259 0 : skip |= eLogicalSideBitsBStart;
260 : }
261 :
262 1904 : if (aReflowInput) {
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 162 : if (NS_UNCONSTRAINEDSIZE != aReflowInput->AvailableBSize()) {
269 0 : nscoord effectiveCH = this->GetEffectiveComputedBSize(*aReflowInput);
270 0 : if (effectiveCH != NS_INTRINSICSIZE &&
271 0 : effectiveCH > aReflowInput->AvailableBSize()) {
272 : // Our content height is going to exceed our available height, so we're
273 : // going to need a next-in-flow.
274 0 : skip |= eLogicalSideBitsBEnd;
275 : }
276 : }
277 : } else {
278 1742 : nsIFrame* nif = GetNextInFlow();
279 1742 : if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
280 0 : skip |= eLogicalSideBitsBEnd;
281 : }
282 : }
283 :
284 1904 : return skip;
285 : }
286 :
287 : LogicalSides
288 0 : nsSplittableFrame::PreReflowBlockLevelLogicalSkipSides() const
289 : {
290 0 : if (MOZ_UNLIKELY(IS_TRUE_OVERFLOW_CONTAINER(this))) {
291 0 : return LogicalSides(mozilla::eLogicalSideBitsBBoth);
292 : }
293 0 : if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak !=
294 0 : StyleBoxDecorationBreak::Clone) &&
295 0 : GetPrevInFlow()) {
296 0 : return LogicalSides(mozilla::eLogicalSideBitsBStart);
297 : }
298 0 : return LogicalSides();
299 : }
300 :
301 : #ifdef DEBUG
302 : void
303 0 : nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
304 : {
305 0 : nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent);
306 0 : if (nullptr != mNextContinuation) {
307 0 : IndentBy(out, aIndent);
308 0 : fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation);
309 : }
310 0 : if (nullptr != mPrevContinuation) {
311 0 : IndentBy(out, aIndent);
312 0 : fprintf(out, "<prev-continuation va=\"%p\"/>\n", (void*)mPrevContinuation);
313 : }
314 :
315 0 : }
316 : #endif
|