layout/generic/nsLineBox.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 /* representation of one line within a block frame, a CSS line box */
michael@0 8
michael@0 9 #include "nsLineBox.h"
michael@0 10 #include "prprf.h"
michael@0 11 #include "nsFrame.h"
michael@0 12 #include "nsPresArena.h"
michael@0 13 #include "nsBidiPresUtils.h"
michael@0 14 #include "nsIFrameInlines.h"
michael@0 15 #include "WritingModes.h"
michael@0 16 #include "mozilla/Assertions.h"
michael@0 17 #include "mozilla/Likely.h"
michael@0 18 #include "nsPrintfCString.h"
michael@0 19
michael@0 20 #ifdef DEBUG
michael@0 21 static int32_t ctorCount;
michael@0 22 int32_t nsLineBox::GetCtorCount() { return ctorCount; }
michael@0 23 #endif
michael@0 24
michael@0 25 #ifndef _MSC_VER
michael@0 26 // static nsLineBox constant; initialized in the header file.
michael@0 27 const uint32_t nsLineBox::kMinChildCountForHashtable;
michael@0 28 #endif
michael@0 29
michael@0 30 using namespace mozilla;
michael@0 31
michael@0 32 nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
michael@0 33 : mFirstChild(aFrame)
michael@0 34 , mContainerWidth(-1)
michael@0 35 , mBounds(WritingMode()) // mBounds will be initialized with the correct
michael@0 36 // writing mode when it is set
michael@0 37 // NOTE: memory is already zeroed since we allocate with AllocateByObjectID.
michael@0 38 {
michael@0 39 MOZ_COUNT_CTOR(nsLineBox);
michael@0 40 #ifdef DEBUG
michael@0 41 ++ctorCount;
michael@0 42 NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
michael@0 43 nsIFrame* f = aFrame;
michael@0 44 for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
michael@0 45 NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
michael@0 46 "wrong kind of child frame");
michael@0 47 }
michael@0 48 #endif
michael@0 49
michael@0 50 static_assert(NS_STYLE_CLEAR_MAX <= 15,
michael@0 51 "FlagBits needs more bits to store the full range of "
michael@0 52 "break type ('clear') values");
michael@0 53 #if NS_STYLE_CLEAR_NONE > 0
michael@0 54 mFlags.mBreakType = NS_STYLE_CLEAR_NONE;
michael@0 55 #endif
michael@0 56 mChildCount = aCount;
michael@0 57 MarkDirty();
michael@0 58 mFlags.mBlock = aIsBlock;
michael@0 59 }
michael@0 60
michael@0 61 nsLineBox::~nsLineBox()
michael@0 62 {
michael@0 63 MOZ_COUNT_DTOR(nsLineBox);
michael@0 64 if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
michael@0 65 delete mFrames;
michael@0 66 }
michael@0 67 Cleanup();
michael@0 68 }
michael@0 69
michael@0 70 nsLineBox*
michael@0 71 NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
michael@0 72 {
michael@0 73 return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
michael@0 74 }
michael@0 75
michael@0 76 nsLineBox*
michael@0 77 NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
michael@0 78 nsIFrame* aFrame, int32_t aCount)
michael@0 79 {
michael@0 80 nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
michael@0 81 newLine->NoteFramesMovedFrom(aFromLine);
michael@0 82 newLine->mContainerWidth = aFromLine->mContainerWidth;
michael@0 83 return newLine;
michael@0 84 }
michael@0 85
michael@0 86 void
michael@0 87 nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
michael@0 88 {
michael@0 89 MOZ_ASSERT(!mFlags.mHasHashedFrames);
michael@0 90 MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
michael@0 91 mFrames = aFromLine->mFrames;
michael@0 92 mFlags.mHasHashedFrames = 1;
michael@0 93 aFromLine->mFlags.mHasHashedFrames = 0;
michael@0 94 aFromLine->mChildCount = aFromLineNewCount;
michael@0 95 // remove aFromLine's frames that aren't on this line
michael@0 96 nsIFrame* f = aFromLine->mFirstChild;
michael@0 97 for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
michael@0 98 mFrames->RemoveEntry(f);
michael@0 99 }
michael@0 100 }
michael@0 101
michael@0 102 void
michael@0 103 nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
michael@0 104 {
michael@0 105 uint32_t fromCount = aFromLine->GetChildCount();
michael@0 106 uint32_t toCount = GetChildCount();
michael@0 107 MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
michael@0 108 uint32_t fromNewCount = fromCount - toCount;
michael@0 109 if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
michael@0 110 aFromLine->mChildCount = fromNewCount;
michael@0 111 MOZ_ASSERT(toCount < kMinChildCountForHashtable);
michael@0 112 } else if (fromNewCount < kMinChildCountForHashtable) {
michael@0 113 // aFromLine has a hash table but will not have it after moving the frames
michael@0 114 // so this line can steal the hash table if it needs it.
michael@0 115 if (toCount >= kMinChildCountForHashtable) {
michael@0 116 StealHashTableFrom(aFromLine, fromNewCount);
michael@0 117 } else {
michael@0 118 delete aFromLine->mFrames;
michael@0 119 aFromLine->mFlags.mHasHashedFrames = 0;
michael@0 120 aFromLine->mChildCount = fromNewCount;
michael@0 121 }
michael@0 122 } else {
michael@0 123 // aFromLine still needs a hash table.
michael@0 124 if (toCount < kMinChildCountForHashtable) {
michael@0 125 // remove the moved frames from it
michael@0 126 nsIFrame* f = mFirstChild;
michael@0 127 for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
michael@0 128 aFromLine->mFrames->RemoveEntry(f);
michael@0 129 }
michael@0 130 } else if (toCount <= fromNewCount) {
michael@0 131 // This line needs a hash table, allocate a hash table for it since that
michael@0 132 // means fewer hash ops.
michael@0 133 nsIFrame* f = mFirstChild;
michael@0 134 for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
michael@0 135 aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
michael@0 136 }
michael@0 137 SwitchToHashtable(); // toCount PutEntry
michael@0 138 } else {
michael@0 139 // This line needs a hash table, but it's fewer hash ops to steal
michael@0 140 // aFromLine's hash table and allocate a new hash table for that line.
michael@0 141 StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
michael@0 142 aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
michael@0 143 }
michael@0 144 }
michael@0 145 }
michael@0 146
michael@0 147 // Overloaded new operator. Uses an arena (which comes from the presShell)
michael@0 148 // to perform the allocation.
michael@0 149 void*
michael@0 150 nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
michael@0 151 {
michael@0 152 return aPresShell->AllocateByObjectID(nsPresArena::nsLineBox_id, sz);
michael@0 153 }
michael@0 154
michael@0 155 void
michael@0 156 nsLineBox::Destroy(nsIPresShell* aPresShell)
michael@0 157 {
michael@0 158 this->nsLineBox::~nsLineBox();
michael@0 159 aPresShell->FreeByObjectID(nsPresArena::nsLineBox_id, this);
michael@0 160 }
michael@0 161
michael@0 162 void
michael@0 163 nsLineBox::Cleanup()
michael@0 164 {
michael@0 165 if (mData) {
michael@0 166 if (IsBlock()) {
michael@0 167 delete mBlockData;
michael@0 168 }
michael@0 169 else {
michael@0 170 delete mInlineData;
michael@0 171 }
michael@0 172 mData = nullptr;
michael@0 173 }
michael@0 174 }
michael@0 175
michael@0 176 #ifdef DEBUG_FRAME_DUMP
michael@0 177 static void
michael@0 178 ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats)
michael@0 179 {
michael@0 180 nsFloatCache* fc = aFloats.Head();
michael@0 181 while (fc) {
michael@0 182 nsCString str(aPrefix);
michael@0 183 nsIFrame* frame = fc->mFloat;
michael@0 184 str += nsPrintfCString("floatframe@%p ", static_cast<void*>(frame));
michael@0 185 if (frame) {
michael@0 186 nsAutoString frameName;
michael@0 187 frame->GetFrameName(frameName);
michael@0 188 str += NS_ConvertUTF16toUTF8(frameName).get();
michael@0 189 }
michael@0 190 else {
michael@0 191 str += "\n###!!! NULL out-of-flow frame";
michael@0 192 }
michael@0 193 fprintf_stderr(out, "%s\n", str.get());
michael@0 194 fc = fc->Next();
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 const char *
michael@0 199 BreakTypeToString(uint8_t aBreakType)
michael@0 200 {
michael@0 201 switch (aBreakType) {
michael@0 202 case NS_STYLE_CLEAR_NONE: return "nobr";
michael@0 203 case NS_STYLE_CLEAR_LEFT: return "leftbr";
michael@0 204 case NS_STYLE_CLEAR_RIGHT: return "rightbr";
michael@0 205 case NS_STYLE_CLEAR_BOTH: return "leftbr+rightbr";
michael@0 206 case NS_STYLE_CLEAR_LINE: return "linebr";
michael@0 207 default:
michael@0 208 break;
michael@0 209 }
michael@0 210 return "unknown";
michael@0 211 }
michael@0 212
michael@0 213 char*
michael@0 214 nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
michael@0 215 {
michael@0 216 PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
michael@0 217 IsBlock() ? "block" : "inline",
michael@0 218 IsDirty() ? "dirty" : "clean",
michael@0 219 IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
michael@0 220 IsImpactedByFloat() ? "impacted" : "not impacted",
michael@0 221 IsLineWrapped() ? "wrapped" : "not wrapped",
michael@0 222 BreakTypeToString(GetBreakTypeBefore()),
michael@0 223 BreakTypeToString(GetBreakTypeAfter()),
michael@0 224 mAllFlags);
michael@0 225 return aBuf;
michael@0 226 }
michael@0 227
michael@0 228 void
michael@0 229 nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
michael@0 230 {
michael@0 231 nsCString str;
michael@0 232 while (aIndent-- > 0) {
michael@0 233 str += " ";
michael@0 234 }
michael@0 235 List(out, str.get(), aFlags);
michael@0 236 }
michael@0 237
michael@0 238 void
michael@0 239 nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
michael@0 240 {
michael@0 241 nsCString str(aPrefix);
michael@0 242 char cbuf[100];
michael@0 243 str += nsPrintfCString("line %p: count=%d state=%s ",
michael@0 244 static_cast<const void*>(this), GetChildCount(),
michael@0 245 StateToString(cbuf, sizeof(cbuf)));
michael@0 246 if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) {
michael@0 247 str += nsPrintfCString("bm=%d ", GetCarriedOutBottomMargin().get());
michael@0 248 }
michael@0 249 nsRect bounds = GetPhysicalBounds();
michael@0 250 str += nsPrintfCString("{%d,%d,%d,%d} ",
michael@0 251 bounds.x, bounds.y, bounds.width, bounds.height);
michael@0 252 if (mData &&
michael@0 253 (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
michael@0 254 !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
michael@0 255 str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
michael@0 256 mData->mOverflowAreas.VisualOverflow().x,
michael@0 257 mData->mOverflowAreas.VisualOverflow().y,
michael@0 258 mData->mOverflowAreas.VisualOverflow().width,
michael@0 259 mData->mOverflowAreas.VisualOverflow().height,
michael@0 260 mData->mOverflowAreas.ScrollableOverflow().x,
michael@0 261 mData->mOverflowAreas.ScrollableOverflow().y,
michael@0 262 mData->mOverflowAreas.ScrollableOverflow().width,
michael@0 263 mData->mOverflowAreas.ScrollableOverflow().height);
michael@0 264 }
michael@0 265 fprintf_stderr(out, "%s<\n", str.get());
michael@0 266
michael@0 267 nsIFrame* frame = mFirstChild;
michael@0 268 int32_t n = GetChildCount();
michael@0 269 nsCString pfx(aPrefix);
michael@0 270 pfx += " ";
michael@0 271 while (--n >= 0) {
michael@0 272 frame->List(out, pfx.get(), aFlags);
michael@0 273 frame = frame->GetNextSibling();
michael@0 274 }
michael@0 275
michael@0 276 if (HasFloats()) {
michael@0 277 fprintf_stderr(out, "%s> floats <\n", aPrefix);
michael@0 278 ListFloats(out, pfx.get(), mInlineData->mFloats);
michael@0 279 }
michael@0 280 fprintf_stderr(out, "%s>\n", aPrefix);
michael@0 281 }
michael@0 282 #endif
michael@0 283
michael@0 284 #ifdef DEBUG
michael@0 285 nsIFrame*
michael@0 286 nsLineBox::LastChild() const
michael@0 287 {
michael@0 288 nsIFrame* frame = mFirstChild;
michael@0 289 int32_t n = GetChildCount() - 1;
michael@0 290 while (--n >= 0) {
michael@0 291 frame = frame->GetNextSibling();
michael@0 292 }
michael@0 293 return frame;
michael@0 294 }
michael@0 295 #endif
michael@0 296
michael@0 297 int32_t
michael@0 298 nsLineBox::IndexOf(nsIFrame* aFrame) const
michael@0 299 {
michael@0 300 int32_t i, n = GetChildCount();
michael@0 301 nsIFrame* frame = mFirstChild;
michael@0 302 for (i = 0; i < n; i++) {
michael@0 303 if (frame == aFrame) {
michael@0 304 return i;
michael@0 305 }
michael@0 306 frame = frame->GetNextSibling();
michael@0 307 }
michael@0 308 return -1;
michael@0 309 }
michael@0 310
michael@0 311 bool
michael@0 312 nsLineBox::IsEmpty() const
michael@0 313 {
michael@0 314 if (IsBlock())
michael@0 315 return mFirstChild->IsEmpty();
michael@0 316
michael@0 317 int32_t n;
michael@0 318 nsIFrame *kid;
michael@0 319 for (n = GetChildCount(), kid = mFirstChild;
michael@0 320 n > 0;
michael@0 321 --n, kid = kid->GetNextSibling())
michael@0 322 {
michael@0 323 if (!kid->IsEmpty())
michael@0 324 return false;
michael@0 325 }
michael@0 326 if (HasBullet()) {
michael@0 327 return false;
michael@0 328 }
michael@0 329 return true;
michael@0 330 }
michael@0 331
michael@0 332 bool
michael@0 333 nsLineBox::CachedIsEmpty()
michael@0 334 {
michael@0 335 if (mFlags.mDirty) {
michael@0 336 return IsEmpty();
michael@0 337 }
michael@0 338
michael@0 339 if (mFlags.mEmptyCacheValid) {
michael@0 340 return mFlags.mEmptyCacheState;
michael@0 341 }
michael@0 342
michael@0 343 bool result;
michael@0 344 if (IsBlock()) {
michael@0 345 result = mFirstChild->CachedIsEmpty();
michael@0 346 } else {
michael@0 347 int32_t n;
michael@0 348 nsIFrame *kid;
michael@0 349 result = true;
michael@0 350 for (n = GetChildCount(), kid = mFirstChild;
michael@0 351 n > 0;
michael@0 352 --n, kid = kid->GetNextSibling())
michael@0 353 {
michael@0 354 if (!kid->CachedIsEmpty()) {
michael@0 355 result = false;
michael@0 356 break;
michael@0 357 }
michael@0 358 }
michael@0 359 if (HasBullet()) {
michael@0 360 result = false;
michael@0 361 }
michael@0 362 }
michael@0 363
michael@0 364 mFlags.mEmptyCacheValid = true;
michael@0 365 mFlags.mEmptyCacheState = result;
michael@0 366 return result;
michael@0 367 }
michael@0 368
michael@0 369 void
michael@0 370 nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
michael@0 371 nsIFrame* aDestructRoot, nsFrameList* aFrames)
michael@0 372 {
michael@0 373 nsIPresShell* shell = aPresContext->PresShell();
michael@0 374
michael@0 375 // Keep our line list and frame list up to date as we
michael@0 376 // remove frames, in case something wants to traverse the
michael@0 377 // frame tree while we're destroying.
michael@0 378 while (!aLines.empty()) {
michael@0 379 nsLineBox* line = aLines.front();
michael@0 380 if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
michael@0 381 line->SwitchToCounter(); // Avoid expensive has table removals.
michael@0 382 }
michael@0 383 while (line->GetChildCount() > 0) {
michael@0 384 nsIFrame* child = aFrames->RemoveFirstChild();
michael@0 385 MOZ_ASSERT(child == line->mFirstChild, "Lines out of sync");
michael@0 386 line->mFirstChild = aFrames->FirstChild();
michael@0 387 line->NoteFrameRemoved(child);
michael@0 388 child->DestroyFrom(aDestructRoot);
michael@0 389 }
michael@0 390
michael@0 391 aLines.pop_front();
michael@0 392 line->Destroy(shell);
michael@0 393 }
michael@0 394 }
michael@0 395
michael@0 396 bool
michael@0 397 nsLineBox::RFindLineContaining(nsIFrame* aFrame,
michael@0 398 const nsLineList::iterator& aBegin,
michael@0 399 nsLineList::iterator& aEnd,
michael@0 400 nsIFrame* aLastFrameBeforeEnd,
michael@0 401 int32_t* aFrameIndexInLine)
michael@0 402 {
michael@0 403 NS_PRECONDITION(aFrame, "null ptr");
michael@0 404
michael@0 405 nsIFrame* curFrame = aLastFrameBeforeEnd;
michael@0 406 while (aBegin != aEnd) {
michael@0 407 --aEnd;
michael@0 408 NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
michael@0 409 if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
michael@0 410 !aEnd->Contains(aFrame)) {
michael@0 411 if (aEnd->mFirstChild) {
michael@0 412 curFrame = aEnd->mFirstChild->GetPrevSibling();
michael@0 413 }
michael@0 414 continue;
michael@0 415 }
michael@0 416 // i is the index of curFrame in aEnd
michael@0 417 int32_t i = aEnd->GetChildCount() - 1;
michael@0 418 while (i >= 0) {
michael@0 419 if (curFrame == aFrame) {
michael@0 420 *aFrameIndexInLine = i;
michael@0 421 return true;
michael@0 422 }
michael@0 423 --i;
michael@0 424 curFrame = curFrame->GetPrevSibling();
michael@0 425 }
michael@0 426 MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
michael@0 427 }
michael@0 428 *aFrameIndexInLine = -1;
michael@0 429 return false;
michael@0 430 }
michael@0 431
michael@0 432 nsCollapsingMargin
michael@0 433 nsLineBox::GetCarriedOutBottomMargin() const
michael@0 434 {
michael@0 435 NS_ASSERTION(IsBlock(),
michael@0 436 "GetCarriedOutBottomMargin called on non-block line.");
michael@0 437 return (IsBlock() && mBlockData)
michael@0 438 ? mBlockData->mCarriedOutBottomMargin
michael@0 439 : nsCollapsingMargin();
michael@0 440 }
michael@0 441
michael@0 442 bool
michael@0 443 nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue)
michael@0 444 {
michael@0 445 bool changed = false;
michael@0 446 if (IsBlock()) {
michael@0 447 if (!aValue.IsZero()) {
michael@0 448 if (!mBlockData) {
michael@0 449 mBlockData = new ExtraBlockData(GetPhysicalBounds());
michael@0 450 }
michael@0 451 changed = aValue != mBlockData->mCarriedOutBottomMargin;
michael@0 452 mBlockData->mCarriedOutBottomMargin = aValue;
michael@0 453 }
michael@0 454 else if (mBlockData) {
michael@0 455 changed = aValue != mBlockData->mCarriedOutBottomMargin;
michael@0 456 mBlockData->mCarriedOutBottomMargin = aValue;
michael@0 457 MaybeFreeData();
michael@0 458 }
michael@0 459 }
michael@0 460 return changed;
michael@0 461 }
michael@0 462
michael@0 463 void
michael@0 464 nsLineBox::MaybeFreeData()
michael@0 465 {
michael@0 466 nsRect bounds = GetPhysicalBounds();
michael@0 467 if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
michael@0 468 if (IsInline()) {
michael@0 469 if (mInlineData->mFloats.IsEmpty()) {
michael@0 470 delete mInlineData;
michael@0 471 mInlineData = nullptr;
michael@0 472 }
michael@0 473 }
michael@0 474 else if (mBlockData->mCarriedOutBottomMargin.IsZero()) {
michael@0 475 delete mBlockData;
michael@0 476 mBlockData = nullptr;
michael@0 477 }
michael@0 478 }
michael@0 479 }
michael@0 480
michael@0 481 // XXX get rid of this???
michael@0 482 nsFloatCache*
michael@0 483 nsLineBox::GetFirstFloat()
michael@0 484 {
michael@0 485 NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
michael@0 486 return mInlineData ? mInlineData->mFloats.Head() : nullptr;
michael@0 487 }
michael@0 488
michael@0 489 // XXX this might be too eager to free memory
michael@0 490 void
michael@0 491 nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
michael@0 492 {
michael@0 493 NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
michael@0 494 if (IsInline() && mInlineData) {
michael@0 495 if (mInlineData->mFloats.NotEmpty()) {
michael@0 496 aFreeList.Append(mInlineData->mFloats);
michael@0 497 }
michael@0 498 MaybeFreeData();
michael@0 499 }
michael@0 500 }
michael@0 501
michael@0 502 void
michael@0 503 nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
michael@0 504 {
michael@0 505 NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
michael@0 506 if (IsInline()) {
michael@0 507 if (aFreeList.NotEmpty()) {
michael@0 508 if (!mInlineData) {
michael@0 509 mInlineData = new ExtraInlineData(GetPhysicalBounds());
michael@0 510 }
michael@0 511 mInlineData->mFloats.Append(aFreeList);
michael@0 512 }
michael@0 513 }
michael@0 514 }
michael@0 515
michael@0 516 bool
michael@0 517 nsLineBox::RemoveFloat(nsIFrame* aFrame)
michael@0 518 {
michael@0 519 NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
michael@0 520 if (IsInline() && mInlineData) {
michael@0 521 nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
michael@0 522 if (fc) {
michael@0 523 // Note: the placeholder is part of the line's child list
michael@0 524 // and will be removed later.
michael@0 525 mInlineData->mFloats.Remove(fc);
michael@0 526 delete fc;
michael@0 527 MaybeFreeData();
michael@0 528 return true;
michael@0 529 }
michael@0 530 }
michael@0 531 return false;
michael@0 532 }
michael@0 533
michael@0 534 void
michael@0 535 nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
michael@0 536 {
michael@0 537 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
michael@0 538 NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
michael@0 539 "illegal width for combined area");
michael@0 540 NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
michael@0 541 "illegal height for combined area");
michael@0 542 }
michael@0 543 nsRect bounds = GetPhysicalBounds();
michael@0 544 if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
michael@0 545 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
michael@0 546 if (!mData) {
michael@0 547 if (IsInline()) {
michael@0 548 mInlineData = new ExtraInlineData(bounds);
michael@0 549 }
michael@0 550 else {
michael@0 551 mBlockData = new ExtraBlockData(bounds);
michael@0 552 }
michael@0 553 }
michael@0 554 mData->mOverflowAreas = aOverflowAreas;
michael@0 555 }
michael@0 556 else if (mData) {
michael@0 557 // Store away new value so that MaybeFreeData compares against
michael@0 558 // the right value.
michael@0 559 mData->mOverflowAreas = aOverflowAreas;
michael@0 560 MaybeFreeData();
michael@0 561 }
michael@0 562 }
michael@0 563
michael@0 564 //----------------------------------------------------------------------
michael@0 565
michael@0 566
michael@0 567 static nsLineBox* gDummyLines[1];
michael@0 568
michael@0 569 nsLineIterator::nsLineIterator()
michael@0 570 {
michael@0 571 mLines = gDummyLines;
michael@0 572 mNumLines = 0;
michael@0 573 mIndex = 0;
michael@0 574 mRightToLeft = false;
michael@0 575 }
michael@0 576
michael@0 577 nsLineIterator::~nsLineIterator()
michael@0 578 {
michael@0 579 if (mLines != gDummyLines) {
michael@0 580 delete [] mLines;
michael@0 581 }
michael@0 582 }
michael@0 583
michael@0 584 /* virtual */ void
michael@0 585 nsLineIterator::DisposeLineIterator()
michael@0 586 {
michael@0 587 delete this;
michael@0 588 }
michael@0 589
michael@0 590 nsresult
michael@0 591 nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
michael@0 592 {
michael@0 593 mRightToLeft = aRightToLeft;
michael@0 594
michael@0 595 // Count the lines
michael@0 596 int32_t numLines = aLines.size();
michael@0 597 if (0 == numLines) {
michael@0 598 // Use gDummyLines so that we don't need null pointer checks in
michael@0 599 // the accessor methods
michael@0 600 mLines = gDummyLines;
michael@0 601 return NS_OK;
michael@0 602 }
michael@0 603
michael@0 604 // Make a linear array of the lines
michael@0 605 mLines = new nsLineBox*[numLines];
michael@0 606 if (!mLines) {
michael@0 607 // Use gDummyLines so that we don't need null pointer checks in
michael@0 608 // the accessor methods
michael@0 609 mLines = gDummyLines;
michael@0 610 return NS_ERROR_OUT_OF_MEMORY;
michael@0 611 }
michael@0 612 nsLineBox** lp = mLines;
michael@0 613 for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
michael@0 614 line != line_end;
michael@0 615 ++line)
michael@0 616 {
michael@0 617 *lp++ = line;
michael@0 618 }
michael@0 619 mNumLines = numLines;
michael@0 620 return NS_OK;
michael@0 621 }
michael@0 622
michael@0 623 int32_t
michael@0 624 nsLineIterator::GetNumLines()
michael@0 625 {
michael@0 626 return mNumLines;
michael@0 627 }
michael@0 628
michael@0 629 bool
michael@0 630 nsLineIterator::GetDirection()
michael@0 631 {
michael@0 632 return mRightToLeft;
michael@0 633 }
michael@0 634
michael@0 635 NS_IMETHODIMP
michael@0 636 nsLineIterator::GetLine(int32_t aLineNumber,
michael@0 637 nsIFrame** aFirstFrameOnLine,
michael@0 638 int32_t* aNumFramesOnLine,
michael@0 639 nsRect& aLineBounds,
michael@0 640 uint32_t* aLineFlags)
michael@0 641 {
michael@0 642 NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
michael@0 643 NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
michael@0 644 NS_ENSURE_ARG_POINTER(aLineFlags);
michael@0 645
michael@0 646 if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
michael@0 647 *aFirstFrameOnLine = nullptr;
michael@0 648 *aNumFramesOnLine = 0;
michael@0 649 aLineBounds.SetRect(0, 0, 0, 0);
michael@0 650 return NS_OK;
michael@0 651 }
michael@0 652 nsLineBox* line = mLines[aLineNumber];
michael@0 653 *aFirstFrameOnLine = line->mFirstChild;
michael@0 654 *aNumFramesOnLine = line->GetChildCount();
michael@0 655 aLineBounds = line->GetPhysicalBounds();
michael@0 656
michael@0 657 uint32_t flags = 0;
michael@0 658 if (line->IsBlock()) {
michael@0 659 flags |= NS_LINE_FLAG_IS_BLOCK;
michael@0 660 }
michael@0 661 else {
michael@0 662 if (line->HasBreakAfter())
michael@0 663 flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
michael@0 664 }
michael@0 665 *aLineFlags = flags;
michael@0 666
michael@0 667 return NS_OK;
michael@0 668 }
michael@0 669
michael@0 670 int32_t
michael@0 671 nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
michael@0 672 {
michael@0 673 NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
michael@0 674 int32_t lineNumber = aStartLine;
michael@0 675 while (lineNumber != mNumLines) {
michael@0 676 nsLineBox* line = mLines[lineNumber];
michael@0 677 if (line->Contains(aFrame)) {
michael@0 678 return lineNumber;
michael@0 679 }
michael@0 680 ++lineNumber;
michael@0 681 }
michael@0 682 return -1;
michael@0 683 }
michael@0 684
michael@0 685 NS_IMETHODIMP
michael@0 686 nsLineIterator::CheckLineOrder(int32_t aLine,
michael@0 687 bool *aIsReordered,
michael@0 688 nsIFrame **aFirstVisual,
michael@0 689 nsIFrame **aLastVisual)
michael@0 690 {
michael@0 691 NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
michael@0 692 nsLineBox* line = mLines[aLine];
michael@0 693
michael@0 694 if (!line->mFirstChild) { // empty line
michael@0 695 *aIsReordered = false;
michael@0 696 *aFirstVisual = nullptr;
michael@0 697 *aLastVisual = nullptr;
michael@0 698 return NS_OK;
michael@0 699 }
michael@0 700
michael@0 701 nsIFrame* leftmostFrame;
michael@0 702 nsIFrame* rightmostFrame;
michael@0 703 *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
michael@0 704
michael@0 705 // map leftmost/rightmost to first/last according to paragraph direction
michael@0 706 *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
michael@0 707 *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
michael@0 708
michael@0 709 return NS_OK;
michael@0 710 }
michael@0 711
michael@0 712 NS_IMETHODIMP
michael@0 713 nsLineIterator::FindFrameAt(int32_t aLineNumber,
michael@0 714 nscoord aX,
michael@0 715 nsIFrame** aFrameFound,
michael@0 716 bool* aXIsBeforeFirstFrame,
michael@0 717 bool* aXIsAfterLastFrame)
michael@0 718 {
michael@0 719 NS_PRECONDITION(aFrameFound && aXIsBeforeFirstFrame && aXIsAfterLastFrame,
michael@0 720 "null OUT ptr");
michael@0 721 if (!aFrameFound || !aXIsBeforeFirstFrame || !aXIsAfterLastFrame) {
michael@0 722 return NS_ERROR_NULL_POINTER;
michael@0 723 }
michael@0 724 if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
michael@0 725 return NS_ERROR_INVALID_ARG;
michael@0 726 }
michael@0 727
michael@0 728 nsLineBox* line = mLines[aLineNumber];
michael@0 729 if (!line) {
michael@0 730 *aFrameFound = nullptr;
michael@0 731 *aXIsBeforeFirstFrame = true;
michael@0 732 *aXIsAfterLastFrame = false;
michael@0 733 return NS_OK;
michael@0 734 }
michael@0 735
michael@0 736 if (line->ISize() == 0 && line->BSize() == 0)
michael@0 737 return NS_ERROR_FAILURE;
michael@0 738
michael@0 739 nsIFrame* frame = line->mFirstChild;
michael@0 740 nsIFrame* closestFromLeft = nullptr;
michael@0 741 nsIFrame* closestFromRight = nullptr;
michael@0 742 int32_t n = line->GetChildCount();
michael@0 743 while (n--) {
michael@0 744 nsRect rect = frame->GetRect();
michael@0 745 if (rect.width > 0) {
michael@0 746 // If aX is inside this frame - this is it
michael@0 747 if (rect.x <= aX && rect.XMost() > aX) {
michael@0 748 closestFromLeft = closestFromRight = frame;
michael@0 749 break;
michael@0 750 }
michael@0 751 if (rect.x < aX) {
michael@0 752 if (!closestFromLeft ||
michael@0 753 rect.XMost() > closestFromLeft->GetRect().XMost())
michael@0 754 closestFromLeft = frame;
michael@0 755 }
michael@0 756 else {
michael@0 757 if (!closestFromRight ||
michael@0 758 rect.x < closestFromRight->GetRect().x)
michael@0 759 closestFromRight = frame;
michael@0 760 }
michael@0 761 }
michael@0 762 frame = frame->GetNextSibling();
michael@0 763 }
michael@0 764 if (!closestFromLeft && !closestFromRight) {
michael@0 765 // All frames were zero-width. Just take the first one.
michael@0 766 closestFromLeft = closestFromRight = line->mFirstChild;
michael@0 767 }
michael@0 768 *aXIsBeforeFirstFrame = mRightToLeft ? !closestFromRight : !closestFromLeft;
michael@0 769 *aXIsAfterLastFrame = mRightToLeft ? !closestFromLeft : !closestFromRight;
michael@0 770 if (closestFromLeft == closestFromRight) {
michael@0 771 *aFrameFound = closestFromLeft;
michael@0 772 }
michael@0 773 else if (!closestFromLeft) {
michael@0 774 *aFrameFound = closestFromRight;
michael@0 775 }
michael@0 776 else if (!closestFromRight) {
michael@0 777 *aFrameFound = closestFromLeft;
michael@0 778 }
michael@0 779 else { // we're between two frames
michael@0 780 nscoord delta = closestFromRight->GetRect().x - closestFromLeft->GetRect().XMost();
michael@0 781 if (aX < closestFromLeft->GetRect().XMost() + delta/2)
michael@0 782 *aFrameFound = closestFromLeft;
michael@0 783 else
michael@0 784 *aFrameFound = closestFromRight;
michael@0 785 }
michael@0 786 return NS_OK;
michael@0 787 }
michael@0 788
michael@0 789 NS_IMETHODIMP
michael@0 790 nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
michael@0 791 {
michael@0 792 aFrame = aFrame->GetNextSibling();
michael@0 793 return NS_OK;
michael@0 794 }
michael@0 795
michael@0 796 //----------------------------------------------------------------------
michael@0 797
michael@0 798 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 799 nsFloatCacheList::nsFloatCacheList() :
michael@0 800 mHead(nullptr)
michael@0 801 {
michael@0 802 MOZ_COUNT_CTOR(nsFloatCacheList);
michael@0 803 }
michael@0 804 #endif
michael@0 805
michael@0 806 nsFloatCacheList::~nsFloatCacheList()
michael@0 807 {
michael@0 808 DeleteAll();
michael@0 809 MOZ_COUNT_DTOR(nsFloatCacheList);
michael@0 810 }
michael@0 811
michael@0 812 void
michael@0 813 nsFloatCacheList::DeleteAll()
michael@0 814 {
michael@0 815 nsFloatCache* c = mHead;
michael@0 816 while (c) {
michael@0 817 nsFloatCache* next = c->Next();
michael@0 818 delete c;
michael@0 819 c = next;
michael@0 820 }
michael@0 821 mHead = nullptr;
michael@0 822 }
michael@0 823
michael@0 824 nsFloatCache*
michael@0 825 nsFloatCacheList::Tail() const
michael@0 826 {
michael@0 827 nsFloatCache* fc = mHead;
michael@0 828 while (fc) {
michael@0 829 if (!fc->mNext) {
michael@0 830 break;
michael@0 831 }
michael@0 832 fc = fc->mNext;
michael@0 833 }
michael@0 834 return fc;
michael@0 835 }
michael@0 836
michael@0 837 void
michael@0 838 nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
michael@0 839 {
michael@0 840 NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
michael@0 841
michael@0 842 nsFloatCache* tail = Tail();
michael@0 843 if (tail) {
michael@0 844 NS_ASSERTION(!tail->mNext, "Bogus!");
michael@0 845 tail->mNext = aList.mHead;
michael@0 846 }
michael@0 847 else {
michael@0 848 NS_ASSERTION(!mHead, "Bogus!");
michael@0 849 mHead = aList.mHead;
michael@0 850 }
michael@0 851 aList.mHead = nullptr;
michael@0 852 aList.mTail = nullptr;
michael@0 853 }
michael@0 854
michael@0 855 nsFloatCache*
michael@0 856 nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
michael@0 857 {
michael@0 858 nsFloatCache* fc = mHead;
michael@0 859 while (fc) {
michael@0 860 if (fc->mFloat == aOutOfFlowFrame) {
michael@0 861 break;
michael@0 862 }
michael@0 863 fc = fc->Next();
michael@0 864 }
michael@0 865 return fc;
michael@0 866 }
michael@0 867
michael@0 868 nsFloatCache*
michael@0 869 nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
michael@0 870 {
michael@0 871 nsFloatCache* fc = mHead;
michael@0 872 nsFloatCache* prev = nullptr;
michael@0 873 while (fc) {
michael@0 874 if (fc == aElement) {
michael@0 875 if (prev) {
michael@0 876 prev->mNext = fc->mNext;
michael@0 877 } else {
michael@0 878 mHead = fc->mNext;
michael@0 879 }
michael@0 880 return prev;
michael@0 881 }
michael@0 882 prev = fc;
michael@0 883 fc = fc->mNext;
michael@0 884 }
michael@0 885 return nullptr;
michael@0 886 }
michael@0 887
michael@0 888 //----------------------------------------------------------------------
michael@0 889
michael@0 890 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 891 nsFloatCacheFreeList::nsFloatCacheFreeList() :
michael@0 892 mTail(nullptr)
michael@0 893 {
michael@0 894 MOZ_COUNT_CTOR(nsFloatCacheFreeList);
michael@0 895 }
michael@0 896
michael@0 897 nsFloatCacheFreeList::~nsFloatCacheFreeList()
michael@0 898 {
michael@0 899 MOZ_COUNT_DTOR(nsFloatCacheFreeList);
michael@0 900 }
michael@0 901 #endif
michael@0 902
michael@0 903 void
michael@0 904 nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
michael@0 905 {
michael@0 906 NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
michael@0 907
michael@0 908 if (mTail) {
michael@0 909 NS_ASSERTION(!mTail->mNext, "Bogus");
michael@0 910 mTail->mNext = aList.mHead;
michael@0 911 }
michael@0 912 else {
michael@0 913 NS_ASSERTION(!mHead, "Bogus");
michael@0 914 mHead = aList.mHead;
michael@0 915 }
michael@0 916 mTail = aList.Tail();
michael@0 917 aList.mHead = nullptr;
michael@0 918 }
michael@0 919
michael@0 920 void
michael@0 921 nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
michael@0 922 {
michael@0 923 nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
michael@0 924 if (mTail == aElement) {
michael@0 925 mTail = prev;
michael@0 926 }
michael@0 927 }
michael@0 928
michael@0 929 void
michael@0 930 nsFloatCacheFreeList::DeleteAll()
michael@0 931 {
michael@0 932 nsFloatCacheList::DeleteAll();
michael@0 933 mTail = nullptr;
michael@0 934 }
michael@0 935
michael@0 936 nsFloatCache*
michael@0 937 nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
michael@0 938 {
michael@0 939 NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
michael@0 940 "This is a float cache, why isn't the frame out-of-flow?");
michael@0 941 nsFloatCache* fc = mHead;
michael@0 942 if (mHead) {
michael@0 943 if (mHead == mTail) {
michael@0 944 mHead = mTail = nullptr;
michael@0 945 }
michael@0 946 else {
michael@0 947 mHead = fc->mNext;
michael@0 948 }
michael@0 949 fc->mNext = nullptr;
michael@0 950 }
michael@0 951 else {
michael@0 952 fc = new nsFloatCache();
michael@0 953 }
michael@0 954 fc->mFloat = aFloat;
michael@0 955 return fc;
michael@0 956 }
michael@0 957
michael@0 958 void
michael@0 959 nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
michael@0 960 {
michael@0 961 NS_ASSERTION(!aFloat->mNext, "Bogus!");
michael@0 962 aFloat->mNext = nullptr;
michael@0 963 if (mTail) {
michael@0 964 NS_ASSERTION(!mTail->mNext, "Bogus!");
michael@0 965 mTail->mNext = aFloat;
michael@0 966 mTail = aFloat;
michael@0 967 }
michael@0 968 else {
michael@0 969 NS_ASSERTION(!mHead, "Bogus!");
michael@0 970 mHead = mTail = aFloat;
michael@0 971 }
michael@0 972 }
michael@0 973
michael@0 974 //----------------------------------------------------------------------
michael@0 975
michael@0 976 nsFloatCache::nsFloatCache()
michael@0 977 : mFloat(nullptr),
michael@0 978 mNext(nullptr)
michael@0 979 {
michael@0 980 MOZ_COUNT_CTOR(nsFloatCache);
michael@0 981 }
michael@0 982
michael@0 983 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 984 nsFloatCache::~nsFloatCache()
michael@0 985 {
michael@0 986 MOZ_COUNT_DTOR(nsFloatCache);
michael@0 987 }
michael@0 988 #endif

mercurial