LCOV - code coverage report
Current view: top level - gfx/thebes - gfxTextRun.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 619 1649 37.5 %
Date: 2017-07-14 16:53:18 Functions: 57 91 62.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=4 et sw=4 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             : #include "gfxTextRun.h"
       8             : #include "gfxGlyphExtents.h"
       9             : #include "gfxPlatformFontList.h"
      10             : #include "gfxUserFontSet.h"
      11             : #include "mozilla/gfx/2D.h"
      12             : #include "mozilla/gfx/PathHelpers.h"
      13             : #include "mozilla/SizePrintfMacros.h"
      14             : #include "mozilla/Sprintf.h"
      15             : 
      16             : #include "gfxContext.h"
      17             : #include "gfxFontConstants.h"
      18             : #include "gfxFontMissingGlyphs.h"
      19             : #include "gfxScriptItemizer.h"
      20             : #include "nsUnicodeProperties.h"
      21             : #include "nsUnicodeRange.h"
      22             : #include "nsStyleConsts.h"
      23             : #include "mozilla/Likely.h"
      24             : #include "gfx2DGlue.h"
      25             : #include "mozilla/gfx/Logging.h"        // for gfxCriticalError
      26             : #include "mozilla/UniquePtr.h"
      27             : 
      28             : #ifdef XP_WIN
      29             : #include "gfxWindowsPlatform.h"
      30             : #endif
      31             : 
      32             : #include "cairo.h"
      33             : 
      34             : using namespace mozilla;
      35             : using namespace mozilla::gfx;
      36             : using namespace mozilla::unicode;
      37             : using mozilla::services::GetObserverService;
      38             : 
      39             : static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
      40             : static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
      41             : 
      42             : #ifdef DEBUG_roc
      43             : #define DEBUG_TEXT_RUN_STORAGE_METRICS
      44             : #endif
      45             : 
      46             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
      47             : extern uint32_t gTextRunStorageHighWaterMark;
      48             : extern uint32_t gTextRunStorage;
      49             : extern uint32_t gFontCount;
      50             : extern uint32_t gGlyphExtentsCount;
      51             : extern uint32_t gGlyphExtentsWidthsTotalSize;
      52             : extern uint32_t gGlyphExtentsSetupEagerSimple;
      53             : extern uint32_t gGlyphExtentsSetupEagerTight;
      54             : extern uint32_t gGlyphExtentsSetupLazyTight;
      55             : extern uint32_t gGlyphExtentsSetupFallBackToTight;
      56             : #endif
      57             : 
      58             : bool
      59         154 : gfxTextRun::GlyphRunIterator::NextRun()
      60             : {
      61             :     uint32_t glyphRunCount;
      62         154 :     if (mTextRun->mHasGlyphRunArray) {
      63           0 :         glyphRunCount = mTextRun->mGlyphRunArray.Length();
      64           0 :         if (mNextIndex >= glyphRunCount) {
      65           0 :             return false;
      66             :         }
      67           0 :         mGlyphRun = &mTextRun->mGlyphRunArray[mNextIndex];
      68             :     } else {
      69         154 :         if (mNextIndex > 0) {
      70          77 :             return false;
      71             :         }
      72          77 :         glyphRunCount = 1;
      73          77 :         mGlyphRun = &mTextRun->mSingleGlyphRun;
      74             :     }
      75             : 
      76          77 :     if (mGlyphRun->mCharacterOffset >= mEndOffset) {
      77           0 :         return false;
      78             :     }
      79             : 
      80          77 :     mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
      81          77 :     uint32_t last = mNextIndex + 1 < glyphRunCount
      82         154 :         ? mTextRun->mGlyphRunArray[mNextIndex + 1].mCharacterOffset
      83         154 :         : mTextRun->GetLength();
      84          77 :     mStringEnd = std::min(mEndOffset, last);
      85             : 
      86          77 :     ++mNextIndex;
      87          77 :     return true;
      88             : }
      89             : 
      90             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
      91             : static void
      92             : AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
      93             : {
      94             :     // Ignores detailed glyphs... we don't know when those have been constructed
      95             :     // Also ignores gfxSkipChars dynamic storage (which won't be anything
      96             :     // for preformatted text)
      97             :     // Also ignores GlyphRun array, again because it hasn't been constructed
      98             :     // by the time this gets called. If there's only one glyphrun that's stored
      99             :     // directly in the textrun anyway so no additional overhead.
     100             :     uint32_t length = aTextRun->GetLength();
     101             :     int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
     102             :     bytes += sizeof(gfxTextRun);
     103             :     gTextRunStorage += bytes*aSign;
     104             :     gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
     105             : }
     106             : #endif
     107             : 
     108             : static bool
     109          71 : NeedsGlyphExtents(gfxTextRun *aTextRun)
     110             : {
     111          71 :     if (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX)
     112           9 :         return true;
     113             :     uint32_t numRuns;
     114          62 :     const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
     115         124 :     for (uint32_t i = 0; i < numRuns; ++i) {
     116          62 :         if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
     117           0 :             return true;
     118             :     }
     119          62 :     return false;
     120             : }
     121             : 
     122             : // Helper for textRun creation to preallocate storage for glyph records;
     123             : // this function returns a pointer to the newly-allocated glyph storage.
     124             : // Returns nullptr if allocation fails.
     125             : void *
     126          83 : gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
     127             : {
     128             :     // Allocate the storage we need, returning nullptr on failure rather than
     129             :     // throwing an exception (because web content can create huge runs).
     130          83 :     void *storage = malloc(aSize + aLength * sizeof(CompressedGlyph));
     131          83 :     if (!storage) {
     132           0 :         NS_WARNING("failed to allocate storage for text run!");
     133           0 :         return nullptr;
     134             :     }
     135             : 
     136             :     // Initialize the glyph storage (beyond aSize) to zero
     137          83 :     memset(reinterpret_cast<char*>(storage) + aSize, 0,
     138          83 :            aLength * sizeof(CompressedGlyph));
     139             : 
     140          83 :     return storage;
     141             : }
     142             : 
     143             : already_AddRefed<gfxTextRun>
     144          83 : gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
     145             :                    uint32_t aLength, gfxFontGroup *aFontGroup,
     146             :                    gfx::ShapedTextFlags aFlags, 
     147             :                    nsTextFrameUtils::Flags aFlags2)
     148             : {
     149          83 :     void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
     150          83 :     if (!storage) {
     151           0 :         return nullptr;
     152             :     }
     153             : 
     154             :     RefPtr<gfxTextRun> result = new (storage) gfxTextRun(aParams, aLength,
     155             :                                                          aFontGroup,
     156         166 :                                                          aFlags, aFlags2);
     157          83 :     return result.forget();
     158             : }
     159             : 
     160          83 : gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
     161             :                        uint32_t aLength, gfxFontGroup *aFontGroup,
     162             :                        gfx::ShapedTextFlags aFlags,
     163          83 :                        nsTextFrameUtils::Flags aFlags2)
     164          83 :     : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
     165             :     , mSingleGlyphRun()
     166          83 :     , mUserData(aParams->mUserData)
     167             :     , mFontGroup(aFontGroup)
     168             :     , mFlags2(aFlags2)
     169             :     , mReleasedFontGroup(false)
     170             :     , mHasGlyphRunArray(false)
     171         166 :     , mShapingState(eShapingState_Normal)
     172             : {
     173          83 :     NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
     174          83 :     NS_ADDREF(mFontGroup);
     175             : 
     176             : #ifndef RELEASE_OR_BETA
     177          83 :     gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
     178          83 :     if (tp) {
     179           0 :         tp->current.textrunConst++;
     180             :     }
     181             : #endif
     182             : 
     183          83 :     mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
     184             : 
     185          83 :     if (aParams->mSkipChars) {
     186          21 :         mSkipChars.TakeFrom(aParams->mSkipChars);
     187             :     }
     188             : 
     189             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     190             :     AccountStorageForTextRun(this, 1);
     191             : #endif
     192             : 
     193          83 :     mSkipDrawing = mFontGroup->ShouldSkipDrawing();
     194          83 : }
     195             : 
     196         213 : gfxTextRun::~gfxTextRun()
     197             : {
     198             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     199             :     AccountStorageForTextRun(this, -1);
     200             : #endif
     201             : #ifdef DEBUG
     202             :     // Make it easy to detect a dead text run
     203         142 :     mFlags = ~gfx::ShapedTextFlags();
     204          71 :     mFlags2 = ~nsTextFrameUtils::Flags();
     205             : #endif
     206             : 
     207          71 :     if (mHasGlyphRunArray) {
     208           0 :         mGlyphRunArray.~nsTArray<GlyphRun>();
     209             :     } else {
     210          71 :         mSingleGlyphRun.mFont = nullptr;
     211             :     }
     212             : 
     213             :     // The cached ellipsis textrun (if any) in a fontgroup will have already
     214             :     // been told to release its reference to the group, so we mustn't do that
     215             :     // again here.
     216          71 :     if (!mReleasedFontGroup) {
     217             : #ifndef RELEASE_OR_BETA
     218          71 :         gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
     219          71 :         if (tp) {
     220           0 :             tp->current.textrunDestr++;
     221             :         }
     222             : #endif
     223          71 :         NS_RELEASE(mFontGroup);
     224             :     }
     225         142 : }
     226             : 
     227             : void
     228           0 : gfxTextRun::ReleaseFontGroup()
     229             : {
     230           0 :     NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
     231           0 :     NS_RELEASE(mFontGroup);
     232           0 :     mReleasedFontGroup = true;
     233           0 : }
     234             : 
     235             : bool
     236          10 : gfxTextRun::SetPotentialLineBreaks(Range aRange, const uint8_t* aBreakBefore)
     237             : {
     238          10 :     NS_ASSERTION(aRange.end <= GetLength(), "Overflow");
     239             : 
     240          10 :     uint32_t changed = 0;
     241          10 :     CompressedGlyph* cg = mCharacterGlyphs + aRange.start;
     242          10 :     const CompressedGlyph* const end = cg + aRange.Length();
     243         318 :     while (cg < end) {
     244         154 :         uint8_t canBreak = *aBreakBefore++;
     245         154 :         if (canBreak && !cg->IsClusterStart()) {
     246             :             // XXX If we replace the line-breaker with one based more closely
     247             :             // on UAX#14 (e.g. using ICU), this may not be needed any more.
     248             :             // Avoid possible breaks inside a cluster, EXCEPT when the previous
     249             :             // character was a space (compare UAX#14 rules LB9, LB10).
     250           0 :             if (cg == mCharacterGlyphs || !(cg - 1)->CharIsSpace()) {
     251           0 :                 canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     252             :             }
     253             :         }
     254         154 :         changed |= cg->SetCanBreakBefore(canBreak);
     255         154 :         ++cg;
     256             :     }
     257          10 :     return changed != 0;
     258             : }
     259             : 
     260             : gfxTextRun::LigatureData
     261           0 : gfxTextRun::ComputeLigatureData(Range aPartRange,
     262             :                                 PropertyProvider *aProvider) const
     263             : {
     264           0 :     NS_ASSERTION(aPartRange.start < aPartRange.end,
     265             :                  "Computing ligature data for empty range");
     266           0 :     NS_ASSERTION(aPartRange.end <= GetLength(), "Character length overflow");
     267             : 
     268           0 :     LigatureData result;
     269           0 :     const CompressedGlyph *charGlyphs = mCharacterGlyphs;
     270             : 
     271             :     uint32_t i;
     272           0 :     for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
     273           0 :         NS_ASSERTION(i > 0, "Ligature at the start of the run??");
     274             :     }
     275           0 :     result.mRange.start = i;
     276           0 :     for (i = aPartRange.start + 1;
     277           0 :          i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
     278             :     }
     279           0 :     result.mRange.end = i;
     280             : 
     281           0 :     int32_t ligatureWidth = GetAdvanceForGlyphs(result.mRange);
     282             :     // Count the number of started clusters we have seen
     283           0 :     uint32_t totalClusterCount = 0;
     284           0 :     uint32_t partClusterIndex = 0;
     285           0 :     uint32_t partClusterCount = 0;
     286           0 :     for (i = result.mRange.start; i < result.mRange.end; ++i) {
     287             :         // Treat the first character of the ligature as the start of a
     288             :         // cluster for our purposes of allocating ligature width to its
     289             :         // characters.
     290           0 :         if (i == result.mRange.start || charGlyphs[i].IsClusterStart()) {
     291           0 :             ++totalClusterCount;
     292           0 :             if (i < aPartRange.start) {
     293           0 :                 ++partClusterIndex;
     294           0 :             } else if (i < aPartRange.end) {
     295           0 :                 ++partClusterCount;
     296             :             }
     297             :         }
     298             :     }
     299           0 :     NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
     300           0 :     result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
     301           0 :     result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
     302             : 
     303             :     // Any rounding errors are apportioned to the final part of the ligature,
     304             :     // so that measuring all parts of a ligature and summing them is equal to
     305             :     // the ligature width.
     306           0 :     if (aPartRange.end == result.mRange.end) {
     307           0 :         gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
     308           0 :         result.mPartWidth += ligatureWidth - allParts;
     309             :     }
     310             : 
     311           0 :     if (partClusterCount == 0) {
     312             :         // nothing to draw
     313           0 :         result.mClipBeforePart = result.mClipAfterPart = true;
     314             :     } else {
     315             :         // Determine whether we should clip before or after this part when
     316             :         // drawing its slice of the ligature.
     317             :         // We need to clip before the part if any cluster is drawn before
     318             :         // this part.
     319           0 :         result.mClipBeforePart = partClusterIndex > 0;
     320             :         // We need to clip after the part if any cluster is drawn after
     321             :         // this part.
     322           0 :         result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
     323             :     }
     324             : 
     325           0 :     if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
     326             :         gfxFont::Spacing spacing;
     327           0 :         if (aPartRange.start == result.mRange.start) {
     328           0 :             aProvider->GetSpacing(
     329           0 :                 Range(aPartRange.start, aPartRange.start + 1), &spacing);
     330           0 :             result.mPartWidth += spacing.mBefore;
     331             :         }
     332           0 :         if (aPartRange.end == result.mRange.end) {
     333           0 :             aProvider->GetSpacing(
     334           0 :                 Range(aPartRange.end - 1, aPartRange.end), &spacing);
     335           0 :             result.mPartWidth += spacing.mAfter;
     336             :         }
     337             :     }
     338             : 
     339           0 :     return result;
     340             : }
     341             : 
     342             : gfxFloat
     343         118 : gfxTextRun::ComputePartialLigatureWidth(Range aPartRange,
     344             :                                         PropertyProvider *aProvider) const
     345             : {
     346         118 :     if (aPartRange.start >= aPartRange.end)
     347         118 :         return 0;
     348           0 :     LigatureData data = ComputeLigatureData(aPartRange, aProvider);
     349           0 :     return data.mPartWidth;
     350             : }
     351             : 
     352             : int32_t
     353         488 : gfxTextRun::GetAdvanceForGlyphs(Range aRange) const
     354             : {
     355         488 :     int32_t advance = 0;
     356        1652 :     for (auto i = aRange.start; i < aRange.end; ++i) {
     357        1164 :         advance += GetAdvanceForGlyph(i);
     358             :     }
     359         488 :     return advance;
     360             : }
     361             : 
     362             : static void
     363           0 : GetAdjustedSpacing(const gfxTextRun *aTextRun, gfxTextRun::Range aRange,
     364             :                    gfxTextRun::PropertyProvider *aProvider,
     365             :                    gfxTextRun::PropertyProvider::Spacing *aSpacing)
     366             : {
     367           0 :     if (aRange.start >= aRange.end)
     368           0 :         return;
     369             : 
     370           0 :     aProvider->GetSpacing(aRange, aSpacing);
     371             : 
     372             : #ifdef DEBUG
     373             :     // Check to see if we have spacing inside ligatures
     374             : 
     375           0 :     const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
     376             :     uint32_t i;
     377             : 
     378           0 :     for (i = aRange.start; i < aRange.end; ++i) {
     379           0 :         if (!charGlyphs[i].IsLigatureGroupStart()) {
     380           0 :             NS_ASSERTION(i == aRange.start ||
     381             :                          aSpacing[i - aRange.start].mBefore == 0,
     382             :                          "Before-spacing inside a ligature!");
     383           0 :             NS_ASSERTION(i - 1 <= aRange.start ||
     384             :                          aSpacing[i - 1 - aRange.start].mAfter == 0,
     385             :                          "After-spacing inside a ligature!");
     386             :         }
     387             :     }
     388             : #endif
     389             : }
     390             : 
     391             : bool
     392          69 : gfxTextRun::GetAdjustedSpacingArray(Range aRange, PropertyProvider *aProvider,
     393             :                                     Range aSpacingRange,
     394             :                                     nsTArray<PropertyProvider::Spacing>*
     395             :                                         aSpacing) const
     396             : {
     397          69 :     if (!aProvider || !(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING))
     398          69 :         return false;
     399           0 :     if (!aSpacing->AppendElements(aRange.Length()))
     400           0 :         return false;
     401           0 :     auto spacingOffset = aSpacingRange.start - aRange.start;
     402           0 :     memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset);
     403           0 :     GetAdjustedSpacing(this, aSpacingRange, aProvider,
     404           0 :                        aSpacing->Elements() + spacingOffset);
     405           0 :     memset(aSpacing->Elements() + aSpacingRange.end - aRange.start, 0,
     406           0 :            sizeof(gfxFont::Spacing) * (aRange.end - aSpacingRange.end));
     407           0 :     return true;
     408             : }
     409             : 
     410             : void
     411         152 : gfxTextRun::ShrinkToLigatureBoundaries(Range* aRange) const
     412             : {
     413         152 :     if (aRange->start >= aRange->end)
     414          10 :         return;
     415             : 
     416         142 :     const CompressedGlyph *charGlyphs = mCharacterGlyphs;
     417             : 
     418         284 :     while (aRange->start < aRange->end &&
     419         142 :            !charGlyphs[aRange->start].IsLigatureGroupStart()) {
     420           0 :         ++aRange->start;
     421             :     }
     422         142 :     if (aRange->end < GetLength()) {
     423           2 :         while (aRange->end > aRange->start &&
     424           1 :                !charGlyphs[aRange->end].IsLigatureGroupStart()) {
     425           0 :             --aRange->end;
     426             :         }
     427             :     }
     428             : }
     429             : 
     430             : void
     431          21 : gfxTextRun::DrawGlyphs(gfxFont *aFont, Range aRange, gfxPoint *aPt,
     432             :                        PropertyProvider *aProvider, Range aSpacingRange,
     433             :                        TextRunDrawParams& aParams,
     434             :                        gfx::ShapedTextFlags aOrientation) const
     435             : {
     436          42 :     AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     437             :     bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
     438          21 :                                                aSpacingRange, &spacingBuffer);
     439          21 :     aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
     440          21 :     aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation);
     441          21 : }
     442             : 
     443             : static void
     444           0 : ClipPartialLigature(const gfxTextRun* aTextRun,
     445             :                     gfxFloat *aStart, gfxFloat *aEnd,
     446             :                     gfxFloat aOrigin,
     447             :                     gfxTextRun::LigatureData *aLigature)
     448             : {
     449           0 :     if (aLigature->mClipBeforePart) {
     450           0 :         if (aTextRun->IsRightToLeft()) {
     451           0 :             *aEnd = std::min(*aEnd, aOrigin);
     452             :         } else {
     453           0 :             *aStart = std::max(*aStart, aOrigin);
     454             :         }
     455             :     }
     456           0 :     if (aLigature->mClipAfterPart) {
     457             :         gfxFloat endEdge =
     458           0 :             aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth;
     459           0 :         if (aTextRun->IsRightToLeft()) {
     460           0 :             *aStart = std::max(*aStart, endEdge);
     461             :         } else {
     462           0 :             *aEnd = std::min(*aEnd, endEdge);
     463             :         }
     464             :     }
     465           0 : }
     466             : 
     467             : void
     468          42 : gfxTextRun::DrawPartialLigature(gfxFont *aFont, Range aRange,
     469             :                                 gfxPoint *aPt, PropertyProvider *aProvider,
     470             :                                 TextRunDrawParams& aParams,
     471             :                                 gfx::ShapedTextFlags aOrientation) const
     472             : {
     473          42 :     if (aRange.start >= aRange.end) {
     474          42 :         return;
     475             :     }
     476             : 
     477             :     // Draw partial ligature. We hack this by clipping the ligature.
     478           0 :     LigatureData data = ComputeLigatureData(aRange, aProvider);
     479           0 :     gfxRect clipExtents = aParams.context->GetClipExtents();
     480             :     gfxFloat start, end;
     481           0 :     if (aParams.isVerticalRun) {
     482           0 :         start = clipExtents.Y() * mAppUnitsPerDevUnit;
     483           0 :         end = clipExtents.YMost() * mAppUnitsPerDevUnit;
     484           0 :         ClipPartialLigature(this, &start, &end, aPt->y, &data);
     485             :     } else {
     486           0 :         start = clipExtents.X() * mAppUnitsPerDevUnit;
     487           0 :         end = clipExtents.XMost() * mAppUnitsPerDevUnit;
     488           0 :         ClipPartialLigature(this, &start, &end, aPt->x, &data);
     489             :     }
     490             : 
     491             :     {
     492             :       // use division here to ensure that when the rect is aligned on multiples
     493             :       // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
     494             :       // Also, make sure we snap the rectangle to device pixels.
     495           0 :       Rect clipRect = aParams.isVerticalRun ?
     496           0 :           Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
     497           0 :                clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit) :
     498           0 :           Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
     499           0 :                (end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
     500           0 :       MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
     501             : 
     502           0 :       aParams.context->Clip(clipRect);
     503             :     }
     504             : 
     505           0 :     gfxPoint pt;
     506           0 :     if (aParams.isVerticalRun) {
     507           0 :         pt = gfxPoint(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
     508             :     } else {
     509           0 :         pt = gfxPoint(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
     510             :     }
     511             : 
     512             :     DrawGlyphs(aFont, data.mRange, &pt,
     513           0 :                aProvider, aRange, aParams, aOrientation);
     514           0 :     aParams.context->PopClip();
     515             : 
     516           0 :     if (aParams.isVerticalRun) {
     517           0 :         aPt->y += aParams.direction * data.mPartWidth;
     518             :     } else {
     519           0 :         aPt->x += aParams.direction * data.mPartWidth;
     520             :     }
     521             : }
     522             : 
     523             : // Returns true if a glyph run is using a font with synthetic bolding enabled,
     524             : // or a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to
     525             : // check whether the text run needs to be explicitly composited in order to
     526             : // support opacity.
     527             : static bool
     528           8 : HasSyntheticBoldOrColor(const gfxTextRun *aRun, gfxTextRun::Range aRange)
     529             : {
     530           8 :     gfxTextRun::GlyphRunIterator iter(aRun, aRange);
     531          24 :     while (iter.NextRun()) {
     532           8 :         gfxFont *font = iter.GetGlyphRun()->mFont;
     533           8 :         if (font) {
     534           8 :             if (font->IsSyntheticBold()) {
     535           0 :                 return true;
     536             :             }
     537           8 :             gfxFontEntry* fe = font->GetFontEntry();
     538           8 :             if (fe->TryGetSVGData(font) || fe->TryGetColorGlyphs()) {
     539           0 :                 return true;
     540             :             }
     541             : #if defined(XP_MACOSX) // sbix fonts only supported via Core Text
     542             :             if (fe->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
     543             :                 return true;
     544             :             }
     545             : #endif
     546             :         }
     547             :     }
     548           8 :     return false;
     549             : }
     550             : 
     551             : // Returns true if color is neither opaque nor transparent (i.e. alpha is not 0
     552             : // or 1), and false otherwise. If true, aCurrentColorOut is set on output.
     553             : static bool
     554          21 : HasNonOpaqueNonTransparentColor(gfxContext *aContext, Color& aCurrentColorOut)
     555             : {
     556          21 :     if (aContext->GetDeviceColor(aCurrentColorOut)) {
     557          21 :         if (0.f < aCurrentColorOut.a && aCurrentColorOut.a < 1.f) {
     558           8 :             return true;
     559             :         }
     560             :     }
     561          13 :     return false;
     562             : }
     563             : 
     564             : // helper class for double-buffering drawing with non-opaque color
     565             : struct BufferAlphaColor {
     566          21 :     explicit BufferAlphaColor(gfxContext *aContext)
     567          21 :         : mContext(aContext)
     568             :     {
     569             : 
     570          21 :     }
     571             : 
     572          21 :     ~BufferAlphaColor() {}
     573             : 
     574           0 :     void PushSolidColor(const gfxRect& aBounds, const Color& aAlphaColor, uint32_t appsPerDevUnit)
     575             :     {
     576           0 :         mContext->Save();
     577           0 :         mContext->NewPath();
     578           0 :         mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
     579           0 :                     aBounds.Y() / appsPerDevUnit,
     580           0 :                     aBounds.Width() / appsPerDevUnit,
     581           0 :                     aBounds.Height() / appsPerDevUnit), true);
     582           0 :         mContext->Clip();
     583           0 :         mContext->SetColor(Color(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
     584           0 :         mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a);
     585           0 :     }
     586             : 
     587           0 :     void PopAlpha()
     588             :     {
     589             :         // pop the text, using the color alpha as the opacity
     590           0 :         mContext->PopGroupAndBlend();
     591           0 :         mContext->Restore();
     592           0 :     }
     593             : 
     594             :     gfxContext *mContext;
     595             : };
     596             : 
     597             : void
     598          21 : gfxTextRun::Draw(Range aRange, gfxPoint aPt, const DrawParams& aParams) const
     599             : {
     600          21 :     NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
     601          21 :     NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH ||
     602             :                  !(aParams.drawMode & DrawMode::GLYPH_PATH),
     603             :                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
     604          21 :     NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks,
     605             :                  "callback must not be specified unless using GLYPH_PATH");
     606             : 
     607          21 :     bool skipDrawing = mSkipDrawing;
     608          21 :     if (aParams.drawMode & DrawMode::GLYPH_FILL) {
     609          21 :         Color currentColor;
     610          42 :         if (aParams.context->GetDeviceColor(currentColor) &&
     611          21 :             currentColor.a == 0) {
     612           0 :             skipDrawing = true;
     613             :         }
     614             :     }
     615             : 
     616          21 :     gfxFloat direction = GetDirection();
     617             : 
     618          21 :     if (skipDrawing) {
     619             :         // We don't need to draw anything;
     620             :         // but if the caller wants advance width, we need to compute it here
     621           0 :         if (aParams.advanceWidth) {
     622             :             gfxTextRun::Metrics metrics = MeasureText(
     623             :                 aRange, gfxFont::LOOSE_INK_EXTENTS,
     624           0 :                 aParams.context->GetDrawTarget(), aParams.provider);
     625           0 :             *aParams.advanceWidth = metrics.mAdvanceWidth * direction;
     626             :         }
     627             : 
     628             :         // return without drawing
     629           0 :         return;
     630             :     }
     631             : 
     632             :     // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
     633             :     // correctly unless first drawn without alpha
     634          42 :     BufferAlphaColor syntheticBoldBuffer(aParams.context);
     635          21 :     Color currentColor;
     636          21 :     bool needToRestore = false;
     637             : 
     638          63 :     if (aParams.drawMode & DrawMode::GLYPH_FILL &&
     639          50 :         HasNonOpaqueNonTransparentColor(aParams.context, currentColor) &&
     640           8 :         HasSyntheticBoldOrColor(this, aRange)) {
     641           0 :         needToRestore = true;
     642             :         // Measure text; use the bounding box to determine the area we need
     643             :         // to buffer.
     644             :         gfxTextRun::Metrics metrics = MeasureText(
     645             :             aRange, gfxFont::LOOSE_INK_EXTENTS,
     646           0 :             aParams.context->GetDrawTarget(), aParams.provider);
     647           0 :         if (IsRightToLeft()) {
     648           0 :             metrics.mBoundingBox.MoveBy(gfxPoint(aPt.x - metrics.mAdvanceWidth,
     649           0 :                                                  aPt.y));
     650             :         } else {
     651           0 :             metrics.mBoundingBox.MoveBy(aPt);
     652             :         }
     653           0 :         syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
     654           0 :                                            GetAppUnitsPerDevUnit());
     655             :     }
     656             : 
     657             :     // Set up parameters that will be constant across all glyph runs we need
     658             :     // to draw, regardless of the font used.
     659          42 :     TextRunDrawParams params;
     660          21 :     params.context = aParams.context;
     661          21 :     params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
     662          21 :     params.isVerticalRun = IsVertical();
     663          21 :     params.isRTL = IsRightToLeft();
     664          21 :     params.direction = direction;
     665          21 :     params.strokeOpts = aParams.strokeOpts;
     666          21 :     params.textStrokeColor = aParams.textStrokeColor;
     667          21 :     params.textStrokePattern = aParams.textStrokePattern;
     668          21 :     params.drawOpts = aParams.drawOpts;
     669          21 :     params.drawMode = aParams.drawMode;
     670          21 :     params.callbacks = aParams.callbacks;
     671          21 :     params.runContextPaint = aParams.contextPaint;
     672          21 :     params.paintSVGGlyphs = !aParams.callbacks ||
     673           0 :         aParams.callbacks->mShouldPaintSVGGlyphs;
     674          21 :     params.dt = aParams.context->GetDrawTarget();
     675             :     params.fontSmoothingBGColor =
     676          21 :         aParams.context->GetFontSmoothingBackgroundColor();
     677             : 
     678          21 :     GlyphRunIterator iter(this, aRange);
     679          21 :     gfxFloat advance = 0.0;
     680             : 
     681          63 :     while (iter.NextRun()) {
     682          21 :         gfxFont *font = iter.GetGlyphRun()->mFont;
     683          21 :         uint32_t start = iter.GetStringStart();
     684          21 :         uint32_t end = iter.GetStringEnd();
     685          21 :         Range ligatureRange(start, end);
     686          21 :         ShrinkToLigatureBoundaries(&ligatureRange);
     687             : 
     688         105 :         bool drawPartial = (aParams.drawMode & DrawMode::GLYPH_FILL) ||
     689           0 :                            (aParams.drawMode == DrawMode::GLYPH_PATH &&
     690          42 :                             aParams.callbacks);
     691          21 :         gfxPoint origPt = aPt;
     692             : 
     693          21 :         if (drawPartial) {
     694          42 :             DrawPartialLigature(font, Range(start, ligatureRange.start),
     695          21 :                                 &aPt, aParams.provider, params,
     696          42 :                                 iter.GetGlyphRun()->mOrientation);
     697             :         }
     698             : 
     699             :         DrawGlyphs(font, ligatureRange, &aPt,
     700          21 :                    aParams.provider, ligatureRange, params,
     701          42 :                    iter.GetGlyphRun()->mOrientation);
     702             : 
     703          21 :         if (drawPartial) {
     704          42 :             DrawPartialLigature(font, Range(ligatureRange.end, end),
     705          21 :                                 &aPt, aParams.provider, params,
     706          42 :                                 iter.GetGlyphRun()->mOrientation);
     707             :         }
     708             : 
     709          21 :         if (params.isVerticalRun) {
     710           0 :             advance += (aPt.y - origPt.y) * params.direction;
     711             :         } else {
     712          21 :             advance += (aPt.x - origPt.x) * params.direction;
     713             :         }
     714             :     }
     715             : 
     716             :     // composite result when synthetic bolding used
     717          21 :     if (needToRestore) {
     718           0 :         syntheticBoldBuffer.PopAlpha();
     719             :     }
     720             : 
     721          21 :     if (aParams.advanceWidth) {
     722          18 :         *aParams.advanceWidth = advance;
     723             :     }
     724             : }
     725             : 
     726             : // This method is mostly parallel to Draw().
     727             : void
     728           0 : gfxTextRun::DrawEmphasisMarks(gfxContext *aContext, gfxTextRun* aMark,
     729             :                               gfxFloat aMarkAdvance, gfxPoint aPt,
     730             :                               Range aRange, PropertyProvider* aProvider) const
     731             : {
     732           0 :     MOZ_ASSERT(aRange.end <= GetLength());
     733             : 
     734             :     EmphasisMarkDrawParams params;
     735           0 :     params.context = aContext;
     736           0 :     params.mark = aMark;
     737           0 :     params.advance = aMarkAdvance;
     738           0 :     params.direction = GetDirection();
     739           0 :     params.isVertical = IsVertical();
     740             : 
     741           0 :     gfxFloat& inlineCoord = params.isVertical ? aPt.y : aPt.x;
     742           0 :     gfxFloat direction = params.direction;
     743             : 
     744           0 :     GlyphRunIterator iter(this, aRange);
     745           0 :     while (iter.NextRun()) {
     746           0 :         gfxFont* font = iter.GetGlyphRun()->mFont;
     747           0 :         uint32_t start = iter.GetStringStart();
     748           0 :         uint32_t end = iter.GetStringEnd();
     749           0 :         Range ligatureRange(start, end);
     750           0 :         ShrinkToLigatureBoundaries(&ligatureRange);
     751             : 
     752           0 :         inlineCoord += direction * ComputePartialLigatureWidth(
     753           0 :             Range(start, ligatureRange.start), aProvider);
     754             : 
     755           0 :         AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
     756             :         bool haveSpacing = GetAdjustedSpacingArray(
     757           0 :             ligatureRange, aProvider, ligatureRange, &spacingBuffer);
     758           0 :         params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
     759           0 :         font->DrawEmphasisMarks(this, &aPt, ligatureRange.start,
     760           0 :                                 ligatureRange.Length(), params);
     761             : 
     762           0 :         inlineCoord += direction * ComputePartialLigatureWidth(
     763           0 :             Range(ligatureRange.end, end), aProvider);
     764             :     }
     765           0 : }
     766             : 
     767             : void
     768          48 : gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, Range aRange,
     769             :                                     gfxFont::BoundingBoxType aBoundingBoxType,
     770             :                                     DrawTarget* aRefDrawTarget,
     771             :                                     PropertyProvider *aProvider,
     772             :                                     Range aSpacingRange,
     773             :                                     gfx::ShapedTextFlags aOrientation,
     774             :                                     Metrics *aMetrics) const
     775             : {
     776          96 :     AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     777             :     bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
     778          48 :                                                aSpacingRange, &spacingBuffer);
     779             :     Metrics metrics = aFont->Measure(this, aRange.start, aRange.end,
     780             :                                      aBoundingBoxType, aRefDrawTarget,
     781             :                                      haveSpacing ? spacingBuffer.Elements() : nullptr,
     782          48 :                                      aOrientation);
     783          48 :     aMetrics->CombineWith(metrics, IsRightToLeft());
     784          48 : }
     785             : 
     786             : void
     787          96 : gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, Range aRange,
     788             :     gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
     789             :     PropertyProvider *aProvider, gfx::ShapedTextFlags aOrientation,
     790             :     Metrics *aMetrics) const
     791             : {
     792          96 :     if (aRange.start >= aRange.end)
     793          96 :         return;
     794             : 
     795             :     // Measure partial ligature. We hack this by clipping the metrics in the
     796             :     // same way we clip the drawing.
     797           0 :     LigatureData data = ComputeLigatureData(aRange, aProvider);
     798             : 
     799             :     // First measure the complete ligature
     800           0 :     Metrics metrics;
     801             :     AccumulateMetricsForRun(aFont, data.mRange,
     802             :                             aBoundingBoxType, aRefDrawTarget,
     803           0 :                             aProvider, aRange, aOrientation, &metrics);
     804             : 
     805             :     // Clip the bounding box to the ligature part
     806           0 :     gfxFloat bboxLeft = metrics.mBoundingBox.X();
     807           0 :     gfxFloat bboxRight = metrics.mBoundingBox.XMost();
     808             :     // Where we are going to start "drawing" relative to our left baseline origin
     809           0 :     gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
     810           0 :     ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
     811           0 :     metrics.mBoundingBox.x = bboxLeft;
     812           0 :     metrics.mBoundingBox.width = bboxRight - bboxLeft;
     813             : 
     814             :     // mBoundingBox is now relative to the left baseline origin for the entire
     815             :     // ligature. Shift it left.
     816           0 :     metrics.mBoundingBox.x -=
     817           0 :         IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
     818           0 :             : data.mPartAdvance;
     819           0 :     metrics.mAdvanceWidth = data.mPartWidth;
     820             : 
     821           0 :     aMetrics->CombineWith(metrics, IsRightToLeft());
     822             : }
     823             : 
     824             : gfxTextRun::Metrics
     825          48 : gfxTextRun::MeasureText(Range aRange,
     826             :                         gfxFont::BoundingBoxType aBoundingBoxType,
     827             :                         DrawTarget* aRefDrawTarget,
     828             :                         PropertyProvider *aProvider) const
     829             : {
     830          48 :     NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
     831             : 
     832          48 :     Metrics accumulatedMetrics;
     833          48 :     GlyphRunIterator iter(this, aRange);
     834         144 :     while (iter.NextRun()) {
     835          48 :         gfxFont *font = iter.GetGlyphRun()->mFont;
     836          48 :         uint32_t start = iter.GetStringStart();
     837          48 :         uint32_t end = iter.GetStringEnd();
     838          48 :         Range ligatureRange(start, end);
     839          48 :         ShrinkToLigatureBoundaries(&ligatureRange);
     840             : 
     841          96 :         AccumulatePartialLigatureMetrics(
     842             :             font, Range(start, ligatureRange.start),
     843             :             aBoundingBoxType, aRefDrawTarget, aProvider,
     844          96 :             iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
     845             : 
     846             :         // XXX This sucks. We have to get glyph extents just so we can detect
     847             :         // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
     848             :         // even though in almost all cases we could get correct results just
     849             :         // by getting some ascent/descent from the font and using our stored
     850             :         // advance widths.
     851             :         AccumulateMetricsForRun(font,
     852             :             ligatureRange, aBoundingBoxType,
     853             :             aRefDrawTarget, aProvider, ligatureRange,
     854          48 :             iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
     855             : 
     856          96 :         AccumulatePartialLigatureMetrics(
     857             :             font, Range(ligatureRange.end, end),
     858             :             aBoundingBoxType, aRefDrawTarget, aProvider,
     859          96 :             iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
     860             :     }
     861             : 
     862          48 :     return accumulatedMetrics;
     863             : }
     864             : 
     865             : #define MEASUREMENT_BUFFER_SIZE 100
     866             : 
     867             : void
     868           0 : gfxTextRun::ClassifyAutoHyphenations(uint32_t aStart, Range aRange,
     869             :                                      nsTArray<HyphenType>& aHyphenBuffer,
     870             :                                      HyphenationState* aWordState)
     871             : {
     872           0 :   NS_PRECONDITION(aRange.end - aStart <= aHyphenBuffer.Length() &&
     873             :                   aRange.start >= aStart, "Range out of bounds");
     874           0 :   MOZ_ASSERT(aWordState->mostRecentBoundary >= aStart,
     875             :              "Unexpected aMostRecentWordBoundary!!");
     876             : 
     877           0 :   uint32_t start = std::min<uint32_t>(aRange.start, aWordState->mostRecentBoundary);
     878             : 
     879           0 :   for (uint32_t i = start; i < aRange.end; ++i) {
     880           0 :     if (aHyphenBuffer[i - aStart] == HyphenType::Explicit &&
     881           0 :         !aWordState->hasExplicitHyphen) {
     882           0 :       aWordState->hasExplicitHyphen = true;
     883             :     }
     884           0 :     if (!aWordState->hasManualHyphen &&
     885           0 :         (aHyphenBuffer[i - aStart] == HyphenType::Soft ||
     886           0 :          aHyphenBuffer[i - aStart] == HyphenType::Explicit)) {
     887           0 :       aWordState->hasManualHyphen = true;
     888             :       // This is the first manual hyphen in the current word. We can only
     889             :       // know if the current word has a manual hyphen until now. So, we need
     890             :       // to run a sub loop to update the auto hyphens between the start of
     891             :       // the current word and this manual hyphen.
     892           0 :       if (aWordState->hasAutoHyphen) {
     893           0 :         for (uint32_t j = aWordState->mostRecentBoundary; j < i; j++) {
     894           0 :           if (aHyphenBuffer[j - aStart] == HyphenType::AutoWithoutManualInSameWord) {
     895           0 :             aHyphenBuffer[j - aStart] = HyphenType::AutoWithManualInSameWord;
     896             :           }
     897             :         }
     898             :       }
     899             :     }
     900           0 :     if (aHyphenBuffer[i - aStart] == HyphenType::AutoWithoutManualInSameWord) {
     901           0 :       if (!aWordState->hasAutoHyphen) {
     902           0 :         aWordState->hasAutoHyphen = true;
     903             :       }
     904           0 :       if (aWordState->hasManualHyphen) {
     905           0 :         aHyphenBuffer[i - aStart] = HyphenType::AutoWithManualInSameWord;
     906             :       }
     907             :     }
     908             : 
     909             :     // If we're at the word boundary, clear/reset couple states.
     910           0 :     if (mCharacterGlyphs[i].CharIsSpace() ||
     911           0 :         mCharacterGlyphs[i].CharIsTab() ||
     912           0 :         mCharacterGlyphs[i].CharIsNewline() ||
     913             :         // Since we will not have a boundary in the end of the string, let's
     914             :         // call the end of the string a special case for word boundary.
     915           0 :         i == GetLength() - 1) {
     916             :       // We can only get to know whether we should raise/clear an explicit
     917             :       // manual hyphen until we get to the end of a word, because this depends
     918             :       // on whether there exists at least one auto hyphen in the same word.
     919           0 :       if (!aWordState->hasAutoHyphen && aWordState->hasExplicitHyphen) {
     920           0 :         for (uint32_t j = aWordState->mostRecentBoundary; j <= i; j++) {
     921           0 :           if (aHyphenBuffer[j - aStart] == HyphenType::Explicit) {
     922           0 :             aHyphenBuffer[j - aStart] = HyphenType::None;
     923             :           }
     924             :         }
     925             :       }
     926           0 :       aWordState->mostRecentBoundary = i;
     927           0 :       aWordState->hasManualHyphen = false;
     928           0 :       aWordState->hasAutoHyphen = false;
     929           0 :       aWordState->hasExplicitHyphen = false;
     930             :     }
     931             :   }
     932           0 : }
     933             : 
     934             : uint32_t
     935          24 : gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
     936             :                                 bool aLineBreakBefore, gfxFloat aWidth,
     937             :                                 PropertyProvider *aProvider,
     938             :                                 SuppressBreak aSuppressBreak,
     939             :                                 gfxFloat *aTrimWhitespace,
     940             :                                 bool aWhitespaceCanHang,
     941             :                                 Metrics *aMetrics,
     942             :                                 gfxFont::BoundingBoxType aBoundingBoxType,
     943             :                                 DrawTarget* aRefDrawTarget,
     944             :                                 bool *aUsedHyphenation,
     945             :                                 uint32_t *aLastBreak,
     946             :                                 bool aCanWordWrap,
     947             :                                 gfxBreakPriority *aBreakPriority)
     948             : {
     949          24 :     aMaxLength = std::min(aMaxLength, GetLength() - aStart);
     950             : 
     951          24 :     NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
     952             : 
     953             :     Range bufferRange(aStart, aStart +
     954          24 :         std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE));
     955             :     PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
     956          72 :     bool haveSpacing = aProvider &&
     957          72 :         !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING);
     958          24 :     if (haveSpacing) {
     959           0 :         GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
     960             :     }
     961          48 :     AutoTArray<HyphenType, 4096> hyphenBuffer;
     962          24 :     HyphenationState wordState;
     963          24 :     wordState.mostRecentBoundary = aStart;
     964          48 :     bool haveHyphenation = aProvider &&
     965          48 :         (aProvider->GetHyphensOption() == StyleHyphens::Auto ||
     966          72 :          (aProvider->GetHyphensOption() == StyleHyphens::Manual &&
     967          72 :           !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS)));
     968          24 :     if (haveHyphenation) {
     969           0 :         if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
     970           0 :             aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer.Elements());
     971           0 :             if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
     972             :                 ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
     973           0 :                                          &wordState);
     974             :             }
     975             :         } else {
     976           0 :             haveHyphenation = false;
     977             :         }
     978             :     }
     979             : 
     980          24 :     gfxFloat width = 0;
     981          24 :     gfxFloat advance = 0;
     982             :     // The number of space characters that can be trimmed or hang at a soft-wrap
     983          24 :     uint32_t trimmableChars = 0;
     984             :     // The amount of space removed by ignoring trimmableChars
     985          24 :     gfxFloat trimmableAdvance = 0;
     986          24 :     int32_t lastBreak = -1;
     987          24 :     int32_t lastBreakTrimmableChars = -1;
     988          24 :     gfxFloat lastBreakTrimmableAdvance = -1;
     989             :     // Cache the last candidate break
     990          24 :     int32_t lastCandidateBreak = -1;
     991          24 :     int32_t lastCandidateBreakTrimmableChars = -1;
     992          24 :     gfxFloat lastCandidateBreakTrimmableAdvance = -1;
     993          24 :     bool lastCandidateBreakUsedHyphenation = false;
     994          24 :     gfxBreakPriority lastCandidateBreakPriority = gfxBreakPriority::eNoBreak;
     995          24 :     bool aborted = false;
     996          24 :     uint32_t end = aStart + aMaxLength;
     997          24 :     bool lastBreakUsedHyphenation = false;
     998          24 :     Range ligatureRange(aStart, end);
     999          24 :     ShrinkToLigatureBoundaries(&ligatureRange);
    1000             : 
    1001             :     // We may need to move `i` backwards in the following loop, and re-scan
    1002             :     // part of the textrun; we'll use `rescanLimit` so we can tell when that
    1003             :     // is happening: if `i < rescanLimit` then we're rescanning.
    1004          24 :     uint32_t rescanLimit = aStart;
    1005         453 :     for (uint32_t i = aStart; i < end; ++i) {
    1006         429 :         if (i >= bufferRange.end) {
    1007             :             // Fetch more spacing and hyphenation data
    1008           0 :             uint32_t oldHyphenBufferLength = hyphenBuffer.Length();
    1009           0 :             bufferRange.start = i;
    1010           0 :             bufferRange.end = std::min(aStart + aMaxLength,
    1011           0 :                                        i + MEASUREMENT_BUFFER_SIZE);
    1012             :             // For spacing, we always overwrite the old data with the newly
    1013             :             // fetched one. However, for hyphenation, hyphenation data sometimes
    1014             :             // depends on the context in every word (if "hyphens: auto" is set).
    1015             :             // To ensure we get enough information between neighboring buffers,
    1016             :             // we grow the hyphenBuffer instead of overwrite it.
    1017             :             // NOTE that this means bufferRange does not correspond to the
    1018             :             // entire hyphenBuffer, but only to the most recently added portion.
    1019             :             // Therefore, we need to add the old length to hyphenBuffer.Elements()
    1020             :             // when getting more data.
    1021           0 :             if (haveSpacing) {
    1022           0 :                 GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
    1023             :             }
    1024           0 :             if (haveHyphenation) {
    1025           0 :                 if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
    1026           0 :                     aProvider->GetHyphenationBreaks(
    1027           0 :                         bufferRange, hyphenBuffer.Elements() + oldHyphenBufferLength);
    1028           0 :                     if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
    1029           0 :                         uint32_t prevMostRecentWordBoundary = wordState.mostRecentBoundary;
    1030             :                         ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
    1031           0 :                                                  &wordState);
    1032             :                         // If the buffer boundary is in the middle of a word,
    1033             :                         // we need to go back to the start of the current word.
    1034             :                         // So, we can correct the wrong candidates that we set
    1035             :                         // in the previous runs of the loop.
    1036           0 :                         if (prevMostRecentWordBoundary < oldHyphenBufferLength) {
    1037           0 :                             rescanLimit = i;
    1038           0 :                             i = prevMostRecentWordBoundary - 1;
    1039           0 :                             continue;
    1040             :                         }
    1041             :                     }
    1042             :                 } else {
    1043           0 :                     haveHyphenation = false;
    1044             :                 }
    1045             :             }
    1046             :         }
    1047             : 
    1048             :         // There can't be a word-wrap break opportunity at the beginning of the
    1049             :         // line: if the width is too small for even one character to fit, it
    1050             :         // could be the first and last break opportunity on the line, and that
    1051             :         // would trigger an infinite loop.
    1052         429 :         if (aSuppressBreak != eSuppressAllBreaks &&
    1053         429 :             (aSuppressBreak != eSuppressInitialBreak || i > aStart)) {
    1054         405 :             bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
    1055         405 :             bool atHyphenationBreak = !atNaturalBreak && haveHyphenation &&
    1056         405 :                 hyphenBuffer[i - aStart] != HyphenType::None;
    1057         405 :             bool atAutoHyphenWithManualHyphenInSameWord = atHyphenationBreak &&
    1058         405 :                 hyphenBuffer[i - aStart] == HyphenType::AutoWithManualInSameWord;
    1059         405 :             bool atBreak = atNaturalBreak || atHyphenationBreak;
    1060             :             bool wordWrapping =
    1061         405 :                 aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
    1062         405 :                 *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
    1063             : 
    1064         405 :             if (atBreak || wordWrapping) {
    1065           4 :                 gfxFloat hyphenatedAdvance = advance;
    1066           4 :                 if (atHyphenationBreak) {
    1067           0 :                     hyphenatedAdvance += aProvider->GetHyphenWidth();
    1068             :                 }
    1069             : 
    1070           7 :                 if (lastBreak < 0 ||
    1071           3 :                     width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
    1072             :                     // We can break here.
    1073           4 :                     lastBreak = i;
    1074           4 :                     lastBreakTrimmableChars = trimmableChars;
    1075           4 :                     lastBreakTrimmableAdvance = trimmableAdvance;
    1076           4 :                     lastBreakUsedHyphenation = atHyphenationBreak;
    1077           4 :                     *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
    1078             :                                               : gfxBreakPriority::eWordWrapBreak;
    1079             :                 }
    1080             : 
    1081           4 :                 width += advance;
    1082           4 :                 advance = 0;
    1083           4 :                 if (width - trimmableAdvance > aWidth) {
    1084             :                     // No more text fits. Abort
    1085           0 :                     aborted = true;
    1086           0 :                     break;
    1087             :                 }
    1088             :                 // There are various kinds of break opportunities:
    1089             :                 // 1. word wrap break,
    1090             :                 // 2. natural break,
    1091             :                 // 3. manual hyphenation break,
    1092             :                 // 4. auto hyphenation break without any manual hyphenation
    1093             :                 //    in the same word,
    1094             :                 // 5. auto hyphenation break with another manual hyphenation
    1095             :                 //    in the same word.
    1096             :                 // Allow all of them except the last one to be a candidate.
    1097             :                 // So, we can ensure that we don't use an automatic
    1098             :                 // hyphenation opportunity within a word that contains another
    1099             :                 // manual hyphenation, unless it is the only choice.
    1100           8 :                 if (wordWrapping ||
    1101           4 :                     !atAutoHyphenWithManualHyphenInSameWord) {
    1102           4 :                     lastCandidateBreak = lastBreak;
    1103           4 :                     lastCandidateBreakTrimmableChars = lastBreakTrimmableChars;
    1104           4 :                     lastCandidateBreakTrimmableAdvance = lastBreakTrimmableAdvance;
    1105           4 :                     lastCandidateBreakUsedHyphenation = lastBreakUsedHyphenation;
    1106           4 :                     lastCandidateBreakPriority = *aBreakPriority;
    1107             :                 }
    1108             :             }
    1109             :         }
    1110             : 
    1111             :         // If we're re-scanning part of a word (to re-process potential
    1112             :         // hyphenation types) then we don't want to accumulate widths again
    1113             :         // for the characters that were already added to `advance`.
    1114         429 :         if (i < rescanLimit) {
    1115           0 :             continue;
    1116             :         }
    1117             : 
    1118             :         gfxFloat charAdvance;
    1119         429 :         if (i >= ligatureRange.start && i < ligatureRange.end) {
    1120         429 :             charAdvance = GetAdvanceForGlyphs(Range(i, i + 1));
    1121         429 :             if (haveSpacing) {
    1122             :                 PropertyProvider::Spacing *space =
    1123           0 :                     &spacingBuffer[i - bufferRange.start];
    1124           0 :                 charAdvance += space->mBefore + space->mAfter;
    1125         429 :             }
    1126             :         } else {
    1127             :             charAdvance =
    1128           0 :                 ComputePartialLigatureWidth(Range(i, i + 1), aProvider);
    1129             :         }
    1130             : 
    1131         429 :         advance += charAdvance;
    1132         429 :         if (aTrimWhitespace || aWhitespaceCanHang) {
    1133         105 :             if (mCharacterGlyphs[i].CharIsSpace()) {
    1134           7 :                 ++trimmableChars;
    1135           7 :                 trimmableAdvance += charAdvance;
    1136             :             } else {
    1137          98 :                 trimmableAdvance = 0;
    1138          98 :                 trimmableChars = 0;
    1139             :             }
    1140             :         }
    1141             :     }
    1142             : 
    1143          24 :     if (!aborted) {
    1144          24 :         width += advance;
    1145             :     }
    1146             : 
    1147             :     // There are three possibilities:
    1148             :     // 1) all the text fit (width <= aWidth)
    1149             :     // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
    1150             :     // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
    1151             :     uint32_t charsFit;
    1152          24 :     bool usedHyphenation = false;
    1153          24 :     if (width - trimmableAdvance <= aWidth) {
    1154          21 :         charsFit = aMaxLength;
    1155           3 :     } else if (lastBreak >= 0) {
    1156           0 :         if (lastCandidateBreak >= 0 && lastCandidateBreak != lastBreak) {
    1157           0 :             lastBreak = lastCandidateBreak;
    1158           0 :             lastBreakTrimmableChars = lastCandidateBreakTrimmableChars;
    1159           0 :             lastBreakTrimmableAdvance = lastCandidateBreakTrimmableAdvance;
    1160           0 :             lastBreakUsedHyphenation = lastCandidateBreakUsedHyphenation;
    1161           0 :             *aBreakPriority = lastCandidateBreakPriority;
    1162             :         }
    1163           0 :         charsFit = lastBreak - aStart;
    1164           0 :         trimmableChars = lastBreakTrimmableChars;
    1165           0 :         trimmableAdvance = lastBreakTrimmableAdvance;
    1166           0 :         usedHyphenation = lastBreakUsedHyphenation;
    1167             :     } else {
    1168           3 :         charsFit = aMaxLength;
    1169             :     }
    1170             : 
    1171          24 :     if (aMetrics) {
    1172          24 :         auto fitEnd = aStart + charsFit;
    1173             :         // Initially, measure everything, so that our bounding box includes
    1174             :         // any trimmable or hanging whitespace.
    1175             :         *aMetrics = MeasureText(Range(aStart, fitEnd),
    1176             :                                 aBoundingBoxType, aRefDrawTarget,
    1177          24 :                                 aProvider);
    1178          24 :         if (aTrimWhitespace || aWhitespaceCanHang) {
    1179             :             // Measure trailing whitespace that is to be trimmed/hung.
    1180             :             Metrics trimOrHangMetrics =
    1181             :                 MeasureText(Range(fitEnd - trimmableChars, fitEnd),
    1182             :                             aBoundingBoxType, aRefDrawTarget,
    1183           5 :                             aProvider);
    1184           5 :             if (aTrimWhitespace) {
    1185           5 :                 aMetrics->mAdvanceWidth -= trimOrHangMetrics.mAdvanceWidth;
    1186           0 :             } else if (aMetrics->mAdvanceWidth > aWidth) {
    1187             :                 // Restrict width of hanging whitespace so it doesn't overflow.
    1188           0 :                 aMetrics->mAdvanceWidth =
    1189           0 :                     std::max(aWidth, aMetrics->mAdvanceWidth -
    1190           0 :                                      trimOrHangMetrics.mAdvanceWidth);
    1191             :             }
    1192             :         }
    1193             :     }
    1194          24 :     if (aTrimWhitespace) {
    1195           5 :         *aTrimWhitespace = trimmableAdvance;
    1196             :     }
    1197          24 :     if (aUsedHyphenation) {
    1198          24 :         *aUsedHyphenation = usedHyphenation;
    1199             :     }
    1200          24 :     if (aLastBreak && charsFit == aMaxLength) {
    1201          24 :         if (lastBreak < 0) {
    1202          23 :             *aLastBreak = UINT32_MAX;
    1203             :         } else {
    1204           1 :             *aLastBreak = lastBreak - aStart;
    1205             :         }
    1206             :     }
    1207             : 
    1208          48 :     return charsFit;
    1209             : }
    1210             : 
    1211             : gfxFloat
    1212          59 : gfxTextRun::GetAdvanceWidth(Range aRange, PropertyProvider *aProvider,
    1213             :                             PropertyProvider::Spacing* aSpacing) const
    1214             : {
    1215          59 :     NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
    1216             : 
    1217          59 :     Range ligatureRange = aRange;
    1218          59 :     ShrinkToLigatureBoundaries(&ligatureRange);
    1219             : 
    1220             :     gfxFloat result =
    1221         118 :         ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
    1222             :                                     aProvider) +
    1223         118 :         ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
    1224          59 :                                     aProvider);
    1225             : 
    1226          59 :     if (aSpacing) {
    1227           0 :         aSpacing->mBefore = aSpacing->mAfter = 0;
    1228             :     }
    1229             : 
    1230             :     // Account for all remaining spacing here. This is more efficient than
    1231             :     // processing it along with the glyphs.
    1232          59 :     if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
    1233             :         uint32_t i;
    1234           0 :         AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
    1235           0 :         if (spacingBuffer.AppendElements(aRange.Length())) {
    1236           0 :             GetAdjustedSpacing(this, ligatureRange, aProvider,
    1237           0 :                                spacingBuffer.Elements());
    1238           0 :             for (i = 0; i < ligatureRange.Length(); ++i) {
    1239           0 :                 PropertyProvider::Spacing *space = &spacingBuffer[i];
    1240           0 :                 result += space->mBefore + space->mAfter;
    1241             :             }
    1242           0 :             if (aSpacing) {
    1243           0 :                 aSpacing->mBefore = spacingBuffer[0].mBefore;
    1244           0 :                 aSpacing->mAfter = spacingBuffer.LastElement().mAfter;
    1245             :             }
    1246             :         }
    1247             :     }
    1248             : 
    1249          59 :     return result + GetAdvanceForGlyphs(ligatureRange);
    1250             : }
    1251             : 
    1252             : bool
    1253          24 : gfxTextRun::SetLineBreaks(Range aRange,
    1254             :                           bool aLineBreakBefore, bool aLineBreakAfter,
    1255             :                           gfxFloat *aAdvanceWidthDelta)
    1256             : {
    1257             :     // Do nothing because our shaping does not currently take linebreaks into
    1258             :     // account. There is no change in advance width.
    1259          24 :     if (aAdvanceWidthDelta) {
    1260          24 :         *aAdvanceWidthDelta = 0;
    1261             :     }
    1262          24 :     return false;
    1263             : }
    1264             : 
    1265             : uint32_t
    1266          77 : gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const
    1267             : {
    1268          77 :     NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
    1269          77 :     NS_ASSERTION(GetLength() == 0 ||
    1270             :                  (!mHasGlyphRunArray && mSingleGlyphRun.mFont) ||
    1271             :                  (mHasGlyphRunArray && mGlyphRunArray.Length() > 0),
    1272             :                  "non-empty text but no glyph runs present!");
    1273          77 :     if (!mHasGlyphRunArray) {
    1274          77 :         return 0;
    1275             :     }
    1276           0 :     if (aOffset == GetLength()) {
    1277           0 :         return mGlyphRunArray.Length();
    1278             :     }
    1279           0 :     uint32_t start = 0;
    1280           0 :     uint32_t end = mGlyphRunArray.Length();
    1281           0 :     while (end - start > 1) {
    1282           0 :         uint32_t mid = (start + end)/2;
    1283           0 :         if (mGlyphRunArray[mid].mCharacterOffset <= aOffset) {
    1284           0 :             start = mid;
    1285             :         } else {
    1286           0 :             end = mid;
    1287             :         }
    1288             :     }
    1289           0 :     NS_ASSERTION(mGlyphRunArray[start].mCharacterOffset <= aOffset,
    1290             :                  "Hmm, something went wrong, aOffset should have been found");
    1291           0 :     return start;
    1292             : }
    1293             : 
    1294             : nsresult
    1295         129 : gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
    1296             :                         uint32_t aUTF16Offset, bool aForceNewRun,
    1297             :                         gfx::ShapedTextFlags aOrientation)
    1298             : {
    1299         129 :     NS_ASSERTION(aFont, "adding glyph run for null font!");
    1300         129 :     NS_ASSERTION(aOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
    1301             :                  "mixed orientation should have been resolved");
    1302         129 :     if (!aFont) {
    1303           0 :         return NS_OK;
    1304             :     }
    1305         129 :     if (!mHasGlyphRunArray) {
    1306             :         // We don't currently have an array.
    1307         129 :         if (!mSingleGlyphRun.mFont) {
    1308             :             // This is the first glyph run: just store it directly.
    1309          71 :             mSingleGlyphRun.mFont = aFont;
    1310          71 :             mSingleGlyphRun.mMatchType = aMatchType;
    1311          71 :             mSingleGlyphRun.mOrientation = aOrientation;
    1312          71 :             mSingleGlyphRun.mCharacterOffset = aUTF16Offset;
    1313          71 :             return NS_OK;
    1314             :         }
    1315             :     }
    1316          58 :     uint32_t numGlyphRuns = mHasGlyphRunArray ? mGlyphRunArray.Length() : 1;
    1317          58 :     if (!aForceNewRun && numGlyphRuns > 0) {
    1318             :         GlyphRun* lastGlyphRun =
    1319          58 :             mHasGlyphRunArray ? &mGlyphRunArray[numGlyphRuns - 1]
    1320          58 :                               : &mSingleGlyphRun;
    1321             : 
    1322          58 :         NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
    1323             :                      "Glyph runs out of order (and run not forced)");
    1324             : 
    1325             :         // Don't append a run if the font is already the one we want
    1326         174 :         if (lastGlyphRun->mFont == aFont &&
    1327         116 :             lastGlyphRun->mMatchType == aMatchType &&
    1328          58 :             lastGlyphRun->mOrientation == aOrientation)
    1329             :         {
    1330          58 :             return NS_OK;
    1331             :         }
    1332             : 
    1333             :         // If the offset has not changed, avoid leaving a zero-length run
    1334             :         // by overwriting the last entry instead of appending...
    1335           0 :         if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
    1336             : 
    1337             :             // ...except that if the run before the last entry had the same
    1338             :             // font as the new one wants, merge with it instead of creating
    1339             :             // adjacent runs with the same font
    1340           0 :             if (numGlyphRuns > 1 &&
    1341           0 :                 mGlyphRunArray[numGlyphRuns - 2].mFont == aFont &&
    1342           0 :                 mGlyphRunArray[numGlyphRuns - 2].mMatchType == aMatchType &&
    1343           0 :                 mGlyphRunArray[numGlyphRuns - 2].mOrientation == aOrientation)
    1344             :             {
    1345           0 :                 mGlyphRunArray.TruncateLength(numGlyphRuns - 1);
    1346           0 :                 if (mGlyphRunArray.Length() == 1) {
    1347           0 :                     ConvertFromGlyphRunArray();
    1348             :                 }
    1349           0 :                 return NS_OK;
    1350             :             }
    1351             : 
    1352           0 :             lastGlyphRun->mFont = aFont;
    1353           0 :             lastGlyphRun->mMatchType = aMatchType;
    1354           0 :             lastGlyphRun->mOrientation = aOrientation;
    1355           0 :             return NS_OK;
    1356             :         }
    1357             :     }
    1358             : 
    1359           0 :     NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
    1360             :                  "First run doesn't cover the first character (and run not forced)?");
    1361             : 
    1362           0 :     if (!mHasGlyphRunArray) {
    1363           0 :         ConvertToGlyphRunArray();
    1364             :     }
    1365             : 
    1366           0 :     GlyphRun* glyphRun = mGlyphRunArray.AppendElement();
    1367           0 :     if (!glyphRun) {
    1368           0 :         if (mGlyphRunArray.Length() == 1) {
    1369           0 :             ConvertFromGlyphRunArray();
    1370             :         }
    1371           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1372             :     }
    1373           0 :     glyphRun->mFont = aFont;
    1374           0 :     glyphRun->mCharacterOffset = aUTF16Offset;
    1375           0 :     glyphRun->mMatchType = aMatchType;
    1376           0 :     glyphRun->mOrientation = aOrientation;
    1377             : 
    1378           0 :     return NS_OK;
    1379             : }
    1380             : 
    1381             : void
    1382          71 : gfxTextRun::SortGlyphRuns()
    1383             : {
    1384          71 :     if (!mHasGlyphRunArray) {
    1385          71 :         return;
    1386             :     }
    1387             : 
    1388             :     // We should never have an empty or one-element array here; if there's only
    1389             :     // one glyphrun, it should be stored directly in the textrun without using
    1390             :     // an array at all.
    1391           0 :     MOZ_ASSERT(mGlyphRunArray.Length() > 1);
    1392             : 
    1393           0 :     AutoTArray<GlyphRun,16> runs(Move(mGlyphRunArray));
    1394             :     GlyphRunOffsetComparator comp;
    1395           0 :     runs.Sort(comp);
    1396             : 
    1397             :     // Now copy back, coalescing adjacent glyph runs that have the same font
    1398           0 :     mGlyphRunArray.Clear();
    1399           0 :     gfxFont* prevFont = nullptr;
    1400           0 :     gfx::ShapedTextFlags prevOrient = gfx::ShapedTextFlags();
    1401           0 :     DebugOnly<uint32_t> prevOffset = 0;
    1402           0 :     for (auto& run : runs) {
    1403             :         // a GlyphRun with the same font and orientation as the previous can
    1404             :         // just be skipped; the last GlyphRun will cover its character range.
    1405           0 :         MOZ_ASSERT(run.mFont != nullptr);
    1406           0 :         if (prevFont == nullptr ||
    1407           0 :             run.mFont != prevFont || run.mOrientation != prevOrient) {
    1408             :             // If two fonts have the same character offset, Sort() will have
    1409             :             // randomized the order.
    1410           0 :             MOZ_ASSERT(prevFont == nullptr ||
    1411             :                        run.mCharacterOffset != prevOffset,
    1412             :                        "Two fonts for the same run, glyph indices unreliable");
    1413           0 :             prevFont = run.mFont;
    1414           0 :             prevOrient = run.mOrientation;
    1415             : #ifdef DEBUG
    1416           0 :             prevOffset = run.mCharacterOffset;
    1417             : #endif
    1418           0 :             if (!mGlyphRunArray.AppendElement(Move(run))) {
    1419           0 :                 NS_WARNING("Failed to append glyph run!");
    1420             :             }
    1421             :         }
    1422             :     }
    1423             : 
    1424           0 :     MOZ_ASSERT(mGlyphRunArray.Length() > 0);
    1425           0 :     if (mGlyphRunArray.Length() == 1) {
    1426           0 :         ConvertFromGlyphRunArray();
    1427             :     }
    1428             : }
    1429             : 
    1430             : // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
    1431             : // therefore we only call it once, at the end of textrun construction,
    1432             : // NOT incrementally as each glyph run is added (bug 680402).
    1433             : void
    1434          71 : gfxTextRun::SanitizeGlyphRuns()
    1435             : {
    1436          71 :     if (!mHasGlyphRunArray) {
    1437          71 :         return;
    1438             :     }
    1439             : 
    1440           0 :     MOZ_ASSERT(mGlyphRunArray.Length() > 1);
    1441             : 
    1442             :     // If any glyph run starts with ligature-continuation characters, we need to advance it
    1443             :     // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
    1444             :     // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
    1445             :     // it appear as if a ligature has been formed)
    1446           0 :     int32_t i, lastRunIndex = mGlyphRunArray.Length() - 1;
    1447           0 :     const CompressedGlyph *charGlyphs = mCharacterGlyphs;
    1448           0 :     for (i = lastRunIndex; i >= 0; --i) {
    1449           0 :         GlyphRun& run = mGlyphRunArray[i];
    1450           0 :         while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
    1451           0 :                run.mCharacterOffset < GetLength()) {
    1452           0 :             run.mCharacterOffset++;
    1453             :         }
    1454             :         // if the run has become empty, eliminate it
    1455           0 :         if ((i < lastRunIndex &&
    1456           0 :              run.mCharacterOffset >= mGlyphRunArray[i+1].mCharacterOffset) ||
    1457           0 :             (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
    1458           0 :             mGlyphRunArray.RemoveElementAt(i);
    1459           0 :             --lastRunIndex;
    1460             :         }
    1461             :     }
    1462             : 
    1463           0 :     MOZ_ASSERT(mGlyphRunArray.Length() > 0);
    1464           0 :     if (mGlyphRunArray.Length() == 1) {
    1465           0 :         ConvertFromGlyphRunArray();
    1466             :     }
    1467             : }
    1468             : 
    1469             : uint32_t
    1470           0 : gfxTextRun::CountMissingGlyphs() const
    1471             : {
    1472             :     uint32_t i;
    1473           0 :     uint32_t count = 0;
    1474           0 :     for (i = 0; i < GetLength(); ++i) {
    1475           0 :         if (mCharacterGlyphs[i].IsMissing()) {
    1476           0 :             ++count;
    1477             :         }
    1478             :     }
    1479           0 :     return count;
    1480             : }
    1481             : 
    1482             : void
    1483         127 : gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
    1484             : {
    1485         127 :     uint32_t wordLen = aShapedWord->GetLength();
    1486         127 :     NS_ASSERTION(aOffset + wordLen <= GetLength(),
    1487             :                  "word overruns end of textrun!");
    1488             : 
    1489         127 :     CompressedGlyph *charGlyphs = GetCharacterGlyphs();
    1490         127 :     const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
    1491         127 :     if (aShapedWord->HasDetailedGlyphs()) {
    1492           0 :         for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
    1493           0 :             const CompressedGlyph& g = wordGlyphs[i];
    1494           0 :             if (g.IsSimpleGlyph()) {
    1495           0 :                 charGlyphs[aOffset] = g;
    1496             :             } else {
    1497             :                 const DetailedGlyph *details =
    1498           0 :                     g.GetGlyphCount() > 0 ?
    1499           0 :                         aShapedWord->GetDetailedGlyphs(i) : nullptr;
    1500           0 :                 SetGlyphs(aOffset, g, details);
    1501             :             }
    1502             :         }
    1503             :     } else {
    1504         127 :         memcpy(charGlyphs + aOffset, wordGlyphs,
    1505         127 :                wordLen * sizeof(CompressedGlyph));
    1506             :     }
    1507         127 : }
    1508             : 
    1509             : void
    1510           0 : gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, Range aRange, uint32_t aDest)
    1511             : {
    1512           0 :     NS_ASSERTION(aRange.end <= aSource->GetLength(),
    1513             :                  "Source substring out of range");
    1514           0 :     NS_ASSERTION(aDest + aRange.Length() <= GetLength(),
    1515             :                  "Destination substring out of range");
    1516             : 
    1517           0 :     if (aSource->mSkipDrawing) {
    1518           0 :         mSkipDrawing = true;
    1519             :     }
    1520             : 
    1521             :     // Copy base glyph data, and DetailedGlyph data where present
    1522           0 :     const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aRange.start;
    1523           0 :     CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
    1524           0 :     for (uint32_t i = 0; i < aRange.Length(); ++i) {
    1525           0 :         CompressedGlyph g = srcGlyphs[i];
    1526           0 :         g.SetCanBreakBefore(!g.IsClusterStart() ?
    1527             :             CompressedGlyph::FLAG_BREAK_TYPE_NONE :
    1528           0 :             dstGlyphs[i].CanBreakBefore());
    1529           0 :         if (!g.IsSimpleGlyph()) {
    1530           0 :             uint32_t count = g.GetGlyphCount();
    1531           0 :             if (count > 0) {
    1532           0 :                 DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
    1533           0 :                 if (dst) {
    1534             :                     DetailedGlyph *src =
    1535           0 :                         aSource->GetDetailedGlyphs(i + aRange.start);
    1536           0 :                     if (src) {
    1537           0 :                         ::memcpy(dst, src, count * sizeof(DetailedGlyph));
    1538             :                     } else {
    1539           0 :                         g.SetMissing(0);
    1540             :                     }
    1541             :                 } else {
    1542           0 :                     g.SetMissing(0);
    1543             :                 }
    1544             :             }
    1545             :         }
    1546           0 :         dstGlyphs[i] = g;
    1547             :     }
    1548             : 
    1549             :     // Copy glyph runs
    1550           0 :     GlyphRunIterator iter(aSource, aRange);
    1551             : #ifdef DEBUG
    1552           0 :     const GlyphRun *prevRun = nullptr;
    1553             : #endif
    1554           0 :     while (iter.NextRun()) {
    1555           0 :         gfxFont *font = iter.GetGlyphRun()->mFont;
    1556           0 :         NS_ASSERTION(!prevRun || prevRun->mFont != iter.GetGlyphRun()->mFont ||
    1557             :                      prevRun->mMatchType != iter.GetGlyphRun()->mMatchType ||
    1558             :                      prevRun->mOrientation != iter.GetGlyphRun()->mOrientation,
    1559             :                      "Glyphruns not coalesced?");
    1560             : #ifdef DEBUG
    1561           0 :         prevRun = iter.GetGlyphRun();
    1562           0 :         uint32_t end = iter.GetStringEnd();
    1563             : #endif
    1564           0 :         uint32_t start = iter.GetStringStart();
    1565             : 
    1566             :         // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
    1567             :         // Although it's unusual (and not desirable), it's possible for us to assign
    1568             :         // different fonts to a base character and a following diacritic.
    1569             :         // Example on OSX 10.5/10.6 with default fonts installed:
    1570             :         //     data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
    1571             :         //                    &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
    1572             :         // This means the rendering of the cluster will probably not be very good,
    1573             :         // but it's the best we can do for now if the specified font only covered the
    1574             :         // initial base character and not its applied marks.
    1575           0 :         NS_WARNING_ASSERTION(
    1576             :           aSource->IsClusterStart(start),
    1577             :           "Started font run in the middle of a cluster");
    1578           0 :         NS_WARNING_ASSERTION(
    1579             :           end == aSource->GetLength() || aSource->IsClusterStart(end),
    1580             :           "Ended font run in the middle of a cluster");
    1581             : 
    1582           0 :         nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
    1583           0 :                                   start - aRange.start + aDest, false,
    1584           0 :                                   iter.GetGlyphRun()->mOrientation);
    1585           0 :         if (NS_FAILED(rv))
    1586           0 :             return;
    1587             :     }
    1588             : }
    1589             : 
    1590             : void
    1591           0 : gfxTextRun::ClearGlyphsAndCharacters()
    1592             : {
    1593           0 :     ResetGlyphRuns();
    1594           0 :     memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
    1595           0 :            mLength * sizeof(CompressedGlyph));
    1596           0 :     mDetailedGlyphs = nullptr;
    1597           0 : }
    1598             : 
    1599             : void
    1600           0 : gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
    1601             :                           uint32_t aCharIndex,
    1602             :                           gfx::ShapedTextFlags aOrientation)
    1603             : {
    1604           0 :     if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
    1605           0 :         return;
    1606             :     }
    1607             : 
    1608           0 :     aFont->InitWordCache();
    1609             :     static const uint8_t space = ' ';
    1610             :     gfx::ShapedTextFlags
    1611           0 :         flags = gfx::ShapedTextFlags::TEXT_IS_8BIT |
    1612           0 :                 aOrientation;
    1613             :     bool vertical =
    1614           0 :         !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT);
    1615             :     gfxFontShaper::RoundingFlags roundingFlags =
    1616           0 :         aFont->GetRoundOffsetsToPixels(aDrawTarget);
    1617           0 :     gfxShapedWord* sw = aFont->GetShapedWord(aDrawTarget,
    1618             :                                              &space, 1,
    1619             :                                              gfxShapedWord::HashMix(0, ' '),
    1620             :                                              Script::LATIN,
    1621             :                                              vertical,
    1622           0 :                                              mAppUnitsPerDevUnit,
    1623             :                                              flags,
    1624             :                                              roundingFlags,
    1625           0 :                                              nullptr);
    1626           0 :     if (sw) {
    1627             :         AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
    1628           0 :                     aOrientation);
    1629           0 :         CopyGlyphDataFrom(sw, aCharIndex);
    1630             :     }
    1631             : }
    1632             : 
    1633             : bool
    1634          58 : gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
    1635             :                                   char16_t aSpaceChar,
    1636             :                                   gfx::ShapedTextFlags aOrientation)
    1637             : {
    1638          58 :     uint32_t spaceGlyph = aFont->GetSpaceGlyph();
    1639          58 :     if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
    1640           0 :         return false;
    1641             :     }
    1642             : 
    1643             :     gfxFont::Orientation fontOrientation =
    1644         174 :         (aOrientation & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) ?
    1645         116 :             gfxFont::eVertical : gfxFont::eHorizontal;
    1646             :     uint32_t spaceWidthAppUnits =
    1647         116 :         NS_lroundf(aFont->GetMetrics(fontOrientation).spaceWidth *
    1648         116 :                    mAppUnitsPerDevUnit);
    1649          58 :     if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
    1650           0 :         return false;
    1651             :     }
    1652             : 
    1653             :     AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
    1654          58 :                 aOrientation);
    1655          58 :     CompressedGlyph g;
    1656          58 :     g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
    1657          58 :     if (aSpaceChar == ' ') {
    1658          58 :         g.SetIsSpace();
    1659             :     }
    1660          58 :     GetCharacterGlyphs()[aCharIndex] = g;
    1661          58 :     return true;
    1662             : }
    1663             : 
    1664             : void
    1665          71 : gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget)
    1666             : {
    1667          71 :     bool needsGlyphExtents = NeedsGlyphExtents(this);
    1668          71 :     if (!needsGlyphExtents && !mDetailedGlyphs)
    1669          62 :         return;
    1670             : 
    1671             :     uint32_t runCount;
    1672           9 :     const GlyphRun* glyphRuns = GetGlyphRuns(&runCount);
    1673           9 :     CompressedGlyph *charGlyphs = mCharacterGlyphs;
    1674          18 :     for (uint32_t i = 0; i < runCount; ++i) {
    1675           9 :         const GlyphRun& run = glyphRuns[i];
    1676           9 :         gfxFont *font = run.mFont;
    1677          18 :         if (MOZ_UNLIKELY(font->GetStyle()->size == 0) ||
    1678           9 :             MOZ_UNLIKELY(font->GetStyle()->sizeAdjust == 0.0f)) {
    1679           0 :             continue;
    1680             :         }
    1681             : 
    1682           9 :         uint32_t start = run.mCharacterOffset;
    1683          18 :         uint32_t end = i + 1 < runCount ?
    1684          18 :             glyphRuns[i + 1].mCharacterOffset : GetLength();
    1685           9 :         bool fontIsSetup = false;
    1686             :         uint32_t j;
    1687           9 :         gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
    1688             : 
    1689         211 :         for (j = start; j < end; ++j) {
    1690         202 :             const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
    1691         202 :             if (glyphData->IsSimpleGlyph()) {
    1692             :                 // If we're in speed mode, don't set up glyph extents here; we'll
    1693             :                 // just return "optimistic" glyph bounds later
    1694         202 :                 if (needsGlyphExtents) {
    1695         202 :                     uint32_t glyphIndex = glyphData->GetSimpleGlyph();
    1696         202 :                     if (!extents->IsGlyphKnown(glyphIndex)) {
    1697          52 :                         if (!fontIsSetup) {
    1698           4 :                             if (!font->SetupCairoFont(aRefDrawTarget)) {
    1699           0 :                                 NS_WARNING("failed to set up font for glyph extents");
    1700           0 :                                 break;
    1701             :                             }
    1702           4 :                             fontIsSetup = true;
    1703             :                         }
    1704             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    1705             :                         ++gGlyphExtentsSetupEagerSimple;
    1706             : #endif
    1707             :                         font->SetupGlyphExtents(aRefDrawTarget,
    1708          52 :                                                 glyphIndex, false, extents);
    1709             :                     }
    1710             :                 }
    1711           0 :             } else if (!glyphData->IsMissing()) {
    1712           0 :                 uint32_t glyphCount = glyphData->GetGlyphCount();
    1713           0 :                 if (glyphCount == 0) {
    1714           0 :                     continue;
    1715             :                 }
    1716           0 :                 const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
    1717           0 :                 if (!details) {
    1718           0 :                     continue;
    1719             :                 }
    1720           0 :                 for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
    1721           0 :                     uint32_t glyphIndex = details->mGlyphID;
    1722           0 :                     if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
    1723           0 :                         if (!fontIsSetup) {
    1724           0 :                             if (!font->SetupCairoFont(aRefDrawTarget)) {
    1725           0 :                                 NS_WARNING("failed to set up font for glyph extents");
    1726           0 :                                 break;
    1727             :                             }
    1728           0 :                             fontIsSetup = true;
    1729             :                         }
    1730             : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    1731             :                         ++gGlyphExtentsSetupEagerTight;
    1732             : #endif
    1733             :                         font->SetupGlyphExtents(aRefDrawTarget,
    1734           0 :                                                 glyphIndex, true, extents);
    1735             :                     }
    1736             :                 }
    1737             :             }
    1738             :         }
    1739             :     }
    1740             : }
    1741             : 
    1742             : 
    1743             : size_t
    1744           0 : gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
    1745             : {
    1746             :     // The second arg is how much gfxTextRun::AllocateStorage would have
    1747             :     // allocated.
    1748           0 :     size_t total = mHasGlyphRunArray
    1749           0 :         ? mGlyphRunArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
    1750           0 :         : 0;
    1751             : 
    1752           0 :     if (mDetailedGlyphs) {
    1753           0 :         total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
    1754             :     }
    1755             : 
    1756           0 :     return total;
    1757             : }
    1758             : 
    1759             : size_t
    1760           0 : gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
    1761             : {
    1762           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    1763             : }
    1764             : 
    1765             : 
    1766             : #ifdef DEBUG
    1767             : void
    1768           0 : gfxTextRun::Dump(FILE* aOutput) {
    1769           0 :     if (!aOutput) {
    1770           0 :         aOutput = stdout;
    1771             :     }
    1772             : 
    1773           0 :     fputc('[', aOutput);
    1774             :     uint32_t numGlyphRuns;
    1775           0 :     const GlyphRun* glyphRuns = GetGlyphRuns(&numGlyphRuns);
    1776           0 :     for (uint32_t i = 0; i < numGlyphRuns; ++i) {
    1777           0 :         if (i > 0) {
    1778           0 :             fputc(',', aOutput);
    1779             :         }
    1780           0 :         gfxFont* font = glyphRuns[i].mFont;
    1781           0 :         const gfxFontStyle* style = font->GetStyle();
    1782           0 :         NS_ConvertUTF16toUTF8 fontName(font->GetName());
    1783           0 :         nsAutoCString lang;
    1784           0 :         style->language->ToUTF8String(lang);
    1785           0 :         fprintf(aOutput, "%d: %s %f/%d/%d/%s", glyphRuns[i].mCharacterOffset,
    1786           0 :                 fontName.get(), style->size,
    1787           0 :                 style->weight, style->style, lang.get());
    1788             :     }
    1789           0 :     fputc(']', aOutput);
    1790           0 : }
    1791             : #endif
    1792             : 
    1793           6 : gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
    1794             :                            const gfxFontStyle *aStyle,
    1795             :                            gfxTextPerfMetrics* aTextPerf,
    1796             :                            gfxUserFontSet *aUserFontSet,
    1797           6 :                            gfxFloat aDevToCssSize)
    1798             :     : mFamilyList(aFontFamilyList)
    1799             :     , mStyle(*aStyle)
    1800             :     , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
    1801             :     , mHyphenWidth(-1)
    1802             :     , mDevToCssSize(aDevToCssSize)
    1803             :     , mUserFontSet(aUserFontSet)
    1804             :     , mTextPerf(aTextPerf)
    1805             :     , mLastPrefLang(eFontPrefLang_Western)
    1806           6 :     , mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language))
    1807             :     , mLastPrefFirstFont(false)
    1808          12 :     , mSkipDrawing(false)
    1809             : {
    1810             :     // We don't use SetUserFontSet() here, as we want to unconditionally call
    1811             :     // BuildFontList() rather than only do UpdateUserFonts() if it changed.
    1812           6 :     mCurrGeneration = GetGeneration();
    1813           6 :     BuildFontList();
    1814           6 : }
    1815             : 
    1816           3 : gfxFontGroup::~gfxFontGroup()
    1817             : {
    1818             :     // Should not be dropped by stylo
    1819           1 :     MOZ_ASSERT(NS_IsMainThread());
    1820           3 : }
    1821             : 
    1822             : void
    1823           6 : gfxFontGroup::BuildFontList()
    1824             : {
    1825             :     // initialize fonts in the font family list
    1826          12 :     AutoTArray<gfxFontFamily*,10> fonts;
    1827           6 :     gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
    1828             : 
    1829             :     // lookup fonts in the fontlist
    1830          12 :     for (const FontFamilyName& name : mFamilyList.GetFontlist()) {
    1831           6 :         if (name.IsNamed()) {
    1832           1 :             AddPlatformFont(name.mName, fonts);
    1833             :         } else {
    1834           5 :             pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
    1835           5 :             if (mTextPerf) {
    1836           0 :                 mTextPerf->current.genericLookups++;
    1837             :             }
    1838             :         }
    1839             :     }
    1840             : 
    1841             :     // if necessary, append default generic onto the end
    1842           6 :     if (mFamilyList.GetDefaultFontType() != eFamily_none &&
    1843           0 :         !mFamilyList.HasDefaultGeneric()) {
    1844           0 :         pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(),
    1845           0 :                              mStyle.language, fonts);
    1846           0 :         if (mTextPerf) {
    1847           0 :             mTextPerf->current.genericLookups++;
    1848             :         }
    1849             :     }
    1850             : 
    1851             :     // build the fontlist from the specified families
    1852          22 :     for (gfxFontFamily* fontFamily : fonts) {
    1853          16 :         AddFamilyToFontList(fontFamily);
    1854             :     }
    1855           6 : }
    1856             : 
    1857             : void
    1858           1 : gfxFontGroup::AddPlatformFont(const nsAString& aName,
    1859             :                               nsTArray<gfxFontFamily*>& aFamilyList)
    1860             : {
    1861             :     // First, look up in the user font set...
    1862             :     // If the fontSet matches the family, we must not look for a platform
    1863             :     // font of the same name, even if we fail to actually get a fontEntry
    1864             :     // here; we'll fall back to the next name in the CSS font-family list.
    1865           1 :     if (mUserFontSet) {
    1866             :         // Add userfonts to the fontlist whether already loaded
    1867             :         // or not. Loading is initiated during font matching.
    1868           0 :         gfxFontFamily* family = mUserFontSet->LookupFamily(aName);
    1869           0 :         if (family) {
    1870           0 :             aFamilyList.AppendElement(family);
    1871           0 :             return;
    1872             :         }
    1873             :     }
    1874             : 
    1875             :     // Not known in the user font set ==> check system fonts
    1876           1 :     gfxPlatformFontList::PlatformFontList()
    1877           1 :         ->FindAndAddFamilies(aName, &aFamilyList, &mStyle, mDevToCssSize);
    1878             : }
    1879             : 
    1880             : void
    1881          16 : gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily)
    1882             : {
    1883          16 :     NS_ASSERTION(aFamily, "trying to add a null font family to fontlist");
    1884          32 :     AutoTArray<gfxFontEntry*,4> fontEntryList;
    1885             :     bool needsBold;
    1886          16 :     aFamily->FindAllFontsForStyle(mStyle, fontEntryList, needsBold);
    1887             :     // add these to the fontlist
    1888          37 :     for (gfxFontEntry* fe : fontEntryList) {
    1889          21 :         if (!HasFont(fe)) {
    1890          42 :             FamilyFace ff(aFamily, fe, needsBold);
    1891          21 :             if (fe->mIsUserFontContainer) {
    1892           0 :                 ff.CheckState(mSkipDrawing);
    1893             :             }
    1894          21 :             mFonts.AppendElement(ff);
    1895             :         }
    1896             :     }
    1897             :     // for a family marked as "check fallback faces", only mark the last
    1898             :     // entry so that fallbacks for a family are only checked once
    1899          37 :     if (aFamily->CheckForFallbackFaces() &&
    1900          21 :         !fontEntryList.IsEmpty() && !mFonts.IsEmpty()) {
    1901           5 :         mFonts.LastElement().SetCheckForFallbackFaces();
    1902             :     }
    1903          16 : }
    1904             : 
    1905             : bool
    1906          21 : gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
    1907             : {
    1908          21 :     uint32_t count = mFonts.Length();
    1909          51 :     for (uint32_t i = 0; i < count; ++i) {
    1910          30 :         if (mFonts[i].FontEntry() == aFontEntry) {
    1911           0 :             return true;
    1912             :         }
    1913             :     }
    1914          21 :     return false;
    1915             : }
    1916             : 
    1917             : gfxFont*
    1918         866 : gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh)
    1919             : {
    1920         866 :     if (uint32_t(i) >= mFonts.Length()) {
    1921           0 :         return nullptr;
    1922             :     }
    1923             : 
    1924         866 :     FamilyFace& ff = mFonts[i];
    1925         866 :     if (ff.IsInvalid() || ff.IsLoading()) {
    1926           0 :         return nullptr;
    1927             :     }
    1928             : 
    1929         866 :     gfxFont* font = ff.Font();
    1930         866 :     if (!font) {
    1931           6 :         gfxFontEntry* fe = mFonts[i].FontEntry();
    1932           6 :         gfxCharacterMap* unicodeRangeMap = nullptr;
    1933           6 :         if (fe->mIsUserFontContainer) {
    1934           0 :             gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
    1935           0 :             if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
    1936           0 :                 ufe->CharacterInUnicodeRange(aCh) &&
    1937           0 :                 !FontLoadingForFamily(ff.Family(), aCh)) {
    1938           0 :                 ufe->Load();
    1939           0 :                 ff.CheckState(mSkipDrawing);
    1940             :             }
    1941           0 :             fe = ufe->GetPlatformFontEntry();
    1942           0 :             if (!fe) {
    1943           0 :                 return nullptr;
    1944             :             }
    1945           0 :             unicodeRangeMap = ufe->GetUnicodeRangeMap();
    1946             :         }
    1947           6 :         font = fe->FindOrMakeFont(&mStyle, mFonts[i].NeedsBold(),
    1948           6 :                                   unicodeRangeMap);
    1949           6 :         if (!font || !font->Valid()) {
    1950           0 :             ff.SetInvalid();
    1951             :             // We can't just |delete font| here, in case there are other
    1952             :             // references to the object FindOrMakeFont returned.
    1953           0 :             RefPtr<gfxFont> ref(font);
    1954           0 :             return nullptr;
    1955             :         }
    1956           6 :         mFonts[i].SetFont(font);
    1957             :     }
    1958         866 :     return font;
    1959             : }
    1960             : 
    1961             : void
    1962           0 : gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing)
    1963             : {
    1964           0 :     gfxFontEntry* fe = FontEntry();
    1965           0 :     if (fe->mIsUserFontContainer) {
    1966           0 :         gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
    1967           0 :         gfxUserFontEntry::UserFontLoadState state = ufe->LoadState();
    1968           0 :         switch (state) {
    1969             :             case gfxUserFontEntry::STATUS_LOAD_PENDING:
    1970             :             case gfxUserFontEntry::STATUS_LOADING:
    1971           0 :                 SetLoading(true);
    1972           0 :                 break;
    1973             :             case gfxUserFontEntry::STATUS_FAILED:
    1974           0 :                 SetInvalid();
    1975             :                 // fall-thru to the default case
    1976             :                 MOZ_FALLTHROUGH;
    1977             :             default:
    1978           0 :                 SetLoading(false);
    1979             :         }
    1980           0 :         if (ufe->WaitForUserFont()) {
    1981           0 :             aSkipDrawing = true;
    1982             :         }
    1983             :     }
    1984           0 : }
    1985             : 
    1986             : bool
    1987           0 : gfxFontGroup::FamilyFace::EqualsUserFont(const gfxUserFontEntry* aUserFont) const
    1988             : {
    1989           0 :     gfxFontEntry* fe = FontEntry();
    1990             :     // if there's a font, the entry is the underlying platform font
    1991           0 :     if (mFontCreated) {
    1992           0 :         gfxFontEntry* pfe = aUserFont->GetPlatformFontEntry();
    1993           0 :         if (pfe == fe) {
    1994           0 :             return true;
    1995             :         }
    1996           0 :     } else if (fe == aUserFont) {
    1997           0 :         return true;
    1998             :     }
    1999           0 :     return false;
    2000             : }
    2001             : 
    2002             : bool
    2003           0 : gfxFontGroup::FontLoadingForFamily(gfxFontFamily* aFamily, uint32_t aCh) const
    2004             : {
    2005           0 :     uint32_t count = mFonts.Length();
    2006           0 :     for (uint32_t i = 0; i < count; ++i) {
    2007           0 :         const FamilyFace& ff = mFonts[i];
    2008           0 :         if (ff.IsLoading() && ff.Family() == aFamily) {
    2009             :             const gfxUserFontEntry* ufe =
    2010           0 :                 static_cast<gfxUserFontEntry*>(ff.FontEntry());
    2011           0 :             if (ufe->CharacterInUnicodeRange(aCh)) {
    2012           0 :                 return true;
    2013             :             }
    2014             :         }
    2015             :     }
    2016           0 :     return false;
    2017             : }
    2018             : 
    2019             : gfxFont*
    2020           0 : gfxFontGroup::GetDefaultFont()
    2021             : {
    2022           0 :     if (mDefaultFont) {
    2023           0 :         return mDefaultFont.get();
    2024             :     }
    2025             : 
    2026             :     bool needsBold;
    2027           0 :     gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
    2028           0 :     gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
    2029           0 :     NS_ASSERTION(defaultFamily,
    2030             :                  "invalid default font returned by GetDefaultFont");
    2031             : 
    2032           0 :     if (defaultFamily) {
    2033           0 :         gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
    2034           0 :                                                            needsBold);
    2035           0 :         if (fe) {
    2036           0 :             mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
    2037             :         }
    2038             :     }
    2039             : 
    2040             :     uint32_t numInits, loaderState;
    2041           0 :     pfl->GetFontlistInitInfo(numInits, loaderState);
    2042           0 :     NS_ASSERTION(numInits != 0,
    2043             :                  "must initialize system fontlist before getting default font!");
    2044             : 
    2045           0 :     uint32_t numFonts = 0;
    2046           0 :     if (!mDefaultFont) {
    2047             :         // Try for a "font of last resort...."
    2048             :         // Because an empty font list would be Really Bad for later code
    2049             :         // that assumes it will be able to get valid metrics for layout,
    2050             :         // just look for the first usable font and put in the list.
    2051             :         // (see bug 554544)
    2052           0 :         AutoTArray<RefPtr<gfxFontFamily>,200> familyList;
    2053           0 :         pfl->GetFontFamilyList(familyList);
    2054           0 :         numFonts = familyList.Length();
    2055           0 :         for (uint32_t i = 0; i < numFonts; ++i) {
    2056           0 :             gfxFontEntry *fe = familyList[i]->FindFontForStyle(mStyle,
    2057           0 :                                                                needsBold);
    2058           0 :             if (fe) {
    2059           0 :                 mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
    2060           0 :                 if (mDefaultFont) {
    2061           0 :                     break;
    2062             :                 }
    2063             :             }
    2064             :         }
    2065             :     }
    2066             : 
    2067           0 :     if (!mDefaultFont) {
    2068             :         // an empty font list at this point is fatal; we're not going to
    2069             :         // be able to do even the most basic layout operations
    2070             : 
    2071             :         // annotate crash report with fontlist info
    2072           0 :         nsAutoCString fontInitInfo;
    2073             :         fontInitInfo.AppendPrintf("no fonts - init: %d fonts: %d loader: %d",
    2074           0 :                                   numInits, numFonts, loaderState);
    2075             : #ifdef XP_WIN
    2076             :         bool dwriteEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
    2077             :         double upTime = (double) GetTickCount();
    2078             :         fontInitInfo.AppendPrintf(" backend: %s system-uptime: %9.3f sec",
    2079             :                                   dwriteEnabled ? "directwrite" : "gdi", upTime/1000);
    2080             : #endif
    2081           0 :         gfxCriticalError() << fontInitInfo.get();
    2082             : 
    2083             :         char msg[256]; // CHECK buffer length if revising message below
    2084           0 :         nsAutoString familiesString;
    2085           0 :         mFamilyList.ToString(familiesString);
    2086           0 :         SprintfLiteral(msg, "unable to find a usable font (%.220s)",
    2087           0 :                        NS_ConvertUTF16toUTF8(familiesString).get());
    2088           0 :         NS_RUNTIMEABORT(msg);
    2089             :     }
    2090             : 
    2091           0 :     return mDefaultFont.get();
    2092             : }
    2093             : 
    2094             : gfxFont*
    2095        1093 : gfxFontGroup::GetFirstValidFont(uint32_t aCh)
    2096             : {
    2097        1093 :     uint32_t count = mFonts.Length();
    2098        1093 :     for (uint32_t i = 0; i < count; ++i) {
    2099        1093 :         FamilyFace& ff = mFonts[i];
    2100        1093 :         if (ff.IsInvalid()) {
    2101           0 :             continue;
    2102             :         }
    2103             : 
    2104             :         // already have a font?
    2105        1093 :         gfxFont* font = ff.Font();
    2106        1093 :         if (font) {
    2107        1087 :             return font;
    2108             :         }
    2109             : 
    2110             :         // Need to build a font, loading userfont if not loaded. In
    2111             :         // cases where unicode range might apply, use the character
    2112             :         // provided.
    2113           6 :         if (ff.IsUserFontContainer()) {
    2114             :             gfxUserFontEntry* ufe =
    2115           0 :                 static_cast<gfxUserFontEntry*>(mFonts[i].FontEntry());
    2116           0 :             bool inRange = ufe->CharacterInUnicodeRange(aCh);
    2117           0 :             if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
    2118           0 :                 inRange && !FontLoadingForFamily(ff.Family(), aCh)) {
    2119           0 :                 ufe->Load();
    2120           0 :                 ff.CheckState(mSkipDrawing);
    2121             :             }
    2122           0 :             if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED ||
    2123           0 :                 !inRange) {
    2124           0 :                 continue;
    2125             :             }
    2126             :         }
    2127             : 
    2128           6 :         font = GetFontAt(i, aCh);
    2129           6 :         if (font) {
    2130           6 :             return font;
    2131             :         }
    2132             :     }
    2133           0 :     return GetDefaultFont();
    2134             : }
    2135             : 
    2136             : gfxFont *
    2137           0 : gfxFontGroup::GetFirstMathFont()
    2138             : {
    2139           0 :     uint32_t count = mFonts.Length();
    2140           0 :     for (uint32_t i = 0; i < count; ++i) {
    2141           0 :         gfxFont* font = GetFontAt(i);
    2142           0 :         if (font && font->TryGetMathTable()) {
    2143           0 :             return font;
    2144             :         }
    2145             :     }
    2146           0 :     return nullptr;
    2147             : }
    2148             : 
    2149             : gfxFontGroup *
    2150           0 : gfxFontGroup::Copy(const gfxFontStyle *aStyle)
    2151             : {
    2152             :     gfxFontGroup *fg =
    2153             :         new gfxFontGroup(mFamilyList, aStyle, mTextPerf,
    2154           0 :                          mUserFontSet, mDevToCssSize);
    2155           0 :     return fg;
    2156             : }
    2157             : 
    2158             : bool
    2159         199 : gfxFontGroup::IsInvalidChar(uint8_t ch)
    2160             : {
    2161         199 :     return ((ch & 0x7f) < 0x20 || ch == 0x7f);
    2162             : }
    2163             : 
    2164             : bool
    2165         674 : gfxFontGroup::IsInvalidChar(char16_t ch)
    2166             : {
    2167             :     // All printable 7-bit ASCII values are OK
    2168         674 :     if (ch >= ' ' && ch < 0x7f) {
    2169         603 :         return false;
    2170             :     }
    2171             :     // No point in sending non-printing control chars through font shaping
    2172          71 :     if (ch <= 0x9f) {
    2173          62 :         return true;
    2174             :     }
    2175          18 :     return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
    2176          27 :              (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
    2177          18 :             IsBidiControl(ch));
    2178             : }
    2179             : 
    2180             : already_AddRefed<gfxTextRun>
    2181          12 : gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams,
    2182             :                                gfx::ShapedTextFlags aFlags,
    2183             :                                nsTextFrameUtils::Flags aFlags2)
    2184             : {
    2185          12 :     aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
    2186          12 :     return gfxTextRun::Create(aParams, 0, this, aFlags, aFlags2);
    2187             : }
    2188             : 
    2189             : already_AddRefed<gfxTextRun>
    2190           0 : gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams,
    2191             :                                gfx::ShapedTextFlags aFlags,
    2192             :                                nsTextFrameUtils::Flags aFlags2)
    2193             : {
    2194           0 :     aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
    2195             : 
    2196             :     RefPtr<gfxTextRun> textRun =
    2197           0 :         gfxTextRun::Create(aParams, 1, this, aFlags, aFlags2);
    2198           0 :     if (!textRun) {
    2199           0 :         return nullptr;
    2200             :     }
    2201             : 
    2202           0 :     gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
    2203           0 :     if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
    2204           0 :         orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
    2205             :     }
    2206             : 
    2207           0 :     gfxFont *font = GetFirstValidFont();
    2208           0 :     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
    2209           0 :         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
    2210             :         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
    2211             :         // them, and always create at least size 1 fonts, i.e. they still
    2212             :         // render something for size 0 fonts.
    2213           0 :         textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false,
    2214           0 :                              orientation);
    2215             :     }
    2216             :     else {
    2217           0 :         if (font->GetSpaceGlyph()) {
    2218             :             // Normally, the font has a cached space glyph, so we can avoid
    2219             :             // the cost of calling FindFontForChar.
    2220           0 :             textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
    2221             :         } else {
    2222             :             // In case the primary font doesn't have <space> (bug 970891),
    2223             :             // find one that does.
    2224             :             uint8_t matchType;
    2225             :             gfxFont* spaceFont =
    2226             :                 FindFontForChar(' ', 0, 0, Script::LATIN, nullptr,
    2227           0 :                                 &matchType);
    2228           0 :             if (spaceFont) {
    2229           0 :                 textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0,
    2230           0 :                                        orientation);
    2231             :             }
    2232             :         }
    2233             :     }
    2234             : 
    2235             :     // Note that the gfxGlyphExtents glyph bounds storage for the font will
    2236             :     // always contain an entry for the font's space glyph, so we don't have
    2237             :     // to call FetchGlyphExtents here.
    2238           0 :     return textRun.forget();
    2239             : }
    2240             : 
    2241             : already_AddRefed<gfxTextRun>
    2242           0 : gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
    2243             :                                const Parameters *aParams,
    2244             :                                gfx::ShapedTextFlags aFlags,
    2245             :                                nsTextFrameUtils::Flags aFlags2)
    2246             : {
    2247             :     RefPtr<gfxTextRun> textRun =
    2248           0 :         gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
    2249           0 :     if (!textRun) {
    2250           0 :         return nullptr;
    2251             :     }
    2252             : 
    2253           0 :     gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
    2254           0 :     if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
    2255           0 :         orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
    2256             :     }
    2257           0 :     textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
    2258           0 :                          orientation);
    2259           0 :     return textRun.forget();
    2260             : }
    2261             : 
    2262             : already_AddRefed<gfxTextRun>
    2263           0 : gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
    2264             :                                 uint32_t aAppUnitsPerDevUnit)
    2265             : {
    2266             :     // only use U+2010 if it is supported by the first font in the group;
    2267             :     // it's better to use ASCII '-' from the primary font than to fall back to
    2268             :     // U+2010 from some other, possibly poorly-matching face
    2269             :     static const char16_t hyphen = 0x2010;
    2270           0 :     gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
    2271           0 :     if (font->HasCharacter(hyphen)) {
    2272             :         return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit,
    2273             :                            ShapedTextFlags(),
    2274           0 :                            nsTextFrameUtils::Flags(), nullptr);
    2275             :     }
    2276             : 
    2277             :     static const uint8_t dash = '-';
    2278             :     return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit,
    2279             :                        ShapedTextFlags(),
    2280           0 :                        nsTextFrameUtils::Flags(), nullptr);
    2281             : }
    2282             : 
    2283             : gfxFloat
    2284           0 : gfxFontGroup::GetHyphenWidth(const gfxTextRun::PropertyProvider* aProvider)
    2285             : {
    2286           0 :     if (mHyphenWidth < 0) {
    2287           0 :         RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
    2288           0 :         if (dt) {
    2289             :             RefPtr<gfxTextRun>
    2290           0 :                 hyphRun(MakeHyphenTextRun(dt,
    2291           0 :                                           aProvider->GetAppUnitsPerDevUnit()));
    2292           0 :             mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
    2293             :         }
    2294             :     }
    2295           0 :     return mHyphenWidth;
    2296             : }
    2297             : 
    2298             : already_AddRefed<gfxTextRun>
    2299          21 : gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
    2300             :                           const Parameters *aParams,
    2301             :                           gfx::ShapedTextFlags aFlags,
    2302             :                           nsTextFrameUtils::Flags aFlags2,
    2303             :                           gfxMissingFontRecorder *aMFR)
    2304             : {
    2305          21 :     if (aLength == 0) {
    2306          12 :         return MakeEmptyTextRun(aParams, aFlags, aFlags2);
    2307             :     }
    2308           9 :     if (aLength == 1 && aString[0] == ' ') {
    2309           0 :         return MakeSpaceTextRun(aParams, aFlags, aFlags2);
    2310             :     }
    2311             : 
    2312           9 :     aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
    2313             : 
    2314          18 :     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
    2315           9 :         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
    2316             :         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
    2317             :         // them, and always create at least size 1 fonts, i.e. they still
    2318             :         // render something for size 0 fonts.
    2319           0 :         return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
    2320             :     }
    2321             : 
    2322          18 :     RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
    2323          18 :                                                     aFlags, aFlags2);
    2324           9 :     if (!textRun) {
    2325           0 :         return nullptr;
    2326             :     }
    2327             : 
    2328           9 :     InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
    2329             : 
    2330           9 :     textRun->FetchGlyphExtents(aParams->mDrawTarget);
    2331             : 
    2332           9 :     return textRun.forget();
    2333             : }
    2334             : 
    2335             : already_AddRefed<gfxTextRun>
    2336          62 : gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
    2337             :                           const Parameters *aParams,
    2338             :                           gfx::ShapedTextFlags aFlags, 
    2339             :                           nsTextFrameUtils::Flags aFlags2,
    2340             :                           gfxMissingFontRecorder *aMFR)
    2341             : {
    2342          62 :     if (aLength == 0) {
    2343           0 :         return MakeEmptyTextRun(aParams, aFlags, aFlags2);
    2344             :     }
    2345          62 :     if (aLength == 1 && aString[0] == ' ') {
    2346           0 :         return MakeSpaceTextRun(aParams, aFlags, aFlags2);
    2347             :     }
    2348         124 :     if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
    2349          62 :         MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
    2350           0 :         return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
    2351             :     }
    2352             : 
    2353         124 :     RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
    2354         124 :                                                     aFlags, aFlags2);
    2355          62 :     if (!textRun) {
    2356           0 :         return nullptr;
    2357             :     }
    2358             : 
    2359          62 :     InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
    2360             : 
    2361          62 :     textRun->FetchGlyphExtents(aParams->mDrawTarget);
    2362             : 
    2363          62 :     return textRun.forget();
    2364             : }
    2365             : 
    2366             : template<typename T>
    2367             : void
    2368          71 : gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget,
    2369             :                           gfxTextRun *aTextRun,
    2370             :                           const T *aString,
    2371             :                           uint32_t aLength,
    2372             :                           gfxMissingFontRecorder *aMFR)
    2373             : {
    2374          71 :     NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
    2375             : 
    2376             :     // we need to do numeral processing even on 8-bit text,
    2377             :     // in case we're converting Western to Hindi/Arabic digits
    2378          71 :     int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
    2379         142 :     UniquePtr<char16_t[]> transformedString;
    2380          71 :     if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
    2381             :         // scan the string for numerals that may need to be transformed;
    2382             :         // if we find any, we'll make a local copy here and use that for
    2383             :         // font matching and glyph generation/shaping
    2384             :         bool prevIsArabic =
    2385           0 :             !!(aTextRun->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR);
    2386           0 :         for (uint32_t i = 0; i < aLength; ++i) {
    2387           0 :             char16_t origCh = aString[i];
    2388           0 :             char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
    2389           0 :             if (newCh != origCh) {
    2390           0 :                 if (!transformedString) {
    2391           0 :                     transformedString = MakeUnique<char16_t[]>(aLength);
    2392             :                     if (sizeof(T) == sizeof(char16_t)) {
    2393           0 :                         memcpy(transformedString.get(), aString, i * sizeof(char16_t));
    2394             :                     } else {
    2395           0 :                         for (uint32_t j = 0; j < i; ++j) {
    2396           0 :                             transformedString[j] = aString[j];
    2397             :                         }
    2398             :                     }
    2399             :                 }
    2400             :             }
    2401           0 :             if (transformedString) {
    2402           0 :                 transformedString[i] = newCh;
    2403             :             }
    2404           0 :             prevIsArabic = IS_ARABIC_CHAR(newCh);
    2405             :         }
    2406             :     }
    2407             : 
    2408          71 :     LogModule* log = mStyle.systemFont
    2409             :                    ? gfxPlatform::GetLog(eGfxLog_textrunui)
    2410          71 :                    : gfxPlatform::GetLog(eGfxLog_textrun);
    2411             : 
    2412             :     // variant fallback handling may end up passing through this twice
    2413             :     bool redo;
    2414          71 :     do {
    2415          71 :         redo = false;
    2416             : 
    2417          71 :         if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
    2418             : 
    2419           9 :             if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
    2420           0 :                 nsAutoCString lang;
    2421           0 :                 mStyle.language->ToUTF8String(lang);
    2422           0 :                 nsAutoString families;
    2423           0 :                 mFamilyList.ToString(families);
    2424           0 :                 nsAutoCString str((const char*)aString, aLength);
    2425           0 :                 MOZ_LOG(log, LogLevel::Warning,\
    2426             :                        ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
    2427             :                         "len %d weight: %d width: %d style: %s size: %6.2f %" PRIuSIZE "-byte "
    2428             :                         "TEXTRUN [%s] ENDTEXTRUN\n",
    2429             :                         (mStyle.systemFont ? "textrunui" : "textrun"),
    2430             :                         NS_ConvertUTF16toUTF8(families).get(),
    2431             :                         (mFamilyList.GetDefaultFontType() == eFamily_serif ?
    2432             :                          "serif" :
    2433             :                          (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
    2434             :                           "sans-serif" : "none")),
    2435             :                         lang.get(), static_cast<int>(Script::LATIN), aLength,
    2436             :                         uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
    2437             :                         (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
    2438             :                         (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
    2439             :                                                                 "normal")),
    2440             :                         mStyle.size,
    2441             :                         sizeof(T),
    2442             :                         str.get()));
    2443             :             }
    2444             : 
    2445             :             // the text is still purely 8-bit; bypass the script-run itemizer
    2446             :             // and treat it as a single Latin run
    2447           9 :             InitScriptRun(aDrawTarget, aTextRun, aString,
    2448             :                           0, aLength, Script::LATIN, aMFR);
    2449             :         } else {
    2450             :             const char16_t *textPtr;
    2451          62 :             if (transformedString) {
    2452           0 :                 textPtr = transformedString.get();
    2453             :             } else {
    2454             :                 // typecast to avoid compilation error for the 8-bit version,
    2455             :                 // even though this is dead code in that case
    2456          62 :                 textPtr = reinterpret_cast<const char16_t*>(aString);
    2457             :             }
    2458             : 
    2459             :             // split into script runs so that script can potentially influence
    2460             :             // the font matching process below
    2461          62 :             gfxScriptItemizer scriptRuns(textPtr, aLength);
    2462             : 
    2463          62 :             uint32_t runStart = 0, runLimit = aLength;
    2464          62 :             Script runScript = Script::LATIN;
    2465         186 :             while (scriptRuns.Next(runStart, runLimit, runScript)) {
    2466             : 
    2467          62 :                 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
    2468           0 :                     nsAutoCString lang;
    2469           0 :                     mStyle.language->ToUTF8String(lang);
    2470           0 :                     nsAutoString families;
    2471           0 :                     mFamilyList.ToString(families);
    2472           0 :                     uint32_t runLen = runLimit - runStart;
    2473           0 :                     MOZ_LOG(log, LogLevel::Warning,\
    2474             :                            ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
    2475             :                             "len %d weight: %d width: %d style: %s size: %6.2f "
    2476             :                             "%" PRIuSIZE "-byte TEXTRUN [%s] ENDTEXTRUN\n",
    2477             :                             (mStyle.systemFont ? "textrunui" : "textrun"),
    2478             :                             NS_ConvertUTF16toUTF8(families).get(),
    2479             :                             (mFamilyList.GetDefaultFontType() == eFamily_serif ?
    2480             :                              "serif" :
    2481             :                              (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
    2482             :                               "sans-serif" : "none")),
    2483             :                             lang.get(), static_cast<int>(runScript), runLen,
    2484             :                             uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
    2485             :                             (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
    2486             :                             (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
    2487             :                                                                     "normal")),
    2488             :                             mStyle.size,
    2489             :                             sizeof(T),
    2490             :                             NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
    2491             :                 }
    2492             : 
    2493          62 :                 InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart,
    2494             :                               runStart, runLimit - runStart, runScript, aMFR);
    2495             :             }
    2496             :         }
    2497             : 
    2498             :         // if shaping was aborted due to lack of feature support, clear out
    2499             :         // glyph runs and redo shaping with fallback forced on
    2500          71 :         if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
    2501           0 :             redo = true;
    2502           0 :             aTextRun->SetShapingState(
    2503             :                 gfxTextRun::eShapingState_ForceFallbackFeature);
    2504           0 :             aTextRun->ClearGlyphsAndCharacters();
    2505             :         }
    2506             : 
    2507             :     } while (redo);
    2508             : 
    2509          62 :     if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
    2510          62 :         gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
    2511          62 :         if (!glyph->IsSimpleGlyph()) {
    2512           0 :             glyph->SetClusterStart(true);
    2513             :         }
    2514             :     }
    2515             : 
    2516             :     // It's possible for CoreText to omit glyph runs if it decides they contain
    2517             :     // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
    2518             :     // need to eliminate them from the glyph run array to avoid drawing "partial
    2519             :     // ligatures" with the wrong font.
    2520             :     // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
    2521             :     // it will iterate back over all glyphruns in the textrun, which leads to
    2522             :     // pathologically-bad perf in the case where a textrun contains many script
    2523             :     // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
    2524             :     // every time a new script subrun is processed.
    2525          71 :     aTextRun->SanitizeGlyphRuns();
    2526             : 
    2527          71 :     aTextRun->SortGlyphRuns();
    2528          71 : }
    2529             : 
    2530             : static inline bool
    2531           0 : IsPUA(uint32_t aUSV)
    2532             : {
    2533             :     // We could look up the General Category of the codepoint here,
    2534             :     // but it's simpler to check PUA codepoint ranges.
    2535           0 :     return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000);
    2536             : }
    2537             : 
    2538             : template<typename T>
    2539             : void
    2540          71 : gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
    2541             :                             gfxTextRun *aTextRun,
    2542             :                             const T *aString, // text for this script run,
    2543             :                                               // not the entire textrun
    2544             :                             uint32_t aOffset, // position of the script run
    2545             :                                               // within the textrun
    2546             :                             uint32_t aLength, // length of the script run
    2547             :                             Script aRunScript,
    2548             :                             gfxMissingFontRecorder *aMFR)
    2549             : {
    2550          71 :     NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
    2551          71 :     NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
    2552             :                  "don't call InitScriptRun with aborted shaping state");
    2553             : 
    2554             :     // confirm the load state of userfonts in the list
    2555          71 :     if (mUserFontSet &&
    2556           0 :         mCurrGeneration != mUserFontSet->GetGeneration()) {
    2557           0 :         UpdateUserFonts();
    2558             :     }
    2559             : 
    2560          71 :     gfxFont *mainFont = GetFirstValidFont();
    2561             : 
    2562          71 :     uint32_t runStart = 0;
    2563         142 :     AutoTArray<gfxTextRange,3> fontRanges;
    2564          71 :     ComputeRanges(fontRanges, aString, aLength, aRunScript,
    2565         142 :                   aTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK);
    2566          71 :     uint32_t numRanges = fontRanges.Length();
    2567          71 :     bool missingChars = false;
    2568             : 
    2569         142 :     for (uint32_t r = 0; r < numRanges; r++) {
    2570          71 :         const gfxTextRange& range = fontRanges[r];
    2571          71 :         uint32_t matchedLength = range.Length();
    2572          71 :         gfxFont *matchedFont = range.font;
    2573             :         bool vertical =
    2574          71 :             range.orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
    2575             :         // create the glyph run for this range
    2576          71 :         if (matchedFont && mStyle.noFallbackVariantFeatures) {
    2577             :             // common case - just do glyph layout and record the
    2578             :             // resulting positioned glyphs
    2579          71 :             aTextRun->AddGlyphRun(matchedFont, range.matchType,
    2580             :                                   aOffset + runStart, (matchedLength > 0),
    2581          71 :                                   range.orientation);
    2582         204 :             if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
    2583          62 :                                                   aString + runStart,
    2584             :                                                   aOffset + runStart,
    2585             :                                                   matchedLength,
    2586             :                                                   aRunScript,
    2587             :                                                   vertical)) {
    2588             :                 // glyph layout failed! treat as missing glyphs
    2589           0 :                 matchedFont = nullptr;
    2590             :             }
    2591           0 :         } else if (matchedFont) {
    2592             :             // shape with some variant feature that requires fallback handling
    2593           0 :             bool petiteToSmallCaps = false;
    2594           0 :             bool syntheticLower = false;
    2595           0 :             bool syntheticUpper = false;
    2596             : 
    2597           0 :             if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
    2598           0 :                 (aTextRun->GetShapingState() ==
    2599           0 :                      gfxTextRun::eShapingState_ForceFallbackFeature ||
    2600           0 :                  !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper,
    2601             :                                                       aString, aLength,
    2602             :                                                       aRunScript)))
    2603             :             {
    2604             :                 // fallback for subscript/superscript variant glyphs
    2605             : 
    2606             :                 // if the feature was already used, abort and force
    2607             :                 // fallback across the entire textrun
    2608           0 :                 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
    2609             : 
    2610           0 :                 if (ss == gfxTextRun::eShapingState_Normal) {
    2611           0 :                     aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFallback);
    2612           0 :                 } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
    2613           0 :                     aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
    2614           0 :                     return;
    2615             :                 }
    2616             : 
    2617             :                 RefPtr<gfxFont> subSuperFont =
    2618           0 :                     matchedFont->GetSubSuperscriptFont(aTextRun->GetAppUnitsPerDevUnit());
    2619           0 :                 aTextRun->AddGlyphRun(subSuperFont, range.matchType,
    2620             :                                       aOffset + runStart, (matchedLength > 0),
    2621           0 :                                       range.orientation);
    2622           0 :                 if (!subSuperFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
    2623           0 :                                                        aString + runStart,
    2624             :                                                        aOffset + runStart,
    2625             :                                                        matchedLength,
    2626             :                                                        aRunScript,
    2627             :                                                        vertical)) {
    2628             :                     // glyph layout failed! treat as missing glyphs
    2629           0 :                     matchedFont = nullptr;
    2630             :                 }
    2631           0 :             } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
    2632           0 :                        !matchedFont->SupportsVariantCaps(aRunScript,
    2633           0 :                                                          mStyle.variantCaps,
    2634             :                                                          petiteToSmallCaps,
    2635             :                                                          syntheticLower,
    2636             :                                                          syntheticUpper))
    2637             :             {
    2638             :                 // fallback for small-caps variant glyphs
    2639           0 :                 if (!matchedFont->InitFakeSmallCapsRun(aDrawTarget, aTextRun,
    2640           0 :                                                        aString + runStart,
    2641             :                                                        aOffset + runStart,
    2642             :                                                        matchedLength,
    2643           0 :                                                        range.matchType,
    2644           0 :                                                        range.orientation,
    2645             :                                                        aRunScript,
    2646             :                                                        syntheticLower,
    2647             :                                                        syntheticUpper)) {
    2648           0 :                     matchedFont = nullptr;
    2649             :                 }
    2650             :             } else {
    2651             :                 // shape normally with variant feature enabled
    2652           0 :                 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
    2653             : 
    2654             :                 // adjust the shaping state if necessary
    2655           0 :                 if (ss == gfxTextRun::eShapingState_Normal) {
    2656           0 :                     aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFeature);
    2657           0 :                 } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
    2658             :                     // already have shaping results using fallback, need to redo
    2659           0 :                     aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
    2660           0 :                     return;
    2661             :                 }
    2662             : 
    2663             :                 // do glyph layout and record the resulting positioned glyphs
    2664           0 :                 aTextRun->AddGlyphRun(matchedFont, range.matchType,
    2665             :                                       aOffset + runStart, (matchedLength > 0),
    2666           0 :                                       range.orientation);
    2667           0 :                 if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
    2668           0 :                                                       aString + runStart,
    2669             :                                                       aOffset + runStart,
    2670             :                                                       matchedLength,
    2671             :                                                       aRunScript,
    2672             :                                                       vertical)) {
    2673             :                     // glyph layout failed! treat as missing glyphs
    2674           0 :                     matchedFont = nullptr;
    2675             :                 }
    2676             :             }
    2677             :         } else {
    2678           0 :             aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
    2679             :                                   aOffset + runStart, (matchedLength > 0),
    2680           0 :                                   range.orientation);
    2681             :         }
    2682             : 
    2683          71 :         if (!matchedFont) {
    2684             :             // We need to set cluster boundaries (and mark spaces) so that
    2685             :             // surrogate pairs, combining characters, etc behave properly,
    2686             :             // even if we don't have glyphs for them
    2687           0 :             aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
    2688             :                                              matchedLength);
    2689             : 
    2690             :             // various "missing" characters may need special handling,
    2691             :             // so we check for them here
    2692           0 :             uint32_t runLimit = runStart + matchedLength;
    2693           0 :             for (uint32_t index = runStart; index < runLimit; index++) {
    2694           0 :                 T ch = aString[index];
    2695             : 
    2696             :                 // tab and newline are not to be displayed as hexboxes,
    2697             :                 // but do need to be recorded in the textrun
    2698           0 :                 if (ch == '\n') {
    2699           0 :                     aTextRun->SetIsNewline(aOffset + index);
    2700           0 :                     continue;
    2701             :                 }
    2702           0 :                 if (ch == '\t') {
    2703           0 :                     aTextRun->SetIsTab(aOffset + index);
    2704           0 :                     continue;
    2705             :                 }
    2706             : 
    2707             :                 // for 16-bit textruns only, check for surrogate pairs and
    2708             :                 // special Unicode spaces; omit these checks in 8-bit runs
    2709             :                 if (sizeof(T) == sizeof(char16_t)) {
    2710           0 :                     if (NS_IS_HIGH_SURROGATE(ch) &&
    2711           0 :                         index + 1 < aLength &&
    2712           0 :                         NS_IS_LOW_SURROGATE(aString[index + 1]))
    2713             :                     {
    2714             :                         uint32_t usv =
    2715           0 :                             SURROGATE_TO_UCS4(ch, aString[index + 1]);
    2716           0 :                         aTextRun->SetMissingGlyph(aOffset + index,
    2717             :                                                   usv,
    2718             :                                                   mainFont);
    2719           0 :                         index++;
    2720           0 :                         if (!mSkipDrawing && !IsPUA(usv)) {
    2721           0 :                             missingChars = true;
    2722             :                         }
    2723           0 :                         continue;
    2724             :                     }
    2725             : 
    2726             :                     // check if this is a known Unicode whitespace character that
    2727             :                     // we can render using the space glyph with a custom width
    2728           0 :                     gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
    2729           0 :                     if (wid >= 0.0) {
    2730             :                         nscoord advance =
    2731           0 :                             aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
    2732           0 :                         if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
    2733           0 :                             aTextRun->GetCharacterGlyphs()[aOffset + index].
    2734           0 :                                 SetSimpleGlyph(advance,
    2735           0 :                                                mainFont->GetSpaceGlyph());
    2736             :                         } else {
    2737             :                             gfxTextRun::DetailedGlyph detailedGlyph;
    2738           0 :                             detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
    2739           0 :                             detailedGlyph.mAdvance = advance;
    2740           0 :                             detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
    2741           0 :                             gfxShapedText::CompressedGlyph g;
    2742           0 :                             g.SetComplex(true, true, 1);
    2743           0 :                             aTextRun->SetGlyphs(aOffset + index,
    2744             :                                                 g, &detailedGlyph);
    2745             :                         }
    2746           0 :                         continue;
    2747             :                     }
    2748             :                 }
    2749             : 
    2750           0 :                 if (IsInvalidChar(ch)) {
    2751             :                     // invalid chars are left as zero-width/invisible
    2752           0 :                     continue;
    2753             :                 }
    2754             : 
    2755             :                 // record char code so we can draw a box with the Unicode value
    2756           0 :                 aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
    2757           0 :                 if (!mSkipDrawing && !IsPUA(ch)) {
    2758           0 :                     missingChars = true;
    2759             :                 }
    2760             :             }
    2761             :         }
    2762             : 
    2763          71 :         runStart += matchedLength;
    2764             :     }
    2765             : 
    2766          71 :     if (aMFR && missingChars) {
    2767           0 :         aMFR->RecordScript(aRunScript);
    2768             :     }
    2769             : }
    2770             : 
    2771             : gfxTextRun *
    2772           0 : gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
    2773             :                                  gfx::ShapedTextFlags aFlags,
    2774             :                                  LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
    2775             : {
    2776           0 :     MOZ_ASSERT(!(aFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK),
    2777             :                "flags here should only be used to specify orientation");
    2778           0 :     if (mCachedEllipsisTextRun &&
    2779           0 :         (mCachedEllipsisTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK) == aFlags &&
    2780           0 :         mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
    2781           0 :         return mCachedEllipsisTextRun.get();
    2782             :     }
    2783             : 
    2784             :     // Use a Unicode ellipsis if the font supports it,
    2785             :     // otherwise use three ASCII periods as fallback.
    2786           0 :     gfxFont* firstFont = GetFirstValidFont(uint32_t(kEllipsisChar[0]));
    2787           0 :     nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
    2788           0 :         ? nsDependentString(kEllipsisChar,
    2789           0 :                             ArrayLength(kEllipsisChar) - 1)
    2790             :         : nsDependentString(kASCIIPeriodsChar,
    2791           0 :                             ArrayLength(kASCIIPeriodsChar) - 1);
    2792             : 
    2793           0 :     RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
    2794             :     Parameters params = {
    2795             :         refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
    2796           0 :     };
    2797             :     mCachedEllipsisTextRun =
    2798           0 :         MakeTextRun(ellipsis.get(), ellipsis.Length(), &params,
    2799           0 :                     aFlags, nsTextFrameUtils::Flags(), nullptr);
    2800           0 :     if (!mCachedEllipsisTextRun) {
    2801           0 :         return nullptr;
    2802             :     }
    2803             :     // don't let the presence of a cached ellipsis textrun prolong the
    2804             :     // fontgroup's life
    2805           0 :     mCachedEllipsisTextRun->ReleaseFontGroup();
    2806           0 :     return mCachedEllipsisTextRun.get();
    2807             : }
    2808             : 
    2809             : gfxFont*
    2810           0 : gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
    2811             :                                       Script aRunScript)
    2812             : {
    2813           0 :     GlobalFontMatch data(aCh, aRunScript, &mStyle);
    2814           0 :     aFamily->SearchAllFontsForChar(&data);
    2815           0 :     gfxFontEntry* fe = data.mBestMatch;
    2816           0 :     if (!fe) {
    2817           0 :         return nullptr;
    2818             :     }
    2819             : 
    2820           0 :     bool needsBold = mStyle.weight >= 600 && !fe->IsBold() &&
    2821           0 :                      mStyle.allowSyntheticWeight;
    2822           0 :     return fe->FindOrMakeFont(&mStyle, needsBold);
    2823             : }
    2824             : 
    2825             : gfxFloat
    2826         139 : gfxFontGroup::GetUnderlineOffset()
    2827             : {
    2828         139 :     if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
    2829             :         // if the fontlist contains a bad underline font, make the underline
    2830             :         // offset the min of the first valid font and bad font underline offsets
    2831           4 :         uint32_t len = mFonts.Length();
    2832          17 :         for (uint32_t i = 0; i < len; i++) {
    2833          13 :             FamilyFace& ff = mFonts[i];
    2834          39 :             if (!ff.IsUserFontContainer() &&
    2835          26 :                 !ff.FontEntry()->IsUserFont() &&
    2836          39 :                 ff.Family() &&
    2837          13 :                 ff.Family()->IsBadUnderlineFamily()) {
    2838           0 :                 gfxFont* font = GetFontAt(i);
    2839           0 :                 if (!font) {
    2840           0 :                     continue;
    2841             :                 }
    2842           0 :                 gfxFloat bad = font->GetMetrics(gfxFont::eHorizontal).
    2843           0 :                                          underlineOffset;
    2844             :                 gfxFloat first =
    2845           0 :                     GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal).
    2846           0 :                                              underlineOffset;
    2847           0 :                 mUnderlineOffset = std::min(first, bad);
    2848           0 :                 return mUnderlineOffset;
    2849             :             }
    2850             :         }
    2851             : 
    2852             :         // no bad underline fonts, use the first valid font's metric
    2853           4 :         mUnderlineOffset = GetFirstValidFont()->
    2854           4 :             GetMetrics(gfxFont::eHorizontal).underlineOffset;
    2855             :     }
    2856             : 
    2857         139 :     return mUnderlineOffset;
    2858             : }
    2859             : 
    2860             : #define NARROW_NO_BREAK_SPACE 0x202fu
    2861             : 
    2862             : gfxFont*
    2863           0 : gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
    2864             :                               Script aRunScript, gfxFont *aPrevMatchedFont,
    2865             :                               uint8_t *aMatchType)
    2866             : {
    2867             :     // If the char is a cluster extender, we want to use the same font as the
    2868             :     // preceding character if possible. This is preferable to using the font
    2869             :     // group because it avoids breaks in shaping within a cluster.
    2870           0 :     if (aPrevMatchedFont && IsClusterExtender(aCh) &&
    2871           0 :         aPrevMatchedFont->HasCharacter(aCh)) {
    2872           0 :         return aPrevMatchedFont;
    2873             :     }
    2874             : 
    2875             :     // Special cases for NNBSP (as used in Mongolian):
    2876           0 :     if (aCh == NARROW_NO_BREAK_SPACE) {
    2877             :         // If there is no preceding character, try the font that we'd use
    2878             :         // for the next char (unless it's just another NNBSP; we don't try
    2879             :         // to look ahead through a whole run of them).
    2880           0 :         if (!aPrevCh && aNextCh && aNextCh != NARROW_NO_BREAK_SPACE) {
    2881             :             gfxFont* nextFont =
    2882             :                 FindFontForChar(aNextCh, 0, 0, aRunScript, aPrevMatchedFont,
    2883           0 :                                 aMatchType);
    2884           0 :             if (nextFont && nextFont->HasCharacter(aCh)) {
    2885           0 :                 return nextFont;
    2886             :             }
    2887             :         }
    2888             :         // Otherwise, treat NNBSP like a cluster extender (as above) and try
    2889             :         // to continue the preceding font run.
    2890           0 :         if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
    2891           0 :             return aPrevMatchedFont;
    2892             :         }
    2893             :     }
    2894             : 
    2895             :     // To optimize common cases, try the first font in the font-group
    2896             :     // before going into the more detailed checks below
    2897           0 :     uint32_t nextIndex = 0;
    2898           0 :     bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
    2899           0 :     bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
    2900           0 :     bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
    2901             : 
    2902           0 :     if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
    2903           0 :         gfxFont* firstFont = GetFontAt(0, aCh);
    2904           0 :         if (firstFont) {
    2905           0 :             if (firstFont->HasCharacter(aCh)) {
    2906           0 :                 *aMatchType = gfxTextRange::kFontGroup;
    2907           0 :                 return firstFont;
    2908             :             }
    2909             : 
    2910           0 :             gfxFont* font = nullptr;
    2911           0 :             if (mFonts[0].CheckForFallbackFaces()) {
    2912           0 :                 font = FindFallbackFaceForChar(mFonts[0].Family(), aCh,
    2913           0 :                                                aRunScript);
    2914           0 :             } else if (!firstFont->GetFontEntry()->IsUserFont()) {
    2915             :                 // For platform fonts (but not userfonts), we may need to do
    2916             :                 // fallback within the family to handle cases where some faces
    2917             :                 // such as Italic or Black have reduced character sets compared
    2918             :                 // to the family's Regular face.
    2919           0 :                 gfxFontEntry* fe = firstFont->GetFontEntry();
    2920           0 :                 if (!fe->IsUpright() ||
    2921           0 :                     fe->Weight() != NS_FONT_WEIGHT_NORMAL ||
    2922           0 :                     fe->Stretch() != NS_FONT_STRETCH_NORMAL) {
    2923             :                     // If style/weight/stretch was not Normal, see if we can
    2924             :                     // fall back to a next-best face (e.g. Arial Black -> Bold,
    2925             :                     // or Arial Narrow -> Regular).
    2926           0 :                     font = FindFallbackFaceForChar(mFonts[0].Family(), aCh,
    2927           0 :                                                    aRunScript);
    2928             :                 }
    2929             :             }
    2930           0 :             if (font) {
    2931           0 :                 *aMatchType = gfxTextRange::kFontGroup;
    2932           0 :                 return font;
    2933             :             }
    2934             :         }
    2935             : 
    2936             :         // we don't need to check the first font again below
    2937           0 :         ++nextIndex;
    2938             :     }
    2939             : 
    2940           0 :     if (aPrevMatchedFont) {
    2941             :         // Don't switch fonts for control characters, regardless of
    2942             :         // whether they are present in the current font, as they won't
    2943             :         // actually be rendered (see bug 716229)
    2944           0 :         if (isJoinControl ||
    2945           0 :             GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
    2946           0 :             return aPrevMatchedFont;
    2947             :         }
    2948             : 
    2949             :         // if previous character was a join-causer (ZWJ),
    2950             :         // use the same font as the previous range if we can
    2951           0 :         if (wasJoinCauser) {
    2952           0 :             if (aPrevMatchedFont->HasCharacter(aCh)) {
    2953           0 :                 return aPrevMatchedFont;
    2954             :             }
    2955             :         }
    2956             :     }
    2957             : 
    2958             :     // if this character is a variation selector,
    2959             :     // use the previous font regardless of whether it supports VS or not.
    2960             :     // otherwise the text run will be divided.
    2961           0 :     if (isVarSelector) {
    2962           0 :         if (aPrevMatchedFont) {
    2963           0 :             return aPrevMatchedFont;
    2964             :         }
    2965             :         // VS alone. it's meaningless to search different fonts
    2966           0 :         return nullptr;
    2967             :     }
    2968             : 
    2969             :     // 1. check remaining fonts in the font group
    2970           0 :     uint32_t fontListLength = mFonts.Length();
    2971           0 :     for (uint32_t i = nextIndex; i < fontListLength; i++) {
    2972           0 :         FamilyFace& ff = mFonts[i];
    2973           0 :         if (ff.IsInvalid() || ff.IsLoading()) {
    2974           0 :             continue;
    2975             :         }
    2976             : 
    2977             :         // if available, use already made gfxFont and check for character
    2978           0 :         gfxFont* font = ff.Font();
    2979           0 :         if (font) {
    2980           0 :             if (font->HasCharacter(aCh)) {
    2981           0 :                 return font;
    2982             :             }
    2983           0 :             continue;
    2984             :         }
    2985             : 
    2986             :         // don't have a gfxFont yet, test before building
    2987           0 :         gfxFontEntry *fe = ff.FontEntry();
    2988           0 :         if (fe->mIsUserFontContainer) {
    2989             :             // for userfonts, need to test both the unicode range map and
    2990             :             // the cmap of the platform font entry
    2991           0 :             gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
    2992             : 
    2993             :             // never match a character outside the defined unicode range
    2994           0 :             if (!ufe->CharacterInUnicodeRange(aCh)) {
    2995           0 :                 continue;
    2996             :             }
    2997             : 
    2998             :             // load if not already loaded but only if no other font in similar
    2999             :             // range within family is loading
    3000           0 :             if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
    3001           0 :                 !FontLoadingForFamily(ff.Family(), aCh)) {
    3002           0 :                 ufe->Load();
    3003           0 :                 ff.CheckState(mSkipDrawing);
    3004             :             }
    3005           0 :             gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
    3006           0 :             if (pfe && pfe->HasCharacter(aCh)) {
    3007           0 :                 font = GetFontAt(i, aCh);
    3008           0 :                 if (font) {
    3009           0 :                     *aMatchType = gfxTextRange::kFontGroup;
    3010           0 :                     return font;
    3011             :                 }
    3012             :             }
    3013           0 :         } else if (fe->HasCharacter(aCh)) {
    3014             :             // for normal platform fonts, after checking the cmap
    3015             :             // build the font via GetFontAt
    3016           0 :             font = GetFontAt(i, aCh);
    3017           0 :             if (font) {
    3018           0 :                 *aMatchType = gfxTextRange::kFontGroup;
    3019           0 :                 return font;
    3020             :             }
    3021             :         }
    3022             : 
    3023             :         // check other family faces if needed
    3024           0 :         if (ff.CheckForFallbackFaces()) {
    3025           0 :             NS_ASSERTION(i == 0 ? true :
    3026             :                          !mFonts[i-1].CheckForFallbackFaces() ||
    3027             :                          !mFonts[i-1].Family()->Name().Equals(ff.Family()->Name()),
    3028             :                          "should only do fallback once per font family");
    3029           0 :             font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
    3030           0 :             if (font) {
    3031           0 :                 *aMatchType = gfxTextRange::kFontGroup;
    3032           0 :                 return font;
    3033             :             }
    3034             :         } else {
    3035             :             // For platform fonts, but not user fonts, consider intra-family
    3036             :             // fallback to handle styles with reduced character sets (see
    3037             :             // also above).
    3038           0 :             fe = ff.FontEntry();
    3039           0 :             if (!fe->mIsUserFontContainer && !fe->IsUserFont() &&
    3040           0 :                 (!fe->IsUpright() ||
    3041           0 :                  fe->Weight() != NS_FONT_WEIGHT_NORMAL ||
    3042           0 :                  fe->Stretch() != NS_FONT_STRETCH_NORMAL)) {
    3043           0 :                 font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
    3044           0 :                 if (font) {
    3045           0 :                     *aMatchType = gfxTextRange::kFontGroup;
    3046           0 :                     return font;
    3047             :                 }
    3048             :             }
    3049             :         }
    3050             :     }
    3051             : 
    3052           0 :     if (fontListLength == 0) {
    3053           0 :         gfxFont* defaultFont = GetDefaultFont();
    3054           0 :         if (defaultFont->HasCharacter(aCh)) {
    3055           0 :             *aMatchType = gfxTextRange::kFontGroup;
    3056           0 :             return defaultFont;
    3057             :         }
    3058             :     }
    3059             : 
    3060             :     // if character is in Private Use Area, don't do matching against pref or system fonts
    3061           0 :     if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
    3062           0 :         return nullptr;
    3063             : 
    3064             :     // 2. search pref fonts
    3065           0 :     gfxFont* font = WhichPrefFontSupportsChar(aCh);
    3066           0 :     if (font) {
    3067           0 :         *aMatchType = gfxTextRange::kPrefsFallback;
    3068           0 :         return font;
    3069             :     }
    3070             : 
    3071             :     // 3. use fallback fonts
    3072             :     // -- before searching for something else check the font used for the previous character
    3073           0 :     if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
    3074           0 :         *aMatchType = gfxTextRange::kSystemFallback;
    3075           0 :         return aPrevMatchedFont;
    3076             :     }
    3077             : 
    3078             :     // for known "space" characters, don't do a full system-fallback search;
    3079             :     // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
    3080           0 :     if (GetGeneralCategory(aCh) ==
    3081           0 :             HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
    3082           0 :         GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0)
    3083             :     {
    3084           0 :         return nullptr;
    3085             :     }
    3086             : 
    3087             :     // -- otherwise look for other stuff
    3088           0 :     *aMatchType = gfxTextRange::kSystemFallback;
    3089           0 :     return WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
    3090             : }
    3091             : 
    3092             : template<typename T>
    3093          71 : void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
    3094             :                                  const T *aString, uint32_t aLength,
    3095             :                                  Script aRunScript,
    3096             :                                  gfx::ShapedTextFlags aOrientation)
    3097             : {
    3098          71 :     NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
    3099          71 :     NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
    3100             : 
    3101          71 :     uint32_t prevCh = 0;
    3102          71 :     uint32_t nextCh = aString[0];
    3103             :     if (sizeof(T) == sizeof(char16_t)) {
    3104          62 :         if (aLength > 1 && NS_IS_HIGH_SURROGATE(nextCh) &&
    3105           0 :                            NS_IS_LOW_SURROGATE(aString[1])) {
    3106           0 :             nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
    3107             :         }
    3108             :     }
    3109          71 :     int32_t lastRangeIndex = -1;
    3110             : 
    3111             :     // initialize prevFont to the group's primary font, so that this will be
    3112             :     // used for string-initial control chars, etc rather than risk hitting font
    3113             :     // fallback for these (bug 716229)
    3114          71 :     gfxFont *prevFont = GetFirstValidFont();
    3115             : 
    3116             :     // if we use the initial value of prevFont, we treat this as a match from
    3117             :     // the font group; fixes bug 978313
    3118          71 :     uint8_t matchType = gfxTextRange::kFontGroup;
    3119             : 
    3120         931 :     for (uint32_t i = 0; i < aLength; i++) {
    3121             : 
    3122         860 :         const uint32_t origI = i; // save off in case we increase for surrogate
    3123             : 
    3124             :         // set up current ch
    3125         860 :         uint32_t ch = nextCh;
    3126             : 
    3127             :         // Get next char (if any) so that FindFontForChar can look ahead
    3128             :         // for a possible variation selector.
    3129             : 
    3130             :         if (sizeof(T) == sizeof(char16_t)) {
    3131             :             // In 16-bit case only, check for surrogate pairs.
    3132         658 :             if (ch > 0xffffu) {
    3133           0 :                 i++;
    3134             :             }
    3135         658 :             if (i < aLength - 1) {
    3136         596 :                 nextCh = aString[i + 1];
    3137         596 :                 if ((i + 2 < aLength) && NS_IS_HIGH_SURROGATE(nextCh) &&
    3138           0 :                                          NS_IS_LOW_SURROGATE(aString[i + 2])) {
    3139           0 :                     nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]);
    3140             :                 }
    3141             :             } else {
    3142          62 :                 nextCh = 0;
    3143             :             }
    3144             :         } else {
    3145             :             // 8-bit case is trivial.
    3146         202 :             nextCh = i < aLength - 1 ? aString[i + 1] : 0;
    3147             :         }
    3148             : 
    3149         860 :         if (ch == 0xa0) {
    3150           0 :             ch = ' ';
    3151             :         }
    3152             : 
    3153             :         gfxFont* font;
    3154             : 
    3155             :         // Find the font for this char; but try to avoid calling the expensive
    3156             :         // FindFontForChar method for the most common case, where the first
    3157             :         // font in the list supports the current char, and it is not one of
    3158             :         // the special cases where FindFontForChar will attempt to propagate
    3159             :         // the font selected for an adjacent character.
    3160         860 :         if ((font = GetFontAt(0, ch)) != nullptr
    3161         860 :             && font->HasCharacter(ch)
    3162        2378 :             && (sizeof(T) == sizeof(uint8_t)
    3163         658 :                 || (!IsClusterExtender(ch)
    3164         658 :                     && ch != NARROW_NO_BREAK_SPACE
    3165         658 :                     && !gfxFontUtils::IsJoinControl(ch)
    3166         658 :                     && !gfxFontUtils::IsJoinCauser(prevCh)
    3167         658 :                     && !gfxFontUtils::IsVarSelector(ch)))) {
    3168         860 :             matchType = gfxTextRange::kFontGroup;
    3169             :         } else {
    3170           0 :             font = FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont,
    3171             :                                    &matchType);
    3172             :         }
    3173             : 
    3174             : #ifndef RELEASE_OR_BETA
    3175         860 :         if (MOZ_UNLIKELY(mTextPerf)) {
    3176           0 :             if (matchType == gfxTextRange::kPrefsFallback) {
    3177           0 :                 mTextPerf->current.fallbackPrefs++;
    3178           0 :             } else if (matchType == gfxTextRange::kSystemFallback) {
    3179           0 :                 mTextPerf->current.fallbackSystem++;
    3180             :             }
    3181             :         }
    3182             : #endif
    3183             : 
    3184         860 :         prevCh = ch;
    3185             : 
    3186         860 :         ShapedTextFlags orient = aOrientation;
    3187         860 :         if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
    3188             :             // For CSS text-orientation:mixed, we need to resolve orientation
    3189             :             // on a per-character basis using the UTR50 orientation property.
    3190           0 :             switch (GetVerticalOrientation(ch)) {
    3191             :             case VERTICAL_ORIENTATION_U:
    3192             :             case VERTICAL_ORIENTATION_Tr:
    3193             :             case VERTICAL_ORIENTATION_Tu:
    3194           0 :                 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
    3195           0 :                 break;
    3196             :             case VERTICAL_ORIENTATION_R:
    3197           0 :                 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
    3198           0 :                 break;
    3199             :             }
    3200             :         }
    3201             : 
    3202         860 :         if (lastRangeIndex == -1) {
    3203             :             // first char ==> make a new range
    3204          71 :             aRanges.AppendElement(gfxTextRange(0, 1, font, matchType, orient));
    3205          71 :             lastRangeIndex++;
    3206          71 :             prevFont = font;
    3207             :         } else {
    3208             :             // if font or orientation has changed, make a new range...
    3209             :             // unless ch is a variation selector (bug 1248248)
    3210         789 :             gfxTextRange& prevRange = aRanges[lastRangeIndex];
    3211        1578 :             if (prevRange.font != font || prevRange.matchType != matchType ||
    3212         789 :                 (prevRange.orientation != orient && !IsClusterExtender(ch))) {
    3213             :                 // close out the previous range
    3214           0 :                 prevRange.end = origI;
    3215           0 :                 aRanges.AppendElement(gfxTextRange(origI, i + 1,
    3216             :                                                    font, matchType, orient));
    3217           0 :                 lastRangeIndex++;
    3218             : 
    3219             :                 // update prevFont for the next match, *unless* we switched
    3220             :                 // fonts on a ZWJ, in which case propagating the changed font
    3221             :                 // is probably not a good idea (see bug 619511)
    3222           0 :                 if (sizeof(T) == sizeof(uint8_t) ||
    3223           0 :                     !gfxFontUtils::IsJoinCauser(ch))
    3224             :                 {
    3225           0 :                     prevFont = font;
    3226             :                 }
    3227             :             }
    3228             :         }
    3229             :     }
    3230             : 
    3231          71 :     aRanges[lastRangeIndex].end = aLength;
    3232             : 
    3233             : #ifndef RELEASE_OR_BETA
    3234          71 :     LogModule* log = mStyle.systemFont
    3235             :                    ? gfxPlatform::GetLog(eGfxLog_textrunui)
    3236          71 :                    : gfxPlatform::GetLog(eGfxLog_textrun);
    3237             : 
    3238          71 :     if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
    3239           0 :         nsAutoCString lang;
    3240           0 :         mStyle.language->ToUTF8String(lang);
    3241           0 :         nsAutoString families;
    3242           0 :         mFamilyList.ToString(families);
    3243             : 
    3244             :         // collect the font matched for each range
    3245           0 :         nsAutoCString fontMatches;
    3246           0 :         for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
    3247           0 :             const gfxTextRange& r = aRanges[i];
    3248           0 :             fontMatches.AppendPrintf(" [%u:%u] %.200s (%s)", r.start, r.end,
    3249           0 :                 (r.font.get() ?
    3250           0 :                  NS_ConvertUTF16toUTF8(r.font->GetName()).get() : "<null>"),
    3251           0 :                 (r.matchType == gfxTextRange::kFontGroup ?
    3252             :                  "list" :
    3253           0 :                  (r.matchType == gfxTextRange::kPrefsFallback) ?
    3254             :                   "prefs" : "sys"));
    3255             :         }
    3256           0 :         MOZ_LOG(log, LogLevel::Debug,\
    3257             :                ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
    3258             :                 "%s\n",
    3259             :                 (mStyle.systemFont ? "textrunui" : "textrun"),
    3260             :                 NS_ConvertUTF16toUTF8(families).get(),
    3261             :                 (mFamilyList.GetDefaultFontType() == eFamily_serif ?
    3262             :                  "serif" :
    3263             :                  (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
    3264             :                   "sans-serif" : "none")),
    3265             :                 lang.get(), static_cast<int>(aRunScript),
    3266             :                 fontMatches.get()));
    3267             :     }
    3268             : #endif
    3269          71 : }
    3270             : 
    3271             : gfxUserFontSet*
    3272         409 : gfxFontGroup::GetUserFontSet()
    3273             : {
    3274         409 :     return mUserFontSet;
    3275             : }
    3276             : 
    3277             : void
    3278           0 : gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
    3279             : {
    3280           0 :     if (aUserFontSet == mUserFontSet) {
    3281           0 :         return;
    3282             :     }
    3283           0 :     mUserFontSet = aUserFontSet;
    3284           0 :     mCurrGeneration = GetGeneration() - 1;
    3285           0 :     UpdateUserFonts();
    3286             : }
    3287             : 
    3288             : uint64_t
    3289         415 : gfxFontGroup::GetGeneration()
    3290             : {
    3291         415 :     if (!mUserFontSet)
    3292         415 :         return 0;
    3293           0 :     return mUserFontSet->GetGeneration();
    3294             : }
    3295             : 
    3296             : uint64_t
    3297         409 : gfxFontGroup::GetRebuildGeneration()
    3298             : {
    3299         409 :     if (!mUserFontSet)
    3300         409 :         return 0;
    3301           0 :     return mUserFontSet->GetRebuildGeneration();
    3302             : }
    3303             : 
    3304             : void
    3305         409 : gfxFontGroup::UpdateUserFonts()
    3306             : {
    3307         409 :     if (mCurrGeneration < GetRebuildGeneration()) {
    3308             :         // fonts in userfont set changed, need to redo the fontlist
    3309           0 :         mFonts.Clear();
    3310           0 :         ClearCachedData();
    3311           0 :         BuildFontList();
    3312           0 :         mCurrGeneration = GetGeneration();
    3313         409 :     } else if (mCurrGeneration != GetGeneration()) {
    3314             :         // load state change occurred, verify load state and validity of fonts
    3315           0 :         ClearCachedData();
    3316             : 
    3317           0 :         uint32_t len = mFonts.Length();
    3318           0 :         for (uint32_t i = 0; i < len; i++) {
    3319           0 :             FamilyFace& ff = mFonts[i];
    3320           0 :             if (ff.Font() || !ff.IsUserFontContainer()) {
    3321           0 :                 continue;
    3322             :             }
    3323           0 :             ff.CheckState(mSkipDrawing);
    3324             :         }
    3325             : 
    3326           0 :         mCurrGeneration = GetGeneration();
    3327             :     }
    3328         409 : }
    3329             : 
    3330             : bool
    3331           0 : gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont)
    3332             : {
    3333           0 :     UpdateUserFonts();
    3334             :     // search through the fonts list for a specific user font
    3335           0 :     uint32_t len = mFonts.Length();
    3336           0 :     for (uint32_t i = 0; i < len; i++) {
    3337           0 :         FamilyFace& ff = mFonts[i];
    3338           0 :         if (ff.EqualsUserFont(aUserFont)) {
    3339           0 :             return true;
    3340             :         }
    3341             :     }
    3342           0 :     return false;
    3343             : }
    3344             : 
    3345             : gfxFont*
    3346           0 : gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
    3347             : {
    3348             :     // get the pref font list if it hasn't been set up already
    3349           0 :     uint32_t unicodeRange = FindCharUnicodeRange(aCh);
    3350           0 :     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
    3351           0 :     eFontPrefLang charLang = pfl->GetFontPrefLangFor(unicodeRange);
    3352             : 
    3353             :     // if the last pref font was the first family in the pref list, no need to recheck through a list of families
    3354           0 :     if (mLastPrefFont && charLang == mLastPrefLang &&
    3355           0 :         mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
    3356           0 :         return mLastPrefFont;
    3357             :     }
    3358             : 
    3359             :     // based on char lang and page lang, set up list of pref lang fonts to check
    3360             :     eFontPrefLang prefLangs[kMaxLenPrefLangList];
    3361           0 :     uint32_t i, numLangs = 0;
    3362             : 
    3363           0 :     pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
    3364             : 
    3365           0 :     for (i = 0; i < numLangs; i++) {
    3366           0 :         eFontPrefLang currentLang = prefLangs[i];
    3367             :         mozilla::FontFamilyType defaultGeneric =
    3368           0 :             pfl->GetDefaultGeneric(currentLang);
    3369             :         nsTArray<RefPtr<gfxFontFamily>>* families =
    3370           0 :             pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang);
    3371           0 :         NS_ASSERTION(families, "no pref font families found");
    3372             : 
    3373             :         // find the first pref font that includes the character
    3374             :         uint32_t  j, numPrefs;
    3375           0 :         numPrefs = families->Length();
    3376           0 :         for (j = 0; j < numPrefs; j++) {
    3377             :             // look up the appropriate face
    3378           0 :             gfxFontFamily *family = (*families)[j];
    3379           0 :             if (!family) continue;
    3380             : 
    3381             :             // if a pref font is used, it's likely to be used again in the same text run.
    3382             :             // the style doesn't change so the face lookup can be cached rather than calling
    3383             :             // FindOrMakeFont repeatedly.  speeds up FindFontForChar lookup times for subsequent
    3384             :             // pref font lookups
    3385           0 :             if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
    3386           0 :                 return mLastPrefFont;
    3387             :             }
    3388             : 
    3389             :             bool needsBold;
    3390           0 :             gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
    3391             :             // if ch in cmap, create and return a gfxFont
    3392           0 :             if (fe && fe->HasCharacter(aCh)) {
    3393           0 :                 gfxFont* prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
    3394           0 :                 if (!prefFont) {
    3395           0 :                     continue;
    3396             :                 }
    3397           0 :                 mLastPrefFamily = family;
    3398           0 :                 mLastPrefFont = prefFont;
    3399           0 :                 mLastPrefLang = charLang;
    3400           0 :                 mLastPrefFirstFont = (i == 0 && j == 0);
    3401           0 :                 return prefFont;
    3402             :             }
    3403             : 
    3404             :         }
    3405             :     }
    3406             : 
    3407           0 :     return nullptr;
    3408             : }
    3409             : 
    3410             : gfxFont*
    3411           0 : gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
    3412             :                                           Script aRunScript)
    3413             : {
    3414             :     gfxFontEntry *fe =
    3415             :         gfxPlatformFontList::PlatformFontList()->
    3416           0 :             SystemFindFontForChar(aCh, aNextCh, aRunScript, &mStyle);
    3417           0 :     if (fe) {
    3418           0 :         bool wantBold = mStyle.ComputeWeight() >= 6;
    3419           0 :         return fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
    3420             :     }
    3421             : 
    3422           0 :     return nullptr;
    3423             : }
    3424             : 
    3425             : void
    3426           0 : gfxMissingFontRecorder::Flush()
    3427             : {
    3428             :     static bool mNotifiedFontsInitialized = false;
    3429             :     static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
    3430           0 :     if (!mNotifiedFontsInitialized) {
    3431           0 :         memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
    3432           0 :         mNotifiedFontsInitialized = true;
    3433             :     }
    3434             : 
    3435           0 :     nsAutoString fontNeeded;
    3436           0 :     for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
    3437           0 :         mMissingFonts[i] &= ~mNotifiedFonts[i];
    3438           0 :         if (!mMissingFonts[i]) {
    3439           0 :             continue;
    3440             :         }
    3441           0 :         for (uint32_t j = 0; j < 32; ++j) {
    3442           0 :             if (!(mMissingFonts[i] & (1 << j))) {
    3443           0 :                 continue;
    3444             :             }
    3445           0 :             mNotifiedFonts[i] |= (1 << j);
    3446           0 :             if (!fontNeeded.IsEmpty()) {
    3447           0 :                 fontNeeded.Append(char16_t(','));
    3448             :             }
    3449           0 :             uint32_t sc = i * 32 + j;
    3450           0 :             MOZ_ASSERT(sc < static_cast<uint32_t>(Script::NUM_SCRIPT_CODES),
    3451             :                        "how did we set the bit for an invalid script code?");
    3452           0 :             uint32_t tag = GetScriptTagForCode(static_cast<Script>(sc));
    3453           0 :             fontNeeded.Append(char16_t(tag >> 24));
    3454           0 :             fontNeeded.Append(char16_t((tag >> 16) & 0xff));
    3455           0 :             fontNeeded.Append(char16_t((tag >> 8) & 0xff));
    3456           0 :             fontNeeded.Append(char16_t(tag & 0xff));
    3457             :         }
    3458           0 :         mMissingFonts[i] = 0;
    3459             :     }
    3460           0 :     if (!fontNeeded.IsEmpty()) {
    3461           0 :         nsCOMPtr<nsIObserverService> service = GetObserverService();
    3462           0 :         service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
    3463             :     }
    3464           0 : }

Generated by: LCOV version 1.13