layout/generic/nsBlockFrame.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 // vim:cindent:ts=2:et:sw=2:
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * rendering object for CSS display:block, inline-block, and list-item
michael@0 9 * boxes, also used for various anonymous boxes
michael@0 10 */
michael@0 11
michael@0 12 #include "nsBlockFrame.h"
michael@0 13
michael@0 14 #include "mozilla/DebugOnly.h"
michael@0 15
michael@0 16 #include "nsCOMPtr.h"
michael@0 17 #include "nsAbsoluteContainingBlock.h"
michael@0 18 #include "nsBlockReflowContext.h"
michael@0 19 #include "nsBlockReflowState.h"
michael@0 20 #include "nsBulletFrame.h"
michael@0 21 #include "nsLineBox.h"
michael@0 22 #include "nsLineLayout.h"
michael@0 23 #include "nsPlaceholderFrame.h"
michael@0 24 #include "nsStyleConsts.h"
michael@0 25 #include "nsFrameManager.h"
michael@0 26 #include "nsPresContext.h"
michael@0 27 #include "nsIPresShell.h"
michael@0 28 #include "nsStyleContext.h"
michael@0 29 #include "nsHTMLParts.h"
michael@0 30 #include "nsGkAtoms.h"
michael@0 31 #include "nsGenericHTMLElement.h"
michael@0 32 #include "nsAttrValueInlines.h"
michael@0 33 #include "prprf.h"
michael@0 34 #include "nsFloatManager.h"
michael@0 35 #include "prenv.h"
michael@0 36 #include "plstr.h"
michael@0 37 #include "nsError.h"
michael@0 38 #include "nsAutoPtr.h"
michael@0 39 #include "nsIScrollableFrame.h"
michael@0 40 #include <algorithm>
michael@0 41 #ifdef ACCESSIBILITY
michael@0 42 #include "nsIDOMHTMLDocument.h"
michael@0 43 #endif
michael@0 44 #include "nsLayoutUtils.h"
michael@0 45 #include "nsDisplayList.h"
michael@0 46 #include "nsCSSAnonBoxes.h"
michael@0 47 #include "nsCSSFrameConstructor.h"
michael@0 48 #include "nsRenderingContext.h"
michael@0 49 #include "TextOverflow.h"
michael@0 50 #include "nsIFrameInlines.h"
michael@0 51
michael@0 52 #include "nsBidiPresUtils.h"
michael@0 53
michael@0 54 static const int MIN_LINES_NEEDING_CURSOR = 20;
michael@0 55
michael@0 56 static const char16_t kDiscCharacter = 0x2022;
michael@0 57 static const char16_t kCircleCharacter = 0x25e6;
michael@0 58 static const char16_t kSquareCharacter = 0x25aa;
michael@0 59
michael@0 60 #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
michael@0 61
michael@0 62 using namespace mozilla;
michael@0 63 using namespace mozilla::css;
michael@0 64 using namespace mozilla::layout;
michael@0 65
michael@0 66 #ifdef DEBUG
michael@0 67 #include "nsBlockDebugFlags.h"
michael@0 68
michael@0 69 bool nsBlockFrame::gLamePaintMetrics;
michael@0 70 bool nsBlockFrame::gLameReflowMetrics;
michael@0 71 bool nsBlockFrame::gNoisy;
michael@0 72 bool nsBlockFrame::gNoisyDamageRepair;
michael@0 73 bool nsBlockFrame::gNoisyIntrinsic;
michael@0 74 bool nsBlockFrame::gNoisyReflow;
michael@0 75 bool nsBlockFrame::gReallyNoisyReflow;
michael@0 76 bool nsBlockFrame::gNoisyFloatManager;
michael@0 77 bool nsBlockFrame::gVerifyLines;
michael@0 78 bool nsBlockFrame::gDisableResizeOpt;
michael@0 79
michael@0 80 int32_t nsBlockFrame::gNoiseIndent;
michael@0 81
michael@0 82 struct BlockDebugFlags {
michael@0 83 const char* name;
michael@0 84 bool* on;
michael@0 85 };
michael@0 86
michael@0 87 static const BlockDebugFlags gFlags[] = {
michael@0 88 { "reflow", &nsBlockFrame::gNoisyReflow },
michael@0 89 { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
michael@0 90 { "intrinsic", &nsBlockFrame::gNoisyIntrinsic },
michael@0 91 { "float-manager", &nsBlockFrame::gNoisyFloatManager },
michael@0 92 { "verify-lines", &nsBlockFrame::gVerifyLines },
michael@0 93 { "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
michael@0 94 { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
michael@0 95 { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
michael@0 96 { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
michael@0 97 };
michael@0 98 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
michael@0 99
michael@0 100 static void
michael@0 101 ShowDebugFlags()
michael@0 102 {
michael@0 103 printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
michael@0 104 const BlockDebugFlags* bdf = gFlags;
michael@0 105 const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
michael@0 106 for (; bdf < end; bdf++) {
michael@0 107 printf(" %s\n", bdf->name);
michael@0 108 }
michael@0 109 printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
michael@0 110 printf("names (no whitespace)\n");
michael@0 111 }
michael@0 112
michael@0 113 void
michael@0 114 nsBlockFrame::InitDebugFlags()
michael@0 115 {
michael@0 116 static bool firstTime = true;
michael@0 117 if (firstTime) {
michael@0 118 firstTime = false;
michael@0 119 char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
michael@0 120 if (flags) {
michael@0 121 bool error = false;
michael@0 122 for (;;) {
michael@0 123 char* cm = PL_strchr(flags, ',');
michael@0 124 if (cm) *cm = '\0';
michael@0 125
michael@0 126 bool found = false;
michael@0 127 const BlockDebugFlags* bdf = gFlags;
michael@0 128 const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
michael@0 129 for (; bdf < end; bdf++) {
michael@0 130 if (PL_strcasecmp(bdf->name, flags) == 0) {
michael@0 131 *(bdf->on) = true;
michael@0 132 printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
michael@0 133 gNoisy = true;
michael@0 134 found = true;
michael@0 135 break;
michael@0 136 }
michael@0 137 }
michael@0 138 if (!found) {
michael@0 139 error = true;
michael@0 140 }
michael@0 141
michael@0 142 if (!cm) break;
michael@0 143 *cm = ',';
michael@0 144 flags = cm + 1;
michael@0 145 }
michael@0 146 if (error) {
michael@0 147 ShowDebugFlags();
michael@0 148 }
michael@0 149 }
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 #endif
michael@0 154
michael@0 155 // add in a sanity check for absurdly deep frame trees. See bug 42138
michael@0 156 // can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
michael@0 157 #define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
michael@0 158
michael@0 159 //----------------------------------------------------------------------
michael@0 160
michael@0 161 // Debugging support code
michael@0 162
michael@0 163 #ifdef DEBUG
michael@0 164 const char* nsBlockFrame::kReflowCommandType[] = {
michael@0 165 "ContentChanged",
michael@0 166 "StyleChanged",
michael@0 167 "ReflowDirty",
michael@0 168 "Timeout",
michael@0 169 "UserDefined",
michael@0 170 };
michael@0 171 #endif
michael@0 172
michael@0 173 #ifdef REALLY_NOISY_FIRST_LINE
michael@0 174 static void
michael@0 175 DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
michael@0 176 {
michael@0 177 fputs(gap, stdout);
michael@0 178 nsFrame::ListTag(stdout, aFrame);
michael@0 179 printf(": ");
michael@0 180 nsStyleContext* sc = aFrame->StyleContext();
michael@0 181 while (nullptr != sc) {
michael@0 182 nsStyleContext* psc;
michael@0 183 printf("%p ", sc);
michael@0 184 psc = sc->GetParent();
michael@0 185 sc = psc;
michael@0 186 }
michael@0 187 printf("\n");
michael@0 188 }
michael@0 189 #endif
michael@0 190
michael@0 191 #ifdef REFLOW_STATUS_COVERAGE
michael@0 192 static void
michael@0 193 RecordReflowStatus(bool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
michael@0 194 {
michael@0 195 static uint32_t record[2];
michael@0 196
michael@0 197 // 0: child-is-block
michael@0 198 // 1: child-is-inline
michael@0 199 int index = 0;
michael@0 200 if (!aChildIsBlock) index |= 1;
michael@0 201
michael@0 202 // Compute new status
michael@0 203 uint32_t newS = record[index];
michael@0 204 if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
michael@0 205 if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
michael@0 206 newS |= 1;
michael@0 207 }
michael@0 208 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
michael@0 209 newS |= 2;
michael@0 210 }
michael@0 211 else {
michael@0 212 newS |= 4;
michael@0 213 }
michael@0 214 }
michael@0 215 else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
michael@0 216 newS |= 8;
michael@0 217 }
michael@0 218 else {
michael@0 219 newS |= 16;
michael@0 220 }
michael@0 221
michael@0 222 // Log updates to the status that yield different values
michael@0 223 if (record[index] != newS) {
michael@0 224 record[index] = newS;
michael@0 225 printf("record(%d): %02x %02x\n", index, record[0], record[1]);
michael@0 226 }
michael@0 227 }
michael@0 228 #endif
michael@0 229
michael@0 230 // Destructor function for the overflowLines frame property
michael@0 231 static void
michael@0 232 DestroyOverflowLines(void* aPropertyValue)
michael@0 233 {
michael@0 234 NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
michael@0 235 }
michael@0 236
michael@0 237 NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines)
michael@0 238 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
michael@0 239 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
michael@0 240 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty)
michael@0 241 NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nullptr)
michael@0 242 NS_DECLARE_FRAME_PROPERTY(BottomEdgeOfChildrenProperty, nullptr)
michael@0 243
michael@0 244 //----------------------------------------------------------------------
michael@0 245
michael@0 246 nsIFrame*
michael@0 247 NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags)
michael@0 248 {
michael@0 249 nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
michael@0 250 it->SetFlags(aFlags);
michael@0 251 return it;
michael@0 252 }
michael@0 253
michael@0 254 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
michael@0 255
michael@0 256 nsBlockFrame::~nsBlockFrame()
michael@0 257 {
michael@0 258 }
michael@0 259
michael@0 260 void
michael@0 261 nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 262 {
michael@0 263 ClearLineCursor();
michael@0 264 DestroyAbsoluteFrames(aDestructRoot);
michael@0 265 mFloats.DestroyFramesFrom(aDestructRoot);
michael@0 266 nsPresContext* presContext = PresContext();
michael@0 267 nsIPresShell* shell = presContext->PresShell();
michael@0 268 nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
michael@0 269 &mFrames);
michael@0 270
michael@0 271 FramePropertyTable* props = presContext->PropertyTable();
michael@0 272
michael@0 273 if (HasPushedFloats()) {
michael@0 274 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
michael@0 275 PushedFloatProperty());
michael@0 276 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
michael@0 277 }
michael@0 278
michael@0 279 // destroy overflow lines now
michael@0 280 FrameLines* overflowLines = RemoveOverflowLines();
michael@0 281 if (overflowLines) {
michael@0 282 nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
michael@0 283 aDestructRoot, &overflowLines->mFrames);
michael@0 284 delete overflowLines;
michael@0 285 }
michael@0 286
michael@0 287 if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
michael@0 288 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
michael@0 289 OverflowOutOfFlowsProperty());
michael@0 290 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
michael@0 291 }
michael@0 292
michael@0 293 if (HasOutsideBullet()) {
michael@0 294 SafelyDestroyFrameListProp(aDestructRoot, shell, props,
michael@0 295 OutsideBulletProperty());
michael@0 296 RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
michael@0 297 }
michael@0 298
michael@0 299 nsBlockFrameSuper::DestroyFrom(aDestructRoot);
michael@0 300 }
michael@0 301
michael@0 302 /* virtual */ nsILineIterator*
michael@0 303 nsBlockFrame::GetLineIterator()
michael@0 304 {
michael@0 305 nsLineIterator* it = new nsLineIterator;
michael@0 306 if (!it)
michael@0 307 return nullptr;
michael@0 308
michael@0 309 const nsStyleVisibility* visibility = StyleVisibility();
michael@0 310 nsresult rv = it->Init(mLines, visibility->mDirection == NS_STYLE_DIRECTION_RTL);
michael@0 311 if (NS_FAILED(rv)) {
michael@0 312 delete it;
michael@0 313 return nullptr;
michael@0 314 }
michael@0 315 return it;
michael@0 316 }
michael@0 317
michael@0 318 NS_QUERYFRAME_HEAD(nsBlockFrame)
michael@0 319 NS_QUERYFRAME_ENTRY(nsBlockFrame)
michael@0 320 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrameSuper)
michael@0 321
michael@0 322 nsSplittableType
michael@0 323 nsBlockFrame::GetSplittableType() const
michael@0 324 {
michael@0 325 return NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
michael@0 326 }
michael@0 327
michael@0 328 #ifdef DEBUG_FRAME_DUMP
michael@0 329 void
michael@0 330 nsBlockFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
michael@0 331 {
michael@0 332 nsCString str;
michael@0 333 ListGeneric(str, aPrefix, aFlags);
michael@0 334
michael@0 335 fprintf_stderr(out, "%s<\n", str.get());
michael@0 336
michael@0 337 nsCString pfx(aPrefix);
michael@0 338 pfx += " ";
michael@0 339
michael@0 340 // Output the lines
michael@0 341 if (!mLines.empty()) {
michael@0 342 const_line_iterator line = begin_lines(), line_end = end_lines();
michael@0 343 for ( ; line != line_end; ++line) {
michael@0 344 line->List(out, pfx.get(), aFlags);
michael@0 345 }
michael@0 346 }
michael@0 347
michael@0 348 // Output the overflow lines.
michael@0 349 const FrameLines* overflowLines = GetOverflowLines();
michael@0 350 if (overflowLines && !overflowLines->mLines.empty()) {
michael@0 351 fprintf_stderr(out, "%sOverflow-lines %p/%p <\n", pfx.get(), overflowLines, &overflowLines->mFrames);
michael@0 352 nsCString nestedPfx(pfx);
michael@0 353 nestedPfx += " ";
michael@0 354 const_line_iterator line = overflowLines->mLines.begin(),
michael@0 355 line_end = overflowLines->mLines.end();
michael@0 356 for ( ; line != line_end; ++line) {
michael@0 357 line->List(out, nestedPfx.get(), aFlags);
michael@0 358 }
michael@0 359 fprintf_stderr(out, "%s>\n", pfx.get());
michael@0 360 }
michael@0 361
michael@0 362 // skip the principal list - we printed the lines above
michael@0 363 // skip the overflow list - we printed the overflow lines above
michael@0 364 ChildListIterator lists(this);
michael@0 365 ChildListIDs skip(kPrincipalList | kOverflowList);
michael@0 366 for (; !lists.IsDone(); lists.Next()) {
michael@0 367 if (skip.Contains(lists.CurrentID())) {
michael@0 368 continue;
michael@0 369 }
michael@0 370 fprintf_stderr(out, "%s%s %p <\n", pfx.get(),
michael@0 371 mozilla::layout::ChildListName(lists.CurrentID()),
michael@0 372 &GetChildList(lists.CurrentID()));
michael@0 373 nsCString nestedPfx(pfx);
michael@0 374 nestedPfx += " ";
michael@0 375 nsFrameList::Enumerator childFrames(lists.CurrentList());
michael@0 376 for (; !childFrames.AtEnd(); childFrames.Next()) {
michael@0 377 nsIFrame* kid = childFrames.get();
michael@0 378 kid->List(out, nestedPfx.get(), aFlags);
michael@0 379 }
michael@0 380 fprintf_stderr(out, "%s>\n", pfx.get());
michael@0 381 }
michael@0 382
michael@0 383 fprintf_stderr(out, "%s>\n", aPrefix);
michael@0 384 }
michael@0 385
michael@0 386 nsresult
michael@0 387 nsBlockFrame::GetFrameName(nsAString& aResult) const
michael@0 388 {
michael@0 389 return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
michael@0 390 }
michael@0 391 #endif
michael@0 392
michael@0 393 #ifdef DEBUG
michael@0 394 nsFrameState
michael@0 395 nsBlockFrame::GetDebugStateBits() const
michael@0 396 {
michael@0 397 // We don't want to include our cursor flag in the bits the
michael@0 398 // regression tester looks at
michael@0 399 return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
michael@0 400 }
michael@0 401 #endif
michael@0 402
michael@0 403 nsIAtom*
michael@0 404 nsBlockFrame::GetType() const
michael@0 405 {
michael@0 406 return nsGkAtoms::blockFrame;
michael@0 407 }
michael@0 408
michael@0 409 void
michael@0 410 nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey)
michael@0 411 {
michael@0 412 if (IsSVGText()) {
michael@0 413 NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame,
michael@0 414 "unexpected block frame in SVG text");
michael@0 415 GetParent()->InvalidateFrame();
michael@0 416 return;
michael@0 417 }
michael@0 418 nsBlockFrameSuper::InvalidateFrame(aDisplayItemKey);
michael@0 419 }
michael@0 420
michael@0 421 void
michael@0 422 nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
michael@0 423 {
michael@0 424 if (IsSVGText()) {
michael@0 425 NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame,
michael@0 426 "unexpected block frame in SVG text");
michael@0 427 GetParent()->InvalidateFrame();
michael@0 428 return;
michael@0 429 }
michael@0 430 nsBlockFrameSuper::InvalidateFrameWithRect(aRect, aDisplayItemKey);
michael@0 431 }
michael@0 432
michael@0 433 nscoord
michael@0 434 nsBlockFrame::GetBaseline() const
michael@0 435 {
michael@0 436 nscoord result;
michael@0 437 if (nsLayoutUtils::GetLastLineBaseline(this, &result))
michael@0 438 return result;
michael@0 439 return nsFrame::GetBaseline();
michael@0 440 }
michael@0 441
michael@0 442 nscoord
michael@0 443 nsBlockFrame::GetCaretBaseline() const
michael@0 444 {
michael@0 445 nsRect contentRect = GetContentRect();
michael@0 446 nsMargin bp = GetUsedBorderAndPadding();
michael@0 447
michael@0 448 if (!mLines.empty()) {
michael@0 449 const_line_iterator line = begin_lines();
michael@0 450 const nsLineBox* firstLine = line;
michael@0 451 if (firstLine->GetChildCount()) {
michael@0 452 return bp.top + firstLine->mFirstChild->GetCaretBaseline();
michael@0 453 }
michael@0 454 }
michael@0 455 nsRefPtr<nsFontMetrics> fm;
michael@0 456 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
michael@0 457 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
michael@0 458 nscoord lineHeight =
michael@0 459 nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
michael@0 460 contentRect.height, inflation);
michael@0 461 return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight) + bp.top;
michael@0 462 }
michael@0 463
michael@0 464 /////////////////////////////////////////////////////////////////////////////
michael@0 465 // Child frame enumeration
michael@0 466
michael@0 467 const nsFrameList&
michael@0 468 nsBlockFrame::GetChildList(ChildListID aListID) const
michael@0 469 {
michael@0 470 switch (aListID) {
michael@0 471 case kPrincipalList:
michael@0 472 return mFrames;
michael@0 473 case kOverflowList: {
michael@0 474 FrameLines* overflowLines = GetOverflowLines();
michael@0 475 return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
michael@0 476 }
michael@0 477 case kFloatList:
michael@0 478 return mFloats;
michael@0 479 case kOverflowOutOfFlowList: {
michael@0 480 const nsFrameList* list = GetOverflowOutOfFlows();
michael@0 481 return list ? *list : nsFrameList::EmptyList();
michael@0 482 }
michael@0 483 case kPushedFloatsList: {
michael@0 484 const nsFrameList* list = GetPushedFloats();
michael@0 485 return list ? *list : nsFrameList::EmptyList();
michael@0 486 }
michael@0 487 case kBulletList: {
michael@0 488 const nsFrameList* list = GetOutsideBulletList();
michael@0 489 return list ? *list : nsFrameList::EmptyList();
michael@0 490 }
michael@0 491 default:
michael@0 492 return nsContainerFrame::GetChildList(aListID);
michael@0 493 }
michael@0 494 }
michael@0 495
michael@0 496 void
michael@0 497 nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const
michael@0 498 {
michael@0 499 nsContainerFrame::GetChildLists(aLists);
michael@0 500 FrameLines* overflowLines = GetOverflowLines();
michael@0 501 if (overflowLines) {
michael@0 502 overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList);
michael@0 503 }
michael@0 504 const nsFrameList* list = GetOverflowOutOfFlows();
michael@0 505 if (list) {
michael@0 506 list->AppendIfNonempty(aLists, kOverflowOutOfFlowList);
michael@0 507 }
michael@0 508 mFloats.AppendIfNonempty(aLists, kFloatList);
michael@0 509 list = GetOutsideBulletList();
michael@0 510 if (list) {
michael@0 511 list->AppendIfNonempty(aLists, kBulletList);
michael@0 512 }
michael@0 513 list = GetPushedFloats();
michael@0 514 if (list) {
michael@0 515 list->AppendIfNonempty(aLists, kPushedFloatsList);
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 /* virtual */ bool
michael@0 520 nsBlockFrame::IsFloatContainingBlock() const
michael@0 521 {
michael@0 522 return true;
michael@0 523 }
michael@0 524
michael@0 525 static void
michael@0 526 ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, nsIFrame* aNewParent)
michael@0 527 {
michael@0 528 NS_ASSERTION(aOldParent == aFrame->GetParent(),
michael@0 529 "Parent not consistent with expectations");
michael@0 530
michael@0 531 aFrame->SetParent(aNewParent);
michael@0 532
michael@0 533 // When pushing and pulling frames we need to check for whether any
michael@0 534 // views need to be reparented
michael@0 535 nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent);
michael@0 536 }
michael@0 537
michael@0 538 static void
michael@0 539 ReparentFrames(nsFrameList& aFrameList, nsIFrame* aOldParent,
michael@0 540 nsIFrame* aNewParent)
michael@0 541 {
michael@0 542 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
michael@0 543 ReparentFrame(e.get(), aOldParent, aNewParent);
michael@0 544 }
michael@0 545 }
michael@0 546
michael@0 547 /**
michael@0 548 * Remove the first line from aFromLines and adjust the associated frame list
michael@0 549 * aFromFrames accordingly. The removed line is assigned to *aOutLine and
michael@0 550 * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
michael@0 551 * that were extracted from the head of aFromFrames.
michael@0 552 * aFromLines must contain at least one line, the line may be empty.
michael@0 553 * @return true if aFromLines becomes empty
michael@0 554 */
michael@0 555 static bool
michael@0 556 RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
michael@0 557 nsLineBox** aOutLine, nsFrameList* aOutFrames)
michael@0 558 {
michael@0 559 nsLineList_iterator removedLine = aFromLines.begin();
michael@0 560 *aOutLine = removedLine;
michael@0 561 nsLineList_iterator next = aFromLines.erase(removedLine);
michael@0 562 bool isLastLine = next == aFromLines.end();
michael@0 563 nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild()
michael@0 564 : next->mFirstChild->GetPrevSibling();
michael@0 565 nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame);
michael@0 566 *aOutFrames = aFromFrames.ExtractHead(linkToBreak);
michael@0 567 return isLastLine;
michael@0 568 }
michael@0 569
michael@0 570 //////////////////////////////////////////////////////////////////////
michael@0 571 // Reflow methods
michael@0 572
michael@0 573 /* virtual */ void
michael@0 574 nsBlockFrame::MarkIntrinsicWidthsDirty()
michael@0 575 {
michael@0 576 nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation());
michael@0 577 dirtyBlock->mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 578 dirtyBlock->mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 579 if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
michael@0 580 for (nsIFrame* frame = dirtyBlock; frame;
michael@0 581 frame = frame->GetNextContinuation()) {
michael@0 582 frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
michael@0 583 }
michael@0 584 }
michael@0 585
michael@0 586 nsBlockFrameSuper::MarkIntrinsicWidthsDirty();
michael@0 587 }
michael@0 588
michael@0 589 void
michael@0 590 nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState()
michael@0 591 {
michael@0 592 nsPresContext *presContext = PresContext();
michael@0 593 if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) {
michael@0 594 return;
michael@0 595 }
michael@0 596 bool inflationEnabled =
michael@0 597 !presContext->mInflationDisabledForShrinkWrap;
michael@0 598 if (inflationEnabled !=
michael@0 599 !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED)) {
michael@0 600 mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 601 mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
michael@0 602 if (inflationEnabled) {
michael@0 603 AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
michael@0 604 } else {
michael@0 605 RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
michael@0 606 }
michael@0 607 }
michael@0 608 }
michael@0 609
michael@0 610 /* virtual */ nscoord
michael@0 611 nsBlockFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 612 {
michael@0 613 nsIFrame* firstInFlow = FirstContinuation();
michael@0 614 if (firstInFlow != this)
michael@0 615 return firstInFlow->GetMinWidth(aRenderingContext);
michael@0 616
michael@0 617 DISPLAY_MIN_WIDTH(this, mMinWidth);
michael@0 618
michael@0 619 CheckIntrinsicCacheAgainstShrinkWrapState();
michael@0 620
michael@0 621 if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
michael@0 622 return mMinWidth;
michael@0 623
michael@0 624 #ifdef DEBUG
michael@0 625 if (gNoisyIntrinsic) {
michael@0 626 IndentBy(stdout, gNoiseIndent);
michael@0 627 ListTag(stdout);
michael@0 628 printf(": GetMinWidth\n");
michael@0 629 }
michael@0 630 AutoNoisyIndenter indenter(gNoisyIntrinsic);
michael@0 631 #endif
michael@0 632
michael@0 633 for (nsBlockFrame* curFrame = this; curFrame;
michael@0 634 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
michael@0 635 curFrame->LazyMarkLinesDirty();
michael@0 636 }
michael@0 637
michael@0 638 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
michael@0 639 ResolveBidi();
michael@0 640 InlineMinWidthData data;
michael@0 641 for (nsBlockFrame* curFrame = this; curFrame;
michael@0 642 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
michael@0 643 for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
michael@0 644 line != line_end; ++line)
michael@0 645 {
michael@0 646 #ifdef DEBUG
michael@0 647 if (gNoisyIntrinsic) {
michael@0 648 IndentBy(stdout, gNoiseIndent);
michael@0 649 printf("line (%s%s)\n",
michael@0 650 line->IsBlock() ? "block" : "inline",
michael@0 651 line->IsEmpty() ? ", empty" : "");
michael@0 652 }
michael@0 653 AutoNoisyIndenter lineindent(gNoisyIntrinsic);
michael@0 654 #endif
michael@0 655 if (line->IsBlock()) {
michael@0 656 data.ForceBreak(aRenderingContext);
michael@0 657 data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 658 line->mFirstChild, nsLayoutUtils::MIN_WIDTH);
michael@0 659 data.ForceBreak(aRenderingContext);
michael@0 660 } else {
michael@0 661 if (!curFrame->GetPrevContinuation() &&
michael@0 662 line == curFrame->begin_lines()) {
michael@0 663 // Only add text-indent if it has no percentages; using a
michael@0 664 // percentage basis of 0 unconditionally would give strange
michael@0 665 // behavior for calc(10%-3px).
michael@0 666 const nsStyleCoord &indent = StyleText()->mTextIndent;
michael@0 667 if (indent.ConvertsToLength())
michael@0 668 data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
michael@0 669 }
michael@0 670 // XXX Bug NNNNNN Should probably handle percentage text-indent.
michael@0 671
michael@0 672 data.line = &line;
michael@0 673 data.lineContainer = curFrame;
michael@0 674 nsIFrame *kid = line->mFirstChild;
michael@0 675 for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
michael@0 676 ++i, kid = kid->GetNextSibling()) {
michael@0 677 kid->AddInlineMinWidth(aRenderingContext, &data);
michael@0 678 }
michael@0 679 }
michael@0 680 #ifdef DEBUG
michael@0 681 if (gNoisyIntrinsic) {
michael@0 682 IndentBy(stdout, gNoiseIndent);
michael@0 683 printf("min: [prevLines=%d currentLine=%d]\n",
michael@0 684 data.prevLines, data.currentLine);
michael@0 685 }
michael@0 686 #endif
michael@0 687 }
michael@0 688 }
michael@0 689 data.ForceBreak(aRenderingContext);
michael@0 690
michael@0 691 mMinWidth = data.prevLines;
michael@0 692 return mMinWidth;
michael@0 693 }
michael@0 694
michael@0 695 /* virtual */ nscoord
michael@0 696 nsBlockFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 697 {
michael@0 698 nsIFrame* firstInFlow = FirstContinuation();
michael@0 699 if (firstInFlow != this)
michael@0 700 return firstInFlow->GetPrefWidth(aRenderingContext);
michael@0 701
michael@0 702 DISPLAY_PREF_WIDTH(this, mPrefWidth);
michael@0 703
michael@0 704 CheckIntrinsicCacheAgainstShrinkWrapState();
michael@0 705
michael@0 706 if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
michael@0 707 return mPrefWidth;
michael@0 708
michael@0 709 #ifdef DEBUG
michael@0 710 if (gNoisyIntrinsic) {
michael@0 711 IndentBy(stdout, gNoiseIndent);
michael@0 712 ListTag(stdout);
michael@0 713 printf(": GetPrefWidth\n");
michael@0 714 }
michael@0 715 AutoNoisyIndenter indenter(gNoisyIntrinsic);
michael@0 716 #endif
michael@0 717
michael@0 718 for (nsBlockFrame* curFrame = this; curFrame;
michael@0 719 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
michael@0 720 curFrame->LazyMarkLinesDirty();
michael@0 721 }
michael@0 722
michael@0 723 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
michael@0 724 ResolveBidi();
michael@0 725 InlinePrefWidthData data;
michael@0 726 for (nsBlockFrame* curFrame = this; curFrame;
michael@0 727 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
michael@0 728 for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
michael@0 729 line != line_end; ++line)
michael@0 730 {
michael@0 731 #ifdef DEBUG
michael@0 732 if (gNoisyIntrinsic) {
michael@0 733 IndentBy(stdout, gNoiseIndent);
michael@0 734 printf("line (%s%s)\n",
michael@0 735 line->IsBlock() ? "block" : "inline",
michael@0 736 line->IsEmpty() ? ", empty" : "");
michael@0 737 }
michael@0 738 AutoNoisyIndenter lineindent(gNoisyIntrinsic);
michael@0 739 #endif
michael@0 740 if (line->IsBlock()) {
michael@0 741 data.ForceBreak(aRenderingContext);
michael@0 742 data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 743 line->mFirstChild, nsLayoutUtils::PREF_WIDTH);
michael@0 744 data.ForceBreak(aRenderingContext);
michael@0 745 } else {
michael@0 746 if (!curFrame->GetPrevContinuation() &&
michael@0 747 line == curFrame->begin_lines()) {
michael@0 748 // Only add text-indent if it has no percentages; using a
michael@0 749 // percentage basis of 0 unconditionally would give strange
michael@0 750 // behavior for calc(10%-3px).
michael@0 751 const nsStyleCoord &indent = StyleText()->mTextIndent;
michael@0 752 if (indent.ConvertsToLength())
michael@0 753 data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
michael@0 754 }
michael@0 755 // XXX Bug NNNNNN Should probably handle percentage text-indent.
michael@0 756
michael@0 757 data.line = &line;
michael@0 758 data.lineContainer = curFrame;
michael@0 759 nsIFrame *kid = line->mFirstChild;
michael@0 760 for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
michael@0 761 ++i, kid = kid->GetNextSibling()) {
michael@0 762 kid->AddInlinePrefWidth(aRenderingContext, &data);
michael@0 763 }
michael@0 764 }
michael@0 765 #ifdef DEBUG
michael@0 766 if (gNoisyIntrinsic) {
michael@0 767 IndentBy(stdout, gNoiseIndent);
michael@0 768 printf("pref: [prevLines=%d currentLine=%d]\n",
michael@0 769 data.prevLines, data.currentLine);
michael@0 770 }
michael@0 771 #endif
michael@0 772 }
michael@0 773 }
michael@0 774 data.ForceBreak(aRenderingContext);
michael@0 775
michael@0 776 mPrefWidth = data.prevLines;
michael@0 777 return mPrefWidth;
michael@0 778 }
michael@0 779
michael@0 780 nsRect
michael@0 781 nsBlockFrame::ComputeTightBounds(gfxContext* aContext) const
michael@0 782 {
michael@0 783 // be conservative
michael@0 784 if (StyleContext()->HasTextDecorationLines()) {
michael@0 785 return GetVisualOverflowRect();
michael@0 786 }
michael@0 787 return ComputeSimpleTightBounds(aContext);
michael@0 788 }
michael@0 789
michael@0 790 /* virtual */ nsresult
michael@0 791 nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext,
michael@0 792 nscoord* aX,
michael@0 793 nscoord* aXMost)
michael@0 794 {
michael@0 795 nsIFrame* firstInFlow = FirstContinuation();
michael@0 796 if (firstInFlow != this) {
michael@0 797 return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost);
michael@0 798 }
michael@0 799
michael@0 800 *aX = 0;
michael@0 801 *aXMost = 0;
michael@0 802
michael@0 803 nsresult rv;
michael@0 804 InlinePrefWidthData data;
michael@0 805 for (nsBlockFrame* curFrame = this; curFrame;
michael@0 806 curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
michael@0 807 for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
michael@0 808 line != line_end; ++line)
michael@0 809 {
michael@0 810 nscoord childX, childXMost;
michael@0 811 if (line->IsBlock()) {
michael@0 812 data.ForceBreak(aRenderingContext);
michael@0 813 rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext,
michael@0 814 &childX, &childXMost);
michael@0 815 NS_ENSURE_SUCCESS(rv, rv);
michael@0 816 *aX = std::min(*aX, childX);
michael@0 817 *aXMost = std::max(*aXMost, childXMost);
michael@0 818 } else {
michael@0 819 if (!curFrame->GetPrevContinuation() &&
michael@0 820 line == curFrame->begin_lines()) {
michael@0 821 // Only add text-indent if it has no percentages; using a
michael@0 822 // percentage basis of 0 unconditionally would give strange
michael@0 823 // behavior for calc(10%-3px).
michael@0 824 const nsStyleCoord &indent = StyleText()->mTextIndent;
michael@0 825 if (indent.ConvertsToLength()) {
michael@0 826 data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
michael@0 827 }
michael@0 828 }
michael@0 829 // XXX Bug NNNNNN Should probably handle percentage text-indent.
michael@0 830
michael@0 831 data.line = &line;
michael@0 832 data.lineContainer = curFrame;
michael@0 833 nsIFrame *kid = line->mFirstChild;
michael@0 834 for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
michael@0 835 ++i, kid = kid->GetNextSibling()) {
michael@0 836 rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX,
michael@0 837 &childXMost);
michael@0 838 NS_ENSURE_SUCCESS(rv, rv);
michael@0 839 *aX = std::min(*aX, data.currentLine + childX);
michael@0 840 *aXMost = std::max(*aXMost, data.currentLine + childXMost);
michael@0 841 kid->AddInlinePrefWidth(aRenderingContext, &data);
michael@0 842 }
michael@0 843 }
michael@0 844 }
michael@0 845 }
michael@0 846 data.ForceBreak(aRenderingContext);
michael@0 847
michael@0 848 return NS_OK;
michael@0 849 }
michael@0 850
michael@0 851 static bool
michael@0 852 AvailableSpaceShrunk(const nsRect& aOldAvailableSpace,
michael@0 853 const nsRect& aNewAvailableSpace)
michael@0 854 {
michael@0 855 if (aNewAvailableSpace.width == 0) {
michael@0 856 // Positions are not significant if the width is zero.
michael@0 857 return aOldAvailableSpace.width != 0;
michael@0 858 }
michael@0 859 NS_ASSERTION(aOldAvailableSpace.x <= aNewAvailableSpace.x &&
michael@0 860 aOldAvailableSpace.XMost() >= aNewAvailableSpace.XMost(),
michael@0 861 "available space should never grow");
michael@0 862 return aOldAvailableSpace.width != aNewAvailableSpace.width;
michael@0 863 }
michael@0 864
michael@0 865 static nsSize
michael@0 866 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
michael@0 867 nsSize aFrameSize)
michael@0 868 {
michael@0 869 // The issue here is that for a 'height' of 'auto' the reflow state
michael@0 870 // code won't know how to calculate the containing block height
michael@0 871 // because it's calculated bottom up. So we use our own computed
michael@0 872 // size as the dimensions.
michael@0 873 nsIFrame* frame = aReflowState.frame;
michael@0 874
michael@0 875 nsSize cbSize(aFrameSize);
michael@0 876 // Containing block is relative to the padding edge
michael@0 877 const nsMargin& border =
michael@0 878 aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding();
michael@0 879 cbSize.width -= border.LeftRight();
michael@0 880 cbSize.height -= border.TopBottom();
michael@0 881
michael@0 882 if (frame->GetParent()->GetContent() == frame->GetContent() &&
michael@0 883 frame->GetParent()->GetType() != nsGkAtoms::canvasFrame) {
michael@0 884 // We are a wrapped frame for the content (and the wrapper is not the
michael@0 885 // canvas frame, whose size is not meaningful here).
michael@0 886 // Use the container's dimensions, if they have been precomputed.
michael@0 887 // XXX This is a hack! We really should be waiting until the outermost
michael@0 888 // frame is fully reflowed and using the resulting dimensions, even
michael@0 889 // if they're intrinsic.
michael@0 890 // In fact we should be attaching absolute children to the outermost
michael@0 891 // frame and not always sticking them in block frames.
michael@0 892
michael@0 893 // First, find the reflow state for the outermost frame for this
michael@0 894 // content, except for fieldsets where the inner anonymous frame has
michael@0 895 // the correct padding area with the legend taken into account.
michael@0 896 const nsHTMLReflowState* aLastRS = &aReflowState;
michael@0 897 const nsHTMLReflowState* lastButOneRS = &aReflowState;
michael@0 898 while (aLastRS->parentReflowState &&
michael@0 899 aLastRS->parentReflowState->frame->GetContent() == frame->GetContent() &&
michael@0 900 aLastRS->parentReflowState->frame->GetType() != nsGkAtoms::fieldSetFrame) {
michael@0 901 lastButOneRS = aLastRS;
michael@0 902 aLastRS = aLastRS->parentReflowState;
michael@0 903 }
michael@0 904 if (aLastRS != &aReflowState) {
michael@0 905 // Scrollbars need to be specifically excluded, if present, because they are outside the
michael@0 906 // padding-edge. We need better APIs for getting the various boxes from a frame.
michael@0 907 nsIScrollableFrame* scrollFrame = do_QueryFrame(aLastRS->frame);
michael@0 908 nsMargin scrollbars(0,0,0,0);
michael@0 909 if (scrollFrame) {
michael@0 910 scrollbars =
michael@0 911 scrollFrame->GetDesiredScrollbarSizes(aLastRS->frame->PresContext(),
michael@0 912 aLastRS->rendContext);
michael@0 913 if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
michael@0 914 scrollbars.top = scrollbars.bottom = 0;
michael@0 915 }
michael@0 916 if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
michael@0 917 scrollbars.left = scrollbars.right = 0;
michael@0 918 }
michael@0 919 }
michael@0 920 // We found a reflow state for the outermost wrapping frame, so use
michael@0 921 // its computed metrics if available
michael@0 922 if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) {
michael@0 923 cbSize.width = std::max(0,
michael@0 924 aLastRS->ComputedWidth() + aLastRS->ComputedPhysicalPadding().LeftRight() - scrollbars.LeftRight());
michael@0 925 }
michael@0 926 if (aLastRS->ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
michael@0 927 cbSize.height = std::max(0,
michael@0 928 aLastRS->ComputedHeight() + aLastRS->ComputedPhysicalPadding().TopBottom() - scrollbars.TopBottom());
michael@0 929 }
michael@0 930 }
michael@0 931 }
michael@0 932
michael@0 933 return cbSize;
michael@0 934 }
michael@0 935
michael@0 936 nsresult
michael@0 937 nsBlockFrame::Reflow(nsPresContext* aPresContext,
michael@0 938 nsHTMLReflowMetrics& aMetrics,
michael@0 939 const nsHTMLReflowState& aReflowState,
michael@0 940 nsReflowStatus& aStatus)
michael@0 941 {
michael@0 942 DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
michael@0 943 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
michael@0 944 #ifdef DEBUG
michael@0 945 if (gNoisyReflow) {
michael@0 946 IndentBy(stdout, gNoiseIndent);
michael@0 947 ListTag(stdout);
michael@0 948 printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
michael@0 949 aReflowState.AvailableWidth(), aReflowState.AvailableHeight(),
michael@0 950 aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
michael@0 951 }
michael@0 952 AutoNoisyIndenter indent(gNoisy);
michael@0 953 PRTime start = 0; // Initialize these variablies to silence the compiler.
michael@0 954 int32_t ctc = 0; // We only use these if they are set (gLameReflowMetrics).
michael@0 955 if (gLameReflowMetrics) {
michael@0 956 start = PR_Now();
michael@0 957 ctc = nsLineBox::GetCtorCount();
michael@0 958 }
michael@0 959 #endif
michael@0 960
michael@0 961 const nsHTMLReflowState *reflowState = &aReflowState;
michael@0 962 nscoord consumedHeight = GetConsumedHeight();
michael@0 963 nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState,
michael@0 964 consumedHeight);
michael@0 965 Maybe<nsHTMLReflowState> mutableReflowState;
michael@0 966 // If we have non-auto height, we're clipping our kids and we fit,
michael@0 967 // make sure our kids fit too.
michael@0 968 if (aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
michael@0 969 aReflowState.ComputedHeight() != NS_AUTOHEIGHT &&
michael@0 970 ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
michael@0 971 LogicalMargin blockDirExtras = aReflowState.ComputedLogicalBorderPadding();
michael@0 972 WritingMode wm = aReflowState.GetWritingMode();
michael@0 973 if (GetLogicalSkipSides() & (LOGICAL_SIDE_B_START)) {
michael@0 974 blockDirExtras.BStart(wm) = 0;
michael@0 975 } else {
michael@0 976 // Bottom margin never causes us to create continuations, so we
michael@0 977 // don't need to worry about whether it fits in its entirety.
michael@0 978 blockDirExtras.BStart(wm) +=
michael@0 979 aReflowState.ComputedLogicalMargin().BStart(wm);
michael@0 980 }
michael@0 981
michael@0 982 if (effectiveComputedHeight + blockDirExtras.BStartEnd(wm) <=
michael@0 983 aReflowState.AvailableBSize()) {
michael@0 984 mutableReflowState.construct(aReflowState);
michael@0 985 mutableReflowState.ref().AvailableBSize() = NS_UNCONSTRAINEDSIZE;
michael@0 986 reflowState = mutableReflowState.addr();
michael@0 987 }
michael@0 988 }
michael@0 989
michael@0 990 // See comment below about oldSize. Use *only* for the
michael@0 991 // abs-pos-containing-block-size-change optimization!
michael@0 992 nsSize oldSize = GetSize();
michael@0 993
michael@0 994 // Should we create a float manager?
michael@0 995 nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState&>(*reflowState));
michael@0 996
michael@0 997 // XXXldb If we start storing the float manager in the frame rather
michael@0 998 // than keeping it around only during reflow then we should create it
michael@0 999 // only when there are actually floats to manage. Otherwise things
michael@0 1000 // like tables will gain significant bloat.
michael@0 1001 bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this);
michael@0 1002 if (needFloatManager)
michael@0 1003 autoFloatManager.CreateFloatManager(aPresContext);
michael@0 1004
michael@0 1005 // OK, some lines may be reflowed. Blow away any saved line cursor
michael@0 1006 // because we may invalidate the nondecreasing
michael@0 1007 // overflowArea.VisualOverflow().y/yMost invariant, and we may even
michael@0 1008 // delete the line with the line cursor.
michael@0 1009 ClearLineCursor();
michael@0 1010
michael@0 1011 if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) {
michael@0 1012 return NS_OK;
michael@0 1013 }
michael@0 1014
michael@0 1015 bool topMarginRoot, bottomMarginRoot;
michael@0 1016 IsMarginRoot(&topMarginRoot, &bottomMarginRoot);
michael@0 1017
michael@0 1018 // Cache the consumed height in the block reflow state so that we don't have
michael@0 1019 // to continually recompute it.
michael@0 1020 nsBlockReflowState state(*reflowState, aPresContext, this,
michael@0 1021 topMarginRoot, bottomMarginRoot, needFloatManager,
michael@0 1022 consumedHeight);
michael@0 1023
michael@0 1024 if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
michael@0 1025 static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
michael@0 1026
michael@0 1027 if (RenumberLists(aPresContext)) {
michael@0 1028 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 1029 }
michael@0 1030
michael@0 1031 nsresult rv = NS_OK;
michael@0 1032
michael@0 1033 // ALWAYS drain overflow. We never want to leave the previnflow's
michael@0 1034 // overflow lines hanging around; block reflow depends on the
michael@0 1035 // overflow line lists being cleared out between reflow passes.
michael@0 1036 DrainOverflowLines();
michael@0 1037
michael@0 1038 // Handle paginated overflow (see nsContainerFrame.h)
michael@0 1039 nsOverflowAreas ocBounds;
michael@0 1040 nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
michael@0 1041 if (GetPrevInFlow()) {
michael@0 1042 ReflowOverflowContainerChildren(aPresContext, *reflowState, ocBounds, 0,
michael@0 1043 ocStatus);
michael@0 1044 }
michael@0 1045
michael@0 1046 // Now that we're done cleaning up our overflow container lists, we can
michael@0 1047 // give |state| its nsOverflowContinuationTracker.
michael@0 1048 nsOverflowContinuationTracker tracker(this, false);
michael@0 1049 state.mOverflowTracker = &tracker;
michael@0 1050
michael@0 1051 // Drain & handle pushed floats
michael@0 1052 DrainPushedFloats(state);
michael@0 1053 nsOverflowAreas fcBounds;
michael@0 1054 nsReflowStatus fcStatus = NS_FRAME_COMPLETE;
michael@0 1055 ReflowPushedFloats(state, fcBounds, fcStatus);
michael@0 1056
michael@0 1057 // If we're not dirty (which means we'll mark everything dirty later)
michael@0 1058 // and our width has changed, mark the lines dirty that we need to
michael@0 1059 // mark dirty for a resize reflow.
michael@0 1060 if (!(GetStateBits() & NS_FRAME_IS_DIRTY) && reflowState->mFlags.mHResize) {
michael@0 1061 PrepareResizeReflow(state);
michael@0 1062 }
michael@0 1063
michael@0 1064 LazyMarkLinesDirty();
michael@0 1065
michael@0 1066 mState &= ~NS_FRAME_FIRST_REFLOW;
michael@0 1067
michael@0 1068 // Now reflow...
michael@0 1069 rv = ReflowDirtyLines(state);
michael@0 1070
michael@0 1071 // If we have a next-in-flow, and that next-in-flow has pushed floats from
michael@0 1072 // this frame from a previous iteration of reflow, then we should not return
michael@0 1073 // a status of NS_FRAME_COMPLETE, since we actually have overflow, it's just
michael@0 1074 // already been handled.
michael@0 1075
michael@0 1076 // NOTE: This really shouldn't happen, since we _should_ pull back our floats
michael@0 1077 // and reflow them, but just in case it does, this is a safety precaution so
michael@0 1078 // we don't end up with a placeholder pointing to frames that have already
michael@0 1079 // been deleted as part of removing our next-in-flow.
michael@0 1080 if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) {
michael@0 1081 nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow());
michael@0 1082 while (nif) {
michael@0 1083 if (nif->HasPushedFloatsFromPrevContinuation()) {
michael@0 1084 NS_MergeReflowStatusInto(&state.mReflowStatus, NS_FRAME_NOT_COMPLETE);
michael@0 1085 }
michael@0 1086
michael@0 1087 nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow());
michael@0 1088 }
michael@0 1089 }
michael@0 1090
michael@0 1091 NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
michael@0 1092 if (NS_FAILED(rv)) return rv;
michael@0 1093
michael@0 1094 NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus);
michael@0 1095 NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus);
michael@0 1096
michael@0 1097 // If we end in a BR with clear and affected floats continue,
michael@0 1098 // we need to continue, too.
michael@0 1099 if (NS_UNCONSTRAINEDSIZE != reflowState->AvailableHeight() &&
michael@0 1100 NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
michael@0 1101 state.mFloatManager->ClearContinues(FindTrailingClear())) {
michael@0 1102 NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
michael@0 1103 }
michael@0 1104
michael@0 1105 if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
michael@0 1106 if (HasOverflowLines() || HasPushedFloats()) {
michael@0 1107 state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 1108 }
michael@0 1109
michael@0 1110 #ifdef DEBUG_kipp
michael@0 1111 ListTag(stdout); printf(": block is not fully complete\n");
michael@0 1112 #endif
michael@0 1113 }
michael@0 1114
michael@0 1115 // Place the "marker" (bullet) frame if it is placed next to a block
michael@0 1116 // child.
michael@0 1117 //
michael@0 1118 // According to the CSS2 spec, section 12.6.1, the "marker" box
michael@0 1119 // participates in the height calculation of the list-item box's
michael@0 1120 // first line box.
michael@0 1121 //
michael@0 1122 // There are exactly two places a bullet can be placed: near the
michael@0 1123 // first or second line. It's only placed on the second line in a
michael@0 1124 // rare case: an empty first line followed by a second line that
michael@0 1125 // contains a block (example: <LI>\n<P>... ). This is where
michael@0 1126 // the second case can happen.
michael@0 1127 if (HasOutsideBullet() && !mLines.empty() &&
michael@0 1128 (mLines.front()->IsBlock() ||
michael@0 1129 (0 == mLines.front()->BSize() &&
michael@0 1130 mLines.front() != mLines.back() &&
michael@0 1131 mLines.begin().next()->IsBlock()))) {
michael@0 1132 // Reflow the bullet
michael@0 1133 nsHTMLReflowMetrics metrics(aReflowState);
michael@0 1134 // XXX Use the entire line when we fix bug 25888.
michael@0 1135 nsLayoutUtils::LinePosition position;
michael@0 1136 bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position);
michael@0 1137 nscoord lineTop = havePosition ? position.mTop
michael@0 1138 : reflowState->ComputedPhysicalBorderPadding().top;
michael@0 1139 nsIFrame* bullet = GetOutsideBullet();
michael@0 1140 ReflowBullet(bullet, state, metrics, lineTop);
michael@0 1141 NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
michael@0 1142 "empty bullet took up space");
michael@0 1143
michael@0 1144 if (havePosition && !BulletIsEmpty()) {
michael@0 1145 // We have some lines to align the bullet with.
michael@0 1146
michael@0 1147 // Doing the alignment using the baseline will also cater for
michael@0 1148 // bullets that are placed next to a child block (bug 92896)
michael@0 1149
michael@0 1150 // Tall bullets won't look particularly nice here...
michael@0 1151 nsRect bbox = bullet->GetRect();
michael@0 1152 bbox.y = position.mBaseline - metrics.TopAscent();
michael@0 1153 bullet->SetRect(bbox);
michael@0 1154 }
michael@0 1155 // Otherwise just leave the bullet where it is, up against our top padding.
michael@0 1156 }
michael@0 1157
michael@0 1158 CheckFloats(state);
michael@0 1159
michael@0 1160 // Compute our final size
michael@0 1161 nscoord bottomEdgeOfChildren;
michael@0 1162 ComputeFinalSize(*reflowState, state, aMetrics, &bottomEdgeOfChildren);
michael@0 1163 nsRect areaBounds = nsRect(0, 0, aMetrics.Width(), aMetrics.Height());
michael@0 1164 ComputeOverflowAreas(areaBounds, reflowState->mStyleDisplay,
michael@0 1165 bottomEdgeOfChildren, aMetrics.mOverflowAreas);
michael@0 1166 // Factor overflow container child bounds into the overflow area
michael@0 1167 aMetrics.mOverflowAreas.UnionWith(ocBounds);
michael@0 1168 // Factor pushed float child bounds into the overflow area
michael@0 1169 aMetrics.mOverflowAreas.UnionWith(fcBounds);
michael@0 1170
michael@0 1171 // Let the absolutely positioned container reflow any absolutely positioned
michael@0 1172 // child frames that need to be reflowed, e.g., elements with a percentage
michael@0 1173 // based width/height
michael@0 1174 // We want to do this under either of two conditions:
michael@0 1175 // 1. If we didn't do the incremental reflow above.
michael@0 1176 // 2. If our size changed.
michael@0 1177 // Even though it's the padding edge that's the containing block, we
michael@0 1178 // can use our rect (the border edge) since if the border style
michael@0 1179 // changed, the reflow would have been targeted at us so we'd satisfy
michael@0 1180 // condition 1.
michael@0 1181 // XXX checking oldSize is bogus, there are various reasons we might have
michael@0 1182 // reflowed but our size might not have been changed to what we
michael@0 1183 // asked for (e.g., we ended up being pushed to a new page)
michael@0 1184 // When WillReflowAgainForClearance is true, we will reflow again without
michael@0 1185 // resetting the size. Because of this, we must not reflow our abs-pos children
michael@0 1186 // in that situation --- what we think is our "new size"
michael@0 1187 // will not be our real new size. This also happens to be more efficient.
michael@0 1188 if (HasAbsolutelyPositionedChildren()) {
michael@0 1189 nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
michael@0 1190 bool haveInterrupt = aPresContext->HasPendingInterrupt();
michael@0 1191 if (reflowState->WillReflowAgainForClearance() ||
michael@0 1192 haveInterrupt) {
michael@0 1193 // Make sure that when we reflow again we'll actually reflow all the abs
michael@0 1194 // pos frames that might conceivably depend on our size (or all of them,
michael@0 1195 // if we're dirty right now and interrupted; in that case we also need
michael@0 1196 // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
michael@0 1197 // better than that, because we don't really know what our size will be,
michael@0 1198 // and it might in fact not change on the followup reflow!
michael@0 1199 if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) {
michael@0 1200 absoluteContainer->MarkAllFramesDirty();
michael@0 1201 } else {
michael@0 1202 absoluteContainer->MarkSizeDependentFramesDirty();
michael@0 1203 }
michael@0 1204 } else {
michael@0 1205 nsSize containingBlockSize =
michael@0 1206 CalculateContainingBlockSizeForAbsolutes(*reflowState,
michael@0 1207 nsSize(aMetrics.Width(),
michael@0 1208 aMetrics.Height()));
michael@0 1209
michael@0 1210 // Mark frames that depend on changes we just made to this frame as dirty:
michael@0 1211 // Now we can assume that the padding edge hasn't moved.
michael@0 1212 // We need to reflow the absolutes if one of them depends on
michael@0 1213 // its placeholder position, or the containing block size in a
michael@0 1214 // direction in which the containing block size might have
michael@0 1215 // changed.
michael@0 1216 bool cbWidthChanged = aMetrics.Width() != oldSize.width;
michael@0 1217 bool isRoot = !GetContent()->GetParent();
michael@0 1218 // If isRoot and we have auto height, then we are the initial
michael@0 1219 // containing block and the containing block height is the
michael@0 1220 // viewport height, which can't change during incremental
michael@0 1221 // reflow.
michael@0 1222 bool cbHeightChanged =
michael@0 1223 !(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) &&
michael@0 1224 aMetrics.Height() != oldSize.height;
michael@0 1225
michael@0 1226 nsRect containingBlock(nsPoint(0, 0), containingBlockSize);
michael@0 1227 absoluteContainer->Reflow(this, aPresContext, *reflowState,
michael@0 1228 state.mReflowStatus,
michael@0 1229 containingBlock, true,
michael@0 1230 cbWidthChanged, cbHeightChanged,
michael@0 1231 &aMetrics.mOverflowAreas);
michael@0 1232
michael@0 1233 //XXXfr Why isn't this rv (and others in this file) checked/returned?
michael@0 1234 }
michael@0 1235 }
michael@0 1236
michael@0 1237 FinishAndStoreOverflow(&aMetrics);
michael@0 1238
michael@0 1239 // Clear the float manager pointer in the block reflow state so we
michael@0 1240 // don't waste time translating the coordinate system back on a dead
michael@0 1241 // float manager.
michael@0 1242 if (needFloatManager)
michael@0 1243 state.mFloatManager = nullptr;
michael@0 1244
michael@0 1245 aStatus = state.mReflowStatus;
michael@0 1246
michael@0 1247 #ifdef DEBUG
michael@0 1248 // Between when we drain pushed floats and when we complete reflow,
michael@0 1249 // we're allowed to have multiple continuations of the same float on
michael@0 1250 // our floats list, since a first-in-flow might get pushed to a later
michael@0 1251 // continuation of its containing block. But it's not permitted
michael@0 1252 // outside that time.
michael@0 1253 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
michael@0 1254
michael@0 1255 if (gNoisyReflow) {
michael@0 1256 IndentBy(stdout, gNoiseIndent);
michael@0 1257 ListTag(stdout);
michael@0 1258 printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
michael@0 1259 aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
michael@0 1260 aMetrics.Width(), aMetrics.Height(),
michael@0 1261 aMetrics.mCarriedOutBottomMargin.get());
michael@0 1262 if (HasOverflowAreas()) {
michael@0 1263 printf(" overflow-vis={%d,%d,%d,%d}",
michael@0 1264 aMetrics.VisualOverflow().x,
michael@0 1265 aMetrics.VisualOverflow().y,
michael@0 1266 aMetrics.VisualOverflow().width,
michael@0 1267 aMetrics.VisualOverflow().height);
michael@0 1268 printf(" overflow-scr={%d,%d,%d,%d}",
michael@0 1269 aMetrics.ScrollableOverflow().x,
michael@0 1270 aMetrics.ScrollableOverflow().y,
michael@0 1271 aMetrics.ScrollableOverflow().width,
michael@0 1272 aMetrics.ScrollableOverflow().height);
michael@0 1273 }
michael@0 1274 printf("\n");
michael@0 1275 }
michael@0 1276
michael@0 1277 if (gLameReflowMetrics) {
michael@0 1278 PRTime end = PR_Now();
michael@0 1279
michael@0 1280 int32_t ectc = nsLineBox::GetCtorCount();
michael@0 1281 int32_t numLines = mLines.size();
michael@0 1282 if (!numLines) numLines = 1;
michael@0 1283 PRTime delta, perLineDelta, lines;
michael@0 1284 lines = int64_t(numLines);
michael@0 1285 delta = end - start;
michael@0 1286 perLineDelta = delta / lines;
michael@0 1287
michael@0 1288 ListTag(stdout);
michael@0 1289 char buf[400];
michael@0 1290 PR_snprintf(buf, sizeof(buf),
michael@0 1291 ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
michael@0 1292 delta, perLineDelta, numLines, ectc - ctc);
michael@0 1293 printf("%s\n", buf);
michael@0 1294 }
michael@0 1295 #endif
michael@0 1296
michael@0 1297 NS_FRAME_SET_TRUNCATION(aStatus, (*reflowState), aMetrics);
michael@0 1298 return rv;
michael@0 1299 }
michael@0 1300
michael@0 1301 bool
michael@0 1302 nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
michael@0 1303 {
michael@0 1304 line_iterator begin = begin_lines();
michael@0 1305 line_iterator line = end_lines();
michael@0 1306
michael@0 1307 while (true) {
michael@0 1308 if (begin == line) {
michael@0 1309 return false;
michael@0 1310 }
michael@0 1311 --line;
michael@0 1312 if (line->BSize() != 0 || !line->CachedIsEmpty()) {
michael@0 1313 return false;
michael@0 1314 }
michael@0 1315 if (line->HasClearance()) {
michael@0 1316 return true;
michael@0 1317 }
michael@0 1318 }
michael@0 1319 // not reached
michael@0 1320 }
michael@0 1321
michael@0 1322 void
michael@0 1323 nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
michael@0 1324 nsBlockReflowState& aState,
michael@0 1325 nsHTMLReflowMetrics& aMetrics,
michael@0 1326 nscoord* aBottomEdgeOfChildren)
michael@0 1327 {
michael@0 1328 const nsMargin& borderPadding = aState.BorderPadding();
michael@0 1329 #ifdef NOISY_FINAL_SIZE
michael@0 1330 ListTag(stdout);
michael@0 1331 printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
michael@0 1332 aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no",
michael@0 1333 aState.mPrevBottomMargin,
michael@0 1334 borderPadding.top, borderPadding.bottom);
michael@0 1335 #endif
michael@0 1336
michael@0 1337 // Compute final width
michael@0 1338 aMetrics.Width() =
michael@0 1339 NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.left,
michael@0 1340 aReflowState.ComputedWidth()),
michael@0 1341 borderPadding.right);
michael@0 1342
michael@0 1343 // Return bottom margin information
michael@0 1344 // rbs says he hit this assertion occasionally (see bug 86947), so
michael@0 1345 // just set the margin to zero and we'll figure out why later
michael@0 1346 //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
michael@0 1347 // "someone else set the margin");
michael@0 1348 nscoord nonCarriedOutVerticalMargin = 0;
michael@0 1349 if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
michael@0 1350 // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
michael@0 1351 // line with clearance and a non-zero top margin and all
michael@0 1352 // subsequent lines are empty, then we do not allow our children's
michael@0 1353 // carried out bottom margin to be carried out of us and collapse
michael@0 1354 // with our own bottom margin.
michael@0 1355 if (CheckForCollapsedBottomMarginFromClearanceLine()) {
michael@0 1356 // Convert the children's carried out margin to something that
michael@0 1357 // we will include in our height
michael@0 1358 nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
michael@0 1359 aState.mPrevBottomMargin.Zero();
michael@0 1360 }
michael@0 1361 aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
michael@0 1362 } else {
michael@0 1363 aMetrics.mCarriedOutBottomMargin.Zero();
michael@0 1364 }
michael@0 1365
michael@0 1366 nscoord bottomEdgeOfChildren = aState.mY + nonCarriedOutVerticalMargin;
michael@0 1367 // Shrink wrap our height around our contents.
michael@0 1368 if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ||
michael@0 1369 NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) {
michael@0 1370 // When we are a bottom-margin root make sure that our last
michael@0 1371 // childs bottom margin is fully applied. We also do this when
michael@0 1372 // we have a computed height, since in that case the carried out
michael@0 1373 // margin is not going to be applied anywhere, so we should note it
michael@0 1374 // here to be included in the overflow area.
michael@0 1375 // Apply the margin only if there's space for it.
michael@0 1376 if (bottomEdgeOfChildren < aState.mReflowState.AvailableHeight())
michael@0 1377 {
michael@0 1378 // Truncate bottom margin if it doesn't fit to our available height.
michael@0 1379 bottomEdgeOfChildren =
michael@0 1380 std::min(bottomEdgeOfChildren + aState.mPrevBottomMargin.get(),
michael@0 1381 aState.mReflowState.AvailableHeight());
michael@0 1382 }
michael@0 1383 }
michael@0 1384 if (aState.GetFlag(BRS_FLOAT_MGR)) {
michael@0 1385 // Include the float manager's state to properly account for the
michael@0 1386 // bottom margin of any floated elements; e.g., inside a table cell.
michael@0 1387 nscoord floatHeight =
michael@0 1388 aState.ClearFloats(bottomEdgeOfChildren, NS_STYLE_CLEAR_BOTH,
michael@0 1389 nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS);
michael@0 1390 bottomEdgeOfChildren = std::max(bottomEdgeOfChildren, floatHeight);
michael@0 1391 }
michael@0 1392
michael@0 1393 if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()
michael@0 1394 && (mParent->GetType() != nsGkAtoms::columnSetFrame ||
michael@0 1395 aReflowState.parentReflowState->AvailableHeight() == NS_UNCONSTRAINEDSIZE)) {
michael@0 1396 ComputeFinalHeight(aReflowState, &aState.mReflowStatus,
michael@0 1397 aState.mY + nonCarriedOutVerticalMargin,
michael@0 1398 borderPadding, aMetrics, aState.mConsumedHeight);
michael@0 1399 if (!NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
michael@0 1400 // Use the current height; continuations will take up the rest.
michael@0 1401 // Do extend the height to at least consume the available
michael@0 1402 // height, otherwise our left/right borders (for example) won't
michael@0 1403 // extend all the way to the break.
michael@0 1404 aMetrics.Height() = std::max(aReflowState.AvailableHeight(),
michael@0 1405 aState.mY + nonCarriedOutVerticalMargin);
michael@0 1406 // ... but don't take up more height than is available
michael@0 1407 nscoord effectiveComputedHeight =
michael@0 1408 GetEffectiveComputedHeight(aReflowState, aState.GetConsumedHeight());
michael@0 1409 aMetrics.Height() = std::min(aMetrics.Height(),
michael@0 1410 borderPadding.top + effectiveComputedHeight);
michael@0 1411 // XXX It's pretty wrong that our bottom border still gets drawn on
michael@0 1412 // on its own on the last-in-flow, even if we ran out of height
michael@0 1413 // here. We need GetSkipSides to check whether we ran out of content
michael@0 1414 // height in the current frame, not whether it's last-in-flow.
michael@0 1415 }
michael@0 1416
michael@0 1417 // Don't carry out a bottom margin when our height is fixed.
michael@0 1418 aMetrics.mCarriedOutBottomMargin.Zero();
michael@0 1419 }
michael@0 1420 else if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
michael@0 1421 nscoord contentHeight = bottomEdgeOfChildren - borderPadding.top;
michael@0 1422 nscoord autoHeight = aReflowState.ApplyMinMaxHeight(contentHeight);
michael@0 1423 if (autoHeight != contentHeight) {
michael@0 1424 // Our min-height or max-height made our height change. Don't carry out
michael@0 1425 // our kids' bottom margins.
michael@0 1426 aMetrics.mCarriedOutBottomMargin.Zero();
michael@0 1427 }
michael@0 1428 autoHeight += borderPadding.top + borderPadding.bottom;
michael@0 1429 aMetrics.Height() = autoHeight;
michael@0 1430 }
michael@0 1431 else {
michael@0 1432 NS_ASSERTION(aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE,
michael@0 1433 "Shouldn't be incomplete if availableHeight is UNCONSTRAINED.");
michael@0 1434 aMetrics.Height() = std::max(aState.mY, aReflowState.AvailableHeight());
michael@0 1435 if (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE)
michael@0 1436 // This should never happen, but it does. See bug 414255
michael@0 1437 aMetrics.Height() = aState.mY;
michael@0 1438 }
michael@0 1439
michael@0 1440 if (IS_TRUE_OVERFLOW_CONTAINER(this) &&
michael@0 1441 NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
michael@0 1442 // Overflow containers can only be overflow complete.
michael@0 1443 // Note that auto height overflow containers have no normal children
michael@0 1444 NS_ASSERTION(aMetrics.Height() == 0, "overflow containers must be zero-height");
michael@0 1445 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
michael@0 1446 }
michael@0 1447
michael@0 1448 // Screen out negative heights --- can happen due to integer overflows :-(
michael@0 1449 aMetrics.Height() = std::max(0, aMetrics.Height());
michael@0 1450 *aBottomEdgeOfChildren = bottomEdgeOfChildren;
michael@0 1451
michael@0 1452 FrameProperties properties = Properties();
michael@0 1453 if (bottomEdgeOfChildren != aMetrics.Height() - borderPadding.bottom) {
michael@0 1454 properties.Set(BottomEdgeOfChildrenProperty(),
michael@0 1455 NS_INT32_TO_PTR(bottomEdgeOfChildren));
michael@0 1456 } else {
michael@0 1457 properties.Delete(BottomEdgeOfChildrenProperty());
michael@0 1458 }
michael@0 1459
michael@0 1460 #ifdef DEBUG_blocks
michael@0 1461 if (CRAZY_SIZE(aMetrics.Width()) || CRAZY_SIZE(aMetrics.Height())) {
michael@0 1462 ListTag(stdout);
michael@0 1463 printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
michael@0 1464 }
michael@0 1465 #endif
michael@0 1466 }
michael@0 1467
michael@0 1468 static void
michael@0 1469 ConsiderBottomEdgeOfChildren(nscoord aBottomEdgeOfChildren,
michael@0 1470 nsOverflowAreas& aOverflowAreas)
michael@0 1471 {
michael@0 1472 // Factor in the bottom edge of the children. Child frames will be added
michael@0 1473 // to the overflow area as we iterate through the lines, but their margins
michael@0 1474 // won't, so we need to account for bottom margins here.
michael@0 1475 // REVIEW: For now, we do this for both visual and scrollable area,
michael@0 1476 // although when we make scrollable overflow area not be a subset of
michael@0 1477 // visual, we can change this.
michael@0 1478 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
michael@0 1479 nsRect& o = aOverflowAreas.Overflow(otype);
michael@0 1480 o.height = std::max(o.YMost(), aBottomEdgeOfChildren) - o.y;
michael@0 1481 }
michael@0 1482 }
michael@0 1483
michael@0 1484 void
michael@0 1485 nsBlockFrame::ComputeOverflowAreas(const nsRect& aBounds,
michael@0 1486 const nsStyleDisplay* aDisplay,
michael@0 1487 nscoord aBottomEdgeOfChildren,
michael@0 1488 nsOverflowAreas& aOverflowAreas)
michael@0 1489 {
michael@0 1490 // Compute the overflow areas of our children
michael@0 1491 // XXX_perf: This can be done incrementally. It is currently one of
michael@0 1492 // the things that makes incremental reflow O(N^2).
michael@0 1493 nsOverflowAreas areas(aBounds, aBounds);
michael@0 1494 if (!ShouldApplyOverflowClipping(this, aDisplay)) {
michael@0 1495 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 1496 line != line_end;
michael@0 1497 ++line) {
michael@0 1498 areas.UnionWith(line->GetOverflowAreas());
michael@0 1499 }
michael@0 1500
michael@0 1501 // Factor an outside bullet in; normally the bullet will be factored into
michael@0 1502 // the line-box's overflow areas. However, if the line is a block
michael@0 1503 // line then it won't; if there are no lines, it won't. So just
michael@0 1504 // factor it in anyway (it can't hurt if it was already done).
michael@0 1505 // XXXldb Can we just fix GetOverflowArea instead?
michael@0 1506 nsIFrame* outsideBullet = GetOutsideBullet();
michael@0 1507 if (outsideBullet) {
michael@0 1508 areas.UnionAllWith(outsideBullet->GetRect());
michael@0 1509 }
michael@0 1510
michael@0 1511 ConsiderBottomEdgeOfChildren(aBottomEdgeOfChildren, areas);
michael@0 1512 }
michael@0 1513
michael@0 1514 #ifdef NOISY_COMBINED_AREA
michael@0 1515 ListTag(stdout);
michael@0 1516 printf(": ca=%d,%d,%d,%d\n", area.x, area.y, area.width, area.height);
michael@0 1517 #endif
michael@0 1518
michael@0 1519 aOverflowAreas = areas;
michael@0 1520 }
michael@0 1521
michael@0 1522 bool
michael@0 1523 nsBlockFrame::UpdateOverflow()
michael@0 1524 {
michael@0 1525 nsRect rect(nsPoint(0, 0), GetSize());
michael@0 1526 nsOverflowAreas overflowAreas(rect, rect);
michael@0 1527
michael@0 1528 // We need to update the overflow areas of lines manually, as they
michael@0 1529 // get cached and re-used otherwise. Lines aren't exposed as normal
michael@0 1530 // frame children, so calling UnionChildOverflow alone will end up
michael@0 1531 // using the old cached values.
michael@0 1532 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 1533 line != line_end;
michael@0 1534 ++line) {
michael@0 1535 nsRect bounds = line->GetPhysicalBounds();
michael@0 1536 nsOverflowAreas lineAreas(bounds, bounds);
michael@0 1537
michael@0 1538 int32_t n = line->GetChildCount();
michael@0 1539 for (nsIFrame* lineFrame = line->mFirstChild;
michael@0 1540 n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
michael@0 1541 ConsiderChildOverflow(lineAreas, lineFrame);
michael@0 1542 }
michael@0 1543
michael@0 1544 // Consider the overflow areas of the floats attached to the line as well
michael@0 1545 if (line->HasFloats()) {
michael@0 1546 for (nsFloatCache* fc = line->GetFirstFloat(); fc; fc = fc->Next()) {
michael@0 1547 ConsiderChildOverflow(lineAreas, fc->mFloat);
michael@0 1548 }
michael@0 1549 }
michael@0 1550
michael@0 1551 line->SetOverflowAreas(lineAreas);
michael@0 1552 overflowAreas.UnionWith(lineAreas);
michael@0 1553 }
michael@0 1554
michael@0 1555 // Line cursor invariants depend on the overflow areas of the lines, so
michael@0 1556 // we must clear the line cursor since those areas may have changed.
michael@0 1557 ClearLineCursor();
michael@0 1558
michael@0 1559 // Union with child frames, skipping the principal and float lists
michael@0 1560 // since we already handled those using the line boxes.
michael@0 1561 nsLayoutUtils::UnionChildOverflow(this, overflowAreas,
michael@0 1562 kPrincipalList | kFloatList);
michael@0 1563
michael@0 1564 bool found;
michael@0 1565 nscoord bottomEdgeOfChildren = NS_PTR_TO_INT32(
michael@0 1566 Properties().Get(BottomEdgeOfChildrenProperty(), &found));
michael@0 1567 if (found) {
michael@0 1568 ConsiderBottomEdgeOfChildren(bottomEdgeOfChildren, overflowAreas);
michael@0 1569 }
michael@0 1570
michael@0 1571 return FinishAndStoreOverflow(overflowAreas, GetSize());
michael@0 1572 }
michael@0 1573
michael@0 1574 void
michael@0 1575 nsBlockFrame::LazyMarkLinesDirty()
michael@0 1576 {
michael@0 1577 if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES) {
michael@0 1578 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 1579 line != line_end; ++line) {
michael@0 1580 int32_t n = line->GetChildCount();
michael@0 1581 for (nsIFrame* lineFrame = line->mFirstChild;
michael@0 1582 n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
michael@0 1583 if (NS_SUBTREE_DIRTY(lineFrame)) {
michael@0 1584 // NOTE: MarkLineDirty does more than just marking the line dirty.
michael@0 1585 MarkLineDirty(line, &mLines);
michael@0 1586 break;
michael@0 1587 }
michael@0 1588 }
michael@0 1589 }
michael@0 1590 RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
michael@0 1591 }
michael@0 1592 }
michael@0 1593
michael@0 1594 void
michael@0 1595 nsBlockFrame::MarkLineDirty(line_iterator aLine, const nsLineList* aLineList)
michael@0 1596 {
michael@0 1597 // Mark aLine dirty
michael@0 1598 aLine->MarkDirty();
michael@0 1599 aLine->SetInvalidateTextRuns(true);
michael@0 1600 #ifdef DEBUG
michael@0 1601 if (gNoisyReflow) {
michael@0 1602 IndentBy(stdout, gNoiseIndent);
michael@0 1603 ListTag(stdout);
michael@0 1604 printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
michael@0 1605 }
michael@0 1606 #endif
michael@0 1607
michael@0 1608 // Mark previous line dirty if it's an inline line so that it can
michael@0 1609 // maybe pullup something from the line just affected.
michael@0 1610 // XXX We don't need to do this if aPrevLine ends in a break-after...
michael@0 1611 if (aLine != aLineList->front() && aLine->IsInline() &&
michael@0 1612 aLine.prev()->IsInline()) {
michael@0 1613 aLine.prev()->MarkDirty();
michael@0 1614 aLine.prev()->SetInvalidateTextRuns(true);
michael@0 1615 #ifdef DEBUG
michael@0 1616 if (gNoisyReflow) {
michael@0 1617 IndentBy(stdout, gNoiseIndent);
michael@0 1618 ListTag(stdout);
michael@0 1619 printf(": mark prev-line %p dirty\n",
michael@0 1620 static_cast<void*>(aLine.prev().get()));
michael@0 1621 }
michael@0 1622 #endif
michael@0 1623 }
michael@0 1624 }
michael@0 1625
michael@0 1626 /**
michael@0 1627 * Test whether lines are certain to be aligned left so that we can make
michael@0 1628 * resizing optimizations
michael@0 1629 */
michael@0 1630 static inline bool
michael@0 1631 IsAlignedLeft(uint8_t aAlignment,
michael@0 1632 uint8_t aDirection,
michael@0 1633 uint8_t aUnicodeBidi,
michael@0 1634 nsIFrame* aFrame)
michael@0 1635 {
michael@0 1636 return aFrame->IsSVGText() ||
michael@0 1637 NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
michael@0 1638 (((NS_STYLE_TEXT_ALIGN_DEFAULT == aAlignment &&
michael@0 1639 NS_STYLE_DIRECTION_LTR == aDirection) ||
michael@0 1640 (NS_STYLE_TEXT_ALIGN_END == aAlignment &&
michael@0 1641 NS_STYLE_DIRECTION_RTL == aDirection)) &&
michael@0 1642 !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
michael@0 1643 }
michael@0 1644
michael@0 1645 void
michael@0 1646 nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
michael@0 1647 {
michael@0 1648 // See if we can try and avoid marking all the lines as dirty
michael@0 1649 bool tryAndSkipLines =
michael@0 1650 // The left content-edge must be a constant distance from the left
michael@0 1651 // border-edge.
michael@0 1652 !StylePadding()->mPadding.GetLeft().HasPercent();
michael@0 1653
michael@0 1654 #ifdef DEBUG
michael@0 1655 if (gDisableResizeOpt) {
michael@0 1656 tryAndSkipLines = false;
michael@0 1657 }
michael@0 1658 if (gNoisyReflow) {
michael@0 1659 if (!tryAndSkipLines) {
michael@0 1660 IndentBy(stdout, gNoiseIndent);
michael@0 1661 ListTag(stdout);
michael@0 1662 printf(": marking all lines dirty: availWidth=%d\n",
michael@0 1663 aState.mReflowState.AvailableWidth());
michael@0 1664 }
michael@0 1665 }
michael@0 1666 #endif
michael@0 1667
michael@0 1668 if (tryAndSkipLines) {
michael@0 1669 nscoord newAvailWidth = aState.mReflowState.ComputedPhysicalBorderPadding().left +
michael@0 1670 aState.mReflowState.ComputedWidth();
michael@0 1671 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedPhysicalBorderPadding().left &&
michael@0 1672 NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedWidth(),
michael@0 1673 "math on NS_UNCONSTRAINEDSIZE");
michael@0 1674
michael@0 1675 #ifdef DEBUG
michael@0 1676 if (gNoisyReflow) {
michael@0 1677 IndentBy(stdout, gNoiseIndent);
michael@0 1678 ListTag(stdout);
michael@0 1679 printf(": trying to avoid marking all lines dirty\n");
michael@0 1680 }
michael@0 1681 #endif
michael@0 1682
michael@0 1683 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 1684 line != line_end;
michael@0 1685 ++line)
michael@0 1686 {
michael@0 1687 // We let child blocks make their own decisions the same
michael@0 1688 // way we are here.
michael@0 1689 bool isLastLine = line == mLines.back() && !GetNextInFlow();
michael@0 1690 if (line->IsBlock() ||
michael@0 1691 line->HasFloats() ||
michael@0 1692 (!isLastLine && !line->HasBreakAfter()) ||
michael@0 1693 ((isLastLine || !line->IsLineWrapped())) ||
michael@0 1694 line->ResizeReflowOptimizationDisabled() ||
michael@0 1695 line->IsImpactedByFloat() ||
michael@0 1696 (line->IEnd() > newAvailWidth)) {
michael@0 1697 line->MarkDirty();
michael@0 1698 }
michael@0 1699
michael@0 1700 #ifdef REALLY_NOISY_REFLOW
michael@0 1701 if (!line->IsBlock()) {
michael@0 1702 printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
michael@0 1703 line.get(), line->IsImpactedByFloat() ? "" : "not ");
michael@0 1704 }
michael@0 1705 #endif
michael@0 1706 #ifdef DEBUG
michael@0 1707 if (gNoisyReflow && !line->IsDirty()) {
michael@0 1708 IndentBy(stdout, gNoiseIndent + 1);
michael@0 1709 printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
michael@0 1710 static_cast<void*>(line.get()),
michael@0 1711 static_cast<void*>((line.next() != end_lines() ? line.next().get() : nullptr)),
michael@0 1712 line->IsBlock() ? "block" : "inline",
michael@0 1713 line->HasBreakAfter() ? "has-break-after " : "",
michael@0 1714 line->HasFloats() ? "has-floats " : "",
michael@0 1715 line->IsImpactedByFloat() ? "impacted " : "",
michael@0 1716 line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
michael@0 1717 line->IEnd());
michael@0 1718 }
michael@0 1719 #endif
michael@0 1720 }
michael@0 1721 }
michael@0 1722 else {
michael@0 1723 // Mark everything dirty
michael@0 1724 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 1725 line != line_end;
michael@0 1726 ++line)
michael@0 1727 {
michael@0 1728 line->MarkDirty();
michael@0 1729 }
michael@0 1730 }
michael@0 1731 }
michael@0 1732
michael@0 1733 //----------------------------------------
michael@0 1734
michael@0 1735 /**
michael@0 1736 * Propagate reflow "damage" from from earlier lines to the current
michael@0 1737 * line. The reflow damage comes from the following sources:
michael@0 1738 * 1. The regions of float damage remembered during reflow.
michael@0 1739 * 2. The combination of nonzero |aDeltaY| and any impact by a float,
michael@0 1740 * either the previous reflow or now.
michael@0 1741 *
michael@0 1742 * When entering this function, |aLine| is still at its old position and
michael@0 1743 * |aDeltaY| indicates how much it will later be slid (assuming it
michael@0 1744 * doesn't get marked dirty and reflowed entirely).
michael@0 1745 */
michael@0 1746 void
michael@0 1747 nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
michael@0 1748 nsLineBox* aLine,
michael@0 1749 nscoord aDeltaY)
michael@0 1750 {
michael@0 1751 nsFloatManager *floatManager = aState.mReflowState.mFloatManager;
michael@0 1752 NS_ASSERTION((aState.mReflowState.parentReflowState &&
michael@0 1753 aState.mReflowState.parentReflowState->mFloatManager == floatManager) ||
michael@0 1754 aState.mReflowState.mBlockDelta == 0, "Bad block delta passed in");
michael@0 1755
michael@0 1756 // Check to see if there are any floats; if there aren't, there can't
michael@0 1757 // be any float damage
michael@0 1758 if (!floatManager->HasAnyFloats())
michael@0 1759 return;
michael@0 1760
michael@0 1761 // Check the damage region recorded in the float damage.
michael@0 1762 if (floatManager->HasFloatDamage()) {
michael@0 1763 // Need to check mBounds *and* mCombinedArea to find intersections
michael@0 1764 // with aLine's floats
michael@0 1765 nscoord lineYA = aLine->BStart() + aDeltaY;
michael@0 1766 nscoord lineYB = lineYA + aLine->BSize();
michael@0 1767 // Scrollable overflow should be sufficient for things that affect
michael@0 1768 // layout.
michael@0 1769 nsRect overflow = aLine->GetOverflowArea(eScrollableOverflow);
michael@0 1770 nscoord lineYCombinedA = overflow.y + aDeltaY;
michael@0 1771 nscoord lineYCombinedB = lineYCombinedA + overflow.height;
michael@0 1772 if (floatManager->IntersectsDamage(lineYA, lineYB) ||
michael@0 1773 floatManager->IntersectsDamage(lineYCombinedA, lineYCombinedB)) {
michael@0 1774 aLine->MarkDirty();
michael@0 1775 return;
michael@0 1776 }
michael@0 1777 }
michael@0 1778
michael@0 1779 // Check if the line is moving relative to the float manager
michael@0 1780 if (aDeltaY + aState.mReflowState.mBlockDelta != 0) {
michael@0 1781 if (aLine->IsBlock()) {
michael@0 1782 // Unconditionally reflow sliding blocks; we only really need to reflow
michael@0 1783 // if there's a float impacting this block, but the current float manager
michael@0 1784 // makes it difficult to check that. Therefore, we let the child block
michael@0 1785 // decide what it needs to reflow.
michael@0 1786 aLine->MarkDirty();
michael@0 1787 } else {
michael@0 1788 bool wasImpactedByFloat = aLine->IsImpactedByFloat();
michael@0 1789 nsFlowAreaRect floatAvailableSpace =
michael@0 1790 aState.GetFloatAvailableSpaceForHeight(aLine->BStart() + aDeltaY,
michael@0 1791 aLine->BSize(),
michael@0 1792 nullptr);
michael@0 1793
michael@0 1794 #ifdef REALLY_NOISY_REFLOW
michael@0 1795 printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
michael@0 1796 this, wasImpactedByFloat, floatAvailableSpace.mHasFloats);
michael@0 1797 #endif
michael@0 1798
michael@0 1799 // Mark the line dirty if it was or is affected by a float
michael@0 1800 // We actually only really need to reflow if the amount of impact
michael@0 1801 // changes, but that's not straightforward to check
michael@0 1802 if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) {
michael@0 1803 aLine->MarkDirty();
michael@0 1804 }
michael@0 1805 }
michael@0 1806 }
michael@0 1807 }
michael@0 1808
michael@0 1809 static bool LineHasClear(nsLineBox* aLine) {
michael@0 1810 return aLine->IsBlock()
michael@0 1811 ? (aLine->GetBreakTypeBefore() ||
michael@0 1812 (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) ||
michael@0 1813 !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
michael@0 1814 : aLine->HasFloatBreakAfter();
michael@0 1815 }
michael@0 1816
michael@0 1817
michael@0 1818 /**
michael@0 1819 * Reparent a whole list of floats from aOldParent to this block. The
michael@0 1820 * floats might be taken from aOldParent's overflow list. They will be
michael@0 1821 * removed from the list. They end up appended to our mFloats list.
michael@0 1822 */
michael@0 1823 void
michael@0 1824 nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
michael@0 1825 bool aReparentSiblings) {
michael@0 1826 nsFrameList list;
michael@0 1827 aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
michael@0 1828 if (list.NotEmpty()) {
michael@0 1829 for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) {
michael@0 1830 ReparentFrame(f, aOldParent, this);
michael@0 1831 }
michael@0 1832 mFloats.AppendFrames(nullptr, list);
michael@0 1833 }
michael@0 1834 }
michael@0 1835
michael@0 1836 static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine,
michael@0 1837 nscoord aDeltaY, int32_t aDeltaIndent) {
michael@0 1838 #ifdef DEBUG
michael@0 1839 if (nsBlockFrame::gNoisyReflow) {
michael@0 1840 nsRect ovis(aLine->GetVisualOverflowArea());
michael@0 1841 nsRect oscr(aLine->GetScrollableOverflowArea());
michael@0 1842 nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
michael@0 1843 printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
michael@0 1844 static_cast<void*>(aLine), aState.mY,
michael@0 1845 aLine->IsDirty() ? "yes" : "no",
michael@0 1846 aLine->IStart(), aLine->BStart(),
michael@0 1847 aLine->ISize(), aLine->BSize(),
michael@0 1848 ovis.x, ovis.y, ovis.width, ovis.height,
michael@0 1849 oscr.x, oscr.y, oscr.width, oscr.height,
michael@0 1850 aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount());
michael@0 1851 }
michael@0 1852 #endif
michael@0 1853 }
michael@0 1854
michael@0 1855 /**
michael@0 1856 * Reflow the dirty lines
michael@0 1857 */
michael@0 1858 nsresult
michael@0 1859 nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
michael@0 1860 {
michael@0 1861 nsresult rv = NS_OK;
michael@0 1862 bool keepGoing = true;
michael@0 1863 bool repositionViews = false; // should we really need this?
michael@0 1864 bool foundAnyClears = aState.mFloatBreakType != NS_STYLE_CLEAR_NONE;
michael@0 1865 bool willReflowAgain = false;
michael@0 1866
michael@0 1867 #ifdef DEBUG
michael@0 1868 if (gNoisyReflow) {
michael@0 1869 IndentBy(stdout, gNoiseIndent);
michael@0 1870 ListTag(stdout);
michael@0 1871 printf(": reflowing dirty lines");
michael@0 1872 printf(" computedWidth=%d\n", aState.mReflowState.ComputedWidth());
michael@0 1873 }
michael@0 1874 AutoNoisyIndenter indent(gNoisyReflow);
michael@0 1875 #endif
michael@0 1876
michael@0 1877 bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) ||
michael@0 1878 (aState.mReflowState.mFlags.mVResize &&
michael@0 1879 (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT));
michael@0 1880
michael@0 1881 // Reflow our last line if our availableHeight has increased
michael@0 1882 // so that we (and our last child) pull up content as necessary
michael@0 1883 if (aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE
michael@0 1884 && GetNextInFlow() && aState.mReflowState.AvailableHeight() > mRect.height) {
michael@0 1885 line_iterator lastLine = end_lines();
michael@0 1886 if (lastLine != begin_lines()) {
michael@0 1887 --lastLine;
michael@0 1888 lastLine->MarkDirty();
michael@0 1889 }
michael@0 1890 }
michael@0 1891 // the amount by which we will slide the current line if it is not
michael@0 1892 // dirty
michael@0 1893 nscoord deltaY = 0;
michael@0 1894
michael@0 1895 // whether we did NOT reflow the previous line and thus we need to
michael@0 1896 // recompute the carried out margin before the line if we want to
michael@0 1897 // reflow it or if its previous margin is dirty
michael@0 1898 bool needToRecoverState = false;
michael@0 1899 // Float continuations were reflowed in ReflowPushedFloats
michael@0 1900 bool reflowedFloat = mFloats.NotEmpty() &&
michael@0 1901 (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
michael@0 1902 bool lastLineMovedUp = false;
michael@0 1903 // We save up information about BR-clearance here
michael@0 1904 uint8_t inlineFloatBreakType = aState.mFloatBreakType;
michael@0 1905
michael@0 1906 line_iterator line = begin_lines(), line_end = end_lines();
michael@0 1907
michael@0 1908 // Reflow the lines that are already ours
michael@0 1909 for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
michael@0 1910 DumpLine(aState, line, deltaY, 0);
michael@0 1911 #ifdef DEBUG
michael@0 1912 AutoNoisyIndenter indent2(gNoisyReflow);
michael@0 1913 #endif
michael@0 1914
michael@0 1915 if (selfDirty)
michael@0 1916 line->MarkDirty();
michael@0 1917
michael@0 1918 // This really sucks, but we have to look inside any blocks that have clear
michael@0 1919 // elements inside them.
michael@0 1920 // XXX what can we do smarter here?
michael@0 1921 if (!line->IsDirty() && line->IsBlock() &&
michael@0 1922 (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
michael@0 1923 line->MarkDirty();
michael@0 1924 }
michael@0 1925
michael@0 1926 nsIFrame *replacedBlock = nullptr;
michael@0 1927 if (line->IsBlock() &&
michael@0 1928 !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
michael@0 1929 replacedBlock = line->mFirstChild;
michael@0 1930 }
michael@0 1931
michael@0 1932 // We have to reflow the line if it's a block whose clearance
michael@0 1933 // might have changed, so detect that.
michael@0 1934 if (!line->IsDirty() &&
michael@0 1935 (line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE ||
michael@0 1936 replacedBlock)) {
michael@0 1937 nscoord curY = aState.mY;
michael@0 1938 // See where we would be after applying any clearance due to
michael@0 1939 // BRs.
michael@0 1940 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
michael@0 1941 curY = aState.ClearFloats(curY, inlineFloatBreakType);
michael@0 1942 }
michael@0 1943
michael@0 1944 nscoord newY =
michael@0 1945 aState.ClearFloats(curY, line->GetBreakTypeBefore(), replacedBlock);
michael@0 1946
michael@0 1947 if (line->HasClearance()) {
michael@0 1948 // Reflow the line if it might not have clearance anymore.
michael@0 1949 if (newY == curY
michael@0 1950 // aState.mY is the clearance point which should be the
michael@0 1951 // top border-edge of the block frame. If sliding the
michael@0 1952 // block by deltaY isn't going to put it in the predicted
michael@0 1953 // position, then we'd better reflow the line.
michael@0 1954 || newY != line->BStart() + deltaY) {
michael@0 1955 line->MarkDirty();
michael@0 1956 }
michael@0 1957 } else {
michael@0 1958 // Reflow the line if the line might have clearance now.
michael@0 1959 if (curY != newY) {
michael@0 1960 line->MarkDirty();
michael@0 1961 }
michael@0 1962 }
michael@0 1963 }
michael@0 1964
michael@0 1965 // We might have to reflow a line that is after a clearing BR.
michael@0 1966 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
michael@0 1967 aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
michael@0 1968 if (aState.mY != line->BStart() + deltaY) {
michael@0 1969 // SlideLine is not going to put the line where the clearance
michael@0 1970 // put it. Reflow the line to be sure.
michael@0 1971 line->MarkDirty();
michael@0 1972 }
michael@0 1973 inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
michael@0 1974 }
michael@0 1975
michael@0 1976 bool previousMarginWasDirty = line->IsPreviousMarginDirty();
michael@0 1977 if (previousMarginWasDirty) {
michael@0 1978 // If the previous margin is dirty, reflow the current line
michael@0 1979 line->MarkDirty();
michael@0 1980 line->ClearPreviousMarginDirty();
michael@0 1981 } else if (line->BEnd() + deltaY > aState.mBottomEdge) {
michael@0 1982 // Lines that aren't dirty but get slid past our height constraint must
michael@0 1983 // be reflowed.
michael@0 1984 line->MarkDirty();
michael@0 1985 }
michael@0 1986
michael@0 1987 // If we have a constrained height (i.e., breaking columns/pages),
michael@0 1988 // and the distance to the bottom might have changed, then we need
michael@0 1989 // to reflow any line that might have floats in it, both because the
michael@0 1990 // breakpoints within those floats may have changed and because we
michael@0 1991 // might have to push/pull the floats in their entirety.
michael@0 1992 // FIXME: What about a deltaY or height change that forces us to
michael@0 1993 // push lines? Why does that work?
michael@0 1994 if (!line->IsDirty() &&
michael@0 1995 aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
michael@0 1996 (deltaY != 0 || aState.mReflowState.mFlags.mVResize ||
michael@0 1997 aState.mReflowState.mFlags.mMustReflowPlaceholders) &&
michael@0 1998 (line->IsBlock() || line->HasFloats() || line->HadFloatPushed())) {
michael@0 1999 line->MarkDirty();
michael@0 2000 }
michael@0 2001
michael@0 2002 if (!line->IsDirty()) {
michael@0 2003 // See if there's any reflow damage that requires that we mark the
michael@0 2004 // line dirty.
michael@0 2005 PropagateFloatDamage(aState, line, deltaY);
michael@0 2006 }
michael@0 2007
michael@0 2008 // If the container width has changed reset the container width. If the
michael@0 2009 // line's writing mode is not ltr, or if the line is not left-aligned, also
michael@0 2010 // mark the line dirty.
michael@0 2011 if (aState.mContainerWidth != line->mContainerWidth) {
michael@0 2012 line->mContainerWidth = aState.mContainerWidth;
michael@0 2013
michael@0 2014 bool isLastLine = line == mLines.back() &&
michael@0 2015 !GetNextInFlow() &&
michael@0 2016 NS_STYLE_TEXT_ALIGN_AUTO == StyleText()->mTextAlignLast;
michael@0 2017 uint8_t align = isLastLine ?
michael@0 2018 StyleText()->mTextAlign : StyleText()->mTextAlignLast;
michael@0 2019
michael@0 2020 if (line->mWritingMode.IsVertical() ||
michael@0 2021 !line->mWritingMode.IsBidiLTR() ||
michael@0 2022 !IsAlignedLeft(align,
michael@0 2023 aState.mReflowState.mStyleVisibility->mDirection,
michael@0 2024 StyleTextReset()->mUnicodeBidi, this)) {
michael@0 2025 line->MarkDirty();
michael@0 2026 }
michael@0 2027 }
michael@0 2028
michael@0 2029 if (needToRecoverState && line->IsDirty()) {
michael@0 2030 // We need to reconstruct the bottom margin only if we didn't
michael@0 2031 // reflow the previous line and we do need to reflow (or repair
michael@0 2032 // the top position of) the next line.
michael@0 2033 aState.ReconstructMarginAbove(line);
michael@0 2034 }
michael@0 2035
michael@0 2036 bool reflowedPrevLine = !needToRecoverState;
michael@0 2037 if (needToRecoverState) {
michael@0 2038 needToRecoverState = false;
michael@0 2039
michael@0 2040 // Update aState.mPrevChild as if we had reflowed all of the frames in
michael@0 2041 // this line.
michael@0 2042 if (line->IsDirty()) {
michael@0 2043 NS_ASSERTION(line->mFirstChild->GetPrevSibling() ==
michael@0 2044 line.prev()->LastChild(), "unexpected line frames");
michael@0 2045 aState.mPrevChild = line->mFirstChild->GetPrevSibling();
michael@0 2046 }
michael@0 2047 }
michael@0 2048
michael@0 2049 // Now repair the line and update |aState.mY| by calling
michael@0 2050 // |ReflowLine| or |SlideLine|.
michael@0 2051 // If we're going to reflow everything again, then no need to reflow
michael@0 2052 // the dirty line ... unless the line has floats, in which case we'd
michael@0 2053 // better reflow it now to refresh its float cache, which may contain
michael@0 2054 // dangling frame pointers! Ugh! This reflow of the line may be
michael@0 2055 // incorrect because we skipped reflowing previous lines (e.g., floats
michael@0 2056 // may be placed incorrectly), but that's OK because we'll mark the
michael@0 2057 // line dirty below under "if (aState.mReflowState.mDiscoveredClearance..."
michael@0 2058 if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
michael@0 2059 lastLineMovedUp = true;
michael@0 2060
michael@0 2061 bool maybeReflowingForFirstTime =
michael@0 2062 line->IStart() == 0 && line->BStart() == 0 &&
michael@0 2063 line->ISize() == 0 && line->BSize() == 0;
michael@0 2064
michael@0 2065 // Compute the dirty lines "before" BEnd, after factoring in
michael@0 2066 // the running deltaY value - the running value is implicit in
michael@0 2067 // aState.mY.
michael@0 2068 nscoord oldY = line->BStart();
michael@0 2069 nscoord oldYMost = line->BEnd();
michael@0 2070
michael@0 2071 NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
michael@0 2072 "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this");
michael@0 2073
michael@0 2074 // Reflow the dirty line. If it's an incremental reflow, then force
michael@0 2075 // it to invalidate the dirty area if necessary
michael@0 2076 rv = ReflowLine(aState, line, &keepGoing);
michael@0 2077 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2078
michael@0 2079 if (aState.mReflowState.WillReflowAgainForClearance()) {
michael@0 2080 line->MarkDirty();
michael@0 2081 willReflowAgain = true;
michael@0 2082 // Note that once we've entered this state, every line that gets here
michael@0 2083 // (e.g. because it has floats) gets marked dirty and reflowed again.
michael@0 2084 // in the next pass. This is important, see above.
michael@0 2085 }
michael@0 2086
michael@0 2087 if (line->HasFloats()) {
michael@0 2088 reflowedFloat = true;
michael@0 2089 }
michael@0 2090
michael@0 2091 if (!keepGoing) {
michael@0 2092 DumpLine(aState, line, deltaY, -1);
michael@0 2093 if (0 == line->GetChildCount()) {
michael@0 2094 DeleteLine(aState, line, line_end);
michael@0 2095 }
michael@0 2096 break;
michael@0 2097 }
michael@0 2098
michael@0 2099 // Test to see whether the margin that should be carried out
michael@0 2100 // to the next line (NL) might have changed. In ReflowBlockFrame
michael@0 2101 // we call nextLine->MarkPreviousMarginDirty if the block's
michael@0 2102 // actual carried-out bottom margin changed. So here we only
michael@0 2103 // need to worry about the following effects:
michael@0 2104 // 1) the line was just created, and it might now be blocking
michael@0 2105 // a carried-out bottom margin from previous lines that
michael@0 2106 // used to reach NL from reaching NL
michael@0 2107 // 2) the line used to be empty, and is now not empty,
michael@0 2108 // thus blocking a carried-out bottom margin from previous lines
michael@0 2109 // that used to reach NL from reaching NL
michael@0 2110 // 3) the line wasn't empty, but now is, so a carried-out
michael@0 2111 // bottom margin from previous lines that didn't used to reach NL
michael@0 2112 // now does
michael@0 2113 // 4) the line might have changed in a way that affects NL's
michael@0 2114 // ShouldApplyTopMargin decision. The three things that matter
michael@0 2115 // are the line's emptiness, its adjacency to the top of the block,
michael@0 2116 // and whether it has clearance (the latter only matters if the block
michael@0 2117 // was and is adjacent to the top and empty).
michael@0 2118 //
michael@0 2119 // If the line is empty now, we can't reliably tell if the line was empty
michael@0 2120 // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
michael@0 2121 // This means the checks in 4) are redundant; if the line is empty now
michael@0 2122 // we don't need to check 4), but if the line is not empty now and we're sure
michael@0 2123 // it wasn't empty before, any adjacency and clearance changes are irrelevant
michael@0 2124 // to the result of nextLine->ShouldApplyTopMargin.
michael@0 2125 if (line.next() != end_lines()) {
michael@0 2126 bool maybeWasEmpty = oldY == line.next()->BStart();
michael@0 2127 bool isEmpty = line->CachedIsEmpty();
michael@0 2128 if (maybeReflowingForFirstTime /*1*/ ||
michael@0 2129 (isEmpty || maybeWasEmpty) /*2/3/4*/) {
michael@0 2130 line.next()->MarkPreviousMarginDirty();
michael@0 2131 // since it's marked dirty, nobody will care about |deltaY|
michael@0 2132 }
michael@0 2133 }
michael@0 2134
michael@0 2135 // If the line was just reflowed for the first time, then its
michael@0 2136 // old mBounds cannot be trusted so this deltaY computation is
michael@0 2137 // bogus. But that's OK because we just did
michael@0 2138 // MarkPreviousMarginDirty on the next line which will force it
michael@0 2139 // to be reflowed, so this computation of deltaY will not be
michael@0 2140 // used.
michael@0 2141 deltaY = line->BEnd() - oldYMost;
michael@0 2142
michael@0 2143 // Now do an interrupt check. We want to do this only in the case when we
michael@0 2144 // actually reflow the line, so that if we get back in here we'll get
michael@0 2145 // further on the reflow before interrupting.
michael@0 2146 aState.mPresContext->CheckForInterrupt(this);
michael@0 2147 } else {
michael@0 2148 aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
michael@0 2149 // Nop except for blocks (we don't create overflow container
michael@0 2150 // continuations for any inlines atm), so only checking mFirstChild
michael@0 2151 // is enough
michael@0 2152
michael@0 2153 lastLineMovedUp = deltaY < 0;
michael@0 2154
michael@0 2155 if (deltaY != 0)
michael@0 2156 SlideLine(aState, line, deltaY);
michael@0 2157 else
michael@0 2158 repositionViews = true;
michael@0 2159
michael@0 2160 NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
michael@0 2161 "Possibly stale float cache here!");
michael@0 2162 if (willReflowAgain && line->IsBlock()) {
michael@0 2163 // If we're going to reflow everything again, and this line is a block,
michael@0 2164 // then there is no need to recover float state. The line may contain
michael@0 2165 // other lines with floats, but in that case RecoverStateFrom would only
michael@0 2166 // add floats to the float manager. We don't need to do that because
michael@0 2167 // everything's going to get reflowed again "for real". Calling
michael@0 2168 // RecoverStateFrom in this situation could be lethal because the
michael@0 2169 // block's descendant lines may have float caches containing dangling
michael@0 2170 // frame pointers. Ugh!
michael@0 2171 // If this line is inline, then we need to recover its state now
michael@0 2172 // to make sure that we don't forget to move its floats by deltaY.
michael@0 2173 } else {
michael@0 2174 // XXX EVIL O(N^2) EVIL
michael@0 2175 aState.RecoverStateFrom(line, deltaY);
michael@0 2176 }
michael@0 2177
michael@0 2178 // Keep mY up to date in case we're propagating reflow damage
michael@0 2179 // and also because our final height may depend on it. If the
michael@0 2180 // line is inlines, then only update mY if the line is not
michael@0 2181 // empty, because that's what PlaceLine does. (Empty blocks may
michael@0 2182 // want to update mY, e.g. if they have clearance.)
michael@0 2183 if (line->IsBlock() || !line->CachedIsEmpty()) {
michael@0 2184 aState.mY = line->BEnd();
michael@0 2185 }
michael@0 2186
michael@0 2187 needToRecoverState = true;
michael@0 2188
michael@0 2189 if (reflowedPrevLine && !line->IsBlock() &&
michael@0 2190 aState.mPresContext->HasPendingInterrupt()) {
michael@0 2191 // Need to make sure to pull overflows from any prev-in-flows
michael@0 2192 for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
michael@0 2193 inlineKid = inlineKid->GetFirstPrincipalChild()) {
michael@0 2194 inlineKid->PullOverflowsFromPrevInFlow();
michael@0 2195 }
michael@0 2196 }
michael@0 2197 }
michael@0 2198
michael@0 2199 // Record if we need to clear floats before reflowing the next
michael@0 2200 // line. Note that inlineFloatBreakType will be handled and
michael@0 2201 // cleared before the next line is processed, so there is no
michael@0 2202 // need to combine break types here.
michael@0 2203 if (line->HasFloatBreakAfter()) {
michael@0 2204 inlineFloatBreakType = line->GetBreakTypeAfter();
michael@0 2205 }
michael@0 2206
michael@0 2207 if (LineHasClear(line.get())) {
michael@0 2208 foundAnyClears = true;
michael@0 2209 }
michael@0 2210
michael@0 2211 DumpLine(aState, line, deltaY, -1);
michael@0 2212
michael@0 2213 if (aState.mPresContext->HasPendingInterrupt()) {
michael@0 2214 willReflowAgain = true;
michael@0 2215 // Another option here might be to leave |line| clean if
michael@0 2216 // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
michael@0 2217 // that case the line really did reflow as it should have. Not sure
michael@0 2218 // whether that would be safe, so doing this for now instead. Also not
michael@0 2219 // sure whether we really want to mark all lines dirty after an
michael@0 2220 // interrupt, but until we get better at propagating float damage we
michael@0 2221 // really do need to do it this way; see comments inside MarkLineDirty.
michael@0 2222 MarkLineDirtyForInterrupt(line);
michael@0 2223 }
michael@0 2224 }
michael@0 2225
michael@0 2226 // Handle BR-clearance from the last line of the block
michael@0 2227 if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
michael@0 2228 aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
michael@0 2229 }
michael@0 2230
michael@0 2231 if (needToRecoverState) {
michael@0 2232 // Is this expensive?
michael@0 2233 aState.ReconstructMarginAbove(line);
michael@0 2234
michael@0 2235 // Update aState.mPrevChild as if we had reflowed all of the frames in
michael@0 2236 // the last line.
michael@0 2237 NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
michael@0 2238 line.prev()->LastChild(), "unexpected line frames");
michael@0 2239 aState.mPrevChild =
michael@0 2240 line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling();
michael@0 2241 }
michael@0 2242
michael@0 2243 // Should we really have to do this?
michael@0 2244 if (repositionViews)
michael@0 2245 nsContainerFrame::PlaceFrameView(this);
michael@0 2246
michael@0 2247 // We can skip trying to pull up the next line if our height is constrained
michael@0 2248 // (so we can report being incomplete) and there is no next in flow or we
michael@0 2249 // were told not to or we know it will be futile, i.e.,
michael@0 2250 // -- the next in flow is not changing
michael@0 2251 // -- and we cannot have added more space for its first line to be
michael@0 2252 // pulled up into,
michael@0 2253 // -- it's an incremental reflow of a descendant
michael@0 2254 // -- and we didn't reflow any floats (so the available space
michael@0 2255 // didn't change)
michael@0 2256 // -- my chain of next-in-flows either has no first line, or its first
michael@0 2257 // line isn't dirty.
michael@0 2258 bool heightConstrained =
michael@0 2259 aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE;
michael@0 2260 bool skipPull = willReflowAgain && heightConstrained;
michael@0 2261 if (!skipPull && heightConstrained && aState.mNextInFlow &&
michael@0 2262 (aState.mReflowState.mFlags.mNextInFlowUntouched &&
michael@0 2263 !lastLineMovedUp &&
michael@0 2264 !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
michael@0 2265 !reflowedFloat)) {
michael@0 2266 // We'll place lineIter at the last line of this block, so that
michael@0 2267 // nsBlockInFlowLineIterator::Next() will take us to the first
michael@0 2268 // line of my next-in-flow-chain. (But first, check that I
michael@0 2269 // have any lines -- if I don't, just bail out of this
michael@0 2270 // optimization.)
michael@0 2271 line_iterator lineIter = this->end_lines();
michael@0 2272 if (lineIter != this->begin_lines()) {
michael@0 2273 lineIter--; // I have lines; step back from dummy iterator to last line.
michael@0 2274 nsBlockInFlowLineIterator bifLineIter(this, lineIter);
michael@0 2275
michael@0 2276 // Check for next-in-flow-chain's first line.
michael@0 2277 // (First, see if there is such a line, and second, see if it's clean)
michael@0 2278 if (!bifLineIter.Next() ||
michael@0 2279 !bifLineIter.GetLine()->IsDirty()) {
michael@0 2280 skipPull=true;
michael@0 2281 }
michael@0 2282 }
michael@0 2283 }
michael@0 2284
michael@0 2285 if (skipPull && aState.mNextInFlow) {
michael@0 2286 NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
michael@0 2287 if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
michael@0 2288 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
michael@0 2289 else
michael@0 2290 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 2291 }
michael@0 2292
michael@0 2293 if (!skipPull && aState.mNextInFlow) {
michael@0 2294 // Pull data from a next-in-flow if there's still room for more
michael@0 2295 // content here.
michael@0 2296 while (keepGoing && aState.mNextInFlow) {
michael@0 2297 // Grab first line from our next-in-flow
michael@0 2298 nsBlockFrame* nextInFlow = aState.mNextInFlow;
michael@0 2299 nsLineBox* pulledLine;
michael@0 2300 nsFrameList pulledFrames;
michael@0 2301 if (!nextInFlow->mLines.empty()) {
michael@0 2302 RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames,
michael@0 2303 &pulledLine, &pulledFrames);
michael@0 2304 } else {
michael@0 2305 // Grab an overflow line if there are any
michael@0 2306 FrameLines* overflowLines = nextInFlow->GetOverflowLines();
michael@0 2307 if (!overflowLines) {
michael@0 2308 aState.mNextInFlow =
michael@0 2309 static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
michael@0 2310 continue;
michael@0 2311 }
michael@0 2312 bool last =
michael@0 2313 RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
michael@0 2314 &pulledLine, &pulledFrames);
michael@0 2315 if (last) {
michael@0 2316 nextInFlow->DestroyOverflowLines();
michael@0 2317 }
michael@0 2318 }
michael@0 2319
michael@0 2320 if (pulledFrames.IsEmpty()) {
michael@0 2321 // The line is empty. Try the next one.
michael@0 2322 NS_ASSERTION(pulledLine->GetChildCount() == 0 &&
michael@0 2323 !pulledLine->mFirstChild, "bad empty line");
michael@0 2324 nextInFlow->FreeLineBox(pulledLine);
michael@0 2325 continue;
michael@0 2326 }
michael@0 2327
michael@0 2328 if (pulledLine == nextInFlow->GetLineCursor()) {
michael@0 2329 nextInFlow->ClearLineCursor();
michael@0 2330 }
michael@0 2331 ReparentFrames(pulledFrames, nextInFlow, this);
michael@0 2332
michael@0 2333 NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
michael@0 2334 "Unexpected last frame");
michael@0 2335 NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here");
michael@0 2336 NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
michael@0 2337 "Incorrect aState.mPrevChild before inserting line at end");
michael@0 2338
michael@0 2339 // Shift pulledLine's frames into our mFrames list.
michael@0 2340 mFrames.AppendFrames(nullptr, pulledFrames);
michael@0 2341
michael@0 2342 // Add line to our line list, and set its last child as our new prev-child
michael@0 2343 line = mLines.before_insert(end_lines(), pulledLine);
michael@0 2344 aState.mPrevChild = mFrames.LastChild();
michael@0 2345
michael@0 2346 // Reparent floats whose placeholders are in the line.
michael@0 2347 ReparentFloats(pulledLine->mFirstChild, nextInFlow, true);
michael@0 2348
michael@0 2349 DumpLine(aState, pulledLine, deltaY, 0);
michael@0 2350 #ifdef DEBUG
michael@0 2351 AutoNoisyIndenter indent2(gNoisyReflow);
michael@0 2352 #endif
michael@0 2353
michael@0 2354 if (aState.mPresContext->HasPendingInterrupt()) {
michael@0 2355 MarkLineDirtyForInterrupt(line);
michael@0 2356 } else {
michael@0 2357 // Now reflow it and any lines that it makes during it's reflow
michael@0 2358 // (we have to loop here because reflowing the line may cause a new
michael@0 2359 // line to be created; see SplitLine's callers for examples of
michael@0 2360 // when this happens).
michael@0 2361 while (line != end_lines()) {
michael@0 2362 rv = ReflowLine(aState, line, &keepGoing);
michael@0 2363 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2364
michael@0 2365 if (aState.mReflowState.WillReflowAgainForClearance()) {
michael@0 2366 line->MarkDirty();
michael@0 2367 keepGoing = false;
michael@0 2368 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 2369 break;
michael@0 2370 }
michael@0 2371
michael@0 2372 DumpLine(aState, line, deltaY, -1);
michael@0 2373 if (!keepGoing) {
michael@0 2374 if (0 == line->GetChildCount()) {
michael@0 2375 DeleteLine(aState, line, line_end);
michael@0 2376 }
michael@0 2377 break;
michael@0 2378 }
michael@0 2379
michael@0 2380 if (LineHasClear(line.get())) {
michael@0 2381 foundAnyClears = true;
michael@0 2382 }
michael@0 2383
michael@0 2384 if (aState.mPresContext->CheckForInterrupt(this)) {
michael@0 2385 MarkLineDirtyForInterrupt(line);
michael@0 2386 break;
michael@0 2387 }
michael@0 2388
michael@0 2389 // If this is an inline frame then its time to stop
michael@0 2390 ++line;
michael@0 2391 aState.AdvanceToNextLine();
michael@0 2392 }
michael@0 2393 }
michael@0 2394 }
michael@0 2395
michael@0 2396 if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
michael@0 2397 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 2398 } //XXXfr shouldn't set this flag when nextinflow has no lines
michael@0 2399 }
michael@0 2400
michael@0 2401 // Handle an odd-ball case: a list-item with no lines
michael@0 2402 if (HasOutsideBullet() && mLines.empty()) {
michael@0 2403 nsHTMLReflowMetrics metrics(aState.mReflowState);
michael@0 2404 nsIFrame* bullet = GetOutsideBullet();
michael@0 2405 ReflowBullet(bullet, aState, metrics,
michael@0 2406 aState.mReflowState.ComputedPhysicalBorderPadding().top);
michael@0 2407 NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
michael@0 2408 "empty bullet took up space");
michael@0 2409
michael@0 2410 if (!BulletIsEmpty()) {
michael@0 2411 // There are no lines so we have to fake up some y motion so that
michael@0 2412 // we end up with *some* height.
michael@0 2413
michael@0 2414 if (metrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
michael@0 2415 nscoord ascent;
michael@0 2416 if (nsLayoutUtils::GetFirstLineBaseline(bullet, &ascent)) {
michael@0 2417 metrics.SetTopAscent(ascent);
michael@0 2418 } else {
michael@0 2419 metrics.SetTopAscent(metrics.Height());
michael@0 2420 }
michael@0 2421 }
michael@0 2422
michael@0 2423 nsRefPtr<nsFontMetrics> fm;
michael@0 2424 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
michael@0 2425 nsLayoutUtils::FontSizeInflationFor(this));
michael@0 2426 aState.mReflowState.rendContext->SetFont(fm); // FIXME: needed?
michael@0 2427
michael@0 2428 nscoord minAscent =
michael@0 2429 nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight);
michael@0 2430 nscoord minDescent = aState.mMinLineHeight - minAscent;
michael@0 2431
michael@0 2432 aState.mY += std::max(minAscent, metrics.TopAscent()) +
michael@0 2433 std::max(minDescent, metrics.Height() - metrics.TopAscent());
michael@0 2434
michael@0 2435 nscoord offset = minAscent - metrics.TopAscent();
michael@0 2436 if (offset > 0) {
michael@0 2437 bullet->SetRect(bullet->GetRect() + nsPoint(0, offset));
michael@0 2438 }
michael@0 2439 }
michael@0 2440 }
michael@0 2441
michael@0 2442 if (foundAnyClears) {
michael@0 2443 AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
michael@0 2444 } else {
michael@0 2445 RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
michael@0 2446 }
michael@0 2447
michael@0 2448 #ifdef DEBUG
michael@0 2449 VerifyLines(true);
michael@0 2450 VerifyOverflowSituation();
michael@0 2451 if (gNoisyReflow) {
michael@0 2452 IndentBy(stdout, gNoiseIndent - 1);
michael@0 2453 ListTag(stdout);
michael@0 2454 printf(": done reflowing dirty lines (status=%x)\n",
michael@0 2455 aState.mReflowStatus);
michael@0 2456 }
michael@0 2457 #endif
michael@0 2458
michael@0 2459 return rv;
michael@0 2460 }
michael@0 2461
michael@0 2462 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
michael@0 2463 {
michael@0 2464 nsLineList::iterator line = aBlock->begin_lines();
michael@0 2465 nsLineList::iterator endLine = aBlock->end_lines();
michael@0 2466 while (line != endLine) {
michael@0 2467 if (line->IsBlock()) {
michael@0 2468 nsIFrame* f = line->mFirstChild;
michael@0 2469 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
michael@0 2470 if (bf) {
michael@0 2471 MarkAllDescendantLinesDirty(bf);
michael@0 2472 }
michael@0 2473 }
michael@0 2474 line->MarkDirty();
michael@0 2475 ++line;
michael@0 2476 }
michael@0 2477 }
michael@0 2478
michael@0 2479 void
michael@0 2480 nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine)
michael@0 2481 {
michael@0 2482 aLine->MarkDirty();
michael@0 2483
michael@0 2484 // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
michael@0 2485 // marked the lines that need to be marked dirty based on our
michael@0 2486 // vertical resize stuff. So we'll definitely reflow all those kids;
michael@0 2487 // the only question is how they should behave.
michael@0 2488 if (GetStateBits() & NS_FRAME_IS_DIRTY) {
michael@0 2489 // Mark all our child frames dirty so we make sure to reflow them
michael@0 2490 // later.
michael@0 2491 int32_t n = aLine->GetChildCount();
michael@0 2492 for (nsIFrame* f = aLine->mFirstChild; n > 0;
michael@0 2493 f = f->GetNextSibling(), --n) {
michael@0 2494 f->AddStateBits(NS_FRAME_IS_DIRTY);
michael@0 2495 }
michael@0 2496 // And mark all the floats whose reflows we might be skipping dirty too.
michael@0 2497 if (aLine->HasFloats()) {
michael@0 2498 for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) {
michael@0 2499 fc->mFloat->AddStateBits(NS_FRAME_IS_DIRTY);
michael@0 2500 }
michael@0 2501 }
michael@0 2502 } else {
michael@0 2503 // Dirty all the descendant lines of block kids to handle float damage,
michael@0 2504 // since our nsFloatManager will go away by the next time we're reflowing.
michael@0 2505 // XXXbz Can we do something more like what PropagateFloatDamage does?
michael@0 2506 // Would need to sort out the exact business with mBlockDelta for that....
michael@0 2507 // This marks way too much dirty. If we ever make this better, revisit
michael@0 2508 // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
michael@0 2509 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
michael@0 2510 if (bf) {
michael@0 2511 MarkAllDescendantLinesDirty(bf);
michael@0 2512 }
michael@0 2513 }
michael@0 2514 }
michael@0 2515
michael@0 2516 void
michael@0 2517 nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
michael@0 2518 nsLineList::iterator aLine,
michael@0 2519 nsLineList::iterator aLineEnd)
michael@0 2520 {
michael@0 2521 NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
michael@0 2522 if (0 == aLine->GetChildCount()) {
michael@0 2523 NS_ASSERTION(aState.mCurrentLine == aLine,
michael@0 2524 "using function more generally than designed, "
michael@0 2525 "but perhaps OK now");
michael@0 2526 nsLineBox* line = aLine;
michael@0 2527 aLine = mLines.erase(aLine);
michael@0 2528 FreeLineBox(line);
michael@0 2529 // Mark the previous margin of the next line dirty since we need to
michael@0 2530 // recompute its top position.
michael@0 2531 if (aLine != aLineEnd)
michael@0 2532 aLine->MarkPreviousMarginDirty();
michael@0 2533 }
michael@0 2534 }
michael@0 2535
michael@0 2536 /**
michael@0 2537 * Reflow a line. The line will either contain a single block frame
michael@0 2538 * or contain 1 or more inline frames. aKeepReflowGoing indicates
michael@0 2539 * whether or not the caller should continue to reflow more lines.
michael@0 2540 */
michael@0 2541 nsresult
michael@0 2542 nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
michael@0 2543 line_iterator aLine,
michael@0 2544 bool* aKeepReflowGoing)
michael@0 2545 {
michael@0 2546 nsresult rv = NS_OK;
michael@0 2547
michael@0 2548 NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
michael@0 2549
michael@0 2550 // Setup the line-layout for the new line
michael@0 2551 aState.mCurrentLine = aLine;
michael@0 2552 aLine->ClearDirty();
michael@0 2553 aLine->InvalidateCachedIsEmpty();
michael@0 2554 aLine->ClearHadFloatPushed();
michael@0 2555
michael@0 2556 // Now that we know what kind of line we have, reflow it
michael@0 2557 if (aLine->IsBlock()) {
michael@0 2558 rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
michael@0 2559 } else {
michael@0 2560 aLine->SetLineWrapped(false);
michael@0 2561 rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
michael@0 2562 }
michael@0 2563
michael@0 2564 return rv;
michael@0 2565 }
michael@0 2566
michael@0 2567 nsIFrame*
michael@0 2568 nsBlockFrame::PullFrame(nsBlockReflowState& aState,
michael@0 2569 line_iterator aLine)
michael@0 2570 {
michael@0 2571 // First check our remaining lines.
michael@0 2572 if (end_lines() != aLine.next()) {
michael@0 2573 return PullFrameFrom(aLine, this, aLine.next());
michael@0 2574 }
michael@0 2575
michael@0 2576 NS_ASSERTION(!GetOverflowLines(),
michael@0 2577 "Our overflow lines should have been removed at the start of reflow");
michael@0 2578
michael@0 2579 // Try each next-in-flow.
michael@0 2580 nsBlockFrame* nextInFlow = aState.mNextInFlow;
michael@0 2581 while (nextInFlow) {
michael@0 2582 if (nextInFlow->mLines.empty()) {
michael@0 2583 nextInFlow->DrainSelfOverflowList();
michael@0 2584 }
michael@0 2585 if (!nextInFlow->mLines.empty()) {
michael@0 2586 return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin());
michael@0 2587 }
michael@0 2588 nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
michael@0 2589 aState.mNextInFlow = nextInFlow;
michael@0 2590 }
michael@0 2591
michael@0 2592 return nullptr;
michael@0 2593 }
michael@0 2594
michael@0 2595 nsIFrame*
michael@0 2596 nsBlockFrame::PullFrameFrom(nsLineBox* aLine,
michael@0 2597 nsBlockFrame* aFromContainer,
michael@0 2598 nsLineList::iterator aFromLine)
michael@0 2599 {
michael@0 2600 nsLineBox* fromLine = aFromLine;
michael@0 2601 NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
michael@0 2602 NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
michael@0 2603 NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
michael@0 2604
michael@0 2605 NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
michael@0 2606 "Disagreement about whether it's a block or not");
michael@0 2607
michael@0 2608 if (fromLine->IsBlock()) {
michael@0 2609 // If our line is not empty and the child in aFromLine is a block
michael@0 2610 // then we cannot pull up the frame into this line. In this case
michael@0 2611 // we stop pulling.
michael@0 2612 return nullptr;
michael@0 2613 }
michael@0 2614 // Take frame from fromLine
michael@0 2615 nsIFrame* frame = fromLine->mFirstChild;
michael@0 2616 nsIFrame* newFirstChild = frame->GetNextSibling();
michael@0 2617
michael@0 2618 if (aFromContainer != this) {
michael@0 2619 // The frame is being pulled from a next-in-flow; therefore we
michael@0 2620 // need to add it to our sibling list.
michael@0 2621 MOZ_ASSERT(aLine == mLines.back());
michael@0 2622 MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
michael@0 2623 "should only pull from first line");
michael@0 2624 aFromContainer->mFrames.RemoveFrame(frame);
michael@0 2625
michael@0 2626 // When pushing and pulling frames we need to check for whether any
michael@0 2627 // views need to be reparented.
michael@0 2628 ReparentFrame(frame, aFromContainer, this);
michael@0 2629 mFrames.AppendFrame(nullptr, frame);
michael@0 2630
michael@0 2631 // The frame might have (or contain) floats that need to be brought
michael@0 2632 // over too. (pass 'false' since there are no siblings to check)
michael@0 2633 ReparentFloats(frame, aFromContainer, false);
michael@0 2634 } else {
michael@0 2635 MOZ_ASSERT(aLine == aFromLine.prev());
michael@0 2636 }
michael@0 2637
michael@0 2638 aLine->NoteFrameAdded(frame);
michael@0 2639 fromLine->NoteFrameRemoved(frame);
michael@0 2640
michael@0 2641 if (fromLine->GetChildCount() > 0) {
michael@0 2642 // Mark line dirty now that we pulled a child
michael@0 2643 fromLine->MarkDirty();
michael@0 2644 fromLine->mFirstChild = newFirstChild;
michael@0 2645 } else {
michael@0 2646 // Free up the fromLine now that it's empty.
michael@0 2647 // Its bounds might need to be redrawn, though.
michael@0 2648 if (aFromLine.next() != aFromContainer->mLines.end()) {
michael@0 2649 aFromLine.next()->MarkPreviousMarginDirty();
michael@0 2650 }
michael@0 2651 aFromContainer->mLines.erase(aFromLine);
michael@0 2652 // aFromLine is now invalid
michael@0 2653 aFromContainer->FreeLineBox(fromLine);
michael@0 2654 }
michael@0 2655
michael@0 2656 #ifdef DEBUG
michael@0 2657 VerifyLines(true);
michael@0 2658 VerifyOverflowSituation();
michael@0 2659 #endif
michael@0 2660
michael@0 2661 return frame;
michael@0 2662 }
michael@0 2663
michael@0 2664 void
michael@0 2665 nsBlockFrame::SlideLine(nsBlockReflowState& aState,
michael@0 2666 nsLineBox* aLine, nscoord aDY)
michael@0 2667 {
michael@0 2668 NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
michael@0 2669
michael@0 2670 // Adjust line state
michael@0 2671 aLine->SlideBy(aDY, aState.mContainerWidth);
michael@0 2672
michael@0 2673 // Adjust the frames in the line
michael@0 2674 nsIFrame* kid = aLine->mFirstChild;
michael@0 2675 if (!kid) {
michael@0 2676 return;
michael@0 2677 }
michael@0 2678
michael@0 2679 if (aLine->IsBlock()) {
michael@0 2680 if (aDY) {
michael@0 2681 kid->MovePositionBy(nsPoint(0, aDY));
michael@0 2682 }
michael@0 2683
michael@0 2684 // Make sure the frame's view and any child views are updated
michael@0 2685 nsContainerFrame::PlaceFrameView(kid);
michael@0 2686 }
michael@0 2687 else {
michael@0 2688 // Adjust the Y coordinate of the frames in the line.
michael@0 2689 // Note: we need to re-position views even if aDY is 0, because
michael@0 2690 // one of our parent frames may have moved and so the view's position
michael@0 2691 // relative to its parent may have changed
michael@0 2692 int32_t n = aLine->GetChildCount();
michael@0 2693 while (--n >= 0) {
michael@0 2694 if (aDY) {
michael@0 2695 kid->MovePositionBy(nsPoint(0, aDY));
michael@0 2696 }
michael@0 2697 // Make sure the frame's view and any child views are updated
michael@0 2698 nsContainerFrame::PlaceFrameView(kid);
michael@0 2699 kid = kid->GetNextSibling();
michael@0 2700 }
michael@0 2701 }
michael@0 2702 }
michael@0 2703
michael@0 2704 nsresult
michael@0 2705 nsBlockFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 2706 nsIAtom* aAttribute,
michael@0 2707 int32_t aModType)
michael@0 2708 {
michael@0 2709 nsresult rv = nsBlockFrameSuper::AttributeChanged(aNameSpaceID,
michael@0 2710 aAttribute, aModType);
michael@0 2711
michael@0 2712 if (NS_FAILED(rv)) {
michael@0 2713 return rv;
michael@0 2714 }
michael@0 2715 if (nsGkAtoms::start == aAttribute ||
michael@0 2716 (nsGkAtoms::reversed == aAttribute && mContent->IsHTML(nsGkAtoms::ol))) {
michael@0 2717 nsPresContext* presContext = PresContext();
michael@0 2718
michael@0 2719 // XXX Not sure if this is necessary anymore
michael@0 2720 if (RenumberLists(presContext)) {
michael@0 2721 presContext->PresShell()->
michael@0 2722 FrameNeedsReflow(this, nsIPresShell::eStyleChange,
michael@0 2723 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 2724 }
michael@0 2725 }
michael@0 2726 else if (nsGkAtoms::value == aAttribute) {
michael@0 2727 const nsStyleDisplay* styleDisplay = StyleDisplay();
michael@0 2728 if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
michael@0 2729 // Search for the closest ancestor that's a block frame. We
michael@0 2730 // make the assumption that all related list items share a
michael@0 2731 // common block parent.
michael@0 2732 // XXXldb I think that's a bad assumption.
michael@0 2733 nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this);
michael@0 2734
michael@0 2735 // Tell the enclosing block frame to renumber list items within
michael@0 2736 // itself
michael@0 2737 if (nullptr != blockParent) {
michael@0 2738 nsPresContext* presContext = PresContext();
michael@0 2739 // XXX Not sure if this is necessary anymore
michael@0 2740 if (blockParent->RenumberLists(presContext)) {
michael@0 2741 presContext->PresShell()->
michael@0 2742 FrameNeedsReflow(blockParent, nsIPresShell::eStyleChange,
michael@0 2743 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 2744 }
michael@0 2745 }
michael@0 2746 }
michael@0 2747 }
michael@0 2748
michael@0 2749 return rv;
michael@0 2750 }
michael@0 2751
michael@0 2752 static inline bool
michael@0 2753 IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord)
michael@0 2754 {
michael@0 2755 if (aCoord.GetUnit() == eStyleUnit_Auto)
michael@0 2756 return false;
michael@0 2757 if (aCoord.IsCoordPercentCalcUnit()) {
michael@0 2758 // If we evaluate the length/percent/calc at a percentage basis of
michael@0 2759 // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
michael@0 2760 // length, percent, or combination thereof. Test > 0 so we clamp
michael@0 2761 // negative calc() results to 0.
michael@0 2762 return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
michael@0 2763 nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
michael@0 2764 }
michael@0 2765 NS_ABORT_IF_FALSE(false, "unexpected unit for height or min-height");
michael@0 2766 return true;
michael@0 2767 }
michael@0 2768
michael@0 2769 /* virtual */ bool
michael@0 2770 nsBlockFrame::IsSelfEmpty()
michael@0 2771 {
michael@0 2772 // Blocks which are margin-roots (including inline-blocks) cannot be treated
michael@0 2773 // as empty for margin-collapsing and other purposes. They're more like
michael@0 2774 // replaced elements.
michael@0 2775 if (GetStateBits() & NS_BLOCK_MARGIN_ROOT)
michael@0 2776 return false;
michael@0 2777
michael@0 2778 const nsStylePosition* position = StylePosition();
michael@0 2779
michael@0 2780 if (IsNonAutoNonZeroHeight(position->mMinHeight) ||
michael@0 2781 IsNonAutoNonZeroHeight(position->mHeight))
michael@0 2782 return false;
michael@0 2783
michael@0 2784 const nsStyleBorder* border = StyleBorder();
michael@0 2785 const nsStylePadding* padding = StylePadding();
michael@0 2786 if (border->GetComputedBorderWidth(NS_SIDE_TOP) != 0 ||
michael@0 2787 border->GetComputedBorderWidth(NS_SIDE_BOTTOM) != 0 ||
michael@0 2788 !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) ||
michael@0 2789 !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom())) {
michael@0 2790 return false;
michael@0 2791 }
michael@0 2792
michael@0 2793 if (HasOutsideBullet() && !BulletIsEmpty()) {
michael@0 2794 return false;
michael@0 2795 }
michael@0 2796
michael@0 2797 return true;
michael@0 2798 }
michael@0 2799
michael@0 2800 bool
michael@0 2801 nsBlockFrame::CachedIsEmpty()
michael@0 2802 {
michael@0 2803 if (!IsSelfEmpty()) {
michael@0 2804 return false;
michael@0 2805 }
michael@0 2806
michael@0 2807 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 2808 line != line_end;
michael@0 2809 ++line)
michael@0 2810 {
michael@0 2811 if (!line->CachedIsEmpty())
michael@0 2812 return false;
michael@0 2813 }
michael@0 2814
michael@0 2815 return true;
michael@0 2816 }
michael@0 2817
michael@0 2818 bool
michael@0 2819 nsBlockFrame::IsEmpty()
michael@0 2820 {
michael@0 2821 if (!IsSelfEmpty()) {
michael@0 2822 return false;
michael@0 2823 }
michael@0 2824
michael@0 2825 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 2826 line != line_end;
michael@0 2827 ++line)
michael@0 2828 {
michael@0 2829 if (!line->IsEmpty())
michael@0 2830 return false;
michael@0 2831 }
michael@0 2832
michael@0 2833 return true;
michael@0 2834 }
michael@0 2835
michael@0 2836 bool
michael@0 2837 nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
michael@0 2838 nsLineBox* aLine)
michael@0 2839 {
michael@0 2840 if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
michael@0 2841 // Apply short-circuit check to avoid searching the line list
michael@0 2842 return true;
michael@0 2843 }
michael@0 2844
michael@0 2845 if (!aState.IsAdjacentWithTop()) {
michael@0 2846 // If we aren't at the top Y coordinate then something of non-zero
michael@0 2847 // height must have been placed. Therefore the childs top-margin
michael@0 2848 // applies.
michael@0 2849 aState.SetFlag(BRS_APPLYTOPMARGIN, true);
michael@0 2850 return true;
michael@0 2851 }
michael@0 2852
michael@0 2853 // Determine if this line is "essentially" the first line
michael@0 2854 line_iterator line = begin_lines();
michael@0 2855 if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) {
michael@0 2856 line = aState.mLineAdjacentToTop;
michael@0 2857 }
michael@0 2858 while (line != aLine) {
michael@0 2859 if (!line->CachedIsEmpty() || line->HasClearance()) {
michael@0 2860 // A line which precedes aLine is non-empty, or has clearance,
michael@0 2861 // so therefore the top margin applies.
michael@0 2862 aState.SetFlag(BRS_APPLYTOPMARGIN, true);
michael@0 2863 return true;
michael@0 2864 }
michael@0 2865 // No need to apply the top margin if the line has floats. We
michael@0 2866 // should collapse anyway (bug 44419)
michael@0 2867 ++line;
michael@0 2868 aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, true);
michael@0 2869 aState.mLineAdjacentToTop = line;
michael@0 2870 }
michael@0 2871
michael@0 2872 // The line being reflowed is "essentially" the first line in the
michael@0 2873 // block. Therefore its top-margin will be collapsed by the
michael@0 2874 // generational collapsing logic with its parent (us).
michael@0 2875 return false;
michael@0 2876 }
michael@0 2877
michael@0 2878 nsresult
michael@0 2879 nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
michael@0 2880 line_iterator aLine,
michael@0 2881 bool* aKeepReflowGoing)
michael@0 2882 {
michael@0 2883 NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
michael@0 2884
michael@0 2885 nsresult rv = NS_OK;
michael@0 2886
michael@0 2887 nsIFrame* frame = aLine->mFirstChild;
michael@0 2888 if (!frame) {
michael@0 2889 NS_ASSERTION(false, "program error - unexpected empty line");
michael@0 2890 return NS_ERROR_NULL_POINTER;
michael@0 2891 }
michael@0 2892
michael@0 2893 // Prepare the block reflow engine
michael@0 2894 const nsStyleDisplay* display = frame->StyleDisplay();
michael@0 2895 nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
michael@0 2896
michael@0 2897 uint8_t breakType = display->mBreakType;
michael@0 2898 if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
michael@0 2899 breakType = nsLayoutUtils::CombineBreakType(breakType,
michael@0 2900 aState.mFloatBreakType);
michael@0 2901 aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
michael@0 2902 }
michael@0 2903
michael@0 2904 // Clear past floats before the block if the clear style is not none
michael@0 2905 aLine->SetBreakTypeBefore(breakType);
michael@0 2906
michael@0 2907 // See if we should apply the top margin. If the block frame being
michael@0 2908 // reflowed is a continuation (non-null prev-in-flow) then we don't
michael@0 2909 // apply its top margin because it's not significant. Otherwise, dig
michael@0 2910 // deeper.
michael@0 2911 bool applyTopMargin =
michael@0 2912 !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
michael@0 2913
michael@0 2914 if (applyTopMargin) {
michael@0 2915 // The HasClearance setting is only valid if ShouldApplyTopMargin
michael@0 2916 // returned false (in which case the top-margin-root set our
michael@0 2917 // clearance flag). Otherwise clear it now. We'll set it later on
michael@0 2918 // ourselves if necessary.
michael@0 2919 aLine->ClearHasClearance();
michael@0 2920 }
michael@0 2921 bool treatWithClearance = aLine->HasClearance();
michael@0 2922
michael@0 2923 bool mightClearFloats = breakType != NS_STYLE_CLEAR_NONE;
michael@0 2924 nsIFrame *replacedBlock = nullptr;
michael@0 2925 if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
michael@0 2926 mightClearFloats = true;
michael@0 2927 replacedBlock = frame;
michael@0 2928 }
michael@0 2929
michael@0 2930 // If our top margin was counted as part of some parents top-margin
michael@0 2931 // collapse and we are being speculatively reflowed assuming this
michael@0 2932 // frame DID NOT need clearance, then we need to check that
michael@0 2933 // assumption.
michael@0 2934 if (!treatWithClearance && !applyTopMargin && mightClearFloats &&
michael@0 2935 aState.mReflowState.mDiscoveredClearance) {
michael@0 2936 nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
michael@0 2937 nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
michael@0 2938 if (clearY != curY) {
michael@0 2939 // Looks like that assumption was invalid, we do need
michael@0 2940 // clearance. Tell our ancestor so it can reflow again. It is
michael@0 2941 // responsible for actually setting our clearance flag before
michael@0 2942 // the next reflow.
michael@0 2943 treatWithClearance = true;
michael@0 2944 // Only record the first frame that requires clearance
michael@0 2945 if (!*aState.mReflowState.mDiscoveredClearance) {
michael@0 2946 *aState.mReflowState.mDiscoveredClearance = frame;
michael@0 2947 }
michael@0 2948 aState.mPrevChild = frame;
michael@0 2949 // Exactly what we do now is flexible since we'll definitely be
michael@0 2950 // reflowed.
michael@0 2951 return NS_OK;
michael@0 2952 }
michael@0 2953 }
michael@0 2954 if (treatWithClearance) {
michael@0 2955 applyTopMargin = true;
michael@0 2956 }
michael@0 2957
michael@0 2958 nsIFrame* clearanceFrame = nullptr;
michael@0 2959 nscoord startingY = aState.mY;
michael@0 2960 nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
michael@0 2961 nscoord clearance;
michael@0 2962 // Save the original position of the frame so that we can reposition
michael@0 2963 // its view as needed.
michael@0 2964 nsPoint originalPosition = frame->GetPosition();
michael@0 2965 while (true) {
michael@0 2966 clearance = 0;
michael@0 2967 nscoord topMargin = 0;
michael@0 2968 bool mayNeedRetry = false;
michael@0 2969 bool clearedFloats = false;
michael@0 2970 if (applyTopMargin) {
michael@0 2971 // Precompute the blocks top margin value so that we can get the
michael@0 2972 // correct available space (there might be a float that's
michael@0 2973 // already been placed below the aState.mPrevBottomMargin
michael@0 2974
michael@0 2975 // Setup a reflowState to get the style computed margin-top
michael@0 2976 // value. We'll use a reason of `resize' so that we don't fudge
michael@0 2977 // any incremental reflow state.
michael@0 2978
michael@0 2979 // The availSpace here is irrelevant to our needs - all we want
michael@0 2980 // out if this setup is the margin-top value which doesn't depend
michael@0 2981 // on the childs available space.
michael@0 2982 // XXX building a complete nsHTMLReflowState just to get the margin-top
michael@0 2983 // seems like a waste. And we do this for almost every block!
michael@0 2984 nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
michael@0 2985 nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
michael@0 2986 frame, availSpace);
michael@0 2987
michael@0 2988 if (treatWithClearance) {
michael@0 2989 aState.mY += aState.mPrevBottomMargin.get();
michael@0 2990 aState.mPrevBottomMargin.Zero();
michael@0 2991 }
michael@0 2992
michael@0 2993 // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
michael@0 2994 // that all child margins collapse down to clearanceFrame.
michael@0 2995 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
michael@0 2996 &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
michael@0 2997
michael@0 2998 // XXX optimization; we could check the collapsing children to see if they are sure
michael@0 2999 // to require clearance, and so avoid retrying them
michael@0 3000
michael@0 3001 if (clearanceFrame) {
michael@0 3002 // Don't allow retries on the second pass. The clearance decisions for the
michael@0 3003 // blocks whose top-margins collapse with ours are now fixed.
michael@0 3004 mayNeedRetry = false;
michael@0 3005 }
michael@0 3006
michael@0 3007 if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
michael@0 3008 // We don't know if we need clearance and this is the first,
michael@0 3009 // optimistic pass. So determine whether *this block* needs
michael@0 3010 // clearance. Note that we do not allow the decision for whether
michael@0 3011 // this block has clearance to change on the second pass; that
michael@0 3012 // decision is only allowed to be made under the optimistic
michael@0 3013 // first pass.
michael@0 3014 nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
michael@0 3015 nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
michael@0 3016 if (clearY != curY) {
michael@0 3017 // Looks like we need clearance and we didn't know about it already. So
michael@0 3018 // recompute collapsed margin
michael@0 3019 treatWithClearance = true;
michael@0 3020 // Remember this decision, needed for incremental reflow
michael@0 3021 aLine->SetHasClearance();
michael@0 3022
michael@0 3023 // Apply incoming margins
michael@0 3024 aState.mY += aState.mPrevBottomMargin.get();
michael@0 3025 aState.mPrevBottomMargin.Zero();
michael@0 3026
michael@0 3027 // Compute the collapsed margin again, ignoring the incoming margin this time
michael@0 3028 mayNeedRetry = false;
michael@0 3029 nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
michael@0 3030 &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
michael@0 3031 }
michael@0 3032 }
michael@0 3033
michael@0 3034 // Temporarily advance the running Y value so that the
michael@0 3035 // GetAvailableSpace method will return the right available
michael@0 3036 // space. This undone as soon as the horizontal margins are
michael@0 3037 // computed.
michael@0 3038 topMargin = aState.mPrevBottomMargin.get();
michael@0 3039
michael@0 3040 if (treatWithClearance) {
michael@0 3041 nscoord currentY = aState.mY;
michael@0 3042 // advance mY to the clear position.
michael@0 3043 aState.mY = aState.ClearFloats(aState.mY, breakType, replacedBlock);
michael@0 3044
michael@0 3045 clearedFloats = aState.mY != currentY;
michael@0 3046
michael@0 3047 // Compute clearance. It's the amount we need to add to the top
michael@0 3048 // border-edge of the frame, after applying collapsed margins
michael@0 3049 // from the frame and its children, to get it to line up with
michael@0 3050 // the bottom of the floats. The former is currentY + topMargin,
michael@0 3051 // the latter is the current aState.mY.
michael@0 3052 // Note that negative clearance is possible
michael@0 3053 clearance = aState.mY - (currentY + topMargin);
michael@0 3054
michael@0 3055 // Add clearance to our top margin while we compute available
michael@0 3056 // space for the frame
michael@0 3057 topMargin += clearance;
michael@0 3058
michael@0 3059 // Note that aState.mY should stay where it is: at the top
michael@0 3060 // border-edge of the frame
michael@0 3061 } else {
michael@0 3062 // Advance aState.mY to the top border-edge of the frame.
michael@0 3063 aState.mY += topMargin;
michael@0 3064 }
michael@0 3065 }
michael@0 3066
michael@0 3067 // Here aState.mY is the top border-edge of the block.
michael@0 3068 // Compute the available space for the block
michael@0 3069 nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
michael@0 3070 #ifdef REALLY_NOISY_REFLOW
michael@0 3071 printf("setting line %p isImpacted to %s\n",
michael@0 3072 aLine.get(), floatAvailableSpace.mHasFloats?"true":"false");
michael@0 3073 #endif
michael@0 3074 aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats);
michael@0 3075 nsRect availSpace;
michael@0 3076 aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace,
michael@0 3077 replacedBlock != nullptr, availSpace);
michael@0 3078
michael@0 3079 // The check for
michael@0 3080 // (!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats)
michael@0 3081 // is to some degree out of paranoia: if we reliably eat up top
michael@0 3082 // margins at the top of the page as we ought to, it wouldn't be
michael@0 3083 // needed.
michael@0 3084 if ((!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats) &&
michael@0 3085 availSpace.height < 0) {
michael@0 3086 // We know already that this child block won't fit on this
michael@0 3087 // page/column due to the top margin or the clearance. So we need
michael@0 3088 // to get out of here now. (If we don't, most blocks will handle
michael@0 3089 // things fine, and report break-before, but zero-height blocks
michael@0 3090 // won't, and will thus make their parent overly-large and force
michael@0 3091 // *it* to be pushed in its entirety.)
michael@0 3092 // Doing this means that we also don't need to worry about the
michael@0 3093 // |availSpace.height += topMargin| below interacting with pushed
michael@0 3094 // floats (which force nscoord_MAX clearance) to cause a
michael@0 3095 // constrained height to turn into an unconstrained one.
michael@0 3096 aState.mY = startingY;
michael@0 3097 aState.mPrevBottomMargin = incomingMargin;
michael@0 3098 *aKeepReflowGoing = false;
michael@0 3099 if (ShouldAvoidBreakInside(aState.mReflowState)) {
michael@0 3100 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 3101 } else {
michael@0 3102 PushLines(aState, aLine.prev());
michael@0 3103 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 3104 }
michael@0 3105 return NS_OK;
michael@0 3106 }
michael@0 3107
michael@0 3108 // Now put the Y coordinate back to the top of the top-margin +
michael@0 3109 // clearance, and flow the block.
michael@0 3110 aState.mY -= topMargin;
michael@0 3111 availSpace.y -= topMargin;
michael@0 3112 if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
michael@0 3113 availSpace.height += topMargin;
michael@0 3114 }
michael@0 3115
michael@0 3116 // Reflow the block into the available space
michael@0 3117 // construct the html reflow state for the block. ReflowBlock
michael@0 3118 // will initialize it
michael@0 3119 nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
michael@0 3120 availSpace.Size());
michael@0 3121 blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
michael@0 3122
michael@0 3123 nsFloatManager::SavedState floatManagerState;
michael@0 3124 if (mayNeedRetry) {
michael@0 3125 blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
michael@0 3126 aState.mFloatManager->PushState(&floatManagerState);
michael@0 3127 } else if (!applyTopMargin) {
michael@0 3128 blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
michael@0 3129 }
michael@0 3130
michael@0 3131 nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
michael@0 3132 rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
michael@0 3133 clearance, aState.IsAdjacentWithTop(),
michael@0 3134 aLine.get(), blockHtmlRS, frameReflowStatus, aState);
michael@0 3135
michael@0 3136 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3137
michael@0 3138 if (mayNeedRetry && clearanceFrame) {
michael@0 3139 aState.mFloatManager->PopState(&floatManagerState);
michael@0 3140 aState.mY = startingY;
michael@0 3141 aState.mPrevBottomMargin = incomingMargin;
michael@0 3142 continue;
michael@0 3143 }
michael@0 3144
michael@0 3145 aState.mPrevChild = frame;
michael@0 3146
michael@0 3147 if (blockHtmlRS.WillReflowAgainForClearance()) {
michael@0 3148 // If an ancestor of ours is going to reflow for clearance, we
michael@0 3149 // need to avoid calling PlaceBlock, because it unsets dirty bits
michael@0 3150 // on the child block (both itself, and through its call to
michael@0 3151 // nsFrame::DidReflow), and those dirty bits imply dirtiness for
michael@0 3152 // all of the child block, including the lines it didn't reflow.
michael@0 3153 NS_ASSERTION(originalPosition == frame->GetPosition(),
michael@0 3154 "we need to call PositionChildViews");
michael@0 3155 return NS_OK;
michael@0 3156 }
michael@0 3157
michael@0 3158 #if defined(REFLOW_STATUS_COVERAGE)
michael@0 3159 RecordReflowStatus(true, frameReflowStatus);
michael@0 3160 #endif
michael@0 3161
michael@0 3162 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
michael@0 3163 // None of the child block fits.
michael@0 3164 *aKeepReflowGoing = false;
michael@0 3165 if (ShouldAvoidBreakInside(aState.mReflowState)) {
michael@0 3166 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 3167 } else {
michael@0 3168 PushLines(aState, aLine.prev());
michael@0 3169 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 3170 }
michael@0 3171 }
michael@0 3172 else {
michael@0 3173 // Note: line-break-after a block is a nop
michael@0 3174
michael@0 3175 // Try to place the child block.
michael@0 3176 // Don't force the block to fit if we have positive clearance, because
michael@0 3177 // pushing it to the next page would give it more room.
michael@0 3178 // Don't force the block to fit if it's impacted by a float. If it is,
michael@0 3179 // then pushing it to the next page would give it more room. Note that
michael@0 3180 // isImpacted doesn't include impact from the block's own floats.
michael@0 3181 bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
michael@0 3182 !floatAvailableSpace.mHasFloats;
michael@0 3183 nsCollapsingMargin collapsedBottomMargin;
michael@0 3184 nsOverflowAreas overflowAreas;
michael@0 3185 *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
michael@0 3186 collapsedBottomMargin,
michael@0 3187 overflowAreas,
michael@0 3188 frameReflowStatus,
michael@0 3189 aState.mContainerWidth);
michael@0 3190 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) &&
michael@0 3191 ShouldAvoidBreakInside(aState.mReflowState)) {
michael@0 3192 *aKeepReflowGoing = false;
michael@0 3193 }
michael@0 3194
michael@0 3195 if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
michael@0 3196 line_iterator nextLine = aLine;
michael@0 3197 ++nextLine;
michael@0 3198 if (nextLine != end_lines()) {
michael@0 3199 nextLine->MarkPreviousMarginDirty();
michael@0 3200 }
michael@0 3201 }
michael@0 3202
michael@0 3203 aLine->SetOverflowAreas(overflowAreas);
michael@0 3204 if (*aKeepReflowGoing) {
michael@0 3205 // Some of the child block fit
michael@0 3206
michael@0 3207 // Advance to new Y position
michael@0 3208 nscoord newY = aLine->BEnd();
michael@0 3209 aState.mY = newY;
michael@0 3210
michael@0 3211 // Continue the block frame now if it didn't completely fit in
michael@0 3212 // the available space.
michael@0 3213 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
michael@0 3214 bool madeContinuation =
michael@0 3215 CreateContinuationFor(aState, nullptr, frame);
michael@0 3216
michael@0 3217 nsIFrame* nextFrame = frame->GetNextInFlow();
michael@0 3218 NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
michael@0 3219
michael@0 3220 if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
michael@0 3221 // If nextFrame used to be an overflow container, make it a normal block
michael@0 3222 if (!madeContinuation &&
michael@0 3223 (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
michael@0 3224 nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame);
michael@0 3225 nsContainerFrame* parent =
michael@0 3226 static_cast<nsContainerFrame*>(nextFrame->GetParent());
michael@0 3227 rv = parent->StealFrame(nextFrame);
michael@0 3228 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3229 if (parent != this)
michael@0 3230 ReparentFrame(nextFrame, parent, this);
michael@0 3231 mFrames.InsertFrame(nullptr, frame, nextFrame);
michael@0 3232 madeContinuation = true; // needs to be added to mLines
michael@0 3233 nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
michael@0 3234 frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 3235 }
michael@0 3236
michael@0 3237 // Push continuation to a new line, but only if we actually made one.
michael@0 3238 if (madeContinuation) {
michael@0 3239 nsLineBox* line = NewLineBox(nextFrame, true);
michael@0 3240 mLines.after_insert(aLine, line);
michael@0 3241 }
michael@0 3242
michael@0 3243 PushLines(aState, aLine);
michael@0 3244 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 3245
michael@0 3246 // If we need to reflow the continuation of the block child,
michael@0 3247 // then we'd better reflow our continuation
michael@0 3248 if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
michael@0 3249 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 3250 // We also need to make that continuation's line dirty so
michael@0 3251 // it gets reflowed when we reflow our next in flow. The
michael@0 3252 // nif's line must always be either a line of the nif's
michael@0 3253 // parent block (only if we didn't make a continuation) or
michael@0 3254 // else one of our own overflow lines. In the latter case
michael@0 3255 // the line is already marked dirty, so just handle the
michael@0 3256 // first case.
michael@0 3257 if (!madeContinuation) {
michael@0 3258 nsBlockFrame* nifBlock =
michael@0 3259 nsLayoutUtils::GetAsBlock(nextFrame->GetParent());
michael@0 3260 NS_ASSERTION(nifBlock,
michael@0 3261 "A block's child's next in flow's parent must be a block!");
michael@0 3262 for (line_iterator line = nifBlock->begin_lines(),
michael@0 3263 line_end = nifBlock->end_lines(); line != line_end; ++line) {
michael@0 3264 if (line->Contains(nextFrame)) {
michael@0 3265 line->MarkDirty();
michael@0 3266 break;
michael@0 3267 }
michael@0 3268 }
michael@0 3269 }
michael@0 3270 }
michael@0 3271 *aKeepReflowGoing = false;
michael@0 3272
michael@0 3273 // The bottom margin for a block is only applied on the last
michael@0 3274 // flow block. Since we just continued the child block frame,
michael@0 3275 // we know that line->mFirstChild is not the last flow block
michael@0 3276 // therefore zero out the running margin value.
michael@0 3277 #ifdef NOISY_VERTICAL_MARGINS
michael@0 3278 ListTag(stdout);
michael@0 3279 printf(": reflow incomplete, frame=");
michael@0 3280 nsFrame::ListTag(stdout, frame);
michael@0 3281 printf(" prevBottomMargin=%d, setting to zero\n",
michael@0 3282 aState.mPrevBottomMargin);
michael@0 3283 #endif
michael@0 3284 aState.mPrevBottomMargin.Zero();
michael@0 3285 }
michael@0 3286 else { // frame is complete but its overflow is not complete
michael@0 3287 // Disconnect the next-in-flow and put it in our overflow tracker
michael@0 3288 if (!madeContinuation &&
michael@0 3289 !(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
michael@0 3290 // It already exists, but as a normal next-in-flow, so we need
michael@0 3291 // to dig it out of the child lists.
michael@0 3292 nsContainerFrame* parent = static_cast<nsContainerFrame*>
michael@0 3293 (nextFrame->GetParent());
michael@0 3294 rv = parent->StealFrame(nextFrame);
michael@0 3295 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3296 }
michael@0 3297 else if (madeContinuation) {
michael@0 3298 mFrames.RemoveFrame(nextFrame);
michael@0 3299 }
michael@0 3300
michael@0 3301 // Put it in our overflow list
michael@0 3302 aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
michael@0 3303 NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus);
michael@0 3304
michael@0 3305 #ifdef NOISY_VERTICAL_MARGINS
michael@0 3306 ListTag(stdout);
michael@0 3307 printf(": reflow complete but overflow incomplete for ");
michael@0 3308 nsFrame::ListTag(stdout, frame);
michael@0 3309 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
michael@0 3310 aState.mPrevBottomMargin, collapsedBottomMargin.get());
michael@0 3311 #endif
michael@0 3312 aState.mPrevBottomMargin = collapsedBottomMargin;
michael@0 3313 }
michael@0 3314 }
michael@0 3315 else { // frame is fully complete
michael@0 3316 #ifdef NOISY_VERTICAL_MARGINS
michael@0 3317 ListTag(stdout);
michael@0 3318 printf(": reflow complete for ");
michael@0 3319 nsFrame::ListTag(stdout, frame);
michael@0 3320 printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
michael@0 3321 aState.mPrevBottomMargin, collapsedBottomMargin.get());
michael@0 3322 #endif
michael@0 3323 aState.mPrevBottomMargin = collapsedBottomMargin;
michael@0 3324 }
michael@0 3325 #ifdef NOISY_VERTICAL_MARGINS
michael@0 3326 ListTag(stdout);
michael@0 3327 printf(": frame=");
michael@0 3328 nsFrame::ListTag(stdout, frame);
michael@0 3329 printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
michael@0 3330 brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
michael@0 3331 aState.mPrevBottomMargin);
michael@0 3332 #endif
michael@0 3333 } else {
michael@0 3334 if ((aLine == mLines.front() && !GetPrevInFlow()) ||
michael@0 3335 ShouldAvoidBreakInside(aState.mReflowState)) {
michael@0 3336 // If it's our very first line *or* we're not at the top of the page
michael@0 3337 // and we have page-break-inside:avoid, then we need to be pushed to
michael@0 3338 // our parent's next-in-flow.
michael@0 3339 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 3340 } else {
michael@0 3341 // Push the line that didn't fit and any lines that follow it
michael@0 3342 // to our next-in-flow.
michael@0 3343 PushLines(aState, aLine.prev());
michael@0 3344 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 3345 }
michael@0 3346 }
michael@0 3347 }
michael@0 3348 break; // out of the reflow retry loop
michael@0 3349 }
michael@0 3350
michael@0 3351 // Now that we've got its final position all figured out, position any child
michael@0 3352 // views it may have. Note that the case when frame has a view got handled
michael@0 3353 // by FinishReflowChild, but that function didn't have the coordinates needed
michael@0 3354 // to correctly decide whether to reposition child views.
michael@0 3355 if (originalPosition != frame->GetPosition() && !frame->HasView()) {
michael@0 3356 nsContainerFrame::PositionChildViews(frame);
michael@0 3357 }
michael@0 3358
michael@0 3359 #ifdef DEBUG
michael@0 3360 VerifyLines(true);
michael@0 3361 #endif
michael@0 3362 return rv;
michael@0 3363 }
michael@0 3364
michael@0 3365 nsresult
michael@0 3366 nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
michael@0 3367 line_iterator aLine,
michael@0 3368 bool* aKeepReflowGoing)
michael@0 3369 {
michael@0 3370 nsresult rv = NS_OK;
michael@0 3371 *aKeepReflowGoing = true;
michael@0 3372
michael@0 3373 aLine->SetLineIsImpactedByFloat(false);
michael@0 3374
michael@0 3375 // Setup initial coordinate system for reflowing the inline frames
michael@0 3376 // into. Apply a previous block frame's bottom margin first.
michael@0 3377 if (ShouldApplyTopMargin(aState, aLine)) {
michael@0 3378 aState.mY += aState.mPrevBottomMargin.get();
michael@0 3379 }
michael@0 3380 nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
michael@0 3381
michael@0 3382 LineReflowStatus lineReflowStatus;
michael@0 3383 do {
michael@0 3384 nscoord availableSpaceHeight = 0;
michael@0 3385 do {
michael@0 3386 bool allowPullUp = true;
michael@0 3387 nsIContent* forceBreakInContent = nullptr;
michael@0 3388 int32_t forceBreakOffset = -1;
michael@0 3389 gfxBreakPriority forceBreakPriority = gfxBreakPriority::eNoBreak;
michael@0 3390 do {
michael@0 3391 nsFloatManager::SavedState floatManagerState;
michael@0 3392 aState.mReflowState.mFloatManager->PushState(&floatManagerState);
michael@0 3393
michael@0 3394 // Once upon a time we allocated the first 30 nsLineLayout objects
michael@0 3395 // on the stack, and then we switched to the heap. At that time
michael@0 3396 // these objects were large (1100 bytes on a 32 bit system).
michael@0 3397 // Then the nsLineLayout object was shrunk to 156 bytes by
michael@0 3398 // removing some internal buffers. Given that it is so much
michael@0 3399 // smaller, the complexity of 2 different ways of allocating
michael@0 3400 // no longer makes sense. Now we always allocate on the stack.
michael@0 3401 nsLineLayout lineLayout(aState.mPresContext,
michael@0 3402 aState.mReflowState.mFloatManager,
michael@0 3403 &aState.mReflowState, &aLine);
michael@0 3404 lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
michael@0 3405 if (forceBreakInContent) {
michael@0 3406 lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
michael@0 3407 }
michael@0 3408 rv = DoReflowInlineFrames(aState, lineLayout, aLine,
michael@0 3409 floatAvailableSpace, availableSpaceHeight,
michael@0 3410 &floatManagerState, aKeepReflowGoing,
michael@0 3411 &lineReflowStatus, allowPullUp);
michael@0 3412 lineLayout.EndLineReflow();
michael@0 3413
michael@0 3414 if (NS_FAILED(rv)) {
michael@0 3415 return rv;
michael@0 3416 }
michael@0 3417
michael@0 3418 if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
michael@0 3419 LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus ||
michael@0 3420 LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
michael@0 3421 if (lineLayout.NeedsBackup()) {
michael@0 3422 NS_ASSERTION(!forceBreakInContent, "Backing up twice; this should never be necessary");
michael@0 3423 // If there is no saved break position, then this will set
michael@0 3424 // set forceBreakInContent to null and we won't back up, which is
michael@0 3425 // correct.
michael@0 3426 forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset, &forceBreakPriority);
michael@0 3427 } else {
michael@0 3428 forceBreakInContent = nullptr;
michael@0 3429 }
michael@0 3430 // restore the float manager state
michael@0 3431 aState.mReflowState.mFloatManager->PopState(&floatManagerState);
michael@0 3432 // Clear out float lists
michael@0 3433 aState.mCurrentLineFloats.DeleteAll();
michael@0 3434 aState.mBelowCurrentLineFloats.DeleteAll();
michael@0 3435 }
michael@0 3436
michael@0 3437 // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
michael@0 3438 allowPullUp = false;
michael@0 3439 } while (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus);
michael@0 3440 } while (LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus);
michael@0 3441 } while (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus);
michael@0 3442
michael@0 3443 return rv;
michael@0 3444 }
michael@0 3445
michael@0 3446 void
michael@0 3447 nsBlockFrame::PushTruncatedLine(nsBlockReflowState& aState,
michael@0 3448 line_iterator aLine,
michael@0 3449 bool* aKeepReflowGoing)
michael@0 3450 {
michael@0 3451 PushLines(aState, aLine.prev());
michael@0 3452 *aKeepReflowGoing = false;
michael@0 3453 NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
michael@0 3454 }
michael@0 3455
michael@0 3456 #ifdef DEBUG
michael@0 3457 static const char* LineReflowStatusNames[] = {
michael@0 3458 "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
michael@0 3459 "LINE_REFLOW_REDO_MORE_FLOATS",
michael@0 3460 "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
michael@0 3461 };
michael@0 3462 #endif
michael@0 3463
michael@0 3464 nsresult
michael@0 3465 nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
michael@0 3466 nsLineLayout& aLineLayout,
michael@0 3467 line_iterator aLine,
michael@0 3468 nsFlowAreaRect& aFloatAvailableSpace,
michael@0 3469 nscoord& aAvailableSpaceHeight,
michael@0 3470 nsFloatManager::SavedState*
michael@0 3471 aFloatStateBeforeLine,
michael@0 3472 bool* aKeepReflowGoing,
michael@0 3473 LineReflowStatus* aLineReflowStatus,
michael@0 3474 bool aAllowPullUp)
michael@0 3475 {
michael@0 3476 // Forget all of the floats on the line
michael@0 3477 aLine->FreeFloats(aState.mFloatCacheFreeList);
michael@0 3478 aState.mFloatOverflowAreas.Clear();
michael@0 3479
michael@0 3480 // We need to set this flag on the line if any of our reflow passes
michael@0 3481 // are impacted by floats.
michael@0 3482 if (aFloatAvailableSpace.mHasFloats)
michael@0 3483 aLine->SetLineIsImpactedByFloat(true);
michael@0 3484 #ifdef REALLY_NOISY_REFLOW
michael@0 3485 printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
michael@0 3486 this, aFloatAvailableSpace.mHasFloats);
michael@0 3487 #endif
michael@0 3488
michael@0 3489 WritingMode wm = GetWritingMode(aLine->mFirstChild);
michael@0 3490 LogicalRect lineRect(wm, aFloatAvailableSpace.mRect, aState.mContainerWidth);
michael@0 3491
michael@0 3492 nscoord iStart = lineRect.IStart(wm);
michael@0 3493
michael@0 3494 nscoord availISize = lineRect.ISize(wm);
michael@0 3495 nscoord availBSize;
michael@0 3496 if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
michael@0 3497 availBSize = NS_UNCONSTRAINEDSIZE;
michael@0 3498 }
michael@0 3499 else {
michael@0 3500 /* XXX get the height right! */
michael@0 3501 availBSize = lineRect.BSize(wm);
michael@0 3502 }
michael@0 3503
michael@0 3504 // Make sure to enable resize optimization before we call BeginLineReflow
michael@0 3505 // because it might get disabled there
michael@0 3506 aLine->EnableResizeReflowOptimization();
michael@0 3507
michael@0 3508 aLineLayout.BeginLineReflow(iStart, aState.mY,
michael@0 3509 availISize, availBSize,
michael@0 3510 aFloatAvailableSpace.mHasFloats,
michael@0 3511 false, /*XXX isTopOfPage*/
michael@0 3512 wm, aState.mContainerWidth);
michael@0 3513
michael@0 3514 aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, false);
michael@0 3515
michael@0 3516 // XXX Unfortunately we need to know this before reflowing the first
michael@0 3517 // inline frame in the line. FIX ME.
michael@0 3518 if ((0 == aLineLayout.GetLineNumber()) &&
michael@0 3519 (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
michael@0 3520 (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
michael@0 3521 aLineLayout.SetFirstLetterStyleOK(true);
michael@0 3522 }
michael@0 3523 NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
michael@0 3524 GetPrevContinuation()),
michael@0 3525 "first letter child bit should only be on first continuation");
michael@0 3526
michael@0 3527 // Reflow the frames that are already on the line first
michael@0 3528 nsresult rv = NS_OK;
michael@0 3529 LineReflowStatus lineReflowStatus = LINE_REFLOW_OK;
michael@0 3530 int32_t i;
michael@0 3531 nsIFrame* frame = aLine->mFirstChild;
michael@0 3532
michael@0 3533 if (aFloatAvailableSpace.mHasFloats) {
michael@0 3534 // There is a soft break opportunity at the start of the line, because
michael@0 3535 // we can always move this line down below float(s).
michael@0 3536 if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0, true, gfxBreakPriority::eNormalBreak)) {
michael@0 3537 lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
michael@0 3538 }
michael@0 3539 }
michael@0 3540
michael@0 3541 // need to repeatedly call GetChildCount here, because the child
michael@0 3542 // count can change during the loop!
michael@0 3543 for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount();
michael@0 3544 i++, frame = frame->GetNextSibling()) {
michael@0 3545 rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
michael@0 3546 &lineReflowStatus);
michael@0 3547 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3548 if (LINE_REFLOW_OK != lineReflowStatus) {
michael@0 3549 // It is possible that one or more of next lines are empty
michael@0 3550 // (because of DeleteNextInFlowChild). If so, delete them now
michael@0 3551 // in case we are finished.
michael@0 3552 ++aLine;
michael@0 3553 while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) {
michael@0 3554 // XXX Is this still necessary now that DeleteNextInFlowChild
michael@0 3555 // uses DoRemoveFrame?
michael@0 3556 nsLineBox *toremove = aLine;
michael@0 3557 aLine = mLines.erase(aLine);
michael@0 3558 NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line");
michael@0 3559 FreeLineBox(toremove);
michael@0 3560 }
michael@0 3561 --aLine;
michael@0 3562
michael@0 3563 NS_ASSERTION(lineReflowStatus != LINE_REFLOW_TRUNCATED,
michael@0 3564 "ReflowInlineFrame should never determine that a line "
michael@0 3565 "needs to go to the next page/column");
michael@0 3566 }
michael@0 3567 }
michael@0 3568
michael@0 3569 // Don't pull up new frames into lines with continuation placeholders
michael@0 3570 if (aAllowPullUp) {
michael@0 3571 // Pull frames and reflow them until we can't
michael@0 3572 while (LINE_REFLOW_OK == lineReflowStatus) {
michael@0 3573 frame = PullFrame(aState, aLine);
michael@0 3574 if (!frame) {
michael@0 3575 break;
michael@0 3576 }
michael@0 3577
michael@0 3578 while (LINE_REFLOW_OK == lineReflowStatus) {
michael@0 3579 int32_t oldCount = aLine->GetChildCount();
michael@0 3580 rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
michael@0 3581 &lineReflowStatus);
michael@0 3582 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3583 if (aLine->GetChildCount() != oldCount) {
michael@0 3584 // We just created a continuation for aFrame AND its going
michael@0 3585 // to end up on this line (e.g. :first-letter
michael@0 3586 // situation). Therefore we have to loop here before trying
michael@0 3587 // to pull another frame.
michael@0 3588 frame = frame->GetNextSibling();
michael@0 3589 }
michael@0 3590 else {
michael@0 3591 break;
michael@0 3592 }
michael@0 3593 }
michael@0 3594 }
michael@0 3595 }
michael@0 3596
michael@0 3597 aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, aLineLayout.LineIsEmpty());
michael@0 3598
michael@0 3599 // We only need to backup if the line isn't going to be reflowed again anyway
michael@0 3600 bool needsBackup = aLineLayout.NeedsBackup() &&
michael@0 3601 (lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK);
michael@0 3602 if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
michael@0 3603 NS_WARNING("We shouldn't be backing up more than once! "
michael@0 3604 "Someone must have set a break opportunity beyond the available width, "
michael@0 3605 "even though there were better break opportunities before it");
michael@0 3606 needsBackup = false;
michael@0 3607 }
michael@0 3608 if (needsBackup) {
michael@0 3609 // We need to try backing up to before a text run
michael@0 3610 int32_t offset;
michael@0 3611 gfxBreakPriority breakPriority;
michael@0 3612 nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset, &breakPriority);
michael@0 3613 // XXX It's possible, in fact not unusual, for the break opportunity to already
michael@0 3614 // be the end of the line. We should detect that and optimize to not
michael@0 3615 // re-do the line.
michael@0 3616 if (breakContent) {
michael@0 3617 // We can back up!
michael@0 3618 lineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
michael@0 3619 }
michael@0 3620 } else {
michael@0 3621 // In case we reflow this line again, remember that we don't
michael@0 3622 // need to force any breaking
michael@0 3623 aLineLayout.ClearOptionalBreakPosition();
michael@0 3624 }
michael@0 3625
michael@0 3626 if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
michael@0 3627 // This happens only when we have a line that is impacted by
michael@0 3628 // floats and the first element in the line doesn't fit with
michael@0 3629 // the floats.
michael@0 3630 //
michael@0 3631 // What we do is to advance past the first float we find and
michael@0 3632 // then reflow the line all over again.
michael@0 3633 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.height,
michael@0 3634 "unconstrained height on totally empty line");
michael@0 3635
michael@0 3636 // See the analogous code for blocks in nsBlockReflowState::ClearFloats.
michael@0 3637 if (aFloatAvailableSpace.mRect.height > 0) {
michael@0 3638 NS_ASSERTION(aFloatAvailableSpace.mHasFloats,
michael@0 3639 "redo line on totally empty line with non-empty band...");
michael@0 3640 // We should never hit this case if we've placed floats on the
michael@0 3641 // line; if we have, then the GetFloatAvailableSpace call is wrong
michael@0 3642 // and needs to happen after the caller pops the space manager
michael@0 3643 // state.
michael@0 3644 aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
michael@0 3645 aState.mY += aFloatAvailableSpace.mRect.height;
michael@0 3646 aFloatAvailableSpace = aState.GetFloatAvailableSpace();
michael@0 3647 } else {
michael@0 3648 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.AvailableHeight(),
michael@0 3649 "We shouldn't be running out of height here");
michael@0 3650 if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.AvailableHeight()) {
michael@0 3651 // just move it down a bit to try to get out of this mess
michael@0 3652 aState.mY += 1;
michael@0 3653 // We should never hit this case if we've placed floats on the
michael@0 3654 // line; if we have, then the GetFloatAvailableSpace call is wrong
michael@0 3655 // and needs to happen after the caller pops the space manager
michael@0 3656 // state.
michael@0 3657 aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
michael@0 3658 aFloatAvailableSpace = aState.GetFloatAvailableSpace();
michael@0 3659 } else {
michael@0 3660 // There's nowhere to retry placing the line, so we want to push
michael@0 3661 // it to the next page/column where its contents can fit not
michael@0 3662 // next to a float.
michael@0 3663 lineReflowStatus = LINE_REFLOW_TRUNCATED;
michael@0 3664 PushTruncatedLine(aState, aLine, aKeepReflowGoing);
michael@0 3665 }
michael@0 3666 }
michael@0 3667
michael@0 3668 // XXX: a small optimization can be done here when paginating:
michael@0 3669 // if the new Y coordinate is past the end of the block then
michael@0 3670 // push the line and return now instead of later on after we are
michael@0 3671 // past the float.
michael@0 3672 }
michael@0 3673 else if (LINE_REFLOW_TRUNCATED != lineReflowStatus &&
michael@0 3674 LINE_REFLOW_REDO_NO_PULL != lineReflowStatus) {
michael@0 3675 // If we are propagating out a break-before status then there is
michael@0 3676 // no point in placing the line.
michael@0 3677 if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
michael@0 3678 if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
michael@0 3679 aFloatAvailableSpace.mRect, aAvailableSpaceHeight,
michael@0 3680 aKeepReflowGoing)) {
michael@0 3681 lineReflowStatus = LINE_REFLOW_REDO_MORE_FLOATS;
michael@0 3682 // PlaceLine already called GetAvailableSpaceForHeight for us.
michael@0 3683 }
michael@0 3684 }
michael@0 3685 }
michael@0 3686 #ifdef DEBUG
michael@0 3687 if (gNoisyReflow) {
michael@0 3688 printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]);
michael@0 3689 }
michael@0 3690 #endif
michael@0 3691
michael@0 3692 if (aLineLayout.GetDirtyNextLine()) {
michael@0 3693 // aLine may have been pushed to the overflow lines.
michael@0 3694 FrameLines* overflowLines = GetOverflowLines();
michael@0 3695 // We can't just compare iterators front() to aLine here, since they may be in
michael@0 3696 // different lists.
michael@0 3697 bool pushedToOverflowLines = overflowLines &&
michael@0 3698 overflowLines->mLines.front() == aLine.get();
michael@0 3699 if (pushedToOverflowLines) {
michael@0 3700 // aLine is stale, it's associated with the main line list but it should
michael@0 3701 // be associated with the overflow line list now
michael@0 3702 aLine = overflowLines->mLines.begin();
michael@0 3703 }
michael@0 3704 nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
michael@0 3705 if (iter.Next() && iter.GetLine()->IsInline()) {
michael@0 3706 iter.GetLine()->MarkDirty();
michael@0 3707 if (iter.GetContainer() != this) {
michael@0 3708 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 3709 }
michael@0 3710 }
michael@0 3711 }
michael@0 3712
michael@0 3713 *aLineReflowStatus = lineReflowStatus;
michael@0 3714
michael@0 3715 return rv;
michael@0 3716 }
michael@0 3717
michael@0 3718 /**
michael@0 3719 * Reflow an inline frame. The reflow status is mapped from the frames
michael@0 3720 * reflow status to the lines reflow status (not to our reflow status).
michael@0 3721 * The line reflow status is simple: true means keep placing frames
michael@0 3722 * on the line; false means don't (the line is done). If the line
michael@0 3723 * has some sort of breaking affect then aLine's break-type will be set
michael@0 3724 * to something other than NS_STYLE_CLEAR_NONE.
michael@0 3725 */
michael@0 3726 nsresult
michael@0 3727 nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
michael@0 3728 nsLineLayout& aLineLayout,
michael@0 3729 line_iterator aLine,
michael@0 3730 nsIFrame* aFrame,
michael@0 3731 LineReflowStatus* aLineReflowStatus)
michael@0 3732 {
michael@0 3733 NS_ENSURE_ARG_POINTER(aFrame);
michael@0 3734
michael@0 3735 *aLineReflowStatus = LINE_REFLOW_OK;
michael@0 3736
michael@0 3737 #ifdef NOISY_FIRST_LETTER
michael@0 3738 ListTag(stdout);
michael@0 3739 printf(": reflowing ");
michael@0 3740 nsFrame::ListTag(stdout, aFrame);
michael@0 3741 printf(" reflowingFirstLetter=%s\n",
michael@0 3742 aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
michael@0 3743 #endif
michael@0 3744
michael@0 3745 // Reflow the inline frame
michael@0 3746 nsReflowStatus frameReflowStatus;
michael@0 3747 bool pushedFrame;
michael@0 3748 nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
michael@0 3749 nullptr, pushedFrame);
michael@0 3750 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3751
michael@0 3752 if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
michael@0 3753 aLineLayout.SetDirtyNextLine();
michael@0 3754 }
michael@0 3755
michael@0 3756 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3757 #ifdef REALLY_NOISY_REFLOW_CHILD
michael@0 3758 nsFrame::ListTag(stdout, aFrame);
michael@0 3759 printf(": status=%x\n", frameReflowStatus);
michael@0 3760 #endif
michael@0 3761
michael@0 3762 #if defined(REFLOW_STATUS_COVERAGE)
michael@0 3763 RecordReflowStatus(false, frameReflowStatus);
michael@0 3764 #endif
michael@0 3765
michael@0 3766 // Send post-reflow notification
michael@0 3767 aState.mPrevChild = aFrame;
michael@0 3768
michael@0 3769 /* XXX
michael@0 3770 This is where we need to add logic to handle some odd behavior.
michael@0 3771 For one thing, we should usually place at least one thing next
michael@0 3772 to a left float, even when that float takes up all the width on a line.
michael@0 3773 see bug 22496
michael@0 3774 */
michael@0 3775
michael@0 3776 // Process the child frames reflow status. There are 5 cases:
michael@0 3777 // complete, not-complete, break-before, break-after-complete,
michael@0 3778 // break-after-not-complete. There are two situations: we are a
michael@0 3779 // block or we are an inline. This makes a total of 10 cases
michael@0 3780 // (fortunately, there is some overlap).
michael@0 3781 aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
michael@0 3782 if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
michael@0 3783 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
michael@0 3784 // Always abort the line reflow (because a line break is the
michael@0 3785 // minimal amount of break we do).
michael@0 3786 *aLineReflowStatus = LINE_REFLOW_STOP;
michael@0 3787
michael@0 3788 // XXX what should aLine's break-type be set to in all these cases?
michael@0 3789 uint8_t breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
michael@0 3790 NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) ||
michael@0 3791 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
michael@0 3792 NS_ASSERTION(NS_STYLE_CLEAR_MAX >= breakType, "invalid break type");
michael@0 3793
michael@0 3794 if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
michael@0 3795 // Break-before cases.
michael@0 3796 if (aFrame == aLine->mFirstChild) {
michael@0 3797 // If we break before the first frame on the line then we must
michael@0 3798 // be trying to place content where there's no room (e.g. on a
michael@0 3799 // line with wide floats). Inform the caller to reflow the
michael@0 3800 // line after skipping past a float.
michael@0 3801 *aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
michael@0 3802 }
michael@0 3803 else {
michael@0 3804 // It's not the first child on this line so go ahead and split
michael@0 3805 // the line. We will see the frame again on the next-line.
michael@0 3806 SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
michael@0 3807
michael@0 3808 // If we're splitting the line because the frame didn't fit and it
michael@0 3809 // was pushed, then mark the line as having word wrapped. We need to
michael@0 3810 // know that if we're shrink wrapping our width
michael@0 3811 if (pushedFrame) {
michael@0 3812 aLine->SetLineWrapped(true);
michael@0 3813 }
michael@0 3814 }
michael@0 3815 }
michael@0 3816 else {
michael@0 3817 // If a float split and its prev-in-flow was followed by a <BR>, then combine
michael@0 3818 // the <BR>'s break type with the inline's break type (the inline will be the very
michael@0 3819 // next frame after the split float).
michael@0 3820 if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
michael@0 3821 breakType = nsLayoutUtils::CombineBreakType(breakType,
michael@0 3822 aState.mFloatBreakType);
michael@0 3823 aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
michael@0 3824 }
michael@0 3825 // Break-after cases
michael@0 3826 if (breakType == NS_STYLE_CLEAR_LINE) {
michael@0 3827 if (!aLineLayout.GetLineEndsInBR()) {
michael@0 3828 breakType = NS_STYLE_CLEAR_NONE;
michael@0 3829 }
michael@0 3830 }
michael@0 3831 aLine->SetBreakTypeAfter(breakType);
michael@0 3832 if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) {
michael@0 3833 // Split line, but after the frame just reflowed
michael@0 3834 SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
michael@0 3835
michael@0 3836 if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
michael@0 3837 !aLineLayout.GetLineEndsInBR()) {
michael@0 3838 aLineLayout.SetDirtyNextLine();
michael@0 3839 }
michael@0 3840 }
michael@0 3841 }
michael@0 3842 }
michael@0 3843
michael@0 3844 if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
michael@0 3845 // Create a continuation for the incomplete frame. Note that the
michael@0 3846 // frame may already have a continuation.
michael@0 3847 CreateContinuationFor(aState, aLine, aFrame);
michael@0 3848
michael@0 3849 // Remember that the line has wrapped
michael@0 3850 if (!aLineLayout.GetLineEndsInBR()) {
michael@0 3851 aLine->SetLineWrapped(true);
michael@0 3852 }
michael@0 3853
michael@0 3854 // If we just ended a first-letter frame or reflowed a placeholder then
michael@0 3855 // don't split the line and don't stop the line reflow...
michael@0 3856 // But if we are going to stop anyways we'd better split the line.
michael@0 3857 if ((!(frameReflowStatus & NS_INLINE_BREAK_FIRST_LETTER_COMPLETE) &&
michael@0 3858 nsGkAtoms::placeholderFrame != aFrame->GetType()) ||
michael@0 3859 *aLineReflowStatus == LINE_REFLOW_STOP) {
michael@0 3860 // Split line after the current frame
michael@0 3861 *aLineReflowStatus = LINE_REFLOW_STOP;
michael@0 3862 SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
michael@0 3863 }
michael@0 3864 }
michael@0 3865
michael@0 3866 return NS_OK;
michael@0 3867 }
michael@0 3868
michael@0 3869 bool
michael@0 3870 nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
michael@0 3871 nsLineBox* aLine,
michael@0 3872 nsIFrame* aFrame)
michael@0 3873 {
michael@0 3874 nsIFrame* newFrame = nullptr;
michael@0 3875
michael@0 3876 if (!aFrame->GetNextInFlow()) {
michael@0 3877 newFrame = aState.mPresContext->PresShell()->FrameConstructor()->
michael@0 3878 CreateContinuingFrame(aState.mPresContext, aFrame, this);
michael@0 3879
michael@0 3880 mFrames.InsertFrame(nullptr, aFrame, newFrame);
michael@0 3881
michael@0 3882 if (aLine) {
michael@0 3883 aLine->NoteFrameAdded(newFrame);
michael@0 3884 }
michael@0 3885 }
michael@0 3886 #ifdef DEBUG
michael@0 3887 VerifyLines(false);
michael@0 3888 #endif
michael@0 3889 return !!newFrame;
michael@0 3890 }
michael@0 3891
michael@0 3892 nsresult
michael@0 3893 nsBlockFrame::SplitFloat(nsBlockReflowState& aState,
michael@0 3894 nsIFrame* aFloat,
michael@0 3895 nsReflowStatus aFloatStatus)
michael@0 3896 {
michael@0 3897 nsIFrame* nextInFlow = aFloat->GetNextInFlow();
michael@0 3898 if (nextInFlow) {
michael@0 3899 nsContainerFrame *oldParent =
michael@0 3900 static_cast<nsContainerFrame*>(nextInFlow->GetParent());
michael@0 3901 DebugOnly<nsresult> rv = oldParent->StealFrame(nextInFlow);
michael@0 3902 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
michael@0 3903 if (oldParent != this) {
michael@0 3904 ReparentFrame(nextInFlow, oldParent, this);
michael@0 3905 }
michael@0 3906 } else {
michael@0 3907 nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()->
michael@0 3908 CreateContinuingFrame(aState.mPresContext, aFloat, this);
michael@0 3909 }
michael@0 3910 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus))
michael@0 3911 aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
michael@0 3912
michael@0 3913 // The containing block is now overflow-incomplete.
michael@0 3914 NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
michael@0 3915
michael@0 3916 if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
michael@0 3917 aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
michael@0 3918 } else {
michael@0 3919 NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats ==
michael@0 3920 NS_STYLE_FLOAT_RIGHT, "unexpected float side");
michael@0 3921 aState.mFloatManager->SetSplitRightFloatAcrossBreak();
michael@0 3922 }
michael@0 3923
michael@0 3924 aState.AppendPushedFloat(nextInFlow);
michael@0 3925 return NS_OK;
michael@0 3926 }
michael@0 3927
michael@0 3928 static nsFloatCache*
michael@0 3929 GetLastFloat(nsLineBox* aLine)
michael@0 3930 {
michael@0 3931 nsFloatCache* fc = aLine->GetFirstFloat();
michael@0 3932 while (fc && fc->Next()) {
michael@0 3933 fc = fc->Next();
michael@0 3934 }
michael@0 3935 return fc;
michael@0 3936 }
michael@0 3937
michael@0 3938 static bool
michael@0 3939 CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
michael@0 3940 {
michael@0 3941 if (!aFC)
michael@0 3942 return true;
michael@0 3943 NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
michael@0 3944 "float in a line should never be a continuation");
michael@0 3945 NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
michael@0 3946 "float in a line should never be a pushed float");
michael@0 3947 nsIFrame* ph = aBlock->PresContext()->FrameManager()->
michael@0 3948 GetPlaceholderFrameFor(aFC->mFloat->FirstInFlow());
michael@0 3949 for (nsIFrame* f = ph; f; f = f->GetParent()) {
michael@0 3950 if (f->GetParent() == aBlock)
michael@0 3951 return aLine->Contains(f);
michael@0 3952 }
michael@0 3953 NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
michael@0 3954 return true;
michael@0 3955 }
michael@0 3956
michael@0 3957 void
michael@0 3958 nsBlockFrame::SplitLine(nsBlockReflowState& aState,
michael@0 3959 nsLineLayout& aLineLayout,
michael@0 3960 line_iterator aLine,
michael@0 3961 nsIFrame* aFrame,
michael@0 3962 LineReflowStatus* aLineReflowStatus)
michael@0 3963 {
michael@0 3964 NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
michael@0 3965
michael@0 3966 int32_t pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
michael@0 3967 NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count");
michael@0 3968
michael@0 3969 #ifdef DEBUG
michael@0 3970 if (gNoisyReflow) {
michael@0 3971 nsFrame::IndentBy(stdout, gNoiseIndent);
michael@0 3972 printf("split line: from line=%p pushCount=%d aFrame=",
michael@0 3973 static_cast<void*>(aLine.get()), pushCount);
michael@0 3974 if (aFrame) {
michael@0 3975 nsFrame::ListTag(stdout, aFrame);
michael@0 3976 }
michael@0 3977 else {
michael@0 3978 printf("(null)");
michael@0 3979 }
michael@0 3980 printf("\n");
michael@0 3981 if (gReallyNoisyReflow) {
michael@0 3982 aLine->List(stdout, gNoiseIndent+1);
michael@0 3983 }
michael@0 3984 }
michael@0 3985 #endif
michael@0 3986
michael@0 3987 if (0 != pushCount) {
michael@0 3988 NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
michael@0 3989 NS_ABORT_IF_FALSE(nullptr != aFrame, "whoops");
michael@0 3990 #ifdef DEBUG
michael@0 3991 {
michael@0 3992 nsIFrame *f = aFrame;
michael@0 3993 int32_t count = pushCount;
michael@0 3994 while (f && count > 0) {
michael@0 3995 f = f->GetNextSibling();
michael@0 3996 --count;
michael@0 3997 }
michael@0 3998 NS_ASSERTION(count == 0, "Not enough frames to push");
michael@0 3999 }
michael@0 4000 #endif
michael@0 4001
michael@0 4002 // Put frames being split out into their own line
michael@0 4003 nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
michael@0 4004 mLines.after_insert(aLine, newLine);
michael@0 4005 #ifdef DEBUG
michael@0 4006 if (gReallyNoisyReflow) {
michael@0 4007 newLine->List(stdout, gNoiseIndent+1);
michael@0 4008 }
michael@0 4009 #endif
michael@0 4010
michael@0 4011 // Let line layout know that some frames are no longer part of its
michael@0 4012 // state.
michael@0 4013 aLineLayout.SplitLineTo(aLine->GetChildCount());
michael@0 4014
michael@0 4015 // If floats have been placed whose placeholders have been pushed to the new
michael@0 4016 // line, we need to reflow the old line again. We don't want to look at the
michael@0 4017 // frames in the new line, because as a large paragraph is laid out the
michael@0 4018 // we'd get O(N^2) performance. So instead we just check that the last
michael@0 4019 // float and the last below-current-line float are still in aLine.
michael@0 4020 if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
michael@0 4021 !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
michael@0 4022 *aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
michael@0 4023 }
michael@0 4024
michael@0 4025 #ifdef DEBUG
michael@0 4026 VerifyLines(true);
michael@0 4027 #endif
michael@0 4028 }
michael@0 4029 }
michael@0 4030
michael@0 4031 bool
michael@0 4032 nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
michael@0 4033 line_iterator aLine)
michael@0 4034 {
michael@0 4035 while (++aLine != end_lines()) {
michael@0 4036 // There is another line
michael@0 4037 if (0 != aLine->GetChildCount()) {
michael@0 4038 // If the next line is a block line then this line is the last in a
michael@0 4039 // group of inline lines.
michael@0 4040 return aLine->IsBlock();
michael@0 4041 }
michael@0 4042 // The next line is empty, try the next one
michael@0 4043 }
michael@0 4044
michael@0 4045 // XXX Not sure about this part
michael@0 4046 // Try our next-in-flows lines to answer the question
michael@0 4047 nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
michael@0 4048 while (nullptr != nextInFlow) {
michael@0 4049 for (line_iterator line = nextInFlow->begin_lines(),
michael@0 4050 line_end = nextInFlow->end_lines();
michael@0 4051 line != line_end;
michael@0 4052 ++line)
michael@0 4053 {
michael@0 4054 if (0 != line->GetChildCount())
michael@0 4055 return line->IsBlock();
michael@0 4056 }
michael@0 4057 nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
michael@0 4058 }
michael@0 4059
michael@0 4060 // This is the last line - so don't allow justification
michael@0 4061 return true;
michael@0 4062 }
michael@0 4063
michael@0 4064 bool
michael@0 4065 nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
michael@0 4066 nsLineLayout& aLineLayout,
michael@0 4067 line_iterator aLine,
michael@0 4068 nsFloatManager::SavedState *aFloatStateBeforeLine,
michael@0 4069 nsRect& aFloatAvailableSpace,
michael@0 4070 nscoord& aAvailableSpaceHeight,
michael@0 4071 bool* aKeepReflowGoing)
michael@0 4072 {
michael@0 4073 // Trim extra white-space from the line before placing the frames
michael@0 4074 aLineLayout.TrimTrailingWhiteSpace();
michael@0 4075
michael@0 4076 // Vertically align the frames on this line.
michael@0 4077 //
michael@0 4078 // According to the CSS2 spec, section 12.6.1, the "marker" box
michael@0 4079 // participates in the height calculation of the list-item box's
michael@0 4080 // first line box.
michael@0 4081 //
michael@0 4082 // There are exactly two places a bullet can be placed: near the
michael@0 4083 // first or second line. It's only placed on the second line in a
michael@0 4084 // rare case: when the first line is empty.
michael@0 4085 bool addedBullet = false;
michael@0 4086 if (HasOutsideBullet() &&
michael@0 4087 ((aLine == mLines.front() &&
michael@0 4088 (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) ||
michael@0 4089 (mLines.front() != mLines.back() &&
michael@0 4090 0 == mLines.front()->BSize() &&
michael@0 4091 aLine == mLines.begin().next()))) {
michael@0 4092 nsHTMLReflowMetrics metrics(aState.mReflowState);
michael@0 4093 nsIFrame* bullet = GetOutsideBullet();
michael@0 4094 ReflowBullet(bullet, aState, metrics, aState.mY);
michael@0 4095 NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
michael@0 4096 "empty bullet took up space");
michael@0 4097 aLineLayout.AddBulletFrame(bullet, metrics);
michael@0 4098 addedBullet = true;
michael@0 4099 }
michael@0 4100 aLineLayout.BlockDirAlignLine();
michael@0 4101
michael@0 4102 // We want to compare to the available space that we would have had in
michael@0 4103 // the line's height *before* we placed any floats in the line itself.
michael@0 4104 // Floats that are in the line are handled during line reflow (and may
michael@0 4105 // result in floats being pushed to below the line or (I HOPE???) in a
michael@0 4106 // reflow with a forced break position).
michael@0 4107 nsRect oldFloatAvailableSpace(aFloatAvailableSpace);
michael@0 4108 // As we redo for floats, we can't reduce the amount of height we're
michael@0 4109 // checking.
michael@0 4110 aAvailableSpaceHeight = std::max(aAvailableSpaceHeight, aLine->BSize());
michael@0 4111 aFloatAvailableSpace =
michael@0 4112 aState.GetFloatAvailableSpaceForHeight(aLine->BStart(),
michael@0 4113 aAvailableSpaceHeight,
michael@0 4114 aFloatStateBeforeLine).mRect;
michael@0 4115 NS_ASSERTION(aFloatAvailableSpace.y == oldFloatAvailableSpace.y, "yikes");
michael@0 4116 // Restore the height to the position of the next band.
michael@0 4117 aFloatAvailableSpace.height = oldFloatAvailableSpace.height;
michael@0 4118 // If the available space between the floats is smaller now that we
michael@0 4119 // know the height, return false (and cause another pass with
michael@0 4120 // LINE_REFLOW_REDO_MORE_FLOATS).
michael@0 4121 if (AvailableSpaceShrunk(oldFloatAvailableSpace, aFloatAvailableSpace)) {
michael@0 4122 return false;
michael@0 4123 }
michael@0 4124
michael@0 4125 #ifdef DEBUG
michael@0 4126 {
michael@0 4127 static nscoord lastHeight = 0;
michael@0 4128 if (CRAZY_SIZE(aLine->BStart())) {
michael@0 4129 lastHeight = aLine->BStart();
michael@0 4130 if (abs(aLine->BStart() - lastHeight) > CRAZY_COORD/10) {
michael@0 4131 nsFrame::ListTag(stdout);
michael@0 4132 printf(": line=%p y=%d line.bounds.height=%d\n",
michael@0 4133 static_cast<void*>(aLine.get()),
michael@0 4134 aLine->BStart(), aLine->BSize());
michael@0 4135 }
michael@0 4136 }
michael@0 4137 else {
michael@0 4138 lastHeight = 0;
michael@0 4139 }
michael@0 4140 }
michael@0 4141 #endif
michael@0 4142
michael@0 4143 // Only block frames horizontally align their children because
michael@0 4144 // inline frames "shrink-wrap" around their children (therefore
michael@0 4145 // there is no extra horizontal space).
michael@0 4146 const nsStyleText* styleText = StyleText();
michael@0 4147
michael@0 4148 /**
michael@0 4149 * text-align-last defaults to the same value as text-align when
michael@0 4150 * text-align-last is set to auto (except when text-align is set to justify),
michael@0 4151 * so in that case we don't need to set isLastLine.
michael@0 4152 *
michael@0 4153 * In other words, isLastLine really means isLastLineAndWeCare.
michael@0 4154 */
michael@0 4155 bool isLastLine =
michael@0 4156 !IsSVGText() &&
michael@0 4157 ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
michael@0 4158 NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
michael@0 4159 (aLineLayout.GetLineEndsInBR() ||
michael@0 4160 IsLastLine(aState, aLine)));
michael@0 4161
michael@0 4162 aLineLayout.InlineDirAlignFrames(aLine, isLastLine);
michael@0 4163
michael@0 4164 // From here on, pfd->mBounds rectangles are incorrect because bidi
michael@0 4165 // might have moved frames around!
michael@0 4166 nsOverflowAreas overflowAreas;
michael@0 4167 aLineLayout.RelativePositionFrames(overflowAreas);
michael@0 4168 aLine->SetOverflowAreas(overflowAreas);
michael@0 4169 if (addedBullet) {
michael@0 4170 aLineLayout.RemoveBulletFrame(GetOutsideBullet());
michael@0 4171 }
michael@0 4172
michael@0 4173 // Inline lines do not have margins themselves; however they are
michael@0 4174 // impacted by prior block margins. If this line ends up having some
michael@0 4175 // height then we zero out the previous bottom margin value that was
michael@0 4176 // already applied to the line's starting Y coordinate. Otherwise we
michael@0 4177 // leave it be so that the previous blocks bottom margin can be
michael@0 4178 // collapsed with a block that follows.
michael@0 4179 nscoord newY;
michael@0 4180
michael@0 4181 if (!aLine->CachedIsEmpty()) {
michael@0 4182 // This line has some height. Therefore the application of the
michael@0 4183 // previous-bottom-margin should stick.
michael@0 4184 aState.mPrevBottomMargin.Zero();
michael@0 4185 newY = aLine->BEnd();
michael@0 4186 }
michael@0 4187 else {
michael@0 4188 // Don't let the previous-bottom-margin value affect the newY
michael@0 4189 // coordinate (it was applied in ReflowInlineFrames speculatively)
michael@0 4190 // since the line is empty.
michael@0 4191 // We already called |ShouldApplyTopMargin|, and if we applied it
michael@0 4192 // then BRS_APPLYTOPMARGIN is set.
michael@0 4193 nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN)
michael@0 4194 ? -aState.mPrevBottomMargin.get() : 0;
michael@0 4195 newY = aState.mY + dy;
michael@0 4196 }
michael@0 4197
michael@0 4198 if (!NS_FRAME_IS_FULLY_COMPLETE(aState.mReflowStatus) &&
michael@0 4199 ShouldAvoidBreakInside(aState.mReflowState)) {
michael@0 4200 aLine->AppendFloats(aState.mCurrentLineFloats);
michael@0 4201 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 4202 return true;
michael@0 4203 }
michael@0 4204
michael@0 4205 // See if the line fit (our first line always does).
michael@0 4206 if (mLines.front() != aLine &&
michael@0 4207 newY > aState.mBottomEdge &&
michael@0 4208 aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) {
michael@0 4209 NS_ASSERTION(aState.mCurrentLine == aLine, "oops");
michael@0 4210 if (ShouldAvoidBreakInside(aState.mReflowState)) {
michael@0 4211 // All our content doesn't fit, start on the next page.
michael@0 4212 aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 4213 } else {
michael@0 4214 // Push aLine and all of its children and anything else that
michael@0 4215 // follows to our next-in-flow.
michael@0 4216 PushTruncatedLine(aState, aLine, aKeepReflowGoing);
michael@0 4217 }
michael@0 4218 return true;
michael@0 4219 }
michael@0 4220
michael@0 4221 aState.mY = newY;
michael@0 4222
michael@0 4223 // Add the already placed current-line floats to the line
michael@0 4224 aLine->AppendFloats(aState.mCurrentLineFloats);
michael@0 4225
michael@0 4226 // Any below current line floats to place?
michael@0 4227 if (aState.mBelowCurrentLineFloats.NotEmpty()) {
michael@0 4228 // Reflow the below-current-line floats, which places on the line's
michael@0 4229 // float list.
michael@0 4230 aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats, aLine);
michael@0 4231 aLine->AppendFloats(aState.mBelowCurrentLineFloats);
michael@0 4232 }
michael@0 4233
michael@0 4234 // When a line has floats, factor them into the combined-area
michael@0 4235 // computations.
michael@0 4236 if (aLine->HasFloats()) {
michael@0 4237 // Combine the float combined area (stored in aState) and the
michael@0 4238 // value computed by the line layout code.
michael@0 4239 nsOverflowAreas lineOverflowAreas;
michael@0 4240 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
michael@0 4241 nsRect &o = lineOverflowAreas.Overflow(otype);
michael@0 4242 o = aLine->GetOverflowArea(otype);
michael@0 4243 #ifdef NOISY_COMBINED_AREA
michael@0 4244 ListTag(stdout);
michael@0 4245 printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
michael@0 4246 otype,
michael@0 4247 o.x, o.y, o.width, o.height,
michael@0 4248 aState.mFloatOverflowAreas.Overflow(otype).x,
michael@0 4249 aState.mFloatOverflowAreas.Overflow(otype).y,
michael@0 4250 aState.mFloatOverflowAreas.Overflow(otype).width,
michael@0 4251 aState.mFloatOverflowAreas.Overflow(otype).height);
michael@0 4252 #endif
michael@0 4253 o.UnionRect(aState.mFloatOverflowAreas.Overflow(otype), o);
michael@0 4254
michael@0 4255 #ifdef NOISY_COMBINED_AREA
michael@0 4256 printf(" ==> final lineCA=%d,%d,%d,%d\n",
michael@0 4257 o.x, o.y, o.width, o.height);
michael@0 4258 #endif
michael@0 4259 }
michael@0 4260 aLine->SetOverflowAreas(lineOverflowAreas);
michael@0 4261 }
michael@0 4262
michael@0 4263 // Apply break-after clearing if necessary
michael@0 4264 // This must stay in sync with |ReflowDirtyLines|.
michael@0 4265 if (aLine->HasFloatBreakAfter()) {
michael@0 4266 aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
michael@0 4267 }
michael@0 4268 return true;
michael@0 4269 }
michael@0 4270
michael@0 4271 void
michael@0 4272 nsBlockFrame::PushLines(nsBlockReflowState& aState,
michael@0 4273 nsLineList::iterator aLineBefore)
michael@0 4274 {
michael@0 4275 // NOTE: aLineBefore is always a normal line, not an overflow line.
michael@0 4276 // The following expression will assert otherwise.
michael@0 4277 DebugOnly<bool> check = aLineBefore == mLines.begin();
michael@0 4278
michael@0 4279 nsLineList::iterator overBegin(aLineBefore.next());
michael@0 4280
michael@0 4281 // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
michael@0 4282 bool firstLine = overBegin == begin_lines();
michael@0 4283
michael@0 4284 if (overBegin != end_lines()) {
michael@0 4285 // Remove floats in the lines from mFloats
michael@0 4286 nsFrameList floats;
michael@0 4287 CollectFloats(overBegin->mFirstChild, floats, true);
michael@0 4288
michael@0 4289 if (floats.NotEmpty()) {
michael@0 4290 // Push the floats onto the front of the overflow out-of-flows list
michael@0 4291 nsAutoOOFFrameList oofs(this);
michael@0 4292 oofs.mList.InsertFrames(nullptr, nullptr, floats);
michael@0 4293 }
michael@0 4294
michael@0 4295 // overflow lines can already exist in some cases, in particular,
michael@0 4296 // when shrinkwrapping and we discover that the shrinkwap causes
michael@0 4297 // the height of some child block to grow which creates additional
michael@0 4298 // overflowing content. In such cases we must prepend the new
michael@0 4299 // overflow to the existing overflow.
michael@0 4300 FrameLines* overflowLines = RemoveOverflowLines();
michael@0 4301 if (!overflowLines) {
michael@0 4302 // XXXldb use presshell arena!
michael@0 4303 overflowLines = new FrameLines();
michael@0 4304 }
michael@0 4305 if (overflowLines) {
michael@0 4306 nsIFrame* lineBeforeLastFrame;
michael@0 4307 if (firstLine) {
michael@0 4308 lineBeforeLastFrame = nullptr; // removes all frames
michael@0 4309 } else {
michael@0 4310 nsIFrame* f = overBegin->mFirstChild;
michael@0 4311 lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
michael@0 4312 NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
michael@0 4313 "unexpected line frames");
michael@0 4314 }
michael@0 4315 nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame);
michael@0 4316 overflowLines->mFrames.InsertFrames(nullptr, nullptr, pushedFrames);
michael@0 4317
michael@0 4318 overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
michael@0 4319 overBegin, end_lines());
michael@0 4320 NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
michael@0 4321 // this takes ownership but it won't delete it immediately so we
michael@0 4322 // can keep using it.
michael@0 4323 SetOverflowLines(overflowLines);
michael@0 4324
michael@0 4325 // Mark all the overflow lines dirty so that they get reflowed when
michael@0 4326 // they are pulled up by our next-in-flow.
michael@0 4327
michael@0 4328 // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
michael@0 4329 for (line_iterator line = overflowLines->mLines.begin(),
michael@0 4330 line_end = overflowLines->mLines.end();
michael@0 4331 line != line_end;
michael@0 4332 ++line)
michael@0 4333 {
michael@0 4334 line->MarkDirty();
michael@0 4335 line->MarkPreviousMarginDirty();
michael@0 4336 line->SetBoundsEmpty();
michael@0 4337 if (line->HasFloats()) {
michael@0 4338 line->FreeFloats(aState.mFloatCacheFreeList);
michael@0 4339 }
michael@0 4340 }
michael@0 4341 }
michael@0 4342 }
michael@0 4343
michael@0 4344 #ifdef DEBUG
michael@0 4345 VerifyOverflowSituation();
michael@0 4346 #endif
michael@0 4347 }
michael@0 4348
michael@0 4349 // The overflowLines property is stored as a pointer to a line list,
michael@0 4350 // which must be deleted. However, the following functions all maintain
michael@0 4351 // the invariant that the property is never set if the list is empty.
michael@0 4352
michael@0 4353 bool
michael@0 4354 nsBlockFrame::DrainOverflowLines()
michael@0 4355 {
michael@0 4356 #ifdef DEBUG
michael@0 4357 VerifyOverflowSituation();
michael@0 4358 #endif
michael@0 4359
michael@0 4360 // Steal the prev-in-flow's overflow lines and prepend them.
michael@0 4361 bool didFindOverflow = false;
michael@0 4362 nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
michael@0 4363 if (prevBlock) {
michael@0 4364 prevBlock->ClearLineCursor();
michael@0 4365 FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
michael@0 4366 if (overflowLines) {
michael@0 4367 // Make all the frames on the overflow line list mine.
michael@0 4368 ReparentFrames(overflowLines->mFrames, prevBlock, this);
michael@0 4369
michael@0 4370 // Make the overflow out-of-flow frames mine too.
michael@0 4371 nsAutoOOFFrameList oofs(prevBlock);
michael@0 4372 if (oofs.mList.NotEmpty()) {
michael@0 4373 ReparentFrames(oofs.mList, prevBlock, this);
michael@0 4374 mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
michael@0 4375 }
michael@0 4376
michael@0 4377 if (!mLines.empty()) {
michael@0 4378 // Remember to recompute the margins on the first line. This will
michael@0 4379 // also recompute the correct deltaY if necessary.
michael@0 4380 mLines.front()->MarkPreviousMarginDirty();
michael@0 4381 }
michael@0 4382 // The overflow lines have already been marked dirty and their previous
michael@0 4383 // margins marked dirty also.
michael@0 4384
michael@0 4385 // Prepend the overflow frames/lines to our principal list.
michael@0 4386 mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
michael@0 4387 mLines.splice(mLines.begin(), overflowLines->mLines);
michael@0 4388 NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
michael@0 4389 delete overflowLines;
michael@0 4390 didFindOverflow = true;
michael@0 4391 }
michael@0 4392 }
michael@0 4393
michael@0 4394 // Now append our own overflow lines.
michael@0 4395 return DrainSelfOverflowList() || didFindOverflow;
michael@0 4396 }
michael@0 4397
michael@0 4398 bool
michael@0 4399 nsBlockFrame::DrainSelfOverflowList()
michael@0 4400 {
michael@0 4401 nsAutoPtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
michael@0 4402 if (!ourOverflowLines) {
michael@0 4403 return false;
michael@0 4404 }
michael@0 4405
michael@0 4406 // No need to reparent frames in our own overflow lines/oofs, because they're
michael@0 4407 // already ours. But we should put overflow floats back in mFloats.
michael@0 4408 nsAutoOOFFrameList oofs(this);
michael@0 4409 if (oofs.mList.NotEmpty()) {
michael@0 4410 // The overflow floats go after our regular floats.
michael@0 4411 mFloats.AppendFrames(nullptr, oofs.mList);
michael@0 4412 }
michael@0 4413
michael@0 4414 if (!ourOverflowLines->mLines.empty()) {
michael@0 4415 mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
michael@0 4416 mLines.splice(mLines.end(), ourOverflowLines->mLines);
michael@0 4417 }
michael@0 4418 return true;
michael@0 4419 }
michael@0 4420
michael@0 4421 /**
michael@0 4422 * Pushed floats are floats whose placeholders are in a previous
michael@0 4423 * continuation. They might themselves be next-continuations of a float
michael@0 4424 * that partially fit in an earlier continuation, or they might be the
michael@0 4425 * first continuation of a float that couldn't be placed at all.
michael@0 4426 *
michael@0 4427 * Pushed floats live permanently at the beginning of a block's float
michael@0 4428 * list, where they must live *before* any floats whose placeholders are
michael@0 4429 * in that block.
michael@0 4430 *
michael@0 4431 * Temporarily, during reflow, they also live on the pushed floats list,
michael@0 4432 * which only holds them between (a) when one continuation pushes them to
michael@0 4433 * its pushed floats list because they don't fit and (b) when the next
michael@0 4434 * continuation pulls them onto the beginning of its float list.
michael@0 4435 *
michael@0 4436 * DrainPushedFloats sets up pushed floats the way we need them at the
michael@0 4437 * start of reflow; they are then reflowed by ReflowPushedFloats (which
michael@0 4438 * might push some of them on). Floats with placeholders in this block
michael@0 4439 * are reflowed by (nsBlockReflowState/nsLineLayout)::AddFloat, which
michael@0 4440 * also maintains these invariants.
michael@0 4441 */
michael@0 4442 void
michael@0 4443 nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState)
michael@0 4444 {
michael@0 4445 #ifdef DEBUG
michael@0 4446 // Between when we drain pushed floats and when we complete reflow,
michael@0 4447 // we're allowed to have multiple continuations of the same float on
michael@0 4448 // our floats list, since a first-in-flow might get pushed to a later
michael@0 4449 // continuation of its containing block. But it's not permitted
michael@0 4450 // outside that time.
michael@0 4451 nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
michael@0 4452 #endif
michael@0 4453
michael@0 4454 // If we're getting reflowed multiple times without our
michael@0 4455 // next-continuation being reflowed, we might need to pull back floats
michael@0 4456 // that we just put in the list to be pushed to our next-in-flow.
michael@0 4457 // We don't want to pull back any next-in-flows of floats on our own
michael@0 4458 // float list, and we only need to pull back first-in-flows whose
michael@0 4459 // placeholders were in earlier blocks (since first-in-flows whose
michael@0 4460 // placeholders are in this block will get pulled appropriately by
michael@0 4461 // AddFloat, and will then be more likely to be in the correct order).
michael@0 4462 // FIXME: What if there's a continuation in our pushed floats list
michael@0 4463 // whose prev-in-flow is in a previous continuation of this block
michael@0 4464 // rather than this block? Might we need to pull it back so we don't
michael@0 4465 // report ourselves complete?
michael@0 4466 // FIXME: Maybe we should just pull all of them back?
michael@0 4467 nsPresContext* presContext = PresContext();
michael@0 4468 nsFrameList* ourPushedFloats = GetPushedFloats();
michael@0 4469 if (ourPushedFloats) {
michael@0 4470 // When we pull back floats, we want to put them with the pushed
michael@0 4471 // floats, which must live at the start of our float list, but we
michael@0 4472 // want them at the end of those pushed floats.
michael@0 4473 // FIXME: This isn't quite right! What if they're all pushed floats?
michael@0 4474 nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */
michael@0 4475 for (nsIFrame* f = mFloats.FirstChild();
michael@0 4476 f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
michael@0 4477 f = f->GetNextSibling()) {
michael@0 4478 insertionPrevSibling = f;
michael@0 4479 }
michael@0 4480
michael@0 4481 for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
michael@0 4482 next = f->GetPrevSibling();
michael@0 4483
michael@0 4484 if (f->GetPrevContinuation()) {
michael@0 4485 // FIXME
michael@0 4486 } else {
michael@0 4487 nsPlaceholderFrame *placeholder =
michael@0 4488 presContext->FrameManager()->GetPlaceholderFrameFor(f);
michael@0 4489 nsIFrame *floatOriginalParent = presContext->PresShell()->
michael@0 4490 FrameConstructor()->GetFloatContainingBlock(placeholder);
michael@0 4491 if (floatOriginalParent != this) {
michael@0 4492 // This is a first continuation that was pushed from one of our
michael@0 4493 // previous continuations. Take it out of the pushed floats
michael@0 4494 // list and put it in our floats list, before any of our
michael@0 4495 // floats, but after other pushed floats.
michael@0 4496 ourPushedFloats->RemoveFrame(f);
michael@0 4497 mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
michael@0 4498 }
michael@0 4499 }
michael@0 4500 }
michael@0 4501
michael@0 4502 if (ourPushedFloats->IsEmpty()) {
michael@0 4503 RemovePushedFloats()->Delete(presContext->PresShell());
michael@0 4504 }
michael@0 4505 }
michael@0 4506
michael@0 4507 // After our prev-in-flow has completed reflow, it may have a pushed
michael@0 4508 // floats list, containing floats that we need to own. Take these.
michael@0 4509 nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
michael@0 4510 if (prevBlock) {
michael@0 4511 AutoFrameListPtr list(presContext, prevBlock->RemovePushedFloats());
michael@0 4512 if (list && list->NotEmpty()) {
michael@0 4513 mFloats.InsertFrames(this, nullptr, *list);
michael@0 4514 }
michael@0 4515 }
michael@0 4516 }
michael@0 4517
michael@0 4518 nsBlockFrame::FrameLines*
michael@0 4519 nsBlockFrame::GetOverflowLines() const
michael@0 4520 {
michael@0 4521 if (!HasOverflowLines()) {
michael@0 4522 return nullptr;
michael@0 4523 }
michael@0 4524 FrameLines* prop =
michael@0 4525 static_cast<FrameLines*>(Properties().Get(OverflowLinesProperty()));
michael@0 4526 NS_ASSERTION(prop && !prop->mLines.empty() &&
michael@0 4527 prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
michael@0 4528 prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
michael@0 4529 "value should always be stored and non-empty when state set");
michael@0 4530 return prop;
michael@0 4531 }
michael@0 4532
michael@0 4533 nsBlockFrame::FrameLines*
michael@0 4534 nsBlockFrame::RemoveOverflowLines()
michael@0 4535 {
michael@0 4536 if (!HasOverflowLines()) {
michael@0 4537 return nullptr;
michael@0 4538 }
michael@0 4539 FrameLines* prop =
michael@0 4540 static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
michael@0 4541 NS_ASSERTION(prop && !prop->mLines.empty() &&
michael@0 4542 prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
michael@0 4543 prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
michael@0 4544 "value should always be stored and non-empty when state set");
michael@0 4545 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
michael@0 4546 return prop;
michael@0 4547 }
michael@0 4548
michael@0 4549 void
michael@0 4550 nsBlockFrame::DestroyOverflowLines()
michael@0 4551 {
michael@0 4552 NS_ASSERTION(HasOverflowLines(), "huh?");
michael@0 4553 FrameLines* prop =
michael@0 4554 static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
michael@0 4555 NS_ASSERTION(prop && prop->mLines.empty(),
michael@0 4556 "value should always be stored but empty when destroying");
michael@0 4557 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
michael@0 4558 delete prop;
michael@0 4559 }
michael@0 4560
michael@0 4561 // This takes ownership of aOverflowLines.
michael@0 4562 // XXX We should allocate overflowLines from presShell arena!
michael@0 4563 void
michael@0 4564 nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines)
michael@0 4565 {
michael@0 4566 NS_ASSERTION(aOverflowLines, "null lines");
michael@0 4567 NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
michael@0 4568 NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
michael@0 4569 aOverflowLines->mFrames.FirstChild(),
michael@0 4570 "invalid overflow lines / frames");
michael@0 4571 NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
michael@0 4572 "Overwriting existing overflow lines");
michael@0 4573
michael@0 4574 FrameProperties props = Properties();
michael@0 4575 // Verify that we won't overwrite an existing overflow list
michael@0 4576 NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list");
michael@0 4577 props.Set(OverflowLinesProperty(), aOverflowLines);
michael@0 4578 AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
michael@0 4579 }
michael@0 4580
michael@0 4581 nsFrameList*
michael@0 4582 nsBlockFrame::GetOverflowOutOfFlows() const
michael@0 4583 {
michael@0 4584 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
michael@0 4585 return nullptr;
michael@0 4586 }
michael@0 4587 nsFrameList* result =
michael@0 4588 GetPropTableFrames(OverflowOutOfFlowsProperty());
michael@0 4589 NS_ASSERTION(result, "value should always be non-empty when state set");
michael@0 4590 return result;
michael@0 4591 }
michael@0 4592
michael@0 4593 // This takes ownership of the frames
michael@0 4594 void
michael@0 4595 nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList,
michael@0 4596 nsFrameList* aPropValue)
michael@0 4597 {
michael@0 4598 NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
michael@0 4599 !!aPropValue, "state does not match value");
michael@0 4600
michael@0 4601 if (aList.IsEmpty()) {
michael@0 4602 if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
michael@0 4603 return;
michael@0 4604 }
michael@0 4605 nsFrameList* list = RemovePropTableFrames(OverflowOutOfFlowsProperty());
michael@0 4606 NS_ASSERTION(aPropValue == list, "prop value mismatch");
michael@0 4607 list->Clear();
michael@0 4608 list->Delete(PresContext()->PresShell());
michael@0 4609 RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
michael@0 4610 }
michael@0 4611 else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
michael@0 4612 NS_ASSERTION(aPropValue == GetPropTableFrames(OverflowOutOfFlowsProperty()),
michael@0 4613 "prop value mismatch");
michael@0 4614 *aPropValue = aList;
michael@0 4615 }
michael@0 4616 else {
michael@0 4617 SetPropTableFrames(new (PresContext()->PresShell()) nsFrameList(aList),
michael@0 4618 OverflowOutOfFlowsProperty());
michael@0 4619 AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
michael@0 4620 }
michael@0 4621 }
michael@0 4622
michael@0 4623 nsBulletFrame*
michael@0 4624 nsBlockFrame::GetInsideBullet() const
michael@0 4625 {
michael@0 4626 if (!HasInsideBullet()) {
michael@0 4627 return nullptr;
michael@0 4628 }
michael@0 4629 NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
michael@0 4630 nsBulletFrame* frame =
michael@0 4631 static_cast<nsBulletFrame*>(Properties().Get(InsideBulletProperty()));
michael@0 4632 NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame,
michael@0 4633 "bogus inside bullet frame");
michael@0 4634 return frame;
michael@0 4635 }
michael@0 4636
michael@0 4637 nsBulletFrame*
michael@0 4638 nsBlockFrame::GetOutsideBullet() const
michael@0 4639 {
michael@0 4640 nsFrameList* list = GetOutsideBulletList();
michael@0 4641 return list ? static_cast<nsBulletFrame*>(list->FirstChild())
michael@0 4642 : nullptr;
michael@0 4643 }
michael@0 4644
michael@0 4645 nsFrameList*
michael@0 4646 nsBlockFrame::GetOutsideBulletList() const
michael@0 4647 {
michael@0 4648 if (!HasOutsideBullet()) {
michael@0 4649 return nullptr;
michael@0 4650 }
michael@0 4651 NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
michael@0 4652 nsFrameList* list =
michael@0 4653 static_cast<nsFrameList*>(Properties().Get(OutsideBulletProperty()));
michael@0 4654 NS_ASSERTION(list && list->GetLength() == 1 &&
michael@0 4655 list->FirstChild()->GetType() == nsGkAtoms::bulletFrame,
michael@0 4656 "bogus outside bullet list");
michael@0 4657 return list;
michael@0 4658 }
michael@0 4659
michael@0 4660 nsFrameList*
michael@0 4661 nsBlockFrame::GetPushedFloats() const
michael@0 4662 {
michael@0 4663 if (!HasPushedFloats()) {
michael@0 4664 return nullptr;
michael@0 4665 }
michael@0 4666 nsFrameList* result =
michael@0 4667 static_cast<nsFrameList*>(Properties().Get(PushedFloatProperty()));
michael@0 4668 NS_ASSERTION(result, "value should always be non-empty when state set");
michael@0 4669 return result;
michael@0 4670 }
michael@0 4671
michael@0 4672 nsFrameList*
michael@0 4673 nsBlockFrame::EnsurePushedFloats()
michael@0 4674 {
michael@0 4675 nsFrameList *result = GetPushedFloats();
michael@0 4676 if (result)
michael@0 4677 return result;
michael@0 4678
michael@0 4679 result = new (PresContext()->PresShell()) nsFrameList;
michael@0 4680 Properties().Set(PushedFloatProperty(), result);
michael@0 4681 AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
michael@0 4682
michael@0 4683 return result;
michael@0 4684 }
michael@0 4685
michael@0 4686 nsFrameList*
michael@0 4687 nsBlockFrame::RemovePushedFloats()
michael@0 4688 {
michael@0 4689 if (!HasPushedFloats()) {
michael@0 4690 return nullptr;
michael@0 4691 }
michael@0 4692 nsFrameList *result =
michael@0 4693 static_cast<nsFrameList*>(Properties().Remove(PushedFloatProperty()));
michael@0 4694 RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
michael@0 4695 NS_ASSERTION(result, "value should always be non-empty when state set");
michael@0 4696 return result;
michael@0 4697 }
michael@0 4698
michael@0 4699 //////////////////////////////////////////////////////////////////////
michael@0 4700 // Frame list manipulation routines
michael@0 4701
michael@0 4702 nsresult
michael@0 4703 nsBlockFrame::AppendFrames(ChildListID aListID,
michael@0 4704 nsFrameList& aFrameList)
michael@0 4705 {
michael@0 4706 if (aFrameList.IsEmpty()) {
michael@0 4707 return NS_OK;
michael@0 4708 }
michael@0 4709 if (aListID != kPrincipalList) {
michael@0 4710 if (kAbsoluteList == aListID) {
michael@0 4711 return nsContainerFrame::AppendFrames(aListID, aFrameList);
michael@0 4712 }
michael@0 4713 else if (kFloatList == aListID) {
michael@0 4714 mFloats.AppendFrames(nullptr, aFrameList);
michael@0 4715 return NS_OK;
michael@0 4716 }
michael@0 4717 else {
michael@0 4718 NS_ERROR("unexpected child list");
michael@0 4719 return NS_ERROR_INVALID_ARG;
michael@0 4720 }
michael@0 4721 }
michael@0 4722
michael@0 4723 // Find the proper last-child for where the append should go
michael@0 4724 nsIFrame* lastKid = mFrames.LastChild();
michael@0 4725 NS_ASSERTION((mLines.empty() ? nullptr : mLines.back()->LastChild()) ==
michael@0 4726 lastKid, "out-of-sync mLines / mFrames");
michael@0 4727
michael@0 4728 // Add frames after the last child
michael@0 4729 #ifdef NOISY_REFLOW_REASON
michael@0 4730 ListTag(stdout);
michael@0 4731 printf(": append ");
michael@0 4732 nsFrame::ListTag(stdout, aFrameList);
michael@0 4733 if (lastKid) {
michael@0 4734 printf(" after ");
michael@0 4735 nsFrame::ListTag(stdout, lastKid);
michael@0 4736 }
michael@0 4737 printf("\n");
michael@0 4738 #endif
michael@0 4739
michael@0 4740 AddFrames(aFrameList, lastKid);
michael@0 4741 PresContext()->PresShell()->
michael@0 4742 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 4743 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
michael@0 4744 return NS_OK;
michael@0 4745 }
michael@0 4746
michael@0 4747 nsresult
michael@0 4748 nsBlockFrame::InsertFrames(ChildListID aListID,
michael@0 4749 nsIFrame* aPrevFrame,
michael@0 4750 nsFrameList& aFrameList)
michael@0 4751 {
michael@0 4752 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
michael@0 4753 "inserting after sibling frame with different parent");
michael@0 4754
michael@0 4755 if (aListID != kPrincipalList) {
michael@0 4756 if (kAbsoluteList == aListID) {
michael@0 4757 return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
michael@0 4758 }
michael@0 4759 else if (kFloatList == aListID) {
michael@0 4760 mFloats.InsertFrames(this, aPrevFrame, aFrameList);
michael@0 4761 return NS_OK;
michael@0 4762 }
michael@0 4763 else if (kNoReflowPrincipalList != aListID) {
michael@0 4764 NS_ERROR("unexpected child list");
michael@0 4765 return NS_ERROR_INVALID_ARG;
michael@0 4766 }
michael@0 4767 }
michael@0 4768
michael@0 4769 #ifdef NOISY_REFLOW_REASON
michael@0 4770 ListTag(stdout);
michael@0 4771 printf(": insert ");
michael@0 4772 nsFrame::ListTag(stdout, aFrameList);
michael@0 4773 if (aPrevFrame) {
michael@0 4774 printf(" after ");
michael@0 4775 nsFrame::ListTag(stdout, aPrevFrame);
michael@0 4776 }
michael@0 4777 printf("\n");
michael@0 4778 #endif
michael@0 4779
michael@0 4780 AddFrames(aFrameList, aPrevFrame);
michael@0 4781
michael@0 4782 if (aListID != kNoReflowPrincipalList)
michael@0 4783 PresContext()->PresShell()->
michael@0 4784 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 4785 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
michael@0 4786 return NS_OK;
michael@0 4787 }
michael@0 4788
michael@0 4789 static bool
michael@0 4790 ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
michael@0 4791 {
michael@0 4792 nsIAtom* type = aLastFrame->GetType();
michael@0 4793 if (type == nsGkAtoms::brFrame) {
michael@0 4794 return true;
michael@0 4795 }
michael@0 4796 // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
michael@0 4797 if (type == nsGkAtoms::textFrame &&
michael@0 4798 !(aLastFrame->GetStateBits() & TEXT_OFFSETS_NEED_FIXING)) {
michael@0 4799 return aLastFrame->HasSignificantTerminalNewline();
michael@0 4800 }
michael@0 4801 return false;
michael@0 4802 }
michael@0 4803
michael@0 4804 void
michael@0 4805 nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
michael@0 4806 {
michael@0 4807 // Clear our line cursor, since our lines may change.
michael@0 4808 ClearLineCursor();
michael@0 4809
michael@0 4810 if (aFrameList.IsEmpty()) {
michael@0 4811 return;
michael@0 4812 }
michael@0 4813
michael@0 4814 // If we're inserting at the beginning of our list and we have an
michael@0 4815 // inside bullet, insert after that bullet.
michael@0 4816 if (!aPrevSibling && HasInsideBullet()) {
michael@0 4817 aPrevSibling = GetInsideBullet();
michael@0 4818 }
michael@0 4819
michael@0 4820 // Attempt to find the line that contains the previous sibling
michael@0 4821 FrameLines* overflowLines;
michael@0 4822 nsLineList* lineList = &mLines;
michael@0 4823 nsLineList::iterator prevSibLine = lineList->end();
michael@0 4824 int32_t prevSiblingIndex = -1;
michael@0 4825 if (aPrevSibling) {
michael@0 4826 // XXX_perf This is technically O(N^2) in some cases, but by using
michael@0 4827 // RFind instead of Find, we make it O(N) in the most common case,
michael@0 4828 // which is appending content.
michael@0 4829
michael@0 4830 // Find the line that contains the previous sibling
michael@0 4831 if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
michael@0 4832 prevSibLine, mFrames.LastChild(),
michael@0 4833 &prevSiblingIndex)) {
michael@0 4834 // Not in mLines - try overflow lines.
michael@0 4835 overflowLines = GetOverflowLines();
michael@0 4836 lineList = overflowLines ? &overflowLines->mLines : nullptr;
michael@0 4837 if (overflowLines) {
michael@0 4838 prevSibLine = overflowLines->mLines.end();
michael@0 4839 prevSiblingIndex = -1;
michael@0 4840 if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
michael@0 4841 prevSibLine,
michael@0 4842 overflowLines->mFrames.LastChild(),
michael@0 4843 &prevSiblingIndex)) {
michael@0 4844 lineList = nullptr;
michael@0 4845 }
michael@0 4846 }
michael@0 4847 if (!lineList) {
michael@0 4848 // Note: defensive code! RFindLineContaining must not return
michael@0 4849 // false in this case, so if it does...
michael@0 4850 NS_NOTREACHED("prev sibling not in line list");
michael@0 4851 lineList = &mLines;
michael@0 4852 aPrevSibling = nullptr;
michael@0 4853 prevSibLine = lineList->end();
michael@0 4854 }
michael@0 4855 }
michael@0 4856 }
michael@0 4857
michael@0 4858 // Find the frame following aPrevSibling so that we can join up the
michael@0 4859 // two lists of frames.
michael@0 4860 if (aPrevSibling) {
michael@0 4861 // Split line containing aPrevSibling in two if the insertion
michael@0 4862 // point is somewhere in the middle of the line.
michael@0 4863 int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
michael@0 4864 if (rem) {
michael@0 4865 // Split the line in two where the frame(s) are being inserted.
michael@0 4866 nsLineBox* line = NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
michael@0 4867 lineList->after_insert(prevSibLine, line);
michael@0 4868 // Mark prevSibLine dirty and as needing textrun invalidation, since
michael@0 4869 // we may be breaking up text in the line. Its previous line may also
michael@0 4870 // need to be invalidated because it may be able to pull some text up.
michael@0 4871 MarkLineDirty(prevSibLine, lineList);
michael@0 4872 // The new line will also need its textruns recomputed because of the
michael@0 4873 // frame changes.
michael@0 4874 line->MarkDirty();
michael@0 4875 line->SetInvalidateTextRuns(true);
michael@0 4876 }
michael@0 4877 }
michael@0 4878 else if (! lineList->empty()) {
michael@0 4879 lineList->front()->MarkDirty();
michael@0 4880 lineList->front()->SetInvalidateTextRuns(true);
michael@0 4881 }
michael@0 4882 nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames;
michael@0 4883 const nsFrameList::Slice& newFrames =
michael@0 4884 frames.InsertFrames(nullptr, aPrevSibling, aFrameList);
michael@0 4885
michael@0 4886 // Walk through the new frames being added and update the line data
michael@0 4887 // structures to fit.
michael@0 4888 for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
michael@0 4889 nsIFrame* newFrame = e.get();
michael@0 4890 NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
michael@0 4891 "Unexpected aPrevSibling");
michael@0 4892 NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
michael@0 4893 (!newFrame->IsAbsolutelyPositioned() &&
michael@0 4894 !newFrame->IsFloating()),
michael@0 4895 "Placeholders should not float or be positioned");
michael@0 4896
michael@0 4897 bool isBlock = newFrame->IsBlockOutside();
michael@0 4898
michael@0 4899 // If the frame is a block frame, or if there is no previous line or if the
michael@0 4900 // previous line is a block line we need to make a new line. We also make
michael@0 4901 // a new line, as an optimization, in the two cases we know we'll need it:
michael@0 4902 // if the previous line ended with a <br>, or if it has significant whitespace
michael@0 4903 // and ended in a newline.
michael@0 4904 if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
michael@0 4905 (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
michael@0 4906 // Create a new line for the frame and add its line to the line
michael@0 4907 // list.
michael@0 4908 nsLineBox* line = NewLineBox(newFrame, isBlock);
michael@0 4909 if (prevSibLine != lineList->end()) {
michael@0 4910 // Append new line after prevSibLine
michael@0 4911 lineList->after_insert(prevSibLine, line);
michael@0 4912 ++prevSibLine;
michael@0 4913 }
michael@0 4914 else {
michael@0 4915 // New line is going before the other lines
michael@0 4916 lineList->push_front(line);
michael@0 4917 prevSibLine = lineList->begin();
michael@0 4918 }
michael@0 4919 }
michael@0 4920 else {
michael@0 4921 prevSibLine->NoteFrameAdded(newFrame);
michael@0 4922 // We're adding inline content to prevSibLine, so we need to mark it
michael@0 4923 // dirty, ensure its textruns are recomputed, and possibly do the same
michael@0 4924 // to its previous line since that line may be able to pull content up.
michael@0 4925 MarkLineDirty(prevSibLine, lineList);
michael@0 4926 }
michael@0 4927
michael@0 4928 aPrevSibling = newFrame;
michael@0 4929 }
michael@0 4930
michael@0 4931 #ifdef DEBUG
michael@0 4932 MOZ_ASSERT(aFrameList.IsEmpty());
michael@0 4933 VerifyLines(true);
michael@0 4934 #endif
michael@0 4935 }
michael@0 4936
michael@0 4937 void
michael@0 4938 nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat)
michael@0 4939 {
michael@0 4940 // Find which line contains the float, so we can update
michael@0 4941 // the float cache.
michael@0 4942 line_iterator line = begin_lines(), line_end = end_lines();
michael@0 4943 for ( ; line != line_end; ++line) {
michael@0 4944 if (line->IsInline() && line->RemoveFloat(aFloat)) {
michael@0 4945 break;
michael@0 4946 }
michael@0 4947 }
michael@0 4948 }
michael@0 4949
michael@0 4950 void
michael@0 4951 nsBlockFrame::RemoveFloat(nsIFrame* aFloat)
michael@0 4952 {
michael@0 4953 #ifdef DEBUG
michael@0 4954 // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
michael@0 4955 // frame list properties.
michael@0 4956 if (!mFloats.ContainsFrame(aFloat)) {
michael@0 4957 MOZ_ASSERT((GetOverflowOutOfFlows() &&
michael@0 4958 GetOverflowOutOfFlows()->ContainsFrame(aFloat)) ||
michael@0 4959 (GetPushedFloats() &&
michael@0 4960 GetPushedFloats()->ContainsFrame(aFloat)),
michael@0 4961 "aFloat is not our child or on an unexpected frame list");
michael@0 4962 }
michael@0 4963 #endif
michael@0 4964
michael@0 4965 if (mFloats.StartRemoveFrame(aFloat)) {
michael@0 4966 return;
michael@0 4967 }
michael@0 4968
michael@0 4969 nsFrameList* list = GetPushedFloats();
michael@0 4970 if (list && list->ContinueRemoveFrame(aFloat)) {
michael@0 4971 #if 0
michael@0 4972 // XXXmats not yet - need to investigate nsBlockReflowState::mPushedFloats
michael@0 4973 // first so we don't leave it pointing to a deleted list.
michael@0 4974 if (list->IsEmpty()) {
michael@0 4975 delete RemovePushedFloats();
michael@0 4976 }
michael@0 4977 #endif
michael@0 4978 return;
michael@0 4979 }
michael@0 4980
michael@0 4981 {
michael@0 4982 nsAutoOOFFrameList oofs(this);
michael@0 4983 if (oofs.mList.ContinueRemoveFrame(aFloat)) {
michael@0 4984 return;
michael@0 4985 }
michael@0 4986 }
michael@0 4987 }
michael@0 4988
michael@0 4989 static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock)
michael@0 4990 {
michael@0 4991 nsBlockFrame* blockWithFloatMgr = aBlock;
michael@0 4992 while (!(blockWithFloatMgr->GetStateBits() & NS_BLOCK_FLOAT_MGR)) {
michael@0 4993 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(blockWithFloatMgr->GetParent());
michael@0 4994 if (!bf) {
michael@0 4995 break;
michael@0 4996 }
michael@0 4997 blockWithFloatMgr = bf;
michael@0 4998 }
michael@0 4999
michael@0 5000 // Mark every line at and below the line where the float was
michael@0 5001 // dirty, and mark their lines dirty too. We could probably do
michael@0 5002 // something more efficient --- e.g., just dirty the lines that intersect
michael@0 5003 // the float vertically.
michael@0 5004 MarkAllDescendantLinesDirty(blockWithFloatMgr);
michael@0 5005 }
michael@0 5006
michael@0 5007 /**
michael@0 5008 * Returns true if aFrame is a block that has one or more float children.
michael@0 5009 */
michael@0 5010 static bool BlockHasAnyFloats(nsIFrame* aFrame)
michael@0 5011 {
michael@0 5012 nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
michael@0 5013 if (!block)
michael@0 5014 return false;
michael@0 5015 if (block->GetFirstChild(nsIFrame::kFloatList))
michael@0 5016 return true;
michael@0 5017
michael@0 5018 nsLineList::iterator line = block->begin_lines();
michael@0 5019 nsLineList::iterator endLine = block->end_lines();
michael@0 5020 while (line != endLine) {
michael@0 5021 if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild))
michael@0 5022 return true;
michael@0 5023 ++line;
michael@0 5024 }
michael@0 5025 return false;
michael@0 5026 }
michael@0 5027
michael@0 5028 nsresult
michael@0 5029 nsBlockFrame::RemoveFrame(ChildListID aListID,
michael@0 5030 nsIFrame* aOldFrame)
michael@0 5031 {
michael@0 5032 nsresult rv = NS_OK;
michael@0 5033
michael@0 5034 #ifdef NOISY_REFLOW_REASON
michael@0 5035 ListTag(stdout);
michael@0 5036 printf(": remove ");
michael@0 5037 nsFrame::ListTag(stdout, aOldFrame);
michael@0 5038 printf("\n");
michael@0 5039 #endif
michael@0 5040
michael@0 5041 if (aListID == kPrincipalList) {
michael@0 5042 bool hasFloats = BlockHasAnyFloats(aOldFrame);
michael@0 5043 rv = DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
michael@0 5044 if (hasFloats) {
michael@0 5045 MarkSameFloatManagerLinesDirty(this);
michael@0 5046 }
michael@0 5047 }
michael@0 5048 else if (kAbsoluteList == aListID) {
michael@0 5049 nsContainerFrame::RemoveFrame(aListID, aOldFrame);
michael@0 5050 return NS_OK;
michael@0 5051 }
michael@0 5052 else if (kFloatList == aListID) {
michael@0 5053 // Make sure to mark affected lines dirty for the float frame
michael@0 5054 // we are removing; this way is a bit messy, but so is the rest of the code.
michael@0 5055 // See bug 390762.
michael@0 5056 NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
michael@0 5057 "RemoveFrame should not be called on pushed floats.");
michael@0 5058 for (nsIFrame* f = aOldFrame;
michael@0 5059 f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
michael@0 5060 f = f->GetNextContinuation()) {
michael@0 5061 MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent()));
michael@0 5062 }
michael@0 5063 DoRemoveOutOfFlowFrame(aOldFrame);
michael@0 5064 }
michael@0 5065 else if (kNoReflowPrincipalList == aListID) {
michael@0 5066 // Skip the call to |FrameNeedsReflow| below by returning now.
michael@0 5067 return DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
michael@0 5068 }
michael@0 5069 else {
michael@0 5070 NS_ERROR("unexpected child list");
michael@0 5071 rv = NS_ERROR_INVALID_ARG;
michael@0 5072 }
michael@0 5073
michael@0 5074 if (NS_SUCCEEDED(rv)) {
michael@0 5075 PresContext()->PresShell()->
michael@0 5076 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 5077 NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
michael@0 5078 }
michael@0 5079 return rv;
michael@0 5080 }
michael@0 5081
michael@0 5082 void
michael@0 5083 nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
michael@0 5084 {
michael@0 5085 // The containing block is always the parent of aFrame.
michael@0 5086 nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
michael@0 5087
michael@0 5088 // Remove aFrame from the appropriate list.
michael@0 5089 if (aFrame->IsAbsolutelyPositioned()) {
michael@0 5090 // This also deletes the next-in-flows
michael@0 5091 block->GetAbsoluteContainingBlock()->RemoveFrame(block,
michael@0 5092 kAbsoluteList,
michael@0 5093 aFrame);
michael@0 5094 }
michael@0 5095 else {
michael@0 5096 // First remove aFrame's next-in-flows.
michael@0 5097 nsIFrame* nif = aFrame->GetNextInFlow();
michael@0 5098 if (nif) {
michael@0 5099 static_cast<nsContainerFrame*>(nif->GetParent())
michael@0 5100 ->DeleteNextInFlowChild(nif, false);
michael@0 5101 }
michael@0 5102 // Now remove aFrame from its child list and Destroy it.
michael@0 5103 block->RemoveFloatFromFloatCache(aFrame);
michael@0 5104 block->RemoveFloat(aFrame);
michael@0 5105 aFrame->Destroy();
michael@0 5106 }
michael@0 5107 }
michael@0 5108
michael@0 5109 /**
michael@0 5110 * This helps us iterate over the list of all normal + overflow lines
michael@0 5111 */
michael@0 5112 void
michael@0 5113 nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
michael@0 5114 nsLineList::iterator* aStartIterator,
michael@0 5115 nsLineList::iterator* aEndIterator,
michael@0 5116 bool* aInOverflowLines,
michael@0 5117 FrameLines** aOverflowLines)
michael@0 5118 {
michael@0 5119 if (*aIterator == *aEndIterator) {
michael@0 5120 if (!*aInOverflowLines) {
michael@0 5121 // Try the overflow lines
michael@0 5122 *aInOverflowLines = true;
michael@0 5123 FrameLines* lines = GetOverflowLines();
michael@0 5124 if (lines) {
michael@0 5125 *aStartIterator = lines->mLines.begin();
michael@0 5126 *aIterator = *aStartIterator;
michael@0 5127 *aEndIterator = lines->mLines.end();
michael@0 5128 *aOverflowLines = lines;
michael@0 5129 }
michael@0 5130 }
michael@0 5131 }
michael@0 5132 }
michael@0 5133
michael@0 5134 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
michael@0 5135 line_iterator aLine)
michael@0 5136 : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines)
michael@0 5137 {
michael@0 5138 // This will assert if aLine isn't in mLines of aFrame:
michael@0 5139 DebugOnly<bool> check = aLine == mFrame->begin_lines();
michael@0 5140 }
michael@0 5141
michael@0 5142 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
michael@0 5143 line_iterator aLine, bool aInOverflow)
michael@0 5144 : mFrame(aFrame), mLine(aLine),
michael@0 5145 mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines
michael@0 5146 : &aFrame->mLines)
michael@0 5147 {
michael@0 5148 }
michael@0 5149
michael@0 5150 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
michael@0 5151 bool* aFoundValidLine)
michael@0 5152 : mFrame(aFrame), mLineList(&aFrame->mLines)
michael@0 5153 {
michael@0 5154 mLine = aFrame->begin_lines();
michael@0 5155 *aFoundValidLine = FindValidLine();
michael@0 5156 }
michael@0 5157
michael@0 5158 static nsIFrame*
michael@0 5159 FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame)
michael@0 5160 {
michael@0 5161 NS_ASSERTION(aFrame, "must have frame");
michael@0 5162 nsIFrame* child;
michael@0 5163 while (true) {
michael@0 5164 nsIFrame* block = aFrame;
michael@0 5165 do {
michael@0 5166 child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
michael@0 5167 if (child)
michael@0 5168 break;
michael@0 5169 block = block->GetNextContinuation();
michael@0 5170 } while (block);
michael@0 5171 if (!child)
michael@0 5172 return nullptr;
michael@0 5173 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
michael@0 5174 break;
michael@0 5175 aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child);
michael@0 5176 }
michael@0 5177
michael@0 5178 return child;
michael@0 5179 }
michael@0 5180
michael@0 5181 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
michael@0 5182 nsIFrame* aFindFrame, bool* aFoundValidLine)
michael@0 5183 : mFrame(aFrame), mLineList(&aFrame->mLines)
michael@0 5184 {
michael@0 5185 *aFoundValidLine = false;
michael@0 5186
michael@0 5187 nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
michael@0 5188 if (!child)
michael@0 5189 return;
michael@0 5190
michael@0 5191 // Try to use the cursor if it exists, otherwise fall back to the first line
michael@0 5192 nsLineBox* cursor = aFrame->GetLineCursor();
michael@0 5193 if (!cursor) {
michael@0 5194 line_iterator iter = aFrame->begin_lines();
michael@0 5195 if (iter != aFrame->end_lines()) {
michael@0 5196 cursor = iter;
michael@0 5197 }
michael@0 5198 }
michael@0 5199
michael@0 5200 if (cursor) {
michael@0 5201 // Perform a simultaneous forward and reverse search starting from the
michael@0 5202 // line cursor.
michael@0 5203 nsBlockFrame::line_iterator line = aFrame->line(cursor);
michael@0 5204 nsBlockFrame::reverse_line_iterator rline = aFrame->rline(cursor);
michael@0 5205 nsBlockFrame::line_iterator line_end = aFrame->end_lines();
michael@0 5206 nsBlockFrame::reverse_line_iterator rline_end = aFrame->rend_lines();
michael@0 5207 // rline is positioned on the line containing 'cursor', so it's not
michael@0 5208 // rline_end. So we can safely increment it (i.e. move it to one line
michael@0 5209 // earlier) to start searching there.
michael@0 5210 ++rline;
michael@0 5211 while (line != line_end || rline != rline_end) {
michael@0 5212 if (line != line_end) {
michael@0 5213 if (line->Contains(child)) {
michael@0 5214 *aFoundValidLine = true;
michael@0 5215 mLine = line;
michael@0 5216 return;
michael@0 5217 }
michael@0 5218 ++line;
michael@0 5219 }
michael@0 5220 if (rline != rline_end) {
michael@0 5221 if (rline->Contains(child)) {
michael@0 5222 *aFoundValidLine = true;
michael@0 5223 mLine = rline;
michael@0 5224 return;
michael@0 5225 }
michael@0 5226 ++rline;
michael@0 5227 }
michael@0 5228 }
michael@0 5229 // Didn't find the line
michael@0 5230 }
michael@0 5231
michael@0 5232 // If we reach here, it means that we have not been able to find the
michael@0 5233 // desired frame in our in-flow lines. So we should start looking at
michael@0 5234 // our overflow lines. In order to do that, we set mLine to the end
michael@0 5235 // iterator so that FindValidLine starts to look at overflow lines,
michael@0 5236 // if any.
michael@0 5237
michael@0 5238 mLine = aFrame->end_lines();
michael@0 5239
michael@0 5240 if (!FindValidLine())
michael@0 5241 return;
michael@0 5242
michael@0 5243 do {
michael@0 5244 if (mLine->Contains(child)) {
michael@0 5245 *aFoundValidLine = true;
michael@0 5246 return;
michael@0 5247 }
michael@0 5248 } while (Next());
michael@0 5249 }
michael@0 5250
michael@0 5251 nsBlockFrame::line_iterator
michael@0 5252 nsBlockInFlowLineIterator::End()
michael@0 5253 {
michael@0 5254 return mLineList->end();
michael@0 5255 }
michael@0 5256
michael@0 5257 bool
michael@0 5258 nsBlockInFlowLineIterator::IsLastLineInList()
michael@0 5259 {
michael@0 5260 line_iterator end = End();
michael@0 5261 return mLine != end && mLine.next() == end;
michael@0 5262 }
michael@0 5263
michael@0 5264 bool
michael@0 5265 nsBlockInFlowLineIterator::Next()
michael@0 5266 {
michael@0 5267 ++mLine;
michael@0 5268 return FindValidLine();
michael@0 5269 }
michael@0 5270
michael@0 5271 bool
michael@0 5272 nsBlockInFlowLineIterator::Prev()
michael@0 5273 {
michael@0 5274 line_iterator begin = mLineList->begin();
michael@0 5275 if (mLine != begin) {
michael@0 5276 --mLine;
michael@0 5277 return true;
michael@0 5278 }
michael@0 5279 bool currentlyInOverflowLines = GetInOverflow();
michael@0 5280 while (true) {
michael@0 5281 if (currentlyInOverflowLines) {
michael@0 5282 mLineList = &mFrame->mLines;
michael@0 5283 mLine = mLineList->end();
michael@0 5284 if (mLine != mLineList->begin()) {
michael@0 5285 --mLine;
michael@0 5286 return true;
michael@0 5287 }
michael@0 5288 } else {
michael@0 5289 mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
michael@0 5290 if (!mFrame)
michael@0 5291 return false;
michael@0 5292 nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
michael@0 5293 if (overflowLines) {
michael@0 5294 mLineList = &overflowLines->mLines;
michael@0 5295 mLine = mLineList->end();
michael@0 5296 NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?");
michael@0 5297 --mLine;
michael@0 5298 return true;
michael@0 5299 }
michael@0 5300 }
michael@0 5301 currentlyInOverflowLines = !currentlyInOverflowLines;
michael@0 5302 }
michael@0 5303 }
michael@0 5304
michael@0 5305 bool
michael@0 5306 nsBlockInFlowLineIterator::FindValidLine()
michael@0 5307 {
michael@0 5308 line_iterator end = mLineList->end();
michael@0 5309 if (mLine != end)
michael@0 5310 return true;
michael@0 5311 bool currentlyInOverflowLines = GetInOverflow();
michael@0 5312 while (true) {
michael@0 5313 if (currentlyInOverflowLines) {
michael@0 5314 mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
michael@0 5315 if (!mFrame)
michael@0 5316 return false;
michael@0 5317 mLineList = &mFrame->mLines;
michael@0 5318 mLine = mLineList->begin();
michael@0 5319 if (mLine != mLineList->end())
michael@0 5320 return true;
michael@0 5321 } else {
michael@0 5322 nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
michael@0 5323 if (overflowLines) {
michael@0 5324 mLineList = &overflowLines->mLines;
michael@0 5325 mLine = mLineList->begin();
michael@0 5326 NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?");
michael@0 5327 return true;
michael@0 5328 }
michael@0 5329 }
michael@0 5330 currentlyInOverflowLines = !currentlyInOverflowLines;
michael@0 5331 }
michael@0 5332 }
michael@0 5333
michael@0 5334 static nsresult RemoveBlockChild(nsIFrame* aFrame,
michael@0 5335 bool aRemoveOnlyFluidContinuations)
michael@0 5336 {
michael@0 5337 if (!aFrame)
michael@0 5338 return NS_OK;
michael@0 5339
michael@0 5340 nsBlockFrame* nextBlock = nsLayoutUtils::GetAsBlock(aFrame->GetParent());
michael@0 5341 NS_ASSERTION(nextBlock,
michael@0 5342 "Our child's continuation's parent is not a block?");
michael@0 5343 return nextBlock->DoRemoveFrame(aFrame,
michael@0 5344 (aRemoveOnlyFluidContinuations ? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS));
michael@0 5345 }
michael@0 5346
michael@0 5347 // This function removes aDeletedFrame and all its continuations. It
michael@0 5348 // is optimized for deleting a whole series of frames. The easy
michael@0 5349 // implementation would invoke itself recursively on
michael@0 5350 // aDeletedFrame->GetNextContinuation, then locate the line containing
michael@0 5351 // aDeletedFrame and remove aDeletedFrame from that line. But here we
michael@0 5352 // start by locating aDeletedFrame and then scanning from that point
michael@0 5353 // on looking for continuations.
michael@0 5354 nsresult
michael@0 5355 nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags)
michael@0 5356 {
michael@0 5357 // Clear our line cursor, since our lines may change.
michael@0 5358 ClearLineCursor();
michael@0 5359
michael@0 5360 if (aDeletedFrame->GetStateBits() &
michael@0 5361 (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
michael@0 5362 if (!aDeletedFrame->GetPrevInFlow()) {
michael@0 5363 NS_ASSERTION(aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
michael@0 5364 "Expected out-of-flow frame");
michael@0 5365 DoRemoveOutOfFlowFrame(aDeletedFrame);
michael@0 5366 }
michael@0 5367 else {
michael@0 5368 nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame,
michael@0 5369 (aFlags & FRAMES_ARE_EMPTY) != 0);
michael@0 5370 }
michael@0 5371 return NS_OK;
michael@0 5372 }
michael@0 5373
michael@0 5374 // Find the line that contains deletedFrame
michael@0 5375 nsLineList::iterator line_start = mLines.begin(),
michael@0 5376 line_end = mLines.end();
michael@0 5377 nsLineList::iterator line = line_start;
michael@0 5378 FrameLines* overflowLines = nullptr;
michael@0 5379 bool searchingOverflowList = false;
michael@0 5380 // Make sure we look in the overflow lines even if the normal line
michael@0 5381 // list is empty
michael@0 5382 TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
michael@0 5383 &overflowLines);
michael@0 5384 while (line != line_end) {
michael@0 5385 if (line->Contains(aDeletedFrame)) {
michael@0 5386 break;
michael@0 5387 }
michael@0 5388 ++line;
michael@0 5389 TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
michael@0 5390 &overflowLines);
michael@0 5391 }
michael@0 5392
michael@0 5393 if (line == line_end) {
michael@0 5394 NS_ERROR("can't find deleted frame in lines");
michael@0 5395 return NS_ERROR_FAILURE;
michael@0 5396 }
michael@0 5397
michael@0 5398 if (!(aFlags & FRAMES_ARE_EMPTY)) {
michael@0 5399 if (line != line_start) {
michael@0 5400 line.prev()->MarkDirty();
michael@0 5401 line.prev()->SetInvalidateTextRuns(true);
michael@0 5402 }
michael@0 5403 else if (searchingOverflowList && !mLines.empty()) {
michael@0 5404 mLines.back()->MarkDirty();
michael@0 5405 mLines.back()->SetInvalidateTextRuns(true);
michael@0 5406 }
michael@0 5407 }
michael@0 5408
michael@0 5409 while (line != line_end && aDeletedFrame) {
michael@0 5410 NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
michael@0 5411 NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
michael@0 5412
michael@0 5413 if (!(aFlags & FRAMES_ARE_EMPTY)) {
michael@0 5414 line->MarkDirty();
michael@0 5415 line->SetInvalidateTextRuns(true);
michael@0 5416 }
michael@0 5417
michael@0 5418 // If the frame being deleted is the last one on the line then
michael@0 5419 // optimize away the line->Contains(next-in-flow) call below.
michael@0 5420 bool isLastFrameOnLine = 1 == line->GetChildCount();
michael@0 5421 if (!isLastFrameOnLine) {
michael@0 5422 line_iterator next = line.next();
michael@0 5423 nsIFrame* lastFrame = next != line_end ?
michael@0 5424 next->mFirstChild->GetPrevSibling() :
michael@0 5425 (searchingOverflowList ? overflowLines->mFrames.LastChild() :
michael@0 5426 mFrames.LastChild());
michael@0 5427 NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
michael@0 5428 "unexpected line frames");
michael@0 5429 isLastFrameOnLine = lastFrame == aDeletedFrame;
michael@0 5430 }
michael@0 5431
michael@0 5432 // Remove aDeletedFrame from the line
michael@0 5433 if (line->mFirstChild == aDeletedFrame) {
michael@0 5434 // We should be setting this to null if aDeletedFrame
michael@0 5435 // is the only frame on the line. HOWEVER in that case
michael@0 5436 // we will be removing the line anyway, see below.
michael@0 5437 line->mFirstChild = aDeletedFrame->GetNextSibling();
michael@0 5438 }
michael@0 5439
michael@0 5440 // Hmm, this won't do anything if we're removing a frame in the first
michael@0 5441 // overflow line... Hopefully doesn't matter
michael@0 5442 --line;
michael@0 5443 if (line != line_end && !line->IsBlock()) {
michael@0 5444 // Since we just removed a frame that follows some inline
michael@0 5445 // frames, we need to reflow the previous line.
michael@0 5446 line->MarkDirty();
michael@0 5447 }
michael@0 5448 ++line;
michael@0 5449
michael@0 5450 // Take aDeletedFrame out of the sibling list. Note that
michael@0 5451 // prevSibling will only be nullptr when we are deleting the very
michael@0 5452 // first frame in the main or overflow list.
michael@0 5453 if (searchingOverflowList) {
michael@0 5454 overflowLines->mFrames.RemoveFrame(aDeletedFrame);
michael@0 5455 } else {
michael@0 5456 mFrames.RemoveFrame(aDeletedFrame);
michael@0 5457 }
michael@0 5458
michael@0 5459 // Update the child count of the line to be accurate
michael@0 5460 line->NoteFrameRemoved(aDeletedFrame);
michael@0 5461
michael@0 5462 // Destroy frame; capture its next continuation first in case we need
michael@0 5463 // to destroy that too.
michael@0 5464 nsIFrame* deletedNextContinuation = (aFlags & REMOVE_FIXED_CONTINUATIONS) ?
michael@0 5465 aDeletedFrame->GetNextContinuation() : aDeletedFrame->GetNextInFlow();
michael@0 5466 #ifdef NOISY_REMOVE_FRAME
michael@0 5467 printf("DoRemoveFrame: %s line=%p frame=",
michael@0 5468 searchingOverflowList?"overflow":"normal", line.get());
michael@0 5469 nsFrame::ListTag(stdout, aDeletedFrame);
michael@0 5470 printf(" prevSibling=%p deletedNextContinuation=%p\n",
michael@0 5471 aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
michael@0 5472 #endif
michael@0 5473
michael@0 5474 // If next-in-flow is an overflow container, must remove it first.
michael@0 5475 if (deletedNextContinuation &&
michael@0 5476 deletedNextContinuation->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
michael@0 5477 static_cast<nsContainerFrame*>(deletedNextContinuation->GetParent())
michael@0 5478 ->DeleteNextInFlowChild(deletedNextContinuation, false);
michael@0 5479 deletedNextContinuation = nullptr;
michael@0 5480 }
michael@0 5481
michael@0 5482 aDeletedFrame->Destroy();
michael@0 5483 aDeletedFrame = deletedNextContinuation;
michael@0 5484
michael@0 5485 bool haveAdvancedToNextLine = false;
michael@0 5486 // If line is empty, remove it now.
michael@0 5487 if (0 == line->GetChildCount()) {
michael@0 5488 #ifdef NOISY_REMOVE_FRAME
michael@0 5489 printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
michael@0 5490 searchingOverflowList?"overflow":"normal", line.get());
michael@0 5491 #endif
michael@0 5492 nsLineBox *cur = line;
michael@0 5493 if (!searchingOverflowList) {
michael@0 5494 line = mLines.erase(line);
michael@0 5495 // Invalidate the space taken up by the line.
michael@0 5496 // XXX We need to do this if we're removing a frame as a result of
michael@0 5497 // a call to RemoveFrame(), but we may not need to do this in all
michael@0 5498 // cases...
michael@0 5499 #ifdef NOISY_BLOCK_INVALIDATE
michael@0 5500 nsRect visOverflow(cur->GetVisualOverflowArea());
michael@0 5501 printf("%p invalidate 10 (%d, %d, %d, %d)\n",
michael@0 5502 this, visOverflow.x, visOverflow.y,
michael@0 5503 visOverflow.width, visOverflow.height);
michael@0 5504 #endif
michael@0 5505 } else {
michael@0 5506 line = overflowLines->mLines.erase(line);
michael@0 5507 if (overflowLines->mLines.empty()) {
michael@0 5508 DestroyOverflowLines();
michael@0 5509 overflowLines = nullptr;
michael@0 5510 // We just invalidated our iterators. Since we were in
michael@0 5511 // the overflow lines list, which is now empty, set them
michael@0 5512 // so we're at the end of the regular line list.
michael@0 5513 line_start = mLines.begin();
michael@0 5514 line_end = mLines.end();
michael@0 5515 line = line_end;
michael@0 5516 }
michael@0 5517 }
michael@0 5518 FreeLineBox(cur);
michael@0 5519
michael@0 5520 // If we're removing a line, ReflowDirtyLines isn't going to
michael@0 5521 // know that it needs to slide lines unless something is marked
michael@0 5522 // dirty. So mark the previous margin of the next line dirty if
michael@0 5523 // there is one.
michael@0 5524 if (line != line_end) {
michael@0 5525 line->MarkPreviousMarginDirty();
michael@0 5526 }
michael@0 5527 haveAdvancedToNextLine = true;
michael@0 5528 } else {
michael@0 5529 // Make the line that just lost a frame dirty, and advance to
michael@0 5530 // the next line.
michael@0 5531 if (!deletedNextContinuation || isLastFrameOnLine ||
michael@0 5532 !line->Contains(deletedNextContinuation)) {
michael@0 5533 line->MarkDirty();
michael@0 5534 ++line;
michael@0 5535 haveAdvancedToNextLine = true;
michael@0 5536 }
michael@0 5537 }
michael@0 5538
michael@0 5539 if (deletedNextContinuation) {
michael@0 5540 // See if we should keep looking in the current flow's line list.
michael@0 5541 if (deletedNextContinuation->GetParent() != this) {
michael@0 5542 // The deceased frames continuation is not a child of the
michael@0 5543 // current block. So break out of the loop so that we advance
michael@0 5544 // to the next parent.
michael@0 5545 //
michael@0 5546 // If we have a continuation in a different block then all bets are
michael@0 5547 // off regarding whether we are deleting frames without actual content,
michael@0 5548 // so don't propagate FRAMES_ARE_EMPTY any further.
michael@0 5549 aFlags &= ~FRAMES_ARE_EMPTY;
michael@0 5550 break;
michael@0 5551 }
michael@0 5552
michael@0 5553 // If we advanced to the next line then check if we should switch to the
michael@0 5554 // overflow line list.
michael@0 5555 if (haveAdvancedToNextLine) {
michael@0 5556 if (line != line_end && !searchingOverflowList &&
michael@0 5557 !line->Contains(deletedNextContinuation)) {
michael@0 5558 // We have advanced to the next *normal* line but the next-in-flow
michael@0 5559 // is not there - force a switch to the overflow line list.
michael@0 5560 line = line_end;
michael@0 5561 }
michael@0 5562
michael@0 5563 TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
michael@0 5564 &overflowLines);
michael@0 5565 #ifdef NOISY_REMOVE_FRAME
michael@0 5566 printf("DoRemoveFrame: now on %s line=%p\n",
michael@0 5567 searchingOverflowList?"overflow":"normal", line.get());
michael@0 5568 #endif
michael@0 5569 }
michael@0 5570 }
michael@0 5571 }
michael@0 5572
michael@0 5573 if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
michael@0 5574 line.next()->MarkDirty();
michael@0 5575 line.next()->SetInvalidateTextRuns(true);
michael@0 5576 }
michael@0 5577
michael@0 5578 #ifdef DEBUG
michael@0 5579 VerifyLines(true);
michael@0 5580 VerifyOverflowSituation();
michael@0 5581 #endif
michael@0 5582
michael@0 5583 // Advance to next flow block if the frame has more continuations
michael@0 5584 return RemoveBlockChild(aDeletedFrame, !(aFlags & REMOVE_FIXED_CONTINUATIONS));
michael@0 5585 }
michael@0 5586
michael@0 5587 static bool
michael@0 5588 FindBlockLineFor(nsIFrame* aChild,
michael@0 5589 nsLineList::iterator aBegin,
michael@0 5590 nsLineList::iterator aEnd,
michael@0 5591 nsLineList::iterator* aResult)
michael@0 5592 {
michael@0 5593 MOZ_ASSERT(aChild->IsBlockOutside());
michael@0 5594 for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
michael@0 5595 MOZ_ASSERT(line->GetChildCount() > 0);
michael@0 5596 if (line->IsBlock() && line->mFirstChild == aChild) {
michael@0 5597 MOZ_ASSERT(line->GetChildCount() == 1);
michael@0 5598 *aResult = line;
michael@0 5599 return true;
michael@0 5600 }
michael@0 5601 }
michael@0 5602 return false;
michael@0 5603 }
michael@0 5604
michael@0 5605 static bool
michael@0 5606 FindInlineLineFor(nsIFrame* aChild,
michael@0 5607 const nsFrameList& aFrameList,
michael@0 5608 nsLineList::iterator aBegin,
michael@0 5609 nsLineList::iterator aEnd,
michael@0 5610 nsLineList::iterator* aResult)
michael@0 5611 {
michael@0 5612 MOZ_ASSERT(!aChild->IsBlockOutside());
michael@0 5613 for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
michael@0 5614 MOZ_ASSERT(line->GetChildCount() > 0);
michael@0 5615 if (!line->IsBlock()) {
michael@0 5616 // Optimize by comparing the line's last child first.
michael@0 5617 nsLineList::iterator next = line.next();
michael@0 5618 if (aChild == (next == aEnd ? aFrameList.LastChild()
michael@0 5619 : next->mFirstChild->GetPrevSibling()) ||
michael@0 5620 line->Contains(aChild)) {
michael@0 5621 *aResult = line;
michael@0 5622 return true;
michael@0 5623 }
michael@0 5624 }
michael@0 5625 }
michael@0 5626 return false;
michael@0 5627 }
michael@0 5628
michael@0 5629 static bool
michael@0 5630 FindLineFor(nsIFrame* aChild,
michael@0 5631 const nsFrameList& aFrameList,
michael@0 5632 nsLineList::iterator aBegin,
michael@0 5633 nsLineList::iterator aEnd,
michael@0 5634 nsLineList::iterator* aResult)
michael@0 5635 {
michael@0 5636 return aChild->IsBlockOutside() ?
michael@0 5637 FindBlockLineFor(aChild, aBegin, aEnd, aResult) :
michael@0 5638 FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult);
michael@0 5639 }
michael@0 5640
michael@0 5641 nsresult
michael@0 5642 nsBlockFrame::StealFrame(nsIFrame* aChild,
michael@0 5643 bool aForceNormal)
michael@0 5644 {
michael@0 5645 MOZ_ASSERT(aChild->GetParent() == this);
michael@0 5646
michael@0 5647 if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
michael@0 5648 aChild->IsFloating()) {
michael@0 5649 RemoveFloat(aChild);
michael@0 5650 return NS_OK;
michael@0 5651 }
michael@0 5652
michael@0 5653 if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
michael@0 5654 && !aForceNormal) {
michael@0 5655 return nsContainerFrame::StealFrame(aChild);
michael@0 5656 }
michael@0 5657
michael@0 5658 MOZ_ASSERT(!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
michael@0 5659
michael@0 5660 nsLineList::iterator line;
michael@0 5661 if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) {
michael@0 5662 RemoveFrameFromLine(aChild, line, mFrames, mLines);
michael@0 5663 } else {
michael@0 5664 FrameLines* overflowLines = GetOverflowLines();
michael@0 5665 DebugOnly<bool> found;
michael@0 5666 found = FindLineFor(aChild, overflowLines->mFrames,
michael@0 5667 overflowLines->mLines.begin(),
michael@0 5668 overflowLines->mLines.end(), &line);
michael@0 5669 MOZ_ASSERT(found);
michael@0 5670 RemoveFrameFromLine(aChild, line, overflowLines->mFrames,
michael@0 5671 overflowLines->mLines);
michael@0 5672 if (overflowLines->mLines.empty()) {
michael@0 5673 DestroyOverflowLines();
michael@0 5674 }
michael@0 5675 }
michael@0 5676
michael@0 5677 return NS_OK;
michael@0 5678 }
michael@0 5679
michael@0 5680 void
michael@0 5681 nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine,
michael@0 5682 nsFrameList& aFrameList, nsLineList& aLineList)
michael@0 5683 {
michael@0 5684 aFrameList.RemoveFrame(aChild);
michael@0 5685 if (aChild == aLine->mFirstChild) {
michael@0 5686 aLine->mFirstChild = aChild->GetNextSibling();
michael@0 5687 }
michael@0 5688 aLine->NoteFrameRemoved(aChild);
michael@0 5689 if (aLine->GetChildCount() > 0) {
michael@0 5690 aLine->MarkDirty();
michael@0 5691 } else {
michael@0 5692 // The line became empty - destroy it.
michael@0 5693 nsLineBox* lineBox = aLine;
michael@0 5694 aLine = aLineList.erase(aLine);
michael@0 5695 if (aLine != aLineList.end()) {
michael@0 5696 aLine->MarkPreviousMarginDirty();
michael@0 5697 }
michael@0 5698 FreeLineBox(lineBox);
michael@0 5699 }
michael@0 5700 }
michael@0 5701
michael@0 5702 void
michael@0 5703 nsBlockFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
michael@0 5704 bool aDeletingEmptyFrames)
michael@0 5705 {
michael@0 5706 NS_PRECONDITION(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
michael@0 5707
michael@0 5708 if (aNextInFlow->GetStateBits() &
michael@0 5709 (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
michael@0 5710 nsContainerFrame::DeleteNextInFlowChild(aNextInFlow, aDeletingEmptyFrames);
michael@0 5711 }
michael@0 5712 else {
michael@0 5713 #ifdef DEBUG
michael@0 5714 if (aDeletingEmptyFrames) {
michael@0 5715 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
michael@0 5716 }
michael@0 5717 #endif
michael@0 5718 DoRemoveFrame(aNextInFlow,
michael@0 5719 aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
michael@0 5720 }
michael@0 5721 }
michael@0 5722
michael@0 5723 const nsStyleText*
michael@0 5724 nsBlockFrame::StyleTextForLineLayout()
michael@0 5725 {
michael@0 5726 // Return the pointer to an unmodified style text
michael@0 5727 return StyleText();
michael@0 5728 }
michael@0 5729
michael@0 5730 ////////////////////////////////////////////////////////////////////////
michael@0 5731 // Float support
michael@0 5732
michael@0 5733 nsRect
michael@0 5734 nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState,
michael@0 5735 const nsRect& aFloatAvailableSpace,
michael@0 5736 nsIFrame* aFloatFrame)
michael@0 5737 {
michael@0 5738 // Compute the available width. By default, assume the width of the
michael@0 5739 // containing block.
michael@0 5740 nscoord availWidth;
michael@0 5741 const nsStyleDisplay* floatDisplay = aFloatFrame->StyleDisplay();
michael@0 5742
michael@0 5743 if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
michael@0 5744 eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
michael@0 5745 availWidth = aState.mContentArea.width;
michael@0 5746 }
michael@0 5747 else {
michael@0 5748 // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
michael@0 5749 // give tables only the available space
michael@0 5750 // if they can shrink we may not be constrained to place
michael@0 5751 // them in the next line
michael@0 5752 availWidth = aFloatAvailableSpace.width;
michael@0 5753 }
michael@0 5754
michael@0 5755 nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height
michael@0 5756 ? NS_UNCONSTRAINEDSIZE
michael@0 5757 : std::max(0, aState.mContentArea.YMost() - aState.mY);
michael@0 5758
michael@0 5759 #ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS
michael@0 5760 if (availHeight != NS_UNCONSTRAINEDSIZE &&
michael@0 5761 nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame)) {
michael@0 5762 // Tell the float it has unrestricted height, so it won't break.
michael@0 5763 // If the float doesn't actually fit in the column it will fail to be
michael@0 5764 // placed, and either move to the top of the next column or just
michael@0 5765 // overflow.
michael@0 5766 availHeight = NS_UNCONSTRAINEDSIZE;
michael@0 5767 }
michael@0 5768 #endif
michael@0 5769
michael@0 5770 return nsRect(aState.mContentArea.x,
michael@0 5771 aState.mContentArea.y,
michael@0 5772 availWidth, availHeight);
michael@0 5773 }
michael@0 5774
michael@0 5775 nscoord
michael@0 5776 nsBlockFrame::ComputeFloatWidth(nsBlockReflowState& aState,
michael@0 5777 const nsRect& aFloatAvailableSpace,
michael@0 5778 nsIFrame* aFloat)
michael@0 5779 {
michael@0 5780 NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
michael@0 5781 "aFloat must be an out-of-flow frame");
michael@0 5782 // Reflow the float.
michael@0 5783 nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace,
michael@0 5784 aFloat);
michael@0 5785
michael@0 5786 nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
michael@0 5787 availSpace.Size());
michael@0 5788 return floatRS.ComputedWidth() + floatRS.ComputedPhysicalBorderPadding().LeftRight() +
michael@0 5789 floatRS.ComputedPhysicalMargin().LeftRight();
michael@0 5790 }
michael@0 5791
michael@0 5792 nsresult
michael@0 5793 nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
michael@0 5794 const nsRect& aAdjustedAvailableSpace,
michael@0 5795 nsIFrame* aFloat,
michael@0 5796 nsMargin& aFloatMargin,
michael@0 5797 nsMargin& aFloatOffsets,
michael@0 5798 bool aFloatPushedDown,
michael@0 5799 nsReflowStatus& aReflowStatus)
michael@0 5800 {
michael@0 5801 NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
michael@0 5802 "aFloat must be an out-of-flow frame");
michael@0 5803 // Reflow the float.
michael@0 5804 aReflowStatus = NS_FRAME_COMPLETE;
michael@0 5805
michael@0 5806 #ifdef NOISY_FLOAT
michael@0 5807 printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
michael@0 5808 aFloat, this,
michael@0 5809 aFloatAvailableSpace.x, aFloatAvailableSpace.y,
michael@0 5810 aFloatAvailableSpace.width, aFloatAvailableSpace.height
michael@0 5811 );
michael@0 5812 #endif
michael@0 5813
michael@0 5814 nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
michael@0 5815 nsSize(aAdjustedAvailableSpace.width,
michael@0 5816 aAdjustedAvailableSpace.height));
michael@0 5817
michael@0 5818 // Normally the mIsTopOfPage state is copied from the parent reflow
michael@0 5819 // state. However, when reflowing a float, if we've placed other
michael@0 5820 // floats that force this float *down* or *narrower*, we should unset
michael@0 5821 // the mIsTopOfPage state.
michael@0 5822 // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
michael@0 5823 // variable below, which has the exact same effect. Perhaps it should
michael@0 5824 // be merged into that, except that the test for narrowing here is not
michael@0 5825 // about adjacency with the top, so it seems misleading.
michael@0 5826 if (floatRS.mFlags.mIsTopOfPage &&
michael@0 5827 (aFloatPushedDown ||
michael@0 5828 aAdjustedAvailableSpace.width != aState.mContentArea.width)) {
michael@0 5829 floatRS.mFlags.mIsTopOfPage = false;
michael@0 5830 }
michael@0 5831
michael@0 5832 // Setup a block reflow context to reflow the float.
michael@0 5833 nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
michael@0 5834
michael@0 5835 // Reflow the float
michael@0 5836 bool isAdjacentWithTop = aState.IsAdjacentWithTop();
michael@0 5837
michael@0 5838 nsIFrame* clearanceFrame = nullptr;
michael@0 5839 nsresult rv;
michael@0 5840 do {
michael@0 5841 nsCollapsingMargin margin;
michael@0 5842 bool mayNeedRetry = false;
michael@0 5843 floatRS.mDiscoveredClearance = nullptr;
michael@0 5844 // Only first in flow gets a top margin.
michael@0 5845 if (!aFloat->GetPrevInFlow()) {
michael@0 5846 nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
michael@0 5847 clearanceFrame, &mayNeedRetry);
michael@0 5848
michael@0 5849 if (mayNeedRetry && !clearanceFrame) {
michael@0 5850 floatRS.mDiscoveredClearance = &clearanceFrame;
michael@0 5851 // We don't need to push the float manager state because the the block has its own
michael@0 5852 // float manager that will be destroyed and recreated
michael@0 5853 }
michael@0 5854 }
michael@0 5855
michael@0 5856 rv = brc.ReflowBlock(aAdjustedAvailableSpace, true, margin,
michael@0 5857 0, isAdjacentWithTop,
michael@0 5858 nullptr, floatRS,
michael@0 5859 aReflowStatus, aState);
michael@0 5860 } while (NS_SUCCEEDED(rv) && clearanceFrame);
michael@0 5861
michael@0 5862 if (!NS_FRAME_IS_FULLY_COMPLETE(aReflowStatus) &&
michael@0 5863 ShouldAvoidBreakInside(floatRS)) {
michael@0 5864 aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 5865 } else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
michael@0 5866 (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.height)) {
michael@0 5867 // An incomplete reflow status means we should split the float
michael@0 5868 // if the height is constrained (bug 145305).
michael@0 5869 aReflowStatus = NS_FRAME_COMPLETE;
michael@0 5870 }
michael@0 5871
michael@0 5872 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
michael@0 5873 aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 5874 }
michael@0 5875
michael@0 5876 if (aFloat->GetType() == nsGkAtoms::letterFrame) {
michael@0 5877 // We never split floating first letters; an incomplete state for
michael@0 5878 // such frames simply means that there is more content to be
michael@0 5879 // reflowed on the line.
michael@0 5880 if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus))
michael@0 5881 aReflowStatus = NS_FRAME_COMPLETE;
michael@0 5882 }
michael@0 5883
michael@0 5884 if (NS_FAILED(rv)) {
michael@0 5885 return rv;
michael@0 5886 }
michael@0 5887
michael@0 5888 // Capture the margin and offsets information for the caller
michael@0 5889 aFloatMargin = floatRS.ComputedPhysicalMargin(); // float margins don't collapse
michael@0 5890 aFloatOffsets = floatRS.ComputedPhysicalOffsets();
michael@0 5891
michael@0 5892 const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
michael@0 5893
michael@0 5894 // Set the rect, make sure the view is properly sized and positioned,
michael@0 5895 // and tell the frame we're done reflowing it
michael@0 5896 // XXXldb This seems like the wrong place to be doing this -- shouldn't
michael@0 5897 // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
michael@0 5898 // we've positioned the float, and shouldn't we be doing the equivalent
michael@0 5899 // of |PlaceFrameView| here?
michael@0 5900 aFloat->SetSize(nsSize(metrics.Width(), metrics.Height()));
michael@0 5901 if (aFloat->HasView()) {
michael@0 5902 nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, aFloat,
michael@0 5903 aFloat->GetView(),
michael@0 5904 metrics.VisualOverflow(),
michael@0 5905 NS_FRAME_NO_MOVE_VIEW);
michael@0 5906 }
michael@0 5907 // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
michael@0 5908 aFloat->DidReflow(aState.mPresContext, &floatRS,
michael@0 5909 nsDidReflowStatus::FINISHED);
michael@0 5910
michael@0 5911 #ifdef NOISY_FLOAT
michael@0 5912 printf("end ReflowFloat %p, sized to %d,%d\n",
michael@0 5913 aFloat, metrics.Width(), metrics.Height());
michael@0 5914 #endif
michael@0 5915
michael@0 5916 return NS_OK;
michael@0 5917 }
michael@0 5918
michael@0 5919 uint8_t
michael@0 5920 nsBlockFrame::FindTrailingClear()
michael@0 5921 {
michael@0 5922 // find the break type of the last line
michael@0 5923 for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
michael@0 5924 nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
michael@0 5925 line_iterator endLine = block->end_lines();
michael@0 5926 if (endLine != block->begin_lines()) {
michael@0 5927 --endLine;
michael@0 5928 return endLine->GetBreakTypeAfter();
michael@0 5929 }
michael@0 5930 }
michael@0 5931 return NS_STYLE_CLEAR_NONE;
michael@0 5932 }
michael@0 5933
michael@0 5934 void
michael@0 5935 nsBlockFrame::ReflowPushedFloats(nsBlockReflowState& aState,
michael@0 5936 nsOverflowAreas& aOverflowAreas,
michael@0 5937 nsReflowStatus& aStatus)
michael@0 5938 {
michael@0 5939 // Pushed floats live at the start of our float list; see comment
michael@0 5940 // above nsBlockFrame::DrainPushedFloats.
michael@0 5941 for (nsIFrame* f = mFloats.FirstChild(), *next;
michael@0 5942 f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
michael@0 5943 f = next) {
michael@0 5944 // save next sibling now, since reflowing could push the entire
michael@0 5945 // float, changing its siblings
michael@0 5946 next = f->GetNextSibling();
michael@0 5947
michael@0 5948 // When we push a first-continuation float in a non-initial reflow,
michael@0 5949 // it's possible that we end up with two continuations with the same
michael@0 5950 // parent. This happens if, on the previous reflow of the block or
michael@0 5951 // a previous reflow of the line containing the block, the float was
michael@0 5952 // split between continuations A and B of the parent, but on the
michael@0 5953 // current reflow, none of the float can fit in A.
michael@0 5954 //
michael@0 5955 // When this happens, we might even have the two continuations
michael@0 5956 // out-of-order due to the management of the pushed floats. In
michael@0 5957 // particular, if the float's placeholder was in a pushed line that
michael@0 5958 // we reflowed before it was pushed, and we split the float during
michael@0 5959 // that reflow, we might have the continuation of the float before
michael@0 5960 // the float itself. (In the general case, however, it's correct
michael@0 5961 // for floats in the pushed floats list to come before floats
michael@0 5962 // anchored in pushed lines; however, in this case it's wrong. We
michael@0 5963 // should probably find a way to fix it somehow, since it leads to
michael@0 5964 // incorrect layout in some cases.)
michael@0 5965 //
michael@0 5966 // When we have these out-of-order continuations, we might hit the
michael@0 5967 // next-continuation before the previous-continuation. When that
michael@0 5968 // happens, just push it. When we reflow the next continuation,
michael@0 5969 // we'll either pull all of its content back and destroy it (by
michael@0 5970 // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
michael@0 5971 // pull it out of its current position and push it again (and
michael@0 5972 // potentially repeat this cycle for the next continuation, although
michael@0 5973 // hopefully then they'll be in the right order).
michael@0 5974 //
michael@0 5975 // We should also need this code for the in-order case if the first
michael@0 5976 // continuation of a float gets moved across more than one
michael@0 5977 // continuation of the containing block. In this case we'd manage
michael@0 5978 // to push the second continuation without this check, but not the
michael@0 5979 // third and later.
michael@0 5980 nsIFrame *prevContinuation = f->GetPrevContinuation();
michael@0 5981 if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
michael@0 5982 mFloats.RemoveFrame(f);
michael@0 5983 aState.AppendPushedFloat(f);
michael@0 5984 continue;
michael@0 5985 }
michael@0 5986
michael@0 5987 // Always call FlowAndPlaceFloat; we might need to place this float
michael@0 5988 // if didn't belong to this block the last time it was reflowed.
michael@0 5989 aState.FlowAndPlaceFloat(f);
michael@0 5990
michael@0 5991 ConsiderChildOverflow(aOverflowAreas, f);
michael@0 5992 }
michael@0 5993
michael@0 5994 // If there are continued floats, then we may need to continue BR clearance
michael@0 5995 if (0 != aState.ClearFloats(0, NS_STYLE_CLEAR_BOTH)) {
michael@0 5996 aState.mFloatBreakType = static_cast<nsBlockFrame*>(GetPrevInFlow())
michael@0 5997 ->FindTrailingClear();
michael@0 5998 }
michael@0 5999 }
michael@0 6000
michael@0 6001 void
michael@0 6002 nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager)
michael@0 6003 {
michael@0 6004 // Recover our own floats
michael@0 6005 nsIFrame* stop = nullptr; // Stop before we reach pushed floats that
michael@0 6006 // belong to our next-in-flow
michael@0 6007 for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) {
michael@0 6008 nsRect region = nsFloatManager::GetRegionFor(f);
michael@0 6009 aFloatManager.AddFloat(f, region);
michael@0 6010 if (!stop && f->GetNextInFlow())
michael@0 6011 stop = f->GetNextInFlow();
michael@0 6012 }
michael@0 6013
michael@0 6014 // Recurse into our overflow container children
michael@0 6015 for (nsIFrame* oc = GetFirstChild(kOverflowContainersList);
michael@0 6016 oc; oc = oc->GetNextSibling()) {
michael@0 6017 RecoverFloatsFor(oc, aFloatManager);
michael@0 6018 }
michael@0 6019
michael@0 6020 // Recurse into our normal children
michael@0 6021 for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) {
michael@0 6022 if (line->IsBlock()) {
michael@0 6023 RecoverFloatsFor(line->mFirstChild, aFloatManager);
michael@0 6024 }
michael@0 6025 }
michael@0 6026 }
michael@0 6027
michael@0 6028 void
michael@0 6029 nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
michael@0 6030 nsFloatManager& aFloatManager)
michael@0 6031 {
michael@0 6032 NS_PRECONDITION(aFrame, "null frame");
michael@0 6033 // Only blocks have floats
michael@0 6034 nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
michael@0 6035 // Don't recover any state inside a block that has its own space manager
michael@0 6036 // (we don't currently have any blocks like this, though, thanks to our
michael@0 6037 // use of extra frames for 'overflow')
michael@0 6038 if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
michael@0 6039 // If the element is relatively positioned, then adjust x and y
michael@0 6040 // accordingly so that we consider relatively positioned frames
michael@0 6041 // at their original position.
michael@0 6042 nsPoint pos = block->GetNormalPosition();
michael@0 6043 aFloatManager.Translate(pos.x, pos.y);
michael@0 6044 block->RecoverFloats(aFloatManager);
michael@0 6045 aFloatManager.Translate(-pos.x, -pos.y);
michael@0 6046 }
michael@0 6047 }
michael@0 6048
michael@0 6049 //////////////////////////////////////////////////////////////////////
michael@0 6050 // Painting, event handling
michael@0 6051
michael@0 6052 #ifdef DEBUG
michael@0 6053 static void ComputeVisualOverflowArea(nsLineList& aLines,
michael@0 6054 nscoord aWidth, nscoord aHeight,
michael@0 6055 nsRect& aResult)
michael@0 6056 {
michael@0 6057 nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
michael@0 6058 for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
michael@0 6059 line != line_end;
michael@0 6060 ++line) {
michael@0 6061 // Compute min and max x/y values for the reflowed frame's
michael@0 6062 // combined areas
michael@0 6063 nsRect visOverflow(line->GetVisualOverflowArea());
michael@0 6064 nscoord x = visOverflow.x;
michael@0 6065 nscoord y = visOverflow.y;
michael@0 6066 nscoord xmost = x + visOverflow.width;
michael@0 6067 nscoord ymost = y + visOverflow.height;
michael@0 6068 if (x < xa) {
michael@0 6069 xa = x;
michael@0 6070 }
michael@0 6071 if (xmost > xb) {
michael@0 6072 xb = xmost;
michael@0 6073 }
michael@0 6074 if (y < ya) {
michael@0 6075 ya = y;
michael@0 6076 }
michael@0 6077 if (ymost > yb) {
michael@0 6078 yb = ymost;
michael@0 6079 }
michael@0 6080 }
michael@0 6081
michael@0 6082 aResult.x = xa;
michael@0 6083 aResult.y = ya;
michael@0 6084 aResult.width = xb - xa;
michael@0 6085 aResult.height = yb - ya;
michael@0 6086 }
michael@0 6087 #endif
michael@0 6088
michael@0 6089 bool
michael@0 6090 nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
michael@0 6091 {
michael@0 6092 if (mContent->IsHTML() && (mContent->Tag() == nsGkAtoms::html ||
michael@0 6093 mContent->Tag() == nsGkAtoms::body))
michael@0 6094 return true;
michael@0 6095
michael@0 6096 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
michael@0 6097 bool visible;
michael@0 6098 nsresult rv = aSelection->ContainsNode(node, true, &visible);
michael@0 6099 return NS_SUCCEEDED(rv) && visible;
michael@0 6100 }
michael@0 6101
michael@0 6102 #ifdef DEBUG
michael@0 6103 static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) {
michael@0 6104 if (nsBlockFrame::gNoisyDamageRepair) {
michael@0 6105 nsFrame::IndentBy(stdout, aDepth+1);
michael@0 6106 nsRect lineArea = aLine->GetVisualOverflowArea();
michael@0 6107 printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
michael@0 6108 aDrawn ? "draw" : "skip",
michael@0 6109 static_cast<void*>(aLine),
michael@0 6110 aLine->IStart(), aLine->BStart(),
michael@0 6111 aLine->ISize(), aLine->BSize(),
michael@0 6112 lineArea.x, lineArea.y,
michael@0 6113 lineArea.width, lineArea.height);
michael@0 6114 }
michael@0 6115 }
michael@0 6116 #endif
michael@0 6117
michael@0 6118 static void
michael@0 6119 DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
michael@0 6120 const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine,
michael@0 6121 int32_t aDepth, int32_t& aDrawnLines, const nsDisplayListSet& aLists,
michael@0 6122 nsBlockFrame* aFrame, TextOverflow* aTextOverflow) {
michael@0 6123 // If the line's combined area (which includes child frames that
michael@0 6124 // stick outside of the line's bounding box or our bounding box)
michael@0 6125 // intersects the dirty rect then paint the line.
michael@0 6126 bool intersect = aLineArea.Intersects(aDirtyRect);
michael@0 6127 #ifdef DEBUG
michael@0 6128 if (nsBlockFrame::gLamePaintMetrics) {
michael@0 6129 aDrawnLines++;
michael@0 6130 }
michael@0 6131 DebugOutputDrawLine(aDepth, aLine.get(), intersect);
michael@0 6132 #endif
michael@0 6133 // The line might contain a placeholder for a visible out-of-flow, in which
michael@0 6134 // case we need to descend into it. If there is such a placeholder, we will
michael@0 6135 // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
michael@0 6136 // In particular, we really want to check ShouldDescendIntoFrame()
michael@0 6137 // on all the frames on the line, but that might be expensive. So
michael@0 6138 // we approximate it by checking it on aFrame; if it's true for any
michael@0 6139 // frame in the line, it's also true for aFrame.
michael@0 6140 bool lineInline = aLine->IsInline();
michael@0 6141 bool lineMayHaveTextOverflow = aTextOverflow && lineInline;
michael@0 6142 if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
michael@0 6143 !lineMayHaveTextOverflow)
michael@0 6144 return;
michael@0 6145
michael@0 6146 // Collect our line's display items in a temporary nsDisplayListCollection,
michael@0 6147 // so that we can apply any "text-overflow" clipping to the entire collection
michael@0 6148 // without affecting previous lines.
michael@0 6149 nsDisplayListCollection collection;
michael@0 6150
michael@0 6151 // Block-level child backgrounds go on the blockBorderBackgrounds list ...
michael@0 6152 // Inline-level child backgrounds go on the regular child content list.
michael@0 6153 nsDisplayListSet childLists(collection,
michael@0 6154 lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
michael@0 6155
michael@0 6156 uint32_t flags = lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0;
michael@0 6157
michael@0 6158 nsIFrame* kid = aLine->mFirstChild;
michael@0 6159 int32_t n = aLine->GetChildCount();
michael@0 6160 while (--n >= 0) {
michael@0 6161 aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect,
michael@0 6162 childLists, flags);
michael@0 6163 kid = kid->GetNextSibling();
michael@0 6164 }
michael@0 6165
michael@0 6166 if (lineMayHaveTextOverflow) {
michael@0 6167 aTextOverflow->ProcessLine(collection, aLine.get());
michael@0 6168 }
michael@0 6169
michael@0 6170 collection.MoveTo(aLists);
michael@0 6171 }
michael@0 6172
michael@0 6173 void
michael@0 6174 nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 6175 const nsRect& aDirtyRect,
michael@0 6176 const nsDisplayListSet& aLists)
michael@0 6177 {
michael@0 6178 int32_t drawnLines; // Will only be used if set (gLamePaintMetrics).
michael@0 6179 int32_t depth = 0;
michael@0 6180 #ifdef DEBUG
michael@0 6181 if (gNoisyDamageRepair) {
michael@0 6182 depth = GetDepth();
michael@0 6183 nsRect ca;
michael@0 6184 ::ComputeVisualOverflowArea(mLines, mRect.width, mRect.height, ca);
michael@0 6185 nsFrame::IndentBy(stdout, depth);
michael@0 6186 ListTag(stdout);
michael@0 6187 printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
michael@0 6188 mRect.x, mRect.y, mRect.width, mRect.height,
michael@0 6189 aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
michael@0 6190 ca.x, ca.y, ca.width, ca.height);
michael@0 6191 }
michael@0 6192 PRTime start = 0; // Initialize these variables to silence the compiler.
michael@0 6193 if (gLamePaintMetrics) {
michael@0 6194 start = PR_Now();
michael@0 6195 drawnLines = 0;
michael@0 6196 }
michael@0 6197 #endif
michael@0 6198
michael@0 6199 DisplayBorderBackgroundOutline(aBuilder, aLists);
michael@0 6200
michael@0 6201 if (GetPrevInFlow()) {
michael@0 6202 DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
michael@0 6203 for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
michael@0 6204 if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
michael@0 6205 BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
michael@0 6206 }
michael@0 6207 }
michael@0 6208
michael@0 6209 aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
michael@0 6210
michael@0 6211 // Prepare for text-overflow processing.
michael@0 6212 nsAutoPtr<TextOverflow> textOverflow(
michael@0 6213 TextOverflow::WillProcessLines(aBuilder, this));
michael@0 6214
michael@0 6215 // We'll collect our lines' display items here, & then append this to aLists.
michael@0 6216 nsDisplayListCollection linesDisplayListCollection;
michael@0 6217
michael@0 6218 // Don't use the line cursor if we might have a descendant placeholder ...
michael@0 6219 // it might skip lines that contain placeholders but don't themselves
michael@0 6220 // intersect with the dirty area.
michael@0 6221 // In particular, we really want to check ShouldDescendIntoFrame()
michael@0 6222 // on all our child frames, but that might be expensive. So we
michael@0 6223 // approximate it by checking it on |this|; if it's true for any
michael@0 6224 // frame in our child list, it's also true for |this|.
michael@0 6225 nsLineBox* cursor = aBuilder->ShouldDescendIntoFrame(this) ?
michael@0 6226 nullptr : GetFirstLineContaining(aDirtyRect.y);
michael@0 6227 line_iterator line_end = end_lines();
michael@0 6228
michael@0 6229 if (cursor) {
michael@0 6230 for (line_iterator line = mLines.begin(cursor);
michael@0 6231 line != line_end;
michael@0 6232 ++line) {
michael@0 6233 nsRect lineArea = line->GetVisualOverflowArea();
michael@0 6234 if (!lineArea.IsEmpty()) {
michael@0 6235 // Because we have a cursor, the combinedArea.ys are non-decreasing.
michael@0 6236 // Once we've passed aDirtyRect.YMost(), we can never see it again.
michael@0 6237 if (lineArea.y >= aDirtyRect.YMost()) {
michael@0 6238 break;
michael@0 6239 }
michael@0 6240 DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
michael@0 6241 linesDisplayListCollection, this, textOverflow);
michael@0 6242 }
michael@0 6243 }
michael@0 6244 } else {
michael@0 6245 bool nonDecreasingYs = true;
michael@0 6246 int32_t lineCount = 0;
michael@0 6247 nscoord lastY = INT32_MIN;
michael@0 6248 nscoord lastYMost = INT32_MIN;
michael@0 6249 for (line_iterator line = begin_lines();
michael@0 6250 line != line_end;
michael@0 6251 ++line) {
michael@0 6252 nsRect lineArea = line->GetVisualOverflowArea();
michael@0 6253 DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
michael@0 6254 linesDisplayListCollection, this, textOverflow);
michael@0 6255 if (!lineArea.IsEmpty()) {
michael@0 6256 if (lineArea.y < lastY
michael@0 6257 || lineArea.YMost() < lastYMost) {
michael@0 6258 nonDecreasingYs = false;
michael@0 6259 }
michael@0 6260 lastY = lineArea.y;
michael@0 6261 lastYMost = lineArea.YMost();
michael@0 6262 }
michael@0 6263 lineCount++;
michael@0 6264 }
michael@0 6265
michael@0 6266 if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
michael@0 6267 SetupLineCursor();
michael@0 6268 }
michael@0 6269 }
michael@0 6270
michael@0 6271 // Pick up the resulting text-overflow markers. We append them to
michael@0 6272 // PositionedDescendants just before we append the lines' display items,
michael@0 6273 // so that our text-overflow markers will appear on top of this block's
michael@0 6274 // normal content but below any of its its' positioned children.
michael@0 6275 if (textOverflow) {
michael@0 6276 aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers());
michael@0 6277 }
michael@0 6278 linesDisplayListCollection.MoveTo(aLists);
michael@0 6279
michael@0 6280 if (HasOutsideBullet()) {
michael@0 6281 // Display outside bullets manually
michael@0 6282 nsIFrame* bullet = GetOutsideBullet();
michael@0 6283 BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists);
michael@0 6284 }
michael@0 6285
michael@0 6286 #ifdef DEBUG
michael@0 6287 if (gLamePaintMetrics) {
michael@0 6288 PRTime end = PR_Now();
michael@0 6289
michael@0 6290 int32_t numLines = mLines.size();
michael@0 6291 if (!numLines) numLines = 1;
michael@0 6292 PRTime lines, deltaPerLine, delta;
michael@0 6293 lines = int64_t(numLines);
michael@0 6294 delta = end - start;
michael@0 6295 deltaPerLine = delta / lines;
michael@0 6296
michael@0 6297 ListTag(stdout);
michael@0 6298 char buf[400];
michael@0 6299 PR_snprintf(buf, sizeof(buf),
michael@0 6300 ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
michael@0 6301 delta, deltaPerLine,
michael@0 6302 numLines, drawnLines, numLines - drawnLines);
michael@0 6303 printf("%s\n", buf);
michael@0 6304 }
michael@0 6305 #endif
michael@0 6306 }
michael@0 6307
michael@0 6308 #ifdef ACCESSIBILITY
michael@0 6309 a11y::AccType
michael@0 6310 nsBlockFrame::AccessibleType()
michael@0 6311 {
michael@0 6312 // block frame may be for <hr>
michael@0 6313 if (mContent->Tag() == nsGkAtoms::hr) {
michael@0 6314 return a11y::eHTMLHRType;
michael@0 6315 }
michael@0 6316
michael@0 6317 if (!HasBullet() || !PresContext()) {
michael@0 6318 if (!mContent->GetParent()) {
michael@0 6319 // Don't create accessible objects for the root content node, they are redundant with
michael@0 6320 // the nsDocAccessible object created with the document node
michael@0 6321 return a11y::eNoType;
michael@0 6322 }
michael@0 6323
michael@0 6324 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
michael@0 6325 do_QueryInterface(mContent->GetDocument());
michael@0 6326 if (htmlDoc) {
michael@0 6327 nsCOMPtr<nsIDOMHTMLElement> body;
michael@0 6328 htmlDoc->GetBody(getter_AddRefs(body));
michael@0 6329 if (SameCOMIdentity(body, mContent)) {
michael@0 6330 // Don't create accessible objects for the body, they are redundant with
michael@0 6331 // the nsDocAccessible object created with the document node
michael@0 6332 return a11y::eNoType;
michael@0 6333 }
michael@0 6334 }
michael@0 6335
michael@0 6336 // Not a bullet, treat as normal HTML container
michael@0 6337 return a11y::eHyperTextType;
michael@0 6338 }
michael@0 6339
michael@0 6340 // Create special list bullet accessible
michael@0 6341 return a11y::eHTMLLiType;
michael@0 6342 }
michael@0 6343 #endif
michael@0 6344
michael@0 6345 void nsBlockFrame::ClearLineCursor()
michael@0 6346 {
michael@0 6347 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
michael@0 6348 return;
michael@0 6349 }
michael@0 6350
michael@0 6351 Properties().Delete(LineCursorProperty());
michael@0 6352 RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
michael@0 6353 }
michael@0 6354
michael@0 6355 void nsBlockFrame::SetupLineCursor()
michael@0 6356 {
michael@0 6357 if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
michael@0 6358 || mLines.empty()) {
michael@0 6359 return;
michael@0 6360 }
michael@0 6361
michael@0 6362 Properties().Set(LineCursorProperty(), mLines.front());
michael@0 6363 AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
michael@0 6364 }
michael@0 6365
michael@0 6366 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
michael@0 6367 {
michael@0 6368 if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
michael@0 6369 return nullptr;
michael@0 6370 }
michael@0 6371
michael@0 6372 FrameProperties props = Properties();
michael@0 6373
michael@0 6374 nsLineBox* property = static_cast<nsLineBox*>
michael@0 6375 (props.Get(LineCursorProperty()));
michael@0 6376 line_iterator cursor = mLines.begin(property);
michael@0 6377 nsRect cursorArea = cursor->GetVisualOverflowArea();
michael@0 6378
michael@0 6379 while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
michael@0 6380 && cursor != mLines.front()) {
michael@0 6381 cursor = cursor.prev();
michael@0 6382 cursorArea = cursor->GetVisualOverflowArea();
michael@0 6383 }
michael@0 6384 while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
michael@0 6385 && cursor != mLines.back()) {
michael@0 6386 cursor = cursor.next();
michael@0 6387 cursorArea = cursor->GetVisualOverflowArea();
michael@0 6388 }
michael@0 6389
michael@0 6390 if (cursor.get() != property) {
michael@0 6391 props.Set(LineCursorProperty(), cursor.get());
michael@0 6392 }
michael@0 6393
michael@0 6394 return cursor.get();
michael@0 6395 }
michael@0 6396
michael@0 6397 /* virtual */ void
michael@0 6398 nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
michael@0 6399 {
michael@0 6400 // See if the child is absolutely positioned
michael@0 6401 if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
michael@0 6402 aChild->IsAbsolutelyPositioned()) {
michael@0 6403 // do nothing
michael@0 6404 } else if (aChild == GetOutsideBullet()) {
michael@0 6405 // The bullet lives in the first line, unless the first line has
michael@0 6406 // height 0 and there is a second line, in which case it lives
michael@0 6407 // in the second line.
michael@0 6408 line_iterator bulletLine = begin_lines();
michael@0 6409 if (bulletLine != end_lines() && bulletLine->BSize() == 0 &&
michael@0 6410 bulletLine != mLines.back()) {
michael@0 6411 bulletLine = bulletLine.next();
michael@0 6412 }
michael@0 6413
michael@0 6414 if (bulletLine != end_lines()) {
michael@0 6415 MarkLineDirty(bulletLine, &mLines);
michael@0 6416 }
michael@0 6417 // otherwise we have an empty line list, and ReflowDirtyLines
michael@0 6418 // will handle reflowing the bullet.
michael@0 6419 } else {
michael@0 6420 // Note that we should go through our children to mark lines dirty
michael@0 6421 // before the next reflow. Doing it now could make things O(N^2)
michael@0 6422 // since finding the right line is O(N).
michael@0 6423 // We don't need to worry about marking lines on the overflow list
michael@0 6424 // as dirty; we're guaranteed to reflow them if we take them off the
michael@0 6425 // overflow list.
michael@0 6426 // However, we might have gotten a float, in which case we need to
michael@0 6427 // reflow the line containing its placeholder. So find the
michael@0 6428 // ancestor-or-self of the placeholder that's a child of the block,
michael@0 6429 // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
michael@0 6430 // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
michael@0 6431 // We need to take some care to handle the case where a float is in
michael@0 6432 // a different continuation than its placeholder, including marking
michael@0 6433 // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
michael@0 6434 if (!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
michael@0 6435 AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
michael@0 6436 } else {
michael@0 6437 NS_ASSERTION(aChild->IsFloating(), "should be a float");
michael@0 6438 nsIFrame *thisFC = FirstContinuation();
michael@0 6439 nsIFrame *placeholderPath =
michael@0 6440 PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild);
michael@0 6441 // SVG code sometimes sends FrameNeedsReflow notifications during
michael@0 6442 // frame destruction, leading to null placeholders, but we're safe
michael@0 6443 // ignoring those.
michael@0 6444 if (placeholderPath) {
michael@0 6445 for (;;) {
michael@0 6446 nsIFrame *parent = placeholderPath->GetParent();
michael@0 6447 if (parent->GetContent() == mContent &&
michael@0 6448 parent->FirstContinuation() == thisFC) {
michael@0 6449 parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
michael@0 6450 break;
michael@0 6451 }
michael@0 6452 placeholderPath = parent;
michael@0 6453 }
michael@0 6454 placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 6455 }
michael@0 6456 }
michael@0 6457 }
michael@0 6458
michael@0 6459 nsBlockFrameSuper::ChildIsDirty(aChild);
michael@0 6460 }
michael@0 6461
michael@0 6462 void
michael@0 6463 nsBlockFrame::Init(nsIContent* aContent,
michael@0 6464 nsIFrame* aParent,
michael@0 6465 nsIFrame* aPrevInFlow)
michael@0 6466 {
michael@0 6467 if (aPrevInFlow) {
michael@0 6468 // Copy over the inherited block frame bits from the prev-in-flow.
michael@0 6469 SetFlags(aPrevInFlow->GetStateBits() &
michael@0 6470 (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
michael@0 6471 }
michael@0 6472
michael@0 6473 nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow);
michael@0 6474
michael@0 6475 if (!aPrevInFlow ||
michael@0 6476 aPrevInFlow->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
michael@0 6477 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
michael@0 6478
michael@0 6479 if ((GetStateBits() &
michael@0 6480 (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) ==
michael@0 6481 (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) {
michael@0 6482 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
michael@0 6483 }
michael@0 6484 }
michael@0 6485
michael@0 6486 nsresult
michael@0 6487 nsBlockFrame::SetInitialChildList(ChildListID aListID,
michael@0 6488 nsFrameList& aChildList)
michael@0 6489 {
michael@0 6490 NS_ASSERTION(aListID != kPrincipalList ||
michael@0 6491 (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET |
michael@0 6492 NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0,
michael@0 6493 "how can we have a bullet already?");
michael@0 6494
michael@0 6495 if (kAbsoluteList == aListID) {
michael@0 6496 nsContainerFrame::SetInitialChildList(aListID, aChildList);
michael@0 6497 }
michael@0 6498 else if (kFloatList == aListID) {
michael@0 6499 mFloats.SetFrames(aChildList);
michael@0 6500 }
michael@0 6501 else {
michael@0 6502 nsPresContext* presContext = PresContext();
michael@0 6503
michael@0 6504 #ifdef DEBUG
michael@0 6505 // The only times a block that is an anonymous box is allowed to have a
michael@0 6506 // first-letter frame are when it's the block inside a non-anonymous cell,
michael@0 6507 // the block inside a fieldset, a scrolled content block, or a column
michael@0 6508 // content block. Note that this means that blocks which are the anonymous
michael@0 6509 // block in {ib} splits do NOT get first-letter frames. Note that
michael@0 6510 // NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations of the
michael@0 6511 // block.
michael@0 6512 nsIAtom *pseudo = StyleContext()->GetPseudo();
michael@0 6513 bool haveFirstLetterStyle =
michael@0 6514 (!pseudo ||
michael@0 6515 (pseudo == nsCSSAnonBoxes::cellContent &&
michael@0 6516 mParent->StyleContext()->GetPseudo() == nullptr) ||
michael@0 6517 pseudo == nsCSSAnonBoxes::fieldsetContent ||
michael@0 6518 pseudo == nsCSSAnonBoxes::scrolledContent ||
michael@0 6519 pseudo == nsCSSAnonBoxes::columnContent ||
michael@0 6520 pseudo == nsCSSAnonBoxes::mozSVGText) &&
michael@0 6521 !IsFrameOfType(eMathML) &&
michael@0 6522 nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nullptr;
michael@0 6523 NS_ASSERTION(haveFirstLetterStyle ==
michael@0 6524 ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
michael@0 6525 "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
michael@0 6526 #endif
michael@0 6527
michael@0 6528 AddFrames(aChildList, nullptr);
michael@0 6529
michael@0 6530 // Create a list bullet if this is a list-item. Note that this is
michael@0 6531 // done here so that RenumberLists will work (it needs the bullets
michael@0 6532 // to store the bullet numbers). Also note that due to various
michael@0 6533 // wrapper frames (scrollframes, columns) we want to use the
michael@0 6534 // outermost (primary, ideally, but it's not set yet when we get
michael@0 6535 // here) frame of our content for the display check. On the other
michael@0 6536 // hand, we look at ourselves for the GetPrevInFlow() check, since
michael@0 6537 // for a columnset we don't want a bullet per column. Note that
michael@0 6538 // the outermost frame for the content is the primary frame in
michael@0 6539 // most cases; the ones when it's not (like tables) can't be
michael@0 6540 // NS_STYLE_DISPLAY_LIST_ITEM).
michael@0 6541 nsIFrame* possibleListItem = this;
michael@0 6542 while (1) {
michael@0 6543 nsIFrame* parent = possibleListItem->GetParent();
michael@0 6544 if (parent->GetContent() != GetContent()) {
michael@0 6545 break;
michael@0 6546 }
michael@0 6547 possibleListItem = parent;
michael@0 6548 }
michael@0 6549 if (NS_STYLE_DISPLAY_LIST_ITEM ==
michael@0 6550 possibleListItem->StyleDisplay()->mDisplay &&
michael@0 6551 !GetPrevInFlow()) {
michael@0 6552 // Resolve style for the bullet frame
michael@0 6553 const nsStyleList* styleList = StyleList();
michael@0 6554 nsCSSPseudoElements::Type pseudoType;
michael@0 6555 switch (styleList->mListStyleType) {
michael@0 6556 case NS_STYLE_LIST_STYLE_DISC:
michael@0 6557 case NS_STYLE_LIST_STYLE_CIRCLE:
michael@0 6558 case NS_STYLE_LIST_STYLE_SQUARE:
michael@0 6559 pseudoType = nsCSSPseudoElements::ePseudo_mozListBullet;
michael@0 6560 break;
michael@0 6561 default:
michael@0 6562 pseudoType = nsCSSPseudoElements::ePseudo_mozListNumber;
michael@0 6563 break;
michael@0 6564 }
michael@0 6565
michael@0 6566 nsIPresShell *shell = presContext->PresShell();
michael@0 6567
michael@0 6568 nsStyleContext* parentStyle =
michael@0 6569 CorrectStyleParentFrame(this,
michael@0 6570 nsCSSPseudoElements::GetPseudoAtom(pseudoType))->StyleContext();
michael@0 6571 nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
michael@0 6572 ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
michael@0 6573 parentStyle, nullptr);
michael@0 6574
michael@0 6575 // Create bullet frame
michael@0 6576 nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
michael@0 6577 bullet->Init(mContent, this, nullptr);
michael@0 6578
michael@0 6579 // If the list bullet frame should be positioned inside then add
michael@0 6580 // it to the flow now.
michael@0 6581 if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
michael@0 6582 styleList->mListStylePosition) {
michael@0 6583 nsFrameList bulletList(bullet, bullet);
michael@0 6584 AddFrames(bulletList, nullptr);
michael@0 6585 Properties().Set(InsideBulletProperty(), bullet);
michael@0 6586 AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
michael@0 6587 } else {
michael@0 6588 nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
michael@0 6589 Properties().Set(OutsideBulletProperty(), bulletList);
michael@0 6590 AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
michael@0 6591 }
michael@0 6592 }
michael@0 6593 }
michael@0 6594
michael@0 6595 return NS_OK;
michael@0 6596 }
michael@0 6597
michael@0 6598 bool
michael@0 6599 nsBlockFrame::BulletIsEmpty() const
michael@0 6600 {
michael@0 6601 NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
michael@0 6602 NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(),
michael@0 6603 "should only care when we have an outside bullet");
michael@0 6604 const nsStyleList* list = StyleList();
michael@0 6605 return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE &&
michael@0 6606 !list->GetListStyleImage();
michael@0 6607 }
michael@0 6608
michael@0 6609 void
michael@0 6610 nsBlockFrame::GetBulletText(nsAString& aText) const
michael@0 6611 {
michael@0 6612 aText.Truncate();
michael@0 6613
michael@0 6614 const nsStyleList* myList = StyleList();
michael@0 6615 if (myList->GetListStyleImage() ||
michael@0 6616 myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC) {
michael@0 6617 aText.Assign(kDiscCharacter);
michael@0 6618 }
michael@0 6619 else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE) {
michael@0 6620 aText.Assign(kCircleCharacter);
michael@0 6621 }
michael@0 6622 else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) {
michael@0 6623 aText.Assign(kSquareCharacter);
michael@0 6624 }
michael@0 6625 else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) {
michael@0 6626 nsBulletFrame* bullet = GetBullet();
michael@0 6627 if (bullet) {
michael@0 6628 nsAutoString text;
michael@0 6629 bullet->GetListItemText(*myList, text);
michael@0 6630 aText = text;
michael@0 6631 }
michael@0 6632 }
michael@0 6633 }
michael@0 6634
michael@0 6635 // static
michael@0 6636 bool
michael@0 6637 nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
michael@0 6638 {
michael@0 6639 nsIContent* content = aFrame->GetContent();
michael@0 6640 if (!content || !content->IsHTML())
michael@0 6641 return false;
michael@0 6642
michael@0 6643 nsIAtom *localName = content->NodeInfo()->NameAtom();
michael@0 6644 return localName == nsGkAtoms::ol ||
michael@0 6645 localName == nsGkAtoms::ul ||
michael@0 6646 localName == nsGkAtoms::dir ||
michael@0 6647 localName == nsGkAtoms::menu;
michael@0 6648 }
michael@0 6649
michael@0 6650 bool
michael@0 6651 nsBlockFrame::RenumberLists(nsPresContext* aPresContext)
michael@0 6652 {
michael@0 6653 if (!FrameStartsCounterScope(this)) {
michael@0 6654 // If this frame doesn't start a counter scope then we don't need
michael@0 6655 // to renumber child list items.
michael@0 6656 return false;
michael@0 6657 }
michael@0 6658
michael@0 6659 MOZ_ASSERT(mContent->IsHTML(),
michael@0 6660 "FrameStartsCounterScope should only return true for HTML elements");
michael@0 6661
michael@0 6662 // Setup initial list ordinal value
michael@0 6663 // XXX Map html's start property to counter-reset style
michael@0 6664 int32_t ordinal = 1;
michael@0 6665 int32_t increment;
michael@0 6666 if (mContent->Tag() == nsGkAtoms::ol &&
michael@0 6667 mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) {
michael@0 6668 increment = -1;
michael@0 6669 } else {
michael@0 6670 increment = 1;
michael@0 6671 }
michael@0 6672
michael@0 6673 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
michael@0 6674 // Must be non-null, since FrameStartsCounterScope only returns true
michael@0 6675 // for HTML elements.
michael@0 6676 MOZ_ASSERT(hc, "How is mContent not HTML?");
michael@0 6677 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
michael@0 6678 if (attr && attr->Type() == nsAttrValue::eInteger) {
michael@0 6679 ordinal = attr->GetIntegerValue();
michael@0 6680 } else if (increment < 0) {
michael@0 6681 // <ol reversed> case, or some other case with a negative increment: count
michael@0 6682 // up the child list
michael@0 6683 ordinal = 0;
michael@0 6684 for (nsIContent* kid = mContent->GetFirstChild(); kid;
michael@0 6685 kid = kid->GetNextSibling()) {
michael@0 6686 if (kid->IsHTML(nsGkAtoms::li)) {
michael@0 6687 // FIXME: This isn't right in terms of what CSS says to do for
michael@0 6688 // overflow of counters (but it only matters when this node has
michael@0 6689 // more than numeric_limits<int32_t>::max() children).
michael@0 6690 ordinal -= increment;
michael@0 6691 }
michael@0 6692 }
michael@0 6693 }
michael@0 6694
michael@0 6695 // Get to first-in-flow
michael@0 6696 nsBlockFrame* block = static_cast<nsBlockFrame*>(FirstInFlow());
michael@0 6697 return RenumberListsInBlock(aPresContext, block, &ordinal, 0, increment);
michael@0 6698 }
michael@0 6699
michael@0 6700 bool
michael@0 6701 nsBlockFrame::RenumberListsInBlock(nsPresContext* aPresContext,
michael@0 6702 nsBlockFrame* aBlockFrame,
michael@0 6703 int32_t* aOrdinal,
michael@0 6704 int32_t aDepth,
michael@0 6705 int32_t aIncrement)
michael@0 6706 {
michael@0 6707 // Examine each line in the block
michael@0 6708 bool foundValidLine;
michael@0 6709 nsBlockInFlowLineIterator bifLineIter(aBlockFrame, &foundValidLine);
michael@0 6710
michael@0 6711 if (!foundValidLine)
michael@0 6712 return false;
michael@0 6713
michael@0 6714 bool renumberedABullet = false;
michael@0 6715
michael@0 6716 do {
michael@0 6717 nsLineList::iterator line = bifLineIter.GetLine();
michael@0 6718 nsIFrame* kid = line->mFirstChild;
michael@0 6719 int32_t n = line->GetChildCount();
michael@0 6720 while (--n >= 0) {
michael@0 6721 bool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal,
michael@0 6722 aDepth, aIncrement);
michael@0 6723 if (kidRenumberedABullet) {
michael@0 6724 line->MarkDirty();
michael@0 6725 renumberedABullet = true;
michael@0 6726 }
michael@0 6727 kid = kid->GetNextSibling();
michael@0 6728 }
michael@0 6729 } while (bifLineIter.Next());
michael@0 6730
michael@0 6731 // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
michael@0 6732 // the bullet and the caller of RenumberLists. But the caller itself
michael@0 6733 // has to be responsible for setting the bit itself, since that caller
michael@0 6734 // might be making a FrameNeedsReflow call, which requires that the
michael@0 6735 // bit not be set yet.
michael@0 6736 if (renumberedABullet && aDepth != 0) {
michael@0 6737 aBlockFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 6738 }
michael@0 6739
michael@0 6740 return renumberedABullet;
michael@0 6741 }
michael@0 6742
michael@0 6743 bool
michael@0 6744 nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext,
michael@0 6745 nsIFrame* aKid,
michael@0 6746 int32_t* aOrdinal,
michael@0 6747 int32_t aDepth,
michael@0 6748 int32_t aIncrement)
michael@0 6749 {
michael@0 6750 NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
michael@0 6751
michael@0 6752 // add in a sanity check for absurdly deep frame trees. See bug 42138
michael@0 6753 if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
michael@0 6754 return false;
michael@0 6755
michael@0 6756 // if the frame is a placeholder, then get the out of flow frame
michael@0 6757 nsIFrame* kid = nsPlaceholderFrame::GetRealFrameFor(aKid);
michael@0 6758 const nsStyleDisplay* display = kid->StyleDisplay();
michael@0 6759
michael@0 6760 // drill down through any wrappers to the real frame
michael@0 6761 kid = kid->GetContentInsertionFrame();
michael@0 6762
michael@0 6763 // possible there is no content insertion frame
michael@0 6764 if (!kid)
michael@0 6765 return false;
michael@0 6766
michael@0 6767 bool kidRenumberedABullet = false;
michael@0 6768
michael@0 6769 // If the frame is a list-item and the frame implements our
michael@0 6770 // block frame API then get its bullet and set the list item
michael@0 6771 // ordinal.
michael@0 6772 if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
michael@0 6773 // Make certain that the frame is a block frame in case
michael@0 6774 // something foreign has crept in.
michael@0 6775 nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid);
michael@0 6776 if (listItem) {
michael@0 6777 nsBulletFrame* bullet = listItem->GetBullet();
michael@0 6778 if (bullet) {
michael@0 6779 bool changed;
michael@0 6780 *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement);
michael@0 6781 if (changed) {
michael@0 6782 kidRenumberedABullet = true;
michael@0 6783
michael@0 6784 // The ordinal changed - mark the bullet frame, and any
michael@0 6785 // intermediate frames between it and the block (are there
michael@0 6786 // ever any?), dirty.
michael@0 6787 // The calling code will make the necessary FrameNeedsReflow
michael@0 6788 // call for the list ancestor.
michael@0 6789 bullet->AddStateBits(NS_FRAME_IS_DIRTY);
michael@0 6790 nsIFrame *f = bullet;
michael@0 6791 do {
michael@0 6792 nsIFrame *parent = f->GetParent();
michael@0 6793 parent->ChildIsDirty(f);
michael@0 6794 f = parent;
michael@0 6795 } while (f != listItem);
michael@0 6796 }
michael@0 6797 }
michael@0 6798
michael@0 6799 // XXX temporary? if the list-item has child list-items they
michael@0 6800 // should be numbered too; especially since the list-item is
michael@0 6801 // itself (ASSUMED!) not to be a counter-resetter.
michael@0 6802 bool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal,
michael@0 6803 aDepth + 1, aIncrement);
michael@0 6804 if (meToo) {
michael@0 6805 kidRenumberedABullet = true;
michael@0 6806 }
michael@0 6807 }
michael@0 6808 }
michael@0 6809 else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
michael@0 6810 if (FrameStartsCounterScope(kid)) {
michael@0 6811 // Don't bother recursing into a block frame that is a new
michael@0 6812 // counter scope. Any list-items in there will be handled by
michael@0 6813 // it.
michael@0 6814 }
michael@0 6815 else {
michael@0 6816 // If the display=block element is a block frame then go ahead
michael@0 6817 // and recurse into it, as it might have child list-items.
michael@0 6818 nsBlockFrame* kidBlock = nsLayoutUtils::GetAsBlock(kid);
michael@0 6819 if (kidBlock) {
michael@0 6820 kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock,
michael@0 6821 aOrdinal, aDepth + 1,
michael@0 6822 aIncrement);
michael@0 6823 }
michael@0 6824 }
michael@0 6825 }
michael@0 6826 return kidRenumberedABullet;
michael@0 6827 }
michael@0 6828
michael@0 6829 void
michael@0 6830 nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
michael@0 6831 nsBlockReflowState& aState,
michael@0 6832 nsHTMLReflowMetrics& aMetrics,
michael@0 6833 nscoord aLineTop)
michael@0 6834 {
michael@0 6835 const nsHTMLReflowState &rs = aState.mReflowState;
michael@0 6836
michael@0 6837 // Reflow the bullet now
michael@0 6838 nsSize availSize;
michael@0 6839 // Make up a width since it doesn't really matter (XXX).
michael@0 6840 availSize.width = aState.mContentArea.width;
michael@0 6841 availSize.height = NS_UNCONSTRAINEDSIZE;
michael@0 6842
michael@0 6843 // Get the reason right.
michael@0 6844 // XXXwaterson Should this look just like the logic in
michael@0 6845 // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
michael@0 6846 nsHTMLReflowState reflowState(aState.mPresContext, rs,
michael@0 6847 aBulletFrame, availSize);
michael@0 6848 nsReflowStatus status;
michael@0 6849 aBulletFrame->WillReflow(aState.mPresContext);
michael@0 6850 aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
michael@0 6851
michael@0 6852 // Get the float available space using our saved state from before we
michael@0 6853 // started reflowing the block, so that we ignore any floats inside
michael@0 6854 // the block.
michael@0 6855 // FIXME: aLineTop isn't actually set correctly by some callers, since
michael@0 6856 // they reposition the line.
michael@0 6857 nsRect floatAvailSpace =
michael@0 6858 aState.GetFloatAvailableSpaceWithState(aLineTop,
michael@0 6859 &aState.mFloatManagerStateBefore)
michael@0 6860 .mRect;
michael@0 6861 // FIXME (bug 25888): need to check the entire region that the first
michael@0 6862 // line overlaps, not just the top pixel.
michael@0 6863
michael@0 6864 // Place the bullet now. We want to place the bullet relative to the
michael@0 6865 // border-box of the associated block (using the right/left margin of
michael@0 6866 // the bullet frame as separation). However, if a line box would be
michael@0 6867 // displaced by floats that are *outside* the associated block, we
michael@0 6868 // want to displace it by the same amount. That is, we act as though
michael@0 6869 // the edge of the floats is the content-edge of the block, and place
michael@0 6870 // the bullet at a position offset from there by the block's padding,
michael@0 6871 // the block's border, and the bullet frame's margin.
michael@0 6872
michael@0 6873 // IStart from floatAvailSpace gives us the content/float start edge
michael@0 6874 // in the current writing mode. Then we subtract out the start
michael@0 6875 // border/padding and the bullet's width and margin to offset the position.
michael@0 6876 WritingMode wm = rs.GetWritingMode();
michael@0 6877 nscoord containerWidth = floatAvailSpace.XMost();
michael@0 6878 LogicalRect logicalFAS(wm, floatAvailSpace, containerWidth);
michael@0 6879 // Get the bullet's margin, converted to our writing mode so that we can
michael@0 6880 // combine it with other logical values here.
michael@0 6881 WritingMode bulletWM = reflowState.GetWritingMode();
michael@0 6882 LogicalMargin bulletMargin =
michael@0 6883 reflowState.ComputedLogicalMargin().ConvertTo(wm, bulletWM);
michael@0 6884 nscoord iStart = logicalFAS.IStart(wm) -
michael@0 6885 rs.ComputedLogicalBorderPadding().IStart(wm) -
michael@0 6886 bulletMargin.IEnd(wm) -
michael@0 6887 aMetrics.ISize();
michael@0 6888
michael@0 6889 // Approximate the bullets position; vertical alignment will provide
michael@0 6890 // the final vertical location. We pass our writing-mode here, because
michael@0 6891 // it may be different from the bullet frame's mode.
michael@0 6892 nscoord bStart = logicalFAS.BStart(wm);
michael@0 6893 aBulletFrame->SetRect(wm, LogicalRect(wm, LogicalPoint(wm, iStart, bStart),
michael@0 6894 LogicalSize(wm, aMetrics.ISize(),
michael@0 6895 aMetrics.BSize())),
michael@0 6896 containerWidth);
michael@0 6897 aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState,
michael@0 6898 nsDidReflowStatus::FINISHED);
michael@0 6899 }
michael@0 6900
michael@0 6901 // This is used to scan frames for any float placeholders, add their
michael@0 6902 // floats to the list represented by aList, and remove the
michael@0 6903 // floats from whatever list they might be in. We don't search descendants
michael@0 6904 // that are float containing blocks. Floats that or not children of 'this'
michael@0 6905 // are ignored (they are not added to aList).
michael@0 6906 void
michael@0 6907 nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
michael@0 6908 bool aCollectSiblings)
michael@0 6909 {
michael@0 6910 while (aFrame) {
michael@0 6911 // Don't descend into float containing blocks.
michael@0 6912 if (!aFrame->IsFloatContainingBlock()) {
michael@0 6913 nsIFrame *outOfFlowFrame =
michael@0 6914 aFrame->GetType() == nsGkAtoms::placeholderFrame ?
michael@0 6915 nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nullptr;
michael@0 6916 if (outOfFlowFrame && outOfFlowFrame->GetParent() == this) {
michael@0 6917 RemoveFloat(outOfFlowFrame);
michael@0 6918 aList.AppendFrame(nullptr, outOfFlowFrame);
michael@0 6919 // FIXME: By not pulling floats whose parent is one of our
michael@0 6920 // later siblings, are we risking the pushed floats getting
michael@0 6921 // out-of-order?
michael@0 6922 // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
michael@0 6923 }
michael@0 6924
michael@0 6925 DoCollectFloats(aFrame->GetFirstPrincipalChild(), aList, true);
michael@0 6926 DoCollectFloats(aFrame->GetFirstChild(kOverflowList), aList, true);
michael@0 6927 }
michael@0 6928 if (!aCollectSiblings)
michael@0 6929 break;
michael@0 6930 aFrame = aFrame->GetNextSibling();
michael@0 6931 }
michael@0 6932 }
michael@0 6933
michael@0 6934 void
michael@0 6935 nsBlockFrame::CheckFloats(nsBlockReflowState& aState)
michael@0 6936 {
michael@0 6937 #ifdef DEBUG
michael@0 6938 // If any line is still dirty, that must mean we're going to reflow this
michael@0 6939 // block again soon (e.g. because we bailed out after noticing that
michael@0 6940 // clearance was imposed), so don't worry if the floats are out of sync.
michael@0 6941 bool anyLineDirty = false;
michael@0 6942
michael@0 6943 // Check that the float list is what we would have built
michael@0 6944 nsAutoTArray<nsIFrame*, 8> lineFloats;
michael@0 6945 for (line_iterator line = begin_lines(), line_end = end_lines();
michael@0 6946 line != line_end; ++line) {
michael@0 6947 if (line->HasFloats()) {
michael@0 6948 nsFloatCache* fc = line->GetFirstFloat();
michael@0 6949 while (fc) {
michael@0 6950 lineFloats.AppendElement(fc->mFloat);
michael@0 6951 fc = fc->Next();
michael@0 6952 }
michael@0 6953 }
michael@0 6954 if (line->IsDirty()) {
michael@0 6955 anyLineDirty = true;
michael@0 6956 }
michael@0 6957 }
michael@0 6958
michael@0 6959 nsAutoTArray<nsIFrame*, 8> storedFloats;
michael@0 6960 bool equal = true;
michael@0 6961 uint32_t i = 0;
michael@0 6962 for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
michael@0 6963 if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
michael@0 6964 continue;
michael@0 6965 storedFloats.AppendElement(f);
michael@0 6966 if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
michael@0 6967 equal = false;
michael@0 6968 }
michael@0 6969 ++i;
michael@0 6970 }
michael@0 6971
michael@0 6972 if ((!equal || lineFloats.Length() != storedFloats.Length()) && !anyLineDirty) {
michael@0 6973 NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
michael@0 6974 #if defined(DEBUG_roc)
michael@0 6975 nsFrame::RootFrameList(PresContext(), stdout, 0);
michael@0 6976 for (i = 0; i < lineFloats.Length(); ++i) {
michael@0 6977 printf("Line float: %p\n", lineFloats.ElementAt(i));
michael@0 6978 }
michael@0 6979 for (i = 0; i < storedFloats.Length(); ++i) {
michael@0 6980 printf("Stored float: %p\n", storedFloats.ElementAt(i));
michael@0 6981 }
michael@0 6982 #endif
michael@0 6983 }
michael@0 6984 #endif
michael@0 6985
michael@0 6986 const nsFrameList* oofs = GetOverflowOutOfFlows();
michael@0 6987 if (oofs && oofs->NotEmpty()) {
michael@0 6988 // Floats that were pushed should be removed from our float
michael@0 6989 // manager. Otherwise the float manager's YMost or XMost might
michael@0 6990 // be larger than necessary, causing this block to get an
michael@0 6991 // incorrect desired height (or width). Some of these floats
michael@0 6992 // may not actually have been added to the float manager because
michael@0 6993 // they weren't reflowed before being pushed; that's OK,
michael@0 6994 // RemoveRegions will ignore them. It is safe to do this here
michael@0 6995 // because we know from here on the float manager will only be
michael@0 6996 // used for its XMost and YMost, not to place new floats and
michael@0 6997 // lines.
michael@0 6998 aState.mFloatManager->RemoveTrailingRegions(oofs->FirstChild());
michael@0 6999 }
michael@0 7000 }
michael@0 7001
michael@0 7002 void
michael@0 7003 nsBlockFrame::IsMarginRoot(bool* aTopMarginRoot, bool* aBottomMarginRoot)
michael@0 7004 {
michael@0 7005 if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT)) {
michael@0 7006 nsIFrame* parent = GetParent();
michael@0 7007 if (!parent || parent->IsFloatContainingBlock()) {
michael@0 7008 *aTopMarginRoot = false;
michael@0 7009 *aBottomMarginRoot = false;
michael@0 7010 return;
michael@0 7011 }
michael@0 7012 if (parent->GetType() == nsGkAtoms::columnSetFrame) {
michael@0 7013 *aTopMarginRoot = GetPrevInFlow() == nullptr;
michael@0 7014 *aBottomMarginRoot = GetNextInFlow() == nullptr;
michael@0 7015 return;
michael@0 7016 }
michael@0 7017 }
michael@0 7018
michael@0 7019 *aTopMarginRoot = true;
michael@0 7020 *aBottomMarginRoot = true;
michael@0 7021 }
michael@0 7022
michael@0 7023 /* static */
michael@0 7024 bool
michael@0 7025 nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock)
michael@0 7026 {
michael@0 7027 NS_PRECONDITION(aBlock, "Must have a frame");
michael@0 7028 NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block");
michael@0 7029
michael@0 7030 nsIFrame* parent = aBlock->GetParent();
michael@0 7031 return (aBlock->GetStateBits() & NS_BLOCK_FLOAT_MGR) ||
michael@0 7032 (parent && !parent->IsFloatContainingBlock());
michael@0 7033 }
michael@0 7034
michael@0 7035 /* static */
michael@0 7036 bool
michael@0 7037 nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame)
michael@0 7038 {
michael@0 7039 return aFrame->IsFrameOfType(nsIFrame::eBlockFrame) &&
michael@0 7040 !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
michael@0 7041 !(aFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR);
michael@0 7042 }
michael@0 7043
michael@0 7044 // Note that this width can vary based on the vertical position.
michael@0 7045 // However, the cases where it varies are the cases where the width fits
michael@0 7046 // in the available space given, which means that variation shouldn't
michael@0 7047 // matter.
michael@0 7048 /* static */
michael@0 7049 nsBlockFrame::ReplacedElementWidthToClear
michael@0 7050 nsBlockFrame::WidthToClearPastFloats(nsBlockReflowState& aState,
michael@0 7051 const nsRect& aFloatAvailableSpace,
michael@0 7052 nsIFrame* aFrame)
michael@0 7053 {
michael@0 7054 nscoord leftOffset, rightOffset;
michael@0 7055 nsCSSOffsetState offsetState(aFrame, aState.mReflowState.rendContext,
michael@0 7056 aState.mContentArea.width);
michael@0 7057
michael@0 7058 ReplacedElementWidthToClear result;
michael@0 7059 aState.ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace,
michael@0 7060 leftOffset, rightOffset);
michael@0 7061 nscoord availWidth = aState.mContentArea.width - leftOffset - rightOffset;
michael@0 7062
michael@0 7063 // We actually don't want the min width here; see bug 427782; we only
michael@0 7064 // want to displace if the width won't compute to a value small enough
michael@0 7065 // to fit.
michael@0 7066 // All we really need here is the result of ComputeSize, and we
michael@0 7067 // could *almost* get that from an nsCSSOffsetState, except for the
michael@0 7068 // last argument.
michael@0 7069 nsSize availSpace(availWidth, NS_UNCONSTRAINEDSIZE);
michael@0 7070 nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
michael@0 7071 aFrame, availSpace);
michael@0 7072 result.borderBoxWidth = reflowState.ComputedWidth() +
michael@0 7073 reflowState.ComputedPhysicalBorderPadding().LeftRight();
michael@0 7074 // Use the margins from offsetState rather than reflowState so that
michael@0 7075 // they aren't reduced by ignoring margins in overconstrained cases.
michael@0 7076 result.marginLeft = offsetState.ComputedPhysicalMargin().left;
michael@0 7077 result.marginRight = offsetState.ComputedPhysicalMargin().right;
michael@0 7078 return result;
michael@0 7079 }
michael@0 7080
michael@0 7081 /* static */
michael@0 7082 nsBlockFrame*
michael@0 7083 nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate)
michael@0 7084 {
michael@0 7085 nsBlockFrame* block = nullptr;
michael@0 7086 while(aCandidate) {
michael@0 7087 block = nsLayoutUtils::GetAsBlock(aCandidate);
michael@0 7088 if (block) {
michael@0 7089 // yay, candidate is a block!
michael@0 7090 return block;
michael@0 7091 }
michael@0 7092 // Not a block. Check its parent next.
michael@0 7093 aCandidate = aCandidate->GetParent();
michael@0 7094 }
michael@0 7095 NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
michael@0 7096 return nullptr;
michael@0 7097 }
michael@0 7098
michael@0 7099 void
michael@0 7100 nsBlockFrame::ComputeFinalHeight(const nsHTMLReflowState& aReflowState,
michael@0 7101 nsReflowStatus* aStatus,
michael@0 7102 nscoord aContentHeight,
michael@0 7103 const nsMargin& aBorderPadding,
michael@0 7104 nsHTMLReflowMetrics& aMetrics,
michael@0 7105 nscoord aConsumed)
michael@0 7106 {
michael@0 7107
michael@0 7108 // Figure out how much of the computed height should be
michael@0 7109 // applied to this frame.
michael@0 7110 nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState,
michael@0 7111 aConsumed);
michael@0 7112 NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
michael@0 7113 && computedHeightLeftOver ),
michael@0 7114 "overflow container must not have computedHeightLeftOver");
michael@0 7115
michael@0 7116 aMetrics.Height() =
michael@0 7117 NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding.top,
michael@0 7118 computedHeightLeftOver),
michael@0 7119 aBorderPadding.bottom);
michael@0 7120
michael@0 7121 if (NS_FRAME_IS_NOT_COMPLETE(*aStatus)
michael@0 7122 && aMetrics.Height() < aReflowState.AvailableHeight()) {
michael@0 7123 // We ran out of height on this page but we're incomplete
michael@0 7124 // Set status to complete except for overflow
michael@0 7125 NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus);
michael@0 7126 }
michael@0 7127
michael@0 7128 if (NS_FRAME_IS_COMPLETE(*aStatus)) {
michael@0 7129 if (computedHeightLeftOver > 0 &&
michael@0 7130 NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
michael@0 7131 aMetrics.Height() > aReflowState.AvailableHeight()) {
michael@0 7132 if (ShouldAvoidBreakInside(aReflowState)) {
michael@0 7133 *aStatus = NS_INLINE_LINE_BREAK_BEFORE();
michael@0 7134 return;
michael@0 7135 }
michael@0 7136 // We don't fit and we consumed some of the computed height,
michael@0 7137 // so we should consume all the available height and then
michael@0 7138 // break. If our bottom border/padding straddles the break
michael@0 7139 // point, then this will increase our height and push the
michael@0 7140 // border/padding to the next page/column.
michael@0 7141 aMetrics.Height() = std::max(aReflowState.AvailableHeight(),
michael@0 7142 aContentHeight);
michael@0 7143 NS_FRAME_SET_INCOMPLETE(*aStatus);
michael@0 7144 if (!GetNextInFlow())
michael@0 7145 *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
michael@0 7146 }
michael@0 7147 }
michael@0 7148 }
michael@0 7149
michael@0 7150 nsresult
michael@0 7151 nsBlockFrame::ResolveBidi()
michael@0 7152 {
michael@0 7153 NS_ASSERTION(!GetPrevInFlow(),
michael@0 7154 "ResolveBidi called on non-first continuation");
michael@0 7155
michael@0 7156 nsPresContext* presContext = PresContext();
michael@0 7157 if (!presContext->BidiEnabled()) {
michael@0 7158 return NS_OK;
michael@0 7159 }
michael@0 7160
michael@0 7161 return nsBidiPresUtils::Resolve(this);
michael@0 7162 }
michael@0 7163
michael@0 7164 #ifdef DEBUG
michael@0 7165 void
michael@0 7166 nsBlockFrame::VerifyLines(bool aFinalCheckOK)
michael@0 7167 {
michael@0 7168 if (!gVerifyLines) {
michael@0 7169 return;
michael@0 7170 }
michael@0 7171 if (mLines.empty()) {
michael@0 7172 return;
michael@0 7173 }
michael@0 7174
michael@0 7175 nsLineBox* cursor = GetLineCursor();
michael@0 7176
michael@0 7177 // Add up the counts on each line. Also validate that IsFirstLine is
michael@0 7178 // set properly.
michael@0 7179 int32_t count = 0;
michael@0 7180 line_iterator line, line_end;
michael@0 7181 for (line = begin_lines(), line_end = end_lines();
michael@0 7182 line != line_end;
michael@0 7183 ++line) {
michael@0 7184 if (line == cursor) {
michael@0 7185 cursor = nullptr;
michael@0 7186 }
michael@0 7187 if (aFinalCheckOK) {
michael@0 7188 NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
michael@0 7189 if (line->IsBlock()) {
michael@0 7190 NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
michael@0 7191 }
michael@0 7192 }
michael@0 7193 count += line->GetChildCount();
michael@0 7194 }
michael@0 7195
michael@0 7196 // Then count the frames
michael@0 7197 int32_t frameCount = 0;
michael@0 7198 nsIFrame* frame = mLines.front()->mFirstChild;
michael@0 7199 while (frame) {
michael@0 7200 frameCount++;
michael@0 7201 frame = frame->GetNextSibling();
michael@0 7202 }
michael@0 7203 NS_ASSERTION(count == frameCount, "bad line list");
michael@0 7204
michael@0 7205 // Next: test that each line has right number of frames on it
michael@0 7206 for (line = begin_lines(), line_end = end_lines();
michael@0 7207 line != line_end;
michael@0 7208 ) {
michael@0 7209 count = line->GetChildCount();
michael@0 7210 frame = line->mFirstChild;
michael@0 7211 while (--count >= 0) {
michael@0 7212 frame = frame->GetNextSibling();
michael@0 7213 }
michael@0 7214 ++line;
michael@0 7215 if ((line != line_end) && (0 != line->GetChildCount())) {
michael@0 7216 NS_ASSERTION(frame == line->mFirstChild, "bad line list");
michael@0 7217 }
michael@0 7218 }
michael@0 7219
michael@0 7220 if (cursor) {
michael@0 7221 FrameLines* overflowLines = GetOverflowLines();
michael@0 7222 if (overflowLines) {
michael@0 7223 line_iterator line = overflowLines->mLines.begin();
michael@0 7224 line_iterator line_end = overflowLines->mLines.end();
michael@0 7225 for (; line != line_end; ++line) {
michael@0 7226 if (line == cursor) {
michael@0 7227 cursor = nullptr;
michael@0 7228 break;
michael@0 7229 }
michael@0 7230 }
michael@0 7231 }
michael@0 7232 }
michael@0 7233 NS_ASSERTION(!cursor, "stale LineCursorProperty");
michael@0 7234 }
michael@0 7235
michael@0 7236 void
michael@0 7237 nsBlockFrame::VerifyOverflowSituation()
michael@0 7238 {
michael@0 7239 nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow());
michael@0 7240 while (flow) {
michael@0 7241 FrameLines* overflowLines = flow->GetOverflowLines();
michael@0 7242 if (overflowLines) {
michael@0 7243 NS_ASSERTION(!overflowLines->mLines.empty(),
michael@0 7244 "should not be empty if present");
michael@0 7245 NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
michael@0 7246 "bad overflow lines");
michael@0 7247 NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
michael@0 7248 overflowLines->mFrames.FirstChild(),
michael@0 7249 "bad overflow frames / lines");
michael@0 7250 }
michael@0 7251 nsLineBox* cursor = flow->GetLineCursor();
michael@0 7252 if (cursor) {
michael@0 7253 line_iterator line = flow->begin_lines();
michael@0 7254 line_iterator line_end = flow->end_lines();
michael@0 7255 for (; line != line_end && line != cursor; ++line)
michael@0 7256 ;
michael@0 7257 if (line == line_end && overflowLines) {
michael@0 7258 line = overflowLines->mLines.begin();
michael@0 7259 line_end = overflowLines->mLines.end();
michael@0 7260 for (; line != line_end && line != cursor; ++line)
michael@0 7261 ;
michael@0 7262 }
michael@0 7263 MOZ_ASSERT(line != line_end, "stale LineCursorProperty");
michael@0 7264 }
michael@0 7265 flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
michael@0 7266 }
michael@0 7267 }
michael@0 7268
michael@0 7269 int32_t
michael@0 7270 nsBlockFrame::GetDepth() const
michael@0 7271 {
michael@0 7272 int32_t depth = 0;
michael@0 7273 nsIFrame* parent = mParent;
michael@0 7274 while (parent) {
michael@0 7275 parent = parent->GetParent();
michael@0 7276 depth++;
michael@0 7277 }
michael@0 7278 return depth;
michael@0 7279 }
michael@0 7280 #endif

mercurial