LCOV - code coverage report
Current view: top level - layout/generic - nsTextFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1552 4921 31.5 %
Date: 2017-07-14 16:53:18 Functions: 149 337 44.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : /* rendering object for textual content of elements */
       8             : 
       9             : #include "nsTextFrame.h"
      10             : 
      11             : #include "gfx2DGlue.h"
      12             : #include "gfxPrefs.h"
      13             : #include "gfxUtils.h"
      14             : #include "mozilla/Attributes.h"
      15             : #include "mozilla/DebugOnly.h"
      16             : #include "mozilla/gfx/2D.h"
      17             : #include "mozilla/Likely.h"
      18             : #include "mozilla/MathAlgorithms.h"
      19             : #include "mozilla/TextEvents.h"
      20             : #include "mozilla/BinarySearch.h"
      21             : #include "mozilla/IntegerRange.h"
      22             : #include "mozilla/Unused.h"
      23             : #include "mozilla/PodOperations.h"
      24             : 
      25             : #include "nsCOMPtr.h"
      26             : #include "nsBlockFrame.h"
      27             : #include "nsFontMetrics.h"
      28             : #include "nsSplittableFrame.h"
      29             : #include "nsLineLayout.h"
      30             : #include "nsString.h"
      31             : #include "nsUnicharUtils.h"
      32             : #include "nsPresContext.h"
      33             : #include "nsIContent.h"
      34             : #include "nsStyleConsts.h"
      35             : #include "nsStyleContext.h"
      36             : #include "nsStyleStruct.h"
      37             : #include "nsStyleStructInlines.h"
      38             : #include "SVGTextFrame.h"
      39             : #include "nsCoord.h"
      40             : #include "gfxContext.h"
      41             : #include "nsIPresShell.h"
      42             : #include "nsTArray.h"
      43             : #include "nsCSSPseudoElements.h"
      44             : #include "nsCSSFrameConstructor.h"
      45             : #include "nsCompatibility.h"
      46             : #include "nsCSSColorUtils.h"
      47             : #include "nsLayoutUtils.h"
      48             : #include "nsDisplayList.h"
      49             : #include "nsFrame.h"
      50             : #include "nsIMathMLFrame.h"
      51             : #include "nsPlaceholderFrame.h"
      52             : #include "nsTextFrameUtils.h"
      53             : #include "nsTextRunTransformations.h"
      54             : #include "MathMLTextRunFactory.h"
      55             : #include "nsUnicodeProperties.h"
      56             : #include "nsStyleUtil.h"
      57             : #include "nsRubyFrame.h"
      58             : 
      59             : #include "nsTextFragment.h"
      60             : #include "nsGkAtoms.h"
      61             : #include "nsFrameSelection.h"
      62             : #include "nsRange.h"
      63             : #include "nsCSSRendering.h"
      64             : #include "nsContentUtils.h"
      65             : #include "nsLineBreaker.h"
      66             : #include "nsIWordBreaker.h"
      67             : #include "nsGenericDOMDataNode.h"
      68             : #include "nsIFrameInlines.h"
      69             : #include "mozilla/StyleSetHandle.h"
      70             : #include "mozilla/StyleSetHandleInlines.h"
      71             : #include "mozilla/layers/LayersMessages.h"
      72             : #include "mozilla/layers/WebRenderLayerManager.h"
      73             : #include "mozilla/layers/WebRenderBridgeChild.h"
      74             : #include "mozilla/webrender/WebRenderAPI.h"
      75             : #include "mozilla/layers/StackingContextHelper.h"
      76             : 
      77             : #include <algorithm>
      78             : #include <limits>
      79             : #ifdef ACCESSIBILITY
      80             : #include "nsAccessibilityService.h"
      81             : #endif
      82             : 
      83             : #include "nsPrintfCString.h"
      84             : 
      85             : #include "gfxContext.h"
      86             : 
      87             : #include "mozilla/UniquePtr.h"
      88             : #include "mozilla/dom/Element.h"
      89             : #include "mozilla/LookAndFeel.h"
      90             : 
      91             : #include "GeckoProfiler.h"
      92             : 
      93             : #ifdef DEBUG
      94             : #undef NOISY_REFLOW
      95             : #undef NOISY_TRIM
      96             : #else
      97             : #undef NOISY_REFLOW
      98             : #undef NOISY_TRIM
      99             : #endif
     100             : 
     101             : #ifdef DrawText
     102             : #undef DrawText
     103             : #endif
     104             : 
     105             : using namespace mozilla;
     106             : using namespace mozilla::dom;
     107             : using namespace mozilla::gfx;
     108             : using namespace mozilla::layers;
     109             : 
     110             : struct TabWidth {
     111           0 :   TabWidth(uint32_t aOffset, uint32_t aWidth)
     112           0 :     : mOffset(aOffset), mWidth(float(aWidth))
     113           0 :   { }
     114             : 
     115             :   uint32_t mOffset; // DOM offset relative to the current frame's offset.
     116             :   float    mWidth;  // extra space to be added at this position (in app units)
     117             : };
     118             : 
     119           0 : struct TabWidthStore {
     120           0 :   explicit TabWidthStore(int32_t aValidForContentOffset)
     121           0 :     : mLimit(0)
     122           0 :     , mValidForContentOffset(aValidForContentOffset)
     123           0 :   { }
     124             : 
     125             :   // Apply tab widths to the aSpacing array, which corresponds to characters
     126             :   // beginning at aOffset and has length aLength. (Width records outside this
     127             :   // range will be ignored.)
     128             :   void ApplySpacing(gfxTextRun::PropertyProvider::Spacing *aSpacing,
     129             :                     uint32_t aOffset, uint32_t aLength);
     130             : 
     131             :   // Offset up to which tabs have been measured; positions beyond this have not
     132             :   // been calculated yet but may be appended if needed later.  It's a DOM
     133             :   // offset relative to the current frame's offset.
     134             :   uint32_t mLimit;
     135             : 
     136             :   // Need to recalc tab offsets if frame content offset differs from this.
     137             :   int32_t mValidForContentOffset;
     138             : 
     139             :   // A TabWidth record for each tab character measured so far.
     140             :   nsTArray<TabWidth> mWidths;
     141             : };
     142             : 
     143             : namespace {
     144             : 
     145             : struct TabwidthAdaptor
     146             : {
     147             :   const nsTArray<TabWidth>& mWidths;
     148           0 :   explicit TabwidthAdaptor(const nsTArray<TabWidth>& aWidths)
     149           0 :     : mWidths(aWidths) {}
     150           0 :   uint32_t operator[](size_t aIdx) const {
     151           0 :     return mWidths[aIdx].mOffset;
     152             :   }
     153             : };
     154             : 
     155             : } // namespace
     156             : 
     157             : void
     158           0 : TabWidthStore::ApplySpacing(gfxTextRun::PropertyProvider::Spacing *aSpacing,
     159             :                             uint32_t aOffset, uint32_t aLength)
     160             : {
     161           0 :   size_t i = 0;
     162           0 :   const size_t len = mWidths.Length();
     163             : 
     164             :   // If aOffset is non-zero, do a binary search to find where to start
     165             :   // processing the tab widths, in case the list is really long. (See bug
     166             :   // 953247.)
     167             :   // We need to start from the first entry where mOffset >= aOffset.
     168           0 :   if (aOffset > 0) {
     169           0 :     mozilla::BinarySearch(TabwidthAdaptor(mWidths), 0, len, aOffset, &i);
     170             :   }
     171             : 
     172           0 :   uint32_t limit = aOffset + aLength;
     173           0 :   while (i < len) {
     174           0 :     const TabWidth& tw = mWidths[i];
     175           0 :     if (tw.mOffset >= limit) {
     176           0 :       break;
     177             :     }
     178           0 :     aSpacing[tw.mOffset - aOffset].mAfter += tw.mWidth;
     179           0 :     i++;
     180             :   }
     181           0 : }
     182             : 
     183          21 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(TabWidthProperty, TabWidthStore)
     184             : 
     185           4 : NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(OffsetToFrameProperty, nsTextFrame)
     186             : 
     187           0 : NS_DECLARE_FRAME_PROPERTY_RELEASABLE(UninflatedTextRunProperty, gfxTextRun)
     188             : 
     189           0 : NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
     190             : 
     191             : /**
     192             :  * A glyph observer for the change of a font glyph in a text run.
     193             :  *
     194             :  * This is stored in {Simple, Complex}TextRunUserData.
     195             :  */
     196           0 : class GlyphObserver : public gfxFont::GlyphChangeObserver {
     197             : public:
     198           0 :   GlyphObserver(gfxFont* aFont, gfxTextRun* aTextRun)
     199           0 :     : gfxFont::GlyphChangeObserver(aFont), mTextRun(aTextRun) {
     200           0 :     MOZ_ASSERT(aTextRun->GetUserData());
     201           0 :   }
     202             :   virtual void NotifyGlyphsChanged() override;
     203             : private:
     204             :   gfxTextRun* mTextRun;
     205             : };
     206             : 
     207           3 : static const nsFrameState TEXT_REFLOW_FLAGS =
     208             :    TEXT_FIRST_LETTER |
     209             :    TEXT_START_OF_LINE |
     210             :    TEXT_END_OF_LINE |
     211             :    TEXT_HYPHEN_BREAK |
     212             :    TEXT_TRIMMED_TRAILING_WHITESPACE |
     213             :    TEXT_JUSTIFICATION_ENABLED |
     214             :    TEXT_HAS_NONCOLLAPSED_CHARACTERS |
     215           3 :    TEXT_SELECTION_UNDERLINE_OVERFLOWED |
     216             :    TEXT_NO_RENDERED_GLYPHS;
     217             : 
     218           3 : static const nsFrameState TEXT_WHITESPACE_FLAGS =
     219           3 :     TEXT_IS_ONLY_WHITESPACE |
     220             :     TEXT_ISNOT_ONLY_WHITESPACE;
     221             : 
     222             : /*
     223             :  * Some general notes
     224             :  *
     225             :  * Text frames delegate work to gfxTextRun objects. The gfxTextRun object
     226             :  * transforms text to positioned glyphs. It can report the geometry of the
     227             :  * glyphs and paint them. Text frames configure gfxTextRuns by providing text,
     228             :  * spacing, language, and other information.
     229             :  *
     230             :  * A gfxTextRun can cover more than one DOM text node. This is necessary to
     231             :  * get kerning, ligatures and shaping for text that spans multiple text nodes
     232             :  * but is all the same font.
     233             :  *
     234             :  * The userdata for a gfxTextRun object can be:
     235             :  *
     236             :  *   - A nsTextFrame* in the case a text run maps to only one flow. In this
     237             :  *   case, the textrun's user data pointer is a pointer to mStartFrame for that
     238             :  *   flow, mDOMOffsetToBeforeTransformOffset is zero, and mContentLength is the
     239             :  *   length of the text node.
     240             :  *
     241             :  *   - A SimpleTextRunUserData in the case a text run maps to one flow, but we
     242             :  *   still have to keep a list of glyph observers.
     243             :  *
     244             :  *   - A ComplexTextRunUserData in the case a text run maps to multiple flows,
     245             :  *   but we need to keep a list of glyph observers.
     246             :  *
     247             :  *   - A TextRunUserData in the case a text run maps multiple flows, but it
     248             :  *   doesn't have any glyph observer for changes in SVG fonts.
     249             :  *
     250             :  * You can differentiate between the four different cases with the
     251             :  * TEXT_IS_SIMPLE_FLOW and TEXT_MIGHT_HAVE_GLYPH_CHANGES flags.
     252             :  *
     253             :  * We go to considerable effort to make sure things work even if in-flow
     254             :  * siblings have different style contexts (i.e., first-letter and first-line).
     255             :  *
     256             :  * Our convention is that unsigned integer character offsets are offsets into
     257             :  * the transformed string. Signed integer character offsets are offsets into
     258             :  * the DOM string.
     259             :  *
     260             :  * XXX currently we don't handle hyphenated breaks between text frames where the
     261             :  * hyphen occurs at the end of the first text frame, e.g.
     262             :  *   <b>Kit&shy;</b>ty
     263             :  */
     264             : 
     265             : /**
     266             :  * This is our user data for the textrun, when textRun->GetFlags2() has
     267             :  * TEXT_IS_SIMPLE_FLOW set, and also TEXT_MIGHT_HAVE_GLYPH_CHANGES.
     268             :  *
     269             :  * This allows having an array of observers if there are fonts whose glyphs
     270             :  * might change, but also avoid allocation in the simple case that there aren't.
     271             :  */
     272           0 : struct SimpleTextRunUserData {
     273             :   nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
     274             :   nsTextFrame* mFrame;
     275           0 :   explicit SimpleTextRunUserData(nsTextFrame* aFrame)
     276           0 :     : mFrame(aFrame)
     277             :   {
     278           0 :   }
     279             : };
     280             : 
     281             : /**
     282             :  * We use an array of these objects to record which text frames
     283             :  * are associated with the textrun. mStartFrame is the start of a list of
     284             :  * text frames. Some sequence of its continuations are covered by the textrun.
     285             :  * A content textnode can have at most one TextRunMappedFlow associated with it
     286             :  * for a given textrun.
     287             :  *
     288             :  * mDOMOffsetToBeforeTransformOffset is added to DOM offsets for those frames to obtain
     289             :  * the offset into the before-transformation text of the textrun. It can be
     290             :  * positive (when a text node starts in the middle of a text run) or
     291             :  * negative (when a text run starts in the middle of a text node). Of course
     292             :  * it can also be zero.
     293             :  */
     294             : struct TextRunMappedFlow {
     295             :   nsTextFrame* mStartFrame;
     296             :   int32_t      mDOMOffsetToBeforeTransformOffset;
     297             :   // The text mapped starts at mStartFrame->GetContentOffset() and is this long
     298             :   uint32_t     mContentLength;
     299             : };
     300             : 
     301             : /**
     302             :  * This is the type in the gfxTextRun's userdata field in the common case that
     303             :  * the text run maps to multiple flows, but no fonts have been found with
     304             :  * animatable glyphs.
     305             :  *
     306             :  * This way, we avoid allocating and constructing the extra nsTArray.
     307             :  */
     308           0 : struct TextRunUserData {
     309             : #ifdef DEBUG
     310             :   TextRunMappedFlow* mMappedFlows;
     311             : #endif
     312             :   uint32_t           mMappedFlowCount;
     313             :   uint32_t           mLastFlowIndex;
     314             : };
     315             : 
     316             : /**
     317             :  * This is our user data for the textrun, when textRun->GetFlags2() does not
     318             :  * have TEXT_IS_SIMPLE_FLOW set and has the TEXT_MIGHT HAVE_GLYPH_CHANGES flag.
     319             :  */
     320           0 : struct ComplexTextRunUserData : public TextRunUserData {
     321             :   nsTArray<UniquePtr<GlyphObserver>> mGlyphObservers;
     322             : };
     323             : 
     324             : /**
     325             :  * This helper object computes colors used for painting, and also IME
     326             :  * underline information. The data is computed lazily and cached as necessary.
     327             :  * These live for just the duration of one paint operation.
     328             :  */
     329          17 : class nsTextPaintStyle {
     330             : public:
     331             :   explicit nsTextPaintStyle(nsTextFrame* aFrame);
     332             : 
     333          17 :   void SetResolveColors(bool aResolveColors) {
     334          17 :     mResolveColors = aResolveColors;
     335          17 :   }
     336             : 
     337             :   nscolor GetTextColor();
     338             : 
     339             :   // SVG text has its own painting process, so we should never get its stroke
     340             :   // property from here.
     341          18 :   nscolor GetWebkitTextStrokeColor() {
     342          18 :     if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
     343           0 :       return 0;
     344             :     }
     345          18 :     return mFrame->StyleColor()->
     346          36 :       CalcComplexColor(mFrame->StyleText()->mWebkitTextStrokeColor);
     347             :   }
     348          18 :   float GetWebkitTextStrokeWidth() {
     349          18 :     if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
     350           0 :       return 0.0f;
     351             :     }
     352          18 :     nscoord coord = mFrame->StyleText()->mWebkitTextStrokeWidth;
     353          18 :     return mFrame->PresContext()->AppUnitsToFloatDevPixels(coord);
     354             :   }
     355             : 
     356             :   /**
     357             :    * Compute the colors for normally-selected text. Returns false if
     358             :    * the normal selection is not being displayed.
     359             :    */
     360             :   bool GetSelectionColors(nscolor* aForeColor,
     361             :                             nscolor* aBackColor);
     362             :   void GetHighlightColors(nscolor* aForeColor,
     363             :                           nscolor* aBackColor);
     364             :   void GetURLSecondaryColor(nscolor* aForeColor);
     365             :   void GetIMESelectionColors(int32_t  aIndex,
     366             :                              nscolor* aForeColor,
     367             :                              nscolor* aBackColor);
     368             :   // if this returns false, we don't need to draw underline.
     369             :   bool GetSelectionUnderlineForPaint(int32_t  aIndex,
     370             :                                        nscolor* aLineColor,
     371             :                                        float*   aRelativeSize,
     372             :                                        uint8_t* aStyle);
     373             : 
     374             :   // if this returns false, we don't need to draw underline.
     375             :   static bool GetSelectionUnderline(nsPresContext* aPresContext,
     376             :                                       int32_t aIndex,
     377             :                                       nscolor* aLineColor,
     378             :                                       float* aRelativeSize,
     379             :                                       uint8_t* aStyle);
     380             : 
     381             :   // if this returns false, no text-shadow was specified for the selection
     382             :   // and the *aShadow parameter was not modified.
     383             :   bool GetSelectionShadow(nsCSSShadowArray** aShadow);
     384             : 
     385          18 :   nsPresContext* PresContext() const { return mPresContext; }
     386             : 
     387             :   enum {
     388             :     eIndexRawInput = 0,
     389             :     eIndexSelRawText,
     390             :     eIndexConvText,
     391             :     eIndexSelConvText,
     392             :     eIndexSpellChecker
     393             :   };
     394             : 
     395           0 :   static int32_t GetUnderlineStyleIndexForSelectionType(
     396             :                    SelectionType aSelectionType)
     397             :   {
     398           0 :     switch (aSelectionType) {
     399             :       case SelectionType::eIMERawClause:
     400           0 :         return eIndexRawInput;
     401             :       case SelectionType::eIMESelectedRawClause:
     402           0 :         return eIndexSelRawText;
     403             :       case SelectionType::eIMEConvertedClause:
     404           0 :         return eIndexConvText;
     405             :       case SelectionType::eIMESelectedClause:
     406           0 :         return eIndexSelConvText;
     407             :       case SelectionType::eSpellCheck:
     408           0 :         return eIndexSpellChecker;
     409             :       default:
     410           0 :         NS_WARNING("non-IME selection type");
     411           0 :         return eIndexRawInput;
     412             :     }
     413             :   }
     414             : 
     415             :   nscolor GetSystemFieldForegroundColor();
     416             :   nscolor GetSystemFieldBackgroundColor();
     417             : 
     418             : protected:
     419             :   nsTextFrame*   mFrame;
     420             :   nsPresContext* mPresContext;
     421             :   bool           mInitCommonColors;
     422             :   bool           mInitSelectionColorsAndShadow;
     423             :   bool           mResolveColors;
     424             : 
     425             :   // Selection data
     426             : 
     427             :   int16_t      mSelectionStatus; // see nsIDocument.h SetDisplaySelection()
     428             :   nscolor      mSelectionTextColor;
     429             :   nscolor      mSelectionBGColor;
     430             :   RefPtr<nsCSSShadowArray> mSelectionShadow;
     431             :   bool                       mHasSelectionShadow;
     432             : 
     433             :   // Common data
     434             : 
     435             :   int32_t mSufficientContrast;
     436             :   nscolor mFrameBackgroundColor;
     437             :   nscolor mSystemFieldForegroundColor;
     438             :   nscolor mSystemFieldBackgroundColor;
     439             : 
     440             :   // selection colors and underline info, the colors are resolved colors if
     441             :   // mResolveColors is true (which is the default), i.e., the foreground color
     442             :   // and background color are swapped if it's needed. And also line color will
     443             :   // be resolved from them.
     444             :   struct nsSelectionStyle {
     445             :     bool    mInit;
     446             :     nscolor mTextColor;
     447             :     nscolor mBGColor;
     448             :     nscolor mUnderlineColor;
     449             :     uint8_t mUnderlineStyle;
     450             :     float   mUnderlineRelativeSize;
     451             :   };
     452             :   nsSelectionStyle mSelectionStyle[5];
     453             : 
     454             :   // Color initializations
     455             :   void InitCommonColors();
     456             :   bool InitSelectionColorsAndShadow();
     457             : 
     458             :   nsSelectionStyle* GetSelectionStyle(int32_t aIndex);
     459             :   void InitSelectionStyle(int32_t aIndex);
     460             : 
     461             :   bool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor);
     462             : 
     463             :   nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
     464             :                                nscolor aBackColor);
     465             : };
     466             : 
     467             : static TextRunUserData*
     468           0 : CreateUserData(uint32_t aMappedFlowCount)
     469             : {
     470             :   TextRunUserData* data = static_cast<TextRunUserData*>
     471           0 :       (moz_xmalloc(sizeof(TextRunUserData) +
     472           0 :        aMappedFlowCount * sizeof(TextRunMappedFlow)));
     473             : #ifdef DEBUG
     474           0 :   data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
     475             : #endif
     476           0 :   data->mMappedFlowCount = aMappedFlowCount;
     477           0 :   data->mLastFlowIndex = 0;
     478           0 :   return data;
     479             : }
     480             : 
     481             : static void
     482           0 : DestroyUserData(TextRunUserData* aUserData)
     483             : {
     484           0 :   if (aUserData) {
     485           0 :     free(aUserData);
     486             :   }
     487           0 : }
     488             : 
     489             : static ComplexTextRunUserData*
     490           0 : CreateComplexUserData(uint32_t aMappedFlowCount)
     491             : {
     492             :   ComplexTextRunUserData* data = static_cast<ComplexTextRunUserData*>
     493           0 :       (moz_xmalloc(sizeof(ComplexTextRunUserData) +
     494           0 :        aMappedFlowCount * sizeof(TextRunMappedFlow)));
     495           0 :   new (data) ComplexTextRunUserData();
     496             : #ifdef DEBUG
     497           0 :   data->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(data + 1);
     498             : #endif
     499           0 :   data->mMappedFlowCount = aMappedFlowCount;
     500           0 :   data->mLastFlowIndex = 0;
     501           0 :   return data;
     502             : }
     503             : 
     504             : static void
     505           0 : DestroyComplexUserData(ComplexTextRunUserData* aUserData)
     506             : {
     507           0 :   if (aUserData) {
     508           0 :     aUserData->~ComplexTextRunUserData();
     509           0 :     free(aUserData);
     510             :   }
     511           0 : }
     512             : 
     513             : static void
     514           9 : DestroyTextRunUserData(gfxTextRun* aTextRun)
     515             : {
     516           9 :   MOZ_ASSERT(aTextRun->GetUserData());
     517           9 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
     518           9 :     if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
     519           0 :       delete static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
     520             :     }
     521             :   } else {
     522           0 :     if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
     523             :       DestroyComplexUserData(
     524           0 :         static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()));
     525             :     } else {
     526             :       DestroyUserData(
     527           0 :         static_cast<TextRunUserData*>(aTextRun->GetUserData()));
     528             :     }
     529             :   }
     530           9 :   aTextRun->ClearFlagBits(nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES);
     531           9 :   aTextRun->SetUserData(nullptr);
     532           9 : }
     533             : 
     534             : static TextRunMappedFlow*
     535           0 : GetMappedFlows(const gfxTextRun* aTextRun)
     536             : {
     537           0 :   MOZ_ASSERT(aTextRun->GetUserData(), "UserData must exist.");
     538           0 :   MOZ_ASSERT(!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW),
     539             :             "The method should not be called for simple flows.");
     540             :   TextRunMappedFlow* flows;
     541           0 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
     542           0 :     flows = reinterpret_cast<TextRunMappedFlow*>(
     543           0 :       static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData()) + 1);
     544             :   } else {
     545           0 :     flows = reinterpret_cast<TextRunMappedFlow*>(
     546           0 :       static_cast<TextRunUserData*>(aTextRun->GetUserData()) + 1);
     547             :   }
     548           0 :   MOZ_ASSERT(static_cast<TextRunUserData*>(aTextRun->GetUserData())->
     549             :              mMappedFlows == flows,
     550             :              "GetMappedFlows should return the same pointer as mMappedFlows.");
     551           0 :   return flows;
     552             : }
     553             : 
     554             : /**
     555             :  * These are utility functions just for helping with the complexity related with
     556             :  * the text runs user data.
     557             :  */
     558             : static nsTextFrame*
     559           9 : GetFrameForSimpleFlow(const gfxTextRun* aTextRun)
     560             : {
     561           9 :   MOZ_ASSERT(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW,
     562             :              "Not so simple flow?");
     563           9 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES) {
     564           0 :     return static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())->mFrame;
     565             :   }
     566             : 
     567           9 :   return static_cast<nsTextFrame*>(aTextRun->GetUserData());
     568             : }
     569             : 
     570             : /**
     571             :  * Remove |aTextRun| from the frame continuation chain starting at
     572             :  * |aStartContinuation| if non-null, otherwise starting at |aFrame|.
     573             :  * Unmark |aFrame| as a text run owner if it's the frame we start at.
     574             :  * Return true if |aStartContinuation| is non-null and was found
     575             :  * in the next-continuation chain of |aFrame|.
     576             :  */
     577             : static bool
     578           9 : ClearAllTextRunReferences(nsTextFrame* aFrame, gfxTextRun* aTextRun,
     579             :                           nsTextFrame* aStartContinuation,
     580             :                           nsFrameState aWhichTextRunState)
     581             : {
     582           9 :   NS_PRECONDITION(aFrame, "");
     583           9 :   NS_PRECONDITION(!aStartContinuation ||
     584             :                   (!aStartContinuation->GetTextRun(nsTextFrame::eInflated) ||
     585             :                    aStartContinuation->GetTextRun(nsTextFrame::eInflated) == aTextRun) ||
     586             :                   (!aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) ||
     587             :                    aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) == aTextRun),
     588             :                   "wrong aStartContinuation for this text run");
     589             : 
     590           9 :   if (!aStartContinuation || aStartContinuation == aFrame) {
     591           9 :     aFrame->RemoveStateBits(aWhichTextRunState);
     592             :   } else {
     593           0 :     do {
     594           0 :       NS_ASSERTION(aFrame->IsTextFrame(), "Bad frame");
     595           0 :       aFrame = aFrame->GetNextContinuation();
     596           0 :     } while (aFrame && aFrame != aStartContinuation);
     597             :   }
     598           9 :   bool found = aStartContinuation == aFrame;
     599          27 :   while (aFrame) {
     600           9 :     NS_ASSERTION(aFrame->IsTextFrame(), "Bad frame");
     601           9 :     if (!aFrame->RemoveTextRun(aTextRun)) {
     602           0 :       break;
     603             :     }
     604           9 :     aFrame = aFrame->GetNextContinuation();
     605             :   }
     606           9 :   NS_POSTCONDITION(!found || aStartContinuation, "how did we find null?");
     607           9 :   return found;
     608             : }
     609             : 
     610             : /**
     611             :  * Kill all references to |aTextRun| starting at |aStartContinuation|.
     612             :  * It could be referenced by any of its owners, and all their in-flows.
     613             :  * If |aStartContinuation| is null then process all userdata frames
     614             :  * and their continuations.
     615             :  * @note the caller is expected to take care of possibly destroying the
     616             :  * text run if all userdata frames were reset (userdata is deallocated
     617             :  * by this function though). The caller can detect this has occured by
     618             :  * checking |aTextRun->GetUserData() == nullptr|.
     619             :  */
     620             : static void
     621           9 : UnhookTextRunFromFrames(gfxTextRun* aTextRun, nsTextFrame* aStartContinuation)
     622             : {
     623           9 :   if (!aTextRun->GetUserData()) {
     624           0 :     return;
     625             :   }
     626             : 
     627           9 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
     628           9 :     nsTextFrame* userDataFrame = GetFrameForSimpleFlow(aTextRun);
     629             :     nsFrameState whichTextRunState =
     630           9 :       userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
     631           9 :         ? TEXT_IN_TEXTRUN_USER_DATA
     632           9 :         : TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
     633             :     DebugOnly<bool> found =
     634          18 :       ClearAllTextRunReferences(userDataFrame, aTextRun,
     635          18 :                                 aStartContinuation, whichTextRunState);
     636           9 :     NS_ASSERTION(!aStartContinuation || found,
     637             :                  "aStartContinuation wasn't found in simple flow text run");
     638           9 :     if (!(userDataFrame->GetStateBits() & whichTextRunState)) {
     639           9 :       DestroyTextRunUserData(aTextRun);
     640             :     }
     641             :   } else {
     642           0 :     auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
     643           0 :     TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
     644           0 :     int32_t destroyFromIndex = aStartContinuation ? -1 : 0;
     645           0 :     for (uint32_t i = 0; i < userData->mMappedFlowCount; ++i) {
     646           0 :       nsTextFrame* userDataFrame = userMappedFlows[i].mStartFrame;
     647             :       nsFrameState whichTextRunState =
     648           0 :         userDataFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
     649           0 :           ? TEXT_IN_TEXTRUN_USER_DATA
     650           0 :           : TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
     651             :       bool found =
     652             :         ClearAllTextRunReferences(userDataFrame, aTextRun,
     653           0 :                                   aStartContinuation, whichTextRunState);
     654           0 :       if (found) {
     655           0 :         if (userDataFrame->GetStateBits() & whichTextRunState) {
     656           0 :           destroyFromIndex = i + 1;
     657             :         } else {
     658           0 :           destroyFromIndex = i;
     659             :         }
     660           0 :         aStartContinuation = nullptr;
     661             :       }
     662             :     }
     663           0 :     NS_ASSERTION(destroyFromIndex >= 0,
     664             :                  "aStartContinuation wasn't found in multi flow text run");
     665           0 :     if (destroyFromIndex == 0) {
     666           0 :       DestroyTextRunUserData(aTextRun);
     667             :     } else {
     668           0 :       userData->mMappedFlowCount = uint32_t(destroyFromIndex);
     669           0 :       if (userData->mLastFlowIndex >= uint32_t(destroyFromIndex)) {
     670           0 :         userData->mLastFlowIndex = uint32_t(destroyFromIndex) - 1;
     671             :       }
     672             :     }
     673             :   }
     674             : }
     675             : 
     676             : static void
     677           0 : InvalidateFrameDueToGlyphsChanged(nsIFrame* aFrame)
     678             : {
     679           0 :   MOZ_ASSERT(aFrame);
     680             : 
     681           0 :   nsIPresShell* shell = aFrame->PresContext()->PresShell();
     682           0 :   for (nsIFrame* f = aFrame; f;
     683             :        f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
     684           0 :     f->InvalidateFrame();
     685             : 
     686             :     // If this is a non-display text frame within SVG <text>, we need
     687             :     // to reflow the SVGTextFrame. (This is similar to reflowing the
     688             :     // SVGTextFrame in response to style changes, in
     689             :     // SVGTextFrame::DidSetStyleContext.)
     690           0 :     if (nsSVGUtils::IsInSVGTextSubtree(f) &&
     691           0 :         f->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
     692             :       auto svgTextFrame = static_cast<SVGTextFrame*>(
     693           0 :         nsLayoutUtils::GetClosestFrameOfType(f, LayoutFrameType::SVGText));
     694           0 :       svgTextFrame->ScheduleReflowSVGNonDisplayText(nsIPresShell::eResize);
     695             :     } else {
     696             :       // Theoretically we could just update overflow areas, perhaps using
     697             :       // OverflowChangedTracker, but that would do a bunch of work eagerly that
     698             :       // we should probably do lazily here since there could be a lot
     699             :       // of text frames affected and we'd like to coalesce the work. So that's
     700             :       // not easy to do well.
     701           0 :       shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
     702             :     }
     703             :   }
     704           0 : }
     705             : 
     706             : void
     707           0 : GlyphObserver::NotifyGlyphsChanged()
     708             : {
     709           0 :   if (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
     710           0 :     InvalidateFrameDueToGlyphsChanged(GetFrameForSimpleFlow(mTextRun));
     711           0 :     return;
     712             :   }
     713             : 
     714           0 :   auto data = static_cast<TextRunUserData*>(mTextRun->GetUserData());
     715           0 :   TextRunMappedFlow* userMappedFlows = GetMappedFlows(mTextRun);
     716           0 :   for (uint32_t i = 0; i < data->mMappedFlowCount; ++i) {
     717           0 :     InvalidateFrameDueToGlyphsChanged(userMappedFlows[i].mStartFrame);
     718             :   }
     719             : }
     720             : 
     721         378 : int32_t nsTextFrame::GetContentEnd() const {
     722         378 :   nsTextFrame* next = GetNextContinuation();
     723         378 :   return next ? next->GetContentOffset() : mContent->GetText()->GetLength();
     724             : }
     725             : 
     726             : struct FlowLengthProperty {
     727             :   int32_t mStartOffset;
     728             :   // The offset of the next fixed continuation after mStartOffset, or
     729             :   // of the end of the text if there is none
     730             :   int32_t mEndFlowOffset;
     731             : };
     732             : 
     733          48 : int32_t nsTextFrame::GetInFlowContentLength() {
     734          48 :   if (!(mState & NS_FRAME_IS_BIDI)) {
     735          48 :     return mContent->TextLength() - mContentOffset;
     736             :   }
     737             : 
     738             :   FlowLengthProperty* flowLength =
     739           0 :     static_cast<FlowLengthProperty*>(mContent->GetProperty(nsGkAtoms::flowlength));
     740             : 
     741             :   /**
     742             :    * This frame must start inside the cached flow. If the flow starts at
     743             :    * mContentOffset but this frame is empty, logically it might be before the
     744             :    * start of the cached flow.
     745             :    */
     746           0 :   if (flowLength &&
     747           0 :       (flowLength->mStartOffset < mContentOffset ||
     748           0 :        (flowLength->mStartOffset == mContentOffset && GetContentEnd() > mContentOffset)) &&
     749           0 :       flowLength->mEndFlowOffset > mContentOffset) {
     750             : #ifdef DEBUG
     751           0 :     NS_ASSERTION(flowLength->mEndFlowOffset >= GetContentEnd(),
     752             :                  "frame crosses fixed continuation boundary");
     753             : #endif
     754           0 :     return flowLength->mEndFlowOffset - mContentOffset;
     755             :   }
     756             : 
     757           0 :   nsTextFrame* nextBidi = LastInFlow()->GetNextContinuation();
     758           0 :   int32_t endFlow = nextBidi ? nextBidi->GetContentOffset() : mContent->TextLength();
     759             : 
     760           0 :   if (!flowLength) {
     761           0 :     flowLength = new FlowLengthProperty;
     762           0 :     if (NS_FAILED(mContent->SetProperty(nsGkAtoms::flowlength, flowLength,
     763             :                                         nsINode::DeleteProperty<FlowLengthProperty>))) {
     764             :       delete flowLength;
     765           0 :       flowLength = nullptr;
     766             :     }
     767             :   }
     768           0 :   if (flowLength) {
     769           0 :     flowLength->mStartOffset = mContentOffset;
     770           0 :     flowLength->mEndFlowOffset = endFlow;
     771             :   }
     772             : 
     773           0 :   return endFlow - mContentOffset;
     774             : }
     775             : 
     776             : // Smarter versions of dom::IsSpaceCharacter.
     777             : // Unicode is really annoying; sometimes a space character isn't whitespace ---
     778             : // when it combines with another character
     779             : // So we have several versions of IsSpace for use in different contexts.
     780             : 
     781           0 : static bool IsSpaceCombiningSequenceTail(const nsTextFragment* aFrag, uint32_t aPos)
     782             : {
     783           0 :   NS_ASSERTION(aPos <= aFrag->GetLength(), "Bad offset");
     784           0 :   if (!aFrag->Is2b())
     785           0 :     return false;
     786           0 :   return nsTextFrameUtils::IsSpaceCombiningSequenceTail(
     787           0 :     aFrag->Get2b() + aPos, aFrag->GetLength() - aPos);
     788             : }
     789             : 
     790             : // Check whether aPos is a space for CSS 'word-spacing' purposes
     791             : static bool
     792           0 : IsCSSWordSpacingSpace(const nsTextFragment* aFrag, uint32_t aPos,
     793             :                       const nsTextFrame* aFrame, const nsStyleText* aStyleText)
     794             : {
     795           0 :   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
     796             : 
     797           0 :   char16_t ch = aFrag->CharAt(aPos);
     798           0 :   switch (ch) {
     799             :   case ' ':
     800             :   case CH_NBSP:
     801           0 :     return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
     802             :   case '\r':
     803           0 :   case '\t': return !aStyleText->WhiteSpaceIsSignificant();
     804           0 :   case '\n': return !aStyleText->NewlineIsSignificant(aFrame);
     805           0 :   default: return false;
     806             :   }
     807             : }
     808             : 
     809             : // Check whether the string aChars/aLength starts with space that's
     810             : // trimmable according to CSS 'white-space:normal/nowrap'.
     811           0 : static bool IsTrimmableSpace(const char16_t* aChars, uint32_t aLength)
     812             : {
     813           0 :   NS_ASSERTION(aLength > 0, "No text for IsSpace!");
     814             : 
     815           0 :   char16_t ch = *aChars;
     816           0 :   if (ch == ' ')
     817           0 :     return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(aChars + 1, aLength - 1);
     818           0 :   return ch == '\t' || ch == '\f' || ch == '\n' || ch == '\r';
     819             : }
     820             : 
     821             : // Check whether the character aCh is trimmable according to CSS
     822             : // 'white-space:normal/nowrap'
     823          31 : static bool IsTrimmableSpace(char aCh)
     824             : {
     825          31 :   return aCh == ' ' || aCh == '\t' || aCh == '\f' || aCh == '\n' || aCh == '\r';
     826             : }
     827             : 
     828          32 : static bool IsTrimmableSpace(const nsTextFragment* aFrag, uint32_t aPos,
     829             :                                const nsStyleText* aStyleText)
     830             : {
     831          32 :   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
     832             : 
     833          32 :   switch (aFrag->CharAt(aPos)) {
     834           0 :   case ' ': return !aStyleText->WhiteSpaceIsSignificant() &&
     835           0 :                    !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
     836           0 :   case '\n': return !aStyleText->NewlineIsSignificantStyle() &&
     837           0 :                     aStyleText->mWhiteSpace != mozilla::StyleWhiteSpace::PreSpace;
     838             :   case '\t':
     839             :   case '\r':
     840           0 :   case '\f': return !aStyleText->WhiteSpaceIsSignificant();
     841          32 :   default: return false;
     842             :   }
     843             : }
     844             : 
     845           0 : static bool IsSelectionSpace(const nsTextFragment* aFrag, uint32_t aPos)
     846             : {
     847           0 :   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
     848           0 :   char16_t ch = aFrag->CharAt(aPos);
     849           0 :   if (ch == ' ' || ch == CH_NBSP)
     850           0 :     return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
     851           0 :   return ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r';
     852             : }
     853             : 
     854             : // Count the amount of trimmable whitespace (as per CSS
     855             : // 'white-space:normal/nowrap') in a text fragment. The first
     856             : // character is at offset aStartOffset; the maximum number of characters
     857             : // to check is aLength. aDirection is -1 or 1 depending on whether we should
     858             : // progress backwards or forwards.
     859             : static uint32_t
     860          31 : GetTrimmableWhitespaceCount(const nsTextFragment* aFrag,
     861             :                             int32_t aStartOffset, int32_t aLength,
     862             :                             int32_t aDirection)
     863             : {
     864          31 :   int32_t count = 0;
     865          31 :   if (aFrag->Is2b()) {
     866           0 :     const char16_t* str = aFrag->Get2b() + aStartOffset;
     867           0 :     int32_t fragLen = aFrag->GetLength() - aStartOffset;
     868           0 :     for (; count < aLength; ++count) {
     869           0 :       if (!IsTrimmableSpace(str, fragLen))
     870           0 :         break;
     871           0 :       str += aDirection;
     872           0 :       fragLen -= aDirection;
     873             :     }
     874             :   } else {
     875          31 :     const char* str = aFrag->Get1b() + aStartOffset;
     876          31 :     for (; count < aLength; ++count) {
     877          31 :       if (!IsTrimmableSpace(*str))
     878          31 :         break;
     879           0 :       str += aDirection;
     880             :     }
     881             :   }
     882          31 :   return count;
     883             : }
     884             : 
     885             : static bool
     886          22 : IsAllWhitespace(const nsTextFragment* aFrag, bool aAllowNewline)
     887             : {
     888          22 :   if (aFrag->Is2b())
     889           0 :     return false;
     890          22 :   int32_t len = aFrag->GetLength();
     891          22 :   const char* str = aFrag->Get1b();
     892          22 :   for (int32_t i = 0; i < len; ++i) {
     893           6 :     char ch = str[i];
     894           6 :     if (ch == ' ' || ch == '\t' || ch == '\r' || (ch == '\n' && aAllowNewline))
     895           0 :       continue;
     896           6 :     return false;
     897             :   }
     898          16 :   return true;
     899             : }
     900             : 
     901             : static void
     902          21 : ClearObserversFromTextRun(gfxTextRun* aTextRun)
     903             : {
     904          21 :   if (!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
     905          21 :     return;
     906             :   }
     907             : 
     908           0 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
     909           0 :     static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData())
     910           0 :       ->mGlyphObservers.Clear();
     911             :   } else {
     912           0 :     static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData())
     913           0 :       ->mGlyphObservers.Clear();
     914             :   }
     915             : }
     916             : 
     917             : static void
     918          21 : CreateObserversForAnimatedGlyphs(gfxTextRun* aTextRun)
     919             : {
     920          21 :   if (!aTextRun->GetUserData()) {
     921          21 :     return;
     922             :   }
     923             : 
     924          21 :   ClearObserversFromTextRun(aTextRun);
     925             : 
     926          21 :   nsTArray<gfxFont*> fontsWithAnimatedGlyphs;
     927             :   uint32_t numGlyphRuns;
     928             :   const gfxTextRun::GlyphRun* glyphRuns =
     929          21 :     aTextRun->GetGlyphRuns(&numGlyphRuns);
     930          30 :   for (uint32_t i = 0; i < numGlyphRuns; ++i) {
     931           9 :     gfxFont* font = glyphRuns[i].mFont;
     932           9 :     if (font->GlyphsMayChange() && !fontsWithAnimatedGlyphs.Contains(font)) {
     933           0 :       fontsWithAnimatedGlyphs.AppendElement(font);
     934             :     }
     935             :   }
     936          21 :   if (fontsWithAnimatedGlyphs.IsEmpty()) {
     937             :     // NB: Theoretically, we should clear the TEXT_MIGHT_HAVE_GLYPH_CHANGES
     938             :     // here. That would involve de-allocating the simple user data struct if
     939             :     // present too, and resetting the pointer to the frame. In practice, I
     940             :     // don't think worth doing that work here, given the flag's only purpose is
     941             :     // to distinguish what kind of user data is there.
     942          21 :     return;
     943             :   }
     944             : 
     945             :   nsTArray<UniquePtr<GlyphObserver>>* observers;
     946             : 
     947           0 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
     948             :     // Swap the frame pointer for a just-allocated SimpleTextRunUserData if
     949             :     // appropriate.
     950           0 :     if (!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
     951           0 :       auto frame = static_cast<nsTextFrame*>(aTextRun->GetUserData());
     952           0 :       aTextRun->SetUserData(new SimpleTextRunUserData(frame));
     953             :     }
     954             : 
     955             :     auto data =
     956           0 :       static_cast<SimpleTextRunUserData*>(aTextRun->GetUserData());
     957           0 :     observers = &data->mGlyphObservers;
     958             :   } else {
     959           0 :     if (!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES)) {
     960           0 :       auto oldData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
     961           0 :       TextRunMappedFlow* oldMappedFlows = GetMappedFlows(aTextRun);
     962             :       ComplexTextRunUserData* data =
     963           0 :         CreateComplexUserData(oldData->mMappedFlowCount);
     964             :       TextRunMappedFlow* dataMappedFlows =
     965           0 :         reinterpret_cast<TextRunMappedFlow*>(data + 1);
     966           0 :       data->mLastFlowIndex = oldData->mLastFlowIndex;
     967           0 :       for (uint32_t i = 0; i < oldData->mMappedFlowCount; ++i) {
     968           0 :         dataMappedFlows[i] = oldMappedFlows[i];
     969             :       }
     970           0 :       DestroyUserData(oldData);
     971           0 :       aTextRun->SetUserData(data);
     972             :     }
     973           0 :     auto data = static_cast<ComplexTextRunUserData*>(aTextRun->GetUserData());
     974           0 :     observers = &data->mGlyphObservers;
     975             :   }
     976             : 
     977           0 :   aTextRun->SetFlagBits(nsTextFrameUtils::Flags::TEXT_MIGHT_HAVE_GLYPH_CHANGES);
     978             : 
     979           0 :   for (auto font : fontsWithAnimatedGlyphs) {
     980           0 :     observers->AppendElement(new GlyphObserver(font, aTextRun));
     981             :   }
     982             : }
     983             : 
     984             : /**
     985             :  * This class accumulates state as we scan a paragraph of text. It detects
     986             :  * textrun boundaries (changes from text to non-text, hard
     987             :  * line breaks, and font changes) and builds a gfxTextRun at each boundary.
     988             :  * It also detects linebreaker run boundaries (changes from text to non-text,
     989             :  * and hard line breaks) and at each boundary runs the linebreaker to compute
     990             :  * potential line breaks. It also records actual line breaks to store them in
     991             :  * the textruns.
     992             :  */
     993             : class BuildTextRunsScanner {
     994             : public:
     995          21 :   BuildTextRunsScanner(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
     996          21 :       nsIFrame* aLineContainer, nsTextFrame::TextRunType aWhichTextRun) :
     997             :     mDrawTarget(aDrawTarget),
     998             :     mLineContainer(aLineContainer),
     999             :     mCommonAncestorWithLastFrame(nullptr),
    1000          21 :     mMissingFonts(aPresContext->MissingFontRecorder()),
    1001          21 :     mBidiEnabled(aPresContext->BidiEnabled()),
    1002             :     mSkipIncompleteTextRuns(false),
    1003             :     mWhichTextRun(aWhichTextRun),
    1004             :     mNextRunContextInfo(nsTextFrameUtils::INCOMING_NONE),
    1005          63 :     mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) {
    1006          21 :     ResetRunInfo();
    1007          21 :   }
    1008          42 :   ~BuildTextRunsScanner() {
    1009          21 :     NS_ASSERTION(mBreakSinks.IsEmpty(), "Should have been cleared");
    1010          21 :     NS_ASSERTION(mLineBreakBeforeFrames.IsEmpty(), "Should have been cleared");
    1011          21 :     NS_ASSERTION(mMappedFlows.IsEmpty(), "Should have been cleared");
    1012          21 :   }
    1013             : 
    1014          42 :   void SetAtStartOfLine() {
    1015          42 :     mStartOfLine = true;
    1016          42 :     mCanStopOnThisLine = false;
    1017          42 :   }
    1018          21 :   void SetSkipIncompleteTextRuns(bool aSkip) {
    1019          21 :     mSkipIncompleteTextRuns = aSkip;
    1020          21 :   }
    1021          21 :   void SetCommonAncestorWithLastFrame(nsIFrame* aFrame) {
    1022          21 :     mCommonAncestorWithLastFrame = aFrame;
    1023          21 :   }
    1024           0 :   bool CanStopOnThisLine() {
    1025           0 :     return mCanStopOnThisLine;
    1026             :   }
    1027             :   nsIFrame* GetCommonAncestorWithLastFrame() {
    1028             :     return mCommonAncestorWithLastFrame;
    1029             :   }
    1030           0 :   void LiftCommonAncestorWithLastFrameToParent(nsIFrame* aFrame) {
    1031           0 :     if (mCommonAncestorWithLastFrame &&
    1032           0 :         mCommonAncestorWithLastFrame->GetParent() == aFrame) {
    1033           0 :       mCommonAncestorWithLastFrame = aFrame;
    1034             :     }
    1035           0 :   }
    1036             :   void ScanFrame(nsIFrame* aFrame);
    1037             :   bool IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun);
    1038             :   void FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak);
    1039             :   void FlushLineBreaks(gfxTextRun* aTrailingTextRun);
    1040          42 :   void ResetRunInfo() {
    1041          42 :     mLastFrame = nullptr;
    1042          42 :     mMappedFlows.Clear();
    1043          42 :     mLineBreakBeforeFrames.Clear();
    1044          42 :     mMaxTextLength = 0;
    1045          42 :     mDoubleByteText = false;
    1046          42 :   }
    1047             :   void AccumulateRunInfo(nsTextFrame* aFrame);
    1048             :   /**
    1049             :    * @return null to indicate either textrun construction failed or
    1050             :    * we constructed just a partial textrun to set up linebreaker and other
    1051             :    * state for following textruns.
    1052             :    */
    1053             :   already_AddRefed<gfxTextRun> BuildTextRunForFrames(void* aTextBuffer);
    1054             :   bool SetupLineBreakerContext(gfxTextRun *aTextRun);
    1055             :   void AssignTextRun(gfxTextRun* aTextRun, float aInflation);
    1056             :   nsTextFrame* GetNextBreakBeforeFrame(uint32_t* aIndex);
    1057             :   void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
    1058             :   void SetupTextEmphasisForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
    1059             :   struct FindBoundaryState {
    1060             :     nsIFrame*    mStopAtFrame;
    1061             :     nsTextFrame* mFirstTextFrame;
    1062             :     nsTextFrame* mLastTextFrame;
    1063             :     bool mSeenTextRunBoundaryOnLaterLine;
    1064             :     bool mSeenTextRunBoundaryOnThisLine;
    1065             :     bool mSeenSpaceForLineBreakingOnThisLine;
    1066             :     nsTArray<char16_t>& mBuffer;
    1067             :   };
    1068             :   enum FindBoundaryResult {
    1069             :     FB_CONTINUE,
    1070             :     FB_STOPPED_AT_STOP_FRAME,
    1071             :     FB_FOUND_VALID_TEXTRUN_BOUNDARY
    1072             :   };
    1073             :   FindBoundaryResult FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState);
    1074             : 
    1075             :   bool ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2);
    1076             : 
    1077             :   // Like TextRunMappedFlow but with some differences. mStartFrame to mEndFrame
    1078             :   // (exclusive) are a sequence of in-flow frames (if mEndFrame is null, then
    1079             :   // continuations starting from mStartFrame are a sequence of in-flow frames).
    1080             :   struct MappedFlow {
    1081             :     nsTextFrame* mStartFrame;
    1082             :     nsTextFrame* mEndFrame;
    1083             :     // When we consider breaking between elements, the nearest common
    1084             :     // ancestor of the elements containing the characters is the one whose
    1085             :     // CSS 'white-space' property governs. So this records the nearest common
    1086             :     // ancestor of mStartFrame and the previous text frame, or null if there
    1087             :     // was no previous text frame on this line.
    1088             :     nsIFrame*    mAncestorControllingInitialBreak;
    1089             : 
    1090          63 :     int32_t GetContentEnd() {
    1091         126 :       return mEndFrame ? mEndFrame->GetContentOffset()
    1092         126 :           : mStartFrame->GetContent()->GetText()->GetLength();
    1093             :     }
    1094             :   };
    1095             : 
    1096          21 :   class BreakSink final : public nsILineBreakSink {
    1097             :   public:
    1098          21 :     BreakSink(gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
    1099             :               uint32_t aOffsetIntoTextRun)
    1100          21 :       : mTextRun(aTextRun)
    1101             :       , mDrawTarget(aDrawTarget)
    1102          21 :       , mOffsetIntoTextRun(aOffsetIntoTextRun)
    1103          21 :     {}
    1104             : 
    1105          10 :     virtual void SetBreaks(uint32_t aOffset, uint32_t aLength,
    1106             :                            uint8_t* aBreakBefore) override {
    1107          10 :       gfxTextRun::Range range(aOffset + mOffsetIntoTextRun,
    1108          20 :                               aOffset + mOffsetIntoTextRun + aLength);
    1109          10 :       if (mTextRun->SetPotentialLineBreaks(range, aBreakBefore)) {
    1110             :         // Be conservative and assume that some breaks have been set
    1111           1 :         mTextRun->ClearFlagBits(nsTextFrameUtils::Flags::TEXT_NO_BREAKS);
    1112             :       }
    1113          10 :     }
    1114             : 
    1115           0 :     virtual void SetCapitalization(uint32_t aOffset, uint32_t aLength,
    1116             :                                    bool* aCapitalize) override {
    1117           0 :       MOZ_ASSERT(mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_TRANSFORMED,
    1118             :                  "Text run should be transformed!");
    1119           0 :       if (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_TRANSFORMED) {
    1120             :         nsTransformedTextRun* transformedTextRun =
    1121           0 :           static_cast<nsTransformedTextRun*>(mTextRun.get());
    1122           0 :         transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun, aLength,
    1123           0 :                                               aCapitalize);
    1124             :       }
    1125           0 :     }
    1126             : 
    1127          21 :     void Finish(gfxMissingFontRecorder* aMFR) {
    1128          21 :       MOZ_ASSERT(!(mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_UNUSED_FLAGS),
    1129             :                    "Flag set that should never be set! (memory safety error?)");
    1130          21 :       if (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_TRANSFORMED) {
    1131             :         nsTransformedTextRun* transformedTextRun =
    1132           0 :           static_cast<nsTransformedTextRun*>(mTextRun.get());
    1133           0 :         transformedTextRun->FinishSettingProperties(mDrawTarget, aMFR);
    1134             :       }
    1135             :       // The way nsTransformedTextRun is implemented, its glyph runs aren't
    1136             :       // available until after nsTransformedTextRun::FinishSettingProperties()
    1137             :       // is called. So that's why we defer checking for animated glyphs to here.
    1138          21 :       CreateObserversForAnimatedGlyphs(mTextRun);
    1139          21 :     }
    1140             : 
    1141             :     RefPtr<gfxTextRun> mTextRun;
    1142             :     DrawTarget*  mDrawTarget;
    1143             :     uint32_t     mOffsetIntoTextRun;
    1144             :   };
    1145             : 
    1146             : private:
    1147             :   AutoTArray<MappedFlow,10>   mMappedFlows;
    1148             :   AutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames;
    1149             :   AutoTArray<UniquePtr<BreakSink>,10> mBreakSinks;
    1150             :   nsLineBreaker                 mLineBreaker;
    1151             :   RefPtr<gfxTextRun>            mCurrentFramesAllSameTextRun;
    1152             :   DrawTarget*                   mDrawTarget;
    1153             :   nsIFrame*                     mLineContainer;
    1154             :   nsTextFrame*                  mLastFrame;
    1155             :   // The common ancestor of the current frame and the previous leaf frame
    1156             :   // on the line, or null if there was no previous leaf frame.
    1157             :   nsIFrame*                     mCommonAncestorWithLastFrame;
    1158             :   gfxMissingFontRecorder*       mMissingFonts;
    1159             :   // mMaxTextLength is an upper bound on the size of the text in all mapped frames
    1160             :   // The value UINT32_MAX represents overflow; text will be discarded
    1161             :   uint32_t                      mMaxTextLength;
    1162             :   bool                          mDoubleByteText;
    1163             :   bool                          mBidiEnabled;
    1164             :   bool                          mStartOfLine;
    1165             :   bool                          mSkipIncompleteTextRuns;
    1166             :   bool                          mCanStopOnThisLine;
    1167             :   nsTextFrame::TextRunType      mWhichTextRun;
    1168             :   uint8_t                       mNextRunContextInfo;
    1169             :   uint8_t                       mCurrentRunContextInfo;
    1170             : };
    1171             : 
    1172             : static nsIFrame*
    1173         118 : FindLineContainer(nsIFrame* aFrame)
    1174             : {
    1175         236 :   while (aFrame && (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
    1176          59 :                     aFrame->CanContinueTextRun())) {
    1177          59 :     aFrame = aFrame->GetParent();
    1178             :   }
    1179          59 :   return aFrame;
    1180             : }
    1181             : 
    1182             : static bool
    1183           0 : IsLineBreakingWhiteSpace(char16_t aChar)
    1184             : {
    1185             :   // 0x0A (\n) is not handled as white-space by the line breaker, since
    1186             :   // we break before it, if it isn't transformed to a normal space.
    1187             :   // (If we treat it as normal white-space then we'd only break after it.)
    1188             :   // However, it does induce a line break or is converted to a regular
    1189             :   // space, and either way it can be used to bound the region of text
    1190             :   // that needs to be analyzed for line breaking.
    1191           0 :   return nsLineBreaker::IsSpace(aChar) || aChar == 0x0A;
    1192             : }
    1193             : 
    1194             : static bool
    1195           0 : TextContainsLineBreakerWhiteSpace(const void* aText, uint32_t aLength,
    1196             :                                   bool aIsDoubleByte)
    1197             : {
    1198           0 :   if (aIsDoubleByte) {
    1199           0 :     const char16_t* chars = static_cast<const char16_t*>(aText);
    1200           0 :     for (uint32_t i = 0; i < aLength; ++i) {
    1201           0 :       if (IsLineBreakingWhiteSpace(chars[i]))
    1202           0 :         return true;
    1203             :     }
    1204           0 :     return false;
    1205             :   } else {
    1206           0 :     const uint8_t* chars = static_cast<const uint8_t*>(aText);
    1207           0 :     for (uint32_t i = 0; i < aLength; ++i) {
    1208           0 :       if (IsLineBreakingWhiteSpace(chars[i]))
    1209           0 :         return true;
    1210             :     }
    1211           0 :     return false;
    1212             :   }
    1213             : }
    1214             : 
    1215             : static_assert(uint8_t(mozilla::StyleWhiteSpace::Normal) == 0, "Convention: StyleWhiteSpace::Normal should be 0");
    1216             : static_assert(uint8_t(mozilla::StyleWhiteSpace::Pre) == 1, "Convention: StyleWhiteSpace::Pre should be 1");
    1217             : static_assert(uint8_t(mozilla::StyleWhiteSpace::Nowrap) == 2, "Convention: StyleWhiteSpace::NoWrap should be 2");
    1218             : static_assert(uint8_t(mozilla::StyleWhiteSpace::PreWrap) == 3, "Convention: StyleWhiteSpace::PreWrap should be 3");
    1219             : static_assert(uint8_t(mozilla::StyleWhiteSpace::PreLine) == 4, "Convention: StyleWhiteSpace::PreLine should be 4");
    1220             : static_assert(uint8_t(mozilla::StyleWhiteSpace::PreSpace) == 5, "Convention: StyleWhiteSpace::PreSpace should be 5");
    1221             : 
    1222             : static nsTextFrameUtils::CompressionMode
    1223          21 : GetCSSWhitespaceToCompressionMode(nsTextFrame* aFrame,
    1224             :                                   const nsStyleText* aStyleText)
    1225             : {
    1226             :   static const nsTextFrameUtils::CompressionMode sModes[] =
    1227             :   {
    1228             :     nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE,     // normal
    1229             :     nsTextFrameUtils::COMPRESS_NONE,                   // pre
    1230             :     nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE,     // nowrap
    1231             :     nsTextFrameUtils::COMPRESS_NONE,                   // pre-wrap
    1232             :     nsTextFrameUtils::COMPRESS_WHITESPACE,             // pre-line
    1233             :     nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE // -moz-pre-space
    1234             :   };
    1235             : 
    1236          21 :   auto compression = sModes[uint8_t(aStyleText->mWhiteSpace)];
    1237          28 :   if (compression == nsTextFrameUtils::COMPRESS_NONE &&
    1238           7 :       !aStyleText->NewlineIsSignificant(aFrame)) {
    1239             :     // If newline is set to be preserved, but then suppressed,
    1240             :     // transform newline to space.
    1241           0 :     compression = nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE;
    1242             :   }
    1243          21 :   return compression;
    1244             : }
    1245             : 
    1246             : struct FrameTextTraversal
    1247             : {
    1248           0 :   FrameTextTraversal()
    1249           0 :     : mFrameToScan(nullptr)
    1250             :     , mOverflowFrameToScan(nullptr)
    1251             :     , mScanSiblings(false)
    1252             :     , mLineBreakerCanCrossFrameBoundary(false)
    1253           0 :     , mTextRunCanCrossFrameBoundary(false)
    1254           0 :   {}
    1255             : 
    1256             :   // These fields identify which frames should be recursively scanned
    1257             :   // The first normal frame to scan (or null, if no such frame should be scanned)
    1258             :   nsIFrame*    mFrameToScan;
    1259             :   // The first overflow frame to scan (or null, if no such frame should be scanned)
    1260             :   nsIFrame*    mOverflowFrameToScan;
    1261             :   // Whether to scan the siblings of mFrameToDescendInto/mOverflowFrameToDescendInto
    1262             :   bool mScanSiblings;
    1263             : 
    1264             :   // These identify the boundaries of the context required for
    1265             :   // line breaking or textrun construction
    1266             :   bool mLineBreakerCanCrossFrameBoundary;
    1267             :   bool mTextRunCanCrossFrameBoundary;
    1268             : 
    1269           0 :   nsIFrame* NextFrameToScan() {
    1270             :     nsIFrame* f;
    1271           0 :     if (mFrameToScan) {
    1272           0 :       f = mFrameToScan;
    1273           0 :       mFrameToScan = mScanSiblings ? f->GetNextSibling() : nullptr;
    1274           0 :     } else if (mOverflowFrameToScan) {
    1275           0 :       f = mOverflowFrameToScan;
    1276           0 :       mOverflowFrameToScan = mScanSiblings ? f->GetNextSibling() : nullptr;
    1277             :     } else {
    1278           0 :       f = nullptr;
    1279             :     }
    1280           0 :     return f;
    1281             :   }
    1282             : };
    1283             : 
    1284             : static FrameTextTraversal
    1285           0 : CanTextCrossFrameBoundary(nsIFrame* aFrame)
    1286             : {
    1287           0 :   FrameTextTraversal result;
    1288             : 
    1289           0 :   bool continuesTextRun = aFrame->CanContinueTextRun();
    1290           0 :   if (aFrame->IsPlaceholderFrame()) {
    1291             :     // placeholders are "invisible", so a text run should be able to span
    1292             :     // across one. But don't descend into the out-of-flow.
    1293           0 :     result.mLineBreakerCanCrossFrameBoundary = true;
    1294           0 :     if (continuesTextRun) {
    1295             :       // ... Except for first-letter floats, which are really in-flow
    1296             :       // from the point of view of capitalization etc, so we'd better
    1297             :       // descend into them. But we actually need to break the textrun for
    1298             :       // first-letter floats since things look bad if, say, we try to make a
    1299             :       // ligature across the float boundary.
    1300           0 :       result.mFrameToScan =
    1301           0 :         (static_cast<nsPlaceholderFrame*>(aFrame))->GetOutOfFlowFrame();
    1302             :     } else {
    1303           0 :       result.mTextRunCanCrossFrameBoundary = true;
    1304             :     }
    1305             :   } else {
    1306           0 :     if (continuesTextRun) {
    1307           0 :       result.mFrameToScan = aFrame->PrincipalChildList().FirstChild();
    1308           0 :       result.mOverflowFrameToScan =
    1309           0 :         aFrame->GetChildList(nsIFrame::kOverflowList).FirstChild();
    1310           0 :       NS_WARNING_ASSERTION(
    1311             :         !result.mOverflowFrameToScan,
    1312             :         "Scanning overflow inline frames is something we should avoid");
    1313           0 :       result.mScanSiblings = true;
    1314           0 :       result.mTextRunCanCrossFrameBoundary = true;
    1315           0 :       result.mLineBreakerCanCrossFrameBoundary = true;
    1316             :     } else {
    1317           0 :       MOZ_ASSERT(!aFrame->IsRubyTextContainerFrame(),
    1318             :                  "Shouldn't call this method for ruby text container");
    1319             :     }
    1320             :   }
    1321           0 :   return result;
    1322             : }
    1323             : 
    1324             : BuildTextRunsScanner::FindBoundaryResult
    1325           0 : BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState)
    1326             : {
    1327           0 :   LayoutFrameType frameType = aFrame->Type();
    1328           0 :   if (frameType == LayoutFrameType::RubyTextContainer) {
    1329             :     // Don't stop a text run for ruby text container. We want ruby text
    1330             :     // containers to be skipped, but continue the text run across them.
    1331           0 :     return FB_CONTINUE;
    1332             :   }
    1333             : 
    1334             :   nsTextFrame* textFrame = frameType == LayoutFrameType::Text
    1335           0 :                              ? static_cast<nsTextFrame*>(aFrame)
    1336           0 :                              : nullptr;
    1337           0 :   if (textFrame) {
    1338           0 :     if (aState->mLastTextFrame &&
    1339           0 :         textFrame != aState->mLastTextFrame->GetNextInFlow() &&
    1340           0 :         !ContinueTextRunAcrossFrames(aState->mLastTextFrame, textFrame)) {
    1341           0 :       aState->mSeenTextRunBoundaryOnThisLine = true;
    1342           0 :       if (aState->mSeenSpaceForLineBreakingOnThisLine)
    1343           0 :         return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1344             :     }
    1345           0 :     if (!aState->mFirstTextFrame) {
    1346           0 :       aState->mFirstTextFrame = textFrame;
    1347             :     }
    1348           0 :     aState->mLastTextFrame = textFrame;
    1349             :   }
    1350             : 
    1351           0 :   if (aFrame == aState->mStopAtFrame)
    1352           0 :     return FB_STOPPED_AT_STOP_FRAME;
    1353             : 
    1354           0 :   if (textFrame) {
    1355           0 :     if (aState->mSeenSpaceForLineBreakingOnThisLine) {
    1356           0 :       return FB_CONTINUE;
    1357             :     }
    1358           0 :     const nsTextFragment* frag = textFrame->GetContent()->GetText();
    1359           0 :     uint32_t start = textFrame->GetContentOffset();
    1360           0 :     uint32_t length = textFrame->GetContentLength();
    1361             :     const void* text;
    1362           0 :     if (frag->Is2b()) {
    1363             :       // It is possible that we may end up removing all whitespace in
    1364             :       // a piece of text because of The White Space Processing Rules,
    1365             :       // so we need to transform it before we can check existence of
    1366             :       // such whitespaces.
    1367           0 :       aState->mBuffer.EnsureLengthAtLeast(length);
    1368             :       nsTextFrameUtils::CompressionMode compression =
    1369           0 :         GetCSSWhitespaceToCompressionMode(textFrame, textFrame->StyleText());
    1370           0 :       uint8_t incomingFlags = 0;
    1371           0 :       gfxSkipChars skipChars;
    1372             :       nsTextFrameUtils::Flags analysisFlags;
    1373           0 :       char16_t* bufStart = aState->mBuffer.Elements();
    1374           0 :       char16_t* bufEnd = nsTextFrameUtils::TransformText(
    1375           0 :         frag->Get2b() + start, length, bufStart, compression,
    1376           0 :         &incomingFlags, &skipChars, &analysisFlags);
    1377           0 :       text = bufStart;
    1378           0 :       length = bufEnd - bufStart;
    1379             :     } else {
    1380             :       // If the text only contains ASCII characters, it is currently
    1381             :       // impossible that TransformText would remove all whitespaces,
    1382             :       // and thus the check below should return the same result for
    1383             :       // transformed text and original text. So we don't need to try
    1384             :       // transforming it here.
    1385           0 :       text = static_cast<const void*>(frag->Get1b() + start);
    1386             :     }
    1387           0 :     if (TextContainsLineBreakerWhiteSpace(text, length, frag->Is2b())) {
    1388           0 :       aState->mSeenSpaceForLineBreakingOnThisLine = true;
    1389           0 :       if (aState->mSeenTextRunBoundaryOnLaterLine) {
    1390           0 :         return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1391             :       }
    1392             :     }
    1393           0 :     return FB_CONTINUE;
    1394             :   }
    1395             : 
    1396           0 :   FrameTextTraversal traversal = CanTextCrossFrameBoundary(aFrame);
    1397           0 :   if (!traversal.mTextRunCanCrossFrameBoundary) {
    1398           0 :     aState->mSeenTextRunBoundaryOnThisLine = true;
    1399           0 :     if (aState->mSeenSpaceForLineBreakingOnThisLine)
    1400           0 :       return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1401             :   }
    1402             : 
    1403           0 :   for (nsIFrame* f = traversal.NextFrameToScan(); f;
    1404             :        f = traversal.NextFrameToScan()) {
    1405           0 :     FindBoundaryResult result = FindBoundaries(f, aState);
    1406           0 :     if (result != FB_CONTINUE)
    1407           0 :       return result;
    1408             :   }
    1409             : 
    1410           0 :   if (!traversal.mTextRunCanCrossFrameBoundary) {
    1411           0 :     aState->mSeenTextRunBoundaryOnThisLine = true;
    1412           0 :     if (aState->mSeenSpaceForLineBreakingOnThisLine)
    1413           0 :       return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1414             :   }
    1415             : 
    1416           0 :   return FB_CONTINUE;
    1417             : }
    1418             : 
    1419             : // build text runs for the 200 lines following aForFrame, and stop after that
    1420             : // when we get a chance.
    1421             : #define NUM_LINES_TO_BUILD_TEXT_RUNS 200
    1422             : 
    1423             : /**
    1424             :  * General routine for building text runs. This is hairy because of the need
    1425             :  * to build text runs that span content nodes.
    1426             :  *
    1427             :  * @param aContext The gfxContext we're using to construct this text run.
    1428             :  * @param aForFrame The nsTextFrame for which we're building this text run.
    1429             :  * @param aLineContainer the line container containing aForFrame; if null,
    1430             :  *        we'll walk the ancestors to find it.  It's required to be non-null
    1431             :  *        when aForFrameLine is non-null.
    1432             :  * @param aForFrameLine the line containing aForFrame; if null, we'll figure
    1433             :  *        out the line (slowly)
    1434             :  * @param aWhichTextRun The type of text run we want to build. If font inflation
    1435             :  *        is enabled, this will be eInflated, otherwise it's eNotInflated.
    1436             :  */
    1437             : static void
    1438          21 : BuildTextRuns(DrawTarget* aDrawTarget, nsTextFrame* aForFrame,
    1439             :               nsIFrame* aLineContainer,
    1440             :               const nsLineList::iterator* aForFrameLine,
    1441             :               nsTextFrame::TextRunType aWhichTextRun)
    1442             : {
    1443          21 :   MOZ_ASSERT(aForFrame, "for no frame?");
    1444          21 :   NS_ASSERTION(!aForFrameLine || aLineContainer,
    1445             :                "line but no line container");
    1446             : 
    1447          21 :   nsIFrame* lineContainerChild = aForFrame;
    1448          21 :   if (!aLineContainer) {
    1449           0 :     if (aForFrame->IsFloatingFirstLetterChild()) {
    1450           0 :       lineContainerChild = aForFrame->GetParent()->GetPlaceholderFrame();
    1451             :     }
    1452           0 :     aLineContainer = FindLineContainer(lineContainerChild);
    1453             :   } else {
    1454          21 :     NS_ASSERTION((aLineContainer == FindLineContainer(aForFrame) ||
    1455             :                   (aLineContainer->IsLetterFrame() &&
    1456             :                    aLineContainer->IsFloating())),
    1457             :                  "Wrong line container hint");
    1458             :   }
    1459             : 
    1460          21 :   if (aForFrame->HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML)) {
    1461           0 :     aLineContainer->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);
    1462           0 :     if (aForFrame->HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI)) {
    1463           0 :       aLineContainer->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
    1464             :     }
    1465             :   }
    1466          21 :   if (aForFrame->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
    1467           0 :     aLineContainer->AddStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT);
    1468             :   }
    1469             : 
    1470          21 :   nsPresContext* presContext = aLineContainer->PresContext();
    1471             :   BuildTextRunsScanner scanner(presContext, aDrawTarget,
    1472          42 :                                aLineContainer, aWhichTextRun);
    1473             : 
    1474          21 :   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aLineContainer);
    1475             : 
    1476          21 :   if (!block) {
    1477           0 :     nsIFrame* textRunContainer = aLineContainer;
    1478           0 :     if (aLineContainer->IsRubyTextContainerFrame()) {
    1479           0 :       textRunContainer = aForFrame;
    1480           0 :       while (textRunContainer && !textRunContainer->IsRubyTextFrame()) {
    1481           0 :         textRunContainer = textRunContainer->GetParent();
    1482             :       }
    1483           0 :       MOZ_ASSERT(textRunContainer &&
    1484             :                  textRunContainer->GetParent() == aLineContainer);
    1485             :     } else {
    1486           0 :       NS_ASSERTION(
    1487             :         !aLineContainer->GetPrevInFlow() && !aLineContainer->GetNextInFlow(),
    1488             :         "Breakable non-block line containers other than "
    1489             :         "ruby text container is not supported");
    1490             :     }
    1491             :     // Just loop through all the children of the linecontainer ... it's really
    1492             :     // just one line
    1493           0 :     scanner.SetAtStartOfLine();
    1494           0 :     scanner.SetCommonAncestorWithLastFrame(nullptr);
    1495           0 :     for (nsIFrame* child : textRunContainer->PrincipalChildList()) {
    1496           0 :       scanner.ScanFrame(child);
    1497             :     }
    1498             :     // Set mStartOfLine so FlushFrames knows its textrun ends a line
    1499           0 :     scanner.SetAtStartOfLine();
    1500           0 :     scanner.FlushFrames(true, false);
    1501           0 :     return;
    1502             :   }
    1503             : 
    1504             :   // Find the line containing 'lineContainerChild'.
    1505             : 
    1506          21 :   bool isValid = true;
    1507          21 :   nsBlockInFlowLineIterator backIterator(block, &isValid);
    1508          21 :   if (aForFrameLine) {
    1509          21 :     backIterator = nsBlockInFlowLineIterator(block, *aForFrameLine);
    1510             :   } else {
    1511           0 :     backIterator = nsBlockInFlowLineIterator(block, lineContainerChild, &isValid);
    1512           0 :     NS_ASSERTION(isValid, "aForFrame not found in block, someone lied to us");
    1513           0 :     NS_ASSERTION(backIterator.GetContainer() == block,
    1514             :                  "Someone lied to us about the block");
    1515             :   }
    1516          21 :   nsBlockFrame::LineIterator startLine = backIterator.GetLine();
    1517             : 
    1518             :   // Find a line where we can start building text runs. We choose the last line
    1519             :   // where:
    1520             :   // -- there is a textrun boundary between the start of the line and the
    1521             :   // start of aForFrame
    1522             :   // -- there is a space between the start of the line and the textrun boundary
    1523             :   // (this is so we can be sure the line breaks will be set properly
    1524             :   // on the textruns we construct).
    1525             :   // The possibly-partial text runs up to and including the first space
    1526             :   // are not reconstructed. We construct partial text runs for that text ---
    1527             :   // for the sake of simplifying the code and feeding the linebreaker ---
    1528             :   // but we discard them instead of assigning them to frames.
    1529             :   // This is a little awkward because we traverse lines in the reverse direction
    1530             :   // but we traverse the frames in each line in the forward direction.
    1531          21 :   nsBlockInFlowLineIterator forwardIterator = backIterator;
    1532          21 :   nsIFrame* stopAtFrame = lineContainerChild;
    1533          21 :   nsTextFrame* nextLineFirstTextFrame = nullptr;
    1534          42 :   AutoTArray<char16_t, BIG_TEXT_NODE_SIZE> buffer;
    1535          21 :   bool seenTextRunBoundaryOnLaterLine = false;
    1536          21 :   bool mayBeginInTextRun = true;
    1537             :   while (true) {
    1538          21 :     forwardIterator = backIterator;
    1539          21 :     nsBlockFrame::LineIterator line = backIterator.GetLine();
    1540          21 :     if (!backIterator.Prev() || backIterator.GetLine()->IsBlock()) {
    1541          21 :       mayBeginInTextRun = false;
    1542          42 :       break;
    1543             :     }
    1544             : 
    1545             :     BuildTextRunsScanner::FindBoundaryState state = { stopAtFrame, nullptr, nullptr,
    1546           0 :       bool(seenTextRunBoundaryOnLaterLine), false, false, buffer };
    1547           0 :     nsIFrame* child = line->mFirstChild;
    1548           0 :     bool foundBoundary = false;
    1549           0 :     for (int32_t i = line->GetChildCount() - 1; i >= 0; --i) {
    1550             :       BuildTextRunsScanner::FindBoundaryResult result =
    1551           0 :           scanner.FindBoundaries(child, &state);
    1552           0 :       if (result == BuildTextRunsScanner::FB_FOUND_VALID_TEXTRUN_BOUNDARY) {
    1553           0 :         foundBoundary = true;
    1554           0 :         break;
    1555           0 :       } else if (result == BuildTextRunsScanner::FB_STOPPED_AT_STOP_FRAME) {
    1556           0 :         break;
    1557             :       }
    1558           0 :       child = child->GetNextSibling();
    1559             :     }
    1560           0 :     if (foundBoundary)
    1561           0 :       break;
    1562           0 :     if (!stopAtFrame && state.mLastTextFrame && nextLineFirstTextFrame &&
    1563           0 :         !scanner.ContinueTextRunAcrossFrames(state.mLastTextFrame, nextLineFirstTextFrame)) {
    1564             :       // Found a usable textrun boundary at the end of the line
    1565           0 :       if (state.mSeenSpaceForLineBreakingOnThisLine)
    1566           0 :         break;
    1567           0 :       seenTextRunBoundaryOnLaterLine = true;
    1568           0 :     } else if (state.mSeenTextRunBoundaryOnThisLine) {
    1569           0 :       seenTextRunBoundaryOnLaterLine = true;
    1570             :     }
    1571           0 :     stopAtFrame = nullptr;
    1572           0 :     if (state.mFirstTextFrame) {
    1573           0 :       nextLineFirstTextFrame = state.mFirstTextFrame;
    1574             :     }
    1575           0 :   }
    1576          21 :   scanner.SetSkipIncompleteTextRuns(mayBeginInTextRun);
    1577             : 
    1578             :   // Now iterate over all text frames starting from the current line. First-in-flow
    1579             :   // text frames will be accumulated into textRunFrames as we go. When a
    1580             :   // text run boundary is required we flush textRunFrames ((re)building their
    1581             :   // gfxTextRuns as necessary).
    1582          21 :   bool seenStartLine = false;
    1583          21 :   uint32_t linesAfterStartLine = 0;
    1584          21 :   do {
    1585          21 :     nsBlockFrame::LineIterator line = forwardIterator.GetLine();
    1586          21 :     if (line->IsBlock())
    1587           0 :       break;
    1588          21 :     line->SetInvalidateTextRuns(false);
    1589          21 :     scanner.SetAtStartOfLine();
    1590          21 :     scanner.SetCommonAncestorWithLastFrame(nullptr);
    1591          21 :     nsIFrame* child = line->mFirstChild;
    1592          42 :     for (int32_t i = line->GetChildCount() - 1; i >= 0; --i) {
    1593          21 :       scanner.ScanFrame(child);
    1594          21 :       child = child->GetNextSibling();
    1595             :     }
    1596          21 :     if (line.get() == startLine.get()) {
    1597          21 :       seenStartLine = true;
    1598             :     }
    1599          21 :     if (seenStartLine) {
    1600          21 :       ++linesAfterStartLine;
    1601          21 :       if (linesAfterStartLine >= NUM_LINES_TO_BUILD_TEXT_RUNS && scanner.CanStopOnThisLine()) {
    1602             :         // Don't flush frames; we may be in the middle of a textrun
    1603             :         // that we can't end here. That's OK, we just won't build it.
    1604             :         // Note that we must already have finished the textrun for aForFrame,
    1605             :         // because we've seen the end of a textrun in a line after the line
    1606             :         // containing aForFrame.
    1607           0 :         scanner.FlushLineBreaks(nullptr);
    1608             :         // This flushes out mMappedFlows and mLineBreakBeforeFrames, which
    1609             :         // silences assertions in the scanner destructor.
    1610           0 :         scanner.ResetRunInfo();
    1611           0 :         return;
    1612             :       }
    1613             :     }
    1614             :   } while (forwardIterator.Next());
    1615             : 
    1616             :   // Set mStartOfLine so FlushFrames knows its textrun ends a line
    1617          21 :   scanner.SetAtStartOfLine();
    1618          21 :   scanner.FlushFrames(true, false);
    1619             : }
    1620             : 
    1621             : static char16_t*
    1622           0 : ExpandBuffer(char16_t* aDest, uint8_t* aSrc, uint32_t aCount)
    1623             : {
    1624           0 :   while (aCount) {
    1625           0 :     *aDest = *aSrc;
    1626           0 :     ++aDest;
    1627           0 :     ++aSrc;
    1628           0 :     --aCount;
    1629             :   }
    1630           0 :   return aDest;
    1631             : }
    1632             : 
    1633             : bool
    1634           0 : BuildTextRunsScanner::IsTextRunValidForMappedFlows(const gfxTextRun* aTextRun)
    1635             : {
    1636           0 :   if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
    1637           0 :     return mMappedFlows.Length() == 1 &&
    1638           0 :       mMappedFlows[0].mStartFrame == GetFrameForSimpleFlow(aTextRun) &&
    1639           0 :       mMappedFlows[0].mEndFrame == nullptr;
    1640             :   }
    1641             : 
    1642           0 :   auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
    1643           0 :   TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
    1644           0 :   if (userData->mMappedFlowCount != mMappedFlows.Length())
    1645           0 :     return false;
    1646           0 :   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
    1647           0 :     if (userMappedFlows[i].mStartFrame != mMappedFlows[i].mStartFrame ||
    1648           0 :         int32_t(userMappedFlows[i].mContentLength) !=
    1649           0 :             mMappedFlows[i].GetContentEnd() - mMappedFlows[i].mStartFrame->GetContentOffset())
    1650           0 :       return false;
    1651             :   }
    1652           0 :   return true;
    1653             : }
    1654             : 
    1655             : /**
    1656             :  * This gets called when we need to make a text run for the current list of
    1657             :  * frames.
    1658             :  */
    1659          21 : void BuildTextRunsScanner::FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak)
    1660             : {
    1661          42 :   RefPtr<gfxTextRun> textRun;
    1662          21 :   if (!mMappedFlows.IsEmpty()) {
    1663         105 :     if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
    1664          21 :         !!(mCurrentFramesAllSameTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_INCOMING_WHITESPACE) ==
    1665           0 :         !!(mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) &&
    1666          21 :         !!(mCurrentFramesAllSameTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_INCOMING_ARABICCHAR) ==
    1667          42 :         !!(mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) &&
    1668           0 :         IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
    1669             :       // Optimization: We do not need to (re)build the textrun.
    1670           0 :       textRun = mCurrentFramesAllSameTextRun;
    1671             : 
    1672             :       // Feed this run's text into the linebreaker to provide context.
    1673           0 :       if (!SetupLineBreakerContext(textRun)) {
    1674           0 :         return;
    1675             :       }
    1676             : 
    1677             :       // Update mNextRunContextInfo appropriately
    1678           0 :       mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE;
    1679           0 :       if (textRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_TRAILING_WHITESPACE) {
    1680           0 :         mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE;
    1681             :       }
    1682           0 :       if (textRun->GetFlags() & gfx::ShapedTextFlags::TEXT_TRAILING_ARABICCHAR) {
    1683           0 :         mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR;
    1684             :       }
    1685             :     } else {
    1686          42 :       AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> buffer;
    1687          21 :       uint32_t bufferSize = mMaxTextLength*(mDoubleByteText ? 2 : 1);
    1688          42 :       if (bufferSize < mMaxTextLength || bufferSize == UINT32_MAX ||
    1689          21 :           !buffer.AppendElements(bufferSize, fallible)) {
    1690           0 :         return;
    1691             :       }
    1692          21 :       textRun = BuildTextRunForFrames(buffer.Elements());
    1693             :     }
    1694             :   }
    1695             : 
    1696          21 :   if (aFlushLineBreaks) {
    1697          21 :     FlushLineBreaks(aSuppressTrailingBreak ? nullptr : textRun.get());
    1698             :   }
    1699             : 
    1700          21 :   mCanStopOnThisLine = true;
    1701          21 :   ResetRunInfo();
    1702             : }
    1703             : 
    1704          21 : void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun)
    1705             : {
    1706             :   bool trailingLineBreak;
    1707          21 :   nsresult rv = mLineBreaker.Reset(&trailingLineBreak);
    1708             :   // textRun may be null for various reasons, including because we constructed
    1709             :   // a partial textrun just to get the linebreaker and other state set up
    1710             :   // to build the next textrun.
    1711          21 :   if (NS_SUCCEEDED(rv) && trailingLineBreak && aTrailingTextRun) {
    1712           0 :     aTrailingTextRun->SetFlagBits(nsTextFrameUtils::Flags::TEXT_HAS_TRAILING_BREAK);
    1713             :   }
    1714             : 
    1715          42 :   for (uint32_t i = 0; i < mBreakSinks.Length(); ++i) {
    1716             :     // TODO cause frames associated with the textrun to be reflowed, if they
    1717             :     // aren't being reflowed already!
    1718          21 :     mBreakSinks[i]->Finish(mMissingFonts);
    1719             :   }
    1720          21 :   mBreakSinks.Clear();
    1721          21 : }
    1722             : 
    1723          21 : void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame)
    1724             : {
    1725          21 :   if (mMaxTextLength != UINT32_MAX) {
    1726          21 :     NS_ASSERTION(mMaxTextLength < UINT32_MAX - aFrame->GetContentLength(), "integer overflow");
    1727          21 :     if (mMaxTextLength >= UINT32_MAX - aFrame->GetContentLength()) {
    1728           0 :       mMaxTextLength = UINT32_MAX;
    1729             :     } else {
    1730          21 :       mMaxTextLength += aFrame->GetContentLength();
    1731             :     }
    1732             :   }
    1733          21 :   mDoubleByteText |= aFrame->GetContent()->GetText()->Is2b();
    1734          21 :   mLastFrame = aFrame;
    1735          21 :   mCommonAncestorWithLastFrame = aFrame->GetParent();
    1736             : 
    1737          21 :   MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
    1738          21 :   NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
    1739             :                mappedFlow->GetContentEnd() == aFrame->GetContentOffset(),
    1740             :                "Overlapping or discontiguous frames => BAD");
    1741          21 :   mappedFlow->mEndFrame = aFrame->GetNextContinuation();
    1742          21 :   if (mCurrentFramesAllSameTextRun != aFrame->GetTextRun(mWhichTextRun)) {
    1743           0 :     mCurrentFramesAllSameTextRun = nullptr;
    1744             :   }
    1745             : 
    1746          21 :   if (mStartOfLine) {
    1747          21 :     mLineBreakBeforeFrames.AppendElement(aFrame);
    1748          21 :     mStartOfLine = false;
    1749             :   }
    1750          21 : }
    1751             : 
    1752             : static bool
    1753           0 : HasTerminalNewline(const nsTextFrame* aFrame)
    1754             : {
    1755           0 :   if (aFrame->GetContentLength() == 0)
    1756           0 :     return false;
    1757           0 :   const nsTextFragment* frag = aFrame->GetContent()->GetText();
    1758           0 :   return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n';
    1759             : }
    1760             : 
    1761             : static gfxFont::Metrics
    1762           0 : GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVerticalMetrics)
    1763             : {
    1764           0 :   if (!aFontGroup)
    1765           0 :     return gfxFont::Metrics();
    1766           0 :   gfxFont* font = aFontGroup->GetFirstValidFont();
    1767             :   return font->GetMetrics(aVerticalMetrics ? gfxFont::eVertical
    1768           0 :                                            : gfxFont::eHorizontal);
    1769             : }
    1770             : 
    1771             : static gfxFloat
    1772           0 : GetSpaceWidthAppUnits(const gfxTextRun* aTextRun)
    1773             : {
    1774             :   // Round the space width when converting to appunits the same way textruns
    1775             :   // do.
    1776             :   gfxFloat spaceWidthAppUnits =
    1777           0 :     NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
    1778           0 :                                  aTextRun->UseCenterBaseline()).spaceWidth *
    1779           0 :              aTextRun->GetAppUnitsPerDevUnit());
    1780             : 
    1781           0 :   return spaceWidthAppUnits;
    1782             : }
    1783             : 
    1784             : static nscoord
    1785         101 : LetterSpacing(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr)
    1786             : {
    1787         101 :   if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
    1788           0 :     return 0;
    1789             :   }
    1790         101 :   if (!aStyleText) {
    1791          18 :     aStyleText = aFrame->StyleText();
    1792             :   }
    1793             : 
    1794         101 :   const nsStyleCoord& coord = aStyleText->mLetterSpacing;
    1795         101 :   if (eStyleUnit_Coord == coord.GetUnit()) {
    1796           0 :     return coord.GetCoordValue();
    1797             :   }
    1798         101 :   return 0;
    1799             : }
    1800             : 
    1801             : // This function converts non-coord values (e.g. percentages) to nscoord.
    1802             : static nscoord
    1803          80 : WordSpacing(nsIFrame* aFrame, const gfxTextRun* aTextRun,
    1804             :             const nsStyleText* aStyleText = nullptr)
    1805             : {
    1806          80 :   if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
    1807           0 :     return 0;
    1808             :   }
    1809          80 :   if (!aStyleText) {
    1810          18 :     aStyleText = aFrame->StyleText();
    1811             :   }
    1812             : 
    1813          80 :   const nsStyleCoord& coord = aStyleText->mWordSpacing;
    1814          80 :   if (coord.IsCoordPercentCalcUnit()) {
    1815          80 :     nscoord pctBasis = coord.HasPercent() ? GetSpaceWidthAppUnits(aTextRun) : 0;
    1816          80 :     return nsRuleNode::ComputeCoordPercentCalc(coord, pctBasis);
    1817             :   }
    1818           0 :   return 0;
    1819             : }
    1820             : 
    1821             : // Returns gfxTextRunFactory::TEXT_ENABLE_SPACING if non-standard
    1822             : // letter-spacing or word-spacing is present.
    1823             : static gfx::ShapedTextFlags
    1824          21 : GetSpacingFlags(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr)
    1825             : {
    1826          21 :   if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
    1827           0 :     return gfx::ShapedTextFlags();
    1828             :   }
    1829             : 
    1830          21 :   const nsStyleText* styleText = aFrame->StyleText();
    1831          21 :   const nsStyleCoord& ls = styleText->mLetterSpacing;
    1832          21 :   const nsStyleCoord& ws = styleText->mWordSpacing;
    1833             : 
    1834             :   // It's possible to have a calc() value that computes to zero but for which
    1835             :   // IsDefinitelyZero() is false, in which case we'll return
    1836             :   // TEXT_ENABLE_SPACING unnecessarily. That's ok because such cases are likely
    1837             :   // to be rare, and avoiding TEXT_ENABLE_SPACING is just an optimization.
    1838             :   bool nonStandardSpacing =
    1839          42 :     (eStyleUnit_Coord == ls.GetUnit() && ls.GetCoordValue() != 0) ||
    1840          63 :     (eStyleUnit_Coord == ws.GetUnit() && ws.GetCoordValue() != 0) ||
    1841          63 :     (eStyleUnit_Percent == ws.GetUnit() && ws.GetPercentValue() != 0) ||
    1842          42 :     (eStyleUnit_Calc == ws.GetUnit() && !ws.GetCalcValue()->IsDefinitelyZero());
    1843             : 
    1844             :   return nonStandardSpacing
    1845          21 :     ? gfx::ShapedTextFlags::TEXT_ENABLE_SPACING
    1846          21 :     : gfx::ShapedTextFlags();
    1847             : }
    1848             : 
    1849             : bool
    1850           0 : BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2)
    1851             : {
    1852             :   // We don't need to check font size inflation, since
    1853             :   // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|)
    1854             :   // ensures that text runs never cross block boundaries.  This means
    1855             :   // that the font size inflation on all text frames in the text run is
    1856             :   // already guaranteed to be the same as each other (and for the line
    1857             :   // container).
    1858           0 :   if (mBidiEnabled) {
    1859           0 :     FrameBidiData data1 = aFrame1->GetBidiData();
    1860           0 :     FrameBidiData data2 = aFrame2->GetBidiData();
    1861           0 :     if (data1.embeddingLevel != data2.embeddingLevel ||
    1862           0 :         data2.precedingControl != kBidiLevelNone) {
    1863           0 :       return false;
    1864             :     }
    1865             :   }
    1866             : 
    1867           0 :   nsStyleContext* sc1 = aFrame1->StyleContext();
    1868           0 :   const nsStyleText* textStyle1 = sc1->StyleText();
    1869             :   // If the first frame ends in a preformatted newline, then we end the textrun
    1870             :   // here. This avoids creating giant textruns for an entire plain text file.
    1871             :   // Note that we create a single text frame for a preformatted text node,
    1872             :   // even if it has newlines in it, so typically we won't see trailing newlines
    1873             :   // until after reflow has broken up the frame into one (or more) frames per
    1874             :   // line. That's OK though.
    1875           0 :   if (textStyle1->NewlineIsSignificant(aFrame1) && HasTerminalNewline(aFrame1))
    1876           0 :     return false;
    1877             : 
    1878           0 :   if (aFrame1->GetContent() == aFrame2->GetContent() &&
    1879           0 :       aFrame1->GetNextInFlow() != aFrame2) {
    1880             :     // aFrame2 must be a non-fluid continuation of aFrame1. This can happen
    1881             :     // sometimes when the unicode-bidi property is used; the bidi resolver
    1882             :     // breaks text into different frames even though the text has the same
    1883             :     // direction. We can't allow these two frames to share the same textrun
    1884             :     // because that would violate our invariant that two flows in the same
    1885             :     // textrun have different content elements.
    1886           0 :     return false;
    1887             :   }
    1888             : 
    1889           0 :   nsStyleContext* sc2 = aFrame2->StyleContext();
    1890           0 :   const nsStyleText* textStyle2 = sc2->StyleText();
    1891           0 :   if (sc1 == sc2)
    1892           0 :     return true;
    1893             : 
    1894           0 :   const nsStyleFont* fontStyle1 = sc1->StyleFont();
    1895           0 :   const nsStyleFont* fontStyle2 = sc2->StyleFont();
    1896           0 :   nscoord letterSpacing1 = LetterSpacing(aFrame1);
    1897           0 :   nscoord letterSpacing2 = LetterSpacing(aFrame2);
    1898           0 :   return fontStyle1->mFont == fontStyle2->mFont &&
    1899           0 :     fontStyle1->mLanguage == fontStyle2->mLanguage &&
    1900           0 :     textStyle1->mTextTransform == textStyle2->mTextTransform &&
    1901           0 :     nsLayoutUtils::GetTextRunFlagsForStyle(sc1, fontStyle1, textStyle1, letterSpacing1) ==
    1902           0 :       nsLayoutUtils::GetTextRunFlagsForStyle(sc2, fontStyle2, textStyle2, letterSpacing2);
    1903             : }
    1904             : 
    1905          21 : void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
    1906             : {
    1907          21 :   LayoutFrameType frameType = aFrame->Type();
    1908          21 :   if (frameType == LayoutFrameType::RubyTextContainer) {
    1909             :     // Don't include any ruby text container into the text run.
    1910          21 :     return;
    1911             :   }
    1912             : 
    1913             :   // First check if we can extend the current mapped frame block. This is common.
    1914          21 :   if (mMappedFlows.Length() > 0) {
    1915           0 :     MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
    1916           0 :     if (mappedFlow->mEndFrame == aFrame &&
    1917           0 :         (aFrame->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION)) {
    1918           0 :       NS_ASSERTION(frameType == LayoutFrameType::Text,
    1919             :                    "Flow-sibling of a text frame is not a text frame?");
    1920             : 
    1921             :       // Don't do this optimization if mLastFrame has a terminal newline...
    1922             :       // it's quite likely preformatted and we might want to end the textrun here.
    1923             :       // This is almost always true:
    1924           0 :       if (mLastFrame->StyleContext() == aFrame->StyleContext() &&
    1925           0 :           !HasTerminalNewline(mLastFrame)) {
    1926           0 :         AccumulateRunInfo(static_cast<nsTextFrame*>(aFrame));
    1927           0 :         return;
    1928             :       }
    1929             :     }
    1930             :   }
    1931             : 
    1932             :   // Now see if we can add a new set of frames to the current textrun
    1933          21 :   if (frameType == LayoutFrameType::Text) {
    1934          21 :     nsTextFrame* frame = static_cast<nsTextFrame*>(aFrame);
    1935             : 
    1936          21 :     if (mLastFrame) {
    1937           0 :       if (!ContinueTextRunAcrossFrames(mLastFrame, frame)) {
    1938           0 :         FlushFrames(false, false);
    1939             :       } else {
    1940           0 :         if (mLastFrame->GetContent() == frame->GetContent()) {
    1941           0 :           AccumulateRunInfo(frame);
    1942           0 :           return;
    1943             :         }
    1944             :       }
    1945             :     }
    1946             : 
    1947          21 :     MappedFlow* mappedFlow = mMappedFlows.AppendElement();
    1948          21 :     if (!mappedFlow)
    1949           0 :       return;
    1950             : 
    1951          21 :     mappedFlow->mStartFrame = frame;
    1952          21 :     mappedFlow->mAncestorControllingInitialBreak = mCommonAncestorWithLastFrame;
    1953             : 
    1954          21 :     AccumulateRunInfo(frame);
    1955          21 :     if (mMappedFlows.Length() == 1) {
    1956          21 :       mCurrentFramesAllSameTextRun = frame->GetTextRun(mWhichTextRun);
    1957          21 :       mCurrentRunContextInfo = mNextRunContextInfo;
    1958             :     }
    1959          21 :     return;
    1960             :   }
    1961             : 
    1962           0 :   FrameTextTraversal traversal = CanTextCrossFrameBoundary(aFrame);
    1963           0 :   bool isBR = frameType == LayoutFrameType::Br;
    1964           0 :   if (!traversal.mLineBreakerCanCrossFrameBoundary) {
    1965             :     // BR frames are special. We do not need or want to record a break opportunity
    1966             :     // before a BR frame.
    1967           0 :     FlushFrames(true, isBR);
    1968           0 :     mCommonAncestorWithLastFrame = aFrame;
    1969           0 :     mNextRunContextInfo &= ~nsTextFrameUtils::INCOMING_WHITESPACE;
    1970           0 :     mStartOfLine = false;
    1971           0 :   } else if (!traversal.mTextRunCanCrossFrameBoundary) {
    1972           0 :     FlushFrames(false, false);
    1973             :   }
    1974             : 
    1975           0 :   for (nsIFrame* f = traversal.NextFrameToScan(); f;
    1976             :        f = traversal.NextFrameToScan()) {
    1977           0 :     ScanFrame(f);
    1978             :   }
    1979             : 
    1980           0 :   if (!traversal.mLineBreakerCanCrossFrameBoundary) {
    1981             :     // Really if we're a BR frame this is unnecessary since descendInto will be
    1982             :     // false. In fact this whole "if" statement should move into the descendInto.
    1983           0 :     FlushFrames(true, isBR);
    1984           0 :     mCommonAncestorWithLastFrame = aFrame;
    1985           0 :     mNextRunContextInfo &= ~nsTextFrameUtils::INCOMING_WHITESPACE;
    1986           0 :   } else if (!traversal.mTextRunCanCrossFrameBoundary) {
    1987           0 :     FlushFrames(false, false);
    1988             :   }
    1989             : 
    1990           0 :   LiftCommonAncestorWithLastFrameToParent(aFrame->GetParent());
    1991             : }
    1992             : 
    1993             : nsTextFrame*
    1994          42 : BuildTextRunsScanner::GetNextBreakBeforeFrame(uint32_t* aIndex)
    1995             : {
    1996          42 :   uint32_t index = *aIndex;
    1997          42 :   if (index >= mLineBreakBeforeFrames.Length())
    1998          21 :     return nullptr;
    1999          21 :   *aIndex = index + 1;
    2000          21 :   return static_cast<nsTextFrame*>(mLineBreakBeforeFrames.ElementAt(index));
    2001             : }
    2002             : 
    2003             : static gfxFontGroup*
    2004          62 : GetFontGroupForFrame(const nsIFrame* aFrame, float aFontSizeInflation,
    2005             :                      nsFontMetrics** aOutFontMetrics = nullptr)
    2006             : {
    2007             :   RefPtr<nsFontMetrics> metrics =
    2008         124 :     nsLayoutUtils::GetFontMetricsForFrame(aFrame, aFontSizeInflation);
    2009          62 :   gfxFontGroup* fontGroup = metrics->GetThebesFontGroup();
    2010             : 
    2011             :   // Populate outparam before we return:
    2012          62 :   if (aOutFontMetrics) {
    2013          41 :     metrics.forget(aOutFontMetrics);
    2014             :   }
    2015             :   // XXX this is a bit bogus, we're releasing 'metrics' so the
    2016             :   // returned font-group might actually be torn down, although because
    2017             :   // of the way the device context caches font metrics, this seems to
    2018             :   // not actually happen. But we should fix this.
    2019         124 :   return fontGroup;
    2020             : }
    2021             : 
    2022             : static already_AddRefed<DrawTarget>
    2023           0 : CreateReferenceDrawTarget(const nsTextFrame* aTextFrame)
    2024             : {
    2025             :   RefPtr<gfxContext> ctx =
    2026           0 :     aTextFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
    2027           0 :   RefPtr<DrawTarget> dt = ctx->GetDrawTarget();
    2028           0 :   return dt.forget();
    2029             : }
    2030             : 
    2031             : static already_AddRefed<gfxTextRun>
    2032           0 : GetHyphenTextRun(const gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
    2033             :                  nsTextFrame* aTextFrame)
    2034             : {
    2035           0 :   RefPtr<DrawTarget> dt = aDrawTarget;
    2036           0 :   if (!dt) {
    2037           0 :     dt = CreateReferenceDrawTarget(aTextFrame);
    2038           0 :     if (!dt) {
    2039           0 :       return nullptr;
    2040             :     }
    2041             :   }
    2042             : 
    2043             :   return aTextRun->GetFontGroup()->
    2044           0 :     MakeHyphenTextRun(dt, aTextRun->GetAppUnitsPerDevUnit());
    2045             : }
    2046             : 
    2047             : already_AddRefed<gfxTextRun>
    2048          21 : BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
    2049             : {
    2050          42 :   gfxSkipChars skipChars;
    2051             : 
    2052          21 :   const void* textPtr = aTextBuffer;
    2053          21 :   bool anyTextTransformStyle = false;
    2054          21 :   bool anyMathMLStyling = false;
    2055          21 :   bool anyTextEmphasis = false;
    2056          21 :   uint8_t sstyScriptLevel = 0;
    2057          21 :   uint32_t mathFlags = 0;
    2058          21 :   gfx::ShapedTextFlags flags = gfx::ShapedTextFlags();
    2059          21 :   nsTextFrameUtils::Flags flags2 = nsTextFrameUtils::Flags::TEXT_NO_BREAKS;
    2060             : 
    2061          21 :   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
    2062           0 :     flags2 |= nsTextFrameUtils::Flags::TEXT_INCOMING_WHITESPACE;
    2063             :   }
    2064          21 :   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
    2065           0 :     flags |= gfx::ShapedTextFlags::TEXT_INCOMING_ARABICCHAR;
    2066             :   }
    2067             : 
    2068          42 :   AutoTArray<int32_t,50> textBreakPoints;
    2069             :   TextRunUserData dummyData;
    2070             :   TextRunMappedFlow dummyMappedFlow;
    2071             :   TextRunMappedFlow* userMappedFlows;
    2072             :   TextRunUserData* userData;
    2073             :   TextRunUserData* userDataToDestroy;
    2074             :   // If the situation is particularly simple (and common) we don't need to
    2075             :   // allocate userData.
    2076          42 :   if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
    2077          21 :       mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
    2078          21 :     userData = &dummyData;
    2079          21 :     userMappedFlows = &dummyMappedFlow;
    2080          21 :     userDataToDestroy = nullptr;
    2081          21 :     dummyData.mMappedFlowCount = mMappedFlows.Length();
    2082          21 :     dummyData.mLastFlowIndex = 0;
    2083             :   } else {
    2084           0 :     userData = CreateUserData(mMappedFlows.Length());
    2085           0 :     userMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
    2086           0 :     userDataToDestroy = userData;
    2087             :   }
    2088             : 
    2089          21 :   uint32_t currentTransformedTextOffset = 0;
    2090             : 
    2091          21 :   uint32_t nextBreakIndex = 0;
    2092          21 :   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    2093          21 :   bool isSVG = nsSVGUtils::IsInSVGTextSubtree(mLineContainer);
    2094             :   bool enabledJustification =
    2095          42 :     (mLineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
    2096          42 :      mLineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY);
    2097             : 
    2098          21 :   const nsStyleText* textStyle = nullptr;
    2099          21 :   const nsStyleFont* fontStyle = nullptr;
    2100          21 :   nsStyleContext* lastStyleContext = nullptr;
    2101          42 :   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
    2102          21 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2103          21 :     nsTextFrame* f = mappedFlow->mStartFrame;
    2104             : 
    2105          21 :     lastStyleContext = f->StyleContext();
    2106             :     // Detect use of text-transform or font-variant anywhere in the run
    2107          21 :     textStyle = f->StyleText();
    2108          42 :     if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform ||
    2109             :         // text-combine-upright requires converting from full-width
    2110             :         // characters to non-full-width correspendent in some cases.
    2111          21 :         lastStyleContext->IsTextCombined()) {
    2112           0 :       anyTextTransformStyle = true;
    2113             :     }
    2114          21 :     if (textStyle->HasTextEmphasis()) {
    2115           0 :       anyTextEmphasis = true;
    2116             :     }
    2117          21 :     flags |= GetSpacingFlags(f);
    2118             :     nsTextFrameUtils::CompressionMode compression =
    2119          21 :       GetCSSWhitespaceToCompressionMode(f, textStyle);
    2120          42 :     if ((enabledJustification || f->ShouldSuppressLineBreak()) &&
    2121          21 :         !textStyle->WhiteSpaceIsSignificant() && !isSVG) {
    2122           0 :       flags |= gfx::ShapedTextFlags::TEXT_ENABLE_SPACING;
    2123             :     }
    2124          21 :     fontStyle = f->StyleFont();
    2125          21 :     nsIFrame* parent = mLineContainer->GetParent();
    2126          21 :     if (NS_MATHML_MATHVARIANT_NONE != fontStyle->mMathVariant) {
    2127           0 :       if (NS_MATHML_MATHVARIANT_NORMAL != fontStyle->mMathVariant) {
    2128           0 :         anyMathMLStyling = true;
    2129             :       }
    2130          21 :     } else if (mLineContainer->GetStateBits() & NS_FRAME_IS_IN_SINGLE_CHAR_MI) {
    2131           0 :       flags2 |= nsTextFrameUtils::Flags::TEXT_IS_SINGLE_CHAR_MI;
    2132           0 :       anyMathMLStyling = true;
    2133             :       // Test for fontstyle attribute as StyleFont() may not be accurate
    2134             :       // To be consistent in terms of ignoring CSS style changes, fontweight
    2135             :       // gets checked too.
    2136           0 :       if (parent) {
    2137           0 :         nsIContent* content = parent->GetContent();
    2138           0 :         if (content) {
    2139           0 :           if (content->AttrValueIs(kNameSpaceID_None,
    2140             :                                   nsGkAtoms::fontstyle_,
    2141           0 :                                   NS_LITERAL_STRING("normal"),
    2142           0 :                                   eCaseMatters)) {
    2143           0 :             mathFlags |= MathMLTextRunFactory::MATH_FONT_STYLING_NORMAL;
    2144             :           }
    2145           0 :           if (content->AttrValueIs(kNameSpaceID_None,
    2146             :                                    nsGkAtoms::fontweight_,
    2147           0 :                                    NS_LITERAL_STRING("bold"),
    2148           0 :                                    eCaseMatters)) {
    2149           0 :             mathFlags |= MathMLTextRunFactory::MATH_FONT_WEIGHT_BOLD;
    2150             :           }
    2151             :         }
    2152             :       }
    2153             :     }
    2154          21 :     if (mLineContainer->HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML)) {
    2155             :       // All MathML tokens except <mtext> use 'math' script.
    2156           0 :       if (!(parent && parent->GetContent() &&
    2157           0 :           parent->GetContent()->IsMathMLElement(nsGkAtoms::mtext_))) {
    2158           0 :         flags |= gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT;
    2159             :       }
    2160           0 :       nsIMathMLFrame* mathFrame = do_QueryFrame(parent);
    2161           0 :       if (mathFrame) {
    2162           0 :         nsPresentationData presData;
    2163           0 :         mathFrame->GetPresentationData(presData);
    2164           0 :         if (NS_MATHML_IS_DTLS_SET(presData.flags)) {
    2165           0 :           mathFlags |= MathMLTextRunFactory::MATH_FONT_FEATURE_DTLS;
    2166           0 :           anyMathMLStyling = true;
    2167             :         }
    2168             :       }
    2169             :     }
    2170          21 :     nsIFrame* child = mLineContainer;
    2171          21 :     uint8_t oldScriptLevel = 0;
    2172          42 :     while (parent &&
    2173          21 :            child->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
    2174             :       // Reconstruct the script level ignoring any user overrides. It is
    2175             :       // calculated this way instead of using scriptlevel to ensure the
    2176             :       // correct ssty font feature setting is used even if the user sets a
    2177             :       // different (especially negative) scriptlevel.
    2178           0 :       nsIMathMLFrame* mathFrame= do_QueryFrame(parent);
    2179           0 :       if (mathFrame) {
    2180           0 :         sstyScriptLevel += mathFrame->ScriptIncrement(child);
    2181             :       }
    2182           0 :       if (sstyScriptLevel < oldScriptLevel) {
    2183             :         // overflow
    2184           0 :         sstyScriptLevel = UINT8_MAX;
    2185           0 :         break;
    2186             :       }
    2187           0 :       child = parent;
    2188           0 :       parent = parent->GetParent();
    2189           0 :       oldScriptLevel = sstyScriptLevel;
    2190             :     }
    2191          21 :     if (sstyScriptLevel) {
    2192           0 :       anyMathMLStyling = true;
    2193             :     }
    2194             : 
    2195             :     // Figure out what content is included in this flow.
    2196          21 :     nsIContent* content = f->GetContent();
    2197          21 :     const nsTextFragment* frag = content->GetText();
    2198          21 :     int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
    2199          21 :     int32_t contentEnd = mappedFlow->GetContentEnd();
    2200          21 :     int32_t contentLength = contentEnd - contentStart;
    2201             : 
    2202          21 :     TextRunMappedFlow* newFlow = &userMappedFlows[i];
    2203          21 :     newFlow->mStartFrame = mappedFlow->mStartFrame;
    2204          42 :     newFlow->mDOMOffsetToBeforeTransformOffset = skipChars.GetOriginalCharCount() -
    2205          21 :       mappedFlow->mStartFrame->GetContentOffset();
    2206          21 :     newFlow->mContentLength = contentLength;
    2207             : 
    2208          63 :     while (nextBreakBeforeFrame && nextBreakBeforeFrame->GetContent() == content) {
    2209             :       textBreakPoints.AppendElement(
    2210          21 :           nextBreakBeforeFrame->GetContentOffset() + newFlow->mDOMOffsetToBeforeTransformOffset);
    2211          21 :       nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    2212             :     }
    2213             : 
    2214             :     nsTextFrameUtils::Flags analysisFlags;
    2215          21 :     if (frag->Is2b()) {
    2216           0 :       NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
    2217           0 :       char16_t* bufStart = static_cast<char16_t*>(aTextBuffer);
    2218           0 :       char16_t* bufEnd = nsTextFrameUtils::TransformText(
    2219           0 :           frag->Get2b() + contentStart, contentLength, bufStart,
    2220           0 :           compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
    2221           0 :       aTextBuffer = bufEnd;
    2222           0 :       currentTransformedTextOffset = bufEnd - static_cast<const char16_t*>(textPtr);
    2223             :     } else {
    2224          21 :       if (mDoubleByteText) {
    2225             :         // Need to expand the text. First transform it into a temporary buffer,
    2226             :         // then expand.
    2227           0 :         AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> tempBuf;
    2228           0 :         uint8_t* bufStart = tempBuf.AppendElements(contentLength, fallible);
    2229           0 :         if (!bufStart) {
    2230           0 :           DestroyUserData(userDataToDestroy);
    2231           0 :           return nullptr;
    2232             :         }
    2233           0 :         uint8_t* end = nsTextFrameUtils::TransformText(
    2234           0 :             reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
    2235           0 :             bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
    2236           0 :         aTextBuffer = ExpandBuffer(static_cast<char16_t*>(aTextBuffer),
    2237           0 :                                    tempBuf.Elements(), end - tempBuf.Elements());
    2238           0 :         currentTransformedTextOffset =
    2239           0 :           static_cast<char16_t*>(aTextBuffer) - static_cast<const char16_t*>(textPtr);
    2240             :       } else {
    2241          21 :         uint8_t* bufStart = static_cast<uint8_t*>(aTextBuffer);
    2242          42 :         uint8_t* end = nsTextFrameUtils::TransformText(
    2243          21 :             reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
    2244          21 :             bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
    2245          21 :         aTextBuffer = end;
    2246          21 :         currentTransformedTextOffset = end - static_cast<const uint8_t*>(textPtr);
    2247             :       }
    2248             :     }
    2249          21 :     flags2 |= analysisFlags;
    2250             :   }
    2251             : 
    2252             :   void* finalUserData;
    2253          21 :   if (userData == &dummyData) {
    2254          21 :     flags2 |= nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW;
    2255          21 :     userData = nullptr;
    2256          21 :     finalUserData = mMappedFlows[0].mStartFrame;
    2257             :   } else {
    2258           0 :     finalUserData = userData;
    2259             :   }
    2260             : 
    2261          21 :   uint32_t transformedLength = currentTransformedTextOffset;
    2262             : 
    2263             :   // Now build the textrun
    2264          21 :   nsTextFrame* firstFrame = mMappedFlows[0].mStartFrame;
    2265             :   float fontInflation;
    2266          21 :   if (mWhichTextRun == nsTextFrame::eNotInflated) {
    2267          19 :     fontInflation = 1.0f;
    2268             :   } else {
    2269           2 :     fontInflation = nsLayoutUtils::FontSizeInflationFor(firstFrame);
    2270             :   }
    2271             : 
    2272          21 :   gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame, fontInflation);
    2273          21 :   if (!fontGroup) {
    2274           0 :     DestroyUserData(userDataToDestroy);
    2275           0 :     return nullptr;
    2276             :   }
    2277             : 
    2278          21 :   if (flags2 & nsTextFrameUtils::Flags::TEXT_HAS_TAB) {
    2279           0 :     flags |= gfx::ShapedTextFlags::TEXT_ENABLE_SPACING;
    2280             :   }
    2281          21 :   if (flags2 & nsTextFrameUtils::Flags::TEXT_HAS_SHY) {
    2282           0 :     flags |= gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS;
    2283             :   }
    2284          21 :   if (mBidiEnabled && (IS_LEVEL_RTL(firstFrame->GetEmbeddingLevel()))) {
    2285           0 :     flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
    2286             :   }
    2287          21 :   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
    2288           0 :     flags2 |= nsTextFrameUtils::Flags::TEXT_TRAILING_WHITESPACE;
    2289             :   }
    2290          21 :   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
    2291           0 :     flags |= gfx::ShapedTextFlags::TEXT_TRAILING_ARABICCHAR;
    2292             :   }
    2293             :   // ContinueTextRunAcrossFrames guarantees that it doesn't matter which
    2294             :   // frame's style is used, so we use a mixture of the first frame and
    2295             :   // last frame's style
    2296             :   flags |= nsLayoutUtils::GetTextRunFlagsForStyle(lastStyleContext,
    2297          21 :       fontStyle, textStyle, LetterSpacing(firstFrame, textStyle));
    2298             :   // XXX this is a bit of a hack. For performance reasons, if we're favouring
    2299             :   // performance over quality, don't try to get accurate glyph extents.
    2300          21 :   if (!(flags & gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED)) {
    2301          21 :     flags |= gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX;
    2302             :   }
    2303             : 
    2304             :   // Convert linebreak coordinates to transformed string offsets
    2305          21 :   NS_ASSERTION(nextBreakIndex == mLineBreakBeforeFrames.Length(),
    2306             :                "Didn't find all the frames to break-before...");
    2307          21 :   gfxSkipCharsIterator iter(skipChars);
    2308          42 :   AutoTArray<uint32_t,50> textBreakPointsAfterTransform;
    2309          42 :   for (uint32_t i = 0; i < textBreakPoints.Length(); ++i) {
    2310          21 :     nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform,
    2311          42 :             iter.ConvertOriginalToSkipped(textBreakPoints[i]));
    2312             :   }
    2313          21 :   if (mStartOfLine) {
    2314             :     nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform,
    2315          21 :                                             transformedLength);
    2316             :   }
    2317             : 
    2318             :   // Setup factory chain
    2319          42 :   UniquePtr<nsTransformingTextRunFactory> transformingFactory;
    2320          21 :   if (anyTextTransformStyle) {
    2321             :     transformingFactory =
    2322           0 :       MakeUnique<nsCaseTransformTextRunFactory>(Move(transformingFactory));
    2323             :   }
    2324          21 :   if (anyMathMLStyling) {
    2325             :     transformingFactory =
    2326           0 :       MakeUnique<MathMLTextRunFactory>(Move(transformingFactory), mathFlags,
    2327           0 :                                        sstyScriptLevel, fontInflation);
    2328             :   }
    2329          42 :   nsTArray<RefPtr<nsTransformedCharStyle>> styles;
    2330          21 :   if (transformingFactory) {
    2331           0 :     iter.SetOriginalOffset(0);
    2332           0 :     for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
    2333           0 :       MappedFlow* mappedFlow = &mMappedFlows[i];
    2334             :       nsTextFrame* f;
    2335           0 :       nsStyleContext* sc = nullptr;
    2336           0 :       RefPtr<nsTransformedCharStyle> charStyle;
    2337           0 :       for (f = mappedFlow->mStartFrame; f != mappedFlow->mEndFrame;
    2338             :            f = f->GetNextContinuation()) {
    2339           0 :         uint32_t offset = iter.GetSkippedOffset();
    2340           0 :         iter.AdvanceOriginal(f->GetContentLength());
    2341           0 :         uint32_t end = iter.GetSkippedOffset();
    2342             :         // Text-combined frames have content-dependent transform, so we
    2343             :         // want to create new nsTransformedCharStyle for them anyway.
    2344           0 :         if (sc != f->StyleContext() || sc->IsTextCombined()) {
    2345           0 :           sc = f->StyleContext();
    2346           0 :           charStyle = new nsTransformedCharStyle(sc);
    2347           0 :           if (sc->IsTextCombined() && f->CountGraphemeClusters() > 1) {
    2348           0 :             charStyle->mForceNonFullWidth = true;
    2349             :           }
    2350             :         }
    2351             :         uint32_t j;
    2352           0 :         for (j = offset; j < end; ++j) {
    2353           0 :           styles.AppendElement(charStyle);
    2354             :         }
    2355             :       }
    2356             :     }
    2357           0 :     flags2 |= nsTextFrameUtils::Flags::TEXT_IS_TRANSFORMED;
    2358           0 :     NS_ASSERTION(iter.GetSkippedOffset() == transformedLength,
    2359             :                  "We didn't cover all the characters in the text run!");
    2360             :   }
    2361             : 
    2362          42 :   RefPtr<gfxTextRun> textRun;
    2363             :   gfxTextRunFactory::Parameters params =
    2364          21 :       { mDrawTarget, finalUserData, &skipChars,
    2365          21 :         textBreakPointsAfterTransform.Elements(),
    2366          21 :         uint32_t(textBreakPointsAfterTransform.Length()),
    2367          63 :         int32_t(firstFrame->PresContext()->AppUnitsPerDevPixel())};
    2368             : 
    2369          21 :   if (mDoubleByteText) {
    2370           0 :     const char16_t* text = static_cast<const char16_t*>(textPtr);
    2371           0 :     if (transformingFactory) {
    2372           0 :       textRun = transformingFactory->MakeTextRun(text, transformedLength,
    2373             :                                                  &params, fontGroup, flags, flags2,
    2374           0 :                                                  Move(styles), true);
    2375           0 :       if (textRun) {
    2376             :         // ownership of the factory has passed to the textrun
    2377             :         // TODO: bug 1285316: clean up ownership transfer from the factory to
    2378             :         // the textrun
    2379           0 :         Unused << transformingFactory.release();
    2380             :       }
    2381             :     } else {
    2382           0 :       textRun = fontGroup->MakeTextRun(text, transformedLength, &params,
    2383           0 :                                        flags, flags2, mMissingFonts);
    2384             :     }
    2385             :   } else {
    2386          21 :     const uint8_t* text = static_cast<const uint8_t*>(textPtr);
    2387          21 :     flags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
    2388          21 :     if (transformingFactory) {
    2389           0 :       textRun = transformingFactory->MakeTextRun(text, transformedLength,
    2390             :                                                  &params, fontGroup, flags, flags2,
    2391           0 :                                                  Move(styles), true);
    2392           0 :       if (textRun) {
    2393             :         // ownership of the factory has passed to the textrun
    2394             :         // TODO: bug 1285316: clean up ownership transfer from the factory to
    2395             :         // the textrun
    2396           0 :         Unused << transformingFactory.release();
    2397             :       }
    2398             :     } else {
    2399          42 :       textRun = fontGroup->MakeTextRun(text, transformedLength, &params,
    2400          21 :                                        flags, flags2, mMissingFonts);
    2401             :     }
    2402             :   }
    2403          21 :   if (!textRun) {
    2404           0 :     DestroyUserData(userDataToDestroy);
    2405           0 :     return nullptr;
    2406             :   }
    2407             : 
    2408             :   // We have to set these up after we've created the textrun, because
    2409             :   // the breaks may be stored in the textrun during this very call.
    2410             :   // This is a bit annoying because it requires another loop over the frames
    2411             :   // making up the textrun, but I don't see a way to avoid this.
    2412          21 :   SetupBreakSinksForTextRun(textRun.get(), textPtr);
    2413             : 
    2414          21 :   if (anyTextEmphasis) {
    2415           0 :     SetupTextEmphasisForTextRun(textRun.get(), textPtr);
    2416             :   }
    2417             : 
    2418          21 :   if (mSkipIncompleteTextRuns) {
    2419           0 :     mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr,
    2420           0 :         transformedLength, mDoubleByteText);
    2421             :     // Since we're doing to destroy the user data now, avoid a dangling
    2422             :     // pointer. Strictly speaking we don't need to do this since it should
    2423             :     // not be used (since this textrun will not be used and will be
    2424             :     // itself deleted soon), but it's always better to not have dangling
    2425             :     // pointers around.
    2426           0 :     textRun->SetUserData(nullptr);
    2427           0 :     DestroyUserData(userDataToDestroy);
    2428           0 :     return nullptr;
    2429             :   }
    2430             : 
    2431             :   // Actually wipe out the textruns associated with the mapped frames and associate
    2432             :   // those frames with this text run.
    2433          21 :   AssignTextRun(textRun.get(), fontInflation);
    2434          21 :   return textRun.forget();
    2435             : }
    2436             : 
    2437             : // This is a cut-down version of BuildTextRunForFrames used to set up
    2438             : // context for the line-breaker, when the textrun has already been created.
    2439             : // So it does the same walk over the mMappedFlows, but doesn't actually
    2440             : // build a new textrun.
    2441             : bool
    2442           0 : BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
    2443             : {
    2444           0 :   AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> buffer;
    2445           0 :   uint32_t bufferSize = mMaxTextLength*(mDoubleByteText ? 2 : 1);
    2446           0 :   if (bufferSize < mMaxTextLength || bufferSize == UINT32_MAX) {
    2447           0 :     return false;
    2448             :   }
    2449           0 :   void *textPtr = buffer.AppendElements(bufferSize, fallible);
    2450           0 :   if (!textPtr) {
    2451           0 :     return false;
    2452             :   }
    2453             : 
    2454           0 :   gfxSkipChars skipChars;
    2455             : 
    2456           0 :   AutoTArray<int32_t,50> textBreakPoints;
    2457             :   TextRunUserData dummyData;
    2458             :   TextRunMappedFlow dummyMappedFlow;
    2459             :   TextRunMappedFlow* userMappedFlows;
    2460             :   TextRunUserData* userData;
    2461             :   TextRunUserData* userDataToDestroy;
    2462             :   // If the situation is particularly simple (and common) we don't need to
    2463             :   // allocate userData.
    2464           0 :   if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
    2465           0 :       mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
    2466           0 :     userData = &dummyData;
    2467           0 :     userMappedFlows = &dummyMappedFlow;
    2468           0 :     userDataToDestroy = nullptr;
    2469           0 :     dummyData.mMappedFlowCount = mMappedFlows.Length();
    2470           0 :     dummyData.mLastFlowIndex = 0;
    2471             :   } else {
    2472           0 :     userData = CreateUserData(mMappedFlows.Length());
    2473           0 :     userMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
    2474           0 :     userDataToDestroy = userData;
    2475             :   }
    2476             : 
    2477           0 :   uint32_t nextBreakIndex = 0;
    2478           0 :   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    2479             : 
    2480           0 :   const nsStyleText* textStyle = nullptr;
    2481           0 :   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
    2482           0 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2483           0 :     nsTextFrame* f = mappedFlow->mStartFrame;
    2484             : 
    2485           0 :     textStyle = f->StyleText();
    2486             :     nsTextFrameUtils::CompressionMode compression =
    2487           0 :       GetCSSWhitespaceToCompressionMode(f, textStyle);
    2488             : 
    2489             :     // Figure out what content is included in this flow.
    2490           0 :     nsIContent* content = f->GetContent();
    2491           0 :     const nsTextFragment* frag = content->GetText();
    2492           0 :     int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
    2493           0 :     int32_t contentEnd = mappedFlow->GetContentEnd();
    2494           0 :     int32_t contentLength = contentEnd - contentStart;
    2495             : 
    2496           0 :     TextRunMappedFlow* newFlow = &userMappedFlows[i];
    2497           0 :     newFlow->mStartFrame = mappedFlow->mStartFrame;
    2498           0 :     newFlow->mDOMOffsetToBeforeTransformOffset = skipChars.GetOriginalCharCount() -
    2499           0 :       mappedFlow->mStartFrame->GetContentOffset();
    2500           0 :     newFlow->mContentLength = contentLength;
    2501             : 
    2502           0 :     while (nextBreakBeforeFrame && nextBreakBeforeFrame->GetContent() == content) {
    2503             :       textBreakPoints.AppendElement(
    2504           0 :           nextBreakBeforeFrame->GetContentOffset() + newFlow->mDOMOffsetToBeforeTransformOffset);
    2505           0 :       nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    2506             :     }
    2507             : 
    2508             :     nsTextFrameUtils::Flags analysisFlags;
    2509           0 :     if (frag->Is2b()) {
    2510           0 :       NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
    2511           0 :       char16_t* bufStart = static_cast<char16_t*>(textPtr);
    2512           0 :       char16_t* bufEnd = nsTextFrameUtils::TransformText(
    2513           0 :           frag->Get2b() + contentStart, contentLength, bufStart,
    2514           0 :           compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
    2515           0 :       textPtr = bufEnd;
    2516             :     } else {
    2517           0 :       if (mDoubleByteText) {
    2518             :         // Need to expand the text. First transform it into a temporary buffer,
    2519             :         // then expand.
    2520           0 :         AutoTArray<uint8_t,BIG_TEXT_NODE_SIZE> tempBuf;
    2521           0 :         uint8_t* bufStart = tempBuf.AppendElements(contentLength, fallible);
    2522           0 :         if (!bufStart) {
    2523           0 :           DestroyUserData(userDataToDestroy);
    2524           0 :           return false;
    2525             :         }
    2526           0 :         uint8_t* end = nsTextFrameUtils::TransformText(
    2527           0 :             reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
    2528           0 :             bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
    2529           0 :         textPtr = ExpandBuffer(static_cast<char16_t*>(textPtr),
    2530           0 :                                tempBuf.Elements(), end - tempBuf.Elements());
    2531             :       } else {
    2532           0 :         uint8_t* bufStart = static_cast<uint8_t*>(textPtr);
    2533           0 :         uint8_t* end = nsTextFrameUtils::TransformText(
    2534           0 :             reinterpret_cast<const uint8_t*>(frag->Get1b()) + contentStart, contentLength,
    2535           0 :             bufStart, compression, &mNextRunContextInfo, &skipChars, &analysisFlags);
    2536           0 :         textPtr = end;
    2537             :       }
    2538             :     }
    2539             :   }
    2540             : 
    2541             :   // We have to set these up after we've created the textrun, because
    2542             :   // the breaks may be stored in the textrun during this very call.
    2543             :   // This is a bit annoying because it requires another loop over the frames
    2544             :   // making up the textrun, but I don't see a way to avoid this.
    2545           0 :   SetupBreakSinksForTextRun(aTextRun, buffer.Elements());
    2546             : 
    2547           0 :   DestroyUserData(userDataToDestroy);
    2548             : 
    2549           0 :   return true;
    2550             : }
    2551             : 
    2552             : static bool
    2553          21 : HasCompressedLeadingWhitespace(nsTextFrame* aFrame, const nsStyleText* aStyleText,
    2554             :                                int32_t aContentEndOffset,
    2555             :                                const gfxSkipCharsIterator& aIterator)
    2556             : {
    2557          21 :   if (!aIterator.IsOriginalCharSkipped())
    2558           9 :     return false;
    2559             : 
    2560          12 :   gfxSkipCharsIterator iter = aIterator;
    2561          12 :   int32_t frameContentOffset = aFrame->GetContentOffset();
    2562          12 :   const nsTextFragment* frag = aFrame->GetContent()->GetText();
    2563          12 :   while (frameContentOffset < aContentEndOffset && iter.IsOriginalCharSkipped()) {
    2564           0 :     if (IsTrimmableSpace(frag, frameContentOffset, aStyleText))
    2565           0 :       return true;
    2566           0 :     ++frameContentOffset;
    2567           0 :     iter.AdvanceOriginal(1);
    2568             :   }
    2569          12 :   return false;
    2570             : }
    2571             : 
    2572             : void
    2573          21 : BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
    2574             :                                                 const void* aTextPtr)
    2575             : {
    2576             :   // for word-break style
    2577          21 :   switch (mLineContainer->StyleText()->mWordBreak) {
    2578             :     case NS_STYLE_WORDBREAK_BREAK_ALL:
    2579           0 :       mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_BreakAll);
    2580           0 :       break;
    2581             :     case NS_STYLE_WORDBREAK_KEEP_ALL:
    2582           0 :       mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_KeepAll);
    2583           0 :       break;
    2584             :     default:
    2585          21 :       mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_Normal);
    2586          21 :       break;
    2587             :   }
    2588             : 
    2589             :   // textruns have uniform language
    2590          21 :   const nsStyleFont *styleFont = mMappedFlows[0].mStartFrame->StyleFont();
    2591             :   // We should only use a language for hyphenation if it was specified
    2592             :   // explicitly.
    2593             :   nsIAtom* hyphenationLanguage =
    2594          21 :     styleFont->mExplicitLanguage ? styleFont->mLanguage.get() : nullptr;
    2595             :   // We keep this pointed at the skip-chars data for the current mappedFlow.
    2596             :   // This lets us cheaply check whether the flow has compressed initial
    2597             :   // whitespace...
    2598          21 :   gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
    2599             : 
    2600          42 :   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
    2601          21 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2602          21 :     uint32_t offset = iter.GetSkippedOffset();
    2603          21 :     gfxSkipCharsIterator iterNext = iter;
    2604          42 :     iterNext.AdvanceOriginal(mappedFlow->GetContentEnd() -
    2605          42 :             mappedFlow->mStartFrame->GetContentOffset());
    2606             : 
    2607             :     UniquePtr<BreakSink>* breakSink =
    2608          21 :       mBreakSinks.AppendElement(MakeUnique<BreakSink>(aTextRun, mDrawTarget, offset));
    2609          21 :     if (!breakSink || !*breakSink)
    2610           0 :       return;
    2611             : 
    2612          21 :     uint32_t length = iterNext.GetSkippedOffset() - offset;
    2613          21 :     uint32_t flags = 0;
    2614          21 :     nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
    2615          21 :     if (!initialBreakController) {
    2616          21 :       initialBreakController = mLineContainer;
    2617             :     }
    2618          21 :     if (!initialBreakController->StyleText()->
    2619          21 :                                  WhiteSpaceCanWrap(initialBreakController)) {
    2620          10 :       flags |= nsLineBreaker::BREAK_SUPPRESS_INITIAL;
    2621             :     }
    2622          21 :     nsTextFrame* startFrame = mappedFlow->mStartFrame;
    2623          21 :     const nsStyleText* textStyle = startFrame->StyleText();
    2624          21 :     if (!textStyle->WhiteSpaceCanWrap(startFrame)) {
    2625          10 :       flags |= nsLineBreaker::BREAK_SUPPRESS_INSIDE;
    2626             :     }
    2627          21 :     if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_NO_BREAKS) {
    2628          21 :       flags |= nsLineBreaker::BREAK_SKIP_SETTING_NO_BREAKS;
    2629             :     }
    2630          21 :     if (textStyle->mTextTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE) {
    2631           0 :       flags |= nsLineBreaker::BREAK_NEED_CAPITALIZATION;
    2632             :     }
    2633          21 :     if (textStyle->mHyphens == StyleHyphens::Auto) {
    2634           0 :       flags |= nsLineBreaker::BREAK_USE_AUTO_HYPHENATION;
    2635             :     }
    2636             : 
    2637          21 :     if (HasCompressedLeadingWhitespace(startFrame, textStyle,
    2638             :                                        mappedFlow->GetContentEnd(), iter)) {
    2639           0 :       mLineBreaker.AppendInvisibleWhitespace(flags);
    2640             :     }
    2641             : 
    2642          21 :     if (length > 0) {
    2643             :       BreakSink* sink =
    2644           9 :         mSkipIncompleteTextRuns ? nullptr : (*breakSink).get();
    2645           9 :       if (mDoubleByteText) {
    2646           0 :         const char16_t* text = reinterpret_cast<const char16_t*>(aTextPtr);
    2647           0 :         mLineBreaker.AppendText(hyphenationLanguage, text + offset,
    2648           0 :                                 length, flags, sink);
    2649             :       } else {
    2650           9 :         const uint8_t* text = reinterpret_cast<const uint8_t*>(aTextPtr);
    2651           9 :         mLineBreaker.AppendText(hyphenationLanguage, text + offset,
    2652           9 :                                 length, flags, sink);
    2653             :       }
    2654             :     }
    2655             : 
    2656          21 :     iter = iterNext;
    2657             :   }
    2658             : }
    2659             : 
    2660             : static bool
    2661           0 : MayCharacterHaveEmphasisMark(uint32_t aCh)
    2662             : {
    2663           0 :   auto category = unicode::GetGeneralCategory(aCh);
    2664             :   // Comparing an unsigned variable against zero is a compile error,
    2665             :   // so we use static assert here to ensure we really don't need to
    2666             :   // compare it with the given constant.
    2667             :   static_assert(IsUnsigned<decltype(category)>::value &&
    2668             :                 HB_UNICODE_GENERAL_CATEGORY_CONTROL == 0,
    2669             :                 "if this constant is not zero, or category is signed, "
    2670             :                 "we need to explicitly do the comparison below");
    2671           0 :   return !(category <= HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED ||
    2672           0 :            (category >= HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR &&
    2673           0 :             category <= HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR));
    2674             : }
    2675             : 
    2676             : static bool
    2677           0 : MayCharacterHaveEmphasisMark(uint8_t aCh)
    2678             : {
    2679             :   // 0x00~0x1f and 0x7f~0x9f are in category Cc
    2680             :   // 0x20 and 0xa0 are in category Zs
    2681           0 :   bool result = !(aCh <= 0x20 || (aCh >= 0x7f && aCh <= 0xa0));
    2682           0 :   MOZ_ASSERT(result == MayCharacterHaveEmphasisMark(uint32_t(aCh)),
    2683             :              "result for uint8_t should match result for uint32_t");
    2684           0 :   return result;
    2685             : }
    2686             : 
    2687             : void
    2688           0 : BuildTextRunsScanner::SetupTextEmphasisForTextRun(gfxTextRun* aTextRun,
    2689             :                                                   const void* aTextPtr)
    2690             : {
    2691           0 :   if (!mDoubleByteText) {
    2692           0 :     auto text = reinterpret_cast<const uint8_t*>(aTextPtr);
    2693           0 :     for (auto i : IntegerRange(aTextRun->GetLength())) {
    2694           0 :       if (!MayCharacterHaveEmphasisMark(text[i])) {
    2695           0 :         aTextRun->SetNoEmphasisMark(i);
    2696             :       }
    2697             :     }
    2698             :   } else {
    2699           0 :     auto text = reinterpret_cast<const char16_t*>(aTextPtr);
    2700           0 :     auto length = aTextRun->GetLength();
    2701           0 :     for (size_t i = 0; i < length; ++i) {
    2702           0 :       if (NS_IS_HIGH_SURROGATE(text[i]) && i + 1 < length &&
    2703           0 :           NS_IS_LOW_SURROGATE(text[i + 1])) {
    2704           0 :         uint32_t ch = SURROGATE_TO_UCS4(text[i], text[i + 1]);
    2705           0 :         if (!MayCharacterHaveEmphasisMark(ch)) {
    2706           0 :           aTextRun->SetNoEmphasisMark(i);
    2707           0 :           aTextRun->SetNoEmphasisMark(i + 1);
    2708             :         }
    2709           0 :         ++i;
    2710             :       } else {
    2711           0 :         if (!MayCharacterHaveEmphasisMark(uint32_t(text[i]))) {
    2712           0 :           aTextRun->SetNoEmphasisMark(i);
    2713             :         }
    2714             :       }
    2715             :     }
    2716             :   }
    2717           0 : }
    2718             : 
    2719             : // Find the flow corresponding to aContent in aUserData
    2720             : static inline TextRunMappedFlow*
    2721           0 : FindFlowForContent(TextRunUserData* aUserData, nsIContent* aContent,
    2722             :                    TextRunMappedFlow* userMappedFlows)
    2723             : {
    2724             :   // Find the flow that contains us
    2725           0 :   int32_t i = aUserData->mLastFlowIndex;
    2726           0 :   int32_t delta = 1;
    2727           0 :   int32_t sign = 1;
    2728             :   // Search starting at the current position and examine close-by
    2729             :   // positions first, moving further and further away as we go.
    2730           0 :   while (i >= 0 && uint32_t(i) < aUserData->mMappedFlowCount) {
    2731           0 :     TextRunMappedFlow* flow = &userMappedFlows[i];
    2732           0 :     if (flow->mStartFrame->GetContent() == aContent) {
    2733           0 :       return flow;
    2734             :     }
    2735             : 
    2736           0 :     i += delta;
    2737           0 :     sign = -sign;
    2738           0 :     delta = -delta + sign;
    2739             :   }
    2740             : 
    2741             :   // We ran into an array edge.  Add |delta| to |i| once more to get
    2742             :   // back to the side where we still need to search, then step in
    2743             :   // the |sign| direction.
    2744           0 :   i += delta;
    2745           0 :   if (sign > 0) {
    2746           0 :     for (; i < int32_t(aUserData->mMappedFlowCount); ++i) {
    2747           0 :       TextRunMappedFlow* flow = &userMappedFlows[i];
    2748           0 :       if (flow->mStartFrame->GetContent() == aContent) {
    2749           0 :         return flow;
    2750             :       }
    2751             :     }
    2752             :   } else {
    2753           0 :     for (; i >= 0; --i) {
    2754           0 :       TextRunMappedFlow* flow = &userMappedFlows[i];
    2755           0 :       if (flow->mStartFrame->GetContent() == aContent) {
    2756           0 :         return flow;
    2757             :       }
    2758             :     }
    2759             :   }
    2760             : 
    2761           0 :   return nullptr;
    2762             : }
    2763             : 
    2764             : void
    2765          21 : BuildTextRunsScanner::AssignTextRun(gfxTextRun* aTextRun, float aInflation)
    2766             : {
    2767          42 :   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
    2768          21 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2769          21 :     nsTextFrame* startFrame = mappedFlow->mStartFrame;
    2770          21 :     nsTextFrame* endFrame = mappedFlow->mEndFrame;
    2771             :     nsTextFrame* f;
    2772          42 :     for (f = startFrame; f != endFrame; f = f->GetNextContinuation()) {
    2773             : #ifdef DEBUG_roc
    2774             :       if (f->GetTextRun(mWhichTextRun)) {
    2775             :         gfxTextRun* textRun = f->GetTextRun(mWhichTextRun);
    2776             :         if (textRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
    2777             :           if (mMappedFlows[0].mStartFrame != GetFrameForSimpleFlow(textRun)) {
    2778             :             NS_WARNING("REASSIGNING SIMPLE FLOW TEXT RUN!");
    2779             :           }
    2780             :         } else {
    2781             :           auto userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
    2782             :           TextRunMappedFlow* userMappedFlows = GetMappedFlows(aTextRun);
    2783             :           if (userData->mMappedFlowCount >= mMappedFlows.Length() ||
    2784             :               userMappedFlows[userData->mMappedFlowCount - 1].mStartFrame !=
    2785             :               mMappedFlows[userdata->mMappedFlowCount - 1].mStartFrame) {
    2786             :             NS_WARNING("REASSIGNING MULTIFLOW TEXT RUN (not append)!");
    2787             :           }
    2788             :         }
    2789             :       }
    2790             : #endif
    2791             : 
    2792          21 :       gfxTextRun* oldTextRun = f->GetTextRun(mWhichTextRun);
    2793          21 :       if (oldTextRun) {
    2794           0 :         nsTextFrame* firstFrame = nullptr;
    2795           0 :         uint32_t startOffset = 0;
    2796           0 :         if (oldTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
    2797           0 :           firstFrame = GetFrameForSimpleFlow(oldTextRun);
    2798             :         } else {
    2799           0 :           auto userData = static_cast<TextRunUserData*>(oldTextRun->GetUserData());
    2800           0 :           TextRunMappedFlow* userMappedFlows = GetMappedFlows(oldTextRun);
    2801           0 :           firstFrame = userMappedFlows[0].mStartFrame;
    2802           0 :           if (MOZ_UNLIKELY(f != firstFrame)) {
    2803             :             TextRunMappedFlow* flow =
    2804           0 :               FindFlowForContent(userData, f->GetContent(), userMappedFlows);
    2805           0 :             if (flow) {
    2806           0 :               startOffset = flow->mDOMOffsetToBeforeTransformOffset;
    2807             :             } else {
    2808           0 :               NS_ERROR("Can't find flow containing frame 'f'");
    2809             :             }
    2810             :           }
    2811             :         }
    2812             : 
    2813             :         // Optimization: if |f| is the first frame in the flow then there are no
    2814             :         // prev-continuations that use |oldTextRun|.
    2815           0 :         nsTextFrame* clearFrom = nullptr;
    2816           0 :         if (MOZ_UNLIKELY(f != firstFrame)) {
    2817             :           // If all the frames in the mapped flow starting at |f| (inclusive)
    2818             :           // are empty then we let the prev-continuations keep the old text run.
    2819           0 :           gfxSkipCharsIterator iter(oldTextRun->GetSkipChars(), startOffset, f->GetContentOffset());
    2820           0 :           uint32_t textRunOffset = iter.ConvertOriginalToSkipped(f->GetContentOffset());
    2821           0 :           clearFrom = textRunOffset == oldTextRun->GetLength() ? f : nullptr;
    2822             :         }
    2823           0 :         f->ClearTextRun(clearFrom, mWhichTextRun);
    2824             : 
    2825             : #ifdef DEBUG
    2826           0 :         if (firstFrame && !firstFrame->GetTextRun(mWhichTextRun)) {
    2827             :           // oldTextRun was destroyed - assert that we don't reference it.
    2828           0 :           for (uint32_t j = 0; j < mBreakSinks.Length(); ++j) {
    2829           0 :             NS_ASSERTION(oldTextRun != mBreakSinks[j]->mTextRun,
    2830             :                          "destroyed text run is still in use");
    2831             :           }
    2832             :         }
    2833             : #endif
    2834             :       }
    2835          21 :       f->SetTextRun(aTextRun, mWhichTextRun, aInflation);
    2836             :     }
    2837             :     // Set this bit now; we can't set it any earlier because
    2838             :     // f->ClearTextRun() might clear it out.
    2839             :     nsFrameState whichTextRunState =
    2840          21 :       startFrame->GetTextRun(nsTextFrame::eInflated) == aTextRun
    2841          21 :         ? TEXT_IN_TEXTRUN_USER_DATA
    2842          21 :         : TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA;
    2843          21 :     startFrame->AddStateBits(whichTextRunState);
    2844             :   }
    2845          21 : }
    2846             : 
    2847          28 : NS_QUERYFRAME_HEAD(nsTextFrame)
    2848           0 :   NS_QUERYFRAME_ENTRY(nsTextFrame)
    2849          28 : NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
    2850             : 
    2851             : gfxSkipCharsIterator
    2852         104 : nsTextFrame::EnsureTextRun(TextRunType aWhichTextRun,
    2853             :                            DrawTarget* aRefDrawTarget,
    2854             :                            nsIFrame* aLineContainer,
    2855             :                            const nsLineList::iterator* aLine,
    2856             :                            uint32_t* aFlowEndInTextRun)
    2857             : {
    2858         104 :   gfxTextRun *textRun = GetTextRun(aWhichTextRun);
    2859         104 :   if (!textRun || (aLine && (*aLine)->GetInvalidateTextRuns())) {
    2860          42 :     RefPtr<DrawTarget> refDT = aRefDrawTarget;
    2861          21 :     if (!refDT) {
    2862           0 :       refDT = CreateReferenceDrawTarget(this);
    2863             :     }
    2864          21 :     if (refDT) {
    2865          21 :       BuildTextRuns(refDT, this, aLineContainer, aLine, aWhichTextRun);
    2866             :     }
    2867          21 :     textRun = GetTextRun(aWhichTextRun);
    2868          21 :     if (!textRun) {
    2869             :       // A text run was not constructed for this frame. This is bad. The caller
    2870             :       // will check mTextRun.
    2871             :       return gfxSkipCharsIterator(gfxPlatform::
    2872           0 :                                   GetPlatform()->EmptySkipChars(), 0);
    2873             :     }
    2874          21 :     TabWidthStore* tabWidths = GetProperty(TabWidthProperty());
    2875          21 :     if (tabWidths && tabWidths->mValidForContentOffset != GetContentOffset()) {
    2876           0 :       DeleteProperty(TabWidthProperty());
    2877             :     }
    2878             :   }
    2879             : 
    2880         104 :   if (textRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SIMPLE_FLOW) {
    2881         104 :     if (aFlowEndInTextRun) {
    2882          62 :       *aFlowEndInTextRun = textRun->GetLength();
    2883             :     }
    2884         104 :     return gfxSkipCharsIterator(textRun->GetSkipChars(), 0, mContentOffset);
    2885             :   }
    2886             : 
    2887           0 :   auto userData = static_cast<TextRunUserData*>(textRun->GetUserData());
    2888           0 :   TextRunMappedFlow* userMappedFlows = GetMappedFlows(textRun);
    2889             :   TextRunMappedFlow* flow =
    2890           0 :     FindFlowForContent(userData, mContent, userMappedFlows);
    2891           0 :   if (flow) {
    2892             :     // Since textruns can only contain one flow for a given content element,
    2893             :     // this must be our flow.
    2894           0 :     uint32_t flowIndex = flow - userMappedFlows;
    2895           0 :     userData->mLastFlowIndex = flowIndex;
    2896             :     gfxSkipCharsIterator iter(textRun->GetSkipChars(),
    2897           0 :                               flow->mDOMOffsetToBeforeTransformOffset, mContentOffset);
    2898           0 :     if (aFlowEndInTextRun) {
    2899           0 :       if (flowIndex + 1 < userData->mMappedFlowCount) {
    2900           0 :         gfxSkipCharsIterator end(textRun->GetSkipChars());
    2901           0 :         *aFlowEndInTextRun = end.ConvertOriginalToSkipped(
    2902           0 :               flow[1].mStartFrame->GetContentOffset() + flow[1].mDOMOffsetToBeforeTransformOffset);
    2903             :       } else {
    2904           0 :         *aFlowEndInTextRun = textRun->GetLength();
    2905             :       }
    2906             :     }
    2907           0 :     return iter;
    2908             :   }
    2909             : 
    2910           0 :   NS_ERROR("Can't find flow containing this frame???");
    2911           0 :   return gfxSkipCharsIterator(gfxPlatform::GetPlatform()->EmptySkipChars(), 0);
    2912             : }
    2913             : 
    2914             : static uint32_t
    2915           4 : GetEndOfTrimmedText(const nsTextFragment* aFrag, const nsStyleText* aStyleText,
    2916             :                     uint32_t aStart, uint32_t aEnd,
    2917             :                     gfxSkipCharsIterator* aIterator)
    2918             : {
    2919           4 :   aIterator->SetSkippedOffset(aEnd);
    2920           4 :   while (aIterator->GetSkippedOffset() > aStart) {
    2921           4 :     aIterator->AdvanceSkipped(-1);
    2922           4 :     if (!IsTrimmableSpace(aFrag, aIterator->GetOriginalOffset(), aStyleText))
    2923           4 :       return aIterator->GetSkippedOffset() + 1;
    2924             :   }
    2925           0 :   return aStart;
    2926             : }
    2927             : 
    2928             : nsTextFrame::TrimmedOffsets
    2929          42 : nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
    2930             :                                bool aTrimAfter, bool aPostReflow) const
    2931             : {
    2932          42 :   NS_ASSERTION(mTextRun, "Need textrun here");
    2933          42 :   if (aPostReflow) {
    2934             :     // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
    2935             :     // to be set correctly.  If our parent wasn't reflowed due to the frame
    2936             :     // tree being too deep then the return value doesn't matter.
    2937          42 :     NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
    2938             :                  (GetParent()->GetStateBits() &
    2939             :                   NS_FRAME_TOO_DEEP_IN_FRAME_TREE),
    2940             :                  "Can only call this on frames that have been reflowed");
    2941          42 :     NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
    2942             :                  "Can only call this on frames that are not being reflowed");
    2943             :   }
    2944             : 
    2945          42 :   TrimmedOffsets offsets = { GetContentOffset(), GetContentLength() };
    2946          42 :   const nsStyleText* textStyle = StyleText();
    2947             :   // Note that pre-line newlines should still allow us to trim spaces
    2948             :   // for display
    2949          42 :   if (textStyle->WhiteSpaceIsSignificant())
    2950          29 :     return offsets;
    2951             : 
    2952          13 :   if (!aPostReflow || (GetStateBits() & TEXT_START_OF_LINE)) {
    2953             :     int32_t whitespaceCount =
    2954          13 :       GetTrimmableWhitespaceCount(aFrag,
    2955          13 :                                   offsets.mStart, offsets.mLength, 1);
    2956          13 :     offsets.mStart += whitespaceCount;
    2957          13 :     offsets.mLength -= whitespaceCount;
    2958             :   }
    2959             : 
    2960          13 :   if (aTrimAfter && (!aPostReflow || (GetStateBits() & TEXT_END_OF_LINE))) {
    2961             :     // This treats a trailing 'pre-line' newline as trimmable. That's fine,
    2962             :     // it's actually what we want since we want whitespace before it to
    2963             :     // be trimmed.
    2964             :     int32_t whitespaceCount =
    2965          26 :       GetTrimmableWhitespaceCount(aFrag,
    2966          26 :                                   offsets.GetEnd() - 1, offsets.mLength, -1);
    2967          13 :     offsets.mLength -= whitespaceCount;
    2968             :   }
    2969          13 :   return offsets;
    2970             : }
    2971             : 
    2972           0 : static bool IsJustifiableCharacter(const nsStyleText* aTextStyle,
    2973             :                                    const nsTextFragment* aFrag, int32_t aPos,
    2974             :                                    bool aLangIsCJ)
    2975             : {
    2976           0 :   NS_ASSERTION(aPos >= 0, "negative position?!");
    2977             : 
    2978           0 :   StyleTextJustify justifyStyle = aTextStyle->mTextJustify;
    2979           0 :   if (justifyStyle == StyleTextJustify::None) {
    2980           0 :     return false;
    2981             :   }
    2982             : 
    2983           0 :   char16_t ch = aFrag->CharAt(aPos);
    2984           0 :   if (ch == '\n' || ch == '\t' || ch == '\r') {
    2985           0 :     return true;
    2986             :   }
    2987           0 :   if (ch == ' ' || ch == CH_NBSP) {
    2988             :     // Don't justify spaces that are combined with diacriticals
    2989           0 :     if (!aFrag->Is2b()) {
    2990           0 :       return true;
    2991             :     }
    2992           0 :     return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(
    2993           0 :       aFrag->Get2b() + aPos + 1, aFrag->GetLength() - (aPos + 1));
    2994             :   }
    2995             : 
    2996           0 :   if (justifyStyle == StyleTextJustify::InterCharacter) {
    2997           0 :     return true;
    2998           0 :   } else if (justifyStyle == StyleTextJustify::InterWord) {
    2999           0 :     return false;
    3000             :   }
    3001             : 
    3002             :   // text-justify: auto
    3003           0 :   if (ch < 0x2150u) {
    3004           0 :     return false;
    3005             :   }
    3006           0 :   if (aLangIsCJ) {
    3007           0 :     if ((0x2150u <= ch && ch <= 0x22ffu) || // Number Forms, Arrows, Mathematical Operators
    3008           0 :         (0x2460u <= ch && ch <= 0x24ffu) || // Enclosed Alphanumerics
    3009           0 :         (0x2580u <= ch && ch <= 0x27bfu) || // Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats
    3010           0 :         (0x27f0u <= ch && ch <= 0x2bffu) || // Supplemental Arrows-A, Braille Patterns, Supplemental Arrows-B,
    3011             :                                             // Miscellaneous Mathematical Symbols-B, Supplemental Mathematical Operators,
    3012             :                                             // Miscellaneous Symbols and Arrows
    3013           0 :         (0x2e80u <= ch && ch <= 0x312fu) || // CJK Radicals Supplement, CJK Radicals Supplement,
    3014             :                                             // Ideographic Description Characters, CJK Symbols and Punctuation,
    3015             :                                             // Hiragana, Katakana, Bopomofo
    3016           0 :         (0x3190u <= ch && ch <= 0xabffu) || // Kanbun, Bopomofo Extended, Katakana Phonetic Extensions,
    3017             :                                             // Enclosed CJK Letters and Months, CJK Compatibility,
    3018             :                                             // CJK Unified Ideographs Extension A, Yijing Hexagram Symbols,
    3019             :                                             // CJK Unified Ideographs, Yi Syllables, Yi Radicals
    3020           0 :         (0xf900u <= ch && ch <= 0xfaffu) || // CJK Compatibility Ideographs
    3021           0 :         (0xff5eu <= ch && ch <= 0xff9fu)    // Halfwidth and Fullwidth Forms(a part)
    3022             :        ) {
    3023           0 :       return true;
    3024             :     }
    3025             :     char16_t ch2;
    3026           0 :     if (NS_IS_HIGH_SURROGATE(ch) && aFrag->GetLength() > uint32_t(aPos) + 1 &&
    3027           0 :         NS_IS_LOW_SURROGATE(ch2 = aFrag->CharAt(aPos + 1))) {
    3028           0 :       uint32_t u = SURROGATE_TO_UCS4(ch, ch2);
    3029           0 :       if (0x20000u <= u && u <= 0x2ffffu) { // CJK Unified Ideographs Extension B,
    3030             :                                             // CJK Unified Ideographs Extension C,
    3031             :                                             // CJK Unified Ideographs Extension D,
    3032             :                                             // CJK Compatibility Ideographs Supplement
    3033           0 :         return true;
    3034             :       }
    3035             :     }
    3036             :   }
    3037           0 :   return false;
    3038             : }
    3039             : 
    3040             : void
    3041          24 : nsTextFrame::ClearMetrics(ReflowOutput& aMetrics)
    3042             : {
    3043          24 :   aMetrics.ClearSize();
    3044          24 :   aMetrics.SetBlockStartAscent(0);
    3045          24 :   mAscent = 0;
    3046             : 
    3047          24 :   AddStateBits(TEXT_NO_RENDERED_GLYPHS);
    3048          24 : }
    3049             : 
    3050          19 : static int32_t FindChar(const nsTextFragment* frag,
    3051             :                         int32_t aOffset, int32_t aLength, char16_t ch)
    3052             : {
    3053          19 :   int32_t i = 0;
    3054          19 :   if (frag->Is2b()) {
    3055           0 :     const char16_t* str = frag->Get2b() + aOffset;
    3056           0 :     for (; i < aLength; ++i) {
    3057           0 :       if (*str == ch)
    3058           0 :         return i + aOffset;
    3059           0 :       ++str;
    3060             :     }
    3061             :   } else {
    3062          19 :     if (uint16_t(ch) <= 0xFF) {
    3063          19 :       const char* str = frag->Get1b() + aOffset;
    3064          19 :       const void* p = memchr(str, ch, aLength);
    3065          19 :       if (p)
    3066           0 :         return (static_cast<const char*>(p) - str) + aOffset;
    3067             :     }
    3068             :   }
    3069          19 :   return -1;
    3070             : }
    3071             : 
    3072           0 : static bool IsChineseOrJapanese(const nsTextFrame* aFrame)
    3073             : {
    3074           0 :   if (aFrame->ShouldSuppressLineBreak()) {
    3075             :     // Always treat ruby as CJ language so that those characters can
    3076             :     // be expanded properly even when surrounded by other language.
    3077           0 :     return true;
    3078             :   }
    3079             : 
    3080           0 :   nsIAtom* language = aFrame->StyleFont()->mLanguage;
    3081           0 :   if (!language) {
    3082           0 :     return false;
    3083             :   }
    3084           0 :   return nsStyleUtil::MatchesLanguagePrefix(language, u"ja") ||
    3085           0 :          nsStyleUtil::MatchesLanguagePrefix(language, u"zh");
    3086             : }
    3087             : 
    3088             : #ifdef DEBUG
    3089           0 : static bool IsInBounds(const gfxSkipCharsIterator& aStart, int32_t aContentLength,
    3090             :                        gfxTextRun::Range aRange) {
    3091           0 :   if (aStart.GetSkippedOffset() > aRange.start)
    3092           0 :     return false;
    3093           0 :   if (aContentLength == INT32_MAX)
    3094           0 :     return true;
    3095           0 :   gfxSkipCharsIterator iter(aStart);
    3096           0 :   iter.AdvanceOriginal(aContentLength);
    3097           0 :   return iter.GetSkippedOffset() >= aRange.end;
    3098             : }
    3099             : #endif
    3100             : 
    3101          80 : class MOZ_STACK_CLASS PropertyProvider final : public gfxTextRun::PropertyProvider {
    3102             :   typedef gfxTextRun::Range Range;
    3103             :   typedef gfxTextRun::HyphenType HyphenType;
    3104             : 
    3105             : public:
    3106             :   /**
    3107             :    * Use this constructor for reflow, when we don't know what text is
    3108             :    * really mapped by the frame and we have a lot of other data around.
    3109             :    *
    3110             :    * @param aLength can be INT32_MAX to indicate we cover all the text
    3111             :    * associated with aFrame up to where its flow chain ends in the given
    3112             :    * textrun. If INT32_MAX is passed, justification and hyphen-related methods
    3113             :    * cannot be called, nor can GetOriginalLength().
    3114             :    */
    3115          62 :   PropertyProvider(gfxTextRun* aTextRun, const nsStyleText* aTextStyle,
    3116             :                    const nsTextFragment* aFrag, nsTextFrame* aFrame,
    3117             :                    const gfxSkipCharsIterator& aStart, int32_t aLength,
    3118             :                    nsIFrame* aLineContainer,
    3119             :                    nscoord aOffsetFromBlockOriginForTabs,
    3120             :                    nsTextFrame::TextRunType aWhichTextRun)
    3121          62 :     : mTextRun(aTextRun), mFontGroup(nullptr),
    3122             :       mTextStyle(aTextStyle), mFrag(aFrag),
    3123             :       mLineContainer(aLineContainer),
    3124             :       mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
    3125             :       mTabWidths(nullptr), mTabWidthsAnalyzedLimit(0),
    3126             :       mLength(aLength),
    3127          62 :       mWordSpacing(WordSpacing(aFrame, mTextRun, aTextStyle)),
    3128          62 :       mLetterSpacing(LetterSpacing(aFrame, aTextStyle)),
    3129             :       mHyphenWidth(-1),
    3130             :       mOffsetFromBlockOriginForTabs(aOffsetFromBlockOriginForTabs),
    3131             :       mReflowing(true),
    3132         186 :       mWhichTextRun(aWhichTextRun)
    3133             :   {
    3134          62 :     NS_ASSERTION(mStart.IsInitialized(), "Start not initialized?");
    3135          62 :   }
    3136             : 
    3137             :   /**
    3138             :    * Use this constructor after the frame has been reflowed and we don't
    3139             :    * have other data around. Gets everything from the frame. EnsureTextRun
    3140             :    * *must* be called before this!!!
    3141             :    */
    3142          18 :   PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart,
    3143             :                    nsTextFrame::TextRunType aWhichTextRun)
    3144          18 :     : mTextRun(aFrame->GetTextRun(aWhichTextRun)), mFontGroup(nullptr),
    3145          18 :       mTextStyle(aFrame->StyleText()),
    3146          18 :       mFrag(aFrame->GetContent()->GetText()),
    3147             :       mLineContainer(nullptr),
    3148             :       mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
    3149             :       mTabWidths(nullptr), mTabWidthsAnalyzedLimit(0),
    3150          18 :       mLength(aFrame->GetContentLength()),
    3151          18 :       mWordSpacing(WordSpacing(aFrame, mTextRun)),
    3152          18 :       mLetterSpacing(LetterSpacing(aFrame)),
    3153             :       mHyphenWidth(-1),
    3154             :       mOffsetFromBlockOriginForTabs(0),
    3155             :       mReflowing(false),
    3156         108 :       mWhichTextRun(aWhichTextRun)
    3157             :   {
    3158          18 :     NS_ASSERTION(mTextRun, "Textrun not initialized!");
    3159          18 :   }
    3160             : 
    3161             :   // Call this after construction if you're not going to reflow the text
    3162             :   void InitializeForDisplay(bool aTrimAfter);
    3163             : 
    3164             :   void InitializeForMeasure();
    3165             : 
    3166             :   void GetSpacing(Range aRange, Spacing* aSpacing) const;
    3167             :   gfxFloat GetHyphenWidth() const;
    3168             :   void GetHyphenationBreaks(Range aRange, HyphenType* aBreakBefore) const;
    3169          48 :   StyleHyphens GetHyphensOption() const {
    3170          48 :     return mTextStyle->mHyphens;
    3171             :   }
    3172             : 
    3173           0 :   already_AddRefed<DrawTarget> GetDrawTarget() const {
    3174           0 :     return CreateReferenceDrawTarget(GetFrame());
    3175             :   }
    3176             : 
    3177           0 :   uint32_t GetAppUnitsPerDevUnit() const {
    3178           0 :     return mTextRun->GetAppUnitsPerDevUnit();
    3179             :   }
    3180             : 
    3181             :   void GetSpacingInternal(Range aRange, Spacing* aSpacing, bool aIgnoreTabs) const;
    3182             : 
    3183             :   /**
    3184             :    * Compute the justification information in given DOM range, return
    3185             :    * justification info and assignments if requested.
    3186             :    */
    3187             :   JustificationInfo ComputeJustification(
    3188             :     Range aRange, nsTArray<JustificationAssignment>* aAssignments = nullptr);
    3189             : 
    3190           2 :   const nsTextFrame* GetFrame() const { return mFrame; }
    3191             :   // This may not be equal to the frame offset/length in because we may have
    3192             :   // adjusted for whitespace trimming according to the state bits set in the frame
    3193             :   // (for the static provider)
    3194          45 :   const gfxSkipCharsIterator& GetStart() const { return mStart; }
    3195             :   // May return INT32_MAX if that was given to the constructor
    3196          18 :   uint32_t GetOriginalLength() const {
    3197          18 :     NS_ASSERTION(mLength != INT32_MAX, "Length not known");
    3198          18 :     return mLength;
    3199             :   }
    3200          28 :   const nsTextFragment* GetFragment() const { return mFrag; }
    3201             : 
    3202          18 :   gfxFontGroup* GetFontGroup() const {
    3203          18 :     if (!mFontGroup) {
    3204          17 :       InitFontGroupAndFontMetrics();
    3205             :     }
    3206          18 :     return mFontGroup;
    3207             :   }
    3208             : 
    3209          24 :   nsFontMetrics* GetFontMetrics() const {
    3210          24 :     if (!mFontMetrics) {
    3211          24 :       InitFontGroupAndFontMetrics();
    3212             :     }
    3213          24 :     return mFontMetrics;
    3214             :   }
    3215             : 
    3216             :   void CalcTabWidths(Range aTransformedRange, gfxFloat aTabWidth) const;
    3217             : 
    3218          24 :   const gfxSkipCharsIterator& GetEndHint() const { return mTempIterator; }
    3219             : 
    3220             : protected:
    3221             :   void SetupJustificationSpacing(bool aPostReflow);
    3222             : 
    3223          41 :   void InitFontGroupAndFontMetrics() const {
    3224          41 :     float inflation = (mWhichTextRun == nsTextFrame::eInflated)
    3225          41 :       ? mFrame->GetFontSizeInflation() : 1.0f;
    3226          41 :     mFontGroup = GetFontGroupForFrame(mFrame, inflation,
    3227          82 :                                       getter_AddRefs(mFontMetrics));
    3228          41 :   }
    3229             : 
    3230             :   const RefPtr<gfxTextRun>        mTextRun;
    3231             :   mutable gfxFontGroup*           mFontGroup;
    3232             :   mutable RefPtr<nsFontMetrics>   mFontMetrics;
    3233             :   const nsStyleText*              mTextStyle;
    3234             :   const nsTextFragment*           mFrag;
    3235             :   const nsIFrame*                 mLineContainer;
    3236             :   nsTextFrame*                    mFrame;
    3237             :   gfxSkipCharsIterator            mStart;  // Offset in original and transformed string
    3238             :   const gfxSkipCharsIterator      mTempIterator;
    3239             : 
    3240             :   // Either null, or pointing to the frame's TabWidthProperty.
    3241             :   mutable TabWidthStore*          mTabWidths;
    3242             :   // How far we've done tab-width calculation; this is ONLY valid when
    3243             :   // mTabWidths is nullptr (otherwise rely on mTabWidths->mLimit instead).
    3244             :   // It's a DOM offset relative to the current frame's offset.
    3245             :   mutable uint32_t                mTabWidthsAnalyzedLimit;
    3246             : 
    3247             :   int32_t                         mLength;  // DOM string length, may be INT32_MAX
    3248             :   const gfxFloat                  mWordSpacing; // space for each whitespace char
    3249             :   const gfxFloat                  mLetterSpacing; // space for each letter
    3250             :   mutable gfxFloat                mHyphenWidth;
    3251             :   mutable gfxFloat                mOffsetFromBlockOriginForTabs;
    3252             : 
    3253             :   // The values in mJustificationSpacings corresponds to unskipped
    3254             :   // characters start from mJustificationArrayStart.
    3255             :   uint32_t                        mJustificationArrayStart;
    3256             :   nsTArray<Spacing>               mJustificationSpacings;
    3257             : 
    3258             :   const bool                      mReflowing;
    3259             :   const nsTextFrame::TextRunType  mWhichTextRun;
    3260             : };
    3261             : 
    3262             : /**
    3263             :  * Finds the offset of the first character of the cluster containing aPos
    3264             :  */
    3265           0 : static void FindClusterStart(const gfxTextRun* aTextRun,
    3266             :                              int32_t aOriginalStart,
    3267             :                              gfxSkipCharsIterator* aPos)
    3268             : {
    3269           0 :   while (aPos->GetOriginalOffset() > aOriginalStart) {
    3270           0 :     if (aPos->IsOriginalCharSkipped() ||
    3271           0 :         aTextRun->IsClusterStart(aPos->GetSkippedOffset())) {
    3272           0 :       break;
    3273             :     }
    3274           0 :     aPos->AdvanceOriginal(-1);
    3275             :   }
    3276           0 : }
    3277             : 
    3278             : /**
    3279             :  * Finds the offset of the last character of the cluster containing aPos.
    3280             :  * If aAllowSplitLigature is false, we also check for a ligature-group
    3281             :  * start.
    3282             :  */
    3283           0 : static void FindClusterEnd(const gfxTextRun* aTextRun,
    3284             :                            int32_t aOriginalEnd,
    3285             :                            gfxSkipCharsIterator* aPos,
    3286             :                            bool aAllowSplitLigature = true)
    3287             : {
    3288           0 :   NS_PRECONDITION(aPos->GetOriginalOffset() < aOriginalEnd,
    3289             :                   "character outside string");
    3290           0 :   aPos->AdvanceOriginal(1);
    3291           0 :   while (aPos->GetOriginalOffset() < aOriginalEnd) {
    3292           0 :     if (aPos->IsOriginalCharSkipped() ||
    3293           0 :         (aTextRun->IsClusterStart(aPos->GetSkippedOffset()) &&
    3294           0 :          (aAllowSplitLigature ||
    3295           0 :           aTextRun->IsLigatureGroupStart(aPos->GetSkippedOffset())))) {
    3296           0 :       break;
    3297             :     }
    3298           0 :     aPos->AdvanceOriginal(1);
    3299             :   }
    3300           0 :   aPos->AdvanceOriginal(-1);
    3301           0 : }
    3302             : 
    3303             : JustificationInfo
    3304           0 : PropertyProvider::ComputeJustification(
    3305             :   Range aRange, nsTArray<JustificationAssignment>* aAssignments)
    3306             : {
    3307           0 :   JustificationInfo info;
    3308             : 
    3309             :   // Horizontal-in-vertical frame is orthogonal to the line, so it
    3310             :   // doesn't actually include any justification opportunity inside.
    3311             :   // The spec says such frame should be treated as a U+FFFC. Since we
    3312             :   // do not insert justification opportunities on the sides of that
    3313             :   // character, the sides of this frame are not justifiable either.
    3314           0 :   if (mFrame->StyleContext()->IsTextCombined()) {
    3315           0 :     return info;
    3316             :   }
    3317             : 
    3318           0 :   bool isCJ = IsChineseOrJapanese(mFrame);
    3319             :   nsSkipCharsRunIterator run(
    3320           0 :     mStart, nsSkipCharsRunIterator::LENGTH_INCLUDES_SKIPPED, aRange.Length());
    3321           0 :   run.SetOriginalOffset(aRange.start);
    3322           0 :   mJustificationArrayStart = run.GetSkippedOffset();
    3323             : 
    3324           0 :   nsTArray<JustificationAssignment> assignments;
    3325           0 :   assignments.SetCapacity(aRange.Length());
    3326           0 :   while (run.NextRun()) {
    3327           0 :     uint32_t originalOffset = run.GetOriginalOffset();
    3328           0 :     uint32_t skippedOffset = run.GetSkippedOffset();
    3329           0 :     uint32_t length = run.GetRunLength();
    3330           0 :     assignments.SetLength(skippedOffset + length - mJustificationArrayStart);
    3331             : 
    3332           0 :     gfxSkipCharsIterator iter = run.GetPos();
    3333           0 :     for (uint32_t i = 0; i < length; ++i) {
    3334           0 :       uint32_t offset = originalOffset + i;
    3335           0 :       if (!IsJustifiableCharacter(mTextStyle, mFrag, offset, isCJ)) {
    3336           0 :         continue;
    3337             :       }
    3338             : 
    3339           0 :       iter.SetOriginalOffset(offset);
    3340             : 
    3341           0 :       FindClusterStart(mTextRun, originalOffset, &iter);
    3342           0 :       uint32_t firstCharOffset = iter.GetSkippedOffset();
    3343           0 :       uint32_t firstChar = firstCharOffset > mJustificationArrayStart ?
    3344           0 :         firstCharOffset - mJustificationArrayStart : 0;
    3345           0 :       if (!firstChar) {
    3346           0 :         info.mIsStartJustifiable = true;
    3347             :       } else {
    3348           0 :         auto& assign = assignments[firstChar];
    3349           0 :         auto& prevAssign = assignments[firstChar - 1];
    3350           0 :         if (prevAssign.mGapsAtEnd) {
    3351           0 :           prevAssign.mGapsAtEnd = 1;
    3352           0 :           assign.mGapsAtStart = 1;
    3353             :         } else {
    3354           0 :           assign.mGapsAtStart = 2;
    3355           0 :           info.mInnerOpportunities++;
    3356             :         }
    3357             :       }
    3358             : 
    3359           0 :       FindClusterEnd(mTextRun, originalOffset + length, &iter);
    3360           0 :       uint32_t lastChar = iter.GetSkippedOffset() - mJustificationArrayStart;
    3361             :       // Assign the two gaps temporary to the last char. If the next cluster is
    3362             :       // justifiable as well, one of the gaps will be removed by code above.
    3363           0 :       assignments[lastChar].mGapsAtEnd = 2;
    3364           0 :       info.mInnerOpportunities++;
    3365             : 
    3366             :       // Skip the whole cluster
    3367           0 :       i = iter.GetOriginalOffset() - originalOffset;
    3368             :     }
    3369             :   }
    3370             : 
    3371           0 :   if (!assignments.IsEmpty() && assignments.LastElement().mGapsAtEnd) {
    3372             :     // We counted the expansion opportunity after the last character,
    3373             :     // but it is not an inner opportunity.
    3374           0 :     MOZ_ASSERT(info.mInnerOpportunities > 0);
    3375           0 :     info.mInnerOpportunities--;
    3376           0 :     info.mIsEndJustifiable = true;
    3377             :   }
    3378             : 
    3379           0 :   if (aAssignments) {
    3380           0 :     *aAssignments = Move(assignments);
    3381             :   }
    3382           0 :   return info;
    3383             : }
    3384             : 
    3385             : // aStart, aLength in transformed string offsets
    3386             : void
    3387           0 : PropertyProvider::GetSpacing(Range aRange, Spacing* aSpacing) const
    3388             : {
    3389           0 :   GetSpacingInternal(aRange, aSpacing,
    3390           0 :                      !(mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_HAS_TAB));
    3391           0 : }
    3392             : 
    3393             : static bool
    3394           0 : CanAddSpacingAfter(const gfxTextRun* aTextRun, uint32_t aOffset)
    3395             : {
    3396           0 :   if (aOffset + 1 >= aTextRun->GetLength())
    3397           0 :     return true;
    3398           0 :   return aTextRun->IsClusterStart(aOffset + 1) &&
    3399           0 :     aTextRun->IsLigatureGroupStart(aOffset + 1);
    3400             : }
    3401             : 
    3402             : static gfxFloat
    3403           0 : ComputeTabWidthAppUnits(const nsIFrame* aFrame, gfxTextRun* aTextRun)
    3404             : {
    3405           0 :   const nsStyleText* textStyle = aFrame->StyleText();
    3406           0 :   if (textStyle->mTabSize.GetUnit() != eStyleUnit_Factor) {
    3407           0 :     nscoord w = textStyle->mTabSize.GetCoordValue();
    3408           0 :     MOZ_ASSERT(w >= 0);
    3409           0 :     return w;
    3410             :   }
    3411             : 
    3412           0 :   gfxFloat spaces = textStyle->mTabSize.GetFactorValue();
    3413           0 :   MOZ_ASSERT(spaces >= 0);
    3414             : 
    3415             :   // Round the space width when converting to appunits the same way
    3416             :   // textruns do.
    3417             :   gfxFloat spaceWidthAppUnits =
    3418           0 :     NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
    3419           0 :                                  aTextRun->IsVertical()).spaceWidth *
    3420           0 :              aTextRun->GetAppUnitsPerDevUnit());
    3421           0 :   return spaces * spaceWidthAppUnits;
    3422             : }
    3423             : 
    3424             : void
    3425           0 : PropertyProvider::GetSpacingInternal(Range aRange, Spacing* aSpacing,
    3426             :                                      bool aIgnoreTabs) const
    3427             : {
    3428           0 :   NS_PRECONDITION(IsInBounds(mStart, mLength, aRange), "Range out of bounds");
    3429             : 
    3430             :   uint32_t index;
    3431           0 :   for (index = 0; index < aRange.Length(); ++index) {
    3432           0 :     aSpacing[index].mBefore = 0.0;
    3433           0 :     aSpacing[index].mAfter = 0.0;
    3434             :   }
    3435             : 
    3436           0 :   if (mFrame->StyleContext()->IsTextCombined()) {
    3437           0 :     return;
    3438             :   }
    3439             : 
    3440             :   // Find our offset into the original+transformed string
    3441           0 :   gfxSkipCharsIterator start(mStart);
    3442           0 :   start.SetSkippedOffset(aRange.start);
    3443             : 
    3444             :   // First, compute the word and letter spacing
    3445           0 :   if (mWordSpacing || mLetterSpacing) {
    3446             :     // Iterate over non-skipped characters
    3447             :     nsSkipCharsRunIterator run(
    3448           0 :         start, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aRange.Length());
    3449           0 :     while (run.NextRun()) {
    3450           0 :       uint32_t runOffsetInSubstring = run.GetSkippedOffset() - aRange.start;
    3451           0 :       gfxSkipCharsIterator iter = run.GetPos();
    3452           0 :       for (int32_t i = 0; i < run.GetRunLength(); ++i) {
    3453           0 :         if (CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i)) {
    3454             :           // End of a cluster, not in a ligature: put letter-spacing after it
    3455           0 :           aSpacing[runOffsetInSubstring + i].mAfter += mLetterSpacing;
    3456             :         }
    3457           0 :         if (IsCSSWordSpacingSpace(mFrag, i + run.GetOriginalOffset(),
    3458           0 :                                   mFrame, mTextStyle)) {
    3459             :           // It kinda sucks, but space characters can be part of clusters,
    3460             :           // and even still be whitespace (I think!)
    3461           0 :           iter.SetSkippedOffset(run.GetSkippedOffset() + i);
    3462           0 :           FindClusterEnd(mTextRun, run.GetOriginalOffset() + run.GetRunLength(),
    3463           0 :                          &iter);
    3464           0 :           uint32_t runOffset = iter.GetSkippedOffset() - aRange.start;
    3465           0 :           aSpacing[runOffset].mAfter += mWordSpacing;
    3466             :         }
    3467             :       }
    3468             :     }
    3469             :   }
    3470             : 
    3471             :   // Now add tab spacing, if there is any
    3472           0 :   if (!aIgnoreTabs) {
    3473           0 :     gfxFloat tabWidth = ComputeTabWidthAppUnits(mFrame, mTextRun);
    3474           0 :     if (tabWidth > 0) {
    3475           0 :       CalcTabWidths(aRange, tabWidth);
    3476           0 :       if (mTabWidths) {
    3477           0 :         mTabWidths->ApplySpacing(aSpacing,
    3478           0 :                                  aRange.start - mStart.GetSkippedOffset(),
    3479           0 :                                  aRange.Length());
    3480             :       }
    3481             :     }
    3482             :   }
    3483             : 
    3484             :   // Now add in justification spacing
    3485           0 :   if (mJustificationSpacings.Length() > 0) {
    3486             :     // If there is any spaces trimmed at the end, aStart + aLength may
    3487             :     // be larger than the flags array. When that happens, we can simply
    3488             :     // ignore those spaces.
    3489           0 :     auto arrayEnd = mJustificationArrayStart +
    3490           0 :       static_cast<uint32_t>(mJustificationSpacings.Length());
    3491           0 :     auto end = std::min(aRange.end, arrayEnd);
    3492           0 :     MOZ_ASSERT(aRange.start >= mJustificationArrayStart);
    3493           0 :     for (auto i = aRange.start; i < end; i++) {
    3494             :       const auto& spacing =
    3495           0 :         mJustificationSpacings[i - mJustificationArrayStart];
    3496           0 :       uint32_t offset = i - aRange.start;
    3497           0 :       aSpacing[offset].mBefore += spacing.mBefore;
    3498           0 :       aSpacing[offset].mAfter += spacing.mAfter;
    3499             :     }
    3500             :   }
    3501             : }
    3502             : 
    3503             : // aX and the result are in whole appunits.
    3504             : static gfxFloat
    3505           0 : AdvanceToNextTab(gfxFloat aX, gfxFloat aTabWidth)
    3506             : {
    3507             : 
    3508             :   // Advance aX to the next multiple of *aCachedTabWidth. We must advance
    3509             :   // by at least 1 appunit.
    3510             :   // XXX should we make this 1 CSS pixel?
    3511           0 :   return ceil((aX + 1) / aTabWidth) * aTabWidth;
    3512             : }
    3513             : 
    3514             : void
    3515           0 : PropertyProvider::CalcTabWidths(Range aRange, gfxFloat aTabWidth) const
    3516             : {
    3517           0 :   MOZ_ASSERT(aTabWidth > 0);
    3518             : 
    3519           0 :   if (!mTabWidths) {
    3520           0 :     if (mReflowing && !mLineContainer) {
    3521             :       // Intrinsic width computation does its own tab processing. We
    3522             :       // just don't do anything here.
    3523           0 :       return;
    3524             :     }
    3525           0 :     if (!mReflowing) {
    3526           0 :       mTabWidths = mFrame->GetProperty(TabWidthProperty());
    3527             : #ifdef DEBUG
    3528             :       // If we're not reflowing, we should have already computed the
    3529             :       // tab widths; check that they're available as far as the last
    3530             :       // tab character present (if any)
    3531           0 :       for (uint32_t i = aRange.end; i > aRange.start; --i) {
    3532           0 :         if (mTextRun->CharIsTab(i - 1)) {
    3533           0 :           uint32_t startOffset = mStart.GetSkippedOffset();
    3534           0 :           NS_ASSERTION(mTabWidths && mTabWidths->mLimit + startOffset >= i,
    3535             :                        "Precomputed tab widths are missing!");
    3536           0 :           break;
    3537             :         }
    3538             :       }
    3539             : #endif
    3540           0 :       return;
    3541             :     }
    3542             :   }
    3543             : 
    3544           0 :   uint32_t startOffset = mStart.GetSkippedOffset();
    3545           0 :   MOZ_ASSERT(aRange.start >= startOffset, "wrong start offset");
    3546           0 :   MOZ_ASSERT(aRange.end <= startOffset + mLength, "beyond the end");
    3547             :   uint32_t tabsEnd =
    3548           0 :     (mTabWidths ? mTabWidths->mLimit : mTabWidthsAnalyzedLimit) + startOffset;
    3549           0 :   if (tabsEnd < aRange.end) {
    3550           0 :     NS_ASSERTION(mReflowing,
    3551             :                  "We need precomputed tab widths, but don't have enough.");
    3552             : 
    3553           0 :     for (uint32_t i = tabsEnd; i < aRange.end; ++i) {
    3554             :       Spacing spacing;
    3555           0 :       GetSpacingInternal(Range(i, i + 1), &spacing, true);
    3556           0 :       mOffsetFromBlockOriginForTabs += spacing.mBefore;
    3557             : 
    3558           0 :       if (!mTextRun->CharIsTab(i)) {
    3559           0 :         if (mTextRun->IsClusterStart(i)) {
    3560           0 :           uint32_t clusterEnd = i + 1;
    3561           0 :           while (clusterEnd < mTextRun->GetLength() &&
    3562           0 :                  !mTextRun->IsClusterStart(clusterEnd)) {
    3563           0 :             ++clusterEnd;
    3564             :           }
    3565           0 :           mOffsetFromBlockOriginForTabs +=
    3566           0 :             mTextRun->GetAdvanceWidth(Range(i, clusterEnd), nullptr);
    3567             :         }
    3568             :       } else {
    3569           0 :         if (!mTabWidths) {
    3570           0 :           mTabWidths = new TabWidthStore(mFrame->GetContentOffset());
    3571           0 :           mFrame->SetProperty(TabWidthProperty(), mTabWidths);
    3572             :         }
    3573           0 :         double nextTab = AdvanceToNextTab(mOffsetFromBlockOriginForTabs,
    3574           0 :                                           aTabWidth);
    3575           0 :         mTabWidths->mWidths.AppendElement(TabWidth(i - startOffset,
    3576           0 :                 NSToIntRound(nextTab - mOffsetFromBlockOriginForTabs)));
    3577           0 :         mOffsetFromBlockOriginForTabs = nextTab;
    3578             :       }
    3579             : 
    3580           0 :       mOffsetFromBlockOriginForTabs += spacing.mAfter;
    3581             :     }
    3582             : 
    3583           0 :     if (mTabWidths) {
    3584           0 :       mTabWidths->mLimit = aRange.end - startOffset;
    3585             :     }
    3586             :   }
    3587             : 
    3588           0 :   if (!mTabWidths) {
    3589             :     // Delete any stale property that may be left on the frame
    3590           0 :     mFrame->DeleteProperty(TabWidthProperty());
    3591           0 :     mTabWidthsAnalyzedLimit = std::max(mTabWidthsAnalyzedLimit,
    3592           0 :                                        aRange.end - startOffset);
    3593             :   }
    3594             : }
    3595             : 
    3596             : gfxFloat
    3597           0 : PropertyProvider::GetHyphenWidth() const
    3598             : {
    3599           0 :   if (mHyphenWidth < 0) {
    3600           0 :     mHyphenWidth = GetFontGroup()->GetHyphenWidth(this);
    3601             :   }
    3602           0 :   return mHyphenWidth + mLetterSpacing;
    3603             : }
    3604             : 
    3605             : static inline bool
    3606           0 : IS_HYPHEN(char16_t u)
    3607             : {
    3608           0 :   return (u == char16_t('-') ||
    3609           0 :           u == 0x058A || // ARMENIAN HYPHEN
    3610           0 :           u == 0x2010 || // HYPHEN
    3611           0 :           u == 0x2012 || // FIGURE DASH
    3612           0 :           u == 0x2013);  // EN DASH
    3613             : }
    3614             : 
    3615             : void
    3616           0 : PropertyProvider::GetHyphenationBreaks(Range aRange, HyphenType* aBreakBefore) const
    3617             : {
    3618           0 :   NS_PRECONDITION(IsInBounds(mStart, mLength, aRange), "Range out of bounds");
    3619           0 :   NS_PRECONDITION(mLength != INT32_MAX, "Can't call this with undefined length");
    3620             : 
    3621           0 :   if (!mTextStyle->WhiteSpaceCanWrap(mFrame) ||
    3622           0 :       mTextStyle->mHyphens == StyleHyphens::None)
    3623             :   {
    3624           0 :     memset(aBreakBefore, static_cast<uint8_t>(HyphenType::None),
    3625           0 :            aRange.Length() * sizeof(HyphenType));
    3626           0 :     return;
    3627             :   }
    3628             : 
    3629             :   // Iterate through the original-string character runs
    3630             :   nsSkipCharsRunIterator run(
    3631           0 :       mStart, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aRange.Length());
    3632           0 :   run.SetSkippedOffset(aRange.start);
    3633             :   // We need to visit skipped characters so that we can detect SHY
    3634           0 :   run.SetVisitSkipped();
    3635             : 
    3636           0 :   int32_t prevTrailingCharOffset = run.GetPos().GetOriginalOffset() - 1;
    3637             :   bool allowHyphenBreakBeforeNextChar =
    3638           0 :     prevTrailingCharOffset >= mStart.GetOriginalOffset() &&
    3639           0 :     prevTrailingCharOffset < mStart.GetOriginalOffset() + mLength &&
    3640           0 :     mFrag->CharAt(prevTrailingCharOffset) == CH_SHY;
    3641             : 
    3642           0 :   while (run.NextRun()) {
    3643           0 :     NS_ASSERTION(run.GetRunLength() > 0, "Shouldn't return zero-length runs");
    3644           0 :     if (run.IsSkipped()) {
    3645             :       // Check if there's a soft hyphen which would let us hyphenate before
    3646             :       // the next non-skipped character. Don't look at soft hyphens followed
    3647             :       // by other skipped characters, we won't use them.
    3648           0 :       allowHyphenBreakBeforeNextChar =
    3649           0 :         mFrag->CharAt(run.GetOriginalOffset() + run.GetRunLength() - 1) == CH_SHY;
    3650             :     } else {
    3651           0 :       int32_t runOffsetInSubstring = run.GetSkippedOffset() - aRange.start;
    3652           0 :       memset(aBreakBefore + runOffsetInSubstring,
    3653             :              static_cast<uint8_t>(HyphenType::None),
    3654           0 :              run.GetRunLength() * sizeof(HyphenType));
    3655             :       // Don't allow hyphen breaks at the start of the line
    3656           0 :       aBreakBefore[runOffsetInSubstring] =
    3657           0 :           allowHyphenBreakBeforeNextChar &&
    3658           0 :           (!(mFrame->GetStateBits() & TEXT_START_OF_LINE) ||
    3659           0 :            run.GetSkippedOffset() > mStart.GetSkippedOffset())
    3660           0 :           ? HyphenType::Soft
    3661             :           : HyphenType::None;
    3662           0 :       allowHyphenBreakBeforeNextChar = false;
    3663             :     }
    3664             :   }
    3665             : 
    3666           0 :   if (mTextStyle->mHyphens == StyleHyphens::Auto) {
    3667           0 :     for (uint32_t i = 0; i < aRange.Length(); ++i) {
    3668           0 :       int32_t fragIndex = mFrag->GetLength() > aRange.end ?
    3669           0 :                           aRange.start + i : i;
    3670           0 :       if (IS_HYPHEN(mFrag->CharAt(fragIndex))) {
    3671           0 :         aBreakBefore[i] = HyphenType::Explicit;
    3672           0 :         continue;
    3673             :       }
    3674             : 
    3675           0 :       if (mTextRun->CanHyphenateBefore(aRange.start + i) &&
    3676           0 :           aBreakBefore[i] == HyphenType::None) {
    3677           0 :         aBreakBefore[i] = HyphenType::AutoWithoutManualInSameWord;
    3678             :       }
    3679             :     }
    3680             :   }
    3681             : }
    3682             : 
    3683             : void
    3684          18 : PropertyProvider::InitializeForDisplay(bool aTrimAfter)
    3685             : {
    3686             :   nsTextFrame::TrimmedOffsets trimmed =
    3687          18 :     mFrame->GetTrimmedOffsets(mFrag, aTrimAfter);
    3688          18 :   mStart.SetOriginalOffset(trimmed.mStart);
    3689          18 :   mLength = trimmed.mLength;
    3690          18 :   SetupJustificationSpacing(true);
    3691          18 : }
    3692             : 
    3693             : void
    3694           0 : PropertyProvider::InitializeForMeasure()
    3695             : {
    3696             :   nsTextFrame::TrimmedOffsets trimmed =
    3697           0 :     mFrame->GetTrimmedOffsets(mFrag, true, false);
    3698           0 :   mStart.SetOriginalOffset(trimmed.mStart);
    3699           0 :   mLength = trimmed.mLength;
    3700           0 :   SetupJustificationSpacing(false);
    3701           0 : }
    3702             : 
    3703             : 
    3704             : void
    3705          18 : PropertyProvider::SetupJustificationSpacing(bool aPostReflow)
    3706             : {
    3707          18 :   NS_PRECONDITION(mLength != INT32_MAX, "Can't call this with undefined length");
    3708             : 
    3709          18 :   if (!(mFrame->GetStateBits() & TEXT_JUSTIFICATION_ENABLED)) {
    3710          36 :     return;
    3711             :   }
    3712             : 
    3713           0 :   gfxSkipCharsIterator start(mStart), end(mStart);
    3714             :   // We can't just use our mLength here; when InitializeForDisplay is
    3715             :   // called with false for aTrimAfter, we still shouldn't be assigning
    3716             :   // justification space to any trailing whitespace.
    3717             :   nsTextFrame::TrimmedOffsets trimmed =
    3718           0 :     mFrame->GetTrimmedOffsets(mFrag, true, aPostReflow);
    3719           0 :   end.AdvanceOriginal(trimmed.mLength);
    3720           0 :   gfxSkipCharsIterator realEnd(end);
    3721             : 
    3722           0 :   Range range(uint32_t(start.GetOriginalOffset()),
    3723           0 :               uint32_t(end.GetOriginalOffset()));
    3724           0 :   nsTArray<JustificationAssignment> assignments;
    3725           0 :   JustificationInfo info = ComputeJustification(range, &assignments);
    3726             : 
    3727           0 :   auto assign = mFrame->GetJustificationAssignment();
    3728           0 :   auto totalGaps = JustificationUtils::CountGaps(info, assign);
    3729           0 :   if (!totalGaps || assignments.IsEmpty()) {
    3730             :     // Nothing to do, nothing is justifiable and we shouldn't have any
    3731             :     // justification space assigned
    3732           0 :     return;
    3733             :   }
    3734             : 
    3735             :   // Remember that textrun measurements are in the run's orientation,
    3736             :   // so its advance "width" is actually a height in vertical writing modes,
    3737             :   // corresponding to the inline-direction of the frame.
    3738             :   gfxFloat naturalWidth =
    3739           0 :     mTextRun->GetAdvanceWidth(Range(mStart.GetSkippedOffset(),
    3740           0 :                                     realEnd.GetSkippedOffset()), this);
    3741           0 :   if (mFrame->GetStateBits() & TEXT_HYPHEN_BREAK) {
    3742           0 :     naturalWidth += GetHyphenWidth();
    3743             :   }
    3744           0 :   nscoord totalSpacing = mFrame->ISize() - naturalWidth;
    3745           0 :   if (totalSpacing <= 0) {
    3746             :     // No space available
    3747           0 :     return;
    3748             :   }
    3749             : 
    3750           0 :   assignments[0].mGapsAtStart = assign.mGapsAtStart;
    3751           0 :   assignments.LastElement().mGapsAtEnd = assign.mGapsAtEnd;
    3752             : 
    3753           0 :   MOZ_ASSERT(mJustificationSpacings.IsEmpty());
    3754           0 :   JustificationApplicationState state(totalGaps, totalSpacing);
    3755           0 :   mJustificationSpacings.SetCapacity(assignments.Length());
    3756           0 :   for (const JustificationAssignment& assign : assignments) {
    3757           0 :     Spacing* spacing = mJustificationSpacings.AppendElement();
    3758           0 :     spacing->mBefore = state.Consume(assign.mGapsAtStart);
    3759           0 :     spacing->mAfter = state.Consume(assign.mGapsAtEnd);
    3760             :   }
    3761             : }
    3762             : 
    3763             : //----------------------------------------------------------------------
    3764             : 
    3765             : static nscolor
    3766           0 : EnsureDifferentColors(nscolor colorA, nscolor colorB)
    3767             : {
    3768           0 :   if (colorA == colorB) {
    3769             :     nscolor res;
    3770           0 :     res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
    3771             :                  NS_GET_G(colorA) ^ 0xff,
    3772             :                  NS_GET_B(colorA) ^ 0xff);
    3773           0 :     return res;
    3774             :   }
    3775           0 :   return colorA;
    3776             : }
    3777             : 
    3778             : //-----------------------------------------------------------------------------
    3779             : 
    3780          17 : nsTextPaintStyle::nsTextPaintStyle(nsTextFrame* aFrame)
    3781             :   : mFrame(aFrame),
    3782          17 :     mPresContext(aFrame->PresContext()),
    3783             :     mInitCommonColors(false),
    3784             :     mInitSelectionColorsAndShadow(false),
    3785             :     mResolveColors(true),
    3786          34 :     mHasSelectionShadow(false)
    3787             : {
    3788         102 :   for (uint32_t i = 0; i < ArrayLength(mSelectionStyle); i++)
    3789          85 :     mSelectionStyle[i].mInit = false;
    3790          17 : }
    3791             : 
    3792             : bool
    3793           0 : nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor)
    3794             : {
    3795           0 :   InitCommonColors();
    3796             : 
    3797             :   // If the combination of selection background color and frame background color
    3798             :   // is sufficient contrast, don't exchange the selection colors.
    3799             :   int32_t backLuminosityDifference =
    3800           0 :             NS_LUMINOSITY_DIFFERENCE(*aBackColor, mFrameBackgroundColor);
    3801           0 :   if (backLuminosityDifference >= mSufficientContrast)
    3802           0 :     return false;
    3803             : 
    3804             :   // Otherwise, we should use the higher-contrast color for the selection
    3805             :   // background color.
    3806             :   int32_t foreLuminosityDifference =
    3807           0 :             NS_LUMINOSITY_DIFFERENCE(*aForeColor, mFrameBackgroundColor);
    3808           0 :   if (backLuminosityDifference < foreLuminosityDifference) {
    3809           0 :     nscolor tmpColor = *aForeColor;
    3810           0 :     *aForeColor = *aBackColor;
    3811           0 :     *aBackColor = tmpColor;
    3812           0 :     return true;
    3813             :   }
    3814           0 :   return false;
    3815             : }
    3816             : 
    3817             : nscolor
    3818          19 : nsTextPaintStyle::GetTextColor()
    3819             : {
    3820          19 :   if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
    3821           0 :     if (!mResolveColors)
    3822           0 :       return NS_SAME_AS_FOREGROUND_COLOR;
    3823             : 
    3824           0 :     const nsStyleSVG* style = mFrame->StyleSVG();
    3825           0 :     switch (style->mFill.Type()) {
    3826             :       case eStyleSVGPaintType_None:
    3827           0 :         return NS_RGBA(0, 0, 0, 0);
    3828             :       case eStyleSVGPaintType_Color:
    3829           0 :         return nsLayoutUtils::GetColor(mFrame, &nsStyleSVG::mFill);
    3830             :       default:
    3831           0 :         NS_ERROR("cannot resolve SVG paint to nscolor");
    3832           0 :         return NS_RGBA(0, 0, 0, 255);
    3833             :     }
    3834             :   }
    3835             : 
    3836          19 :   return nsLayoutUtils::GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
    3837             : }
    3838             : 
    3839             : bool
    3840           0 : nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor,
    3841             :                                      nscolor* aBackColor)
    3842             : {
    3843           0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3844           0 :   NS_ASSERTION(aBackColor, "aBackColor is null");
    3845             : 
    3846           0 :   if (!InitSelectionColorsAndShadow())
    3847           0 :     return false;
    3848             : 
    3849           0 :   *aForeColor = mSelectionTextColor;
    3850           0 :   *aBackColor = mSelectionBGColor;
    3851           0 :   return true;
    3852             : }
    3853             : 
    3854             : void
    3855           0 : nsTextPaintStyle::GetHighlightColors(nscolor* aForeColor,
    3856             :                                      nscolor* aBackColor)
    3857             : {
    3858           0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3859           0 :   NS_ASSERTION(aBackColor, "aBackColor is null");
    3860             : 
    3861           0 :   const nsFrameSelection* frameSelection = mFrame->GetConstFrameSelection();
    3862             :   const Selection* selection =
    3863           0 :     frameSelection->GetSelection(SelectionType::eFind);
    3864           0 :   const SelectionCustomColors* customColors = nullptr;
    3865           0 :   if (selection) {
    3866           0 :     customColors = selection->GetCustomColors();
    3867             :   }
    3868             : 
    3869           0 :   if (!customColors) {
    3870             :     nscolor backColor =
    3871           0 :       LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightBackground);
    3872             :     nscolor foreColor =
    3873           0 :       LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightForeground);
    3874           0 :     EnsureSufficientContrast(&foreColor, &backColor);
    3875           0 :     *aForeColor = foreColor;
    3876           0 :     *aBackColor = backColor;
    3877             : 
    3878           0 :     return;
    3879             :   }
    3880             : 
    3881           0 :   if (customColors->mForegroundColor && customColors->mBackgroundColor) {
    3882           0 :     nscolor foreColor = *customColors->mForegroundColor;
    3883           0 :     nscolor backColor = *customColors->mBackgroundColor;
    3884             : 
    3885           0 :     if (EnsureSufficientContrast(&foreColor, &backColor) &&
    3886           0 :         customColors->mAltForegroundColor &&
    3887           0 :         customColors->mAltBackgroundColor) {
    3888           0 :       foreColor = *customColors->mAltForegroundColor;
    3889           0 :       backColor = *customColors->mAltBackgroundColor;
    3890             :     }
    3891             : 
    3892           0 :     *aForeColor = foreColor;
    3893           0 :     *aBackColor = backColor;
    3894           0 :     return;
    3895             :   }
    3896             : 
    3897           0 :   InitCommonColors();
    3898             : 
    3899           0 :   if (customColors->mBackgroundColor) {
    3900             :     // !mForegroundColor means "currentColor"; the current color of the text.
    3901           0 :     nscolor foreColor = GetTextColor();
    3902           0 :     nscolor backColor = *customColors->mBackgroundColor;
    3903             : 
    3904             :     int32_t luminosityDifference =
    3905           0 :               NS_LUMINOSITY_DIFFERENCE(foreColor, backColor);
    3906             : 
    3907           0 :     if (mSufficientContrast > luminosityDifference &&
    3908           0 :         customColors->mAltBackgroundColor) {
    3909             :       int32_t altLuminosityDifference =
    3910           0 :                 NS_LUMINOSITY_DIFFERENCE(foreColor, *customColors->mAltBackgroundColor);
    3911             : 
    3912           0 :       if (luminosityDifference < altLuminosityDifference) {
    3913           0 :         backColor = *customColors->mAltBackgroundColor;
    3914             :       }
    3915             :     }
    3916             : 
    3917           0 :     *aForeColor = foreColor;
    3918           0 :     *aBackColor = backColor;
    3919           0 :     return;
    3920             :   }
    3921             : 
    3922           0 :   if (customColors->mForegroundColor) {
    3923           0 :     nscolor foreColor = *customColors->mForegroundColor;
    3924             :     // !mBackgroundColor means "transparent"; the current color of the background.
    3925             : 
    3926             :     int32_t luminosityDifference =
    3927           0 :               NS_LUMINOSITY_DIFFERENCE(foreColor, mFrameBackgroundColor);
    3928             : 
    3929           0 :     if (mSufficientContrast > luminosityDifference &&
    3930           0 :         customColors->mAltForegroundColor) {
    3931             :       int32_t altLuminosityDifference =
    3932           0 :                 NS_LUMINOSITY_DIFFERENCE(*customColors->mForegroundColor, mFrameBackgroundColor);
    3933             : 
    3934           0 :       if (luminosityDifference < altLuminosityDifference) {
    3935           0 :         foreColor = *customColors->mAltForegroundColor;
    3936             :       }
    3937             :     }
    3938             : 
    3939           0 :     *aForeColor = foreColor;
    3940           0 :     *aBackColor = NS_TRANSPARENT;
    3941           0 :     return;
    3942             :   }
    3943             : 
    3944             :   // There are neither mForegroundColor nor mBackgroundColor.
    3945           0 :   *aForeColor = GetTextColor();
    3946           0 :   *aBackColor = NS_TRANSPARENT;
    3947             : }
    3948             : 
    3949             : void
    3950           2 : nsTextPaintStyle::GetURLSecondaryColor(nscolor* aForeColor)
    3951             : {
    3952           2 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3953             : 
    3954           2 :   nscolor textColor = GetTextColor();
    3955           2 :   textColor = NS_RGBA(NS_GET_R(textColor),
    3956             :                       NS_GET_G(textColor),
    3957             :                       NS_GET_B(textColor),
    3958             :                       (uint8_t)(255 * 0.5f));
    3959             :   // Don't use true alpha color for readability.
    3960           2 :   InitCommonColors();
    3961           2 :   *aForeColor = NS_ComposeColors(mFrameBackgroundColor, textColor);
    3962           2 : }
    3963             : 
    3964             : void
    3965           0 : nsTextPaintStyle::GetIMESelectionColors(int32_t  aIndex,
    3966             :                                         nscolor* aForeColor,
    3967             :                                         nscolor* aBackColor)
    3968             : {
    3969           0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3970           0 :   NS_ASSERTION(aBackColor, "aBackColor is null");
    3971           0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
    3972             : 
    3973           0 :   nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
    3974           0 :   *aForeColor = selectionStyle->mTextColor;
    3975           0 :   *aBackColor = selectionStyle->mBGColor;
    3976           0 : }
    3977             : 
    3978             : bool
    3979           0 : nsTextPaintStyle::GetSelectionUnderlineForPaint(int32_t  aIndex,
    3980             :                                                 nscolor* aLineColor,
    3981             :                                                 float*   aRelativeSize,
    3982             :                                                 uint8_t* aStyle)
    3983             : {
    3984           0 :   NS_ASSERTION(aLineColor, "aLineColor is null");
    3985           0 :   NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
    3986           0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
    3987             : 
    3988           0 :   nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
    3989           0 :   if (selectionStyle->mUnderlineStyle == NS_STYLE_BORDER_STYLE_NONE ||
    3990           0 :       selectionStyle->mUnderlineColor == NS_TRANSPARENT ||
    3991           0 :       selectionStyle->mUnderlineRelativeSize <= 0.0f)
    3992           0 :     return false;
    3993             : 
    3994           0 :   *aLineColor = selectionStyle->mUnderlineColor;
    3995           0 :   *aRelativeSize = selectionStyle->mUnderlineRelativeSize;
    3996           0 :   *aStyle = selectionStyle->mUnderlineStyle;
    3997           0 :   return true;
    3998             : }
    3999             : 
    4000             : void
    4001           2 : nsTextPaintStyle::InitCommonColors()
    4002             : {
    4003           2 :   if (mInitCommonColors)
    4004           1 :     return;
    4005             : 
    4006             :   nsIFrame* bgFrame =
    4007           1 :     nsCSSRendering::FindNonTransparentBackgroundFrame(mFrame);
    4008           1 :   NS_ASSERTION(bgFrame, "Cannot find NonTransparentBackgroundFrame.");
    4009             :   nscolor bgColor = bgFrame->
    4010           1 :     GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
    4011             : 
    4012           1 :   nscolor defaultBgColor = mPresContext->DefaultBackgroundColor();
    4013           1 :   mFrameBackgroundColor = NS_ComposeColors(defaultBgColor, bgColor);
    4014             : 
    4015           1 :   mSystemFieldForegroundColor =
    4016           1 :     LookAndFeel::GetColor(LookAndFeel::eColorID__moz_fieldtext);
    4017           1 :   mSystemFieldBackgroundColor =
    4018           1 :     LookAndFeel::GetColor(LookAndFeel::eColorID__moz_field);
    4019             : 
    4020           1 :   if (bgFrame->IsThemed()) {
    4021             :     // Assume a native widget has sufficient contrast always
    4022           0 :     mSufficientContrast = 0;
    4023           0 :     mInitCommonColors = true;
    4024           0 :     return;
    4025             :   }
    4026             : 
    4027           1 :   NS_ASSERTION(NS_GET_A(defaultBgColor) == 255,
    4028             :                "default background color is not opaque");
    4029             : 
    4030             :   nscolor defaultWindowBackgroundColor =
    4031           1 :     LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground);
    4032             :   nscolor selectionTextColor =
    4033           1 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
    4034             :   nscolor selectionBGColor =
    4035           1 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
    4036             : 
    4037           1 :   mSufficientContrast =
    4038           1 :     std::min(std::min(NS_SUFFICIENT_LUMINOSITY_DIFFERENCE,
    4039           2 :                   NS_LUMINOSITY_DIFFERENCE(selectionTextColor,
    4040             :                                            selectionBGColor)),
    4041           2 :                   NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
    4042           2 :                                            selectionBGColor));
    4043             : 
    4044           1 :   mInitCommonColors = true;
    4045             : }
    4046             : 
    4047             : nscolor
    4048           0 : nsTextPaintStyle::GetSystemFieldForegroundColor()
    4049             : {
    4050           0 :   InitCommonColors();
    4051           0 :   return mSystemFieldForegroundColor;
    4052             : }
    4053             : 
    4054             : nscolor
    4055           0 : nsTextPaintStyle::GetSystemFieldBackgroundColor()
    4056             : {
    4057           0 :   InitCommonColors();
    4058           0 :   return mSystemFieldBackgroundColor;
    4059             : }
    4060             : 
    4061             : static Element*
    4062           0 : FindElementAncestorForMozSelection(nsIContent* aContent)
    4063             : {
    4064           0 :   NS_ENSURE_TRUE(aContent, nullptr);
    4065           0 :   while (aContent && aContent->IsInNativeAnonymousSubtree()) {
    4066           0 :     aContent = aContent->GetBindingParent();
    4067             :   }
    4068           0 :   NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
    4069           0 :   while (aContent && !aContent->IsElement()) {
    4070           0 :     aContent = aContent->GetParent();
    4071             :   }
    4072           0 :   return aContent ? aContent->AsElement() : nullptr;
    4073             : }
    4074             : 
    4075             : bool
    4076           0 : nsTextPaintStyle::InitSelectionColorsAndShadow()
    4077             : {
    4078           0 :   if (mInitSelectionColorsAndShadow)
    4079           0 :     return true;
    4080             : 
    4081             :   int16_t selectionFlags;
    4082           0 :   int16_t selectionStatus = mFrame->GetSelectionStatus(&selectionFlags);
    4083           0 :   if (!(selectionFlags & nsISelectionDisplay::DISPLAY_TEXT) ||
    4084             :       selectionStatus < nsISelectionController::SELECTION_ON) {
    4085             :     // Not displaying the normal selection.
    4086             :     // We're not caching this fact, so every call to GetSelectionColors
    4087             :     // will come through here. We could avoid this, but it's not really worth it.
    4088           0 :     return false;
    4089             :   }
    4090             : 
    4091           0 :   mInitSelectionColorsAndShadow = true;
    4092             : 
    4093           0 :   nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(mFrame);
    4094             :   Element* selectionElement =
    4095           0 :     FindElementAncestorForMozSelection(nonGeneratedAncestor->GetContent());
    4096             : 
    4097           0 :   if (selectionElement &&
    4098             :       selectionStatus == nsISelectionController::SELECTION_ON) {
    4099           0 :     RefPtr<nsStyleContext> sc = nullptr;
    4100           0 :     sc = mPresContext->StyleSet()->
    4101           0 :       ProbePseudoElementStyle(selectionElement,
    4102             :                               CSSPseudoElementType::mozSelection,
    4103           0 :                               mFrame->StyleContext());
    4104             :     // Use -moz-selection pseudo class.
    4105           0 :     if (sc) {
    4106           0 :       mSelectionBGColor =
    4107           0 :         sc->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
    4108           0 :       mSelectionTextColor =
    4109           0 :         sc->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
    4110           0 :       mHasSelectionShadow =
    4111           0 :         nsRuleNode::HasAuthorSpecifiedRules(sc,
    4112             :                                             NS_AUTHOR_SPECIFIED_TEXT_SHADOW,
    4113             :                                             true);
    4114           0 :       if (mHasSelectionShadow) {
    4115           0 :         mSelectionShadow = sc->StyleText()->mTextShadow;
    4116             :       }
    4117           0 :       return true;
    4118             :     }
    4119             :   }
    4120             : 
    4121             :   nscolor selectionBGColor =
    4122           0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
    4123             : 
    4124           0 :   if (selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
    4125           0 :     mSelectionBGColor =
    4126           0 :       LookAndFeel::GetColor(
    4127             :         LookAndFeel::eColorID_TextSelectBackgroundAttention);
    4128           0 :     mSelectionBGColor  = EnsureDifferentColors(mSelectionBGColor,
    4129             :                                                selectionBGColor);
    4130           0 :   } else if (selectionStatus != nsISelectionController::SELECTION_ON) {
    4131           0 :     mSelectionBGColor =
    4132           0 :       LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackgroundDisabled);
    4133           0 :     mSelectionBGColor  = EnsureDifferentColors(mSelectionBGColor,
    4134             :                                                selectionBGColor);
    4135             :   } else {
    4136           0 :     mSelectionBGColor = selectionBGColor;
    4137             :   }
    4138             : 
    4139           0 :   mSelectionTextColor =
    4140           0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
    4141             : 
    4142           0 :   if (mResolveColors) {
    4143             :     // On MacOS X, we don't exchange text color and BG color.
    4144           0 :     if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
    4145           0 :       nscolor frameColor = nsSVGUtils::IsInSVGTextSubtree(mFrame)
    4146           0 :         ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
    4147           0 :         : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
    4148           0 :       mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor);
    4149           0 :     } else if (mSelectionTextColor == NS_CHANGE_COLOR_IF_SAME_AS_BG) {
    4150           0 :       nscolor frameColor = nsSVGUtils::IsInSVGTextSubtree(mFrame)
    4151           0 :         ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
    4152           0 :         : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
    4153           0 :       if (frameColor == mSelectionBGColor) {
    4154           0 :         mSelectionTextColor =
    4155           0 :           LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForegroundCustom);
    4156             :       }
    4157             :     } else {
    4158           0 :       EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
    4159             :     }
    4160             :   } else {
    4161           0 :     if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
    4162           0 :       mSelectionTextColor = NS_SAME_AS_FOREGROUND_COLOR;
    4163             :     }
    4164             :   }
    4165           0 :   return true;
    4166             : }
    4167             : 
    4168             : nsTextPaintStyle::nsSelectionStyle*
    4169           0 : nsTextPaintStyle::GetSelectionStyle(int32_t aIndex)
    4170             : {
    4171           0 :   InitSelectionStyle(aIndex);
    4172           0 :   return &mSelectionStyle[aIndex];
    4173             : }
    4174             : 
    4175             : struct StyleIDs {
    4176             :   LookAndFeel::ColorID mForeground, mBackground, mLine;
    4177             :   LookAndFeel::IntID mLineStyle;
    4178             :   LookAndFeel::FloatID mLineRelativeSize;
    4179             : };
    4180             : static StyleIDs SelectionStyleIDs[] = {
    4181             :   { LookAndFeel::eColorID_IMERawInputForeground,
    4182             :     LookAndFeel::eColorID_IMERawInputBackground,
    4183             :     LookAndFeel::eColorID_IMERawInputUnderline,
    4184             :     LookAndFeel::eIntID_IMERawInputUnderlineStyle,
    4185             :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    4186             :   { LookAndFeel::eColorID_IMESelectedRawTextForeground,
    4187             :     LookAndFeel::eColorID_IMESelectedRawTextBackground,
    4188             :     LookAndFeel::eColorID_IMESelectedRawTextUnderline,
    4189             :     LookAndFeel::eIntID_IMESelectedRawTextUnderlineStyle,
    4190             :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    4191             :   { LookAndFeel::eColorID_IMEConvertedTextForeground,
    4192             :     LookAndFeel::eColorID_IMEConvertedTextBackground,
    4193             :     LookAndFeel::eColorID_IMEConvertedTextUnderline,
    4194             :     LookAndFeel::eIntID_IMEConvertedTextUnderlineStyle,
    4195             :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    4196             :   { LookAndFeel::eColorID_IMESelectedConvertedTextForeground,
    4197             :     LookAndFeel::eColorID_IMESelectedConvertedTextBackground,
    4198             :     LookAndFeel::eColorID_IMESelectedConvertedTextUnderline,
    4199             :     LookAndFeel::eIntID_IMESelectedConvertedTextUnderline,
    4200             :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    4201             :   { LookAndFeel::eColorID_LAST_COLOR,
    4202             :     LookAndFeel::eColorID_LAST_COLOR,
    4203             :     LookAndFeel::eColorID_SpellCheckerUnderline,
    4204             :     LookAndFeel::eIntID_SpellCheckerUnderlineStyle,
    4205             :     LookAndFeel::eFloatID_SpellCheckerUnderlineRelativeSize }
    4206             : };
    4207             : 
    4208             : void
    4209           0 : nsTextPaintStyle::InitSelectionStyle(int32_t aIndex)
    4210             : {
    4211           0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "aIndex is invalid");
    4212           0 :   nsSelectionStyle* selectionStyle = &mSelectionStyle[aIndex];
    4213           0 :   if (selectionStyle->mInit)
    4214           0 :     return;
    4215             : 
    4216           0 :   StyleIDs* styleIDs = &SelectionStyleIDs[aIndex];
    4217             : 
    4218             :   nscolor foreColor, backColor;
    4219           0 :   if (styleIDs->mForeground == LookAndFeel::eColorID_LAST_COLOR) {
    4220           0 :     foreColor = NS_SAME_AS_FOREGROUND_COLOR;
    4221             :   } else {
    4222           0 :     foreColor = LookAndFeel::GetColor(styleIDs->mForeground);
    4223             :   }
    4224           0 :   if (styleIDs->mBackground == LookAndFeel::eColorID_LAST_COLOR) {
    4225           0 :     backColor = NS_TRANSPARENT;
    4226             :   } else {
    4227           0 :     backColor = LookAndFeel::GetColor(styleIDs->mBackground);
    4228             :   }
    4229             : 
    4230             :   // Convert special color to actual color
    4231           0 :   NS_ASSERTION(foreColor != NS_TRANSPARENT,
    4232             :                "foreColor cannot be NS_TRANSPARENT");
    4233           0 :   NS_ASSERTION(backColor != NS_SAME_AS_FOREGROUND_COLOR,
    4234             :                "backColor cannot be NS_SAME_AS_FOREGROUND_COLOR");
    4235           0 :   NS_ASSERTION(backColor != NS_40PERCENT_FOREGROUND_COLOR,
    4236             :                "backColor cannot be NS_40PERCENT_FOREGROUND_COLOR");
    4237             : 
    4238           0 :   if (mResolveColors) {
    4239           0 :     foreColor = GetResolvedForeColor(foreColor, GetTextColor(), backColor);
    4240             : 
    4241           0 :     if (NS_GET_A(backColor) > 0)
    4242           0 :       EnsureSufficientContrast(&foreColor, &backColor);
    4243             :   }
    4244             : 
    4245             :   nscolor lineColor;
    4246             :   float relativeSize;
    4247             :   uint8_t lineStyle;
    4248           0 :   GetSelectionUnderline(mPresContext, aIndex,
    4249           0 :                         &lineColor, &relativeSize, &lineStyle);
    4250             : 
    4251           0 :   if (mResolveColors)
    4252           0 :     lineColor = GetResolvedForeColor(lineColor, foreColor, backColor);
    4253             : 
    4254           0 :   selectionStyle->mTextColor       = foreColor;
    4255           0 :   selectionStyle->mBGColor         = backColor;
    4256           0 :   selectionStyle->mUnderlineColor  = lineColor;
    4257           0 :   selectionStyle->mUnderlineStyle  = lineStyle;
    4258           0 :   selectionStyle->mUnderlineRelativeSize = relativeSize;
    4259           0 :   selectionStyle->mInit            = true;
    4260             : }
    4261             : 
    4262             : /* static */ bool
    4263           0 : nsTextPaintStyle::GetSelectionUnderline(nsPresContext* aPresContext,
    4264             :                                         int32_t aIndex,
    4265             :                                         nscolor* aLineColor,
    4266             :                                         float* aRelativeSize,
    4267             :                                         uint8_t* aStyle)
    4268             : {
    4269           0 :   NS_ASSERTION(aPresContext, "aPresContext is null");
    4270           0 :   NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
    4271           0 :   NS_ASSERTION(aStyle, "aStyle is null");
    4272           0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
    4273             : 
    4274           0 :   StyleIDs& styleID = SelectionStyleIDs[aIndex];
    4275             : 
    4276           0 :   nscolor color = LookAndFeel::GetColor(styleID.mLine);
    4277           0 :   int32_t style = LookAndFeel::GetInt(styleID.mLineStyle);
    4278           0 :   if (style > NS_STYLE_TEXT_DECORATION_STYLE_MAX) {
    4279           0 :     NS_ERROR("Invalid underline style value is specified");
    4280           0 :     style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
    4281             :   }
    4282           0 :   float size = LookAndFeel::GetFloat(styleID.mLineRelativeSize);
    4283             : 
    4284           0 :   NS_ASSERTION(size, "selection underline relative size must be larger than 0");
    4285             : 
    4286           0 :   if (aLineColor) {
    4287           0 :     *aLineColor = color;
    4288             :   }
    4289           0 :   *aRelativeSize = size;
    4290           0 :   *aStyle = style;
    4291             : 
    4292           0 :   return style != NS_STYLE_TEXT_DECORATION_STYLE_NONE &&
    4293           0 :          color != NS_TRANSPARENT &&
    4294           0 :          size > 0.0f;
    4295             : }
    4296             : 
    4297             : bool
    4298           0 : nsTextPaintStyle::GetSelectionShadow(nsCSSShadowArray** aShadow)
    4299             : {
    4300           0 :   if (!InitSelectionColorsAndShadow()) {
    4301           0 :     return false;
    4302             :   }
    4303             : 
    4304           0 :   if (mHasSelectionShadow) {
    4305           0 :     *aShadow = mSelectionShadow;
    4306           0 :     return true;
    4307             :   }
    4308             : 
    4309           0 :   return false;
    4310             : }
    4311             : 
    4312           0 : inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor)
    4313             : {
    4314           0 :   nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor),
    4315             :                               NS_GET_G(aForeColor),
    4316             :                               NS_GET_B(aForeColor),
    4317             :                               (uint8_t)(255 * 0.4f));
    4318             :   // Don't use true alpha color for readability.
    4319           0 :   return NS_ComposeColors(aBackColor, foreColor);
    4320             : }
    4321             : 
    4322             : nscolor
    4323           0 : nsTextPaintStyle::GetResolvedForeColor(nscolor aColor,
    4324             :                                        nscolor aDefaultForeColor,
    4325             :                                        nscolor aBackColor)
    4326             : {
    4327           0 :   if (aColor == NS_SAME_AS_FOREGROUND_COLOR)
    4328           0 :     return aDefaultForeColor;
    4329             : 
    4330           0 :   if (aColor != NS_40PERCENT_FOREGROUND_COLOR)
    4331           0 :     return aColor;
    4332             : 
    4333             :   // Get actual background color
    4334           0 :   nscolor actualBGColor = aBackColor;
    4335           0 :   if (actualBGColor == NS_TRANSPARENT) {
    4336           0 :     InitCommonColors();
    4337           0 :     actualBGColor = mFrameBackgroundColor;
    4338             :   }
    4339           0 :   return Get40PercentColor(aDefaultForeColor, actualBGColor);
    4340             : }
    4341             : 
    4342             : //-----------------------------------------------------------------------------
    4343             : 
    4344             : #ifdef ACCESSIBILITY
    4345             : a11y::AccType
    4346           0 : nsTextFrame::AccessibleType()
    4347             : {
    4348           0 :   if (IsEmpty()) {
    4349             :     RenderedText text = GetRenderedText(0,
    4350             :         UINT32_MAX, TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
    4351           0 :         TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
    4352           0 :     if (text.mString.IsEmpty()) {
    4353           0 :       return a11y::eNoType;
    4354             :     }
    4355             :   }
    4356             : 
    4357           0 :   return a11y::eTextLeafType;
    4358             : }
    4359             : #endif
    4360             : 
    4361             : 
    4362             : //-----------------------------------------------------------------------------
    4363             : void
    4364          18 : nsTextFrame::Init(nsIContent*       aContent,
    4365             :                   nsContainerFrame* aParent,
    4366             :                   nsIFrame*         aPrevInFlow)
    4367             : {
    4368          18 :   NS_ASSERTION(!aPrevInFlow, "Can't be a continuation!");
    4369          18 :   NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT),
    4370             :                   "Bogus content!");
    4371             : 
    4372             :   // Remove any NewlineOffsetProperty or InFlowContentLengthProperty since they
    4373             :   // might be invalid if the content was modified while there was no frame
    4374          18 :   aContent->DeleteProperty(nsGkAtoms::newline);
    4375          18 :   if (PresContext()->BidiEnabled()) {
    4376           0 :     aContent->DeleteProperty(nsGkAtoms::flowlength);
    4377             :   }
    4378             : 
    4379             :   // Since our content has a frame now, this flag is no longer needed.
    4380          18 :   aContent->UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE);
    4381             : 
    4382             :   // We're not a continuing frame.
    4383             :   // mContentOffset = 0; not necessary since we get zeroed out at init
    4384          18 :   nsFrame::Init(aContent, aParent, aPrevInFlow);
    4385          18 : }
    4386             : 
    4387             : void
    4388           6 : nsTextFrame::ClearFrameOffsetCache()
    4389             : {
    4390             :   // See if we need to remove ourselves from the offset cache
    4391           6 :   if (GetStateBits() & TEXT_IN_OFFSET_CACHE) {
    4392           0 :     nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
    4393           0 :     if (primaryFrame) {
    4394             :       // The primary frame might be null here.  For example, nsLineBox::DeleteLineList
    4395             :       // just destroys the frames in order, which means that the primary frame is already
    4396             :       // dead if we're a continuing text frame, in which case, all of its properties are
    4397             :       // gone, and we don't need to worry about deleting this property here.
    4398           0 :       primaryFrame->DeleteProperty(OffsetToFrameProperty());
    4399             :     }
    4400           0 :     RemoveStateBits(TEXT_IN_OFFSET_CACHE);
    4401             :   }
    4402           6 : }
    4403             : 
    4404             : void
    4405           6 : nsTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
    4406             : {
    4407           6 :   ClearFrameOffsetCache();
    4408             : 
    4409             :   // We might want to clear NS_CREATE_FRAME_IF_NON_WHITESPACE or
    4410             :   // NS_REFRAME_IF_WHITESPACE on mContent here, since our parent frame
    4411             :   // type might be changing.  Not clear whether it's worth it.
    4412           6 :   ClearTextRuns();
    4413           6 :   if (mNextContinuation) {
    4414           0 :     mNextContinuation->SetPrevInFlow(nullptr);
    4415             :   }
    4416             :   // Let the base class destroy the frame
    4417           6 :   nsFrame::DestroyFrom(aDestructRoot);
    4418           6 : }
    4419             : 
    4420           0 : class nsContinuingTextFrame final : public nsTextFrame
    4421             : {
    4422             : public:
    4423           0 :   NS_DECL_FRAMEARENA_HELPERS(nsContinuingTextFrame)
    4424             : 
    4425             :   friend nsIFrame* NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
    4426             : 
    4427             :   void Init(nsIContent* aContent,
    4428             :             nsContainerFrame* aParent,
    4429             :             nsIFrame* aPrevInFlow) override;
    4430             : 
    4431             :   void DestroyFrom(nsIFrame* aDestructRoot) override;
    4432             : 
    4433           0 :   nsTextFrame* GetPrevContinuation() const override
    4434             :   {
    4435           0 :     return mPrevContinuation;
    4436             :   }
    4437           0 :   void SetPrevContinuation(nsIFrame* aPrevContinuation) override
    4438             :   {
    4439           0 :     NS_ASSERTION(!aPrevContinuation || Type() == aPrevContinuation->Type(),
    4440             :                  "setting a prev continuation with incorrect type!");
    4441           0 :     NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevContinuation, this),
    4442             :                   "creating a loop in continuation chain!");
    4443           0 :     mPrevContinuation = static_cast<nsTextFrame*>(aPrevContinuation);
    4444           0 :     RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
    4445           0 :   }
    4446           0 :   nsIFrame* GetPrevInFlowVirtual() const override { return GetPrevInFlow(); }
    4447           0 :   nsTextFrame* GetPrevInFlow() const
    4448             :   {
    4449           0 :     return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
    4450             :   }
    4451           0 :   void SetPrevInFlow(nsIFrame* aPrevInFlow) override
    4452             :   {
    4453           0 :     NS_ASSERTION(!aPrevInFlow || Type() == aPrevInFlow->Type(),
    4454             :                  "setting a prev in flow with incorrect type!");
    4455           0 :     NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevInFlow, this),
    4456             :                   "creating a loop in continuation chain!");
    4457           0 :     mPrevContinuation = static_cast<nsTextFrame*>(aPrevInFlow);
    4458           0 :     AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
    4459           0 :   }
    4460             :   nsIFrame* FirstInFlow() const override;
    4461             :   nsIFrame* FirstContinuation() const override;
    4462             : 
    4463             :   void AddInlineMinISize(gfxContext* aRenderingContext,
    4464             :                          InlineMinISizeData* aData) override;
    4465             :   void AddInlinePrefISize(gfxContext* aRenderingContext,
    4466             :                           InlinePrefISizeData* aData) override;
    4467             : 
    4468             : protected:
    4469           0 :   explicit nsContinuingTextFrame(nsStyleContext* aContext)
    4470           0 :     : nsTextFrame(aContext, kClassID)
    4471           0 :   {}
    4472             : 
    4473             :   nsTextFrame* mPrevContinuation;
    4474             : };
    4475             : 
    4476             : void
    4477           0 : nsContinuingTextFrame::Init(nsIContent*       aContent,
    4478             :                             nsContainerFrame* aParent,
    4479             :                             nsIFrame*         aPrevInFlow)
    4480             : {
    4481           0 :   NS_ASSERTION(aPrevInFlow, "Must be a continuation!");
    4482             :   // NOTE: bypassing nsTextFrame::Init!!!
    4483           0 :   nsFrame::Init(aContent, aParent, aPrevInFlow);
    4484             : 
    4485           0 :   nsTextFrame* prev = static_cast<nsTextFrame*>(aPrevInFlow);
    4486           0 :   nsTextFrame* nextContinuation = prev->GetNextContinuation();
    4487             :   // Hook the frame into the flow
    4488           0 :   SetPrevInFlow(aPrevInFlow);
    4489           0 :   aPrevInFlow->SetNextInFlow(this);
    4490           0 :   mContentOffset = prev->GetContentOffset() + prev->GetContentLengthHint();
    4491           0 :   NS_ASSERTION(mContentOffset < int32_t(aContent->GetText()->GetLength()),
    4492             :                "Creating ContinuingTextFrame, but there is no more content");
    4493           0 :   if (prev->StyleContext() != StyleContext()) {
    4494             :     // We're taking part of prev's text, and its style may be different
    4495             :     // so clear its textrun which may no longer be valid (and don't set ours)
    4496           0 :     prev->ClearTextRuns();
    4497             :   } else {
    4498           0 :     float inflation = prev->GetFontSizeInflation();
    4499           0 :     SetFontSizeInflation(inflation);
    4500           0 :     mTextRun = prev->GetTextRun(nsTextFrame::eInflated);
    4501           0 :     if (inflation != 1.0f) {
    4502             :       gfxTextRun *uninflatedTextRun =
    4503           0 :         prev->GetTextRun(nsTextFrame::eNotInflated);
    4504           0 :       if (uninflatedTextRun) {
    4505           0 :         SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
    4506             :       }
    4507             :     }
    4508             :   }
    4509           0 :   if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
    4510           0 :     FrameBidiData bidiData = aPrevInFlow->GetBidiData();
    4511           0 :     bidiData.precedingControl = kBidiLevelNone;
    4512           0 :     SetProperty(BidiDataProperty(), bidiData);
    4513             : 
    4514           0 :     if (nextContinuation) {
    4515           0 :       SetNextContinuation(nextContinuation);
    4516           0 :       nextContinuation->SetPrevContinuation(this);
    4517             :       // Adjust next-continuations' content offset as needed.
    4518           0 :       while (nextContinuation &&
    4519           0 :              nextContinuation->GetContentOffset() < mContentOffset) {
    4520             : #ifdef DEBUG
    4521           0 :         FrameBidiData nextBidiData = nextContinuation->GetBidiData();
    4522           0 :         NS_ASSERTION(bidiData.embeddingLevel == nextBidiData.embeddingLevel &&
    4523             :                      bidiData.baseLevel == nextBidiData.baseLevel,
    4524             :                      "stealing text from different type of BIDI continuation");
    4525           0 :         MOZ_ASSERT(nextBidiData.precedingControl == kBidiLevelNone,
    4526             :                    "There shouldn't be any virtual bidi formatting character "
    4527             :                    "between continuations");
    4528             : #endif
    4529           0 :         nextContinuation->mContentOffset = mContentOffset;
    4530           0 :         nextContinuation = nextContinuation->GetNextContinuation();
    4531             :       }
    4532             :     }
    4533           0 :     mState |= NS_FRAME_IS_BIDI;
    4534             :   } // prev frame is bidi
    4535           0 : }
    4536             : 
    4537             : void
    4538           0 : nsContinuingTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
    4539             : {
    4540           0 :   ClearFrameOffsetCache();
    4541             : 
    4542             :   // The text associated with this frame will become associated with our
    4543             :   // prev-continuation. If that means the text has changed style, then
    4544             :   // we need to wipe out the text run for the text.
    4545             :   // Note that mPrevContinuation can be null if we're destroying the whole
    4546             :   // frame chain from the start to the end.
    4547             :   // If this frame is mentioned in the userData for a textrun (say
    4548             :   // because there's a direction change at the start of this frame), then
    4549             :   // we have to clear the textrun because we're going away and the
    4550             :   // textrun had better not keep a dangling reference to us.
    4551           0 :   if (IsInTextRunUserData() ||
    4552           0 :       (mPrevContinuation &&
    4553           0 :        mPrevContinuation->StyleContext() != StyleContext())) {
    4554           0 :     ClearTextRuns();
    4555             :     // Clear the previous continuation's text run also, so that it can rebuild
    4556             :     // the text run to include our text.
    4557           0 :     if (mPrevContinuation) {
    4558           0 :       mPrevContinuation->ClearTextRuns();
    4559             :     }
    4560             :   }
    4561           0 :   nsSplittableFrame::RemoveFromFlow(this);
    4562             :   // Let the base class destroy the frame
    4563           0 :   nsFrame::DestroyFrom(aDestructRoot);
    4564           0 : }
    4565             : 
    4566             : nsIFrame*
    4567           0 : nsContinuingTextFrame::FirstInFlow() const
    4568             : {
    4569             :   // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
    4570             :   nsIFrame *firstInFlow,
    4571             :            *previous = const_cast<nsIFrame*>
    4572           0 :                                  (static_cast<const nsIFrame*>(this));
    4573           0 :   do {
    4574           0 :     firstInFlow = previous;
    4575           0 :     previous = firstInFlow->GetPrevInFlow();
    4576           0 :   } while (previous);
    4577           0 :   MOZ_ASSERT(firstInFlow, "post-condition failed");
    4578           0 :   return firstInFlow;
    4579             : }
    4580             : 
    4581             : nsIFrame*
    4582           0 : nsContinuingTextFrame::FirstContinuation() const
    4583             : {
    4584             :   // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
    4585             :   nsIFrame *firstContinuation,
    4586             :   *previous = const_cast<nsIFrame*>
    4587           0 :                         (static_cast<const nsIFrame*>(mPrevContinuation));
    4588             : 
    4589           0 :   NS_ASSERTION(previous, "How can an nsContinuingTextFrame be the first continuation?");
    4590             : 
    4591           0 :   do {
    4592           0 :     firstContinuation = previous;
    4593           0 :     previous = firstContinuation->GetPrevContinuation();
    4594           0 :   } while (previous);
    4595           0 :   MOZ_ASSERT(firstContinuation, "post-condition failed");
    4596           0 :   return firstContinuation;
    4597             : }
    4598             : 
    4599             : // XXX Do we want to do all the work for the first-in-flow or do the
    4600             : // work for each part?  (Be careful of first-letter / first-line, though,
    4601             : // especially first-line!)  Doing all the work on the first-in-flow has
    4602             : // the advantage of avoiding the potential for incremental reflow bugs,
    4603             : // but depends on our maintining the frame tree in reasonable ways even
    4604             : // for edge cases (block-within-inline splits, nextBidi, etc.)
    4605             : 
    4606             : // XXX We really need to make :first-letter happen during frame
    4607             : // construction.
    4608             : 
    4609             : // Needed for text frames in XUL.
    4610             : /* virtual */ nscoord
    4611           0 : nsTextFrame::GetMinISize(gfxContext *aRenderingContext)
    4612             : {
    4613           0 :   return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
    4614             : }
    4615             : 
    4616             : // Needed for text frames in XUL.
    4617             : /* virtual */ nscoord
    4618           0 : nsTextFrame::GetPrefISize(gfxContext *aRenderingContext)
    4619             : {
    4620           0 :   return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
    4621             : }
    4622             : 
    4623             : /* virtual */ void
    4624           0 : nsContinuingTextFrame::AddInlineMinISize(gfxContext *aRenderingContext,
    4625             :                                          InlineMinISizeData *aData)
    4626             : {
    4627             :   // Do nothing, since the first-in-flow accounts for everything.
    4628           0 :   return;
    4629             : }
    4630             : 
    4631             : /* virtual */ void
    4632           0 : nsContinuingTextFrame::AddInlinePrefISize(gfxContext *aRenderingContext,
    4633             :                                           InlinePrefISizeData *aData)
    4634             : {
    4635             :   // Do nothing, since the first-in-flow accounts for everything.
    4636           0 :   return;
    4637             : }
    4638             : 
    4639             : //----------------------------------------------------------------------
    4640             : 
    4641             : #if defined(DEBUG_rbs) || defined(DEBUG_bzbarsky)
    4642             : static void
    4643             : VerifyNotDirty(nsFrameState state)
    4644             : {
    4645             :   bool isZero = state & NS_FRAME_FIRST_REFLOW;
    4646             :   bool isDirty = state & NS_FRAME_IS_DIRTY;
    4647             :   if (!isZero && isDirty)
    4648             :     NS_WARNING("internal offsets may be out-of-sync");
    4649             : }
    4650             : #define DEBUG_VERIFY_NOT_DIRTY(state) \
    4651             : VerifyNotDirty(state)
    4652             : #else
    4653             : #define DEBUG_VERIFY_NOT_DIRTY(state)
    4654             : #endif
    4655             : 
    4656             : nsIFrame*
    4657          18 : NS_NewTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    4658             : {
    4659          18 :   return new (aPresShell) nsTextFrame(aContext);
    4660             : }
    4661             : 
    4662          18 : NS_IMPL_FRAMEARENA_HELPERS(nsTextFrame)
    4663             : 
    4664             : nsIFrame*
    4665           0 : NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    4666             : {
    4667           0 :   return new (aPresShell) nsContinuingTextFrame(aContext);
    4668             : }
    4669             : 
    4670           0 : NS_IMPL_FRAMEARENA_HELPERS(nsContinuingTextFrame)
    4671             : 
    4672           6 : nsTextFrame::~nsTextFrame()
    4673             : {
    4674           6 : }
    4675             : 
    4676             : nsresult
    4677           0 : nsTextFrame::GetCursor(const nsPoint& aPoint,
    4678             :                        nsIFrame::Cursor& aCursor)
    4679             : {
    4680           0 :   FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
    4681           0 :   if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
    4682           0 :     if (!IsSelectable(nullptr)) {
    4683           0 :       aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
    4684             :     } else {
    4685           0 :       aCursor.mCursor = GetWritingMode().IsVertical()
    4686           0 :         ? NS_STYLE_CURSOR_VERTICAL_TEXT : NS_STYLE_CURSOR_TEXT;
    4687             :     }
    4688           0 :     return NS_OK;
    4689             :   } else {
    4690           0 :     return nsFrame::GetCursor(aPoint, aCursor);
    4691             :   }
    4692             : }
    4693             : 
    4694             : nsTextFrame*
    4695          48 : nsTextFrame::LastInFlow() const
    4696             : {
    4697          48 :   nsTextFrame* lastInFlow = const_cast<nsTextFrame*>(this);
    4698          48 :   while (lastInFlow->GetNextInFlow())  {
    4699           0 :     lastInFlow = lastInFlow->GetNextInFlow();
    4700             :   }
    4701          48 :   MOZ_ASSERT(lastInFlow, "post-condition failed");
    4702          48 :   return lastInFlow;
    4703             : }
    4704             : 
    4705             : nsTextFrame*
    4706           3 : nsTextFrame::LastContinuation() const
    4707             : {
    4708           3 :   nsTextFrame* lastContinuation = const_cast<nsTextFrame*>(this);
    4709           3 :   while (lastContinuation->mNextContinuation)  {
    4710           0 :     lastContinuation = lastContinuation->mNextContinuation;
    4711             :   }
    4712           3 :   MOZ_ASSERT(lastContinuation, "post-condition failed");
    4713           3 :   return lastContinuation;
    4714             : }
    4715             : 
    4716             : void
    4717          41 : nsTextFrame::InvalidateFrame(uint32_t aDisplayItemKey)
    4718             : {
    4719          41 :   if (nsSVGUtils::IsInSVGTextSubtree(this)) {
    4720             :     nsIFrame* svgTextFrame = nsLayoutUtils::GetClosestFrameOfType(
    4721           0 :       GetParent(), LayoutFrameType::SVGText);
    4722           0 :     svgTextFrame->InvalidateFrame();
    4723           0 :     return;
    4724             :   }
    4725          41 :   nsFrame::InvalidateFrame(aDisplayItemKey);
    4726             : }
    4727             : 
    4728             : void
    4729           0 : nsTextFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
    4730             : {
    4731           0 :   if (nsSVGUtils::IsInSVGTextSubtree(this)) {
    4732             :     nsIFrame* svgTextFrame = nsLayoutUtils::GetClosestFrameOfType(
    4733           0 :       GetParent(), LayoutFrameType::SVGText);
    4734           0 :     svgTextFrame->InvalidateFrame();
    4735           0 :     return;
    4736             :   }
    4737           0 :   nsFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
    4738             : }
    4739             : 
    4740             : gfxTextRun*
    4741           0 : nsTextFrame::GetUninflatedTextRun()
    4742             : {
    4743           0 :   return GetProperty(UninflatedTextRunProperty());
    4744             : }
    4745             : 
    4746             : void
    4747          21 : nsTextFrame::SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
    4748             :                         float aInflation)
    4749             : {
    4750          21 :   NS_ASSERTION(aTextRun, "must have text run");
    4751             : 
    4752             :   // Our inflated text run is always stored in mTextRun.  In the cases
    4753             :   // where our current inflation is not 1.0, however, we store two text
    4754             :   // runs, and the uninflated one goes in a frame property.  We never
    4755             :   // store a single text run in both.
    4756          21 :   if (aWhichTextRun == eInflated) {
    4757           2 :     if (HasFontSizeInflation() && aInflation == 1.0f) {
    4758             :       // FIXME: Probably shouldn't do this within each SetTextRun
    4759             :       // method, but it doesn't hurt.
    4760           0 :       ClearTextRun(nullptr, nsTextFrame::eNotInflated);
    4761             :     }
    4762           2 :     SetFontSizeInflation(aInflation);
    4763             :   } else {
    4764          19 :     MOZ_ASSERT(aInflation == 1.0f, "unexpected inflation");
    4765          19 :     if (HasFontSizeInflation()) {
    4766             :       // Setting the property will not automatically increment the textrun's
    4767             :       // reference count, so we need to do it here.
    4768           0 :       aTextRun->AddRef();
    4769           0 :       SetProperty(UninflatedTextRunProperty(), aTextRun);
    4770           0 :       return;
    4771             :     }
    4772             :     // fall through to setting mTextRun
    4773             :   }
    4774             : 
    4775          21 :   mTextRun = aTextRun;
    4776             : 
    4777             :   // FIXME: Add assertions testing the relationship between
    4778             :   // GetFontSizeInflation() and whether we have an uninflated text run
    4779             :   // (but be aware that text runs can go away).
    4780             : }
    4781             : 
    4782             : bool
    4783           9 : nsTextFrame::RemoveTextRun(gfxTextRun* aTextRun)
    4784             : {
    4785           9 :   if (aTextRun == mTextRun) {
    4786           9 :     mTextRun = nullptr;
    4787           9 :     return true;
    4788             :   }
    4789           0 :   if ((GetStateBits() & TEXT_HAS_FONT_INFLATION) &&
    4790           0 :       GetProperty(UninflatedTextRunProperty()) == aTextRun) {
    4791           0 :     DeleteProperty(UninflatedTextRunProperty());
    4792           0 :     return true;
    4793             :   }
    4794           0 :   return false;
    4795             : }
    4796             : 
    4797             : void
    4798          17 : nsTextFrame::ClearTextRun(nsTextFrame* aStartContinuation,
    4799             :                           TextRunType aWhichTextRun)
    4800             : {
    4801          26 :   RefPtr<gfxTextRun> textRun = GetTextRun(aWhichTextRun);
    4802          17 :   if (!textRun) {
    4803           8 :     return;
    4804             :   }
    4805             : 
    4806          18 :   DebugOnly<bool> checkmTextrun = textRun == mTextRun;
    4807           9 :   UnhookTextRunFromFrames(textRun, aStartContinuation);
    4808           9 :   MOZ_ASSERT(checkmTextrun ? !mTextRun
    4809             :                            : !GetProperty(UninflatedTextRunProperty()));
    4810             : }
    4811             : 
    4812             : void
    4813           0 : nsTextFrame::DisconnectTextRuns()
    4814             : {
    4815           0 :   MOZ_ASSERT(!IsInTextRunUserData(),
    4816             :              "Textrun mentions this frame in its user data so we can't just disconnect");
    4817           0 :   mTextRun = nullptr;
    4818           0 :   if ((GetStateBits() & TEXT_HAS_FONT_INFLATION)) {
    4819           0 :     DeleteProperty(UninflatedTextRunProperty());
    4820             :   }
    4821           0 : }
    4822             : 
    4823             : nsresult
    4824           0 : nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
    4825             : {
    4826           0 :   mContent->DeleteProperty(nsGkAtoms::newline);
    4827           0 :   if (PresContext()->BidiEnabled()) {
    4828           0 :     mContent->DeleteProperty(nsGkAtoms::flowlength);
    4829             :   }
    4830             : 
    4831             :   // Find the first frame whose text has changed. Frames that are entirely
    4832             :   // before the text change are completely unaffected.
    4833             :   nsTextFrame* next;
    4834           0 :   nsTextFrame* textFrame = this;
    4835             :   while (true) {
    4836           0 :     next = textFrame->GetNextContinuation();
    4837           0 :     if (!next || next->GetContentOffset() > int32_t(aInfo->mChangeStart))
    4838           0 :       break;
    4839           0 :     textFrame = next;
    4840             :   }
    4841             : 
    4842           0 :   int32_t endOfChangedText = aInfo->mChangeStart + aInfo->mReplaceLength;
    4843           0 :   nsTextFrame* lastDirtiedFrame = nullptr;
    4844             : 
    4845           0 :   nsIPresShell* shell = PresContext()->GetPresShell();
    4846           0 :   do {
    4847             :     // textFrame contained deleted text (or the insertion point,
    4848             :     // if this was a pure insertion).
    4849           0 :     textFrame->mState &= ~TEXT_WHITESPACE_FLAGS;
    4850           0 :     textFrame->ClearTextRuns();
    4851           0 :     if (!lastDirtiedFrame ||
    4852           0 :         lastDirtiedFrame->GetParent() != textFrame->GetParent()) {
    4853             :       // Ask the parent frame to reflow me.
    4854             :       shell->FrameNeedsReflow(textFrame, nsIPresShell::eStyleChange,
    4855           0 :                               NS_FRAME_IS_DIRTY);
    4856           0 :       lastDirtiedFrame = textFrame;
    4857             :     } else {
    4858             :       // if the parent is a block, we're cheating here because we should
    4859             :       // be marking our line dirty, but we're not. nsTextFrame::SetLength
    4860             :       // will do that when it gets called during reflow.
    4861           0 :       textFrame->AddStateBits(NS_FRAME_IS_DIRTY);
    4862             :     }
    4863           0 :     textFrame->InvalidateFrame();
    4864             : 
    4865             :     // Below, frames that start after the deleted text will be adjusted so that
    4866             :     // their offsets move with the trailing unchanged text. If this change
    4867             :     // deletes more text than it inserts, those frame offsets will decrease.
    4868             :     // We need to maintain the invariant that mContentOffset is non-decreasing
    4869             :     // along the continuation chain. So we need to ensure that frames that
    4870             :     // started in the deleted text are all still starting before the
    4871             :     // unchanged text.
    4872           0 :     if (textFrame->mContentOffset > endOfChangedText) {
    4873           0 :       textFrame->mContentOffset = endOfChangedText;
    4874             :     }
    4875             : 
    4876           0 :     textFrame = textFrame->GetNextContinuation();
    4877           0 :   } while (textFrame && textFrame->GetContentOffset() < int32_t(aInfo->mChangeEnd));
    4878             : 
    4879             :   // This is how much the length of the string changed by --- i.e.,
    4880             :   // how much the trailing unchanged text moved.
    4881             :   int32_t sizeChange =
    4882           0 :     aInfo->mChangeStart + aInfo->mReplaceLength - aInfo->mChangeEnd;
    4883             : 
    4884           0 :   if (sizeChange) {
    4885             :     // Fix the offsets of the text frames that start in the trailing
    4886             :     // unchanged text.
    4887           0 :     while (textFrame) {
    4888           0 :       textFrame->mContentOffset += sizeChange;
    4889             :       // XXX we could rescue some text runs by adjusting their user data
    4890             :       // to reflect the change in DOM offsets
    4891           0 :       textFrame->ClearTextRuns();
    4892           0 :       textFrame = textFrame->GetNextContinuation();
    4893             :     }
    4894             :   }
    4895             : 
    4896           0 :   return NS_OK;
    4897             : }
    4898             : 
    4899             : class nsDisplayText : public nsCharClipDisplayItem {
    4900             : public:
    4901             :   nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
    4902             :                 const Maybe<bool>& aIsSelected);
    4903             : #ifdef NS_BUILD_REFCNT_LOGGING
    4904         146 :   virtual ~nsDisplayText() {
    4905          73 :     MOZ_COUNT_DTOR(nsDisplayText);
    4906          73 :   }
    4907             : #endif
    4908             : 
    4909         324 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
    4910             :                            bool* aSnap) override {
    4911         324 :     *aSnap = false;
    4912         324 :     return mBounds;
    4913             :   }
    4914           0 :   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    4915             :                        HitTestState* aState,
    4916             :                        nsTArray<nsIFrame*> *aOutFrames) override {
    4917           0 :     MOZ_ASSERT(mMergedFrames.IsEmpty());
    4918           0 :     if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
    4919           0 :       aOutFrames->AppendElement(mFrame);
    4920             :     }
    4921           0 :   }
    4922             :   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
    4923             :                                    LayerManager* aManager,
    4924             :                                    const ContainerLayerParameters& aParameters) override;
    4925             :   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
    4926             :                                              LayerManager* aManager,
    4927             :                                              const ContainerLayerParameters& aContainerParameters) override;
    4928             :   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
    4929             :                                        const StackingContextHelper& aSc,
    4930             :                                        nsTArray<WebRenderParentCommand>& aParentCommands,
    4931             :                                        WebRenderLayerManager* aManager,
    4932             :                                        nsDisplayListBuilder* aDisplayListBuilder) override;
    4933             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    4934             :                      gfxContext* aCtx) override;
    4935         547 :   NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)
    4936             : 
    4937           6 :   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
    4938             :   {
    4939           6 :     if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
    4940             :       // On OS X, web authors can turn off subpixel text rendering using the
    4941             :       // CSS property -moz-osx-font-smoothing. If they do that, we don't need
    4942             :       // to use component alpha layers for the affected text.
    4943           0 :       if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
    4944           0 :         return nsRect();
    4945             :       }
    4946             :     }
    4947             :     bool snap;
    4948           6 :     return GetBounds(aBuilder, &snap);
    4949             :   }
    4950             : 
    4951             :   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
    4952             : 
    4953             :   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
    4954             :                                          const nsDisplayItemGeometry* aGeometry,
    4955             :                                          nsRegion *aInvalidRegion) override;
    4956             : 
    4957           0 :   virtual void DisableComponentAlpha() override {
    4958           0 :     mDisableSubpixelAA = true;
    4959           0 :   }
    4960             : 
    4961             :   void RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording = false);
    4962             : 
    4963          74 :   bool CanApplyOpacity() const override
    4964             :   {
    4965          74 :     nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
    4966          74 :     if (f->IsSelected()) {
    4967           0 :       return false;
    4968             :     }
    4969             : 
    4970          74 :     const nsStyleText* textStyle = f->StyleText();
    4971          74 :     if (textStyle->mTextShadow) {
    4972           0 :       return false;
    4973             :     }
    4974             : 
    4975         148 :     nsTextFrame::TextDecorations decorations;
    4976          74 :     f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, decorations);
    4977          74 :     if (decorations.HasDecorationLines()) {
    4978           0 :       return false;
    4979             :     }
    4980             : 
    4981          74 :     return true;
    4982             :   }
    4983             : 
    4984          37 :   void ApplyOpacity(nsDisplayListBuilder* aBuilder,
    4985             :                     float aOpacity,
    4986             :                     const DisplayItemClipChain* aClip) override
    4987             :   {
    4988          37 :     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
    4989          37 :     mOpacity = aOpacity;
    4990          37 :     IntersectClip(aBuilder, aClip);
    4991          37 :   }
    4992             : 
    4993           0 :   void WriteDebugInfo(std::stringstream& aStream) override
    4994             :   {
    4995             : #ifdef DEBUG
    4996           0 :     aStream << " (\"";
    4997             : 
    4998           0 :     nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
    4999           0 :     nsCString buf;
    5000             :     int32_t totalContentLength;
    5001           0 :     f->ToCString(buf, &totalContentLength);
    5002             : 
    5003           0 :     for (nsTextFrame* f : mMergedFrames) {
    5004           0 :       f->ToCString(buf, &totalContentLength);
    5005             :     }
    5006           0 :     aStream << buf.get() << "\")";
    5007             : #endif
    5008           0 :   }
    5009             : 
    5010          73 :   void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) override
    5011             :   {
    5012          73 :     aFrames->AppendElements(mMergedFrames);
    5013          73 :   }
    5014             : 
    5015          31 :   bool TryMerge(nsDisplayItem* aItem) override {
    5016          31 :     if (aItem->GetType() != TYPE_TEXT)
    5017          31 :       return false;
    5018           0 :     if (aItem->GetClipChain() != GetClipChain())
    5019           0 :       return false;
    5020             : 
    5021           0 :     nsDisplayText* other = static_cast<nsDisplayText*>(aItem);
    5022           0 :     if (!mFont || !other->mFont || mFont != other->mFont) {
    5023           0 :       return false;
    5024             :     }
    5025           0 :     if (mOpacity != other->mOpacity) {
    5026           0 :       return false;
    5027             :     }
    5028             : 
    5029           0 :     mBounds.UnionRect(mBounds, other->mBounds);
    5030           0 :     mVisibleRect.UnionRect(mVisibleRect, other->mVisibleRect);
    5031           0 :     mMergedFrames.AppendElement(static_cast<nsTextFrame*>(other->mFrame));
    5032           0 :     mMergedFrames.AppendElements(mozilla::Move(other->mMergedFrames));
    5033             : 
    5034           0 :     for (GlyphArray& g : other->mGlyphs) {
    5035           0 :       GlyphArray* append = mGlyphs.AppendElement();
    5036           0 :       append->color() = g.color();
    5037           0 :       append->glyphs().SwapElements(g.glyphs());
    5038             :     }
    5039           0 :     return true;
    5040             : }
    5041             : 
    5042             :   RefPtr<ScaledFont> mFont;
    5043             :   nsTArray<GlyphArray> mGlyphs;
    5044             :   nsTArray<nsTextFrame*> mMergedFrames;
    5045             :   nsRect mBounds;
    5046             : 
    5047             :   float mOpacity;
    5048             :   bool mDisableSubpixelAA;
    5049             : };
    5050             : 
    5051          30 : class nsDisplayTextGeometry : public nsCharClipGeometry
    5052             : {
    5053             : public:
    5054          14 :   nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
    5055          14 :     : nsCharClipGeometry(aItem, aBuilder)
    5056          14 :     , mOpacity(aItem->mOpacity)
    5057             :   {
    5058          14 :     nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame());
    5059          14 :     f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, mDecorations);
    5060          14 :   }
    5061             : 
    5062             :   /**
    5063             :    * We store the computed text decorations here since they are
    5064             :    * computed using style data from parent frames. Any changes to these
    5065             :    * styles will only invalidate the parent frame and not this frame.
    5066             :    */
    5067             :   nsTextFrame::TextDecorations mDecorations;
    5068             :   float mOpacity;
    5069             : };
    5070             : 
    5071             : nsDisplayItemGeometry*
    5072          14 : nsDisplayText::AllocateGeometry(nsDisplayListBuilder* aBuilder)
    5073             : {
    5074          14 :   return new nsDisplayTextGeometry(this, aBuilder);
    5075             : }
    5076             : 
    5077             : void
    5078          61 : nsDisplayText::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
    5079             :                                          const nsDisplayItemGeometry* aGeometry,
    5080             :                                          nsRegion *aInvalidRegion)
    5081             : {
    5082          61 :   const nsDisplayTextGeometry* geometry = static_cast<const nsDisplayTextGeometry*>(aGeometry);
    5083          61 :   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
    5084             : 
    5085         122 :   nsTextFrame::TextDecorations decorations;
    5086          61 :   f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, decorations);
    5087             : 
    5088             :   bool snap;
    5089         122 :   nsRect newRect = geometry->mBounds;
    5090         122 :   nsRect oldRect = GetBounds(aBuilder, &snap);
    5091         246 :   if (decorations != geometry->mDecorations ||
    5092         122 :       mVisIStartEdge != geometry->mVisIStartEdge ||
    5093         122 :       mVisIEndEdge != geometry->mVisIEndEdge ||
    5094         179 :       !oldRect.IsEqualInterior(newRect) ||
    5095         419 :       !geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
    5096          59 :       mOpacity != geometry->mOpacity) {
    5097           2 :     aInvalidRegion->Or(oldRect, newRect);
    5098             :   }
    5099          61 : }
    5100             : 
    5101           0 : NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TextCombineScaleFactorProperty, float)
    5102             : 
    5103             : static float
    5104           0 : GetTextCombineScaleFactor(nsTextFrame* aFrame)
    5105             : {
    5106           0 :   float factor = aFrame->GetProperty(TextCombineScaleFactorProperty());
    5107           0 :   return factor ? factor : 1.0f;
    5108             : }
    5109             : 
    5110          73 : nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
    5111          73 :                              const Maybe<bool>& aIsSelected)
    5112             :   : nsCharClipDisplayItem(aBuilder, aFrame)
    5113             :   , mOpacity(1.0f)
    5114          73 :   , mDisableSubpixelAA(false)
    5115             : {
    5116          73 :   MOZ_COUNT_CTOR(nsDisplayText);
    5117          73 :   mIsFrameSelected = aIsSelected;
    5118             : 
    5119          73 :   mBounds = mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
    5120             :     // Bug 748228
    5121          73 :   mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
    5122             : 
    5123          73 :   if (ShouldUseAdvancedLayer(aBuilder->GetWidgetLayerManager(), gfxPrefs::LayersAllowTextLayers)) {
    5124             :     RefPtr<DrawTargetCapture> capture =
    5125           0 :       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreateCaptureDT(IntSize());
    5126           0 :     RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(capture);
    5127             : 
    5128             :     // TODO: Paint() checks mDisableSubpixelAA, we should too.
    5129           0 :     RenderToContext(captureCtx, aBuilder, true);
    5130             : 
    5131             :     // TODO: Ideally we'd re-use captureCtx in Paint() if we couldn't build
    5132             :     // a layer here. We have to deal with the problem that the ScreenReferenceDrawTarget
    5133             :     // might not be compatible with the DT used for layer rendering.
    5134             : 
    5135           0 :     GlyphArray* g = mGlyphs.AppendElement();
    5136           0 :     std::vector<Glyph> glyphs;
    5137           0 :     Color color;
    5138           0 :     if (!capture->ContainsOnlyColoredGlyphs(mFont, color, glyphs)
    5139           0 :         || !mFont
    5140           0 :         || !mFont->CanSerialize()) {
    5141           0 :       mFont = nullptr;
    5142           0 :       mGlyphs.Clear();
    5143             :     } else {
    5144           0 :       g->glyphs().SetLength(glyphs.size());
    5145           0 :       PodCopy(g->glyphs().Elements(), glyphs.data(), glyphs.size());
    5146           0 :       g->color() = color;
    5147             :     }
    5148             :   }
    5149          73 : }
    5150             : 
    5151             : LayerState
    5152          73 : nsDisplayText::GetLayerState(nsDisplayListBuilder* aBuilder,
    5153             :                              LayerManager* aManager,
    5154             :                              const ContainerLayerParameters& aParameters)
    5155             : {
    5156          73 :   if (mFont) {
    5157           0 :     return mozilla::LAYER_ACTIVE;
    5158             :   }
    5159          73 :   MOZ_ASSERT(mMergedFrames.IsEmpty());
    5160          73 :   return mozilla::LAYER_NONE;
    5161             : }
    5162             : 
    5163             : void
    5164          17 : nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
    5165             :                      gfxContext* aCtx) {
    5166          34 :   AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
    5167             : 
    5168          17 :   MOZ_ASSERT(mMergedFrames.IsEmpty());
    5169             : 
    5170             :   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
    5171          34 :                                                     mDisableSubpixelAA);
    5172          17 :   RenderToContext(aCtx, aBuilder);
    5173          17 : }
    5174             : 
    5175             : bool
    5176           0 : nsDisplayText::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
    5177             :                                        const StackingContextHelper& aSc,
    5178             :                                        nsTArray<WebRenderParentCommand>& aParentCommands,
    5179             :                                        WebRenderLayerManager* aManager,
    5180             :                                        nsDisplayListBuilder* aDisplayListBuilder)
    5181             : {
    5182           0 :   if (aManager->IsLayersFreeTransaction()) {
    5183           0 :     ContainerLayerParameters parameter;
    5184           0 :     if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
    5185           0 :       return false;
    5186             :     }
    5187             :   }
    5188             : 
    5189           0 :   if (mBounds.IsEmpty()) {
    5190           0 :     return true;
    5191             :   }
    5192             : 
    5193           0 :   auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
    5194             :   LayoutDeviceRect rect = LayoutDeviceRect::FromAppUnits(
    5195           0 :       mBounds, appUnitsPerDevPixel);
    5196           0 :   LayoutDeviceRect clipRect = rect;
    5197           0 :   if (GetClip().HasClip()) {
    5198             :     clipRect = LayoutDeviceRect::FromAppUnits(
    5199           0 :                 GetClip().GetClipRect(), appUnitsPerDevPixel);
    5200             :   }
    5201           0 :   aManager->WrBridge()->PushGlyphs(aBuilder, mGlyphs, mFont, aSc,
    5202           0 :                                    LayerRect::FromUnknownRect(rect.ToUnknownRect()),
    5203           0 :                                    LayerRect::FromUnknownRect(clipRect.ToUnknownRect()));
    5204             : 
    5205           0 :   return true;
    5206             : }
    5207             : 
    5208             : already_AddRefed<layers::Layer>
    5209           0 : nsDisplayText::BuildLayer(nsDisplayListBuilder* aBuilder,
    5210             :                           LayerManager* aManager,
    5211             :                           const ContainerLayerParameters& aContainerParameters)
    5212             : {
    5213             :   // We should have all the glyphs recorded now, build
    5214             :   // the TextLayer.
    5215             :   RefPtr<layers::TextLayer> layer = static_cast<layers::TextLayer*>
    5216           0 :     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
    5217           0 :   if (!layer) {
    5218           0 :     layer = aManager->CreateTextLayer();
    5219             :   }
    5220             : 
    5221           0 :   layer->SetGlyphs(Move(mGlyphs));
    5222           0 :   layer->SetScaledFont(mFont);
    5223             : 
    5224           0 :   auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
    5225             :   bool dummy;
    5226             :   const LayoutDeviceIntRect destBounds =
    5227           0 :           LayoutDeviceIntRect::FromAppUnitsToOutside(GetBounds(aBuilder, &dummy), A2D);
    5228           0 :   layer->SetBounds(IntRect(destBounds.x, destBounds.y, destBounds.width, destBounds.height));
    5229             : 
    5230           0 :   layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
    5231           0 :                                                       aContainerParameters.mOffset.y, 0));
    5232           0 :   return layer.forget();
    5233             : }
    5234             : 
    5235             : void
    5236          17 : nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording)
    5237             : {
    5238          17 :   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
    5239             : 
    5240             :   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
    5241             :   // antialiased pixels beyond the measured text extents.
    5242             :   // This is temporary until we do this in the actual calculation of text extents.
    5243          17 :   auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
    5244             :   LayoutDeviceRect extraVisible =
    5245          17 :     LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D);
    5246          17 :   extraVisible.Inflate(1);
    5247             : 
    5248          34 :   gfxRect pixelVisible(extraVisible.x, extraVisible.y,
    5249          51 :                        extraVisible.width, extraVisible.height);
    5250          17 :   pixelVisible.Inflate(2);
    5251          17 :   pixelVisible.RoundOut();
    5252             : 
    5253          34 :   bool willClip = !aBuilder->IsForGenerateGlyphMask() &&
    5254          34 :                   !aBuilder->IsForPaintingSelectionBG() &&
    5255          34 :                   !aIsRecording;
    5256          17 :   if (willClip) {
    5257          17 :     aCtx->NewPath();
    5258          17 :     aCtx->Rectangle(pixelVisible);
    5259          17 :     aCtx->Clip();
    5260             :   }
    5261             : 
    5262          17 :   NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
    5263          17 :   NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
    5264             : 
    5265          34 :   gfxContextMatrixAutoSaveRestore matrixSR;
    5266             : 
    5267          17 :   nsPoint framePt = ToReferenceFrame();
    5268          17 :   if (f->StyleContext()->IsTextCombined()) {
    5269           0 :     float scaleFactor = GetTextCombineScaleFactor(f);
    5270           0 :     if (scaleFactor != 1.0f) {
    5271           0 :       matrixSR.SetContext(aCtx);
    5272             :       // Setup matrix to compress text for text-combine-upright if
    5273             :       // necessary. This is done here because we want selection be
    5274             :       // compressed at the same time as text.
    5275           0 :       gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
    5276           0 :       gfxMatrix mat = aCtx->CurrentMatrix()
    5277           0 :         .PreTranslate(pt).PreScale(scaleFactor, 1.0).PreTranslate(-pt);
    5278           0 :       aCtx->SetMatrix (mat);
    5279             :     }
    5280             :   }
    5281          17 :   nsTextFrame::PaintTextParams params(aCtx);
    5282          17 :   params.framePt = gfxPoint(framePt.x, framePt.y);
    5283          17 :   params.dirtyRect = extraVisible;
    5284             : 
    5285          17 :   if (aBuilder->IsForGenerateGlyphMask()) {
    5286           0 :     MOZ_ASSERT(!aBuilder->IsForPaintingSelectionBG());
    5287           0 :     params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
    5288          17 :   } else if (aBuilder->IsForPaintingSelectionBG()) {
    5289           0 :     params.state = nsTextFrame::PaintTextParams::PaintTextBGColor;
    5290             :   } else {
    5291          17 :     params.state = nsTextFrame::PaintTextParams::PaintText;
    5292             :   }
    5293             : 
    5294          17 :   f->PaintText(params, *this, mOpacity);
    5295             : 
    5296          17 :   if (willClip) {
    5297          17 :     aCtx->PopClip();
    5298             :   }
    5299          17 : }
    5300             : 
    5301             : void
    5302          73 : nsTextFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    5303             :                               const nsRect&           aDirtyRect,
    5304             :                               const nsDisplayListSet& aLists)
    5305             : {
    5306          73 :   if (!IsVisibleForPainting(aBuilder))
    5307           0 :     return;
    5308             : 
    5309          73 :   DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame");
    5310             : 
    5311          73 :   const nsStyleColor* sc = StyleColor();
    5312          73 :   const nsStyleText* st = StyleText();
    5313             :   bool isTextTransparent =
    5314          73 :     NS_GET_A(sc->CalcComplexColor(st->mWebkitTextFillColor)) == 0 &&
    5315          73 :     NS_GET_A(sc->CalcComplexColor(st->mWebkitTextStrokeColor)) == 0;
    5316         146 :   Maybe<bool> isSelected;
    5317         219 :   if (((GetStateBits() & TEXT_NO_RENDERED_GLYPHS) ||
    5318           0 :        (isTextTransparent && !StyleText()->HasTextShadow())) &&
    5319          73 :       aBuilder->IsForPainting() && !nsSVGUtils::IsInSVGTextSubtree(this)) {
    5320           0 :     isSelected.emplace(IsSelected());
    5321           0 :     if (!isSelected.value()) {
    5322           0 :       TextDecorations textDecs;
    5323           0 :       GetTextDecorations(PresContext(), eResolvedColors, textDecs);
    5324           0 :       if (!textDecs.HasDecorationLines()) {
    5325           0 :         return;
    5326             :       }
    5327             :     }
    5328             :   }
    5329             : 
    5330          73 :   aLists.Content()->AppendNewToTop(
    5331         146 :     new (aBuilder) nsDisplayText(aBuilder, this, isSelected));
    5332             : }
    5333             : 
    5334             : static nsIFrame*
    5335           0 : GetGeneratedContentOwner(nsIFrame* aFrame, bool* aIsBefore)
    5336             : {
    5337           0 :   *aIsBefore = false;
    5338           0 :   while (aFrame && (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
    5339           0 :     if (aFrame->StyleContext()->GetPseudo() == nsCSSPseudoElements::before) {
    5340           0 :       *aIsBefore = true;
    5341             :     }
    5342           0 :     aFrame = aFrame->GetParent();
    5343             :   }
    5344           0 :   return aFrame;
    5345             : }
    5346             : 
    5347             : UniquePtr<SelectionDetails>
    5348           2 : nsTextFrame::GetSelectionDetails()
    5349             : {
    5350           2 :   const nsFrameSelection* frameSelection = GetConstFrameSelection();
    5351           2 :   if (frameSelection->GetTableCellSelection()) {
    5352           0 :     return nullptr;
    5353             :   }
    5354           2 :   if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
    5355             :     UniquePtr<SelectionDetails> details =
    5356             :       frameSelection->LookUpSelection(mContent, GetContentOffset(),
    5357           4 :                                       GetContentLength(), false);
    5358           4 :     for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
    5359           2 :       sd->mStart += mContentOffset;
    5360           2 :       sd->mEnd += mContentOffset;
    5361             :     }
    5362           2 :     return details;
    5363             :   }
    5364             : 
    5365             :   // Check if the beginning or end of the element is selected, depending on
    5366             :   // whether we're :before content or :after content.
    5367             :   bool isBefore;
    5368           0 :   nsIFrame* owner = GetGeneratedContentOwner(this, &isBefore);
    5369           0 :   if (!owner || !owner->GetContent())
    5370           0 :     return nullptr;
    5371             : 
    5372             :   UniquePtr<SelectionDetails> details =
    5373             :     frameSelection->LookUpSelection(owner->GetContent(),
    5374           0 :         isBefore ? 0 : owner->GetContent()->GetChildCount(), 0, false);
    5375           0 :   for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
    5376             :     // The entire text is selected!
    5377           0 :     sd->mStart = GetContentOffset();
    5378           0 :     sd->mEnd = GetContentEnd();
    5379             :   }
    5380           0 :   return details;
    5381             : }
    5382             : 
    5383             : static void
    5384           0 : PaintSelectionBackground(DrawTarget& aDrawTarget,
    5385             :                          nscolor aColor,
    5386             :                          const LayoutDeviceRect& aDirtyRect,
    5387             :                          const LayoutDeviceRect& aRect,
    5388             :                          nsTextFrame::DrawPathCallbacks* aCallbacks)
    5389             : {
    5390           0 :   Rect rect = aRect.Intersect(aDirtyRect).ToUnknownRect();
    5391           0 :   MaybeSnapToDevicePixels(rect, aDrawTarget);
    5392             : 
    5393           0 :   if (aCallbacks) {
    5394           0 :     aCallbacks->NotifySelectionBackgroundNeedsFill(rect, aColor, aDrawTarget);
    5395             :   } else {
    5396           0 :     ColorPattern color(ToDeviceColor(aColor));
    5397           0 :     aDrawTarget.FillRect(rect, color);
    5398             :   }
    5399           0 : }
    5400             : 
    5401             : // Attempt to get the LineBaselineOffset property of aChildFrame
    5402             : // If not set, calculate this value for all child frames of aBlockFrame
    5403             : static nscoord
    5404           0 : LazyGetLineBaselineOffset(nsIFrame* aChildFrame, nsBlockFrame* aBlockFrame)
    5405             : {
    5406             :   bool offsetFound;
    5407           0 :   nscoord offset = aChildFrame->GetProperty(
    5408           0 :     nsIFrame::LineBaselineOffset(), &offsetFound);
    5409             : 
    5410           0 :   if (!offsetFound) {
    5411           0 :     for (nsBlockFrame::LineIterator line = aBlockFrame->LinesBegin(),
    5412           0 :                                     line_end = aBlockFrame->LinesEnd();
    5413             :          line != line_end; line++) {
    5414           0 :       if (line->IsInline()) {
    5415           0 :         int32_t n = line->GetChildCount();
    5416           0 :         nscoord lineBaseline = line->BStart() + line->GetLogicalAscent();
    5417           0 :         for (nsIFrame* lineFrame = line->mFirstChild;
    5418           0 :              n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
    5419           0 :           offset = lineBaseline - lineFrame->GetNormalPosition().y;
    5420           0 :           lineFrame->SetProperty(nsIFrame::LineBaselineOffset(), offset);
    5421             :         }
    5422             :       }
    5423             :     }
    5424           0 :     return aChildFrame->GetProperty(
    5425           0 :       nsIFrame::LineBaselineOffset(), &offsetFound);
    5426             :   } else {
    5427           0 :     return offset;
    5428             :   }
    5429             : }
    5430             : 
    5431           0 : static bool IsUnderlineRight(nsIFrame* aFrame)
    5432             : {
    5433           0 :   nsIAtom* langAtom = aFrame->StyleFont()->mLanguage;
    5434           0 :   if (!langAtom) {
    5435           0 :     return false;
    5436             :   }
    5437           0 :   nsAtomString langStr(langAtom);
    5438           0 :   return (StringBeginsWith(langStr, NS_LITERAL_STRING("ja")) ||
    5439           0 :           StringBeginsWith(langStr, NS_LITERAL_STRING("ko"))) &&
    5440           0 :          (langStr.Length() == 2 || langStr[2] == '-');
    5441             : }
    5442             : 
    5443             : void
    5444         167 : nsTextFrame::GetTextDecorations(
    5445             :                     nsPresContext* aPresContext,
    5446             :                     nsTextFrame::TextDecorationColorResolution aColorResolution,
    5447             :                     nsTextFrame::TextDecorations& aDecorations)
    5448             : {
    5449         167 :   const nsCompatibility compatMode = aPresContext->CompatibilityMode();
    5450             : 
    5451         167 :   bool useOverride = false;
    5452         167 :   nscolor overrideColor = NS_RGBA(0, 0, 0, 0);
    5453             : 
    5454         167 :   bool nearestBlockFound = false;
    5455             :   // Use writing mode of parent frame for orthogonal text frame to work.
    5456             :   // See comment in nsTextFrame::DrawTextRunAndDecorations.
    5457         167 :   WritingMode wm = GetParent()->GetWritingMode();
    5458         167 :   bool vertical = wm.IsVertical();
    5459             : 
    5460         167 :   nscoord ascent = GetLogicalBaseline(wm);
    5461             :   // physicalBlockStartOffset represents the offset from our baseline
    5462             :   // to f's physical block start, which is top in horizontal writing
    5463             :   // mode, and left in vertical writing modes, in our coordinate space.
    5464             :   // This physical block start is logical block start in most cases,
    5465             :   // but for vertical-rl, it is logical block end, and consequently in
    5466             :   // that case, it starts from the descent instead of ascent.
    5467             :   nscoord physicalBlockStartOffset =
    5468         167 :     wm.IsVerticalRL() ? GetSize().width - ascent : ascent;
    5469             :   // baselineOffset represents the offset from our baseline to f's baseline or
    5470             :   // the nearest block's baseline, in our coordinate space, whichever is closest
    5471             :   // during the particular iteration
    5472         167 :   nscoord baselineOffset = 0;
    5473             : 
    5474         167 :   for (nsIFrame* f = this, *fChild = nullptr;
    5475         167 :        f;
    5476           0 :        fChild = f,
    5477             :        f = nsLayoutUtils::GetParentOrPlaceholderFor(f))
    5478             :   {
    5479         167 :     nsStyleContext *const context = f->StyleContext();
    5480         167 :     if (!context->HasTextDecorationLines()) {
    5481         167 :       break;
    5482             :     }
    5483             : 
    5484           0 :     const nsStyleTextReset *const styleText = context->StyleTextReset();
    5485           0 :     const uint8_t textDecorations = styleText->mTextDecorationLine;
    5486             : 
    5487           0 :     if (!useOverride &&
    5488           0 :         (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations)) {
    5489             :       // This handles the <a href="blah.html"><font color="green">La
    5490             :       // la la</font></a> case. The link underline should be green.
    5491           0 :       useOverride = true;
    5492             :       overrideColor =
    5493           0 :         nsLayoutUtils::GetColor(f, &nsStyleTextReset::mTextDecorationColor);
    5494             :     }
    5495             : 
    5496           0 :     nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
    5497           0 :     const bool firstBlock = !nearestBlockFound && fBlock;
    5498             : 
    5499             :     // Not updating positions once we hit a parent block is equivalent to
    5500             :     // the CSS 2.1 spec that blocks should propagate decorations down to their
    5501             :     // children (albeit the style should be preserved)
    5502             :     // However, if we're vertically aligned within a block, then we need to
    5503             :     // recover the correct baseline from the line by querying the FrameProperty
    5504             :     // that should be set (see nsLineLayout::VerticalAlignLine).
    5505           0 :     if (firstBlock) {
    5506             :       // At this point, fChild can't be null since TextFrames can't be blocks
    5507           0 :       if (fChild->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
    5508             : 
    5509             :         // Since offset is the offset in the child's coordinate space, we have
    5510             :         // to undo the accumulation to bring the transform out of the block's
    5511             :         // coordinate space
    5512             :         const nscoord lineBaselineOffset = LazyGetLineBaselineOffset(fChild,
    5513           0 :                                                                      fBlock);
    5514             : 
    5515           0 :         baselineOffset = physicalBlockStartOffset - lineBaselineOffset -
    5516           0 :           (vertical ? fChild->GetNormalPosition().x
    5517           0 :                     : fChild->GetNormalPosition().y);
    5518             :       }
    5519             :     }
    5520           0 :     else if (!nearestBlockFound) {
    5521             :       // offset here is the offset from f's baseline to f's top/left
    5522             :       // boundary. It's descent for vertical-rl, and ascent otherwise.
    5523           0 :       nscoord offset = wm.IsVerticalRL() ?
    5524           0 :         f->GetSize().width - f->GetLogicalBaseline(wm) :
    5525           0 :         f->GetLogicalBaseline(wm);
    5526           0 :       baselineOffset = physicalBlockStartOffset - offset;
    5527             :     }
    5528             : 
    5529           0 :     nearestBlockFound = nearestBlockFound || firstBlock;
    5530           0 :     physicalBlockStartOffset +=
    5531           0 :       vertical ? f->GetNormalPosition().x : f->GetNormalPosition().y;
    5532             : 
    5533           0 :     const uint8_t style = styleText->mTextDecorationStyle;
    5534           0 :     if (textDecorations) {
    5535             :       nscolor color;
    5536           0 :       if (useOverride) {
    5537           0 :         color = overrideColor;
    5538           0 :       } else if (nsSVGUtils::IsInSVGTextSubtree(this)) {
    5539             :         // XXX We might want to do something with text-decoration-color when
    5540             :         //     painting SVG text, but it's not clear what we should do.  We
    5541             :         //     at least need SVG text decorations to paint with 'fill' if
    5542             :         //     text-decoration-color has its initial value currentColor.
    5543             :         //     We could choose to interpret currentColor as "currentFill"
    5544             :         //     for SVG text, and have e.g. text-decoration-color:red to
    5545             :         //     override the fill paint of the decoration.
    5546           0 :         color = aColorResolution == eResolvedColors ?
    5547             :                   nsLayoutUtils::GetColor(f, &nsStyleSVG::mFill) :
    5548             :                   NS_SAME_AS_FOREGROUND_COLOR;
    5549             :       } else {
    5550             :         color = nsLayoutUtils::
    5551           0 :           GetColor(f, &nsStyleTextReset::mTextDecorationColor);
    5552             :       }
    5553             : 
    5554           0 :       bool swapUnderlineAndOverline = vertical && IsUnderlineRight(f);
    5555             :       const uint8_t kUnderline =
    5556             :         swapUnderlineAndOverline ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE :
    5557           0 :                                    NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
    5558             :       const uint8_t kOverline =
    5559             :         swapUnderlineAndOverline ? NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE :
    5560           0 :                                    NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
    5561             : 
    5562           0 :       if (textDecorations & kUnderline) {
    5563           0 :         aDecorations.mUnderlines.AppendElement(
    5564           0 :           nsTextFrame::LineDecoration(f, baselineOffset, color, style));
    5565             :       }
    5566           0 :       if (textDecorations & kOverline) {
    5567           0 :         aDecorations.mOverlines.AppendElement(
    5568           0 :           nsTextFrame::LineDecoration(f, baselineOffset, color, style));
    5569             :       }
    5570           0 :       if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
    5571           0 :         aDecorations.mStrikes.AppendElement(
    5572           0 :           nsTextFrame::LineDecoration(f, baselineOffset, color, style));
    5573             :       }
    5574             :     }
    5575             : 
    5576             :     // In all modes, if we're on an inline-block or inline-table (or
    5577             :     // inline-stack, inline-box, inline-grid), we're done.
    5578             :     // If we're on a ruby frame other than ruby text container, we
    5579             :     // should continue.
    5580           0 :     mozilla::StyleDisplay display = f->GetDisplay();
    5581           0 :     if (display != mozilla::StyleDisplay::Inline &&
    5582           0 :         (!nsStyleDisplay::IsRubyDisplayType(display) ||
    5583           0 :          display == mozilla::StyleDisplay::RubyTextContainer) &&
    5584           0 :         nsStyleDisplay::IsDisplayTypeInlineOutside(display)) {
    5585           0 :       break;
    5586             :     }
    5587             : 
    5588             :     // In quirks mode, if we're on an HTML table element, we're done.
    5589           0 :     if (compatMode == eCompatibility_NavQuirks &&
    5590           0 :         f->GetContent()->IsHTMLElement(nsGkAtoms::table)) {
    5591           0 :       break;
    5592             :     }
    5593             : 
    5594             :     // If we're on an absolutely-positioned element or a floating
    5595             :     // element, we're done.
    5596           0 :     if (f->IsFloating() || f->IsAbsolutelyPositioned()) {
    5597           0 :       break;
    5598             :     }
    5599             : 
    5600             :     // If we're an outer <svg> element, which is classified as an atomic
    5601             :     // inline-level element, we're done.
    5602           0 :     if (f->IsSVGOuterSVGFrame()) {
    5603           0 :       break;
    5604             :     }
    5605             :   }
    5606         167 : }
    5607             : 
    5608             : static float
    5609           0 : GetInflationForTextDecorations(nsIFrame* aFrame, nscoord aInflationMinFontSize)
    5610             : {
    5611           0 :   if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
    5612           0 :     const nsIFrame* container = aFrame;
    5613           0 :     while (!container->IsSVGTextFrame()) {
    5614           0 :       container = container->GetParent();
    5615             :     }
    5616           0 :     NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
    5617             :     return
    5618           0 :       static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
    5619             :   }
    5620           0 :   return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize);
    5621             : }
    5622             : 
    5623           0 : struct EmphasisMarkInfo
    5624             : {
    5625             :   RefPtr<gfxTextRun> textRun;
    5626             :   gfxFloat advance;
    5627             :   gfxFloat baselineOffset;
    5628             : };
    5629             : 
    5630           0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(EmphasisMarkProperty, EmphasisMarkInfo)
    5631             : 
    5632             : already_AddRefed<gfxTextRun>
    5633           0 : GenerateTextRunForEmphasisMarks(nsTextFrame* aFrame,
    5634             :                                 nsFontMetrics* aFontMetrics,
    5635             :                                 nsStyleContext* aStyleContext,
    5636             :                                 const nsStyleText* aStyleText)
    5637             : {
    5638           0 :   const nsString& emphasisString = aStyleText->mTextEmphasisStyleString;
    5639           0 :   RefPtr<DrawTarget> dt = CreateReferenceDrawTarget(aFrame);
    5640           0 :   auto appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
    5641           0 :   gfx::ShapedTextFlags flags = nsLayoutUtils::GetTextRunOrientFlagsForStyle(aStyleContext);
    5642           0 :   if (flags == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
    5643             :     // The emphasis marks should always be rendered upright per spec.
    5644           0 :     flags = gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
    5645             :   }
    5646             :   return aFontMetrics->GetThebesFontGroup()->
    5647             :     MakeTextRun<char16_t>(emphasisString.get(), emphasisString.Length(),
    5648             :                           dt, appUnitsPerDevUnit, flags,
    5649           0 :                           nsTextFrameUtils::Flags(), nullptr);
    5650             : }
    5651             : 
    5652             : static nsRubyFrame*
    5653           0 : FindFurthestInlineRubyAncestor(nsTextFrame* aFrame)
    5654             : {
    5655           0 :   nsRubyFrame* rubyFrame = nullptr;
    5656           0 :   for (nsIFrame* frame = aFrame->GetParent();
    5657           0 :        frame && frame->IsFrameOfType(nsIFrame::eLineParticipant);
    5658             :        frame = frame->GetParent()) {
    5659           0 :     if (frame->IsRubyFrame()) {
    5660           0 :       rubyFrame = static_cast<nsRubyFrame*>(frame);
    5661             :     }
    5662             :   }
    5663           0 :   return rubyFrame;
    5664             : }
    5665             : 
    5666             : nsRect
    5667           0 : nsTextFrame::UpdateTextEmphasis(WritingMode aWM, PropertyProvider& aProvider)
    5668             : {
    5669           0 :   const nsStyleText* styleText = StyleText();
    5670           0 :   if (!styleText->HasTextEmphasis()) {
    5671           0 :     DeleteProperty(EmphasisMarkProperty());
    5672           0 :     return nsRect();
    5673             :   }
    5674             : 
    5675           0 :   nsStyleContext* styleContext = StyleContext();
    5676           0 :   bool isTextCombined = styleContext->IsTextCombined();
    5677           0 :   if (isTextCombined) {
    5678           0 :     styleContext = GetParent()->StyleContext();
    5679             :   }
    5680             :   RefPtr<nsFontMetrics> fm = nsLayoutUtils::
    5681           0 :     GetFontMetricsOfEmphasisMarks(styleContext, GetFontSizeInflation());
    5682           0 :   EmphasisMarkInfo* info = new EmphasisMarkInfo;
    5683             :   info->textRun =
    5684           0 :     GenerateTextRunForEmphasisMarks(this, fm, styleContext, styleText);
    5685           0 :   info->advance = info->textRun->GetAdvanceWidth();
    5686             : 
    5687             :   // Calculate the baseline offset
    5688           0 :   LogicalSide side = styleText->TextEmphasisSide(aWM);
    5689           0 :   LogicalSize frameSize = GetLogicalSize(aWM);
    5690             :   // The overflow rect is inflated in the inline direction by half
    5691             :   // advance of the emphasis mark on each side, so that even if a mark
    5692             :   // is drawn for a zero-width character, it won't be clipped.
    5693           0 :   LogicalRect overflowRect(aWM, -info->advance / 2,
    5694             :                            /* BStart to be computed below */ 0,
    5695           0 :                            frameSize.ISize(aWM) + info->advance,
    5696           0 :                            fm->MaxAscent() + fm->MaxDescent());
    5697             :   RefPtr<nsFontMetrics> baseFontMetrics = isTextCombined
    5698           0 :     ? nsLayoutUtils::GetInflatedFontMetricsForFrame(GetParent())
    5699           0 :     : do_AddRef(aProvider.GetFontMetrics());
    5700             :   // When the writing mode is vertical-lr the line is inverted, and thus
    5701             :   // the ascent and descent are swapped.
    5702           0 :   nscoord absOffset = (side == eLogicalSideBStart) != aWM.IsLineInverted() ?
    5703           0 :     baseFontMetrics->MaxAscent() + fm->MaxDescent() :
    5704           0 :     baseFontMetrics->MaxDescent() + fm->MaxAscent();
    5705           0 :   RubyBlockLeadings leadings;
    5706           0 :   if (nsRubyFrame* ruby = FindFurthestInlineRubyAncestor(this)) {
    5707           0 :     leadings = ruby->GetBlockLeadings();
    5708             :   }
    5709           0 :   if (side == eLogicalSideBStart) {
    5710           0 :     info->baselineOffset = -absOffset - leadings.mStart;
    5711           0 :     overflowRect.BStart(aWM) = -overflowRect.BSize(aWM) - leadings.mStart;
    5712             :   } else {
    5713           0 :     MOZ_ASSERT(side == eLogicalSideBEnd);
    5714           0 :     info->baselineOffset = absOffset + leadings.mEnd;
    5715           0 :     overflowRect.BStart(aWM) = frameSize.BSize(aWM) + leadings.mEnd;
    5716             :   }
    5717             :   // If text combined, fix the gap between the text frame and its parent.
    5718           0 :   if (isTextCombined) {
    5719           0 :     nscoord gap = (baseFontMetrics->MaxHeight() - frameSize.BSize(aWM)) / 2;
    5720           0 :     overflowRect.BStart(aWM) += gap * (side == eLogicalSideBStart ? -1 : 1);
    5721             :   }
    5722             : 
    5723           0 :   SetProperty(EmphasisMarkProperty(), info);
    5724           0 :   return overflowRect.GetPhysicalRect(aWM, frameSize.GetPhysicalSize(aWM));
    5725             : }
    5726             : 
    5727             : void
    5728          24 : nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
    5729             :                                      nsIFrame* aBlock,
    5730             :                                      PropertyProvider& aProvider,
    5731             :                                      nsRect* aVisualOverflowRect,
    5732             :                                      bool aIncludeTextDecorations)
    5733             : {
    5734          24 :   const WritingMode wm = GetWritingMode();
    5735          24 :   bool verticalRun = mTextRun->IsVertical();
    5736          24 :   const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
    5737             : 
    5738          24 :   if (IsFloatingFirstLetterChild()) {
    5739           0 :     bool inverted = wm.IsLineInverted();
    5740             :     // The underline/overline drawable area must be contained in the overflow
    5741             :     // rect when this is in floating first letter frame at *both* modes.
    5742             :     // In this case, aBlock is the ::first-letter frame.
    5743             :     uint8_t decorationStyle = aBlock->StyleContext()->
    5744           0 :                                 StyleTextReset()->mTextDecorationStyle;
    5745             :     // If the style is none, let's include decoration line rect as solid style
    5746             :     // since changing the style from none to solid/dotted/dashed doesn't cause
    5747             :     // reflow.
    5748           0 :     if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
    5749           0 :       decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
    5750             :     }
    5751           0 :     nsFontMetrics* fontMetrics = aProvider.GetFontMetrics();
    5752             :     nscoord underlineOffset, underlineSize;
    5753           0 :     fontMetrics->GetUnderline(underlineOffset, underlineSize);
    5754           0 :     nscoord maxAscent = inverted ? fontMetrics->MaxDescent()
    5755           0 :                                  : fontMetrics->MaxAscent();
    5756             : 
    5757           0 :     nsCSSRendering::DecorationRectParams params;
    5758             :     Float gfxWidth =
    5759           0 :       (verticalRun ? aVisualOverflowRect->height
    5760           0 :                    : aVisualOverflowRect->width) /
    5761           0 :       appUnitsPerDevUnit;
    5762           0 :     params.lineSize = Size(gfxWidth, underlineSize / appUnitsPerDevUnit);
    5763           0 :     params.ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
    5764           0 :     params.style = decorationStyle;
    5765           0 :     params.vertical = verticalRun;
    5766             : 
    5767           0 :     params.offset = underlineOffset / appUnitsPerDevUnit;
    5768           0 :     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
    5769             :     nsRect underlineRect =
    5770           0 :       nsCSSRendering::GetTextDecorationRect(aPresContext, params);
    5771           0 :     params.offset = maxAscent / appUnitsPerDevUnit;
    5772           0 :     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
    5773             :     nsRect overlineRect =
    5774           0 :       nsCSSRendering::GetTextDecorationRect(aPresContext, params);
    5775             : 
    5776           0 :     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
    5777           0 :     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
    5778             : 
    5779             :     // XXX If strikeoutSize is much thicker than the underlineSize, it may
    5780             :     //     cause overflowing from the overflow rect.  However, such case
    5781             :     //     isn't realistic, we don't need to compute it now.
    5782             :   }
    5783          24 :   if (aIncludeTextDecorations) {
    5784             :     // Use writing mode of parent frame for orthogonal text frame to
    5785             :     // work. See comment in nsTextFrame::DrawTextRunAndDecorations.
    5786           0 :     WritingMode parentWM = GetParent()->GetWritingMode();
    5787           0 :     bool verticalDec = parentWM.IsVertical();
    5788           0 :     bool useVerticalMetrics = verticalDec != verticalRun
    5789           0 :       ? verticalDec : verticalRun && mTextRun->UseCenterBaseline();
    5790             : 
    5791             :     // Since CSS 2.1 requires that text-decoration defined on ancestors maintain
    5792             :     // style and position, they can be drawn at virtually any y-offset, so
    5793             :     // maxima and minima are required to reliably generate the rectangle for
    5794             :     // them
    5795           0 :     TextDecorations textDecs;
    5796           0 :     GetTextDecorations(aPresContext, eResolvedColors, textDecs);
    5797           0 :     if (textDecs.HasDecorationLines()) {
    5798             :       nscoord inflationMinFontSize =
    5799           0 :         nsLayoutUtils::InflationMinFontSizeFor(aBlock);
    5800             : 
    5801           0 :       const nscoord measure = verticalDec ? GetSize().height : GetSize().width;
    5802           0 :       gfxFloat gfxWidth = measure / appUnitsPerDevUnit;
    5803           0 :       gfxFloat ascent = gfxFloat(GetLogicalBaseline(parentWM))
    5804           0 :                           / appUnitsPerDevUnit;
    5805           0 :       nscoord frameBStart = 0;
    5806           0 :       if (parentWM.IsVerticalRL()) {
    5807           0 :         frameBStart = GetSize().width;
    5808           0 :         ascent = -ascent;
    5809             :       }
    5810             :       // The decoration-line offsets need to be reversed for sideways-lr mode,
    5811             :       // so we will multiply the values from metrics by this factor.
    5812           0 :       gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
    5813             : 
    5814           0 :       nsCSSRendering::DecorationRectParams params;
    5815           0 :       params.lineSize = Size(gfxWidth, 0);
    5816           0 :       params.ascent = ascent;
    5817           0 :       params.vertical = verticalDec;
    5818             : 
    5819           0 :       nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN);
    5820             :       typedef gfxFont::Metrics Metrics;
    5821             :       auto accumulateDecorationRect = [&](const LineDecoration& dec,
    5822             :                                           gfxFloat Metrics::* lineSize,
    5823           0 :                                           gfxFloat Metrics::* lineOffset) {
    5824           0 :         params.style = dec.mStyle;
    5825             :         // If the style is solid, let's include decoration line rect of solid
    5826             :         // style since changing the style from none to solid/dotted/dashed
    5827             :         // doesn't cause reflow.
    5828           0 :         if (params.style == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
    5829           0 :           params.style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
    5830             :         }
    5831             : 
    5832             :         float inflation =
    5833           0 :           GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
    5834             :         const Metrics metrics =
    5835           0 :           GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
    5836           0 :                               useVerticalMetrics);
    5837             : 
    5838           0 :         params.lineSize.height = metrics.*lineSize;
    5839           0 :         params.offset = decorationOffsetDir * metrics.*lineOffset;
    5840             :         const nsRect decorationRect =
    5841           0 :           nsCSSRendering::GetTextDecorationRect(aPresContext, params) +
    5842           0 :           (verticalDec ? nsPoint(frameBStart - dec.mBaselineOffset, 0)
    5843           0 :                        : nsPoint(0, -dec.mBaselineOffset));
    5844             : 
    5845           0 :         if (verticalDec) {
    5846           0 :           topOrLeft = std::min(decorationRect.x, topOrLeft);
    5847           0 :           bottomOrRight = std::max(decorationRect.XMost(), bottomOrRight);
    5848             :         } else {
    5849           0 :           topOrLeft = std::min(decorationRect.y, topOrLeft);
    5850           0 :           bottomOrRight = std::max(decorationRect.YMost(), bottomOrRight);
    5851             :         }
    5852           0 :       };
    5853             : 
    5854             :       // Below we loop through all text decorations and compute the rectangle
    5855             :       // containing all of them, in this frame's coordinate space
    5856           0 :       params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
    5857           0 :       for (const LineDecoration& dec : textDecs.mUnderlines) {
    5858             :         accumulateDecorationRect(dec, &Metrics::underlineSize,
    5859           0 :                                  &Metrics::underlineOffset);
    5860             :       }
    5861           0 :       params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
    5862           0 :       for (const LineDecoration& dec : textDecs.mOverlines) {
    5863             :         accumulateDecorationRect(dec, &Metrics::underlineSize,
    5864           0 :                                  &Metrics::maxAscent);
    5865             :       }
    5866           0 :       params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
    5867           0 :       for (const LineDecoration& dec : textDecs.mStrikes) {
    5868             :         accumulateDecorationRect(dec, &Metrics::strikeoutSize,
    5869           0 :                                  &Metrics::strikeoutOffset);
    5870             :       }
    5871             : 
    5872             :       aVisualOverflowRect->UnionRect(
    5873             :         *aVisualOverflowRect,
    5874           0 :         verticalDec ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
    5875           0 :                     : nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft));
    5876             :     }
    5877             : 
    5878             :     aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
    5879           0 :                                    UpdateTextEmphasis(parentWM, aProvider));
    5880             :   }
    5881             : 
    5882             :   // text-stroke overflows
    5883          24 :   nscoord textStrokeWidth = StyleText()->mWebkitTextStrokeWidth;
    5884          24 :   if (textStrokeWidth > 0) {
    5885           0 :     nsRect strokeRect = *aVisualOverflowRect;
    5886           0 :     strokeRect.x -= textStrokeWidth;
    5887           0 :     strokeRect.y -= textStrokeWidth;
    5888           0 :     strokeRect.width += textStrokeWidth;
    5889           0 :     strokeRect.height += textStrokeWidth;
    5890           0 :     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, strokeRect);
    5891             :   }
    5892             : 
    5893             :   // Text-shadow overflows
    5894             :   nsRect shadowRect =
    5895          24 :     nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
    5896          24 :   aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
    5897             : 
    5898             :   // When this frame is not selected, the text-decoration area must be in
    5899             :   // frame bounds.
    5900          25 :   if (!IsSelected() ||
    5901           1 :       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
    5902          24 :     return;
    5903           0 :   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
    5904             : }
    5905             : 
    5906             : gfxFloat
    5907           1 : nsTextFrame::ComputeDescentLimitForSelectionUnderline(
    5908             :                nsPresContext* aPresContext,
    5909             :                const gfxFont::Metrics& aFontMetrics)
    5910             : {
    5911           1 :   gfxFloat app = aPresContext->AppUnitsPerDevPixel();
    5912             :   nscoord lineHeightApp =
    5913           1 :     ReflowInput::CalcLineHeight(GetContent(),
    5914             :                                       StyleContext(), NS_AUTOHEIGHT,
    5915           1 :                                       GetFontSizeInflation());
    5916           1 :   gfxFloat lineHeight = gfxFloat(lineHeightApp) / app;
    5917           1 :   if (lineHeight <= aFontMetrics.maxHeight) {
    5918           1 :     return aFontMetrics.maxDescent;
    5919             :   }
    5920           0 :   return aFontMetrics.maxDescent + (lineHeight - aFontMetrics.maxHeight) / 2;
    5921             : }
    5922             : 
    5923             : 
    5924             : // Make sure this stays in sync with DrawSelectionDecorations below
    5925             : static const RawSelectionType kRawSelectionTypesWithDecorations =
    5926             :   nsISelectionController::SELECTION_SPELLCHECK |
    5927             :   nsISelectionController::SELECTION_URLSTRIKEOUT |
    5928             :   nsISelectionController::SELECTION_IME_RAWINPUT |
    5929             :   nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT |
    5930             :   nsISelectionController::SELECTION_IME_CONVERTEDTEXT |
    5931             :   nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
    5932             : 
    5933             : /* static */
    5934             : gfxFloat
    5935           0 : nsTextFrame::ComputeSelectionUnderlineHeight(
    5936             :                nsPresContext* aPresContext,
    5937             :                const gfxFont::Metrics& aFontMetrics,
    5938             :                SelectionType aSelectionType)
    5939             : {
    5940           0 :   switch (aSelectionType) {
    5941             :     case SelectionType::eIMERawClause:
    5942             :     case SelectionType::eIMESelectedRawClause:
    5943             :     case SelectionType::eIMEConvertedClause:
    5944             :     case SelectionType::eIMESelectedClause:
    5945           0 :       return aFontMetrics.underlineSize;
    5946             :     case SelectionType::eSpellCheck: {
    5947             :       // The thickness of the spellchecker underline shouldn't honor the font
    5948             :       // metrics.  It should be constant pixels value which is decided from the
    5949             :       // default font size.  Note that if the actual font size is smaller than
    5950             :       // the default font size, we should use the actual font size because the
    5951             :       // computed value from the default font size can be too thick for the
    5952             :       // current font size.
    5953           0 :       nscoord defaultFontSize = aPresContext->GetDefaultFont(
    5954           0 :           kPresContext_DefaultVariableFont_ID, nullptr)->size;
    5955           0 :       int32_t zoomedFontSize = aPresContext->AppUnitsToDevPixels(
    5956           0 :           nsStyleFont::ZoomText(aPresContext, defaultFontSize));
    5957           0 :       gfxFloat fontSize = std::min(gfxFloat(zoomedFontSize),
    5958           0 :                                    aFontMetrics.emHeight);
    5959           0 :       fontSize = std::max(fontSize, 1.0);
    5960           0 :       return ceil(fontSize / 20);
    5961             :     }
    5962             :     default:
    5963           0 :       NS_WARNING("Requested underline style is not valid");
    5964           0 :       return aFontMetrics.underlineSize;
    5965             :   }
    5966             : }
    5967             : 
    5968             : enum class DecorationType
    5969             : {
    5970             :   Normal, Selection
    5971             : };
    5972           0 : struct nsTextFrame::PaintDecorationLineParams
    5973             :   : nsCSSRendering::DecorationRectParams
    5974             : {
    5975             :   gfxContext* context = nullptr;
    5976             :   LayoutDeviceRect dirtyRect;
    5977             :   Point pt;
    5978             :   const nscolor* overrideColor = nullptr;
    5979             :   nscolor color = NS_RGBA(0, 0, 0, 0);
    5980             :   gfxFloat icoordInFrame = 0.0f;
    5981             :   DecorationType decorationType = DecorationType::Normal;
    5982             :   DrawPathCallbacks* callbacks = nullptr;
    5983             : };
    5984             : 
    5985             : void
    5986           0 : nsTextFrame::PaintDecorationLine(const PaintDecorationLineParams& aParams)
    5987             : {
    5988           0 :   nsCSSRendering::PaintDecorationLineParams params;
    5989           0 :   static_cast<nsCSSRendering::DecorationRectParams&>(params) = aParams;
    5990           0 :   params.dirtyRect = aParams.dirtyRect.ToUnknownRect();
    5991           0 :   params.pt = aParams.pt;
    5992           0 :   params.color = aParams.overrideColor ? *aParams.overrideColor : aParams.color;
    5993           0 :   params.icoordInFrame = Float(aParams.icoordInFrame);
    5994           0 :   if (aParams.callbacks) {
    5995           0 :     Rect path = nsCSSRendering::DecorationLineToPath(params);
    5996           0 :     if (aParams.decorationType == DecorationType::Normal) {
    5997           0 :       aParams.callbacks->PaintDecorationLine(path, params.color);
    5998             :     } else {
    5999           0 :       aParams.callbacks->PaintSelectionDecorationLine(path, params.color);
    6000             :     }
    6001             :   } else {
    6002           0 :     nsCSSRendering::PaintDecorationLine(
    6003           0 :       this, *aParams.context->GetDrawTarget(), params);
    6004             :   }
    6005           0 : }
    6006             : 
    6007             : /**
    6008             :  * This, plus kRawSelectionTypesWithDecorations, encapsulates all knowledge
    6009             :  * about drawing text decoration for selections.
    6010             :  */
    6011             : void
    6012           0 : nsTextFrame::DrawSelectionDecorations(gfxContext* aContext,
    6013             :                                       const LayoutDeviceRect& aDirtyRect,
    6014             :                                       SelectionType aSelectionType,
    6015             :                                       nsTextPaintStyle& aTextPaintStyle,
    6016             :                                       const TextRangeStyle &aRangeStyle,
    6017             :                                       const Point& aPt,
    6018             :                                       gfxFloat aICoordInFrame,
    6019             :                                       gfxFloat aWidth,
    6020             :                                       gfxFloat aAscent,
    6021             :                                       const gfxFont::Metrics& aFontMetrics,
    6022             :                                       DrawPathCallbacks* aCallbacks,
    6023             :                                       bool aVertical,
    6024             :                                       gfxFloat aDecorationOffsetDir,
    6025             :                                       uint8_t aDecoration)
    6026             : {
    6027           0 :   PaintDecorationLineParams params;
    6028           0 :   params.context = aContext;
    6029           0 :   params.dirtyRect = aDirtyRect;
    6030           0 :   params.pt = aPt;
    6031           0 :   params.lineSize.width = aWidth;
    6032           0 :   params.ascent = aAscent;
    6033           0 :   params.offset = aDecoration == NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ?
    6034             :                   aFontMetrics.underlineOffset : aFontMetrics.maxAscent;
    6035           0 :   params.decoration = aDecoration;
    6036           0 :   params.decorationType = DecorationType::Selection;
    6037           0 :   params.callbacks = aCallbacks;
    6038           0 :   params.vertical = aVertical;
    6039           0 :   params.descentLimit =
    6040           0 :     ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(),
    6041             :                                              aFontMetrics);
    6042             : 
    6043             :   float relativeSize;
    6044             : 
    6045           0 :   switch (aSelectionType) {
    6046             :     case SelectionType::eIMERawClause:
    6047             :     case SelectionType::eIMESelectedRawClause:
    6048             :     case SelectionType::eIMEConvertedClause:
    6049             :     case SelectionType::eIMESelectedClause:
    6050             :     case SelectionType::eSpellCheck: {
    6051             :       int32_t index = nsTextPaintStyle::
    6052           0 :         GetUnderlineStyleIndexForSelectionType(aSelectionType);
    6053             :       bool weDefineSelectionUnderline =
    6054             :         aTextPaintStyle.GetSelectionUnderlineForPaint(index, &params.color,
    6055             :                                                       &relativeSize,
    6056           0 :                                                       &params.style);
    6057           0 :       params.lineSize.height =
    6058           0 :         ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(),
    6059             :                                         aFontMetrics, aSelectionType);
    6060           0 :       bool isIMEType = aSelectionType != SelectionType::eSpellCheck;
    6061             : 
    6062           0 :       if (isIMEType) {
    6063             :         // IME decoration lines should not be drawn on the both ends, i.e., we
    6064             :         // need to cut both edges of the decoration lines.  Because same style
    6065             :         // IME selections can adjoin, but the users need to be able to know
    6066             :         // where are the boundaries of the selections.
    6067             :         //
    6068             :         //  X: underline
    6069             :         //
    6070             :         //     IME selection #1        IME selection #2      IME selection #3
    6071             :         //  |                     |                      |
    6072             :         //  | XXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXX
    6073             :         //  +---------------------+----------------------+--------------------
    6074             :         //   ^                   ^ ^                    ^ ^
    6075             :         //  gap                  gap                    gap
    6076           0 :         params.pt.x += 1.0;
    6077           0 :         params.lineSize.width -= 2.0;
    6078             :       }
    6079           0 :       if (isIMEType && aRangeStyle.IsDefined()) {
    6080             :         // If IME defines the style, that should override our definition.
    6081           0 :         if (aRangeStyle.IsLineStyleDefined()) {
    6082           0 :           if (aRangeStyle.mLineStyle == TextRangeStyle::LINESTYLE_NONE) {
    6083           0 :             return;
    6084             :           }
    6085           0 :           params.style = aRangeStyle.mLineStyle;
    6086           0 :           relativeSize = aRangeStyle.mIsBoldLine ? 2.0f : 1.0f;
    6087           0 :         } else if (!weDefineSelectionUnderline) {
    6088             :           // There is no underline style definition.
    6089           0 :           return;
    6090             :         }
    6091             :         // If underline color is defined and that doesn't depend on the
    6092             :         // foreground color, we should use the color directly.
    6093           0 :         if (aRangeStyle.IsUnderlineColorDefined() &&
    6094           0 :             (!aRangeStyle.IsForegroundColorDefined() ||
    6095           0 :              aRangeStyle.mUnderlineColor != aRangeStyle.mForegroundColor)) {
    6096           0 :           params.color = aRangeStyle.mUnderlineColor;
    6097             :         }
    6098             :         // If foreground color or background color is defined, the both colors
    6099             :         // are computed by GetSelectionTextColors().  Then, we should use its
    6100             :         // foreground color always.  The color should have sufficient contrast
    6101             :         // with the background color.
    6102           0 :         else if (aRangeStyle.IsForegroundColorDefined() ||
    6103           0 :                  aRangeStyle.IsBackgroundColorDefined()) {
    6104             :           nscolor bg;
    6105             :           GetSelectionTextColors(aSelectionType, aTextPaintStyle,
    6106           0 :                                  aRangeStyle, &params.color, &bg);
    6107             :         }
    6108             :         // Otherwise, use the foreground color of the frame.
    6109             :         else {
    6110           0 :           params.color = aTextPaintStyle.GetTextColor();
    6111             :         }
    6112           0 :       } else if (!weDefineSelectionUnderline) {
    6113             :         // IME doesn't specify the selection style and we don't define selection
    6114             :         // underline.
    6115           0 :         return;
    6116             :       }
    6117           0 :       break;
    6118             :     }
    6119             :     case SelectionType::eURLStrikeout: {
    6120             :       nscoord inflationMinFontSize =
    6121           0 :         nsLayoutUtils::InflationMinFontSizeFor(this);
    6122             :       float inflation =
    6123           0 :         GetInflationForTextDecorations(this, inflationMinFontSize);
    6124             :       const gfxFont::Metrics metrics =
    6125           0 :         GetFirstFontMetrics(GetFontGroupForFrame(this, inflation), aVertical);
    6126             : 
    6127           0 :       relativeSize = 2.0f;
    6128           0 :       aTextPaintStyle.GetURLSecondaryColor(&params.color);
    6129           0 :       params.style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
    6130           0 :       params.lineSize.height = metrics.strikeoutSize;
    6131           0 :       params.offset = metrics.strikeoutOffset + 0.5;
    6132           0 :       params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
    6133           0 :       break;
    6134             :     }
    6135             :     default:
    6136           0 :       NS_WARNING("Requested selection decorations when there aren't any");
    6137           0 :       return;
    6138             :   }
    6139           0 :   params.offset *= aDecorationOffsetDir;
    6140           0 :   params.lineSize.height *= relativeSize;
    6141           0 :   params.icoordInFrame = (aVertical ? params.pt.y - aPt.y
    6142           0 :                                     : params.pt.x - aPt.x) + aICoordInFrame;
    6143           0 :   PaintDecorationLine(params);
    6144             : }
    6145             : 
    6146             : /* static */
    6147             : bool
    6148           3 : nsTextFrame::GetSelectionTextColors(SelectionType aSelectionType,
    6149             :                                     nsTextPaintStyle& aTextPaintStyle,
    6150             :                                     const TextRangeStyle &aRangeStyle,
    6151             :                                     nscolor* aForeground,
    6152             :                                     nscolor* aBackground)
    6153             : {
    6154           3 :   switch (aSelectionType) {
    6155             :     case SelectionType::eNormal:
    6156           0 :       return aTextPaintStyle.GetSelectionColors(aForeground, aBackground);
    6157             :     case SelectionType::eFind:
    6158           0 :       aTextPaintStyle.GetHighlightColors(aForeground, aBackground);
    6159           0 :       return true;
    6160             :     case SelectionType::eURLSecondary:
    6161           2 :       aTextPaintStyle.GetURLSecondaryColor(aForeground);
    6162           2 :       *aBackground = NS_RGBA(0,0,0,0);
    6163           2 :       return true;
    6164             :     case SelectionType::eIMERawClause:
    6165             :     case SelectionType::eIMESelectedRawClause:
    6166             :     case SelectionType::eIMEConvertedClause:
    6167             :     case SelectionType::eIMESelectedClause:
    6168           0 :       if (aRangeStyle.IsDefined()) {
    6169           0 :         if (!aRangeStyle.IsForegroundColorDefined() &&
    6170           0 :             !aRangeStyle.IsBackgroundColorDefined()) {
    6171           0 :           *aForeground = aTextPaintStyle.GetTextColor();
    6172           0 :           *aBackground = NS_RGBA(0,0,0,0);
    6173           0 :           return false;
    6174             :         }
    6175           0 :         if (aRangeStyle.IsForegroundColorDefined()) {
    6176           0 :           *aForeground = aRangeStyle.mForegroundColor;
    6177           0 :           if (aRangeStyle.IsBackgroundColorDefined()) {
    6178           0 :             *aBackground = aRangeStyle.mBackgroundColor;
    6179             :           } else {
    6180             :             // If foreground color is defined but background color isn't
    6181             :             // defined, we can guess that IME must expect that the background
    6182             :             // color is system's default field background color.
    6183           0 :             *aBackground = aTextPaintStyle.GetSystemFieldBackgroundColor();
    6184             :           }
    6185             :         } else { // aRangeStyle.IsBackgroundColorDefined() is true
    6186           0 :           *aBackground = aRangeStyle.mBackgroundColor;
    6187             :           // If background color is defined but foreground color isn't defined,
    6188             :           // we can assume that IME must expect that the foreground color is
    6189             :           // same as system's field text color.
    6190           0 :           *aForeground = aTextPaintStyle.GetSystemFieldForegroundColor();
    6191             :         }
    6192           0 :         return true;
    6193             :       }
    6194           0 :       aTextPaintStyle.GetIMESelectionColors(
    6195             :         nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(
    6196             :           aSelectionType),
    6197           0 :         aForeground, aBackground);
    6198           0 :       return true;
    6199             :     default:
    6200           1 :       *aForeground = aTextPaintStyle.GetTextColor();
    6201           1 :       *aBackground = NS_RGBA(0,0,0,0);
    6202           1 :       return false;
    6203             :   }
    6204             : }
    6205             : 
    6206             : /**
    6207             :  * This sets *aShadow to the appropriate shadow, if any, for the given
    6208             :  * type of selection. Returns true if *aShadow was set.
    6209             :  * If text-shadow was not specified, *aShadow is left untouched
    6210             :  * (NOT reset to null), and the function returns false.
    6211             :  */
    6212           2 : static bool GetSelectionTextShadow(nsIFrame* aFrame,
    6213             :                                    SelectionType aSelectionType,
    6214             :                                    nsTextPaintStyle& aTextPaintStyle,
    6215             :                                    nsCSSShadowArray** aShadow)
    6216             : {
    6217           2 :   switch (aSelectionType) {
    6218             :     case SelectionType::eNormal:
    6219           0 :       return aTextPaintStyle.GetSelectionShadow(aShadow);
    6220             :     default:
    6221           2 :       return false;
    6222             :   }
    6223             : }
    6224             : 
    6225             : /**
    6226             :  * This class lets us iterate over chunks of text in a uniform selection state,
    6227             :  * observing cluster boundaries, in content order, maintaining the current
    6228             :  * x-offset as we go, and telling whether the text chunk has a hyphen after
    6229             :  * it or not. The caller is responsible for actually computing the advance
    6230             :  * width of each chunk.
    6231             :  */
    6232           1 : class SelectionIterator {
    6233             : public:
    6234             :   /**
    6235             :    * aStart and aLength are in the original string. aSelectionDetails is
    6236             :    * according to the original string.
    6237             :    * @param aXOffset the offset from the origin of the frame to the start
    6238             :    * of the text (the left baseline origin for LTR, the right baseline origin
    6239             :    * for RTL)
    6240             :    */
    6241             :   SelectionIterator(SelectionDetails** aSelectionDetails,
    6242             :                     gfxTextRun::Range aRange, PropertyProvider& aProvider,
    6243             :                     gfxTextRun* aTextRun, gfxFloat aXOffset);
    6244             : 
    6245             :   /**
    6246             :    * Returns the next segment of uniformly selected (or not) text.
    6247             :    * @param aXOffset the offset from the origin of the frame to the start
    6248             :    * of the text (the left baseline origin for LTR, the right baseline origin
    6249             :    * for RTL)
    6250             :    * @param aRange the transformed string range of the text for this segment
    6251             :    * @param aHyphenWidth if a hyphen is to be rendered after the text, the
    6252             :    * width of the hyphen, otherwise zero
    6253             :    * @param aSelectionType the selection type for this segment
    6254             :    * @param aStyle the selection style for this segment
    6255             :    * @return false if there are no more segments
    6256             :    */
    6257             :   bool GetNextSegment(gfxFloat* aXOffset, gfxTextRun::Range* aRange,
    6258             :                       gfxFloat* aHyphenWidth,
    6259             :                       SelectionType* aSelectionType,
    6260             :                       TextRangeStyle* aStyle);
    6261           2 :   void UpdateWithAdvance(gfxFloat aAdvance) {
    6262           2 :     mXOffset += aAdvance*mTextRun->GetDirection();
    6263           2 :   }
    6264             : 
    6265             : private:
    6266             :   SelectionDetails**      mSelectionDetails;
    6267             :   PropertyProvider&       mProvider;
    6268             :   RefPtr<gfxTextRun>      mTextRun;
    6269             :   gfxSkipCharsIterator    mIterator;
    6270             :   gfxTextRun::Range       mOriginalRange;
    6271             :   gfxFloat                mXOffset;
    6272             : };
    6273             : 
    6274           1 : SelectionIterator::SelectionIterator(SelectionDetails** aSelectionDetails,
    6275             :                                      gfxTextRun::Range aRange,
    6276             :                                      PropertyProvider& aProvider,
    6277           1 :                                      gfxTextRun* aTextRun, gfxFloat aXOffset)
    6278             :   : mSelectionDetails(aSelectionDetails), mProvider(aProvider),
    6279             :     mTextRun(aTextRun), mIterator(aProvider.GetStart()),
    6280           1 :     mOriginalRange(aRange), mXOffset(aXOffset)
    6281             : {
    6282           1 :   mIterator.SetOriginalOffset(aRange.start);
    6283           1 : }
    6284             : 
    6285           3 : bool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
    6286             :                                        gfxTextRun::Range* aRange,
    6287             :                                        gfxFloat* aHyphenWidth,
    6288             :                                        SelectionType* aSelectionType,
    6289             :                                        TextRangeStyle* aStyle)
    6290             : {
    6291           3 :   if (mIterator.GetOriginalOffset() >= int32_t(mOriginalRange.end))
    6292           1 :     return false;
    6293             : 
    6294             :   // save offset into transformed string now
    6295           2 :   uint32_t runOffset = mIterator.GetSkippedOffset();
    6296             : 
    6297           2 :   uint32_t index = mIterator.GetOriginalOffset() - mOriginalRange.start;
    6298           2 :   SelectionDetails* sdptr = mSelectionDetails[index];
    6299             :   SelectionType selectionType =
    6300           2 :     sdptr ? sdptr->mSelectionType : SelectionType::eNone;
    6301           2 :   TextRangeStyle style;
    6302           2 :   if (sdptr) {
    6303           1 :     style = sdptr->mTextRangeStyle;
    6304             :   }
    6305          46 :   for (++index; index < mOriginalRange.Length(); ++index) {
    6306          45 :     if (sdptr != mSelectionDetails[index])
    6307           1 :       break;
    6308             :   }
    6309           2 :   mIterator.SetOriginalOffset(index + mOriginalRange.start);
    6310             : 
    6311             :   // Advance to the next cluster boundary
    6312           5 :   while (mIterator.GetOriginalOffset() < int32_t(mOriginalRange.end) &&
    6313           3 :          !mIterator.IsOriginalCharSkipped() &&
    6314           1 :          !mTextRun->IsClusterStart(mIterator.GetSkippedOffset())) {
    6315           0 :     mIterator.AdvanceOriginal(1);
    6316             :   }
    6317             : 
    6318             :   bool haveHyphenBreak =
    6319           2 :     (mProvider.GetFrame()->GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
    6320           2 :   aRange->start = runOffset;
    6321           2 :   aRange->end = mIterator.GetSkippedOffset();
    6322           2 :   *aXOffset = mXOffset;
    6323           2 :   *aHyphenWidth = 0;
    6324           2 :   if (mIterator.GetOriginalOffset() == int32_t(mOriginalRange.end) &&
    6325             :       haveHyphenBreak) {
    6326           0 :     *aHyphenWidth = mProvider.GetHyphenWidth();
    6327             :   }
    6328           2 :   *aSelectionType = selectionType;
    6329           2 :   *aStyle = style;
    6330           2 :   return true;
    6331             : }
    6332             : 
    6333             : static void
    6334           0 : AddHyphenToMetrics(nsTextFrame* aTextFrame, const gfxTextRun* aBaseTextRun,
    6335             :                    gfxTextRun::Metrics* aMetrics,
    6336             :                    gfxFont::BoundingBoxType aBoundingBoxType,
    6337             :                    DrawTarget* aDrawTarget)
    6338             : {
    6339             :   // Fix up metrics to include hyphen
    6340             :   RefPtr<gfxTextRun> hyphenTextRun =
    6341           0 :     GetHyphenTextRun(aBaseTextRun, aDrawTarget, aTextFrame);
    6342           0 :   if (!hyphenTextRun) {
    6343           0 :     return;
    6344             :   }
    6345             : 
    6346             :   gfxTextRun::Metrics hyphenMetrics =
    6347           0 :     hyphenTextRun->MeasureText(aBoundingBoxType, aDrawTarget);
    6348           0 :   if (aTextFrame->GetWritingMode().IsLineInverted()) {
    6349           0 :     hyphenMetrics.mBoundingBox.y = -hyphenMetrics.mBoundingBox.YMost();
    6350             :   }
    6351           0 :   aMetrics->CombineWith(hyphenMetrics, aBaseTextRun->IsRightToLeft());
    6352             : }
    6353             : 
    6354             : void
    6355           0 : nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
    6356             :                             nsCSSShadowItem* aShadowDetails,
    6357             :                             gfxRect& aBoundingBox, uint32_t aBlurFlags)
    6358             : {
    6359           0 :   AUTO_PROFILER_LABEL("nsTextFrame::PaintOneShadow", GRAPHICS);
    6360             : 
    6361           0 :   gfxPoint shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset);
    6362           0 :   nscoord blurRadius = std::max(aShadowDetails->mRadius, 0);
    6363             : 
    6364             :   // This rect is the box which is equivalent to where the shadow will be painted.
    6365             :   // The origin of aBoundingBox is the text baseline left, so we must translate it by
    6366             :   // that much in order to make the origin the top-left corner of the text bounding box.
    6367             :   // Note that aLeftSideOffset is line-left, so actually means top offset in
    6368             :   // vertical writing modes.
    6369           0 :   gfxRect shadowGfxRect;
    6370           0 :   WritingMode wm = GetWritingMode();
    6371           0 :   if (wm.IsVertical()) {
    6372           0 :     shadowGfxRect = aBoundingBox;
    6373           0 :     if (wm.IsVerticalRL()) {
    6374             :       // for vertical-RL, reverse direction of x-coords of bounding box
    6375           0 :       shadowGfxRect.x = -shadowGfxRect.XMost();
    6376             :     }
    6377           0 :     shadowGfxRect += gfxPoint(aParams.textBaselinePt.x,
    6378           0 :                               aParams.framePt.y + aParams.leftSideOffset);
    6379             :   } else {
    6380             :     shadowGfxRect =
    6381           0 :       aBoundingBox + gfxPoint(aParams.framePt.x + aParams.leftSideOffset,
    6382           0 :                               aParams.textBaselinePt.y);
    6383             :   }
    6384           0 :   shadowGfxRect += shadowOffset;
    6385             : 
    6386             :   nsRect shadowRect(NSToCoordRound(shadowGfxRect.X()),
    6387             :                     NSToCoordRound(shadowGfxRect.Y()),
    6388             :                     NSToCoordRound(shadowGfxRect.Width()),
    6389           0 :                     NSToCoordRound(shadowGfxRect.Height()));
    6390             : 
    6391           0 :   nsContextBoxBlur contextBoxBlur;
    6392           0 :   const auto A2D = PresContext()->AppUnitsPerDevPixel();
    6393             :   gfxContext* shadowContext = contextBoxBlur.Init(
    6394           0 :     shadowRect, 0, blurRadius, A2D, aParams.context,
    6395           0 :     LayoutDevicePixel::ToAppUnits(aParams.dirtyRect, A2D), nullptr, aBlurFlags);
    6396           0 :   if (!shadowContext)
    6397           0 :     return;
    6398             : 
    6399             :   nscolor shadowColor;
    6400             :   const nscolor* decorationOverrideColor;
    6401           0 :   if (aShadowDetails->mHasColor) {
    6402           0 :     shadowColor = aShadowDetails->mColor;
    6403           0 :     decorationOverrideColor = &shadowColor;
    6404             :   } else {
    6405           0 :     shadowColor = aParams.foregroundColor;
    6406           0 :     decorationOverrideColor = nullptr;
    6407             :   }
    6408             : 
    6409           0 :   aParams.context->Save();
    6410           0 :   aParams.context->SetColor(Color::FromABGR(shadowColor));
    6411             : 
    6412             :   // Draw the text onto our alpha-only surface to capture the alpha values.
    6413             :   // Remember that the box blur context has a device offset on it, so we don't need to
    6414             :   // translate any coordinates to fit on the surface.
    6415             :   gfxFloat advanceWidth;
    6416           0 :   nsTextPaintStyle textPaintStyle(this);
    6417           0 :   DrawTextParams params(shadowContext);
    6418           0 :   params.advanceWidth = &advanceWidth;
    6419           0 :   params.dirtyRect = aParams.dirtyRect;
    6420           0 :   params.framePt = aParams.framePt + shadowOffset;
    6421           0 :   params.provider = aParams.provider;
    6422           0 :   params.textStyle = &textPaintStyle;
    6423           0 :   params.textColor =
    6424           0 :     aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0);
    6425           0 :   params.clipEdges = aParams.clipEdges;
    6426           0 :   params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
    6427           0 :   params.decorationOverrideColor = decorationOverrideColor;
    6428           0 :   DrawText(aParams.range, aParams.textBaselinePt + shadowOffset, params);
    6429             : 
    6430           0 :   contextBoxBlur.DoPaint();
    6431           0 :   aParams.context->Restore();
    6432             : }
    6433             : 
    6434             : // Paints selection backgrounds and text in the correct colors. Also computes
    6435             : // aAllTypes, the union of all selection types that are applying to this text.
    6436             : bool
    6437           1 : nsTextFrame::PaintTextWithSelectionColors(
    6438             :     const PaintTextSelectionParams& aParams,
    6439             :     const UniquePtr<SelectionDetails>& aDetails,
    6440             :     RawSelectionType* aAllRawSelectionTypes,
    6441             :     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
    6442             : {
    6443           1 :   const gfxTextRun::Range& contentRange = aParams.contentRange;
    6444             : 
    6445             :   // Figure out which selections control the colors to use for each character.
    6446             :   // Note: prevailingSelectionsBuffer is keeping extra raw pointers to
    6447             :   // uniquely-owned resources, but it's safe because it's temporary and the
    6448             :   // resources are owned by the caller. Therefore, they'll outlive this object.
    6449           2 :   AutoTArray<SelectionDetails*,BIG_TEXT_NODE_SIZE> prevailingSelectionsBuffer;
    6450             :   SelectionDetails** prevailingSelections =
    6451           1 :     prevailingSelectionsBuffer.AppendElements(contentRange.Length(), fallible);
    6452           1 :   if (!prevailingSelections) {
    6453           0 :     return false;
    6454             :   }
    6455             : 
    6456           1 :   RawSelectionType allRawSelectionTypes = 0;
    6457          47 :   for (uint32_t i = 0; i < contentRange.Length(); ++i) {
    6458          46 :     prevailingSelections[i] = nullptr;
    6459             :   }
    6460             : 
    6461           1 :   bool anyBackgrounds = false;
    6462           2 :   for (SelectionDetails* sdptr = aDetails.get(); sdptr; sdptr = sdptr->mNext.get()) {
    6463           1 :     int32_t start = std::max(0, sdptr->mStart - int32_t(contentRange.start));
    6464           2 :     int32_t end = std::min(int32_t(contentRange.Length()),
    6465           3 :                            sdptr->mEnd - int32_t(contentRange.start));
    6466           1 :     SelectionType selectionType = sdptr->mSelectionType;
    6467           1 :     if (start < end) {
    6468           1 :       allRawSelectionTypes |= ToRawSelectionType(selectionType);
    6469             :       // Ignore selections that don't set colors
    6470             :       nscolor foreground, background;
    6471           1 :       if (GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
    6472             :                                  sdptr->mTextRangeStyle,
    6473             :                                  &foreground, &background)) {
    6474           1 :         if (NS_GET_A(background) > 0) {
    6475           0 :           anyBackgrounds = true;
    6476             :         }
    6477          38 :         for (int32_t i = start; i < end; ++i) {
    6478             :           // Favour normal selection over IME selections
    6479          37 :           if (!prevailingSelections[i] ||
    6480           0 :               selectionType < prevailingSelections[i]->mSelectionType) {
    6481          37 :             prevailingSelections[i] = sdptr;
    6482             :           }
    6483             :         }
    6484             :       }
    6485             :     }
    6486             :   }
    6487           1 :   *aAllRawSelectionTypes = allRawSelectionTypes;
    6488             : 
    6489           1 :   if (!allRawSelectionTypes) {
    6490             :     // Nothing is selected in the given text range. XXX can this still occur?
    6491           0 :     return false;
    6492             :   }
    6493             : 
    6494           1 :   bool vertical = mTextRun->IsVertical();
    6495           2 :   const gfxFloat startIOffset = vertical ?
    6496           0 :     aParams.textBaselinePt.y - aParams.framePt.y :
    6497           2 :     aParams.textBaselinePt.x - aParams.framePt.x;
    6498             :   gfxFloat iOffset, hyphenWidth;
    6499           1 :   Range range; // in transformed string
    6500           1 :   TextRangeStyle rangeStyle;
    6501             :   // Draw background colors
    6502           1 :   if (anyBackgrounds && !aParams.IsGenerateTextMask()) {
    6503             :     int32_t appUnitsPerDevPixel =
    6504           0 :       aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel();
    6505             :     SelectionIterator iterator(prevailingSelections, contentRange,
    6506           0 :                                *aParams.provider, mTextRun, startIOffset);
    6507             :     SelectionType selectionType;
    6508           0 :     while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
    6509             :                                    &selectionType, &rangeStyle)) {
    6510             :       nscolor foreground, background;
    6511           0 :       GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
    6512           0 :                              rangeStyle, &foreground, &background);
    6513             :       // Draw background color
    6514           0 :       gfxFloat advance = hyphenWidth +
    6515           0 :         mTextRun->GetAdvanceWidth(range, aParams.provider);
    6516           0 :       if (NS_GET_A(background) > 0) {
    6517           0 :         nsRect bgRect;
    6518           0 :         gfxFloat offs = iOffset - (mTextRun->IsInlineReversed() ? advance : 0);
    6519           0 :         if (vertical) {
    6520           0 :           bgRect = nsRect(aParams.framePt.x, aParams.framePt.y + offs,
    6521           0 :                           GetSize().width, advance);
    6522             :         } else {
    6523           0 :           bgRect = nsRect(aParams.framePt.x + offs, aParams.framePt.y,
    6524           0 :                           advance, GetSize().height);
    6525             :         }
    6526           0 :         PaintSelectionBackground(
    6527           0 :           *aParams.context->GetDrawTarget(), background, aParams.dirtyRect,
    6528           0 :           LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel),
    6529           0 :           aParams.callbacks);
    6530             :       }
    6531           0 :       iterator.UpdateWithAdvance(advance);
    6532             :     }
    6533             :   }
    6534             : 
    6535           1 :   if (aParams.IsPaintBGColor()) {
    6536           0 :     return true;
    6537             :   }
    6538             : 
    6539             :   gfxFloat advance;
    6540           1 :   DrawTextParams params(aParams.context);
    6541           1 :   params.dirtyRect = aParams.dirtyRect;
    6542           1 :   params.framePt = aParams.framePt;
    6543           1 :   params.provider = aParams.provider;
    6544           1 :   params.textStyle = aParams.textPaintStyle;
    6545           1 :   params.clipEdges = &aClipEdges;
    6546           1 :   params.advanceWidth = &advance;
    6547           1 :   params.callbacks = aParams.callbacks;
    6548             : 
    6549           1 :   PaintShadowParams shadowParams(aParams);
    6550           1 :   shadowParams.provider = aParams.provider;
    6551           1 :   shadowParams.clipEdges = &aClipEdges;
    6552             : 
    6553             :   // Draw text
    6554           1 :   const nsStyleText* textStyle = StyleText();
    6555             :   SelectionIterator iterator(prevailingSelections, contentRange,
    6556           2 :                              *aParams.provider, mTextRun, startIOffset);
    6557             :   SelectionType selectionType;
    6558           5 :   while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
    6559             :                                  &selectionType, &rangeStyle)) {
    6560             :     nscolor foreground, background;
    6561           2 :     if (aParams.IsGenerateTextMask()) {
    6562           0 :       foreground = NS_RGBA(0, 0, 0, 255);
    6563             :     } else {
    6564           2 :       GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
    6565           2 :                              rangeStyle, &foreground, &background);
    6566             :     }
    6567             : 
    6568             :     gfxPoint textBaselinePt = vertical ?
    6569           0 :       gfxPoint(aParams.textBaselinePt.x, aParams.framePt.y + iOffset) :
    6570           2 :       gfxPoint(aParams.framePt.x + iOffset, aParams.textBaselinePt.y);
    6571             : 
    6572             :     // Determine what shadow, if any, to draw - either from textStyle
    6573             :     // or from the ::-moz-selection pseudo-class if specified there
    6574           2 :     nsCSSShadowArray* shadow = textStyle->GetTextShadow();
    6575           4 :     GetSelectionTextShadow(this, selectionType, *aParams.textPaintStyle,
    6576           4 :                            &shadow);
    6577           2 :     if (shadow) {
    6578           0 :       nscoord startEdge = iOffset;
    6579           0 :       if (mTextRun->IsInlineReversed()) {
    6580           0 :         startEdge -= hyphenWidth +
    6581           0 :           mTextRun->GetAdvanceWidth(range, aParams.provider);
    6582             :       }
    6583           0 :       shadowParams.range = range;
    6584           0 :       shadowParams.textBaselinePt = textBaselinePt;
    6585           0 :       shadowParams.foregroundColor = foreground;
    6586           0 :       shadowParams.leftSideOffset = startEdge;
    6587           0 :       PaintShadows(shadow, shadowParams);
    6588             :     }
    6589             : 
    6590             :     // Draw text segment
    6591           2 :     params.textColor = foreground;
    6592           2 :     params.textStrokeColor = aParams.textPaintStyle->GetWebkitTextStrokeColor();
    6593           2 :     params.textStrokeWidth = aParams.textPaintStyle->GetWebkitTextStrokeWidth();
    6594           2 :     params.drawSoftHyphen = hyphenWidth > 0;
    6595           2 :     DrawText(range, textBaselinePt, params);
    6596           2 :     advance += hyphenWidth;
    6597           2 :     iterator.UpdateWithAdvance(advance);
    6598             :   }
    6599           1 :   return true;
    6600             : }
    6601             : 
    6602             : void
    6603           0 : nsTextFrame::PaintTextSelectionDecorations(
    6604             :     const PaintTextSelectionParams& aParams,
    6605             :     const UniquePtr<SelectionDetails>& aDetails,
    6606             :     SelectionType aSelectionType)
    6607             : {
    6608             :   // Hide text decorations if we're currently hiding @font-face fallback text
    6609           0 :   if (aParams.provider->GetFontGroup()->ShouldSkipDrawing())
    6610           0 :     return;
    6611             : 
    6612             :   // Figure out which characters will be decorated for this selection.
    6613             :   // Note: selectedCharsBuffer is keeping extra raw pointers to
    6614             :   // uniquely-owned resources, but it's safe because it's temporary and the
    6615             :   // resources are owned by the caller. Therefore, they'll outlive this object.
    6616           0 :   const gfxTextRun::Range& contentRange = aParams.contentRange;
    6617           0 :   AutoTArray<SelectionDetails*, BIG_TEXT_NODE_SIZE> selectedCharsBuffer;
    6618             :   SelectionDetails** selectedChars =
    6619           0 :     selectedCharsBuffer.AppendElements(contentRange.Length(), fallible);
    6620           0 :   if (!selectedChars) {
    6621           0 :     return;
    6622             :   }
    6623           0 :   for (uint32_t i = 0; i < contentRange.Length(); ++i) {
    6624           0 :     selectedChars[i] = nullptr;
    6625             :   }
    6626             : 
    6627           0 :   for (SelectionDetails* sdptr = aDetails.get(); sdptr; sdptr = sdptr->mNext.get()) {
    6628           0 :     if (sdptr->mSelectionType == aSelectionType) {
    6629           0 :       int32_t start = std::max(0, sdptr->mStart - int32_t(contentRange.start));
    6630           0 :       int32_t end = std::min(int32_t(contentRange.Length()),
    6631           0 :                              sdptr->mEnd - int32_t(contentRange.start));
    6632           0 :       for (int32_t i = start; i < end; ++i) {
    6633           0 :         selectedChars[i] = sdptr;
    6634             :       }
    6635             :     }
    6636             :   }
    6637             : 
    6638           0 :   gfxFont* firstFont = aParams.provider->GetFontGroup()->GetFirstValidFont();
    6639           0 :   bool verticalRun = mTextRun->IsVertical();
    6640           0 :   bool rightUnderline = verticalRun && IsUnderlineRight(this);
    6641             :   const uint8_t kDecoration =
    6642             :     rightUnderline ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE :
    6643           0 :                      NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
    6644           0 :   bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
    6645             :   gfxFont::Metrics
    6646             :     decorationMetrics(firstFont->GetMetrics(useVerticalMetrics ?
    6647           0 :       gfxFont::eVertical : gfxFont::eHorizontal));
    6648           0 :   if (!useVerticalMetrics) {
    6649             :     // The potential adjustment from using gfxFontGroup::GetUnderlineOffset
    6650             :     // is only valid for horizontal font metrics.
    6651           0 :     decorationMetrics.underlineOffset =
    6652           0 :       aParams.provider->GetFontGroup()->GetUnderlineOffset();
    6653             :   }
    6654             : 
    6655           0 :   gfxFloat startIOffset = verticalRun ?
    6656           0 :     aParams.textBaselinePt.y - aParams.framePt.y :
    6657           0 :     aParams.textBaselinePt.x - aParams.framePt.x;
    6658             :   SelectionIterator iterator(selectedChars, contentRange,
    6659           0 :                              *aParams.provider, mTextRun, startIOffset);
    6660             :   gfxFloat iOffset, hyphenWidth;
    6661           0 :   Range range;
    6662           0 :   int32_t app = aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel();
    6663             :   // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
    6664           0 :   Point pt;
    6665           0 :   if (verticalRun) {
    6666           0 :     pt.x = (aParams.textBaselinePt.x - mAscent) / app;
    6667             :   } else {
    6668           0 :     pt.y = (aParams.textBaselinePt.y - mAscent) / app;
    6669             :   }
    6670           0 :   gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
    6671             :   SelectionType nextSelectionType;
    6672           0 :   TextRangeStyle selectedStyle;
    6673           0 :   while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
    6674             :                                  &nextSelectionType, &selectedStyle)) {
    6675           0 :     gfxFloat advance = hyphenWidth +
    6676           0 :       mTextRun->GetAdvanceWidth(range, aParams.provider);
    6677           0 :     if (nextSelectionType == aSelectionType) {
    6678           0 :       if (verticalRun) {
    6679           0 :         pt.y = (aParams.framePt.y + iOffset -
    6680           0 :                (mTextRun->IsInlineReversed() ? advance : 0)) / app;
    6681             :       } else {
    6682           0 :         pt.x = (aParams.framePt.x + iOffset -
    6683           0 :                (mTextRun->IsInlineReversed() ? advance : 0)) / app;
    6684             :       }
    6685           0 :       gfxFloat width = Abs(advance) / app;
    6686           0 :       gfxFloat xInFrame = pt.x - (aParams.framePt.x / app);
    6687           0 :       DrawSelectionDecorations(
    6688           0 :         aParams.context, aParams.dirtyRect, aSelectionType,
    6689           0 :         *aParams.textPaintStyle, selectedStyle, pt, xInFrame,
    6690           0 :         width, mAscent / app, decorationMetrics, aParams.callbacks,
    6691           0 :         verticalRun, decorationOffsetDir, kDecoration);
    6692             :     }
    6693           0 :     iterator.UpdateWithAdvance(advance);
    6694             :   }
    6695             : }
    6696             : 
    6697             : bool
    6698           1 : nsTextFrame::PaintTextWithSelection(
    6699             :     const PaintTextSelectionParams& aParams,
    6700             :     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
    6701             : {
    6702           1 :   NS_ASSERTION(GetContent()->IsSelectionDescendant(), "wrong paint path");
    6703             : 
    6704           2 :   UniquePtr<SelectionDetails> details = GetSelectionDetails();
    6705           1 :   if (!details) {
    6706           0 :     return false;
    6707             :   }
    6708             : 
    6709             :   RawSelectionType allRawSelectionTypes;
    6710           1 :   if (!PaintTextWithSelectionColors(aParams, details, &allRawSelectionTypes,
    6711             :                                     aClipEdges)) {
    6712           0 :     return false;
    6713             :   }
    6714             :   // Iterate through just the selection rawSelectionTypes that paint decorations
    6715             :   // and paint decorations for any that actually occur in this frame. Paint
    6716             :   // higher-numbered selection rawSelectionTypes below lower-numered ones on the
    6717             :   // general principal that lower-numbered selections are higher priority.
    6718           1 :   allRawSelectionTypes &= kRawSelectionTypesWithDecorations;
    6719          11 :   for (size_t i = kSelectionTypeCount - 1; i >= 1; --i) {
    6720          10 :     SelectionType selectionType = ToSelectionType(1 << (i - 1));
    6721          10 :     if (selectionType & allRawSelectionTypes) {
    6722             :       // There is some selection of this selectionType. Try to paint its
    6723             :       // decorations (there might not be any for this type but that's OK,
    6724             :       // PaintTextSelectionDecorations will exit early).
    6725           0 :       PaintTextSelectionDecorations(aParams, details, selectionType);
    6726             :     }
    6727             :   }
    6728             : 
    6729           1 :   return true;
    6730             : }
    6731             : 
    6732             : void
    6733           0 : nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM,
    6734             :                                const gfxPoint& aTextBaselinePt,
    6735             :                                const gfxPoint& aFramePt, Range aRange,
    6736             :                                const nscolor* aDecorationOverrideColor,
    6737             :                                PropertyProvider* aProvider)
    6738             : {
    6739           0 :   const EmphasisMarkInfo* info = GetProperty(EmphasisMarkProperty());
    6740           0 :   if (!info) {
    6741           0 :     return;
    6742             :   }
    6743             : 
    6744           0 :   bool isTextCombined = StyleContext()->IsTextCombined();
    6745           0 :   nscolor color = aDecorationOverrideColor ? *aDecorationOverrideColor :
    6746           0 :     nsLayoutUtils::GetColor(this, &nsStyleText::mTextEmphasisColor);
    6747           0 :   aContext->SetColor(Color::FromABGR(color));
    6748           0 :   gfxPoint pt;
    6749           0 :   if (!isTextCombined) {
    6750           0 :     pt = aTextBaselinePt;
    6751             :   } else {
    6752           0 :     MOZ_ASSERT(aWM.IsVertical());
    6753           0 :     pt = aFramePt;
    6754           0 :     if (aWM.IsVerticalRL()) {
    6755           0 :       pt.x += GetSize().width - GetLogicalBaseline(aWM);
    6756             :     } else {
    6757           0 :       pt.x += GetLogicalBaseline(aWM);
    6758             :     }
    6759             :   }
    6760           0 :   if (!aWM.IsVertical()) {
    6761           0 :     pt.y += info->baselineOffset;
    6762             :   } else {
    6763           0 :     if (aWM.IsVerticalRL()) {
    6764           0 :       pt.x -= info->baselineOffset;
    6765             :     } else {
    6766           0 :       pt.x += info->baselineOffset;
    6767             :     }
    6768             :   }
    6769           0 :   if (!isTextCombined) {
    6770           0 :     mTextRun->DrawEmphasisMarks(aContext, info->textRun.get(), info->advance,
    6771           0 :                                 pt, aRange, aProvider);
    6772             :   } else {
    6773           0 :     pt.y += (GetSize().height - info->advance) / 2;
    6774           0 :     info->textRun->Draw(Range(info->textRun.get()), pt,
    6775           0 :                         gfxTextRun::DrawParams(aContext));
    6776             :   }
    6777             : }
    6778             : 
    6779             : nscolor
    6780           0 : nsTextFrame::GetCaretColorAt(int32_t aOffset)
    6781             : {
    6782           0 :   NS_PRECONDITION(aOffset >= 0, "aOffset must be positive");
    6783             : 
    6784           0 :   nscolor result = nsFrame::GetCaretColorAt(aOffset);
    6785           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    6786           0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    6787           0 :   int32_t contentOffset = provider.GetStart().GetOriginalOffset();
    6788           0 :   int32_t contentLength = provider.GetOriginalLength();
    6789           0 :   NS_PRECONDITION(aOffset >= contentOffset &&
    6790             :                   aOffset <= contentOffset + contentLength,
    6791             :                   "aOffset must be in the frame's range");
    6792           0 :   int32_t offsetInFrame = aOffset - contentOffset;
    6793           0 :   if (offsetInFrame < 0 || offsetInFrame >= contentLength) {
    6794           0 :     return result;
    6795             :   }
    6796             : 
    6797           0 :   bool isSolidTextColor = true;
    6798           0 :   if (nsSVGUtils::IsInSVGTextSubtree(this)) {
    6799           0 :     const nsStyleSVG* style = StyleSVG();
    6800           0 :     if (style->mFill.Type() != eStyleSVGPaintType_None &&
    6801           0 :         style->mFill.Type() != eStyleSVGPaintType_Color) {
    6802           0 :       isSolidTextColor = false;
    6803             :     }
    6804             :   }
    6805             : 
    6806           0 :   nsTextPaintStyle textPaintStyle(this);
    6807           0 :   textPaintStyle.SetResolveColors(isSolidTextColor);
    6808           0 :   UniquePtr<SelectionDetails> details = GetSelectionDetails();
    6809           0 :   SelectionType selectionType = SelectionType::eNone;
    6810           0 :   for (SelectionDetails* sdptr = details.get(); sdptr; sdptr = sdptr->mNext.get()) {
    6811           0 :     int32_t start = std::max(0, sdptr->mStart - contentOffset);
    6812           0 :     int32_t end = std::min(contentLength, sdptr->mEnd - contentOffset);
    6813           0 :     if (start <= offsetInFrame && offsetInFrame < end &&
    6814           0 :         (selectionType == SelectionType::eNone ||
    6815           0 :          sdptr->mSelectionType < selectionType)) {
    6816             :       nscolor foreground, background;
    6817           0 :       if (GetSelectionTextColors(sdptr->mSelectionType, textPaintStyle,
    6818             :                                  sdptr->mTextRangeStyle,
    6819             :                                  &foreground, &background)) {
    6820           0 :         if (!isSolidTextColor &&
    6821           0 :             NS_IS_SELECTION_SPECIAL_COLOR(foreground)) {
    6822           0 :           result = NS_RGBA(0, 0, 0, 255);
    6823             :         } else {
    6824           0 :           result = foreground;
    6825             :         }
    6826           0 :         selectionType = sdptr->mSelectionType;
    6827             :       }
    6828             :     }
    6829             :   }
    6830             : 
    6831           0 :   return result;
    6832             : }
    6833             : 
    6834             : static gfxTextRun::Range
    6835          17 : ComputeTransformedRange(PropertyProvider& aProvider)
    6836             : {
    6837          17 :   gfxSkipCharsIterator iter(aProvider.GetStart());
    6838          17 :   uint32_t start = iter.GetSkippedOffset();
    6839          17 :   iter.AdvanceOriginal(aProvider.GetOriginalLength());
    6840          17 :   return gfxTextRun::Range(start, iter.GetSkippedOffset());
    6841             : }
    6842             : 
    6843             : bool
    6844           0 : nsTextFrame::MeasureCharClippedText(nscoord aVisIStartEdge,
    6845             :                                     nscoord aVisIEndEdge,
    6846             :                                     nscoord* aSnappedStartEdge,
    6847             :                                     nscoord* aSnappedEndEdge)
    6848             : {
    6849             :   // We need a *reference* rendering context (not one that might have a
    6850             :   // transform), so we don't have a rendering context argument.
    6851             :   // XXX get the block and line passed to us somehow! This is slow!
    6852           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    6853           0 :   if (!mTextRun)
    6854           0 :     return false;
    6855             : 
    6856           0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    6857             :   // Trim trailing whitespace
    6858           0 :   provider.InitializeForDisplay(true);
    6859             : 
    6860           0 :   Range range = ComputeTransformedRange(provider);
    6861           0 :   uint32_t startOffset = range.start;
    6862           0 :   uint32_t maxLength = range.Length();
    6863             :   return MeasureCharClippedText(provider, aVisIStartEdge, aVisIEndEdge,
    6864             :                                 &startOffset, &maxLength,
    6865           0 :                                 aSnappedStartEdge, aSnappedEndEdge);
    6866             : }
    6867             : 
    6868           0 : static uint32_t GetClusterLength(const gfxTextRun* aTextRun,
    6869             :                                  uint32_t    aStartOffset,
    6870             :                                  uint32_t    aMaxLength,
    6871             :                                  bool        aIsRTL)
    6872             : {
    6873           0 :   uint32_t clusterLength = aIsRTL ? 0 : 1;
    6874           0 :   while (clusterLength < aMaxLength) {
    6875           0 :     if (aTextRun->IsClusterStart(aStartOffset + clusterLength)) {
    6876           0 :       if (aIsRTL) {
    6877           0 :         ++clusterLength;
    6878             :       }
    6879           0 :       break;
    6880             :     }
    6881           0 :     ++clusterLength;
    6882             :   }
    6883           0 :   return clusterLength;
    6884             : }
    6885             : 
    6886             : bool
    6887          17 : nsTextFrame::MeasureCharClippedText(PropertyProvider& aProvider,
    6888             :                                     nscoord aVisIStartEdge,
    6889             :                                     nscoord aVisIEndEdge,
    6890             :                                     uint32_t* aStartOffset,
    6891             :                                     uint32_t* aMaxLength,
    6892             :                                     nscoord*  aSnappedStartEdge,
    6893             :                                     nscoord*  aSnappedEndEdge)
    6894             : {
    6895          17 :   *aSnappedStartEdge = 0;
    6896          17 :   *aSnappedEndEdge = 0;
    6897          17 :   if (aVisIStartEdge <= 0 && aVisIEndEdge <= 0) {
    6898          17 :     return true;
    6899             :   }
    6900             : 
    6901           0 :   uint32_t offset = *aStartOffset;
    6902           0 :   uint32_t maxLength = *aMaxLength;
    6903           0 :   const nscoord frameISize = ISize();
    6904           0 :   const bool rtl = mTextRun->IsRightToLeft();
    6905           0 :   gfxFloat advanceWidth = 0;
    6906           0 :   const nscoord startEdge = rtl ? aVisIEndEdge : aVisIStartEdge;
    6907           0 :   if (startEdge > 0) {
    6908           0 :     const gfxFloat maxAdvance = gfxFloat(startEdge);
    6909           0 :     while (maxLength > 0) {
    6910             :       uint32_t clusterLength =
    6911           0 :         GetClusterLength(mTextRun, offset, maxLength, rtl);
    6912           0 :       advanceWidth += mTextRun->
    6913           0 :         GetAdvanceWidth(Range(offset, offset + clusterLength), &aProvider);
    6914           0 :       maxLength -= clusterLength;
    6915           0 :       offset += clusterLength;
    6916           0 :       if (advanceWidth >= maxAdvance) {
    6917           0 :         break;
    6918             :       }
    6919             :     }
    6920           0 :     nscoord* snappedStartEdge = rtl ? aSnappedEndEdge : aSnappedStartEdge;
    6921           0 :     *snappedStartEdge = NSToCoordFloor(advanceWidth);
    6922           0 :     *aStartOffset = offset;
    6923             :   }
    6924             : 
    6925           0 :   const nscoord endEdge = rtl ? aVisIStartEdge : aVisIEndEdge;
    6926           0 :   if (endEdge > 0) {
    6927           0 :     const gfxFloat maxAdvance = gfxFloat(frameISize - endEdge);
    6928           0 :     while (maxLength > 0) {
    6929             :       uint32_t clusterLength =
    6930           0 :         GetClusterLength(mTextRun, offset, maxLength, rtl);
    6931           0 :       gfxFloat nextAdvance = advanceWidth + mTextRun->GetAdvanceWidth(
    6932           0 :           Range(offset, offset + clusterLength), &aProvider);
    6933           0 :       if (nextAdvance > maxAdvance) {
    6934           0 :         break;
    6935             :       }
    6936             :       // This cluster fits, include it.
    6937           0 :       advanceWidth = nextAdvance;
    6938           0 :       maxLength -= clusterLength;
    6939           0 :       offset += clusterLength;
    6940             :     }
    6941           0 :     maxLength = offset - *aStartOffset;
    6942           0 :     nscoord* snappedEndEdge = rtl ? aSnappedStartEdge : aSnappedEndEdge;
    6943           0 :     *snappedEndEdge = NSToCoordFloor(gfxFloat(frameISize) - advanceWidth);
    6944             :   }
    6945           0 :   *aMaxLength = maxLength;
    6946           0 :   return maxLength != 0;
    6947             : }
    6948             : 
    6949             : void
    6950          16 : nsTextFrame::PaintShadows(nsCSSShadowArray* aShadow,
    6951             :                           const PaintShadowParams& aParams)
    6952             : {
    6953          16 :   if (!aShadow) {
    6954          16 :     return;
    6955             :   }
    6956             : 
    6957             :   gfxTextRun::Metrics shadowMetrics =
    6958             :     mTextRun->MeasureText(aParams.range, gfxFont::LOOSE_INK_EXTENTS,
    6959           0 :                           nullptr, aParams.provider);
    6960           0 :   if (GetWritingMode().IsLineInverted()) {
    6961           0 :     Swap(shadowMetrics.mAscent, shadowMetrics.mDescent);
    6962           0 :     shadowMetrics.mBoundingBox.y = -shadowMetrics.mBoundingBox.YMost();
    6963             :   }
    6964           0 :   if (GetStateBits() & TEXT_HYPHEN_BREAK) {
    6965           0 :     AddHyphenToMetrics(this, mTextRun, &shadowMetrics,
    6966             :                        gfxFont::LOOSE_INK_EXTENTS,
    6967           0 :                        aParams.context->GetDrawTarget());
    6968             :   }
    6969             :   // Add bounds of text decorations
    6970           0 :   gfxRect decorationRect(0, -shadowMetrics.mAscent,
    6971           0 :       shadowMetrics.mAdvanceWidth, shadowMetrics.mAscent + shadowMetrics.mDescent);
    6972             :   shadowMetrics.mBoundingBox.UnionRect(shadowMetrics.mBoundingBox,
    6973           0 :                                        decorationRect);
    6974             : 
    6975             :   // If the textrun uses any color or SVG fonts, we need to force use of a mask
    6976             :   // for shadow rendering even if blur radius is zero.
    6977           0 :   uint32_t blurFlags = 0;
    6978             :   uint32_t numGlyphRuns;
    6979           0 :   const gfxTextRun::GlyphRun* run = mTextRun->GetGlyphRuns(&numGlyphRuns);
    6980           0 :   while (numGlyphRuns-- > 0) {
    6981           0 :     if (run->mFont->AlwaysNeedsMaskForShadow()) {
    6982           0 :       blurFlags = nsContextBoxBlur::FORCE_MASK;
    6983           0 :       break;
    6984             :     }
    6985           0 :     run++;
    6986             :   }
    6987             : 
    6988           0 :   if (mTextRun->IsVertical()) {
    6989           0 :     Swap(shadowMetrics.mBoundingBox.x, shadowMetrics.mBoundingBox.y);
    6990           0 :     Swap(shadowMetrics.mBoundingBox.width, shadowMetrics.mBoundingBox.height);
    6991             :   }
    6992             : 
    6993           0 :   for (uint32_t i = aShadow->Length(); i > 0; --i) {
    6994           0 :     PaintOneShadow(aParams, aShadow->ShadowAt(i - 1),
    6995           0 :                    shadowMetrics.mBoundingBox, blurFlags);
    6996             :   }
    6997             : }
    6998             : 
    6999             : static bool
    7000          18 : ShouldDrawSelection(const nsIFrame* aFrame)
    7001             : {
    7002             :   // Normal text-with-selection rendering sequence is:
    7003             :   //   * Paint background > Paint text-selection-color > Paint text
    7004             :   // When we have an parent frame with background-clip-text style, rendering
    7005             :   // sequence changes to:
    7006             :   //   * Paint text-selection-color > Paint background > Paint text
    7007             :   //
    7008             :   // If there is a parent frame has background-clip:text style,
    7009             :   // text-selection-color should be drawn with the background of that parent
    7010             :   // frame, so we should not draw it again while painting text frames.
    7011             : 
    7012          18 :   if (!aFrame) {
    7013           1 :     return true;
    7014             :   }
    7015             : 
    7016          17 :   const nsStyleBackground* bg = aFrame->StyleContext()->StyleBackground();
    7017          17 :   const nsStyleImageLayers& layers = bg->mImage;
    7018          34 :   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
    7019          17 :     if (layers.mLayers[i].mClip == StyleGeometryBox::Text) {
    7020           0 :       return false;
    7021             :     }
    7022             :   }
    7023             : 
    7024          17 :   return ShouldDrawSelection(aFrame->GetParent());
    7025             : }
    7026             : 
    7027             : void
    7028          17 : nsTextFrame::PaintText(const PaintTextParams& aParams,
    7029             :                        const nsCharClipDisplayItem& aItem,
    7030             :                        float aOpacity /* = 1.0f */)
    7031             : {
    7032             :   // Don't pass in the rendering context here, because we need a
    7033             :   // *reference* context and rendering context might have some transform
    7034             :   // in it
    7035             :   // XXX get the block and line passed to us somehow! This is slow!
    7036          17 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    7037          17 :   if (!mTextRun)
    7038           1 :     return;
    7039             : 
    7040          33 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    7041          17 :   if (aItem.mIsFrameSelected.isNothing()) {
    7042          17 :     aItem.mIsFrameSelected.emplace(IsSelected());
    7043             :   }
    7044             :   // Trim trailing whitespace, unless we're painting a selection highlight,
    7045             :   // which should include trailing spaces if present (bug 1146754).
    7046          17 :   provider.InitializeForDisplay(!aItem.mIsFrameSelected.value());
    7047             : 
    7048          17 :   const bool reversed = mTextRun->IsInlineReversed();
    7049          17 :   const bool verticalRun = mTextRun->IsVertical();
    7050          17 :   WritingMode wm = GetWritingMode();
    7051          17 :   const gfxFloat frameWidth = GetSize().width;
    7052          17 :   const gfxFloat frameHeight = GetSize().height;
    7053          17 :   gfxPoint textBaselinePt;
    7054          17 :   if (verticalRun) {
    7055           0 :     if (wm.IsVerticalLR()) {
    7056           0 :       textBaselinePt.x = nsLayoutUtils::GetSnappedBaselineX(
    7057           0 :         this, aParams.context, nscoord(aParams.framePt.x), mAscent);
    7058             :     } else {
    7059           0 :       textBaselinePt.x = nsLayoutUtils::GetSnappedBaselineX(
    7060           0 :         this, aParams.context, nscoord(aParams.framePt.x) + frameWidth,
    7061           0 :         -mAscent);
    7062             :     }
    7063           0 :     textBaselinePt.y = reversed ? aParams.framePt.y + frameHeight
    7064             :                                 : aParams.framePt.y;
    7065             :   } else {
    7066          17 :     textBaselinePt =
    7067          68 :       gfxPoint(reversed ? aParams.framePt.x + frameWidth : aParams.framePt.x,
    7068             :                nsLayoutUtils::GetSnappedBaselineY(
    7069          34 :                  this, aParams.context, aParams.framePt.y, mAscent));
    7070             :   }
    7071          17 :   Range range = ComputeTransformedRange(provider);
    7072          17 :   uint32_t startOffset = range.start;
    7073          17 :   uint32_t maxLength = range.Length();
    7074             :   nscoord snappedStartEdge, snappedEndEdge;
    7075          17 :   if (!MeasureCharClippedText(provider, aItem.mVisIStartEdge, aItem.mVisIEndEdge,
    7076             :          &startOffset, &maxLength, &snappedStartEdge, &snappedEndEdge)) {
    7077           0 :     return;
    7078             :   }
    7079          17 :   if (verticalRun) {
    7080           0 :     textBaselinePt.y += reversed ? -snappedEndEdge : snappedStartEdge;
    7081             :   } else {
    7082          17 :     textBaselinePt.x += reversed ? -snappedEndEdge : snappedStartEdge;
    7083             :   }
    7084             :   nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedStartEdge,
    7085          17 :                                              snappedEndEdge);
    7086          33 :   nsTextPaintStyle textPaintStyle(this);
    7087          17 :   textPaintStyle.SetResolveColors(!aParams.callbacks);
    7088             : 
    7089             :   // Fork off to the (slower) paint-with-selection path if necessary.
    7090          19 :   if (aItem.mIsFrameSelected.value() &&
    7091           2 :       (aParams.IsPaintBGColor() || ShouldDrawSelection(this->GetParent()))) {
    7092           1 :     MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!");
    7093           1 :     gfxSkipCharsIterator tmp(provider.GetStart());
    7094             :     Range contentRange(
    7095           1 :       uint32_t(tmp.ConvertSkippedToOriginal(startOffset)),
    7096           2 :       uint32_t(tmp.ConvertSkippedToOriginal(startOffset + maxLength)));
    7097           1 :     PaintTextSelectionParams params(aParams);
    7098           1 :     params.textBaselinePt = textBaselinePt;
    7099           1 :     params.provider = &provider;
    7100           1 :     params.contentRange = contentRange;
    7101           1 :     params.textPaintStyle = &textPaintStyle;
    7102           1 :     if (PaintTextWithSelection(params, clipEdges)) {
    7103           1 :       return;
    7104             :     }
    7105             :   }
    7106             : 
    7107          16 :   if (aParams.IsPaintBGColor()) {
    7108           0 :     return;
    7109             :   }
    7110             : 
    7111          16 :   nscolor foregroundColor = aParams.IsGenerateTextMask()
    7112          16 :                             ? NS_RGBA(0, 0, 0, 255)
    7113          16 :                             : textPaintStyle.GetTextColor();
    7114          16 :   if (aOpacity != 1.0f) {
    7115           8 :     gfx::Color gfxColor = gfx::Color::FromABGR(foregroundColor);
    7116           8 :     gfxColor.a *= aOpacity;
    7117           8 :     foregroundColor = gfxColor.ToABGR();
    7118             :   }
    7119             : 
    7120          16 :   nscolor textStrokeColor = aParams.IsGenerateTextMask()
    7121          16 :                             ? NS_RGBA(0, 0, 0, 255)
    7122          16 :                             : textPaintStyle.GetWebkitTextStrokeColor();
    7123          16 :   if (aOpacity != 1.0f) {
    7124           8 :     gfx::Color gfxColor = gfx::Color::FromABGR(textStrokeColor);
    7125           8 :     gfxColor.a *= aOpacity;
    7126           8 :     textStrokeColor = gfxColor.ToABGR();
    7127             :   }
    7128             : 
    7129          16 :   range = Range(startOffset, startOffset + maxLength);
    7130          16 :   if (!aParams.callbacks && aParams.IsPaintText()) {
    7131          16 :     const nsStyleText* textStyle = StyleText();
    7132          16 :     PaintShadowParams shadowParams(aParams);
    7133          16 :     shadowParams.range = range;
    7134          16 :     shadowParams.textBaselinePt = textBaselinePt;
    7135          16 :     shadowParams.leftSideOffset = snappedStartEdge;
    7136          16 :     shadowParams.provider = &provider;
    7137          16 :     shadowParams.foregroundColor = foregroundColor;
    7138          16 :     shadowParams.clipEdges = &clipEdges;
    7139          16 :     PaintShadows(textStyle->mTextShadow, shadowParams);
    7140             :   }
    7141             : 
    7142             :   gfxFloat advanceWidth;
    7143          16 :   DrawTextParams params(aParams.context);
    7144          16 :   params.dirtyRect = aParams.dirtyRect;
    7145          16 :   params.framePt = aParams.framePt;
    7146          16 :   params.provider = &provider;
    7147          16 :   params.advanceWidth = &advanceWidth;
    7148          16 :   params.textStyle = &textPaintStyle;
    7149          16 :   params.textColor = foregroundColor;
    7150          16 :   params.textStrokeColor = textStrokeColor;
    7151          16 :   params.textStrokeWidth = textPaintStyle.GetWebkitTextStrokeWidth();
    7152          16 :   params.clipEdges = &clipEdges;
    7153          16 :   params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
    7154          16 :   params.contextPaint = aParams.contextPaint;
    7155          16 :   params.callbacks = aParams.callbacks;
    7156          16 :   DrawText(range, textBaselinePt, params);
    7157             : }
    7158             : 
    7159             : static void
    7160          18 : DrawTextRun(const gfxTextRun* aTextRun,
    7161             :             const gfxPoint& aTextBaselinePt,
    7162             :             gfxTextRun::Range aRange,
    7163             :             const nsTextFrame::DrawTextRunParams& aParams)
    7164             : {
    7165          18 :   gfxTextRun::DrawParams params(aParams.context);
    7166          18 :   params.provider = aParams.provider;
    7167          18 :   params.advanceWidth = aParams.advanceWidth;
    7168          18 :   params.contextPaint = aParams.contextPaint;
    7169          18 :   params.callbacks = aParams.callbacks;
    7170          18 :   if (aParams.callbacks) {
    7171           0 :     aParams.callbacks->NotifyBeforeText(aParams.textColor);
    7172           0 :     params.drawMode = DrawMode::GLYPH_PATH;
    7173           0 :     aTextRun->Draw(aRange, aTextBaselinePt, params);
    7174           0 :     aParams.callbacks->NotifyAfterText();
    7175             :   } else {
    7176          18 :     if (NS_GET_A(aParams.textColor) != 0) {
    7177             :       // Default drawMode is DrawMode::GLYPH_FILL
    7178          18 :       aParams.context->SetColor(Color::FromABGR(aParams.textColor));
    7179             :     } else {
    7180           0 :       params.drawMode = DrawMode::GLYPH_STROKE;
    7181             :     }
    7182             : 
    7183          36 :     if (NS_GET_A(aParams.textStrokeColor) != 0 &&
    7184          18 :         aParams.textStrokeWidth != 0.0f) {
    7185           0 :       StrokeOptions strokeOpts;
    7186           0 :       params.drawMode |= DrawMode::GLYPH_STROKE;
    7187           0 :       params.textStrokeColor = aParams.textStrokeColor;
    7188           0 :       strokeOpts.mLineWidth = aParams.textStrokeWidth;
    7189           0 :       params.strokeOpts = &strokeOpts;
    7190           0 :       aTextRun->Draw(aRange, aTextBaselinePt, params);
    7191             :     } else {
    7192          18 :       aTextRun->Draw(aRange, aTextBaselinePt, params);
    7193             :     }
    7194             :   }
    7195          18 : }
    7196             : 
    7197             : void
    7198          18 : nsTextFrame::DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt,
    7199             :                          const DrawTextRunParams& aParams)
    7200             : {
    7201          18 :   MOZ_ASSERT(aParams.advanceWidth, "Must provide advanceWidth");
    7202          18 :   ::DrawTextRun(mTextRun, aTextBaselinePt, aRange, aParams);
    7203             : 
    7204          18 :   if (aParams.drawSoftHyphen) {
    7205             :     // Don't use ctx as the context, because we need a reference context here,
    7206             :     // ctx may be transformed.
    7207             :     RefPtr<gfxTextRun> hyphenTextRun =
    7208           0 :       GetHyphenTextRun(mTextRun, nullptr, this);
    7209           0 :     if (hyphenTextRun) {
    7210             :       // For right-to-left text runs, the soft-hyphen is positioned at the left
    7211             :       // of the text, minus its own width
    7212           0 :       gfxFloat hyphenBaselineX = aTextBaselinePt.x +
    7213           0 :         mTextRun->GetDirection() * (*aParams.advanceWidth) -
    7214           0 :         (mTextRun->IsRightToLeft() ? hyphenTextRun->GetAdvanceWidth() : 0);
    7215           0 :       DrawTextRunParams params = aParams;
    7216           0 :       params.provider = nullptr;
    7217           0 :       params.advanceWidth = nullptr;
    7218           0 :       ::DrawTextRun(hyphenTextRun.get(),
    7219           0 :                     gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
    7220           0 :                     Range(hyphenTextRun.get()), params);
    7221             :     }
    7222             :   }
    7223          18 : }
    7224             : 
    7225             : void
    7226           0 : nsTextFrame::DrawTextRunAndDecorations(Range aRange,
    7227             :                                        const gfxPoint& aTextBaselinePt,
    7228             :                                        const DrawTextParams& aParams,
    7229             :                                        const TextDecorations& aDecorations)
    7230             : {
    7231             :     const gfxFloat app =
    7232           0 :       aParams.textStyle->PresContext()->AppUnitsPerDevPixel();
    7233             :     // Writing mode of parent frame is used because the text frame may
    7234             :     // be orthogonal to its parent when text-combine-upright is used or
    7235             :     // its parent has "display: contents", and in those cases, we want
    7236             :     // to draw the decoration lines according to parents' direction
    7237             :     // rather than ours.
    7238           0 :     const WritingMode wm = GetParent()->GetWritingMode();
    7239           0 :     bool verticalDec = wm.IsVertical();
    7240           0 :     bool verticalRun = mTextRun->IsVertical();
    7241             :     // If the text run and the decoration is orthogonal, we choose the
    7242             :     // metrics for decoration so that decoration line won't be broken.
    7243             :     bool useVerticalMetrics = verticalDec != verticalRun
    7244           0 :       ? verticalDec : verticalRun && mTextRun->UseCenterBaseline();
    7245             : 
    7246             :     // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
    7247           0 :     nscoord x = NSToCoordRound(aParams.framePt.x);
    7248           0 :     nscoord y = NSToCoordRound(aParams.framePt.y);
    7249             : 
    7250             :     // 'measure' here is textrun-relative, so for a horizontal run it's the
    7251             :     // width, while for a vertical run it's the height of the decoration
    7252           0 :     const nsSize frameSize = GetSize();
    7253           0 :     nscoord measure = verticalDec ? frameSize.height : frameSize.width;
    7254             : 
    7255           0 :     if (verticalDec) {
    7256           0 :       aParams.clipEdges->Intersect(&y, &measure);
    7257             :     } else {
    7258           0 :       aParams.clipEdges->Intersect(&x, &measure);
    7259             :     }
    7260             : 
    7261             :     // decSize is a textrun-relative size, so its 'width' field is actually
    7262             :     // the run-relative measure, and 'height' will be the line thickness
    7263           0 :     gfxFloat ascent = gfxFloat(GetLogicalBaseline(wm)) / app;
    7264             :     // The starting edge of the frame in block direction
    7265           0 :     gfxFloat frameBStart = verticalDec ? aParams.framePt.x : aParams.framePt.y;
    7266             : 
    7267             :     // In vertical-rl mode, block coordinates are measured from the
    7268             :     // right, so we need to adjust here.
    7269           0 :     if (wm.IsVerticalRL()) {
    7270           0 :       frameBStart += frameSize.width;
    7271           0 :       ascent = -ascent;
    7272             :     }
    7273             : 
    7274             :     nscoord inflationMinFontSize =
    7275           0 :       nsLayoutUtils::InflationMinFontSizeFor(this);
    7276             : 
    7277             :     // The decoration-line offsets need to be reversed for sideways-lr mode,
    7278             :     // so we will multiply the values from metrics by this factor.
    7279           0 :     gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
    7280             : 
    7281           0 :     PaintDecorationLineParams params;
    7282           0 :     params.context = aParams.context;
    7283           0 :     params.dirtyRect = aParams.dirtyRect;
    7284           0 :     params.overrideColor = aParams.decorationOverrideColor;
    7285           0 :     params.callbacks = aParams.callbacks;
    7286             :     // pt is the physical point where the decoration is to be drawn,
    7287             :     // relative to the frame; one of its coordinates will be updated below.
    7288           0 :     params.pt = Point(x / app, y / app);
    7289           0 :     Float& bCoord = verticalDec ? params.pt.x : params.pt.y;
    7290           0 :     params.lineSize = Size(measure / app, 0);
    7291           0 :     params.ascent = ascent;
    7292           0 :     params.vertical = verticalDec;
    7293             : 
    7294             :     // The matrix of the context may have been altered for text-combine-
    7295             :     // upright. However, we want to draw decoration lines unscaled, thus
    7296             :     // we need to revert the scaling here.
    7297           0 :     gfxContextMatrixAutoSaveRestore scaledRestorer;
    7298           0 :     if (StyleContext()->IsTextCombined()) {
    7299           0 :       float scaleFactor = GetTextCombineScaleFactor(this);
    7300           0 :       if (scaleFactor != 1.0f) {
    7301           0 :         scaledRestorer.SetContext(aParams.context);
    7302           0 :         gfxMatrix unscaled = aParams.context->CurrentMatrix();
    7303           0 :         gfxPoint pt(x / app, y / app);
    7304           0 :         unscaled.PreTranslate(pt).PreScale(1.0f / scaleFactor, 1.0f).PreTranslate(-pt);
    7305           0 :         aParams.context->SetMatrix(unscaled);
    7306             :       }
    7307             :     }
    7308             : 
    7309             :     typedef gfxFont::Metrics Metrics;
    7310             :     auto paintDecorationLine = [&](const LineDecoration& dec,
    7311             :                                    gfxFloat Metrics::* lineSize,
    7312           0 :                                    gfxFloat Metrics::* lineOffset) {
    7313           0 :       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
    7314           0 :         return;
    7315             :       }
    7316             : 
    7317             :       float inflation =
    7318           0 :         GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
    7319             :       const Metrics metrics =
    7320           0 :         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
    7321           0 :                             useVerticalMetrics);
    7322             : 
    7323           0 :       params.lineSize.height = metrics.*lineSize;
    7324           0 :       bCoord = (frameBStart - dec.mBaselineOffset) / app;
    7325             : 
    7326           0 :       params.color = dec.mColor;
    7327           0 :       params.offset = decorationOffsetDir * metrics.*lineOffset;
    7328           0 :       params.style = dec.mStyle;
    7329           0 :       PaintDecorationLine(params);
    7330           0 :     };
    7331             : 
    7332             :     // Underlines
    7333           0 :     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
    7334           0 :     for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
    7335             :       paintDecorationLine(dec, &Metrics::underlineSize,
    7336           0 :                           &Metrics::underlineOffset);
    7337             :     }
    7338             :     // Overlines
    7339           0 :     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
    7340           0 :     for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
    7341           0 :       paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
    7342             :     }
    7343             : 
    7344             :     {
    7345           0 :       gfxContextMatrixAutoSaveRestore unscaledRestorer;
    7346           0 :       if (scaledRestorer.HasMatrix()) {
    7347           0 :         unscaledRestorer.SetContext(aParams.context);
    7348           0 :         aParams.context->SetMatrix(scaledRestorer.Matrix());
    7349             :       }
    7350             : 
    7351             :       // CSS 2.1 mandates that text be painted after over/underlines,
    7352             :       // and *then* line-throughs
    7353           0 :       DrawTextRun(aRange, aTextBaselinePt, aParams);
    7354             :     }
    7355             : 
    7356             :     // Emphasis marks
    7357           0 :     DrawEmphasisMarks(aParams.context, wm,
    7358             :                       aTextBaselinePt, aParams.framePt, aRange,
    7359           0 :                       aParams.decorationOverrideColor, aParams.provider);
    7360             : 
    7361             :     // Line-throughs
    7362           0 :     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
    7363           0 :     for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
    7364             :       paintDecorationLine(dec, &Metrics::strikeoutSize,
    7365           0 :                           &Metrics::strikeoutOffset);
    7366             :     }
    7367           0 : }
    7368             : 
    7369             : void
    7370          18 : nsTextFrame::DrawText(Range aRange, const gfxPoint& aTextBaselinePt,
    7371             :                       const DrawTextParams& aParams)
    7372             : {
    7373          36 :   TextDecorations decorations;
    7374          18 :   GetTextDecorations(aParams.textStyle->PresContext(),
    7375          18 :                      aParams.callbacks ? eUnresolvedColors : eResolvedColors,
    7376          18 :                      decorations);
    7377             : 
    7378             :   // Hide text decorations if we're currently hiding @font-face fallback text
    7379             :   const bool drawDecorations =
    7380          36 :     !aParams.provider->GetFontGroup()->ShouldSkipDrawing() &&
    7381          54 :     (decorations.HasDecorationLines() || StyleText()->HasTextEmphasis());
    7382          18 :   if (drawDecorations) {
    7383           0 :     DrawTextRunAndDecorations(aRange, aTextBaselinePt, aParams, decorations);
    7384             :   } else {
    7385          18 :     DrawTextRun(aRange, aTextBaselinePt, aParams);
    7386             :   }
    7387          18 : }
    7388             : 
    7389             : int16_t
    7390           0 : nsTextFrame::GetSelectionStatus(int16_t* aSelectionFlags)
    7391             : {
    7392             :   // get the selection controller
    7393           0 :   nsCOMPtr<nsISelectionController> selectionController;
    7394           0 :   nsresult rv = GetSelectionController(PresContext(),
    7395           0 :                                        getter_AddRefs(selectionController));
    7396           0 :   if (NS_FAILED(rv) || !selectionController)
    7397           0 :     return nsISelectionController::SELECTION_OFF;
    7398             : 
    7399           0 :   selectionController->GetSelectionFlags(aSelectionFlags);
    7400             : 
    7401             :   int16_t selectionValue;
    7402           0 :   selectionController->GetDisplaySelection(&selectionValue);
    7403             : 
    7404           0 :   return selectionValue;
    7405             : }
    7406             : 
    7407             : bool
    7408           0 : nsTextFrame::IsVisibleInSelection(nsISelection* aSelection)
    7409             : {
    7410             :   // Check the quick way first
    7411           0 :   if (!GetContent()->IsSelectionDescendant())
    7412           0 :     return false;
    7413             : 
    7414           0 :   UniquePtr<SelectionDetails> details = GetSelectionDetails();
    7415           0 :   bool found = false;
    7416             : 
    7417             :   // where are the selection points "really"
    7418           0 :   for (SelectionDetails* sdptr = details.get(); sdptr; sdptr = sdptr->mNext.get()) {
    7419           0 :     if (sdptr->mEnd > GetContentOffset() &&
    7420           0 :         sdptr->mStart < GetContentEnd() &&
    7421           0 :         sdptr->mSelectionType == SelectionType::eNormal) {
    7422           0 :       found = true;
    7423           0 :       break;
    7424             :     }
    7425             :   }
    7426             : 
    7427           0 :   return found;
    7428             : }
    7429             : 
    7430             : /**
    7431             :  * Compute the longest prefix of text whose width is <= aWidth. Return
    7432             :  * the length of the prefix. Also returns the width of the prefix in aFitWidth.
    7433             :  */
    7434             : static uint32_t
    7435           0 : CountCharsFit(const gfxTextRun* aTextRun, gfxTextRun::Range aRange,
    7436             :               gfxFloat aWidth, PropertyProvider* aProvider,
    7437             :               gfxFloat* aFitWidth)
    7438             : {
    7439           0 :   uint32_t last = 0;
    7440           0 :   gfxFloat width = 0;
    7441           0 :   for (uint32_t i = 1; i <= aRange.Length(); ++i) {
    7442           0 :     if (i == aRange.Length() || aTextRun->IsClusterStart(aRange.start + i)) {
    7443           0 :       gfxTextRun::Range range(aRange.start + last, aRange.start + i);
    7444           0 :       gfxFloat nextWidth = width + aTextRun->GetAdvanceWidth(range, aProvider);
    7445           0 :       if (nextWidth > aWidth)
    7446           0 :         break;
    7447           0 :       last = i;
    7448           0 :       width = nextWidth;
    7449             :     }
    7450             :   }
    7451           0 :   *aFitWidth = width;
    7452           0 :   return last;
    7453             : }
    7454             : 
    7455             : nsIFrame::ContentOffsets
    7456           0 : nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
    7457             : {
    7458           0 :   return GetCharacterOffsetAtFramePointInternal(aPoint, true);
    7459             : }
    7460             : 
    7461             : nsIFrame::ContentOffsets
    7462           0 : nsTextFrame::GetCharacterOffsetAtFramePoint(const nsPoint &aPoint)
    7463             : {
    7464           0 :   return GetCharacterOffsetAtFramePointInternal(aPoint, false);
    7465             : }
    7466             : 
    7467             : nsIFrame::ContentOffsets
    7468           0 : nsTextFrame::GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
    7469             :                                                     bool aForInsertionPoint)
    7470             : {
    7471           0 :   ContentOffsets offsets;
    7472             : 
    7473           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    7474           0 :   if (!mTextRun)
    7475           0 :     return offsets;
    7476             : 
    7477           0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    7478             :   // Trim leading but not trailing whitespace if possible
    7479           0 :   provider.InitializeForDisplay(false);
    7480           0 :   gfxFloat width = mTextRun->IsVertical()
    7481           0 :     ? (mTextRun->IsInlineReversed() ? mRect.height - aPoint.y : aPoint.y)
    7482           0 :     : (mTextRun->IsInlineReversed() ? mRect.width - aPoint.x : aPoint.x);
    7483           0 :   if (StyleContext()->IsTextCombined()) {
    7484           0 :     width /= GetTextCombineScaleFactor(this);
    7485             :   }
    7486             :   gfxFloat fitWidth;
    7487           0 :   Range skippedRange = ComputeTransformedRange(provider);
    7488             : 
    7489             :   uint32_t charsFit = CountCharsFit(mTextRun, skippedRange,
    7490           0 :                                     width, &provider, &fitWidth);
    7491             : 
    7492             :   int32_t selectedOffset;
    7493           0 :   if (charsFit < skippedRange.Length()) {
    7494             :     // charsFit characters fitted, but no more could fit. See if we're
    7495             :     // more than halfway through the cluster.. If we are, choose the next
    7496             :     // cluster.
    7497           0 :     gfxSkipCharsIterator extraCluster(provider.GetStart());
    7498           0 :     extraCluster.AdvanceSkipped(charsFit);
    7499             : 
    7500           0 :     bool allowSplitLigature = true; // Allow selection of partial ligature...
    7501             : 
    7502             :     // ...but don't let selection/insertion-point split two Regional Indicator
    7503             :     // chars that are ligated in the textrun to form a single flag symbol.
    7504           0 :     uint32_t offs = extraCluster.GetOriginalOffset();
    7505           0 :     const nsTextFragment* frag = GetContent()->GetText();
    7506           0 :     if (offs + 1 < frag->GetLength() &&
    7507           0 :         NS_IS_HIGH_SURROGATE(frag->CharAt(offs)) &&
    7508           0 :         NS_IS_LOW_SURROGATE(frag->CharAt(offs + 1)) &&
    7509             :         gfxFontUtils::IsRegionalIndicator
    7510           0 :           (SURROGATE_TO_UCS4(frag->CharAt(offs), frag->CharAt(offs + 1)))) {
    7511           0 :       allowSplitLigature = false;
    7512           0 :       if (extraCluster.GetSkippedOffset() > 1 &&
    7513           0 :           !mTextRun->IsLigatureGroupStart(extraCluster.GetSkippedOffset())) {
    7514             :         // CountCharsFit() left us in the middle of the flag; back up over the
    7515             :         // first character of the ligature, and adjust fitWidth accordingly.
    7516           0 :         extraCluster.AdvanceSkipped(-2); // it's a surrogate pair: 2 code units
    7517           0 :         fitWidth -= mTextRun->GetAdvanceWidth(
    7518             :           Range(extraCluster.GetSkippedOffset(),
    7519           0 :                 extraCluster.GetSkippedOffset() + 2), &provider);
    7520             :       }
    7521             :     }
    7522             : 
    7523           0 :     gfxSkipCharsIterator extraClusterLastChar(extraCluster);
    7524           0 :     FindClusterEnd(mTextRun,
    7525           0 :                    provider.GetStart().GetOriginalOffset() + provider.GetOriginalLength(),
    7526           0 :                    &extraClusterLastChar, allowSplitLigature);
    7527             :     PropertyProvider::Spacing spacing;
    7528             :     Range extraClusterRange(extraCluster.GetSkippedOffset(),
    7529           0 :                             extraClusterLastChar.GetSkippedOffset() + 1);
    7530             :     gfxFloat charWidth =
    7531           0 :         mTextRun->GetAdvanceWidth(extraClusterRange, &provider, &spacing);
    7532           0 :     charWidth -= spacing.mBefore + spacing.mAfter;
    7533           0 :     selectedOffset = !aForInsertionPoint ||
    7534           0 :       width <= fitWidth + spacing.mBefore + charWidth/2
    7535           0 :         ? extraCluster.GetOriginalOffset()
    7536           0 :         : extraClusterLastChar.GetOriginalOffset() + 1;
    7537             :   } else {
    7538             :     // All characters fitted, we're at (or beyond) the end of the text.
    7539             :     // XXX This could be some pathological situation where negative spacing
    7540             :     // caused characters to move backwards. We can't really handle that
    7541             :     // in the current frame system because frames can't have negative
    7542             :     // intrinsic widths.
    7543           0 :     selectedOffset =
    7544           0 :         provider.GetStart().GetOriginalOffset() + provider.GetOriginalLength();
    7545             :     // If we're at the end of a preformatted line which has a terminating
    7546             :     // linefeed, we want to reduce the offset by one to make sure that the
    7547             :     // selection is placed before the linefeed character.
    7548           0 :     if (HasSignificantTerminalNewline()) {
    7549           0 :       --selectedOffset;
    7550             :     }
    7551             :   }
    7552             : 
    7553           0 :   offsets.content = GetContent();
    7554           0 :   offsets.offset = offsets.secondaryOffset = selectedOffset;
    7555           0 :   offsets.associate =
    7556           0 :     mContentOffset == offsets.offset ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
    7557           0 :   return offsets;
    7558             : }
    7559             : 
    7560             : bool
    7561           1 : nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
    7562             :                                            nsRect& aRect)
    7563             : {
    7564           1 :   if (aRect.IsEmpty())
    7565           0 :     return false;
    7566             : 
    7567           2 :   nsRect givenRect = aRect;
    7568             : 
    7569             :   RefPtr<nsFontMetrics> fm =
    7570           2 :     nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
    7571           1 :   gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
    7572           1 :   gfxFont* firstFont = fontGroup->GetFirstValidFont();
    7573           1 :   WritingMode wm = GetWritingMode();
    7574           1 :   bool verticalRun = wm.IsVertical();
    7575           1 :   bool useVerticalMetrics = verticalRun && !wm.IsSideways();
    7576             :   const gfxFont::Metrics& metrics =
    7577             :     firstFont->GetMetrics(useVerticalMetrics ? gfxFont::eVertical
    7578           1 :                                              : gfxFont::eHorizontal);
    7579             : 
    7580           1 :   nsCSSRendering::DecorationRectParams params;
    7581           1 :   params.ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
    7582           1 :   params.offset = fontGroup->GetUnderlineOffset();
    7583           1 :   params.descentLimit =
    7584           1 :     ComputeDescentLimitForSelectionUnderline(aPresContext, metrics);
    7585           1 :   params.vertical = verticalRun;
    7586             : 
    7587           2 :   UniquePtr<SelectionDetails> details = GetSelectionDetails();
    7588           2 :   for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
    7589           3 :     if (sd->mStart == sd->mEnd ||
    7590           1 :         !(sd->mSelectionType & kRawSelectionTypesWithDecorations) ||
    7591             :         // URL strikeout does not use underline.
    7592           0 :         sd->mSelectionType == SelectionType::eURLStrikeout) {
    7593           2 :       continue;
    7594             :     }
    7595             : 
    7596             :     float relativeSize;
    7597             :     int32_t index =
    7598           0 :       nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(
    7599           0 :         sd->mSelectionType);
    7600           0 :     if (sd->mSelectionType == SelectionType::eSpellCheck) {
    7601           0 :       if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nullptr,
    7602             :                                                    &relativeSize,
    7603             :                                                    &params.style)) {
    7604           0 :         continue;
    7605             :       }
    7606             :     } else {
    7607             :       // IME selections
    7608           0 :       TextRangeStyle& rangeStyle = sd->mTextRangeStyle;
    7609           0 :       if (rangeStyle.IsDefined()) {
    7610           0 :         if (!rangeStyle.IsLineStyleDefined() ||
    7611           0 :             rangeStyle.mLineStyle == TextRangeStyle::LINESTYLE_NONE) {
    7612           0 :           continue;
    7613             :         }
    7614           0 :         params.style = rangeStyle.mLineStyle;
    7615           0 :         relativeSize = rangeStyle.mIsBoldLine ? 2.0f : 1.0f;
    7616           0 :       } else if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index,
    7617             :                                                           nullptr, &relativeSize,
    7618             :                                                           &params.style)) {
    7619           0 :         continue;
    7620             :       }
    7621             :     }
    7622           0 :     nsRect decorationArea;
    7623             : 
    7624           0 :     params.lineSize =
    7625           0 :       Size(aPresContext->AppUnitsToGfxUnits(aRect.width),
    7626           0 :            ComputeSelectionUnderlineHeight(aPresContext, metrics,
    7627             :                                            sd->mSelectionType));
    7628           0 :     relativeSize = std::max(relativeSize, 1.0f);
    7629           0 :     params.lineSize.height *= relativeSize;
    7630           0 :     decorationArea =
    7631           0 :       nsCSSRendering::GetTextDecorationRect(aPresContext, params);
    7632           0 :     aRect.UnionRect(aRect, decorationArea);
    7633             :   }
    7634             : 
    7635           1 :   return !aRect.IsEmpty() && !givenRect.Contains(aRect);
    7636             : }
    7637             : 
    7638             : bool
    7639           2 : nsTextFrame::IsFrameSelected() const
    7640             : {
    7641           2 :   NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
    7642             :                "use the public IsSelected() instead");
    7643           2 :   return nsRange::IsNodeSelected(GetContent(), GetContentOffset(),
    7644           4 :                                  GetContentEnd());
    7645             : }
    7646             : 
    7647             : void
    7648           6 : nsTextFrame::SetSelectedRange(uint32_t aStart, uint32_t aEnd, bool aSelected,
    7649             :                               SelectionType aSelectionType)
    7650             : {
    7651           6 :   NS_ASSERTION(!GetPrevContinuation(), "Should only be called for primary frame");
    7652             :   DEBUG_VERIFY_NOT_DIRTY(mState);
    7653             : 
    7654             :   // Selection is collapsed, which can't affect text frame rendering
    7655           6 :   if (aStart == aEnd)
    7656           3 :     return;
    7657             : 
    7658           3 :   nsTextFrame* f = this;
    7659           3 :   while (f && f->GetContentEnd() <= int32_t(aStart)) {
    7660           0 :     f = f->GetNextContinuation();
    7661             :   }
    7662             : 
    7663           3 :   nsPresContext* presContext = PresContext();
    7664           9 :   while (f && f->GetContentOffset() < int32_t(aEnd)) {
    7665             :     // We may need to reflow to recompute the overflow area for
    7666             :     // spellchecking or IME underline if their underline is thicker than
    7667             :     // the normal decoration line.
    7668           3 :     if (aSelectionType & kRawSelectionTypesWithDecorations) {
    7669             :       bool didHaveOverflowingSelection =
    7670           0 :         (f->GetStateBits() & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0;
    7671           0 :       nsRect r(nsPoint(0, 0), GetSize());
    7672             :       bool willHaveOverflowingSelection =
    7673           0 :         aSelected && f->CombineSelectionUnderlineRect(presContext, r);
    7674           0 :       if (didHaveOverflowingSelection || willHaveOverflowingSelection) {
    7675           0 :         presContext->PresShell()->FrameNeedsReflow(f,
    7676             :                                                    nsIPresShell::eStyleChange,
    7677           0 :                                                    NS_FRAME_IS_DIRTY);
    7678             :       }
    7679             :     }
    7680             :     // Selection might change anything. Invalidate the overflow area.
    7681           3 :     f->InvalidateFrame();
    7682             : 
    7683           3 :     f = f->GetNextContinuation();
    7684             :   }
    7685             : }
    7686             : 
    7687             : void
    7688           1 : nsTextFrame::UpdateIteratorFromOffset(const PropertyProvider& aProperties,
    7689             :                                       int32_t& aInOffset,
    7690             :                                       gfxSkipCharsIterator& aIter)
    7691             : {
    7692           1 :   if (aInOffset < GetContentOffset()){
    7693           0 :     NS_WARNING("offset before this frame's content");
    7694           0 :     aInOffset = GetContentOffset();
    7695           1 :   } else if (aInOffset > GetContentEnd()) {
    7696           0 :     NS_WARNING("offset after this frame's content");
    7697           0 :     aInOffset = GetContentEnd();
    7698             :   }
    7699             : 
    7700           1 :   int32_t trimmedOffset = aProperties.GetStart().GetOriginalOffset();
    7701           1 :   int32_t trimmedEnd = trimmedOffset + aProperties.GetOriginalLength();
    7702           1 :   aInOffset = std::max(aInOffset, trimmedOffset);
    7703           1 :   aInOffset = std::min(aInOffset, trimmedEnd);
    7704             : 
    7705           1 :   aIter.SetOriginalOffset(aInOffset);
    7706             : 
    7707           3 :   if (aInOffset < trimmedEnd &&
    7708           2 :       !aIter.IsOriginalCharSkipped() &&
    7709           1 :       !mTextRun->IsClusterStart(aIter.GetSkippedOffset())) {
    7710           0 :     NS_WARNING("called for non-cluster boundary");
    7711           0 :     FindClusterStart(mTextRun, trimmedOffset, &aIter);
    7712             :   }
    7713           1 : }
    7714             : 
    7715             : nsPoint
    7716           1 : nsTextFrame::GetPointFromIterator(const gfxSkipCharsIterator& aIter,
    7717             :                                   PropertyProvider& aProperties)
    7718             : {
    7719           1 :   Range range(aProperties.GetStart().GetSkippedOffset(),
    7720           2 :               aIter.GetSkippedOffset());
    7721           1 :   gfxFloat advance = mTextRun->GetAdvanceWidth(range, &aProperties);
    7722           1 :   nscoord iSize = NSToCoordCeilClamped(advance);
    7723           1 :   nsPoint point;
    7724             : 
    7725           1 :   if (mTextRun->IsVertical()) {
    7726           0 :     point.x = 0;
    7727           0 :     if (mTextRun->IsInlineReversed()) {
    7728           0 :       point.y = mRect.height - iSize;
    7729             :     } else {
    7730           0 :       point.y = iSize;
    7731             :     }
    7732             :   } else {
    7733           1 :     point.y = 0;
    7734           1 :     if (mTextRun->IsInlineReversed()) {
    7735           0 :       point.x = mRect.width - iSize;
    7736             :     } else {
    7737           1 :       point.x = iSize;
    7738             :     }
    7739           1 :     if (StyleContext()->IsTextCombined()) {
    7740           0 :       point.x *= GetTextCombineScaleFactor(this);
    7741             :     }
    7742             :   }
    7743           1 :   return point;
    7744             : }
    7745             : 
    7746             : nsresult
    7747           1 : nsTextFrame::GetPointFromOffset(int32_t inOffset,
    7748             :                                 nsPoint* outPoint)
    7749             : {
    7750           1 :   if (!outPoint)
    7751           0 :     return NS_ERROR_NULL_POINTER;
    7752             : 
    7753             :   DEBUG_VERIFY_NOT_DIRTY(mState);
    7754           1 :   if (mState & NS_FRAME_IS_DIRTY)
    7755           0 :     return NS_ERROR_UNEXPECTED;
    7756             : 
    7757           1 :   if (GetContentLength() <= 0) {
    7758           0 :     outPoint->x = 0;
    7759           0 :     outPoint->y = 0;
    7760           0 :     return NS_OK;
    7761             :   }
    7762             : 
    7763           1 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    7764           1 :   if (!mTextRun)
    7765           0 :     return NS_ERROR_FAILURE;
    7766             : 
    7767           2 :   PropertyProvider properties(this, iter, nsTextFrame::eInflated);
    7768             :   // Don't trim trailing whitespace, we want the caret to appear in the right
    7769             :   // place if it's positioned there
    7770           1 :   properties.InitializeForDisplay(false);
    7771             : 
    7772           1 :   UpdateIteratorFromOffset(properties, inOffset, iter);
    7773             : 
    7774           1 :   *outPoint = GetPointFromIterator(iter, properties);
    7775             : 
    7776           1 :   return NS_OK;
    7777             : }
    7778             : 
    7779             : nsresult
    7780           0 : nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
    7781             :                                       int32_t aLength,
    7782             :                                       nsTArray<nsRect>& aRects)
    7783             : {
    7784             :   DEBUG_VERIFY_NOT_DIRTY(mState);
    7785           0 :   if (mState & NS_FRAME_IS_DIRTY) {
    7786           0 :     return NS_ERROR_UNEXPECTED;
    7787             :   }
    7788             : 
    7789           0 :   if (GetContentLength() <= 0) {
    7790           0 :     return NS_OK;
    7791             :   }
    7792             : 
    7793           0 :   if (!mTextRun) {
    7794           0 :     return NS_ERROR_FAILURE;
    7795             :   }
    7796             : 
    7797           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    7798           0 :   PropertyProvider properties(this, iter, nsTextFrame::eInflated);
    7799             :   // Don't trim trailing whitespace, we want the caret to appear in the right
    7800             :   // place if it's positioned there
    7801           0 :   properties.InitializeForDisplay(false);
    7802             : 
    7803           0 :   UpdateIteratorFromOffset(properties, aInOffset, iter);
    7804             : 
    7805           0 :   const int32_t kContentEnd = GetContentEnd();
    7806           0 :   const int32_t kEndOffset = std::min(aInOffset + aLength, kContentEnd);
    7807           0 :   while (aInOffset < kEndOffset) {
    7808           0 :     if (!iter.IsOriginalCharSkipped() &&
    7809           0 :         !mTextRun->IsClusterStart(iter.GetSkippedOffset())) {
    7810           0 :       FindClusterStart(mTextRun,
    7811           0 :                        properties.GetStart().GetOriginalOffset() +
    7812           0 :                          properties.GetOriginalLength(),
    7813           0 :                        &iter);
    7814             :     }
    7815             : 
    7816           0 :     nsPoint point = GetPointFromIterator(iter, properties);
    7817           0 :     nsRect rect;
    7818           0 :     rect.x = point.x;
    7819           0 :     rect.y = point.y;
    7820             : 
    7821           0 :     nscoord iSize = 0;
    7822           0 :     if (aInOffset < kContentEnd) {
    7823           0 :       gfxSkipCharsIterator nextIter(iter);
    7824           0 :       nextIter.AdvanceOriginal(1);
    7825           0 :       if (!nextIter.IsOriginalCharSkipped() &&
    7826           0 :           !mTextRun->IsClusterStart(nextIter.GetSkippedOffset())) {
    7827           0 :         FindClusterEnd(mTextRun, kContentEnd, &nextIter);
    7828             :       }
    7829             : 
    7830             :       gfxFloat advance =
    7831           0 :         mTextRun->GetAdvanceWidth(Range(iter.GetSkippedOffset(),
    7832             :                                         nextIter.GetSkippedOffset()),
    7833           0 :                                   &properties);
    7834           0 :       iSize = NSToCoordCeilClamped(advance);
    7835             :     }
    7836             : 
    7837           0 :     if (mTextRun->IsVertical()) {
    7838           0 :       rect.width = mRect.width;
    7839           0 :       rect.height = iSize;
    7840             :     } else {
    7841           0 :       rect.width = iSize;
    7842           0 :       rect.height = mRect.height;
    7843             : 
    7844           0 :       if (StyleContext()->IsTextCombined()) {
    7845           0 :         rect.width *= GetTextCombineScaleFactor(this);
    7846             :       }
    7847             :     }
    7848           0 :     aRects.AppendElement(rect);
    7849           0 :     aInOffset++;
    7850             :     // Don't advance iter if we've reached the end
    7851           0 :     if (aInOffset < kEndOffset) {
    7852           0 :       iter.AdvanceOriginal(1);
    7853             :     }
    7854             :   }
    7855             : 
    7856           0 :   return NS_OK;
    7857             : }
    7858             : 
    7859             : nsresult
    7860           2 : nsTextFrame::GetChildFrameContainingOffset(int32_t   aContentOffset,
    7861             :                                            bool      aHint,
    7862             :                                            int32_t*  aOutOffset,
    7863             :                                            nsIFrame**aOutFrame)
    7864             : {
    7865             :   DEBUG_VERIFY_NOT_DIRTY(mState);
    7866             : #if 0 //XXXrbs disable due to bug 310227
    7867             :   if (mState & NS_FRAME_IS_DIRTY)
    7868             :     return NS_ERROR_UNEXPECTED;
    7869             : #endif
    7870             : 
    7871           2 :   NS_ASSERTION(aOutOffset && aOutFrame, "Bad out parameters");
    7872           2 :   NS_ASSERTION(aContentOffset >= 0, "Negative content offset, existing code was very broken!");
    7873           2 :   nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
    7874           2 :   if (this != primaryFrame) {
    7875             :     // This call needs to happen on the primary frame
    7876           0 :     return primaryFrame->GetChildFrameContainingOffset(aContentOffset, aHint,
    7877           0 :                                                        aOutOffset, aOutFrame);
    7878             :   }
    7879             : 
    7880           2 :   nsTextFrame* f = this;
    7881           2 :   int32_t offset = mContentOffset;
    7882             : 
    7883             :   // Try to look up the offset to frame property
    7884           2 :   nsTextFrame* cachedFrame = GetProperty(OffsetToFrameProperty());
    7885             : 
    7886           2 :   if (cachedFrame) {
    7887           1 :     f = cachedFrame;
    7888           1 :     offset = f->GetContentOffset();
    7889             : 
    7890           1 :     f->RemoveStateBits(TEXT_IN_OFFSET_CACHE);
    7891             :   }
    7892             : 
    7893           2 :   if ((aContentOffset >= offset) &&
    7894           0 :       (aHint || aContentOffset != offset)) {
    7895             :     while (true) {
    7896           2 :       nsTextFrame* next = f->GetNextContinuation();
    7897           2 :       if (!next || aContentOffset < next->GetContentOffset())
    7898           2 :         break;
    7899           0 :       if (aContentOffset == next->GetContentOffset()) {
    7900           0 :         if (aHint) {
    7901           0 :           f = next;
    7902           0 :           if (f->GetContentLength() == 0) {
    7903           0 :             continue; // use the last of the empty frames with this offset
    7904             :           }
    7905             :         }
    7906           0 :         break;
    7907             :       }
    7908           0 :       f = next;
    7909           0 :     }
    7910             :   } else {
    7911             :     while (true) {
    7912           0 :       nsTextFrame* prev = f->GetPrevContinuation();
    7913           0 :       if (!prev || aContentOffset > f->GetContentOffset())
    7914           0 :         break;
    7915           0 :       if (aContentOffset == f->GetContentOffset()) {
    7916           0 :         if (!aHint) {
    7917           0 :           f = prev;
    7918           0 :           if (f->GetContentLength() == 0) {
    7919           0 :             continue; // use the first of the empty frames with this offset
    7920             :           }
    7921             :         }
    7922           0 :         break;
    7923             :       }
    7924           0 :       f = prev;
    7925           0 :     }
    7926             :   }
    7927             : 
    7928           2 :   *aOutOffset = aContentOffset - f->GetContentOffset();
    7929           2 :   *aOutFrame = f;
    7930             : 
    7931             :   // cache the frame we found
    7932           2 :   SetProperty(OffsetToFrameProperty(), f);
    7933           2 :   f->AddStateBits(TEXT_IN_OFFSET_CACHE);
    7934             : 
    7935           2 :   return NS_OK;
    7936             : }
    7937             : 
    7938             : nsIFrame::FrameSearchResult
    7939           0 : nsTextFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
    7940             : {
    7941           0 :   NS_ASSERTION(aOffset && *aOffset <= GetContentLength(), "aOffset out of range");
    7942             : 
    7943           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    7944           0 :   if (!mTextRun)
    7945           0 :     return CONTINUE_EMPTY;
    7946             : 
    7947           0 :   TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), true);
    7948             :   // Check whether there are nonskipped characters in the trimmmed range
    7949           0 :   return (iter.ConvertOriginalToSkipped(trimmed.GetEnd()) >
    7950           0 :          iter.ConvertOriginalToSkipped(trimmed.mStart)) ? FOUND : CONTINUE;
    7951             : }
    7952             : 
    7953             : /**
    7954             :  * This class iterates through the clusters before or after the given
    7955             :  * aPosition (which is a content offset). You can test each cluster
    7956             :  * to see if it's whitespace (as far as selection/caret movement is concerned),
    7957             :  * or punctuation, or if there is a word break before the cluster. ("Before"
    7958             :  * is interpreted according to aDirection, so if aDirection is -1, "before"
    7959             :  * means actually *after* the cluster content.)
    7960             :  */
    7961           0 : class MOZ_STACK_CLASS ClusterIterator {
    7962             : public:
    7963             :   ClusterIterator(nsTextFrame* aTextFrame, int32_t aPosition, int32_t aDirection,
    7964             :                   nsString& aContext);
    7965             : 
    7966             :   bool NextCluster();
    7967             :   bool IsWhitespace();
    7968             :   bool IsPunctuation();
    7969           0 :   bool HaveWordBreakBefore() { return mHaveWordBreak; }
    7970             :   int32_t GetAfterOffset();
    7971             :   int32_t GetBeforeOffset();
    7972             : 
    7973             : private:
    7974             :   gfxSkipCharsIterator        mIterator;
    7975             :   const nsTextFragment*       mFrag;
    7976             :   nsTextFrame*                mTextFrame;
    7977             :   int32_t                     mDirection;
    7978             :   int32_t                     mCharIndex;
    7979             :   nsTextFrame::TrimmedOffsets mTrimmed;
    7980             :   nsTArray<bool>      mWordBreaks;
    7981             :   bool                        mHaveWordBreak;
    7982             : };
    7983             : 
    7984             : static bool
    7985           0 : IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter,
    7986             :                           bool aRespectClusters,
    7987             :                           const gfxTextRun* aTextRun,
    7988             :                           nsIFrame* aFrame)
    7989             : {
    7990           0 :   if (aIter.IsOriginalCharSkipped())
    7991           0 :     return false;
    7992           0 :   uint32_t index = aIter.GetSkippedOffset();
    7993           0 :   if (aRespectClusters && !aTextRun->IsClusterStart(index))
    7994           0 :     return false;
    7995           0 :   if (index > 0) {
    7996             :     // Check whether the proposed position is in between the two halves of a
    7997             :     // surrogate pair, or before a Variation Selector character;
    7998             :     // if so, this is not a valid character boundary.
    7999             :     // (In the case where we are respecting clusters, we won't actually get
    8000             :     // this far because the low surrogate is also marked as non-clusterStart
    8001             :     // so we'll return FALSE above.)
    8002           0 :     uint32_t offs = aIter.GetOriginalOffset();
    8003           0 :     const nsTextFragment* frag = aFrame->GetContent()->GetText();
    8004           0 :     uint32_t ch = frag->CharAt(offs);
    8005             : 
    8006           0 :     if (gfxFontUtils::IsVarSelector(ch) ||
    8007           0 :         (NS_IS_LOW_SURROGATE(ch) && offs > 0 &&
    8008           0 :          NS_IS_HIGH_SURROGATE(frag->CharAt(offs - 1)))) {
    8009           0 :       return false;
    8010             :     }
    8011             : 
    8012             :     // If the proposed position is before a high surrogate, we need to decode
    8013             :     // the surrogate pair (if valid) and check the resulting character.
    8014           0 :     if (NS_IS_HIGH_SURROGATE(ch) && offs + 1 < frag->GetLength()) {
    8015           0 :       uint32_t ch2 = frag->CharAt(offs + 1);
    8016           0 :       if (NS_IS_LOW_SURROGATE(ch2)) {
    8017           0 :         ch = SURROGATE_TO_UCS4(ch, ch2);
    8018             :         // If the character is a (Plane-14) variation selector,
    8019             :         // or a Regional Indicator character that is ligated with the previous
    8020             :         // character, this is not a valid boundary.
    8021           0 :         if (gfxFontUtils::IsVarSelector(ch) ||
    8022           0 :             (gfxFontUtils::IsRegionalIndicator(ch) &&
    8023           0 :              !aTextRun->IsLigatureGroupStart(index))) {
    8024           0 :           return false;
    8025             :         }
    8026             :       }
    8027             :     }
    8028             :   }
    8029           0 :   return true;
    8030             : }
    8031             : 
    8032             : nsIFrame::FrameSearchResult
    8033           0 : nsTextFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
    8034             :                                  PeekOffsetCharacterOptions aOptions)
    8035             : {
    8036           0 :   int32_t contentLength = GetContentLength();
    8037           0 :   NS_ASSERTION(aOffset && *aOffset <= contentLength, "aOffset out of range");
    8038             : 
    8039           0 :   if (!aOptions.mIgnoreUserStyleAll) {
    8040             :     StyleUserSelect selectStyle;
    8041           0 :     IsSelectable(&selectStyle);
    8042           0 :     if (selectStyle == StyleUserSelect::All) {
    8043           0 :       return CONTINUE_UNSELECTABLE;
    8044             :     }
    8045             :   }
    8046             : 
    8047           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    8048           0 :   if (!mTextRun)
    8049           0 :     return CONTINUE_EMPTY;
    8050             : 
    8051           0 :   TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), false);
    8052             : 
    8053             :   // A negative offset means "end of frame".
    8054           0 :   int32_t startOffset = GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
    8055             : 
    8056           0 :   if (!aForward) {
    8057             :     // If at the beginning of the line, look at the previous continuation
    8058           0 :     for (int32_t i = std::min(trimmed.GetEnd(), startOffset) - 1;
    8059           0 :          i >= trimmed.mStart; --i) {
    8060           0 :       iter.SetOriginalOffset(i);
    8061           0 :       if (IsAcceptableCaretPosition(iter, aOptions.mRespectClusters, mTextRun,
    8062           0 :                                     this)) {
    8063           0 :         *aOffset = i - mContentOffset;
    8064           0 :         return FOUND;
    8065             :       }
    8066             :     }
    8067           0 :     *aOffset = 0;
    8068             :   } else {
    8069             :     // If we're at the end of a line, look at the next continuation
    8070           0 :     iter.SetOriginalOffset(startOffset);
    8071           0 :     if (startOffset <= trimmed.GetEnd() &&
    8072           0 :         !(startOffset < trimmed.GetEnd() &&
    8073           0 :           StyleText()->NewlineIsSignificant(this) &&
    8074           0 :           iter.GetSkippedOffset() < mTextRun->GetLength() &&
    8075           0 :           mTextRun->CharIsNewline(iter.GetSkippedOffset()))) {
    8076           0 :       for (int32_t i = startOffset + 1; i <= trimmed.GetEnd(); ++i) {
    8077           0 :         iter.SetOriginalOffset(i);
    8078           0 :         if (i == trimmed.GetEnd() ||
    8079           0 :             IsAcceptableCaretPosition(iter, aOptions.mRespectClusters, mTextRun,
    8080           0 :                                       this)) {
    8081           0 :           *aOffset = i - mContentOffset;
    8082           0 :           return FOUND;
    8083             :         }
    8084             :       }
    8085             :     }
    8086           0 :     *aOffset = contentLength;
    8087             :   }
    8088             : 
    8089           0 :   return CONTINUE;
    8090             : }
    8091             : 
    8092             : bool
    8093           0 : ClusterIterator::IsWhitespace()
    8094             : {
    8095           0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    8096           0 :   return IsSelectionSpace(mFrag, mCharIndex);
    8097             : }
    8098             : 
    8099             : bool
    8100           0 : ClusterIterator::IsPunctuation()
    8101             : {
    8102           0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    8103             :   // Return true for all Punctuation categories (Unicode general category P?),
    8104             :   // and also for Symbol categories (S?) except for Modifier Symbol, which is
    8105             :   // kept together with any adjacent letter/number. (Bug 1066756)
    8106           0 :   uint8_t cat = unicode::GetGeneralCategory(mFrag->CharAt(mCharIndex));
    8107           0 :   switch (cat) {
    8108             :     case HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: /* Pc */
    8109             :     case HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION:    /* Pd */
    8110             :     case HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION:   /* Pe */
    8111             :     case HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION:   /* Pf */
    8112             :     case HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: /* Pi */
    8113             :     case HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION:   /* Po */
    8114             :     case HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION:    /* Ps */
    8115             :     case HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL:     /* Sc */
    8116             :     // Deliberately omitted:
    8117             :     // case HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL:     /* Sk */
    8118             :     case HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL:         /* Sm */
    8119             :     case HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL:        /* So */
    8120           0 :       return true;
    8121             :     default:
    8122           0 :       return false;
    8123             :   }
    8124             : }
    8125             : 
    8126             : int32_t
    8127           0 : ClusterIterator::GetBeforeOffset()
    8128             : {
    8129           0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    8130           0 :   return mCharIndex + (mDirection > 0 ? 0 : 1);
    8131             : }
    8132             : 
    8133             : int32_t
    8134           0 : ClusterIterator::GetAfterOffset()
    8135             : {
    8136           0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    8137           0 :   return mCharIndex + (mDirection > 0 ? 1 : 0);
    8138             : }
    8139             : 
    8140             : bool
    8141           0 : ClusterIterator::NextCluster()
    8142             : {
    8143           0 :   if (!mDirection)
    8144           0 :     return false;
    8145           0 :   const gfxTextRun* textRun = mTextFrame->GetTextRun(nsTextFrame::eInflated);
    8146             : 
    8147           0 :   mHaveWordBreak = false;
    8148             :   while (true) {
    8149           0 :     bool keepGoing = false;
    8150           0 :     if (mDirection > 0) {
    8151           0 :       if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd())
    8152           0 :         return false;
    8153           0 :       keepGoing = mIterator.IsOriginalCharSkipped() ||
    8154           0 :           mIterator.GetOriginalOffset() < mTrimmed.mStart ||
    8155           0 :           !textRun->IsClusterStart(mIterator.GetSkippedOffset());
    8156           0 :       mCharIndex = mIterator.GetOriginalOffset();
    8157           0 :       mIterator.AdvanceOriginal(1);
    8158             :     } else {
    8159           0 :       if (mIterator.GetOriginalOffset() <= mTrimmed.mStart)
    8160           0 :         return false;
    8161           0 :       mIterator.AdvanceOriginal(-1);
    8162           0 :       keepGoing = mIterator.IsOriginalCharSkipped() ||
    8163           0 :           mIterator.GetOriginalOffset() >= mTrimmed.GetEnd() ||
    8164           0 :           !textRun->IsClusterStart(mIterator.GetSkippedOffset());
    8165           0 :       mCharIndex = mIterator.GetOriginalOffset();
    8166             :     }
    8167             : 
    8168           0 :     if (mWordBreaks[GetBeforeOffset() - mTextFrame->GetContentOffset()]) {
    8169           0 :       mHaveWordBreak = true;
    8170             :     }
    8171           0 :     if (!keepGoing)
    8172           0 :       return true;
    8173           0 :   }
    8174             : }
    8175             : 
    8176           0 : ClusterIterator::ClusterIterator(nsTextFrame* aTextFrame, int32_t aPosition,
    8177           0 :                                  int32_t aDirection, nsString& aContext)
    8178           0 :   : mTextFrame(aTextFrame), mDirection(aDirection), mCharIndex(-1)
    8179             : {
    8180           0 :   mIterator = aTextFrame->EnsureTextRun(nsTextFrame::eInflated);
    8181           0 :   if (!aTextFrame->GetTextRun(nsTextFrame::eInflated)) {
    8182           0 :     mDirection = 0; // signal failure
    8183           0 :     return;
    8184             :   }
    8185           0 :   mIterator.SetOriginalOffset(aPosition);
    8186             : 
    8187           0 :   mFrag = aTextFrame->GetContent()->GetText();
    8188           0 :   mTrimmed = aTextFrame->GetTrimmedOffsets(mFrag, true);
    8189             : 
    8190           0 :   int32_t textOffset = aTextFrame->GetContentOffset();
    8191           0 :   int32_t textLen = aTextFrame->GetContentLength();
    8192           0 :   if (!mWordBreaks.AppendElements(textLen + 1)) {
    8193           0 :     mDirection = 0; // signal failure
    8194           0 :     return;
    8195             :   }
    8196           0 :   memset(mWordBreaks.Elements(), false, (textLen + 1)*sizeof(bool));
    8197             :   int32_t textStart;
    8198           0 :   if (aDirection > 0) {
    8199           0 :     if (aContext.IsEmpty()) {
    8200             :       // No previous context, so it must be the start of a line or text run
    8201           0 :       mWordBreaks[0] = true;
    8202             :     }
    8203           0 :     textStart = aContext.Length();
    8204           0 :     mFrag->AppendTo(aContext, textOffset, textLen);
    8205             :   } else {
    8206           0 :     if (aContext.IsEmpty()) {
    8207             :       // No following context, so it must be the end of a line or text run
    8208           0 :       mWordBreaks[textLen] = true;
    8209             :     }
    8210           0 :     textStart = 0;
    8211           0 :     nsAutoString str;
    8212           0 :     mFrag->AppendTo(str, textOffset, textLen);
    8213           0 :     aContext.Insert(str, 0);
    8214             :   }
    8215           0 :   nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
    8216           0 :   for (int32_t i = 0; i <= textLen; ++i) {
    8217           0 :     int32_t indexInText = i + textStart;
    8218           0 :     mWordBreaks[i] |=
    8219           0 :       wordBreaker->BreakInBetween(aContext.get(), indexInText,
    8220           0 :                                   aContext.get() + indexInText,
    8221           0 :                                   aContext.Length() - indexInText);
    8222             :   }
    8223             : }
    8224             : 
    8225             : nsIFrame::FrameSearchResult
    8226           0 : nsTextFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
    8227             :                             int32_t* aOffset, PeekWordState* aState)
    8228             : {
    8229           0 :   int32_t contentLength = GetContentLength();
    8230           0 :   NS_ASSERTION (aOffset && *aOffset <= contentLength, "aOffset out of range");
    8231             : 
    8232             :   StyleUserSelect selectStyle;
    8233           0 :   IsSelectable(&selectStyle);
    8234           0 :   if (selectStyle == StyleUserSelect::All)
    8235           0 :     return CONTINUE_UNSELECTABLE;
    8236             : 
    8237           0 :   int32_t offset = GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
    8238           0 :   ClusterIterator cIter(this, offset, aForward ? 1 : -1, aState->mContext);
    8239             : 
    8240           0 :   if (!cIter.NextCluster())
    8241           0 :     return CONTINUE_EMPTY;
    8242             : 
    8243           0 :   do {
    8244           0 :     bool isPunctuation = cIter.IsPunctuation();
    8245           0 :     bool isWhitespace = cIter.IsWhitespace();
    8246           0 :     bool isWordBreakBefore = cIter.HaveWordBreakBefore();
    8247           0 :     if (aWordSelectEatSpace == isWhitespace && !aState->mSawBeforeType) {
    8248           0 :       aState->SetSawBeforeType();
    8249           0 :       aState->Update(isPunctuation, isWhitespace);
    8250           0 :       continue;
    8251             :     }
    8252             :     // See if we can break before the current cluster
    8253           0 :     if (!aState->mAtStart) {
    8254             :       bool canBreak;
    8255           0 :       if (isPunctuation != aState->mLastCharWasPunctuation) {
    8256           0 :         canBreak = BreakWordBetweenPunctuation(aState, aForward,
    8257           0 :                      isPunctuation, isWhitespace, aIsKeyboardSelect);
    8258           0 :       } else if (!aState->mLastCharWasWhitespace &&
    8259           0 :                  !isWhitespace && !isPunctuation && isWordBreakBefore) {
    8260             :         // if both the previous and the current character are not white
    8261             :         // space but this can be word break before, we don't need to eat
    8262             :         // a white space in this case. This case happens in some languages
    8263             :         // that their words are not separated by white spaces. E.g.,
    8264             :         // Japanese and Chinese.
    8265           0 :         canBreak = true;
    8266             :       } else {
    8267           0 :         canBreak = isWordBreakBefore && aState->mSawBeforeType &&
    8268             :           (aWordSelectEatSpace != isWhitespace);
    8269             :       }
    8270           0 :       if (canBreak) {
    8271           0 :         *aOffset = cIter.GetBeforeOffset() - mContentOffset;
    8272           0 :         return FOUND;
    8273             :       }
    8274             :     }
    8275           0 :     aState->Update(isPunctuation, isWhitespace);
    8276             :   } while (cIter.NextCluster());
    8277             : 
    8278           0 :   *aOffset = cIter.GetAfterOffset() - mContentOffset;
    8279           0 :   return CONTINUE;
    8280             : }
    8281             : 
    8282             :  // TODO this needs to be deCOMtaminated with the interface fixed in
    8283             : // nsIFrame.h, but we won't do that until the old textframe is gone.
    8284             : nsresult
    8285           0 : nsTextFrame::CheckVisibility(nsPresContext* aContext, int32_t aStartIndex,
    8286             :     int32_t aEndIndex, bool aRecurse, bool *aFinished, bool *aRetval)
    8287             : {
    8288           0 :   if (!aRetval)
    8289           0 :     return NS_ERROR_NULL_POINTER;
    8290             : 
    8291             :   // Text in the range is visible if there is at least one character in the range
    8292             :   // that is not skipped and is mapped by this frame (which is the primary frame)
    8293             :   // or one of its continuations.
    8294           0 :   for (nsTextFrame* f = this; f; f = f->GetNextContinuation()) {
    8295           0 :     int32_t dummyOffset = 0;
    8296           0 :     if (f->PeekOffsetNoAmount(true, &dummyOffset) == FOUND) {
    8297           0 :       *aRetval = true;
    8298           0 :       return NS_OK;
    8299             :     }
    8300             :   }
    8301             : 
    8302           0 :   *aRetval = false;
    8303           0 :   return NS_OK;
    8304             : }
    8305             : 
    8306             : nsresult
    8307           0 : nsTextFrame::GetOffsets(int32_t &start, int32_t &end) const
    8308             : {
    8309           0 :   start = GetContentOffset();
    8310           0 :   end = GetContentEnd();
    8311           0 :   return NS_OK;
    8312             : }
    8313             : 
    8314             : static int32_t
    8315           0 : FindEndOfPunctuationRun(const nsTextFragment* aFrag,
    8316             :                         const gfxTextRun* aTextRun,
    8317             :                         gfxSkipCharsIterator* aIter,
    8318             :                         int32_t aOffset,
    8319             :                         int32_t aStart,
    8320             :                         int32_t aEnd)
    8321             : {
    8322             :   int32_t i;
    8323             : 
    8324           0 :   for (i = aStart; i < aEnd - aOffset; ++i) {
    8325           0 :     if (nsContentUtils::IsFirstLetterPunctuationAt(aFrag, aOffset + i)) {
    8326           0 :       aIter->SetOriginalOffset(aOffset + i);
    8327           0 :       FindClusterEnd(aTextRun, aEnd, aIter);
    8328           0 :       i = aIter->GetOriginalOffset() - aOffset;
    8329             :     } else {
    8330           0 :       break;
    8331             :     }
    8332             :   }
    8333           0 :   return i;
    8334             : }
    8335             : 
    8336             : /**
    8337             :  * Returns true if this text frame completes the first-letter, false
    8338             :  * if it does not contain a true "letter".
    8339             :  * If returns true, then it also updates aLength to cover just the first-letter
    8340             :  * text.
    8341             :  *
    8342             :  * XXX :first-letter should be handled during frame construction
    8343             :  * (and it has a good bit in common with nextBidi)
    8344             :  *
    8345             :  * @param aLength an in/out parameter: on entry contains the maximum length to
    8346             :  * return, on exit returns length of the first-letter fragment (which may
    8347             :  * include leading and trailing punctuation, for example)
    8348             :  */
    8349             : static bool
    8350           0 : FindFirstLetterRange(const nsTextFragment* aFrag,
    8351             :                      const gfxTextRun* aTextRun,
    8352             :                      int32_t aOffset, const gfxSkipCharsIterator& aIter,
    8353             :                      int32_t* aLength)
    8354             : {
    8355             :   int32_t i;
    8356           0 :   int32_t length = *aLength;
    8357           0 :   int32_t endOffset = aOffset + length;
    8358           0 :   gfxSkipCharsIterator iter(aIter);
    8359             : 
    8360             :   // skip leading whitespace, then consume clusters that start with punctuation
    8361           0 :   i = FindEndOfPunctuationRun(aFrag, aTextRun, &iter, aOffset,
    8362           0 :                               GetTrimmableWhitespaceCount(aFrag, aOffset, length, 1),
    8363           0 :                               endOffset);
    8364           0 :   if (i == length)
    8365           0 :     return false;
    8366             : 
    8367             :   // If the next character is not a letter or number, there is no first-letter.
    8368             :   // Return true so that we don't go on looking, but set aLength to 0.
    8369           0 :   if (!nsContentUtils::IsAlphanumericAt(aFrag, aOffset + i)) {
    8370           0 :     *aLength = 0;
    8371           0 :     return true;
    8372             :   }
    8373             : 
    8374             :   // consume another cluster (the actual first letter)
    8375             : 
    8376             :   // For complex scripts such as Indic and SEAsian, where first-letter
    8377             :   // should extend to entire orthographic "syllable" clusters, we don't
    8378             :   // want to allow this to split a ligature.
    8379             :   bool allowSplitLigature;
    8380             : 
    8381             :   typedef unicode::Script Script;
    8382           0 :   switch (unicode::GetScriptCode(aFrag->CharAt(aOffset + i))) {
    8383             :     default:
    8384           0 :       allowSplitLigature = true;
    8385           0 :       break;
    8386             : 
    8387             :     // For now, lacking any definitive specification of when to apply this
    8388             :     // behavior, we'll base the decision on the HarfBuzz shaping engine
    8389             :     // used for each script: those that are handled by the Indic, Tibetan,
    8390             :     // Myanmar and SEAsian shapers will apply the "don't split ligatures"
    8391             :     // rule.
    8392             : 
    8393             :     // Indic
    8394             :     case Script::BENGALI:
    8395             :     case Script::DEVANAGARI:
    8396             :     case Script::GUJARATI:
    8397             :     case Script::GURMUKHI:
    8398             :     case Script::KANNADA:
    8399             :     case Script::MALAYALAM:
    8400             :     case Script::ORIYA:
    8401             :     case Script::TAMIL:
    8402             :     case Script::TELUGU:
    8403             :     case Script::SINHALA:
    8404             :     case Script::BALINESE:
    8405             :     case Script::LEPCHA:
    8406             :     case Script::REJANG:
    8407             :     case Script::SUNDANESE:
    8408             :     case Script::JAVANESE:
    8409             :     case Script::KAITHI:
    8410             :     case Script::MEETEI_MAYEK:
    8411             :     case Script::CHAKMA:
    8412             :     case Script::SHARADA:
    8413             :     case Script::TAKRI:
    8414             :     case Script::KHMER:
    8415             : 
    8416             :     // Tibetan
    8417             :     case Script::TIBETAN:
    8418             : 
    8419             :     // Myanmar
    8420             :     case Script::MYANMAR:
    8421             : 
    8422             :     // Other SEAsian
    8423             :     case Script::BUGINESE:
    8424             :     case Script::NEW_TAI_LUE:
    8425             :     case Script::CHAM:
    8426             :     case Script::TAI_THAM:
    8427             : 
    8428             :     // What about Thai/Lao - any special handling needed?
    8429             :     // Should we special-case Arabic lam-alef?
    8430             : 
    8431           0 :       allowSplitLigature = false;
    8432           0 :       break;
    8433             :   }
    8434             : 
    8435           0 :   iter.SetOriginalOffset(aOffset + i);
    8436           0 :   FindClusterEnd(aTextRun, endOffset, &iter, allowSplitLigature);
    8437             : 
    8438           0 :   i = iter.GetOriginalOffset() - aOffset;
    8439           0 :   if (i + 1 == length)
    8440           0 :     return true;
    8441             : 
    8442             :   // consume clusters that start with punctuation
    8443           0 :   i = FindEndOfPunctuationRun(aFrag, aTextRun, &iter, aOffset, i + 1, endOffset);
    8444           0 :   if (i < length)
    8445           0 :     *aLength = i;
    8446           0 :   return true;
    8447             : }
    8448             : 
    8449             : static uint32_t
    8450          38 : FindStartAfterSkippingWhitespace(PropertyProvider* aProvider,
    8451             :                                  nsIFrame::InlineIntrinsicISizeData* aData,
    8452             :                                  const nsStyleText* aTextStyle,
    8453             :                                  gfxSkipCharsIterator* aIterator,
    8454             :                                  uint32_t aFlowEndInTextRun)
    8455             : {
    8456          38 :   if (aData->mSkipWhitespace) {
    8457          52 :     while (aIterator->GetSkippedOffset() < aFlowEndInTextRun &&
    8458          14 :            IsTrimmableSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset(), aTextStyle)) {
    8459           0 :       aIterator->AdvanceOriginal(1);
    8460             :     }
    8461             :   }
    8462          38 :   return aIterator->GetSkippedOffset();
    8463             : }
    8464             : 
    8465             : float
    8466          91 : nsTextFrame::GetFontSizeInflation() const
    8467             : {
    8468          91 :   if (!HasFontSizeInflation()) {
    8469          91 :     return 1.0f;
    8470             :   }
    8471           0 :   return GetProperty(FontSizeInflationProperty());
    8472             : }
    8473             : 
    8474             : void
    8475           2 : nsTextFrame::SetFontSizeInflation(float aInflation)
    8476             : {
    8477           2 :   if (aInflation == 1.0f) {
    8478           2 :     if (HasFontSizeInflation()) {
    8479           0 :       RemoveStateBits(TEXT_HAS_FONT_INFLATION);
    8480           0 :       DeleteProperty(FontSizeInflationProperty());
    8481             :     }
    8482           2 :     return;
    8483             :   }
    8484             : 
    8485           0 :   AddStateBits(TEXT_HAS_FONT_INFLATION);
    8486           0 :   SetProperty(FontSizeInflationProperty(), aInflation);
    8487             : }
    8488             : 
    8489             : /* virtual */
    8490          11 : void nsTextFrame::MarkIntrinsicISizesDirty()
    8491             : {
    8492          11 :   ClearTextRuns();
    8493          11 :   nsFrame::MarkIntrinsicISizesDirty();
    8494          11 : }
    8495             : 
    8496             : // XXX this doesn't handle characters shaped by line endings. We need to
    8497             : // temporarily override the "current line ending" settings.
    8498             : void
    8499          19 : nsTextFrame::AddInlineMinISizeForFlow(gfxContext *aRenderingContext,
    8500             :                                       nsIFrame::InlineMinISizeData *aData,
    8501             :                                       TextRunType aTextRunType)
    8502             : {
    8503             :   uint32_t flowEndInTextRun;
    8504             :   gfxSkipCharsIterator iter =
    8505             :     EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(),
    8506          19 :                   aData->LineContainer(), aData->mLine, &flowEndInTextRun);
    8507          19 :   gfxTextRun *textRun = GetTextRun(aTextRunType);
    8508          19 :   if (!textRun)
    8509           0 :     return;
    8510             : 
    8511             :   // Pass null for the line container. This will disable tab spacing, but that's
    8512             :   // OK since we can't really handle tabs for intrinsic sizing anyway.
    8513          19 :   const nsStyleText* textStyle = StyleText();
    8514          19 :   const nsTextFragment* frag = mContent->GetText();
    8515             : 
    8516             :   // If we're hyphenating, the PropertyProvider needs the actual length;
    8517             :   // otherwise we can just pass INT32_MAX to mean "all the text"
    8518          19 :   int32_t len = INT32_MAX;
    8519          26 :   bool hyphenating = frag->GetLength() > 0 &&
    8520          14 :     (textStyle->mHyphens == StyleHyphens::Auto ||
    8521          21 :      (textStyle->mHyphens == StyleHyphens::Manual &&
    8522          33 :       !!(textRun->GetFlags() & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS)));
    8523          19 :   if (hyphenating) {
    8524           0 :     gfxSkipCharsIterator tmp(iter);
    8525           0 :     len = std::min<int32_t>(GetContentOffset() + GetInFlowContentLength(),
    8526           0 :                  tmp.ConvertSkippedToOriginal(flowEndInTextRun)) - iter.GetOriginalOffset();
    8527             :   }
    8528             :   PropertyProvider provider(textRun, textStyle, frag, this,
    8529          38 :                             iter, len, nullptr, 0, aTextRunType);
    8530             : 
    8531          19 :   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
    8532          19 :   bool preformatNewlines = textStyle->NewlineIsSignificant(this);
    8533          19 :   bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
    8534          19 :   gfxFloat tabWidth = -1;
    8535             :   uint32_t start =
    8536          19 :     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
    8537             : 
    8538             :   // text-combine-upright frame is constantly 1em on inline-axis.
    8539          19 :   if (StyleContext()->IsTextCombined()) {
    8540           0 :     if (start < flowEndInTextRun && textRun->CanBreakLineBefore(start)) {
    8541           0 :       aData->OptionallyBreak();
    8542             :     }
    8543           0 :     aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
    8544           0 :     aData->mTrailingWhitespace = 0;
    8545           0 :     return;
    8546             :   }
    8547             : 
    8548          38 :   AutoTArray<gfxTextRun::HyphenType, BIG_TEXT_NODE_SIZE> hyphBuffer;
    8549          19 :   if (hyphenating) {
    8550           0 :     if (hyphBuffer.AppendElements(flowEndInTextRun - start, fallible)) {
    8551           0 :       provider.GetHyphenationBreaks(Range(start, flowEndInTextRun),
    8552           0 :                                     hyphBuffer.Elements());
    8553             :     } else {
    8554           0 :       hyphenating = false;
    8555             :     }
    8556             :   }
    8557             : 
    8558         202 :   for (uint32_t i = start, wordStart = start; i <= flowEndInTextRun; ++i) {
    8559         183 :     bool preformattedNewline = false;
    8560         183 :     bool preformattedTab = false;
    8561         183 :     if (i < flowEndInTextRun) {
    8562             :       // XXXldb Shouldn't we be including the newline as part of the
    8563             :       // segment that it ends rather than part of the segment that it
    8564             :       // starts?
    8565         164 :       preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
    8566         164 :       preformattedTab = preformatTabs && textRun->CharIsTab(i);
    8567         492 :       if (!textRun->CanBreakLineBefore(i) &&
    8568         328 :           !preformattedNewline &&
    8569         656 :           !preformattedTab &&
    8570         164 :           (!hyphenating ||
    8571           0 :            hyphBuffer[i - start] == gfxTextRun::HyphenType::None))
    8572             :       {
    8573             :         // we can't break here (and it's not the end of the flow)
    8574         164 :         continue;
    8575             :       }
    8576             :     }
    8577             : 
    8578          19 :     if (i > wordStart) {
    8579          14 :       nscoord width = NSToCoordCeilClamped(
    8580           7 :         textRun->GetAdvanceWidth(Range(wordStart, i), &provider));
    8581           7 :       width = std::max(0, width);
    8582           7 :       aData->mCurrentLine = NSCoordSaturatingAdd(aData->mCurrentLine, width);
    8583           7 :       aData->mAtStartOfLine = false;
    8584             : 
    8585           7 :       if (collapseWhitespace) {
    8586           2 :         uint32_t trimStart = GetEndOfTrimmedText(frag, textStyle, wordStart, i, &iter);
    8587           2 :         if (trimStart == start) {
    8588             :           // This is *all* trimmable whitespace, so whatever trailingWhitespace
    8589             :           // we saw previously is still trailing...
    8590           0 :           aData->mTrailingWhitespace += width;
    8591             :         } else {
    8592             :           // Some non-whitespace so the old trailingWhitespace is no longer trailing
    8593           4 :           nscoord wsWidth = NSToCoordCeilClamped(
    8594           2 :             textRun->GetAdvanceWidth(Range(trimStart, i), &provider));
    8595           2 :           aData->mTrailingWhitespace = std::max(0, wsWidth);
    8596             :         }
    8597             :       } else {
    8598           5 :         aData->mTrailingWhitespace = 0;
    8599             :       }
    8600             :     }
    8601             : 
    8602          19 :     if (preformattedTab) {
    8603             :       PropertyProvider::Spacing spacing;
    8604           0 :       provider.GetSpacing(Range(i, i + 1), &spacing);
    8605           0 :       aData->mCurrentLine += nscoord(spacing.mBefore);
    8606           0 :       if (tabWidth < 0) {
    8607           0 :         tabWidth = ComputeTabWidthAppUnits(this, textRun);
    8608             :       }
    8609             :       gfxFloat afterTab =
    8610           0 :         AdvanceToNextTab(aData->mCurrentLine, tabWidth);
    8611           0 :       aData->mCurrentLine = nscoord(afterTab + spacing.mAfter);
    8612           0 :       wordStart = i + 1;
    8613          57 :     } else if (i < flowEndInTextRun ||
    8614          57 :         (i == textRun->GetLength() &&
    8615          76 :          (textRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_HAS_TRAILING_BREAK))) {
    8616           0 :       if (preformattedNewline) {
    8617           0 :         aData->ForceBreak();
    8618           0 :       } else if (i < flowEndInTextRun && hyphenating &&
    8619           0 :                  hyphBuffer[i - start] != gfxTextRun::HyphenType::None) {
    8620           0 :         aData->OptionallyBreak(NSToCoordRound(provider.GetHyphenWidth()));
    8621             :       } else {
    8622           0 :         aData->OptionallyBreak();
    8623             :       }
    8624           0 :       wordStart = i;
    8625             :     }
    8626             :   }
    8627             : 
    8628          19 :   if (start < flowEndInTextRun) {
    8629             :     // Check if we have collapsible whitespace at the end
    8630           7 :     aData->mSkipWhitespace =
    8631           7 :       IsTrimmableSpace(provider.GetFragment(),
    8632           7 :                        iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
    8633             :                        textStyle);
    8634             :   }
    8635             : }
    8636             : 
    8637          48 : bool nsTextFrame::IsCurrentFontInflation(float aInflation) const {
    8638          48 :   return fabsf(aInflation - GetFontSizeInflation()) < 1e-6;
    8639             : }
    8640             : 
    8641             : // XXX Need to do something here to avoid incremental reflow bugs due to
    8642             : // first-line and first-letter changing min-width
    8643             : /* virtual */ void
    8644          19 : nsTextFrame::AddInlineMinISize(gfxContext *aRenderingContext,
    8645             :                                nsIFrame::InlineMinISizeData *aData)
    8646             : {
    8647          19 :   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
    8648          19 :   TextRunType trtype = (inflation == 1.0f) ? eNotInflated : eInflated;
    8649             : 
    8650          19 :   if (trtype == eInflated && !IsCurrentFontInflation(inflation)) {
    8651             :     // FIXME: Ideally, if we already have a text run, we'd move it to be
    8652             :     // the uninflated text run.
    8653           0 :     ClearTextRun(nullptr, nsTextFrame::eInflated);
    8654             :   }
    8655             : 
    8656             :   nsTextFrame* f;
    8657          19 :   const gfxTextRun* lastTextRun = nullptr;
    8658             :   // nsContinuingTextFrame does nothing for AddInlineMinISize; all text frames
    8659             :   // in the flow are handled right here.
    8660          38 :   for (f = this; f; f = f->GetNextContinuation()) {
    8661             :     // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
    8662             :     // haven't set up textruns yet for f.  Except in OOM situations,
    8663             :     // lastTextRun will only be null for the first text frame.
    8664          19 :     if (f == this || f->GetTextRun(trtype) != lastTextRun) {
    8665             :       nsIFrame* lc;
    8666          38 :       if (aData->LineContainer() &&
    8667          19 :           aData->LineContainer() != (lc = FindLineContainer(f))) {
    8668           0 :         NS_ASSERTION(f != this, "wrong InlineMinISizeData container"
    8669             :                                 " for first continuation");
    8670           0 :         aData->mLine = nullptr;
    8671           0 :         aData->SetLineContainer(lc);
    8672             :       }
    8673             : 
    8674             :       // This will process all the text frames that share the same textrun as f.
    8675          19 :       f->AddInlineMinISizeForFlow(aRenderingContext, aData, trtype);
    8676          19 :       lastTextRun = f->GetTextRun(trtype);
    8677             :     }
    8678             :   }
    8679          19 : }
    8680             : 
    8681             : // XXX this doesn't handle characters shaped by line endings. We need to
    8682             : // temporarily override the "current line ending" settings.
    8683             : void
    8684          19 : nsTextFrame::AddInlinePrefISizeForFlow(gfxContext *aRenderingContext,
    8685             :                                        nsIFrame::InlinePrefISizeData *aData,
    8686             :                                        TextRunType aTextRunType)
    8687             : {
    8688             :   uint32_t flowEndInTextRun;
    8689             :   gfxSkipCharsIterator iter =
    8690             :     EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(),
    8691          19 :                   aData->LineContainer(), aData->mLine, &flowEndInTextRun);
    8692          19 :   gfxTextRun *textRun = GetTextRun(aTextRunType);
    8693          19 :   if (!textRun)
    8694           0 :     return;
    8695             : 
    8696             :   // Pass null for the line container. This will disable tab spacing, but that's
    8697             :   // OK since we can't really handle tabs for intrinsic sizing anyway.
    8698             : 
    8699          19 :   const nsStyleText* textStyle = StyleText();
    8700          19 :   const nsTextFragment* frag = mContent->GetText();
    8701             :   PropertyProvider provider(textRun, textStyle, frag, this,
    8702          38 :                             iter, INT32_MAX, nullptr, 0, aTextRunType);
    8703             : 
    8704             :   // text-combine-upright frame is constantly 1em on inline-axis.
    8705          19 :   if (StyleContext()->IsTextCombined()) {
    8706           0 :     aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
    8707           0 :     aData->mTrailingWhitespace = 0;
    8708           0 :     aData->mLineIsEmpty = false;
    8709           0 :     return;
    8710             :   }
    8711             : 
    8712          19 :   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
    8713          19 :   bool preformatNewlines = textStyle->NewlineIsSignificant(this);
    8714          19 :   bool preformatTabs = textStyle->TabIsSignificant();
    8715          19 :   gfxFloat tabWidth = -1;
    8716             :   uint32_t start =
    8717          19 :     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
    8718             : 
    8719             :   // XXX Should we consider hyphenation here?
    8720             :   // If newlines and tabs aren't preformatted, nothing to do inside
    8721             :   // the loop so make i skip to the end
    8722          19 :   uint32_t loopStart = (preformatNewlines || preformatTabs) ? start : flowEndInTextRun;
    8723         142 :   for (uint32_t i = loopStart, lineStart = start; i <= flowEndInTextRun; ++i) {
    8724         123 :     bool preformattedNewline = false;
    8725         123 :     bool preformattedTab = false;
    8726         123 :     if (i < flowEndInTextRun) {
    8727             :       // XXXldb Shouldn't we be including the newline as part of the
    8728             :       // segment that it ends rather than part of the segment that it
    8729             :       // starts?
    8730         104 :       NS_ASSERTION(preformatNewlines || preformatTabs,
    8731             :                    "We can't be here unless newlines are "
    8732             :                    "hard breaks or there are tabs");
    8733         104 :       preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
    8734         104 :       preformattedTab = preformatTabs && textRun->CharIsTab(i);
    8735         104 :       if (!preformattedNewline && !preformattedTab) {
    8736             :         // we needn't break here (and it's not the end of the flow)
    8737         104 :         continue;
    8738             :       }
    8739             :     }
    8740             : 
    8741          19 :     if (i > lineStart) {
    8742          14 :       nscoord width = NSToCoordCeilClamped(
    8743           7 :         textRun->GetAdvanceWidth(Range(lineStart, i), &provider));
    8744           7 :       width = std::max(0, width);
    8745           7 :       aData->mCurrentLine = NSCoordSaturatingAdd(aData->mCurrentLine, width);
    8746           7 :       aData->mLineIsEmpty = false;
    8747             : 
    8748           7 :       if (collapseWhitespace) {
    8749           2 :         uint32_t trimStart = GetEndOfTrimmedText(frag, textStyle, lineStart, i, &iter);
    8750           2 :         if (trimStart == start) {
    8751             :           // This is *all* trimmable whitespace, so whatever trailingWhitespace
    8752             :           // we saw previously is still trailing...
    8753           0 :           aData->mTrailingWhitespace += width;
    8754             :         } else {
    8755             :           // Some non-whitespace so the old trailingWhitespace is no longer trailing
    8756           4 :           nscoord wsWidth = NSToCoordCeilClamped(
    8757           2 :             textRun->GetAdvanceWidth(Range(trimStart, i), &provider));
    8758           2 :           aData->mTrailingWhitespace = std::max(0, wsWidth);
    8759             :         }
    8760             :       } else {
    8761           5 :         aData->mTrailingWhitespace = 0;
    8762             :       }
    8763             :     }
    8764             : 
    8765          19 :     if (preformattedTab) {
    8766             :       PropertyProvider::Spacing spacing;
    8767           0 :       provider.GetSpacing(Range(i, i + 1), &spacing);
    8768           0 :       aData->mCurrentLine += nscoord(spacing.mBefore);
    8769           0 :       if (tabWidth < 0) {
    8770           0 :         tabWidth = ComputeTabWidthAppUnits(this, textRun);
    8771             :       }
    8772             :       gfxFloat afterTab =
    8773           0 :         AdvanceToNextTab(aData->mCurrentLine, tabWidth);
    8774           0 :       aData->mCurrentLine = nscoord(afterTab + spacing.mAfter);
    8775           0 :       aData->mLineIsEmpty = false;
    8776           0 :       lineStart = i + 1;
    8777          19 :     } else if (preformattedNewline) {
    8778           0 :       aData->ForceBreak();
    8779           0 :       lineStart = i;
    8780             :     }
    8781             :   }
    8782             : 
    8783             :   // Check if we have collapsible whitespace at the end
    8784          19 :   if (start < flowEndInTextRun) {
    8785           7 :     aData->mSkipWhitespace =
    8786           7 :       IsTrimmableSpace(provider.GetFragment(),
    8787           7 :                        iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
    8788             :                        textStyle);
    8789             :   }
    8790             : }
    8791             : 
    8792             : // XXX Need to do something here to avoid incremental reflow bugs due to
    8793             : // first-line and first-letter changing pref-width
    8794             : /* virtual */ void
    8795          19 : nsTextFrame::AddInlinePrefISize(gfxContext *aRenderingContext,
    8796             :                                 nsIFrame::InlinePrefISizeData *aData)
    8797             : {
    8798          19 :   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
    8799          19 :   TextRunType trtype = (inflation == 1.0f) ? eNotInflated : eInflated;
    8800             : 
    8801          19 :   if (trtype == eInflated && !IsCurrentFontInflation(inflation)) {
    8802             :     // FIXME: Ideally, if we already have a text run, we'd move it to be
    8803             :     // the uninflated text run.
    8804           0 :     ClearTextRun(nullptr, nsTextFrame::eInflated);
    8805             :   }
    8806             : 
    8807             :   nsTextFrame* f;
    8808          19 :   const gfxTextRun* lastTextRun = nullptr;
    8809             :   // nsContinuingTextFrame does nothing for AddInlineMinISize; all text frames
    8810             :   // in the flow are handled right here.
    8811          38 :   for (f = this; f; f = f->GetNextContinuation()) {
    8812             :     // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
    8813             :     // haven't set up textruns yet for f.  Except in OOM situations,
    8814             :     // lastTextRun will only be null for the first text frame.
    8815          19 :     if (f == this || f->GetTextRun(trtype) != lastTextRun) {
    8816             :       nsIFrame* lc;
    8817          38 :       if (aData->LineContainer() &&
    8818          19 :           aData->LineContainer() != (lc = FindLineContainer(f))) {
    8819           0 :         NS_ASSERTION(f != this, "wrong InlinePrefISizeData container"
    8820             :                                 " for first continuation");
    8821           0 :         aData->mLine = nullptr;
    8822           0 :         aData->SetLineContainer(lc);
    8823             :       }
    8824             : 
    8825             :       // This will process all the text frames that share the same textrun as f.
    8826          19 :       f->AddInlinePrefISizeForFlow(aRenderingContext, aData, trtype);
    8827          19 :       lastTextRun = f->GetTextRun(trtype);
    8828             :     }
    8829             :   }
    8830          19 : }
    8831             : 
    8832             : /* virtual */
    8833             : LogicalSize
    8834           0 : nsTextFrame::ComputeSize(gfxContext *aRenderingContext,
    8835             :                          WritingMode aWM,
    8836             :                          const LogicalSize& aCBSize,
    8837             :                          nscoord aAvailableISize,
    8838             :                          const LogicalSize& aMargin,
    8839             :                          const LogicalSize& aBorder,
    8840             :                          const LogicalSize& aPadding,
    8841             :                          ComputeSizeFlags aFlags)
    8842             : {
    8843             :   // Inlines and text don't compute size before reflow.
    8844           0 :   return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
    8845             : }
    8846             : 
    8847             : static nsRect
    8848          24 : RoundOut(const gfxRect& aRect)
    8849             : {
    8850          24 :   nsRect r;
    8851          24 :   r.x = NSToCoordFloor(aRect.X());
    8852          24 :   r.y = NSToCoordFloor(aRect.Y());
    8853          24 :   r.width = NSToCoordCeil(aRect.XMost()) - r.x;
    8854          24 :   r.height = NSToCoordCeil(aRect.YMost()) - r.y;
    8855          24 :   return r;
    8856             : }
    8857             : 
    8858             : nsRect
    8859           0 : nsTextFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const
    8860             : {
    8861           0 :   if (StyleContext()->HasTextDecorationLines() ||
    8862           0 :       (GetStateBits() & TEXT_HYPHEN_BREAK)) {
    8863             :     // This is conservative, but OK.
    8864           0 :     return GetVisualOverflowRect();
    8865             :   }
    8866             : 
    8867             :   gfxSkipCharsIterator iter =
    8868           0 :     const_cast<nsTextFrame*>(this)->EnsureTextRun(nsTextFrame::eInflated);
    8869           0 :   if (!mTextRun)
    8870           0 :     return nsRect(0, 0, 0, 0);
    8871             : 
    8872             :   PropertyProvider provider(const_cast<nsTextFrame*>(this), iter,
    8873           0 :                             nsTextFrame::eInflated);
    8874             :   // Trim trailing whitespace
    8875           0 :   provider.InitializeForDisplay(true);
    8876             : 
    8877             :   gfxTextRun::Metrics metrics =
    8878             :         mTextRun->MeasureText(ComputeTransformedRange(provider),
    8879             :                               gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
    8880           0 :                               aDrawTarget, &provider);
    8881           0 :   if (GetWritingMode().IsLineInverted()) {
    8882           0 :     metrics.mBoundingBox.y = -metrics.mBoundingBox.YMost();
    8883             :   }
    8884             :   // mAscent should be the same as metrics.mAscent, but it's what we use to
    8885             :   // paint so that's the one we'll use.
    8886           0 :   nsRect boundingBox = RoundOut(metrics.mBoundingBox);
    8887           0 :   boundingBox += nsPoint(0, mAscent);
    8888           0 :   if (mTextRun->IsVertical()) {
    8889             :     // Swap line-relative textMetrics dimensions to physical coordinates.
    8890           0 :     Swap(boundingBox.x, boundingBox.y);
    8891           0 :     Swap(boundingBox.width, boundingBox.height);
    8892             :   }
    8893           0 :   return boundingBox;
    8894             : }
    8895             : 
    8896             : /* virtual */ nsresult
    8897           0 : nsTextFrame::GetPrefWidthTightBounds(gfxContext* aContext,
    8898             :                                      nscoord* aX,
    8899             :                                      nscoord* aXMost)
    8900             : {
    8901             :   gfxSkipCharsIterator iter =
    8902           0 :     const_cast<nsTextFrame*>(this)->EnsureTextRun(nsTextFrame::eInflated);
    8903           0 :   if (!mTextRun)
    8904           0 :     return NS_ERROR_FAILURE;
    8905             : 
    8906             :   PropertyProvider provider(const_cast<nsTextFrame*>(this), iter,
    8907           0 :                             nsTextFrame::eInflated);
    8908           0 :   provider.InitializeForMeasure();
    8909             : 
    8910             :   gfxTextRun::Metrics metrics =
    8911             :         mTextRun->MeasureText(ComputeTransformedRange(provider),
    8912             :                               gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
    8913           0 :                               aContext->GetDrawTarget(), &provider);
    8914             :   // Round it like nsTextFrame::ComputeTightBounds() to ensure consistency.
    8915           0 :   *aX = NSToCoordFloor(metrics.mBoundingBox.x);
    8916           0 :   *aXMost = NSToCoordCeil(metrics.mBoundingBox.XMost());
    8917             : 
    8918           0 :   return NS_OK;
    8919             : }
    8920             : 
    8921             : static bool
    8922          24 : HasSoftHyphenBefore(const nsTextFragment* aFrag, const gfxTextRun* aTextRun,
    8923             :                     int32_t aStartOffset, const gfxSkipCharsIterator& aIter)
    8924             : {
    8925          24 :   if (aIter.GetSkippedOffset() < aTextRun->GetLength() &&
    8926           0 :       aTextRun->CanHyphenateBefore(aIter.GetSkippedOffset())) {
    8927           0 :     return true;
    8928             :   }
    8929          24 :   if (!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_HAS_SHY))
    8930          24 :     return false;
    8931           0 :   gfxSkipCharsIterator iter = aIter;
    8932           0 :   while (iter.GetOriginalOffset() > aStartOffset) {
    8933           0 :     iter.AdvanceOriginal(-1);
    8934           0 :     if (!iter.IsOriginalCharSkipped())
    8935           0 :       break;
    8936           0 :     if (aFrag->CharAt(iter.GetOriginalOffset()) == CH_SHY)
    8937           0 :       return true;
    8938             :   }
    8939           0 :   return false;
    8940             : }
    8941             : 
    8942             : /**
    8943             :  * Removes all frames from aFrame up to (but not including) aFirstToNotRemove,
    8944             :  * because their text has all been taken and reflowed by earlier frames.
    8945             :  */
    8946             : static void
    8947           0 : RemoveEmptyInFlows(nsTextFrame* aFrame, nsTextFrame* aFirstToNotRemove)
    8948             : {
    8949           0 :   NS_PRECONDITION(aFrame != aFirstToNotRemove, "This will go very badly");
    8950             :   // We have to be careful here, because some RemoveFrame implementations
    8951             :   // remove and destroy not only the passed-in frame but also all its following
    8952             :   // in-flows (and sometimes all its following continuations in general).  So
    8953             :   // we remove |f| and everything up to but not including firstToNotRemove from
    8954             :   // the flow first, to make sure that only the things we want destroyed are
    8955             :   // destroyed.
    8956             : 
    8957             :   // This sadly duplicates some of the logic from
    8958             :   // nsSplittableFrame::RemoveFromFlow.  We can get away with not duplicating
    8959             :   // all of it, because we know that the prev-continuation links of
    8960             :   // firstToNotRemove and f are fluid, and non-null.
    8961           0 :   NS_ASSERTION(aFirstToNotRemove->GetPrevContinuation() ==
    8962             :                aFirstToNotRemove->GetPrevInFlow() &&
    8963             :                aFirstToNotRemove->GetPrevInFlow() != nullptr,
    8964             :                "aFirstToNotRemove should have a fluid prev continuation");
    8965           0 :   NS_ASSERTION(aFrame->GetPrevContinuation() ==
    8966             :                aFrame->GetPrevInFlow() &&
    8967             :                aFrame->GetPrevInFlow() != nullptr,
    8968             :                "aFrame should have a fluid prev continuation");
    8969             : 
    8970           0 :   nsTextFrame* prevContinuation = aFrame->GetPrevContinuation();
    8971           0 :   nsTextFrame* lastRemoved = aFirstToNotRemove->GetPrevContinuation();
    8972             : 
    8973           0 :   for (nsTextFrame* f = aFrame; f != aFirstToNotRemove;
    8974             :        f = f->GetNextContinuation()) {
    8975             :     // f is going to be destroyed soon, after it is unlinked from the
    8976             :     // continuation chain. If its textrun is going to be destroyed we need to
    8977             :     // do it now, before we unlink the frames to remove from the flow,
    8978             :     // because DestroyFrom calls ClearTextRuns() and that will start at the
    8979             :     // first frame with the text run and walk the continuations.
    8980           0 :     if (f->IsInTextRunUserData()) {
    8981           0 :       f->ClearTextRuns();
    8982             :     } else {
    8983           0 :       f->DisconnectTextRuns();
    8984             :     }
    8985             :   }
    8986             : 
    8987           0 :   prevContinuation->SetNextInFlow(aFirstToNotRemove);
    8988           0 :   aFirstToNotRemove->SetPrevInFlow(prevContinuation);
    8989             : 
    8990           0 :   aFrame->SetPrevInFlow(nullptr);
    8991           0 :   lastRemoved->SetNextInFlow(nullptr);
    8992             : 
    8993           0 :   nsContainerFrame* parent = aFrame->GetParent();
    8994           0 :   nsBlockFrame* parentBlock = nsLayoutUtils::GetAsBlock(parent);
    8995           0 :   if (parentBlock) {
    8996             :     // Manually call DoRemoveFrame so we can tell it that we're
    8997             :     // removing empty frames; this will keep it from blowing away
    8998             :     // text runs.
    8999           0 :     parentBlock->DoRemoveFrame(aFrame, nsBlockFrame::FRAMES_ARE_EMPTY);
    9000             :   } else {
    9001             :     // Just remove it normally; use kNoReflowPrincipalList to avoid posting
    9002             :     // new reflows.
    9003           0 :     parent->RemoveFrame(nsIFrame::kNoReflowPrincipalList, aFrame);
    9004             :   }
    9005           0 : }
    9006             : 
    9007             : void
    9008          24 : nsTextFrame::SetLength(int32_t aLength, nsLineLayout* aLineLayout,
    9009             :                        uint32_t aSetLengthFlags)
    9010             : {
    9011          24 :   mContentLengthHint = aLength;
    9012          24 :   int32_t end = GetContentOffset() + aLength;
    9013          24 :   nsTextFrame* f = GetNextInFlow();
    9014          24 :   if (!f)
    9015          24 :     return;
    9016             : 
    9017             :   // If our end offset is moving, then even if frames are not being pushed or
    9018             :   // pulled, content is moving to or from the next line and the next line
    9019             :   // must be reflowed.
    9020             :   // If the next-continuation is dirty, then we should dirty the next line now
    9021             :   // because we may have skipped doing it if we dirtied it in
    9022             :   // CharacterDataChanged. This is ugly but teaching FrameNeedsReflow
    9023             :   // and ChildIsDirty to handle a range of frames would be worse.
    9024           0 :   if (aLineLayout &&
    9025           0 :       (end != f->mContentOffset || (f->GetStateBits() & NS_FRAME_IS_DIRTY))) {
    9026           0 :     aLineLayout->SetDirtyNextLine();
    9027             :   }
    9028             : 
    9029           0 :   if (end < f->mContentOffset) {
    9030             :     // Our frame is shrinking. Give the text to our next in flow.
    9031           0 :     if (aLineLayout && HasSignificantTerminalNewline() &&
    9032           0 :         !GetParent()->IsLetterFrame() &&
    9033           0 :         (aSetLengthFlags & ALLOW_FRAME_CREATION_AND_DESTRUCTION)) {
    9034             :       // Whatever text we hand to our next-in-flow will end up in a frame all of
    9035             :       // its own, since it ends in a forced linebreak.  Might as well just put
    9036             :       // it in a separate frame now.  This is important to prevent text run
    9037             :       // churn; if we did not do that, then we'd likely end up rebuilding
    9038             :       // textruns for all our following continuations.
    9039             :       // We skip this optimization when the parent is a first-letter frame
    9040             :       // because it doesn't deal well with more than one child frame.
    9041             :       // We also skip this optimization if we were called during bidi
    9042             :       // resolution, so as not to create a new frame which doesn't appear in
    9043             :       // the bidi resolver's list of frames
    9044           0 :       nsPresContext* presContext = PresContext();
    9045             :       nsIFrame* newFrame = presContext->PresShell()->FrameConstructor()->
    9046           0 :         CreateContinuingFrame(presContext, this, GetParent());
    9047           0 :       nsTextFrame* next = static_cast<nsTextFrame*>(newFrame);
    9048           0 :       nsFrameList temp(next, next);
    9049           0 :       GetParent()->InsertFrames(kNoReflowPrincipalList, this, temp);
    9050           0 :       f = next;
    9051             :     }
    9052             : 
    9053           0 :     f->mContentOffset = end;
    9054           0 :     if (f->GetTextRun(nsTextFrame::eInflated) != mTextRun) {
    9055           0 :       ClearTextRuns();
    9056           0 :       f->ClearTextRuns();
    9057             :     }
    9058           0 :     return;
    9059             :   }
    9060             :   // Our frame is growing. Take text from our in-flow(s).
    9061             :   // We can take text from frames in lines beyond just the next line.
    9062             :   // We don't dirty those lines. That's OK, because when we reflow
    9063             :   // our empty next-in-flow, it will take text from its next-in-flow and
    9064             :   // dirty that line.
    9065             : 
    9066             :   // Note that in the process we may end up removing some frames from
    9067             :   // the flow if they end up empty.
    9068           0 :   nsTextFrame* framesToRemove = nullptr;
    9069           0 :   while (f && f->mContentOffset < end) {
    9070           0 :     f->mContentOffset = end;
    9071           0 :     if (f->GetTextRun(nsTextFrame::eInflated) != mTextRun) {
    9072           0 :       ClearTextRuns();
    9073           0 :       f->ClearTextRuns();
    9074             :     }
    9075           0 :     nsTextFrame* next = f->GetNextInFlow();
    9076             :     // Note: the "f->GetNextSibling() == next" check below is to restrict
    9077             :     // this optimization to the case where they are on the same child list.
    9078             :     // Otherwise we might remove the only child of a nsFirstLetterFrame
    9079             :     // for example and it can't handle that.  See bug 597627 for details.
    9080           0 :     if (next && next->mContentOffset <= end && f->GetNextSibling() == next &&
    9081           0 :         (aSetLengthFlags & ALLOW_FRAME_CREATION_AND_DESTRUCTION)) {
    9082             :       // |f| is now empty.  We may as well remove it, instead of copying all
    9083             :       // the text from |next| into it instead; the latter leads to use
    9084             :       // rebuilding textruns for all following continuations.
    9085             :       // We skip this optimization if we were called during bidi resolution,
    9086             :       // since the bidi resolver may try to handle the destroyed frame later
    9087             :       // and crash
    9088           0 :       if (!framesToRemove) {
    9089             :         // Remember that we have to remove this frame.
    9090           0 :         framesToRemove = f;
    9091             :       }
    9092           0 :     } else if (framesToRemove) {
    9093           0 :       RemoveEmptyInFlows(framesToRemove, f);
    9094           0 :       framesToRemove = nullptr;
    9095             :     }
    9096           0 :     f = next;
    9097             :   }
    9098           0 :   NS_POSTCONDITION(!framesToRemove || (f && f->mContentOffset == end),
    9099             :                    "How did we exit the loop if we null out framesToRemove if "
    9100             :                    "!next || next->mContentOffset > end ?");
    9101           0 :   if (framesToRemove) {
    9102             :     // We are guaranteed that we exited the loop with f not null, per the
    9103             :     // postcondition above
    9104           0 :     RemoveEmptyInFlows(framesToRemove, f);
    9105             :   }
    9106             : 
    9107             : #ifdef DEBUG
    9108           0 :   f = this;
    9109           0 :   int32_t iterations = 0;
    9110           0 :   while (f && iterations < 10) {
    9111           0 :     f->GetContentLength(); // Assert if negative length
    9112           0 :     f = f->GetNextContinuation();
    9113           0 :     ++iterations;
    9114             :   }
    9115           0 :   f = this;
    9116           0 :   iterations = 0;
    9117           0 :   while (f && iterations < 10) {
    9118           0 :     f->GetContentLength(); // Assert if negative length
    9119           0 :     f = f->GetPrevContinuation();
    9120           0 :     ++iterations;
    9121             :   }
    9122             : #endif
    9123             : }
    9124             : 
    9125             : bool
    9126          48 : nsTextFrame::IsFloatingFirstLetterChild() const
    9127             : {
    9128          48 :   nsIFrame* frame = GetParent();
    9129          48 :   return frame && frame->IsFloating() && frame->IsLetterFrame();
    9130             : }
    9131             : 
    9132             : bool
    9133          24 : nsTextFrame::IsInitialLetterChild() const
    9134             : {
    9135          24 :   nsIFrame* frame = GetParent();
    9136          24 :   return frame && frame->StyleTextReset()->mInitialLetterSize != 0.0f &&
    9137          24 :          frame->IsLetterFrame();
    9138             : }
    9139             : 
    9140             : struct NewlineProperty {
    9141             :   int32_t mStartOffset;
    9142             :   // The offset of the first \n after mStartOffset, or -1 if there is none
    9143             :   int32_t mNewlineOffset;
    9144             : };
    9145             : 
    9146             : void
    9147           0 : nsTextFrame::Reflow(nsPresContext*           aPresContext,
    9148             :                     ReflowOutput&     aMetrics,
    9149             :                     const ReflowInput& aReflowInput,
    9150             :                     nsReflowStatus&          aStatus)
    9151             : {
    9152           0 :   MarkInReflow();
    9153           0 :   DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
    9154           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
    9155             : 
    9156             :   // XXX If there's no line layout, we shouldn't even have created this
    9157             :   // frame. This may happen if, for example, this is text inside a table
    9158             :   // but not inside a cell. For now, just don't reflow.
    9159           0 :   if (!aReflowInput.mLineLayout) {
    9160           0 :     ClearMetrics(aMetrics);
    9161           0 :     aStatus.Reset();
    9162           0 :     return;
    9163             :   }
    9164             : 
    9165           0 :   ReflowText(*aReflowInput.mLineLayout, aReflowInput.AvailableWidth(),
    9166           0 :              aReflowInput.mRenderingContext->GetDrawTarget(), aMetrics, aStatus);
    9167             : 
    9168           0 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
    9169             : }
    9170             : 
    9171             : #ifdef ACCESSIBILITY
    9172             : /**
    9173             :  * Notifies accessibility about text reflow. Used by nsTextFrame::ReflowText.
    9174             :  */
    9175             : class MOZ_STACK_CLASS ReflowTextA11yNotifier
    9176             : {
    9177             : public:
    9178          46 :   ReflowTextA11yNotifier(nsPresContext* aPresContext, nsIContent* aContent) :
    9179          46 :     mContent(aContent), mPresContext(aPresContext)
    9180             :   {
    9181          46 :   }
    9182          46 :   ~ReflowTextA11yNotifier()
    9183          46 :   {
    9184          46 :     nsAccessibilityService* accService = nsIPresShell::AccService();
    9185          46 :     if (accService) {
    9186           0 :       accService->UpdateText(mPresContext->PresShell(), mContent);
    9187             :     }
    9188          46 :   }
    9189             : private:
    9190             :   ReflowTextA11yNotifier();
    9191             :   ReflowTextA11yNotifier(const ReflowTextA11yNotifier&);
    9192             :   ReflowTextA11yNotifier& operator =(const ReflowTextA11yNotifier&);
    9193             : 
    9194             :   nsIContent* mContent;
    9195             :   nsPresContext* mPresContext;
    9196             : };
    9197             : #endif
    9198             : 
    9199             : void
    9200          48 : nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
    9201             :                         DrawTarget* aDrawTarget,
    9202             :                         ReflowOutput& aMetrics,
    9203             :                         nsReflowStatus& aStatus)
    9204             : {
    9205             : #ifdef NOISY_REFLOW
    9206             :   ListTag(stdout);
    9207             :   printf(": BeginReflow: availableWidth=%d\n", aAvailableWidth);
    9208             : #endif
    9209             : 
    9210          48 :   nsPresContext* presContext = PresContext();
    9211             : 
    9212             : #ifdef ACCESSIBILITY
    9213             :   // Schedule the update of accessible tree since rendered text might be changed.
    9214          48 :   if (StyleVisibility()->IsVisible()) {
    9215          46 :     ReflowTextA11yNotifier(presContext, mContent);
    9216             :   }
    9217             : #endif
    9218             : 
    9219             :   /////////////////////////////////////////////////////////////////////
    9220             :   // Set up flags and clear out state
    9221             :   /////////////////////////////////////////////////////////////////////
    9222             : 
    9223             :   // Clear out the reflow state flags in mState. We also clear the whitespace
    9224             :   // flags because this can change whether the frame maps whitespace-only text
    9225             :   // or not.
    9226          48 :   RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
    9227             : 
    9228             :   // Temporarily map all possible content while we construct our new textrun.
    9229             :   // so that when doing reflow our styles prevail over any part of the
    9230             :   // textrun we look at. Note that next-in-flows may be mapping the same
    9231             :   // content; gfxTextRun construction logic will ensure that we take priority.
    9232          48 :   int32_t maxContentLength = GetInFlowContentLength();
    9233             : 
    9234             :   // We don't need to reflow if there is no content.
    9235          48 :   if (!maxContentLength) {
    9236          24 :     ClearMetrics(aMetrics);
    9237          24 :     aStatus.Reset();
    9238          24 :     return;
    9239             :   }
    9240             : 
    9241             : #ifdef NOISY_BIDI
    9242             :     printf("Reflowed textframe\n");
    9243             : #endif
    9244             : 
    9245          24 :   const nsStyleText* textStyle = StyleText();
    9246             : 
    9247          24 :   bool atStartOfLine = aLineLayout.LineAtStart();
    9248          24 :   if (atStartOfLine) {
    9249          24 :     AddStateBits(TEXT_START_OF_LINE);
    9250             :   }
    9251             : 
    9252             :   uint32_t flowEndInTextRun;
    9253          24 :   nsIFrame* lineContainer = aLineLayout.LineContainerFrame();
    9254          24 :   const nsTextFragment* frag = mContent->GetText();
    9255             : 
    9256             :   // DOM offsets of the text range we need to measure, after trimming
    9257             :   // whitespace, restricting to first-letter, and restricting preformatted text
    9258             :   // to nearest newline
    9259          24 :   int32_t length = maxContentLength;
    9260          24 :   int32_t offset = GetContentOffset();
    9261             : 
    9262             :   // Restrict preformatted text to the nearest newline
    9263          24 :   int32_t newLineOffset = -1; // this will be -1 or a content offset
    9264          24 :   int32_t contentNewLineOffset = -1;
    9265             :   // Pointer to the nsGkAtoms::newline set on this frame's element
    9266          24 :   NewlineProperty* cachedNewlineOffset = nullptr;
    9267          24 :   if (textStyle->NewlineIsSignificant(this)) {
    9268             :     cachedNewlineOffset =
    9269          19 :       static_cast<NewlineProperty*>(mContent->GetProperty(nsGkAtoms::newline));
    9270          19 :     if (cachedNewlineOffset && cachedNewlineOffset->mStartOffset <= offset &&
    9271           0 :         (cachedNewlineOffset->mNewlineOffset == -1 ||
    9272           0 :          cachedNewlineOffset->mNewlineOffset >= offset)) {
    9273           0 :       contentNewLineOffset = cachedNewlineOffset->mNewlineOffset;
    9274             :     } else {
    9275          19 :       contentNewLineOffset = FindChar(frag, offset,
    9276          38 :                                       mContent->TextLength() - offset, '\n');
    9277             :     }
    9278          19 :     if (contentNewLineOffset < offset + length) {
    9279             :       /*
    9280             :         The new line offset could be outside this frame if the frame has been
    9281             :         split by bidi resolution. In that case we won't use it in this reflow
    9282             :         (newLineOffset will remain -1), but we will still cache it in mContent
    9283             :       */
    9284          19 :       newLineOffset = contentNewLineOffset;
    9285             :     }
    9286          19 :     if (newLineOffset >= 0) {
    9287           0 :       length = newLineOffset + 1 - offset;
    9288             :     }
    9289             :   }
    9290          43 :   if ((atStartOfLine && !textStyle->WhiteSpaceIsSignificant()) ||
    9291          19 :       (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML)) {
    9292             :     // Skip leading whitespace. Make sure we don't skip a 'pre-line'
    9293             :     // newline if there is one.
    9294           5 :     int32_t skipLength = newLineOffset >= 0 ? length - 1 : length;
    9295             :     int32_t whitespaceCount =
    9296           5 :       GetTrimmableWhitespaceCount(frag, offset, skipLength, 1);
    9297           5 :     if (whitespaceCount) {
    9298           0 :       offset += whitespaceCount;
    9299           0 :       length -= whitespaceCount;
    9300             :       // Make sure this frame maps the trimmable whitespace.
    9301           0 :       if (MOZ_UNLIKELY(offset > GetContentEnd())) {
    9302           0 :         SetLength(offset - GetContentOffset(), &aLineLayout,
    9303           0 :                   ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    9304             :       }
    9305             :     }
    9306             :   }
    9307             : 
    9308          24 :   bool completedFirstLetter = false;
    9309             :   // Layout dependent styles are a problem because we need to reconstruct
    9310             :   // the gfxTextRun based on our layout.
    9311          24 :   if (aLineLayout.GetInFirstLetter() || aLineLayout.GetInFirstLine()) {
    9312             :     SetLength(maxContentLength, &aLineLayout,
    9313           0 :               ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    9314             : 
    9315           0 :     if (aLineLayout.GetInFirstLetter()) {
    9316             :       // floating first-letter boundaries are significant in textrun
    9317             :       // construction, so clear the textrun out every time we hit a first-letter
    9318             :       // and have changed our length (which controls the first-letter boundary)
    9319           0 :       ClearTextRuns();
    9320             :       // Find the length of the first-letter. We need a textrun for this.
    9321             :       // REVIEW: maybe-bogus inflation should be ok (fixed below)
    9322             :       gfxSkipCharsIterator iter =
    9323             :         EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
    9324           0 :                       lineContainer, aLineLayout.GetLine(),
    9325           0 :                       &flowEndInTextRun);
    9326             : 
    9327           0 :       if (mTextRun) {
    9328           0 :         int32_t firstLetterLength = length;
    9329           0 :         if (aLineLayout.GetFirstLetterStyleOK()) {
    9330             :           completedFirstLetter =
    9331           0 :             FindFirstLetterRange(frag, mTextRun, offset, iter, &firstLetterLength);
    9332           0 :           if (newLineOffset >= 0) {
    9333             :             // Don't allow a preformatted newline to be part of a first-letter.
    9334           0 :             firstLetterLength = std::min(firstLetterLength, length - 1);
    9335           0 :             if (length == 1) {
    9336             :               // There is no text to be consumed by the first-letter before the
    9337             :               // preformatted newline. Note that the first letter is therefore
    9338             :               // complete (FindFirstLetterRange will have returned false).
    9339           0 :               completedFirstLetter = true;
    9340             :             }
    9341             :           }
    9342             :         } else {
    9343             :           // We're in a first-letter frame's first in flow, so if there
    9344             :           // was a first-letter, we'd be it. However, for one reason
    9345             :           // or another (e.g., preformatted line break before this text),
    9346             :           // we're not actually supposed to have first-letter style. So
    9347             :           // just make a zero-length first-letter.
    9348           0 :           firstLetterLength = 0;
    9349           0 :           completedFirstLetter = true;
    9350             :         }
    9351           0 :         length = firstLetterLength;
    9352           0 :         if (length) {
    9353           0 :           AddStateBits(TEXT_FIRST_LETTER);
    9354             :         }
    9355             :         // Change this frame's length to the first-letter length right now
    9356             :         // so that when we rebuild the textrun it will be built with the
    9357             :         // right first-letter boundary
    9358           0 :         SetLength(offset + length - GetContentOffset(), &aLineLayout,
    9359           0 :                   ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    9360             :         // Ensure that the textrun will be rebuilt
    9361           0 :         ClearTextRuns();
    9362             :       }
    9363             :     }
    9364             :   }
    9365             : 
    9366          24 :   float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
    9367             : 
    9368          24 :   if (!IsCurrentFontInflation(fontSizeInflation)) {
    9369             :     // FIXME: Ideally, if we already have a text run, we'd move it to be
    9370             :     // the uninflated text run.
    9371           0 :     ClearTextRun(nullptr, nsTextFrame::eInflated);
    9372             :   }
    9373             : 
    9374             :   gfxSkipCharsIterator iter =
    9375             :     EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
    9376          24 :                   lineContainer, aLineLayout.GetLine(), &flowEndInTextRun);
    9377             : 
    9378          24 :   NS_ASSERTION(IsCurrentFontInflation(fontSizeInflation),
    9379             :                "EnsureTextRun should have set font size inflation");
    9380             : 
    9381          24 :   if (mTextRun && iter.GetOriginalEnd() < offset + length) {
    9382             :     // The textrun does not map enough text for this frame. This can happen
    9383             :     // when the textrun was ended in the middle of a text node because a
    9384             :     // preformatted newline was encountered, and prev-in-flow frames have
    9385             :     // consumed all the text of the textrun. We need a new textrun.
    9386           0 :     ClearTextRuns();
    9387           0 :     iter = EnsureTextRun(nsTextFrame::eInflated, aDrawTarget,
    9388           0 :                          lineContainer, aLineLayout.GetLine(),
    9389             :                          &flowEndInTextRun);
    9390             :   }
    9391             : 
    9392          24 :   if (!mTextRun) {
    9393           0 :     ClearMetrics(aMetrics);
    9394           0 :     aStatus.Reset();
    9395           0 :     return;
    9396             :   }
    9397             : 
    9398          24 :   NS_ASSERTION(gfxSkipCharsIterator(iter).ConvertOriginalToSkipped(offset + length)
    9399             :                     <= mTextRun->GetLength(),
    9400             :                "Text run does not map enough text for our reflow");
    9401             : 
    9402             :   /////////////////////////////////////////////////////////////////////
    9403             :   // See how much text should belong to this text frame, and measure it
    9404             :   /////////////////////////////////////////////////////////////////////
    9405             : 
    9406          24 :   iter.SetOriginalOffset(offset);
    9407          72 :   nscoord xOffsetForTabs = (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_HAS_TAB) ?
    9408           0 :     (aLineLayout.GetCurrentFrameInlineDistanceFromBlock() -
    9409          24 :        lineContainer->GetUsedBorderAndPadding().left)
    9410          48 :     : -1;
    9411             :   PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length,
    9412          48 :       lineContainer, xOffsetForTabs, nsTextFrame::eInflated);
    9413             : 
    9414          24 :   uint32_t transformedOffset = provider.GetStart().GetSkippedOffset();
    9415             : 
    9416             :   // The metrics for the text go in here
    9417          24 :   gfxTextRun::Metrics textMetrics;
    9418             :   gfxFont::BoundingBoxType boundingBoxType =
    9419          48 :     IsFloatingFirstLetterChild() || IsInitialLetterChild()
    9420          24 :     ? gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS
    9421          24 :     : gfxFont::LOOSE_INK_EXTENTS;
    9422          24 :   NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
    9423             :                "We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
    9424             : 
    9425          24 :   int32_t limitLength = length;
    9426          24 :   int32_t forceBreak = aLineLayout.GetForcedBreakPosition(this);
    9427          24 :   bool forceBreakAfter = false;
    9428          24 :   if (forceBreak >= length) {
    9429           0 :     forceBreakAfter = forceBreak == length;
    9430             :     // The break is not within the text considered for this textframe.
    9431           0 :     forceBreak = -1;
    9432             :   }
    9433          24 :   if (forceBreak >= 0) {
    9434           0 :     limitLength = forceBreak;
    9435             :   }
    9436             :   // This is the heart of text reflow right here! We don't know where
    9437             :   // to break, so we need to see how much text fits in the available width.
    9438             :   uint32_t transformedLength;
    9439          24 :   if (offset + limitLength >= int32_t(frag->GetLength())) {
    9440          24 :     NS_ASSERTION(offset + limitLength == int32_t(frag->GetLength()),
    9441             :                  "Content offset/length out of bounds");
    9442          24 :     NS_ASSERTION(flowEndInTextRun >= transformedOffset,
    9443             :                  "Negative flow length?");
    9444          24 :     transformedLength = flowEndInTextRun - transformedOffset;
    9445             :   } else {
    9446             :     // we're not looking at all the content, so we need to compute the
    9447             :     // length of the transformed substring we're looking at
    9448           0 :     gfxSkipCharsIterator iter(provider.GetStart());
    9449           0 :     iter.SetOriginalOffset(offset + limitLength);
    9450           0 :     transformedLength = iter.GetSkippedOffset() - transformedOffset;
    9451             :   }
    9452          24 :   uint32_t transformedLastBreak = 0;
    9453             :   bool usedHyphenation;
    9454          24 :   gfxFloat trimmedWidth = 0;
    9455          24 :   gfxFloat availWidth = aAvailableWidth;
    9456          24 :   if (StyleContext()->IsTextCombined()) {
    9457             :     // If text-combine-upright is 'all', we would compress whatever long
    9458             :     // text into ~1em width, so there is no limited on the avail width.
    9459           0 :     availWidth = std::numeric_limits<gfxFloat>::infinity();
    9460             :   }
    9461          43 :   bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
    9462          43 :                                    (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML);
    9463             :   // allow whitespace to overflow the container
    9464          25 :   bool whitespaceCanHang = textStyle->WhiteSpaceCanWrapStyle() &&
    9465          25 :                            textStyle->WhiteSpaceIsSignificant();
    9466          24 :   gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority();
    9467          24 :   gfxTextRun::SuppressBreak suppressBreak = gfxTextRun::eNoSuppressBreak;
    9468          24 :   bool shouldSuppressLineBreak = ShouldSuppressLineBreak();
    9469          24 :   if (shouldSuppressLineBreak) {
    9470           0 :     suppressBreak = gfxTextRun::eSuppressAllBreaks;
    9471          24 :   } else if (!aLineLayout.LineIsBreakable()) {
    9472          24 :     suppressBreak = gfxTextRun::eSuppressInitialBreak;
    9473             :   }
    9474             :   uint32_t transformedCharsFit =
    9475          48 :     mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
    9476          24 :                                   (GetStateBits() & TEXT_START_OF_LINE) != 0,
    9477             :                                   availWidth,
    9478             :                                   &provider, suppressBreak,
    9479             :                                   canTrimTrailingWhitespace ? &trimmedWidth : nullptr,
    9480             :                                   whitespaceCanHang,
    9481             :                                   &textMetrics, boundingBoxType,
    9482             :                                   aDrawTarget,
    9483             :                                   &usedHyphenation, &transformedLastBreak,
    9484          48 :                                   textStyle->WordCanWrap(this), &breakPriority);
    9485          24 :   if (!length && !textMetrics.mAscent && !textMetrics.mDescent) {
    9486             :     // If we're measuring a zero-length piece of text, update
    9487             :     // the height manually.
    9488           0 :     nsFontMetrics* fm = provider.GetFontMetrics();
    9489           0 :     if (fm) {
    9490           0 :       textMetrics.mAscent = gfxFloat(fm->MaxAscent());
    9491           0 :       textMetrics.mDescent = gfxFloat(fm->MaxDescent());
    9492             :     }
    9493             :   }
    9494          24 :   if (GetWritingMode().IsLineInverted()) {
    9495           0 :     Swap(textMetrics.mAscent, textMetrics.mDescent);
    9496           0 :     textMetrics.mBoundingBox.y = -textMetrics.mBoundingBox.YMost();
    9497             :   }
    9498             :   // The "end" iterator points to the first character after the string mapped
    9499             :   // by this frame. Basically, its original-string offset is offset+charsFit
    9500             :   // after we've computed charsFit.
    9501          24 :   gfxSkipCharsIterator end(provider.GetEndHint());
    9502          24 :   end.SetSkippedOffset(transformedOffset + transformedCharsFit);
    9503          24 :   int32_t charsFit = end.GetOriginalOffset() - offset;
    9504          24 :   if (offset + charsFit == newLineOffset) {
    9505             :     // We broke before a trailing preformatted '\n'. The newline should
    9506             :     // be assigned to this frame. Note that newLineOffset will be -1 if
    9507             :     // there was no preformatted newline, so we wouldn't get here in that
    9508             :     // case.
    9509           0 :     ++charsFit;
    9510             :   }
    9511             :   // That might have taken us beyond our assigned content range (because
    9512             :   // we might have advanced over some skipped chars that extend outside
    9513             :   // this frame), so get back in.
    9514          24 :   int32_t lastBreak = -1;
    9515          24 :   if (charsFit >= limitLength) {
    9516          24 :     charsFit = limitLength;
    9517          24 :     if (transformedLastBreak != UINT32_MAX) {
    9518             :       // lastBreak is needed.
    9519             :       // This may set lastBreak greater than 'length', but that's OK
    9520           1 :       lastBreak = end.ConvertSkippedToOriginal(transformedOffset + transformedLastBreak);
    9521             :     }
    9522          24 :     end.SetOriginalOffset(offset + charsFit);
    9523             :     // If we were forced to fit, and the break position is after a soft hyphen,
    9524             :     // note that this is a hyphenation break.
    9525          24 :     if ((forceBreak >= 0 || forceBreakAfter) &&
    9526           0 :         HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
    9527           0 :       usedHyphenation = true;
    9528             :     }
    9529             :   }
    9530          24 :   if (usedHyphenation) {
    9531             :     // Fix up metrics to include hyphen
    9532             :     AddHyphenToMetrics(this, mTextRun, &textMetrics, boundingBoxType,
    9533           0 :                        aDrawTarget);
    9534           0 :     AddStateBits(TEXT_HYPHEN_BREAK | TEXT_HAS_NONCOLLAPSED_CHARACTERS);
    9535             :   }
    9536          24 :   if (textMetrics.mBoundingBox.IsEmpty()) {
    9537           0 :     AddStateBits(TEXT_NO_RENDERED_GLYPHS);
    9538             :   }
    9539             : 
    9540          24 :   gfxFloat trimmableWidth = 0;
    9541          24 :   bool brokeText = forceBreak >= 0 || transformedCharsFit < transformedLength;
    9542          24 :   if (canTrimTrailingWhitespace) {
    9543             :     // Optimization: if we trimmed trailing whitespace, and we can be sure
    9544             :     // this frame will be at the end of the line, then leave it trimmed off.
    9545             :     // Otherwise we have to undo the trimming, in case we're not at the end of
    9546             :     // the line. (If we actually do end up at the end of the line, we'll have
    9547             :     // to trim it off again in TrimTrailingWhiteSpace, and we'd like to avoid
    9548             :     // having to re-do it.)
    9549          10 :     if (brokeText ||
    9550           5 :         (GetStateBits() & TEXT_IS_IN_TOKEN_MATHML)) {
    9551             :       // We're definitely going to break so our trailing whitespace should
    9552             :       // definitely be trimmed. Record that we've already done it.
    9553           0 :       AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
    9554           5 :     } else if (!(GetStateBits() & TEXT_IS_IN_TOKEN_MATHML)) {
    9555             :       // We might not be at the end of the line. (Note that even if this frame
    9556             :       // ends in breakable whitespace, it might not be at the end of the line
    9557             :       // because it might be followed by breakable, but preformatted, whitespace.)
    9558             :       // Undo the trimming.
    9559           5 :       textMetrics.mAdvanceWidth += trimmedWidth;
    9560           5 :       trimmableWidth = trimmedWidth;
    9561           5 :       if (mTextRun->IsRightToLeft()) {
    9562             :         // Space comes before text, so the bounding box is moved to the
    9563             :         // right by trimmdWidth
    9564           0 :         textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
    9565             :       }
    9566             :     }
    9567             :   }
    9568             : 
    9569          24 :   if (!brokeText && lastBreak >= 0) {
    9570             :     // Since everything fit and no break was forced,
    9571             :     // record the last break opportunity
    9572           1 :     NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= availWidth,
    9573             :                  "If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?");
    9574           1 :     MOZ_ASSERT(lastBreak >= offset, "Strange break position");
    9575           1 :     aLineLayout.NotifyOptionalBreakPosition(this, lastBreak - offset,
    9576           2 :                                             true, breakPriority);
    9577             :   }
    9578             : 
    9579          24 :   int32_t contentLength = offset + charsFit - GetContentOffset();
    9580             : 
    9581             :   /////////////////////////////////////////////////////////////////////
    9582             :   // Compute output metrics
    9583             :   /////////////////////////////////////////////////////////////////////
    9584             : 
    9585             :   // first-letter frames should use the tight bounding box metrics for ascent/descent
    9586             :   // for good drop-cap effects
    9587          24 :   if (GetStateBits() & TEXT_FIRST_LETTER) {
    9588           0 :     textMetrics.mAscent = std::max(gfxFloat(0.0), -textMetrics.mBoundingBox.Y());
    9589           0 :     textMetrics.mDescent = std::max(gfxFloat(0.0), textMetrics.mBoundingBox.YMost());
    9590             :   }
    9591             : 
    9592             :   // Setup metrics for caller
    9593             :   // Disallow negative widths
    9594          24 :   WritingMode wm = GetWritingMode();
    9595          24 :   LogicalSize finalSize(wm);
    9596          48 :   finalSize.ISize(wm) = NSToCoordCeil(std::max(gfxFloat(0.0),
    9597          24 :                                                textMetrics.mAdvanceWidth));
    9598             : 
    9599          24 :   if (transformedCharsFit == 0 && !usedHyphenation) {
    9600           0 :     aMetrics.SetBlockStartAscent(0);
    9601           0 :     finalSize.BSize(wm) = 0;
    9602          24 :   } else if (boundingBoxType != gfxFont::LOOSE_INK_EXTENTS) {
    9603             :     // Use actual text metrics for floating first letter frame.
    9604           0 :     aMetrics.SetBlockStartAscent(NSToCoordCeil(textMetrics.mAscent));
    9605           0 :     finalSize.BSize(wm) = aMetrics.BlockStartAscent() +
    9606           0 :       NSToCoordCeil(textMetrics.mDescent);
    9607             :   } else {
    9608             :     // Otherwise, ascent should contain the overline drawable area.
    9609             :     // And also descent should contain the underline drawable area.
    9610             :     // nsFontMetrics::GetMaxAscent/GetMaxDescent contains them.
    9611          24 :     nsFontMetrics* fm = provider.GetFontMetrics();
    9612             :     nscoord fontAscent =
    9613          24 :       wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
    9614             :     nscoord fontDescent =
    9615          24 :       wm.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
    9616          24 :     aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
    9617          24 :     nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
    9618          24 :     finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
    9619             :   }
    9620          24 :   if (StyleContext()->IsTextCombined()) {
    9621           0 :     nsFontMetrics* fm = provider.GetFontMetrics();
    9622           0 :     gfxFloat width = finalSize.ISize(wm);
    9623           0 :     gfxFloat em = fm->EmHeight();
    9624             :     // Compress the characters in horizontal axis if necessary.
    9625           0 :     if (width <= em) {
    9626           0 :       RemoveProperty(TextCombineScaleFactorProperty());
    9627             :     } else {
    9628           0 :       SetProperty(TextCombineScaleFactorProperty(), em / width);
    9629           0 :       finalSize.ISize(wm) = em;
    9630             :     }
    9631             :     // Make the characters be in an 1em square.
    9632           0 :     if (finalSize.BSize(wm) != em) {
    9633           0 :       aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
    9634           0 :                                    (em - finalSize.BSize(wm)) / 2);
    9635           0 :       finalSize.BSize(wm) = em;
    9636             :     }
    9637             :   }
    9638          24 :   aMetrics.SetSize(wm, finalSize);
    9639             : 
    9640          24 :   NS_ASSERTION(aMetrics.BlockStartAscent() >= 0,
    9641             :                "Negative ascent???");
    9642          24 :   NS_ASSERTION((StyleContext()->IsTextCombined()
    9643             :                 ? aMetrics.ISize(aMetrics.GetWritingMode())
    9644             :                 : aMetrics.BSize(aMetrics.GetWritingMode())) -
    9645             :                aMetrics.BlockStartAscent() >= 0,
    9646             :                "Negative descent???");
    9647             : 
    9648          24 :   mAscent = aMetrics.BlockStartAscent();
    9649             : 
    9650             :   // Handle text that runs outside its normal bounds.
    9651          48 :   nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
    9652          24 :   if (mTextRun->IsVertical()) {
    9653             :     // Swap line-relative textMetrics dimensions to physical coordinates.
    9654           0 :     Swap(boundingBox.x, boundingBox.y);
    9655           0 :     Swap(boundingBox.width, boundingBox.height);
    9656           0 :     if (GetWritingMode().IsVerticalRL()) {
    9657           0 :       boundingBox.x = -boundingBox.XMost();
    9658           0 :       boundingBox.x += aMetrics.Width() - mAscent;
    9659             :     } else {
    9660           0 :       boundingBox.x += mAscent;
    9661             :     }
    9662             :   } else {
    9663          24 :     boundingBox.y += mAscent;
    9664             :   }
    9665          24 :   aMetrics.SetOverflowAreasToDesiredBounds();
    9666          24 :   aMetrics.VisualOverflow().UnionRect(aMetrics.VisualOverflow(), boundingBox);
    9667             : 
    9668             :   // When we have text decorations, we don't need to compute their overflow now
    9669             :   // because we're guaranteed to do it later
    9670             :   // (see nsLineLayout::RelativePositionFrames)
    9671          24 :   UnionAdditionalOverflow(presContext, aLineLayout.LineContainerRI()->mFrame,
    9672          48 :                           provider, &aMetrics.VisualOverflow(), false);
    9673             : 
    9674             :   /////////////////////////////////////////////////////////////////////
    9675             :   // Clean up, update state
    9676             :   /////////////////////////////////////////////////////////////////////
    9677             : 
    9678             :   // If all our characters are discarded or collapsed, then trimmable width
    9679             :   // from the last textframe should be preserved. Otherwise the trimmable width
    9680             :   // from this textframe overrides. (Currently in CSS trimmable width can be
    9681             :   // at most one space so there's no way for trimmable width from a previous
    9682             :   // frame to accumulate with trimmable width from this frame.)
    9683          24 :   if (transformedCharsFit > 0) {
    9684          24 :     aLineLayout.SetTrimmableISize(NSToCoordFloor(trimmableWidth));
    9685          24 :     AddStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
    9686             :   }
    9687          24 :   bool breakAfter = forceBreakAfter;
    9688          24 :   if (!shouldSuppressLineBreak) {
    9689          72 :     if (charsFit > 0 && charsFit == length &&
    9690          72 :         textStyle->mHyphens != StyleHyphens::None &&
    9691          24 :         HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
    9692             :       bool fits =
    9693           0 :         textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth;
    9694             :       // Record a potential break after final soft hyphen
    9695           0 :       aLineLayout.NotifyOptionalBreakPosition(this, length, fits,
    9696           0 :                                               gfxBreakPriority::eNormalBreak);
    9697             :     }
    9698             :     // length == 0 means either the text is empty or it's all collapsed away
    9699          24 :     bool emptyTextAtStartOfLine = atStartOfLine && length == 0;
    9700         120 :     if (!breakAfter && charsFit == length && !emptyTextAtStartOfLine &&
    9701         120 :         transformedOffset + transformedLength == mTextRun->GetLength() &&
    9702          96 :         (mTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_HAS_TRAILING_BREAK)) {
    9703             :       // We placed all the text in the textrun and we have a break opportunity
    9704             :       // at the end of the textrun. We need to record it because the following
    9705             :       // content may not care about nsLineBreaker.
    9706             : 
    9707             :       // Note that because we didn't break, we can be sure that (thanks to the
    9708             :       // code up above) textMetrics.mAdvanceWidth includes the width of any
    9709             :       // trailing whitespace. So we need to subtract trimmableWidth here
    9710             :       // because if we did break at this point, that much width would be
    9711             :       // trimmed.
    9712           0 :       if (textMetrics.mAdvanceWidth - trimmableWidth > availWidth) {
    9713           0 :         breakAfter = true;
    9714             :       } else {
    9715           0 :         aLineLayout.NotifyOptionalBreakPosition(this, length, true,
    9716           0 :                                                 gfxBreakPriority::eNormalBreak);
    9717             :       }
    9718             :     }
    9719             :   }
    9720             : 
    9721             :   // Compute reflow status
    9722          24 :   aStatus.Reset();
    9723          24 :   if (contentLength != maxContentLength) {
    9724           0 :     aStatus.SetIncomplete();
    9725             :   }
    9726             : 
    9727          24 :   if (charsFit == 0 && length > 0 && !usedHyphenation) {
    9728             :     // Couldn't place any text
    9729           0 :     aStatus.SetInlineLineBreakBeforeAndReset();
    9730          24 :   } else if (contentLength > 0 && mContentOffset + contentLength - 1 == newLineOffset) {
    9731             :     // Ends in \n
    9732           0 :     aStatus.SetInlineLineBreakAfter();
    9733           0 :     aLineLayout.SetLineEndsInBR(true);
    9734          24 :   } else if (breakAfter) {
    9735           0 :     aStatus.SetInlineLineBreakAfter();
    9736             :   }
    9737          24 :   if (completedFirstLetter) {
    9738           0 :     aLineLayout.SetFirstLetterStyleOK(false);
    9739           0 :     aStatus.SetFirstLetterComplete();
    9740             :   }
    9741             : 
    9742             :   // Updated the cached NewlineProperty, or delete it.
    9743          24 :   if (contentLength < maxContentLength &&
    9744          24 :       textStyle->NewlineIsSignificant(this) &&
    9745           0 :       (contentNewLineOffset < 0 ||
    9746           0 :        mContentOffset + contentLength <= contentNewLineOffset)) {
    9747           0 :     if (!cachedNewlineOffset) {
    9748           0 :       cachedNewlineOffset = new NewlineProperty;
    9749           0 :       if (NS_FAILED(mContent->SetProperty(nsGkAtoms::newline, cachedNewlineOffset,
    9750             :                                           nsINode::DeleteProperty<NewlineProperty>))) {
    9751             :         delete cachedNewlineOffset;
    9752           0 :         cachedNewlineOffset = nullptr;
    9753             :       }
    9754             :     }
    9755           0 :     if (cachedNewlineOffset) {
    9756           0 :       cachedNewlineOffset->mStartOffset = offset;
    9757           0 :       cachedNewlineOffset->mNewlineOffset = contentNewLineOffset;
    9758             :     }
    9759          24 :   } else if (cachedNewlineOffset) {
    9760           0 :     mContent->DeleteProperty(nsGkAtoms::newline);
    9761             :   }
    9762             : 
    9763             :   // Compute space and letter counts for justification, if required
    9764          53 :   if (!textStyle->WhiteSpaceIsSignificant() &&
    9765          10 :       (lineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
    9766          10 :        lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
    9767          24 :        shouldSuppressLineBreak) &&
    9768           0 :       !nsSVGUtils::IsInSVGTextSubtree(lineContainer)) {
    9769           0 :     AddStateBits(TEXT_JUSTIFICATION_ENABLED);
    9770           0 :     Range range(uint32_t(offset), uint32_t(offset + charsFit));
    9771           0 :     aLineLayout.SetJustificationInfo(provider.ComputeJustification(range));
    9772             :   }
    9773             : 
    9774          24 :   SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    9775             : 
    9776          24 :   InvalidateFrame();
    9777             : 
    9778             : #ifdef NOISY_REFLOW
    9779             :   ListTag(stdout);
    9780             :   printf(": desiredSize=%d,%d(b=%d) status=%x\n",
    9781             :          aMetrics.Width(), aMetrics.Height(), aMetrics.BlockStartAscent(),
    9782             :          aStatus);
    9783             : #endif
    9784             : }
    9785             : 
    9786             : /* virtual */ bool
    9787          48 : nsTextFrame::CanContinueTextRun() const
    9788             : {
    9789             :   // We can continue a text run through a text frame
    9790          48 :   return true;
    9791             : }
    9792             : 
    9793             : nsTextFrame::TrimOutput
    9794          48 : nsTextFrame::TrimTrailingWhiteSpace(DrawTarget* aDrawTarget)
    9795             : {
    9796             :   TrimOutput result;
    9797          48 :   result.mChanged = false;
    9798          48 :   result.mDeltaWidth = 0;
    9799             : 
    9800          48 :   AddStateBits(TEXT_END_OF_LINE);
    9801             : 
    9802          48 :   int32_t contentLength = GetContentLength();
    9803          48 :   if (!contentLength)
    9804          24 :     return result;
    9805             : 
    9806             :   gfxSkipCharsIterator start =
    9807          24 :     EnsureTextRun(nsTextFrame::eInflated, aDrawTarget);
    9808          24 :   NS_ENSURE_TRUE(mTextRun, result);
    9809             : 
    9810          24 :   uint32_t trimmedStart = start.GetSkippedOffset();
    9811             : 
    9812          24 :   const nsTextFragment* frag = mContent->GetText();
    9813          24 :   TrimmedOffsets trimmed = GetTrimmedOffsets(frag, true);
    9814          24 :   gfxSkipCharsIterator trimmedEndIter = start;
    9815          24 :   const nsStyleText* textStyle = StyleText();
    9816          24 :   gfxFloat delta = 0;
    9817          24 :   uint32_t trimmedEnd = trimmedEndIter.ConvertOriginalToSkipped(trimmed.GetEnd());
    9818             : 
    9819          48 :   if (!(GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) &&
    9820          24 :       trimmed.GetEnd() < GetContentEnd()) {
    9821           0 :     gfxSkipCharsIterator end = trimmedEndIter;
    9822           0 :     uint32_t endOffset = end.ConvertOriginalToSkipped(GetContentOffset() + contentLength);
    9823           0 :     if (trimmedEnd < endOffset) {
    9824             :       // We can't be dealing with tabs here ... they wouldn't be trimmed. So it's
    9825             :       // OK to pass null for the line container.
    9826             :       PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength,
    9827           0 :                                 nullptr, 0, nsTextFrame::eInflated);
    9828             :       delta = mTextRun->
    9829           0 :         GetAdvanceWidth(Range(trimmedEnd, endOffset), &provider);
    9830           0 :       result.mChanged = true;
    9831             :     }
    9832             :   }
    9833             : 
    9834             :   gfxFloat advanceDelta;
    9835          72 :   mTextRun->SetLineBreaks(Range(trimmedStart, trimmedEnd),
    9836          24 :                           (GetStateBits() & TEXT_START_OF_LINE) != 0, true,
    9837          48 :                           &advanceDelta);
    9838          24 :   if (advanceDelta != 0) {
    9839           0 :     result.mChanged = true;
    9840             :   }
    9841             : 
    9842             :   // aDeltaWidth is *subtracted* from our width.
    9843             :   // If advanceDelta is positive then setting the line break made us longer,
    9844             :   // so aDeltaWidth could go negative.
    9845          24 :   result.mDeltaWidth = NSToCoordFloor(delta - advanceDelta);
    9846             :   // If aDeltaWidth goes negative, that means this frame might not actually fit
    9847             :   // anymore!!! We need higher level line layout to recover somehow.
    9848             :   // If it's because the frame has a soft hyphen that is now being displayed,
    9849             :   // this should actually be OK, because our reflow recorded the break
    9850             :   // opportunity that allowed the soft hyphen to be used, and we wouldn't
    9851             :   // have recorded the opportunity unless the hyphen fit (or was the first
    9852             :   // opportunity on the line).
    9853             :   // Otherwise this can/ really only happen when we have glyphs with special
    9854             :   // shapes at the end of lines, I think. Breaking inside a kerning pair won't
    9855             :   // do it because that would mean we broke inside this textrun, and
    9856             :   // BreakAndMeasureText should make sure the resulting shaped substring fits.
    9857             :   // Maybe if we passed a maxTextLength? But that only happens at direction
    9858             :   // changes (so we wouldn't kern across the boundary) or for first-letter
    9859             :   // (which always fits because it starts the line!).
    9860          24 :   NS_WARNING_ASSERTION(result.mDeltaWidth >= 0,
    9861             :                        "Negative deltawidth, something odd is happening");
    9862             : 
    9863             : #ifdef NOISY_TRIM
    9864             :   ListTag(stdout);
    9865             :   printf(": trim => %d\n", result.mDeltaWidth);
    9866             : #endif
    9867          24 :   return result;
    9868             : }
    9869             : 
    9870             : nsOverflowAreas
    9871           0 : nsTextFrame::RecomputeOverflow(nsIFrame* aBlockFrame)
    9872             : {
    9873           0 :   nsRect bounds(nsPoint(0, 0), GetSize());
    9874           0 :   nsOverflowAreas result(bounds, bounds);
    9875             : 
    9876           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    9877           0 :   if (!mTextRun)
    9878           0 :     return result;
    9879             : 
    9880           0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    9881             :   // Don't trim trailing space, in case we need to paint it as selected.
    9882           0 :   provider.InitializeForDisplay(false);
    9883             : 
    9884             :   gfxTextRun::Metrics textMetrics =
    9885             :     mTextRun->MeasureText(ComputeTransformedRange(provider),
    9886             :                           gfxFont::LOOSE_INK_EXTENTS, nullptr,
    9887           0 :                           &provider);
    9888           0 :   if (GetWritingMode().IsLineInverted()) {
    9889           0 :     textMetrics.mBoundingBox.y = -textMetrics.mBoundingBox.YMost();
    9890             :   }
    9891           0 :   nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
    9892           0 :   boundingBox += nsPoint(0, mAscent);
    9893           0 :   if (mTextRun->IsVertical()) {
    9894             :     // Swap line-relative textMetrics dimensions to physical coordinates.
    9895           0 :     Swap(boundingBox.x, boundingBox.y);
    9896           0 :     Swap(boundingBox.width, boundingBox.height);
    9897             :   }
    9898           0 :   nsRect &vis = result.VisualOverflow();
    9899           0 :   vis.UnionRect(vis, boundingBox);
    9900           0 :   UnionAdditionalOverflow(PresContext(), aBlockFrame, provider, &vis, true);
    9901           0 :   return result;
    9902             : }
    9903             : 
    9904           0 : static void TransformChars(nsTextFrame* aFrame, const nsStyleText* aStyle,
    9905             :                            const gfxTextRun* aTextRun, uint32_t aSkippedOffset,
    9906             :                            const nsTextFragment* aFrag, int32_t aFragOffset,
    9907             :                            int32_t aFragLen, nsAString& aOut)
    9908             : {
    9909           0 :   nsAutoString fragString;
    9910             :   char16_t* out;
    9911           0 :   if (aStyle->mTextTransform == NS_STYLE_TEXT_TRANSFORM_NONE) {
    9912             :     // No text-transform, so we can copy directly to the output string.
    9913           0 :     aOut.SetLength(aOut.Length() + aFragLen);
    9914           0 :     out = aOut.EndWriting() - aFragLen;
    9915             :   } else {
    9916             :     // Use a temporary string as source for the transform.
    9917           0 :     fragString.SetLength(aFragLen);
    9918           0 :     out = fragString.BeginWriting();
    9919             :   }
    9920             : 
    9921             :   // Copy the text, with \n and \t replaced by <space> if appropriate.
    9922           0 :   for (int32_t i = 0; i < aFragLen; ++i) {
    9923           0 :     char16_t ch = aFrag->CharAt(aFragOffset + i);
    9924           0 :     if ((ch == '\n' && !aStyle->NewlineIsSignificant(aFrame)) ||
    9925           0 :         (ch == '\t' && !aStyle->TabIsSignificant())) {
    9926           0 :       ch = ' ';
    9927             :     }
    9928           0 :     out[i] = ch;
    9929             :   }
    9930             : 
    9931           0 :   if (aStyle->mTextTransform != NS_STYLE_TEXT_TRANSFORM_NONE) {
    9932           0 :     MOZ_ASSERT(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_TRANSFORMED);
    9933           0 :     if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_TRANSFORMED) {
    9934             :       // Apply text-transform according to style in the transformed run.
    9935             :       auto transformedTextRun =
    9936           0 :         static_cast<const nsTransformedTextRun*>(aTextRun);
    9937           0 :       nsAutoString convertedString;
    9938           0 :       AutoTArray<bool,50> charsToMergeArray;
    9939           0 :       AutoTArray<bool,50> deletedCharsArray;
    9940             :       nsCaseTransformTextRunFactory::TransformString(fragString,
    9941             :                                                      convertedString,
    9942             :                                                      false, nullptr,
    9943             :                                                      charsToMergeArray,
    9944             :                                                      deletedCharsArray,
    9945             :                                                      transformedTextRun,
    9946           0 :                                                      aSkippedOffset);
    9947           0 :       aOut.Append(convertedString);
    9948             :     } else {
    9949             :       // Should not happen (see assertion above), but as a fallback...
    9950           0 :       aOut.Append(fragString);
    9951             :     }
    9952             :   }
    9953           0 : }
    9954             : 
    9955             : static bool
    9956           0 : LineEndsInHardLineBreak(nsTextFrame* aFrame, nsBlockFrame* aLineContainer)
    9957             : {
    9958             :   bool foundValidLine;
    9959           0 :   nsBlockInFlowLineIterator iter(aLineContainer, aFrame, &foundValidLine);
    9960           0 :   if (!foundValidLine) {
    9961           0 :     NS_ERROR("Invalid line!");
    9962           0 :     return true;
    9963             :   }
    9964           0 :   return !iter.GetLine()->IsLineWrapped();
    9965             : }
    9966             : 
    9967             : nsIFrame::RenderedText
    9968           0 : nsTextFrame::GetRenderedText(uint32_t aStartOffset,
    9969             :                              uint32_t aEndOffset,
    9970             :                              TextOffsetType aOffsetType,
    9971             :                              TrailingWhitespace aTrimTrailingWhitespace)
    9972             : {
    9973           0 :   MOZ_ASSERT(aStartOffset <= aEndOffset, "bogus offsets");
    9974           0 :   MOZ_ASSERT(!GetPrevContinuation() ||
    9975             :              (aOffsetType == TextOffsetType::OFFSETS_IN_CONTENT_TEXT &&
    9976             :               aStartOffset >= (uint32_t)GetContentOffset() &&
    9977             :               aEndOffset <= (uint32_t)GetContentEnd()),
    9978             :              "Must be called on first-in-flow, or content offsets must be "
    9979             :              "given and be within this frame.");
    9980             : 
    9981             :   // The handling of offsets could be more efficient...
    9982           0 :   RenderedText result;
    9983           0 :   nsBlockFrame* lineContainer = nullptr;
    9984             :   nsTextFrame* textFrame;
    9985           0 :   const nsTextFragment* textFrag = mContent->GetText();
    9986           0 :   uint32_t offsetInRenderedString = 0;
    9987           0 :   bool haveOffsets = false;
    9988             : 
    9989           0 :   Maybe<nsBlockFrame::AutoLineCursorSetup> autoLineCursor;
    9990           0 :   for (textFrame = this; textFrame;
    9991             :        textFrame = textFrame->GetNextContinuation()) {
    9992           0 :     if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
    9993             :       // We don't trust dirty frames, especially when computing rendered text.
    9994           0 :       break;
    9995             :     }
    9996             : 
    9997             :     // Ensure the text run and grab the gfxSkipCharsIterator for it
    9998             :     gfxSkipCharsIterator iter =
    9999           0 :       textFrame->EnsureTextRun(nsTextFrame::eInflated);
   10000           0 :     if (!textFrame->mTextRun) {
   10001           0 :       break;
   10002             :     }
   10003           0 :     gfxSkipCharsIterator tmpIter = iter;
   10004             : 
   10005             :     // Whether we need to trim whitespaces after the text frame.
   10006             :     bool trimAfter;
   10007           0 :     if (!textFrame->IsAtEndOfLine() ||
   10008             :         aTrimTrailingWhitespace !=
   10009             :           TrailingWhitespace::TRIM_TRAILING_WHITESPACE) {
   10010           0 :       trimAfter = false;
   10011           0 :     } else if (nsBlockFrame* thisLc =
   10012           0 :                do_QueryFrame(FindLineContainer(textFrame))) {
   10013           0 :       if (thisLc != lineContainer) {
   10014             :         // Setup line cursor when needed.
   10015           0 :         lineContainer = thisLc;
   10016           0 :         autoLineCursor.reset();
   10017           0 :         autoLineCursor.emplace(lineContainer);
   10018             :       }
   10019           0 :       trimAfter = LineEndsInHardLineBreak(textFrame, lineContainer);
   10020             :     } else {
   10021             :       // Weird situation where we have a line layout without a block.
   10022             :       // No soft breaks occur in this situation.
   10023           0 :       trimAfter = true;
   10024             :     }
   10025             : 
   10026             :     // Skip to the start of the text run, past ignored chars at start of line
   10027             :     TrimmedOffsets trimmedOffsets =
   10028           0 :         textFrame->GetTrimmedOffsets(textFrag, trimAfter);
   10029             :     bool trimmedSignificantNewline =
   10030           0 :         trimmedOffsets.GetEnd() < GetContentEnd() &&
   10031           0 :         HasSignificantTerminalNewline();
   10032             :     uint32_t skippedToRenderedStringOffset = offsetInRenderedString -
   10033           0 :         tmpIter.ConvertOriginalToSkipped(trimmedOffsets.mStart);
   10034             :     uint32_t nextOffsetInRenderedString =
   10035           0 :         tmpIter.ConvertOriginalToSkipped(trimmedOffsets.GetEnd()) +
   10036           0 :         (trimmedSignificantNewline ? 1 : 0) + skippedToRenderedStringOffset;
   10037             : 
   10038           0 :     if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) {
   10039           0 :       if (nextOffsetInRenderedString <= aStartOffset) {
   10040           0 :         offsetInRenderedString = nextOffsetInRenderedString;
   10041           0 :         continue;
   10042             :       }
   10043           0 :       if (!haveOffsets) {
   10044           0 :         result.mOffsetWithinNodeText =
   10045           0 :             tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset);
   10046           0 :         result.mOffsetWithinNodeRenderedText = aStartOffset;
   10047           0 :         haveOffsets = true;
   10048             :       }
   10049           0 :       if (offsetInRenderedString >= aEndOffset) {
   10050           0 :         break;
   10051             :       }
   10052             :     } else {
   10053           0 :       if (uint32_t(textFrame->GetContentEnd()) <= aStartOffset) {
   10054           0 :         offsetInRenderedString = nextOffsetInRenderedString;
   10055           0 :         continue;
   10056             :       }
   10057           0 :       if (!haveOffsets) {
   10058           0 :         result.mOffsetWithinNodeText = aStartOffset;
   10059             :         // Skip trimmed space when computed the rendered text offset.
   10060           0 :         int32_t clamped = std::max<int32_t>(aStartOffset, trimmedOffsets.mStart);
   10061           0 :         result.mOffsetWithinNodeRenderedText =
   10062           0 :             tmpIter.ConvertOriginalToSkipped(clamped) + skippedToRenderedStringOffset;
   10063           0 :         MOZ_ASSERT(result.mOffsetWithinNodeRenderedText >= offsetInRenderedString &&
   10064             :                    result.mOffsetWithinNodeRenderedText <= INT32_MAX,
   10065             :                    "Bad offset within rendered text");
   10066           0 :         haveOffsets = true;
   10067             :       }
   10068           0 :       if (uint32_t(textFrame->mContentOffset) >= aEndOffset) {
   10069           0 :         break;
   10070             :       }
   10071             :     }
   10072             : 
   10073             :     int32_t startOffset;
   10074             :     int32_t endOffset;
   10075           0 :     if (aOffsetType == TextOffsetType::OFFSETS_IN_RENDERED_TEXT) {
   10076             :       startOffset =
   10077           0 :         tmpIter.ConvertSkippedToOriginal(aStartOffset - skippedToRenderedStringOffset);
   10078             :       endOffset =
   10079           0 :         tmpIter.ConvertSkippedToOriginal(aEndOffset - skippedToRenderedStringOffset);
   10080             :     } else {
   10081           0 :       startOffset = aStartOffset;
   10082           0 :       endOffset = std::min<uint32_t>(INT32_MAX, aEndOffset);
   10083             :     }
   10084           0 :     trimmedOffsets.mStart = std::max<uint32_t>(trimmedOffsets.mStart,
   10085           0 :         startOffset);
   10086           0 :     trimmedOffsets.mLength = std::min<uint32_t>(trimmedOffsets.GetEnd(),
   10087           0 :         endOffset) - trimmedOffsets.mStart;
   10088           0 :     if (trimmedOffsets.mLength <= 0) {
   10089           0 :       offsetInRenderedString = nextOffsetInRenderedString;
   10090           0 :       continue;
   10091             :     }
   10092             : 
   10093           0 :     const nsStyleText* textStyle = textFrame->StyleText();
   10094           0 :     iter.SetOriginalOffset(trimmedOffsets.mStart);
   10095           0 :     while (iter.GetOriginalOffset() < trimmedOffsets.GetEnd()) {
   10096             :       int32_t runLength;
   10097           0 :       bool isSkipped = iter.IsOriginalCharSkipped(&runLength);
   10098           0 :       runLength = std::min(runLength,
   10099           0 :                            trimmedOffsets.GetEnd() - iter.GetOriginalOffset());
   10100           0 :       if (isSkipped) {
   10101           0 :         for (int32_t i = 0; i < runLength; ++i) {
   10102           0 :           char16_t ch = textFrag->CharAt(iter.GetOriginalOffset() + i);
   10103           0 :           if (ch == CH_SHY) {
   10104             :             // We should preserve soft hyphens. They can't be transformed.
   10105           0 :             result.mString.Append(ch);
   10106             :           }
   10107             :         }
   10108             :       } else {
   10109           0 :         TransformChars(textFrame, textStyle, textFrame->mTextRun,
   10110             :                        iter.GetSkippedOffset(), textFrag,
   10111           0 :                        iter.GetOriginalOffset(), runLength, result.mString);
   10112             :       }
   10113           0 :       iter.AdvanceOriginal(runLength);
   10114             :     }
   10115             : 
   10116           0 :     if (trimmedSignificantNewline && GetContentEnd() <= endOffset) {
   10117             :       // A significant newline was trimmed off (we must be
   10118             :       // white-space:pre-line). Put it back.
   10119           0 :       result.mString.Append('\n');
   10120             :     }
   10121           0 :     offsetInRenderedString = nextOffsetInRenderedString;
   10122             :   }
   10123             : 
   10124           0 :   if (!haveOffsets) {
   10125           0 :     result.mOffsetWithinNodeText = textFrag->GetLength();
   10126           0 :     result.mOffsetWithinNodeRenderedText = offsetInRenderedString;
   10127             :   }
   10128           0 :   return result;
   10129             : }
   10130             : 
   10131             : /* virtual */ bool
   10132         114 : nsTextFrame::IsEmpty()
   10133             : {
   10134         114 :   NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
   10135             :                !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
   10136             :                "Invalid state");
   10137             : 
   10138             :   // XXXldb Should this check compatibility mode as well???
   10139         114 :   const nsStyleText* textStyle = StyleText();
   10140         114 :   if (textStyle->WhiteSpaceIsSignificant()) {
   10141             :     // XXX shouldn't we return true if the length is zero?
   10142          27 :     return false;
   10143             :   }
   10144             : 
   10145          87 :   if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
   10146           0 :     return false;
   10147             :   }
   10148             : 
   10149          87 :   if (mState & TEXT_IS_ONLY_WHITESPACE) {
   10150          65 :     return true;
   10151             :   }
   10152             : 
   10153             :   bool isEmpty =
   10154          22 :     IsAllWhitespace(mContent->GetText(),
   10155          44 :                     textStyle->mWhiteSpace != mozilla::StyleWhiteSpace::PreLine);
   10156          22 :   mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
   10157          22 :   return isEmpty;
   10158             : }
   10159             : 
   10160             : #ifdef DEBUG_FRAME_DUMP
   10161             : // Translate the mapped content into a string that's printable
   10162             : void
   10163           0 : nsTextFrame::ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const
   10164             : {
   10165             :   // Get the frames text content
   10166           0 :   const nsTextFragment* frag = mContent->GetText();
   10167           0 :   if (!frag) {
   10168           0 :     return;
   10169             :   }
   10170             : 
   10171             :   // Compute the total length of the text content.
   10172           0 :   *aTotalContentLength = frag->GetLength();
   10173             : 
   10174           0 :   int32_t contentLength = GetContentLength();
   10175             :   // Set current fragment and current fragment offset
   10176           0 :   if (0 == contentLength) {
   10177           0 :     return;
   10178             :   }
   10179           0 :   int32_t fragOffset = GetContentOffset();
   10180           0 :   int32_t n = fragOffset + contentLength;
   10181           0 :   while (fragOffset < n) {
   10182           0 :     char16_t ch = frag->CharAt(fragOffset++);
   10183           0 :     if (ch == '\r') {
   10184           0 :       aBuf.AppendLiteral("\\r");
   10185           0 :     } else if (ch == '\n') {
   10186           0 :       aBuf.AppendLiteral("\\n");
   10187           0 :     } else if (ch == '\t') {
   10188           0 :       aBuf.AppendLiteral("\\t");
   10189           0 :     } else if ((ch < ' ') || (ch >= 127)) {
   10190           0 :       aBuf.Append(nsPrintfCString("\\u%04x", ch));
   10191             :     } else {
   10192           0 :       aBuf.Append(ch);
   10193             :     }
   10194             :   }
   10195             : }
   10196             : 
   10197             : nsresult
   10198           0 : nsTextFrame::GetFrameName(nsAString& aResult) const
   10199             : {
   10200           0 :   MakeFrameName(NS_LITERAL_STRING("Text"), aResult);
   10201             :   int32_t totalContentLength;
   10202           0 :   nsAutoCString tmp;
   10203           0 :   ToCString(tmp, &totalContentLength);
   10204           0 :   tmp.SetLength(std::min(tmp.Length(), 50u));
   10205           0 :   aResult += NS_LITERAL_STRING("\"") + NS_ConvertASCIItoUTF16(tmp) + NS_LITERAL_STRING("\"");
   10206           0 :   return NS_OK;
   10207             : }
   10208             : 
   10209             : void
   10210           0 : nsTextFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
   10211             : {
   10212           0 :   nsCString str;
   10213           0 :   ListGeneric(str, aPrefix, aFlags);
   10214             : 
   10215           0 :   str += nsPrintfCString(" [run=%p]", static_cast<void*>(mTextRun));
   10216             : 
   10217             :   // Output the first/last content offset and prev/next in flow info
   10218           0 :   bool isComplete = uint32_t(GetContentEnd()) == GetContent()->TextLength();
   10219           0 :   str += nsPrintfCString("[%d,%d,%c] ", GetContentOffset(), GetContentLength(),
   10220           0 :           isComplete ? 'T':'F');
   10221             : 
   10222           0 :   if (IsSelected()) {
   10223           0 :     str += " SELECTED";
   10224             :   }
   10225           0 :   fprintf_stderr(out, "%s\n", str.get());
   10226           0 : }
   10227             : #endif
   10228             : 
   10229             : #ifdef DEBUG
   10230             : nsFrameState
   10231           0 : nsTextFrame::GetDebugStateBits() const
   10232             : {
   10233             :   // mask out our emptystate flags; those are just caches
   10234           0 :   return nsFrame::GetDebugStateBits() &
   10235           0 :     ~(TEXT_WHITESPACE_FLAGS | TEXT_REFLOW_FLAGS);
   10236             : }
   10237             : #endif
   10238             : 
   10239             : void
   10240           0 : nsTextFrame::AdjustOffsetsForBidi(int32_t aStart, int32_t aEnd)
   10241             : {
   10242           0 :   AddStateBits(NS_FRAME_IS_BIDI);
   10243           0 :   mContent->DeleteProperty(nsGkAtoms::flowlength);
   10244             : 
   10245             :   /*
   10246             :    * After Bidi resolution we may need to reassign text runs.
   10247             :    * This is called during bidi resolution from the block container, so we
   10248             :    * shouldn't be holding a local reference to a textrun anywhere.
   10249             :    */
   10250           0 :   ClearTextRuns();
   10251             : 
   10252           0 :   nsTextFrame* prev = GetPrevContinuation();
   10253           0 :   if (prev) {
   10254             :     // the bidi resolver can be very evil when columns/pages are involved. Don't
   10255             :     // let it violate our invariants.
   10256           0 :     int32_t prevOffset = prev->GetContentOffset();
   10257           0 :     aStart = std::max(aStart, prevOffset);
   10258           0 :     aEnd = std::max(aEnd, prevOffset);
   10259           0 :     prev->ClearTextRuns();
   10260             :   }
   10261             : 
   10262           0 :   mContentOffset = aStart;
   10263           0 :   SetLength(aEnd - aStart, nullptr, 0);
   10264           0 : }
   10265             : 
   10266             : /**
   10267             :  * @return true if this text frame ends with a newline character.  It should return
   10268             :  * false if it is not a text frame.
   10269             :  */
   10270             : bool
   10271           0 : nsTextFrame::HasSignificantTerminalNewline() const
   10272             : {
   10273           0 :   return ::HasTerminalNewline(this) && StyleText()->NewlineIsSignificant(this);
   10274             : }
   10275             : 
   10276             : bool
   10277           0 : nsTextFrame::IsAtEndOfLine() const
   10278             : {
   10279           0 :   return (GetStateBits() & TEXT_END_OF_LINE) != 0;
   10280             : }
   10281             : 
   10282             : nscoord
   10283         167 : nsTextFrame::GetLogicalBaseline(WritingMode aWM) const
   10284             : {
   10285         167 :   if (!aWM.IsOrthogonalTo(GetWritingMode())) {
   10286         167 :     return mAscent;
   10287             :   }
   10288             : 
   10289             :   // When the text frame has a writing mode orthogonal to the desired
   10290             :   // writing mode, return a baseline coincides its parent frame.
   10291           0 :   nsIFrame* parent = GetParent();
   10292           0 :   nsPoint position = GetNormalPosition();
   10293           0 :   nscoord parentAscent = parent->GetLogicalBaseline(aWM);
   10294           0 :   if (aWM.IsVerticalRL()) {
   10295           0 :     nscoord parentDescent = parent->GetSize().width - parentAscent;
   10296           0 :     nscoord descent = parentDescent - position.x;
   10297           0 :     return GetSize().width - descent;
   10298             :   }
   10299           0 :   return parentAscent - (aWM.IsVertical() ? position.x : position.y);
   10300             : }
   10301             : 
   10302             : bool
   10303           0 : nsTextFrame::HasAnyNoncollapsedCharacters()
   10304             : {
   10305           0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   10306           0 :   int32_t offset = GetContentOffset(),
   10307           0 :           offsetEnd = GetContentEnd();
   10308           0 :   int32_t skippedOffset = iter.ConvertOriginalToSkipped(offset);
   10309           0 :   int32_t skippedOffsetEnd = iter.ConvertOriginalToSkipped(offsetEnd);
   10310           0 :   return skippedOffset != skippedOffsetEnd;
   10311             : }
   10312             : 
   10313             : bool
   10314           0 : nsTextFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
   10315             : {
   10316           0 :   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
   10317           0 :     return true;
   10318             :   }
   10319             : 
   10320             :   nsIFrame* decorationsBlock;
   10321           0 :   if (IsFloatingFirstLetterChild()) {
   10322           0 :     decorationsBlock = GetParent();
   10323             :   } else {
   10324           0 :     nsIFrame* f = this;
   10325             :     for (;;) {
   10326           0 :       nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
   10327           0 :       if (fBlock) {
   10328           0 :         decorationsBlock = fBlock;
   10329           0 :         break;
   10330             :       }
   10331             : 
   10332           0 :       f = f->GetParent();
   10333           0 :       if (!f) {
   10334           0 :         NS_ERROR("Couldn't find any block ancestor (for text decorations)");
   10335           0 :         return nsFrame::ComputeCustomOverflow(aOverflowAreas);
   10336             :       }
   10337           0 :     }
   10338             :   }
   10339             : 
   10340           0 :   aOverflowAreas = RecomputeOverflow(decorationsBlock);
   10341           0 :   return nsFrame::ComputeCustomOverflow(aOverflowAreas);
   10342             : }
   10343             : 
   10344           0 : NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(JustificationAssignmentProperty, int32_t)
   10345             : 
   10346             : void
   10347           0 : nsTextFrame::AssignJustificationGaps(
   10348             :     const mozilla::JustificationAssignment& aAssign)
   10349             : {
   10350           0 :   int32_t encoded = (aAssign.mGapsAtStart << 8) | aAssign.mGapsAtEnd;
   10351             :   static_assert(sizeof(aAssign) == 1,
   10352             :                 "The encoding might be broken if JustificationAssignment "
   10353             :                 "is larger than 1 byte");
   10354           0 :   SetProperty(JustificationAssignmentProperty(), encoded);
   10355           0 : }
   10356             : 
   10357             : mozilla::JustificationAssignment
   10358           0 : nsTextFrame::GetJustificationAssignment() const
   10359             : {
   10360           0 :   int32_t encoded = GetProperty(JustificationAssignmentProperty());
   10361           0 :   mozilla::JustificationAssignment result;
   10362           0 :   result.mGapsAtStart = encoded >> 8;
   10363           0 :   result.mGapsAtEnd = encoded & 0xFF;
   10364           0 :   return result;
   10365             : }
   10366             : 
   10367             : uint32_t
   10368           0 : nsTextFrame::CountGraphemeClusters() const
   10369             : {
   10370           0 :   const nsTextFragment* frag = GetContent()->GetText();
   10371           0 :   MOZ_ASSERT(frag, "Text frame must have text fragment");
   10372           0 :   nsAutoString content;
   10373           0 :   frag->AppendTo(content, GetContentOffset(), GetContentLength());
   10374           0 :   return unicode::CountGraphemeClusters(content.Data(), content.Length());
   10375             : }

Generated by: LCOV version 1.13