nsLineBox.cpp (21606B)
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 #include "nsLineBox.h" 10 11 #include "mozilla/ArenaObjectID.h" 12 #include "mozilla/Assertions.h" 13 #include "mozilla/Likely.h" 14 #include "mozilla/PresShell.h" 15 #include "mozilla/Sprintf.h" 16 #include "mozilla/ToString.h" 17 #include "mozilla/WritingModes.h" 18 #include "nsBidiPresUtils.h" 19 #include "nsIFrame.h" 20 #include "nsIFrameInlines.h" 21 #include "nsPresArena.h" 22 #include "nsPrintfCString.h" 23 #include "nsWindowSizes.h" 24 25 #ifdef DEBUG 26 static int32_t ctorCount; 27 int32_t nsLineBox::GetCtorCount() { return ctorCount; } 28 #endif 29 30 #ifndef _MSC_VER 31 // static nsLineBox constant; initialized in the header file. 32 const uint32_t nsLineBox::kMinChildCountForHashtable; 33 #endif 34 35 using namespace mozilla; 36 37 nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock) 38 : mFirstChild(aFrame), 39 mContainerSize(-1, -1), 40 mBounds(WritingMode()), // mBounds will be initialized with the correct 41 // writing mode when it is set 42 mFrames(), 43 mAscent(), 44 mAllFlags(0), 45 mData(nullptr) { 46 // Assert that the union elements chosen for initialisation are at 47 // least as large as all other elements in their respective unions, so 48 // as to ensure that no parts are missed. 49 static_assert(sizeof(mFrames) >= sizeof(mChildCount), "nsLineBox init #1"); 50 static_assert(sizeof(mAllFlags) >= sizeof(mFlags), "nsLineBox init #2"); 51 static_assert(sizeof(mData) >= sizeof(mBlockData), "nsLineBox init #3"); 52 static_assert(sizeof(mData) >= sizeof(mInlineData), "nsLineBox init #4"); 53 54 MOZ_COUNT_CTOR(nsLineBox); 55 #ifdef DEBUG 56 ++ctorCount; 57 NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child"); 58 nsIFrame* f = aFrame; 59 for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) { 60 NS_ASSERTION(aIsBlock == f->IsBlockOutside(), "wrong kind of child frame"); 61 } 62 #endif 63 mChildCount = aCount; 64 MarkDirty(); 65 mFlags.mBlock = aIsBlock; 66 } 67 68 nsLineBox::~nsLineBox() { 69 MOZ_COUNT_DTOR(nsLineBox); 70 if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) { 71 delete mFrames; 72 } 73 Cleanup(); 74 } 75 76 nsLineBox* NS_NewLineBox(PresShell* aPresShell, nsIFrame* aFrame, 77 bool aIsBlock) { 78 return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock); 79 } 80 81 nsLineBox* NS_NewLineBox(PresShell* aPresShell, nsLineBox* aFromLine, 82 nsIFrame* aFrame, int32_t aCount) { 83 nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false); 84 newLine->NoteFramesMovedFrom(aFromLine); 85 newLine->mContainerSize = aFromLine->mContainerSize; 86 return newLine; 87 } 88 89 void nsLineBox::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { 90 if (mFlags.mHasHashedFrames) { 91 aSizes.mLayoutFramePropertiesSize += 92 mFrames->ShallowSizeOfIncludingThis(aSizes.mState.mMallocSizeOf); 93 } 94 } 95 96 void nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, 97 uint32_t aFromLineNewCount) { 98 MOZ_ASSERT(!mFlags.mHasHashedFrames); 99 MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount)); 100 mFrames = aFromLine->mFrames; 101 mFlags.mHasHashedFrames = 1; 102 aFromLine->mFlags.mHasHashedFrames = 0; 103 aFromLine->mChildCount = aFromLineNewCount; 104 // remove aFromLine's frames that aren't on this line 105 nsIFrame* f = aFromLine->mFirstChild; 106 for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) { 107 mFrames->Remove(f); 108 } 109 } 110 111 void nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine) { 112 uint32_t fromCount = aFromLine->GetChildCount(); 113 uint32_t toCount = GetChildCount(); 114 MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has"); 115 uint32_t fromNewCount = fromCount - toCount; 116 if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) { 117 aFromLine->mChildCount = fromNewCount; 118 MOZ_ASSERT(toCount < kMinChildCountForHashtable); 119 } else if (fromNewCount < kMinChildCountForHashtable) { 120 // aFromLine has a hash table but will not have it after moving the frames 121 // so this line can steal the hash table if it needs it. 122 if (toCount >= kMinChildCountForHashtable) { 123 StealHashTableFrom(aFromLine, fromNewCount); 124 } else { 125 delete aFromLine->mFrames; 126 aFromLine->mFlags.mHasHashedFrames = 0; 127 aFromLine->mChildCount = fromNewCount; 128 } 129 } else { 130 // aFromLine still needs a hash table. 131 if (toCount < kMinChildCountForHashtable) { 132 // remove the moved frames from it 133 nsIFrame* f = mFirstChild; 134 for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) { 135 aFromLine->mFrames->Remove(f); 136 } 137 } else if (toCount <= fromNewCount) { 138 // This line needs a hash table, allocate a hash table for it since that 139 // means fewer hash ops. 140 nsIFrame* f = mFirstChild; 141 for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) { 142 aFromLine->mFrames->Remove(f); // toCount RemoveEntry 143 } 144 SwitchToHashtable(); // toCount PutEntry 145 } else { 146 // This line needs a hash table, but it's fewer hash ops to steal 147 // aFromLine's hash table and allocate a new hash table for that line. 148 StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry 149 aFromLine->SwitchToHashtable(); // fromNewCount PutEntry 150 } 151 } 152 } 153 154 void* nsLineBox::operator new(size_t sz, PresShell* aPresShell) { 155 return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz); 156 } 157 158 void nsLineBox::Destroy(PresShell* aPresShell) { 159 this->nsLineBox::~nsLineBox(); 160 aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this); 161 } 162 163 void nsLineBox::Cleanup() { 164 if (mData) { 165 if (IsBlock()) { 166 delete mBlockData; 167 } else { 168 delete mInlineData; 169 } 170 mData = nullptr; 171 } 172 } 173 174 #ifdef DEBUG_FRAME_DUMP 175 static void ListFloats(FILE* out, const char* aPrefix, 176 const nsTArray<nsIFrame*>& aFloats, 177 bool aListOnlyDeterministic) { 178 for (nsIFrame* f : aFloats) { 179 nsCString str(aPrefix); 180 str += "floatframe"; 181 nsIFrame::ListPtr(str, aListOnlyDeterministic, f, "@"); 182 nsAutoString frameName; 183 f->GetFrameName(frameName); 184 str += NS_ConvertUTF16toUTF8(frameName).get(); 185 fprintf_stderr(out, "%s\n", str.get()); 186 } 187 } 188 189 /* static */ const char* nsLineBox::UsedClearToString(UsedClear aClearType) { 190 switch (aClearType) { 191 case UsedClear::None: 192 return "none"; 193 case UsedClear::Left: 194 return "left"; 195 case UsedClear::Right: 196 return "right"; 197 case UsedClear::Both: 198 return "both"; 199 } 200 return "unknown"; 201 } 202 203 void nsLineBox::List(FILE* out, int32_t aIndent, 204 nsIFrame::ListFlags aFlags) const { 205 nsCString str; 206 while (aIndent-- > 0) { 207 str += " "; 208 } 209 List(out, str.get(), aFlags); 210 } 211 212 void nsLineBox::List(FILE* out, const char* aPrefix, 213 nsIFrame::ListFlags aFlags) const { 214 nsCString str(aPrefix); 215 str += "line"; 216 nsIFrame::ListPtr(str, aFlags, this, "@"); 217 str += nsPrintfCString( 218 " count=%d state=%s,%s,%s,%s,%s,%s,clear-before:%s,clear-after:%s ", 219 GetChildCount(), IsBlock() ? "block" : "inline", 220 IsDirty() ? "dirty" : "clean", 221 IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", 222 IsImpactedByFloat() ? "impacted" : "not-impacted", 223 IsLineWrapped() ? "wrapped" : "not-wrapped", 224 HasForcedLineBreakAfter() ? "forced-break-after" : "no-break", 225 UsedClearToString(FloatClearTypeBefore()), 226 UsedClearToString(FloatClearTypeAfter())); 227 228 if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) { 229 const nscoord bm = GetCarriedOutBEndMargin().Get(); 230 str += nsPrintfCString("bm=%s ", 231 nsIFrame::ConvertToString(bm, aFlags).c_str()); 232 } 233 nsRect bounds = GetPhysicalBounds(); 234 str += 235 nsPrintfCString("%s ", nsIFrame::ConvertToString(bounds, aFlags).c_str()); 236 if (mWritingMode.IsVertical() || mWritingMode.IsBidiRTL()) { 237 str += nsPrintfCString( 238 "wm=%s cs=(%s) logical-rect=%s ", ToString(mWritingMode).c_str(), 239 nsIFrame::ConvertToString(mContainerSize, aFlags).c_str(), 240 nsIFrame::ConvertToString(mBounds, mWritingMode, aFlags).c_str()); 241 } 242 if (mData) { 243 const nsRect vo = mData->mOverflowAreas.InkOverflow(); 244 const nsRect so = mData->mOverflowAreas.ScrollableOverflow(); 245 if (!vo.IsEqualEdges(bounds) || !so.IsEqualEdges(bounds)) { 246 str += nsPrintfCString("ink-overflow=%s scr-overflow=%s ", 247 nsIFrame::ConvertToString(vo, aFlags).c_str(), 248 nsIFrame::ConvertToString(so, aFlags).c_str()); 249 } 250 if (mData->mInFlowChildBounds) { 251 str += nsPrintfCString( 252 "in-flow-scr-overflow=%s ", 253 nsIFrame::ConvertToString(*mData->mInFlowChildBounds, aFlags) 254 .c_str()); 255 } 256 } 257 fprintf_stderr(out, "%s<\n", str.get()); 258 259 nsIFrame* frame = mFirstChild; 260 int32_t n = GetChildCount(); 261 nsCString pfx(aPrefix); 262 pfx += " "; 263 while (--n >= 0) { 264 frame->List(out, pfx.get(), aFlags); 265 frame = frame->GetNextSibling(); 266 } 267 268 if (HasFloats()) { 269 fprintf_stderr(out, "%s> floats <\n", aPrefix); 270 ListFloats(out, pfx.get(), mInlineData->mFloats, 271 aFlags.contains(nsIFrame::ListFlag::OnlyListDeterministicInfo)); 272 } 273 fprintf_stderr(out, "%s>\n", aPrefix); 274 } 275 276 nsIFrame* nsLineBox::LastChild() const { 277 nsIFrame* frame = mFirstChild; 278 int32_t n = GetChildCount() - 1; 279 while (--n >= 0) { 280 frame = frame->GetNextSibling(); 281 } 282 return frame; 283 } 284 #endif 285 286 int32_t nsLineBox::IndexOf(const nsIFrame* aFrame) const { 287 int32_t i, n = GetChildCount(); 288 const nsIFrame* frame = mFirstChild; 289 for (i = 0; i < n; i++) { 290 if (frame == aFrame) { 291 return i; 292 } 293 frame = frame->GetNextSibling(); 294 } 295 return -1; 296 } 297 298 int32_t nsLineBox::RLIndexOf(const nsIFrame* aFrame, 299 const nsIFrame* aLastFrameInLine) const { 300 const nsIFrame* leftFrame = mFirstChild; 301 const nsIFrame* rightFrame = aLastFrameInLine; 302 int32_t leftIndex = 0, rightIndex = GetChildCount() - 1; 303 while (true) { 304 if (aFrame == rightFrame) { 305 return rightIndex; 306 } 307 if (leftIndex == rightIndex) { 308 MOZ_ASSERT(leftFrame == rightFrame, 309 "caller provided incorrect last frame"); 310 break; 311 } 312 if (aFrame == leftFrame) { 313 return leftIndex; 314 } 315 if (++leftIndex == rightIndex) { 316 MOZ_ASSERT(leftFrame->GetNextSibling() == rightFrame, 317 "caller provided incorrect last frame"); 318 break; 319 } 320 leftFrame = leftFrame->GetNextSibling(); 321 rightFrame = rightFrame->GetPrevSibling(); 322 --rightIndex; 323 } 324 return -1; 325 } 326 327 bool nsLineBox::IsEmpty() const { 328 if (IsBlock()) { 329 return mFirstChild->IsEmpty(); 330 } 331 332 nsIFrame* kid = mFirstChild; 333 for (int32_t n = GetChildCount(); n > 0; --n, kid = kid->GetNextSibling()) { 334 if (!kid->IsEmpty()) { 335 return false; 336 } 337 } 338 if (HasMarker()) { 339 return false; 340 } 341 return true; 342 } 343 344 bool nsLineBox::CachedIsEmpty() { 345 if (mFlags.mDirty) { 346 return IsEmpty(); 347 } 348 349 if (mFlags.mEmptyCacheValid) { 350 return mFlags.mEmptyCacheState; 351 } 352 353 bool result; 354 if (IsBlock()) { 355 result = mFirstChild->CachedIsEmpty(); 356 } else { 357 nsIFrame* kid = mFirstChild; 358 result = true; 359 for (int32_t n = GetChildCount(); n > 0; --n, kid = kid->GetNextSibling()) { 360 if (!kid->CachedIsEmpty()) { 361 result = false; 362 break; 363 } 364 } 365 if (HasMarker()) { 366 result = false; 367 } 368 } 369 370 mFlags.mEmptyCacheValid = true; 371 mFlags.mEmptyCacheState = result; 372 return result; 373 } 374 375 void nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines, 376 nsFrameList* aFrames, DestroyContext& aContext) { 377 PresShell* presShell = aPresContext->PresShell(); 378 379 // Keep our line list and frame list up to date as we remove frames, in case 380 // something wants to traverse the frame tree while we're destroying. 381 // 382 // Note: We delete the line list and the frames in each line in reverse order 383 // to avoid unnecessary updates to the first-continuation and first-in-flow 384 // cache. If we delete them from front to back, updating the cache has a 385 // O(n^2) time complexity. 386 while (!aLines.empty()) { 387 nsLineBox* line = aLines.back(); 388 if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) { 389 line->SwitchToCounter(); // Avoid expensive has table removals. 390 } 391 while (line->GetChildCount() > 0) { 392 nsIFrame* child = aFrames->RemoveLastChild(); 393 MOZ_DIAGNOSTIC_ASSERT(child->PresContext() == aPresContext); 394 line->NoteFrameRemoved(child); 395 child->Destroy(aContext); 396 } 397 MOZ_DIAGNOSTIC_ASSERT(line == aLines.back(), 398 "destroying child frames messed up our lines!"); 399 aLines.pop_back(); 400 line->Destroy(presShell); 401 } 402 } 403 404 bool nsLineBox::RFindLineContaining(nsIFrame* aFrame, 405 const nsLineList::iterator& aBegin, 406 nsLineList::iterator& aEnd, 407 nsIFrame* aLastFrameBeforeEnd, 408 int32_t* aFrameIndexInLine) { 409 MOZ_ASSERT(aFrame, "null ptr"); 410 411 nsIFrame* curFrame = aLastFrameBeforeEnd; 412 while (aBegin != aEnd) { 413 --aEnd; 414 NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame"); 415 if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) && 416 !aEnd->Contains(aFrame)) { 417 if (aEnd->mFirstChild) { 418 curFrame = aEnd->mFirstChild->GetPrevSibling(); 419 } 420 continue; 421 } 422 // i is the index of curFrame in aEnd 423 int32_t i = aEnd->GetChildCount() - 1; 424 while (i >= 0) { 425 if (curFrame == aFrame) { 426 *aFrameIndexInLine = i; 427 return true; 428 } 429 --i; 430 curFrame = curFrame->GetPrevSibling(); 431 } 432 MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!"); 433 } 434 *aFrameIndexInLine = -1; 435 return false; 436 } 437 438 CollapsingMargin nsLineBox::GetCarriedOutBEndMargin() const { 439 NS_ASSERTION(IsBlock(), "GetCarriedOutBEndMargin called on non-block line."); 440 return (IsBlock() && mBlockData) ? mBlockData->mCarriedOutBEndMargin 441 : CollapsingMargin(); 442 } 443 444 bool nsLineBox::SetCarriedOutBEndMargin(CollapsingMargin aValue) { 445 bool changed = false; 446 if (IsBlock()) { 447 if (!aValue.IsZero()) { 448 if (!mBlockData) { 449 mBlockData = new ExtraBlockData(GetPhysicalBounds()); 450 } 451 changed = aValue != mBlockData->mCarriedOutBEndMargin; 452 mBlockData->mCarriedOutBEndMargin = aValue; 453 } else if (mBlockData) { 454 changed = aValue != mBlockData->mCarriedOutBEndMargin; 455 mBlockData->mCarriedOutBEndMargin = aValue; 456 MaybeFreeData(); 457 } 458 } 459 return changed; 460 } 461 462 void nsLineBox::MaybeFreeData() { 463 nsRect bounds = GetPhysicalBounds(); 464 // If we have space allocated for additional data but no additional data to 465 // represent, just delete it. 466 if (mData && mData->mOverflowAreas == OverflowAreas(bounds, bounds) && 467 !mData->mInFlowChildBounds) { 468 if (IsInline()) { 469 if (mInlineData->mFloats.IsEmpty()) { 470 delete mInlineData; 471 mInlineData = nullptr; 472 } 473 } else if (mBlockData->mCarriedOutBEndMargin.IsZero()) { 474 delete mBlockData; 475 mBlockData = nullptr; 476 } 477 } 478 } 479 480 void nsLineBox::ClearFloats() { 481 MOZ_ASSERT(IsInline(), "block line can't have floats"); 482 if (IsInline() && mInlineData) { 483 mInlineData->mFloats.Clear(); 484 MaybeFreeData(); 485 } 486 } 487 488 void nsLineBox::AppendFloats(nsTArray<nsIFrame*>&& aFloats) { 489 MOZ_ASSERT(IsInline(), "block line can't have floats"); 490 if (MOZ_UNLIKELY(!IsInline())) { 491 return; 492 } 493 if (!aFloats.IsEmpty()) { 494 if (mInlineData) { 495 mInlineData->mFloats.AppendElements(std::move(aFloats)); 496 } else { 497 mInlineData = new ExtraInlineData(GetPhysicalBounds()); 498 mInlineData->mFloats = std::move(aFloats); 499 } 500 } 501 } 502 503 bool nsLineBox::RemoveFloat(nsIFrame* aFrame) { 504 MOZ_ASSERT(IsInline(), "block line can't have floats"); 505 MOZ_ASSERT(aFrame); 506 if (IsInline() && mInlineData) { 507 if (mInlineData->mFloats.RemoveElement(aFrame)) { 508 // Note: the placeholder is part of the line's child list 509 // and will be removed later. 510 MaybeFreeData(); 511 return true; 512 } 513 } 514 return false; 515 } 516 517 void nsLineBox::SetFloatEdges(nscoord aStart, nscoord aEnd) { 518 MOZ_ASSERT(IsInline(), "block line can't have float edges"); 519 if (!mInlineData) { 520 mInlineData = new ExtraInlineData(GetPhysicalBounds()); 521 } 522 mInlineData->mFloatEdgeIStart = aStart; 523 mInlineData->mFloatEdgeIEnd = aEnd; 524 } 525 526 void nsLineBox::ClearFloatEdges() { 527 MOZ_ASSERT(IsInline(), "block line can't have float edges"); 528 if (mInlineData) { 529 mInlineData->mFloatEdgeIStart = nscoord_MIN; 530 mInlineData->mFloatEdgeIEnd = nscoord_MIN; 531 } 532 } 533 534 void nsLineBox::SetOverflowAreas(const OverflowAreas& aOverflowAreas) { 535 #ifdef DEBUG 536 for (const auto otype : mozilla::AllOverflowTypes()) { 537 NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0, 538 "Illegal width for an overflow area!"); 539 NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0, 540 "Illegal height for an overflow area!"); 541 } 542 #endif 543 544 nsRect bounds = GetPhysicalBounds(); 545 if (!aOverflowAreas.InkOverflow().IsEqualInterior(bounds) || 546 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) { 547 if (!mData) { 548 if (IsInline()) { 549 mInlineData = new ExtraInlineData(bounds); 550 } else { 551 mBlockData = new ExtraBlockData(bounds); 552 } 553 } 554 mData->mOverflowAreas = aOverflowAreas; 555 } else if (mData) { 556 // Store away new value so that MaybeFreeData compares against 557 // the right value. 558 mData->mOverflowAreas = aOverflowAreas; 559 MaybeFreeData(); 560 } 561 } 562 563 void nsLineBox::SetInFlowChildBounds(const Maybe<nsRect>& aInFlowChildBounds) { 564 if (aInFlowChildBounds) { 565 if (!mData) { 566 nsRect bounds = GetPhysicalBounds(); 567 if (IsInline()) { 568 mInlineData = new ExtraInlineData(bounds); 569 } else { 570 mBlockData = new ExtraBlockData(bounds); 571 } 572 } 573 mData->mInFlowChildBounds = aInFlowChildBounds; 574 } else if (mData) { 575 mData->mInFlowChildBounds = Nothing{}; 576 MaybeFreeData(); 577 } 578 } 579 580 Maybe<nsRect> nsLineBox::GetInFlowChildBounds() const { 581 if (!mData) { 582 return Nothing{}; 583 } 584 return mData->mInFlowChildBounds; 585 } 586 587 //---------------------------------------------------------------------- 588 589 Result<nsILineIterator::LineInfo, nsresult> nsLineIterator::GetLine( 590 int32_t aLineNumber) { 591 const nsLineBox* line = GetLineAt(aLineNumber); 592 if (!line) { 593 return Err(NS_ERROR_FAILURE); 594 } 595 LineInfo structure; 596 structure.mFirstFrameOnLine = line->mFirstChild; 597 structure.mNumFramesOnLine = line->GetChildCount(); 598 structure.mLineBounds = line->GetPhysicalBounds(); 599 structure.mIsWrapped = line->IsLineWrapped(); 600 return structure; 601 } 602 603 int32_t nsLineIterator::FindLineContaining(const nsIFrame* aFrame, 604 int32_t aStartLine) { 605 const nsLineBox* line = GetLineAt(aStartLine); 606 MOZ_ASSERT(line, "aStartLine out of range"); 607 while (line) { 608 if (line->Contains(aFrame)) { 609 return mIndex; 610 } 611 line = GetNextLine(); 612 } 613 return -1; 614 } 615 616 NS_IMETHODIMP 617 nsLineIterator::CheckLineOrder(int32_t aLine, bool* aIsReordered, 618 nsIFrame** aFirstVisual, 619 nsIFrame** aLastVisual) { 620 const nsLineBox* line = GetLineAt(aLine); 621 MOZ_ASSERT(line, "aLine out of range!"); 622 623 if (!line || !line->mFirstChild) { // empty line 624 *aIsReordered = false; 625 *aFirstVisual = nullptr; 626 *aLastVisual = nullptr; 627 return NS_OK; 628 } 629 630 nsIFrame* leftmostFrame; 631 nsIFrame* rightmostFrame; 632 *aIsReordered = 633 nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), 634 &leftmostFrame, &rightmostFrame); 635 636 // map leftmost/rightmost to first/last according to paragraph direction 637 *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame; 638 *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame; 639 640 return NS_OK; 641 } 642 643 NS_IMETHODIMP 644 nsLineIterator::FindFrameAt(int32_t aLineNumber, nsPoint aPos, 645 nsIFrame** aFrameFound, 646 bool* aPosIsBeforeFirstFrame, 647 bool* aPosIsAfterLastFrame) { 648 MOZ_ASSERT(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame, 649 "null OUT ptr"); 650 651 if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) { 652 return NS_ERROR_NULL_POINTER; 653 } 654 655 const nsLineBox* line = GetLineAt(aLineNumber); 656 if (!line) { 657 *aFrameFound = nullptr; 658 *aPosIsBeforeFirstFrame = true; 659 *aPosIsAfterLastFrame = false; 660 return NS_OK; 661 } 662 663 if (line->ISize() == 0 && line->BSize() == 0) { 664 return NS_ERROR_FAILURE; 665 } 666 667 LineFrameFinder finder(aPos, line->mContainerSize, line->mWritingMode, 668 mRightToLeft); 669 int32_t n = line->GetChildCount(); 670 nsIFrame* frame = line->mFirstChild; 671 while (n--) { 672 finder.Scan(frame); 673 if (finder.IsDone()) { 674 break; 675 } 676 frame = frame->GetNextSibling(); 677 } 678 finder.Finish(aFrameFound, aPosIsBeforeFirstFrame, aPosIsAfterLastFrame); 679 return NS_OK; 680 }