layout/generic/nsLineLayout.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 /* state and methods used while laying out a single line of a block frame */
michael@0 7
michael@0 8 // This has to be defined before nsLineLayout.h is included, because
michael@0 9 // nsLineLayout.h has a #include for plarena.h, which needs this defined:
michael@0 10 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
michael@0 11 #include "nsLineLayout.h"
michael@0 12
michael@0 13 #include "SVGTextFrame.h"
michael@0 14 #include "nsBlockFrame.h"
michael@0 15 #include "nsStyleConsts.h"
michael@0 16 #include "nsContainerFrame.h"
michael@0 17 #include "nsFloatManager.h"
michael@0 18 #include "nsStyleContext.h"
michael@0 19 #include "nsPresContext.h"
michael@0 20 #include "nsRenderingContext.h"
michael@0 21 #include "nsGkAtoms.h"
michael@0 22 #include "nsIContent.h"
michael@0 23 #include "nsLayoutUtils.h"
michael@0 24 #include "nsTextFrame.h"
michael@0 25 #include "nsStyleStructInlines.h"
michael@0 26 #include "nsBidiPresUtils.h"
michael@0 27 #include <algorithm>
michael@0 28
michael@0 29 #ifdef DEBUG
michael@0 30 #undef NOISY_INLINEDIR_ALIGN
michael@0 31 #undef NOISY_BLOCKDIR_ALIGN
michael@0 32 #undef REALLY_NOISY_BLOCKDIR_ALIGN
michael@0 33 #undef NOISY_REFLOW
michael@0 34 #undef REALLY_NOISY_REFLOW
michael@0 35 #undef NOISY_PUSHING
michael@0 36 #undef REALLY_NOISY_PUSHING
michael@0 37 #undef DEBUG_ADD_TEXT
michael@0 38 #undef NOISY_MAX_ELEMENT_SIZE
michael@0 39 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
michael@0 40 #undef NOISY_CAN_PLACE_FRAME
michael@0 41 #undef NOISY_TRIM
michael@0 42 #undef REALLY_NOISY_TRIM
michael@0 43 #endif
michael@0 44
michael@0 45 using namespace mozilla;
michael@0 46
michael@0 47 //----------------------------------------------------------------------
michael@0 48
michael@0 49 #define FIX_BUG_50257
michael@0 50
michael@0 51 nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
michael@0 52 nsFloatManager* aFloatManager,
michael@0 53 const nsHTMLReflowState* aOuterReflowState,
michael@0 54 const nsLineList::iterator* aLine)
michael@0 55 : mPresContext(aPresContext),
michael@0 56 mFloatManager(aFloatManager),
michael@0 57 mBlockReflowState(aOuterReflowState),
michael@0 58 mLastOptionalBreakContent(nullptr),
michael@0 59 mForceBreakContent(nullptr),
michael@0 60 mBlockRS(nullptr),/* XXX temporary */
michael@0 61 mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak),
michael@0 62 mLastOptionalBreakContentOffset(-1),
michael@0 63 mForceBreakContentOffset(-1),
michael@0 64 mMinLineBSize(0),
michael@0 65 mTextIndent(0),
michael@0 66 mFirstLetterStyleOK(false),
michael@0 67 mIsTopOfPage(false),
michael@0 68 mImpactedByFloats(false),
michael@0 69 mLastFloatWasLetterFrame(false),
michael@0 70 mLineIsEmpty(false),
michael@0 71 mLineEndsInBR(false),
michael@0 72 mNeedBackup(false),
michael@0 73 mInFirstLine(false),
michael@0 74 mGotLineBox(false),
michael@0 75 mInFirstLetter(false),
michael@0 76 mHasBullet(false),
michael@0 77 mDirtyNextLine(false),
michael@0 78 mLineAtStart(false)
michael@0 79 {
michael@0 80 MOZ_ASSERT(aOuterReflowState, "aOuterReflowState must not be null");
michael@0 81 NS_ASSERTION(aFloatManager || aOuterReflowState->frame->GetType() ==
michael@0 82 nsGkAtoms::letterFrame,
michael@0 83 "float manager should be present");
michael@0 84 MOZ_COUNT_CTOR(nsLineLayout);
michael@0 85
michael@0 86 // Stash away some style data that we need
michael@0 87 nsBlockFrame* blockFrame = do_QueryFrame(aOuterReflowState->frame);
michael@0 88 if (blockFrame)
michael@0 89 mStyleText = blockFrame->StyleTextForLineLayout();
michael@0 90 else
michael@0 91 mStyleText = aOuterReflowState->frame->StyleText();
michael@0 92
michael@0 93 mLineNumber = 0;
michael@0 94 mTotalPlacedFrames = 0;
michael@0 95 mBStartEdge = 0;
michael@0 96 mTrimmableWidth = 0;
michael@0 97
michael@0 98 mInflationMinFontSize =
michael@0 99 nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState->frame);
michael@0 100
michael@0 101 // Instead of always pre-initializing the free-lists for frames and
michael@0 102 // spans, we do it on demand so that situations that only use a few
michael@0 103 // frames and spans won't waste a lot of time in unneeded
michael@0 104 // initialization.
michael@0 105 PL_INIT_ARENA_POOL(&mArena, "nsLineLayout", 1024);
michael@0 106 mFrameFreeList = nullptr;
michael@0 107 mSpanFreeList = nullptr;
michael@0 108
michael@0 109 mCurrentSpan = mRootSpan = nullptr;
michael@0 110 mSpanDepth = 0;
michael@0 111
michael@0 112 if (aLine) {
michael@0 113 mGotLineBox = true;
michael@0 114 mLineBox = *aLine;
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 nsLineLayout::~nsLineLayout()
michael@0 119 {
michael@0 120 MOZ_COUNT_DTOR(nsLineLayout);
michael@0 121
michael@0 122 NS_ASSERTION(nullptr == mRootSpan, "bad line-layout user");
michael@0 123
michael@0 124 PL_FinishArenaPool(&mArena);
michael@0 125 }
michael@0 126
michael@0 127 // Find out if the frame has a non-null prev-in-flow, i.e., whether it
michael@0 128 // is a continuation.
michael@0 129 inline bool
michael@0 130 HasPrevInFlow(nsIFrame *aFrame)
michael@0 131 {
michael@0 132 nsIFrame *prevInFlow = aFrame->GetPrevInFlow();
michael@0 133 return prevInFlow != nullptr;
michael@0 134 }
michael@0 135
michael@0 136 void
michael@0 137 nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
michael@0 138 nscoord aISize, nscoord aBSize,
michael@0 139 bool aImpactedByFloats,
michael@0 140 bool aIsTopOfPage,
michael@0 141 WritingMode aWritingMode,
michael@0 142 nscoord aContainerWidth)
michael@0 143 {
michael@0 144 NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user");
michael@0 145 NS_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE,
michael@0 146 "have unconstrained width; this should only result from "
michael@0 147 "very large sizes, not attempts at intrinsic width "
michael@0 148 "calculation");
michael@0 149 #ifdef DEBUG
michael@0 150 if ((aISize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aISize)) {
michael@0 151 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 152 printf(": Init: bad caller: width WAS %d(0x%x)\n",
michael@0 153 aISize, aISize);
michael@0 154 }
michael@0 155 if ((aBSize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aBSize)) {
michael@0 156 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 157 printf(": Init: bad caller: height WAS %d(0x%x)\n",
michael@0 158 aBSize, aBSize);
michael@0 159 }
michael@0 160 #endif
michael@0 161 #ifdef NOISY_REFLOW
michael@0 162 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 163 printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
michael@0 164 aICoord, aBCoord, aISize, aBSize,
michael@0 165 aImpactedByFloats?"true":"false",
michael@0 166 aIsTopOfPage ? "top-of-page" : "");
michael@0 167 #endif
michael@0 168 #ifdef DEBUG
michael@0 169 mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0;
michael@0 170 #endif
michael@0 171
michael@0 172 mFirstLetterStyleOK = false;
michael@0 173 mIsTopOfPage = aIsTopOfPage;
michael@0 174 mImpactedByFloats = aImpactedByFloats;
michael@0 175 mTotalPlacedFrames = 0;
michael@0 176 mLineIsEmpty = true;
michael@0 177 mLineAtStart = true;
michael@0 178 mLineEndsInBR = false;
michael@0 179 mSpanDepth = 0;
michael@0 180 mMaxStartBoxBSize = mMaxEndBoxBSize = 0;
michael@0 181
michael@0 182 if (mGotLineBox) {
michael@0 183 mLineBox->ClearHasBullet();
michael@0 184 }
michael@0 185
michael@0 186 PerSpanData* psd = NewPerSpanData();
michael@0 187 mCurrentSpan = mRootSpan = psd;
michael@0 188 psd->mReflowState = mBlockReflowState;
michael@0 189 psd->mIStart = aICoord;
michael@0 190 psd->mICoord = aICoord;
michael@0 191 psd->mIEnd = aICoord + aISize;
michael@0 192 mContainerWidth = aContainerWidth;
michael@0 193
michael@0 194 // If we're in a constrained height frame, then we don't allow a
michael@0 195 // max line box width to take effect.
michael@0 196 if (!(LineContainerFrame()->GetStateBits() &
michael@0 197 NS_FRAME_IN_CONSTRAINED_HEIGHT)) {
michael@0 198
michael@0 199 // If the available size is greater than the maximum line box width (if
michael@0 200 // specified), then we need to adjust the line box width to be at the max
michael@0 201 // possible width.
michael@0 202 nscoord maxLineBoxWidth =
michael@0 203 LineContainerFrame()->PresContext()->PresShell()->MaxLineBoxWidth();
michael@0 204
michael@0 205 if (maxLineBoxWidth > 0 &&
michael@0 206 psd->mIEnd - psd->mIStart > maxLineBoxWidth) {
michael@0 207 psd->mIEnd = psd->mIStart + maxLineBoxWidth;
michael@0 208 }
michael@0 209 }
michael@0 210
michael@0 211 mBStartEdge = aBCoord;
michael@0 212
michael@0 213 psd->mNoWrap =
michael@0 214 !mStyleText->WhiteSpaceCanWrapStyle() || LineContainerFrame()->IsSVGText();
michael@0 215 psd->mWritingMode = aWritingMode;
michael@0 216
michael@0 217 // If this is the first line of a block then see if the text-indent
michael@0 218 // property amounts to anything.
michael@0 219
michael@0 220 if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowState->frame)) {
michael@0 221 const nsStyleCoord &textIndent = mStyleText->mTextIndent;
michael@0 222 nscoord pctBasis = 0;
michael@0 223 if (textIndent.HasPercent()) {
michael@0 224 pctBasis =
michael@0 225 nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState);
michael@0 226
michael@0 227 if (mGotLineBox) {
michael@0 228 mLineBox->DisableResizeReflowOptimization();
michael@0 229 }
michael@0 230 }
michael@0 231 nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
michael@0 232
michael@0 233 mTextIndent = indent;
michael@0 234
michael@0 235 psd->mICoord += indent;
michael@0 236 }
michael@0 237 }
michael@0 238
michael@0 239 void
michael@0 240 nsLineLayout::EndLineReflow()
michael@0 241 {
michael@0 242 #ifdef NOISY_REFLOW
michael@0 243 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 244 printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart);
michael@0 245 #endif
michael@0 246
michael@0 247 FreeSpan(mRootSpan);
michael@0 248 mCurrentSpan = mRootSpan = nullptr;
michael@0 249
michael@0 250 NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak");
michael@0 251 NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak");
michael@0 252
michael@0 253 #if 0
michael@0 254 static int32_t maxSpansAllocated = NS_LINELAYOUT_NUM_SPANS;
michael@0 255 static int32_t maxFramesAllocated = NS_LINELAYOUT_NUM_FRAMES;
michael@0 256 if (mSpansAllocated > maxSpansAllocated) {
michael@0 257 printf("XXX: saw a line with %d spans\n", mSpansAllocated);
michael@0 258 maxSpansAllocated = mSpansAllocated;
michael@0 259 }
michael@0 260 if (mFramesAllocated > maxFramesAllocated) {
michael@0 261 printf("XXX: saw a line with %d frames\n", mFramesAllocated);
michael@0 262 maxFramesAllocated = mFramesAllocated;
michael@0 263 }
michael@0 264 #endif
michael@0 265 }
michael@0 266
michael@0 267 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
michael@0 268 // on the line is placed. Each span can still have a per-span mICoord that
michael@0 269 // tracks where a child frame is going in its span; they don't need a
michael@0 270 // per-span mIStart?
michael@0 271
michael@0 272 void
michael@0 273 nsLineLayout::UpdateBand(const nsRect& aNewAvailSpace,
michael@0 274 nsIFrame* aFloatFrame)
michael@0 275 {
michael@0 276 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 277 LogicalRect availSpace(lineWM, aNewAvailSpace, mContainerWidth);
michael@0 278 #ifdef REALLY_NOISY_REFLOW
michael@0 279 printf("nsLL::UpdateBand %d, %d, %d, %d, (logical %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n",
michael@0 280 aNewAvailSpace.x, aNewAvailSpace.y,
michael@0 281 aNewAvailSpace.width, aNewAvailSpace.height,
michael@0 282 availSpace.IStart(lineWM), availSpace.BStart(lineWM),
michael@0 283 availSpace.ISize(lineWM), availSpace.BSize(lineWM),
michael@0 284 aFloatFrame);
michael@0 285 #endif
michael@0 286 #ifdef DEBUG
michael@0 287 if ((availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE) &&
michael@0 288 CRAZY_SIZE(availSpace.ISize(lineWM))) {
michael@0 289 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 290 printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n",
michael@0 291 availSpace.ISize(lineWM), availSpace.ISize(lineWM));
michael@0 292 }
michael@0 293 if ((availSpace.BSize(lineWM) != NS_UNCONSTRAINEDSIZE) &&
michael@0 294 CRAZY_SIZE(availSpace.BSize(lineWM))) {
michael@0 295 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 296 printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n",
michael@0 297 availSpace.BSize(lineWM), availSpace.BSize(lineWM));
michael@0 298 }
michael@0 299 #endif
michael@0 300
michael@0 301 // Compute the difference between last times width and the new width
michael@0 302 NS_WARN_IF_FALSE(mRootSpan->mIEnd != NS_UNCONSTRAINEDSIZE &&
michael@0 303 aNewAvailSpace.width != NS_UNCONSTRAINEDSIZE,
michael@0 304 "have unconstrained width; this should only result from "
michael@0 305 "very large sizes, not attempts at intrinsic width "
michael@0 306 "calculation");
michael@0 307 // The root span's mIStart moves to aICoord
michael@0 308 nscoord deltaICoord = availSpace.IStart(lineWM) - mRootSpan->mIStart;
michael@0 309 // The width of all spans changes by this much (the root span's
michael@0 310 // mIEnd moves to aICoord + aISize, its new width is aISize)
michael@0 311 nscoord deltaISize = availSpace.ISize(lineWM) -
michael@0 312 (mRootSpan->mIEnd - mRootSpan->mIStart);
michael@0 313 #ifdef NOISY_REFLOW
michael@0 314 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 315 printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n",
michael@0 316 aNewAvailSpace.x, aNewAvailSpace.y,
michael@0 317 aNewAvailSpace.width, aNewAvailSpace.height, deltaISize, deltaICoord);
michael@0 318 #endif
michael@0 319
michael@0 320 // Update the root span position
michael@0 321 mRootSpan->mIStart += deltaICoord;
michael@0 322 mRootSpan->mIEnd += deltaICoord;
michael@0 323 mRootSpan->mICoord += deltaICoord;
michael@0 324
michael@0 325 // Now update the right edges of the open spans to account for any
michael@0 326 // change in available space width
michael@0 327 for (PerSpanData* psd = mCurrentSpan; psd; psd = psd->mParent) {
michael@0 328 psd->mIEnd += deltaISize;
michael@0 329 psd->mContainsFloat = true;
michael@0 330 #ifdef NOISY_REFLOW
michael@0 331 printf(" span %p: oldIEnd=%d newIEnd=%d\n",
michael@0 332 psd, psd->mIEnd - deltaISize, psd->mIEnd);
michael@0 333 #endif
michael@0 334 }
michael@0 335 NS_ASSERTION(mRootSpan->mContainsFloat &&
michael@0 336 mRootSpan->mIStart == availSpace.IStart(lineWM) &&
michael@0 337 mRootSpan->mIEnd == availSpace.IEnd(lineWM),
michael@0 338 "root span was updated incorrectly?");
michael@0 339
michael@0 340 // Update frame bounds
michael@0 341 // Note: Only adjust the outermost frames (the ones that are direct
michael@0 342 // children of the block), not the ones in the child spans. The reason
michael@0 343 // is simple: the frames in the spans have coordinates local to their
michael@0 344 // parent therefore they are moved when their parent span is moved.
michael@0 345 if (deltaICoord != 0) {
michael@0 346 for (PerFrameData* pfd = mRootSpan->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 347 pfd->mBounds.IStart(lineWM) += deltaICoord;
michael@0 348 }
michael@0 349 }
michael@0 350
michael@0 351 mBStartEdge = aNewAvailSpace.y;
michael@0 352 mImpactedByFloats = true;
michael@0 353
michael@0 354 mLastFloatWasLetterFrame = nsGkAtoms::letterFrame == aFloatFrame->GetType();
michael@0 355 }
michael@0 356
michael@0 357 nsLineLayout::PerSpanData*
michael@0 358 nsLineLayout::NewPerSpanData()
michael@0 359 {
michael@0 360 PerSpanData* psd = mSpanFreeList;
michael@0 361 if (!psd) {
michael@0 362 void *mem;
michael@0 363 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerSpanData));
michael@0 364 if (!mem) {
michael@0 365 NS_RUNTIMEABORT("OOM");
michael@0 366 }
michael@0 367 psd = reinterpret_cast<PerSpanData*>(mem);
michael@0 368 }
michael@0 369 else {
michael@0 370 mSpanFreeList = psd->mNextFreeSpan;
michael@0 371 }
michael@0 372 psd->mParent = nullptr;
michael@0 373 psd->mFrame = nullptr;
michael@0 374 psd->mFirstFrame = nullptr;
michael@0 375 psd->mLastFrame = nullptr;
michael@0 376 psd->mContainsFloat = false;
michael@0 377 psd->mZeroEffectiveSpanBox = false;
michael@0 378 psd->mHasNonemptyContent = false;
michael@0 379
michael@0 380 #ifdef DEBUG
michael@0 381 mSpansAllocated++;
michael@0 382 #endif
michael@0 383 return psd;
michael@0 384 }
michael@0 385
michael@0 386 void
michael@0 387 nsLineLayout::BeginSpan(nsIFrame* aFrame,
michael@0 388 const nsHTMLReflowState* aSpanReflowState,
michael@0 389 nscoord aIStart, nscoord aIEnd,
michael@0 390 nscoord* aBaseline)
michael@0 391 {
michael@0 392 NS_ASSERTION(aIEnd != NS_UNCONSTRAINEDSIZE,
michael@0 393 "should no longer be using unconstrained sizes");
michael@0 394 #ifdef NOISY_REFLOW
michael@0 395 nsFrame::IndentBy(stdout, mSpanDepth+1);
michael@0 396 nsFrame::ListTag(stdout, aFrame);
michael@0 397 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart, aIEnd);
michael@0 398 #endif
michael@0 399
michael@0 400 PerSpanData* psd = NewPerSpanData();
michael@0 401 // Link up span frame's pfd to point to its child span data
michael@0 402 PerFrameData* pfd = mCurrentSpan->mLastFrame;
michael@0 403 NS_ASSERTION(pfd->mFrame == aFrame, "huh?");
michael@0 404 pfd->mSpan = psd;
michael@0 405
michael@0 406 // Init new span
michael@0 407 psd->mFrame = pfd;
michael@0 408 psd->mParent = mCurrentSpan;
michael@0 409 psd->mReflowState = aSpanReflowState;
michael@0 410 psd->mIStart = aIStart;
michael@0 411 psd->mICoord = aIStart;
michael@0 412 psd->mIEnd = aIEnd;
michael@0 413 psd->mBaseline = aBaseline;
michael@0 414
michael@0 415 nsIFrame* frame = aSpanReflowState->frame;
michael@0 416 psd->mNoWrap = !frame->StyleText()->WhiteSpaceCanWrap(frame);
michael@0 417 psd->mWritingMode = aSpanReflowState->GetWritingMode();
michael@0 418
michael@0 419 // Switch to new span
michael@0 420 mCurrentSpan = psd;
michael@0 421 mSpanDepth++;
michael@0 422 }
michael@0 423
michael@0 424 nscoord
michael@0 425 nsLineLayout::EndSpan(nsIFrame* aFrame)
michael@0 426 {
michael@0 427 NS_ASSERTION(mSpanDepth > 0, "end-span without begin-span");
michael@0 428 #ifdef NOISY_REFLOW
michael@0 429 nsFrame::IndentBy(stdout, mSpanDepth);
michael@0 430 nsFrame::ListTag(stdout, aFrame);
michael@0 431 printf(": EndSpan width=%d\n", mCurrentSpan->mICoord - mCurrentSpan->mIStart);
michael@0 432 #endif
michael@0 433 PerSpanData* psd = mCurrentSpan;
michael@0 434 nscoord iSizeResult = psd->mLastFrame ? (psd->mICoord - psd->mIStart) : 0;
michael@0 435
michael@0 436 mSpanDepth--;
michael@0 437 mCurrentSpan->mReflowState = nullptr; // no longer valid so null it out!
michael@0 438 mCurrentSpan = mCurrentSpan->mParent;
michael@0 439 return iSizeResult;
michael@0 440 }
michael@0 441
michael@0 442 int32_t
michael@0 443 nsLineLayout::GetCurrentSpanCount() const
michael@0 444 {
michael@0 445 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
michael@0 446 int32_t count = 0;
michael@0 447 PerFrameData* pfd = mRootSpan->mFirstFrame;
michael@0 448 while (nullptr != pfd) {
michael@0 449 count++;
michael@0 450 pfd = pfd->mNext;
michael@0 451 }
michael@0 452 return count;
michael@0 453 }
michael@0 454
michael@0 455 void
michael@0 456 nsLineLayout::SplitLineTo(int32_t aNewCount)
michael@0 457 {
michael@0 458 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
michael@0 459
michael@0 460 #ifdef REALLY_NOISY_PUSHING
michael@0 461 printf("SplitLineTo %d (current count=%d); before:\n", aNewCount,
michael@0 462 GetCurrentSpanCount());
michael@0 463 DumpPerSpanData(mRootSpan, 1);
michael@0 464 #endif
michael@0 465 PerSpanData* psd = mRootSpan;
michael@0 466 PerFrameData* pfd = psd->mFirstFrame;
michael@0 467 while (nullptr != pfd) {
michael@0 468 if (--aNewCount == 0) {
michael@0 469 // Truncate list at pfd (we keep pfd, but anything following is freed)
michael@0 470 PerFrameData* next = pfd->mNext;
michael@0 471 pfd->mNext = nullptr;
michael@0 472 psd->mLastFrame = pfd;
michael@0 473
michael@0 474 // Now release all of the frames following pfd
michael@0 475 pfd = next;
michael@0 476 while (nullptr != pfd) {
michael@0 477 next = pfd->mNext;
michael@0 478 pfd->mNext = mFrameFreeList;
michael@0 479 mFrameFreeList = pfd;
michael@0 480 #ifdef DEBUG
michael@0 481 mFramesFreed++;
michael@0 482 #endif
michael@0 483 if (nullptr != pfd->mSpan) {
michael@0 484 FreeSpan(pfd->mSpan);
michael@0 485 }
michael@0 486 pfd = next;
michael@0 487 }
michael@0 488 break;
michael@0 489 }
michael@0 490 pfd = pfd->mNext;
michael@0 491 }
michael@0 492 #ifdef NOISY_PUSHING
michael@0 493 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount,
michael@0 494 GetCurrentSpanCount());
michael@0 495 DumpPerSpanData(mRootSpan, 1);
michael@0 496 #endif
michael@0 497 }
michael@0 498
michael@0 499 void
michael@0 500 nsLineLayout::PushFrame(nsIFrame* aFrame)
michael@0 501 {
michael@0 502 PerSpanData* psd = mCurrentSpan;
michael@0 503 NS_ASSERTION(psd->mLastFrame->mFrame == aFrame, "pushing non-last frame");
michael@0 504
michael@0 505 #ifdef REALLY_NOISY_PUSHING
michael@0 506 nsFrame::IndentBy(stdout, mSpanDepth);
michael@0 507 printf("PushFrame %p, before:\n", psd);
michael@0 508 DumpPerSpanData(psd, 1);
michael@0 509 #endif
michael@0 510
michael@0 511 // Take the last frame off of the span's frame list
michael@0 512 PerFrameData* pfd = psd->mLastFrame;
michael@0 513 if (pfd == psd->mFirstFrame) {
michael@0 514 // We are pushing away the only frame...empty the list
michael@0 515 psd->mFirstFrame = nullptr;
michael@0 516 psd->mLastFrame = nullptr;
michael@0 517 }
michael@0 518 else {
michael@0 519 PerFrameData* prevFrame = pfd->mPrev;
michael@0 520 prevFrame->mNext = nullptr;
michael@0 521 psd->mLastFrame = prevFrame;
michael@0 522 }
michael@0 523
michael@0 524 // Now free it, and if it has a span, free that too
michael@0 525 pfd->mNext = mFrameFreeList;
michael@0 526 mFrameFreeList = pfd;
michael@0 527 #ifdef DEBUG
michael@0 528 mFramesFreed++;
michael@0 529 #endif
michael@0 530 if (nullptr != pfd->mSpan) {
michael@0 531 FreeSpan(pfd->mSpan);
michael@0 532 }
michael@0 533 #ifdef NOISY_PUSHING
michael@0 534 nsFrame::IndentBy(stdout, mSpanDepth);
michael@0 535 printf("PushFrame: %p after:\n", psd);
michael@0 536 DumpPerSpanData(psd, 1);
michael@0 537 #endif
michael@0 538 }
michael@0 539
michael@0 540 void
michael@0 541 nsLineLayout::FreeSpan(PerSpanData* psd)
michael@0 542 {
michael@0 543 // Free its frames
michael@0 544 PerFrameData* pfd = psd->mFirstFrame;
michael@0 545 while (nullptr != pfd) {
michael@0 546 if (nullptr != pfd->mSpan) {
michael@0 547 FreeSpan(pfd->mSpan);
michael@0 548 }
michael@0 549 PerFrameData* next = pfd->mNext;
michael@0 550 pfd->mNext = mFrameFreeList;
michael@0 551 mFrameFreeList = pfd;
michael@0 552 #ifdef DEBUG
michael@0 553 mFramesFreed++;
michael@0 554 #endif
michael@0 555 pfd = next;
michael@0 556 }
michael@0 557
michael@0 558 // Now put the span on the free list since it's free too
michael@0 559 psd->mNextFreeSpan = mSpanFreeList;
michael@0 560 mSpanFreeList = psd;
michael@0 561 #ifdef DEBUG
michael@0 562 mSpansFreed++;
michael@0 563 #endif
michael@0 564 }
michael@0 565
michael@0 566 bool
michael@0 567 nsLineLayout::IsZeroBSize()
michael@0 568 {
michael@0 569 PerSpanData* psd = mCurrentSpan;
michael@0 570 PerFrameData* pfd = psd->mFirstFrame;
michael@0 571 while (nullptr != pfd) {
michael@0 572 if (0 != pfd->mBounds.BSize(psd->mWritingMode)) {
michael@0 573 return false;
michael@0 574 }
michael@0 575 pfd = pfd->mNext;
michael@0 576 }
michael@0 577 return true;
michael@0 578 }
michael@0 579
michael@0 580 nsLineLayout::PerFrameData*
michael@0 581 nsLineLayout::NewPerFrameData(nsIFrame* aFrame)
michael@0 582 {
michael@0 583 PerFrameData* pfd = mFrameFreeList;
michael@0 584 if (!pfd) {
michael@0 585 void *mem;
michael@0 586 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerFrameData));
michael@0 587 if (!mem) {
michael@0 588 NS_RUNTIMEABORT("OOM");
michael@0 589 }
michael@0 590 pfd = reinterpret_cast<PerFrameData*>(mem);
michael@0 591 }
michael@0 592 else {
michael@0 593 mFrameFreeList = pfd->mNext;
michael@0 594 }
michael@0 595 pfd->mSpan = nullptr;
michael@0 596 pfd->mNext = nullptr;
michael@0 597 pfd->mPrev = nullptr;
michael@0 598 pfd->mFlags = 0; // all flags default to false
michael@0 599 pfd->mFrame = aFrame;
michael@0 600
michael@0 601 WritingMode frameWM = aFrame->GetWritingMode();
michael@0 602 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 603 pfd->mBounds = LogicalRect(lineWM);
michael@0 604 pfd->mMargin = LogicalMargin(frameWM);
michael@0 605 pfd->mBorderPadding = LogicalMargin(frameWM);
michael@0 606 pfd->mOffsets = LogicalMargin(frameWM);
michael@0 607
michael@0 608 #ifdef DEBUG
michael@0 609 pfd->mBlockDirAlign = 0xFF;
michael@0 610 mFramesAllocated++;
michael@0 611 #endif
michael@0 612 return pfd;
michael@0 613 }
michael@0 614
michael@0 615 bool
michael@0 616 nsLineLayout::LineIsBreakable() const
michael@0 617 {
michael@0 618 // XXX mTotalPlacedFrames should go away and we should just use
michael@0 619 // mLineIsEmpty here instead
michael@0 620 if ((0 != mTotalPlacedFrames) || mImpactedByFloats) {
michael@0 621 return true;
michael@0 622 }
michael@0 623 return false;
michael@0 624 }
michael@0 625
michael@0 626 // Checks all four sides for percentage units. This means it should
michael@0 627 // only be used for things (margin, padding) where percentages on top
michael@0 628 // and bottom depend on the *width* just like percentages on left and
michael@0 629 // right.
michael@0 630 static bool
michael@0 631 HasPercentageUnitSide(const nsStyleSides& aSides)
michael@0 632 {
michael@0 633 NS_FOR_CSS_SIDES(side) {
michael@0 634 if (aSides.Get(side).HasPercent())
michael@0 635 return true;
michael@0 636 }
michael@0 637 return false;
michael@0 638 }
michael@0 639
michael@0 640 static bool
michael@0 641 IsPercentageAware(const nsIFrame* aFrame)
michael@0 642 {
michael@0 643 NS_ASSERTION(aFrame, "null frame is not allowed");
michael@0 644
michael@0 645 nsIAtom *fType = aFrame->GetType();
michael@0 646 if (fType == nsGkAtoms::textFrame) {
michael@0 647 // None of these things can ever be true for text frames.
michael@0 648 return false;
michael@0 649 }
michael@0 650
michael@0 651 // Some of these things don't apply to non-replaced inline frames
michael@0 652 // (that is, fType == nsGkAtoms::inlineFrame), but we won't bother making
michael@0 653 // things unnecessarily complicated, since they'll probably be set
michael@0 654 // quite rarely.
michael@0 655
michael@0 656 const nsStyleMargin* margin = aFrame->StyleMargin();
michael@0 657 if (HasPercentageUnitSide(margin->mMargin)) {
michael@0 658 return true;
michael@0 659 }
michael@0 660
michael@0 661 const nsStylePadding* padding = aFrame->StylePadding();
michael@0 662 if (HasPercentageUnitSide(padding->mPadding)) {
michael@0 663 return true;
michael@0 664 }
michael@0 665
michael@0 666 // Note that borders can't be aware of percentages
michael@0 667
michael@0 668 const nsStylePosition* pos = aFrame->StylePosition();
michael@0 669
michael@0 670 if ((pos->WidthDependsOnContainer() &&
michael@0 671 pos->mWidth.GetUnit() != eStyleUnit_Auto) ||
michael@0 672 pos->MaxWidthDependsOnContainer() ||
michael@0 673 pos->MinWidthDependsOnContainer() ||
michael@0 674 pos->OffsetHasPercent(NS_SIDE_RIGHT) ||
michael@0 675 pos->OffsetHasPercent(NS_SIDE_LEFT)) {
michael@0 676 return true;
michael@0 677 }
michael@0 678
michael@0 679 if (eStyleUnit_Auto == pos->mWidth.GetUnit()) {
michael@0 680 // We need to check for frames that shrink-wrap when they're auto
michael@0 681 // width.
michael@0 682 const nsStyleDisplay* disp = aFrame->StyleDisplay();
michael@0 683 if (disp->mDisplay == NS_STYLE_DISPLAY_INLINE_BLOCK ||
michael@0 684 disp->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE ||
michael@0 685 fType == nsGkAtoms::HTMLButtonControlFrame ||
michael@0 686 fType == nsGkAtoms::gfxButtonControlFrame ||
michael@0 687 fType == nsGkAtoms::fieldSetFrame ||
michael@0 688 fType == nsGkAtoms::comboboxDisplayFrame) {
michael@0 689 return true;
michael@0 690 }
michael@0 691
michael@0 692 // Per CSS 2.1, section 10.3.2:
michael@0 693 // If 'height' and 'width' both have computed values of 'auto' and
michael@0 694 // the element has an intrinsic ratio but no intrinsic height or
michael@0 695 // width and the containing block's width does not itself depend
michael@0 696 // on the replaced element's width, then the used value of 'width'
michael@0 697 // is calculated from the constraint equation used for
michael@0 698 // block-level, non-replaced elements in normal flow.
michael@0 699 nsIFrame *f = const_cast<nsIFrame*>(aFrame);
michael@0 700 if (f->GetIntrinsicRatio() != nsSize(0, 0) &&
michael@0 701 // Some percents are treated like 'auto', so check != coord
michael@0 702 pos->mHeight.GetUnit() != eStyleUnit_Coord) {
michael@0 703 const IntrinsicSize &intrinsicSize = f->GetIntrinsicSize();
michael@0 704 if (intrinsicSize.width.GetUnit() == eStyleUnit_None &&
michael@0 705 intrinsicSize.height.GetUnit() == eStyleUnit_None) {
michael@0 706 return true;
michael@0 707 }
michael@0 708 }
michael@0 709 }
michael@0 710
michael@0 711 return false;
michael@0 712 }
michael@0 713
michael@0 714 nsresult
michael@0 715 nsLineLayout::ReflowFrame(nsIFrame* aFrame,
michael@0 716 nsReflowStatus& aReflowStatus,
michael@0 717 nsHTMLReflowMetrics* aMetrics,
michael@0 718 bool& aPushedFrame)
michael@0 719 {
michael@0 720 // Initialize OUT parameter
michael@0 721 aPushedFrame = false;
michael@0 722
michael@0 723 PerFrameData* pfd = NewPerFrameData(aFrame);
michael@0 724 PerSpanData* psd = mCurrentSpan;
michael@0 725 psd->AppendFrame(pfd);
michael@0 726
michael@0 727 #ifdef REALLY_NOISY_REFLOW
michael@0 728 nsFrame::IndentBy(stdout, mSpanDepth);
michael@0 729 printf("%p: Begin ReflowFrame pfd=%p ", psd, pfd);
michael@0 730 nsFrame::ListTag(stdout, aFrame);
michael@0 731 printf("\n");
michael@0 732 #endif
michael@0 733
michael@0 734 mTextJustificationNumSpaces = 0;
michael@0 735 mTextJustificationNumLetters = 0;
michael@0 736
michael@0 737 // Stash copies of some of the computed state away for later
michael@0 738 // (block-direction alignment, for example)
michael@0 739 WritingMode frameWM = aFrame->GetWritingMode();
michael@0 740 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 741
michael@0 742 // NOTE: While the x coordinate remains relative to the parent span,
michael@0 743 // the y coordinate is fixed at the top edge for the line. During
michael@0 744 // BlockDirAlignFrames we will repair this so that the y coordinate
michael@0 745 // is properly set and relative to the appropriate span.
michael@0 746 pfd->mBounds.IStart(lineWM) = psd->mICoord;
michael@0 747 pfd->mBounds.BStart(lineWM) = mBStartEdge;
michael@0 748
michael@0 749 // We want to guarantee that we always make progress when
michael@0 750 // formatting. Therefore, if the object being placed on the line is
michael@0 751 // too big for the line, but it is the only thing on the line and is not
michael@0 752 // impacted by a float, then we go ahead and place it anyway. (If the line
michael@0 753 // is impacted by one or more floats, then it is safe to break because
michael@0 754 // we can move the line down below float(s).)
michael@0 755 //
michael@0 756 // Capture this state *before* we reflow the frame in case it clears
michael@0 757 // the state out. We need to know how to treat the current frame
michael@0 758 // when breaking.
michael@0 759 bool notSafeToBreak = LineIsEmpty() && !mImpactedByFloats;
michael@0 760
michael@0 761 // Figure out whether we're talking about a textframe here
michael@0 762 nsIAtom* frameType = aFrame->GetType();
michael@0 763 bool isText = frameType == nsGkAtoms::textFrame;
michael@0 764
michael@0 765 // Compute the available size for the frame. This available width
michael@0 766 // includes room for the side margins.
michael@0 767 // For now, set the available height to unconstrained always.
michael@0 768 nsSize availSize(mBlockReflowState->ComputedWidth(), NS_UNCONSTRAINEDSIZE);
michael@0 769
michael@0 770 // Inline-ish and text-ish things don't compute their width;
michael@0 771 // everything else does. We need to give them an available width that
michael@0 772 // reflects the space left on the line.
michael@0 773 NS_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE,
michael@0 774 "have unconstrained width; this should only result from "
michael@0 775 "very large sizes, not attempts at intrinsic width "
michael@0 776 "calculation");
michael@0 777 nscoord availableSpaceOnLine = psd->mIEnd - psd->mICoord;
michael@0 778
michael@0 779 // Setup reflow state for reflowing the frame
michael@0 780 Maybe<nsHTMLReflowState> reflowStateHolder;
michael@0 781 if (!isText) {
michael@0 782 reflowStateHolder.construct(mPresContext, *psd->mReflowState,
michael@0 783 aFrame, availSize);
michael@0 784 nsHTMLReflowState& reflowState = reflowStateHolder.ref();
michael@0 785 reflowState.mLineLayout = this;
michael@0 786 reflowState.mFlags.mIsTopOfPage = mIsTopOfPage;
michael@0 787 if (reflowState.ComputedWidth() == NS_UNCONSTRAINEDSIZE)
michael@0 788 reflowState.AvailableWidth() = availableSpaceOnLine;
michael@0 789 WritingMode stateWM = reflowState.GetWritingMode();
michael@0 790 pfd->mMargin =
michael@0 791 reflowState.ComputedLogicalMargin().ConvertTo(frameWM, stateWM);
michael@0 792 pfd->mBorderPadding =
michael@0 793 reflowState.ComputedLogicalBorderPadding().ConvertTo(frameWM, stateWM);
michael@0 794 pfd->SetFlag(PFD_RELATIVEPOS,
michael@0 795 reflowState.mStyleDisplay->IsRelativelyPositionedStyle());
michael@0 796 if (pfd->GetFlag(PFD_RELATIVEPOS)) {
michael@0 797 pfd->mOffsets =
michael@0 798 reflowState.ComputedLogicalOffsets().ConvertTo(frameWM, stateWM);
michael@0 799 }
michael@0 800
michael@0 801 // Calculate whether the the frame should have a start margin and
michael@0 802 // subtract the margin from the available width if necessary.
michael@0 803 // The margin will be applied to the starting inline coordinates of
michael@0 804 // the frame in CanPlaceFrame() after reflowing the frame.
michael@0 805 AllowForStartMargin(pfd, reflowState);
michael@0 806 }
michael@0 807 // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent,
michael@0 808 // because reflow doesn't look at the dirty bits on the frame being reflowed.
michael@0 809
michael@0 810 // See if this frame depends on the width of its containing block. If
michael@0 811 // so, disable resize reflow optimizations for the line. (Note that,
michael@0 812 // to be conservative, we do this if we *try* to fit a frame on a
michael@0 813 // line, even if we don't succeed.) (Note also that we can only make
michael@0 814 // this IsPercentageAware check *after* we've constructed our
michael@0 815 // nsHTMLReflowState, because that construction may be what forces aFrame
michael@0 816 // to lazily initialize its (possibly-percent-valued) intrinsic size.)
michael@0 817 if (mGotLineBox && IsPercentageAware(aFrame)) {
michael@0 818 mLineBox->DisableResizeReflowOptimization();
michael@0 819 }
michael@0 820
michael@0 821 // Let frame know that are reflowing it. Note that we don't bother
michael@0 822 // positioning the frame yet, because we're probably going to end up
michael@0 823 // moving it when we do the block-direction alignment
michael@0 824 aFrame->WillReflow(mPresContext);
michael@0 825
michael@0 826 // Adjust spacemanager coordinate system for the frame.
michael@0 827 nsHTMLReflowMetrics metrics(lineWM);
michael@0 828 #ifdef DEBUG
michael@0 829 metrics.Width() = nscoord(0xdeadbeef);
michael@0 830 metrics.Height() = nscoord(0xdeadbeef);
michael@0 831 #endif
michael@0 832 nsRect physicalBounds = pfd->mBounds.GetPhysicalRect(lineWM, mContainerWidth);
michael@0 833 nscoord tx = physicalBounds.x;
michael@0 834 nscoord ty = physicalBounds.y;
michael@0 835 mFloatManager->Translate(tx, ty);
michael@0 836
michael@0 837 int32_t savedOptionalBreakOffset;
michael@0 838 gfxBreakPriority savedOptionalBreakPriority;
michael@0 839 nsIContent* savedOptionalBreakContent =
michael@0 840 GetLastOptionalBreakPosition(&savedOptionalBreakOffset,
michael@0 841 &savedOptionalBreakPriority);
michael@0 842
michael@0 843 if (!isText) {
michael@0 844 nsresult rv = aFrame->Reflow(mPresContext, metrics, reflowStateHolder.ref(),
michael@0 845 aReflowStatus);
michael@0 846 if (NS_FAILED(rv)) {
michael@0 847 NS_WARNING( "Reflow of frame failed in nsLineLayout" );
michael@0 848 return rv;
michael@0 849 }
michael@0 850 } else {
michael@0 851 static_cast<nsTextFrame*>(aFrame)->
michael@0 852 ReflowText(*this, availableSpaceOnLine, psd->mReflowState->rendContext,
michael@0 853 metrics, aReflowStatus);
michael@0 854 }
michael@0 855
michael@0 856 pfd->mJustificationNumSpaces = mTextJustificationNumSpaces;
michael@0 857 pfd->mJustificationNumLetters = mTextJustificationNumLetters;
michael@0 858
michael@0 859 // See if the frame is a placeholderFrame and if it is process
michael@0 860 // the float. At the same time, check if the frame has any non-collapsed-away
michael@0 861 // content.
michael@0 862 bool placedFloat = false;
michael@0 863 bool isEmpty;
michael@0 864 if (!frameType) {
michael@0 865 isEmpty = pfd->mFrame->IsEmpty();
michael@0 866 } else {
michael@0 867 if (nsGkAtoms::placeholderFrame == frameType) {
michael@0 868 isEmpty = true;
michael@0 869 pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true);
michael@0 870 nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame);
michael@0 871 if (outOfFlowFrame) {
michael@0 872 // Add mTrimmableWidth to the available width since if the line ends
michael@0 873 // here, the width of the inline content will be reduced by
michael@0 874 // mTrimmableWidth.
michael@0 875 nscoord availableWidth = psd->mIEnd - (psd->mICoord - mTrimmableWidth);
michael@0 876 if (psd->mNoWrap) {
michael@0 877 // If we place floats after inline content where there's
michael@0 878 // no break opportunity, we don't know how much additional
michael@0 879 // width is required for the non-breaking content after the float,
michael@0 880 // so we can't know whether the float plus that content will fit
michael@0 881 // on the line. So for now, don't place floats after inline
michael@0 882 // content where there's no break opportunity. This is incorrect
michael@0 883 // but hopefully rare. Fixing it will require significant
michael@0 884 // restructuring of line layout.
michael@0 885 // We might as well allow zero-width floats to be placed, though.
michael@0 886 availableWidth = 0;
michael@0 887 }
michael@0 888 placedFloat = AddFloat(outOfFlowFrame, availableWidth);
michael@0 889 NS_ASSERTION(!(outOfFlowFrame->GetType() == nsGkAtoms::letterFrame &&
michael@0 890 GetFirstLetterStyleOK()),
michael@0 891 "FirstLetterStyle set on line with floating first letter");
michael@0 892 }
michael@0 893 }
michael@0 894 else if (isText) {
michael@0 895 // Note non-empty text-frames for inline frame compatibility hackery
michael@0 896 pfd->SetFlag(PFD_ISTEXTFRAME, true);
michael@0 897 nsTextFrame* textFrame = static_cast<nsTextFrame*>(pfd->mFrame);
michael@0 898 isEmpty = !textFrame->HasNoncollapsedCharacters();
michael@0 899 if (!isEmpty) {
michael@0 900 pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, true);
michael@0 901 nsIContent* content = textFrame->GetContent();
michael@0 902
michael@0 903 const nsTextFragment* frag = content->GetText();
michael@0 904 if (frag) {
michael@0 905 pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME,
michael@0 906 !content->TextIsOnlyWhitespace());
michael@0 907 }
michael@0 908 }
michael@0 909 }
michael@0 910 else if (nsGkAtoms::brFrame == frameType) {
michael@0 911 pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true);
michael@0 912 isEmpty = false;
michael@0 913 } else {
michael@0 914 if (nsGkAtoms::letterFrame==frameType) {
michael@0 915 pfd->SetFlag(PFD_ISLETTERFRAME, true);
michael@0 916 }
michael@0 917 if (pfd->mSpan) {
michael@0 918 isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty();
michael@0 919 } else {
michael@0 920 isEmpty = pfd->mFrame->IsEmpty();
michael@0 921 }
michael@0 922 }
michael@0 923 }
michael@0 924
michael@0 925 mFloatManager->Translate(-tx, -ty);
michael@0 926
michael@0 927 NS_ASSERTION(metrics.Width() >= 0, "bad width");
michael@0 928 NS_ASSERTION(metrics.Height() >= 0,"bad height");
michael@0 929 if (metrics.Width() < 0) metrics.Width() = 0;
michael@0 930 if (metrics.Height() < 0) metrics.Height() = 0;
michael@0 931
michael@0 932 #ifdef DEBUG
michael@0 933 // Note: break-before means ignore the reflow metrics since the
michael@0 934 // frame will be reflowed another time.
michael@0 935 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
michael@0 936 if (CRAZY_SIZE(metrics.Width()) || CRAZY_SIZE(metrics.Height())) {
michael@0 937 printf("nsLineLayout: ");
michael@0 938 nsFrame::ListTag(stdout, aFrame);
michael@0 939 printf(" metrics=%d,%d!\n", metrics.Width(), metrics.Height());
michael@0 940 }
michael@0 941 if ((metrics.Width() == nscoord(0xdeadbeef)) ||
michael@0 942 (metrics.Height() == nscoord(0xdeadbeef))) {
michael@0 943 printf("nsLineLayout: ");
michael@0 944 nsFrame::ListTag(stdout, aFrame);
michael@0 945 printf(" didn't set w/h %d,%d!\n", metrics.Width(), metrics.Height());
michael@0 946 }
michael@0 947 }
michael@0 948 #endif
michael@0 949
michael@0 950 // Unlike with non-inline reflow, the overflow area here does *not*
michael@0 951 // include the accumulation of the frame's bounds and its inline
michael@0 952 // descendants' bounds. Nor does it include the outline area; it's
michael@0 953 // just the union of the bounds of any absolute children. That is
michael@0 954 // added in later by nsLineLayout::ReflowInlineFrames.
michael@0 955 pfd->mOverflowAreas = metrics.mOverflowAreas;
michael@0 956
michael@0 957 pfd->mBounds.ISize(lineWM) = metrics.ISize();
michael@0 958 pfd->mBounds.BSize(lineWM) = metrics.BSize();
michael@0 959
michael@0 960 // Size the frame, but |RelativePositionFrames| will size the view.
michael@0 961 aFrame->SetSize(nsSize(metrics.Width(), metrics.Height()));
michael@0 962
michael@0 963 // Tell the frame that we're done reflowing it
michael@0 964 aFrame->DidReflow(mPresContext,
michael@0 965 isText ? nullptr : reflowStateHolder.addr(),
michael@0 966 nsDidReflowStatus::FINISHED);
michael@0 967
michael@0 968 if (aMetrics) {
michael@0 969 *aMetrics = metrics;
michael@0 970 }
michael@0 971
michael@0 972 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
michael@0 973 // If frame is complete and has a next-in-flow, we need to delete
michael@0 974 // them now. Do not do this when a break-before is signaled because
michael@0 975 // the frame is going to get reflowed again (and may end up wanting
michael@0 976 // a next-in-flow where it ends up).
michael@0 977 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
michael@0 978 nsIFrame* kidNextInFlow = aFrame->GetNextInFlow();
michael@0 979 if (nullptr != kidNextInFlow) {
michael@0 980 // Remove all of the childs next-in-flows. Make sure that we ask
michael@0 981 // the right parent to do the removal (it's possible that the
michael@0 982 // parent is not this because we are executing pullup code)
michael@0 983 nsContainerFrame* parent = static_cast<nsContainerFrame*>
michael@0 984 (kidNextInFlow->GetParent());
michael@0 985 parent->DeleteNextInFlowChild(kidNextInFlow, true);
michael@0 986 }
michael@0 987 }
michael@0 988
michael@0 989 // Check whether this frame breaks up text runs. All frames break up text
michael@0 990 // runs (hence return false here) except for text frames and inline containers.
michael@0 991 bool continuingTextRun = aFrame->CanContinueTextRun();
michael@0 992
michael@0 993 // Clear any residual mTrimmableWidth if this isn't a text frame
michael@0 994 if (!continuingTextRun && !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) {
michael@0 995 mTrimmableWidth = 0;
michael@0 996 }
michael@0 997
michael@0 998 // See if we can place the frame. If we can't fit it, then we
michael@0 999 // return now.
michael@0 1000 bool optionalBreakAfterFits;
michael@0 1001 NS_ASSERTION(isText ||
michael@0 1002 !reflowStateHolder.ref().IsFloating(),
michael@0 1003 "How'd we get a floated inline frame? "
michael@0 1004 "The frame ctor should've dealt with this.");
michael@0 1005 if (CanPlaceFrame(pfd, notSafeToBreak, continuingTextRun,
michael@0 1006 savedOptionalBreakContent != nullptr, metrics,
michael@0 1007 aReflowStatus, &optionalBreakAfterFits)) {
michael@0 1008 if (!isEmpty) {
michael@0 1009 psd->mHasNonemptyContent = true;
michael@0 1010 mLineIsEmpty = false;
michael@0 1011 if (!pfd->mSpan) {
michael@0 1012 // nonempty leaf content has been placed
michael@0 1013 mLineAtStart = false;
michael@0 1014 }
michael@0 1015 }
michael@0 1016
michael@0 1017 // Place the frame, updating aBounds with the final size and
michael@0 1018 // location. Then apply the bottom+right margins (as
michael@0 1019 // appropriate) to the frame.
michael@0 1020 PlaceFrame(pfd, metrics);
michael@0 1021 PerSpanData* span = pfd->mSpan;
michael@0 1022 if (span) {
michael@0 1023 // The frame we just finished reflowing is an inline
michael@0 1024 // container. It needs its child frames aligned in the block direction,
michael@0 1025 // so do most of it now.
michael@0 1026 BlockDirAlignFrames(span);
michael@0 1027 }
michael@0 1028
michael@0 1029 if (!continuingTextRun) {
michael@0 1030 if (!psd->mNoWrap && (!LineIsEmpty() || placedFloat)) {
michael@0 1031 // record soft break opportunity after this content that can't be
michael@0 1032 // part of a text run. This is not a text frame so we know
michael@0 1033 // that offset INT32_MAX means "after the content".
michael@0 1034 if (NotifyOptionalBreakPosition(aFrame->GetContent(), INT32_MAX, optionalBreakAfterFits, gfxBreakPriority::eNormalBreak)) {
michael@0 1035 // If this returns true then we are being told to actually break here.
michael@0 1036 aReflowStatus = NS_INLINE_LINE_BREAK_AFTER(aReflowStatus);
michael@0 1037 }
michael@0 1038 }
michael@0 1039 }
michael@0 1040 }
michael@0 1041 else {
michael@0 1042 PushFrame(aFrame);
michael@0 1043 aPushedFrame = true;
michael@0 1044 // Undo any saved break positions that the frame might have told us about,
michael@0 1045 // since we didn't end up placing it
michael@0 1046 RestoreSavedBreakPosition(savedOptionalBreakContent,
michael@0 1047 savedOptionalBreakOffset,
michael@0 1048 savedOptionalBreakPriority);
michael@0 1049 }
michael@0 1050 }
michael@0 1051 else {
michael@0 1052 PushFrame(aFrame);
michael@0 1053 }
michael@0 1054
michael@0 1055 #ifdef REALLY_NOISY_REFLOW
michael@0 1056 nsFrame::IndentBy(stdout, mSpanDepth);
michael@0 1057 printf("End ReflowFrame ");
michael@0 1058 nsFrame::ListTag(stdout, aFrame);
michael@0 1059 printf(" status=%x\n", aReflowStatus);
michael@0 1060 #endif
michael@0 1061
michael@0 1062 return NS_OK;
michael@0 1063 }
michael@0 1064
michael@0 1065 void
michael@0 1066 nsLineLayout::AllowForStartMargin(PerFrameData* pfd,
michael@0 1067 nsHTMLReflowState& aReflowState)
michael@0 1068 {
michael@0 1069 NS_ASSERTION(!aReflowState.IsFloating(),
michael@0 1070 "How'd we get a floated inline frame? "
michael@0 1071 "The frame ctor should've dealt with this.");
michael@0 1072
michael@0 1073 WritingMode frameWM = pfd->mFrame->GetWritingMode();
michael@0 1074
michael@0 1075 // Only apply start-margin on the first-in flow for inline frames,
michael@0 1076 // and make sure to not apply it to any inline other than the first
michael@0 1077 // in an ib split. Note that the ib sibling (block-in-inline
michael@0 1078 // sibling) annotations only live on the first continuation, but we
michael@0 1079 // don't want to apply the start margin for later continuations
michael@0 1080 // anyway.
michael@0 1081 if (pfd->mFrame->GetPrevContinuation() ||
michael@0 1082 pfd->mFrame->FrameIsNonFirstInIBSplit()) {
michael@0 1083 // Zero this out so that when we compute the max-element-width of
michael@0 1084 // the frame we will properly avoid adding in the starting margin.
michael@0 1085 pfd->mMargin.IStart(frameWM) = 0;
michael@0 1086 } else {
michael@0 1087 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth(),
michael@0 1088 "have unconstrained width; this should only result from "
michael@0 1089 "very large sizes, not attempts at intrinsic width "
michael@0 1090 "calculation");
michael@0 1091 if (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth()) {
michael@0 1092 // For inline-ish and text-ish things (which don't compute widths
michael@0 1093 // in the reflow state), adjust available width to account for the
michael@0 1094 // start margin. The end margin will be accounted for when we
michael@0 1095 // finish flowing the frame.
michael@0 1096 aReflowState.AvailableWidth() -= pfd->mMargin.IStart(frameWM);
michael@0 1097 }
michael@0 1098 }
michael@0 1099 }
michael@0 1100
michael@0 1101 nscoord
michael@0 1102 nsLineLayout::GetCurrentFrameInlineDistanceFromBlock()
michael@0 1103 {
michael@0 1104 PerSpanData* psd;
michael@0 1105 nscoord x = 0;
michael@0 1106 for (psd = mCurrentSpan; psd; psd = psd->mParent) {
michael@0 1107 x += psd->mICoord;
michael@0 1108 }
michael@0 1109 return x;
michael@0 1110 }
michael@0 1111
michael@0 1112 /**
michael@0 1113 * See if the frame can be placed now that we know it's desired size.
michael@0 1114 * We can always place the frame if the line is empty. Note that we
michael@0 1115 * know that the reflow-status is not a break-before because if it was
michael@0 1116 * ReflowFrame above would have returned false, preventing this method
michael@0 1117 * from being called. The logic in this method assumes that.
michael@0 1118 *
michael@0 1119 * Note that there is no check against the Y coordinate because we
michael@0 1120 * assume that the caller will take care of that.
michael@0 1121 */
michael@0 1122 bool
michael@0 1123 nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
michael@0 1124 bool aNotSafeToBreak,
michael@0 1125 bool aFrameCanContinueTextRun,
michael@0 1126 bool aCanRollBackBeforeFrame,
michael@0 1127 nsHTMLReflowMetrics& aMetrics,
michael@0 1128 nsReflowStatus& aStatus,
michael@0 1129 bool* aOptionalBreakAfterFits)
michael@0 1130 {
michael@0 1131 NS_PRECONDITION(pfd && pfd->mFrame, "bad args, null pointers for frame data");
michael@0 1132
michael@0 1133 *aOptionalBreakAfterFits = true;
michael@0 1134
michael@0 1135 WritingMode frameWM = pfd->mFrame->GetWritingMode();
michael@0 1136 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 1137 /*
michael@0 1138 * We want to only apply the end margin if we're the last continuation and
michael@0 1139 * either not in an {ib} split or the last inline in it. In all other
michael@0 1140 * cases we want to zero it out. That means zeroing it out if any of these
michael@0 1141 * conditions hold:
michael@0 1142 * 1) The frame is not complete (in this case it will get a next-in-flow)
michael@0 1143 * 2) The frame is complete but has a non-fluid continuation on its
michael@0 1144 * continuation chain. Note that if it has a fluid continuation, that
michael@0 1145 * continuation will get destroyed later, so we don't want to drop the
michael@0 1146 * end-margin in that case.
michael@0 1147 * 3) The frame is in an {ib} split and is not the last part.
michael@0 1148 *
michael@0 1149 * However, none of that applies if this is a letter frame (XXXbz why?)
michael@0 1150 */
michael@0 1151 if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) ||
michael@0 1152 pfd->mFrame->LastInFlow()->GetNextContinuation() ||
michael@0 1153 pfd->mFrame->FrameIsNonLastInIBSplit())
michael@0 1154 && !pfd->GetFlag(PFD_ISLETTERFRAME)) {
michael@0 1155 pfd->mMargin.IEnd(frameWM) = 0;
michael@0 1156 }
michael@0 1157
michael@0 1158 // Convert the frame's margins to the line's writing mode and apply
michael@0 1159 // the start margin to the frame bounds.
michael@0 1160 LogicalMargin usedMargins = pfd->mMargin.ConvertTo(lineWM, frameWM);
michael@0 1161 nscoord startMargin = usedMargins.IStart(lineWM);
michael@0 1162 nscoord endMargin = usedMargins.IEnd(lineWM);
michael@0 1163
michael@0 1164 pfd->mBounds.IStart(lineWM) += startMargin;
michael@0 1165
michael@0 1166 PerSpanData* psd = mCurrentSpan;
michael@0 1167 if (psd->mNoWrap) {
michael@0 1168 // When wrapping is off, everything fits.
michael@0 1169 return true;
michael@0 1170 }
michael@0 1171
michael@0 1172 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1173 if (nullptr != psd->mFrame) {
michael@0 1174 nsFrame::ListTag(stdout, psd->mFrame->mFrame);
michael@0 1175 }
michael@0 1176 else {
michael@0 1177 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 1178 }
michael@0 1179 printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak ? "true" : "false");
michael@0 1180 nsFrame::ListTag(stdout, pfd->mFrame);
michael@0 1181 printf(" frameWidth=%d, margins=%d,%d\n",
michael@0 1182 pfd->mBounds.IEnd(lineWM) + endMargin - psd->mICoord,
michael@0 1183 startMargin, endMargin);
michael@0 1184 #endif
michael@0 1185
michael@0 1186 // Set outside to true if the result of the reflow leads to the
michael@0 1187 // frame sticking outside of our available area.
michael@0 1188 bool outside = pfd->mBounds.IEnd(lineWM) - mTrimmableWidth + endMargin >
michael@0 1189 psd->mIEnd;
michael@0 1190 if (!outside) {
michael@0 1191 // If it fits, it fits
michael@0 1192 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1193 printf(" ==> inside\n");
michael@0 1194 #endif
michael@0 1195 return true;
michael@0 1196 }
michael@0 1197 *aOptionalBreakAfterFits = false;
michael@0 1198
michael@0 1199 // When it doesn't fit, check for a few special conditions where we
michael@0 1200 // allow it to fit anyway.
michael@0 1201 if (0 == startMargin + pfd->mBounds.ISize(lineWM) + endMargin) {
michael@0 1202 // Empty frames always fit right where they are
michael@0 1203 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1204 printf(" ==> empty frame fits\n");
michael@0 1205 #endif
michael@0 1206 return true;
michael@0 1207 }
michael@0 1208
michael@0 1209 #ifdef FIX_BUG_50257
michael@0 1210 // another special case: always place a BR
michael@0 1211 if (nsGkAtoms::brFrame == pfd->mFrame->GetType()) {
michael@0 1212 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1213 printf(" ==> BR frame fits\n");
michael@0 1214 #endif
michael@0 1215 return true;
michael@0 1216 }
michael@0 1217 #endif
michael@0 1218
michael@0 1219 if (aNotSafeToBreak) {
michael@0 1220 // There are no frames on the line that take up width and the line is
michael@0 1221 // not impacted by floats, so we must allow the current frame to be
michael@0 1222 // placed on the line
michael@0 1223 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1224 printf(" ==> not-safe and not-impacted fits: ");
michael@0 1225 while (nullptr != psd) {
michael@0 1226 printf("<psd=%p x=%d left=%d> ", psd, psd->mICoord, psd->mIStart);
michael@0 1227 psd = psd->mParent;
michael@0 1228 }
michael@0 1229 printf("\n");
michael@0 1230 #endif
michael@0 1231 return true;
michael@0 1232 }
michael@0 1233
michael@0 1234 // Special check for span frames
michael@0 1235 if (pfd->mSpan && pfd->mSpan->mContainsFloat) {
michael@0 1236 // If the span either directly or indirectly contains a float then
michael@0 1237 // it fits. Why? It's kind of complicated, but here goes:
michael@0 1238 //
michael@0 1239 // 1. CanPlaceFrame is used for all frame placements on a line,
michael@0 1240 // and in a span. This includes recursively placement of frames
michael@0 1241 // inside of spans, and the span itself. Because the logic always
michael@0 1242 // checks for room before proceeding (the code above here), the
michael@0 1243 // only things on a line will be those things that "fit".
michael@0 1244 //
michael@0 1245 // 2. Before a float is placed on a line, the line has to be empty
michael@0 1246 // (otherwise it's a "below current line" float and will be placed
michael@0 1247 // after the line).
michael@0 1248 //
michael@0 1249 // Therefore, if the span directly or indirectly has a float
michael@0 1250 // then it means that at the time of the placement of the float
michael@0 1251 // the line was empty. Because of #1, only the frames that fit can
michael@0 1252 // be added after that point, therefore we can assume that the
michael@0 1253 // current span being placed has fit.
michael@0 1254 //
michael@0 1255 // So how do we get here and have a span that should already fit
michael@0 1256 // and yet doesn't: Simple: span's that have the no-wrap attribute
michael@0 1257 // set on them and contain a float and are placed where they
michael@0 1258 // don't naturally fit.
michael@0 1259 return true;
michael@0 1260 }
michael@0 1261
michael@0 1262 if (aFrameCanContinueTextRun) {
michael@0 1263 // Let it fit, but we reserve the right to roll back.
michael@0 1264 // Note that we usually won't get here because a text frame will break
michael@0 1265 // itself to avoid exceeding the available width.
michael@0 1266 // We'll only get here for text frames that couldn't break early enough.
michael@0 1267 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1268 printf(" ==> placing overflowing textrun, requesting backup\n");
michael@0 1269 #endif
michael@0 1270
michael@0 1271 // We will want to try backup.
michael@0 1272 mNeedBackup = true;
michael@0 1273 return true;
michael@0 1274 }
michael@0 1275
michael@0 1276 #ifdef NOISY_CAN_PLACE_FRAME
michael@0 1277 printf(" ==> didn't fit\n");
michael@0 1278 #endif
michael@0 1279 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 1280 return false;
michael@0 1281 }
michael@0 1282
michael@0 1283 /**
michael@0 1284 * Place the frame. Update running counters.
michael@0 1285 */
michael@0 1286 void
michael@0 1287 nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics)
michael@0 1288 {
michael@0 1289 // Record ascent and update max-ascent and max-descent values
michael@0 1290 if (aMetrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE)
michael@0 1291 pfd->mAscent = pfd->mFrame->GetBaseline();
michael@0 1292 else
michael@0 1293 pfd->mAscent = aMetrics.TopAscent();
michael@0 1294
michael@0 1295 // Advance to next inline coordinate
michael@0 1296 WritingMode frameWM = pfd->mFrame->GetWritingMode();
michael@0 1297 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 1298 mCurrentSpan->mICoord = pfd->mBounds.IEnd(lineWM) +
michael@0 1299 pfd->mMargin.ConvertTo(lineWM, frameWM).IEnd(lineWM);
michael@0 1300
michael@0 1301 // Count the number of non-placeholder frames on the line...
michael@0 1302 if (pfd->mFrame->GetType() == nsGkAtoms::placeholderFrame) {
michael@0 1303 NS_ASSERTION(pfd->mBounds.ISize(lineWM) == 0 &&
michael@0 1304 pfd->mBounds.BSize(lineWM) == 0,
michael@0 1305 "placeholders should have 0 width/height (checking "
michael@0 1306 "placeholders were never counted by the old code in "
michael@0 1307 "this function)");
michael@0 1308 } else {
michael@0 1309 mTotalPlacedFrames++;
michael@0 1310 }
michael@0 1311 }
michael@0 1312
michael@0 1313 void
michael@0 1314 nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
michael@0 1315 const nsHTMLReflowMetrics& aMetrics)
michael@0 1316 {
michael@0 1317 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
michael@0 1318 NS_ASSERTION(mGotLineBox, "must have line box");
michael@0 1319
michael@0 1320 nsIFrame *blockFrame = mBlockReflowState->frame;
michael@0 1321 NS_ASSERTION(blockFrame->IsFrameOfType(nsIFrame::eBlockFrame),
michael@0 1322 "must be for block");
michael@0 1323 if (!static_cast<nsBlockFrame*>(blockFrame)->BulletIsEmpty()) {
michael@0 1324 mHasBullet = true;
michael@0 1325 mLineBox->SetHasBullet();
michael@0 1326 }
michael@0 1327
michael@0 1328 PerFrameData* pfd = NewPerFrameData(aFrame);
michael@0 1329 mRootSpan->AppendFrame(pfd);
michael@0 1330 pfd->SetFlag(PFD_ISBULLET, true);
michael@0 1331 if (aMetrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE)
michael@0 1332 pfd->mAscent = aFrame->GetBaseline();
michael@0 1333 else
michael@0 1334 pfd->mAscent = aMetrics.TopAscent();
michael@0 1335
michael@0 1336 // Note: block-coord value will be updated during block-direction alignment
michael@0 1337 pfd->mBounds = LogicalRect(mRootSpan->mWritingMode,
michael@0 1338 aFrame->GetRect(), mContainerWidth);
michael@0 1339 pfd->mOverflowAreas = aMetrics.mOverflowAreas;
michael@0 1340 }
michael@0 1341
michael@0 1342 #ifdef DEBUG
michael@0 1343 void
michael@0 1344 nsLineLayout::DumpPerSpanData(PerSpanData* psd, int32_t aIndent)
michael@0 1345 {
michael@0 1346 nsFrame::IndentBy(stdout, aIndent);
michael@0 1347 printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd),
michael@0 1348 psd->mIStart, psd->mICoord, psd->mIEnd);
michael@0 1349 PerFrameData* pfd = psd->mFirstFrame;
michael@0 1350 while (nullptr != pfd) {
michael@0 1351 nsFrame::IndentBy(stdout, aIndent+1);
michael@0 1352 nsFrame::ListTag(stdout, pfd->mFrame);
michael@0 1353 nsRect rect = pfd->mBounds.GetPhysicalRect(psd->mWritingMode,
michael@0 1354 mContainerWidth);
michael@0 1355 printf(" %d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
michael@0 1356 if (pfd->mSpan) {
michael@0 1357 DumpPerSpanData(pfd->mSpan, aIndent + 1);
michael@0 1358 }
michael@0 1359 pfd = pfd->mNext;
michael@0 1360 }
michael@0 1361 }
michael@0 1362 #endif
michael@0 1363
michael@0 1364 #define VALIGN_OTHER 0
michael@0 1365 #define VALIGN_TOP 1
michael@0 1366 #define VALIGN_BOTTOM 2
michael@0 1367
michael@0 1368 void
michael@0 1369 nsLineLayout::BlockDirAlignLine()
michael@0 1370 {
michael@0 1371 // Synthesize a PerFrameData for the block frame
michael@0 1372 PerFrameData rootPFD(mBlockReflowState->frame->GetWritingMode());
michael@0 1373 rootPFD.mFrame = mBlockReflowState->frame;
michael@0 1374 rootPFD.mAscent = 0;
michael@0 1375 mRootSpan->mFrame = &rootPFD;
michael@0 1376
michael@0 1377 // Partially place the children of the block frame. The baseline for
michael@0 1378 // this operation is set to zero so that the y coordinates for all
michael@0 1379 // of the placed children will be relative to there.
michael@0 1380 PerSpanData* psd = mRootSpan;
michael@0 1381 BlockDirAlignFrames(psd);
michael@0 1382
michael@0 1383 // *** Note that comments here still use the anachronistic term "line-height"
michael@0 1384 // when we really mean "size of the line in the block direction". This is
michael@0 1385 // partly for brevity and partly to retain the association with the CSS
michael@0 1386 // line-height property
michael@0 1387 //
michael@0 1388 // Compute the line-height. The line-height will be the larger of:
michael@0 1389 //
michael@0 1390 // [1] maxBCoord - minBCoord (the distance between the first child's
michael@0 1391 // block-start edge and the last child's block-end edge)
michael@0 1392 //
michael@0 1393 // [2] the maximum logical box block size (since not every frame may have
michael@0 1394 // participated in #1; for example: block-start/end aligned frames)
michael@0 1395 //
michael@0 1396 // [3] the minimum line height ("line-height" property set on the
michael@0 1397 // block frame)
michael@0 1398 nscoord lineBSize = psd->mMaxBCoord - psd->mMinBCoord;
michael@0 1399
michael@0 1400 // Now that the line-height is computed, we need to know where the
michael@0 1401 // baseline is in the line. Position baseline so that mMinBCoord is just
michael@0 1402 // inside the start of the line box.
michael@0 1403 nscoord baselineBCoord;
michael@0 1404 if (psd->mMinBCoord < 0) {
michael@0 1405 baselineBCoord = mBStartEdge - psd->mMinBCoord;
michael@0 1406 }
michael@0 1407 else {
michael@0 1408 baselineBCoord = mBStartEdge;
michael@0 1409 }
michael@0 1410
michael@0 1411 // It's also possible that the line block-size isn't tall enough because
michael@0 1412 // of block start/end aligned elements that were not accounted for in
michael@0 1413 // min/max BCoord.
michael@0 1414 //
michael@0 1415 // The CSS2 spec doesn't really say what happens when to the
michael@0 1416 // baseline in this situations. What we do is if the largest start
michael@0 1417 // aligned box block size is greater than the line block-size then we leave
michael@0 1418 // the baseline alone. If the largest end aligned box is greater
michael@0 1419 // than the line block-size then we slide the baseline forward by the extra
michael@0 1420 // amount.
michael@0 1421 //
michael@0 1422 // Navigator 4 gives precedence to the first top/bottom aligned
michael@0 1423 // object. We just let block end aligned objects win.
michael@0 1424 if (lineBSize < mMaxEndBoxBSize) {
michael@0 1425 // When the line is shorter than the maximum block start aligned box
michael@0 1426 nscoord extra = mMaxEndBoxBSize - lineBSize;
michael@0 1427 baselineBCoord += extra;
michael@0 1428 lineBSize = mMaxEndBoxBSize;
michael@0 1429 }
michael@0 1430 if (lineBSize < mMaxStartBoxBSize) {
michael@0 1431 lineBSize = mMaxStartBoxBSize;
michael@0 1432 }
michael@0 1433 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1434 printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize, baselineBCoord);
michael@0 1435 #endif
michael@0 1436
michael@0 1437 // Now position all of the frames in the root span. We will also
michael@0 1438 // recurse over the child spans and place any block start/end aligned
michael@0 1439 // frames we find.
michael@0 1440 // XXX PERFORMANCE: set a bit per-span to avoid the extra work
michael@0 1441 // (propagate it upward too)
michael@0 1442 WritingMode lineWM = psd->mWritingMode;
michael@0 1443 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 1444 if (pfd->mBlockDirAlign == VALIGN_OTHER) {
michael@0 1445 pfd->mBounds.BStart(lineWM) += baselineBCoord;
michael@0 1446 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 1447 }
michael@0 1448 }
michael@0 1449 PlaceStartEndFrames(psd, -mBStartEdge, lineBSize);
michael@0 1450
michael@0 1451 // If the frame being reflowed has text decorations, we simulate the
michael@0 1452 // propagation of those decorations to a line-level element by storing the
michael@0 1453 // offset in a frame property on any child frames that are aligned in the
michael@0 1454 // block direction somewhere other than the baseline. This property is then
michael@0 1455 // used by nsTextFrame::GetTextDecorations when the same conditions are met.
michael@0 1456 if (rootPFD.mFrame->StyleContext()->HasTextDecorationLines()) {
michael@0 1457 for (const PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 1458 const nsIFrame *const f = pfd->mFrame;
michael@0 1459 if (f->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
michael@0 1460 const nscoord offset = baselineBCoord - pfd->mBounds.BStart(lineWM);
michael@0 1461 f->Properties().Set(nsIFrame::LineBaselineOffset(),
michael@0 1462 NS_INT32_TO_PTR(offset));
michael@0 1463 }
michael@0 1464 }
michael@0 1465 }
michael@0 1466
michael@0 1467 // Fill in returned line-box and max-element-width data
michael@0 1468 mLineBox->SetBounds(lineWM,
michael@0 1469 psd->mIStart, mBStartEdge,
michael@0 1470 psd->mICoord - psd->mIStart, lineBSize,
michael@0 1471 mContainerWidth);
michael@0 1472
michael@0 1473 mFinalLineBSize = lineBSize;
michael@0 1474 mLineBox->SetAscent(baselineBCoord - mBStartEdge);
michael@0 1475 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1476 printf(
michael@0 1477 " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
michael@0 1478 mLineBox->mBounds.IStart(lineWM), mLineBox->mBounds.BStart(lineWM),
michael@0 1479 mLineBox->mBounds.ISize(lineWM), mLineBox->mBounds.BSize(lineWM),
michael@0 1480 mFinalLineBSize, mLineBox->GetAscent());
michael@0 1481 #endif
michael@0 1482
michael@0 1483 // Undo root-span mFrame pointer to prevent brane damage later on...
michael@0 1484 mRootSpan->mFrame = nullptr;
michael@0 1485 }
michael@0 1486
michael@0 1487 void
michael@0 1488 nsLineLayout::PlaceStartEndFrames(PerSpanData* psd,
michael@0 1489 nscoord aDistanceFromStart,
michael@0 1490 nscoord aLineBSize)
michael@0 1491 {
michael@0 1492 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 1493 PerSpanData* span = pfd->mSpan;
michael@0 1494 #ifdef DEBUG
michael@0 1495 NS_ASSERTION(0xFF != pfd->mBlockDirAlign, "umr");
michael@0 1496 #endif
michael@0 1497 WritingMode frameWM = pfd->mFrame->GetWritingMode();
michael@0 1498 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 1499 switch (pfd->mBlockDirAlign) {
michael@0 1500 case VALIGN_TOP:
michael@0 1501 if (span) {
michael@0 1502 pfd->mBounds.BStart(lineWM) = -aDistanceFromStart - span->mMinBCoord;
michael@0 1503 }
michael@0 1504 else {
michael@0 1505 pfd->mBounds.BStart(lineWM) =
michael@0 1506 -aDistanceFromStart + pfd->mMargin.BStart(frameWM);
michael@0 1507 }
michael@0 1508 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 1509 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1510 printf(" ");
michael@0 1511 nsFrame::ListTag(stdout, pfd->mFrame);
michael@0 1512 printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
michael@0 1513 pfd->mBounds.BStart(lineWM), aDistanceFromStart,
michael@0 1514 span ? pfd->mBorderPadding.BStart(frameWM) : 0,
michael@0 1515 span ? span->mBStartLeading : 0);
michael@0 1516 #endif
michael@0 1517 break;
michael@0 1518 case VALIGN_BOTTOM:
michael@0 1519 if (span) {
michael@0 1520 // Compute bottom leading
michael@0 1521 pfd->mBounds.BStart(lineWM) =
michael@0 1522 -aDistanceFromStart + aLineBSize - span->mMaxBCoord;
michael@0 1523 }
michael@0 1524 else {
michael@0 1525 pfd->mBounds.BStart(lineWM) = -aDistanceFromStart + aLineBSize -
michael@0 1526 pfd->mMargin.BEnd(frameWM) - pfd->mBounds.BSize(lineWM);
michael@0 1527 }
michael@0 1528 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 1529 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1530 printf(" ");
michael@0 1531 nsFrame::ListTag(stdout, pfd->mFrame);
michael@0 1532 printf(": y=%d\n", pfd->mBounds.BStart(lineWM));
michael@0 1533 #endif
michael@0 1534 break;
michael@0 1535 }
michael@0 1536 if (span) {
michael@0 1537 nscoord fromStart = aDistanceFromStart + pfd->mBounds.BStart(lineWM);
michael@0 1538 PlaceStartEndFrames(span, fromStart, aLineBSize);
michael@0 1539 }
michael@0 1540 }
michael@0 1541 }
michael@0 1542
michael@0 1543 static float
michael@0 1544 GetInflationForBlockDirAlignment(nsIFrame* aFrame,
michael@0 1545 nscoord aInflationMinFontSize)
michael@0 1546 {
michael@0 1547 if (aFrame->IsSVGText()) {
michael@0 1548 const nsIFrame* container =
michael@0 1549 nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame);
michael@0 1550 NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
michael@0 1551 return
michael@0 1552 static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
michael@0 1553 }
michael@0 1554 return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize);
michael@0 1555 }
michael@0 1556
michael@0 1557 #define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX
michael@0 1558 #define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN
michael@0 1559
michael@0 1560 // Place frames in the block direction within a given span. Note: this doesn't
michael@0 1561 // place block start/end aligned frames as those have to wait until the
michael@0 1562 // entire line box block size is known. This is called after the span
michael@0 1563 // frame has finished being reflowed so that we know its block size.
michael@0 1564 void
michael@0 1565 nsLineLayout::BlockDirAlignFrames(PerSpanData* psd)
michael@0 1566 {
michael@0 1567 // Get parent frame info
michael@0 1568 PerFrameData* spanFramePFD = psd->mFrame;
michael@0 1569 nsIFrame* spanFrame = spanFramePFD->mFrame;
michael@0 1570
michael@0 1571 // Get the parent frame's font for all of the frames in this span
michael@0 1572 nsRefPtr<nsFontMetrics> fm;
michael@0 1573 float inflation =
michael@0 1574 GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize);
michael@0 1575 nsLayoutUtils::GetFontMetricsForFrame(spanFrame, getter_AddRefs(fm),
michael@0 1576 inflation);
michael@0 1577 mBlockReflowState->rendContext->SetFont(fm);
michael@0 1578
michael@0 1579 bool preMode = mStyleText->WhiteSpaceIsSignificant();
michael@0 1580
michael@0 1581 // See if the span is an empty continuation. It's an empty continuation iff:
michael@0 1582 // - it has a prev-in-flow
michael@0 1583 // - it has no next in flow
michael@0 1584 // - it's zero sized
michael@0 1585 WritingMode frameWM = spanFramePFD->mFrame->GetWritingMode();
michael@0 1586 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 1587 bool emptyContinuation = psd != mRootSpan &&
michael@0 1588 spanFrame->GetPrevInFlow() && !spanFrame->GetNextInFlow() &&
michael@0 1589 spanFramePFD->mBounds.IsZeroSize();
michael@0 1590
michael@0 1591 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1592 printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
michael@0 1593 nsFrame::ListTag(stdout, spanFrame);
michael@0 1594 printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
michael@0 1595 preMode ? "yes" : "no",
michael@0 1596 mPresContext->CompatibilityMode() != eCompatibility_NavQuirks ? "yes" : "no",
michael@0 1597 spanFramePFD->mBounds.ISize(lineWM),
michael@0 1598 spanFramePFD->mBounds.BSize(lineWM),
michael@0 1599 emptyContinuation ? "yes" : "no");
michael@0 1600 if (psd != mRootSpan) {
michael@0 1601 WritingMode frameWM = spanFramePFD->mFrame->GetWritingMode();
michael@0 1602 printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
michael@0 1603 spanFramePFD->mBorderPadding.Top(frameWM),
michael@0 1604 spanFramePFD->mBorderPadding.Right(frameWM),
michael@0 1605 spanFramePFD->mBorderPadding.Bottom(frameWM),
michael@0 1606 spanFramePFD->mBorderPadding.Left(frameWM),
michael@0 1607 spanFramePFD->mMargin.Top(frameWM),
michael@0 1608 spanFramePFD->mMargin.Right(frameWM),
michael@0 1609 spanFramePFD->mMargin.Bottom(frameWM),
michael@0 1610 spanFramePFD->mMargin.Left(frameWM));
michael@0 1611 }
michael@0 1612 printf("\n");
michael@0 1613 #endif
michael@0 1614
michael@0 1615 // Compute the span's mZeroEffectiveSpanBox flag. What we are trying
michael@0 1616 // to determine is how we should treat the span: should it act
michael@0 1617 // "normally" according to css2 or should it effectively
michael@0 1618 // "disappear".
michael@0 1619 //
michael@0 1620 // In general, if the document being processed is in full standards
michael@0 1621 // mode then it should act normally (with one exception). The
michael@0 1622 // exception case is when a span is continued and yet the span is
michael@0 1623 // empty (e.g. compressed whitespace). For this kind of span we treat
michael@0 1624 // it as if it were not there so that it doesn't impact the
michael@0 1625 // line block-size.
michael@0 1626 //
michael@0 1627 // In almost standards mode or quirks mode, we should sometimes make
michael@0 1628 // it disappear. The cases that matter are those where the span
michael@0 1629 // contains no real text elements that would provide an ascent and
michael@0 1630 // descent and height. However, if css style elements have been
michael@0 1631 // applied to the span (border/padding/margin) so that it's clear the
michael@0 1632 // document author is intending css2 behavior then we act as if strict
michael@0 1633 // mode is set.
michael@0 1634 //
michael@0 1635 // This code works correctly for preMode, because a blank line
michael@0 1636 // in PRE mode is encoded as a text node with a LF in it, since
michael@0 1637 // text nodes with only whitespace are considered in preMode.
michael@0 1638 //
michael@0 1639 // Much of this logic is shared with the various implementations of
michael@0 1640 // nsIFrame::IsEmpty since they need to duplicate the way it makes
michael@0 1641 // some lines empty. However, nsIFrame::IsEmpty can't be reused here
michael@0 1642 // since this code sets zeroEffectiveSpanBox even when there are
michael@0 1643 // non-empty children.
michael@0 1644 bool zeroEffectiveSpanBox = false;
michael@0 1645 // XXXldb If we really have empty continuations, then all these other
michael@0 1646 // checks don't make sense for them.
michael@0 1647 // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
michael@0 1648 // it agrees with this code. (If it doesn't agree, it probably should.)
michael@0 1649 if ((emptyContinuation ||
michael@0 1650 mPresContext->CompatibilityMode() != eCompatibility_FullStandards) &&
michael@0 1651 ((psd == mRootSpan) ||
michael@0 1652 (spanFramePFD->mBorderPadding.IsEmpty() &&
michael@0 1653 spanFramePFD->mMargin.IsEmpty()))) {
michael@0 1654 // This code handles an issue with compatibility with non-css
michael@0 1655 // conformant browsers. In particular, there are some cases
michael@0 1656 // where the font-size and line-height for a span must be
michael@0 1657 // ignored and instead the span must *act* as if it were zero
michael@0 1658 // sized. In general, if the span contains any non-compressed
michael@0 1659 // text then we don't use this logic.
michael@0 1660 // However, this is not propagated outwards, since (in compatibility
michael@0 1661 // mode) we don't want big line heights for things like
michael@0 1662 // <p><font size="-1">Text</font></p>
michael@0 1663
michael@0 1664 // We shouldn't include any whitespace that collapses, unless we're
michael@0 1665 // preformatted (in which case it shouldn't, but the width=0 test is
michael@0 1666 // perhaps incorrect). This includes whitespace at the beginning of
michael@0 1667 // a line and whitespace preceded (?) by other whitespace.
michael@0 1668 // See bug 134580 and bug 155333.
michael@0 1669 zeroEffectiveSpanBox = true;
michael@0 1670 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 1671 if (pfd->GetFlag(PFD_ISTEXTFRAME) &&
michael@0 1672 (pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) || preMode ||
michael@0 1673 pfd->mBounds.ISize(mRootSpan->mWritingMode) != 0)) {
michael@0 1674 zeroEffectiveSpanBox = false;
michael@0 1675 break;
michael@0 1676 }
michael@0 1677 }
michael@0 1678 }
michael@0 1679 psd->mZeroEffectiveSpanBox = zeroEffectiveSpanBox;
michael@0 1680
michael@0 1681 // Setup baselineBCoord, minBCoord, and maxBCoord
michael@0 1682 nscoord baselineBCoord, minBCoord, maxBCoord;
michael@0 1683 if (psd == mRootSpan) {
michael@0 1684 // Use a zero baselineBCoord since we don't yet know where the baseline
michael@0 1685 // will be (until we know how tall the line is; then we will
michael@0 1686 // know). In addition, use extreme values for the minBCoord and maxBCoord
michael@0 1687 // values so that only the child frames will impact their values
michael@0 1688 // (since these are children of the block, there is no span box to
michael@0 1689 // provide initial values).
michael@0 1690 baselineBCoord = 0;
michael@0 1691 minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM;
michael@0 1692 maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM;
michael@0 1693 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1694 printf("[RootSpan]");
michael@0 1695 nsFrame::ListTag(stdout, spanFrame);
michael@0 1696 printf(": pass1 valign frames: topEdge=%d minLineBSize=%d zeroEffectiveSpanBox=%s\n",
michael@0 1697 mBStartEdge, mMinLineBSize,
michael@0 1698 zeroEffectiveSpanBox ? "yes" : "no");
michael@0 1699 #endif
michael@0 1700 }
michael@0 1701 else {
michael@0 1702 // Compute the logical block size for this span. The logical block size
michael@0 1703 // is based on the "line-height" value, not the font-size. Also
michael@0 1704 // compute the top leading.
michael@0 1705 float inflation =
michael@0 1706 GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize);
michael@0 1707 nscoord logicalBSize = nsHTMLReflowState::
michael@0 1708 CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(),
michael@0 1709 mBlockReflowState->ComputedHeight(),
michael@0 1710 inflation);
michael@0 1711 nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) -
michael@0 1712 spanFramePFD->mBorderPadding.BStart(frameWM) -
michael@0 1713 spanFramePFD->mBorderPadding.BEnd(frameWM);
michael@0 1714
michael@0 1715 // Special-case for a ::first-letter frame, set the line height to
michael@0 1716 // the frame block size if the user has left line-height == normal
michael@0 1717 if (spanFramePFD->GetFlag(PFD_ISLETTERFRAME) &&
michael@0 1718 !spanFrame->GetPrevInFlow() &&
michael@0 1719 spanFrame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal) {
michael@0 1720 logicalBSize = spanFramePFD->mBounds.BSize(lineWM);
michael@0 1721 }
michael@0 1722
michael@0 1723 nscoord leading = logicalBSize - contentBSize;
michael@0 1724 psd->mBStartLeading = leading / 2;
michael@0 1725 psd->mBEndLeading = leading - psd->mBStartLeading;
michael@0 1726 psd->mLogicalBSize = logicalBSize;
michael@0 1727
michael@0 1728 if (zeroEffectiveSpanBox) {
michael@0 1729 // When the span-box is to be ignored, zero out the initial
michael@0 1730 // values so that the span doesn't impact the final line
michael@0 1731 // height. The contents of the span can impact the final line
michael@0 1732 // height.
michael@0 1733
michael@0 1734 // Note that things are readjusted for this span after its children
michael@0 1735 // are reflowed
michael@0 1736 minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM;
michael@0 1737 maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM;
michael@0 1738 }
michael@0 1739 else {
michael@0 1740
michael@0 1741 // The initial values for the min and max block coord values are in the
michael@0 1742 // span's coordinate space, and cover the logical block size of the span.
michael@0 1743 // If there are child frames in this span that stick out of this area
michael@0 1744 // then the minBCoord and maxBCoord are updated by the amount of logical
michael@0 1745 // blockSize that is outside this range.
michael@0 1746 minBCoord = spanFramePFD->mBorderPadding.BStart(frameWM) -
michael@0 1747 psd->mBStartLeading;
michael@0 1748 maxBCoord = minBCoord + psd->mLogicalBSize;
michael@0 1749 }
michael@0 1750
michael@0 1751 // This is the distance from the top edge of the parents visual
michael@0 1752 // box to the baseline. The span already computed this for us,
michael@0 1753 // so just use it.
michael@0 1754 *psd->mBaseline = baselineBCoord = spanFramePFD->mAscent;
michael@0 1755
michael@0 1756
michael@0 1757 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1758 printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
michael@0 1759 nsFrame::ListTag(stdout, spanFrame);
michael@0 1760 printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
michael@0 1761 baselineBCoord, psd->mLogicalBSize, psd->mBStartLeading,
michael@0 1762 spanFramePFD->mBounds.BSize(lineWM),
michael@0 1763 spanFramePFD->mBorderPadding.Top(frameWM),
michael@0 1764 spanFramePFD->mBorderPadding.Bottom(frameWM),
michael@0 1765 zeroEffectiveSpanBox ? "yes" : "no");
michael@0 1766 #endif
michael@0 1767 }
michael@0 1768
michael@0 1769 nscoord maxStartBoxBSize = 0;
michael@0 1770 nscoord maxEndBoxBSize = 0;
michael@0 1771 PerFrameData* pfd = psd->mFirstFrame;
michael@0 1772 while (nullptr != pfd) {
michael@0 1773 nsIFrame* frame = pfd->mFrame;
michael@0 1774 WritingMode frameWM = frame->GetWritingMode();
michael@0 1775
michael@0 1776 // sanity check (see bug 105168, non-reproducible crashes from null frame)
michael@0 1777 NS_ASSERTION(frame, "null frame in PerFrameData - something is very very bad");
michael@0 1778 if (!frame) {
michael@0 1779 return;
michael@0 1780 }
michael@0 1781
michael@0 1782 // Compute the logical block size of the frame
michael@0 1783 nscoord logicalBSize;
michael@0 1784 PerSpanData* frameSpan = pfd->mSpan;
michael@0 1785 if (frameSpan) {
michael@0 1786 // For span frames the logical-block-size and start-leading were
michael@0 1787 // pre-computed when the span was reflowed.
michael@0 1788 logicalBSize = frameSpan->mLogicalBSize;
michael@0 1789 }
michael@0 1790 else {
michael@0 1791 // For other elements the logical block size is the same as the
michael@0 1792 // frame's block size plus its margins.
michael@0 1793 logicalBSize = pfd->mBounds.BSize(lineWM) +
michael@0 1794 pfd->mMargin.BStartEnd(frameWM);
michael@0 1795 if (logicalBSize < 0 &&
michael@0 1796 mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
michael@0 1797 pfd->mAscent -= logicalBSize;
michael@0 1798 logicalBSize = 0;
michael@0 1799 }
michael@0 1800 }
michael@0 1801
michael@0 1802 // Get vertical-align property ("vertical-align" is the CSS name for
michael@0 1803 // block-direction align)
michael@0 1804 const nsStyleCoord& verticalAlign =
michael@0 1805 frame->StyleTextReset()->mVerticalAlign;
michael@0 1806 uint8_t verticalAlignEnum = frame->VerticalAlignEnum();
michael@0 1807 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 1808 printf(" [frame]");
michael@0 1809 nsFrame::ListTag(stdout, frame);
michael@0 1810 printf(": verticalAlignUnit=%d (enum == %d",
michael@0 1811 verticalAlign.GetUnit(),
michael@0 1812 ((eStyleUnit_Enumerated == verticalAlign.GetUnit())
michael@0 1813 ? verticalAlign.GetIntValue()
michael@0 1814 : -1));
michael@0 1815 if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) {
michael@0 1816 printf(", after SVG dominant-baseline conversion == %d",
michael@0 1817 verticalAlignEnum);
michael@0 1818 }
michael@0 1819 printf(")\n");
michael@0 1820 #endif
michael@0 1821
michael@0 1822 if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) {
michael@0 1823 switch (verticalAlignEnum) {
michael@0 1824 default:
michael@0 1825 case NS_STYLE_VERTICAL_ALIGN_BASELINE:
michael@0 1826 {
michael@0 1827 // The element's baseline is aligned with the baseline of
michael@0 1828 // the parent.
michael@0 1829 pfd->mBounds.BStart(lineWM) = baselineBCoord - pfd->mAscent;
michael@0 1830 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1831 break;
michael@0 1832 }
michael@0 1833
michael@0 1834 case NS_STYLE_VERTICAL_ALIGN_SUB:
michael@0 1835 {
michael@0 1836 // Lower the baseline of the box to the subscript offset
michael@0 1837 // of the parent's box. This is identical to the baseline
michael@0 1838 // alignment except for the addition of the subscript
michael@0 1839 // offset to the baseline BCoord.
michael@0 1840 nscoord parentSubscript = fm->SubscriptOffset();
michael@0 1841 nscoord revisedBaselineBCoord = baselineBCoord + parentSubscript;
michael@0 1842 pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
michael@0 1843 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1844 break;
michael@0 1845 }
michael@0 1846
michael@0 1847 case NS_STYLE_VERTICAL_ALIGN_SUPER:
michael@0 1848 {
michael@0 1849 // Raise the baseline of the box to the superscript offset
michael@0 1850 // of the parent's box. This is identical to the baseline
michael@0 1851 // alignment except for the subtraction of the superscript
michael@0 1852 // offset to the baseline BCoord.
michael@0 1853 nscoord parentSuperscript = fm->SuperscriptOffset();
michael@0 1854 nscoord revisedBaselineBCoord = baselineBCoord - parentSuperscript;
michael@0 1855 pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
michael@0 1856 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1857 break;
michael@0 1858 }
michael@0 1859
michael@0 1860 case NS_STYLE_VERTICAL_ALIGN_TOP:
michael@0 1861 {
michael@0 1862 pfd->mBlockDirAlign = VALIGN_TOP;
michael@0 1863 nscoord subtreeBSize = logicalBSize;
michael@0 1864 if (frameSpan) {
michael@0 1865 subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord;
michael@0 1866 NS_ASSERTION(subtreeBSize >= logicalBSize,
michael@0 1867 "unexpected subtree block size");
michael@0 1868 }
michael@0 1869 if (subtreeBSize > maxStartBoxBSize) {
michael@0 1870 maxStartBoxBSize = subtreeBSize;
michael@0 1871 }
michael@0 1872 break;
michael@0 1873 }
michael@0 1874
michael@0 1875 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
michael@0 1876 {
michael@0 1877 pfd->mBlockDirAlign = VALIGN_BOTTOM;
michael@0 1878 nscoord subtreeBSize = logicalBSize;
michael@0 1879 if (frameSpan) {
michael@0 1880 subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord;
michael@0 1881 NS_ASSERTION(subtreeBSize >= logicalBSize,
michael@0 1882 "unexpected subtree block size");
michael@0 1883 }
michael@0 1884 if (subtreeBSize > maxEndBoxBSize) {
michael@0 1885 maxEndBoxBSize = subtreeBSize;
michael@0 1886 }
michael@0 1887 break;
michael@0 1888 }
michael@0 1889
michael@0 1890 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
michael@0 1891 {
michael@0 1892 // Align the midpoint of the frame with 1/2 the parents
michael@0 1893 // x-height above the baseline.
michael@0 1894 nscoord parentXHeight = fm->XHeight();
michael@0 1895 if (frameSpan) {
michael@0 1896 pfd->mBounds.BStart(lineWM) = baselineBCoord -
michael@0 1897 (parentXHeight + pfd->mBounds.BSize(lineWM))/2;
michael@0 1898 }
michael@0 1899 else {
michael@0 1900 pfd->mBounds.BStart(lineWM) = baselineBCoord -
michael@0 1901 (parentXHeight + logicalBSize)/2 +
michael@0 1902 pfd->mMargin.BStart(frameWM);
michael@0 1903 }
michael@0 1904 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1905 break;
michael@0 1906 }
michael@0 1907
michael@0 1908 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
michael@0 1909 {
michael@0 1910 // The top of the logical box is aligned with the top of
michael@0 1911 // the parent element's text.
michael@0 1912 nscoord parentAscent = fm->MaxAscent();
michael@0 1913 if (frameSpan) {
michael@0 1914 pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent -
michael@0 1915 pfd->mBorderPadding.BStart(frameWM) + frameSpan->mBStartLeading;
michael@0 1916 }
michael@0 1917 else {
michael@0 1918 pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent +
michael@0 1919 pfd->mMargin.BStart(frameWM);
michael@0 1920 }
michael@0 1921 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1922 break;
michael@0 1923 }
michael@0 1924
michael@0 1925 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
michael@0 1926 {
michael@0 1927 // The bottom of the logical box is aligned with the
michael@0 1928 // bottom of the parent elements text.
michael@0 1929 nscoord parentDescent = fm->MaxDescent();
michael@0 1930 if (frameSpan) {
michael@0 1931 pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
michael@0 1932 pfd->mBounds.BSize(lineWM) +
michael@0 1933 pfd->mBorderPadding.BEnd(frameWM) -
michael@0 1934 frameSpan->mBEndLeading;
michael@0 1935 }
michael@0 1936 else {
michael@0 1937 pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
michael@0 1938 pfd->mBounds.BSize(lineWM) -
michael@0 1939 pfd->mMargin.BEnd(frameWM);
michael@0 1940 }
michael@0 1941 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1942 break;
michael@0 1943 }
michael@0 1944
michael@0 1945 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
michael@0 1946 {
michael@0 1947 // Align the midpoint of the frame with the baseline of the parent.
michael@0 1948 if (frameSpan) {
michael@0 1949 pfd->mBounds.BStart(lineWM) = baselineBCoord -
michael@0 1950 pfd->mBounds.BSize(lineWM)/2;
michael@0 1951 }
michael@0 1952 else {
michael@0 1953 pfd->mBounds.BStart(lineWM) = baselineBCoord - logicalBSize/2 +
michael@0 1954 pfd->mMargin.BStart(frameWM);
michael@0 1955 }
michael@0 1956 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1957 break;
michael@0 1958 }
michael@0 1959 }
michael@0 1960 } else {
michael@0 1961 // We have either a coord, a percent, or a calc().
michael@0 1962 nscoord pctBasis = 0;
michael@0 1963 if (verticalAlign.HasPercent()) {
michael@0 1964 // Percentages are like lengths, except treated as a percentage
michael@0 1965 // of the elements line block size value.
michael@0 1966 float inflation =
michael@0 1967 GetInflationForBlockDirAlignment(frame, mInflationMinFontSize);
michael@0 1968 pctBasis = nsHTMLReflowState::CalcLineHeight(frame->GetContent(),
michael@0 1969 frame->StyleContext(), mBlockReflowState->ComputedBSize(),
michael@0 1970 inflation);
michael@0 1971 }
michael@0 1972 nscoord offset =
michael@0 1973 nsRuleNode::ComputeCoordPercentCalc(verticalAlign, pctBasis);
michael@0 1974 // According to the CSS2 spec (10.8.1), a positive value
michael@0 1975 // "raises" the box by the given distance while a negative value
michael@0 1976 // "lowers" the box by the given distance (with zero being the
michael@0 1977 // baseline). Since Y coordinates increase towards the bottom of
michael@0 1978 // the screen we reverse the sign.
michael@0 1979 nscoord revisedBaselineBCoord = baselineBCoord - offset;
michael@0 1980 pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
michael@0 1981 pfd->mBlockDirAlign = VALIGN_OTHER;
michael@0 1982 }
michael@0 1983
michael@0 1984 // Update minBCoord/maxBCoord for frames that we just placed. Do not factor
michael@0 1985 // text into the equation.
michael@0 1986 if (pfd->mBlockDirAlign == VALIGN_OTHER) {
michael@0 1987 // Text frames do not contribute to the min/max Y values for the
michael@0 1988 // line (instead their parent frame's font-size contributes).
michael@0 1989 // XXXrbs -- relax this restriction because it causes text frames
michael@0 1990 // to jam together when 'font-size-adjust' is enabled
michael@0 1991 // and layout is using dynamic font heights (bug 20394)
michael@0 1992 // -- Note #1: With this code enabled and with the fact that we are not
michael@0 1993 // using Em[Ascent|Descent] as nsDimensions for text metrics in
michael@0 1994 // GFX mean that the discussion in bug 13072 cannot hold.
michael@0 1995 // -- Note #2: We still don't want empty-text frames to interfere.
michael@0 1996 // For example in quirks mode, avoiding empty text frames prevents
michael@0 1997 // "tall" lines around elements like <hr> since the rules of <hr>
michael@0 1998 // in quirks.css have pseudo text contents with LF in them.
michael@0 1999 #if 0
michael@0 2000 if (!pfd->GetFlag(PFD_ISTEXTFRAME)) {
michael@0 2001 #else
michael@0 2002 // Only consider non empty text frames when line-height=normal
michael@0 2003 bool canUpdate = !pfd->GetFlag(PFD_ISTEXTFRAME);
michael@0 2004 if (!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) {
michael@0 2005 canUpdate =
michael@0 2006 frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal;
michael@0 2007 }
michael@0 2008 if (canUpdate) {
michael@0 2009 #endif
michael@0 2010 nscoord blockStart, blockEnd;
michael@0 2011 if (frameSpan) {
michael@0 2012 // For spans that were are now placing, use their position
michael@0 2013 // plus their already computed min-Y and max-Y values for
michael@0 2014 // computing blockStart and blockEnd.
michael@0 2015 blockStart = pfd->mBounds.BStart(lineWM) + frameSpan->mMinBCoord;
michael@0 2016 blockEnd = pfd->mBounds.BStart(lineWM) + frameSpan->mMaxBCoord;
michael@0 2017 }
michael@0 2018 else {
michael@0 2019 blockStart = pfd->mBounds.BStart(lineWM) -
michael@0 2020 pfd->mMargin.BStart(frameWM);
michael@0 2021 blockEnd = blockStart + logicalBSize;
michael@0 2022 }
michael@0 2023 if (!preMode &&
michael@0 2024 mPresContext->CompatibilityMode() != eCompatibility_FullStandards &&
michael@0 2025 !logicalBSize) {
michael@0 2026 // Check if it's a BR frame that is not alone on its line (it
michael@0 2027 // is given a block size of zero to indicate this), and if so reset
michael@0 2028 // blockStart and blockEnd so that BR frames don't influence the line.
michael@0 2029 if (nsGkAtoms::brFrame == frame->GetType()) {
michael@0 2030 blockStart = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM;
michael@0 2031 blockEnd = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM;
michael@0 2032 }
michael@0 2033 }
michael@0 2034 if (blockStart < minBCoord) minBCoord = blockStart;
michael@0 2035 if (blockEnd > maxBCoord) maxBCoord = blockEnd;
michael@0 2036 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2037 printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n",
michael@0 2038 pfd->mAscent, pfd->mBounds.BSize(lineWM),
michael@0 2039 pfd->mBorderPadding.Top(frameWM),
michael@0 2040 pfd->mBorderPadding.Bottom(frameWM),
michael@0 2041 logicalBSize,
michael@0 2042 frameSpan ? frameSpan->mBStartLeading : 0,
michael@0 2043 pfd->mBounds.BStart(lineWM), minBCoord, maxBCoord);
michael@0 2044 #endif
michael@0 2045 }
michael@0 2046 if (psd != mRootSpan) {
michael@0 2047 frame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 2048 }
michael@0 2049 }
michael@0 2050 pfd = pfd->mNext;
michael@0 2051 }
michael@0 2052
michael@0 2053 // Factor in the minimum line block-size when handling the root-span for
michael@0 2054 // the block.
michael@0 2055 if (psd == mRootSpan) {
michael@0 2056 // We should factor in the block element's minimum line-height (as
michael@0 2057 // defined in section 10.8.1 of the css2 spec) assuming that
michael@0 2058 // mZeroEffectiveSpanBox is not set on the root span. This only happens
michael@0 2059 // in some cases in quirks mode:
michael@0 2060 // (1) if the root span contains non-whitespace text directly (this
michael@0 2061 // is handled by mZeroEffectiveSpanBox
michael@0 2062 // (2) if this line has a bullet
michael@0 2063 // (3) if this is the last line of an LI, DT, or DD element
michael@0 2064 // (The last line before a block also counts, but not before a
michael@0 2065 // BR) (NN4/IE5 quirk)
michael@0 2066
michael@0 2067 // (1) and (2) above
michael@0 2068 bool applyMinLH = !psd->mZeroEffectiveSpanBox || mHasBullet;
michael@0 2069 bool isLastLine = (!mLineBox->IsLineWrapped() && !mLineEndsInBR);
michael@0 2070 if (!applyMinLH && isLastLine) {
michael@0 2071 nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent();
michael@0 2072 if (blockContent) {
michael@0 2073 nsIAtom *blockTagAtom = blockContent->Tag();
michael@0 2074 // (3) above, if the last line of LI, DT, or DD
michael@0 2075 if (blockTagAtom == nsGkAtoms::li ||
michael@0 2076 blockTagAtom == nsGkAtoms::dt ||
michael@0 2077 blockTagAtom == nsGkAtoms::dd) {
michael@0 2078 applyMinLH = true;
michael@0 2079 }
michael@0 2080 }
michael@0 2081 }
michael@0 2082 if (applyMinLH) {
michael@0 2083 if (psd->mHasNonemptyContent || preMode || mHasBullet) {
michael@0 2084 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2085 printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d", minBCoord, maxBCoord);
michael@0 2086 #endif
michael@0 2087 nscoord minimumLineBSize = mMinLineBSize;
michael@0 2088 nscoord blockStart =
michael@0 2089 -nsLayoutUtils::GetCenteredFontBaseline(fm, minimumLineBSize);
michael@0 2090 nscoord blockEnd = blockStart + minimumLineBSize;
michael@0 2091
michael@0 2092 if (blockStart < minBCoord) minBCoord = blockStart;
michael@0 2093 if (blockEnd > maxBCoord) maxBCoord = blockEnd;
michael@0 2094
michael@0 2095 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2096 printf(" new values: %d,%d\n", minBCoord, maxBCoord);
michael@0 2097 #endif
michael@0 2098 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2099 printf(" Used mMinLineBSize: %d, blockStart: %d, blockEnd: %d\n", mMinLineBSize, blockStart, blockEnd);
michael@0 2100 #endif
michael@0 2101 }
michael@0 2102 else {
michael@0 2103 // XXX issues:
michael@0 2104 // [1] BR's on empty lines stop working
michael@0 2105 // [2] May not honor css2's notion of handling empty elements
michael@0 2106 // [3] blank lines in a pre-section ("\n") (handled with preMode)
michael@0 2107
michael@0 2108 // XXX Are there other problems with this?
michael@0 2109 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2110 printf(" [span]==> zapping min/maxBCoord: currentValues: %d,%d newValues: 0,0\n",
michael@0 2111 minBCoord, maxBCoord);
michael@0 2112 #endif
michael@0 2113 minBCoord = maxBCoord = 0;
michael@0 2114 }
michael@0 2115 }
michael@0 2116 }
michael@0 2117
michael@0 2118 if ((minBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM) ||
michael@0 2119 (maxBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM)) {
michael@0 2120 minBCoord = maxBCoord = baselineBCoord;
michael@0 2121 }
michael@0 2122
michael@0 2123 if ((psd != mRootSpan) && (psd->mZeroEffectiveSpanBox)) {
michael@0 2124 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2125 printf(" [span]adjusting for zeroEffectiveSpanBox\n");
michael@0 2126 printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
michael@0 2127 minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(frameWM),
michael@0 2128 spanFramePFD->mAscent,
michael@0 2129 psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading);
michael@0 2130 #endif
michael@0 2131 nscoord goodMinBCoord = spanFramePFD->mBorderPadding.BStart(frameWM) -
michael@0 2132 psd->mBStartLeading;
michael@0 2133 nscoord goodMaxBCoord = goodMinBCoord + psd->mLogicalBSize;
michael@0 2134
michael@0 2135 // For cases like the one in bug 714519 (text-decoration placement
michael@0 2136 // or making nsLineLayout::IsZeroBSize() handle
michael@0 2137 // vertical-align:top/bottom on a descendant of the line that's not
michael@0 2138 // a child of it), we want to treat elements that are
michael@0 2139 // vertical-align: top or bottom somewhat like children for the
michael@0 2140 // purposes of this quirk. To some extent, this is guessing, since
michael@0 2141 // they might end up being aligned anywhere. However, we'll guess
michael@0 2142 // that they'll be placed aligned with the top or bottom of this
michael@0 2143 // frame (as though this frame is the only thing in the line).
michael@0 2144 // (Guessing isn't crazy, since all we're doing is reducing the
michael@0 2145 // scope of a quirk and making the behavior more standards-like.)
michael@0 2146 if (maxStartBoxBSize > maxBCoord - minBCoord) {
michael@0 2147 // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and
michael@0 2148 // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or
michael@0 2149 // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord.
michael@0 2150 nscoord distribute = maxStartBoxBSize - (maxBCoord - minBCoord);
michael@0 2151 nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0);
michael@0 2152 if (distribute > ascentSpace) {
michael@0 2153 distribute -= ascentSpace;
michael@0 2154 minBCoord -= ascentSpace;
michael@0 2155 nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0);
michael@0 2156 if (distribute > descentSpace) {
michael@0 2157 maxBCoord += descentSpace;
michael@0 2158 } else {
michael@0 2159 maxBCoord += distribute;
michael@0 2160 }
michael@0 2161 } else {
michael@0 2162 minBCoord -= distribute;
michael@0 2163 }
michael@0 2164 }
michael@0 2165 if (maxEndBoxBSize > maxBCoord - minBCoord) {
michael@0 2166 // Likewise, but preferring descent to ascent.
michael@0 2167 nscoord distribute = maxEndBoxBSize - (maxBCoord - minBCoord);
michael@0 2168 nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0);
michael@0 2169 if (distribute > descentSpace) {
michael@0 2170 distribute -= descentSpace;
michael@0 2171 maxBCoord += descentSpace;
michael@0 2172 nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0);
michael@0 2173 if (distribute > ascentSpace) {
michael@0 2174 minBCoord -= ascentSpace;
michael@0 2175 } else {
michael@0 2176 minBCoord -= distribute;
michael@0 2177 }
michael@0 2178 } else {
michael@0 2179 maxBCoord += distribute;
michael@0 2180 }
michael@0 2181 }
michael@0 2182
michael@0 2183 if (minBCoord > goodMinBCoord) {
michael@0 2184 nscoord adjust = minBCoord - goodMinBCoord; // positive
michael@0 2185
michael@0 2186 // shrink the logical extents
michael@0 2187 psd->mLogicalBSize -= adjust;
michael@0 2188 psd->mBStartLeading -= adjust;
michael@0 2189 }
michael@0 2190 if (maxBCoord < goodMaxBCoord) {
michael@0 2191 nscoord adjust = goodMaxBCoord - maxBCoord;
michael@0 2192 psd->mLogicalBSize -= adjust;
michael@0 2193 psd->mBEndLeading -= adjust;
michael@0 2194 }
michael@0 2195 if (minBCoord > 0) {
michael@0 2196
michael@0 2197 // shrink the content by moving its block start down. This is tricky,
michael@0 2198 // since the block start is the 0 for many coordinates, so what we do is
michael@0 2199 // move everything else up.
michael@0 2200 spanFramePFD->mAscent -= minBCoord; // move the baseline up
michael@0 2201 spanFramePFD->mBounds.BSize(lineWM) -= minBCoord; // move the block end up
michael@0 2202 psd->mBStartLeading += minBCoord;
michael@0 2203 *psd->mBaseline -= minBCoord;
michael@0 2204
michael@0 2205 pfd = psd->mFirstFrame;
michael@0 2206 while (nullptr != pfd) {
michael@0 2207 pfd->mBounds.BStart(lineWM) -= minBCoord; // move all the children
michael@0 2208 // back up
michael@0 2209 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 2210 pfd = pfd->mNext;
michael@0 2211 }
michael@0 2212 maxBCoord -= minBCoord; // since minBCoord is in the frame's own
michael@0 2213 // coordinate system
michael@0 2214 minBCoord = 0;
michael@0 2215 }
michael@0 2216 if (maxBCoord < spanFramePFD->mBounds.BSize(lineWM)) {
michael@0 2217 nscoord adjust = spanFramePFD->mBounds.BSize(lineWM) - maxBCoord;
michael@0 2218 spanFramePFD->mBounds.BSize(lineWM) -= adjust; // move the bottom up
michael@0 2219 psd->mBEndLeading += adjust;
michael@0 2220 }
michael@0 2221 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2222 printf(" New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
michael@0 2223 minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM),
michael@0 2224 spanFramePFD->mAscent,
michael@0 2225 psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading);
michael@0 2226 #endif
michael@0 2227 }
michael@0 2228
michael@0 2229 psd->mMinBCoord = minBCoord;
michael@0 2230 psd->mMaxBCoord = maxBCoord;
michael@0 2231 #ifdef NOISY_BLOCKDIR_ALIGN
michael@0 2232 printf(" [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d maxEndBoxBSize=%d\n",
michael@0 2233 minBCoord, maxBCoord, maxBCoord - minBCoord, maxStartBoxBSize, maxEndBoxBSize);
michael@0 2234 #endif
michael@0 2235 if (maxStartBoxBSize > mMaxStartBoxBSize) {
michael@0 2236 mMaxStartBoxBSize = maxStartBoxBSize;
michael@0 2237 }
michael@0 2238 if (maxEndBoxBSize > mMaxEndBoxBSize) {
michael@0 2239 mMaxEndBoxBSize = maxEndBoxBSize;
michael@0 2240 }
michael@0 2241 }
michael@0 2242
michael@0 2243 static void SlideSpanFrameRect(nsIFrame* aFrame, nscoord aDeltaWidth)
michael@0 2244 {
michael@0 2245 // This should not use nsIFrame::MovePositionBy because it happens
michael@0 2246 // prior to relative positioning. In particular, because
michael@0 2247 // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace()
michael@0 2248 // prior to calling aLineLayout.RelativePositionFrames().
michael@0 2249 nsPoint p = aFrame->GetPosition();
michael@0 2250 p.x -= aDeltaWidth;
michael@0 2251 aFrame->SetPosition(p);
michael@0 2252 }
michael@0 2253
michael@0 2254 bool
michael@0 2255 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
michael@0 2256 nscoord* aDeltaISize)
michael@0 2257 {
michael@0 2258 PerFrameData* pfd = psd->mFirstFrame;
michael@0 2259 if (!pfd) {
michael@0 2260 *aDeltaISize = 0;
michael@0 2261 return false;
michael@0 2262 }
michael@0 2263 pfd = pfd->Last();
michael@0 2264 while (nullptr != pfd) {
michael@0 2265 #ifdef REALLY_NOISY_TRIM
michael@0 2266 nsFrame::ListTag(stdout, (psd == mRootSpan
michael@0 2267 ? mBlockReflowState->frame
michael@0 2268 : psd->mFrame->mFrame));
michael@0 2269 printf(": attempting trim of ");
michael@0 2270 nsFrame::ListTag(stdout, pfd->mFrame);
michael@0 2271 printf("\n");
michael@0 2272 #endif
michael@0 2273 PerSpanData* childSpan = pfd->mSpan;
michael@0 2274 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 2275 if (childSpan) {
michael@0 2276 // Maybe the child span has the trailing white-space in it?
michael@0 2277 if (TrimTrailingWhiteSpaceIn(childSpan, aDeltaISize)) {
michael@0 2278 nscoord deltaISize = *aDeltaISize;
michael@0 2279 if (deltaISize) {
michael@0 2280 // Adjust the child spans frame size
michael@0 2281 pfd->mBounds.ISize(lineWM) -= deltaISize;
michael@0 2282 if (psd != mRootSpan) {
michael@0 2283 // When the child span is not a direct child of the block
michael@0 2284 // we need to update the child spans frame rectangle
michael@0 2285 // because it most likely will not be done again. Spans
michael@0 2286 // that are direct children of the block will be updated
michael@0 2287 // later, however, because the BlockDirAlignFrames method
michael@0 2288 // will be run after this method.
michael@0 2289 nsIFrame* f = pfd->mFrame;
michael@0 2290 LogicalRect r(lineWM, f->GetRect(), mContainerWidth);
michael@0 2291 r.ISize(lineWM) -= deltaISize;
michael@0 2292 f->SetRect(lineWM, r, mContainerWidth);
michael@0 2293 }
michael@0 2294
michael@0 2295 // Adjust the inline end edge of the span that contains the child span
michael@0 2296 psd->mICoord -= deltaISize;
michael@0 2297
michael@0 2298 // Slide any frames that follow the child span over by the
michael@0 2299 // correct amount. The only thing that can follow the child
michael@0 2300 // span is empty stuff, so we are just making things
michael@0 2301 // sensible (keeping the combined area honest).
michael@0 2302 while (pfd->mNext) {
michael@0 2303 pfd = pfd->mNext;
michael@0 2304 pfd->mBounds.IStart(lineWM) -= deltaISize;
michael@0 2305 if (psd != mRootSpan) {
michael@0 2306 // When the child span is not a direct child of the block
michael@0 2307 // we need to update the child span's frame rectangle
michael@0 2308 // because it most likely will not be done again. Spans
michael@0 2309 // that are direct children of the block will be updated
michael@0 2310 // later, however, because the BlockDirAlignFrames method
michael@0 2311 // will be run after this method.
michael@0 2312 SlideSpanFrameRect(pfd->mFrame, deltaISize);
michael@0 2313 }
michael@0 2314 }
michael@0 2315 }
michael@0 2316 return true;
michael@0 2317 }
michael@0 2318 }
michael@0 2319 else if (!pfd->GetFlag(PFD_ISTEXTFRAME) &&
michael@0 2320 !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) {
michael@0 2321 // If we hit a frame on the end that's not text and not a placeholder,
michael@0 2322 // then there is no trailing whitespace to trim. Stop the search.
michael@0 2323 *aDeltaISize = 0;
michael@0 2324 return true;
michael@0 2325 }
michael@0 2326 else if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
michael@0 2327 // Call TrimTrailingWhiteSpace even on empty textframes because they
michael@0 2328 // might have a soft hyphen which should now appear, changing the frame's
michael@0 2329 // width
michael@0 2330 nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)->
michael@0 2331 TrimTrailingWhiteSpace(mBlockReflowState->rendContext);
michael@0 2332 #ifdef NOISY_TRIM
michael@0 2333 nsFrame::ListTag(stdout, (psd == mRootSpan
michael@0 2334 ? mBlockReflowState->frame
michael@0 2335 : psd->mFrame->mFrame));
michael@0 2336 printf(": trim of ");
michael@0 2337 nsFrame::ListTag(stdout, pfd->mFrame);
michael@0 2338 printf(" returned %d\n", trimOutput.mDeltaWidth);
michael@0 2339 #endif
michael@0 2340 if (trimOutput.mLastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) {
michael@0 2341 pfd->mJustificationNumSpaces--;
michael@0 2342 }
michael@0 2343
michael@0 2344 if (trimOutput.mChanged) {
michael@0 2345 pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true);
michael@0 2346 }
michael@0 2347
michael@0 2348 if (trimOutput.mDeltaWidth) {
michael@0 2349 pfd->mBounds.ISize(lineWM) -= trimOutput.mDeltaWidth;
michael@0 2350
michael@0 2351 // See if the text frame has already been placed in its parent
michael@0 2352 if (psd != mRootSpan) {
michael@0 2353 // The frame was already placed during psd's
michael@0 2354 // reflow. Update the frames rectangle now.
michael@0 2355 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 2356 }
michael@0 2357
michael@0 2358 // Adjust containing span's right edge
michael@0 2359 psd->mICoord -= trimOutput.mDeltaWidth;
michael@0 2360
michael@0 2361 // Slide any frames that follow the text frame over by the
michael@0 2362 // right amount. The only thing that can follow the text
michael@0 2363 // frame is empty stuff, so we are just making things
michael@0 2364 // sensible (keeping the combined area honest).
michael@0 2365 while (pfd->mNext) {
michael@0 2366 pfd = pfd->mNext;
michael@0 2367 pfd->mBounds.IStart(lineWM) -= trimOutput.mDeltaWidth;
michael@0 2368 if (psd != mRootSpan) {
michael@0 2369 // When the child span is not a direct child of the block
michael@0 2370 // we need to update the child spans frame rectangle
michael@0 2371 // because it most likely will not be done again. Spans
michael@0 2372 // that are direct children of the block will be updated
michael@0 2373 // later, however, because the BlockDirAlignFrames method
michael@0 2374 // will be run after this method.
michael@0 2375 SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth);
michael@0 2376 }
michael@0 2377 }
michael@0 2378 }
michael@0 2379
michael@0 2380 if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) || trimOutput.mChanged) {
michael@0 2381 // Pass up to caller so they can shrink their span
michael@0 2382 *aDeltaISize = trimOutput.mDeltaWidth;
michael@0 2383 return true;
michael@0 2384 }
michael@0 2385 }
michael@0 2386 pfd = pfd->mPrev;
michael@0 2387 }
michael@0 2388
michael@0 2389 *aDeltaISize = 0;
michael@0 2390 return false;
michael@0 2391 }
michael@0 2392
michael@0 2393 bool
michael@0 2394 nsLineLayout::TrimTrailingWhiteSpace()
michael@0 2395 {
michael@0 2396 PerSpanData* psd = mRootSpan;
michael@0 2397 nscoord deltaISize;
michael@0 2398 TrimTrailingWhiteSpaceIn(psd, &deltaISize);
michael@0 2399 return 0 != deltaISize;
michael@0 2400 }
michael@0 2401
michael@0 2402 void
michael@0 2403 nsLineLayout::ComputeJustificationWeights(PerSpanData* aPSD,
michael@0 2404 int32_t* aNumSpaces,
michael@0 2405 int32_t* aNumLetters)
michael@0 2406 {
michael@0 2407 NS_ASSERTION(aPSD, "null arg");
michael@0 2408 NS_ASSERTION(aNumSpaces, "null arg");
michael@0 2409 NS_ASSERTION(aNumLetters, "null arg");
michael@0 2410 int32_t numSpaces = 0;
michael@0 2411 int32_t numLetters = 0;
michael@0 2412
michael@0 2413 for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) {
michael@0 2414
michael@0 2415 if (true == pfd->GetFlag(PFD_ISTEXTFRAME)) {
michael@0 2416 numSpaces += pfd->mJustificationNumSpaces;
michael@0 2417 numLetters += pfd->mJustificationNumLetters;
michael@0 2418 }
michael@0 2419 else if (pfd->mSpan != nullptr) {
michael@0 2420 int32_t spanSpaces;
michael@0 2421 int32_t spanLetters;
michael@0 2422
michael@0 2423 ComputeJustificationWeights(pfd->mSpan, &spanSpaces, &spanLetters);
michael@0 2424
michael@0 2425 numSpaces += spanSpaces;
michael@0 2426 numLetters += spanLetters;
michael@0 2427 }
michael@0 2428 }
michael@0 2429
michael@0 2430 *aNumSpaces = numSpaces;
michael@0 2431 *aNumLetters = numLetters;
michael@0 2432 }
michael@0 2433
michael@0 2434 nscoord
michael@0 2435 nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState)
michael@0 2436 {
michael@0 2437 NS_ASSERTION(aPSD, "null arg");
michael@0 2438 NS_ASSERTION(aState, "null arg");
michael@0 2439
michael@0 2440 nscoord deltaICoord = 0;
michael@0 2441 for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) {
michael@0 2442 // Don't reposition bullets (and other frames that occur out of X-order?)
michael@0 2443 if (!pfd->GetFlag(PFD_ISBULLET)) {
michael@0 2444 nscoord dw = 0;
michael@0 2445 WritingMode lineWM = mRootSpan->mWritingMode;
michael@0 2446
michael@0 2447 pfd->mBounds.IStart(lineWM) += deltaICoord;
michael@0 2448
michael@0 2449 if (true == pfd->GetFlag(PFD_ISTEXTFRAME)) {
michael@0 2450 if (aState->mTotalWidthForSpaces > 0 &&
michael@0 2451 aState->mTotalNumSpaces > 0) {
michael@0 2452 aState->mNumSpacesProcessed += pfd->mJustificationNumSpaces;
michael@0 2453
michael@0 2454 nscoord newAllocatedWidthForSpaces =
michael@0 2455 (aState->mTotalWidthForSpaces*aState->mNumSpacesProcessed)
michael@0 2456 /aState->mTotalNumSpaces;
michael@0 2457
michael@0 2458 dw += newAllocatedWidthForSpaces - aState->mWidthForSpacesProcessed;
michael@0 2459
michael@0 2460 aState->mWidthForSpacesProcessed = newAllocatedWidthForSpaces;
michael@0 2461 }
michael@0 2462
michael@0 2463 if (aState->mTotalWidthForLetters > 0 &&
michael@0 2464 aState->mTotalNumLetters > 0) {
michael@0 2465 aState->mNumLettersProcessed += pfd->mJustificationNumLetters;
michael@0 2466
michael@0 2467 nscoord newAllocatedWidthForLetters =
michael@0 2468 (aState->mTotalWidthForLetters*aState->mNumLettersProcessed)
michael@0 2469 /aState->mTotalNumLetters;
michael@0 2470
michael@0 2471 dw += newAllocatedWidthForLetters - aState->mWidthForLettersProcessed;
michael@0 2472
michael@0 2473 aState->mWidthForLettersProcessed = newAllocatedWidthForLetters;
michael@0 2474 }
michael@0 2475
michael@0 2476 if (dw) {
michael@0 2477 pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true);
michael@0 2478 }
michael@0 2479 }
michael@0 2480 else {
michael@0 2481 if (nullptr != pfd->mSpan) {
michael@0 2482 dw += ApplyFrameJustification(pfd->mSpan, aState);
michael@0 2483 }
michael@0 2484 }
michael@0 2485
michael@0 2486 pfd->mBounds.ISize(lineWM) += dw;
michael@0 2487
michael@0 2488 deltaICoord += dw;
michael@0 2489 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 2490 }
michael@0 2491 }
michael@0 2492 return deltaICoord;
michael@0 2493 }
michael@0 2494
michael@0 2495 void
michael@0 2496 nsLineLayout::InlineDirAlignFrames(nsLineBox* aLine,
michael@0 2497 bool aIsLastLine)
michael@0 2498 {
michael@0 2499 /**
michael@0 2500 * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
michael@0 2501 * only in cases where the last line needs special handling.
michael@0 2502 */
michael@0 2503 PerSpanData* psd = mRootSpan;
michael@0 2504 WritingMode lineWM = psd->mWritingMode;
michael@0 2505 NS_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE,
michael@0 2506 "have unconstrained width; this should only result from "
michael@0 2507 "very large sizes, not attempts at intrinsic width "
michael@0 2508 "calculation");
michael@0 2509 nscoord availISize = psd->mIEnd - psd->mIStart;
michael@0 2510 nscoord remainingISize = availISize - aLine->ISize();
michael@0 2511 #ifdef NOISY_INLINEDIR_ALIGN
michael@0 2512 nsFrame::ListTag(stdout, mBlockReflowState->frame);
michael@0 2513 printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n",
michael@0 2514 availISize, aLine->IStart(), aLine->ISize(), remainingISize);
michael@0 2515 #endif
michael@0 2516
michael@0 2517 // 'text-align-last: auto' is equivalent to the value of the 'text-align'
michael@0 2518 // property except when 'text-align' is set to 'justify', in which case it
michael@0 2519 // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise.
michael@0 2520 //
michael@0 2521 // XXX: the code below will have to change when we implement text-justify
michael@0 2522 //
michael@0 2523 nscoord dx = 0;
michael@0 2524 uint8_t textAlign = mStyleText->mTextAlign;
michael@0 2525 bool textAlignTrue = mStyleText->mTextAlignTrue;
michael@0 2526 if (aIsLastLine) {
michael@0 2527 textAlignTrue = mStyleText->mTextAlignLastTrue;
michael@0 2528 if (mStyleText->mTextAlignLast == NS_STYLE_TEXT_ALIGN_AUTO) {
michael@0 2529 if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
michael@0 2530 textAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
michael@0 2531 }
michael@0 2532 } else {
michael@0 2533 textAlign = mStyleText->mTextAlignLast;
michael@0 2534 }
michael@0 2535 }
michael@0 2536
michael@0 2537 if ((remainingISize > 0 || textAlignTrue) &&
michael@0 2538 !(mBlockReflowState->frame->IsSVGText())) {
michael@0 2539
michael@0 2540 switch (textAlign) {
michael@0 2541 case NS_STYLE_TEXT_ALIGN_JUSTIFY:
michael@0 2542 int32_t numSpaces;
michael@0 2543 int32_t numLetters;
michael@0 2544
michael@0 2545 ComputeJustificationWeights(psd, &numSpaces, &numLetters);
michael@0 2546
michael@0 2547 if (numSpaces > 0) {
michael@0 2548 FrameJustificationState state =
michael@0 2549 { numSpaces, numLetters, remainingISize, 0, 0, 0, 0, 0 };
michael@0 2550
michael@0 2551 // Apply the justification, and make sure to update our linebox
michael@0 2552 // width to account for it.
michael@0 2553 aLine->ExpandBy(ApplyFrameJustification(psd, &state),
michael@0 2554 mContainerWidth);
michael@0 2555 remainingISize = availISize - aLine->ISize();
michael@0 2556 break;
michael@0 2557 }
michael@0 2558 // Fall through to the default case if we could not justify to fill
michael@0 2559 // the space.
michael@0 2560
michael@0 2561 case NS_STYLE_TEXT_ALIGN_DEFAULT:
michael@0 2562 // default alignment is to start edge so do nothing
michael@0 2563 break;
michael@0 2564
michael@0 2565 case NS_STYLE_TEXT_ALIGN_LEFT:
michael@0 2566 case NS_STYLE_TEXT_ALIGN_MOZ_LEFT:
michael@0 2567 if (!lineWM.IsBidiLTR()) {
michael@0 2568 dx = remainingISize;
michael@0 2569 }
michael@0 2570 break;
michael@0 2571
michael@0 2572 case NS_STYLE_TEXT_ALIGN_RIGHT:
michael@0 2573 case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT:
michael@0 2574 if (lineWM.IsBidiLTR()) {
michael@0 2575 dx = remainingISize;
michael@0 2576 }
michael@0 2577 break;
michael@0 2578
michael@0 2579 case NS_STYLE_TEXT_ALIGN_END:
michael@0 2580 dx = remainingISize;
michael@0 2581 break;
michael@0 2582
michael@0 2583
michael@0 2584 case NS_STYLE_TEXT_ALIGN_CENTER:
michael@0 2585 case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
michael@0 2586 dx = remainingISize / 2;
michael@0 2587 break;
michael@0 2588 }
michael@0 2589 }
michael@0 2590
michael@0 2591 if (dx) {
michael@0 2592 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 2593 pfd->mBounds.IStart(lineWM) += dx;
michael@0 2594 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
michael@0 2595 }
michael@0 2596 aLine->IndentBy(dx, mContainerWidth);
michael@0 2597 }
michael@0 2598
michael@0 2599 if (mPresContext->BidiEnabled() &&
michael@0 2600 (!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) {
michael@0 2601 nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame,
michael@0 2602 aLine->GetChildCount(),
michael@0 2603 lineWM, mContainerWidth);
michael@0 2604 }
michael@0 2605 }
michael@0 2606
michael@0 2607 void
michael@0 2608 nsLineLayout::RelativePositionFrames(nsOverflowAreas& aOverflowAreas)
michael@0 2609 {
michael@0 2610 RelativePositionFrames(mRootSpan, aOverflowAreas);
michael@0 2611 }
michael@0 2612
michael@0 2613 void
michael@0 2614 nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas)
michael@0 2615 {
michael@0 2616 nsOverflowAreas overflowAreas;
michael@0 2617 WritingMode wm = psd->mWritingMode;
michael@0 2618 if (nullptr != psd->mFrame) {
michael@0 2619 // The span's overflow areas come in three parts:
michael@0 2620 // -- this frame's width and height
michael@0 2621 // -- pfd->mOverflowAreas, which is the area of a bullet or the union
michael@0 2622 // of a relatively positioned frame's absolute children
michael@0 2623 // -- the bounds of all inline descendants
michael@0 2624 // The former two parts are computed right here, we gather the descendants
michael@0 2625 // below.
michael@0 2626 // At this point psd->mFrame->mBounds might be out of date since
michael@0 2627 // bidi reordering can move and resize the frames. So use the frame's
michael@0 2628 // rect instead of mBounds.
michael@0 2629 nsRect adjustedBounds(nsPoint(0, 0), psd->mFrame->mFrame->GetSize());
michael@0 2630
michael@0 2631 overflowAreas.ScrollableOverflow().UnionRect(
michael@0 2632 psd->mFrame->mOverflowAreas.ScrollableOverflow(), adjustedBounds);
michael@0 2633 overflowAreas.VisualOverflow().UnionRect(
michael@0 2634 psd->mFrame->mOverflowAreas.VisualOverflow(), adjustedBounds);
michael@0 2635 }
michael@0 2636 else {
michael@0 2637 LogicalRect rect(wm, psd->mIStart, mBStartEdge,
michael@0 2638 psd->mICoord - psd->mIStart, mFinalLineBSize);
michael@0 2639 // The minimum combined area for the frames that are direct
michael@0 2640 // children of the block starts at the upper left corner of the
michael@0 2641 // line and is sized to match the size of the line's bounding box
michael@0 2642 // (the same size as the values returned from BlockDirAlignFrames)
michael@0 2643 overflowAreas.VisualOverflow() = rect.GetPhysicalRect(wm, mContainerWidth);
michael@0 2644 overflowAreas.ScrollableOverflow() = overflowAreas.VisualOverflow();
michael@0 2645 }
michael@0 2646
michael@0 2647 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
michael@0 2648 nsIFrame* frame = pfd->mFrame;
michael@0 2649 nsPoint origin = frame->GetPosition();
michael@0 2650
michael@0 2651 // Adjust the origin of the frame
michael@0 2652 if (pfd->GetFlag(PFD_RELATIVEPOS)) {
michael@0 2653 //XXX temporary until ApplyRelativePositioning can handle logical offsets
michael@0 2654 nsMargin physicalOffsets =
michael@0 2655 pfd->mOffsets.GetPhysicalMargin(pfd->mFrame->GetWritingMode());
michael@0 2656 // right and bottom are handled by
michael@0 2657 // nsHTMLReflowState::ComputeRelativeOffsets
michael@0 2658 nsHTMLReflowState::ApplyRelativePositioning(pfd->mFrame,
michael@0 2659 physicalOffsets,
michael@0 2660 &origin);
michael@0 2661 frame->SetPosition(origin);
michael@0 2662 }
michael@0 2663
michael@0 2664 // We must position the view correctly before positioning its
michael@0 2665 // descendants so that widgets are positioned properly (since only
michael@0 2666 // some views have widgets).
michael@0 2667 if (frame->HasView())
michael@0 2668 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
michael@0 2669 frame->GetView(), pfd->mOverflowAreas.VisualOverflow(),
michael@0 2670 NS_FRAME_NO_SIZE_VIEW);
michael@0 2671
michael@0 2672 // Note: the combined area of a child is in its coordinate
michael@0 2673 // system. We adjust the childs combined area into our coordinate
michael@0 2674 // system before computing the aggregated value by adding in
michael@0 2675 // <b>x</b> and <b>y</b> which were computed above.
michael@0 2676 nsOverflowAreas r;
michael@0 2677 if (pfd->mSpan) {
michael@0 2678 // Compute a new combined area for the child span before
michael@0 2679 // aggregating it into our combined area.
michael@0 2680 RelativePositionFrames(pfd->mSpan, r);
michael@0 2681 } else {
michael@0 2682 r = pfd->mOverflowAreas;
michael@0 2683 if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
michael@0 2684 // We need to recompute overflow areas in two cases:
michael@0 2685 // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
michael@0 2686 // (2) When there are text decorations, since we can't recompute the
michael@0 2687 // overflow area until Reflow and BlockDirAlignLine have finished
michael@0 2688 if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW) ||
michael@0 2689 frame->StyleContext()->HasTextDecorationLines()) {
michael@0 2690 nsTextFrame* f = static_cast<nsTextFrame*>(frame);
michael@0 2691 r = f->RecomputeOverflow(*mBlockReflowState);
michael@0 2692 }
michael@0 2693 frame->FinishAndStoreOverflow(r, frame->GetSize());
michael@0 2694 }
michael@0 2695
michael@0 2696 // If we have something that's not an inline but with a complex frame
michael@0 2697 // hierarchy inside that contains views, they need to be
michael@0 2698 // positioned.
michael@0 2699 // All descendant views must be repositioned even if this frame
michael@0 2700 // does have a view in case this frame's view does not have a
michael@0 2701 // widget and some of the descendant views do have widgets --
michael@0 2702 // otherwise the widgets won't be repositioned.
michael@0 2703 nsContainerFrame::PositionChildViews(frame);
michael@0 2704 }
michael@0 2705
michael@0 2706 // Do this here (rather than along with setting the overflow rect
michael@0 2707 // below) so we get leaf frames as well. No need to worry
michael@0 2708 // about the root span, since it doesn't have a frame.
michael@0 2709 if (frame->HasView())
michael@0 2710 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
michael@0 2711 frame->GetView(),
michael@0 2712 r.VisualOverflow(),
michael@0 2713 NS_FRAME_NO_MOVE_VIEW);
michael@0 2714
michael@0 2715 overflowAreas.UnionWith(r + origin);
michael@0 2716 }
michael@0 2717
michael@0 2718 // If we just computed a spans combined area, we need to update its
michael@0 2719 // overflow rect...
michael@0 2720 if (psd->mFrame) {
michael@0 2721 PerFrameData* spanPFD = psd->mFrame;
michael@0 2722 nsIFrame* frame = spanPFD->mFrame;
michael@0 2723 frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize());
michael@0 2724 }
michael@0 2725 aOverflowAreas = overflowAreas;
michael@0 2726 }

mercurial