Bug 1880890 [wpt PR 44658] - DOM: Added a few batch-insertion scenarios that are...
[gecko.git] / layout / generic / nsTextFrame.h
blob707d39dba20e354e08287c39e4cdeb4989329587
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"
15 #include "nsIFrame.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)
24 # undef DrawText
25 #endif
27 class nsTextPaintStyle;
28 class nsLineList_iterator;
29 struct SelectionDetails;
30 class nsTextFragment;
32 namespace mozilla {
33 class SVGContextPaint;
34 class SVGTextFrame;
35 class nsDisplayTextGeometry;
36 class nsDisplayText;
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;
50 public:
51 enum TextRunType : uint8_t;
52 struct TabWidthStore;
54 /**
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;
64 public:
65 /**
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);
81 /**
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);
90 /**
91 * As above, but assuming we want the inflated text run and associated
92 * metrics.
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(
126 Range aRange,
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");
137 return mLength;
139 const nsTextFragment* GetFragment() const { return mFrag; }
141 gfxFontGroup* GetFontGroup() const {
142 if (!mFontGroup) {
143 mFontGroup = GetFontMetrics()->GetThebesFontGroup();
145 return mFontGroup;
148 nsFontMetrics* GetFontMetrics() const {
149 if (!mFontMetrics) {
150 InitFontGroupAndFontMetrics();
152 return mFontMetrics;
155 void CalcTabWidths(Range aTransformedRange, gfxFloat aTabWidth) const;
157 gfxFloat MinTabAdvance() const;
159 const gfxSkipCharsIterator& GetEndHint() const { return mTempIterator; }
161 protected:
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;
172 nsTextFrame* mFrame;
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) {}
203 NS_DECL_FRAMEARENA_HELPERS(nsTextFrame)
205 friend class nsContinuingTextFrame;
207 // nsQueryFrame
208 NS_DECL_QUERYFRAME
210 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContinuationsProperty,
211 nsTArray<nsTextFrame*>)
213 // nsIFrame
214 void BuildDisplayList(nsDisplayListBuilder* aBuilder,
215 const nsDisplayListSet& aLists) final;
217 void Init(nsIContent* aContent, nsContainerFrame* aParent,
218 nsIFrame* aPrevInFlow) override;
220 void Destroy(DestroyContext&) override;
222 Cursor GetCursor(const nsPoint&) final;
224 nsresult CharacterDataChanged(const CharacterDataChangeInfo&) final;
226 nsTextFrame* FirstContinuation() const override {
227 return const_cast<nsTextFrame*>(this);
229 nsTextFrame* GetPrevContinuation() const override { return nullptr; }
230 nsTextFrame* GetNextContinuation() const final { return mNextContinuation; }
231 void SetNextContinuation(nsIFrame* aNextContinuation) final {
232 NS_ASSERTION(!aNextContinuation || Type() == aNextContinuation->Type(),
233 "setting a next continuation with incorrect type!");
234 NS_ASSERTION(
235 !nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this),
236 "creating a loop in continuation chain!");
237 mNextContinuation = static_cast<nsTextFrame*>(aNextContinuation);
238 if (aNextContinuation)
239 aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
240 // Setting a non-fluid continuation might affect our flow length (they're
241 // quite rare so we assume it always does) so we delete our cached value:
242 if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
243 GetContent()->RemoveProperty(nsGkAtoms::flowlength);
244 GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
247 nsTextFrame* GetNextInFlow() const final {
248 return mNextContinuation && mNextContinuation->HasAnyStateBits(
249 NS_FRAME_IS_FLUID_CONTINUATION)
250 ? mNextContinuation
251 : nullptr;
253 void SetNextInFlow(nsIFrame* aNextInFlow) final {
254 NS_ASSERTION(!aNextInFlow || Type() == aNextInFlow->Type(),
255 "setting a next in flow with incorrect type!");
256 NS_ASSERTION(
257 !nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this),
258 "creating a loop in continuation chain!");
259 mNextContinuation = static_cast<nsTextFrame*>(aNextInFlow);
260 if (mNextContinuation &&
261 !mNextContinuation->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) {
262 // Changing from non-fluid to fluid continuation might affect our flow
263 // length, so we delete our cached value:
264 if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
265 GetContent()->RemoveProperty(nsGkAtoms::flowlength);
266 GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
269 if (aNextInFlow) {
270 aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
273 nsTextFrame* LastInFlow() const final;
274 nsTextFrame* LastContinuation() const final;
276 bool ShouldSuppressLineBreak() const;
278 void InvalidateFrame(uint32_t aDisplayItemKey = 0,
279 bool aRebuildDisplayItems = true) final;
280 void InvalidateFrameWithRect(const nsRect& aRect,
281 uint32_t aDisplayItemKey = 0,
282 bool aRebuildDisplayItems = true) final;
284 #ifdef DEBUG_FRAME_DUMP
285 void List(FILE* out = stderr, const char* aPrefix = "",
286 ListFlags aFlags = ListFlags()) const final;
287 nsresult GetFrameName(nsAString& aResult) const final;
288 void ToCString(nsCString& aBuf) const;
289 void ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const final;
290 #endif
292 // Returns this text frame's content's text fragment.
294 // Assertions in Init() ensure we only ever get a Text node as content.
295 const nsTextFragment* TextFragment() const {
296 return &mContent->AsText()->TextFragment();
300 * Check that the text in this frame is entirely whitespace. Importantly,
301 * this function considers non-breaking spaces (0xa0) to be whitespace,
302 * whereas nsTextFrame::IsEmpty does not. It also considers both one and
303 * two-byte chars.
305 bool IsEntirelyWhitespace() const;
307 ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) final;
308 ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint& aPoint);
311 * This is called only on the primary text frame. It indicates that
312 * the selection state of the given character range has changed.
313 * Frames corresponding to the character range are unconditionally invalidated
314 * (Selection::Repaint depends on this).
315 * @param aStart start of character range.
316 * @param aEnd end (exclusive) of character range.
317 * @param aSelected true iff the character range is now selected.
318 * @param aType the type of the changed selection.
320 void SelectionStateChanged(uint32_t aStart, uint32_t aEnd, bool aSelected,
321 SelectionType aSelectionType);
323 FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) final;
324 FrameSearchResult PeekOffsetCharacter(
325 bool aForward, int32_t* aOffset,
326 PeekOffsetCharacterOptions aOptions = PeekOffsetCharacterOptions()) final;
327 FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace,
328 bool aIsKeyboardSelect, int32_t* aOffset,
329 PeekWordState* aState,
330 bool aTrimSpaces) final;
332 // Helper method that editor code uses to test for visibility.
333 [[nodiscard]] bool HasVisibleText();
335 // Flags for aSetLengthFlags
336 enum { ALLOW_FRAME_CREATION_AND_DESTRUCTION = 0x01 };
338 // Update offsets to account for new length. This may clear mTextRun.
339 void SetLength(int32_t aLength, nsLineLayout* aLineLayout,
340 uint32_t aSetLengthFlags = 0);
342 std::pair<int32_t, int32_t> GetOffsets() const final;
344 void AdjustOffsetsForBidi(int32_t start, int32_t end) final;
346 nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) final;
347 nsresult GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
348 nsTArray<nsRect>& aRects) final;
350 nsresult GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint,
351 int32_t* outFrameContentOffset,
352 nsIFrame** outChildFrame) final;
354 bool IsEmpty() final;
355 bool IsSelfEmpty() final { return IsEmpty(); }
356 Maybe<nscoord> GetNaturalBaselineBOffset(
357 mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup,
358 BaselineExportContext) const override;
360 bool HasSignificantTerminalNewline() const final;
363 * Returns true if this text frame is logically adjacent to the end of the
364 * line.
366 bool IsAtEndOfLine() const;
369 * Call this only after reflow the frame. Returns true if non-collapsed
370 * characters are present.
372 bool HasNoncollapsedCharacters() const {
373 return HasAnyStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
376 #ifdef ACCESSIBILITY
377 mozilla::a11y::AccType AccessibleType() final;
378 #endif
380 float GetFontSizeInflation() const;
381 bool IsCurrentFontInflation(float aInflation) const;
382 bool HasFontSizeInflation() const {
383 return HasAnyStateBits(TEXT_HAS_FONT_INFLATION);
385 void SetFontSizeInflation(float aInflation);
387 void MarkIntrinsicISizesDirty() final;
388 nscoord GetMinISize(gfxContext* aRenderingContext) final;
389 nscoord GetPrefISize(gfxContext* aRenderingContext) final;
390 void AddInlineMinISize(gfxContext* aRenderingContext,
391 InlineMinISizeData* aData) override;
392 void AddInlinePrefISize(gfxContext* aRenderingContext,
393 InlinePrefISizeData* aData) override;
394 SizeComputationResult ComputeSize(
395 gfxContext* aRenderingContext, mozilla::WritingMode aWM,
396 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
397 const mozilla::LogicalSize& aMargin,
398 const mozilla::LogicalSize& aBorderPadding,
399 const mozilla::StyleSizeOverrides& aSizeOverrides,
400 mozilla::ComputeSizeFlags aFlags) final;
401 nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const final;
402 nsresult GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
403 nscoord* aXMost) final;
404 void Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
405 const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final;
406 bool CanContinueTextRun() const final;
407 // Method that is called for a text frame that is logically
408 // adjacent to the end of the line (i.e. followed only by empty text frames,
409 // placeholders or inlines containing such).
410 struct TrimOutput {
411 // true if we trimmed some space or changed metrics in some other way.
412 // In this case, we should call RecomputeOverflow on this frame.
413 bool mChanged;
414 // an amount to *subtract* from the frame's width (zero if !mChanged)
415 nscoord mDeltaWidth;
417 TrimOutput TrimTrailingWhiteSpace(DrawTarget* aDrawTarget);
418 RenderedText GetRenderedText(
419 uint32_t aStartOffset = 0, uint32_t aEndOffset = UINT32_MAX,
420 TextOffsetType aOffsetType = TextOffsetType::OffsetsInContentText,
421 TrailingWhitespace aTrimTrailingWhitespace =
422 TrailingWhitespace::Trim) final;
424 mozilla::OverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame,
425 bool aIncludeShadows = true);
427 enum TextRunType : uint8_t {
428 // Anything in reflow (but not intrinsic width calculation) or
429 // painting should use the inflated text run (i.e., with font size
430 // inflation applied).
431 eInflated,
432 // Intrinsic width calculation should use the non-inflated text run.
433 // When there is font size inflation, it will be different.
434 eNotInflated
437 void AddInlineMinISizeForFlow(gfxContext* aRenderingContext,
438 nsIFrame::InlineMinISizeData* aData,
439 TextRunType aTextRunType);
440 void AddInlinePrefISizeForFlow(gfxContext* aRenderingContext,
441 InlinePrefISizeData* aData,
442 TextRunType aTextRunType);
445 * Calculate the horizontal bounds of the grapheme clusters that fit entirely
446 * inside the given left[top]/right[bottom] edges (which are positive lengths
447 * from the respective frame edge). If an input value is zero it is ignored
448 * and the result for that edge is zero. All out parameter values are
449 * undefined when the method returns false.
450 * @return true if at least one whole grapheme cluster fit between the edges
452 bool MeasureCharClippedText(nscoord aVisIStartEdge, nscoord aVisIEndEdge,
453 nscoord* aSnappedStartEdge,
454 nscoord* aSnappedEndEdge);
456 * Same as above; this method also the returns the corresponding text run
457 * offset and number of characters that fit. All out parameter values are
458 * undefined when the method returns false.
459 * @return true if at least one whole grapheme cluster fit between the edges
461 bool MeasureCharClippedText(PropertyProvider& aProvider,
462 nscoord aVisIStartEdge, nscoord aVisIEndEdge,
463 uint32_t* aStartOffset, uint32_t* aMaxLength,
464 nscoord* aSnappedStartEdge,
465 nscoord* aSnappedEndEdge);
468 * Return true if this box has some text to display.
469 * It returns false if at least one of these conditions are met:
470 * a. the frame hasn't been reflowed yet
471 * b. GetContentLength() == 0
472 * c. it contains only non-significant white-space
474 bool HasNonSuppressedText() const;
477 * Object with various callbacks for PaintText() to invoke for different parts
478 * of the frame's text rendering, when we're generating paths rather than
479 * painting.
481 * Callbacks are invoked in the following order:
483 * NotifySelectionBackgroundNeedsFill?
484 * PaintDecorationLine*
485 * NotifyBeforeText
486 * NotifyGlyphPathEmitted*
487 * NotifyAfterText
488 * PaintDecorationLine*
489 * PaintSelectionDecorationLine*
491 * The color of each part of the frame's text rendering is passed as an
492 * argument to the NotifyBefore* callback for that part. The nscolor can take
493 * on one of the three selection special colors defined in LookAndFeel.h --
494 * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and
495 * NS_40PERCENT_FOREGROUND_COLOR.
497 struct DrawPathCallbacks : gfxTextRunDrawCallbacks {
499 * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
501 explicit DrawPathCallbacks(bool aShouldPaintSVGGlyphs = false)
502 : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs) {}
505 * Called to have the selection highlight drawn before the text is drawn
506 * over the top.
508 virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
509 nscolor aColor,
510 DrawTarget& aDrawTarget) {}
513 * Called before (for under/over-line) or after (for line-through) the text
514 * is drawn to have a text decoration line drawn.
516 virtual void PaintDecorationLine(Rect aPath, bool aPaintingShadows,
517 nscolor aColor) {}
520 * Called after selected text is drawn to have a decoration line drawn over
521 * the text. (All types of text decoration are drawn after the text when
522 * text is selected.)
524 virtual void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows,
525 nscolor aColor) {}
528 * Called just before any paths have been emitted to the gfxContext
529 * for the glyphs of the frame's text.
531 virtual void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) {}
534 * Called just after all the paths have been emitted to the gfxContext
535 * for the glyphs of the frame's text.
537 virtual void NotifyAfterText() {}
540 * Called just before a path corresponding to a selection decoration line
541 * has been emitted to the gfxContext.
543 virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor) {}
546 * Called just after a path corresponding to a selection decoration line
547 * has been emitted to the gfxContext.
549 virtual void NotifySelectionDecorationLinePathEmitted() {}
552 struct MOZ_STACK_CLASS PaintTextParams {
553 gfxContext* context;
554 Point framePt;
555 LayoutDeviceRect dirtyRect;
556 mozilla::SVGContextPaint* contextPaint = nullptr;
557 DrawPathCallbacks* callbacks = nullptr;
558 enum {
559 PaintText, // Normal text painting.
560 GenerateTextMask // To generate a mask from a text frame. Should
561 // only paint text itself with opaque color.
562 // Text shadow, text selection color and text
563 // decoration are all discarded in this state.
565 uint8_t state = PaintText;
566 explicit PaintTextParams(gfxContext* aContext) : context(aContext) {}
568 bool IsPaintText() const { return state == PaintText; }
569 bool IsGenerateTextMask() const { return state == GenerateTextMask; }
572 struct PaintTextSelectionParams;
573 struct DrawTextRunParams;
574 struct DrawTextParams;
575 struct ClipEdges;
576 struct PaintShadowParams;
577 struct PaintDecorationLineParams;
579 struct PriorityOrderedSelectionsForRange {
580 /// List of Selection Details active for the given range.
581 /// Ordered by priority, i.e. the last element has the highest priority.
582 nsTArray<const SelectionDetails*> mSelectionRanges;
583 Range mRange;
586 // Primary frame paint method called from nsDisplayText. Can also be used
587 // to generate paths rather than paint the frame's text by passing a callback
588 // object. The private DrawText() is what applies the text to a graphics
589 // context.
590 void PaintText(const PaintTextParams& aParams, const nscoord aVisIStartEdge,
591 const nscoord aVisIEndEdge, const nsPoint& aToReferenceFrame,
592 const bool aIsSelected, float aOpacity = 1.0f);
593 // helper: paint text frame when we're impacted by at least one selection.
594 // Return false if the text was not painted and we should continue with
595 // the fast path.
596 bool PaintTextWithSelection(const PaintTextSelectionParams& aParams,
597 const ClipEdges& aClipEdges);
598 // helper: paint text with foreground and background colors determined
599 // by selection(s). Also computes a mask of all selection types applying to
600 // our text, returned in aAllSelectionTypeMask.
601 // Return false if the text was not painted and we should continue with
602 // the fast path.
603 bool PaintTextWithSelectionColors(
604 const PaintTextSelectionParams& aParams,
605 const mozilla::UniquePtr<SelectionDetails>& aDetails,
606 SelectionTypeMask* aAllSelectionTypeMask, const ClipEdges& aClipEdges);
607 // helper: paint text decorations for text selected by aSelectionType
608 void PaintTextSelectionDecorations(
609 const PaintTextSelectionParams& aParams,
610 const mozilla::UniquePtr<SelectionDetails>& aDetails,
611 SelectionType aSelectionType);
613 SelectionTypeMask ResolveSelections(
614 const PaintTextSelectionParams& aParams, const SelectionDetails* aDetails,
615 nsTArray<PriorityOrderedSelectionsForRange>& aResult,
616 SelectionType aSelectionType, bool* aAnyBackgrounds = nullptr) const;
618 void DrawEmphasisMarks(gfxContext* aContext, mozilla::WritingMode aWM,
619 const mozilla::gfx::Point& aTextBaselinePt,
620 const mozilla::gfx::Point& aFramePt, Range aRange,
621 const nscolor* aDecorationOverrideColor,
622 PropertyProvider* aProvider);
624 nscolor GetCaretColorAt(int32_t aOffset) final;
626 // @param aSelectionFlags may be multiple of nsISelectionDisplay::DISPLAY_*.
627 // @return nsISelectionController.idl's `getDisplaySelection`.
628 int16_t GetSelectionStatus(int16_t* aSelectionFlags);
630 int32_t GetContentOffset() const { return mContentOffset; }
631 int32_t GetContentLength() const {
632 NS_ASSERTION(GetContentEnd() - mContentOffset >= 0, "negative length");
633 return GetContentEnd() - mContentOffset;
635 int32_t GetContentEnd() const;
636 // This returns the length the frame thinks it *should* have after it was
637 // last reflowed (0 if it hasn't been reflowed yet). This should be used only
638 // when setting up the text offsets for a new continuation frame.
639 int32_t GetContentLengthHint() const { return mContentLengthHint; }
641 // Compute the length of the content mapped by this frame
642 // and all its in-flow siblings. Basically this means starting at
643 // mContentOffset and going to the end of the text node or the next bidi
644 // continuation boundary.
645 int32_t GetInFlowContentLength();
648 * Acquires the text run for this content, if necessary.
649 * @param aWhichTextRun indicates whether to get an inflated or non-inflated
650 * text run
651 * @param aRefDrawTarget the DrawTarget to use as a reference for creating the
652 * textrun, if available (if not, we'll create one which will just be slower)
653 * @param aLineContainer the block ancestor for this frame, or nullptr if
654 * unknown
655 * @param aFlowEndInTextRun if non-null, this returns the textrun offset of
656 * end of the text associated with this frame and its in-flow siblings
657 * @return a gfxSkipCharsIterator set up to map DOM offsets for this frame
658 * to offsets into the textrun; its initial offset is set to this frame's
659 * content offset
661 gfxSkipCharsIterator EnsureTextRun(TextRunType aWhichTextRun,
662 DrawTarget* aRefDrawTarget = nullptr,
663 nsIFrame* aLineContainer = nullptr,
664 const nsLineList_iterator* aLine = nullptr,
665 uint32_t* aFlowEndInTextRun = nullptr);
667 gfxTextRun* GetTextRun(TextRunType aWhichTextRun) const {
668 if (aWhichTextRun == eInflated || !HasFontSizeInflation()) return mTextRun;
669 return GetUninflatedTextRun();
671 gfxTextRun* GetUninflatedTextRun() const;
672 void SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
673 float aInflation);
674 bool IsInTextRunUserData() const {
675 return HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA |
676 TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA);
679 * Notify the frame that it should drop its pointer to a text run.
680 * Returns whether the text run was removed (i.e., whether it was
681 * associated with this frame, either as its inflated or non-inflated
682 * text run.
684 bool RemoveTextRun(gfxTextRun* aTextRun);
686 * Clears out |mTextRun| (or the uninflated text run, when aInflated
687 * is nsTextFrame::eNotInflated and there is inflation) from all frames that
688 * hold a reference to it, starting at |aStartContinuation|, or if it's
689 * nullptr, starting at |this|. Deletes the text run if all references
690 * were cleared and it's not cached.
692 void ClearTextRun(nsTextFrame* aStartContinuation, TextRunType aWhichTextRun);
694 void ClearTextRuns() {
695 ClearTextRun(nullptr, nsTextFrame::eInflated);
696 if (HasFontSizeInflation()) {
697 ClearTextRun(nullptr, nsTextFrame::eNotInflated);
702 * Wipe out references to textrun(s) without deleting the textruns.
704 void DisconnectTextRuns();
706 // Get the DOM content range mapped by this frame after excluding
707 // whitespace subject to start-of-line and end-of-line trimming.
708 // The textrun must have been created before calling this.
709 struct TrimmedOffsets {
710 int32_t mStart;
711 int32_t mLength;
712 int32_t GetEnd() const { return mStart + mLength; }
714 enum class TrimmedOffsetFlags : uint8_t {
715 Default = 0,
716 NotPostReflow = 1 << 0,
717 NoTrimAfter = 1 << 1,
718 NoTrimBefore = 1 << 2
720 TrimmedOffsets GetTrimmedOffsets(
721 const nsTextFragment* aFrag,
722 TrimmedOffsetFlags aFlags = TrimmedOffsetFlags::Default) const;
724 // Similar to Reflow(), but for use from nsLineLayout
725 void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
726 DrawTarget* aDrawTarget, ReflowOutput& aMetrics,
727 nsReflowStatus& aStatus);
729 nscoord ComputeLineHeight() const;
731 bool IsFloatingFirstLetterChild() const;
733 bool IsInitialLetterChild() const;
735 bool ComputeCustomOverflow(mozilla::OverflowAreas& aOverflowAreas) final;
736 bool ComputeCustomOverflowInternal(mozilla::OverflowAreas& aOverflowAreas,
737 bool aIncludeShadows);
739 void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
740 mozilla::JustificationAssignment GetJustificationAssignment() const;
742 uint32_t CountGraphemeClusters() const;
744 bool HasAnyNoncollapsedCharacters() final;
747 * Call this after you have manually changed the text node contents without
748 * notifying that change. This behaves as if all the text contents changed.
749 * (You should only use this for native anonymous content.)
751 void NotifyNativeAnonymousTextnodeChange(uint32_t aOldLength);
753 nsFontMetrics* InflatedFontMetrics() const;
755 nsRect WebRenderBounds();
757 // Find the continuation (which may be this frame itself) containing the
758 // given offset. Note that this may return null, if the offset is beyond the
759 // text covered by the continuation chain.
760 // (To be used only on the first textframe in the chain.)
761 nsTextFrame* FindContinuationForOffset(int32_t aOffset);
763 void SetHangableISize(nscoord aISize);
764 nscoord GetHangableISize() const;
765 void ClearHangableISize();
767 void SetTrimmableWS(gfxTextRun::TrimmableWS aTrimmableWS);
768 gfxTextRun::TrimmableWS GetTrimmableWS() const;
769 void ClearTrimmableWS();
771 protected:
772 virtual ~nsTextFrame();
774 friend class mozilla::nsDisplayTextGeometry;
775 friend class mozilla::nsDisplayText;
777 mutable RefPtr<nsFontMetrics> mFontMetrics;
778 RefPtr<gfxTextRun> mTextRun;
779 nsTextFrame* mNextContinuation = nullptr;
780 // The key invariant here is that mContentOffset never decreases along
781 // a next-continuation chain. And of course mContentOffset is always <= the
782 // the text node's content length, and the mContentOffset for the first frame
783 // is always 0. Furthermore the text mapped by a frame is determined by
784 // GetContentOffset() and GetContentLength()/GetContentEnd(), which get
785 // the length from the difference between this frame's offset and the next
786 // frame's offset, or the text length if there is no next frame. This means
787 // the frames always map the text node without overlapping or leaving any
788 // gaps.
789 int32_t mContentOffset = 0;
790 // This does *not* indicate the length of text currently mapped by the frame;
791 // instead it's a hint saying that this frame *wants* to map this much text
792 // so if we create a new continuation, this is where that continuation should
793 // start.
794 int32_t mContentLengthHint = 0;
795 nscoord mAscent = 0;
797 // Cached selection state.
798 enum class SelectionState : uint8_t {
799 Unknown,
800 Selected,
801 NotSelected,
803 mutable SelectionState mIsSelected = SelectionState::Unknown;
805 // Flags used to track whether certain properties are present.
806 // (Public to keep MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS happy.)
807 public:
808 enum class PropertyFlags : uint8_t {
809 // Whether a cached continuations array is present.
810 Continuations = 1 << 0,
811 // Whether a HangableWhitespace property is present.
812 HangableWS = 1 << 1,
813 // Whether a TrimmableWhitespace property is present.
814 TrimmableWS = 2 << 1,
817 protected:
818 PropertyFlags mPropertyFlags = PropertyFlags(0);
821 * Return true if the frame is part of a Selection.
822 * Helper method to implement the public IsSelected() API.
824 bool IsFrameSelected() const final;
826 void InvalidateSelectionState() { mIsSelected = SelectionState::Unknown; }
828 mozilla::UniquePtr<SelectionDetails> GetSelectionDetails();
830 void UnionAdditionalOverflow(nsPresContext* aPresContext, nsIFrame* aBlock,
831 PropertyProvider& aProvider,
832 nsRect* aInkOverflowRect,
833 bool aIncludeTextDecorations,
834 bool aIncludeShadows);
836 // Update information of emphasis marks, and return the visial
837 // overflow rect of the emphasis marks.
838 nsRect UpdateTextEmphasis(mozilla::WritingMode aWM,
839 PropertyProvider& aProvider);
841 void PaintOneShadow(const PaintShadowParams& aParams,
842 const mozilla::StyleSimpleShadow& aShadowDetails,
843 gfxRect& aBoundingBox, uint32_t aBlurFlags);
845 void PaintShadows(mozilla::Span<const mozilla::StyleSimpleShadow>,
846 const PaintShadowParams& aParams);
848 struct LineDecoration {
849 nsIFrame* mFrame;
851 // This is represents the offset from our baseline to mFrame's baseline;
852 // positive offsets are *above* the baseline and negative offsets below
853 nscoord mBaselineOffset;
855 // This represents the offset from the initial position of the underline
856 const mozilla::LengthPercentageOrAuto mTextUnderlineOffset;
858 // for CSS property text-decoration-thickness, the width refers to the
859 // thickness of the decoration line
860 const mozilla::StyleTextDecorationLength mTextDecorationThickness;
861 nscolor mColor;
862 mozilla::StyleTextDecorationStyle mStyle;
864 // The text-underline-position property; affects the underline offset only
865 // if mTextUnderlineOffset is auto.
866 const mozilla::StyleTextUnderlinePosition mTextUnderlinePosition;
868 LineDecoration(nsIFrame* const aFrame, const nscoord aOff,
869 mozilla::StyleTextUnderlinePosition aUnderlinePosition,
870 const mozilla::LengthPercentageOrAuto& aUnderlineOffset,
871 const mozilla::StyleTextDecorationLength& aDecThickness,
872 const nscolor aColor,
873 const mozilla::StyleTextDecorationStyle aStyle)
874 : mFrame(aFrame),
875 mBaselineOffset(aOff),
876 mTextUnderlineOffset(aUnderlineOffset),
877 mTextDecorationThickness(aDecThickness),
878 mColor(aColor),
879 mStyle(aStyle),
880 mTextUnderlinePosition(aUnderlinePosition) {}
882 LineDecoration(const LineDecoration& aOther) = default;
884 bool operator==(const LineDecoration& aOther) const {
885 return mFrame == aOther.mFrame && mStyle == aOther.mStyle &&
886 mColor == aOther.mColor &&
887 mBaselineOffset == aOther.mBaselineOffset &&
888 mTextUnderlinePosition == aOther.mTextUnderlinePosition &&
889 mTextUnderlineOffset == aOther.mTextUnderlineOffset &&
890 mTextDecorationThickness == aOther.mTextDecorationThickness;
893 bool operator!=(const LineDecoration& aOther) const {
894 return !(*this == aOther);
897 struct TextDecorations {
898 AutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes;
900 TextDecorations() = default;
902 bool HasDecorationLines() const {
903 return HasUnderline() || HasOverline() || HasStrikeout();
905 bool HasUnderline() const { return !mUnderlines.IsEmpty(); }
906 bool HasOverline() const { return !mOverlines.IsEmpty(); }
907 bool HasStrikeout() const { return !mStrikes.IsEmpty(); }
908 bool operator==(const TextDecorations& aOther) const {
909 return mOverlines == aOther.mOverlines &&
910 mUnderlines == aOther.mUnderlines && mStrikes == aOther.mStrikes;
912 bool operator!=(const TextDecorations& aOther) const {
913 return !(*this == aOther);
916 enum TextDecorationColorResolution { eResolvedColors, eUnresolvedColors };
917 void GetTextDecorations(nsPresContext* aPresContext,
918 TextDecorationColorResolution aColorResolution,
919 TextDecorations& aDecorations);
921 void DrawTextRun(Range aRange, const mozilla::gfx::Point& aTextBaselinePt,
922 const DrawTextRunParams& aParams);
924 void DrawTextRunAndDecorations(Range aRange,
925 const mozilla::gfx::Point& aTextBaselinePt,
926 const DrawTextParams& aParams,
927 const TextDecorations& aDecorations);
929 void DrawText(Range aRange, const mozilla::gfx::Point& aTextBaselinePt,
930 const DrawTextParams& aParams);
932 // Set non empty rect to aRect, it should be overflow rect or frame rect.
933 // If the result rect is larger than the given rect, this returns true.
934 bool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
935 nsRect& aRect);
937 // This sets *aShadows to the appropriate shadows, if any, for the given
938 // type of selection.
939 // If text-shadow was not specified, *aShadows is left untouched.
940 // Note that the returned shadow(s) will only be valid as long as the
941 // textPaintStyle remains in scope.
942 void GetSelectionTextShadow(
943 SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle,
944 mozilla::Span<const mozilla::StyleSimpleShadow>* aShadows);
947 * Utility methods to paint selection.
949 void DrawSelectionDecorations(
950 gfxContext* aContext, const LayoutDeviceRect& aDirtyRect,
951 mozilla::SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle,
952 const TextRangeStyle& aRangeStyle, const Point& aPt,
953 gfxFloat aICoordInFrame, gfxFloat aWidth, gfxFloat aAscent,
954 const gfxFont::Metrics& aFontMetrics, DrawPathCallbacks* aCallbacks,
955 bool aVertical, mozilla::StyleTextDecorationLine aDecoration);
957 void PaintDecorationLine(const PaintDecorationLineParams& aParams);
959 * ComputeDescentLimitForSelectionUnderline() computes the most far position
960 * where we can put selection underline.
962 * @return The maximum underline offset from the baseline (positive value
963 * means that the underline can put below the baseline).
965 gfxFloat ComputeDescentLimitForSelectionUnderline(
966 nsPresContext* aPresContext, const gfxFont::Metrics& aFontMetrics);
968 * This function encapsulates all knowledge of how selections affect
969 * foreground and background colors.
970 * @param aForeground the foreground color to use
971 * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
972 * background should be painted
973 * @return true if the selection affects colors, false otherwise
975 static bool GetSelectionTextColors(SelectionType aSelectionType,
976 nsAtom* aHighlightName,
977 nsTextPaintStyle& aTextPaintStyle,
978 const TextRangeStyle& aRangeStyle,
979 nscolor* aForeground,
980 nscolor* aBackground);
982 * ComputeSelectionUnderlineHeight() computes selection underline height of
983 * the specified selection type from the font metrics.
985 static gfxFloat ComputeSelectionUnderlineHeight(
986 nsPresContext* aPresContext, const gfxFont::Metrics& aFontMetrics,
987 SelectionType aSelectionType);
990 * @brief Helper struct which contains selection data such as its details,
991 * range and priority.
993 struct SelectionRange {
994 const SelectionDetails* mDetails{nullptr};
995 gfxTextRun::Range mRange;
996 /// used to determine the order of overlapping selections of the same type.
997 uint32_t mPriority{0};
1000 * @brief Helper: Extracts a list of `SelectionRange` structs from given
1001 * `SelectionDetails` and computes a priority for overlapping selection
1002 * ranges.
1004 static SelectionTypeMask CreateSelectionRangeList(
1005 const SelectionDetails* aDetails, SelectionType aSelectionType,
1006 const PaintTextSelectionParams& aParams,
1007 nsTArray<SelectionRange>& aSelectionRanges, bool* aAnyBackgrounds);
1010 * @brief Creates an array of `CombinedSelectionRange`s from given list
1011 * of `SelectionRange`s.
1012 * Each instance of `CombinedSelectionRange` represents a piece of text with
1013 * constant Selections.
1015 * Example:
1017 * Consider this text fragment, [] and () marking selection ranges:
1018 * ab[cd(e]f)g
1019 * This results in the following array of combined ranges:
1020 * - [0]: range: (2, 4), selections: "[]"
1021 * - [1]: range: (4, 5), selections: "[]", "()"
1022 * - [2]: range: (5, 6), selections: "()"
1023 * Depending on the priorities of the ranges, [1] may have a different order
1024 * of its ranges. The given example indicates that "()" has a higher priority
1025 * than "[]".
1027 * @param aSelectionRanges Array of `SelectionRange` objects. Must be
1028 * sorted by the start offset.
1029 * @param aCombinedSelectionRanges Out parameter. Returns the constructed
1030 * array of combined selection ranges.
1032 static void CombineSelectionRanges(
1033 const nsTArray<SelectionRange>& aSelectionRanges,
1034 nsTArray<PriorityOrderedSelectionsForRange>& aCombinedSelectionRanges);
1036 ContentOffsets GetCharacterOffsetAtFramePointInternal(
1037 const nsPoint& aPoint, bool aForInsertionPoint);
1039 static float GetTextCombineScaleFactor(nsTextFrame* aFrame);
1041 void ClearFrameOffsetCache();
1043 void ClearMetrics(ReflowOutput& aMetrics);
1045 // Return pointer to an array of all frames in the continuation chain, or
1046 // null if we're too short of memory.
1047 nsTArray<nsTextFrame*>* GetContinuations();
1049 // Clear any cached continuations array; this should be called whenever the
1050 // chain is modified.
1051 inline void ClearCachedContinuations();
1054 * UpdateIteratorFromOffset() updates the iterator from a given offset.
1055 * Also, aInOffset may be updated to cluster start if aInOffset isn't
1056 * the offset of cluster start.
1058 void UpdateIteratorFromOffset(const PropertyProvider& aProperties,
1059 int32_t& aInOffset,
1060 gfxSkipCharsIterator& aIter);
1062 nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter,
1063 PropertyProvider& aProperties);
1066 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::TrimmedOffsetFlags)
1067 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::PropertyFlags)
1069 inline void nsTextFrame::ClearCachedContinuations() {
1070 MOZ_ASSERT(NS_IsMainThread());
1071 if (mPropertyFlags & PropertyFlags::Continuations) {
1072 RemoveProperty(ContinuationsProperty());
1073 mPropertyFlags &= ~PropertyFlags::Continuations;
1077 #endif