LCOV - code coverage report
Current view: top level - layout/generic - TextOverflow.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 12 442 2.7 %
Date: 2017-07-14 16:53:18 Functions: 3 33 9.1 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "TextOverflow.h"
       8             : #include <algorithm>
       9             : 
      10             : // Please maintain alphabetical order below
      11             : #include "gfxContext.h"
      12             : #include "nsBlockFrame.h"
      13             : #include "nsCaret.h"
      14             : #include "nsContentUtils.h"
      15             : #include "nsCSSAnonBoxes.h"
      16             : #include "nsFontMetrics.h"
      17             : #include "nsGfxScrollFrame.h"
      18             : #include "nsIScrollableFrame.h"
      19             : #include "nsLayoutUtils.h"
      20             : #include "nsPresContext.h"
      21             : #include "nsRect.h"
      22             : #include "nsTextFrame.h"
      23             : #include "nsIFrameInlines.h"
      24             : #include "mozilla/ArrayUtils.h"
      25             : #include "mozilla/Likely.h"
      26             : #include "nsISelection.h"
      27             : 
      28             : namespace mozilla {
      29             : namespace css {
      30             : 
      31             : class LazyReferenceRenderingDrawTargetGetterFromFrame final :
      32             :     public gfxFontGroup::LazyReferenceDrawTargetGetter {
      33             : public:
      34             :   typedef mozilla::gfx::DrawTarget DrawTarget;
      35             : 
      36           0 :   explicit LazyReferenceRenderingDrawTargetGetterFromFrame(nsIFrame* aFrame)
      37           0 :     : mFrame(aFrame) {}
      38           0 :   virtual already_AddRefed<DrawTarget> GetRefDrawTarget() override
      39             :   {
      40             :     RefPtr<gfxContext> ctx =
      41           0 :       mFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
      42           0 :     RefPtr<DrawTarget> dt = ctx->GetDrawTarget();
      43           0 :     return dt.forget();
      44             :   }
      45             : private:
      46             :   nsIFrame* mFrame;
      47             : };
      48             : 
      49             : static gfxTextRun*
      50           0 : GetEllipsisTextRun(nsIFrame* aFrame)
      51             : {
      52             :   RefPtr<nsFontMetrics> fm =
      53           0 :     nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
      54           0 :   LazyReferenceRenderingDrawTargetGetterFromFrame lazyRefDrawTargetGetter(aFrame);
      55           0 :   return fm->GetThebesFontGroup()->GetEllipsisTextRun(
      56             :     aFrame->PresContext()->AppUnitsPerDevPixel(),
      57             :     nsLayoutUtils::GetTextRunOrientFlagsForStyle(aFrame->StyleContext()),
      58           0 :     lazyRefDrawTargetGetter);
      59             : }
      60             : 
      61             : static nsIFrame*
      62           0 : GetSelfOrNearestBlock(nsIFrame* aFrame)
      63             : {
      64           0 :   return nsLayoutUtils::GetAsBlock(aFrame) ? aFrame :
      65           0 :          nsLayoutUtils::FindNearestBlockAncestor(aFrame);
      66             : }
      67             : 
      68             : // Return true if the frame is an atomic inline-level element.
      69             : // It's not supposed to be called for block frames since we never
      70             : // process block descendants for text-overflow.
      71             : static bool
      72           0 : IsAtomicElement(nsIFrame* aFrame, LayoutFrameType aFrameType)
      73             : {
      74           0 :   NS_PRECONDITION(!nsLayoutUtils::GetAsBlock(aFrame) ||
      75             :                   !aFrame->IsBlockOutside(),
      76             :                   "unexpected block frame");
      77           0 :   NS_PRECONDITION(aFrameType != LayoutFrameType::Placeholder,
      78             :                   "unexpected placeholder frame");
      79           0 :   return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
      80             : }
      81             : 
      82             : static bool
      83           0 : IsFullyClipped(nsTextFrame* aFrame, nscoord aLeft, nscoord aRight,
      84             :                nscoord* aSnappedLeft, nscoord* aSnappedRight)
      85             : {
      86           0 :   *aSnappedLeft = aLeft;
      87           0 :   *aSnappedRight = aRight;
      88           0 :   if (aLeft <= 0 && aRight <= 0) {
      89           0 :     return false;
      90             :   }
      91           0 :   return !aFrame->MeasureCharClippedText(aLeft, aRight,
      92           0 :                                          aSnappedLeft, aSnappedRight);
      93             : }
      94             : 
      95             : static bool
      96           0 : IsInlineAxisOverflowVisible(nsIFrame* aFrame)
      97             : {
      98           0 :   NS_PRECONDITION(nsLayoutUtils::GetAsBlock(aFrame) != nullptr,
      99             :                   "expected a block frame");
     100             : 
     101           0 :   nsIFrame* f = aFrame;
     102           0 :   while (f && f->StyleContext()->GetPseudo() && !f->IsScrollFrame()) {
     103           0 :     f = f->GetParent();
     104             :   }
     105           0 :   if (!f) {
     106           0 :     return true;
     107             :   }
     108           0 :   auto overflow = aFrame->GetWritingMode().IsVertical() ?
     109           0 :     f->StyleDisplay()->mOverflowY : f->StyleDisplay()->mOverflowX;
     110           0 :   return overflow == NS_STYLE_OVERFLOW_VISIBLE;
     111             : }
     112             : 
     113             : static void
     114           0 : ClipMarker(const nsRect&                          aContentArea,
     115             :            const nsRect&                          aMarkerRect,
     116             :            DisplayListClipState::AutoSaveRestore& aClipState)
     117             : {
     118           0 :   nscoord rightOverflow = aMarkerRect.XMost() - aContentArea.XMost();
     119           0 :   nsRect markerRect = aMarkerRect;
     120           0 :   if (rightOverflow > 0) {
     121             :     // Marker overflows on the right side (content width < marker width).
     122           0 :     markerRect.width -= rightOverflow;
     123           0 :     aClipState.ClipContentDescendants(markerRect);
     124             :   } else {
     125           0 :     nscoord leftOverflow = aContentArea.x - aMarkerRect.x;
     126           0 :     if (leftOverflow > 0) {
     127             :       // Marker overflows on the left side
     128           0 :       markerRect.width -= leftOverflow;
     129           0 :       markerRect.x += leftOverflow;
     130           0 :       aClipState.ClipContentDescendants(markerRect);
     131             :     }
     132             :   }
     133           0 : }
     134             : 
     135             : static void
     136           0 : InflateIStart(WritingMode aWM, LogicalRect* aRect, nscoord aDelta)
     137             : {
     138           0 :   nscoord iend = aRect->IEnd(aWM);
     139           0 :   aRect->IStart(aWM) -= aDelta;
     140           0 :   aRect->ISize(aWM) = std::max(iend - aRect->IStart(aWM), 0);
     141           0 : }
     142             : 
     143             : static void
     144           0 : InflateIEnd(WritingMode aWM, LogicalRect* aRect, nscoord aDelta)
     145             : {
     146           0 :   aRect->ISize(aWM) = std::max(aRect->ISize(aWM) + aDelta, 0);
     147           0 : }
     148             : 
     149             : static bool
     150           0 : IsFrameDescendantOfAny(nsIFrame* aChild,
     151             :                        const TextOverflow::FrameHashtable& aSetOfFrames,
     152             :                        nsIFrame* aCommonAncestor)
     153             : {
     154           0 :   for (nsIFrame* f = aChild; f && f != aCommonAncestor;
     155             :        f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
     156           0 :     if (aSetOfFrames.GetEntry(f)) {
     157           0 :       return true;
     158             :     }
     159             :   }
     160           0 :   return false;
     161             : }
     162             : 
     163             : class nsDisplayTextOverflowMarker : public nsDisplayItem
     164             : {
     165             : public:
     166           0 :   nsDisplayTextOverflowMarker(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
     167             :                               const nsRect& aRect, nscoord aAscent,
     168             :                               const nsStyleTextOverflowSide* aStyle,
     169             :                               uint32_t aIndex)
     170           0 :     : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
     171           0 :       mStyle(aStyle), mAscent(aAscent), mIndex(aIndex) {
     172           0 :     MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker);
     173           0 :   }
     174             : #ifdef NS_BUILD_REFCNT_LOGGING
     175           0 :   virtual ~nsDisplayTextOverflowMarker() {
     176           0 :     MOZ_COUNT_DTOR(nsDisplayTextOverflowMarker);
     177           0 :   }
     178             : #endif
     179           0 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
     180             :                            bool* aSnap) override {
     181           0 :     *aSnap = false;
     182             :     nsRect shadowRect =
     183           0 :       nsLayoutUtils::GetTextShadowRectsUnion(mRect, mFrame);
     184           0 :     return mRect.Union(shadowRect);
     185             :   }
     186             : 
     187           0 :   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
     188             :   {
     189           0 :     if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
     190             :       // On OS X, web authors can turn off subpixel text rendering using the
     191             :       // CSS property -moz-osx-font-smoothing. If they do that, we don't need
     192             :       // to use component alpha layers for the affected text.
     193           0 :       if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
     194           0 :         return nsRect();
     195             :       }
     196             :     }
     197             :     bool snap;
     198           0 :     return GetBounds(aBuilder, &snap);
     199             :   }
     200             : 
     201             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
     202             :                      gfxContext* aCtx) override;
     203             : 
     204           0 :   virtual uint32_t GetPerFrameKey() override {
     205           0 :     return (mIndex << nsDisplayItem::TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
     206             :   }
     207             :   void PaintTextToContext(gfxContext* aCtx,
     208             :                           nsPoint aOffsetFromRect);
     209           0 :   NS_DISPLAY_DECL_NAME("TextOverflow", TYPE_TEXT_OVERFLOW)
     210             : private:
     211             :   nsRect          mRect;   // in reference frame coordinates
     212             :   const nsStyleTextOverflowSide* mStyle;
     213             :   nscoord         mAscent; // baseline for the marker text in mRect
     214             :   uint32_t        mIndex;
     215             : };
     216             : 
     217             : static void
     218           0 : PaintTextShadowCallback(gfxContext* aCtx,
     219             :                         nsPoint aShadowOffset,
     220             :                         const nscolor& aShadowColor,
     221             :                         void* aData)
     222             : {
     223             :   reinterpret_cast<nsDisplayTextOverflowMarker*>(aData)->
     224           0 :            PaintTextToContext(aCtx, aShadowOffset);
     225           0 : }
     226             : 
     227             : void
     228           0 : nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
     229             :                                    gfxContext*           aCtx)
     230             : {
     231             :   nscolor foregroundColor = nsLayoutUtils::
     232           0 :     GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
     233             : 
     234             :   // Paint the text-shadows for the overflow marker
     235           0 :   nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect, mVisibleRect,
     236             :                                  foregroundColor, PaintTextShadowCallback,
     237           0 :                                  (void*)this);
     238           0 :   aCtx->SetColor(gfx::Color::FromABGR(foregroundColor));
     239           0 :   PaintTextToContext(aCtx, nsPoint(0, 0));
     240           0 : }
     241             : 
     242             : void
     243           0 : nsDisplayTextOverflowMarker::PaintTextToContext(gfxContext* aCtx,
     244             :                                                 nsPoint aOffsetFromRect)
     245             : {
     246           0 :   WritingMode wm = mFrame->GetWritingMode();
     247           0 :   nsPoint pt(mRect.x, mRect.y);
     248           0 :   if (wm.IsVertical()) {
     249           0 :     if (wm.IsVerticalLR()) {
     250           0 :       pt.x = NSToCoordFloor(nsLayoutUtils::GetSnappedBaselineX(
     251             :         mFrame, aCtx, pt.x, mAscent));
     252             :     } else {
     253           0 :       pt.x = NSToCoordFloor(nsLayoutUtils::GetSnappedBaselineX(
     254           0 :         mFrame, aCtx, pt.x + mRect.width, -mAscent));
     255             :     }
     256             :   } else {
     257           0 :     pt.y = NSToCoordFloor(nsLayoutUtils::GetSnappedBaselineY(
     258             :       mFrame, aCtx, pt.y, mAscent));
     259             :   }
     260           0 :   pt += aOffsetFromRect;
     261             : 
     262           0 :   if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
     263           0 :     gfxTextRun* textRun = GetEllipsisTextRun(mFrame);
     264           0 :     if (textRun) {
     265           0 :       NS_ASSERTION(!textRun->IsRightToLeft(),
     266             :                    "Ellipsis textruns should always be LTR!");
     267           0 :       gfxPoint gfxPt(pt.x, pt.y);
     268           0 :       textRun->Draw(gfxTextRun::Range(textRun), gfxPt,
     269           0 :                     gfxTextRun::DrawParams(aCtx));
     270             :     }
     271             :   } else {
     272             :     RefPtr<nsFontMetrics> fm =
     273           0 :       nsLayoutUtils::GetInflatedFontMetricsForFrame(mFrame);
     274           0 :     nsLayoutUtils::DrawString(mFrame, *fm, aCtx, mStyle->mString.get(),
     275           0 :                               mStyle->mString.Length(), pt);
     276             :   }
     277           0 : }
     278             : 
     279           0 : TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
     280           0 :                            nsIFrame* aBlockFrame)
     281             :   : mContentArea(aBlockFrame->GetWritingMode(),
     282           0 :                  aBlockFrame->GetContentRectRelativeToSelf(),
     283           0 :                  aBlockFrame->GetSize())
     284             :   , mBuilder(aBuilder)
     285             :   , mBlock(aBlockFrame)
     286           0 :   , mScrollableFrame(nsLayoutUtils::GetScrollableFrameFor(aBlockFrame))
     287             :   , mBlockSize(aBlockFrame->GetSize())
     288             :   , mBlockWM(aBlockFrame->GetWritingMode())
     289           0 :   , mAdjustForPixelSnapping(false)
     290             : {
     291             : #ifdef MOZ_XUL
     292           0 :   if (!mScrollableFrame) {
     293           0 :     nsIAtom* pseudoType = aBlockFrame->StyleContext()->GetPseudo();
     294           0 :     if (pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
     295           0 :       mScrollableFrame =
     296           0 :         nsLayoutUtils::GetScrollableFrameFor(aBlockFrame->GetParent());
     297             :       // nsXULScrollFrame::ClampAndSetBounds rounds to nearest pixels
     298             :       // for RTL blocks (also for overflow:hidden), so we need to move
     299             :       // the edges 1px outward in ExamineLineFrames to avoid triggering
     300             :       // a text-overflow marker in this case.
     301           0 :       mAdjustForPixelSnapping = !mBlockWM.IsBidiLTR();
     302             :     }
     303             :   }
     304             : #endif
     305           0 :   mCanHaveInlineAxisScrollbar = false;
     306           0 :   if (mScrollableFrame) {
     307           0 :     auto scrollbarStyle = mBlockWM.IsVertical() ?
     308           0 :       mScrollableFrame->GetScrollbarStyles().mVertical :
     309           0 :       mScrollableFrame->GetScrollbarStyles().mHorizontal;
     310           0 :     mCanHaveInlineAxisScrollbar = scrollbarStyle != NS_STYLE_OVERFLOW_HIDDEN;
     311           0 :     if (!mAdjustForPixelSnapping) {
     312             :       // Scrolling to the end position can leave some text still overflowing due
     313             :       // to pixel snapping behaviour in our scrolling code.
     314           0 :       mAdjustForPixelSnapping = mCanHaveInlineAxisScrollbar;
     315             :     }
     316             :     // Use a null containerSize to convert a vector from logical to physical.
     317           0 :     const nsSize nullContainerSize;
     318           0 :     mContentArea.MoveBy(mBlockWM,
     319           0 :                         LogicalPoint(mBlockWM,
     320           0 :                                      mScrollableFrame->GetScrollPosition(),
     321           0 :                                      nullContainerSize));
     322           0 :     nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
     323           0 :     scrollFrame->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
     324             :   }
     325           0 :   uint8_t direction = aBlockFrame->StyleVisibility()->mDirection;
     326           0 :   const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
     327           0 :   if (mBlockWM.IsBidiLTR()) {
     328           0 :     mIStart.Init(style->mTextOverflow.GetLeft(direction));
     329           0 :     mIEnd.Init(style->mTextOverflow.GetRight(direction));
     330             :   } else {
     331           0 :     mIStart.Init(style->mTextOverflow.GetRight(direction));
     332           0 :     mIEnd.Init(style->mTextOverflow.GetLeft(direction));
     333             :   }
     334             :   // The left/right marker string is setup in ExamineLineFrames when a line
     335             :   // has overflow on that side.
     336           0 : }
     337             : 
     338             : /* static */ TextOverflow*
     339         199 : TextOverflow::WillProcessLines(nsDisplayListBuilder*   aBuilder,
     340             :                                nsIFrame*               aBlockFrame)
     341             : {
     342             :   // Ignore 'text-overflow' for event and frame visibility processing.
     343         597 :   if (aBuilder->IsForEventDelivery() ||
     344         398 :       aBuilder->IsForFrameVisibility() ||
     345         199 :       !CanHaveTextOverflow(aBlockFrame)) {
     346         199 :     return nullptr;
     347             :   }
     348           0 :   nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
     349           0 :   if (scrollableFrame && scrollableFrame->IsTransformingByAPZ()) {
     350             :     // If the APZ is actively scrolling this, don't bother with markers.
     351           0 :     return nullptr;
     352             :   }
     353           0 :   return new TextOverflow(aBuilder, aBlockFrame);
     354             : }
     355             : 
     356             : void
     357           0 : TextOverflow::ExamineFrameSubtree(nsIFrame*       aFrame,
     358             :                                   const LogicalRect& aContentArea,
     359             :                                   const LogicalRect& aInsideMarkersArea,
     360             :                                   FrameHashtable* aFramesToHide,
     361             :                                   AlignmentEdges* aAlignmentEdges,
     362             :                                   bool*           aFoundVisibleTextOrAtomic,
     363             :                                   InnerClipEdges* aClippedMarkerEdges)
     364             : {
     365           0 :   const LayoutFrameType frameType = aFrame->Type();
     366           0 :   if (frameType == LayoutFrameType::Br ||
     367             :       frameType == LayoutFrameType::Placeholder) {
     368           0 :     return;
     369             :   }
     370           0 :   const bool isAtomic = IsAtomicElement(aFrame, frameType);
     371           0 :   if (aFrame->StyleVisibility()->IsVisible()) {
     372             :     LogicalRect childRect =
     373           0 :       GetLogicalScrollableOverflowRectRelativeToBlock(aFrame);
     374             :     bool overflowIStart =
     375           0 :       childRect.IStart(mBlockWM) < aContentArea.IStart(mBlockWM);
     376             :     bool overflowIEnd =
     377           0 :       childRect.IEnd(mBlockWM) > aContentArea.IEnd(mBlockWM);
     378           0 :     if (overflowIStart) {
     379           0 :       mIStart.mHasOverflow = true;
     380             :     }
     381           0 :     if (overflowIEnd) {
     382           0 :       mIEnd.mHasOverflow = true;
     383             :     }
     384           0 :     if (isAtomic && ((mIStart.mActive && overflowIStart) ||
     385           0 :                      (mIEnd.mActive && overflowIEnd))) {
     386           0 :       aFramesToHide->PutEntry(aFrame);
     387           0 :     } else if (isAtomic || frameType == LayoutFrameType::Text) {
     388             :       AnalyzeMarkerEdges(aFrame, frameType, aInsideMarkersArea,
     389             :                          aFramesToHide, aAlignmentEdges,
     390             :                          aFoundVisibleTextOrAtomic,
     391           0 :                          aClippedMarkerEdges);
     392             :     }
     393             :   }
     394           0 :   if (isAtomic) {
     395           0 :     return;
     396             :   }
     397             : 
     398           0 :   for (nsIFrame* child : aFrame->PrincipalChildList()) {
     399             :     ExamineFrameSubtree(child, aContentArea, aInsideMarkersArea,
     400             :                         aFramesToHide, aAlignmentEdges,
     401             :                         aFoundVisibleTextOrAtomic,
     402           0 :                         aClippedMarkerEdges);
     403             :   }
     404             : }
     405             : 
     406             : void
     407           0 : TextOverflow::AnalyzeMarkerEdges(nsIFrame* aFrame,
     408             :                                  LayoutFrameType aFrameType,
     409             :                                  const LogicalRect& aInsideMarkersArea,
     410             :                                  FrameHashtable* aFramesToHide,
     411             :                                  AlignmentEdges* aAlignmentEdges,
     412             :                                  bool* aFoundVisibleTextOrAtomic,
     413             :                                  InnerClipEdges* aClippedMarkerEdges)
     414             : {
     415             :   LogicalRect borderRect(mBlockWM,
     416           0 :                          nsRect(aFrame->GetOffsetTo(mBlock),
     417           0 :                                 aFrame->GetSize()),
     418           0 :                          mBlockSize);
     419             :   nscoord istartOverlap = std::max(
     420           0 :     aInsideMarkersArea.IStart(mBlockWM) - borderRect.IStart(mBlockWM), 0);
     421             :   nscoord iendOverlap = std::max(
     422           0 :     borderRect.IEnd(mBlockWM) - aInsideMarkersArea.IEnd(mBlockWM), 0);
     423             :   bool insideIStartEdge =
     424           0 :     aInsideMarkersArea.IStart(mBlockWM) <= borderRect.IEnd(mBlockWM);
     425             :   bool insideIEndEdge =
     426           0 :     borderRect.IStart(mBlockWM) <= aInsideMarkersArea.IEnd(mBlockWM);
     427             : 
     428           0 :   if (istartOverlap > 0) {
     429           0 :     aClippedMarkerEdges->AccumulateIStart(mBlockWM, borderRect);
     430           0 :     if (!mIStart.mActive) {
     431           0 :       istartOverlap = 0;
     432             :     }
     433             :   }
     434           0 :   if (iendOverlap > 0) {
     435           0 :     aClippedMarkerEdges->AccumulateIEnd(mBlockWM, borderRect);
     436           0 :     if (!mIEnd.mActive) {
     437           0 :       iendOverlap = 0;
     438             :     }
     439             :   }
     440             : 
     441           0 :   if ((istartOverlap > 0 && insideIStartEdge) ||
     442           0 :       (iendOverlap > 0 && insideIEndEdge)) {
     443           0 :     if (aFrameType == LayoutFrameType::Text) {
     444           0 :       if (aInsideMarkersArea.IStart(mBlockWM) <
     445           0 :           aInsideMarkersArea.IEnd(mBlockWM)) {
     446             :         // a clipped text frame and there is some room between the markers
     447             :         nscoord snappedIStart, snappedIEnd;
     448           0 :         auto textFrame = static_cast<nsTextFrame*>(aFrame);
     449           0 :         bool isFullyClipped = mBlockWM.IsBidiLTR() ?
     450             :           IsFullyClipped(textFrame, istartOverlap, iendOverlap,
     451             :                          &snappedIStart, &snappedIEnd) :
     452             :           IsFullyClipped(textFrame, iendOverlap, istartOverlap,
     453           0 :                          &snappedIEnd, &snappedIStart);
     454           0 :         if (!isFullyClipped) {
     455           0 :           LogicalRect snappedRect = borderRect;
     456           0 :           if (istartOverlap > 0) {
     457           0 :             snappedRect.IStart(mBlockWM) += snappedIStart;
     458           0 :             snappedRect.ISize(mBlockWM) -= snappedIStart;
     459             :           }
     460           0 :           if (iendOverlap > 0) {
     461           0 :             snappedRect.ISize(mBlockWM) -= snappedIEnd;
     462             :           }
     463           0 :           aAlignmentEdges->Accumulate(mBlockWM, snappedRect);
     464           0 :           *aFoundVisibleTextOrAtomic = true;
     465             :         }
     466             :       }
     467             :     } else {
     468           0 :       aFramesToHide->PutEntry(aFrame);
     469           0 :     }
     470           0 :   } else if (!insideIStartEdge || !insideIEndEdge) {
     471             :     // frame is outside
     472           0 :     if (IsAtomicElement(aFrame, aFrameType)) {
     473           0 :       aFramesToHide->PutEntry(aFrame);
     474             :     }
     475             :   } else {
     476             :     // frame is inside
     477           0 :     aAlignmentEdges->Accumulate(mBlockWM, borderRect);
     478           0 :     *aFoundVisibleTextOrAtomic = true;
     479             :   }
     480           0 : }
     481             : 
     482             : LogicalRect
     483           0 : TextOverflow::ExamineLineFrames(nsLineBox*      aLine,
     484             :                                 FrameHashtable* aFramesToHide,
     485             :                                 AlignmentEdges* aAlignmentEdges)
     486             : {
     487             :   // No ellipsing for 'clip' style.
     488           0 :   bool suppressIStart = mIStart.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
     489           0 :   bool suppressIEnd = mIEnd.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
     490           0 :   if (mCanHaveInlineAxisScrollbar) {
     491           0 :     LogicalPoint pos(mBlockWM, mScrollableFrame->GetScrollPosition(),
     492           0 :                      mBlockSize);
     493           0 :     LogicalRect scrollRange(mBlockWM, mScrollableFrame->GetScrollRange(),
     494           0 :                             mBlockSize);
     495             :     // No ellipsing when nothing to scroll to on that side (this includes
     496             :     // overflow:auto that doesn't trigger a horizontal scrollbar).
     497           0 :     if (pos.I(mBlockWM) <= scrollRange.IStart(mBlockWM)) {
     498           0 :       suppressIStart = true;
     499             :     }
     500           0 :     if (pos.I(mBlockWM) >= scrollRange.IEnd(mBlockWM)) {
     501           0 :       suppressIEnd = true;
     502             :     }
     503             :   }
     504             : 
     505           0 :   LogicalRect contentArea = mContentArea;
     506           0 :   bool snapStart = true, snapEnd = true;
     507             :   nscoord startEdge, endEdge;
     508           0 :   if (aLine->GetFloatEdges(&startEdge, &endEdge)) {
     509             :     // Narrow the |contentArea| to account for any floats on this line, and
     510             :     // don't bother with the snapping quirk on whichever side(s) we narrow.
     511           0 :     nscoord delta = endEdge - contentArea.IEnd(mBlockWM);
     512           0 :     if (delta < 0) {
     513           0 :       nscoord newSize = contentArea.ISize(mBlockWM) + delta;
     514           0 :       contentArea.ISize(mBlockWM) = std::max(nscoord(0), newSize);
     515           0 :       snapEnd = false;
     516             :     }
     517           0 :     delta = startEdge - contentArea.IStart(mBlockWM);
     518           0 :     if (delta > 0) {
     519           0 :       contentArea.IStart(mBlockWM) = startEdge;
     520           0 :       nscoord newSize = contentArea.ISize(mBlockWM) - delta;
     521           0 :       contentArea.ISize(mBlockWM) = std::max(nscoord(0), newSize);
     522           0 :       snapStart = false;
     523             :     }
     524             :   }
     525             :   // Save the non-snapped area since that's what we want to use when placing
     526             :   // the markers (our return value).  The snapped area is only for analysis.
     527           0 :   LogicalRect nonSnappedContentArea = contentArea;
     528           0 :   if (mAdjustForPixelSnapping) {
     529           0 :     const nscoord scrollAdjust = mBlock->PresContext()->AppUnitsPerDevPixel();
     530           0 :     if (snapStart) {
     531           0 :       InflateIStart(mBlockWM, &contentArea, scrollAdjust);
     532             :     }
     533           0 :     if (snapEnd) {
     534           0 :       InflateIEnd(mBlockWM, &contentArea, scrollAdjust);
     535             :     }
     536             :   }
     537             : 
     538           0 :   LogicalRect lineRect(mBlockWM, aLine->GetScrollableOverflowArea(),
     539           0 :                        mBlockSize);
     540             :   const bool istartOverflow =
     541           0 :     !suppressIStart && lineRect.IStart(mBlockWM) < contentArea.IStart(mBlockWM);
     542             :   const bool iendOverflow =
     543           0 :     !suppressIEnd && lineRect.IEnd(mBlockWM) > contentArea.IEnd(mBlockWM);
     544           0 :   if (!istartOverflow && !iendOverflow) {
     545             :     // The line does not overflow on a side we should ellipsize.
     546           0 :     return nonSnappedContentArea;
     547             :   }
     548             : 
     549           0 :   int pass = 0;
     550           0 :   bool retryEmptyLine = true;
     551           0 :   bool guessIStart = istartOverflow;
     552           0 :   bool guessIEnd = iendOverflow;
     553           0 :   mIStart.mActive = istartOverflow;
     554           0 :   mIEnd.mActive = iendOverflow;
     555           0 :   bool clippedIStartMarker = false;
     556           0 :   bool clippedIEndMarker = false;
     557           0 :   do {
     558             :     // Setup marker strings as needed.
     559           0 :     if (guessIStart) {
     560           0 :       mIStart.SetupString(mBlock);
     561             :     }
     562           0 :     if (guessIEnd) {
     563           0 :       mIEnd.SetupString(mBlock);
     564             :     }
     565             : 
     566             :     // If there is insufficient space for both markers then keep the one on the
     567             :     // end side per the block's 'direction'.
     568           0 :     nscoord istartMarkerISize = mIStart.mActive ? mIStart.mISize : 0;
     569           0 :     nscoord iendMarkerISize = mIEnd.mActive ? mIEnd.mISize : 0;
     570           0 :     if (istartMarkerISize && iendMarkerISize &&
     571           0 :         istartMarkerISize + iendMarkerISize > contentArea.ISize(mBlockWM)) {
     572           0 :       istartMarkerISize = 0;
     573             :     }
     574             : 
     575             :     // Calculate the area between the potential markers aligned at the
     576             :     // block's edge.
     577           0 :     LogicalRect insideMarkersArea = nonSnappedContentArea;
     578           0 :     if (guessIStart) {
     579           0 :       InflateIStart(mBlockWM, &insideMarkersArea, -istartMarkerISize);
     580             :     }
     581           0 :     if (guessIEnd) {
     582           0 :       InflateIEnd(mBlockWM, &insideMarkersArea, -iendMarkerISize);
     583             :     }
     584             : 
     585             :     // Analyze the frames on aLine for the overflow situation at the content
     586             :     // edges and at the edges of the area between the markers.
     587           0 :     bool foundVisibleTextOrAtomic = false;
     588           0 :     int32_t n = aLine->GetChildCount();
     589           0 :     nsIFrame* child = aLine->mFirstChild;
     590           0 :     InnerClipEdges clippedMarkerEdges;
     591           0 :     for (; n-- > 0; child = child->GetNextSibling()) {
     592             :       ExamineFrameSubtree(child, contentArea, insideMarkersArea,
     593             :                           aFramesToHide, aAlignmentEdges,
     594             :                           &foundVisibleTextOrAtomic,
     595           0 :                           &clippedMarkerEdges);
     596             :     }
     597           0 :     if (!foundVisibleTextOrAtomic && retryEmptyLine) {
     598           0 :       aAlignmentEdges->mAssigned = false;
     599           0 :       aFramesToHide->Clear();
     600           0 :       pass = -1;
     601           0 :       if (mIStart.IsNeeded() && mIStart.mActive && !clippedIStartMarker) {
     602           0 :         if (clippedMarkerEdges.mAssignedIStart &&
     603           0 :             clippedMarkerEdges.mIStart > nonSnappedContentArea.IStart(mBlockWM)) {
     604           0 :           mIStart.mISize = clippedMarkerEdges.mIStart -
     605           0 :                            nonSnappedContentArea.IStart(mBlockWM);
     606           0 :           NS_ASSERTION(mIStart.mISize < mIStart.mIntrinsicISize,
     607             :                       "clipping a marker should make it strictly smaller");
     608           0 :           clippedIStartMarker = true;
     609             :         } else {
     610           0 :           mIStart.mActive = guessIStart = false;
     611             :         }
     612           0 :         continue;
     613             :       }
     614           0 :       if (mIEnd.IsNeeded() && mIEnd.mActive && !clippedIEndMarker) {
     615           0 :         if (clippedMarkerEdges.mAssignedIEnd &&
     616           0 :             nonSnappedContentArea.IEnd(mBlockWM) > clippedMarkerEdges.mIEnd) {
     617           0 :           mIEnd.mISize = nonSnappedContentArea.IEnd(mBlockWM) -
     618           0 :                          clippedMarkerEdges.mIEnd;
     619           0 :           NS_ASSERTION(mIEnd.mISize < mIEnd.mIntrinsicISize,
     620             :                       "clipping a marker should make it strictly smaller");
     621           0 :           clippedIEndMarker = true;
     622             :         } else {
     623           0 :           mIEnd.mActive = guessIEnd = false;
     624             :         }
     625           0 :         continue;
     626             :       }
     627             :       // The line simply has no visible content even without markers,
     628             :       // so examine the line again without suppressing markers.
     629           0 :       retryEmptyLine = false;
     630           0 :       mIStart.mISize = mIStart.mIntrinsicISize;
     631           0 :       mIStart.mActive = guessIStart = istartOverflow;
     632           0 :       mIEnd.mISize = mIEnd.mIntrinsicISize;
     633           0 :       mIEnd.mActive = guessIEnd = iendOverflow;
     634           0 :       continue;
     635             :     }
     636           0 :     if (guessIStart == (mIStart.mActive && mIStart.IsNeeded()) &&
     637           0 :         guessIEnd == (mIEnd.mActive && mIEnd.IsNeeded())) {
     638           0 :       break;
     639             :     } else {
     640           0 :       guessIStart = mIStart.mActive && mIStart.IsNeeded();
     641           0 :       guessIEnd = mIEnd.mActive && mIEnd.IsNeeded();
     642           0 :       mIStart.Reset();
     643           0 :       mIEnd.Reset();
     644           0 :       aFramesToHide->Clear();
     645             :     }
     646           0 :     NS_ASSERTION(pass == 0, "2nd pass should never guess wrong");
     647             :   } while (++pass != 2);
     648           0 :   if (!istartOverflow || !mIStart.mActive) {
     649           0 :     mIStart.Reset();
     650             :   }
     651           0 :   if (!iendOverflow || !mIEnd.mActive) {
     652           0 :     mIEnd.Reset();
     653             :   }
     654           0 :   return nonSnappedContentArea;
     655             : }
     656             : 
     657             : void
     658           0 : TextOverflow::ProcessLine(const nsDisplayListSet& aLists,
     659             :                           nsLineBox*              aLine)
     660             : {
     661           0 :   NS_ASSERTION(mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
     662             :                mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP,
     663             :                "TextOverflow with 'clip' for both sides");
     664           0 :   mIStart.Reset();
     665           0 :   mIStart.mActive = mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP;
     666           0 :   mIEnd.Reset();
     667           0 :   mIEnd.mActive = mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP;
     668             : 
     669           0 :   FrameHashtable framesToHide(64);
     670           0 :   AlignmentEdges alignmentEdges;
     671             :   const LogicalRect contentArea =
     672           0 :     ExamineLineFrames(aLine, &framesToHide, &alignmentEdges);
     673           0 :   bool needIStart = mIStart.IsNeeded();
     674           0 :   bool needIEnd = mIEnd.IsNeeded();
     675           0 :   if (!needIStart && !needIEnd) {
     676           0 :     return;
     677             :   }
     678           0 :   NS_ASSERTION(mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
     679             :                !needIStart, "left marker for 'clip'");
     680           0 :   NS_ASSERTION(mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
     681             :                !needIEnd, "right marker for 'clip'");
     682             : 
     683             :   // If there is insufficient space for both markers then keep the one on the
     684             :   // end side per the block's 'direction'.
     685           0 :   if (needIStart && needIEnd &&
     686           0 :       mIStart.mISize + mIEnd.mISize > contentArea.ISize(mBlockWM)) {
     687           0 :     needIStart = false;
     688             :   }
     689           0 :   LogicalRect insideMarkersArea = contentArea;
     690           0 :   if (needIStart) {
     691           0 :     InflateIStart(mBlockWM, &insideMarkersArea, -mIStart.mISize);
     692             :   }
     693           0 :   if (needIEnd) {
     694           0 :     InflateIEnd(mBlockWM, &insideMarkersArea, -mIEnd.mISize);
     695             :   }
     696           0 :   if (!mCanHaveInlineAxisScrollbar && alignmentEdges.mAssigned) {
     697             :     LogicalRect alignmentRect(mBlockWM, alignmentEdges.mIStart,
     698           0 :                               insideMarkersArea.BStart(mBlockWM),
     699           0 :                               alignmentEdges.ISize(), 1);
     700           0 :     insideMarkersArea.IntersectRect(insideMarkersArea, alignmentRect);
     701             :   }
     702             : 
     703             :   // Clip and remove display items as needed at the final marker edges.
     704           0 :   nsDisplayList* lists[] = { aLists.Content(), aLists.PositionedDescendants() };
     705           0 :   for (uint32_t i = 0; i < ArrayLength(lists); ++i) {
     706           0 :     PruneDisplayListContents(lists[i], framesToHide, insideMarkersArea);
     707             :   }
     708           0 :   CreateMarkers(aLine, needIStart, needIEnd, insideMarkersArea, contentArea);
     709             : }
     710             : 
     711             : void
     712           0 : TextOverflow::PruneDisplayListContents(nsDisplayList* aList,
     713             :                                        const FrameHashtable& aFramesToHide,
     714             :                                        const LogicalRect& aInsideMarkersArea)
     715             : {
     716           0 :   nsDisplayList saved;
     717             :   nsDisplayItem* item;
     718           0 :   while ((item = aList->RemoveBottom())) {
     719           0 :     nsIFrame* itemFrame = item->Frame();
     720           0 :     if (IsFrameDescendantOfAny(itemFrame, aFramesToHide, mBlock)) {
     721           0 :       item->~nsDisplayItem();
     722           0 :       continue;
     723             :     }
     724             : 
     725           0 :     nsDisplayList* wrapper = item->GetSameCoordinateSystemChildren();
     726           0 :     if (wrapper) {
     727           0 :       if (!itemFrame || GetSelfOrNearestBlock(itemFrame) == mBlock) {
     728           0 :         PruneDisplayListContents(wrapper, aFramesToHide, aInsideMarkersArea);
     729             :       }
     730             :     }
     731             : 
     732           0 :     nsCharClipDisplayItem* charClip = itemFrame ?
     733           0 :       nsCharClipDisplayItem::CheckCast(item) : nullptr;
     734           0 :     if (charClip && GetSelfOrNearestBlock(itemFrame) == mBlock) {
     735             :       LogicalRect rect =
     736           0 :         GetLogicalScrollableOverflowRectRelativeToBlock(itemFrame);
     737           0 :       if (mIStart.IsNeeded()) {
     738             :         nscoord istart =
     739           0 :           aInsideMarkersArea.IStart(mBlockWM) - rect.IStart(mBlockWM);
     740           0 :         if (istart > 0) {
     741           0 :           (mBlockWM.IsBidiLTR() ?
     742           0 :            charClip->mVisIStartEdge : charClip->mVisIEndEdge) = istart;
     743             :         }
     744             :       }
     745           0 :       if (mIEnd.IsNeeded()) {
     746           0 :         nscoord iend = rect.IEnd(mBlockWM) - aInsideMarkersArea.IEnd(mBlockWM);
     747           0 :         if (iend > 0) {
     748           0 :           (mBlockWM.IsBidiLTR() ?
     749           0 :            charClip->mVisIEndEdge : charClip->mVisIStartEdge) = iend;
     750             :         }
     751             :       }
     752             :     }
     753             : 
     754           0 :     saved.AppendToTop(item);
     755             :   }
     756           0 :   aList->AppendToTop(&saved);
     757           0 : }
     758             : 
     759             : /* static */ bool
     760         361 : TextOverflow::HasClippedOverflow(nsIFrame* aBlockFrame)
     761             : {
     762         361 :   const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
     763         722 :   return style->mTextOverflow.mLeft.mType == NS_STYLE_TEXT_OVERFLOW_CLIP &&
     764         722 :          style->mTextOverflow.mRight.mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
     765             : }
     766             : 
     767             : /* static */ bool
     768         361 : TextOverflow::CanHaveTextOverflow(nsIFrame* aBlockFrame)
     769             : {
     770             :   // Nothing to do for text-overflow:clip or if 'overflow-x/y:visible'.
     771         361 :   if (HasClippedOverflow(aBlockFrame) ||
     772           0 :       IsInlineAxisOverflowVisible(aBlockFrame)) {
     773         361 :     return false;
     774             :   }
     775             : 
     776             :   // Skip ComboboxControlFrame because it would clip the drop-down arrow.
     777             :   // Its anon block inherits 'text-overflow' and does what is expected.
     778           0 :   if (aBlockFrame->IsComboboxControlFrame()) {
     779           0 :     return false;
     780             :   }
     781             : 
     782             :   // Inhibit the markers if a descendant content owns the caret.
     783           0 :   RefPtr<nsCaret> caret = aBlockFrame->PresContext()->PresShell()->GetCaret();
     784           0 :   if (caret && caret->IsVisible()) {
     785           0 :     nsCOMPtr<nsISelection> domSelection = caret->GetSelection();
     786           0 :     if (domSelection) {
     787           0 :       nsCOMPtr<nsIDOMNode> node;
     788           0 :       domSelection->GetFocusNode(getter_AddRefs(node));
     789           0 :       nsCOMPtr<nsIContent> content = do_QueryInterface(node);
     790           0 :       if (content && nsContentUtils::ContentIsDescendantOf(content,
     791           0 :                        aBlockFrame->GetContent())) {
     792           0 :         return false;
     793             :       }
     794             :     }
     795             :   }
     796           0 :   return true;
     797             : }
     798             : 
     799             : void
     800           0 : TextOverflow::CreateMarkers(const nsLineBox* aLine,
     801             :                             bool aCreateIStart, bool aCreateIEnd,
     802             :                             const LogicalRect& aInsideMarkersArea,
     803             :                             const LogicalRect& aContentArea)
     804             : {
     805           0 :   if (aCreateIStart) {
     806           0 :     DisplayListClipState::AutoSaveRestore clipState(mBuilder);
     807             : 
     808             :     LogicalRect markerLogicalRect(
     809           0 :       mBlockWM, aInsideMarkersArea.IStart(mBlockWM) - mIStart.mIntrinsicISize,
     810           0 :       aLine->BStart(), mIStart.mIntrinsicISize, aLine->BSize());
     811           0 :     nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
     812             :     nsRect markerRect =
     813           0 :       markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
     814           0 :     ClipMarker(aContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
     815           0 :                markerRect, clipState);
     816             :     nsDisplayItem* marker = new (mBuilder)
     817             :       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
     818           0 :                                   aLine->GetLogicalAscent(), mIStart.mStyle, 0);
     819           0 :     mMarkerList.AppendNewToTop(marker);
     820             :   }
     821             : 
     822           0 :   if (aCreateIEnd) {
     823           0 :     DisplayListClipState::AutoSaveRestore clipState(mBuilder);
     824             : 
     825             :     LogicalRect markerLogicalRect(
     826             :       mBlockWM, aInsideMarkersArea.IEnd(mBlockWM), aLine->BStart(),
     827           0 :       mIEnd.mIntrinsicISize, aLine->BSize());
     828           0 :     nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
     829             :     nsRect markerRect =
     830           0 :       markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
     831           0 :     ClipMarker(aContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
     832           0 :                markerRect, clipState);
     833             :     nsDisplayItem* marker = new (mBuilder)
     834             :       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
     835           0 :                                   aLine->GetLogicalAscent(), mIEnd.mStyle, 1);
     836           0 :     mMarkerList.AppendNewToTop(marker);
     837             :   }
     838           0 : }
     839             : 
     840             : void
     841           0 : TextOverflow::Marker::SetupString(nsIFrame* aFrame)
     842             : {
     843           0 :   if (mInitialized) {
     844           0 :     return;
     845             :   }
     846             : 
     847           0 :   if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
     848           0 :     gfxTextRun* textRun = GetEllipsisTextRun(aFrame);
     849           0 :     if (textRun) {
     850           0 :       mISize = textRun->GetAdvanceWidth();
     851             :     } else {
     852           0 :       mISize = 0;
     853             :     }
     854             :   } else {
     855             :     RefPtr<gfxContext> rc =
     856           0 :       aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
     857             :     RefPtr<nsFontMetrics> fm =
     858           0 :       nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
     859           0 :     mISize = nsLayoutUtils::AppUnitWidthOfStringBidi(mStyle->mString, aFrame,
     860             :                                                      *fm, *rc);
     861             :   }
     862           0 :   mIntrinsicISize = mISize;
     863           0 :   mInitialized = true;
     864             : }
     865             : 
     866             : } // namespace css
     867             : } // namespace mozilla

Generated by: LCOV version 1.13