1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* representation of one line within a block frame, a CSS line box */ 8 9 #ifndef nsLineBox_h___ 10 #define nsLineBox_h___ 11 12 #include "mozilla/Attributes.h" 13 #include "mozilla/Likely.h" 14 15 #include "nsILineIterator.h" 16 #include "nsIFrame.h" 17 #include "nsTHashSet.h" 18 19 #include <algorithm> 20 21 class nsLineBox; 22 class nsFloatCache; 23 class nsFloatCacheList; 24 class nsFloatCacheFreeList; 25 class nsWindowSizes; 26 27 namespace mozilla { 28 class PresShell; 29 } // namespace mozilla 30 31 // State cached after reflowing a float. This state is used during 32 // incremental reflow when we avoid reflowing a float. 33 class nsFloatCache { 34 public: 35 nsFloatCache(); 36 #ifdef NS_BUILD_REFCNT_LOGGING 37 ~nsFloatCache(); 38 #else 39 ~nsFloatCache() = default; 40 #endif 41 Next()42 nsFloatCache* Next() const { return mNext; } 43 44 nsIFrame* mFloat; // floating frame 45 46 protected: 47 nsFloatCache* mNext; 48 49 friend class nsFloatCacheList; 50 friend class nsFloatCacheFreeList; 51 }; 52 53 //---------------------------------------- 54 55 class nsFloatCacheList { 56 public: 57 #ifdef NS_BUILD_REFCNT_LOGGING 58 nsFloatCacheList(); 59 #else 60 nsFloatCacheList() : mHead(nullptr) {} 61 #endif 62 ~nsFloatCacheList(); 63 IsEmpty()64 bool IsEmpty() const { return nullptr == mHead; } 65 NotEmpty()66 bool NotEmpty() const { return nullptr != mHead; } 67 Head()68 nsFloatCache* Head() const { return mHead; } 69 70 nsFloatCache* Tail() const; 71 72 void DeleteAll(); 73 74 nsFloatCache* Find(nsIFrame* aOutOfFlowFrame); 75 76 // Remove a nsFloatCache from this list. Deleting this nsFloatCache 77 // becomes the caller's responsibility. Remove(nsFloatCache * aElement)78 void Remove(nsFloatCache* aElement) { RemoveAndReturnPrev(aElement); } 79 80 // Steal away aList's nsFloatCache objects and put them in this 81 // list. aList must not be empty. 82 void Append(nsFloatCacheFreeList& aList); 83 84 protected: 85 nsFloatCache* mHead; 86 87 // Remove a nsFloatCache from this list. Deleting this nsFloatCache 88 // becomes the caller's responsibility. Returns the nsFloatCache that was 89 // before aElement, or nullptr if aElement was the first. 90 nsFloatCache* RemoveAndReturnPrev(nsFloatCache* aElement); 91 92 friend class nsFloatCacheFreeList; 93 }; 94 95 //--------------------------------------- 96 // Like nsFloatCacheList, but with fast access to the tail 97 98 class nsFloatCacheFreeList : private nsFloatCacheList { 99 public: 100 #ifdef NS_BUILD_REFCNT_LOGGING 101 nsFloatCacheFreeList(); 102 ~nsFloatCacheFreeList(); 103 #else 104 nsFloatCacheFreeList() : mTail(nullptr) {} 105 ~nsFloatCacheFreeList() = default; 106 #endif 107 108 // Reimplement trivial functions IsEmpty()109 bool IsEmpty() const { return nullptr == mHead; } 110 Head()111 nsFloatCache* Head() const { return mHead; } 112 Tail()113 nsFloatCache* Tail() const { return mTail; } 114 NotEmpty()115 bool NotEmpty() const { return nullptr != mHead; } 116 117 void DeleteAll(); 118 119 // Steal away aList's nsFloatCache objects and put them on this 120 // free-list. aList must not be empty. 121 void Append(nsFloatCacheList& aList); 122 123 void Append(nsFloatCache* aFloatCache); 124 125 void Remove(nsFloatCache* aElement); 126 127 // Remove an nsFloatCache object from this list and return it, or create 128 // a new one if this one is empty; Set its mFloat to aFloat. 129 nsFloatCache* Alloc(nsIFrame* aFloat); 130 131 protected: 132 nsFloatCache* mTail; 133 134 friend class nsFloatCacheList; 135 }; 136 137 //---------------------------------------------------------------------- 138 139 #define LINE_MAX_CHILD_COUNT INT32_MAX 140 141 /** 142 * Function to create a line box and initialize it with a single frame. 143 * The allocation is infallible. 144 * If the frame was moved from another line then you're responsible 145 * for notifying that line using NoteFrameRemoved(). Alternatively, 146 * it's better to use the next function that does that for you in an 147 * optimal way. 148 */ 149 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsIFrame* aFrame, 150 bool aIsBlock); 151 /** 152 * Function to create a line box and initialize it with aCount frames 153 * that are currently on aFromLine. The allocation is infallible. 154 */ 155 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsLineBox* aFromLine, 156 nsIFrame* aFrame, int32_t aCount); 157 158 class nsLineList; 159 160 // don't use the following names outside of this file. Instead, use 161 // nsLineList::iterator, etc. These are just here to allow them to 162 // be specified as parameters to methods of nsLineBox. 163 class nsLineList_iterator; 164 class nsLineList_const_iterator; 165 class nsLineList_reverse_iterator; 166 class nsLineList_const_reverse_iterator; 167 168 /** 169 * Users must have the class that is to be part of the list inherit 170 * from nsLineLink. If they want to be efficient, it should be the 171 * first base class. (This was originally nsCLink in a templatized 172 * nsCList, but it's still useful separately.) 173 */ 174 175 class nsLineLink { 176 public: 177 friend class nsLineList; 178 friend class nsLineList_iterator; 179 friend class nsLineList_reverse_iterator; 180 friend class nsLineList_const_iterator; 181 friend class nsLineList_const_reverse_iterator; 182 183 private: 184 nsLineLink* _mNext; // or head 185 nsLineLink* _mPrev; // or tail 186 }; 187 188 /** 189 * The nsLineBox class represents a horizontal line of frames. It contains 190 * enough state to support incremental reflow of the frames, event handling 191 * for the frames, and rendering of the frames. 192 */ 193 class nsLineBox final : public nsLineLink { 194 private: 195 nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock); 196 ~nsLineBox(); 197 198 // Infallible overloaded new operator. Uses an arena (which comes from the 199 // presShell) to perform the allocation. 200 void* operator new(size_t sz, mozilla::PresShell* aPresShell); 201 void operator delete(void* aPtr, size_t sz) = delete; 202 203 public: 204 // Use these functions to allocate and destroy line boxes 205 friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, 206 nsIFrame* aFrame, bool aIsBlock); 207 friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, 208 nsLineBox* aFromLine, nsIFrame* aFrame, 209 int32_t aCount); 210 void Destroy(mozilla::PresShell* aPresShell); 211 212 // mBlock bit IsBlock()213 bool IsBlock() const { return mFlags.mBlock; } IsInline()214 bool IsInline() const { return !mFlags.mBlock; } 215 216 // mDirty bit MarkDirty()217 void MarkDirty() { mFlags.mDirty = 1; } ClearDirty()218 void ClearDirty() { mFlags.mDirty = 0; } IsDirty()219 bool IsDirty() const { return mFlags.mDirty; } 220 221 // mPreviousMarginDirty bit MarkPreviousMarginDirty()222 void MarkPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 1; } ClearPreviousMarginDirty()223 void ClearPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 0; } IsPreviousMarginDirty()224 bool IsPreviousMarginDirty() const { return mFlags.mPreviousMarginDirty; } 225 226 // mHasClearance bit SetHasClearance()227 void SetHasClearance() { mFlags.mHasClearance = 1; } ClearHasClearance()228 void ClearHasClearance() { mFlags.mHasClearance = 0; } HasClearance()229 bool HasClearance() const { return mFlags.mHasClearance; } 230 231 // mImpactedByFloat bit SetLineIsImpactedByFloat(bool aValue)232 void SetLineIsImpactedByFloat(bool aValue) { 233 mFlags.mImpactedByFloat = aValue; 234 } IsImpactedByFloat()235 bool IsImpactedByFloat() const { return mFlags.mImpactedByFloat; } 236 237 // mLineWrapped bit SetLineWrapped(bool aOn)238 void SetLineWrapped(bool aOn) { mFlags.mLineWrapped = aOn; } IsLineWrapped()239 bool IsLineWrapped() const { return mFlags.mLineWrapped; } 240 241 // mInvalidateTextRuns bit SetInvalidateTextRuns(bool aOn)242 void SetInvalidateTextRuns(bool aOn) { mFlags.mInvalidateTextRuns = aOn; } GetInvalidateTextRuns()243 bool GetInvalidateTextRuns() const { return mFlags.mInvalidateTextRuns; } 244 245 // mResizeReflowOptimizationDisabled bit DisableResizeReflowOptimization()246 void DisableResizeReflowOptimization() { 247 mFlags.mResizeReflowOptimizationDisabled = true; 248 } EnableResizeReflowOptimization()249 void EnableResizeReflowOptimization() { 250 mFlags.mResizeReflowOptimizationDisabled = false; 251 } ResizeReflowOptimizationDisabled()252 bool ResizeReflowOptimizationDisabled() const { 253 return mFlags.mResizeReflowOptimizationDisabled; 254 } 255 256 // mHasMarker bit SetHasMarker()257 void SetHasMarker() { 258 mFlags.mHasMarker = true; 259 InvalidateCachedIsEmpty(); 260 } ClearHasMarker()261 void ClearHasMarker() { 262 mFlags.mHasMarker = false; 263 InvalidateCachedIsEmpty(); 264 } HasMarker()265 bool HasMarker() const { return mFlags.mHasMarker; } 266 267 // mHadFloatPushed bit SetHadFloatPushed()268 void SetHadFloatPushed() { mFlags.mHadFloatPushed = true; } ClearHadFloatPushed()269 void ClearHadFloatPushed() { mFlags.mHadFloatPushed = false; } HadFloatPushed()270 bool HadFloatPushed() const { return mFlags.mHadFloatPushed; } 271 272 // mHasLineClampEllipsis bit SetHasLineClampEllipsis()273 void SetHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = true; } ClearHasLineClampEllipsis()274 void ClearHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = false; } HasLineClampEllipsis()275 bool HasLineClampEllipsis() const { return mFlags.mHasLineClampEllipsis; } 276 277 // mMovedFragments bit SetMovedFragments()278 void SetMovedFragments() { mFlags.mMovedFragments = true; } ClearMovedFragments()279 void ClearMovedFragments() { mFlags.mMovedFragments = false; } MovedFragments()280 bool MovedFragments() const { return mFlags.mMovedFragments; } 281 282 private: 283 // Add a hash table for fast lookup when the line has more frames than this. 284 static const uint32_t kMinChildCountForHashtable = 200; 285 286 /** 287 * Take ownership of aFromLine's hash table and remove the frames that 288 * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with 289 * mFirstChild. This method is used to optimize moving a large number 290 * of frames from one line to the next. 291 */ 292 void StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount); 293 294 /** 295 * Does the equivalent of this->NoteFrameAdded and aFromLine->NoteFrameRemoved 296 * for each frame on this line, but in a optimized way. 297 */ 298 void NoteFramesMovedFrom(nsLineBox* aFromLine); 299 SwitchToHashtable()300 void SwitchToHashtable() { 301 MOZ_ASSERT(!mFlags.mHasHashedFrames); 302 uint32_t count = GetChildCount(); 303 mFlags.mHasHashedFrames = 1; 304 uint32_t minLength = 305 std::max(kMinChildCountForHashtable, 306 uint32_t(PLDHashTable::kDefaultInitialLength)); 307 mFrames = new nsTHashSet<nsIFrame*>(std::max(count, minLength)); 308 for (nsIFrame* f = mFirstChild; count-- > 0; f = f->GetNextSibling()) { 309 mFrames->Insert(f); 310 } 311 } SwitchToCounter()312 void SwitchToCounter() { 313 MOZ_ASSERT(mFlags.mHasHashedFrames); 314 uint32_t count = GetChildCount(); 315 delete mFrames; 316 mFlags.mHasHashedFrames = 0; 317 mChildCount = count; 318 } 319 320 public: GetChildCount()321 int32_t GetChildCount() const { 322 return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Count() 323 : mChildCount; 324 } 325 326 /** 327 * Register that aFrame is now on this line. 328 */ NoteFrameAdded(nsIFrame * aFrame)329 void NoteFrameAdded(nsIFrame* aFrame) { 330 if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { 331 mFrames->Insert(aFrame); 332 } else { 333 if (++mChildCount >= kMinChildCountForHashtable) { 334 SwitchToHashtable(); 335 } 336 } 337 } 338 339 /** 340 * Register that aFrame is not on this line anymore. 341 */ NoteFrameRemoved(nsIFrame * aFrame)342 void NoteFrameRemoved(nsIFrame* aFrame) { 343 MOZ_ASSERT(GetChildCount() > 0); 344 if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { 345 mFrames->Remove(aFrame); 346 if (mFrames->Count() < kMinChildCountForHashtable) { 347 SwitchToCounter(); 348 } 349 } else { 350 --mChildCount; 351 } 352 } 353 354 // mBreakType value 355 // Break information is applied *before* the line if the line is a block, 356 // or *after* the line if the line is an inline. Confusing, I know, but 357 // using different names should help. 358 using StyleClear = mozilla::StyleClear; HasBreakBefore()359 bool HasBreakBefore() const { 360 return IsBlock() && StyleClear::None != BreakType(); 361 } SetBreakTypeBefore(StyleClear aBreakType)362 void SetBreakTypeBefore(StyleClear aBreakType) { 363 MOZ_ASSERT(IsBlock(), "Only blocks have break-before"); 364 MOZ_ASSERT( 365 aBreakType == StyleClear::None || aBreakType == StyleClear::Left || 366 aBreakType == StyleClear::Right || aBreakType == StyleClear::Both, 367 "Only float break types are allowed before a line"); 368 mFlags.mBreakType = aBreakType; 369 } GetBreakTypeBefore()370 StyleClear GetBreakTypeBefore() const { 371 return IsBlock() ? BreakType() : StyleClear::None; 372 } 373 HasBreakAfter()374 bool HasBreakAfter() const { 375 return !IsBlock() && StyleClear::None != BreakType(); 376 } SetBreakTypeAfter(StyleClear aBreakType)377 void SetBreakTypeAfter(StyleClear aBreakType) { 378 MOZ_ASSERT(!IsBlock(), "Only inlines have break-after"); 379 mFlags.mBreakType = aBreakType; 380 } HasFloatBreakAfter()381 bool HasFloatBreakAfter() const { 382 return !IsBlock() && (StyleClear::Left == BreakType() || 383 StyleClear::Right == BreakType() || 384 StyleClear::Both == BreakType()); 385 } GetBreakTypeAfter()386 StyleClear GetBreakTypeAfter() const { 387 return !IsBlock() ? BreakType() : StyleClear::None; 388 } 389 390 // mCarriedOutBEndMargin value 391 nsCollapsingMargin GetCarriedOutBEndMargin() const; 392 // Returns true if the margin changed 393 bool SetCarriedOutBEndMargin(nsCollapsingMargin aValue); 394 395 // mFloats HasFloats()396 bool HasFloats() const { 397 return (IsInline() && mInlineData) && mInlineData->mFloats.NotEmpty(); 398 } 399 nsFloatCache* GetFirstFloat(); 400 void FreeFloats(nsFloatCacheFreeList& aFreeList); 401 void AppendFloats(nsFloatCacheFreeList& aFreeList); 402 bool RemoveFloat(nsIFrame* aFrame); 403 404 // The ink overflow area should never be used for things that affect layout. 405 // The scrollable overflow area are permitted to affect layout for handling of 406 // overflow and scrollbars. 407 void SetOverflowAreas(const mozilla::OverflowAreas& aOverflowAreas); GetOverflowArea(mozilla::OverflowType aType,mozilla::WritingMode aWM,const nsSize & aContainerSize)408 mozilla::LogicalRect GetOverflowArea(mozilla::OverflowType aType, 409 mozilla::WritingMode aWM, 410 const nsSize& aContainerSize) { 411 return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize); 412 } GetOverflowArea(mozilla::OverflowType aType)413 nsRect GetOverflowArea(mozilla::OverflowType aType) const { 414 return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds(); 415 } GetOverflowAreas()416 mozilla::OverflowAreas GetOverflowAreas() const { 417 if (mData) { 418 return mData->mOverflowAreas; 419 } 420 nsRect bounds = GetPhysicalBounds(); 421 return mozilla::OverflowAreas(bounds, bounds); 422 } InkOverflowRect()423 nsRect InkOverflowRect() const { 424 return GetOverflowArea(mozilla::OverflowType::Ink); 425 } ScrollableOverflowRect()426 nsRect ScrollableOverflowRect() { 427 return GetOverflowArea(mozilla::OverflowType::Scrollable); 428 } 429 SlideBy(nscoord aDBCoord,const nsSize & aContainerSize)430 void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) { 431 NS_ASSERTION( 432 aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), 433 "container size doesn't match"); 434 mContainerSize = aContainerSize; 435 mBounds.BStart(mWritingMode) += aDBCoord; 436 if (mData) { 437 // Use a null containerSize to convert vector from logical to physical. 438 const nsSize nullContainerSize; 439 nsPoint physicalDelta = 440 mozilla::LogicalPoint(mWritingMode, 0, aDBCoord) 441 .GetPhysicalPoint(mWritingMode, nullContainerSize); 442 for (const auto otype : mozilla::AllOverflowTypes()) { 443 mData->mOverflowAreas.Overflow(otype) += physicalDelta; 444 } 445 } 446 } 447 448 // Container-size for the line is changing (and therefore if writing mode 449 // was vertical-rl, the line will move physically; this is like SlideBy, 450 // but it is the container size instead of the line's own logical coord 451 // that is changing. UpdateContainerSize(const nsSize aNewContainerSize)452 nsSize UpdateContainerSize(const nsSize aNewContainerSize) { 453 NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set"); 454 nsSize delta = mContainerSize - aNewContainerSize; 455 mContainerSize = aNewContainerSize; 456 // this has a physical-coordinate effect only in vertical-rl mode 457 if (mWritingMode.IsVerticalRL() && mData) { 458 nsPoint physicalDelta(-delta.width, 0); 459 for (const auto otype : mozilla::AllOverflowTypes()) { 460 mData->mOverflowAreas.Overflow(otype) += physicalDelta; 461 } 462 } 463 return delta; 464 } 465 IndentBy(nscoord aDICoord,const nsSize & aContainerSize)466 void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) { 467 NS_ASSERTION( 468 aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), 469 "container size doesn't match"); 470 mContainerSize = aContainerSize; 471 mBounds.IStart(mWritingMode) += aDICoord; 472 } 473 ExpandBy(nscoord aDISize,const nsSize & aContainerSize)474 void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) { 475 NS_ASSERTION( 476 aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), 477 "container size doesn't match"); 478 mContainerSize = aContainerSize; 479 mBounds.ISize(mWritingMode) += aDISize; 480 } 481 482 /** 483 * The logical ascent (distance from block-start to baseline) of the 484 * linebox is the logical ascent of the anonymous inline box (for 485 * which we don't actually create a frame) that wraps all the 486 * consecutive inline children of a block. 487 * 488 * This is currently unused for block lines. 489 */ GetLogicalAscent()490 nscoord GetLogicalAscent() const { return mAscent; } SetLogicalAscent(nscoord aAscent)491 void SetLogicalAscent(nscoord aAscent) { mAscent = aAscent; } 492 BStart()493 nscoord BStart() const { return mBounds.BStart(mWritingMode); } BSize()494 nscoord BSize() const { return mBounds.BSize(mWritingMode); } BEnd()495 nscoord BEnd() const { return mBounds.BEnd(mWritingMode); } IStart()496 nscoord IStart() const { return mBounds.IStart(mWritingMode); } ISize()497 nscoord ISize() const { return mBounds.ISize(mWritingMode); } IEnd()498 nscoord IEnd() const { return mBounds.IEnd(mWritingMode); } SetBoundsEmpty()499 void SetBoundsEmpty() { 500 mBounds.IStart(mWritingMode) = 0; 501 mBounds.ISize(mWritingMode) = 0; 502 mBounds.BStart(mWritingMode) = 0; 503 mBounds.BSize(mWritingMode) = 0; 504 } 505 506 using PostDestroyData = nsIFrame::PostDestroyData; 507 static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, 508 nsIFrame* aDestructRoot, nsFrameList* aFrames, 509 PostDestroyData& aPostDestroyData); 510 511 // search from end to beginning of [aBegin, aEnd) 512 // Returns true if it found the line and false if not. 513 // Moves aEnd as it searches so that aEnd points to the resulting line. 514 // aLastFrameBeforeEnd is the last frame before aEnd (so if aEnd is 515 // the end of the line list, it's just the last frame in the frame 516 // list). 517 static bool RFindLineContaining(nsIFrame* aFrame, 518 const nsLineList_iterator& aBegin, 519 nsLineList_iterator& aEnd, 520 nsIFrame* aLastFrameBeforeEnd, 521 int32_t* aFrameIndexInLine); 522 523 #ifdef DEBUG_FRAME_DUMP 524 static const char* BreakTypeToString(StyleClear aBreakType); 525 char* StateToString(char* aBuf, int32_t aBufSize) const; 526 527 void List(FILE* out, int32_t aIndent, 528 nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const; 529 void List(FILE* out = stderr, const char* aPrefix = "", 530 nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const; 531 nsIFrame* LastChild() const; 532 #endif 533 534 void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const; 535 536 // Find the index of aFrame within the line, starting search at the start. 537 int32_t IndexOf(nsIFrame* aFrame) const; 538 539 // Find the index of aFrame within the line, starting search at the end. 540 // (Produces the same result as IndexOf, but with different performance 541 // characteristics.) The caller must provide the last frame in the line. 542 int32_t RIndexOf(nsIFrame* aFrame, nsIFrame* aLastFrameInLine) const; 543 Contains(nsIFrame * aFrame)544 bool Contains(nsIFrame* aFrame) const { 545 return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame) 546 : IndexOf(aFrame) >= 0; 547 } 548 549 // whether the line box is "logically" empty (just like nsIFrame::IsEmpty) 550 bool IsEmpty() const; 551 552 // Call this only while in Reflow() for the block the line belongs 553 // to, only between reflowing the line (or sliding it, if we skip 554 // reflowing it) and the end of reflowing the block. 555 bool CachedIsEmpty(); 556 InvalidateCachedIsEmpty()557 void InvalidateCachedIsEmpty() { mFlags.mEmptyCacheValid = false; } 558 559 // For debugging purposes IsValidCachedIsEmpty()560 bool IsValidCachedIsEmpty() { return mFlags.mEmptyCacheValid; } 561 562 #ifdef DEBUG 563 static int32_t GetCtorCount(); 564 #endif 565 566 nsIFrame* mFirstChild; 567 568 mozilla::WritingMode mWritingMode; 569 570 // Physical size. Use only for physical <-> logical coordinate conversion. 571 nsSize mContainerSize; 572 573 private: 574 mozilla::LogicalRect mBounds; 575 576 public: GetBounds()577 const mozilla::LogicalRect& GetBounds() { return mBounds; } GetPhysicalBounds()578 nsRect GetPhysicalBounds() const { 579 if (mBounds.IsAllZero()) { 580 return nsRect(0, 0, 0, 0); 581 } 582 583 NS_ASSERTION(mContainerSize != nsSize(-1, -1), 584 "mContainerSize not initialized"); 585 return mBounds.GetPhysicalRect(mWritingMode, mContainerSize); 586 } SetBounds(mozilla::WritingMode aWritingMode,nscoord aIStart,nscoord aBStart,nscoord aISize,nscoord aBSize,const nsSize & aContainerSize)587 void SetBounds(mozilla::WritingMode aWritingMode, nscoord aIStart, 588 nscoord aBStart, nscoord aISize, nscoord aBSize, 589 const nsSize& aContainerSize) { 590 mWritingMode = aWritingMode; 591 mContainerSize = aContainerSize; 592 mBounds = 593 mozilla::LogicalRect(aWritingMode, aIStart, aBStart, aISize, aBSize); 594 } 595 596 // mFlags.mHasHashedFrames says which one to use 597 union { 598 nsTHashSet<nsIFrame*>* mFrames; 599 uint32_t mChildCount; 600 }; 601 602 struct FlagBits { 603 bool mDirty : 1; 604 bool mPreviousMarginDirty : 1; 605 bool mHasClearance : 1; 606 bool mBlock : 1; 607 bool mImpactedByFloat : 1; 608 bool mLineWrapped : 1; 609 bool mInvalidateTextRuns : 1; 610 // default 0 = means that the opt potentially applies to this line. 611 // 1 = never skip reflowing this line for a resize reflow 612 bool mResizeReflowOptimizationDisabled : 1; 613 bool mEmptyCacheValid : 1; 614 bool mEmptyCacheState : 1; 615 // mHasMarker indicates that this is an inline line whose block's 616 // ::marker is adjacent to this line and non-empty. 617 bool mHasMarker : 1; 618 // Indicates that this line *may* have a placeholder for a float 619 // that was pushed to a later column or page. 620 bool mHadFloatPushed : 1; 621 bool mHasHashedFrames : 1; 622 // Indicates that this line is the one identified by an ancestor block 623 // with -webkit-line-clamp on its legacy flex container, and that subsequent 624 // lines under that block are "clamped" away, and therefore we need to 625 // render a 'text-overflow: ellipsis'-like marker in this line. At most one 626 // line in the set of lines found by LineClampLineIterator for a given 627 // block will have this flag set. 628 bool mHasLineClampEllipsis : 1; 629 // Has this line moved to a different fragment of the block since 630 // the last time it was reflowed? 631 bool mMovedFragments : 1; 632 StyleClear mBreakType; 633 }; 634 635 struct ExtraData { ExtraDataExtraData636 explicit ExtraData(const nsRect& aBounds) 637 : mOverflowAreas(aBounds, aBounds) {} 638 mozilla::OverflowAreas mOverflowAreas; 639 }; 640 641 struct ExtraBlockData : public ExtraData { ExtraBlockDataExtraBlockData642 explicit ExtraBlockData(const nsRect& aBounds) 643 : ExtraData(aBounds), mCarriedOutBEndMargin() {} 644 nsCollapsingMargin mCarriedOutBEndMargin; 645 }; 646 647 struct ExtraInlineData : public ExtraData { ExtraInlineDataExtraInlineData648 explicit ExtraInlineData(const nsRect& aBounds) 649 : ExtraData(aBounds), 650 mFloatEdgeIStart(nscoord_MIN), 651 mFloatEdgeIEnd(nscoord_MIN) {} 652 nscoord mFloatEdgeIStart; 653 nscoord mFloatEdgeIEnd; 654 nsFloatCacheList mFloats; 655 }; 656 GetFloatEdges(nscoord * aStart,nscoord * aEnd)657 bool GetFloatEdges(nscoord* aStart, nscoord* aEnd) const { 658 MOZ_ASSERT(IsInline(), "block line can't have float edges"); 659 if (mInlineData && mInlineData->mFloatEdgeIStart != nscoord_MIN) { 660 *aStart = mInlineData->mFloatEdgeIStart; 661 *aEnd = mInlineData->mFloatEdgeIEnd; 662 return true; 663 } 664 return false; 665 } 666 void SetFloatEdges(nscoord aStart, nscoord aEnd); 667 void ClearFloatEdges(); 668 669 protected: 670 nscoord mAscent; // see |SetAscent| / |GetAscent| 671 static_assert(sizeof(FlagBits) <= sizeof(uint32_t), 672 "size of FlagBits should not be larger than size of uint32_t"); 673 union { 674 uint32_t mAllFlags; 675 FlagBits mFlags; 676 }; 677 BreakType()678 StyleClear BreakType() const { return mFlags.mBreakType; }; 679 680 union { 681 ExtraData* mData; 682 ExtraBlockData* mBlockData; 683 ExtraInlineData* mInlineData; 684 }; 685 686 void Cleanup(); 687 void MaybeFreeData(); 688 }; 689 690 /** 691 * A linked list type where the items in the list must inherit from 692 * a link type to fuse allocations. 693 * 694 * API heavily based on the |list| class in the C++ standard. 695 */ 696 697 class nsLineList_iterator { 698 public: 699 friend class nsLineList; 700 friend class nsLineList_reverse_iterator; 701 friend class nsLineList_const_iterator; 702 friend class nsLineList_const_reverse_iterator; 703 704 typedef nsLineList_iterator iterator_self_type; 705 typedef nsLineList_reverse_iterator iterator_reverse_type; 706 707 typedef nsLineBox& reference; 708 typedef const nsLineBox& const_reference; 709 710 typedef nsLineBox* pointer; 711 typedef const nsLineBox* const_pointer; 712 713 typedef uint32_t size_type; 714 typedef int32_t difference_type; 715 716 typedef nsLineLink link_type; 717 718 #ifdef DEBUG nsLineList_iterator()719 nsLineList_iterator() : mListLink(nullptr) { 720 memset(&mCurrent, 0xcd, sizeof(mCurrent)); 721 } 722 #else 723 // Auto generated default constructor OK. 724 #endif 725 // Auto generated copy-constructor OK. 726 727 inline iterator_self_type& operator=(const iterator_self_type& aOther); 728 inline iterator_self_type& operator=(const iterator_reverse_type& aOther); 729 730 iterator_self_type& operator++() { 731 mCurrent = mCurrent->_mNext; 732 return *this; 733 } 734 735 iterator_self_type operator++(int) { 736 iterator_self_type rv(*this); 737 mCurrent = mCurrent->_mNext; 738 return rv; 739 } 740 741 iterator_self_type& operator--() { 742 mCurrent = mCurrent->_mPrev; 743 return *this; 744 } 745 746 iterator_self_type operator--(int) { 747 iterator_self_type rv(*this); 748 mCurrent = mCurrent->_mPrev; 749 return rv; 750 } 751 752 reference operator*() { 753 MOZ_ASSERT(mListLink); 754 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 755 return *static_cast<pointer>(mCurrent); 756 } 757 758 pointer operator->() { 759 MOZ_ASSERT(mListLink); 760 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 761 return static_cast<pointer>(mCurrent); 762 } 763 get()764 pointer get() { 765 MOZ_ASSERT(mListLink); 766 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 767 return static_cast<pointer>(mCurrent); 768 } 769 pointer()770 operator pointer() { 771 MOZ_ASSERT(mListLink); 772 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 773 return static_cast<pointer>(mCurrent); 774 } 775 776 const_reference operator*() const { 777 MOZ_ASSERT(mListLink); 778 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 779 return *static_cast<const_pointer>(mCurrent); 780 } 781 782 const_pointer operator->() const { 783 MOZ_ASSERT(mListLink); 784 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 785 return static_cast<const_pointer>(mCurrent); 786 } 787 788 #ifndef __MWERKS__ const_pointer()789 operator const_pointer() const { 790 MOZ_ASSERT(mListLink); 791 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 792 return static_cast<const_pointer>(mCurrent); 793 } 794 #endif /* !__MWERKS__ */ 795 next()796 iterator_self_type next() { 797 iterator_self_type copy(*this); 798 return ++copy; 799 } 800 next()801 const iterator_self_type next() const { 802 iterator_self_type copy(*this); 803 return ++copy; 804 } 805 prev()806 iterator_self_type prev() { 807 iterator_self_type copy(*this); 808 return --copy; 809 } 810 prev()811 const iterator_self_type prev() const { 812 iterator_self_type copy(*this); 813 return --copy; 814 } 815 816 // Passing by value rather than by reference and reference to const 817 // to keep AIX happy. 818 bool operator==(const iterator_self_type aOther) const { 819 MOZ_ASSERT(mListLink); 820 MOZ_ASSERT(mListLink == aOther.mListLink, 821 "comparing iterators over different lists"); 822 return mCurrent == aOther.mCurrent; 823 } 824 bool operator!=(const iterator_self_type aOther) const { 825 MOZ_ASSERT(mListLink); 826 MOZ_ASSERT(mListLink == aOther.mListLink, 827 "comparing iterators over different lists"); 828 return mCurrent != aOther.mCurrent; 829 } 830 bool operator==(const iterator_self_type aOther) { 831 MOZ_ASSERT(mListLink); 832 MOZ_ASSERT(mListLink == aOther.mListLink, 833 "comparing iterators over different lists"); 834 return mCurrent == aOther.mCurrent; 835 } 836 bool operator!=(const iterator_self_type aOther) { 837 MOZ_ASSERT(mListLink); 838 MOZ_ASSERT(mListLink == aOther.mListLink, 839 "comparing iterators over different lists"); 840 return mCurrent != aOther.mCurrent; 841 } 842 843 #ifdef DEBUG IsInSameList(const iterator_self_type aOther)844 bool IsInSameList(const iterator_self_type aOther) const { 845 return mListLink == aOther.mListLink; 846 } 847 #endif 848 849 private: 850 link_type* mCurrent; 851 #ifdef DEBUG 852 link_type* mListLink; // the list's link, i.e., the end 853 #endif 854 }; 855 856 class nsLineList_reverse_iterator { 857 public: 858 friend class nsLineList; 859 friend class nsLineList_iterator; 860 friend class nsLineList_const_iterator; 861 friend class nsLineList_const_reverse_iterator; 862 863 typedef nsLineList_reverse_iterator iterator_self_type; 864 typedef nsLineList_iterator iterator_reverse_type; 865 866 typedef nsLineBox& reference; 867 typedef const nsLineBox& const_reference; 868 869 typedef nsLineBox* pointer; 870 typedef const nsLineBox* const_pointer; 871 872 typedef uint32_t size_type; 873 typedef int32_t difference_type; 874 875 typedef nsLineLink link_type; 876 877 #ifdef DEBUG nsLineList_reverse_iterator()878 nsLineList_reverse_iterator() : mListLink(nullptr) { 879 memset(&mCurrent, 0xcd, sizeof(mCurrent)); 880 } 881 #else 882 // Auto generated default constructor OK. 883 #endif 884 // Auto generated copy-constructor OK. 885 886 inline iterator_self_type& operator=(const iterator_reverse_type& aOther); 887 inline iterator_self_type& operator=(const iterator_self_type& aOther); 888 889 iterator_self_type& operator++() { 890 mCurrent = mCurrent->_mPrev; 891 return *this; 892 } 893 894 iterator_self_type operator++(int) { 895 iterator_self_type rv(*this); 896 mCurrent = mCurrent->_mPrev; 897 return rv; 898 } 899 900 iterator_self_type& operator--() { 901 mCurrent = mCurrent->_mNext; 902 return *this; 903 } 904 905 iterator_self_type operator--(int) { 906 iterator_self_type rv(*this); 907 mCurrent = mCurrent->_mNext; 908 return rv; 909 } 910 911 reference operator*() { 912 MOZ_ASSERT(mListLink); 913 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 914 return *static_cast<pointer>(mCurrent); 915 } 916 917 pointer operator->() { 918 MOZ_ASSERT(mListLink); 919 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 920 return static_cast<pointer>(mCurrent); 921 } 922 get()923 pointer get() { 924 MOZ_ASSERT(mListLink); 925 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 926 return static_cast<pointer>(mCurrent); 927 } 928 pointer()929 operator pointer() { 930 MOZ_ASSERT(mListLink); 931 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 932 return static_cast<pointer>(mCurrent); 933 } 934 935 const_reference operator*() const { 936 MOZ_ASSERT(mListLink); 937 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 938 return *static_cast<const_pointer>(mCurrent); 939 } 940 941 const_pointer operator->() const { 942 MOZ_ASSERT(mListLink); 943 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 944 return static_cast<const_pointer>(mCurrent); 945 } 946 947 #ifndef __MWERKS__ const_pointer()948 operator const_pointer() const { 949 MOZ_ASSERT(mListLink); 950 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 951 return static_cast<const_pointer>(mCurrent); 952 } 953 #endif /* !__MWERKS__ */ 954 955 // Passing by value rather than by reference and reference to const 956 // to keep AIX happy. 957 bool operator==(const iterator_self_type aOther) const { 958 MOZ_ASSERT(mListLink); 959 NS_ASSERTION(mListLink == aOther.mListLink, 960 "comparing iterators over different lists"); 961 return mCurrent == aOther.mCurrent; 962 } 963 bool operator!=(const iterator_self_type aOther) const { 964 MOZ_ASSERT(mListLink); 965 NS_ASSERTION(mListLink == aOther.mListLink, 966 "comparing iterators over different lists"); 967 return mCurrent != aOther.mCurrent; 968 } 969 bool operator==(const iterator_self_type aOther) { 970 MOZ_ASSERT(mListLink); 971 NS_ASSERTION(mListLink == aOther.mListLink, 972 "comparing iterators over different lists"); 973 return mCurrent == aOther.mCurrent; 974 } 975 bool operator!=(const iterator_self_type aOther) { 976 MOZ_ASSERT(mListLink); 977 NS_ASSERTION(mListLink == aOther.mListLink, 978 "comparing iterators over different lists"); 979 return mCurrent != aOther.mCurrent; 980 } 981 982 #ifdef DEBUG IsInSameList(const iterator_self_type aOther)983 bool IsInSameList(const iterator_self_type aOther) const { 984 return mListLink == aOther.mListLink; 985 } 986 #endif 987 988 private: 989 link_type* mCurrent; 990 #ifdef DEBUG 991 link_type* mListLink; // the list's link, i.e., the end 992 #endif 993 }; 994 995 class nsLineList_const_iterator { 996 public: 997 friend class nsLineList; 998 friend class nsLineList_iterator; 999 friend class nsLineList_reverse_iterator; 1000 friend class nsLineList_const_reverse_iterator; 1001 1002 typedef nsLineList_const_iterator iterator_self_type; 1003 typedef nsLineList_const_reverse_iterator iterator_reverse_type; 1004 typedef nsLineList_iterator iterator_nonconst_type; 1005 typedef nsLineList_reverse_iterator iterator_nonconst_reverse_type; 1006 1007 typedef nsLineBox& reference; 1008 typedef const nsLineBox& const_reference; 1009 1010 typedef nsLineBox* pointer; 1011 typedef const nsLineBox* const_pointer; 1012 1013 typedef uint32_t size_type; 1014 typedef int32_t difference_type; 1015 1016 typedef nsLineLink link_type; 1017 1018 #ifdef DEBUG nsLineList_const_iterator()1019 nsLineList_const_iterator() : mListLink(nullptr) { 1020 memset(&mCurrent, 0xcd, sizeof(mCurrent)); 1021 } 1022 #else 1023 // Auto generated default constructor OK. 1024 #endif 1025 // Auto generated copy-constructor OK. 1026 1027 inline iterator_self_type& operator=(const iterator_nonconst_type& aOther); 1028 inline iterator_self_type& operator=( 1029 const iterator_nonconst_reverse_type& aOther); 1030 inline iterator_self_type& operator=(const iterator_self_type& aOther); 1031 inline iterator_self_type& operator=(const iterator_reverse_type& aOther); 1032 1033 iterator_self_type& operator++() { 1034 mCurrent = mCurrent->_mNext; 1035 return *this; 1036 } 1037 1038 iterator_self_type operator++(int) { 1039 iterator_self_type rv(*this); 1040 mCurrent = mCurrent->_mNext; 1041 return rv; 1042 } 1043 1044 iterator_self_type& operator--() { 1045 mCurrent = mCurrent->_mPrev; 1046 return *this; 1047 } 1048 1049 iterator_self_type operator--(int) { 1050 iterator_self_type rv(*this); 1051 mCurrent = mCurrent->_mPrev; 1052 return rv; 1053 } 1054 1055 const_reference operator*() const { 1056 MOZ_ASSERT(mListLink); 1057 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1058 return *static_cast<const_pointer>(mCurrent); 1059 } 1060 1061 const_pointer operator->() const { 1062 MOZ_ASSERT(mListLink); 1063 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1064 return static_cast<const_pointer>(mCurrent); 1065 } 1066 get()1067 const_pointer get() const { 1068 MOZ_ASSERT(mListLink); 1069 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1070 return static_cast<const_pointer>(mCurrent); 1071 } 1072 1073 #ifndef __MWERKS__ const_pointer()1074 operator const_pointer() const { 1075 MOZ_ASSERT(mListLink); 1076 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1077 return static_cast<const_pointer>(mCurrent); 1078 } 1079 #endif /* !__MWERKS__ */ 1080 next()1081 const iterator_self_type next() const { 1082 iterator_self_type copy(*this); 1083 return ++copy; 1084 } 1085 prev()1086 const iterator_self_type prev() const { 1087 iterator_self_type copy(*this); 1088 return --copy; 1089 } 1090 1091 // Passing by value rather than by reference and reference to const 1092 // to keep AIX happy. 1093 bool operator==(const iterator_self_type aOther) const { 1094 MOZ_ASSERT(mListLink); 1095 NS_ASSERTION(mListLink == aOther.mListLink, 1096 "comparing iterators over different lists"); 1097 return mCurrent == aOther.mCurrent; 1098 } 1099 bool operator!=(const iterator_self_type aOther) const { 1100 MOZ_ASSERT(mListLink); 1101 NS_ASSERTION(mListLink == aOther.mListLink, 1102 "comparing iterators over different lists"); 1103 return mCurrent != aOther.mCurrent; 1104 } 1105 bool operator==(const iterator_self_type aOther) { 1106 MOZ_ASSERT(mListLink); 1107 NS_ASSERTION(mListLink == aOther.mListLink, 1108 "comparing iterators over different lists"); 1109 return mCurrent == aOther.mCurrent; 1110 } 1111 bool operator!=(const iterator_self_type aOther) { 1112 MOZ_ASSERT(mListLink); 1113 NS_ASSERTION(mListLink == aOther.mListLink, 1114 "comparing iterators over different lists"); 1115 return mCurrent != aOther.mCurrent; 1116 } 1117 1118 #ifdef DEBUG IsInSameList(const iterator_self_type aOther)1119 bool IsInSameList(const iterator_self_type aOther) const { 1120 return mListLink == aOther.mListLink; 1121 } 1122 #endif 1123 1124 private: 1125 const link_type* mCurrent; 1126 #ifdef DEBUG 1127 const link_type* mListLink; // the list's link, i.e., the end 1128 #endif 1129 }; 1130 1131 class nsLineList_const_reverse_iterator { 1132 public: 1133 friend class nsLineList; 1134 friend class nsLineList_iterator; 1135 friend class nsLineList_reverse_iterator; 1136 friend class nsLineList_const_iterator; 1137 1138 typedef nsLineList_const_reverse_iterator iterator_self_type; 1139 typedef nsLineList_const_iterator iterator_reverse_type; 1140 typedef nsLineList_iterator iterator_nonconst_reverse_type; 1141 typedef nsLineList_reverse_iterator iterator_nonconst_type; 1142 1143 typedef nsLineBox& reference; 1144 typedef const nsLineBox& const_reference; 1145 1146 typedef nsLineBox* pointer; 1147 typedef const nsLineBox* const_pointer; 1148 1149 typedef uint32_t size_type; 1150 typedef int32_t difference_type; 1151 1152 typedef nsLineLink link_type; 1153 1154 #ifdef DEBUG nsLineList_const_reverse_iterator()1155 nsLineList_const_reverse_iterator() : mListLink(nullptr) { 1156 memset(&mCurrent, 0xcd, sizeof(mCurrent)); 1157 } 1158 #else 1159 // Auto generated default constructor OK. 1160 #endif 1161 // Auto generated copy-constructor OK. 1162 1163 inline iterator_self_type& operator=(const iterator_nonconst_type& aOther); 1164 inline iterator_self_type& operator=( 1165 const iterator_nonconst_reverse_type& aOther); 1166 inline iterator_self_type& operator=(const iterator_self_type& aOther); 1167 inline iterator_self_type& operator=(const iterator_reverse_type& aOther); 1168 1169 iterator_self_type& operator++() { 1170 mCurrent = mCurrent->_mPrev; 1171 return *this; 1172 } 1173 1174 iterator_self_type operator++(int) { 1175 iterator_self_type rv(*this); 1176 mCurrent = mCurrent->_mPrev; 1177 return rv; 1178 } 1179 1180 iterator_self_type& operator--() { 1181 mCurrent = mCurrent->_mNext; 1182 return *this; 1183 } 1184 1185 iterator_self_type operator--(int) { 1186 iterator_self_type rv(*this); 1187 mCurrent = mCurrent->_mNext; 1188 return rv; 1189 } 1190 1191 const_reference operator*() const { 1192 MOZ_ASSERT(mListLink); 1193 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1194 return *static_cast<const_pointer>(mCurrent); 1195 } 1196 1197 const_pointer operator->() const { 1198 MOZ_ASSERT(mListLink); 1199 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1200 return static_cast<const_pointer>(mCurrent); 1201 } 1202 get()1203 const_pointer get() const { 1204 MOZ_ASSERT(mListLink); 1205 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1206 return static_cast<const_pointer>(mCurrent); 1207 } 1208 1209 #ifndef __MWERKS__ const_pointer()1210 operator const_pointer() const { 1211 MOZ_ASSERT(mListLink); 1212 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 1213 return static_cast<const_pointer>(mCurrent); 1214 } 1215 #endif /* !__MWERKS__ */ 1216 1217 // Passing by value rather than by reference and reference to const 1218 // to keep AIX happy. 1219 bool operator==(const iterator_self_type aOther) const { 1220 MOZ_ASSERT(mListLink); 1221 NS_ASSERTION(mListLink == aOther.mListLink, 1222 "comparing iterators over different lists"); 1223 return mCurrent == aOther.mCurrent; 1224 } 1225 bool operator!=(const iterator_self_type aOther) const { 1226 MOZ_ASSERT(mListLink); 1227 NS_ASSERTION(mListLink == aOther.mListLink, 1228 "comparing iterators over different lists"); 1229 return mCurrent != aOther.mCurrent; 1230 } 1231 bool operator==(const iterator_self_type aOther) { 1232 MOZ_ASSERT(mListLink); 1233 NS_ASSERTION(mListLink == aOther.mListLink, 1234 "comparing iterators over different lists"); 1235 return mCurrent == aOther.mCurrent; 1236 } 1237 bool operator!=(const iterator_self_type aOther) { 1238 MOZ_ASSERT(mListLink); 1239 NS_ASSERTION(mListLink == aOther.mListLink, 1240 "comparing iterators over different lists"); 1241 return mCurrent != aOther.mCurrent; 1242 } 1243 1244 #ifdef DEBUG IsInSameList(const iterator_self_type aOther)1245 bool IsInSameList(const iterator_self_type aOther) const { 1246 return mListLink == aOther.mListLink; 1247 } 1248 #endif 1249 1250 // private: 1251 const link_type* mCurrent; 1252 #ifdef DEBUG 1253 const link_type* mListLink; // the list's link, i.e., the end 1254 #endif 1255 }; 1256 1257 class nsLineList { 1258 public: 1259 friend class nsLineList_iterator; 1260 friend class nsLineList_reverse_iterator; 1261 friend class nsLineList_const_iterator; 1262 friend class nsLineList_const_reverse_iterator; 1263 1264 typedef uint32_t size_type; 1265 typedef int32_t difference_type; 1266 1267 typedef nsLineLink link_type; 1268 1269 private: 1270 link_type mLink; 1271 1272 public: 1273 typedef nsLineList self_type; 1274 1275 typedef nsLineBox& reference; 1276 typedef const nsLineBox& const_reference; 1277 1278 typedef nsLineBox* pointer; 1279 typedef const nsLineBox* const_pointer; 1280 1281 typedef nsLineList_iterator iterator; 1282 typedef nsLineList_reverse_iterator reverse_iterator; 1283 typedef nsLineList_const_iterator const_iterator; 1284 typedef nsLineList_const_reverse_iterator const_reverse_iterator; 1285 nsLineList()1286 nsLineList() { 1287 MOZ_COUNT_CTOR(nsLineList); 1288 clear(); 1289 } 1290 MOZ_COUNTED_DTOR(nsLineList)1291 MOZ_COUNTED_DTOR(nsLineList) 1292 1293 const_iterator begin() const { 1294 const_iterator rv; 1295 rv.mCurrent = mLink._mNext; 1296 #ifdef DEBUG 1297 rv.mListLink = &mLink; 1298 #endif 1299 return rv; 1300 } 1301 begin()1302 iterator begin() { 1303 iterator rv; 1304 rv.mCurrent = mLink._mNext; 1305 #ifdef DEBUG 1306 rv.mListLink = &mLink; 1307 #endif 1308 return rv; 1309 } 1310 begin(nsLineBox * aLine)1311 iterator begin(nsLineBox* aLine) { 1312 iterator rv; 1313 rv.mCurrent = aLine; 1314 #ifdef DEBUG 1315 rv.mListLink = &mLink; 1316 #endif 1317 return rv; 1318 } 1319 end()1320 const_iterator end() const { 1321 const_iterator rv; 1322 rv.mCurrent = &mLink; 1323 #ifdef DEBUG 1324 rv.mListLink = &mLink; 1325 #endif 1326 return rv; 1327 } 1328 end()1329 iterator end() { 1330 iterator rv; 1331 rv.mCurrent = &mLink; 1332 #ifdef DEBUG 1333 rv.mListLink = &mLink; 1334 #endif 1335 return rv; 1336 } 1337 rbegin()1338 const_reverse_iterator rbegin() const { 1339 const_reverse_iterator rv; 1340 rv.mCurrent = mLink._mPrev; 1341 #ifdef DEBUG 1342 rv.mListLink = &mLink; 1343 #endif 1344 return rv; 1345 } 1346 rbegin()1347 reverse_iterator rbegin() { 1348 reverse_iterator rv; 1349 rv.mCurrent = mLink._mPrev; 1350 #ifdef DEBUG 1351 rv.mListLink = &mLink; 1352 #endif 1353 return rv; 1354 } 1355 rbegin(nsLineBox * aLine)1356 reverse_iterator rbegin(nsLineBox* aLine) { 1357 reverse_iterator rv; 1358 rv.mCurrent = aLine; 1359 #ifdef DEBUG 1360 rv.mListLink = &mLink; 1361 #endif 1362 return rv; 1363 } 1364 rend()1365 const_reverse_iterator rend() const { 1366 const_reverse_iterator rv; 1367 rv.mCurrent = &mLink; 1368 #ifdef DEBUG 1369 rv.mListLink = &mLink; 1370 #endif 1371 return rv; 1372 } 1373 rend()1374 reverse_iterator rend() { 1375 reverse_iterator rv; 1376 rv.mCurrent = &mLink; 1377 #ifdef DEBUG 1378 rv.mListLink = &mLink; 1379 #endif 1380 return rv; 1381 } 1382 empty()1383 bool empty() const { return mLink._mNext == &mLink; } 1384 1385 // NOTE: O(N). size()1386 size_type size() const { 1387 size_type count = 0; 1388 for (const link_type* cur = mLink._mNext; cur != &mLink; 1389 cur = cur->_mNext) { 1390 ++count; 1391 } 1392 return count; 1393 } 1394 front()1395 pointer front() { 1396 NS_ASSERTION(!empty(), "no element to return"); 1397 return static_cast<pointer>(mLink._mNext); 1398 } 1399 front()1400 const_pointer front() const { 1401 NS_ASSERTION(!empty(), "no element to return"); 1402 return static_cast<const_pointer>(mLink._mNext); 1403 } 1404 back()1405 pointer back() { 1406 NS_ASSERTION(!empty(), "no element to return"); 1407 return static_cast<pointer>(mLink._mPrev); 1408 } 1409 back()1410 const_pointer back() const { 1411 NS_ASSERTION(!empty(), "no element to return"); 1412 return static_cast<const_pointer>(mLink._mPrev); 1413 } 1414 push_front(pointer aNew)1415 void push_front(pointer aNew) { 1416 aNew->_mNext = mLink._mNext; 1417 mLink._mNext->_mPrev = aNew; 1418 aNew->_mPrev = &mLink; 1419 mLink._mNext = aNew; 1420 } 1421 pop_front()1422 void pop_front() 1423 // NOTE: leaves dangling next/prev pointers 1424 { 1425 NS_ASSERTION(!empty(), "no element to pop"); 1426 link_type* newFirst = mLink._mNext->_mNext; 1427 newFirst->_mPrev = &mLink; 1428 // mLink._mNext->_mNext = nullptr; 1429 // mLink._mNext->_mPrev = nullptr; 1430 mLink._mNext = newFirst; 1431 } 1432 push_back(pointer aNew)1433 void push_back(pointer aNew) { 1434 aNew->_mPrev = mLink._mPrev; 1435 mLink._mPrev->_mNext = aNew; 1436 aNew->_mNext = &mLink; 1437 mLink._mPrev = aNew; 1438 } 1439 pop_back()1440 void pop_back() 1441 // NOTE: leaves dangling next/prev pointers 1442 { 1443 NS_ASSERTION(!empty(), "no element to pop"); 1444 link_type* newLast = mLink._mPrev->_mPrev; 1445 newLast->_mNext = &mLink; 1446 // mLink._mPrev->_mPrev = nullptr; 1447 // mLink._mPrev->_mNext = nullptr; 1448 mLink._mPrev = newLast; 1449 } 1450 1451 // inserts x before position before_insert(iterator position,pointer x)1452 iterator before_insert(iterator position, pointer x) { 1453 // use |mCurrent| to prevent DEBUG_PASS_END assertions 1454 x->_mPrev = position.mCurrent->_mPrev; 1455 x->_mNext = position.mCurrent; 1456 position.mCurrent->_mPrev->_mNext = x; 1457 position.mCurrent->_mPrev = x; 1458 return --position; 1459 } 1460 1461 // inserts x after position after_insert(iterator position,pointer x)1462 iterator after_insert(iterator position, pointer x) { 1463 // use |mCurrent| to prevent DEBUG_PASS_END assertions 1464 x->_mNext = position.mCurrent->_mNext; 1465 x->_mPrev = position.mCurrent; 1466 position.mCurrent->_mNext->_mPrev = x; 1467 position.mCurrent->_mNext = x; 1468 return ++position; 1469 } 1470 1471 // returns iterator pointing to after the element erase(iterator position)1472 iterator erase(iterator position) 1473 // NOTE: leaves dangling next/prev pointers 1474 { 1475 position->_mPrev->_mNext = position->_mNext; 1476 position->_mNext->_mPrev = position->_mPrev; 1477 return ++position; 1478 } 1479 swap(self_type & y)1480 void swap(self_type& y) { 1481 link_type tmp(y.mLink); 1482 y.mLink = mLink; 1483 mLink = tmp; 1484 1485 if (!empty()) { 1486 mLink._mNext->_mPrev = &mLink; 1487 mLink._mPrev->_mNext = &mLink; 1488 } 1489 1490 if (!y.empty()) { 1491 y.mLink._mNext->_mPrev = &y.mLink; 1492 y.mLink._mPrev->_mNext = &y.mLink; 1493 } 1494 } 1495 clear()1496 void clear() 1497 // NOTE: leaves dangling next/prev pointers 1498 { 1499 mLink._mNext = &mLink; 1500 mLink._mPrev = &mLink; 1501 } 1502 1503 // inserts the conts of x before position and makes x empty splice(iterator position,self_type & x)1504 void splice(iterator position, self_type& x) { 1505 // use |mCurrent| to prevent DEBUG_PASS_END assertions 1506 position.mCurrent->_mPrev->_mNext = x.mLink._mNext; 1507 x.mLink._mNext->_mPrev = position.mCurrent->_mPrev; 1508 x.mLink._mPrev->_mNext = position.mCurrent; 1509 position.mCurrent->_mPrev = x.mLink._mPrev; 1510 x.clear(); 1511 } 1512 1513 // Inserts element *i from list x before position and removes 1514 // it from x. splice(iterator position,self_type & x,iterator i)1515 void splice(iterator position, self_type& x, iterator i) { 1516 NS_ASSERTION(!x.empty(), "Can't insert from empty list."); 1517 NS_ASSERTION(position != i && position.mCurrent != i->_mNext, 1518 "We don't check for this case."); 1519 1520 // remove from |x| 1521 i->_mPrev->_mNext = i->_mNext; 1522 i->_mNext->_mPrev = i->_mPrev; 1523 1524 // use |mCurrent| to prevent DEBUG_PASS_END assertions 1525 // link into |this|, before-side 1526 i->_mPrev = position.mCurrent->_mPrev; 1527 position.mCurrent->_mPrev->_mNext = i.get(); 1528 1529 // link into |this|, after-side 1530 i->_mNext = position.mCurrent; 1531 position.mCurrent->_mPrev = i.get(); 1532 } 1533 1534 // Inserts elements in [|first|, |last|), which are in |x|, 1535 // into |this| before |position| and removes them from |x|. splice(iterator position,self_type & x,iterator first,iterator last)1536 void splice(iterator position, self_type& x, iterator first, iterator last) { 1537 NS_ASSERTION(!x.empty(), "Can't insert from empty list."); 1538 1539 if (first == last) return; 1540 1541 --last; // so we now want to move [first, last] 1542 // remove from |x| 1543 first->_mPrev->_mNext = last->_mNext; 1544 last->_mNext->_mPrev = first->_mPrev; 1545 1546 // use |mCurrent| to prevent DEBUG_PASS_END assertions 1547 // link into |this|, before-side 1548 first->_mPrev = position.mCurrent->_mPrev; 1549 position.mCurrent->_mPrev->_mNext = first.get(); 1550 1551 // link into |this|, after-side 1552 last->_mNext = position.mCurrent; 1553 position.mCurrent->_mPrev = last.get(); 1554 } 1555 }; 1556 1557 // Many of these implementations of operator= don't work yet. I don't 1558 // know why. 1559 1560 #ifdef DEBUG 1561 1562 // NOTE: ASSIGN_FROM is meant to be used *only* as the entire body 1563 // of a function and therefore lacks PR_{BEGIN,END}_MACRO 1564 # define ASSIGN_FROM(other_) \ 1565 mCurrent = other_.mCurrent; \ 1566 mListLink = other_.mListLink; \ 1567 return *this; 1568 1569 #else /* !NS_LINELIST_DEBUG_PASS_END */ 1570 1571 # define ASSIGN_FROM(other_) \ 1572 mCurrent = other_.mCurrent; \ 1573 return *this; 1574 1575 #endif /* !NS_LINELIST_DEBUG_PASS_END */ 1576 1577 inline nsLineList_iterator& nsLineList_iterator::operator=( 1578 const nsLineList_iterator& aOther) = default; 1579 1580 inline nsLineList_iterator& nsLineList_iterator::operator=( 1581 const nsLineList_reverse_iterator& aOther) { 1582 ASSIGN_FROM(aOther) 1583 } 1584 1585 inline nsLineList_reverse_iterator& nsLineList_reverse_iterator::operator=( 1586 const nsLineList_iterator& aOther) { 1587 ASSIGN_FROM(aOther) 1588 } 1589 1590 inline nsLineList_reverse_iterator& nsLineList_reverse_iterator::operator=( 1591 const nsLineList_reverse_iterator& aOther) = default; 1592 1593 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( 1594 const nsLineList_iterator& aOther) { 1595 ASSIGN_FROM(aOther) 1596 } 1597 1598 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( 1599 const nsLineList_reverse_iterator& aOther) { 1600 ASSIGN_FROM(aOther) 1601 } 1602 1603 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( 1604 const nsLineList_const_iterator& aOther) = default; 1605 1606 inline nsLineList_const_iterator& nsLineList_const_iterator::operator=( 1607 const nsLineList_const_reverse_iterator& aOther) { 1608 ASSIGN_FROM(aOther) 1609 } 1610 1611 inline nsLineList_const_reverse_iterator& 1612 nsLineList_const_reverse_iterator::operator=( 1613 const nsLineList_iterator& aOther) { 1614 ASSIGN_FROM(aOther) 1615 } 1616 1617 inline nsLineList_const_reverse_iterator& 1618 nsLineList_const_reverse_iterator::operator=( 1619 const nsLineList_reverse_iterator& aOther) { 1620 ASSIGN_FROM(aOther) 1621 } 1622 1623 inline nsLineList_const_reverse_iterator& 1624 nsLineList_const_reverse_iterator::operator=( 1625 const nsLineList_const_iterator& aOther) { 1626 ASSIGN_FROM(aOther) 1627 } 1628 1629 inline nsLineList_const_reverse_iterator& 1630 nsLineList_const_reverse_iterator::operator=( 1631 const nsLineList_const_reverse_iterator& aOther) = default; 1632 1633 //---------------------------------------------------------------------- 1634 1635 class nsLineIterator final : public nsILineIterator { 1636 public: 1637 nsLineIterator(); 1638 ~nsLineIterator(); 1639 1640 virtual void DisposeLineIterator() override; 1641 1642 virtual int32_t GetNumLines() const override; 1643 virtual bool GetDirection() override; 1644 1645 mozilla::Result<LineInfo, nsresult> GetLine( 1646 int32_t aLineNumber) const override; 1647 virtual int32_t FindLineContaining(nsIFrame* aFrame, 1648 int32_t aStartLine = 0) override; 1649 NS_IMETHOD FindFrameAt(int32_t aLineNumber, nsPoint aPos, 1650 nsIFrame** aFrameFound, bool* aPosIsBeforeFirstFrame, 1651 bool* aPosIsAfterLastFrame) const override; 1652 1653 NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, 1654 int32_t aLineNumber) const override; 1655 NS_IMETHOD CheckLineOrder(int32_t aLine, bool* aIsReordered, 1656 nsIFrame** aFirstVisual, 1657 nsIFrame** aLastVisual) override; 1658 nsresult Init(nsLineList& aLines, bool aRightToLeft); 1659 1660 private: PrevLine()1661 nsLineBox* PrevLine() { 1662 if (0 == mIndex) { 1663 return nullptr; 1664 } 1665 return mLines[--mIndex]; 1666 } 1667 NextLine()1668 nsLineBox* NextLine() { 1669 if (mIndex >= mNumLines - 1) { 1670 return nullptr; 1671 } 1672 return mLines[++mIndex]; 1673 } 1674 LineAt(int32_t aIndex)1675 nsLineBox* LineAt(int32_t aIndex) { 1676 if ((aIndex < 0) || (aIndex >= mNumLines)) { 1677 return nullptr; 1678 } 1679 return mLines[aIndex]; 1680 } 1681 1682 nsLineBox** mLines; 1683 int32_t mIndex; 1684 int32_t mNumLines; 1685 bool mRightToLeft; 1686 }; 1687 1688 #endif /* nsLineBox_h___ */ 1689