nsLineBox.h (35470B)
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 <algorithm> 13 14 #include "mozilla/Likely.h" 15 #include "nsIFrame.h" 16 #include "nsILineIterator.h" 17 #include "nsStyleConsts.h" 18 #include "nsTHashSet.h" 19 20 class nsLineBox; 21 class nsWindowSizes; 22 23 namespace mozilla { 24 class PresShell; 25 } // namespace mozilla 26 27 /** 28 * Function to create a line box and initialize it with a single frame. 29 * The allocation is infallible. 30 * If the frame was moved from another line then you're responsible 31 * for notifying that line using NoteFrameRemoved(). Alternatively, 32 * it's better to use the next function that does that for you in an 33 * optimal way. 34 */ 35 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsIFrame* aFrame, 36 bool aIsBlock); 37 /** 38 * Function to create a line box and initialize it with aCount frames 39 * that are currently on aFromLine. The allocation is infallible. 40 */ 41 nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, nsLineBox* aFromLine, 42 nsIFrame* aFrame, int32_t aCount); 43 44 /** 45 * Users must have the class that is to be part of the list inherit 46 * from nsLineLink. If they want to be efficient, it should be the 47 * first base class. (This was originally nsCLink in a templatized 48 * nsCList, but it's still useful separately.) 49 */ 50 51 class nsLineList; 52 53 class nsLineLink { 54 template <typename Link, bool> 55 friend class GenericLineListIterator; 56 friend class nsLineList; 57 58 nsLineLink* _mNext; // or head 59 nsLineLink* _mPrev; // or tail 60 }; 61 62 /** 63 * The nsLineBox class represents a horizontal line of frames. It contains 64 * enough state to support incremental reflow of the frames, event handling 65 * for the frames, and rendering of the frames. 66 */ 67 class nsLineBox final : public nsLineLink { 68 private: 69 nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock); 70 ~nsLineBox(); 71 72 // Infallible overloaded new operator. Uses an arena (which comes from the 73 // presShell) to perform the allocation. 74 void* operator new(size_t sz, mozilla::PresShell* aPresShell); 75 void operator delete(void* aPtr, size_t sz) = delete; 76 77 public: 78 // Use these functions to allocate and destroy line boxes 79 friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, 80 nsIFrame* aFrame, bool aIsBlock); 81 friend nsLineBox* NS_NewLineBox(mozilla::PresShell* aPresShell, 82 nsLineBox* aFromLine, nsIFrame* aFrame, 83 int32_t aCount); 84 void Destroy(mozilla::PresShell* aPresShell); 85 86 // mBlock bit 87 bool IsBlock() const { return mFlags.mBlock; } 88 bool IsInline() const { return !mFlags.mBlock; } 89 90 // mDirty bit 91 void MarkDirty() { mFlags.mDirty = 1; } 92 void ClearDirty() { mFlags.mDirty = 0; } 93 bool IsDirty() const { return mFlags.mDirty; } 94 95 // mPreviousMarginDirty bit 96 void MarkPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 1; } 97 void ClearPreviousMarginDirty() { mFlags.mPreviousMarginDirty = 0; } 98 bool IsPreviousMarginDirty() const { return mFlags.mPreviousMarginDirty; } 99 100 // mHasClearance bit 101 void SetHasClearance() { mFlags.mHasClearance = 1; } 102 void ClearHasClearance() { mFlags.mHasClearance = 0; } 103 bool HasClearance() const { return mFlags.mHasClearance; } 104 105 // mImpactedByFloat bit 106 void SetLineIsImpactedByFloat(bool aValue) { 107 mFlags.mImpactedByFloat = aValue; 108 } 109 bool IsImpactedByFloat() const { return mFlags.mImpactedByFloat; } 110 111 // mLineWrapped bit 112 void SetLineWrapped(bool aOn) { mFlags.mLineWrapped = aOn; } 113 bool IsLineWrapped() const { return mFlags.mLineWrapped; } 114 115 // mInvalidateTextRuns bit 116 void SetInvalidateTextRuns(bool aOn) { mFlags.mInvalidateTextRuns = aOn; } 117 bool GetInvalidateTextRuns() const { return mFlags.mInvalidateTextRuns; } 118 119 // mResizeReflowOptimizationDisabled bit 120 void DisableResizeReflowOptimization() { 121 mFlags.mResizeReflowOptimizationDisabled = true; 122 } 123 void EnableResizeReflowOptimization() { 124 mFlags.mResizeReflowOptimizationDisabled = false; 125 } 126 bool ResizeReflowOptimizationDisabled() const { 127 return mFlags.mResizeReflowOptimizationDisabled; 128 } 129 130 // mHasMarker bit 131 void SetHasMarker() { 132 mFlags.mHasMarker = true; 133 InvalidateCachedIsEmpty(); 134 } 135 void ClearHasMarker() { 136 mFlags.mHasMarker = false; 137 InvalidateCachedIsEmpty(); 138 } 139 bool HasMarker() const { return mFlags.mHasMarker; } 140 141 // mHadFloatPushed bit 142 void SetHadFloatPushed() { mFlags.mHadFloatPushed = true; } 143 void ClearHadFloatPushed() { mFlags.mHadFloatPushed = false; } 144 bool HadFloatPushed() const { return mFlags.mHadFloatPushed; } 145 146 // mHasLineClampEllipsis bit 147 void SetHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = true; } 148 void ClearHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = false; } 149 bool HasLineClampEllipsis() const { return mFlags.mHasLineClampEllipsis; } 150 151 // mMovedFragments bit 152 void SetMovedFragments() { mFlags.mMovedFragments = true; } 153 void ClearMovedFragments() { mFlags.mMovedFragments = false; } 154 bool MovedFragments() const { return mFlags.mMovedFragments; } 155 156 private: 157 // Add a hash table for fast lookup when the line has more frames than this. 158 static const uint32_t kMinChildCountForHashtable = 200; 159 160 /** 161 * Take ownership of aFromLine's hash table and remove the frames that 162 * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with 163 * mFirstChild. This method is used to optimize moving a large number 164 * of frames from one line to the next. 165 */ 166 void StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount); 167 168 /** 169 * Does the equivalent of this->NoteFrameAdded and aFromLine->NoteFrameRemoved 170 * for each frame on this line, but in a optimized way. 171 */ 172 void NoteFramesMovedFrom(nsLineBox* aFromLine); 173 174 void SwitchToHashtable() { 175 MOZ_ASSERT(!mFlags.mHasHashedFrames); 176 uint32_t count = GetChildCount(); 177 mFlags.mHasHashedFrames = 1; 178 uint32_t minLength = 179 std::max(kMinChildCountForHashtable, 180 uint32_t(PLDHashTable::kDefaultInitialLength)); 181 mFrames = new nsTHashSet<nsIFrame*>(std::max(count, minLength)); 182 for (nsIFrame* f = mFirstChild; count-- > 0; f = f->GetNextSibling()) { 183 mFrames->Insert(f); 184 } 185 } 186 void SwitchToCounter() { 187 MOZ_ASSERT(mFlags.mHasHashedFrames); 188 uint32_t count = GetChildCount(); 189 delete mFrames; 190 mFlags.mHasHashedFrames = 0; 191 mChildCount = count; 192 } 193 194 public: 195 int32_t GetChildCount() const { 196 return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Count() 197 : mChildCount; 198 } 199 200 /** 201 * Register that aFrame is now on this line. 202 */ 203 void NoteFrameAdded(nsIFrame* aFrame) { 204 if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { 205 mFrames->Insert(aFrame); 206 } else { 207 if (++mChildCount >= kMinChildCountForHashtable) { 208 SwitchToHashtable(); 209 } 210 } 211 } 212 213 /** 214 * Register that aFrame is not on this line anymore. 215 */ 216 void NoteFrameRemoved(nsIFrame* aFrame) { 217 MOZ_ASSERT(GetChildCount() > 0); 218 if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { 219 mFrames->Remove(aFrame); 220 if (mFrames->Count() < kMinChildCountForHashtable) { 221 SwitchToCounter(); 222 } 223 } else { 224 --mChildCount; 225 } 226 } 227 228 // mHasForcedLineBreakAfter bit & mFloatClearType value 229 void ClearForcedLineBreak() { 230 mFlags.mHasForcedLineBreakAfter = false; 231 mFlags.mFloatClearType = mozilla::UsedClear::None; 232 } 233 234 bool HasFloatClearTypeBefore() const { 235 return FloatClearTypeBefore() != mozilla::UsedClear::None; 236 } 237 void SetFloatClearTypeBefore(mozilla::UsedClear aClearType) { 238 MOZ_ASSERT(IsBlock(), "Only block lines have break-before status!"); 239 MOZ_ASSERT(aClearType != mozilla::UsedClear::None, 240 "Only UsedClear:Left/Right/Both are allowed before a line"); 241 mFlags.mFloatClearType = aClearType; 242 } 243 mozilla::UsedClear FloatClearTypeBefore() const { 244 return IsBlock() ? mFlags.mFloatClearType : mozilla::UsedClear::None; 245 } 246 247 bool HasForcedLineBreakAfter() const { 248 MOZ_ASSERT(IsInline() || !mFlags.mHasForcedLineBreakAfter, 249 "A block line shouldn't set mHasForcedLineBreakAfter bit!"); 250 return IsInline() && mFlags.mHasForcedLineBreakAfter; 251 } 252 void SetForcedLineBreakAfter(mozilla::UsedClear aClearType) { 253 MOZ_ASSERT(IsInline(), "Only inline lines have break-after status!"); 254 mFlags.mHasForcedLineBreakAfter = true; 255 mFlags.mFloatClearType = aClearType; 256 } 257 bool HasFloatClearTypeAfter() const { 258 return FloatClearTypeAfter() != mozilla::UsedClear::None; 259 } 260 mozilla::UsedClear FloatClearTypeAfter() const { 261 return IsInline() ? mFlags.mFloatClearType : mozilla::UsedClear::None; 262 } 263 264 // mCarriedOutBEndMargin value 265 mozilla::CollapsingMargin GetCarriedOutBEndMargin() const; 266 // Returns true if the margin changed 267 bool SetCarriedOutBEndMargin(mozilla::CollapsingMargin aValue); 268 269 // mFloats 270 bool HasFloats() const { 271 return (IsInline() && mInlineData) && !mInlineData->mFloats.IsEmpty(); 272 } 273 const nsTArray<nsIFrame*>& Floats() const { 274 MOZ_ASSERT(HasFloats()); 275 return mInlineData->mFloats; 276 } 277 // Append aFloats to mFloat. aFloats will be empty. 278 void AppendFloats(nsTArray<nsIFrame*>&& aFloats); 279 void ClearFloats(); 280 bool RemoveFloat(nsIFrame* aFrame); 281 282 // The ink overflow area should never be used for things that affect layout. 283 // The scrollable overflow area are permitted to affect layout for handling of 284 // overflow and scrollbars. 285 void SetOverflowAreas(const mozilla::OverflowAreas& aOverflowAreas); 286 mozilla::LogicalRect GetOverflowArea(mozilla::OverflowType aType, 287 mozilla::WritingMode aWM, 288 const nsSize& aContainerSize) { 289 return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize); 290 } 291 nsRect GetOverflowArea(mozilla::OverflowType aType) const { 292 return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds(); 293 } 294 mozilla::OverflowAreas GetOverflowAreas() const { 295 if (mData) { 296 return mData->mOverflowAreas; 297 } 298 nsRect bounds = GetPhysicalBounds(); 299 return mozilla::OverflowAreas(bounds, bounds); 300 } 301 nsRect InkOverflowRect() const { 302 return GetOverflowArea(mozilla::OverflowType::Ink); 303 } 304 nsRect ScrollableOverflowRect() const { 305 return GetOverflowArea(mozilla::OverflowType::Scrollable); 306 } 307 308 // See comment on `mInFlowChildBounds`. 309 void SetInFlowChildBounds(const mozilla::Maybe<nsRect>& aInFlowChildBounds); 310 mozilla::Maybe<nsRect> GetInFlowChildBounds() const; 311 312 void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) { 313 NS_ASSERTION( 314 aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), 315 "container size doesn't match"); 316 mContainerSize = aContainerSize; 317 mBounds.BStart(mWritingMode) += aDBCoord; 318 if (mData) { 319 // Use a null containerSize to convert vector from logical to physical. 320 const nsSize nullContainerSize; 321 nsPoint physicalDelta = 322 mozilla::LogicalPoint(mWritingMode, 0, aDBCoord) 323 .GetPhysicalPoint(mWritingMode, nullContainerSize); 324 for (const auto otype : mozilla::AllOverflowTypes()) { 325 mData->mOverflowAreas.Overflow(otype) += physicalDelta; 326 } 327 if (mData->mInFlowChildBounds) { 328 *mData->mInFlowChildBounds += physicalDelta; 329 } 330 } 331 } 332 333 // Container-size for the line is changing (and therefore if writing mode 334 // was vertical-rl, the line will move physically; this is like SlideBy, 335 // but it is the container size instead of the line's own logical coord 336 // that is changing. 337 nsSize UpdateContainerSize(const nsSize aNewContainerSize) { 338 NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set"); 339 nsSize delta = mContainerSize - aNewContainerSize; 340 mContainerSize = aNewContainerSize; 341 // this has a physical-coordinate effect only in vertical-rl mode 342 if (mWritingMode.IsVerticalRL() && mData) { 343 nsPoint physicalDelta(-delta.width, 0); 344 for (const auto otype : mozilla::AllOverflowTypes()) { 345 mData->mOverflowAreas.Overflow(otype) += physicalDelta; 346 } 347 if (mData->mInFlowChildBounds) { 348 *mData->mInFlowChildBounds += physicalDelta; 349 } 350 } 351 return delta; 352 } 353 354 void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) { 355 NS_ASSERTION( 356 aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), 357 "container size doesn't match"); 358 mContainerSize = aContainerSize; 359 mBounds.IStart(mWritingMode) += aDICoord; 360 } 361 362 void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) { 363 NS_ASSERTION( 364 aContainerSize == mContainerSize || mContainerSize == nsSize(-1, -1), 365 "container size doesn't match"); 366 mContainerSize = aContainerSize; 367 mBounds.ISize(mWritingMode) += aDISize; 368 } 369 370 /** 371 * The logical ascent (distance from block-start to baseline) of the 372 * linebox is the logical ascent of the anonymous inline box (for 373 * which we don't actually create a frame) that wraps all the 374 * consecutive inline children of a block. 375 * 376 * This is currently unused for block lines. 377 */ 378 nscoord GetLogicalAscent() const { return mAscent; } 379 void SetLogicalAscent(nscoord aAscent) { mAscent = aAscent; } 380 381 nscoord BStart() const { return mBounds.BStart(mWritingMode); } 382 nscoord BSize() const { return mBounds.BSize(mWritingMode); } 383 nscoord BEnd() const { return mBounds.BEnd(mWritingMode); } 384 nscoord IStart() const { return mBounds.IStart(mWritingMode); } 385 nscoord ISize() const { return mBounds.ISize(mWritingMode); } 386 nscoord IEnd() const { return mBounds.IEnd(mWritingMode); } 387 void SetBoundsEmpty() { 388 mBounds.IStart(mWritingMode) = 0; 389 mBounds.ISize(mWritingMode) = 0; 390 mBounds.BStart(mWritingMode) = 0; 391 mBounds.BSize(mWritingMode) = 0; 392 } 393 394 using DestroyContext = nsIFrame::DestroyContext; 395 static void DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, 396 nsFrameList* aFrames, DestroyContext&); 397 398 // search from end to beginning of [aBegin, aEnd) 399 // Returns true if it found the line and false if not. 400 // Moves aEnd as it searches so that aEnd points to the resulting line. 401 // aLastFrameBeforeEnd is the last frame before aEnd (so if aEnd is 402 // the end of the line list, it's just the last frame in the frame 403 // list). 404 static bool RFindLineContaining(nsIFrame* aFrame, 405 const LineListIterator& aBegin, 406 LineListIterator& aEnd, 407 nsIFrame* aLastFrameBeforeEnd, 408 int32_t* aFrameIndexInLine); 409 410 #ifdef DEBUG_FRAME_DUMP 411 static const char* UsedClearToString(mozilla::UsedClear aClearType); 412 413 void List(FILE* out, int32_t aIndent, 414 nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const; 415 void List(FILE* out = stderr, const char* aPrefix = "", 416 nsIFrame::ListFlags aFlags = nsIFrame::ListFlags()) const; 417 nsIFrame* LastChild() const; 418 #endif 419 420 void AddSizeOfExcludingThis(nsWindowSizes& aSizes) const; 421 422 // Find the index of aFrame within the line, starting search at the start. 423 int32_t IndexOf(const nsIFrame* aFrame) const; 424 425 // Find the index of aFrame within the line, starting search from both ends 426 // of the line and working inwards. 427 // (Produces the same result as IndexOf, but with different performance 428 // characteristics.) The caller must provide the last frame in the line. 429 int32_t RLIndexOf(const nsIFrame* aFrame, 430 const nsIFrame* aLastFrameInLine) const; 431 432 bool Contains(const nsIFrame* aFrame) const { 433 return MOZ_UNLIKELY(mFlags.mHasHashedFrames) ? mFrames->Contains(aFrame) 434 : IndexOf(aFrame) >= 0; 435 } 436 437 // whether the line box is "logically" empty (just like nsIFrame::IsEmpty) 438 bool IsEmpty() const; 439 440 // Call this only while in Reflow() for the block the line belongs 441 // to, only between reflowing the line (or sliding it, if we skip 442 // reflowing it) and the end of reflowing the block. 443 bool CachedIsEmpty(); 444 445 void InvalidateCachedIsEmpty() { mFlags.mEmptyCacheValid = false; } 446 447 // For debugging purposes 448 bool IsValidCachedIsEmpty() { return mFlags.mEmptyCacheValid; } 449 450 #ifdef DEBUG 451 static int32_t GetCtorCount(); 452 #endif 453 454 nsIFrame* mFirstChild; 455 456 mozilla::WritingMode mWritingMode; 457 458 // Physical size. Use only for physical <-> logical coordinate conversion. 459 nsSize mContainerSize; 460 461 private: 462 mozilla::LogicalRect mBounds; 463 464 public: 465 const mozilla::LogicalRect& GetBounds() { return mBounds; } 466 nsRect GetPhysicalBounds() const { 467 if (mBounds.IsAllZero()) { 468 return nsRect(0, 0, 0, 0); 469 } 470 471 NS_ASSERTION(mContainerSize != nsSize(-1, -1), 472 "mContainerSize not initialized"); 473 return mBounds.GetPhysicalRect(mWritingMode, mContainerSize); 474 } 475 void SetBounds(mozilla::WritingMode aWritingMode, nscoord aIStart, 476 nscoord aBStart, nscoord aISize, nscoord aBSize, 477 const nsSize& aContainerSize) { 478 mWritingMode = aWritingMode; 479 mContainerSize = aContainerSize; 480 mBounds = 481 mozilla::LogicalRect(aWritingMode, aIStart, aBStart, aISize, aBSize); 482 } 483 484 // mFlags.mHasHashedFrames says which one to use 485 union { 486 nsTHashSet<nsIFrame*>* mFrames; 487 uint32_t mChildCount; 488 }; 489 490 struct FlagBits { 491 bool mDirty : 1; 492 bool mPreviousMarginDirty : 1; 493 bool mHasClearance : 1; 494 bool mBlock : 1; 495 bool mImpactedByFloat : 1; 496 bool mLineWrapped : 1; 497 bool mInvalidateTextRuns : 1; 498 // default 0 = means that the opt potentially applies to this line. 499 // 1 = never skip reflowing this line for a resize reflow 500 bool mResizeReflowOptimizationDisabled : 1; 501 bool mEmptyCacheValid : 1; 502 bool mEmptyCacheState : 1; 503 // mHasMarker indicates that this is an inline line whose block's 504 // ::marker is adjacent to this line and non-empty. 505 bool mHasMarker : 1; 506 // Indicates that this line *may* have a placeholder for a float 507 // that was pushed to a later column or page. 508 bool mHadFloatPushed : 1; 509 bool mHasHashedFrames : 1; 510 // Indicates that this line is the one identified by an ancestor block 511 // with -webkit-line-clamp on its legacy flex container, and that subsequent 512 // lines under that block are "clamped" away, and therefore we need to 513 // render a 'text-overflow: ellipsis'-like marker in this line. At most one 514 // line in the set of lines found by LineClampLineIterator for a given 515 // block will have this flag set. 516 bool mHasLineClampEllipsis : 1; 517 // Has this line moved to a different fragment of the block since 518 // the last time it was reflowed? 519 bool mMovedFragments : 1; 520 // mHasForcedLineBreakAfter indicates that this *inline* line has a 521 // break-after status due to a float clearance or ending with <br>. A block 522 // line shouldn't set this bit. 523 // 524 // Note: This bit is unrelated to CSS break-after property because it is all 525 // about line break-after for inline-level boxes. 526 bool mHasForcedLineBreakAfter : 1; 527 // mFloatClearType indicates that there's a float clearance before a block 528 // line, or after an inline line. 529 mozilla::UsedClear mFloatClearType; 530 }; 531 532 struct ExtraData { 533 explicit ExtraData(const nsRect& aBounds) 534 : mOverflowAreas(aBounds, aBounds) {} 535 mozilla::OverflowAreas mOverflowAreas; 536 // Union of the margin-boxes of our in-flow children (only children, 537 // *not* their descendants). This is part of a special contribution to 538 // the scrollable overflow of a scrolled block; as such, this is only 539 // emplaced if our block is a scrolled frame (and we have in-flow children, 540 // and floats, which are considered in-flow for scrollable overflow). 541 mozilla::Maybe<nsRect> mInFlowChildBounds; 542 }; 543 544 struct ExtraBlockData : public ExtraData { 545 explicit ExtraBlockData(const nsRect& aBounds) : ExtraData(aBounds) {} 546 mozilla::CollapsingMargin mCarriedOutBEndMargin; 547 }; 548 549 struct ExtraInlineData : public ExtraData { 550 explicit ExtraInlineData(const nsRect& aBounds) 551 : ExtraData(aBounds), 552 mFloatEdgeIStart(nscoord_MIN), 553 mFloatEdgeIEnd(nscoord_MIN) {} 554 nscoord mFloatEdgeIStart; 555 nscoord mFloatEdgeIEnd; 556 nsTArray<nsIFrame*> mFloats; 557 }; 558 559 bool GetFloatEdges(nscoord* aStart, nscoord* aEnd) const { 560 MOZ_ASSERT(IsInline(), "block line can't have float edges"); 561 if (mInlineData && mInlineData->mFloatEdgeIStart != nscoord_MIN) { 562 *aStart = mInlineData->mFloatEdgeIStart; 563 *aEnd = mInlineData->mFloatEdgeIEnd; 564 return true; 565 } 566 return false; 567 } 568 void SetFloatEdges(nscoord aStart, nscoord aEnd); 569 void ClearFloatEdges(); 570 571 protected: 572 nscoord mAscent; // see |SetAscent| / |GetAscent| 573 static_assert(sizeof(FlagBits) <= sizeof(uint32_t), 574 "size of FlagBits should not be larger than size of uint32_t"); 575 union { 576 uint32_t mAllFlags; 577 FlagBits mFlags; 578 }; 579 580 union { 581 ExtraData* mData; 582 ExtraBlockData* mBlockData; 583 ExtraInlineData* mInlineData; 584 }; 585 586 void Cleanup(); 587 void MaybeFreeData(); 588 }; 589 590 /** 591 * A linked list type where the items in the list must inherit from 592 * a link type to fuse allocations. 593 * 594 * API heavily based on the |list| class in the C++ standard. 595 */ 596 597 template <typename Link, bool IsReverse> 598 class GenericLineListIterator { 599 template <typename OtherLink, bool> 600 friend class GenericLineListIterator; 601 602 public: 603 friend class nsLineList; 604 605 using self_type = GenericLineListIterator<Link, IsReverse>; 606 static constexpr bool is_const = std::is_const_v<Link>; 607 608 using const_reference = const nsLineBox&; 609 using const_pointer = const nsLineBox*; 610 using reference = std::conditional_t<is_const, const_reference, nsLineBox&>; 611 using pointer = std::conditional_t<is_const, const_pointer, nsLineBox*>; 612 using size_type = uint32_t; 613 using link_type = Link; 614 615 #ifdef DEBUG 616 GenericLineListIterator() : mListLink(nullptr) { 617 memset(&mCurrent, 0xcd, sizeof(mCurrent)); 618 } 619 #else 620 // Auto generated default constructor OK. 621 #endif 622 // Auto generated copy-constructor OK. 623 624 template <typename OtherLink, bool OtherIsReverse> 625 self_type& operator=( 626 const GenericLineListIterator<OtherLink, OtherIsReverse>& aOther) { 627 mCurrent = aOther.mCurrent; 628 #ifdef DEBUG 629 mListLink = aOther.mListLink; 630 #endif 631 return *this; 632 } 633 634 self_type& SetPosition(pointer p) { 635 mCurrent = p; 636 return *this; 637 } 638 639 self_type& operator++() { 640 mCurrent = IsReverse ? mCurrent->_mPrev : mCurrent->_mNext; 641 return *this; 642 } 643 644 self_type operator++(int) { 645 self_type rv(*this); 646 mCurrent = IsReverse ? mCurrent->_mPrev : mCurrent->_mNext; 647 return rv; 648 } 649 650 self_type& operator--() { 651 mCurrent = IsReverse ? mCurrent->_mNext : mCurrent->_mPrev; 652 return *this; 653 } 654 655 self_type operator--(int) { 656 self_type rv(*this); 657 mCurrent = IsReverse ? mCurrent->_mNext : mCurrent->_mPrev; 658 return rv; 659 } 660 661 pointer get() { 662 MOZ_ASSERT(mListLink); 663 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 664 return static_cast<pointer>(mCurrent); 665 } 666 667 const_pointer get() const { 668 MOZ_ASSERT(mListLink); 669 MOZ_ASSERT(mCurrent != mListLink, "running past end"); 670 return static_cast<const_pointer>(mCurrent); 671 } 672 673 reference operator*() { return *get(); } 674 pointer operator->() { return get(); } 675 operator pointer() { return get(); } 676 const_reference operator*() const { return *get(); } 677 const_pointer operator->() const { return get(); } 678 operator const_pointer() const { return get(); } 679 680 self_type next() { 681 self_type copy(*this); 682 return ++copy; 683 } 684 685 self_type next() const { 686 self_type copy(*this); 687 return ++copy; 688 } 689 690 self_type prev() { 691 self_type copy(*this); 692 return --copy; 693 } 694 695 self_type prev() const { 696 self_type copy(*this); 697 return --copy; 698 } 699 700 bool operator==(const self_type& aOther) const { 701 MOZ_ASSERT(mListLink); 702 MOZ_ASSERT(mListLink == aOther.mListLink, 703 "comparing iterators over different lists"); 704 return mCurrent == aOther.mCurrent; 705 } 706 bool operator!=(const self_type&) const = default; 707 708 #ifdef DEBUG 709 bool IsInSameList(const self_type& aOther) const { 710 return mListLink == aOther.mListLink; 711 } 712 #endif 713 714 private: 715 link_type* mCurrent; 716 #ifdef DEBUG 717 link_type* mListLink; // the list's link, i.e., the end 718 #endif 719 }; 720 721 class nsLineList { 722 public: 723 using self_type = nsLineList; 724 using const_reference = const nsLineBox&; 725 using pointer = nsLineBox*; 726 using const_pointer = const nsLineBox*; 727 using size_type = uint32_t; 728 using link_type = nsLineLink; 729 730 private: 731 link_type mLink; 732 733 public: 734 using iterator = GenericLineListIterator<nsLineLink, false>; 735 using reverse_iterator = GenericLineListIterator<nsLineLink, true>; 736 using const_iterator = GenericLineListIterator<const nsLineLink, false>; 737 using const_reverse_iterator = 738 GenericLineListIterator<const nsLineLink, true>; 739 740 nsLineList() { 741 MOZ_COUNT_CTOR(nsLineList); 742 clear(); 743 } 744 745 MOZ_COUNTED_DTOR(nsLineList) 746 747 const_iterator begin() const { 748 const_iterator rv; 749 rv.mCurrent = mLink._mNext; 750 #ifdef DEBUG 751 rv.mListLink = &mLink; 752 #endif 753 return rv; 754 } 755 756 iterator begin() { 757 iterator rv; 758 rv.mCurrent = mLink._mNext; 759 #ifdef DEBUG 760 rv.mListLink = &mLink; 761 #endif 762 return rv; 763 } 764 765 iterator begin(nsLineBox* aLine) { 766 iterator rv; 767 rv.mCurrent = aLine; 768 #ifdef DEBUG 769 rv.mListLink = &mLink; 770 #endif 771 return rv; 772 } 773 774 const_iterator end() const { 775 const_iterator rv; 776 rv.mCurrent = &mLink; 777 #ifdef DEBUG 778 rv.mListLink = &mLink; 779 #endif 780 return rv; 781 } 782 783 iterator end() { 784 iterator rv; 785 rv.mCurrent = &mLink; 786 #ifdef DEBUG 787 rv.mListLink = &mLink; 788 #endif 789 return rv; 790 } 791 792 const_reverse_iterator rbegin() const { 793 const_reverse_iterator rv; 794 rv.mCurrent = mLink._mPrev; 795 #ifdef DEBUG 796 rv.mListLink = &mLink; 797 #endif 798 return rv; 799 } 800 801 reverse_iterator rbegin() { 802 reverse_iterator rv; 803 rv.mCurrent = mLink._mPrev; 804 #ifdef DEBUG 805 rv.mListLink = &mLink; 806 #endif 807 return rv; 808 } 809 810 reverse_iterator rbegin(nsLineBox* aLine) { 811 reverse_iterator rv; 812 rv.mCurrent = aLine; 813 #ifdef DEBUG 814 rv.mListLink = &mLink; 815 #endif 816 return rv; 817 } 818 819 const_reverse_iterator rend() const { 820 const_reverse_iterator rv; 821 rv.mCurrent = &mLink; 822 #ifdef DEBUG 823 rv.mListLink = &mLink; 824 #endif 825 return rv; 826 } 827 828 reverse_iterator rend() { 829 reverse_iterator rv; 830 rv.mCurrent = &mLink; 831 #ifdef DEBUG 832 rv.mListLink = &mLink; 833 #endif 834 return rv; 835 } 836 837 bool empty() const { return mLink._mNext == &mLink; } 838 839 // NOTE: O(N). 840 size_type size() const { 841 size_type count = 0; 842 for (const link_type* cur = mLink._mNext; cur != &mLink; 843 cur = cur->_mNext) { 844 ++count; 845 } 846 return count; 847 } 848 849 pointer front() { 850 NS_ASSERTION(!empty(), "no element to return"); 851 return static_cast<pointer>(mLink._mNext); 852 } 853 854 const_pointer front() const { 855 NS_ASSERTION(!empty(), "no element to return"); 856 return static_cast<const_pointer>(mLink._mNext); 857 } 858 859 pointer back() { 860 NS_ASSERTION(!empty(), "no element to return"); 861 return static_cast<pointer>(mLink._mPrev); 862 } 863 864 const_pointer back() const { 865 NS_ASSERTION(!empty(), "no element to return"); 866 return static_cast<const_pointer>(mLink._mPrev); 867 } 868 869 void push_front(pointer aNew) { 870 aNew->_mNext = mLink._mNext; 871 mLink._mNext->_mPrev = aNew; 872 aNew->_mPrev = &mLink; 873 mLink._mNext = aNew; 874 } 875 876 void pop_front() 877 // NOTE: leaves dangling next/prev pointers 878 { 879 NS_ASSERTION(!empty(), "no element to pop"); 880 link_type* newFirst = mLink._mNext->_mNext; 881 newFirst->_mPrev = &mLink; 882 // mLink._mNext->_mNext = nullptr; 883 // mLink._mNext->_mPrev = nullptr; 884 mLink._mNext = newFirst; 885 } 886 887 void push_back(pointer aNew) { 888 aNew->_mPrev = mLink._mPrev; 889 mLink._mPrev->_mNext = aNew; 890 aNew->_mNext = &mLink; 891 mLink._mPrev = aNew; 892 } 893 894 void pop_back() 895 // NOTE: leaves dangling next/prev pointers 896 { 897 NS_ASSERTION(!empty(), "no element to pop"); 898 link_type* newLast = mLink._mPrev->_mPrev; 899 newLast->_mNext = &mLink; 900 // mLink._mPrev->_mPrev = nullptr; 901 // mLink._mPrev->_mNext = nullptr; 902 mLink._mPrev = newLast; 903 } 904 905 // inserts x before position 906 iterator before_insert(iterator position, pointer x) { 907 // use |mCurrent| to prevent DEBUG_PASS_END assertions 908 x->_mPrev = position.mCurrent->_mPrev; 909 x->_mNext = position.mCurrent; 910 position.mCurrent->_mPrev->_mNext = x; 911 position.mCurrent->_mPrev = x; 912 return --position; 913 } 914 915 // inserts x after position 916 iterator after_insert(iterator position, pointer x) { 917 // use |mCurrent| to prevent DEBUG_PASS_END assertions 918 x->_mNext = position.mCurrent->_mNext; 919 x->_mPrev = position.mCurrent; 920 position.mCurrent->_mNext->_mPrev = x; 921 position.mCurrent->_mNext = x; 922 return ++position; 923 } 924 925 // returns iterator pointing to after the element 926 iterator erase(iterator position) 927 // NOTE: leaves dangling next/prev pointers (except in DEBUG build) 928 { 929 position->_mPrev->_mNext = position->_mNext; 930 position->_mNext->_mPrev = position->_mPrev; 931 #ifdef DEBUG 932 nsLineLink* dead = position; 933 iterator next = ++position; 934 memset(dead, 0, sizeof(*dead)); 935 return next; 936 #else 937 return ++position; 938 #endif 939 } 940 941 void swap(self_type& y) { 942 link_type tmp(y.mLink); 943 y.mLink = mLink; 944 mLink = tmp; 945 946 if (!empty()) { 947 mLink._mNext->_mPrev = &mLink; 948 mLink._mPrev->_mNext = &mLink; 949 } 950 951 if (!y.empty()) { 952 y.mLink._mNext->_mPrev = &y.mLink; 953 y.mLink._mPrev->_mNext = &y.mLink; 954 } 955 } 956 957 void clear() 958 // NOTE: leaves dangling next/prev pointers 959 { 960 mLink._mNext = &mLink; 961 mLink._mPrev = &mLink; 962 } 963 964 // inserts the conts of x before position and makes x empty 965 void splice(iterator position, self_type& x) { 966 // use |mCurrent| to prevent DEBUG_PASS_END assertions 967 position.mCurrent->_mPrev->_mNext = x.mLink._mNext; 968 x.mLink._mNext->_mPrev = position.mCurrent->_mPrev; 969 x.mLink._mPrev->_mNext = position.mCurrent; 970 position.mCurrent->_mPrev = x.mLink._mPrev; 971 x.clear(); 972 } 973 974 // Inserts element *i from list x before position and removes 975 // it from x. 976 void splice(iterator position, self_type& x, iterator i) { 977 NS_ASSERTION(!x.empty(), "Can't insert from empty list."); 978 NS_ASSERTION(position != i && position.mCurrent != i->_mNext, 979 "We don't check for this case."); 980 981 // remove from |x| 982 i->_mPrev->_mNext = i->_mNext; 983 i->_mNext->_mPrev = i->_mPrev; 984 985 // use |mCurrent| to prevent DEBUG_PASS_END assertions 986 // link into |this|, before-side 987 i->_mPrev = position.mCurrent->_mPrev; 988 position.mCurrent->_mPrev->_mNext = i.get(); 989 990 // link into |this|, after-side 991 i->_mNext = position.mCurrent; 992 position.mCurrent->_mPrev = i.get(); 993 } 994 995 // Inserts elements in [|first|, |last|), which are in |x|, 996 // into |this| before |position| and removes them from |x|. 997 void splice(iterator position, self_type& x, iterator first, iterator last) { 998 NS_ASSERTION(!x.empty(), "Can't insert from empty list."); 999 1000 if (first == last) { 1001 return; 1002 } 1003 1004 --last; // so we now want to move [first, last] 1005 // remove from |x| 1006 first->_mPrev->_mNext = last->_mNext; 1007 last->_mNext->_mPrev = first->_mPrev; 1008 1009 // use |mCurrent| to prevent DEBUG_PASS_END assertions 1010 // link into |this|, before-side 1011 first->_mPrev = position.mCurrent->_mPrev; 1012 position.mCurrent->_mPrev->_mNext = first.get(); 1013 1014 // link into |this|, after-side 1015 last->_mNext = position.mCurrent; 1016 position.mCurrent->_mPrev = last.get(); 1017 } 1018 }; 1019 1020 //---------------------------------------------------------------------- 1021 1022 class nsLineIterator final : public nsILineIterator { 1023 public: 1024 nsLineIterator(const nsLineList& aLines, bool aRightToLeft) 1025 : mLines(aLines), mRightToLeft(aRightToLeft) { 1026 mIter = mLines.begin(); 1027 if (mIter != mLines.end()) { 1028 mIndex = 0; 1029 } 1030 } 1031 1032 int32_t GetNumLines() const final { 1033 if (mNumLines < 0) { 1034 mNumLines = int32_t(mLines.size()); // This is O(N) in number of lines! 1035 } 1036 return mNumLines; 1037 } 1038 1039 bool IsLineIteratorFlowRTL() final { return mRightToLeft; } 1040 1041 // Note that this updates the iterator's current position! 1042 mozilla::Result<LineInfo, nsresult> GetLine(int32_t aLineNumber) final; 1043 1044 int32_t FindLineContaining(const nsIFrame* aFrame, 1045 int32_t aStartLine = 0) final; 1046 1047 NS_IMETHOD FindFrameAt(int32_t aLineNumber, nsPoint aPos, 1048 nsIFrame** aFrameFound, bool* aPosIsBeforeFirstFrame, 1049 bool* aPosIsAfterLastFrame) final; 1050 1051 NS_IMETHOD CheckLineOrder(int32_t aLine, bool* aIsReordered, 1052 nsIFrame** aFirstVisual, 1053 nsIFrame** aLastVisual) final; 1054 1055 private: 1056 nsLineIterator() = delete; 1057 nsLineIterator(const nsLineIterator& aOther) = delete; 1058 1059 const nsLineBox* GetNextLine() { 1060 MOZ_ASSERT(mIter != mLines.end(), "Already at end!"); 1061 ++mIndex; 1062 ++mIter; 1063 if (mIter == mLines.end()) { 1064 MOZ_ASSERT(mNumLines < 0 || mNumLines == mIndex); 1065 mNumLines = mIndex; 1066 return nullptr; 1067 } 1068 return mIter.get(); 1069 } 1070 1071 // Note that this updates the iterator's current position to the given line. 1072 const nsLineBox* GetLineAt(int32_t aIndex) { 1073 MOZ_ASSERT(mIndex >= 0); 1074 if (aIndex < 0 || (mNumLines >= 0 && aIndex >= mNumLines)) { 1075 return nullptr; 1076 } 1077 // Check if we should start counting lines from mIndex, or reset to the 1078 // start or end of the list and count from there (if the requested index is 1079 // closer to an end than to the current position). 1080 if (aIndex < mIndex / 2) { 1081 // Reset to the beginning and search from there. 1082 mIter = mLines.begin(); 1083 mIndex = 0; 1084 } else if (mNumLines > 0 && aIndex > (mNumLines + mIndex) / 2) { 1085 // Jump to the end and search back from there. 1086 mIter = mLines.end(); 1087 --mIter; 1088 mIndex = mNumLines - 1; 1089 } 1090 while (mIndex > aIndex) { 1091 // This cannot run past the start of the list, because we checked that 1092 // aIndex is non-negative. 1093 --mIter; 1094 --mIndex; 1095 } 1096 while (mIndex < aIndex) { 1097 // Here we have to check for reaching the end, as aIndex could be out of 1098 // range (if mNumLines was not initialized, so we couldn't range-check 1099 // aIndex on entry). 1100 if (mIter == mLines.end()) { 1101 MOZ_ASSERT(mNumLines < 0 || mNumLines == mIndex); 1102 mNumLines = mIndex; 1103 return nullptr; 1104 } 1105 ++mIter; 1106 ++mIndex; 1107 } 1108 return mIter.get(); 1109 } 1110 1111 const nsLineList& mLines; 1112 nsLineList::const_iterator mIter; 1113 int32_t mIndex = -1; 1114 mutable int32_t mNumLines = -1; 1115 const bool mRightToLeft; 1116 }; 1117 1118 #endif /* nsLineBox_h___ */