summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsTextFrame.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /layout/generic/nsTextFrame.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/generic/nsTextFrame.h')
-rw-r--r--layout/generic/nsTextFrame.h1075
1 files changed, 1075 insertions, 0 deletions
diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h
new file mode 100644
index 0000000000..568d3333c2
--- /dev/null
+++ b/layout/generic/nsTextFrame.h
@@ -0,0 +1,1075 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTextFrame_h__
+#define nsTextFrame_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/Text.h"
+#include "mozilla/gfx/2D.h"
+
+#include "nsIFrame.h"
+#include "nsISelectionController.h"
+#include "nsSplittableFrame.h"
+#include "gfxSkipChars.h"
+#include "gfxTextRun.h"
+#include "JustificationUtils.h"
+
+// Undo the windows.h damage
+#if defined(XP_WIN) && defined(DrawText)
+# undef DrawText
+#endif
+
+class nsTextPaintStyle;
+class nsLineList_iterator;
+struct SelectionDetails;
+class nsTextFragment;
+
+namespace mozilla {
+class SVGContextPaint;
+class SVGTextFrame;
+class nsDisplayTextGeometry;
+class nsDisplayText;
+} // namespace mozilla
+
+class nsTextFrame : public nsIFrame {
+ typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
+ typedef mozilla::SelectionTypeMask SelectionTypeMask;
+ typedef mozilla::SelectionType SelectionType;
+ typedef mozilla::TextRangeStyle TextRangeStyle;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+ typedef mozilla::gfx::Point Point;
+ typedef mozilla::gfx::Rect Rect;
+ typedef mozilla::gfx::Size Size;
+ typedef gfxTextRun::Range Range;
+
+ public:
+ enum TextRunType : uint8_t;
+ struct TabWidthStore;
+
+ /**
+ * An implementation of gfxTextRun::PropertyProvider that computes spacing and
+ * hyphenation based on CSS properties for a text frame.
+ */
+ class MOZ_STACK_CLASS PropertyProvider final
+ : public gfxTextRun::PropertyProvider {
+ typedef gfxTextRun::Range Range;
+ typedef gfxTextRun::HyphenType HyphenType;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ public:
+ /**
+ * Use this constructor for reflow, when we don't know what text is
+ * really mapped by the frame and we have a lot of other data around.
+ *
+ * @param aLength can be INT32_MAX to indicate we cover all the text
+ * associated with aFrame up to where its flow chain ends in the given
+ * textrun. If INT32_MAX is passed, justification and hyphen-related methods
+ * cannot be called, nor can GetOriginalLength().
+ */
+ PropertyProvider(gfxTextRun* aTextRun, const nsStyleText* aTextStyle,
+ const nsTextFragment* aFrag, nsTextFrame* aFrame,
+ const gfxSkipCharsIterator& aStart, int32_t aLength,
+ nsIFrame* aLineContainer,
+ nscoord aOffsetFromBlockOriginForTabs,
+ nsTextFrame::TextRunType aWhichTextRun);
+
+ /**
+ * Use this constructor after the frame has been reflowed and we don't
+ * have other data around. Gets everything from the frame. EnsureTextRun
+ * *must* be called before this!!!
+ */
+ PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart,
+ nsTextFrame::TextRunType aWhichTextRun,
+ nsFontMetrics* aFontMetrics);
+
+ /**
+ * As above, but assuming we want the inflated text run and associated
+ * metrics.
+ */
+ PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart)
+ : PropertyProvider(aFrame, aStart, nsTextFrame::eInflated,
+ aFrame->InflatedFontMetrics()) {}
+
+ // Call this after construction if you're not going to reflow the text
+ void InitializeForDisplay(bool aTrimAfter);
+
+ void InitializeForMeasure();
+
+ void GetSpacing(Range aRange, Spacing* aSpacing) const final;
+ gfxFloat GetHyphenWidth() const final;
+ void GetHyphenationBreaks(Range aRange,
+ HyphenType* aBreakBefore) const final;
+ mozilla::StyleHyphens GetHyphensOption() const final {
+ return mTextStyle->mHyphens;
+ }
+ mozilla::gfx::ShapedTextFlags GetShapedTextFlags() const final;
+
+ already_AddRefed<DrawTarget> GetDrawTarget() const final;
+
+ uint32_t GetAppUnitsPerDevUnit() const final {
+ return mTextRun->GetAppUnitsPerDevUnit();
+ }
+
+ void GetSpacingInternal(Range aRange, Spacing* aSpacing,
+ bool aIgnoreTabs) const;
+
+ /**
+ * Compute the justification information in given DOM range, return
+ * justification info and assignments if requested.
+ */
+ mozilla::JustificationInfo ComputeJustification(
+ Range aRange,
+ nsTArray<mozilla::JustificationAssignment>* aAssignments = nullptr);
+
+ const nsTextFrame* GetFrame() const { return mFrame; }
+ // This may not be equal to the frame offset/length in because we may have
+ // adjusted for whitespace trimming according to the state bits set in the
+ // frame (for the static provider)
+ const gfxSkipCharsIterator& GetStart() const { return mStart; }
+ // May return INT32_MAX if that was given to the constructor
+ uint32_t GetOriginalLength() const {
+ NS_ASSERTION(mLength != INT32_MAX, "Length not known");
+ return mLength;
+ }
+ const nsTextFragment* GetFragment() const { return mFrag; }
+
+ gfxFontGroup* GetFontGroup() const {
+ if (!mFontGroup) {
+ mFontGroup = GetFontMetrics()->GetThebesFontGroup();
+ }
+ return mFontGroup;
+ }
+
+ nsFontMetrics* GetFontMetrics() const {
+ if (!mFontMetrics) {
+ InitFontGroupAndFontMetrics();
+ }
+ return mFontMetrics;
+ }
+
+ void CalcTabWidths(Range aTransformedRange, gfxFloat aTabWidth) const;
+
+ gfxFloat MinTabAdvance() const;
+
+ const gfxSkipCharsIterator& GetEndHint() const { return mTempIterator; }
+
+ protected:
+ void SetupJustificationSpacing(bool aPostReflow);
+
+ void InitFontGroupAndFontMetrics() const;
+
+ const RefPtr<gfxTextRun> mTextRun;
+ mutable gfxFontGroup* mFontGroup;
+ mutable RefPtr<nsFontMetrics> mFontMetrics;
+ const nsStyleText* mTextStyle;
+ const nsTextFragment* mFrag;
+ const nsIFrame* mLineContainer;
+ nsTextFrame* mFrame;
+ gfxSkipCharsIterator mStart; // Offset in original and transformed string
+ const gfxSkipCharsIterator mTempIterator;
+
+ // Either null, or pointing to the frame's TabWidthProperty.
+ mutable nsTextFrame::TabWidthStore* mTabWidths;
+ // How far we've done tab-width calculation; this is ONLY valid when
+ // mTabWidths is nullptr (otherwise rely on mTabWidths->mLimit instead).
+ // It's a DOM offset relative to the current frame's offset.
+ mutable uint32_t mTabWidthsAnalyzedLimit;
+
+ int32_t mLength; // DOM string length, may be INT32_MAX
+ const gfxFloat mWordSpacing; // space for each whitespace char
+ const gfxFloat mLetterSpacing; // space for each letter
+ mutable gfxFloat mMinTabAdvance; // min advance for <tab> char
+ mutable gfxFloat mHyphenWidth;
+ mutable gfxFloat mOffsetFromBlockOriginForTabs;
+
+ // The values in mJustificationSpacings corresponds to unskipped
+ // characters start from mJustificationArrayStart.
+ uint32_t mJustificationArrayStart;
+ nsTArray<Spacing> mJustificationSpacings;
+
+ const bool mReflowing;
+ const nsTextFrame::TextRunType mWhichTextRun;
+ };
+
+ explicit nsTextFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
+ ClassID aID = kClassID)
+ : nsIFrame(aStyle, aPresContext, aID) {}
+
+ NS_DECL_FRAMEARENA_HELPERS(nsTextFrame)
+
+ friend class nsContinuingTextFrame;
+
+ // nsQueryFrame
+ NS_DECL_QUERYFRAME
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContinuationsProperty,
+ nsTArray<nsTextFrame*>)
+
+ // nsIFrame
+ void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists) final;
+
+ void Init(nsIContent* aContent, nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) override;
+
+ void Destroy(DestroyContext&) override;
+
+ Cursor GetCursor(const nsPoint&) final;
+
+ nsresult CharacterDataChanged(const CharacterDataChangeInfo&) final;
+
+ nsTextFrame* FirstContinuation() const override {
+ return const_cast<nsTextFrame*>(this);
+ }
+ nsTextFrame* GetPrevContinuation() const override { return nullptr; }
+ nsTextFrame* GetNextContinuation() const final { return mNextContinuation; }
+ void SetNextContinuation(nsIFrame* aNextContinuation) final {
+ NS_ASSERTION(!aNextContinuation || Type() == aNextContinuation->Type(),
+ "setting a next continuation with incorrect type!");
+ NS_ASSERTION(
+ !nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this),
+ "creating a loop in continuation chain!");
+ mNextContinuation = static_cast<nsTextFrame*>(aNextContinuation);
+ if (aNextContinuation)
+ aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+ // Setting a non-fluid continuation might affect our flow length (they're
+ // quite rare so we assume it always does) so we delete our cached value:
+ if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
+ GetContent()->RemoveProperty(nsGkAtoms::flowlength);
+ GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
+ }
+ }
+ nsTextFrame* GetNextInFlow() const final {
+ return mNextContinuation && mNextContinuation->HasAnyStateBits(
+ NS_FRAME_IS_FLUID_CONTINUATION)
+ ? mNextContinuation
+ : nullptr;
+ }
+ void SetNextInFlow(nsIFrame* aNextInFlow) final {
+ NS_ASSERTION(!aNextInFlow || Type() == aNextInFlow->Type(),
+ "setting a next in flow with incorrect type!");
+ NS_ASSERTION(
+ !nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this),
+ "creating a loop in continuation chain!");
+ mNextContinuation = static_cast<nsTextFrame*>(aNextInFlow);
+ if (mNextContinuation &&
+ !mNextContinuation->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) {
+ // Changing from non-fluid to fluid continuation might affect our flow
+ // length, so we delete our cached value:
+ if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
+ GetContent()->RemoveProperty(nsGkAtoms::flowlength);
+ GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
+ }
+ }
+ if (aNextInFlow) {
+ aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
+ }
+ }
+ nsTextFrame* LastInFlow() const final;
+ nsTextFrame* LastContinuation() const final;
+
+ bool ShouldSuppressLineBreak() const;
+
+ void InvalidateFrame(uint32_t aDisplayItemKey = 0,
+ bool aRebuildDisplayItems = true) final;
+ void InvalidateFrameWithRect(const nsRect& aRect,
+ uint32_t aDisplayItemKey = 0,
+ bool aRebuildDisplayItems = true) final;
+
+#ifdef DEBUG_FRAME_DUMP
+ void List(FILE* out = stderr, const char* aPrefix = "",
+ ListFlags aFlags = ListFlags()) const final;
+ nsresult GetFrameName(nsAString& aResult) const final;
+ void ToCString(nsCString& aBuf) const;
+ void ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const final;
+#endif
+
+ // Returns this text frame's content's text fragment.
+ //
+ // Assertions in Init() ensure we only ever get a Text node as content.
+ const nsTextFragment* TextFragment() const {
+ return &mContent->AsText()->TextFragment();
+ }
+
+ /**
+ * Check that the text in this frame is entirely whitespace. Importantly,
+ * this function considers non-breaking spaces (0xa0) to be whitespace,
+ * whereas nsTextFrame::IsEmpty does not. It also considers both one and
+ * two-byte chars.
+ */
+ bool IsEntirelyWhitespace() const;
+
+ ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) final;
+ ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint& aPoint);
+
+ /**
+ * This is called only on the primary text frame. It indicates that
+ * the selection state of the given character range has changed.
+ * Frames corresponding to the character range are unconditionally invalidated
+ * (Selection::Repaint depends on this).
+ * @param aStart start of character range.
+ * @param aEnd end (exclusive) of character range.
+ * @param aSelected true iff the character range is now selected.
+ * @param aType the type of the changed selection.
+ */
+ void SelectionStateChanged(uint32_t aStart, uint32_t aEnd, bool aSelected,
+ SelectionType aSelectionType);
+
+ FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) final;
+ FrameSearchResult PeekOffsetCharacter(
+ bool aForward, int32_t* aOffset,
+ PeekOffsetCharacterOptions aOptions = PeekOffsetCharacterOptions()) final;
+ FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace,
+ bool aIsKeyboardSelect, int32_t* aOffset,
+ PeekWordState* aState,
+ bool aTrimSpaces) final;
+
+ // Helper method that editor code uses to test for visibility.
+ [[nodiscard]] bool HasVisibleText();
+
+ // Flags for aSetLengthFlags
+ enum { ALLOW_FRAME_CREATION_AND_DESTRUCTION = 0x01 };
+
+ // Update offsets to account for new length. This may clear mTextRun.
+ void SetLength(int32_t aLength, nsLineLayout* aLineLayout,
+ uint32_t aSetLengthFlags = 0);
+
+ std::pair<int32_t, int32_t> GetOffsets() const final;
+
+ void AdjustOffsetsForBidi(int32_t start, int32_t end) final;
+
+ nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) final;
+ nsresult GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
+ nsTArray<nsRect>& aRects) final;
+
+ nsresult GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint,
+ int32_t* outFrameContentOffset,
+ nsIFrame** outChildFrame) final;
+
+ bool IsEmpty() final;
+ bool IsSelfEmpty() final { return IsEmpty(); }
+ Maybe<nscoord> GetNaturalBaselineBOffset(
+ mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup,
+ BaselineExportContext) const override;
+
+ bool HasSignificantTerminalNewline() const final;
+
+ /**
+ * Returns true if this text frame is logically adjacent to the end of the
+ * line.
+ */
+ bool IsAtEndOfLine() const;
+
+ /**
+ * Call this only after reflow the frame. Returns true if non-collapsed
+ * characters are present.
+ */
+ bool HasNoncollapsedCharacters() const {
+ return HasAnyStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
+ }
+
+#ifdef ACCESSIBILITY
+ mozilla::a11y::AccType AccessibleType() final;
+#endif
+
+ float GetFontSizeInflation() const;
+ bool IsCurrentFontInflation(float aInflation) const;
+ bool HasFontSizeInflation() const {
+ return HasAnyStateBits(TEXT_HAS_FONT_INFLATION);
+ }
+ void SetFontSizeInflation(float aInflation);
+
+ void MarkIntrinsicISizesDirty() final;
+ nscoord GetMinISize(gfxContext* aRenderingContext) final;
+ nscoord GetPrefISize(gfxContext* aRenderingContext) final;
+ void AddInlineMinISize(gfxContext* aRenderingContext,
+ InlineMinISizeData* aData) override;
+ void AddInlinePrefISize(gfxContext* aRenderingContext,
+ InlinePrefISizeData* aData) override;
+ SizeComputationResult ComputeSize(
+ gfxContext* aRenderingContext, mozilla::WritingMode aWM,
+ const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
+ const mozilla::LogicalSize& aMargin,
+ const mozilla::LogicalSize& aBorderPadding,
+ const mozilla::StyleSizeOverrides& aSizeOverrides,
+ mozilla::ComputeSizeFlags aFlags) final;
+ nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const final;
+ nsresult GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
+ nscoord* aXMost) final;
+ void Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
+ const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final;
+ bool CanContinueTextRun() const final;
+ // Method that is called for a text frame that is logically
+ // adjacent to the end of the line (i.e. followed only by empty text frames,
+ // placeholders or inlines containing such).
+ struct TrimOutput {
+ // true if we trimmed some space or changed metrics in some other way.
+ // In this case, we should call RecomputeOverflow on this frame.
+ bool mChanged;
+ // an amount to *subtract* from the frame's width (zero if !mChanged)
+ nscoord mDeltaWidth;
+ };
+ TrimOutput TrimTrailingWhiteSpace(DrawTarget* aDrawTarget);
+ RenderedText GetRenderedText(
+ uint32_t aStartOffset = 0, uint32_t aEndOffset = UINT32_MAX,
+ TextOffsetType aOffsetType = TextOffsetType::OffsetsInContentText,
+ TrailingWhitespace aTrimTrailingWhitespace =
+ TrailingWhitespace::Trim) final;
+
+ mozilla::OverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame,
+ bool aIncludeShadows = true);
+
+ enum TextRunType : uint8_t {
+ // Anything in reflow (but not intrinsic width calculation) or
+ // painting should use the inflated text run (i.e., with font size
+ // inflation applied).
+ eInflated,
+ // Intrinsic width calculation should use the non-inflated text run.
+ // When there is font size inflation, it will be different.
+ eNotInflated
+ };
+
+ void AddInlineMinISizeForFlow(gfxContext* aRenderingContext,
+ nsIFrame::InlineMinISizeData* aData,
+ TextRunType aTextRunType);
+ void AddInlinePrefISizeForFlow(gfxContext* aRenderingContext,
+ InlinePrefISizeData* aData,
+ TextRunType aTextRunType);
+
+ /**
+ * Calculate the horizontal bounds of the grapheme clusters that fit entirely
+ * inside the given left[top]/right[bottom] edges (which are positive lengths
+ * from the respective frame edge). If an input value is zero it is ignored
+ * and the result for that edge is zero. All out parameter values are
+ * undefined when the method returns false.
+ * @return true if at least one whole grapheme cluster fit between the edges
+ */
+ bool MeasureCharClippedText(nscoord aVisIStartEdge, nscoord aVisIEndEdge,
+ nscoord* aSnappedStartEdge,
+ nscoord* aSnappedEndEdge);
+ /**
+ * Same as above; this method also the returns the corresponding text run
+ * offset and number of characters that fit. All out parameter values are
+ * undefined when the method returns false.
+ * @return true if at least one whole grapheme cluster fit between the edges
+ */
+ bool MeasureCharClippedText(PropertyProvider& aProvider,
+ nscoord aVisIStartEdge, nscoord aVisIEndEdge,
+ uint32_t* aStartOffset, uint32_t* aMaxLength,
+ nscoord* aSnappedStartEdge,
+ nscoord* aSnappedEndEdge);
+
+ /**
+ * Return true if this box has some text to display.
+ * It returns false if at least one of these conditions are met:
+ * a. the frame hasn't been reflowed yet
+ * b. GetContentLength() == 0
+ * c. it contains only non-significant white-space
+ */
+ bool HasNonSuppressedText() const;
+
+ /**
+ * Object with various callbacks for PaintText() to invoke for different parts
+ * of the frame's text rendering, when we're generating paths rather than
+ * painting.
+ *
+ * Callbacks are invoked in the following order:
+ *
+ * NotifySelectionBackgroundNeedsFill?
+ * PaintDecorationLine*
+ * NotifyBeforeText
+ * NotifyGlyphPathEmitted*
+ * NotifyAfterText
+ * PaintDecorationLine*
+ * PaintSelectionDecorationLine*
+ *
+ * The color of each part of the frame's text rendering is passed as an
+ * argument to the NotifyBefore* callback for that part. The nscolor can take
+ * on one of the three selection special colors defined in LookAndFeel.h --
+ * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and
+ * NS_40PERCENT_FOREGROUND_COLOR.
+ */
+ struct DrawPathCallbacks : gfxTextRunDrawCallbacks {
+ /**
+ * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
+ */
+ explicit DrawPathCallbacks(bool aShouldPaintSVGGlyphs = false)
+ : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs) {}
+
+ /**
+ * Called to have the selection highlight drawn before the text is drawn
+ * over the top.
+ */
+ virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
+ nscolor aColor,
+ DrawTarget& aDrawTarget) {}
+
+ /**
+ * Called before (for under/over-line) or after (for line-through) the text
+ * is drawn to have a text decoration line drawn.
+ */
+ virtual void PaintDecorationLine(Rect aPath, nscolor aColor) {}
+
+ /**
+ * Called after selected text is drawn to have a decoration line drawn over
+ * the text. (All types of text decoration are drawn after the text when
+ * text is selected.)
+ */
+ virtual void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) {}
+
+ /**
+ * Called just before any paths have been emitted to the gfxContext
+ * for the glyphs of the frame's text.
+ */
+ virtual void NotifyBeforeText(nscolor aColor) {}
+
+ /**
+ * Called just after all the paths have been emitted to the gfxContext
+ * for the glyphs of the frame's text.
+ */
+ virtual void NotifyAfterText() {}
+
+ /**
+ * Called just before a path corresponding to a selection decoration line
+ * has been emitted to the gfxContext.
+ */
+ virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor) {}
+
+ /**
+ * Called just after a path corresponding to a selection decoration line
+ * has been emitted to the gfxContext.
+ */
+ virtual void NotifySelectionDecorationLinePathEmitted() {}
+ };
+
+ struct MOZ_STACK_CLASS PaintTextParams {
+ gfxContext* context;
+ Point framePt;
+ LayoutDeviceRect dirtyRect;
+ mozilla::SVGContextPaint* contextPaint = nullptr;
+ DrawPathCallbacks* callbacks = nullptr;
+ enum {
+ PaintText, // Normal text painting.
+ GenerateTextMask // To generate a mask from a text frame. Should
+ // only paint text itself with opaque color.
+ // Text shadow, text selection color and text
+ // decoration are all discarded in this state.
+ };
+ uint8_t state = PaintText;
+ explicit PaintTextParams(gfxContext* aContext) : context(aContext) {}
+
+ bool IsPaintText() const { return state == PaintText; }
+ bool IsGenerateTextMask() const { return state == GenerateTextMask; }
+ };
+
+ struct PaintTextSelectionParams;
+ struct DrawTextRunParams;
+ struct DrawTextParams;
+ struct ClipEdges;
+ struct PaintShadowParams;
+ struct PaintDecorationLineParams;
+
+ struct PriorityOrderedSelectionsForRange {
+ /// List of Selection Details active for the given range.
+ /// Ordered by priority, i.e. the last element has the highest priority.
+ nsTArray<const SelectionDetails*> mSelectionRanges;
+ Range mRange;
+ };
+
+ // Primary frame paint method called from nsDisplayText. Can also be used
+ // to generate paths rather than paint the frame's text by passing a callback
+ // object. The private DrawText() is what applies the text to a graphics
+ // context.
+ void PaintText(const PaintTextParams& aParams, const nscoord aVisIStartEdge,
+ const nscoord aVisIEndEdge, const nsPoint& aToReferenceFrame,
+ const bool aIsSelected, float aOpacity = 1.0f);
+ // helper: paint text frame when we're impacted by at least one selection.
+ // Return false if the text was not painted and we should continue with
+ // the fast path.
+ bool PaintTextWithSelection(const PaintTextSelectionParams& aParams,
+ const ClipEdges& aClipEdges);
+ // helper: paint text with foreground and background colors determined
+ // by selection(s). Also computes a mask of all selection types applying to
+ // our text, returned in aAllSelectionTypeMask.
+ // Return false if the text was not painted and we should continue with
+ // the fast path.
+ bool PaintTextWithSelectionColors(
+ const PaintTextSelectionParams& aParams,
+ const mozilla::UniquePtr<SelectionDetails>& aDetails,
+ SelectionTypeMask* aAllSelectionTypeMask, const ClipEdges& aClipEdges);
+ // helper: paint text decorations for text selected by aSelectionType
+ void PaintTextSelectionDecorations(
+ const PaintTextSelectionParams& aParams,
+ const mozilla::UniquePtr<SelectionDetails>& aDetails,
+ SelectionType aSelectionType);
+
+ SelectionTypeMask ResolveSelections(
+ const PaintTextSelectionParams& aParams, const SelectionDetails* aDetails,
+ nsTArray<PriorityOrderedSelectionsForRange>& aResult,
+ SelectionType aSelectionType, bool* aAnyBackgrounds = nullptr) const;
+
+ void DrawEmphasisMarks(gfxContext* aContext, mozilla::WritingMode aWM,
+ const mozilla::gfx::Point& aTextBaselinePt,
+ const mozilla::gfx::Point& aFramePt, Range aRange,
+ const nscolor* aDecorationOverrideColor,
+ PropertyProvider* aProvider);
+
+ nscolor GetCaretColorAt(int32_t aOffset) final;
+
+ // @param aSelectionFlags may be multiple of nsISelectionDisplay::DISPLAY_*.
+ // @return nsISelectionController.idl's `getDisplaySelection`.
+ int16_t GetSelectionStatus(int16_t* aSelectionFlags);
+
+ int32_t GetContentOffset() const { return mContentOffset; }
+ int32_t GetContentLength() const {
+ NS_ASSERTION(GetContentEnd() - mContentOffset >= 0, "negative length");
+ return GetContentEnd() - mContentOffset;
+ }
+ int32_t GetContentEnd() const;
+ // This returns the length the frame thinks it *should* have after it was
+ // last reflowed (0 if it hasn't been reflowed yet). This should be used only
+ // when setting up the text offsets for a new continuation frame.
+ int32_t GetContentLengthHint() const { return mContentLengthHint; }
+
+ // Compute the length of the content mapped by this frame
+ // and all its in-flow siblings. Basically this means starting at
+ // mContentOffset and going to the end of the text node or the next bidi
+ // continuation boundary.
+ int32_t GetInFlowContentLength();
+
+ /**
+ * Acquires the text run for this content, if necessary.
+ * @param aWhichTextRun indicates whether to get an inflated or non-inflated
+ * text run
+ * @param aRefDrawTarget the DrawTarget to use as a reference for creating the
+ * textrun, if available (if not, we'll create one which will just be slower)
+ * @param aLineContainer the block ancestor for this frame, or nullptr if
+ * unknown
+ * @param aFlowEndInTextRun if non-null, this returns the textrun offset of
+ * end of the text associated with this frame and its in-flow siblings
+ * @return a gfxSkipCharsIterator set up to map DOM offsets for this frame
+ * to offsets into the textrun; its initial offset is set to this frame's
+ * content offset
+ */
+ gfxSkipCharsIterator EnsureTextRun(TextRunType aWhichTextRun,
+ DrawTarget* aRefDrawTarget = nullptr,
+ nsIFrame* aLineContainer = nullptr,
+ const nsLineList_iterator* aLine = nullptr,
+ uint32_t* aFlowEndInTextRun = nullptr);
+
+ gfxTextRun* GetTextRun(TextRunType aWhichTextRun) const {
+ if (aWhichTextRun == eInflated || !HasFontSizeInflation()) return mTextRun;
+ return GetUninflatedTextRun();
+ }
+ gfxTextRun* GetUninflatedTextRun() const;
+ void SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
+ float aInflation);
+ bool IsInTextRunUserData() const {
+ return HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA |
+ TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA);
+ }
+ /**
+ * Notify the frame that it should drop its pointer to a text run.
+ * Returns whether the text run was removed (i.e., whether it was
+ * associated with this frame, either as its inflated or non-inflated
+ * text run.
+ */
+ bool RemoveTextRun(gfxTextRun* aTextRun);
+ /**
+ * Clears out |mTextRun| (or the uninflated text run, when aInflated
+ * is nsTextFrame::eNotInflated and there is inflation) from all frames that
+ * hold a reference to it, starting at |aStartContinuation|, or if it's
+ * nullptr, starting at |this|. Deletes the text run if all references
+ * were cleared and it's not cached.
+ */
+ void ClearTextRun(nsTextFrame* aStartContinuation, TextRunType aWhichTextRun);
+
+ void ClearTextRuns() {
+ ClearTextRun(nullptr, nsTextFrame::eInflated);
+ if (HasFontSizeInflation()) {
+ ClearTextRun(nullptr, nsTextFrame::eNotInflated);
+ }
+ }
+
+ /**
+ * Wipe out references to textrun(s) without deleting the textruns.
+ */
+ void DisconnectTextRuns();
+
+ // Get the DOM content range mapped by this frame after excluding
+ // whitespace subject to start-of-line and end-of-line trimming.
+ // The textrun must have been created before calling this.
+ struct TrimmedOffsets {
+ int32_t mStart;
+ int32_t mLength;
+ int32_t GetEnd() const { return mStart + mLength; }
+ };
+ enum class TrimmedOffsetFlags : uint8_t {
+ Default = 0,
+ NotPostReflow = 1 << 0,
+ NoTrimAfter = 1 << 1,
+ NoTrimBefore = 1 << 2
+ };
+ TrimmedOffsets GetTrimmedOffsets(
+ const nsTextFragment* aFrag,
+ TrimmedOffsetFlags aFlags = TrimmedOffsetFlags::Default) const;
+
+ // Similar to Reflow(), but for use from nsLineLayout
+ void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
+ DrawTarget* aDrawTarget, ReflowOutput& aMetrics,
+ nsReflowStatus& aStatus);
+
+ nscoord ComputeLineHeight() const;
+
+ bool IsFloatingFirstLetterChild() const;
+
+ bool IsInitialLetterChild() const;
+
+ bool ComputeCustomOverflow(mozilla::OverflowAreas& aOverflowAreas) final;
+ bool ComputeCustomOverflowInternal(mozilla::OverflowAreas& aOverflowAreas,
+ bool aIncludeShadows);
+
+ void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
+ mozilla::JustificationAssignment GetJustificationAssignment() const;
+
+ uint32_t CountGraphemeClusters() const;
+
+ bool HasAnyNoncollapsedCharacters() final;
+
+ /**
+ * Call this after you have manually changed the text node contents without
+ * notifying that change. This behaves as if all the text contents changed.
+ * (You should only use this for native anonymous content.)
+ */
+ void NotifyNativeAnonymousTextnodeChange(uint32_t aOldLength);
+
+ nsFontMetrics* InflatedFontMetrics() const;
+
+ nsRect WebRenderBounds();
+
+ // Find the continuation (which may be this frame itself) containing the
+ // given offset. Note that this may return null, if the offset is beyond the
+ // text covered by the continuation chain.
+ // (To be used only on the first textframe in the chain.)
+ nsTextFrame* FindContinuationForOffset(int32_t aOffset);
+
+ void SetHangableISize(nscoord aISize);
+ nscoord GetHangableISize() const;
+ void ClearHangableISize();
+
+ void SetTrimmableWS(gfxTextRun::TrimmableWS aTrimmableWS);
+ gfxTextRun::TrimmableWS GetTrimmableWS() const;
+ void ClearTrimmableWS();
+
+ protected:
+ virtual ~nsTextFrame();
+
+ friend class mozilla::nsDisplayTextGeometry;
+ friend class mozilla::nsDisplayText;
+
+ mutable RefPtr<nsFontMetrics> mFontMetrics;
+ RefPtr<gfxTextRun> mTextRun;
+ nsTextFrame* mNextContinuation = nullptr;
+ // The key invariant here is that mContentOffset never decreases along
+ // a next-continuation chain. And of course mContentOffset is always <= the
+ // the text node's content length, and the mContentOffset for the first frame
+ // is always 0. Furthermore the text mapped by a frame is determined by
+ // GetContentOffset() and GetContentLength()/GetContentEnd(), which get
+ // the length from the difference between this frame's offset and the next
+ // frame's offset, or the text length if there is no next frame. This means
+ // the frames always map the text node without overlapping or leaving any
+ // gaps.
+ int32_t mContentOffset = 0;
+ // This does *not* indicate the length of text currently mapped by the frame;
+ // instead it's a hint saying that this frame *wants* to map this much text
+ // so if we create a new continuation, this is where that continuation should
+ // start.
+ int32_t mContentLengthHint = 0;
+ nscoord mAscent = 0;
+
+ // Cached selection state.
+ enum class SelectionState : uint8_t {
+ Unknown,
+ Selected,
+ NotSelected,
+ };
+ mutable SelectionState mIsSelected = SelectionState::Unknown;
+
+ // Flags used to track whether certain properties are present.
+ // (Public to keep MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS happy.)
+ public:
+ enum class PropertyFlags : uint8_t {
+ // Whether a cached continuations array is present.
+ Continuations = 1 << 0,
+ // Whether a HangableWhitespace property is present.
+ HangableWS = 1 << 1,
+ // Whether a TrimmableWhitespace property is present.
+ TrimmableWS = 2 << 1,
+ };
+
+ protected:
+ PropertyFlags mPropertyFlags = PropertyFlags(0);
+
+ /**
+ * Return true if the frame is part of a Selection.
+ * Helper method to implement the public IsSelected() API.
+ */
+ bool IsFrameSelected() const final;
+
+ void InvalidateSelectionState() { mIsSelected = SelectionState::Unknown; }
+
+ mozilla::UniquePtr<SelectionDetails> GetSelectionDetails();
+
+ void UnionAdditionalOverflow(nsPresContext* aPresContext, nsIFrame* aBlock,
+ PropertyProvider& aProvider,
+ nsRect* aInkOverflowRect,
+ bool aIncludeTextDecorations,
+ bool aIncludeShadows);
+
+ // Update information of emphasis marks, and return the visial
+ // overflow rect of the emphasis marks.
+ nsRect UpdateTextEmphasis(mozilla::WritingMode aWM,
+ PropertyProvider& aProvider);
+
+ void PaintOneShadow(const PaintShadowParams& aParams,
+ const mozilla::StyleSimpleShadow& aShadowDetails,
+ gfxRect& aBoundingBox, uint32_t aBlurFlags);
+
+ void PaintShadows(mozilla::Span<const mozilla::StyleSimpleShadow>,
+ const PaintShadowParams& aParams);
+
+ struct LineDecoration {
+ nsIFrame* mFrame;
+
+ // This is represents the offset from our baseline to mFrame's baseline;
+ // positive offsets are *above* the baseline and negative offsets below
+ nscoord mBaselineOffset;
+
+ // This represents the offset from the initial position of the underline
+ const mozilla::LengthPercentageOrAuto mTextUnderlineOffset;
+
+ // for CSS property text-decoration-thickness, the width refers to the
+ // thickness of the decoration line
+ const mozilla::StyleTextDecorationLength mTextDecorationThickness;
+ nscolor mColor;
+ mozilla::StyleTextDecorationStyle mStyle;
+
+ // The text-underline-position property; affects the underline offset only
+ // if mTextUnderlineOffset is auto.
+ const mozilla::StyleTextUnderlinePosition mTextUnderlinePosition;
+
+ LineDecoration(nsIFrame* const aFrame, const nscoord aOff,
+ mozilla::StyleTextUnderlinePosition aUnderlinePosition,
+ const mozilla::LengthPercentageOrAuto& aUnderlineOffset,
+ const mozilla::StyleTextDecorationLength& aDecThickness,
+ const nscolor aColor,
+ const mozilla::StyleTextDecorationStyle aStyle)
+ : mFrame(aFrame),
+ mBaselineOffset(aOff),
+ mTextUnderlineOffset(aUnderlineOffset),
+ mTextDecorationThickness(aDecThickness),
+ mColor(aColor),
+ mStyle(aStyle),
+ mTextUnderlinePosition(aUnderlinePosition) {}
+
+ LineDecoration(const LineDecoration& aOther) = default;
+
+ bool operator==(const LineDecoration& aOther) const {
+ return mFrame == aOther.mFrame && mStyle == aOther.mStyle &&
+ mColor == aOther.mColor &&
+ mBaselineOffset == aOther.mBaselineOffset &&
+ mTextUnderlinePosition == aOther.mTextUnderlinePosition &&
+ mTextUnderlineOffset == aOther.mTextUnderlineOffset &&
+ mTextDecorationThickness == aOther.mTextDecorationThickness;
+ }
+
+ bool operator!=(const LineDecoration& aOther) const {
+ return !(*this == aOther);
+ }
+ };
+ struct TextDecorations {
+ AutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes;
+
+ TextDecorations() = default;
+
+ bool HasDecorationLines() const {
+ return HasUnderline() || HasOverline() || HasStrikeout();
+ }
+ bool HasUnderline() const { return !mUnderlines.IsEmpty(); }
+ bool HasOverline() const { return !mOverlines.IsEmpty(); }
+ bool HasStrikeout() const { return !mStrikes.IsEmpty(); }
+ bool operator==(const TextDecorations& aOther) const {
+ return mOverlines == aOther.mOverlines &&
+ mUnderlines == aOther.mUnderlines && mStrikes == aOther.mStrikes;
+ }
+ bool operator!=(const TextDecorations& aOther) const {
+ return !(*this == aOther);
+ }
+ };
+ enum TextDecorationColorResolution { eResolvedColors, eUnresolvedColors };
+ void GetTextDecorations(nsPresContext* aPresContext,
+ TextDecorationColorResolution aColorResolution,
+ TextDecorations& aDecorations);
+
+ void DrawTextRun(Range aRange, const mozilla::gfx::Point& aTextBaselinePt,
+ const DrawTextRunParams& aParams);
+
+ void DrawTextRunAndDecorations(Range aRange,
+ const mozilla::gfx::Point& aTextBaselinePt,
+ const DrawTextParams& aParams,
+ const TextDecorations& aDecorations);
+
+ void DrawText(Range aRange, const mozilla::gfx::Point& aTextBaselinePt,
+ const DrawTextParams& aParams);
+
+ // Set non empty rect to aRect, it should be overflow rect or frame rect.
+ // If the result rect is larger than the given rect, this returns true.
+ bool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
+ nsRect& aRect);
+
+ // This sets *aShadows to the appropriate shadows, if any, for the given
+ // type of selection.
+ // If text-shadow was not specified, *aShadows is left untouched.
+ // Note that the returned shadow(s) will only be valid as long as the
+ // textPaintStyle remains in scope.
+ void GetSelectionTextShadow(
+ SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle,
+ mozilla::Span<const mozilla::StyleSimpleShadow>* aShadows);
+
+ /**
+ * Utility methods to paint selection.
+ */
+ void DrawSelectionDecorations(
+ gfxContext* aContext, const LayoutDeviceRect& aDirtyRect,
+ mozilla::SelectionType aSelectionType, nsTextPaintStyle& aTextPaintStyle,
+ const TextRangeStyle& aRangeStyle, const Point& aPt,
+ gfxFloat aICoordInFrame, gfxFloat aWidth, gfxFloat aAscent,
+ const gfxFont::Metrics& aFontMetrics, DrawPathCallbacks* aCallbacks,
+ bool aVertical, mozilla::StyleTextDecorationLine aDecoration);
+
+ void PaintDecorationLine(const PaintDecorationLineParams& aParams);
+ /**
+ * ComputeDescentLimitForSelectionUnderline() computes the most far position
+ * where we can put selection underline.
+ *
+ * @return The maximum underline offset from the baseline (positive value
+ * means that the underline can put below the baseline).
+ */
+ gfxFloat ComputeDescentLimitForSelectionUnderline(
+ nsPresContext* aPresContext, const gfxFont::Metrics& aFontMetrics);
+ /**
+ * This function encapsulates all knowledge of how selections affect
+ * foreground and background colors.
+ * @param aForeground the foreground color to use
+ * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
+ * background should be painted
+ * @return true if the selection affects colors, false otherwise
+ */
+ static bool GetSelectionTextColors(SelectionType aSelectionType,
+ nsAtom* aHighlightName,
+ nsTextPaintStyle& aTextPaintStyle,
+ const TextRangeStyle& aRangeStyle,
+ nscolor* aForeground,
+ nscolor* aBackground);
+ /**
+ * ComputeSelectionUnderlineHeight() computes selection underline height of
+ * the specified selection type from the font metrics.
+ */
+ static gfxFloat ComputeSelectionUnderlineHeight(
+ nsPresContext* aPresContext, const gfxFont::Metrics& aFontMetrics,
+ SelectionType aSelectionType);
+
+ /**
+ * @brief Helper struct which contains selection data such as its details,
+ * range and priority.
+ */
+ struct SelectionRange {
+ const SelectionDetails* mDetails{nullptr};
+ gfxTextRun::Range mRange;
+ /// used to determine the order of overlapping selections of the same type.
+ uint32_t mPriority{0};
+ };
+ /**
+ * @brief Helper: Extracts a list of `SelectionRange` structs from given
+ * `SelectionDetails` and computes a priority for overlapping selection
+ * ranges.
+ */
+ static SelectionTypeMask CreateSelectionRangeList(
+ const SelectionDetails* aDetails, SelectionType aSelectionType,
+ const PaintTextSelectionParams& aParams,
+ nsTArray<SelectionRange>& aSelectionRanges, bool* aAnyBackgrounds);
+
+ /**
+ * @brief Creates an array of `CombinedSelectionRange`s from given list
+ * of `SelectionRange`s.
+ * Each instance of `CombinedSelectionRange` represents a piece of text with
+ * constant Selections.
+ *
+ * Example:
+ *
+ * Consider this text fragment, [] and () marking selection ranges:
+ * ab[cd(e]f)g
+ * This results in the following array of combined ranges:
+ * - [0]: range: (2, 4), selections: "[]"
+ * - [1]: range: (4, 5), selections: "[]", "()"
+ * - [2]: range: (5, 6), selections: "()"
+ * Depending on the priorities of the ranges, [1] may have a different order
+ * of its ranges. The given example indicates that "()" has a higher priority
+ * than "[]".
+ *
+ * @param aSelectionRanges Array of `SelectionRange` objects. Must be
+ * sorted by the start offset.
+ * @param aCombinedSelectionRanges Out parameter. Returns the constructed
+ * array of combined selection ranges.
+ */
+ static void CombineSelectionRanges(
+ const nsTArray<SelectionRange>& aSelectionRanges,
+ nsTArray<PriorityOrderedSelectionsForRange>& aCombinedSelectionRanges);
+
+ ContentOffsets GetCharacterOffsetAtFramePointInternal(
+ const nsPoint& aPoint, bool aForInsertionPoint);
+
+ static float GetTextCombineScaleFactor(nsTextFrame* aFrame);
+
+ void ClearFrameOffsetCache();
+
+ void ClearMetrics(ReflowOutput& aMetrics);
+
+ // Return pointer to an array of all frames in the continuation chain, or
+ // null if we're too short of memory.
+ nsTArray<nsTextFrame*>* GetContinuations();
+
+ // Clear any cached continuations array; this should be called whenever the
+ // chain is modified.
+ inline void ClearCachedContinuations();
+
+ /**
+ * UpdateIteratorFromOffset() updates the iterator from a given offset.
+ * Also, aInOffset may be updated to cluster start if aInOffset isn't
+ * the offset of cluster start.
+ */
+ void UpdateIteratorFromOffset(const PropertyProvider& aProperties,
+ int32_t& aInOffset,
+ gfxSkipCharsIterator& aIter);
+
+ nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter,
+ PropertyProvider& aProperties);
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::TrimmedOffsetFlags)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::PropertyFlags)
+
+inline void nsTextFrame::ClearCachedContinuations() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mPropertyFlags & PropertyFlags::Continuations) {
+ RemoveProperty(ContinuationsProperty());
+ mPropertyFlags &= ~PropertyFlags::Continuations;
+ }
+}
+
+#endif