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

Generated by: LCOV version 1.13-14-ga5dd952