1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "TextOverflow.h"
10 // Please maintain alphabetical order below
11 #include "nsBlockFrame.h"
13 #include "nsContentUtils.h"
14 #include "nsCSSAnonBoxes.h"
15 #include "nsGfxScrollFrame.h"
16 #include "nsIScrollableFrame.h"
17 #include "nsLayoutUtils.h"
18 #include "nsPresContext.h"
20 #include "nsRenderingContext.h"
21 #include "nsTextFrame.h"
22 #include "nsIFrameInlines.h"
23 #include "mozilla/ArrayUtils.h"
24 #include "mozilla/Likely.h"
25 #include "nsISelection.h"
30 class LazyReferenceRenderingContextGetterFromFrame MOZ_FINAL
:
31 public gfxFontGroup::LazyReferenceContextGetter
{
33 explicit LazyReferenceRenderingContextGetterFromFrame(nsIFrame
* aFrame
)
35 virtual already_AddRefed
<gfxContext
> GetRefContext() MOZ_OVERRIDE
37 nsRefPtr
<nsRenderingContext
> rc
=
38 mFrame
->PresContext()->PresShell()->CreateReferenceRenderingContext();
39 nsRefPtr
<gfxContext
> ctx
= rc
->ThebesContext();
47 GetEllipsisTextRun(nsIFrame
* aFrame
)
49 nsRefPtr
<nsFontMetrics
> fm
;
50 nsLayoutUtils::GetFontMetricsForFrame(aFrame
, getter_AddRefs(fm
),
51 nsLayoutUtils::FontSizeInflationFor(aFrame
));
52 LazyReferenceRenderingContextGetterFromFrame
lazyRefContextGetter(aFrame
);
53 return fm
->GetThebesFontGroup()->GetEllipsisTextRun(
54 aFrame
->PresContext()->AppUnitsPerDevPixel(), lazyRefContextGetter
);
58 GetSelfOrNearestBlock(nsIFrame
* aFrame
)
60 return nsLayoutUtils::GetAsBlock(aFrame
) ? aFrame
:
61 nsLayoutUtils::FindNearestBlockAncestor(aFrame
);
64 // Return true if the frame is an atomic inline-level element.
65 // It's not supposed to be called for block frames since we never
66 // process block descendants for text-overflow.
68 IsAtomicElement(nsIFrame
* aFrame
, const nsIAtom
* aFrameType
)
70 NS_PRECONDITION(!nsLayoutUtils::GetAsBlock(aFrame
) ||
71 !aFrame
->IsBlockOutside(),
72 "unexpected block frame");
73 NS_PRECONDITION(aFrameType
!= nsGkAtoms::placeholderFrame
,
74 "unexpected placeholder frame");
75 return !aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
);
79 IsFullyClipped(nsTextFrame
* aFrame
, nscoord aLeft
, nscoord aRight
,
80 nscoord
* aSnappedLeft
, nscoord
* aSnappedRight
)
82 *aSnappedLeft
= aLeft
;
83 *aSnappedRight
= aRight
;
84 if (aLeft
<= 0 && aRight
<= 0) {
87 return !aFrame
->MeasureCharClippedText(aLeft
, aRight
,
88 aSnappedLeft
, aSnappedRight
);
92 IsHorizontalOverflowVisible(nsIFrame
* aFrame
)
94 NS_PRECONDITION(nsLayoutUtils::GetAsBlock(aFrame
) != nullptr,
95 "expected a block frame");
98 while (f
&& f
->StyleContext()->GetPseudo() &&
99 f
->GetType() != nsGkAtoms::scrollFrame
) {
102 return !f
|| f
->StyleDisplay()->mOverflowX
== NS_STYLE_OVERFLOW_VISIBLE
;
106 ClipMarker(const nsRect
& aContentArea
,
107 const nsRect
& aMarkerRect
,
108 DisplayListClipState::AutoSaveRestore
& aClipState
)
110 nscoord rightOverflow
= aMarkerRect
.XMost() - aContentArea
.XMost();
111 nsRect markerRect
= aMarkerRect
;
112 if (rightOverflow
> 0) {
113 // Marker overflows on the right side (content width < marker width).
114 markerRect
.width
-= rightOverflow
;
115 aClipState
.ClipContentDescendants(markerRect
);
117 nscoord leftOverflow
= aContentArea
.x
- aMarkerRect
.x
;
118 if (leftOverflow
> 0) {
119 // Marker overflows on the left side
120 markerRect
.width
-= leftOverflow
;
121 markerRect
.x
+= leftOverflow
;
122 aClipState
.ClipContentDescendants(markerRect
);
128 InflateLeft(nsRect
* aRect
, nscoord aDelta
)
130 nscoord xmost
= aRect
->XMost();
132 aRect
->width
= std::max(xmost
- aRect
->x
, 0);
136 InflateRight(nsRect
* aRect
, nscoord aDelta
)
138 aRect
->width
= std::max(aRect
->width
+ aDelta
, 0);
142 IsFrameDescendantOfAny(nsIFrame
* aChild
,
143 const TextOverflow::FrameHashtable
& aSetOfFrames
,
144 nsIFrame
* aCommonAncestor
)
146 for (nsIFrame
* f
= aChild
; f
&& f
!= aCommonAncestor
;
147 f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
148 if (aSetOfFrames
.GetEntry(f
)) {
155 class nsDisplayTextOverflowMarker
: public nsDisplayItem
158 nsDisplayTextOverflowMarker(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
159 const nsRect
& aRect
, nscoord aAscent
,
160 const nsStyleTextOverflowSide
* aStyle
,
162 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
),
163 mStyle(aStyle
), mAscent(aAscent
), mIndex(aIndex
) {
164 MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker
);
166 #ifdef NS_BUILD_REFCNT_LOGGING
167 virtual ~nsDisplayTextOverflowMarker() {
168 MOZ_COUNT_DTOR(nsDisplayTextOverflowMarker
);
171 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
172 bool* aSnap
) MOZ_OVERRIDE
{
175 nsLayoutUtils::GetTextShadowRectsUnion(mRect
, mFrame
);
176 return mRect
.Union(shadowRect
);
178 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
179 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
181 virtual uint32_t GetPerFrameKey() MOZ_OVERRIDE
{
182 return (mIndex
<< nsDisplayItem::TYPE_BITS
) | nsDisplayItem::GetPerFrameKey();
184 void PaintTextToContext(nsRenderingContext
* aCtx
,
185 nsPoint aOffsetFromRect
);
186 NS_DISPLAY_DECL_NAME("TextOverflow", TYPE_TEXT_OVERFLOW
)
188 nsRect mRect
; // in reference frame coordinates
189 const nsStyleTextOverflowSide
* mStyle
;
190 nscoord mAscent
; // baseline for the marker text in mRect
195 PaintTextShadowCallback(nsRenderingContext
* aCtx
,
196 nsPoint aShadowOffset
,
197 const nscolor
& aShadowColor
,
200 reinterpret_cast<nsDisplayTextOverflowMarker
*>(aData
)->
201 PaintTextToContext(aCtx
, aShadowOffset
);
205 nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder
* aBuilder
,
206 nsRenderingContext
* aCtx
)
208 nscolor foregroundColor
=
209 nsLayoutUtils::GetColor(mFrame
, eCSSProperty_color
);
211 // Paint the text-shadows for the overflow marker
212 nsLayoutUtils::PaintTextShadow(mFrame
, aCtx
, mRect
, mVisibleRect
,
213 foregroundColor
, PaintTextShadowCallback
,
215 aCtx
->SetColor(foregroundColor
);
216 PaintTextToContext(aCtx
, nsPoint(0, 0));
220 nsDisplayTextOverflowMarker::PaintTextToContext(nsRenderingContext
* aCtx
,
221 nsPoint aOffsetFromRect
)
223 gfxFloat y
= nsLayoutUtils::GetSnappedBaselineY(mFrame
, aCtx
->ThebesContext(),
225 nsPoint
baselinePt(mRect
.x
, NSToCoordFloor(y
));
226 nsPoint pt
= baselinePt
+ aOffsetFromRect
;
228 if (mStyle
->mType
== NS_STYLE_TEXT_OVERFLOW_ELLIPSIS
) {
229 gfxTextRun
* textRun
= GetEllipsisTextRun(mFrame
);
231 NS_ASSERTION(!textRun
->IsRightToLeft(),
232 "Ellipsis textruns should always be LTR!");
233 gfxPoint
gfxPt(pt
.x
, pt
.y
);
234 textRun
->Draw(aCtx
->ThebesContext(), gfxPt
, DrawMode::GLYPH_FILL
,
235 0, textRun
->GetLength(), nullptr, nullptr, nullptr);
238 nsRefPtr
<nsFontMetrics
> fm
;
239 nsLayoutUtils::GetFontMetricsForFrame(mFrame
, getter_AddRefs(fm
),
240 nsLayoutUtils::FontSizeInflationFor(mFrame
));
242 nsLayoutUtils::DrawString(mFrame
, aCtx
, mStyle
->mString
.get(),
243 mStyle
->mString
.Length(), pt
);
248 TextOverflow::Init(nsDisplayListBuilder
* aBuilder
,
249 nsIFrame
* aBlockFrame
)
252 mBlock
= aBlockFrame
;
253 mContentArea
= aBlockFrame
->GetContentRectRelativeToSelf();
254 mScrollableFrame
= nsLayoutUtils::GetScrollableFrameFor(aBlockFrame
);
255 uint8_t direction
= aBlockFrame
->StyleVisibility()->mDirection
;
256 mBlockIsRTL
= direction
== NS_STYLE_DIRECTION_RTL
;
257 mAdjustForPixelSnapping
= false;
259 if (!mScrollableFrame
) {
260 nsIAtom
* pseudoType
= aBlockFrame
->StyleContext()->GetPseudo();
261 if (pseudoType
== nsCSSAnonBoxes::mozXULAnonymousBlock
) {
263 nsLayoutUtils::GetScrollableFrameFor(aBlockFrame
->GetParent());
264 // nsXULScrollFrame::ClampAndSetBounds rounds to nearest pixels
265 // for RTL blocks (also for overflow:hidden), so we need to move
266 // the edges 1px outward in ExamineLineFrames to avoid triggering
267 // a text-overflow marker in this case.
268 mAdjustForPixelSnapping
= mBlockIsRTL
;
272 mCanHaveHorizontalScrollbar
= false;
273 if (mScrollableFrame
) {
274 mCanHaveHorizontalScrollbar
=
275 mScrollableFrame
->GetScrollbarStyles().mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
;
276 if (!mAdjustForPixelSnapping
) {
277 // Scrolling to the end position can leave some text still overflowing due
278 // to pixel snapping behaviour in our scrolling code.
279 mAdjustForPixelSnapping
= mCanHaveHorizontalScrollbar
;
281 mContentArea
.MoveBy(mScrollableFrame
->GetScrollPosition());
282 nsIFrame
* scrollFrame
= do_QueryFrame(mScrollableFrame
);
283 scrollFrame
->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL
);
285 const nsStyleTextReset
* style
= aBlockFrame
->StyleTextReset();
286 mLeft
.Init(style
->mTextOverflow
.GetLeft(direction
));
287 mRight
.Init(style
->mTextOverflow
.GetRight(direction
));
288 // The left/right marker string is setup in ExamineLineFrames when a line
289 // has overflow on that side.
292 /* static */ TextOverflow
*
293 TextOverflow::WillProcessLines(nsDisplayListBuilder
* aBuilder
,
294 nsIFrame
* aBlockFrame
)
296 if (!CanHaveTextOverflow(aBuilder
, aBlockFrame
)) {
299 nsAutoPtr
<TextOverflow
> textOverflow(new TextOverflow
);
300 textOverflow
->Init(aBuilder
, aBlockFrame
);
301 return textOverflow
.forget();
305 TextOverflow::ExamineFrameSubtree(nsIFrame
* aFrame
,
306 const nsRect
& aContentArea
,
307 const nsRect
& aInsideMarkersArea
,
308 FrameHashtable
* aFramesToHide
,
309 AlignmentEdges
* aAlignmentEdges
,
310 bool* aFoundVisibleTextOrAtomic
,
311 InnerClipEdges
* aClippedMarkerEdges
)
313 const nsIAtom
* frameType
= aFrame
->GetType();
314 if (frameType
== nsGkAtoms::brFrame
||
315 frameType
== nsGkAtoms::placeholderFrame
) {
318 const bool isAtomic
= IsAtomicElement(aFrame
, frameType
);
319 if (aFrame
->StyleVisibility()->IsVisible()) {
320 nsRect childRect
= aFrame
->GetScrollableOverflowRect() +
321 aFrame
->GetOffsetTo(mBlock
);
322 bool overflowLeft
= childRect
.x
< aContentArea
.x
;
323 bool overflowRight
= childRect
.XMost() > aContentArea
.XMost();
325 mLeft
.mHasOverflow
= true;
328 mRight
.mHasOverflow
= true;
330 if (isAtomic
&& ((mLeft
.mActive
&& overflowLeft
) ||
331 (mRight
.mActive
&& overflowRight
))) {
332 aFramesToHide
->PutEntry(aFrame
);
333 } else if (isAtomic
|| frameType
== nsGkAtoms::textFrame
) {
334 AnalyzeMarkerEdges(aFrame
, frameType
, aInsideMarkersArea
,
335 aFramesToHide
, aAlignmentEdges
,
336 aFoundVisibleTextOrAtomic
,
337 aClippedMarkerEdges
);
344 nsIFrame
* child
= aFrame
->GetFirstPrincipalChild();
346 ExamineFrameSubtree(child
, aContentArea
, aInsideMarkersArea
,
347 aFramesToHide
, aAlignmentEdges
,
348 aFoundVisibleTextOrAtomic
,
349 aClippedMarkerEdges
);
350 child
= child
->GetNextSibling();
355 TextOverflow::AnalyzeMarkerEdges(nsIFrame
* aFrame
,
356 const nsIAtom
* aFrameType
,
357 const nsRect
& aInsideMarkersArea
,
358 FrameHashtable
* aFramesToHide
,
359 AlignmentEdges
* aAlignmentEdges
,
360 bool* aFoundVisibleTextOrAtomic
,
361 InnerClipEdges
* aClippedMarkerEdges
)
363 nsRect
borderRect(aFrame
->GetOffsetTo(mBlock
), aFrame
->GetSize());
364 nscoord leftOverlap
=
365 std::max(aInsideMarkersArea
.x
- borderRect
.x
, 0);
366 nscoord rightOverlap
=
367 std::max(borderRect
.XMost() - aInsideMarkersArea
.XMost(), 0);
368 bool insideLeftEdge
= aInsideMarkersArea
.x
<= borderRect
.XMost();
369 bool insideRightEdge
= borderRect
.x
<= aInsideMarkersArea
.XMost();
371 if (leftOverlap
> 0) {
372 aClippedMarkerEdges
->AccumulateLeft(borderRect
);
373 if (!mLeft
.mActive
) {
377 if (rightOverlap
> 0) {
378 aClippedMarkerEdges
->AccumulateRight(borderRect
);
379 if (!mRight
.mActive
) {
384 if ((leftOverlap
> 0 && insideLeftEdge
) ||
385 (rightOverlap
> 0 && insideRightEdge
)) {
386 if (aFrameType
== nsGkAtoms::textFrame
) {
387 if (aInsideMarkersArea
.x
< aInsideMarkersArea
.XMost()) {
388 // a clipped text frame and there is some room between the markers
389 nscoord snappedLeft
, snappedRight
;
390 bool isFullyClipped
=
391 IsFullyClipped(static_cast<nsTextFrame
*>(aFrame
),
392 leftOverlap
, rightOverlap
, &snappedLeft
, &snappedRight
);
393 if (!isFullyClipped
) {
394 nsRect snappedRect
= borderRect
;
395 if (leftOverlap
> 0) {
396 snappedRect
.x
+= snappedLeft
;
397 snappedRect
.width
-= snappedLeft
;
399 if (rightOverlap
> 0) {
400 snappedRect
.width
-= snappedRight
;
402 aAlignmentEdges
->Accumulate(snappedRect
);
403 *aFoundVisibleTextOrAtomic
= true;
407 aFramesToHide
->PutEntry(aFrame
);
409 } else if (!insideLeftEdge
|| !insideRightEdge
) {
411 if (IsAtomicElement(aFrame
, aFrameType
)) {
412 aFramesToHide
->PutEntry(aFrame
);
416 aAlignmentEdges
->Accumulate(borderRect
);
417 *aFoundVisibleTextOrAtomic
= true;
422 TextOverflow::ExamineLineFrames(nsLineBox
* aLine
,
423 FrameHashtable
* aFramesToHide
,
424 AlignmentEdges
* aAlignmentEdges
)
426 // No ellipsing for 'clip' style.
427 bool suppressLeft
= mLeft
.mStyle
->mType
== NS_STYLE_TEXT_OVERFLOW_CLIP
;
428 bool suppressRight
= mRight
.mStyle
->mType
== NS_STYLE_TEXT_OVERFLOW_CLIP
;
429 if (mCanHaveHorizontalScrollbar
) {
430 nsPoint pos
= mScrollableFrame
->GetScrollPosition();
431 nsRect scrollRange
= mScrollableFrame
->GetScrollRange();
432 // No ellipsing when nothing to scroll to on that side (this includes
433 // overflow:auto that doesn't trigger a horizontal scrollbar).
434 if (pos
.x
<= scrollRange
.x
) {
437 if (pos
.x
>= scrollRange
.XMost()) {
438 suppressRight
= true;
442 nsRect contentArea
= mContentArea
;
443 const nscoord scrollAdjust
= mAdjustForPixelSnapping
?
444 mBlock
->PresContext()->AppUnitsPerDevPixel() : 0;
445 InflateLeft(&contentArea
, scrollAdjust
);
446 InflateRight(&contentArea
, scrollAdjust
);
447 nsRect lineRect
= aLine
->GetScrollableOverflowArea();
448 const bool leftOverflow
=
449 !suppressLeft
&& lineRect
.x
< contentArea
.x
;
450 const bool rightOverflow
=
451 !suppressRight
&& lineRect
.XMost() > contentArea
.XMost();
452 if (!leftOverflow
&& !rightOverflow
) {
453 // The line does not overflow on a side we should ellipsize.
458 bool retryEmptyLine
= true;
459 bool guessLeft
= leftOverflow
;
460 bool guessRight
= rightOverflow
;
461 mLeft
.mActive
= leftOverflow
;
462 mRight
.mActive
= rightOverflow
;
463 bool clippedLeftMarker
= false;
464 bool clippedRightMarker
= false;
466 // Setup marker strings as needed.
468 mLeft
.SetupString(mBlock
);
471 mRight
.SetupString(mBlock
);
474 // If there is insufficient space for both markers then keep the one on the
475 // end side per the block's 'direction'.
476 nscoord rightMarkerWidth
= mRight
.mActive
? mRight
.mWidth
: 0;
477 nscoord leftMarkerWidth
= mLeft
.mActive
? mLeft
.mWidth
: 0;
478 if (leftMarkerWidth
&& rightMarkerWidth
&&
479 leftMarkerWidth
+ rightMarkerWidth
> contentArea
.width
) {
481 rightMarkerWidth
= 0;
487 // Calculate the area between the potential markers aligned at the
489 nsRect insideMarkersArea
= mContentArea
;
491 InflateLeft(&insideMarkersArea
, -leftMarkerWidth
);
494 InflateRight(&insideMarkersArea
, -rightMarkerWidth
);
497 // Analyze the frames on aLine for the overflow situation at the content
498 // edges and at the edges of the area between the markers.
499 bool foundVisibleTextOrAtomic
= false;
500 int32_t n
= aLine
->GetChildCount();
501 nsIFrame
* child
= aLine
->mFirstChild
;
502 InnerClipEdges clippedMarkerEdges
;
503 for (; n
-- > 0; child
= child
->GetNextSibling()) {
504 ExamineFrameSubtree(child
, contentArea
, insideMarkersArea
,
505 aFramesToHide
, aAlignmentEdges
,
506 &foundVisibleTextOrAtomic
,
507 &clippedMarkerEdges
);
509 if (!foundVisibleTextOrAtomic
&& retryEmptyLine
) {
510 aAlignmentEdges
->mAssigned
= false;
511 aFramesToHide
->Clear();
513 if (mLeft
.IsNeeded() && mLeft
.mActive
&& !clippedLeftMarker
) {
514 if (clippedMarkerEdges
.mAssignedLeft
&&
515 clippedMarkerEdges
.mLeft
- mContentArea
.X() > 0) {
516 mLeft
.mWidth
= clippedMarkerEdges
.mLeft
- mContentArea
.X();
517 NS_ASSERTION(mLeft
.mWidth
< mLeft
.mIntrinsicISize
,
518 "clipping a marker should make it strictly smaller");
519 clippedLeftMarker
= true;
521 mLeft
.mActive
= guessLeft
= false;
525 if (mRight
.IsNeeded() && mRight
.mActive
&& !clippedRightMarker
) {
526 if (clippedMarkerEdges
.mAssignedRight
&&
527 mContentArea
.XMost() - clippedMarkerEdges
.mRight
> 0) {
528 mRight
.mWidth
= mContentArea
.XMost() - clippedMarkerEdges
.mRight
;
529 NS_ASSERTION(mRight
.mWidth
< mRight
.mIntrinsicISize
,
530 "clipping a marker should make it strictly smaller");
531 clippedRightMarker
= true;
533 mRight
.mActive
= guessRight
= false;
537 // The line simply has no visible content even without markers,
538 // so examine the line again without suppressing markers.
539 retryEmptyLine
= false;
540 mLeft
.mWidth
= mLeft
.mIntrinsicISize
;
541 mLeft
.mActive
= guessLeft
= leftOverflow
;
542 mRight
.mWidth
= mRight
.mIntrinsicISize
;
543 mRight
.mActive
= guessRight
= rightOverflow
;
546 if (guessLeft
== (mLeft
.mActive
&& mLeft
.IsNeeded()) &&
547 guessRight
== (mRight
.mActive
&& mRight
.IsNeeded())) {
550 guessLeft
= mLeft
.mActive
&& mLeft
.IsNeeded();
551 guessRight
= mRight
.mActive
&& mRight
.IsNeeded();
554 aFramesToHide
->Clear();
556 NS_ASSERTION(pass
== 0, "2nd pass should never guess wrong");
557 } while (++pass
!= 2);
558 if (!leftOverflow
|| !mLeft
.mActive
) {
561 if (!rightOverflow
|| !mRight
.mActive
) {
567 TextOverflow::ProcessLine(const nsDisplayListSet
& aLists
,
570 NS_ASSERTION(mLeft
.mStyle
->mType
!= NS_STYLE_TEXT_OVERFLOW_CLIP
||
571 mRight
.mStyle
->mType
!= NS_STYLE_TEXT_OVERFLOW_CLIP
,
572 "TextOverflow with 'clip' for both sides");
574 mLeft
.mActive
= mLeft
.mStyle
->mType
!= NS_STYLE_TEXT_OVERFLOW_CLIP
;
576 mRight
.mActive
= mRight
.mStyle
->mType
!= NS_STYLE_TEXT_OVERFLOW_CLIP
;
578 FrameHashtable
framesToHide(64);
579 AlignmentEdges alignmentEdges
;
580 ExamineLineFrames(aLine
, &framesToHide
, &alignmentEdges
);
581 bool needLeft
= mLeft
.IsNeeded();
582 bool needRight
= mRight
.IsNeeded();
583 if (!needLeft
&& !needRight
) {
586 NS_ASSERTION(mLeft
.mStyle
->mType
!= NS_STYLE_TEXT_OVERFLOW_CLIP
||
587 !needLeft
, "left marker for 'clip'");
588 NS_ASSERTION(mRight
.mStyle
->mType
!= NS_STYLE_TEXT_OVERFLOW_CLIP
||
589 !needRight
, "right marker for 'clip'");
591 // If there is insufficient space for both markers then keep the one on the
592 // end side per the block's 'direction'.
593 if (needLeft
&& needRight
&&
594 mLeft
.mWidth
+ mRight
.mWidth
> mContentArea
.width
) {
601 nsRect insideMarkersArea
= mContentArea
;
603 InflateLeft(&insideMarkersArea
, -mLeft
.mWidth
);
606 InflateRight(&insideMarkersArea
, -mRight
.mWidth
);
608 if (!mCanHaveHorizontalScrollbar
&& alignmentEdges
.mAssigned
) {
609 nsRect alignmentRect
= nsRect(alignmentEdges
.x
, insideMarkersArea
.y
,
610 alignmentEdges
.Width(), 1);
611 insideMarkersArea
.IntersectRect(insideMarkersArea
, alignmentRect
);
614 // Clip and remove display items as needed at the final marker edges.
615 nsDisplayList
* lists
[] = { aLists
.Content(), aLists
.PositionedDescendants() };
616 for (uint32_t i
= 0; i
< ArrayLength(lists
); ++i
) {
617 PruneDisplayListContents(lists
[i
], framesToHide
, insideMarkersArea
);
619 CreateMarkers(aLine
, needLeft
, needRight
, insideMarkersArea
);
623 TextOverflow::PruneDisplayListContents(nsDisplayList
* aList
,
624 const FrameHashtable
& aFramesToHide
,
625 const nsRect
& aInsideMarkersArea
)
629 while ((item
= aList
->RemoveBottom())) {
630 nsIFrame
* itemFrame
= item
->Frame();
631 if (IsFrameDescendantOfAny(itemFrame
, aFramesToHide
, mBlock
)) {
632 item
->~nsDisplayItem();
636 nsDisplayList
* wrapper
= item
->GetSameCoordinateSystemChildren();
638 if (!itemFrame
|| GetSelfOrNearestBlock(itemFrame
) == mBlock
) {
639 PruneDisplayListContents(wrapper
, aFramesToHide
, aInsideMarkersArea
);
643 nsCharClipDisplayItem
* charClip
= itemFrame
?
644 nsCharClipDisplayItem::CheckCast(item
) : nullptr;
645 if (charClip
&& GetSelfOrNearestBlock(itemFrame
) == mBlock
) {
646 nsRect rect
= itemFrame
->GetScrollableOverflowRect() +
647 itemFrame
->GetOffsetTo(mBlock
);
648 if (mLeft
.IsNeeded() && rect
.x
< aInsideMarkersArea
.x
) {
649 nscoord left
= aInsideMarkersArea
.x
- rect
.x
;
650 if (MOZ_UNLIKELY(left
< 0)) {
651 item
->~nsDisplayItem();
654 charClip
->mLeftEdge
= left
;
656 if (mRight
.IsNeeded() && rect
.XMost() > aInsideMarkersArea
.XMost()) {
657 nscoord right
= rect
.XMost() - aInsideMarkersArea
.XMost();
658 if (MOZ_UNLIKELY(right
< 0)) {
659 item
->~nsDisplayItem();
662 charClip
->mRightEdge
= right
;
666 saved
.AppendToTop(item
);
668 aList
->AppendToTop(&saved
);
672 TextOverflow::CanHaveTextOverflow(nsDisplayListBuilder
* aBuilder
,
673 nsIFrame
* aBlockFrame
)
675 const nsStyleTextReset
* style
= aBlockFrame
->StyleTextReset();
676 // Nothing to do for text-overflow:clip or if 'overflow-x:visible' or if
677 // we're just building items for event processing or image visibility.
678 if ((style
->mTextOverflow
.mLeft
.mType
== NS_STYLE_TEXT_OVERFLOW_CLIP
&&
679 style
->mTextOverflow
.mRight
.mType
== NS_STYLE_TEXT_OVERFLOW_CLIP
) ||
680 IsHorizontalOverflowVisible(aBlockFrame
) ||
681 aBuilder
->IsForEventDelivery() || aBuilder
->IsForImageVisibility()) {
685 // Skip ComboboxControlFrame because it would clip the drop-down arrow.
686 // Its anon block inherits 'text-overflow' and does what is expected.
687 if (aBlockFrame
->GetType() == nsGkAtoms::comboboxControlFrame
) {
691 // Inhibit the markers if a descendant content owns the caret.
692 nsRefPtr
<nsCaret
> caret
= aBlockFrame
->PresContext()->PresShell()->GetCaret();
693 if (caret
&& caret
->IsVisible()) {
694 nsCOMPtr
<nsISelection
> domSelection
= caret
->GetSelection();
696 nsCOMPtr
<nsIDOMNode
> node
;
697 domSelection
->GetFocusNode(getter_AddRefs(node
));
698 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(node
);
699 if (content
&& nsContentUtils::ContentIsDescendantOf(content
,
700 aBlockFrame
->GetContent())) {
709 TextOverflow::CreateMarkers(const nsLineBox
* aLine
,
712 const nsRect
& aInsideMarkersArea
)
715 DisplayListClipState::AutoSaveRestore
clipState(mBuilder
);
717 //XXX Needs vertical text love
718 nsRect markerRect
= nsRect(aInsideMarkersArea
.x
- mLeft
.mIntrinsicISize
,
720 mLeft
.mIntrinsicISize
, aLine
->BSize());
721 markerRect
+= mBuilder
->ToReferenceFrame(mBlock
);
722 ClipMarker(mContentArea
+ mBuilder
->ToReferenceFrame(mBlock
),
723 markerRect
, clipState
);
724 nsDisplayItem
* marker
= new (mBuilder
)
725 nsDisplayTextOverflowMarker(mBuilder
, mBlock
, markerRect
,
726 aLine
->GetLogicalAscent(), mLeft
.mStyle
, 0);
727 mMarkerList
.AppendNewToTop(marker
);
731 DisplayListClipState::AutoSaveRestore
clipState(mBuilder
);
733 nsRect markerRect
= nsRect(aInsideMarkersArea
.XMost(),
735 mRight
.mIntrinsicISize
, aLine
->BSize());
736 markerRect
+= mBuilder
->ToReferenceFrame(mBlock
);
737 ClipMarker(mContentArea
+ mBuilder
->ToReferenceFrame(mBlock
),
738 markerRect
, clipState
);
739 nsDisplayItem
* marker
= new (mBuilder
)
740 nsDisplayTextOverflowMarker(mBuilder
, mBlock
, markerRect
,
741 aLine
->GetLogicalAscent(), mRight
.mStyle
, 1);
742 mMarkerList
.AppendNewToTop(marker
);
747 TextOverflow::Marker::SetupString(nsIFrame
* aFrame
)
753 if (mStyle
->mType
== NS_STYLE_TEXT_OVERFLOW_ELLIPSIS
) {
754 gfxTextRun
* textRun
= GetEllipsisTextRun(aFrame
);
756 mWidth
= textRun
->GetAdvanceWidth(0, textRun
->GetLength(), nullptr);
761 nsRefPtr
<nsRenderingContext
> rc
=
762 aFrame
->PresContext()->PresShell()->CreateReferenceRenderingContext();
763 nsRefPtr
<nsFontMetrics
> fm
;
764 nsLayoutUtils::GetFontMetricsForFrame(aFrame
, getter_AddRefs(fm
),
765 nsLayoutUtils::FontSizeInflationFor(aFrame
));
767 mWidth
= nsLayoutUtils::GetStringWidth(aFrame
, rc
, mStyle
->mString
.get(),
768 mStyle
->mString
.Length());
770 mIntrinsicISize
= mWidth
;
775 } // namespace mozilla