nsTextFrame.h (48030B)
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 #ifndef nsTextFrame_h__ 8 #define nsTextFrame_h__ 9 10 #include "JustificationUtils.h" 11 #include "gfxSkipChars.h" 12 #include "gfxTextRun.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/UniquePtr.h" 15 #include "mozilla/dom/Text.h" 16 #include "mozilla/gfx/2D.h" 17 #include "nsIFrame.h" 18 #include "nsISelectionController.h" 19 #include "nsSplittableFrame.h" 20 21 // Undo the windows.h damage 22 #if defined(XP_WIN) && defined(DrawText) 23 # undef DrawText 24 #endif 25 26 struct SelectionDetails; 27 class nsBlockFrame; 28 class nsTextPaintStyle; 29 30 namespace mozilla { 31 class SVGContextPaint; 32 class SVGTextFrame; 33 class nsDisplayTextGeometry; 34 class nsDisplayText; 35 namespace dom { 36 class CharacterDataBuffer; 37 } 38 39 /** 40 * Helper for CSS text-autospace, used by nsTextFrame::PropertyProvider. 41 */ 42 class MOZ_STACK_CLASS TextAutospace final { 43 public: 44 enum class CharClass : uint8_t { 45 Other, 46 CombiningMark, 47 Ideograph, 48 NonIdeographicLetter, 49 NonIdeographicNumeral, 50 }; 51 52 enum class Boundary : uint8_t { 53 IdeographAlpha, 54 IdeographNumeric, 55 }; 56 using BoundarySet = EnumSet<Boundary>; 57 58 // Returns true if inter-script spacing may be added at boundaries. 59 static bool Enabled(const StyleTextAutospace& aStyleTextAutospace, 60 const nsTextFrame* aFrame); 61 62 TextAutospace(const StyleTextAutospace& aStyleTextAutospace, 63 nscoord aSpacing); 64 65 nscoord InterScriptSpacing() const { return mInterScriptSpacing; } 66 67 // Return true if inter-script spacing should be applied between aPrevClass 68 // and aCurrClass. 69 bool ShouldApplySpacing(CharClass aPrevClass, CharClass aCurrClass) const; 70 71 // Returns true when non-ideographic letters/numerals should not participate 72 // in autospace boundaries for aFrame. 73 static bool ShouldSuppressLetterNumeralSpacing(const nsIFrame* aFrame); 74 75 // Return true if aChar is an ideograph. 76 // https://drafts.csswg.org/css-text-4/#ideographs 77 static bool IsIdeograph(char32_t aChar); 78 79 // Get character class for aChar. 80 // https://drafts.csswg.org/css-text-4/#text-spacing-classes 81 static CharClass GetCharClass(char32_t aChar); 82 83 private: 84 BoundarySet InitBoundarySet( 85 const StyleTextAutospace& aStyleTextAutospace) const; 86 87 // Enabled boundaries. When non-empty, insert spacing at these class 88 // boundaries (e.g. ideograph-alpha, ideograph-numeric). 89 BoundarySet mBoundarySet; 90 91 // Inter-script spacing amount to add at boundaries. 92 nscoord mInterScriptSpacing{}; 93 }; 94 95 } // namespace mozilla 96 97 class nsTextFrame : public nsIFrame { 98 using DrawTarget = mozilla::gfx::DrawTarget; 99 using LayoutDeviceRect = mozilla::LayoutDeviceRect; 100 using Point = mozilla::gfx::Point; 101 using Range = gfxTextRun::Range; 102 using Rect = mozilla::gfx::Rect; 103 using SelectionType = mozilla::SelectionType; 104 using SelectionTypeMask = mozilla::SelectionTypeMask; 105 using Size = mozilla::gfx::Size; 106 using TextRangeStyle = mozilla::TextRangeStyle; 107 108 public: 109 enum TextRunType : uint8_t; 110 struct TabWidthStore; 111 112 /** 113 * An implementation of gfxTextRun::PropertyProvider that computes spacing and 114 * hyphenation based on CSS properties for a text frame. 115 * 116 * nsTextFrame normally creates a PropertyProvider as a temporary object on 117 * on the stack, but this is not marked MOZ_STACK_CLASS because SVGTextFrame 118 * wants to cache an instance across multiple calls using the same textframe. 119 */ 120 class PropertyProvider final : public gfxTextRun::PropertyProvider { 121 using HyphenType = gfxTextRun::HyphenType; 122 123 public: 124 /** 125 * Use this constructor for reflow, when we don't know what text is 126 * really mapped by the frame and we have a lot of other data around. 127 * 128 * @param aLength can be INT32_MAX to indicate we cover all the text 129 * associated with aFrame up to where its flow chain ends in the given 130 * textrun. If INT32_MAX is passed, justification and hyphen-related methods 131 * cannot be called, nor can GetOriginalLength(). 132 */ 133 PropertyProvider(gfxTextRun* aTextRun, const nsStyleText* aTextStyle, 134 const mozilla::dom::CharacterDataBuffer& aBuffer, 135 nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart, 136 int32_t aLength, nsIFrame* aLineContainer, 137 nscoord aOffsetFromBlockOriginForTabs, 138 nsTextFrame::TextRunType aWhichTextRun, 139 bool aAtStartOfLine); 140 141 /** 142 * Use this constructor after the frame has been reflowed and we don't 143 * have other data around. Gets everything from the frame. EnsureTextRun 144 * *must* be called before this!!! 145 */ 146 PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart, 147 nsTextFrame::TextRunType aWhichTextRun, 148 nsFontMetrics* aFontMetrics); 149 150 /** 151 * As above, but assuming we want the inflated text run and associated 152 * metrics. 153 */ 154 PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart) 155 : PropertyProvider(aFrame, aStart, nsTextFrame::eInflated, 156 aFrame->InflatedFontMetrics()) {} 157 158 // Call this after construction if you're not going to reflow the text 159 void InitializeForDisplay(bool aTrimAfter); 160 161 void InitializeForMeasure(); 162 163 bool GetSpacing(Range aRange, Spacing* aSpacing) const final; 164 gfxFloat GetHyphenWidth() const final; 165 void GetHyphenationBreaks(Range aRange, 166 HyphenType* aBreakBefore) const final; 167 mozilla::StyleHyphens GetHyphensOption() const final { 168 return mTextStyle->mHyphens; 169 } 170 mozilla::gfx::ShapedTextFlags GetShapedTextFlags() const final; 171 172 already_AddRefed<DrawTarget> GetDrawTarget() const final; 173 174 uint32_t GetAppUnitsPerDevUnit() const final { 175 return mTextRun->GetAppUnitsPerDevUnit(); 176 } 177 178 bool GetSpacingInternal(Range aRange, Spacing* aSpacing, 179 bool aIgnoreTabs) const; 180 181 /** 182 * Compute the justification information in given DOM range, return 183 * justification info and assignments if requested. 184 */ 185 mozilla::JustificationInfo ComputeJustification( 186 Range aRange, 187 nsTArray<mozilla::JustificationAssignment>* aAssignments = nullptr); 188 189 const nsTextFrame* GetFrame() const { return mFrame; } 190 // This may not be equal to the frame offset/length in because we may have 191 // adjusted for whitespace trimming according to the state bits set in the 192 // frame (for the static provider) 193 const gfxSkipCharsIterator& GetStart() const { return mStart; } 194 // May return INT32_MAX if that was given to the constructor 195 uint32_t GetOriginalLength() const { 196 NS_ASSERTION(mLength != INT32_MAX, "Length not known"); 197 return mLength; 198 } 199 const mozilla::dom::CharacterDataBuffer& GetCharacterDataBuffer() const { 200 return mCharacterDataBuffer; 201 } 202 203 gfxFontGroup* GetFontGroup() const { 204 if (!mFontGroup) { 205 mFontGroup = GetFontMetrics()->GetThebesFontGroup(); 206 } 207 return mFontGroup; 208 } 209 210 nsFontMetrics* GetFontMetrics() const { 211 if (!mFontMetrics) { 212 InitFontGroupAndFontMetrics(); 213 } 214 return mFontMetrics; 215 } 216 217 void CalcTabWidths(Range aTransformedRange, gfxFloat aTabWidth) const; 218 219 gfxFloat MinTabAdvance() const; 220 221 const gfxSkipCharsIterator& GetEndHint() const { return mTempIterator; } 222 223 // Set a position that should be treated as start-of-line (for trimming 224 // potential letter-spacing). 225 void SetStartOfLine(const gfxSkipCharsIterator& aPosition) { 226 mStartOfLineOffset = aPosition.GetSkippedOffset(); 227 } 228 229 protected: 230 void SetupJustificationSpacing(bool aPostReflow); 231 232 void InitFontGroupAndFontMetrics() const; 233 234 // Initialize TextAutospace, if inter-script spacing applies. 235 void InitTextAutospace(); 236 237 const RefPtr<gfxTextRun> mTextRun; 238 mutable gfxFontGroup* mFontGroup; 239 mutable RefPtr<nsFontMetrics> mFontMetrics; 240 const nsStyleText* mTextStyle; 241 const mozilla::dom::CharacterDataBuffer& mCharacterDataBuffer; 242 const nsIFrame* mLineContainer; 243 nsTextFrame* mFrame; 244 245 // Offset in original and transformed string 246 gfxSkipCharsIterator mStart; 247 248 const gfxSkipCharsIterator mTempIterator; 249 250 // Either null, or pointing to the frame's TabWidthProperty. 251 mutable nsTextFrame::TabWidthStore* mTabWidths; 252 253 // How far we've done tab-width calculation; this is ONLY valid when 254 // mTabWidths is nullptr (otherwise rely on mTabWidths->mLimit instead). 255 // It's a DOM offset relative to the current frame's offset. 256 mutable uint32_t mTabWidthsAnalyzedLimit; 257 258 // DOM string length, may be INT32_MAX 259 int32_t mLength; 260 261 // space for each whitespace char 262 const nscoord mWordSpacing; 263 264 // space for each letter 265 const nscoord mLetterSpacing; 266 267 // If TextAutospace exists, inter-script spacing applies. 268 Maybe<mozilla::TextAutospace> mTextAutospace; 269 270 // min advance for <tab> char 271 mutable gfxFloat mMinTabAdvance; 272 273 mutable gfxFloat mHyphenWidth; 274 mutable gfxFloat mOffsetFromBlockOriginForTabs; 275 276 // The values in mJustificationSpacings corresponds to unskipped 277 // characters start from mJustificationArrayStart. 278 uint32_t mJustificationArrayStart; 279 nsTArray<Spacing> mJustificationSpacings; 280 281 const bool mReflowing; 282 const nsTextFrame::TextRunType mWhichTextRun; 283 uint32_t mStartOfLineOffset = UINT32_MAX; 284 }; 285 286 explicit nsTextFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, 287 ClassID aID = kClassID) 288 : nsIFrame(aStyle, aPresContext, aID) {} 289 290 NS_DECL_FRAMEARENA_HELPERS(nsTextFrame) 291 292 friend class nsContinuingTextFrame; 293 294 // nsQueryFrame 295 NS_DECL_QUERYFRAME 296 297 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContinuationsProperty, 298 nsTArray<nsTextFrame*>) 299 300 // nsIFrame 301 void BuildDisplayList(nsDisplayListBuilder* aBuilder, 302 const nsDisplayListSet& aLists) final; 303 304 void Init(nsIContent* aContent, nsContainerFrame* aParent, 305 nsIFrame* aPrevInFlow) override; 306 307 void Destroy(DestroyContext&) override; 308 309 Cursor GetCursor(const nsPoint&) final; 310 311 nsresult CharacterDataChanged(const CharacterDataChangeInfo&) final; 312 313 nsTextFrame* FirstContinuation() const override { 314 return const_cast<nsTextFrame*>(this); 315 } 316 nsTextFrame* GetPrevContinuation() const override { return nullptr; } 317 nsTextFrame* GetNextContinuation() const final { return mNextContinuation; } 318 void SetNextContinuation(nsIFrame* aNextContinuation) final { 319 NS_ASSERTION(!aNextContinuation || Type() == aNextContinuation->Type(), 320 "setting a next continuation with incorrect type!"); 321 NS_ASSERTION( 322 !nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this), 323 "creating a loop in continuation chain!"); 324 mNextContinuation = static_cast<nsTextFrame*>(aNextContinuation); 325 if (aNextContinuation) { 326 aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION); 327 } 328 // Setting a non-fluid continuation might affect our flow length (they're 329 // quite rare so we assume it always does) so we delete our cached value: 330 if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) { 331 GetContent()->RemoveProperty(nsGkAtoms::flowlength); 332 GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY); 333 } 334 } 335 nsTextFrame* GetNextInFlow() const final { 336 return mNextContinuation && mNextContinuation->HasAnyStateBits( 337 NS_FRAME_IS_FLUID_CONTINUATION) 338 ? mNextContinuation 339 : nullptr; 340 } 341 void SetNextInFlow(nsIFrame* aNextInFlow) final { 342 NS_ASSERTION(!aNextInFlow || Type() == aNextInFlow->Type(), 343 "setting a next in flow with incorrect type!"); 344 NS_ASSERTION( 345 !nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this), 346 "creating a loop in continuation chain!"); 347 mNextContinuation = static_cast<nsTextFrame*>(aNextInFlow); 348 if (mNextContinuation && 349 !mNextContinuation->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) { 350 // Changing from non-fluid to fluid continuation might affect our flow 351 // length, so we delete our cached value: 352 if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) { 353 GetContent()->RemoveProperty(nsGkAtoms::flowlength); 354 GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY); 355 } 356 } 357 if (aNextInFlow) { 358 aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); 359 } 360 } 361 nsTextFrame* LastInFlow() const final; 362 nsTextFrame* LastContinuation() const final; 363 364 bool ShouldSuppressLineBreak() const; 365 366 void InvalidateFrame(uint32_t aDisplayItemKey = 0, 367 bool aRebuildDisplayItems = true) final; 368 void InvalidateFrameWithRect(const nsRect& aRect, 369 uint32_t aDisplayItemKey = 0, 370 bool aRebuildDisplayItems = true) final; 371 372 #ifdef DEBUG_FRAME_DUMP 373 void List(FILE* out = stderr, const char* aPrefix = "", 374 ListFlags aFlags = ListFlags()) const final; 375 nsresult GetFrameName(nsAString& aResult) const final; 376 void ToCString(nsCString& aBuf) const; 377 void ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const final; 378 #endif 379 380 // Returns this text frame's content's text fragment. 381 // 382 // Assertions in Init() ensure we only ever get a Text node as content. 383 const mozilla::dom::CharacterDataBuffer& CharacterDataBuffer() const { 384 return mContent->AsText()->DataBuffer(); 385 } 386 387 /** 388 * Check that the text in this frame is entirely whitespace. Importantly, 389 * this function considers non-breaking spaces (0xa0) to be whitespace, 390 * whereas nsTextFrame::IsEmpty does not. It also considers both one and 391 * two-byte chars. 392 */ 393 bool IsEntirelyWhitespace() const; 394 395 ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) final; 396 ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint& aPoint); 397 398 /** 399 * This is called only on the primary text frame. It indicates that 400 * the selection state of the given character range has changed. 401 * Frames corresponding to the character range are unconditionally invalidated 402 * (Selection::Repaint depends on this). 403 * @param aStart start of character range. 404 * @param aEnd end (exclusive) of character range. 405 * @param aSelected true iff the character range is now selected. 406 * @param aType the type of the changed selection. 407 */ 408 void SelectionStateChanged(uint32_t aStart, uint32_t aEnd, bool aSelected, 409 SelectionType aSelectionType); 410 411 FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) final; 412 FrameSearchResult PeekOffsetCharacter( 413 bool aForward, int32_t* aOffset, 414 PeekOffsetCharacterOptions aOptions = PeekOffsetCharacterOptions()) final; 415 FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, 416 bool aIsKeyboardSelect, int32_t* aOffset, 417 PeekWordState* aState, 418 bool aTrimSpaces) final; 419 420 // Helper method that editor code uses to test for visibility. 421 [[nodiscard]] bool HasVisibleText(); 422 423 // Flags for aSetLengthFlags 424 enum { ALLOW_FRAME_CREATION_AND_DESTRUCTION = 0x01 }; 425 426 // Update offsets to account for new length. This may clear mTextRun. 427 void SetLength(int32_t aLength, nsLineLayout* aLineLayout, 428 uint32_t aSetLengthFlags = 0); 429 430 std::pair<int32_t, int32_t> GetOffsets() const final; 431 432 void AdjustOffsetsForBidi(int32_t start, int32_t end) final; 433 434 nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) final; 435 nsresult GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength, 436 nsTArray<nsRect>& aRects) final; 437 438 nsresult GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, 439 int32_t* outFrameContentOffset, 440 nsIFrame** outChildFrame) final; 441 442 bool IsEmpty() final; 443 bool IsSelfEmpty() final { return IsEmpty(); } 444 Maybe<nscoord> GetNaturalBaselineBOffset( 445 mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, 446 BaselineExportContext) const override; 447 nscoord GetCaretBaseline() const override; 448 449 bool HasSignificantTerminalNewline() const final; 450 451 /** 452 * Returns true if this text frame is logically adjacent to the end of the 453 * line. 454 */ 455 bool IsAtEndOfLine() const; 456 457 /** 458 * Call this only after reflow the frame. Returns true if non-collapsed 459 * characters are present. 460 */ 461 bool HasNoncollapsedCharacters() const { 462 return HasAnyStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS); 463 } 464 465 #ifdef ACCESSIBILITY 466 mozilla::a11y::AccType AccessibleType() final; 467 #endif 468 469 float GetFontSizeInflation() const; 470 bool IsCurrentFontInflation(float aInflation) const; 471 bool HasFontSizeInflation() const { 472 return HasAnyStateBits(TEXT_HAS_FONT_INFLATION); 473 } 474 void SetFontSizeInflation(float aInflation); 475 476 void MarkIntrinsicISizesDirty() final; 477 478 nscoord IntrinsicISize(const mozilla::IntrinsicSizeInput& aInput, 479 mozilla::IntrinsicISizeType aType) final; 480 481 void AddInlineMinISize(const mozilla::IntrinsicSizeInput& aInput, 482 InlineMinISizeData* aData) override; 483 void AddInlinePrefISize(const mozilla::IntrinsicSizeInput& aInput, 484 InlinePrefISizeData* aData) override; 485 SizeComputationResult ComputeSize( 486 const SizeComputationInput& aSizingInput, mozilla::WritingMode aWM, 487 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, 488 const mozilla::LogicalSize& aMargin, 489 const mozilla::LogicalSize& aBorderPadding, 490 const mozilla::StyleSizeOverrides& aSizeOverrides, 491 mozilla::ComputeSizeFlags aFlags) final; 492 nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const final; 493 nsresult GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX, 494 nscoord* aXMost) final; 495 void Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, 496 const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final; 497 bool CanContinueTextRun() const final; 498 // Method that is called for a text frame that is logically 499 // adjacent to the end of the line (i.e. followed only by empty text frames, 500 // placeholders or inlines containing such). 501 struct TrimOutput { 502 // true if we trimmed some space or changed metrics in some other way. 503 // In this case, we should call RecomputeOverflow on this frame. 504 bool mChanged; 505 // an amount to *subtract* from the frame's width (zero if !mChanged) 506 nscoord mDeltaWidth; 507 }; 508 TrimOutput TrimTrailingWhiteSpace(DrawTarget* aDrawTarget); 509 RenderedText GetRenderedText( 510 uint32_t aStartOffset = 0, uint32_t aEndOffset = UINT32_MAX, 511 TextOffsetType aOffsetType = TextOffsetType::OffsetsInContentText, 512 TrailingWhitespace aTrimTrailingWhitespace = 513 TrailingWhitespace::Trim) final; 514 515 mozilla::OverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame, 516 bool aIncludeShadows = true); 517 518 enum TextRunType : uint8_t { 519 // Anything in reflow (but not intrinsic width calculation) or 520 // painting should use the inflated text run (i.e., with font size 521 // inflation applied). 522 eInflated, 523 // Intrinsic width calculation should use the non-inflated text run. 524 // When there is font size inflation, it will be different. 525 eNotInflated 526 }; 527 528 void AddInlineMinISizeForFlow(gfxContext* aRenderingContext, 529 InlineMinISizeData* aData, 530 TextRunType aTextRunType); 531 void AddInlinePrefISizeForFlow(gfxContext* aRenderingContext, 532 InlinePrefISizeData* aData, 533 TextRunType aTextRunType); 534 535 /** 536 * Calculate the horizontal bounds of the grapheme clusters that fit entirely 537 * inside the given left[top]/right[bottom] edges (which are positive lengths 538 * from the respective frame edge). If an input value is zero it is ignored 539 * and the result for that edge is zero. All out parameter values are 540 * undefined when the method returns false. 541 * @return true if at least one whole grapheme cluster fit between the edges 542 */ 543 bool MeasureCharClippedText(nscoord aVisIStartEdge, nscoord aVisIEndEdge, 544 nscoord* aSnappedStartEdge, 545 nscoord* aSnappedEndEdge); 546 /** 547 * Same as above; this method also the returns the corresponding text run 548 * offset and number of characters that fit. All out parameter values are 549 * undefined when the method returns false. 550 * @return true if at least one whole grapheme cluster fit between the edges 551 */ 552 bool MeasureCharClippedText(PropertyProvider& aProvider, 553 nscoord aVisIStartEdge, nscoord aVisIEndEdge, 554 uint32_t* aStartOffset, uint32_t* aMaxLength, 555 nscoord* aSnappedStartEdge, 556 nscoord* aSnappedEndEdge); 557 558 /** 559 * Return true if this box has some text to display. 560 * It returns false if at least one of these conditions are met: 561 * a. the frame hasn't been reflowed yet 562 * b. GetContentLength() == 0 563 * c. it contains only non-significant white-space 564 */ 565 bool HasNonSuppressedText() const; 566 567 /** 568 * Object with various callbacks for PaintText() to invoke for different parts 569 * of the frame's text rendering, when we're generating paths rather than 570 * painting. 571 * 572 * Callbacks are invoked in the following order: 573 * 574 * NotifySelectionBackgroundNeedsFill? 575 * PaintDecorationLine* 576 * NotifyBeforeText 577 * NotifyGlyphPathEmitted* 578 * NotifyAfterText 579 * PaintDecorationLine* 580 * PaintSelectionDecorationLine* 581 * 582 * The color of each part of the frame's text rendering is passed as an 583 * argument to the NotifyBefore* callback for that part. The nscolor can take 584 * on one of the three selection special colors defined in LookAndFeel.h -- 585 * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and 586 * NS_40PERCENT_FOREGROUND_COLOR. 587 */ 588 struct DrawPathCallbacks : gfxTextRunDrawCallbacks { 589 /** 590 * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted. 591 */ 592 explicit DrawPathCallbacks(bool aShouldPaintSVGGlyphs = false) 593 : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs) {} 594 595 /** 596 * Called to have the selection highlight drawn before the text is drawn 597 * over the top. 598 */ 599 virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, 600 nscolor aColor, 601 DrawTarget& aDrawTarget) {} 602 603 /** 604 * Called before (for under/over-line) or after (for line-through) the text 605 * is drawn to have a text decoration line drawn. 606 */ 607 virtual void PaintDecorationLine(Rect aPath, bool aPaintingShadows, 608 nscolor aColor) {} 609 610 /** 611 * Called after selected text is drawn to have a decoration line drawn over 612 * the text. (All types of text decoration are drawn after the text when 613 * text is selected.) 614 */ 615 virtual void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows, 616 nscolor aColor) {} 617 618 /** 619 * Called just before any paths have been emitted to the gfxContext 620 * for the glyphs of the frame's text. 621 */ 622 virtual void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) {} 623 624 /** 625 * Called just after all the paths have been emitted to the gfxContext 626 * for the glyphs of the frame's text. 627 */ 628 virtual void NotifyAfterText() {} 629 630 /** 631 * Called just before a path corresponding to a selection decoration line 632 * has been emitted to the gfxContext. 633 */ 634 virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor) {} 635 636 /** 637 * Called just after a path corresponding to a selection decoration line 638 * has been emitted to the gfxContext. 639 */ 640 virtual void NotifySelectionDecorationLinePathEmitted() {} 641 }; 642 643 struct MOZ_STACK_CLASS PaintTextParams { 644 gfxContext* context; 645 Point framePt; 646 LayoutDeviceRect dirtyRect; 647 mozilla::SVGContextPaint* contextPaint = nullptr; 648 DrawPathCallbacks* callbacks = nullptr; 649 enum { 650 PaintText, // Normal text painting. 651 GenerateTextMask // To generate a mask from a text frame. Should 652 // only paint text itself with opaque color. 653 // Text shadow, text selection color and text 654 // decoration are all discarded in this state. 655 }; 656 uint8_t state = PaintText; 657 explicit PaintTextParams(gfxContext* aContext) : context(aContext) {} 658 659 bool IsPaintText() const { return state == PaintText; } 660 bool IsGenerateTextMask() const { return state == GenerateTextMask; } 661 }; 662 663 struct PaintTextSelectionParams; 664 struct DrawTextRunParams; 665 struct DrawTextParams; 666 struct ClipEdges; 667 struct PaintShadowParams; 668 struct PaintDecorationLineParams; 669 670 struct PriorityOrderedSelectionsForRange { 671 /// List of Selection Details active for the given range. 672 /// Ordered by priority, i.e. the last element has the highest priority. 673 nsTArray<const SelectionDetails*> mSelectionRanges; 674 Range mRange; 675 }; 676 677 // Primary frame paint method called from nsDisplayText. Can also be used 678 // to generate paths rather than paint the frame's text by passing a callback 679 // object. The private DrawText() is what applies the text to a graphics 680 // context. 681 void PaintText(const PaintTextParams& aParams, const nscoord aVisIStartEdge, 682 const nscoord aVisIEndEdge, const nsPoint& aToReferenceFrame, 683 const bool aIsSelected, float aOpacity = 1.0f); 684 // helper: paint text frame when we're impacted by at least one selection. 685 // Return false if the text was not painted and we should continue with 686 // the fast path. 687 bool PaintTextWithSelection(const PaintTextSelectionParams& aParams, 688 const ClipEdges& aClipEdges); 689 // helper: paint text with foreground and background colors determined 690 // by selection(s). Also computes a mask of all selection types applying to 691 // our text, returned in aAllSelectionTypeMask. 692 // Return false if the text was not painted and we should continue with 693 // the fast path. 694 bool PaintTextWithSelectionColors( 695 const PaintTextSelectionParams& aParams, 696 const mozilla::UniquePtr<SelectionDetails>& aDetails, 697 SelectionTypeMask* aAllSelectionTypeMask, const ClipEdges& aClipEdges); 698 // helper: paint text decorations for text selected by aSelectionType 699 void PaintTextSelectionDecorations( 700 const PaintTextSelectionParams& aParams, 701 const mozilla::UniquePtr<SelectionDetails>& aDetails, 702 SelectionType aSelectionType); 703 704 SelectionTypeMask ResolveSelections( 705 const PaintTextSelectionParams& aParams, const SelectionDetails* aDetails, 706 nsTArray<PriorityOrderedSelectionsForRange>& aResult, 707 SelectionType aSelectionType, bool* aAnyBackgrounds = nullptr) const; 708 709 void DrawEmphasisMarks(gfxContext* aContext, mozilla::WritingMode aWM, 710 const mozilla::gfx::Point& aTextBaselinePt, 711 const mozilla::gfx::Point& aFramePt, Range aRange, 712 const nscolor* aDecorationOverrideColor, 713 PropertyProvider* aProvider); 714 715 nscolor GetCaretColorAt(int32_t aOffset) final; 716 717 // @param aSelectionFlags may be multiple of nsISelectionDisplay::DISPLAY_*. 718 // @return nsISelectionController.idl's `getDisplaySelection`. 719 int16_t GetSelectionStatus(int16_t* aSelectionFlags); 720 721 int32_t GetContentOffset() const { return mContentOffset; } 722 int32_t GetContentLength() const { 723 NS_ASSERTION(GetContentEnd() - mContentOffset >= 0, "negative length"); 724 return GetContentEnd() - mContentOffset; 725 } 726 int32_t GetContentEnd() const; 727 // This returns the length the frame thinks it *should* have after it was 728 // last reflowed (0 if it hasn't been reflowed yet). This should be used only 729 // when setting up the text offsets for a new continuation frame. 730 int32_t GetContentLengthHint() const { return mContentLengthHint; } 731 732 // Compute the length of the content mapped by this frame 733 // and all its in-flow siblings. Basically this means starting at 734 // mContentOffset and going to the end of the text node or the next bidi 735 // continuation boundary. 736 int32_t GetInFlowContentLength(); 737 738 /** 739 * Acquires the text run for this content, if necessary. 740 * @param aWhichTextRun indicates whether to get an inflated or non-inflated 741 * text run 742 * @param aRefDrawTarget the DrawTarget to use as a reference for creating the 743 * textrun, if available (if not, we'll create one which will just be slower) 744 * @param aLineContainer the block ancestor for this frame, or nullptr if 745 * unknown 746 * @param aFlowEndInTextRun if non-null, this returns the textrun offset of 747 * end of the text associated with this frame and its in-flow siblings 748 * @return a gfxSkipCharsIterator set up to map DOM offsets for this frame 749 * to offsets into the textrun; its initial offset is set to this frame's 750 * content offset 751 */ 752 gfxSkipCharsIterator EnsureTextRun(TextRunType aWhichTextRun, 753 DrawTarget* aRefDrawTarget = nullptr, 754 nsIFrame* aLineContainer = nullptr, 755 const LineListIterator* aLine = nullptr, 756 uint32_t* aFlowEndInTextRun = nullptr); 757 758 gfxTextRun* GetTextRun(TextRunType aWhichTextRun) const { 759 if (aWhichTextRun == eInflated || !HasFontSizeInflation()) { 760 return mTextRun; 761 } 762 return GetUninflatedTextRun(); 763 } 764 gfxTextRun* GetUninflatedTextRun() const; 765 void SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun, 766 float aInflation); 767 bool IsInTextRunUserData() const { 768 return HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA | 769 TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA); 770 } 771 /** 772 * Notify the frame that it should drop its pointer to a text run. 773 * Returns whether the text run was removed (i.e., whether it was 774 * associated with this frame, either as its inflated or non-inflated 775 * text run. 776 */ 777 bool RemoveTextRun(gfxTextRun* aTextRun); 778 /** 779 * Clears out |mTextRun| (or the uninflated text run, when aInflated 780 * is nsTextFrame::eNotInflated and there is inflation) from all frames that 781 * hold a reference to it, starting at |aStartContinuation|, or if it's 782 * nullptr, starting at |this|. Deletes the text run if all references 783 * were cleared and it's not cached. 784 */ 785 void ClearTextRun(nsTextFrame* aStartContinuation, TextRunType aWhichTextRun); 786 787 void ClearTextRuns() { 788 ClearTextRun(nullptr, nsTextFrame::eInflated); 789 if (HasFontSizeInflation()) { 790 ClearTextRun(nullptr, nsTextFrame::eNotInflated); 791 } 792 } 793 794 /** 795 * Wipe out references to textrun(s) without deleting the textruns. 796 */ 797 void DisconnectTextRuns(); 798 799 // Get the DOM content range mapped by this frame after excluding 800 // whitespace subject to start-of-line and end-of-line trimming. 801 // The textrun must have been created before calling this. 802 struct TrimmedOffsets { 803 int32_t mStart; 804 int32_t mLength; 805 int32_t GetEnd() const { return mStart + mLength; } 806 }; 807 enum class TrimmedOffsetFlags : uint8_t { 808 Default = 0, 809 NotPostReflow = 1 << 0, 810 NoTrimAfter = 1 << 1, 811 NoTrimBefore = 1 << 2 812 }; 813 TrimmedOffsets GetTrimmedOffsets( 814 const mozilla::dom::CharacterDataBuffer& aBuffer, 815 TrimmedOffsetFlags aFlags = TrimmedOffsetFlags::Default) const; 816 817 // Similar to Reflow(), but for use from nsLineLayout 818 void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, 819 DrawTarget* aDrawTarget, ReflowOutput& aMetrics, 820 nsReflowStatus& aStatus); 821 822 nscoord ComputeLineHeight() const; 823 824 bool IsFloatingFirstLetterChild() const; 825 826 bool IsInitialLetterChild() const; 827 828 bool ComputeCustomOverflow(mozilla::OverflowAreas& aOverflowAreas) final; 829 bool ComputeCustomOverflowInternal(mozilla::OverflowAreas& aOverflowAreas, 830 bool aIncludeShadows); 831 832 void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign); 833 mozilla::JustificationAssignment GetJustificationAssignment() const; 834 835 uint32_t CountGraphemeClusters() const; 836 837 bool HasAnyNoncollapsedCharacters() final; 838 839 /** 840 * Call this after you have manually changed the text node contents without 841 * notifying that change. This behaves as if all the text contents changed. 842 * (You should only use this for native anonymous content.) 843 */ 844 void NotifyNativeAnonymousTextnodeChange(uint32_t aOldLength); 845 846 nsFontMetrics* InflatedFontMetrics() const; 847 848 nsRect WebRenderBounds(); 849 850 // Find the continuation (which may be this frame itself) containing the 851 // given offset. Note that this may return null, if the offset is beyond the 852 // text covered by the continuation chain. 853 // (To be used only on the first textframe in the chain.) 854 nsTextFrame* FindContinuationForOffset(int32_t aOffset); 855 856 void SetHangableISize(nscoord aISize); 857 nscoord GetHangableISize() const; 858 void ClearHangableISize(); 859 860 void SetTrimmableWS(gfxTextRun::TrimmableWS aTrimmableWS); 861 gfxTextRun::TrimmableWS GetTrimmableWS() const; 862 void ClearTrimmableWS(); 863 864 // Return ShapedTextFlags::TEXT_ENABLE_SPACING if spacing is needed due to 865 // letter-spacing, word-spacing, etc. 866 mozilla::gfx::ShapedTextFlags GetSpacingFlags() const; 867 868 protected: 869 virtual ~nsTextFrame(); 870 871 friend class mozilla::nsDisplayTextGeometry; 872 friend class mozilla::nsDisplayText; 873 874 mutable RefPtr<nsFontMetrics> mFontMetrics; 875 RefPtr<gfxTextRun> mTextRun; 876 nsTextFrame* mNextContinuation = nullptr; 877 // The key invariant here is that mContentOffset never decreases along 878 // a next-continuation chain. And of course mContentOffset is always <= the 879 // the text node's content length, and the mContentOffset for the first frame 880 // is always 0. Furthermore the text mapped by a frame is determined by 881 // GetContentOffset() and GetContentLength()/GetContentEnd(), which get 882 // the length from the difference between this frame's offset and the next 883 // frame's offset, or the text length if there is no next frame. This means 884 // the frames always map the text node without overlapping or leaving any 885 // gaps. 886 int32_t mContentOffset = 0; 887 // This does *not* indicate the length of text currently mapped by the frame; 888 // instead it's a hint saying that this frame *wants* to map this much text 889 // so if we create a new continuation, this is where that continuation should 890 // start. 891 int32_t mContentLengthHint = 0; 892 nscoord mAscent = 0; 893 894 // Cached selection state. 895 enum class SelectionState : uint8_t { 896 Unknown, 897 Selected, 898 NotSelected, 899 }; 900 mutable SelectionState mIsSelected = SelectionState::Unknown; 901 902 // Flags used to track whether certain properties are present. 903 // (Public to keep MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS happy.) 904 public: 905 enum class PropertyFlags : uint8_t { 906 // Whether a cached continuations array is present. 907 Continuations = 1 << 0, 908 // Whether a HangableWhitespace property is present. 909 HangableWS = 1 << 1, 910 // Whether a TrimmableWhitespace property is present. 911 TrimmableWS = 2 << 1, 912 }; 913 914 protected: 915 PropertyFlags mPropertyFlags = PropertyFlags(0); 916 917 /** 918 * Return true if the frame is part of a Selection. 919 * Helper method to implement the public IsSelected() API. 920 */ 921 bool IsFrameSelected() const final; 922 923 void InvalidateSelectionState() { mIsSelected = SelectionState::Unknown; } 924 925 mozilla::UniquePtr<SelectionDetails> GetSelectionDetails(); 926 927 void UnionAdditionalOverflow(nsPresContext* aPresContext, nsIFrame* aBlock, 928 PropertyProvider& aProvider, 929 nsRect* aInkOverflowRect, 930 bool aIncludeTextDecorations, 931 bool aIncludeShadows); 932 933 // Update information of emphasis marks, and return the visial 934 // overflow rect of the emphasis marks. 935 nsRect UpdateTextEmphasis(mozilla::WritingMode aWM, 936 PropertyProvider& aProvider); 937 938 void PaintOneShadow(const PaintShadowParams& aParams, 939 const mozilla::StyleSimpleShadow& aShadowDetails, 940 gfxRect& aBoundingBox, uint32_t aBlurFlags); 941 942 void PaintShadows(mozilla::Span<const mozilla::StyleSimpleShadow>, 943 const PaintShadowParams& aParams); 944 945 struct LineDecoration { 946 nsIFrame* const mFrame; 947 948 // This is represents the offset from our baseline to mFrame's baseline; 949 // positive offsets are *above* the baseline and negative offsets below 950 const nscoord mBaselineOffset; 951 952 // This represents the offset from the initial position of the underline 953 const mozilla::LengthPercentageOrAuto mTextUnderlineOffset; 954 955 // for CSS property text-decoration-thickness, the width refers to the 956 // thickness of the decoration line 957 const mozilla::StyleTextDecorationLength mTextDecorationThickness; 958 const nscolor mColor; 959 const mozilla::StyleTextDecorationStyle mStyle; 960 961 // The text-underline-position property; affects the underline offset only 962 // if mTextUnderlineOffset is auto. 963 const mozilla::StyleTextUnderlinePosition mTextUnderlinePosition; 964 965 const bool mAllowInkSkipping; 966 967 LineDecoration(nsIFrame* const aFrame, const nscoord aOff, 968 const mozilla::StyleTextUnderlinePosition aUnderlinePosition, 969 const mozilla::LengthPercentageOrAuto& aUnderlineOffset, 970 const mozilla::StyleTextDecorationLength& aDecThickness, 971 const nscolor aColor, 972 const mozilla::StyleTextDecorationStyle aStyle, 973 const bool aAllowInkSkipping) 974 : mFrame(aFrame), 975 mBaselineOffset(aOff), 976 mTextUnderlineOffset(aUnderlineOffset), 977 mTextDecorationThickness(aDecThickness), 978 mColor(aColor), 979 mStyle(aStyle), 980 mTextUnderlinePosition(aUnderlinePosition), 981 mAllowInkSkipping(aAllowInkSkipping) {} 982 983 LineDecoration(const LineDecoration& aOther) = default; 984 985 bool operator==(const LineDecoration& aOther) const = default; 986 bool operator!=(const LineDecoration& aOther) const = default; 987 }; 988 struct TextDecorations { 989 AutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes; 990 991 TextDecorations() = default; 992 993 bool HasDecorationLines() const { 994 return HasUnderline() || HasOverline() || HasStrikeout(); 995 } 996 bool HasUnderline() const { return !mUnderlines.IsEmpty(); } 997 bool HasOverline() const { return !mOverlines.IsEmpty(); } 998 bool HasStrikeout() const { return !mStrikes.IsEmpty(); } 999 bool operator==(const TextDecorations& aOther) const = default; 1000 bool operator!=(const TextDecorations& aOther) const = default; 1001 }; 1002 enum TextDecorationColorResolution { eResolvedColors, eUnresolvedColors }; 1003 void GetTextDecorations(nsPresContext* aPresContext, 1004 TextDecorationColorResolution aColorResolution, 1005 TextDecorations& aDecorations); 1006 1007 void DrawTextRun(Range aRange, const mozilla::gfx::Point& aTextBaselinePt, 1008 const DrawTextRunParams& aParams); 1009 1010 void DrawTextRunAndDecorations(Range aRange, 1011 const mozilla::gfx::Point& aTextBaselinePt, 1012 const DrawTextParams& aParams, 1013 const TextDecorations& aDecorations); 1014 1015 void DrawText(Range aRange, const mozilla::gfx::Point& aTextBaselinePt, 1016 const DrawTextParams& aParams); 1017 1018 // Set non empty rect to aRect, it should be overflow rect or frame rect. 1019 // If the result rect is larger than the given rect, this returns true. 1020 bool CombineSelectionUnderlineRect(nsPresContext* aPresContext, 1021 nsRect& aRect); 1022 1023 // This sets *aShadows to the appropriate shadows, if any, for the given 1024 // type of selection. 1025 // If text-shadow was not specified, *aShadows is left untouched. 1026 // Note that the returned shadow(s) will only be valid as long as the 1027 // textPaintStyle remains in scope. 1028 void GetSelectionTextShadow( 1029 SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle, 1030 mozilla::Span<const mozilla::StyleSimpleShadow>* aShadows); 1031 1032 /** 1033 * Utility methods to paint selection. 1034 */ 1035 void DrawSelectionDecorations( 1036 gfxContext* aContext, const LayoutDeviceRect& aDirtyRect, 1037 mozilla::SelectionType aSelectionType, nsAtom* aHighlightName, 1038 nsTextPaintStyle& aTextPaintStyle, const TextRangeStyle& aRangeStyle, 1039 const Point& aPt, gfxFloat aICoordInFrame, gfxFloat aWidth, 1040 gfxFloat aAscent, const gfxFont::Metrics& aFontMetrics, 1041 DrawPathCallbacks* aCallbacks, bool aVertical, 1042 mozilla::StyleTextDecorationLine aDecoration, const Range& aGlyphRange, 1043 PropertyProvider* aProvider); 1044 1045 void PaintDecorationLine(const PaintDecorationLineParams& aParams); 1046 /** 1047 * ComputeDescentLimitForSelectionUnderline() computes the most far position 1048 * where we can put selection underline. 1049 * 1050 * @return The maximum underline offset from the baseline (positive value 1051 * means that the underline can put below the baseline). 1052 */ 1053 gfxFloat ComputeDescentLimitForSelectionUnderline( 1054 nsPresContext* aPresContext, const gfxFont::Metrics& aFontMetrics); 1055 /** 1056 * This function encapsulates all knowledge of how selections affect 1057 * foreground and background colors. 1058 * @param aForeground the foreground color to use 1059 * @param aBackground the background color to use, or RGBA(0,0,0,0) if no 1060 * background should be painted 1061 * @return true if the selection affects colors, false otherwise 1062 */ 1063 static bool GetSelectionTextColors(SelectionType aSelectionType, 1064 nsAtom* aHighlightName, 1065 nsTextPaintStyle& aTextPaintStyle, 1066 const TextRangeStyle& aRangeStyle, 1067 nscolor* aForeground, 1068 nscolor* aBackground); 1069 /** 1070 * ComputeSelectionUnderlineHeight() computes selection underline height of 1071 * the specified selection type from the font metrics. 1072 */ 1073 static gfxFloat ComputeSelectionUnderlineHeight( 1074 nsPresContext* aPresContext, const gfxFont::Metrics& aFontMetrics, 1075 SelectionType aSelectionType); 1076 1077 /** 1078 * @brief Helper struct which contains selection data such as its details, 1079 * range and priority. 1080 */ 1081 struct SelectionRange { 1082 const SelectionDetails* mDetails{nullptr}; 1083 gfxTextRun::Range mRange; 1084 /// used to determine the order of overlapping selections of the same type. 1085 uint32_t mPriority{0}; 1086 }; 1087 /** 1088 * @brief Helper: Extracts a list of `SelectionRange` structs from given 1089 * `SelectionDetails` and computes a priority for overlapping selection 1090 * ranges. 1091 */ 1092 static SelectionTypeMask CreateSelectionRangeList( 1093 const SelectionDetails* aDetails, SelectionType aSelectionType, 1094 const PaintTextSelectionParams& aParams, 1095 nsTArray<SelectionRange>& aSelectionRanges, bool* aAnyBackgrounds); 1096 1097 /** 1098 * @brief Creates an array of `CombinedSelectionRange`s from given list 1099 * of `SelectionRange`s. 1100 * Each instance of `CombinedSelectionRange` represents a piece of text with 1101 * constant Selections. 1102 * 1103 * Example: 1104 * 1105 * Consider this text fragment, [] and () marking selection ranges: 1106 * ab[cd(e]f)g 1107 * This results in the following array of combined ranges: 1108 * - [0]: range: (2, 4), selections: "[]" 1109 * - [1]: range: (4, 5), selections: "[]", "()" 1110 * - [2]: range: (5, 6), selections: "()" 1111 * Depending on the priorities of the ranges, [1] may have a different order 1112 * of its ranges. The given example indicates that "()" has a higher priority 1113 * than "[]". 1114 * 1115 * @param aSelectionRanges Array of `SelectionRange` objects. Must be 1116 * sorted by the start offset. 1117 * @param aCombinedSelectionRanges Out parameter. Returns the constructed 1118 * array of combined selection ranges. 1119 */ 1120 static void CombineSelectionRanges( 1121 const nsTArray<SelectionRange>& aSelectionRanges, 1122 nsTArray<PriorityOrderedSelectionsForRange>& aCombinedSelectionRanges); 1123 1124 ContentOffsets GetCharacterOffsetAtFramePointInternal( 1125 const nsPoint& aPoint, bool aForInsertionPoint); 1126 1127 float GetTextCombineScale() const; 1128 std::pair<nscoord, float> GetTextCombineOffsetAndScale() const; 1129 1130 void ClearFrameOffsetCache(); 1131 1132 void ClearMetrics(ReflowOutput& aMetrics); 1133 1134 // Return pointer to an array of all frames in the continuation chain, or 1135 // null if we're too short of memory. 1136 nsTArray<nsTextFrame*>* GetContinuations(); 1137 1138 // Clear any cached continuations array; this should be called whenever the 1139 // chain is modified. 1140 inline void ClearCachedContinuations(); 1141 1142 /** 1143 * UpdateIteratorFromOffset() updates the iterator from a given offset. 1144 * Also, aInOffset may be updated to cluster start if aInOffset isn't 1145 * the offset of cluster start. 1146 */ 1147 void UpdateIteratorFromOffset(const PropertyProvider& aProperties, 1148 int32_t& aInOffset, 1149 gfxSkipCharsIterator& aIter); 1150 1151 nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter, 1152 PropertyProvider& aProperties); 1153 1154 /** 1155 * Return the content offset of the first preserved newline in this frame, 1156 * or return -1 if no preserved NL. 1157 */ 1158 struct NewlineProperty; 1159 int32_t GetContentNewLineOffset(int32_t aOffset, 1160 NewlineProperty*& aCachedNewlineOffset); 1161 1162 void MaybeSplitFramesForFirstLetter(); 1163 void SetFirstLetterLength(int32_t aLength); 1164 1165 struct AppendRenderedTextState { 1166 // Inputs, constant across all calls in the loop. 1167 const uint32_t mStartOffset; 1168 const uint32_t mEndOffset; 1169 const TextOffsetType mOffsetType; 1170 const TrailingWhitespace mTrimTrailingWhitespace; 1171 const mozilla::dom::CharacterDataBuffer& mCharacterDataBuffer; 1172 // Mutable state, updated as we loop over the continuations. 1173 nsBlockFrame* mLineContainer = nullptr; 1174 uint32_t mOffsetInRenderedString = 0; 1175 bool mHaveOffsets = false; 1176 }; 1177 // Helper for GetRenderedText, to process one frame in the continuation 1178 // chain. Returns true if the caller should continue to loop over the 1179 // following frames, or false to stop. 1180 bool AppendRenderedText(AppendRenderedTextState& aState, 1181 RenderedText& aResult); 1182 }; 1183 1184 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::TrimmedOffsetFlags) 1185 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::PropertyFlags) 1186 1187 inline void nsTextFrame::ClearCachedContinuations() { 1188 MOZ_ASSERT(NS_IsMainThread()); 1189 if (mPropertyFlags & PropertyFlags::Continuations) { 1190 RemoveProperty(ContinuationsProperty()); 1191 mPropertyFlags &= ~PropertyFlags::Continuations; 1192 } 1193 } 1194 1195 #endif