1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsSplittableFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,301 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * base class for rendering objects that can be split across lines, 1.11 + * columns, or pages 1.12 + */ 1.13 + 1.14 +#include "nsSplittableFrame.h" 1.15 +#include "nsContainerFrame.h" 1.16 +#include "nsIFrameInlines.h" 1.17 + 1.18 +NS_IMPL_FRAMEARENA_HELPERS(nsSplittableFrame) 1.19 + 1.20 +void 1.21 +nsSplittableFrame::Init(nsIContent* aContent, 1.22 + nsIFrame* aParent, 1.23 + nsIFrame* aPrevInFlow) 1.24 +{ 1.25 + nsFrame::Init(aContent, aParent, aPrevInFlow); 1.26 + 1.27 + if (aPrevInFlow) { 1.28 + // Hook the frame into the flow 1.29 + SetPrevInFlow(aPrevInFlow); 1.30 + aPrevInFlow->SetNextInFlow(this); 1.31 + } 1.32 +} 1.33 + 1.34 +void 1.35 +nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.36 +{ 1.37 + // Disconnect from the flow list 1.38 + if (mPrevContinuation || mNextContinuation) { 1.39 + RemoveFromFlow(this); 1.40 + } 1.41 + 1.42 + // Let the base class destroy the frame 1.43 + nsFrame::DestroyFrom(aDestructRoot); 1.44 +} 1.45 + 1.46 +nsSplittableType 1.47 +nsSplittableFrame::GetSplittableType() const 1.48 +{ 1.49 + return NS_FRAME_SPLITTABLE; 1.50 +} 1.51 + 1.52 +nsIFrame* nsSplittableFrame::GetPrevContinuation() const 1.53 +{ 1.54 + return mPrevContinuation; 1.55 +} 1.56 + 1.57 +void 1.58 +nsSplittableFrame::SetPrevContinuation(nsIFrame* aFrame) 1.59 +{ 1.60 + NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev continuation with incorrect type!"); 1.61 + NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!"); 1.62 + mPrevContinuation = aFrame; 1.63 + RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION); 1.64 +} 1.65 + 1.66 +nsIFrame* nsSplittableFrame::GetNextContinuation() const 1.67 +{ 1.68 + return mNextContinuation; 1.69 +} 1.70 + 1.71 +void 1.72 +nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame) 1.73 +{ 1.74 + NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next continuation with incorrect type!"); 1.75 + NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!"); 1.76 + mNextContinuation = aFrame; 1.77 + if (aFrame) 1.78 + aFrame->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION); 1.79 +} 1.80 + 1.81 +nsIFrame* 1.82 +nsSplittableFrame::FirstContinuation() const 1.83 +{ 1.84 + nsSplittableFrame* firstContinuation = const_cast<nsSplittableFrame*>(this); 1.85 + while (firstContinuation->mPrevContinuation) { 1.86 + firstContinuation = static_cast<nsSplittableFrame*>(firstContinuation->mPrevContinuation); 1.87 + } 1.88 + MOZ_ASSERT(firstContinuation, "post-condition failed"); 1.89 + return firstContinuation; 1.90 +} 1.91 + 1.92 +nsIFrame* 1.93 +nsSplittableFrame::LastContinuation() const 1.94 +{ 1.95 + nsSplittableFrame* lastContinuation = const_cast<nsSplittableFrame*>(this); 1.96 + while (lastContinuation->mNextContinuation) { 1.97 + lastContinuation = static_cast<nsSplittableFrame*>(lastContinuation->mNextContinuation); 1.98 + } 1.99 + MOZ_ASSERT(lastContinuation, "post-condition failed"); 1.100 + return lastContinuation; 1.101 +} 1.102 + 1.103 +#ifdef DEBUG 1.104 +bool nsSplittableFrame::IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2) 1.105 +{ 1.106 + int32_t iterations = 0; 1.107 + while (aFrame1 && iterations < 10) { 1.108 + // Bail out after 10 iterations so we don't bog down debug builds too much 1.109 + if (aFrame1 == aFrame2) 1.110 + return true; 1.111 + aFrame1 = aFrame1->GetPrevContinuation(); 1.112 + ++iterations; 1.113 + } 1.114 + return false; 1.115 +} 1.116 + 1.117 +bool nsSplittableFrame::IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2) 1.118 +{ 1.119 + int32_t iterations = 0; 1.120 + while (aFrame1 && iterations < 10) { 1.121 + // Bail out after 10 iterations so we don't bog down debug builds too much 1.122 + if (aFrame1 == aFrame2) 1.123 + return true; 1.124 + aFrame1 = aFrame1->GetNextContinuation(); 1.125 + ++iterations; 1.126 + } 1.127 + return false; 1.128 +} 1.129 +#endif 1.130 + 1.131 +nsIFrame* nsSplittableFrame::GetPrevInFlow() const 1.132 +{ 1.133 + return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr; 1.134 +} 1.135 + 1.136 +void 1.137 +nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame) 1.138 +{ 1.139 + NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev in flow with incorrect type!"); 1.140 + NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!"); 1.141 + mPrevContinuation = aFrame; 1.142 + AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); 1.143 +} 1.144 + 1.145 +nsIFrame* nsSplittableFrame::GetNextInFlow() const 1.146 +{ 1.147 + return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? 1.148 + mNextContinuation : nullptr; 1.149 +} 1.150 + 1.151 +void 1.152 +nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame) 1.153 +{ 1.154 + NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next in flow with incorrect type!"); 1.155 + NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!"); 1.156 + mNextContinuation = aFrame; 1.157 + if (aFrame) 1.158 + aFrame->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); 1.159 +} 1.160 + 1.161 +nsIFrame* 1.162 +nsSplittableFrame::FirstInFlow() const 1.163 +{ 1.164 + nsSplittableFrame* firstInFlow = const_cast<nsSplittableFrame*>(this); 1.165 + while (nsIFrame* prev = firstInFlow->GetPrevInFlow()) { 1.166 + firstInFlow = static_cast<nsSplittableFrame*>(prev); 1.167 + } 1.168 + MOZ_ASSERT(firstInFlow, "post-condition failed"); 1.169 + return firstInFlow; 1.170 +} 1.171 + 1.172 +nsIFrame* 1.173 +nsSplittableFrame::LastInFlow() const 1.174 +{ 1.175 + nsSplittableFrame* lastInFlow = const_cast<nsSplittableFrame*>(this); 1.176 + while (nsIFrame* next = lastInFlow->GetNextInFlow()) { 1.177 + lastInFlow = static_cast<nsSplittableFrame*>(next); 1.178 + } 1.179 + MOZ_ASSERT(lastInFlow, "post-condition failed"); 1.180 + return lastInFlow; 1.181 +} 1.182 + 1.183 +// Remove this frame from the flow. Connects prev in flow and next in flow 1.184 +void 1.185 +nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame) 1.186 +{ 1.187 + nsIFrame* prevContinuation = aFrame->GetPrevContinuation(); 1.188 + nsIFrame* nextContinuation = aFrame->GetNextContinuation(); 1.189 + 1.190 + // The new continuation is fluid only if the continuation on both sides 1.191 + // of the removed frame was fluid 1.192 + if (aFrame->GetPrevInFlow() && aFrame->GetNextInFlow()) { 1.193 + if (prevContinuation) { 1.194 + prevContinuation->SetNextInFlow(nextContinuation); 1.195 + } 1.196 + if (nextContinuation) { 1.197 + nextContinuation->SetPrevInFlow(prevContinuation); 1.198 + } 1.199 + } else { 1.200 + if (prevContinuation) { 1.201 + prevContinuation->SetNextContinuation(nextContinuation); 1.202 + } 1.203 + if (nextContinuation) { 1.204 + nextContinuation->SetPrevContinuation(prevContinuation); 1.205 + } 1.206 + } 1.207 + 1.208 + aFrame->SetPrevInFlow(nullptr); 1.209 + aFrame->SetNextInFlow(nullptr); 1.210 +} 1.211 + 1.212 +nscoord 1.213 +nsSplittableFrame::GetConsumedHeight() const 1.214 +{ 1.215 + nscoord height = 0; 1.216 + 1.217 + // Reduce the height by the computed height of prev-in-flows. 1.218 + for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) { 1.219 + height += prev->GetRect().height; 1.220 + } 1.221 + 1.222 + return height; 1.223 +} 1.224 + 1.225 +nscoord 1.226 +nsSplittableFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState, 1.227 + nscoord aConsumedHeight) const 1.228 +{ 1.229 + nscoord height = aReflowState.ComputedHeight(); 1.230 + if (height == NS_INTRINSICSIZE) { 1.231 + return NS_INTRINSICSIZE; 1.232 + } 1.233 + 1.234 + if (aConsumedHeight == NS_INTRINSICSIZE) { 1.235 + aConsumedHeight = GetConsumedHeight(); 1.236 + } 1.237 + 1.238 + height -= aConsumedHeight; 1.239 + 1.240 + if (aConsumedHeight != 0 && aConsumedHeight != NS_INTRINSICSIZE) { 1.241 + // We just subtracted our top-border padding, since it was included in the 1.242 + // first frame's height. Add it back to get the content height. 1.243 + height += aReflowState.ComputedPhysicalBorderPadding().top; 1.244 + } 1.245 + 1.246 + // We may have stretched the frame beyond its computed height. Oh well. 1.247 + height = std::max(0, height); 1.248 + 1.249 + return height; 1.250 +} 1.251 + 1.252 +int 1.253 +nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const 1.254 +{ 1.255 + if (IS_TRUE_OVERFLOW_CONTAINER(this)) { 1.256 + return LOGICAL_SIDES_B_BOTH; 1.257 + } 1.258 + 1.259 + int skip = 0; 1.260 + 1.261 + if (GetPrevInFlow()) { 1.262 + skip |= LOGICAL_SIDE_B_START; 1.263 + } 1.264 + 1.265 + if (aReflowState) { 1.266 + // We're in the midst of reflow right now, so it's possible that we haven't 1.267 + // created a nif yet. If our content height is going to exceed our available 1.268 + // height, though, then we're going to need a next-in-flow, it just hasn't 1.269 + // been created yet. 1.270 + 1.271 + if (NS_UNCONSTRAINEDSIZE != aReflowState->AvailableHeight()) { 1.272 + nscoord effectiveCH = this->GetEffectiveComputedHeight(*aReflowState); 1.273 + if (effectiveCH > aReflowState->AvailableHeight()) { 1.274 + // Our content height is going to exceed our available height, so we're 1.275 + // going to need a next-in-flow. 1.276 + skip |= LOGICAL_SIDE_B_END; 1.277 + } 1.278 + } 1.279 + } else { 1.280 + nsIFrame* nif = GetNextInFlow(); 1.281 + if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) { 1.282 + skip |= LOGICAL_SIDE_B_END; 1.283 + } 1.284 + } 1.285 + 1.286 + return skip; 1.287 +} 1.288 + 1.289 +#ifdef DEBUG 1.290 +void 1.291 +nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) 1.292 +{ 1.293 + nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent); 1.294 + if (nullptr != mNextContinuation) { 1.295 + IndentBy(out, aIndent); 1.296 + fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation); 1.297 + } 1.298 + if (nullptr != mPrevContinuation) { 1.299 + IndentBy(out, aIndent); 1.300 + fprintf(out, "<prev-continuation va=\"%p\"/>\n", (void*)mPrevContinuation); 1.301 + } 1.302 + 1.303 +} 1.304 +#endif