/src/mozilla-central/layout/generic/nsTextFrame.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #ifndef nsTextFrame_h__ |
8 | | #define nsTextFrame_h__ |
9 | | |
10 | | #include "mozilla/Attributes.h" |
11 | | #include "mozilla/EventForwards.h" |
12 | | #include "mozilla/gfx/2D.h" |
13 | | #include "mozilla/UniquePtr.h" |
14 | | #include "mozilla/dom/CharacterData.h" |
15 | | #include "nsFrame.h" |
16 | | #include "nsFrameSelection.h" |
17 | | #include "nsSplittableFrame.h" |
18 | | #include "nsLineBox.h" |
19 | | #include "gfxSkipChars.h" |
20 | | #include "gfxTextRun.h" |
21 | | #include "nsDisplayList.h" |
22 | | #include "JustificationUtils.h" |
23 | | #include "RubyUtils.h" |
24 | | |
25 | | // Undo the windows.h damage |
26 | | #if defined(XP_WIN) && defined(DrawText) |
27 | | #undef DrawText |
28 | | #endif |
29 | | |
30 | | class nsTextPaintStyle; |
31 | | class PropertyProvider; |
32 | | struct SelectionDetails; |
33 | | class nsTextFragment; |
34 | | |
35 | | class nsDisplayTextGeometry; |
36 | | class nsDisplayText; |
37 | | |
38 | | namespace mozilla { |
39 | | class SVGContextPaint; |
40 | | }; |
41 | | |
42 | | class nsTextFrame : public nsFrame |
43 | | { |
44 | | typedef mozilla::LayoutDeviceRect LayoutDeviceRect; |
45 | | typedef mozilla::SelectionTypeMask SelectionTypeMask; |
46 | | typedef mozilla::SelectionType SelectionType; |
47 | | typedef mozilla::TextRangeStyle TextRangeStyle; |
48 | | typedef mozilla::gfx::DrawTarget DrawTarget; |
49 | | typedef mozilla::gfx::Point Point; |
50 | | typedef mozilla::gfx::Rect Rect; |
51 | | typedef mozilla::gfx::Size Size; |
52 | | typedef gfxTextRun::Range Range; |
53 | | |
54 | | public: |
55 | | explicit nsTextFrame(ComputedStyle* aStyle, ClassID aID = kClassID) |
56 | | : nsFrame(aStyle, aID) |
57 | | , mNextContinuation(nullptr) |
58 | | , mContentOffset(0) |
59 | | , mContentLengthHint(0) |
60 | | , mAscent(0) |
61 | 0 | {} |
62 | | |
63 | | NS_DECL_FRAMEARENA_HELPERS(nsTextFrame) |
64 | | |
65 | | friend class nsContinuingTextFrame; |
66 | | friend class nsDisplayTextGeometry; |
67 | | friend class nsDisplayText; |
68 | | |
69 | | // nsQueryFrame |
70 | | NS_DECL_QUERYFRAME |
71 | | |
72 | | // nsIFrame |
73 | | void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
74 | | const nsDisplayListSet& aLists) override; |
75 | | |
76 | | void Init(nsIContent* aContent, |
77 | | nsContainerFrame* aParent, |
78 | | nsIFrame* aPrevInFlow) override; |
79 | | |
80 | | void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override; |
81 | | |
82 | | nsresult GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor) override; |
83 | | |
84 | | nsresult CharacterDataChanged(const CharacterDataChangeInfo&) final; |
85 | | |
86 | 0 | nsTextFrame* GetPrevContinuation() const override { return nullptr; } |
87 | 0 | nsTextFrame* GetNextContinuation() const final { return mNextContinuation; } |
88 | | void SetNextContinuation(nsIFrame* aNextContinuation) final |
89 | 0 | { |
90 | 0 | NS_ASSERTION(!aNextContinuation || Type() == aNextContinuation->Type(), |
91 | 0 | "setting a next continuation with incorrect type!"); |
92 | 0 | NS_ASSERTION( |
93 | 0 | !nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this), |
94 | 0 | "creating a loop in continuation chain!"); |
95 | 0 | mNextContinuation = static_cast<nsTextFrame*>(aNextContinuation); |
96 | 0 | if (aNextContinuation) |
97 | 0 | aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION); |
98 | 0 | // Setting a non-fluid continuation might affect our flow length (they're |
99 | 0 | // quite rare so we assume it always does) so we delete our cached value: |
100 | 0 | if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) { |
101 | 0 | GetContent()->DeleteProperty(nsGkAtoms::flowlength); |
102 | 0 | GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY); |
103 | 0 | } |
104 | 0 | } |
105 | 0 | nsIFrame* GetNextInFlowVirtual() const override { return GetNextInFlow(); } |
106 | | nsTextFrame* GetNextInFlow() const |
107 | 0 | { |
108 | 0 | return mNextContinuation && |
109 | 0 | (mNextContinuation->GetStateBits() & |
110 | 0 | NS_FRAME_IS_FLUID_CONTINUATION) |
111 | 0 | ? mNextContinuation |
112 | 0 | : nullptr; |
113 | 0 | } |
114 | | void SetNextInFlow(nsIFrame* aNextInFlow) final |
115 | 0 | { |
116 | 0 | NS_ASSERTION(!aNextInFlow || Type() == aNextInFlow->Type(), |
117 | 0 | "setting a next in flow with incorrect type!"); |
118 | 0 | NS_ASSERTION( |
119 | 0 | !nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this), |
120 | 0 | "creating a loop in continuation chain!"); |
121 | 0 | mNextContinuation = static_cast<nsTextFrame*>(aNextInFlow); |
122 | 0 | if (mNextContinuation && |
123 | 0 | !mNextContinuation->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) { |
124 | 0 | // Changing from non-fluid to fluid continuation might affect our flow |
125 | 0 | // length, so we delete our cached value: |
126 | 0 | if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) { |
127 | 0 | GetContent()->DeleteProperty(nsGkAtoms::flowlength); |
128 | 0 | GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY); |
129 | 0 | } |
130 | 0 | } |
131 | 0 | if (aNextInFlow) { |
132 | 0 | aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); |
133 | 0 | } |
134 | 0 | } |
135 | | nsTextFrame* LastInFlow() const final; |
136 | | nsTextFrame* LastContinuation() const final; |
137 | | |
138 | | nsSplittableType GetSplittableType() const final |
139 | 0 | { |
140 | 0 | return NS_FRAME_SPLITTABLE; |
141 | 0 | } |
142 | | |
143 | | bool IsFrameOfType(uint32_t aFlags) const final |
144 | 0 | { |
145 | 0 | // Set the frame state bit for text frames to mark them as replaced. |
146 | 0 | // XXX kipp: temporary |
147 | 0 | return nsFrame::IsFrameOfType( |
148 | 0 | aFlags & ~(nsIFrame::eReplaced | nsIFrame::eLineParticipant)); |
149 | 0 | } |
150 | | |
151 | | bool ShouldSuppressLineBreak() const |
152 | 0 | { |
153 | 0 | // If the parent frame of the text frame is ruby content box, it must |
154 | 0 | // suppress line break inside. This check is necessary, because when |
155 | 0 | // a whitespace is only contained by pseudo ruby frames, its style |
156 | 0 | // context won't have SuppressLineBreak bit set. |
157 | 0 | if (mozilla::RubyUtils::IsRubyContentBox(GetParent()->Type())) { |
158 | 0 | return true; |
159 | 0 | } |
160 | 0 | return Style()->ShouldSuppressLineBreak(); |
161 | 0 | } |
162 | | |
163 | | void InvalidateFrame(uint32_t aDisplayItemKey = 0, bool aRebuildDisplayItems = true) override; |
164 | | void InvalidateFrameWithRect(const nsRect& aRect, |
165 | | uint32_t aDisplayItemKey = 0, |
166 | | bool aRebuildDisplayItems = true) override; |
167 | | |
168 | | #ifdef DEBUG_FRAME_DUMP |
169 | | void List(FILE* out = stderr, |
170 | | const char* aPrefix = "", |
171 | | uint32_t aFlags = 0) const override; |
172 | | nsresult GetFrameName(nsAString& aResult) const override; |
173 | | void ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const; |
174 | | #endif |
175 | | |
176 | | ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) override; |
177 | | ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint& aPoint); |
178 | | |
179 | | /** |
180 | | * This is called only on the primary text frame. It indicates that |
181 | | * the selection state of the given character range has changed. |
182 | | * Text in the range is unconditionally invalidated |
183 | | * (Selection::Repaint depends on this). |
184 | | * @param aSelected true if the selection has been added to the range, |
185 | | * false otherwise |
186 | | * @param aType the type of selection added or removed |
187 | | */ |
188 | | void SetSelectedRange(uint32_t aStart, |
189 | | uint32_t aEnd, |
190 | | bool aSelected, |
191 | | SelectionType aSelectionType); |
192 | | |
193 | | FrameSearchResult PeekOffsetNoAmount(bool aForward, |
194 | | int32_t* aOffset) override; |
195 | | FrameSearchResult |
196 | | PeekOffsetCharacter(bool aForward, |
197 | | int32_t* aOffset, |
198 | | PeekOffsetCharacterOptions aOptions = |
199 | | PeekOffsetCharacterOptions()) override; |
200 | | FrameSearchResult PeekOffsetWord(bool aForward, |
201 | | bool aWordSelectEatSpace, |
202 | | bool aIsKeyboardSelect, |
203 | | int32_t* aOffset, |
204 | | PeekWordState* aState) override; |
205 | | |
206 | | nsresult CheckVisibility(nsPresContext* aContext, |
207 | | int32_t aStartIndex, |
208 | | int32_t aEndIndex, |
209 | | bool aRecurse, |
210 | | bool* aFinished, |
211 | | bool* _retval) override; |
212 | | |
213 | | // Flags for aSetLengthFlags |
214 | | enum |
215 | | { |
216 | | ALLOW_FRAME_CREATION_AND_DESTRUCTION = 0x01 |
217 | | }; |
218 | | |
219 | | // Update offsets to account for new length. This may clear mTextRun. |
220 | | void SetLength(int32_t aLength, |
221 | | nsLineLayout* aLineLayout, |
222 | | uint32_t aSetLengthFlags = 0); |
223 | | |
224 | | nsresult GetOffsets(int32_t& start, int32_t& end) const override; |
225 | | |
226 | | void AdjustOffsetsForBidi(int32_t start, int32_t end) override; |
227 | | |
228 | | nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) override; |
229 | | nsresult GetCharacterRectsInRange(int32_t aInOffset, |
230 | | int32_t aLength, |
231 | | nsTArray<nsRect>& aRects) override; |
232 | | |
233 | | nsresult GetChildFrameContainingOffset(int32_t inContentOffset, |
234 | | bool inHint, |
235 | | int32_t* outFrameContentOffset, |
236 | | nsIFrame** outChildFrame) override; |
237 | | |
238 | | bool IsVisibleInSelection(mozilla::dom::Selection* aSelection) override; |
239 | | |
240 | | bool IsEmpty() override; |
241 | 0 | bool IsSelfEmpty() override { return IsEmpty(); } |
242 | | nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const final; |
243 | | |
244 | | bool HasSignificantTerminalNewline() const override; |
245 | | |
246 | | /** |
247 | | * Returns true if this text frame is logically adjacent to the end of the |
248 | | * line. |
249 | | */ |
250 | | bool IsAtEndOfLine() const; |
251 | | |
252 | | /** |
253 | | * Call this only after reflow the frame. Returns true if non-collapsed |
254 | | * characters are present. |
255 | | */ |
256 | | bool HasNoncollapsedCharacters() const |
257 | 0 | { |
258 | 0 | return (GetStateBits() & TEXT_HAS_NONCOLLAPSED_CHARACTERS) != 0; |
259 | 0 | } |
260 | | |
261 | | #ifdef ACCESSIBILITY |
262 | | mozilla::a11y::AccType AccessibleType() override; |
263 | | #endif |
264 | | |
265 | | float GetFontSizeInflation() const; |
266 | | bool IsCurrentFontInflation(float aInflation) const; |
267 | | bool HasFontSizeInflation() const |
268 | 0 | { |
269 | 0 | return (GetStateBits() & TEXT_HAS_FONT_INFLATION) != 0; |
270 | 0 | } |
271 | | void SetFontSizeInflation(float aInflation); |
272 | | |
273 | | void MarkIntrinsicISizesDirty() override; |
274 | | nscoord GetMinISize(gfxContext* aRenderingContext) override; |
275 | | nscoord GetPrefISize(gfxContext* aRenderingContext) override; |
276 | | void AddInlineMinISize(gfxContext* aRenderingContext, |
277 | | InlineMinISizeData* aData) override; |
278 | | void AddInlinePrefISize(gfxContext* aRenderingContext, |
279 | | InlinePrefISizeData* aData) override; |
280 | | mozilla::LogicalSize ComputeSize(gfxContext* aRenderingContext, |
281 | | mozilla::WritingMode aWritingMode, |
282 | | const mozilla::LogicalSize& aCBSize, |
283 | | nscoord aAvailableISize, |
284 | | const mozilla::LogicalSize& aMargin, |
285 | | const mozilla::LogicalSize& aBorder, |
286 | | const mozilla::LogicalSize& aPadding, |
287 | | ComputeSizeFlags aFlags) override; |
288 | | nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override; |
289 | | nsresult GetPrefWidthTightBounds(gfxContext* aContext, |
290 | | nscoord* aX, |
291 | | nscoord* aXMost) override; |
292 | | void Reflow(nsPresContext* aPresContext, |
293 | | ReflowOutput& aMetrics, |
294 | | const ReflowInput& aReflowInput, |
295 | | nsReflowStatus& aStatus) override; |
296 | | bool CanContinueTextRun() const override; |
297 | | // Method that is called for a text frame that is logically |
298 | | // adjacent to the end of the line (i.e. followed only by empty text frames, |
299 | | // placeholders or inlines containing such). |
300 | | struct TrimOutput |
301 | | { |
302 | | // true if we trimmed some space or changed metrics in some other way. |
303 | | // In this case, we should call RecomputeOverflow on this frame. |
304 | | bool mChanged; |
305 | | // an amount to *subtract* from the frame's width (zero if !mChanged) |
306 | | nscoord mDeltaWidth; |
307 | | }; |
308 | | TrimOutput TrimTrailingWhiteSpace(DrawTarget* aDrawTarget); |
309 | | RenderedText GetRenderedText( |
310 | | uint32_t aStartOffset = 0, |
311 | | uint32_t aEndOffset = UINT32_MAX, |
312 | | TextOffsetType aOffsetType = TextOffsetType::OFFSETS_IN_CONTENT_TEXT, |
313 | | TrailingWhitespace aTrimTrailingWhitespace = |
314 | | TrailingWhitespace::TRIM_TRAILING_WHITESPACE) override; |
315 | | |
316 | | nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame); |
317 | | |
318 | | enum TextRunType |
319 | | { |
320 | | // Anything in reflow (but not intrinsic width calculation) or |
321 | | // painting should use the inflated text run (i.e., with font size |
322 | | // inflation applied). |
323 | | eInflated, |
324 | | // Intrinsic width calculation should use the non-inflated text run. |
325 | | // When there is font size inflation, it will be different. |
326 | | eNotInflated |
327 | | }; |
328 | | |
329 | | void AddInlineMinISizeForFlow(gfxContext* aRenderingContext, |
330 | | nsIFrame::InlineMinISizeData* aData, |
331 | | TextRunType aTextRunType); |
332 | | void AddInlinePrefISizeForFlow(gfxContext* aRenderingContext, |
333 | | InlinePrefISizeData* aData, |
334 | | TextRunType aTextRunType); |
335 | | |
336 | | /** |
337 | | * Calculate the horizontal bounds of the grapheme clusters that fit entirely |
338 | | * inside the given left[top]/right[bottom] edges (which are positive lengths |
339 | | * from the respective frame edge). If an input value is zero it is ignored |
340 | | * and the result for that edge is zero. All out parameter values are |
341 | | * undefined when the method returns false. |
342 | | * @return true if at least one whole grapheme cluster fit between the edges |
343 | | */ |
344 | | bool MeasureCharClippedText(nscoord aVisIStartEdge, |
345 | | nscoord aVisIEndEdge, |
346 | | nscoord* aSnappedStartEdge, |
347 | | nscoord* aSnappedEndEdge); |
348 | | /** |
349 | | * Same as above; this method also the returns the corresponding text run |
350 | | * offset and number of characters that fit. All out parameter values are |
351 | | * undefined when the method returns false. |
352 | | * @return true if at least one whole grapheme cluster fit between the edges |
353 | | */ |
354 | | bool MeasureCharClippedText(PropertyProvider& aProvider, |
355 | | nscoord aVisIStartEdge, |
356 | | nscoord aVisIEndEdge, |
357 | | uint32_t* aStartOffset, |
358 | | uint32_t* aMaxLength, |
359 | | nscoord* aSnappedStartEdge, |
360 | | nscoord* aSnappedEndEdge); |
361 | | |
362 | | /** |
363 | | * Return true if this box has some text to display. |
364 | | * It returns false if at least one of these conditions are met: |
365 | | * a. the frame hasn't been reflowed yet |
366 | | * b. GetContentLength() == 0 |
367 | | * c. it contains only non-significant white-space |
368 | | */ |
369 | | bool HasNonSuppressedText(); |
370 | | |
371 | | /** |
372 | | * Object with various callbacks for PaintText() to invoke for different parts |
373 | | * of the frame's text rendering, when we're generating paths rather than |
374 | | * painting. |
375 | | * |
376 | | * Callbacks are invoked in the following order: |
377 | | * |
378 | | * NotifySelectionBackgroundNeedsFill? |
379 | | * PaintDecorationLine* |
380 | | * NotifyBeforeText |
381 | | * NotifyGlyphPathEmitted* |
382 | | * NotifyAfterText |
383 | | * PaintDecorationLine* |
384 | | * PaintSelectionDecorationLine* |
385 | | * |
386 | | * The color of each part of the frame's text rendering is passed as an argument |
387 | | * to the NotifyBefore* callback for that part. The nscolor can take on one of |
388 | | * the three selection special colors defined in LookAndFeel.h -- |
389 | | * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and |
390 | | * NS_40PERCENT_FOREGROUND_COLOR. |
391 | | */ |
392 | | struct DrawPathCallbacks : gfxTextRunDrawCallbacks |
393 | | { |
394 | | /** |
395 | | * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted. |
396 | | */ |
397 | | explicit DrawPathCallbacks(bool aShouldPaintSVGGlyphs = false) |
398 | | : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs) |
399 | 0 | {} |
400 | | |
401 | | /** |
402 | | * Called to have the selection highlight drawn before the text is drawn |
403 | | * over the top. |
404 | | */ |
405 | | virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, |
406 | | nscolor aColor, |
407 | | DrawTarget& aDrawTarget) |
408 | 0 | {} |
409 | | |
410 | | /** |
411 | | * Called before (for under/over-line) or after (for line-through) the text |
412 | | * is drawn to have a text decoration line drawn. |
413 | | */ |
414 | 0 | virtual void PaintDecorationLine(Rect aPath, nscolor aColor) {} |
415 | | |
416 | | /** |
417 | | * Called after selected text is drawn to have a decoration line drawn over |
418 | | * the text. (All types of text decoration are drawn after the text when |
419 | | * text is selected.) |
420 | | */ |
421 | 0 | virtual void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) {} |
422 | | |
423 | | /** |
424 | | * Called just before any paths have been emitted to the gfxContext |
425 | | * for the glyphs of the frame's text. |
426 | | */ |
427 | 0 | virtual void NotifyBeforeText(nscolor aColor) {} |
428 | | |
429 | | /** |
430 | | * Called just after all the paths have been emitted to the gfxContext |
431 | | * for the glyphs of the frame's text. |
432 | | */ |
433 | 0 | virtual void NotifyAfterText() {} |
434 | | |
435 | | /** |
436 | | * Called just before a path corresponding to a selection decoration line |
437 | | * has been emitted to the gfxContext. |
438 | | */ |
439 | 0 | virtual void NotifyBeforeSelectionDecorationLine(nscolor aColor) {} |
440 | | |
441 | | /** |
442 | | * Called just after a path corresponding to a selection decoration line |
443 | | * has been emitted to the gfxContext. |
444 | | */ |
445 | 0 | virtual void NotifySelectionDecorationLinePathEmitted() {} |
446 | | }; |
447 | | |
448 | | struct PaintTextParams |
449 | | { |
450 | | gfxContext* context; |
451 | | mozilla::gfx::Point framePt; |
452 | | LayoutDeviceRect dirtyRect; |
453 | | mozilla::SVGContextPaint* contextPaint = nullptr; |
454 | | DrawPathCallbacks* callbacks = nullptr; |
455 | | enum |
456 | | { |
457 | | PaintText, // Normal text painting. |
458 | | PaintTextBGColor, // Only paint background color of the selected text |
459 | | // range in this state. |
460 | | GenerateTextMask // To generate a mask from a text frame. Should |
461 | | // only paint text itself with opaque color. |
462 | | // Text shadow, text selection color and text |
463 | | // decoration are all discarded in this state. |
464 | | }; |
465 | | uint8_t state = PaintText; |
466 | | explicit PaintTextParams(gfxContext* aContext) |
467 | | : context(aContext) |
468 | 0 | { |
469 | 0 | } |
470 | | |
471 | 0 | bool IsPaintText() const { return state == PaintText; } |
472 | 0 | bool IsGenerateTextMask() const { return state == GenerateTextMask; } |
473 | 0 | bool IsPaintBGColor() const { return state == PaintTextBGColor; } |
474 | | }; |
475 | | |
476 | | struct PaintTextSelectionParams : PaintTextParams |
477 | | { |
478 | | mozilla::gfx::Point textBaselinePt; |
479 | | PropertyProvider* provider = nullptr; |
480 | | Range contentRange; |
481 | | nsTextPaintStyle* textPaintStyle = nullptr; |
482 | | explicit PaintTextSelectionParams(const PaintTextParams& aParams) |
483 | | : PaintTextParams(aParams) |
484 | 0 | {} |
485 | | }; |
486 | | |
487 | | struct DrawTextRunParams |
488 | | { |
489 | | gfxContext* context; |
490 | | PropertyProvider* provider = nullptr; |
491 | | gfxFloat* advanceWidth = nullptr; |
492 | | mozilla::SVGContextPaint* contextPaint = nullptr; |
493 | | DrawPathCallbacks* callbacks = nullptr; |
494 | | nscolor textColor = NS_RGBA(0, 0, 0, 0); |
495 | | nscolor textStrokeColor = NS_RGBA(0, 0, 0, 0); |
496 | | float textStrokeWidth = 0.0f; |
497 | | bool drawSoftHyphen = false; |
498 | | explicit DrawTextRunParams(gfxContext* aContext) |
499 | | : context(aContext) |
500 | 0 | {} |
501 | | }; |
502 | | |
503 | | struct DrawTextParams : DrawTextRunParams |
504 | | { |
505 | | mozilla::gfx::Point framePt; |
506 | | LayoutDeviceRect dirtyRect; |
507 | | const nsTextPaintStyle* textStyle = nullptr; |
508 | | const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr; |
509 | | const nscolor* decorationOverrideColor = nullptr; |
510 | | explicit DrawTextParams(gfxContext* aContext) |
511 | | : DrawTextRunParams(aContext) |
512 | 0 | {} |
513 | | }; |
514 | | |
515 | | // Primary frame paint method called from nsDisplayText. Can also be used |
516 | | // to generate paths rather than paint the frame's text by passing a callback |
517 | | // object. The private DrawText() is what applies the text to a graphics |
518 | | // context. |
519 | | void PaintText(const PaintTextParams& aParams, |
520 | | const nsCharClipDisplayItem& aItem, |
521 | | float aOpacity = 1.0f); |
522 | | // helper: paint text frame when we're impacted by at least one selection. |
523 | | // Return false if the text was not painted and we should continue with |
524 | | // the fast path. |
525 | | bool PaintTextWithSelection( |
526 | | const PaintTextSelectionParams& aParams, |
527 | | const nsCharClipDisplayItem::ClipEdges& aClipEdges); |
528 | | // helper: paint text with foreground and background colors determined |
529 | | // by selection(s). Also computes a mask of all selection types applying to |
530 | | // our text, returned in aAllSelectionTypeMask. |
531 | | // Return false if the text was not painted and we should continue with |
532 | | // the fast path. |
533 | | bool PaintTextWithSelectionColors( |
534 | | const PaintTextSelectionParams& aParams, |
535 | | const mozilla::UniquePtr<SelectionDetails>& aDetails, |
536 | | SelectionTypeMask* aAllSelectionTypeMask, |
537 | | const nsCharClipDisplayItem::ClipEdges& aClipEdges); |
538 | | // helper: paint text decorations for text selected by aSelectionType |
539 | | void PaintTextSelectionDecorations(const PaintTextSelectionParams& aParams, |
540 | | const mozilla::UniquePtr<SelectionDetails>& aDetails, |
541 | | SelectionType aSelectionType); |
542 | | |
543 | | void DrawEmphasisMarks(gfxContext* aContext, |
544 | | mozilla::WritingMode aWM, |
545 | | const mozilla::gfx::Point& aTextBaselinePt, |
546 | | const mozilla::gfx::Point& aFramePt, |
547 | | Range aRange, |
548 | | const nscolor* aDecorationOverrideColor, |
549 | | PropertyProvider* aProvider); |
550 | | |
551 | | nscolor GetCaretColorAt(int32_t aOffset) override; |
552 | | |
553 | | int16_t GetSelectionStatus(int16_t* aSelectionFlags); |
554 | | |
555 | 0 | int32_t GetContentOffset() const { return mContentOffset; } |
556 | | int32_t GetContentLength() const |
557 | 0 | { |
558 | 0 | NS_ASSERTION(GetContentEnd() - mContentOffset >= 0, "negative length"); |
559 | 0 | return GetContentEnd() - mContentOffset; |
560 | 0 | } |
561 | | int32_t GetContentEnd() const; |
562 | | // This returns the length the frame thinks it *should* have after it was |
563 | | // last reflowed (0 if it hasn't been reflowed yet). This should be used only |
564 | | // when setting up the text offsets for a new continuation frame. |
565 | 0 | int32_t GetContentLengthHint() const { return mContentLengthHint; } |
566 | | |
567 | | // Compute the length of the content mapped by this frame |
568 | | // and all its in-flow siblings. Basically this means starting at mContentOffset |
569 | | // and going to the end of the text node or the next bidi continuation |
570 | | // boundary. |
571 | | int32_t GetInFlowContentLength(); |
572 | | |
573 | | /** |
574 | | * Acquires the text run for this content, if necessary. |
575 | | * @param aWhichTextRun indicates whether to get an inflated or non-inflated |
576 | | * text run |
577 | | * @param aRefDrawTarget the DrawTarget to use as a reference for creating the |
578 | | * textrun, if available (if not, we'll create one which will just be slower) |
579 | | * @param aLineContainer the block ancestor for this frame, or nullptr if |
580 | | * unknown |
581 | | * @param aFlowEndInTextRun if non-null, this returns the textrun offset of |
582 | | * end of the text associated with this frame and its in-flow siblings |
583 | | * @return a gfxSkipCharsIterator set up to map DOM offsets for this frame |
584 | | * to offsets into the textrun; its initial offset is set to this frame's |
585 | | * content offset |
586 | | */ |
587 | | gfxSkipCharsIterator EnsureTextRun( |
588 | | TextRunType aWhichTextRun, |
589 | | DrawTarget* aRefDrawTarget = nullptr, |
590 | | nsIFrame* aLineContainer = nullptr, |
591 | | const nsLineList::iterator* aLine = nullptr, |
592 | | uint32_t* aFlowEndInTextRun = nullptr); |
593 | | |
594 | | gfxTextRun* GetTextRun(TextRunType aWhichTextRun) |
595 | 0 | { |
596 | 0 | if (aWhichTextRun == eInflated || !HasFontSizeInflation()) |
597 | 0 | return mTextRun; |
598 | 0 | return GetUninflatedTextRun(); |
599 | 0 | } |
600 | | gfxTextRun* GetUninflatedTextRun(); |
601 | | void SetTextRun(gfxTextRun* aTextRun, |
602 | | TextRunType aWhichTextRun, |
603 | | float aInflation); |
604 | | bool IsInTextRunUserData() const |
605 | 0 | { |
606 | 0 | return GetStateBits() & |
607 | 0 | (TEXT_IN_TEXTRUN_USER_DATA | TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA); |
608 | 0 | } |
609 | | /** |
610 | | * Notify the frame that it should drop its pointer to a text run. |
611 | | * Returns whether the text run was removed (i.e., whether it was |
612 | | * associated with this frame, either as its inflated or non-inflated |
613 | | * text run. |
614 | | */ |
615 | | bool RemoveTextRun(gfxTextRun* aTextRun); |
616 | | /** |
617 | | * Clears out |mTextRun| (or the uninflated text run, when aInflated |
618 | | * is nsTextFrame::eNotInflated and there is inflation) from all frames that hold a |
619 | | * reference to it, starting at |aStartContinuation|, or if it's |
620 | | * nullptr, starting at |this|. Deletes the text run if all references |
621 | | * were cleared and it's not cached. |
622 | | */ |
623 | | void ClearTextRun(nsTextFrame* aStartContinuation, TextRunType aWhichTextRun); |
624 | | |
625 | | void ClearTextRuns() |
626 | 0 | { |
627 | 0 | ClearTextRun(nullptr, nsTextFrame::eInflated); |
628 | 0 | if (HasFontSizeInflation()) { |
629 | 0 | ClearTextRun(nullptr, nsTextFrame::eNotInflated); |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | | /** |
634 | | * Wipe out references to textrun(s) without deleting the textruns. |
635 | | */ |
636 | | void DisconnectTextRuns(); |
637 | | |
638 | | // Get the DOM content range mapped by this frame after excluding |
639 | | // whitespace subject to start-of-line and end-of-line trimming. |
640 | | // The textrun must have been created before calling this. |
641 | | struct TrimmedOffsets |
642 | | { |
643 | | int32_t mStart; |
644 | | int32_t mLength; |
645 | 0 | int32_t GetEnd() const { return mStart + mLength; } |
646 | | }; |
647 | | TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag, |
648 | | bool aTrimAfter, |
649 | | bool aPostReflow = true) const; |
650 | | |
651 | | // Similar to Reflow(), but for use from nsLineLayout |
652 | | void ReflowText(nsLineLayout& aLineLayout, |
653 | | nscoord aAvailableWidth, |
654 | | DrawTarget* aDrawTarget, |
655 | | ReflowOutput& aMetrics, |
656 | | nsReflowStatus& aStatus); |
657 | | |
658 | | bool IsFloatingFirstLetterChild() const; |
659 | | |
660 | | bool IsInitialLetterChild() const; |
661 | | |
662 | | bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override; |
663 | | |
664 | | void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign); |
665 | | mozilla::JustificationAssignment GetJustificationAssignment() const; |
666 | | |
667 | | uint32_t CountGraphemeClusters() const; |
668 | | |
669 | | bool HasAnyNoncollapsedCharacters() override; |
670 | | |
671 | | protected: |
672 | | virtual ~nsTextFrame(); |
673 | | |
674 | | RefPtr<gfxTextRun> mTextRun; |
675 | | nsTextFrame* mNextContinuation; |
676 | | // The key invariant here is that mContentOffset never decreases along |
677 | | // a next-continuation chain. And of course mContentOffset is always <= the |
678 | | // the text node's content length, and the mContentOffset for the first frame |
679 | | // is always 0. Furthermore the text mapped by a frame is determined by |
680 | | // GetContentOffset() and GetContentLength()/GetContentEnd(), which get |
681 | | // the length from the difference between this frame's offset and the next |
682 | | // frame's offset, or the text length if there is no next frame. This means |
683 | | // the frames always map the text node without overlapping or leaving any gaps. |
684 | | int32_t mContentOffset; |
685 | | // This does *not* indicate the length of text currently mapped by the frame; |
686 | | // instead it's a hint saying that this frame *wants* to map this much text |
687 | | // so if we create a new continuation, this is where that continuation should |
688 | | // start. |
689 | | int32_t mContentLengthHint; |
690 | | nscoord mAscent; |
691 | | |
692 | | /** |
693 | | * Return true if the frame is part of a Selection. |
694 | | * Helper method to implement the public IsSelected() API. |
695 | | */ |
696 | | bool IsFrameSelected() const override; |
697 | | |
698 | | mozilla::UniquePtr<SelectionDetails> GetSelectionDetails(); |
699 | | |
700 | | void UnionAdditionalOverflow(nsPresContext* aPresContext, |
701 | | nsIFrame* aBlock, |
702 | | PropertyProvider& aProvider, |
703 | | nsRect* aVisualOverflowRect, |
704 | | bool aIncludeTextDecorations); |
705 | | |
706 | | // Update information of emphasis marks, and return the visial |
707 | | // overflow rect of the emphasis marks. |
708 | | nsRect UpdateTextEmphasis(mozilla::WritingMode aWM, |
709 | | PropertyProvider& aProvider); |
710 | | |
711 | | struct PaintShadowParams |
712 | | { |
713 | | gfxTextRun::Range range; |
714 | | LayoutDeviceRect dirtyRect; |
715 | | mozilla::gfx::Point framePt; |
716 | | mozilla::gfx::Point textBaselinePt; |
717 | | gfxContext* context; |
718 | | nscolor foregroundColor = NS_RGBA(0, 0, 0, 0); |
719 | | const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr; |
720 | | PropertyProvider* provider = nullptr; |
721 | | nscoord leftSideOffset = 0; |
722 | | explicit PaintShadowParams(const PaintTextParams& aParams) |
723 | | : dirtyRect(aParams.dirtyRect) |
724 | | , framePt(aParams.framePt) |
725 | | , context(aParams.context) |
726 | 0 | { |
727 | 0 | } |
728 | | }; |
729 | | |
730 | | void PaintOneShadow(const PaintShadowParams& aParams, |
731 | | nsCSSShadowItem* aShadowDetails, |
732 | | gfxRect& aBoundingBox, |
733 | | uint32_t aBlurFlags); |
734 | | |
735 | | void PaintShadows(nsCSSShadowArray* aShadow, |
736 | | const PaintShadowParams& aParams); |
737 | | |
738 | | struct LineDecoration |
739 | | { |
740 | | nsIFrame* mFrame; |
741 | | |
742 | | // This is represents the offset from our baseline to mFrame's baseline; |
743 | | // positive offsets are *above* the baseline and negative offsets below |
744 | | nscoord mBaselineOffset; |
745 | | |
746 | | nscolor mColor; |
747 | | uint8_t mStyle; |
748 | | |
749 | | LineDecoration(nsIFrame* const aFrame, |
750 | | const nscoord aOff, |
751 | | const nscolor aColor, |
752 | | const uint8_t aStyle) |
753 | | : mFrame(aFrame) |
754 | | , mBaselineOffset(aOff) |
755 | | , mColor(aColor) |
756 | | , mStyle(aStyle) |
757 | 0 | { |
758 | 0 | } |
759 | | |
760 | | LineDecoration(const LineDecoration& aOther) |
761 | | : mFrame(aOther.mFrame) |
762 | | , mBaselineOffset(aOther.mBaselineOffset) |
763 | | , mColor(aOther.mColor) |
764 | | , mStyle(aOther.mStyle) |
765 | 0 | { |
766 | 0 | } |
767 | | |
768 | | bool operator==(const LineDecoration& aOther) const |
769 | 0 | { |
770 | 0 | return mFrame == aOther.mFrame && mStyle == aOther.mStyle && |
771 | 0 | mColor == aOther.mColor && |
772 | 0 | mBaselineOffset == aOther.mBaselineOffset; |
773 | 0 | } |
774 | | |
775 | | bool operator!=(const LineDecoration& aOther) const |
776 | 0 | { |
777 | 0 | return !(*this == aOther); |
778 | 0 | } |
779 | | }; |
780 | | struct TextDecorations |
781 | | { |
782 | | AutoTArray<LineDecoration, 1> mOverlines, mUnderlines, mStrikes; |
783 | | |
784 | 0 | TextDecorations() {} |
785 | | |
786 | | bool HasDecorationLines() const |
787 | 0 | { |
788 | 0 | return HasUnderline() || HasOverline() || HasStrikeout(); |
789 | 0 | } |
790 | 0 | bool HasUnderline() const { return !mUnderlines.IsEmpty(); } |
791 | 0 | bool HasOverline() const { return !mOverlines.IsEmpty(); } |
792 | 0 | bool HasStrikeout() const { return !mStrikes.IsEmpty(); } |
793 | | bool operator==(const TextDecorations& aOther) const |
794 | 0 | { |
795 | 0 | return mOverlines == aOther.mOverlines && |
796 | 0 | mUnderlines == aOther.mUnderlines && mStrikes == aOther.mStrikes; |
797 | 0 | } |
798 | | bool operator!=(const TextDecorations& aOther) const |
799 | 0 | { |
800 | 0 | return !(*this == aOther); |
801 | 0 | } |
802 | | }; |
803 | | enum TextDecorationColorResolution |
804 | | { |
805 | | eResolvedColors, |
806 | | eUnresolvedColors |
807 | | }; |
808 | | void GetTextDecorations(nsPresContext* aPresContext, |
809 | | TextDecorationColorResolution aColorResolution, |
810 | | TextDecorations& aDecorations); |
811 | | |
812 | | void DrawTextRun(Range aRange, |
813 | | const mozilla::gfx::Point& aTextBaselinePt, |
814 | | const DrawTextRunParams& aParams); |
815 | | |
816 | | void DrawTextRunAndDecorations(Range aRange, |
817 | | const mozilla::gfx::Point& aTextBaselinePt, |
818 | | const DrawTextParams& aParams, |
819 | | const TextDecorations& aDecorations); |
820 | | |
821 | | void DrawText(Range aRange, |
822 | | const mozilla::gfx::Point& aTextBaselinePt, |
823 | | const DrawTextParams& aParams); |
824 | | |
825 | | // Set non empty rect to aRect, it should be overflow rect or frame rect. |
826 | | // If the result rect is larger than the given rect, this returns true. |
827 | | bool CombineSelectionUnderlineRect(nsPresContext* aPresContext, |
828 | | nsRect& aRect); |
829 | | |
830 | | /** |
831 | | * Utility methods to paint selection. |
832 | | */ |
833 | | void DrawSelectionDecorations(gfxContext* aContext, |
834 | | const LayoutDeviceRect& aDirtyRect, |
835 | | mozilla::SelectionType aSelectionType, |
836 | | nsTextPaintStyle& aTextPaintStyle, |
837 | | const TextRangeStyle& aRangeStyle, |
838 | | const Point& aPt, |
839 | | gfxFloat aICoordInFrame, |
840 | | gfxFloat aWidth, |
841 | | gfxFloat aAscent, |
842 | | const gfxFont::Metrics& aFontMetrics, |
843 | | DrawPathCallbacks* aCallbacks, |
844 | | bool aVertical, |
845 | | uint8_t aDecoration); |
846 | | |
847 | | struct PaintDecorationLineParams; |
848 | | void PaintDecorationLine(const PaintDecorationLineParams& aParams); |
849 | | /** |
850 | | * ComputeDescentLimitForSelectionUnderline() computes the most far position |
851 | | * where we can put selection underline. |
852 | | * |
853 | | * @return The maximum underline offset from the baseline (positive value |
854 | | * means that the underline can put below the baseline). |
855 | | */ |
856 | | gfxFloat ComputeDescentLimitForSelectionUnderline( |
857 | | nsPresContext* aPresContext, |
858 | | const gfxFont::Metrics& aFontMetrics); |
859 | | /** |
860 | | * This function encapsulates all knowledge of how selections affect |
861 | | * foreground and background colors. |
862 | | * @param aForeground the foreground color to use |
863 | | * @param aBackground the background color to use, or RGBA(0,0,0,0) if no |
864 | | * background should be painted |
865 | | * @return true if the selection affects colors, false otherwise |
866 | | */ |
867 | | static bool GetSelectionTextColors(SelectionType aSelectionType, |
868 | | nsTextPaintStyle& aTextPaintStyle, |
869 | | const TextRangeStyle& aRangeStyle, |
870 | | nscolor* aForeground, |
871 | | nscolor* aBackground); |
872 | | /** |
873 | | * ComputeSelectionUnderlineHeight() computes selection underline height of |
874 | | * the specified selection type from the font metrics. |
875 | | */ |
876 | | static gfxFloat ComputeSelectionUnderlineHeight( |
877 | | nsPresContext* aPresContext, |
878 | | const gfxFont::Metrics& aFontMetrics, |
879 | | SelectionType aSelectionType); |
880 | | |
881 | | ContentOffsets GetCharacterOffsetAtFramePointInternal( |
882 | | const nsPoint& aPoint, |
883 | | bool aForInsertionPoint); |
884 | | |
885 | | void ClearFrameOffsetCache(); |
886 | | |
887 | | void ClearMetrics(ReflowOutput& aMetrics); |
888 | | |
889 | | /** |
890 | | * UpdateIteratorFromOffset() updates the iterator from a given offset. |
891 | | * Also, aInOffset may be updated to cluster start if aInOffset isn't |
892 | | * the offset of cluster start. |
893 | | */ |
894 | | void UpdateIteratorFromOffset(const PropertyProvider& aProperties, |
895 | | int32_t& aInOffset, |
896 | | gfxSkipCharsIterator& aIter); |
897 | | |
898 | | nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter, |
899 | | PropertyProvider& aProperties); |
900 | | }; |
901 | | |
902 | | #endif |