diff options
Diffstat (limited to 'layout/generic/nsTextFrame.h')
-rw-r--r-- | layout/generic/nsTextFrame.h | 840 |
1 files changed, 840 insertions, 0 deletions
diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h new file mode 100644 index 000000000..425dbb737 --- /dev/null +++ b/layout/generic/nsTextFrame.h @@ -0,0 +1,840 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/EventForwards.h" +#include "mozilla/gfx/2D.h" +#include "nsFrame.h" +#include "nsSplittableFrame.h" +#include "nsLineBox.h" +#include "gfxSkipChars.h" +#include "gfxTextRun.h" +#include "nsDisplayList.h" +#include "JustificationUtils.h" +#include "RubyUtils.h" + +// Undo the windows.h damage +#if defined(XP_WIN) && defined(DrawText) +#undef DrawText +#endif + +class nsTextPaintStyle; +class PropertyProvider; +struct SelectionDetails; +class nsTextFragment; + +class nsDisplayTextGeometry; +class nsDisplayText; + +namespace mozilla { +class SVGContextPaint; +}; + +class nsTextFrame : public nsFrame { + typedef mozilla::LayoutDeviceRect LayoutDeviceRect; + typedef mozilla::RawSelectionType RawSelectionType; + 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: + NS_DECL_QUERYFRAME_TARGET(nsTextFrame) + NS_DECL_FRAMEARENA_HELPERS + + friend class nsContinuingTextFrame; + friend class nsDisplayTextGeometry; + friend class nsDisplayText; + + explicit nsTextFrame(nsStyleContext* aContext) + : nsFrame(aContext) + { + NS_ASSERTION(mContentOffset == 0, "Bogus content offset"); + } + + // nsQueryFrame + NS_DECL_QUERYFRAME + + // nsIFrame + virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) override; + + virtual void Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) override; + + virtual void DestroyFrom(nsIFrame* aDestructRoot) override; + + virtual nsresult GetCursor(const nsPoint& aPoint, + nsIFrame::Cursor& aCursor) override; + + virtual nsresult CharacterDataChanged(CharacterDataChangeInfo* aInfo) override; + + virtual nsIFrame* GetNextContinuation() const override { + return mNextContinuation; + } + virtual void SetNextContinuation(nsIFrame* aNextContinuation) override { + NS_ASSERTION (!aNextContinuation || GetType() == aNextContinuation->GetType(), + "setting a next continuation with incorrect type!"); + NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this), + "creating a loop in continuation chain!"); + mNextContinuation = 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: + GetContent()->DeleteProperty(nsGkAtoms::flowlength); + } + virtual nsIFrame* GetNextInFlowVirtual() const override { return GetNextInFlow(); } + nsIFrame* GetNextInFlow() const { + return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? + mNextContinuation : nullptr; + } + virtual void SetNextInFlow(nsIFrame* aNextInFlow) override { + NS_ASSERTION (!aNextInFlow || GetType() == aNextInFlow->GetType(), + "setting a next in flow with incorrect type!"); + NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this), + "creating a loop in continuation chain!"); + mNextContinuation = 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: + GetContent()->DeleteProperty(nsGkAtoms::flowlength); + } + if (aNextInFlow) { + aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); + } + } + virtual nsIFrame* LastInFlow() const override; + virtual nsIFrame* LastContinuation() const override; + + virtual nsSplittableType GetSplittableType() const override { + return NS_FRAME_SPLITTABLE; + } + + /** + * Get the "type" of the frame + * + * @see nsGkAtoms::textFrame + */ + virtual nsIAtom* GetType() const override; + + virtual bool IsFrameOfType(uint32_t aFlags) const override + { + // Set the frame state bit for text frames to mark them as replaced. + // XXX kipp: temporary + return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced | + nsIFrame::eLineParticipant)); + } + + bool ShouldSuppressLineBreak() const + { + // If the parent frame of the text frame is ruby content box, it must + // suppress line break inside. This check is necessary, because when + // a whitespace is only contained by pseudo ruby frames, its style + // context won't have SuppressLineBreak bit set. + if (mozilla::RubyUtils::IsRubyContentBox(GetParent()->GetType())) { + return true; + } + return StyleContext()->ShouldSuppressLineBreak(); + } + + virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) override; + virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override; + +#ifdef DEBUG_FRAME_DUMP + void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override; + virtual nsresult GetFrameName(nsAString& aResult) const override; + void ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const; +#endif + +#ifdef DEBUG + virtual nsFrameState GetDebugStateBits() const override; +#endif + + virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint) override; + 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. + * Text in the range is unconditionally invalidated + * (Selection::Repaint depends on this). + * @param aSelected true if the selection has been added to the range, + * false otherwise + * @param aType the type of selection added or removed + */ + void SetSelectedRange(uint32_t aStart, uint32_t aEnd, bool aSelected, + SelectionType aSelectionType); + + virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) override; + virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset, + bool aRespectClusters = true) override; + virtual FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, + int32_t* aOffset, PeekWordState* aState) override; + + virtual nsresult CheckVisibility(nsPresContext* aContext, int32_t aStartIndex, int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *_retval) override; + + // 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); + + virtual nsresult GetOffsets(int32_t &start, int32_t &end)const override; + + virtual void AdjustOffsetsForBidi(int32_t start, int32_t end) override; + + virtual nsresult GetPointFromOffset(int32_t inOffset, + nsPoint* outPoint) override; + virtual nsresult GetCharacterRectsInRange(int32_t aInOffset, + int32_t aLength, + nsTArray<nsRect>& aRects) override; + + virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset, + bool inHint, + int32_t* outFrameContentOffset, + nsIFrame** outChildFrame) override; + + virtual bool IsVisibleInSelection(nsISelection* aSelection) override; + + virtual bool IsEmpty() override; + virtual bool IsSelfEmpty() override { return IsEmpty(); } + nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const final; + + virtual bool HasSignificantTerminalNewline() const override; + + /** + * 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 (GetStateBits() & TEXT_HAS_NONCOLLAPSED_CHARACTERS) != 0; + } + +#ifdef ACCESSIBILITY + virtual mozilla::a11y::AccType AccessibleType() override; +#endif + + float GetFontSizeInflation() const; + bool IsCurrentFontInflation(float aInflation) const; + bool HasFontSizeInflation() const { + return (GetStateBits() & TEXT_HAS_FONT_INFLATION) != 0; + } + void SetFontSizeInflation(float aInflation); + + virtual void MarkIntrinsicISizesDirty() override; + virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; + virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; + virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext, + InlineMinISizeData *aData) override; + virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext, + InlinePrefISizeData *aData) override; + virtual mozilla::LogicalSize + ComputeSize(nsRenderingContext *aRenderingContext, + mozilla::WritingMode aWritingMode, + const mozilla::LogicalSize& aCBSize, + nscoord aAvailableISize, + const mozilla::LogicalSize& aMargin, + const mozilla::LogicalSize& aBorder, + const mozilla::LogicalSize& aPadding, + ComputeSizeFlags aFlags) override; + virtual nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override; + virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext, + nscoord* aX, + nscoord* aXMost) override; + virtual void Reflow(nsPresContext* aPresContext, + ReflowOutput& aMetrics, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; + virtual bool CanContinueTextRun() const override; + // 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); + virtual RenderedText GetRenderedText(uint32_t aStartOffset = 0, + uint32_t aEndOffset = UINT32_MAX, + TextOffsetType aOffsetType = + TextOffsetType::OFFSETS_IN_CONTENT_TEXT, + TrailingWhitespace aTrimTrailingWhitespace = + TrailingWhitespace::TRIM_TRAILING_WHITESPACE) override; + + nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame); + + enum TextRunType { + // 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(nsRenderingContext *aRenderingContext, + nsIFrame::InlineMinISizeData *aData, + TextRunType aTextRunType); + void AddInlinePrefISizeForFlow(nsRenderingContext *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); + + /** + * 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 PaintTextParams + { + gfxContext* context; + gfxPoint framePt; + LayoutDeviceRect dirtyRect; + mozilla::SVGContextPaint* contextPaint = nullptr; + DrawPathCallbacks* callbacks = nullptr; + enum { + PaintText, // Normal text painting. + PaintTextBGColor, // Only paint background color of the selected text + // range in this state. + 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; } + bool IsPaintBGColor() const { return state == PaintTextBGColor; } + }; + + struct PaintTextSelectionParams : PaintTextParams + { + gfxPoint textBaselinePt; + PropertyProvider* provider = nullptr; + Range contentRange; + nsTextPaintStyle* textPaintStyle = nullptr; + explicit PaintTextSelectionParams(const PaintTextParams& aParams) + : PaintTextParams(aParams) {} + }; + + struct DrawTextRunParams + { + gfxContext* context; + PropertyProvider* provider = nullptr; + gfxFloat* advanceWidth = nullptr; + mozilla::SVGContextPaint* contextPaint = nullptr; + DrawPathCallbacks* callbacks = nullptr; + nscolor textColor = NS_RGBA(0, 0, 0, 0); + nscolor textStrokeColor = NS_RGBA(0, 0, 0, 0); + float textStrokeWidth = 0.0f; + bool drawSoftHyphen = false; + explicit DrawTextRunParams(gfxContext* aContext) + : context(aContext) {} + }; + + struct DrawTextParams : DrawTextRunParams + { + gfxPoint framePt; + LayoutDeviceRect dirtyRect; + const nsTextPaintStyle* textStyle = nullptr; + const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr; + const nscolor* decorationOverrideColor = nullptr; + explicit DrawTextParams(gfxContext* aContext) + : DrawTextRunParams(aContext) {} + }; + + // 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 nsCharClipDisplayItem& aItem, + 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 nsCharClipDisplayItem::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 aAllTypes. + // Return false if the text was not painted and we should continue with + // the fast path. + bool PaintTextWithSelectionColors( + const PaintTextSelectionParams& aParams, + SelectionDetails* aDetails, + RawSelectionType* aAllRawSelectionTypes, + const nsCharClipDisplayItem::ClipEdges& aClipEdges); + // helper: paint text decorations for text selected by aSelectionType + void PaintTextSelectionDecorations(const PaintTextSelectionParams& aParams, + SelectionDetails* aDetails, + SelectionType aSelectionType); + + void DrawEmphasisMarks(gfxContext* aContext, + mozilla::WritingMode aWM, + const gfxPoint& aTextBaselinePt, + const gfxPoint& aFramePt, Range aRange, + const nscolor* aDecorationOverrideColor, + PropertyProvider* aProvider); + + virtual nscolor GetCaretColorAt(int32_t aOffset) override; + + 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) { + if (aWhichTextRun == eInflated || !HasFontSizeInflation()) + return mTextRun; + return GetUninflatedTextRun(); + } + gfxTextRun* GetUninflatedTextRun(); + void SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun, + float aInflation); + bool IsInTextRunUserData() const { + return GetStateBits() & + (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; } + }; + TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag, + bool aTrimAfter, bool aPostReflow = true); + + // Similar to Reflow(), but for use from nsLineLayout + void ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, + DrawTarget* aDrawTarget, + ReflowOutput& aMetrics, nsReflowStatus& aStatus); + + bool IsFloatingFirstLetterChild() const; + + bool IsInitialLetterChild() const; + + virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override; + + void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign); + mozilla::JustificationAssignment GetJustificationAssignment() const; + + uint32_t CountGraphemeClusters() const; + +protected: + virtual ~nsTextFrame(); + + RefPtr<gfxTextRun> mTextRun; + nsIFrame* mNextContinuation; + // 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; + // 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; + nscoord mAscent; + + /** + * Return true if the frame is part of a Selection. + * Helper method to implement the public IsSelected() API. + */ + virtual bool IsFrameSelected() const override; + + // The caller of this method must call DestroySelectionDetails() on the + // return value, if that return value is not null. Calling + // DestroySelectionDetails() on a null value is still OK, just not necessary. + SelectionDetails* GetSelectionDetails(); + + void UnionAdditionalOverflow(nsPresContext* aPresContext, + nsIFrame* aBlock, + PropertyProvider& aProvider, + nsRect* aVisualOverflowRect, + bool aIncludeTextDecorations); + + // Update information of emphasis marks, and return the visial + // overflow rect of the emphasis marks. + nsRect UpdateTextEmphasis(mozilla::WritingMode aWM, + PropertyProvider& aProvider); + + struct PaintShadowParams + { + gfxTextRun::Range range; + LayoutDeviceRect dirtyRect; + gfxPoint framePt; + gfxPoint textBaselinePt; + gfxContext* context; + nscolor foregroundColor = NS_RGBA(0, 0, 0, 0); + const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr; + PropertyProvider* provider = nullptr; + nscoord leftSideOffset = 0; + explicit PaintShadowParams(const PaintTextParams& aParams) + : dirtyRect(aParams.dirtyRect) + , framePt(aParams.framePt) + , context(aParams.context) {} + }; + + void PaintOneShadow(const PaintShadowParams& aParams, + nsCSSShadowItem* aShadowDetails, + gfxRect& aBoundingBox, + uint32_t aBlurFlags); + + void PaintShadows(nsCSSShadowArray* aShadow, + 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; + + nscolor mColor; + uint8_t mStyle; + + LineDecoration(nsIFrame *const aFrame, + const nscoord aOff, + const nscolor aColor, + const uint8_t aStyle) + : mFrame(aFrame), + mBaselineOffset(aOff), + mColor(aColor), + mStyle(aStyle) + {} + + LineDecoration(const LineDecoration& aOther) + : mFrame(aOther.mFrame), + mBaselineOffset(aOther.mBaselineOffset), + mColor(aOther.mColor), + mStyle(aOther.mStyle) + {} + + bool operator==(const LineDecoration& aOther) const { + return mFrame == aOther.mFrame && + mStyle == aOther.mStyle && + mColor == aOther.mColor && + mBaselineOffset == aOther.mBaselineOffset; + } + + bool operator!=(const LineDecoration& aOther) const { + return !(*this == aOther); + } + }; + struct TextDecorations { + AutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes; + + TextDecorations() { } + + 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 gfxPoint& aTextBaselinePt, + const DrawTextRunParams& aParams); + + void DrawTextRunAndDecorations(Range aRange, const gfxPoint& aTextBaselinePt, + const DrawTextParams& aParams, + const TextDecorations& aDecorations); + + void DrawText(Range aRange, const gfxPoint& 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); + + /** + * 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, + gfxFloat aDecorationOffsetDir, + uint8_t aDecoration); + + struct PaintDecorationLineParams; + 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, + 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); + + ContentOffsets GetCharacterOffsetAtFramePointInternal(nsPoint aPoint, + bool aForInsertionPoint); + + void ClearFrameOffsetCache(); + + virtual bool HasAnyNoncollapsedCharacters() override; + + void ClearMetrics(ReflowOutput& aMetrics); + + /** + * 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); +}; + +#endif |