layout/generic/nsSplittableFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial