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/. */
7 #ifndef nsTextFrame_h__
8 #define nsTextFrame_h__
10 #include "mozilla/Attributes.h"
11 #include "mozilla/UniquePtr.h"
12 #include "mozilla/dom/Text.h"
13 #include "mozilla/gfx/2D.h"
16 #include "nsISelectionController.h"
17 #include "nsSplittableFrame.h"
18 #include "gfxSkipChars.h"
19 #include "gfxTextRun.h"
20 #include "JustificationUtils.h"
22 // Undo the windows.h damage
23 #if defined(XP_WIN) && defined(DrawText)
27 class nsTextPaintStyle
;
28 class nsLineList_iterator
;
29 struct SelectionDetails
;
33 class SVGContextPaint
;
35 class nsDisplayTextGeometry
;
37 } // namespace mozilla
39 class nsTextFrame
: public nsIFrame
{
40 typedef mozilla::LayoutDeviceRect LayoutDeviceRect
;
41 typedef mozilla::SelectionTypeMask SelectionTypeMask
;
42 typedef mozilla::SelectionType SelectionType
;
43 typedef mozilla::TextRangeStyle TextRangeStyle
;
44 typedef mozilla::gfx::DrawTarget DrawTarget
;
45 typedef mozilla::gfx::Point Point
;
46 typedef mozilla::gfx::Rect Rect
;
47 typedef mozilla::gfx::Size Size
;
48 typedef gfxTextRun::Range Range
;
51 enum TextRunType
: uint8_t;
55 * An implementation of gfxTextRun::PropertyProvider that computes spacing and
56 * hyphenation based on CSS properties for a text frame.
58 class MOZ_STACK_CLASS PropertyProvider final
59 : public gfxTextRun::PropertyProvider
{
60 typedef gfxTextRun::Range Range
;
61 typedef gfxTextRun::HyphenType HyphenType
;
62 typedef mozilla::gfx::DrawTarget DrawTarget
;
66 * Use this constructor for reflow, when we don't know what text is
67 * really mapped by the frame and we have a lot of other data around.
69 * @param aLength can be INT32_MAX to indicate we cover all the text
70 * associated with aFrame up to where its flow chain ends in the given
71 * textrun. If INT32_MAX is passed, justification and hyphen-related methods
72 * cannot be called, nor can GetOriginalLength().
74 PropertyProvider(gfxTextRun
* aTextRun
, const nsStyleText
* aTextStyle
,
75 const nsTextFragment
* aFrag
, nsTextFrame
* aFrame
,
76 const gfxSkipCharsIterator
& aStart
, int32_t aLength
,
77 nsIFrame
* aLineContainer
,
78 nscoord aOffsetFromBlockOriginForTabs
,
79 nsTextFrame::TextRunType aWhichTextRun
);
82 * Use this constructor after the frame has been reflowed and we don't
83 * have other data around. Gets everything from the frame. EnsureTextRun
84 * *must* be called before this!!!
86 PropertyProvider(nsTextFrame
* aFrame
, const gfxSkipCharsIterator
& aStart
,
87 nsTextFrame::TextRunType aWhichTextRun
,
88 nsFontMetrics
* aFontMetrics
);
91 * As above, but assuming we want the inflated text run and associated
94 PropertyProvider(nsTextFrame
* aFrame
, const gfxSkipCharsIterator
& aStart
)
95 : PropertyProvider(aFrame
, aStart
, nsTextFrame::eInflated
,
96 aFrame
->InflatedFontMetrics()) {}
98 // Call this after construction if you're not going to reflow the text
99 void InitializeForDisplay(bool aTrimAfter
);
101 void InitializeForMeasure();
103 void GetSpacing(Range aRange
, Spacing
* aSpacing
) const final
;
104 gfxFloat
GetHyphenWidth() const final
;
105 void GetHyphenationBreaks(Range aRange
,
106 HyphenType
* aBreakBefore
) const final
;
107 mozilla::StyleHyphens
GetHyphensOption() const final
{
108 return mTextStyle
->mHyphens
;
110 mozilla::gfx::ShapedTextFlags
GetShapedTextFlags() const final
;
112 already_AddRefed
<DrawTarget
> GetDrawTarget() const final
;
114 uint32_t GetAppUnitsPerDevUnit() const final
{
115 return mTextRun
->GetAppUnitsPerDevUnit();
118 void GetSpacingInternal(Range aRange
, Spacing
* aSpacing
,
119 bool aIgnoreTabs
) const;
122 * Compute the justification information in given DOM range, return
123 * justification info and assignments if requested.
125 mozilla::JustificationInfo
ComputeJustification(
127 nsTArray
<mozilla::JustificationAssignment
>* aAssignments
= nullptr);
129 const nsTextFrame
* GetFrame() const { return mFrame
; }
130 // This may not be equal to the frame offset/length in because we may have
131 // adjusted for whitespace trimming according to the state bits set in the
132 // frame (for the static provider)
133 const gfxSkipCharsIterator
& GetStart() const { return mStart
; }
134 // May return INT32_MAX if that was given to the constructor
135 uint32_t GetOriginalLength() const {
136 NS_ASSERTION(mLength
!= INT32_MAX
, "Length not known");
139 const nsTextFragment
* GetFragment() const { return mFrag
; }
141 gfxFontGroup
* GetFontGroup() const {
143 mFontGroup
= GetFontMetrics()->GetThebesFontGroup();
148 nsFontMetrics
* GetFontMetrics() const {
150 InitFontGroupAndFontMetrics();
155 void CalcTabWidths(Range aTransformedRange
, gfxFloat aTabWidth
) const;
157 gfxFloat
MinTabAdvance() const;
159 const gfxSkipCharsIterator
& GetEndHint() const { return mTempIterator
; }
162 void SetupJustificationSpacing(bool aPostReflow
);
164 void InitFontGroupAndFontMetrics() const;
166 const RefPtr
<gfxTextRun
> mTextRun
;
167 mutable gfxFontGroup
* mFontGroup
;
168 mutable RefPtr
<nsFontMetrics
> mFontMetrics
;
169 const nsStyleText
* mTextStyle
;
170 const nsTextFragment
* mFrag
;
171 const nsIFrame
* mLineContainer
;
173 gfxSkipCharsIterator mStart
; // Offset in original and transformed string
174 const gfxSkipCharsIterator mTempIterator
;
176 // Either null, or pointing to the frame's TabWidthProperty.
177 mutable nsTextFrame::TabWidthStore
* mTabWidths
;
178 // How far we've done tab-width calculation; this is ONLY valid when
179 // mTabWidths is nullptr (otherwise rely on mTabWidths->mLimit instead).
180 // It's a DOM offset relative to the current frame's offset.
181 mutable uint32_t mTabWidthsAnalyzedLimit
;
183 int32_t mLength
; // DOM string length, may be INT32_MAX
184 const gfxFloat mWordSpacing
; // space for each whitespace char
185 const gfxFloat mLetterSpacing
; // space for each letter
186 mutable gfxFloat mMinTabAdvance
; // min advance for <tab> char
187 mutable gfxFloat mHyphenWidth
;
188 mutable gfxFloat mOffsetFromBlockOriginForTabs
;
190 // The values in mJustificationSpacings corresponds to unskipped
191 // characters start from mJustificationArrayStart.
192 uint32_t mJustificationArrayStart
;
193 nsTArray
<Spacing
> mJustificationSpacings
;
195 const bool mReflowing
;
196 const nsTextFrame::TextRunType mWhichTextRun
;
199 explicit nsTextFrame(ComputedStyle
* aStyle
, nsPresContext
* aPresContext
,
200 ClassID aID
= kClassID
)
201 : nsIFrame(aStyle
, aPresContext
, aID
),
202 mNextContinuation(nullptr),
204 mContentLengthHint(0),
206 mIsSelected(SelectionState::Unknown
) {}
208 NS_DECL_FRAMEARENA_HELPERS(nsTextFrame
)
210 friend class nsContinuingTextFrame
;
215 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContinuationsProperty
,
216 nsTArray
<nsTextFrame
*>)
219 void BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
220 const nsDisplayListSet
& aLists
) final
;
222 void Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
223 nsIFrame
* aPrevInFlow
) override
;
225 void Destroy(DestroyContext
&) override
;
227 mozilla::Maybe
<Cursor
> GetCursor(const nsPoint
&) final
;
229 nsresult
CharacterDataChanged(const CharacterDataChangeInfo
&) final
;
231 nsTextFrame
* FirstContinuation() const override
{
232 return const_cast<nsTextFrame
*>(this);
234 nsTextFrame
* GetPrevContinuation() const override
{ return nullptr; }
235 nsTextFrame
* GetNextContinuation() const final
{ return mNextContinuation
; }
236 void SetNextContinuation(nsIFrame
* aNextContinuation
) final
{
237 NS_ASSERTION(!aNextContinuation
|| Type() == aNextContinuation
->Type(),
238 "setting a next continuation with incorrect type!");
240 !nsSplittableFrame::IsInNextContinuationChain(aNextContinuation
, this),
241 "creating a loop in continuation chain!");
242 mNextContinuation
= static_cast<nsTextFrame
*>(aNextContinuation
);
243 if (aNextContinuation
)
244 aNextContinuation
->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION
);
245 // Setting a non-fluid continuation might affect our flow length (they're
246 // quite rare so we assume it always does) so we delete our cached value:
247 if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY
)) {
248 GetContent()->RemoveProperty(nsGkAtoms::flowlength
);
249 GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY
);
252 nsTextFrame
* GetNextInFlow() const final
{
253 return mNextContinuation
&& mNextContinuation
->HasAnyStateBits(
254 NS_FRAME_IS_FLUID_CONTINUATION
)
258 void SetNextInFlow(nsIFrame
* aNextInFlow
) final
{
259 NS_ASSERTION(!aNextInFlow
|| Type() == aNextInFlow
->Type(),
260 "setting a next in flow with incorrect type!");
262 !nsSplittableFrame::IsInNextContinuationChain(aNextInFlow
, this),
263 "creating a loop in continuation chain!");
264 mNextContinuation
= static_cast<nsTextFrame
*>(aNextInFlow
);
265 if (mNextContinuation
&&
266 !mNextContinuation
->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION
)) {
267 // Changing from non-fluid to fluid continuation might affect our flow
268 // length, so we delete our cached value:
269 if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY
)) {
270 GetContent()->RemoveProperty(nsGkAtoms::flowlength
);
271 GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY
);
275 aNextInFlow
->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION
);
278 nsTextFrame
* LastInFlow() const final
;
279 nsTextFrame
* LastContinuation() const final
;
281 bool IsFrameOfType(uint32_t aFlags
) const final
{
282 // Set the frame state bit for text frames to mark them as replaced.
283 // XXX kipp: temporary
284 return nsIFrame::IsFrameOfType(
285 aFlags
& ~(nsIFrame::eReplaced
| nsIFrame::eLineParticipant
));
288 bool ShouldSuppressLineBreak() const;
290 void InvalidateFrame(uint32_t aDisplayItemKey
= 0,
291 bool aRebuildDisplayItems
= true) final
;
292 void InvalidateFrameWithRect(const nsRect
& aRect
,
293 uint32_t aDisplayItemKey
= 0,
294 bool aRebuildDisplayItems
= true) final
;
296 #ifdef DEBUG_FRAME_DUMP
297 void List(FILE* out
= stderr
, const char* aPrefix
= "",
298 ListFlags aFlags
= ListFlags()) const final
;
299 nsresult
GetFrameName(nsAString
& aResult
) const final
;
300 void ToCString(nsCString
& aBuf
) const;
301 void ListTextRuns(FILE* out
, nsTHashSet
<const void*>& aSeen
) const final
;
304 // Returns this text frame's content's text fragment.
306 // Assertions in Init() ensure we only ever get a Text node as content.
307 const nsTextFragment
* TextFragment() const {
308 return &mContent
->AsText()->TextFragment();
312 * Check that the text in this frame is entirely whitespace. Importantly,
313 * this function considers non-breaking spaces (0xa0) to be whitespace,
314 * whereas nsTextFrame::IsEmpty does not. It also considers both one and
317 bool IsEntirelyWhitespace() const;
319 ContentOffsets
CalcContentOffsetsFromFramePoint(const nsPoint
& aPoint
) final
;
320 ContentOffsets
GetCharacterOffsetAtFramePoint(const nsPoint
& aPoint
);
323 * This is called only on the primary text frame. It indicates that
324 * the selection state of the given character range has changed.
325 * Frames corresponding to the character range are unconditionally invalidated
326 * (Selection::Repaint depends on this).
327 * @param aStart start of character range.
328 * @param aEnd end (exclusive) of character range.
329 * @param aSelected true iff the character range is now selected.
330 * @param aType the type of the changed selection.
332 void SelectionStateChanged(uint32_t aStart
, uint32_t aEnd
, bool aSelected
,
333 SelectionType aSelectionType
);
335 FrameSearchResult
PeekOffsetNoAmount(bool aForward
, int32_t* aOffset
) final
;
336 FrameSearchResult
PeekOffsetCharacter(
337 bool aForward
, int32_t* aOffset
,
338 PeekOffsetCharacterOptions aOptions
= PeekOffsetCharacterOptions()) final
;
339 FrameSearchResult
PeekOffsetWord(bool aForward
, bool aWordSelectEatSpace
,
340 bool aIsKeyboardSelect
, int32_t* aOffset
,
341 PeekWordState
* aState
,
342 bool aTrimSpaces
) final
;
344 // Helper method that editor code uses to test for visibility.
345 [[nodiscard
]] bool HasVisibleText();
347 // Flags for aSetLengthFlags
348 enum { ALLOW_FRAME_CREATION_AND_DESTRUCTION
= 0x01 };
350 // Update offsets to account for new length. This may clear mTextRun.
351 void SetLength(int32_t aLength
, nsLineLayout
* aLineLayout
,
352 uint32_t aSetLengthFlags
= 0);
354 std::pair
<int32_t, int32_t> GetOffsets() const final
;
356 void AdjustOffsetsForBidi(int32_t start
, int32_t end
) final
;
358 nsresult
GetPointFromOffset(int32_t inOffset
, nsPoint
* outPoint
) final
;
359 nsresult
GetCharacterRectsInRange(int32_t aInOffset
, int32_t aLength
,
360 nsTArray
<nsRect
>& aRects
) final
;
362 nsresult
GetChildFrameContainingOffset(int32_t inContentOffset
, bool inHint
,
363 int32_t* outFrameContentOffset
,
364 nsIFrame
** outChildFrame
) final
;
366 bool IsEmpty() final
;
367 bool IsSelfEmpty() final
{ return IsEmpty(); }
368 Maybe
<nscoord
> GetNaturalBaselineBOffset(
369 mozilla::WritingMode aWM
, BaselineSharingGroup aBaselineGroup
,
370 BaselineExportContext
) const override
;
372 bool HasSignificantTerminalNewline() const final
;
375 * Returns true if this text frame is logically adjacent to the end of the
378 bool IsAtEndOfLine() const;
381 * Call this only after reflow the frame. Returns true if non-collapsed
382 * characters are present.
384 bool HasNoncollapsedCharacters() const {
385 return HasAnyStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS
);
389 mozilla::a11y::AccType
AccessibleType() final
;
392 float GetFontSizeInflation() const;
393 bool IsCurrentFontInflation(float aInflation
) const;
394 bool HasFontSizeInflation() const {
395 return HasAnyStateBits(TEXT_HAS_FONT_INFLATION
);
397 void SetFontSizeInflation(float aInflation
);
399 void MarkIntrinsicISizesDirty() final
;
400 nscoord
GetMinISize(gfxContext
* aRenderingContext
) final
;
401 nscoord
GetPrefISize(gfxContext
* aRenderingContext
) final
;
402 void AddInlineMinISize(gfxContext
* aRenderingContext
,
403 InlineMinISizeData
* aData
) override
;
404 void AddInlinePrefISize(gfxContext
* aRenderingContext
,
405 InlinePrefISizeData
* aData
) override
;
406 SizeComputationResult
ComputeSize(
407 gfxContext
* aRenderingContext
, mozilla::WritingMode aWM
,
408 const mozilla::LogicalSize
& aCBSize
, nscoord aAvailableISize
,
409 const mozilla::LogicalSize
& aMargin
,
410 const mozilla::LogicalSize
& aBorderPadding
,
411 const mozilla::StyleSizeOverrides
& aSizeOverrides
,
412 mozilla::ComputeSizeFlags aFlags
) final
;
413 nsRect
ComputeTightBounds(DrawTarget
* aDrawTarget
) const final
;
414 nsresult
GetPrefWidthTightBounds(gfxContext
* aContext
, nscoord
* aX
,
415 nscoord
* aXMost
) final
;
416 void Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aMetrics
,
417 const ReflowInput
& aReflowInput
, nsReflowStatus
& aStatus
) final
;
418 bool CanContinueTextRun() const final
;
419 // Method that is called for a text frame that is logically
420 // adjacent to the end of the line (i.e. followed only by empty text frames,
421 // placeholders or inlines containing such).
423 // true if we trimmed some space or changed metrics in some other way.
424 // In this case, we should call RecomputeOverflow on this frame.
426 // an amount to *subtract* from the frame's width (zero if !mChanged)
429 TrimOutput
TrimTrailingWhiteSpace(DrawTarget
* aDrawTarget
);
430 RenderedText
GetRenderedText(
431 uint32_t aStartOffset
= 0, uint32_t aEndOffset
= UINT32_MAX
,
432 TextOffsetType aOffsetType
= TextOffsetType::OffsetsInContentText
,
433 TrailingWhitespace aTrimTrailingWhitespace
=
434 TrailingWhitespace::Trim
) final
;
436 mozilla::OverflowAreas
RecomputeOverflow(nsIFrame
* aBlockFrame
,
437 bool aIncludeShadows
= true);
439 enum TextRunType
: uint8_t {
440 // Anything in reflow (but not intrinsic width calculation) or
441 // painting should use the inflated text run (i.e., with font size
442 // inflation applied).
444 // Intrinsic width calculation should use the non-inflated text run.
445 // When there is font size inflation, it will be different.
449 void AddInlineMinISizeForFlow(gfxContext
* aRenderingContext
,
450 nsIFrame::InlineMinISizeData
* aData
,
451 TextRunType aTextRunType
);
452 void AddInlinePrefISizeForFlow(gfxContext
* aRenderingContext
,
453 InlinePrefISizeData
* aData
,
454 TextRunType aTextRunType
);
457 * Calculate the horizontal bounds of the grapheme clusters that fit entirely
458 * inside the given left[top]/right[bottom] edges (which are positive lengths
459 * from the respective frame edge). If an input value is zero it is ignored
460 * and the result for that edge is zero. All out parameter values are
461 * undefined when the method returns false.
462 * @return true if at least one whole grapheme cluster fit between the edges
464 bool MeasureCharClippedText(nscoord aVisIStartEdge
, nscoord aVisIEndEdge
,
465 nscoord
* aSnappedStartEdge
,
466 nscoord
* aSnappedEndEdge
);
468 * Same as above; this method also the returns the corresponding text run
469 * offset and number of characters that fit. All out parameter values are
470 * undefined when the method returns false.
471 * @return true if at least one whole grapheme cluster fit between the edges
473 bool MeasureCharClippedText(PropertyProvider
& aProvider
,
474 nscoord aVisIStartEdge
, nscoord aVisIEndEdge
,
475 uint32_t* aStartOffset
, uint32_t* aMaxLength
,
476 nscoord
* aSnappedStartEdge
,
477 nscoord
* aSnappedEndEdge
);
480 * Return true if this box has some text to display.
481 * It returns false if at least one of these conditions are met:
482 * a. the frame hasn't been reflowed yet
483 * b. GetContentLength() == 0
484 * c. it contains only non-significant white-space
486 bool HasNonSuppressedText() const;
489 * Object with various callbacks for PaintText() to invoke for different parts
490 * of the frame's text rendering, when we're generating paths rather than
493 * Callbacks are invoked in the following order:
495 * NotifySelectionBackgroundNeedsFill?
496 * PaintDecorationLine*
498 * NotifyGlyphPathEmitted*
500 * PaintDecorationLine*
501 * PaintSelectionDecorationLine*
503 * The color of each part of the frame's text rendering is passed as an
504 * argument to the NotifyBefore* callback for that part. The nscolor can take
505 * on one of the three selection special colors defined in LookAndFeel.h --
506 * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and
507 * NS_40PERCENT_FOREGROUND_COLOR.
509 struct DrawPathCallbacks
: gfxTextRunDrawCallbacks
{
511 * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
513 explicit DrawPathCallbacks(bool aShouldPaintSVGGlyphs
= false)
514 : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs
) {}
517 * Called to have the selection highlight drawn before the text is drawn
520 virtual void NotifySelectionBackgroundNeedsFill(const Rect
& aBackgroundRect
,
522 DrawTarget
& aDrawTarget
) {}
525 * Called before (for under/over-line) or after (for line-through) the text
526 * is drawn to have a text decoration line drawn.
528 virtual void PaintDecorationLine(Rect aPath
, nscolor aColor
) {}
531 * Called after selected text is drawn to have a decoration line drawn over
532 * the text. (All types of text decoration are drawn after the text when
535 virtual void PaintSelectionDecorationLine(Rect aPath
, nscolor aColor
) {}
538 * Called just before any paths have been emitted to the gfxContext
539 * for the glyphs of the frame's text.
541 virtual void NotifyBeforeText(nscolor aColor
) {}
544 * Called just after all the paths have been emitted to the gfxContext
545 * for the glyphs of the frame's text.
547 virtual void NotifyAfterText() {}
550 * Called just before a path corresponding to a selection decoration line
551 * has been emitted to the gfxContext.
553 virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor
) {}
556 * Called just after a path corresponding to a selection decoration line
557 * has been emitted to the gfxContext.
559 virtual void NotifySelectionDecorationLinePathEmitted() {}
562 struct MOZ_STACK_CLASS PaintTextParams
{
565 LayoutDeviceRect dirtyRect
;
566 mozilla::SVGContextPaint
* contextPaint
= nullptr;
567 DrawPathCallbacks
* callbacks
= nullptr;
569 PaintText
, // Normal text painting.
570 GenerateTextMask
// To generate a mask from a text frame. Should
571 // only paint text itself with opaque color.
572 // Text shadow, text selection color and text
573 // decoration are all discarded in this state.
575 uint8_t state
= PaintText
;
576 explicit PaintTextParams(gfxContext
* aContext
) : context(aContext
) {}
578 bool IsPaintText() const { return state
== PaintText
; }
579 bool IsGenerateTextMask() const { return state
== GenerateTextMask
; }
582 struct PaintTextSelectionParams
;
583 struct DrawTextRunParams
;
584 struct DrawTextParams
;
586 struct PaintShadowParams
;
587 struct PaintDecorationLineParams
;
589 struct PriorityOrderedSelectionsForRange
{
590 /// List of Selection Details active for the given range.
591 /// Ordered by priority, i.e. the last element has the highest priority.
592 nsTArray
<const SelectionDetails
*> mSelectionRanges
;
596 // Primary frame paint method called from nsDisplayText. Can also be used
597 // to generate paths rather than paint the frame's text by passing a callback
598 // object. The private DrawText() is what applies the text to a graphics
600 void PaintText(const PaintTextParams
& aParams
, const nscoord aVisIStartEdge
,
601 const nscoord aVisIEndEdge
, const nsPoint
& aToReferenceFrame
,
602 const bool aIsSelected
, float aOpacity
= 1.0f
);
603 // helper: paint text frame when we're impacted by at least one selection.
604 // Return false if the text was not painted and we should continue with
606 bool PaintTextWithSelection(const PaintTextSelectionParams
& aParams
,
607 const ClipEdges
& aClipEdges
);
608 // helper: paint text with foreground and background colors determined
609 // by selection(s). Also computes a mask of all selection types applying to
610 // our text, returned in aAllSelectionTypeMask.
611 // Return false if the text was not painted and we should continue with
613 bool PaintTextWithSelectionColors(
614 const PaintTextSelectionParams
& aParams
,
615 const mozilla::UniquePtr
<SelectionDetails
>& aDetails
,
616 SelectionTypeMask
* aAllSelectionTypeMask
, const ClipEdges
& aClipEdges
);
617 // helper: paint text decorations for text selected by aSelectionType
618 void PaintTextSelectionDecorations(
619 const PaintTextSelectionParams
& aParams
,
620 const mozilla::UniquePtr
<SelectionDetails
>& aDetails
,
621 SelectionType aSelectionType
);
623 SelectionTypeMask
ResolveSelections(
624 const PaintTextSelectionParams
& aParams
, const SelectionDetails
* aDetails
,
625 nsTArray
<PriorityOrderedSelectionsForRange
>& aResult
,
626 SelectionType aSelectionType
, bool* aAnyBackgrounds
= nullptr) const;
628 void DrawEmphasisMarks(gfxContext
* aContext
, mozilla::WritingMode aWM
,
629 const mozilla::gfx::Point
& aTextBaselinePt
,
630 const mozilla::gfx::Point
& aFramePt
, Range aRange
,
631 const nscolor
* aDecorationOverrideColor
,
632 PropertyProvider
* aProvider
);
634 nscolor
GetCaretColorAt(int32_t aOffset
) final
;
636 // @param aSelectionFlags may be multiple of nsISelectionDisplay::DISPLAY_*.
637 // @return nsISelectionController.idl's `getDisplaySelection`.
638 int16_t GetSelectionStatus(int16_t* aSelectionFlags
);
640 int32_t GetContentOffset() const { return mContentOffset
; }
641 int32_t GetContentLength() const {
642 NS_ASSERTION(GetContentEnd() - mContentOffset
>= 0, "negative length");
643 return GetContentEnd() - mContentOffset
;
645 int32_t GetContentEnd() const;
646 // This returns the length the frame thinks it *should* have after it was
647 // last reflowed (0 if it hasn't been reflowed yet). This should be used only
648 // when setting up the text offsets for a new continuation frame.
649 int32_t GetContentLengthHint() const { return mContentLengthHint
; }
651 // Compute the length of the content mapped by this frame
652 // and all its in-flow siblings. Basically this means starting at
653 // mContentOffset and going to the end of the text node or the next bidi
654 // continuation boundary.
655 int32_t GetInFlowContentLength();
658 * Acquires the text run for this content, if necessary.
659 * @param aWhichTextRun indicates whether to get an inflated or non-inflated
661 * @param aRefDrawTarget the DrawTarget to use as a reference for creating the
662 * textrun, if available (if not, we'll create one which will just be slower)
663 * @param aLineContainer the block ancestor for this frame, or nullptr if
665 * @param aFlowEndInTextRun if non-null, this returns the textrun offset of
666 * end of the text associated with this frame and its in-flow siblings
667 * @return a gfxSkipCharsIterator set up to map DOM offsets for this frame
668 * to offsets into the textrun; its initial offset is set to this frame's
671 gfxSkipCharsIterator
EnsureTextRun(TextRunType aWhichTextRun
,
672 DrawTarget
* aRefDrawTarget
= nullptr,
673 nsIFrame
* aLineContainer
= nullptr,
674 const nsLineList_iterator
* aLine
= nullptr,
675 uint32_t* aFlowEndInTextRun
= nullptr);
677 gfxTextRun
* GetTextRun(TextRunType aWhichTextRun
) const {
678 if (aWhichTextRun
== eInflated
|| !HasFontSizeInflation()) return mTextRun
;
679 return GetUninflatedTextRun();
681 gfxTextRun
* GetUninflatedTextRun() const;
682 void SetTextRun(gfxTextRun
* aTextRun
, TextRunType aWhichTextRun
,
684 bool IsInTextRunUserData() const {
685 return HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA
|
686 TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA
);
689 * Notify the frame that it should drop its pointer to a text run.
690 * Returns whether the text run was removed (i.e., whether it was
691 * associated with this frame, either as its inflated or non-inflated
694 bool RemoveTextRun(gfxTextRun
* aTextRun
);
696 * Clears out |mTextRun| (or the uninflated text run, when aInflated
697 * is nsTextFrame::eNotInflated and there is inflation) from all frames that
698 * hold a reference to it, starting at |aStartContinuation|, or if it's
699 * nullptr, starting at |this|. Deletes the text run if all references
700 * were cleared and it's not cached.
702 void ClearTextRun(nsTextFrame
* aStartContinuation
, TextRunType aWhichTextRun
);
704 void ClearTextRuns() {
705 ClearTextRun(nullptr, nsTextFrame::eInflated
);
706 if (HasFontSizeInflation()) {
707 ClearTextRun(nullptr, nsTextFrame::eNotInflated
);
712 * Wipe out references to textrun(s) without deleting the textruns.
714 void DisconnectTextRuns();
716 // Get the DOM content range mapped by this frame after excluding
717 // whitespace subject to start-of-line and end-of-line trimming.
718 // The textrun must have been created before calling this.
719 struct TrimmedOffsets
{
722 int32_t GetEnd() const { return mStart
+ mLength
; }
724 enum class TrimmedOffsetFlags
: uint8_t {
726 NotPostReflow
= 1 << 0,
727 NoTrimAfter
= 1 << 1,
728 NoTrimBefore
= 1 << 2
730 TrimmedOffsets
GetTrimmedOffsets(
731 const nsTextFragment
* aFrag
,
732 TrimmedOffsetFlags aFlags
= TrimmedOffsetFlags::Default
) const;
734 // Similar to Reflow(), but for use from nsLineLayout
735 void ReflowText(nsLineLayout
& aLineLayout
, nscoord aAvailableWidth
,
736 DrawTarget
* aDrawTarget
, ReflowOutput
& aMetrics
,
737 nsReflowStatus
& aStatus
);
739 nscoord
ComputeLineHeight() const;
741 bool IsFloatingFirstLetterChild() const;
743 bool IsInitialLetterChild() const;
745 bool ComputeCustomOverflow(mozilla::OverflowAreas
& aOverflowAreas
) final
;
746 bool ComputeCustomOverflowInternal(mozilla::OverflowAreas
& aOverflowAreas
,
747 bool aIncludeShadows
);
749 void AssignJustificationGaps(const mozilla::JustificationAssignment
& aAssign
);
750 mozilla::JustificationAssignment
GetJustificationAssignment() const;
752 uint32_t CountGraphemeClusters() const;
754 bool HasAnyNoncollapsedCharacters() final
;
757 * Call this after you have manually changed the text node contents without
758 * notifying that change. This behaves as if all the text contents changed.
759 * (You should only use this for native anonymous content.)
761 void NotifyNativeAnonymousTextnodeChange(uint32_t aOldLength
);
763 nsFontMetrics
* InflatedFontMetrics() const;
765 nsRect
WebRenderBounds();
767 // Find the continuation (which may be this frame itself) containing the
768 // given offset. Note that this may return null, if the offset is beyond the
769 // text covered by the continuation chain.
770 // (To be used only on the first textframe in the chain.)
771 nsTextFrame
* FindContinuationForOffset(int32_t aOffset
);
773 void SetHangableISize(nscoord aISize
);
774 nscoord
GetHangableISize() const;
775 void ClearHangableISize();
777 void SetTrimmableWS(gfxTextRun::TrimmableWS aTrimmableWS
);
778 gfxTextRun::TrimmableWS
GetTrimmableWS() const;
779 void ClearTrimmableWS();
782 virtual ~nsTextFrame();
784 friend class mozilla::nsDisplayTextGeometry
;
785 friend class mozilla::nsDisplayText
;
787 mutable RefPtr
<nsFontMetrics
> mFontMetrics
;
788 RefPtr
<gfxTextRun
> mTextRun
;
789 nsTextFrame
* mNextContinuation
;
790 // The key invariant here is that mContentOffset never decreases along
791 // a next-continuation chain. And of course mContentOffset is always <= the
792 // the text node's content length, and the mContentOffset for the first frame
793 // is always 0. Furthermore the text mapped by a frame is determined by
794 // GetContentOffset() and GetContentLength()/GetContentEnd(), which get
795 // the length from the difference between this frame's offset and the next
796 // frame's offset, or the text length if there is no next frame. This means
797 // the frames always map the text node without overlapping or leaving any
799 int32_t mContentOffset
;
800 // This does *not* indicate the length of text currently mapped by the frame;
801 // instead it's a hint saying that this frame *wants* to map this much text
802 // so if we create a new continuation, this is where that continuation should
804 int32_t mContentLengthHint
;
807 // Cached selection state.
808 enum class SelectionState
: uint8_t {
813 mutable SelectionState mIsSelected
;
815 // Flags used to track whether certain properties are present.
816 // (Public to keep MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS happy.)
818 enum class PropertyFlags
: uint8_t {
819 // Whether a cached continuations array is present.
820 Continuations
= 1 << 0,
821 // Whether a HangableWhitespace property is present.
823 // Whether a TrimmableWhitespace property is present.
824 TrimmableWS
= 2 << 1,
828 PropertyFlags mPropertyFlags
= PropertyFlags(0);
831 * Return true if the frame is part of a Selection.
832 * Helper method to implement the public IsSelected() API.
834 bool IsFrameSelected() const final
;
836 void InvalidateSelectionState() { mIsSelected
= SelectionState::Unknown
; }
838 mozilla::UniquePtr
<SelectionDetails
> GetSelectionDetails();
840 void UnionAdditionalOverflow(nsPresContext
* aPresContext
, nsIFrame
* aBlock
,
841 PropertyProvider
& aProvider
,
842 nsRect
* aInkOverflowRect
,
843 bool aIncludeTextDecorations
,
844 bool aIncludeShadows
);
846 // Update information of emphasis marks, and return the visial
847 // overflow rect of the emphasis marks.
848 nsRect
UpdateTextEmphasis(mozilla::WritingMode aWM
,
849 PropertyProvider
& aProvider
);
851 void PaintOneShadow(const PaintShadowParams
& aParams
,
852 const mozilla::StyleSimpleShadow
& aShadowDetails
,
853 gfxRect
& aBoundingBox
, uint32_t aBlurFlags
);
855 void PaintShadows(mozilla::Span
<const mozilla::StyleSimpleShadow
>,
856 const PaintShadowParams
& aParams
);
858 struct LineDecoration
{
861 // This is represents the offset from our baseline to mFrame's baseline;
862 // positive offsets are *above* the baseline and negative offsets below
863 nscoord mBaselineOffset
;
865 // This represents the offset from the initial position of the underline
866 const mozilla::LengthPercentageOrAuto mTextUnderlineOffset
;
868 // for CSS property text-decoration-thickness, the width refers to the
869 // thickness of the decoration line
870 const mozilla::StyleTextDecorationLength mTextDecorationThickness
;
872 mozilla::StyleTextDecorationStyle mStyle
;
874 // The text-underline-position property; affects the underline offset only
875 // if mTextUnderlineOffset is auto.
876 const mozilla::StyleTextUnderlinePosition mTextUnderlinePosition
;
878 LineDecoration(nsIFrame
* const aFrame
, const nscoord aOff
,
879 mozilla::StyleTextUnderlinePosition aUnderlinePosition
,
880 const mozilla::LengthPercentageOrAuto
& aUnderlineOffset
,
881 const mozilla::StyleTextDecorationLength
& aDecThickness
,
882 const nscolor aColor
,
883 const mozilla::StyleTextDecorationStyle aStyle
)
885 mBaselineOffset(aOff
),
886 mTextUnderlineOffset(aUnderlineOffset
),
887 mTextDecorationThickness(aDecThickness
),
890 mTextUnderlinePosition(aUnderlinePosition
) {}
892 LineDecoration(const LineDecoration
& aOther
) = default;
894 bool operator==(const LineDecoration
& aOther
) const {
895 return mFrame
== aOther
.mFrame
&& mStyle
== aOther
.mStyle
&&
896 mColor
== aOther
.mColor
&&
897 mBaselineOffset
== aOther
.mBaselineOffset
&&
898 mTextUnderlinePosition
== aOther
.mTextUnderlinePosition
&&
899 mTextUnderlineOffset
== aOther
.mTextUnderlineOffset
&&
900 mTextDecorationThickness
== aOther
.mTextDecorationThickness
;
903 bool operator!=(const LineDecoration
& aOther
) const {
904 return !(*this == aOther
);
907 struct TextDecorations
{
908 AutoTArray
<LineDecoration
, 1> mOverlines
, mUnderlines
, mStrikes
;
910 TextDecorations() = default;
912 bool HasDecorationLines() const {
913 return HasUnderline() || HasOverline() || HasStrikeout();
915 bool HasUnderline() const { return !mUnderlines
.IsEmpty(); }
916 bool HasOverline() const { return !mOverlines
.IsEmpty(); }
917 bool HasStrikeout() const { return !mStrikes
.IsEmpty(); }
918 bool operator==(const TextDecorations
& aOther
) const {
919 return mOverlines
== aOther
.mOverlines
&&
920 mUnderlines
== aOther
.mUnderlines
&& mStrikes
== aOther
.mStrikes
;
922 bool operator!=(const TextDecorations
& aOther
) const {
923 return !(*this == aOther
);
926 enum TextDecorationColorResolution
{ eResolvedColors
, eUnresolvedColors
};
927 void GetTextDecorations(nsPresContext
* aPresContext
,
928 TextDecorationColorResolution aColorResolution
,
929 TextDecorations
& aDecorations
);
931 void DrawTextRun(Range aRange
, const mozilla::gfx::Point
& aTextBaselinePt
,
932 const DrawTextRunParams
& aParams
);
934 void DrawTextRunAndDecorations(Range aRange
,
935 const mozilla::gfx::Point
& aTextBaselinePt
,
936 const DrawTextParams
& aParams
,
937 const TextDecorations
& aDecorations
);
939 void DrawText(Range aRange
, const mozilla::gfx::Point
& aTextBaselinePt
,
940 const DrawTextParams
& aParams
);
942 // Set non empty rect to aRect, it should be overflow rect or frame rect.
943 // If the result rect is larger than the given rect, this returns true.
944 bool CombineSelectionUnderlineRect(nsPresContext
* aPresContext
,
947 // This sets *aShadows to the appropriate shadows, if any, for the given
948 // type of selection.
949 // If text-shadow was not specified, *aShadows is left untouched.
950 // Note that the returned shadow(s) will only be valid as long as the
951 // textPaintStyle remains in scope.
952 void GetSelectionTextShadow(
953 SelectionType aSelectionType
, nsTextPaintStyle
& aTextPaintStyle
,
954 mozilla::Span
<const mozilla::StyleSimpleShadow
>* aShadows
);
957 * Utility methods to paint selection.
959 void DrawSelectionDecorations(
960 gfxContext
* aContext
, const LayoutDeviceRect
& aDirtyRect
,
961 mozilla::SelectionType aSelectionType
, nsTextPaintStyle
& aTextPaintStyle
,
962 const TextRangeStyle
& aRangeStyle
, const Point
& aPt
,
963 gfxFloat aICoordInFrame
, gfxFloat aWidth
, gfxFloat aAscent
,
964 const gfxFont::Metrics
& aFontMetrics
, DrawPathCallbacks
* aCallbacks
,
965 bool aVertical
, mozilla::StyleTextDecorationLine aDecoration
);
967 void PaintDecorationLine(const PaintDecorationLineParams
& aParams
);
969 * ComputeDescentLimitForSelectionUnderline() computes the most far position
970 * where we can put selection underline.
972 * @return The maximum underline offset from the baseline (positive value
973 * means that the underline can put below the baseline).
975 gfxFloat
ComputeDescentLimitForSelectionUnderline(
976 nsPresContext
* aPresContext
, const gfxFont::Metrics
& aFontMetrics
);
978 * This function encapsulates all knowledge of how selections affect
979 * foreground and background colors.
980 * @param aForeground the foreground color to use
981 * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
982 * background should be painted
983 * @return true if the selection affects colors, false otherwise
985 static bool GetSelectionTextColors(SelectionType aSelectionType
,
986 nsAtom
* aHighlightName
,
987 nsTextPaintStyle
& aTextPaintStyle
,
988 const TextRangeStyle
& aRangeStyle
,
989 nscolor
* aForeground
,
990 nscolor
* aBackground
);
992 * ComputeSelectionUnderlineHeight() computes selection underline height of
993 * the specified selection type from the font metrics.
995 static gfxFloat
ComputeSelectionUnderlineHeight(
996 nsPresContext
* aPresContext
, const gfxFont::Metrics
& aFontMetrics
,
997 SelectionType aSelectionType
);
1000 * @brief Helper struct which contains selection data such as its details,
1001 * range and priority.
1003 struct SelectionRange
{
1004 const SelectionDetails
* mDetails
{nullptr};
1005 gfxTextRun::Range mRange
;
1006 /// used to determine the order of overlapping selections of the same type.
1007 uint32_t mPriority
{0};
1010 * @brief Helper: Extracts a list of `SelectionRange` structs from given
1011 * `SelectionDetails` and computes a priority for overlapping selection
1014 static SelectionTypeMask
CreateSelectionRangeList(
1015 const SelectionDetails
* aDetails
, SelectionType aSelectionType
,
1016 const PaintTextSelectionParams
& aParams
,
1017 nsTArray
<SelectionRange
>& aSelectionRanges
, bool* aAnyBackgrounds
);
1020 * @brief Creates an array of `CombinedSelectionRange`s from given list
1021 * of `SelectionRange`s.
1022 * Each instance of `CombinedSelectionRange` represents a piece of text with
1023 * constant Selections.
1027 * Consider this text fragment, [] and () marking selection ranges:
1029 * This results in the following array of combined ranges:
1030 * - [0]: range: (2, 4), selections: "[]"
1031 * - [1]: range: (4, 5), selections: "[]", "()"
1032 * - [2]: range: (5, 6), selections: "()"
1033 * Depending on the priorities of the ranges, [1] may have a different order
1034 * of its ranges. The given example indicates that "()" has a higher priority
1037 * @param aSelectionRanges Array of `SelectionRange` objects. Must be
1038 * sorted by the start offset.
1039 * @param aCombinedSelectionRanges Out parameter. Returns the constructed
1040 * array of combined selection ranges.
1042 static void CombineSelectionRanges(
1043 const nsTArray
<SelectionRange
>& aSelectionRanges
,
1044 nsTArray
<PriorityOrderedSelectionsForRange
>& aCombinedSelectionRanges
);
1046 ContentOffsets
GetCharacterOffsetAtFramePointInternal(
1047 const nsPoint
& aPoint
, bool aForInsertionPoint
);
1049 static float GetTextCombineScaleFactor(nsTextFrame
* aFrame
);
1051 void ClearFrameOffsetCache();
1053 void ClearMetrics(ReflowOutput
& aMetrics
);
1055 // Return pointer to an array of all frames in the continuation chain, or
1056 // null if we're too short of memory.
1057 nsTArray
<nsTextFrame
*>* GetContinuations();
1059 // Clear any cached continuations array; this should be called whenever the
1060 // chain is modified.
1061 inline void ClearCachedContinuations();
1064 * UpdateIteratorFromOffset() updates the iterator from a given offset.
1065 * Also, aInOffset may be updated to cluster start if aInOffset isn't
1066 * the offset of cluster start.
1068 void UpdateIteratorFromOffset(const PropertyProvider
& aProperties
,
1070 gfxSkipCharsIterator
& aIter
);
1072 nsPoint
GetPointFromIterator(const gfxSkipCharsIterator
& aIter
,
1073 PropertyProvider
& aProperties
);
1076 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::TrimmedOffsetFlags
)
1077 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::PropertyFlags
)
1079 inline void nsTextFrame::ClearCachedContinuations() {
1080 MOZ_ASSERT(NS_IsMainThread());
1081 if (mPropertyFlags
& PropertyFlags::Continuations
) {
1082 RemoveProperty(ContinuationsProperty());
1083 mPropertyFlags
&= ~PropertyFlags::Continuations
;