LCOV - code coverage report
Current view: top level - layout/mathml - nsMathMLChar.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1007 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 85 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsMathMLChar.h"
       7             : 
       8             : #include "gfxContext.h"
       9             : #include "gfxTextRun.h"
      10             : #include "gfxUtils.h"
      11             : #include "mozilla/gfx/2D.h"
      12             : #include "mozilla/MathAlgorithms.h"
      13             : #include "mozilla/Unused.h"
      14             : 
      15             : #include "nsAutoPtr.h"
      16             : #include "nsCOMPtr.h"
      17             : #include "nsDeviceContext.h"
      18             : #include "nsFontMetrics.h"
      19             : #include "nsIFrame.h"
      20             : #include "nsLayoutUtils.h"
      21             : #include "nsPresContext.h"
      22             : #include "nsStyleContext.h"
      23             : #include "nsUnicharUtils.h"
      24             : 
      25             : #include "mozilla/Preferences.h"
      26             : #include "nsIPersistentProperties2.h"
      27             : #include "nsIObserverService.h"
      28             : #include "nsIObserver.h"
      29             : #include "nsNetUtil.h"
      30             : #include "nsContentUtils.h"
      31             : 
      32             : #include "mozilla/LookAndFeel.h"
      33             : #include "nsCSSRendering.h"
      34             : #include "mozilla/Sprintf.h"
      35             : 
      36             : #include "nsDisplayList.h"
      37             : 
      38             : #include "nsMathMLOperators.h"
      39             : #include <algorithm>
      40             : 
      41             : #include "gfxMathTable.h"
      42             : #include "nsUnicodeScriptCodes.h"
      43             : 
      44             : using namespace mozilla;
      45             : using namespace mozilla::gfx;
      46             : using namespace mozilla::image;
      47             : 
      48             : //#define NOISY_SEARCH 1
      49             : 
      50             : // BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
      51             : // are installed. "kMaxScaleFactor" is required to limit the scale for the
      52             : // vertical and horizontal stretchy operators.
      53             : static const float kMaxScaleFactor = 20.0;
      54             : static const float kLargeOpFactor = float(M_SQRT2);
      55             : static const float kIntegralFactor = 2.0;
      56             : 
      57             : static void
      58           0 : NormalizeDefaultFont(nsFont& aFont, float aFontSizeInflation)
      59             : {
      60           0 :   if (aFont.fontlist.GetDefaultFontType() != eFamily_none) {
      61           0 :     aFont.fontlist.Append(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
      62           0 :     aFont.fontlist.SetDefaultFontType(eFamily_none);
      63             :   }
      64           0 :   aFont.size = NSToCoordRound(aFont.size * aFontSizeInflation);
      65           0 : }
      66             : 
      67             : // -----------------------------------------------------------------------------
      68             : static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
      69             : 
      70             : // -----------------------------------------------------------------------------
      71             : // nsGlyphTable is a class that provides an interface for accessing glyphs
      72             : // of stretchy chars. It acts like a table that stores the variants of bigger
      73             : // sizes (if any) and the partial glyphs needed to build extensible symbols.
      74             : //
      75             : // Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
      76             : // Partial glyphs can be retrieved with ElementAt(...).
      77             : //
      78             : // A table consists of "nsGlyphCode"s which are viewed either as Unicode
      79             : // points (for nsPropertiesTable) or as direct glyph indices (for
      80             : // nsOpenTypeTable)
      81             : // -----------------------------------------------------------------------------
      82             : 
      83             : class nsGlyphTable {
      84             : public:
      85           0 :   virtual ~nsGlyphTable() {}
      86             : 
      87             :   virtual const FontFamilyName&
      88             :   FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
      89             : 
      90             :   // Getters for the parts
      91             :   virtual nsGlyphCode ElementAt(DrawTarget*   aDrawTarget,
      92             :                                 int32_t       aAppUnitsPerDevPixel,
      93             :                                 gfxFontGroup* aFontGroup,
      94             :                                 char16_t      aChar,
      95             :                                 bool          aVertical,
      96             :                                 uint32_t      aPosition) = 0;
      97             :   virtual nsGlyphCode BigOf(DrawTarget*   aDrawTarget,
      98             :                             int32_t       aAppUnitsPerDevPixel,
      99             :                             gfxFontGroup* aFontGroup,
     100             :                             char16_t      aChar,
     101             :                             bool          aVertical,
     102             :                             uint32_t      aSize) = 0;
     103             : 
     104             :   // True if this table contains parts to render this char
     105             :   virtual bool HasPartsOf(DrawTarget*   aDrawTarget,
     106             :                           int32_t       aAppUnitsPerDevPixel,
     107             :                           gfxFontGroup* aFontGroup,
     108             :                           char16_t      aChar,
     109             :                           bool          aVertical) = 0;
     110             : 
     111             :   virtual already_AddRefed<gfxTextRun>
     112             :   MakeTextRun(DrawTarget*        aDrawTarget,
     113             :               int32_t            aAppUnitsPerDevPixel,
     114             :               gfxFontGroup*      aFontGroup,
     115             :               const nsGlyphCode& aGlyph) = 0;
     116             : protected:
     117           0 :   nsGlyphTable() : mCharCache(0) {}
     118             :   // For speedy re-use, we always cache the last data used in the table.
     119             :   // mCharCache is the Unicode point of the last char that was queried in this
     120             :   // table.
     121             :   char16_t mCharCache;
     122             : };
     123             : 
     124             : // An instance of nsPropertiesTable is associated with one primary font. Extra
     125             : // glyphs can be taken in other additional fonts when stretching certain
     126             : // characters.
     127             : // These supplementary fonts are referred to as "external" fonts to the table.
     128             : 
     129             : // General format of MathFont Property Files from which glyph data are
     130             : // retrieved:
     131             : // -----------------------------------------------------------------------------
     132             : // Each font should have its set of glyph data. For example, the glyph data for
     133             : // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
     134             : // and "mathfontMTExtra.properties", respectively. The mathfont property file
     135             : // is a set of all the stretchy MathML characters that can be rendered with that
     136             : // font using larger and/or partial glyphs. The entry of each stretchy character
     137             : // in the mathfont property file gives, in that order, the 4 partial glyphs:
     138             : // Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
     139             : // sizes (if any).
     140             : // A position that is not relevant to a particular character is indicated there
     141             : // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
     142             : // -----------------------------------------------------------------------------
     143             : 
     144             : #define NS_TABLE_STATE_ERROR       -1
     145             : #define NS_TABLE_STATE_EMPTY        0
     146             : #define NS_TABLE_STATE_READY        1
     147             : 
     148             : // helper to trim off comments from data in a MathFont Property File
     149             : static void
     150           0 : Clean(nsString& aValue)
     151             : {
     152             :   // chop the trailing # comment portion if any ...
     153           0 :   int32_t comment = aValue.RFindChar('#');
     154           0 :   if (comment > 0) aValue.Truncate(comment);
     155           0 :   aValue.CompressWhitespace();
     156           0 : }
     157             : 
     158             : // helper to load a MathFont Property File
     159             : static nsresult
     160           0 : LoadProperties(const nsString& aName,
     161             :                nsCOMPtr<nsIPersistentProperties>& aProperties)
     162             : {
     163           0 :   nsAutoString uriStr;
     164           0 :   uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
     165           0 :   uriStr.Append(aName);
     166           0 :   uriStr.StripWhitespace(); // that may come from aName
     167           0 :   uriStr.AppendLiteral(".properties");
     168           0 :   return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties),
     169           0 :                                                 NS_ConvertUTF16toUTF8(uriStr));
     170             : }
     171             : 
     172             : class nsPropertiesTable final : public nsGlyphTable {
     173             : public:
     174           0 :   explicit nsPropertiesTable(const nsString& aPrimaryFontName)
     175           0 :     : mState(NS_TABLE_STATE_EMPTY)
     176             :   {
     177           0 :     MOZ_COUNT_CTOR(nsPropertiesTable);
     178           0 :     mGlyphCodeFonts.AppendElement(FontFamilyName(aPrimaryFontName, eUnquotedName));
     179           0 :   }
     180             : 
     181           0 :   ~nsPropertiesTable()
     182           0 :   {
     183           0 :     MOZ_COUNT_DTOR(nsPropertiesTable);
     184           0 :   }
     185             : 
     186           0 :   const FontFamilyName& PrimaryFontName() const
     187             :   {
     188           0 :     return mGlyphCodeFonts[0];
     189             :   }
     190             : 
     191             :   const FontFamilyName&
     192           0 :   FontNameFor(const nsGlyphCode& aGlyphCode) const override
     193             :   {
     194           0 :     NS_ASSERTION(!aGlyphCode.IsGlyphID(),
     195             :                  "nsPropertiesTable can only access glyphs by code point");
     196           0 :     return mGlyphCodeFonts[aGlyphCode.font];
     197             :   }
     198             : 
     199             :   virtual nsGlyphCode ElementAt(DrawTarget*   aDrawTarget,
     200             :                                 int32_t       aAppUnitsPerDevPixel,
     201             :                                 gfxFontGroup* aFontGroup,
     202             :                                 char16_t      aChar,
     203             :                                 bool          aVertical,
     204             :                                 uint32_t      aPosition) override;
     205             : 
     206           0 :   virtual nsGlyphCode BigOf(DrawTarget*   aDrawTarget,
     207             :                             int32_t       aAppUnitsPerDevPixel,
     208             :                             gfxFontGroup* aFontGroup,
     209             :                             char16_t      aChar,
     210             :                             bool          aVertical,
     211             :                             uint32_t      aSize) override
     212             :   {
     213             :     return ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
     214           0 :                      aChar, aVertical, 4 + aSize);
     215             :   }
     216             : 
     217           0 :   virtual bool HasPartsOf(DrawTarget*   aDrawTarget,
     218             :                           int32_t       aAppUnitsPerDevPixel,
     219             :                           gfxFontGroup* aFontGroup,
     220             :                           char16_t      aChar,
     221             :                           bool          aVertical) override
     222             :   {
     223           0 :     return (ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
     224           0 :                       aChar, aVertical, 0).Exists() ||
     225           0 :             ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
     226           0 :                       aChar, aVertical, 1).Exists() ||
     227           0 :             ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
     228           0 :                       aChar, aVertical, 2).Exists() ||
     229           0 :             ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
     230           0 :                       aChar, aVertical, 3).Exists());
     231             :   }
     232             : 
     233             :   virtual already_AddRefed<gfxTextRun>
     234             :   MakeTextRun(DrawTarget*        aDrawTarget,
     235             :               int32_t            aAppUnitsPerDevPixel,
     236             :               gfxFontGroup*      aFontGroup,
     237             :               const nsGlyphCode& aGlyph) override;
     238             : private:
     239             : 
     240             :   // mGlyphCodeFonts[0] is the primary font associated to this table. The
     241             :   // others are possible "external" fonts for glyphs not in the primary font
     242             :   // but which are needed to stretch certain characters in the table
     243             :   nsTArray<FontFamilyName> mGlyphCodeFonts;
     244             : 
     245             :   // Tri-state variable for error/empty/ready
     246             :   int32_t mState;
     247             : 
     248             :   // The set of glyph data in this table, as provided by the MathFont Property
     249             :   // File
     250             :   nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
     251             : 
     252             :   // mGlyphCache is a buffer containing the glyph data associated with
     253             :   // mCharCache.
     254             :   // For a property line 'key = value' in the MathFont Property File,
     255             :   // mCharCache will retain the 'key' -- which is a Unicode point, while
     256             :   // mGlyphCache will retain the 'value', which is a consecutive list of
     257             :   // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
     258             :   // which 'code@0' can be specified
     259             :   // without the optional '@0'. However, to ease subsequent processing,
     260             :   // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
     261             :   // that indicates the primary font identifier. Specifically therefore, the
     262             :   // k-th glyph is characterized by :
     263             :   // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
     264             :   // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
     265             :   // from.
     266             :   // A font identifier of '0' means the default primary font associated to this
     267             :   // table. Other digits map to the "external" fonts that may have been
     268             :   // specified in the MathFont Property File.
     269             :   nsString  mGlyphCache;
     270             : };
     271             : 
     272             : /* virtual */
     273             : nsGlyphCode
     274           0 : nsPropertiesTable::ElementAt(DrawTarget*   /* aDrawTarget */,
     275             :                              int32_t       /* aAppUnitsPerDevPixel */,
     276             :                              gfxFontGroup* /* aFontGroup */,
     277             :                              char16_t      aChar,
     278             :                              bool          /* aVertical */,
     279             :                              uint32_t      aPosition)
     280             : {
     281           0 :   if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
     282             :   // Load glyph properties if this is the first time we have been here
     283           0 :   if (mState == NS_TABLE_STATE_EMPTY) {
     284           0 :     nsAutoString primaryFontName;
     285           0 :     mGlyphCodeFonts[0].AppendToString(primaryFontName);
     286           0 :     nsresult rv = LoadProperties(primaryFontName, mGlyphProperties);
     287             : #ifdef DEBUG
     288           0 :     nsAutoCString uriStr;
     289           0 :     uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
     290           0 :     LossyAppendUTF16toASCII(primaryFontName, uriStr);
     291           0 :     uriStr.StripWhitespace(); // that may come from mGlyphCodeFonts
     292           0 :     uriStr.AppendLiteral(".properties");
     293           0 :     printf("Loading %s ... %s\n",
     294             :             uriStr.get(),
     295           0 :             (NS_FAILED(rv)) ? "Failed" : "Done");
     296             : #endif
     297           0 :     if (NS_FAILED(rv)) {
     298           0 :       mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
     299           0 :       return kNullGlyph;
     300             :     }
     301           0 :     mState = NS_TABLE_STATE_READY;
     302             : 
     303             :     // see if there are external fonts needed for certain chars in this table
     304           0 :     nsAutoCString key;
     305           0 :     nsAutoString value;
     306           0 :     for (int32_t i = 1; ; i++) {
     307           0 :       key.AssignLiteral("external.");
     308           0 :       key.AppendInt(i, 10);
     309           0 :       rv = mGlyphProperties->GetStringProperty(key, value);
     310           0 :       if (NS_FAILED(rv)) break;
     311           0 :       Clean(value);
     312           0 :       mGlyphCodeFonts.AppendElement(FontFamilyName(value, eUnquotedName)); // i.e., mGlyphCodeFonts[i] holds this font name
     313             :     }
     314             :   }
     315             : 
     316             :   // Update our cache if it is not associated to this character
     317           0 :   if (mCharCache != aChar) {
     318             :     // The key in the property file is interpreted as ASCII and kept
     319             :     // as such ...
     320           0 :     char key[10]; SprintfLiteral(key, "\\u%04X", aChar);
     321           0 :     nsAutoString value;
     322           0 :     nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key),
     323           0 :                                                       value);
     324           0 :     if (NS_FAILED(rv)) return kNullGlyph;
     325           0 :     Clean(value);
     326             :     // See if this char uses external fonts; e.g., if the 2nd glyph is taken
     327             :     // from the external font '1', the property line looks like
     328             :     // \uNNNN = \uNNNN\uNNNN@1\uNNNN.
     329             :     // This is where mGlyphCache is pre-processed to explicitly store all glyph
     330             :     // codes as combined pairs of 'code@font', excluding the '@' separator. This
     331             :     // means that mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered
     332             :     // with mGlyphCodeFonts[mGlyphCache[3*k+2]]
     333             :     // Note: font identifier is internally an ASCII digit to avoid the null
     334             :     // char issue
     335           0 :     nsAutoString buffer;
     336           0 :     int32_t length = value.Length();
     337           0 :     int32_t i = 0; // index in value
     338           0 :     while (i < length) {
     339           0 :       char16_t code = value[i];
     340           0 :       ++i;
     341           0 :       buffer.Append(code);
     342             :       // Read the next word if we have a non-BMP character.
     343           0 :       if (i < length && NS_IS_HIGH_SURROGATE(code)) {
     344           0 :         code = value[i];
     345           0 :         ++i;
     346             :       } else {
     347           0 :         code = char16_t('\0');
     348             :       }
     349           0 :       buffer.Append(code);
     350             : 
     351             :       // See if an external font is needed for the code point.
     352             :       // Limit of 9 external fonts
     353           0 :       char16_t font = 0;
     354           0 :       if (i+1 < length && value[i] == char16_t('@') &&
     355           0 :           value[i+1] >= char16_t('0') && value[i+1] <= char16_t('9')) {
     356           0 :         ++i;
     357           0 :         font = value[i] - '0';
     358           0 :         ++i;
     359           0 :         if (font >= mGlyphCodeFonts.Length()) {
     360           0 :           NS_ERROR("Nonexistent font referenced in glyph table");
     361           0 :           return kNullGlyph;
     362             :         }
     363             :         // The char cannot be handled if this font is not installed
     364           0 :         if (!mGlyphCodeFonts[font].mName.Length()) {
     365           0 :           return kNullGlyph;
     366             :         }
     367             :       }
     368           0 :       buffer.Append(font);
     369             :     }
     370             :     // update our cache with the new settings
     371           0 :     mGlyphCache.Assign(buffer);
     372           0 :     mCharCache = aChar;
     373             :   }
     374             : 
     375             :   // 3* is to account for the code@font pairs
     376           0 :   uint32_t index = 3*aPosition;
     377           0 :   if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
     378             :   nsGlyphCode ch;
     379           0 :   ch.code[0] = mGlyphCache.CharAt(index);
     380           0 :   ch.code[1] = mGlyphCache.CharAt(index + 1);
     381           0 :   ch.font = mGlyphCache.CharAt(index + 2);
     382           0 :   return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
     383             : }
     384             : 
     385             : /* virtual */
     386             : already_AddRefed<gfxTextRun>
     387           0 : nsPropertiesTable::MakeTextRun(DrawTarget*        aDrawTarget,
     388             :                                int32_t            aAppUnitsPerDevPixel,
     389             :                                gfxFontGroup*      aFontGroup,
     390             :                                const nsGlyphCode& aGlyph)
     391             : {
     392           0 :   NS_ASSERTION(!aGlyph.IsGlyphID(),
     393             :                "nsPropertiesTable can only access glyphs by code point");
     394             :   return aFontGroup->
     395           0 :     MakeTextRun(aGlyph.code, aGlyph.Length(), aDrawTarget, aAppUnitsPerDevPixel,
     396           0 :                 gfx::ShapedTextFlags(), nsTextFrameUtils::Flags(), nullptr);
     397             : }
     398             : 
     399             : // An instance of nsOpenTypeTable is associated with one gfxFontEntry that
     400             : // corresponds to an Open Type font with a MATH table. All the glyphs come from
     401             : // the same font and the calls to access size variants and parts are directly
     402             : // forwarded to the gfx code.
     403             : class nsOpenTypeTable final : public nsGlyphTable {
     404             : public:
     405           0 :   ~nsOpenTypeTable()
     406           0 :   {
     407           0 :     MOZ_COUNT_DTOR(nsOpenTypeTable);
     408           0 :   }
     409             : 
     410             :   virtual nsGlyphCode ElementAt(DrawTarget*   aDrawTarget,
     411             :                                 int32_t       aAppUnitsPerDevPixel,
     412             :                                 gfxFontGroup* aFontGroup,
     413             :                                 char16_t      aChar,
     414             :                                 bool          aVertical,
     415             :                                 uint32_t      aPosition) override;
     416             :   virtual nsGlyphCode BigOf(DrawTarget*   aDrawTarget,
     417             :                             int32_t       aAppUnitsPerDevPixel,
     418             :                             gfxFontGroup* aFontGroup,
     419             :                             char16_t      aChar,
     420             :                             bool          aVertical,
     421             :                             uint32_t      aSize) override;
     422             :   virtual bool HasPartsOf(DrawTarget*   aDrawTarget,
     423             :                           int32_t       aAppUnitsPerDevPixel,
     424             :                           gfxFontGroup* aFontGroup,
     425             :                           char16_t      aChar,
     426             :                           bool          aVertical) override;
     427             : 
     428             :   const FontFamilyName&
     429           0 :   FontNameFor(const nsGlyphCode& aGlyphCode) const override {
     430           0 :     NS_ASSERTION(aGlyphCode.IsGlyphID(),
     431             :                  "nsOpenTypeTable can only access glyphs by id");
     432           0 :     return mFontFamilyName;
     433             :   }
     434             : 
     435             :   virtual already_AddRefed<gfxTextRun>
     436             :   MakeTextRun(DrawTarget*        aDrawTarget,
     437             :               int32_t            aAppUnitsPerDevPixel,
     438             :               gfxFontGroup*      aFontGroup,
     439             :               const nsGlyphCode& aGlyph) override;
     440             : 
     441             :   // This returns a new OpenTypeTable instance to give access to OpenType MATH
     442             :   // table or nullptr if the font does not have such table. Ownership is passed
     443             :   // to the caller.
     444           0 :   static nsOpenTypeTable* Create(gfxFont* aFont)
     445             :   {
     446           0 :     if (!aFont->TryGetMathTable()) {
     447           0 :       return nullptr;
     448             :     }
     449           0 :     return new nsOpenTypeTable(aFont);
     450             :   }
     451             : 
     452             : private:
     453             :   RefPtr<gfxFont> mFont;
     454             :   FontFamilyName mFontFamilyName;
     455             :   uint32_t mGlyphID;
     456             : 
     457           0 :   explicit nsOpenTypeTable(gfxFont* aFont)
     458           0 :     : mFont(aFont),
     459           0 :     mFontFamilyName(aFont->GetFontEntry()->FamilyName(), eUnquotedName) {
     460           0 :     MOZ_COUNT_CTOR(nsOpenTypeTable);
     461           0 :   }
     462             : 
     463             :   void UpdateCache(DrawTarget*   aDrawTarget,
     464             :                    int32_t       aAppUnitsPerDevPixel,
     465             :                    gfxFontGroup* aFontGroup,
     466             :                    char16_t      aChar);
     467             : };
     468             : 
     469             : void
     470           0 : nsOpenTypeTable::UpdateCache(DrawTarget*   aDrawTarget,
     471             :                              int32_t       aAppUnitsPerDevPixel,
     472             :                              gfxFontGroup* aFontGroup,
     473             :                              char16_t      aChar)
     474             : {
     475           0 :   if (mCharCache != aChar) {
     476             :     RefPtr<gfxTextRun> textRun = aFontGroup->
     477           0 :       MakeTextRun(&aChar, 1, aDrawTarget, aAppUnitsPerDevPixel,
     478             :                   gfx::ShapedTextFlags(), nsTextFrameUtils::Flags(),
     479           0 :                   nullptr);
     480           0 :     const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
     481           0 :     if (data.IsSimpleGlyph()) {
     482           0 :       mGlyphID = data.GetSimpleGlyph();
     483           0 :     } else if (data.GetGlyphCount() == 1) {
     484           0 :       mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
     485             :     } else {
     486           0 :       mGlyphID = 0;
     487             :     }
     488           0 :     mCharCache = aChar;
     489             :   }
     490           0 : }
     491             : 
     492             : /* virtual */
     493             : nsGlyphCode
     494           0 : nsOpenTypeTable::ElementAt(DrawTarget*   aDrawTarget,
     495             :                            int32_t       aAppUnitsPerDevPixel,
     496             :                            gfxFontGroup* aFontGroup,
     497             :                            char16_t      aChar,
     498             :                            bool          aVertical,
     499             :                            uint32_t      aPosition)
     500             : {
     501           0 :   UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
     502             : 
     503             :   uint32_t parts[4];
     504           0 :   if (!mFont->MathTable()->VariantsParts(mGlyphID, aVertical, parts)) {
     505           0 :     return kNullGlyph;
     506             :   }
     507             : 
     508           0 :   uint32_t glyphID = parts[aPosition];
     509           0 :   if (!glyphID) {
     510           0 :     return kNullGlyph;
     511             :   }
     512             :   nsGlyphCode glyph;
     513           0 :   glyph.glyphID = glyphID;
     514           0 :   glyph.font = -1;
     515           0 :   return glyph;
     516             : }
     517             : 
     518             : /* virtual */
     519             : nsGlyphCode
     520           0 : nsOpenTypeTable::BigOf(DrawTarget*   aDrawTarget,
     521             :                        int32_t       aAppUnitsPerDevPixel,
     522             :                        gfxFontGroup* aFontGroup,
     523             :                        char16_t      aChar,
     524             :                        bool          aVertical,
     525             :                        uint32_t      aSize)
     526             : {
     527           0 :   UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
     528             : 
     529             :   uint32_t glyphID =
     530           0 :     mFont->MathTable()->VariantsSize(mGlyphID, aVertical, aSize);
     531           0 :   if (!glyphID) {
     532           0 :     return kNullGlyph;
     533             :   }
     534             : 
     535             :   nsGlyphCode glyph;
     536           0 :   glyph.glyphID = glyphID;
     537           0 :   glyph.font = -1;
     538           0 :   return glyph;
     539             : }
     540             : 
     541             : /* virtual */
     542             : bool
     543           0 : nsOpenTypeTable::HasPartsOf(DrawTarget*   aDrawTarget,
     544             :                             int32_t       aAppUnitsPerDevPixel,
     545             :                             gfxFontGroup* aFontGroup,
     546             :                             char16_t      aChar,
     547             :                             bool          aVertical)
     548             : {
     549           0 :   UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
     550             : 
     551             :   uint32_t parts[4];
     552           0 :   if (!mFont->MathTable()->VariantsParts(mGlyphID, aVertical, parts)) {
     553           0 :     return false;
     554             :   }
     555             : 
     556           0 :   return parts[0] || parts[1] || parts[2] || parts[3];
     557             : }
     558             : 
     559             : /* virtual */
     560             : already_AddRefed<gfxTextRun>
     561           0 : nsOpenTypeTable::MakeTextRun(DrawTarget*        aDrawTarget,
     562             :                              int32_t            aAppUnitsPerDevPixel,
     563             :                              gfxFontGroup*      aFontGroup,
     564             :                              const nsGlyphCode& aGlyph)
     565             : {
     566           0 :   NS_ASSERTION(aGlyph.IsGlyphID(),
     567             :                "nsOpenTypeTable can only access glyphs by id");
     568             : 
     569             :   gfxTextRunFactory::Parameters params = {
     570             :     aDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
     571           0 :   };
     572             :   RefPtr<gfxTextRun> textRun =
     573           0 :     gfxTextRun::Create(&params, 1, aFontGroup,
     574           0 :                        gfx::ShapedTextFlags(), nsTextFrameUtils::Flags());
     575           0 :   textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(),
     576             :                        gfxTextRange::kFontGroup, 0,
     577           0 :                        false, gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL);
     578             :                               // We don't care about CSS writing mode here;
     579             :                               // math runs are assumed to be horizontal.
     580             :   gfxTextRun::DetailedGlyph detailedGlyph;
     581           0 :   detailedGlyph.mGlyphID = aGlyph.glyphID;
     582           0 :   detailedGlyph.mAdvance =
     583           0 :     NSToCoordRound(aAppUnitsPerDevPixel *
     584             :                    aFontGroup->GetFirstValidFont()->
     585           0 :                    GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
     586           0 :   detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
     587           0 :   gfxShapedText::CompressedGlyph g;
     588           0 :   g.SetComplex(true, true, 1);
     589           0 :   textRun->SetGlyphs(0, g, &detailedGlyph);
     590             : 
     591           0 :   return textRun.forget();
     592             : }
     593             : 
     594             : // -----------------------------------------------------------------------------
     595             : // This is the list of all the applicable glyph tables.
     596             : // We will maintain a single global instance that will only reveal those
     597             : // glyph tables that are associated to fonts currently installed on the
     598             : // user' system. The class is an XPCOM shutdown observer to allow us to
     599             : // free its allocated data at shutdown
     600             : 
     601             : class nsGlyphTableList final : public nsIObserver
     602             : {
     603             : public:
     604             :   NS_DECL_ISUPPORTS
     605             :   NS_DECL_NSIOBSERVER
     606             : 
     607             :   nsPropertiesTable mUnicodeTable;
     608             : 
     609           0 :   nsGlyphTableList()
     610           0 :     : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
     611             :   {
     612           0 :   }
     613             : 
     614             :   nsresult Initialize();
     615             :   nsresult Finalize();
     616             : 
     617             :   // Add a glyph table in the list, return the new table that was added
     618             :   nsGlyphTable*
     619             :   AddGlyphTable(const nsString& aPrimaryFontName);
     620             : 
     621             :   // Find the glyph table in the list corresponding to the given font family.
     622             :   nsGlyphTable*
     623             :   GetGlyphTableFor(const nsAString& aFamily);
     624             : 
     625             : private:
     626           0 :   ~nsGlyphTableList()
     627           0 :   {
     628           0 :   }
     629             : 
     630           0 :   nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
     631           0 :     return &mPropertiesTableList.ElementAt(aIndex);
     632             :   }
     633           0 :   int32_t PropertiesTableCount() {
     634           0 :     return mPropertiesTableList.Length();
     635             :   }
     636             :   // List of glyph tables;
     637             :   nsTArray<nsPropertiesTable> mPropertiesTableList;
     638             : };
     639             : 
     640           0 : NS_IMPL_ISUPPORTS(nsGlyphTableList, nsIObserver)
     641             : 
     642             : // -----------------------------------------------------------------------------
     643             : // Here is the global list of applicable glyph tables that we will be using
     644             : static nsGlyphTableList* gGlyphTableList = nullptr;
     645             : 
     646             : static bool gGlyphTableInitialized = false;
     647             : 
     648             : // XPCOM shutdown observer
     649             : NS_IMETHODIMP
     650           0 : nsGlyphTableList::Observe(nsISupports*     aSubject,
     651             :                           const char* aTopic,
     652             :                           const char16_t* someData)
     653             : {
     654           0 :   Finalize();
     655           0 :   return NS_OK;
     656             : }
     657             : 
     658             : // Add an observer to XPCOM shutdown so that we can free our data at shutdown
     659             : nsresult
     660           0 : nsGlyphTableList::Initialize()
     661             : {
     662           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     663           0 :   if (!obs)
     664           0 :     return NS_ERROR_FAILURE;
     665             : 
     666           0 :   nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     667           0 :   NS_ENSURE_SUCCESS(rv, rv);
     668             : 
     669           0 :   return NS_OK;
     670             : }
     671             : 
     672             : // Remove our observer and free the memory that were allocated for us
     673             : nsresult
     674           0 : nsGlyphTableList::Finalize()
     675             : {
     676             :   // Remove our observer from the observer service
     677           0 :   nsresult rv = NS_OK;
     678           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     679           0 :   if (obs)
     680           0 :     rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     681             :   else
     682           0 :     rv = NS_ERROR_FAILURE;
     683             : 
     684           0 :   gGlyphTableInitialized = false;
     685             :   // our oneself will be destroyed when our |Release| is called by the observer
     686           0 :   NS_IF_RELEASE(gGlyphTableList);
     687           0 :   return rv;
     688             : }
     689             : 
     690             : nsGlyphTable*
     691           0 : nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
     692             : {
     693             :   // See if there is already a special table for this family.
     694           0 :   nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
     695           0 :   if (glyphTable != &mUnicodeTable)
     696           0 :     return glyphTable;
     697             : 
     698             :   // allocate a table
     699           0 :   glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
     700           0 :   return glyphTable;
     701             : }
     702             : 
     703             : nsGlyphTable*
     704           0 : nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
     705             : {
     706           0 :   for (int32_t i = 0; i < PropertiesTableCount(); i++) {
     707           0 :     nsPropertiesTable* glyphTable = PropertiesTableAt(i);
     708           0 :     const FontFamilyName& primaryFontName = glyphTable->PrimaryFontName();
     709           0 :     nsAutoString primaryFontNameStr;
     710           0 :     primaryFontName.AppendToString(primaryFontNameStr);
     711             :     // TODO: would be nice to consider StripWhitespace and other aliasing
     712           0 :     if (primaryFontNameStr.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
     713           0 :       return glyphTable;
     714             :     }
     715             :   }
     716             :   // Fall back to default Unicode table
     717           0 :   return &mUnicodeTable;
     718             : }
     719             : 
     720             : // -----------------------------------------------------------------------------
     721             : 
     722             : static nsresult
     723           0 : InitCharGlobals()
     724             : {
     725           0 :   NS_ASSERTION(!gGlyphTableInitialized, "Error -- already initialized");
     726           0 :   gGlyphTableInitialized = true;
     727             : 
     728             :   // Allocate the placeholders for the preferred parts and variants
     729           0 :   nsresult rv = NS_ERROR_OUT_OF_MEMORY;
     730           0 :   RefPtr<nsGlyphTableList> glyphTableList = new nsGlyphTableList();
     731           0 :   if (glyphTableList) {
     732           0 :     rv = glyphTableList->Initialize();
     733             :   }
     734           0 :   if (NS_FAILED(rv)) {
     735           0 :     return rv;
     736             :   }
     737             :   // The gGlyphTableList has been successfully registered as a shutdown
     738             :   // observer and will be deleted at shutdown. We now add some private
     739             :   // per font-family tables for stretchy operators, in order of preference.
     740             :   // Do not include the Unicode table in this list.
     741           0 :   if (!glyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXGeneral"))) {
     742           0 :     rv = NS_ERROR_OUT_OF_MEMORY;
     743             :   }
     744             : 
     745           0 :   glyphTableList.forget(&gGlyphTableList);
     746           0 :   return rv;
     747             : }
     748             : 
     749             : // -----------------------------------------------------------------------------
     750             : // And now the implementation of nsMathMLChar
     751             : 
     752           0 : nsMathMLChar::~nsMathMLChar()
     753             : {
     754           0 :   MOZ_COUNT_DTOR(nsMathMLChar);
     755           0 :   mStyleContext->Release();
     756           0 : }
     757             : 
     758             : nsStyleContext*
     759           0 : nsMathMLChar::GetStyleContext() const
     760             : {
     761           0 :   NS_ASSERTION(mStyleContext, "chars should always have style context");
     762           0 :   return mStyleContext;
     763             : }
     764             : 
     765             : void
     766           0 : nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
     767             : {
     768           0 :   NS_PRECONDITION(aStyleContext, "null ptr");
     769           0 :   if (aStyleContext != mStyleContext) {
     770           0 :     if (mStyleContext)
     771           0 :       mStyleContext->Release();
     772           0 :     if (aStyleContext) {
     773           0 :       mStyleContext = aStyleContext;
     774           0 :       aStyleContext->AddRef();
     775             :     }
     776             :   }
     777           0 : }
     778             : 
     779             : void
     780           0 : nsMathMLChar::SetData(nsString& aData)
     781             : {
     782           0 :   if (!gGlyphTableInitialized) {
     783           0 :     InitCharGlobals();
     784             :   }
     785           0 :   mData = aData;
     786             :   // some assumptions until proven otherwise
     787             :   // note that mGlyph is not initialized
     788           0 :   mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
     789           0 :   mBoundingMetrics = nsBoundingMetrics();
     790             :   // check if stretching is applicable ...
     791           0 :   if (gGlyphTableList && (1 == mData.Length())) {
     792           0 :     mDirection = nsMathMLOperators::GetStretchyDirection(mData);
     793             :     // default tentative table (not the one that is necessarily going
     794             :     // to be used)
     795             :   }
     796           0 : }
     797             : 
     798             : // -----------------------------------------------------------------------------
     799             : /*
     800             :  The Stretch:
     801             :  @param aContainerSize - suggested size for the stretched char
     802             :  @param aDesiredStretchSize - OUT parameter. The desired size
     803             :  after stretching. If no stretching is done, the output will
     804             :  simply give the base size.
     805             : 
     806             :  How it works?
     807             :  Summary:-
     808             :  The Stretch() method first looks for a glyph of appropriate
     809             :  size; If a glyph is found, it is cached by this object and
     810             :  its size is returned in aDesiredStretchSize. The cached
     811             :  glyph will then be used at the painting stage.
     812             :  If no glyph of appropriate size is found, a search is made
     813             :  to see if the char can be built by parts.
     814             : 
     815             :  Details:-
     816             :  A character gets stretched through the following pipeline :
     817             : 
     818             :  1) If the base size of the char is sufficient to cover the
     819             :     container' size, we use that. If not, it will still be
     820             :     used as a fallback if the other stages in the pipeline fail.
     821             :     Issues :
     822             :     a) The base size, the parts and the variants of a char can
     823             :        be in different fonts. For eg., the base size for '(' should
     824             :        come from a normal ascii font if CMEX10 is used, since CMEX10
     825             :        only contains the stretched versions. Hence, there are two
     826             :        style contexts in use throughout the process. The leaf style
     827             :        context of the char holds fonts with which to try to stretch
     828             :        the char. The parent style context of the char contains fonts
     829             :        for normal rendering. So the parent context is the one used
     830             :        to get the initial base size at the start of the pipeline.
     831             :     b) For operators that can be largeop's in display mode,
     832             :        we will skip the base size even if it fits, so that
     833             :        the next stage in the pipeline is given a chance to find
     834             :        a largeop variant. If the next stage fails, we fallback
     835             :        to the base size.
     836             : 
     837             :  2) We search for the first larger variant of the char that fits the
     838             :     container' size.  We first search for larger variants using the glyph
     839             :     table corresponding to the first existing font specified in the list of
     840             :     stretchy fonts held by the leaf style context (from -moz-math-stretchy in
     841             :     mathml.css).  Generic fonts are resolved by the preference
     842             :     "font.mathfont-family".
     843             :     Issues :
     844             :     a) the largeop and display settings determine the starting
     845             :        size when we do the above search, regardless of whether
     846             :        smaller variants already fit the container' size.
     847             :     b) if it is a largeopOnly request (i.e., a displaystyle operator
     848             :        with largeop=true and stretchy=false), we break after finding
     849             :        the first starting variant, regardless of whether that
     850             :        variant fits the container's size.
     851             : 
     852             :  3) If a variant of appropriate size wasn't found, we see if the char
     853             :     can be built by parts using the same glyph table.
     854             :     Issue:
     855             :        There are chars that have no middle and glue glyphs. For
     856             :        such chars, the parts need to be joined using the rule.
     857             :        By convention (TeXbook p.225), the descent of the parts is
     858             :        zero while their ascent gives the thickness of the rule that
     859             :        should be used to join them.
     860             : 
     861             :  4) If a match was not found in that glyph table, repeat from 2 to search the
     862             :     ordered list of stretchy fonts for the first font with a glyph table that
     863             :     provides a fit to the container size.  If no fit is found, the closest fit
     864             :     is used.
     865             : 
     866             :  Of note:
     867             :  When the pipeline completes successfully, the desired size of the
     868             :  stretched char can actually be slightly larger or smaller than
     869             :  aContainerSize. But it is the responsibility of the caller to
     870             :  account for the spacing when setting aContainerSize, and to leave
     871             :  any extra margin when placing the stretched char.
     872             : */
     873             : // -----------------------------------------------------------------------------
     874             : 
     875             : 
     876             : // plain TeX settings (TeXbook p.152)
     877             : #define NS_MATHML_DELIMITER_FACTOR             0.901f
     878             : #define NS_MATHML_DELIMITER_SHORTFALL_POINTS   5.0f
     879             : 
     880             : static bool
     881           0 : IsSizeOK(nscoord a, nscoord b, uint32_t aHint)
     882             : {
     883             :   // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
     884             :   // 1-DelimiterFactor). This often gives a chance to the base size to
     885             :   // win, especially in the context of <mfenced> without tall elements
     886             :   // or in sloppy markups without protective <mrow></mrow>
     887             :   bool isNormal =
     888           0 :     (aHint & NS_STRETCH_NORMAL) &&
     889           0 :     Abs<float>(a - b) < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b);
     890             : 
     891             :   // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
     892             :   // as documented in The TeXbook, Ch.17, p.152.
     893             :   // i.e. within 10% and within 5pt
     894           0 :   bool isNearer = false;
     895           0 :   if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
     896           0 :     float c = std::max(float(b) * NS_MATHML_DELIMITER_FACTOR,
     897           0 :                      float(b) - nsPresContext::
     898           0 :                      CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
     899           0 :     isNearer = Abs<float>(b - a) <= float(b) - c;
     900             :   }
     901             : 
     902             :   // Smaller: Mainly for transitory use, to compare two candidate
     903             :   // choices
     904             :   bool isSmaller =
     905           0 :     (aHint & NS_STRETCH_SMALLER) &&
     906           0 :     float(a) >= NS_MATHML_DELIMITER_FACTOR * float(b) &&
     907           0 :     a <= b;
     908             : 
     909             :   // Larger: Critical to the sqrt code to ensure that the radical
     910             :   // size is tall enough
     911             :   bool isLarger =
     912           0 :     (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) &&
     913           0 :     a >= b;
     914             : 
     915           0 :   return (isNormal || isSmaller || isNearer || isLarger);
     916             : }
     917             : 
     918             : static bool
     919           0 : IsSizeBetter(nscoord a, nscoord olda, nscoord b, uint32_t aHint)
     920             : {
     921           0 :   if (0 == olda)
     922           0 :     return true;
     923           0 :   if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
     924           0 :     return (a >= olda) ? (olda < b) : (a >= b);
     925           0 :   if (aHint & NS_STRETCH_SMALLER)
     926           0 :     return (a <= olda) ? (olda > b) : (a <= b);
     927             : 
     928             :   // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
     929           0 :   return Abs(a - b) < Abs(olda - b);
     930             : }
     931             : 
     932             : // We want to place the glyphs even when they don't fit at their
     933             : // full extent, i.e., we may clip to tolerate a small amount of
     934             : // overlap between the parts. This is important to cater for fonts
     935             : // with long glues.
     936             : static nscoord
     937           0 : ComputeSizeFromParts(nsPresContext* aPresContext,
     938             :                      nsGlyphCode* aGlyphs,
     939             :                      nscoord*     aSizes,
     940             :                      nscoord      aTargetSize)
     941             : {
     942             :   enum {first, middle, last, glue};
     943             :   // Add the parts that cannot be left out.
     944           0 :   nscoord sum = 0;
     945           0 :   for (int32_t i = first; i <= last; i++) {
     946           0 :     sum += aSizes[i];
     947             :   }
     948             : 
     949             :   // Determine how much is used in joins
     950           0 :   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
     951           0 :   int32_t joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
     952             : 
     953             :   // Pick a maximum size using a maximum number of glue glyphs that we are
     954             :   // prepared to draw for one character.
     955           0 :   const int32_t maxGlyphs = 1000;
     956             : 
     957             :   // This also takes into account the fact that, if the glue has no size,
     958             :   // then the character can't be lengthened.
     959           0 :   nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
     960           0 :   if (maxSize < aTargetSize)
     961           0 :     return maxSize; // settle with the maximum size
     962             : 
     963             :   // Get the minimum allowable size using some flex.
     964           0 :   nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
     965             : 
     966           0 :   if (minSize > aTargetSize)
     967           0 :     return minSize; // settle with the minimum size
     968             : 
     969             :   // Fill-up the target area
     970           0 :   return aTargetSize;
     971             : }
     972             : 
     973             : // Update the font if there is a family change and returns the font group.
     974             : bool
     975           0 : nsMathMLChar::SetFontFamily(nsPresContext*          aPresContext,
     976             :                             const nsGlyphTable*     aGlyphTable,
     977             :                             const nsGlyphCode&      aGlyphCode,
     978             :                             const FontFamilyList&   aDefaultFamilyList,
     979             :                             nsFont&                 aFont,
     980             :                             RefPtr<gfxFontGroup>* aFontGroup)
     981             : {
     982           0 :   FontFamilyList glyphCodeFont;
     983             : 
     984           0 :   if (aGlyphCode.font) {
     985           0 :     glyphCodeFont.Append(aGlyphTable->FontNameFor(aGlyphCode));
     986             :   }
     987             : 
     988             :   const FontFamilyList& familyList =
     989           0 :     aGlyphCode.font ? glyphCodeFont : aDefaultFamilyList;
     990             : 
     991           0 :   if (!*aFontGroup || !(aFont.fontlist == familyList)) {
     992           0 :     nsFont font = aFont;
     993           0 :     font.fontlist = familyList;
     994           0 :     const nsStyleFont* styleFont = mStyleContext->StyleFont();
     995           0 :     nsFontMetrics::Params params;
     996           0 :     params.language = styleFont->mLanguage;
     997           0 :     params.explicitLanguage = styleFont->mExplicitLanguage;
     998           0 :     params.userFontSet = aPresContext->GetUserFontSet();
     999           0 :     params.textPerf = aPresContext->GetTextPerfMetrics();
    1000             :     RefPtr<nsFontMetrics> fm =
    1001           0 :       aPresContext->DeviceContext()->GetMetricsFor(font, params);
    1002             :     // Set the font if it is an unicode table
    1003             :     // or if the same family name has been found
    1004           0 :     gfxFont *firstFont = fm->GetThebesFontGroup()->GetFirstValidFont();
    1005           0 :     FontFamilyList firstFontList;
    1006             :     firstFontList.Append(
    1007           0 :       FontFamilyName(firstFont->GetFontEntry()->FamilyName(), eUnquotedName));
    1008           0 :     if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
    1009           0 :         firstFontList == familyList) {
    1010           0 :       aFont.fontlist = familyList;
    1011           0 :       *aFontGroup = fm->GetThebesFontGroup();
    1012             :     } else {
    1013           0 :       return false; // We did not set the font
    1014             :     }
    1015             :   }
    1016           0 :   return true;
    1017             : }
    1018             : 
    1019             : static nsBoundingMetrics
    1020           0 : MeasureTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun)
    1021             : {
    1022             :   gfxTextRun::Metrics metrics =
    1023           0 :     aTextRun->MeasureText(gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS, aDrawTarget);
    1024             : 
    1025           0 :   nsBoundingMetrics bm;
    1026           0 :   bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
    1027           0 :   bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
    1028           0 :   bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
    1029           0 :   bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
    1030           0 :   bm.width = NSToCoordRound(metrics.mAdvanceWidth);
    1031             : 
    1032           0 :   return bm;
    1033             : }
    1034             : 
    1035           0 : class nsMathMLChar::StretchEnumContext {
    1036             : public:
    1037           0 :   StretchEnumContext(nsMathMLChar*        aChar,
    1038             :                      nsPresContext*       aPresContext,
    1039             :                      DrawTarget*          aDrawTarget,
    1040             :                      float                aFontSizeInflation,
    1041             :                      nsStretchDirection   aStretchDirection,
    1042             :                      nscoord              aTargetSize,
    1043             :                      uint32_t             aStretchHint,
    1044             :                      nsBoundingMetrics&   aStretchedMetrics,
    1045             :                      const FontFamilyList&  aFamilyList,
    1046             :                      bool&              aGlyphFound)
    1047           0 :     : mChar(aChar),
    1048             :       mPresContext(aPresContext),
    1049             :       mDrawTarget(aDrawTarget),
    1050             :       mFontSizeInflation(aFontSizeInflation),
    1051             :       mDirection(aStretchDirection),
    1052             :       mTargetSize(aTargetSize),
    1053             :       mStretchHint(aStretchHint),
    1054             :       mBoundingMetrics(aStretchedMetrics),
    1055             :       mFamilyList(aFamilyList),
    1056             :       mTryVariants(true),
    1057             :       mTryParts(true),
    1058           0 :       mGlyphFound(aGlyphFound) {}
    1059             : 
    1060             :   static bool
    1061             :   EnumCallback(const FontFamilyName& aFamily, bool aGeneric, void *aData);
    1062             : 
    1063             : private:
    1064             :   bool TryVariants(nsGlyphTable* aGlyphTable,
    1065             :                    RefPtr<gfxFontGroup>* aFontGroup,
    1066             :                    const FontFamilyList& aFamilyList);
    1067             :   bool TryParts(nsGlyphTable* aGlyphTable,
    1068             :                 RefPtr<gfxFontGroup>* aFontGroup,
    1069             :                 const FontFamilyList& aFamilyList);
    1070             : 
    1071             :   nsMathMLChar* mChar;
    1072             :   nsPresContext* mPresContext;
    1073             :   DrawTarget* mDrawTarget;
    1074             :   float mFontSizeInflation;
    1075             :   const nsStretchDirection mDirection;
    1076             :   const nscoord mTargetSize;
    1077             :   const uint32_t mStretchHint;
    1078             :   nsBoundingMetrics& mBoundingMetrics;
    1079             :   // Font families to search
    1080             :   const FontFamilyList& mFamilyList;
    1081             : 
    1082             : public:
    1083             :   bool mTryVariants;
    1084             :   bool mTryParts;
    1085             : 
    1086             : private:
    1087             :   AutoTArray<nsGlyphTable*,16> mTablesTried;
    1088             :   bool&       mGlyphFound;
    1089             : };
    1090             : 
    1091             : 
    1092             : // 2. See if there are any glyphs of the appropriate size.
    1093             : // Returns true if the size is OK, false to keep searching.
    1094             : // Always updates the char if a better match is found.
    1095             : bool
    1096           0 : nsMathMLChar::
    1097             : StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
    1098             :                                 RefPtr<gfxFontGroup>* aFontGroup,
    1099             :                                 const FontFamilyList& aFamilyList)
    1100             : {
    1101             :   // Use our stretchy style context now that stretching is in progress
    1102           0 :   nsStyleContext *sc = mChar->mStyleContext;
    1103           0 :   nsFont font = sc->StyleFont()->mFont;
    1104           0 :   NormalizeDefaultFont(font, mFontSizeInflation);
    1105             : 
    1106           0 :   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
    1107           0 :   nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
    1108           0 :   char16_t uchar = mChar->mData[0];
    1109           0 :   bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
    1110             :   bool largeopOnly =
    1111           0 :     largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
    1112           0 :   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
    1113             : 
    1114             :   nscoord bestSize =
    1115           0 :     isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
    1116           0 :                : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    1117           0 :   bool haveBetter = false;
    1118             : 
    1119             :   // start at size = 1 (size = 0 is the char at its normal size)
    1120           0 :   int32_t size = 1;
    1121             :   nsGlyphCode ch;
    1122           0 :   nscoord displayOperatorMinHeight = 0;
    1123           0 :   if (largeopOnly) {
    1124           0 :     NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
    1125             :     ch = aGlyphTable->BigOf(mDrawTarget, oneDevPixel, *aFontGroup, uchar,
    1126           0 :                             isVertical, 0);
    1127           0 :     if (ch.IsGlyphID()) {
    1128           0 :       gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
    1129             :       // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
    1130             :       // to select the right size variant. Note that the value is sometimes too
    1131             :       // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
    1132           0 :       if (mathFont) {
    1133             :         displayOperatorMinHeight = mathFont->MathTable()->
    1134           0 :           Constant(gfxMathTable::DisplayOperatorMinHeight, oneDevPixel);
    1135             :         RefPtr<gfxTextRun> textRun =
    1136           0 :           aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
    1137           0 :         nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
    1138           0 :         float largeopFactor = kLargeOpFactor;
    1139           0 :         if (NS_STRETCH_INTEGRAL & mStretchHint) {
    1140             :           // integrals are drawn taller
    1141           0 :           largeopFactor = kIntegralFactor;
    1142             :         }
    1143           0 :         nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
    1144           0 :         if (displayOperatorMinHeight < minHeight) {
    1145           0 :           displayOperatorMinHeight = minHeight;
    1146             :         }
    1147             :       }
    1148             :     }
    1149             :   }
    1150             : #ifdef NOISY_SEARCH
    1151             :   printf("  searching in %s ...\n",
    1152             :            NS_LossyConvertUTF16toASCII(aFamily).get());
    1153             : #endif
    1154           0 :   while ((ch = aGlyphTable->BigOf(mDrawTarget, oneDevPixel, *aFontGroup,
    1155           0 :                                   uchar, isVertical, size)).Exists()) {
    1156             : 
    1157           0 :     if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
    1158             :                               aFontGroup)) {
    1159             :       // if largeopOnly is set, break now
    1160           0 :       if (largeopOnly) break;
    1161           0 :       ++size;
    1162           0 :       continue;
    1163             :     }
    1164             : 
    1165             :     RefPtr<gfxTextRun> textRun =
    1166           0 :       aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
    1167           0 :     nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
    1168           0 :     if (ch.IsGlyphID()) {
    1169           0 :       gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
    1170           0 :       if (mathFont) {
    1171             :         // MeasureTextRun should have set the advance width to the right
    1172             :         // bearing for OpenType MATH fonts. We now subtract the italic
    1173             :         // correction, so that nsMathMLmmultiscripts will place the scripts
    1174             :         // correctly.
    1175             :         // Note that STIX-Word does not provide italic corrections but its
    1176             :         // advance widths do not match right bearings.
    1177             :         // (http://sourceforge.net/p/stixfonts/tracking/50/)
    1178             :         gfxFloat italicCorrection =
    1179           0 :           mathFont->MathTable()->ItalicsCorrection(ch.glyphID);
    1180           0 :         if (italicCorrection) {
    1181           0 :           bm.width -=
    1182           0 :             NSToCoordRound(italicCorrection * oneDevPixel);
    1183           0 :           if (bm.width < 0) {
    1184           0 :             bm.width = 0;
    1185             :           }
    1186             :         }
    1187             :       }
    1188             :     }
    1189             : 
    1190             :     nscoord charSize =
    1191           0 :       isVertical ? bm.ascent + bm.descent
    1192           0 :       : bm.rightBearing - bm.leftBearing;
    1193             : 
    1194           0 :     if (largeopOnly ||
    1195           0 :         IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
    1196           0 :       mGlyphFound = true;
    1197           0 :       if (maxWidth) {
    1198             :         // IsSizeBetter() checked that charSize < maxsize;
    1199             :         // Leave ascent, descent, and bestsize as these contain maxsize.
    1200           0 :         if (mBoundingMetrics.width < bm.width)
    1201           0 :           mBoundingMetrics.width = bm.width;
    1202           0 :         if (mBoundingMetrics.leftBearing > bm.leftBearing)
    1203           0 :           mBoundingMetrics.leftBearing = bm.leftBearing;
    1204           0 :         if (mBoundingMetrics.rightBearing < bm.rightBearing)
    1205           0 :           mBoundingMetrics.rightBearing = bm.rightBearing;
    1206             :         // Continue to check other sizes unless largeopOnly
    1207           0 :         haveBetter = largeopOnly;
    1208             :       }
    1209             :       else {
    1210           0 :         mBoundingMetrics = bm;
    1211           0 :         haveBetter = true;
    1212           0 :         bestSize = charSize;
    1213           0 :         mChar->mGlyphs[0] = Move(textRun);
    1214           0 :         mChar->mDraw = DRAW_VARIANT;
    1215             :       }
    1216             : #ifdef NOISY_SEARCH
    1217             :       printf("    size:%d Current best\n", size);
    1218             : #endif
    1219             :     }
    1220             :     else {
    1221             : #ifdef NOISY_SEARCH
    1222             :       printf("    size:%d Rejected!\n", size);
    1223             : #endif
    1224           0 :       if (haveBetter)
    1225           0 :         break; // Not making an futher progress, stop searching
    1226             :     }
    1227             : 
    1228             :     // If this a largeop only operator, we stop if the glyph is large enough.
    1229           0 :     if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
    1230           0 :       break;
    1231             :     }
    1232           0 :     ++size;
    1233             :   }
    1234             : 
    1235           0 :   return haveBetter &&
    1236           0 :     (largeopOnly || IsSizeOK(bestSize, mTargetSize, mStretchHint));
    1237             : }
    1238             : 
    1239             : // 3. Build by parts.
    1240             : // Returns true if the size is OK, false to keep searching.
    1241             : // Always updates the char if a better match is found.
    1242             : bool
    1243           0 : nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
    1244             :                                            RefPtr<gfxFontGroup>* aFontGroup,
    1245             :                                            const FontFamilyList& aFamilyList)
    1246             : {
    1247             :   // Use our stretchy style context now that stretching is in progress
    1248           0 :   nsFont font = mChar->mStyleContext->StyleFont()->mFont;
    1249           0 :   NormalizeDefaultFont(font, mFontSizeInflation);
    1250             : 
    1251             :   // Compute the bounding metrics of all partial glyphs
    1252           0 :   RefPtr<gfxTextRun> textRun[4];
    1253             :   nsGlyphCode chdata[4];
    1254           0 :   nsBoundingMetrics bmdata[4];
    1255             :   nscoord sizedata[4];
    1256             : 
    1257           0 :   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
    1258           0 :   nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
    1259           0 :   char16_t uchar = mChar->mData[0];
    1260           0 :   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
    1261           0 :   if (!aGlyphTable->HasPartsOf(mDrawTarget, oneDevPixel, *aFontGroup,
    1262           0 :                                uchar, isVertical))
    1263           0 :     return false; // to next table
    1264             : 
    1265           0 :   for (int32_t i = 0; i < 4; i++) {
    1266             :     nsGlyphCode ch = aGlyphTable->ElementAt(mDrawTarget, oneDevPixel,
    1267           0 :                                             *aFontGroup, uchar, isVertical, i);
    1268           0 :     chdata[i] = ch;
    1269           0 :     if (ch.Exists()) {
    1270           0 :       if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
    1271             :                                 aFontGroup))
    1272           0 :         return false;
    1273             : 
    1274           0 :       textRun[i] = aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel,
    1275           0 :                                             *aFontGroup, ch);
    1276           0 :       nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun[i].get());
    1277           0 :       bmdata[i] = bm;
    1278           0 :       sizedata[i] = isVertical ? bm.ascent + bm.descent
    1279           0 :                                : bm.rightBearing - bm.leftBearing;
    1280             :     } else {
    1281             :       // Null glue indicates that a rule will be drawn, which can stretch to
    1282             :       // fill any space.
    1283           0 :       textRun[i] = nullptr;
    1284           0 :       bmdata[i] = nsBoundingMetrics();
    1285           0 :       sizedata[i] = i == 3 ? mTargetSize : 0;
    1286             :     }
    1287             :   }
    1288             : 
    1289             :   // For the Unicode table, we check that all the glyphs are actually found and
    1290             :   // come from the same font.
    1291           0 :   if (aGlyphTable == &gGlyphTableList->mUnicodeTable) {
    1292           0 :     gfxFont* unicodeFont = nullptr;
    1293           0 :     for (int32_t i = 0; i < 4; i++) {
    1294           0 :       if (!textRun[i]) {
    1295           0 :         continue;
    1296             :       }
    1297           0 :       if (textRun[i]->GetLength() != 1 ||
    1298           0 :           textRun[i]->GetCharacterGlyphs()[0].IsMissing()) {
    1299           0 :         return false;
    1300             :       }
    1301             :       uint32_t numGlyphRuns;
    1302             :       const gfxTextRun::GlyphRun* glyphRuns =
    1303           0 :         textRun[i]->GetGlyphRuns(&numGlyphRuns);
    1304           0 :       if (numGlyphRuns != 1) {
    1305           0 :         return false;
    1306             :       }
    1307           0 :       if (!unicodeFont) {
    1308           0 :         unicodeFont = glyphRuns[0].mFont;
    1309           0 :       } else if (unicodeFont != glyphRuns[0].mFont) {
    1310           0 :         return false;
    1311             :       }
    1312             :     }
    1313             :   }
    1314             : 
    1315             :   // Build by parts if we have successfully computed the
    1316             :   // bounding metrics of all parts.
    1317           0 :   nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
    1318           0 :                                               mTargetSize);
    1319             : 
    1320             :   nscoord currentSize =
    1321           0 :     isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
    1322           0 :                : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    1323             : 
    1324           0 :   if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
    1325             : #ifdef NOISY_SEARCH
    1326             :     printf("    Font %s Rejected!\n",
    1327             :            NS_LossyConvertUTF16toASCII(fontName).get());
    1328             : #endif
    1329           0 :     return false; // to next table
    1330             :   }
    1331             : 
    1332             : #ifdef NOISY_SEARCH
    1333             :   printf("    Font %s Current best!\n",
    1334             :          NS_LossyConvertUTF16toASCII(fontName).get());
    1335             : #endif
    1336             : 
    1337             :   // The computed size is the best we have found so far...
    1338             :   // now is the time to compute and cache our bounding metrics
    1339           0 :   if (isVertical) {
    1340             :     int32_t i;
    1341             :     // Try and find the first existing part and then determine the extremal
    1342             :     // horizontal metrics of the parts.
    1343           0 :     for (i = 0; i <= 3 && !textRun[i]; i++);
    1344           0 :     if (i == 4) {
    1345           0 :       NS_ERROR("Cannot stretch - All parts missing");
    1346           0 :       return false;
    1347             :     }
    1348           0 :     nscoord lbearing = bmdata[i].leftBearing;
    1349           0 :     nscoord rbearing = bmdata[i].rightBearing;
    1350           0 :     nscoord width = bmdata[i].width;
    1351           0 :     i++;
    1352           0 :     for (; i <= 3; i++) {
    1353           0 :       if (!textRun[i]) continue;
    1354           0 :       lbearing = std::min(lbearing, bmdata[i].leftBearing);
    1355           0 :       rbearing = std::max(rbearing, bmdata[i].rightBearing);
    1356           0 :       width = std::max(width, bmdata[i].width);
    1357             :     }
    1358           0 :     if (maxWidth) {
    1359           0 :       lbearing = std::min(lbearing, mBoundingMetrics.leftBearing);
    1360           0 :       rbearing = std::max(rbearing, mBoundingMetrics.rightBearing);
    1361           0 :       width = std::max(width, mBoundingMetrics.width);
    1362             :     }
    1363           0 :     mBoundingMetrics.width = width;
    1364             :     // When maxWidth, updating ascent and descent indicates that no characters
    1365             :     // larger than this character's minimum size need to be checked as they
    1366             :     // will not be used.
    1367           0 :     mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent
    1368             :                                                 // for height
    1369           0 :     mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
    1370           0 :     mBoundingMetrics.leftBearing = lbearing;
    1371           0 :     mBoundingMetrics.rightBearing = rbearing;
    1372             :   }
    1373             :   else {
    1374             :     int32_t i;
    1375             :     // Try and find the first existing part and then determine the extremal
    1376             :     // vertical metrics of the parts.
    1377           0 :     for (i = 0; i <= 3 && !textRun[i]; i++);
    1378           0 :     if (i == 4) {
    1379           0 :       NS_ERROR("Cannot stretch - All parts missing");
    1380           0 :       return false;
    1381             :     }
    1382           0 :     nscoord ascent = bmdata[i].ascent;
    1383           0 :     nscoord descent = bmdata[i].descent;
    1384           0 :     i++;
    1385           0 :     for (; i <= 3; i++) {
    1386           0 :       if (!textRun[i]) continue;
    1387           0 :       ascent = std::max(ascent, bmdata[i].ascent);
    1388           0 :       descent = std::max(descent, bmdata[i].descent);
    1389             :     }
    1390           0 :     mBoundingMetrics.width = computedSize;
    1391           0 :     mBoundingMetrics.ascent = ascent;
    1392           0 :     mBoundingMetrics.descent = descent;
    1393           0 :     mBoundingMetrics.leftBearing = 0;
    1394           0 :     mBoundingMetrics.rightBearing = computedSize;
    1395             :   }
    1396           0 :   mGlyphFound = true;
    1397           0 :   if (maxWidth)
    1398           0 :     return false; // Continue to check other sizes
    1399             : 
    1400             :   // reset
    1401           0 :   mChar->mDraw = DRAW_PARTS;
    1402           0 :   for (int32_t i = 0; i < 4; i++) {
    1403           0 :     mChar->mGlyphs[i] = Move(textRun[i]);
    1404           0 :     mChar->mBmData[i] = bmdata[i];
    1405             :   }
    1406             : 
    1407           0 :   return IsSizeOK(computedSize, mTargetSize, mStretchHint);
    1408             : }
    1409             : 
    1410             : // This is called for each family, whether it exists or not
    1411             : bool
    1412           0 : nsMathMLChar::StretchEnumContext::EnumCallback(const FontFamilyName& aFamily,
    1413             :                                                bool aGeneric, void *aData)
    1414             : {
    1415           0 :   StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
    1416             : 
    1417             :   // for comparisons, force use of unquoted names
    1418           0 :   FontFamilyName unquotedFamilyName(aFamily);
    1419           0 :   if (unquotedFamilyName.mType == eFamily_named_quoted) {
    1420           0 :     unquotedFamilyName.mType = eFamily_named;
    1421             :   }
    1422             : 
    1423             :   // Check font family if it is not a generic one
    1424             :   // We test with the kNullGlyph
    1425           0 :   nsStyleContext *sc = context->mChar->mStyleContext;
    1426           0 :   nsFont font = sc->StyleFont()->mFont;
    1427           0 :   NormalizeDefaultFont(font, context->mFontSizeInflation);
    1428           0 :   RefPtr<gfxFontGroup> fontGroup;
    1429           0 :   FontFamilyList family;
    1430           0 :   family.Append(unquotedFamilyName);
    1431           0 :   if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
    1432             :                                                   nullptr, kNullGlyph, family,
    1433             :                                                   font, &fontGroup))
    1434           0 :      return true; // Could not set the family
    1435             : 
    1436             :   // Determine the glyph table to use for this font.
    1437           0 :   nsAutoPtr<nsOpenTypeTable> openTypeTable;
    1438             :   nsGlyphTable* glyphTable;
    1439           0 :   if (aGeneric) {
    1440             :     // This is a generic font, use the Unicode table.
    1441           0 :     glyphTable = &gGlyphTableList->mUnicodeTable;
    1442             :   } else {
    1443             :     // If the font contains an Open Type MATH table, use it.
    1444           0 :     openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFirstValidFont());
    1445           0 :     if (openTypeTable) {
    1446           0 :       glyphTable = openTypeTable;
    1447             :     } else {
    1448             :       // Otherwise try to find a .properties file corresponding to that font
    1449             :       // family or fallback to the Unicode table.
    1450           0 :       nsAutoString familyName;
    1451           0 :       unquotedFamilyName.AppendToString(familyName);
    1452           0 :       glyphTable = gGlyphTableList->GetGlyphTableFor(familyName);
    1453             :     }
    1454             :   }
    1455             : 
    1456           0 :   if (!openTypeTable) {
    1457           0 :     if (context->mTablesTried.Contains(glyphTable))
    1458           0 :       return true; // already tried this one
    1459             : 
    1460             :     // Only try this table once.
    1461           0 :     context->mTablesTried.AppendElement(glyphTable);
    1462             :   }
    1463             : 
    1464             :   // If the unicode table is being used, then search all font families.  If a
    1465             :   // special table is being used then the font in this family should have the
    1466             :   // specified glyphs.
    1467           0 :   const FontFamilyList& familyList = glyphTable == &gGlyphTableList->mUnicodeTable ?
    1468           0 :     context->mFamilyList : family;
    1469             : 
    1470           0 :   if((context->mTryVariants &&
    1471           0 :       context->TryVariants(glyphTable, &fontGroup, familyList)) ||
    1472           0 :      (context->mTryParts && context->TryParts(glyphTable,
    1473             :                                               &fontGroup,
    1474             :                                               familyList)))
    1475           0 :     return false; // no need to continue
    1476             : 
    1477           0 :   return true; // true means continue
    1478             : }
    1479             : 
    1480             : // insert math fallback families just before the first generic or at the end
    1481             : // when no generic present
    1482             : static void
    1483           0 : InsertMathFallbacks(FontFamilyList& aFamilyList,
    1484             :                     nsTArray<nsString>& aFallbacks)
    1485             : {
    1486           0 :   FontFamilyList aMergedList;
    1487             : 
    1488           0 :   bool inserted = false;
    1489           0 :   const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
    1490           0 :   uint32_t i, num = fontlist.Length();
    1491           0 :   for (i = 0; i < num; i++) {
    1492           0 :     const FontFamilyName& name = fontlist[i];
    1493           0 :     if (!inserted && name.IsGeneric()) {
    1494           0 :       inserted = true;
    1495           0 :       aMergedList.Append(aFallbacks);
    1496             :     }
    1497           0 :     aMergedList.Append(name);
    1498             :   }
    1499             : 
    1500           0 :   if (!inserted) {
    1501           0 :     aMergedList.Append(aFallbacks);
    1502             :   }
    1503           0 :   aFamilyList = aMergedList;
    1504           0 : }
    1505             : 
    1506             : nsresult
    1507           0 : nsMathMLChar::StretchInternal(nsPresContext*           aPresContext,
    1508             :                               DrawTarget*              aDrawTarget,
    1509             :                               float                    aFontSizeInflation,
    1510             :                               nsStretchDirection&      aStretchDirection,
    1511             :                               const nsBoundingMetrics& aContainerSize,
    1512             :                               nsBoundingMetrics&       aDesiredStretchSize,
    1513             :                               uint32_t                 aStretchHint,
    1514             :                               // These are currently only used when
    1515             :                               // aStretchHint & NS_STRETCH_MAXWIDTH:
    1516             :                               float                    aMaxSize,
    1517             :                               bool                     aMaxSizeIsAbsolute)
    1518             : {
    1519             :   // if we have been called before, and we didn't actually stretch, our
    1520             :   // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
    1521             :   // So first set our direction back to its instrinsic value
    1522           0 :   nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
    1523             : 
    1524             :   // Set default font and get the default bounding metrics
    1525             :   // mStyleContext is a leaf context used only when stretching happens.
    1526             :   // For the base size, the default font should come from the parent context
    1527           0 :   nsFont font = mStyleContext->GetParentAllowServo()->StyleFont()->mFont;
    1528           0 :   NormalizeDefaultFont(font, aFontSizeInflation);
    1529             : 
    1530           0 :   const nsStyleFont* styleFont = mStyleContext->StyleFont();
    1531           0 :   nsFontMetrics::Params params;
    1532           0 :   params.language = styleFont->mLanguage;
    1533           0 :   params.explicitLanguage = styleFont->mExplicitLanguage;
    1534           0 :   params.userFontSet = aPresContext->GetUserFontSet();
    1535           0 :   params.textPerf = aPresContext->GetTextPerfMetrics();
    1536             :   RefPtr<nsFontMetrics> fm =
    1537           0 :     aPresContext->DeviceContext()->GetMetricsFor(font, params);
    1538           0 :   uint32_t len = uint32_t(mData.Length());
    1539             :   mGlyphs[0] = fm->GetThebesFontGroup()->
    1540           0 :     MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aDrawTarget,
    1541             :                 aPresContext->AppUnitsPerDevPixel(),
    1542             :                 gfx::ShapedTextFlags(), nsTextFrameUtils::Flags(),
    1543           0 :                 aPresContext->MissingFontRecorder());
    1544           0 :   aDesiredStretchSize = MeasureTextRun(aDrawTarget, mGlyphs[0].get());
    1545             : 
    1546           0 :   bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
    1547           0 :   if (!maxWidth) {
    1548           0 :     mUnscaledAscent = aDesiredStretchSize.ascent;
    1549             :   }
    1550             : 
    1551             :   //////////////////////////////////////////////////////////////////////////////
    1552             :   // 1. Check the common situations where stretching is not actually needed
    1553             :   //////////////////////////////////////////////////////////////////////////////
    1554             : 
    1555             :   // quick return if there is nothing special about this char
    1556           0 :   if ((aStretchDirection != direction &&
    1557           0 :        aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
    1558           0 :       (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
    1559           0 :     mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
    1560           0 :     return NS_OK;
    1561             :   }
    1562             : 
    1563             :   // if no specified direction, attempt to stretch in our preferred direction
    1564           0 :   if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
    1565           0 :     aStretchDirection = direction;
    1566             :   }
    1567             : 
    1568             :   // see if this is a particular largeop or largeopOnly request
    1569           0 :   bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
    1570           0 :   bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
    1571           0 :   bool largeopOnly = largeop && !stretchy;
    1572             : 
    1573           0 :   bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
    1574             : 
    1575             :   nscoord targetSize =
    1576           0 :     isVertical ? aContainerSize.ascent + aContainerSize.descent
    1577           0 :     : aContainerSize.rightBearing - aContainerSize.leftBearing;
    1578             : 
    1579           0 :   if (maxWidth) {
    1580             :     // See if it is only necessary to consider glyphs up to some maximum size.
    1581             :     // Set the current height to the maximum size, and set aStretchHint to
    1582             :     // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
    1583             :     // are considered.  targetSize from GetMaxWidth() is 0.
    1584           0 :     if (stretchy) {
    1585             :       // variable size stretch - consider all sizes < maxsize
    1586           0 :       aStretchHint =
    1587           0 :         (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
    1588             :     }
    1589             : 
    1590             :     // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
    1591             :     // maxsize is not enforced exactly.
    1592           0 :     if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
    1593           0 :       aDesiredStretchSize.ascent = nscoord_MAX;
    1594           0 :       aDesiredStretchSize.descent = 0;
    1595             :     }
    1596             :     else {
    1597           0 :       nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
    1598           0 :       if (height == 0) {
    1599           0 :         if (aMaxSizeIsAbsolute) {
    1600           0 :           aDesiredStretchSize.ascent =
    1601           0 :             NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
    1602           0 :           aDesiredStretchSize.descent = 0;
    1603             :         }
    1604             :         // else: leave height as 0
    1605             :       }
    1606             :       else {
    1607           0 :         float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
    1608           0 :         scale /= NS_MATHML_DELIMITER_FACTOR;
    1609           0 :         aDesiredStretchSize.ascent =
    1610           0 :           NSToCoordRound(scale * aDesiredStretchSize.ascent);
    1611           0 :         aDesiredStretchSize.descent =
    1612           0 :           NSToCoordRound(scale * aDesiredStretchSize.descent);
    1613             :       }
    1614             :     }
    1615             :   }
    1616             : 
    1617           0 :   nsBoundingMetrics initialSize = aDesiredStretchSize;
    1618             :   nscoord charSize =
    1619           0 :     isVertical ? initialSize.ascent + initialSize.descent
    1620           0 :     : initialSize.rightBearing - initialSize.leftBearing;
    1621             : 
    1622           0 :   bool done = false;
    1623             : 
    1624           0 :   if (!maxWidth && !largeop) {
    1625             :     // Doing Stretch() not GetMaxWidth(),
    1626             :     // and not a largeop in display mode; we're done if size fits
    1627           0 :     if ((targetSize <= 0) ||
    1628           0 :         ((isVertical && charSize >= targetSize) ||
    1629           0 :          IsSizeOK(charSize, targetSize, aStretchHint)))
    1630           0 :       done = true;
    1631             :   }
    1632             : 
    1633             :   //////////////////////////////////////////////////////////////////////////////
    1634             :   // 2/3. Search for a glyph or set of part glyphs of appropriate size
    1635             :   //////////////////////////////////////////////////////////////////////////////
    1636             : 
    1637           0 :   bool glyphFound = false;
    1638             : 
    1639           0 :   if (!done) { // normal case
    1640             :     // Use the css font-family but add preferred fallback fonts.
    1641           0 :     font = mStyleContext->StyleFont()->mFont;
    1642           0 :     NormalizeDefaultFont(font, aFontSizeInflation);
    1643             : 
    1644             :     // really shouldn't be doing things this way but for now
    1645             :     // insert fallbacks into the list
    1646           0 :     AutoTArray<nsString, 16> mathFallbacks;
    1647           0 :     gfxFontUtils::GetPrefsFontList("font.name.serif.x-math", mathFallbacks);
    1648             :     gfxFontUtils::AppendPrefsFontList("font.name-list.serif.x-math",
    1649           0 :                                       mathFallbacks);
    1650           0 :     InsertMathFallbacks(font.fontlist, mathFallbacks);
    1651             : 
    1652             : 
    1653             : #ifdef NOISY_SEARCH
    1654             :     nsAutoString fontlistStr;
    1655             :     font.fontlist.ToString(fontlistStr, false, true);
    1656             :     printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
    1657             :            NS_ConvertUTF16toUTF8(fontlistStr).get(), mData[0], mData[0]&0x00FF);
    1658             : #endif
    1659             :     StretchEnumContext enumData(this, aPresContext, aDrawTarget,
    1660             :                                 aFontSizeInflation,
    1661             :                                 aStretchDirection, targetSize, aStretchHint,
    1662           0 :                                 aDesiredStretchSize, font.fontlist, glyphFound);
    1663           0 :     enumData.mTryParts = !largeopOnly;
    1664             : 
    1665           0 :     const nsTArray<FontFamilyName>& fontlist = font.fontlist.GetFontlist();
    1666           0 :     uint32_t i, num = fontlist.Length();
    1667           0 :     bool next = true;
    1668           0 :     for (i = 0; i < num && next; i++) {
    1669           0 :       const FontFamilyName& name = fontlist[i];
    1670           0 :       next = StretchEnumContext::EnumCallback(name, name.IsGeneric(), &enumData);
    1671             :     }
    1672             :   }
    1673             : 
    1674           0 :   if (!maxWidth) {
    1675             :     // Now, we know how we are going to draw the char. Update the member
    1676             :     // variables accordingly.
    1677           0 :     mUnscaledAscent = aDesiredStretchSize.ascent;
    1678             :   }
    1679             : 
    1680           0 :   if (glyphFound) {
    1681           0 :     return NS_OK;
    1682             :   }
    1683             : 
    1684             :   // We did not find a size variant or a glyph assembly to stretch this
    1685             :   // operator. Verify whether a font with an OpenType MATH table is available
    1686             :   // and record missing math script otherwise.
    1687           0 :   gfxMissingFontRecorder* MFR = aPresContext->MissingFontRecorder();
    1688           0 :   if (MFR && !fm->GetThebesFontGroup()->GetFirstMathFont()) {
    1689           0 :     MFR->RecordScript(unicode::Script::MATHEMATICAL_NOTATION);
    1690             :   }
    1691             : 
    1692             :   // If the scale_stretchy_operators option is disabled, we are done.
    1693           0 :   if (!Preferences::GetBool("mathml.scale_stretchy_operators.enabled", true)) {
    1694           0 :     return NS_OK;
    1695             :   }
    1696             : 
    1697             :   // stretchy character
    1698           0 :   if (stretchy) {
    1699           0 :     if (isVertical) {
    1700             :       float scale =
    1701           0 :         std::min(kMaxScaleFactor, float(aContainerSize.ascent + aContainerSize.descent) /
    1702           0 :         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent));
    1703           0 :       if (!largeop || scale > 1.0) {
    1704             :         // make the character match the desired height.
    1705           0 :         if (!maxWidth) {
    1706           0 :           mScaleY *= scale;
    1707             :         }
    1708           0 :         aDesiredStretchSize.ascent *= scale;
    1709           0 :         aDesiredStretchSize.descent *= scale;
    1710             :       }
    1711             :     } else {
    1712             :       float scale =
    1713           0 :         std::min(kMaxScaleFactor, float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
    1714           0 :         (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing));
    1715           0 :       if (!largeop || scale > 1.0) {
    1716             :         // make the character match the desired width.
    1717           0 :         if (!maxWidth) {
    1718           0 :           mScaleX *= scale;
    1719             :         }
    1720           0 :         aDesiredStretchSize.leftBearing *= scale;
    1721           0 :         aDesiredStretchSize.rightBearing *= scale;
    1722           0 :         aDesiredStretchSize.width *= scale;
    1723             :       }
    1724             :     }
    1725             :   }
    1726             : 
    1727             :   // We do not have a char variant for this largeop in display mode, so we
    1728             :   // apply a scale transform to the base char.
    1729           0 :   if (largeop) {
    1730             :     float scale;
    1731           0 :     float largeopFactor = kLargeOpFactor;
    1732             : 
    1733             :     // increase the width if it is not largeopFactor times larger
    1734             :     // than the initial one.
    1735           0 :     if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
    1736           0 :         largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
    1737           0 :       scale = (largeopFactor *
    1738           0 :                (initialSize.rightBearing - initialSize.leftBearing)) /
    1739           0 :         (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
    1740           0 :       if (!maxWidth) {
    1741           0 :         mScaleX *= scale;
    1742             :       }
    1743           0 :       aDesiredStretchSize.leftBearing *= scale;
    1744           0 :       aDesiredStretchSize.rightBearing *= scale;
    1745           0 :       aDesiredStretchSize.width *= scale;
    1746             :     }
    1747             : 
    1748             :     // increase the height if it is not largeopFactor times larger
    1749             :     // than the initial one.
    1750           0 :     if (NS_STRETCH_INTEGRAL & aStretchHint) {
    1751             :       // integrals are drawn taller
    1752           0 :       largeopFactor = kIntegralFactor;
    1753             :     }
    1754           0 :     if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
    1755           0 :         largeopFactor * (initialSize.ascent + initialSize.descent)) {
    1756           0 :       scale = (largeopFactor *
    1757           0 :                (initialSize.ascent + initialSize.descent)) /
    1758           0 :         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
    1759           0 :       if (!maxWidth) {
    1760           0 :         mScaleY *= scale;
    1761             :       }
    1762           0 :       aDesiredStretchSize.ascent *= scale;
    1763           0 :       aDesiredStretchSize.descent *= scale;
    1764             :     }
    1765             :   }
    1766             : 
    1767           0 :   return NS_OK;
    1768             : }
    1769             : 
    1770             : nsresult
    1771           0 : nsMathMLChar::Stretch(nsPresContext*           aPresContext,
    1772             :                       DrawTarget*              aDrawTarget,
    1773             :                       float                    aFontSizeInflation,
    1774             :                       nsStretchDirection       aStretchDirection,
    1775             :                       const nsBoundingMetrics& aContainerSize,
    1776             :                       nsBoundingMetrics&       aDesiredStretchSize,
    1777             :                       uint32_t                 aStretchHint,
    1778             :                       bool                     aRTL)
    1779             : {
    1780           0 :   NS_ASSERTION(!(aStretchHint &
    1781             :                  ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
    1782             :                    NS_STRETCH_INTEGRAL)),
    1783             :                "Unexpected stretch flags");
    1784             : 
    1785           0 :   mDraw = DRAW_NORMAL;
    1786           0 :   mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
    1787           0 :   mScaleY = mScaleX = 1.0;
    1788           0 :   mDirection = aStretchDirection;
    1789             :   nsresult rv =
    1790           0 :     StretchInternal(aPresContext, aDrawTarget, aFontSizeInflation, mDirection,
    1791           0 :                     aContainerSize, aDesiredStretchSize, aStretchHint);
    1792             : 
    1793             :   // Record the metrics
    1794           0 :   mBoundingMetrics = aDesiredStretchSize;
    1795             : 
    1796           0 :   return rv;
    1797             : }
    1798             : 
    1799             : // What happens here is that the StretchInternal algorithm is used but
    1800             : // modified by passing the NS_STRETCH_MAXWIDTH stretch hint.  That causes
    1801             : // StretchInternal to return horizontal bounding metrics that are the maximum
    1802             : // that might be returned from a Stretch.
    1803             : //
    1804             : // In order to avoid considering widths of some characters in fonts that will
    1805             : // not be used for any stretch size, StretchInternal sets the initial height
    1806             : // to infinity and looks for any characters smaller than this height.  When a
    1807             : // character built from parts is considered, (it will be used by Stretch for
    1808             : // any characters greater than its minimum size, so) the height is set to its
    1809             : // minimum size, so that only widths of smaller subsequent characters are
    1810             : // considered.
    1811             : nscoord
    1812           0 : nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
    1813             :                           float aFontSizeInflation, uint32_t aStretchHint)
    1814             : {
    1815           0 :   nsBoundingMetrics bm;
    1816           0 :   nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
    1817           0 :   const nsBoundingMetrics container; // zero target size
    1818             : 
    1819           0 :   StretchInternal(aPresContext, aDrawTarget, aFontSizeInflation, direction,
    1820           0 :                   container, bm, aStretchHint | NS_STRETCH_MAXWIDTH);
    1821             : 
    1822           0 :   return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
    1823             : }
    1824             : 
    1825             : class nsDisplayMathMLSelectionRect : public nsDisplayItem {
    1826             : public:
    1827           0 :   nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
    1828             :                                nsIFrame* aFrame, const nsRect& aRect)
    1829           0 :     : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
    1830           0 :     MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
    1831           0 :   }
    1832             : #ifdef NS_BUILD_REFCNT_LOGGING
    1833           0 :   virtual ~nsDisplayMathMLSelectionRect() {
    1834           0 :     MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
    1835           0 :   }
    1836             : #endif
    1837             : 
    1838             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1839             :                      gfxContext* aCtx) override;
    1840           0 :   NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
    1841             : private:
    1842             :   nsRect    mRect;
    1843             : };
    1844             : 
    1845           0 : void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
    1846             :                                          gfxContext* aCtx)
    1847             : {
    1848           0 :   DrawTarget* drawTarget = aCtx->GetDrawTarget();
    1849           0 :   Rect rect = NSRectToSnappedRect(mRect + ToReferenceFrame(),
    1850           0 :                                   mFrame->PresContext()->AppUnitsPerDevPixel(),
    1851           0 :                                   *drawTarget);
    1852             :   // get color to use for selection from the look&feel object
    1853             :   nscolor bgColor =
    1854             :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
    1855           0 :                           NS_RGB(0, 0, 0));
    1856           0 :   drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(bgColor)));
    1857           0 : }
    1858             : 
    1859             : class nsDisplayMathMLCharForeground : public nsDisplayItem {
    1860             : public:
    1861           0 :   nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
    1862             :                                 nsIFrame* aFrame, nsMathMLChar* aChar,
    1863             :                                                 uint32_t aIndex, bool aIsSelected)
    1864           0 :     : nsDisplayItem(aBuilder, aFrame), mChar(aChar),
    1865           0 :       mIndex(aIndex), mIsSelected(aIsSelected) {
    1866           0 :     MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
    1867           0 :   }
    1868             : #ifdef NS_BUILD_REFCNT_LOGGING
    1869           0 :   virtual ~nsDisplayMathMLCharForeground() {
    1870           0 :     MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
    1871           0 :   }
    1872             : #endif
    1873             : 
    1874           0 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override {
    1875           0 :     *aSnap = false;
    1876           0 :     nsRect rect;
    1877           0 :     mChar->GetRect(rect);
    1878           0 :     nsPoint offset = ToReferenceFrame() + rect.TopLeft();
    1879           0 :     nsBoundingMetrics bm;
    1880           0 :     mChar->GetBoundingMetrics(bm);
    1881           0 :     nsRect temp(offset.x + bm.leftBearing, offset.y,
    1882           0 :                 bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
    1883             :     // Bug 748220
    1884           0 :     temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
    1885           0 :     return temp;
    1886             :   }
    1887             : 
    1888           0 :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1889             :                      gfxContext* aCtx) override
    1890             :   {
    1891           0 :     mChar->PaintForeground(mFrame->PresContext(), *aCtx,
    1892           0 :                            ToReferenceFrame(), mIsSelected);
    1893           0 :   }
    1894             : 
    1895           0 :   NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
    1896             : 
    1897           0 :   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
    1898             :   {
    1899             :     bool snap;
    1900           0 :     return GetBounds(aBuilder, &snap);
    1901             :   }
    1902             : 
    1903           0 :   virtual uint32_t GetPerFrameKey() override {
    1904           0 :     return (mIndex << nsDisplayItem::TYPE_BITS)
    1905           0 :       | nsDisplayItem::GetPerFrameKey();
    1906             :   }
    1907             : 
    1908             : private:
    1909             :   nsMathMLChar* mChar;
    1910             :   uint32_t      mIndex;
    1911             :   bool          mIsSelected;
    1912             : };
    1913             : 
    1914             : #ifdef DEBUG
    1915             : class nsDisplayMathMLCharDebug : public nsDisplayItem {
    1916             : public:
    1917             :   nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
    1918             :                            nsIFrame* aFrame, const nsRect& aRect)
    1919             :     : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
    1920             :     MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
    1921             :   }
    1922             : #ifdef NS_BUILD_REFCNT_LOGGING
    1923           0 :   virtual ~nsDisplayMathMLCharDebug() {
    1924           0 :     MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
    1925           0 :   }
    1926             : #endif
    1927             : 
    1928             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1929             :                      gfxContext* aCtx) override;
    1930           0 :   NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
    1931             : 
    1932             : private:
    1933             :   nsRect mRect;
    1934             : };
    1935             : 
    1936           0 : void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
    1937             :                                      gfxContext* aCtx)
    1938             : {
    1939             :   // for visual debug
    1940           0 :   Sides skipSides;
    1941           0 :   nsPresContext* presContext = mFrame->PresContext();
    1942           0 :   nsStyleContext* styleContext = mFrame->StyleContext();
    1943           0 :   nsRect rect = mRect + ToReferenceFrame();
    1944             : 
    1945           0 :   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
    1946           0 :                          ? PaintBorderFlags::SYNC_DECODE_IMAGES
    1947           0 :                          : PaintBorderFlags();
    1948             : 
    1949             :   // Since this is used only for debugging, we don't need to worry about
    1950             :   // tracking the DrawResult.
    1951             :   Unused <<
    1952           0 :     nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame, mVisibleRect,
    1953             :                                 rect, styleContext, flags, skipSides);
    1954             : 
    1955           0 :   nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
    1956           0 :                                mVisibleRect, rect, styleContext);
    1957           0 : }
    1958             : #endif
    1959             : 
    1960             : 
    1961             : void
    1962           0 : nsMathMLChar::Display(nsDisplayListBuilder*   aBuilder,
    1963             :                       nsIFrame*               aForFrame,
    1964             :                       const nsDisplayListSet& aLists,
    1965             :                       uint32_t                aIndex,
    1966             :                       const nsRect*           aSelectedRect)
    1967             : {
    1968           0 :   nsStyleContext* parentContext = mStyleContext->GetParentAllowServo();
    1969           0 :   nsStyleContext* styleContext = mStyleContext;
    1970             : 
    1971           0 :   if (mDraw == DRAW_NORMAL) {
    1972             :     // normal drawing if there is nothing special about this char
    1973             :     // Set default context to the parent context
    1974           0 :     styleContext = parentContext;
    1975             :   }
    1976             : 
    1977           0 :   if (!styleContext->StyleVisibility()->IsVisible())
    1978           0 :     return;
    1979             : 
    1980             :   // if the leaf style context that we use for stretchy chars has a background
    1981             :   // color we use it -- this feature is mostly used for testing and debugging
    1982             :   // purposes. Normally, users will set the background on the container frame.
    1983             :   // paint the selection background -- beware MathML frames overlap a lot
    1984           0 :   if (aSelectedRect && !aSelectedRect->IsEmpty()) {
    1985           0 :     aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
    1986           0 :       nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
    1987             :   }
    1988           0 :   else if (mRect.width && mRect.height) {
    1989           0 :     if (styleContext != parentContext &&
    1990           0 :         NS_GET_A(styleContext->StyleBackground()->
    1991             :                  BackgroundColor(styleContext)) > 0) {
    1992           0 :       nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
    1993             :         aBuilder, aForFrame, mRect, aLists.BorderBackground(),
    1994           0 :         /* aAllowWillPaintBorderOptimization */ true, styleContext);
    1995             :     }
    1996             :     //else
    1997             :     //  our container frame will take care of painting its background
    1998             : 
    1999             : #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
    2000             :     // for visual debug
    2001             :     aLists.BorderBackground()->AppendToTop(new (aBuilder)
    2002             :       nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
    2003             : #endif
    2004             :   }
    2005           0 :   aLists.Content()->AppendNewToTop(new (aBuilder)
    2006             :     nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
    2007             :                                   aIndex,
    2008           0 :                                   aSelectedRect &&
    2009           0 :                                   !aSelectedRect->IsEmpty()));
    2010             : }
    2011             : 
    2012             : void
    2013           0 : nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext,
    2014             :                               int32_t aAppUnitsPerGfxUnit,
    2015             :                               nsRect &r)
    2016             : {
    2017             :   // apply the transforms
    2018           0 :   if (mMirrored) {
    2019           0 :     nsPoint pt = r.TopRight();
    2020           0 :     gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
    2021           0 :                             NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit));
    2022             :     aThebesContext->SetMatrix(
    2023           0 :       aThebesContext->CurrentMatrix().PreTranslate(devPixelOffset).
    2024           0 :                                       PreScale(-mScaleX, mScaleY));
    2025             :   } else {
    2026           0 :     nsPoint pt = r.TopLeft();
    2027           0 :     gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
    2028           0 :                             NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit));
    2029             :     aThebesContext->SetMatrix(
    2030           0 :       aThebesContext->CurrentMatrix().PreTranslate(devPixelOffset).
    2031           0 :                                       PreScale(mScaleX, mScaleY));
    2032             :   }
    2033             : 
    2034             :   // update the bounding rectangle.
    2035           0 :   r.x = r.y = 0;
    2036           0 :   r.width /= mScaleX;
    2037           0 :   r.height /= mScaleY;
    2038           0 : }
    2039             : 
    2040             : void
    2041           0 : nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
    2042             :                               gfxContext& aRenderingContext,
    2043             :                               nsPoint aPt,
    2044             :                               bool aIsSelected)
    2045             : {
    2046           0 :   nsStyleContext* parentContext = mStyleContext->GetParentAllowServo();
    2047           0 :   nsStyleContext* styleContext = mStyleContext;
    2048             : 
    2049           0 :   if (mDraw == DRAW_NORMAL) {
    2050             :     // normal drawing if there is nothing special about this char
    2051             :     // Set default context to the parent context
    2052           0 :     styleContext = parentContext;
    2053             :   }
    2054             : 
    2055             :   // Set color ...
    2056             :   nscolor fgColor = styleContext->
    2057           0 :     GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
    2058           0 :   if (aIsSelected) {
    2059             :     // get color to use for selection from the look&feel object
    2060             :     fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
    2061           0 :                                     fgColor);
    2062             :   }
    2063           0 :   aRenderingContext.SetColor(Color::FromABGR(fgColor));
    2064           0 :   aRenderingContext.Save();
    2065           0 :   nsRect r = mRect + aPt;
    2066           0 :   ApplyTransforms(&aRenderingContext, aPresContext->AppUnitsPerDevPixel(), r);
    2067             : 
    2068           0 :   switch(mDraw)
    2069             :   {
    2070             :     case DRAW_NORMAL:
    2071             :     case DRAW_VARIANT:
    2072             :       // draw a single glyph (base size or size variant)
    2073             :       // XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322.
    2074           0 :       if (mGlyphs[0]) {
    2075           0 :         mGlyphs[0]->Draw(Range(mGlyphs[0].get()), gfxPoint(0.0, mUnscaledAscent),
    2076           0 :                          gfxTextRun::DrawParams(&aRenderingContext));
    2077             :       }
    2078           0 :       break;
    2079             :     case DRAW_PARTS: {
    2080             :       // paint by parts
    2081           0 :       if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
    2082           0 :         PaintVertically(aPresContext, &aRenderingContext, r, fgColor);
    2083           0 :       else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
    2084           0 :         PaintHorizontally(aPresContext, &aRenderingContext, r, fgColor);
    2085           0 :       break;
    2086             :     }
    2087             :     default:
    2088           0 :       NS_NOTREACHED("Unknown drawing method");
    2089           0 :       break;
    2090             :   }
    2091             : 
    2092           0 :   aRenderingContext.Restore();
    2093           0 : }
    2094             : 
    2095             : /* =============================================================================
    2096             :   Helper routines that actually do the job of painting the char by parts
    2097             :  */
    2098             : 
    2099             : class AutoPushClipRect {
    2100             :   gfxContext* mThebesContext;
    2101             : public:
    2102           0 :   AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
    2103             :                    const nsRect& aRect)
    2104           0 :     : mThebesContext(aThebesContext) {
    2105           0 :     mThebesContext->Save();
    2106           0 :     mThebesContext->NewPath();
    2107           0 :     gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
    2108           0 :     mThebesContext->SnappedRectangle(clip);
    2109           0 :     mThebesContext->Clip();
    2110           0 :   }
    2111           0 :   ~AutoPushClipRect() {
    2112           0 :     mThebesContext->Restore();
    2113           0 :   }
    2114             : };
    2115             : 
    2116             : static nsPoint
    2117           0 : SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
    2118             :                 const nsPoint& aPt)
    2119             : {
    2120           0 :   gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
    2121           0 :               NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
    2122           0 :   pt = aThebesContext->UserToDevice(pt);
    2123           0 :   pt.Round();
    2124           0 :   pt = aThebesContext->DeviceToUser(pt);
    2125           0 :   return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
    2126           0 :                  NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
    2127             : }
    2128             : 
    2129             : static void
    2130           0 : PaintRule(DrawTarget& aDrawTarget,
    2131             :           int32_t     aAppUnitsPerGfxUnit,
    2132             :           nsRect&     aRect,
    2133             :           nscolor     aColor)
    2134             : {
    2135           0 :   Rect rect = NSRectToSnappedRect(aRect, aAppUnitsPerGfxUnit, aDrawTarget);
    2136           0 :   ColorPattern color(ToDeviceColor(aColor));
    2137           0 :   aDrawTarget.FillRect(rect, color);
    2138           0 : }
    2139             : 
    2140             : // paint a stretchy char by assembling glyphs vertically
    2141             : nsresult
    2142           0 : nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
    2143             :                               gfxContext*    aThebesContext,
    2144             :                               nsRect&        aRect,
    2145             :                               nscolor        aColor)
    2146             : {
    2147           0 :   DrawTarget& aDrawTarget = *aThebesContext->GetDrawTarget();
    2148             : 
    2149             :   // Get the device pixel size in the vertical direction.
    2150             :   // (This makes no effort to optimize for non-translation transformations.)
    2151           0 :   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
    2152             : 
    2153             :   // get metrics data to be re-used later
    2154           0 :   int32_t i = 0;
    2155           0 :   nscoord dx = aRect.x;
    2156             :   nscoord offset[3], start[3], end[3];
    2157           0 :   for (i = 0; i <= 2; ++i) {
    2158           0 :     const nsBoundingMetrics& bm = mBmData[i];
    2159             :     nscoord dy;
    2160           0 :     if (0 == i) { // top
    2161           0 :       dy = aRect.y + bm.ascent;
    2162             :     }
    2163           0 :     else if (2 == i) { // bottom
    2164           0 :       dy = aRect.y + aRect.height - bm.descent;
    2165             :     }
    2166             :     else { // middle
    2167           0 :       dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
    2168             :     }
    2169             :     // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
    2170             :     // Do this now so that we can get the other dimensions right.
    2171             :     // (This may not achieve much with non-rectangular transformations.)
    2172           0 :     dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y;
    2173             :     // abcissa passed to Draw
    2174           0 :     offset[i] = dy;
    2175             :     // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
    2176             :     // pixel, so the bm values can include 1 row of faint pixels on each edge.
    2177             :     // Don't rely on this pixel as it can look like a gap.
    2178           0 :     if (bm.ascent + bm.descent >= 2 * oneDevPixel) {
    2179           0 :       start[i] = dy - bm.ascent + oneDevPixel; // top join
    2180           0 :       end[i] = dy + bm.descent - oneDevPixel; // bottom join
    2181             :     } else {
    2182             :       // To avoid overlaps, we don't add one pixel on each side when the part
    2183             :       // is too small.
    2184           0 :       start[i] = dy - bm.ascent; // top join
    2185           0 :       end[i] = dy + bm.descent; // bottom join
    2186             :     }
    2187             :   }
    2188             : 
    2189             :   // If there are overlaps, then join at the mid point
    2190           0 :   for (i = 0; i < 2; ++i) {
    2191           0 :     if (end[i] > start[i+1]) {
    2192           0 :       end[i] = (end[i] + start[i+1]) / 2;
    2193           0 :       start[i+1] = end[i];
    2194             :     }
    2195             :   }
    2196             : 
    2197           0 :   nsRect unionRect = aRect;
    2198           0 :   unionRect.x += mBoundingMetrics.leftBearing;
    2199           0 :   unionRect.width =
    2200           0 :     mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    2201           0 :   unionRect.Inflate(oneDevPixel, oneDevPixel);
    2202             : 
    2203           0 :   gfxTextRun::DrawParams params(aThebesContext);
    2204             : 
    2205             :   /////////////////////////////////////
    2206             :   // draw top, middle, bottom
    2207           0 :   for (i = 0; i <= 2; ++i) {
    2208             :     // glue can be null
    2209           0 :     if (mGlyphs[i]) {
    2210           0 :       nscoord dy = offset[i];
    2211             :       // Draw a glyph in a clipped area so that we don't have hairy chars
    2212             :       // pending outside
    2213           0 :       nsRect clipRect = unionRect;
    2214             :       // Clip at the join to get a solid edge (without overlap or gap), when
    2215             :       // this won't change the glyph too much.  If the glyph is too small to
    2216             :       // clip then we'll overlap rather than have a gap.
    2217           0 :       nscoord height = mBmData[i].ascent + mBmData[i].descent;
    2218           0 :       if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
    2219           0 :         if (0 == i) { // top
    2220           0 :           clipRect.height = end[i] - clipRect.y;
    2221             :         }
    2222           0 :         else if (2 == i) { // bottom
    2223           0 :           clipRect.height -= start[i] - clipRect.y;
    2224           0 :           clipRect.y = start[i];
    2225             :         }
    2226             :         else { // middle
    2227           0 :           clipRect.y = start[i];
    2228           0 :           clipRect.height = end[i] - start[i];
    2229             :         }
    2230             :       }
    2231           0 :       if (!clipRect.IsEmpty()) {
    2232           0 :         AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
    2233           0 :         mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params);
    2234             :       }
    2235             :     }
    2236             :   }
    2237             : 
    2238             :   ///////////////
    2239             :   // fill the gap between top and middle, and between middle and bottom.
    2240           0 :   if (!mGlyphs[3]) { // null glue : draw a rule
    2241             :     // figure out the dimensions of the rule to be drawn :
    2242             :     // set lbearing to rightmost lbearing among the two current successive
    2243             :     // parts.
    2244             :     // set rbearing to leftmost rbearing among the two current successive parts.
    2245             :     // this not only satisfies the convention used for over/underbraces
    2246             :     // in TeX, but also takes care of broken fonts like the stretchy integral
    2247             :     // in Symbol for small font sizes in unix.
    2248             :     nscoord lbearing, rbearing;
    2249           0 :     int32_t first = 0, last = 1;
    2250           0 :     while (last <= 2) {
    2251           0 :       if (mGlyphs[last]) {
    2252           0 :         lbearing = mBmData[last].leftBearing;
    2253           0 :         rbearing = mBmData[last].rightBearing;
    2254           0 :         if (mGlyphs[first]) {
    2255           0 :           if (lbearing < mBmData[first].leftBearing)
    2256           0 :             lbearing = mBmData[first].leftBearing;
    2257           0 :           if (rbearing > mBmData[first].rightBearing)
    2258           0 :             rbearing = mBmData[first].rightBearing;
    2259             :         }
    2260             :       }
    2261           0 :       else if (mGlyphs[first]) {
    2262           0 :         lbearing = mBmData[first].leftBearing;
    2263           0 :         rbearing = mBmData[first].rightBearing;
    2264             :       }
    2265             :       else {
    2266           0 :         NS_ERROR("Cannot stretch - All parts missing");
    2267           0 :         return NS_ERROR_UNEXPECTED;
    2268             :       }
    2269             :       // paint the rule between the parts
    2270           0 :       nsRect rule(aRect.x + lbearing, end[first],
    2271           0 :                   rbearing - lbearing, start[last] - end[first]);
    2272           0 :       PaintRule(aDrawTarget, oneDevPixel, rule, aColor);
    2273           0 :       first = last;
    2274           0 :       last++;
    2275             :     }
    2276             :   }
    2277           0 :   else if (mBmData[3].ascent + mBmData[3].descent > 0) {
    2278             :     // glue is present
    2279           0 :     nsBoundingMetrics& bm = mBmData[3];
    2280             :     // Ensure the stride for the glue is not reduced to less than one pixel
    2281           0 :     if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
    2282             :       // To protect against gaps, pretend the glue is smaller than it is,
    2283             :       // in order to trim off ends and thus get a solid edge for the join.
    2284           0 :       bm.ascent -= oneDevPixel;
    2285           0 :       bm.descent -= oneDevPixel;
    2286             :     }
    2287             : 
    2288           0 :     nsRect clipRect = unionRect;
    2289             : 
    2290           0 :     for (i = 0; i < 2; ++i) {
    2291             :       // Make sure not to draw outside the character
    2292           0 :       nscoord dy = std::max(end[i], aRect.y);
    2293           0 :       nscoord fillEnd = std::min(start[i+1], aRect.YMost());
    2294           0 :       while (dy < fillEnd) {
    2295           0 :         clipRect.y = dy;
    2296           0 :         clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy);
    2297           0 :         AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
    2298           0 :         dy += bm.ascent;
    2299           0 :         mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params);
    2300           0 :         dy += bm.descent;
    2301             :       }
    2302             :     }
    2303             :   }
    2304             : #ifdef DEBUG
    2305             :   else {
    2306           0 :     for (i = 0; i < 2; ++i) {
    2307           0 :       NS_ASSERTION(end[i] >= start[i+1],
    2308             :                    "gap between parts with missing glue glyph");
    2309             :     }
    2310             :   }
    2311             : #endif
    2312           0 :   return NS_OK;
    2313             : }
    2314             : 
    2315             : // paint a stretchy char by assembling glyphs horizontally
    2316             : nsresult
    2317           0 : nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
    2318             :                                 gfxContext*    aThebesContext,
    2319             :                                 nsRect&        aRect,
    2320             :                                 nscolor        aColor)
    2321             : {
    2322           0 :   DrawTarget& aDrawTarget = *aThebesContext->GetDrawTarget();
    2323             : 
    2324             :   // Get the device pixel size in the horizontal direction.
    2325             :   // (This makes no effort to optimize for non-translation transformations.)
    2326           0 :   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
    2327             : 
    2328             :   // get metrics data to be re-used later
    2329           0 :   int32_t i = 0;
    2330           0 :   nscoord dy = aRect.y + mBoundingMetrics.ascent;
    2331             :   nscoord offset[3], start[3], end[3];
    2332           0 :   for (i = 0; i <= 2; ++i) {
    2333           0 :     const nsBoundingMetrics& bm = mBmData[i];
    2334             :     nscoord dx;
    2335           0 :     if (0 == i) { // left
    2336           0 :       dx = aRect.x - bm.leftBearing;
    2337             :     }
    2338           0 :     else if (2 == i) { // right
    2339           0 :       dx = aRect.x + aRect.width - bm.rightBearing;
    2340             :     }
    2341             :     else { // middle
    2342           0 :       dx = aRect.x + (aRect.width - bm.width)/2;
    2343             :     }
    2344             :     // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
    2345             :     // Do this now so that we can get the other dimensions right.
    2346             :     // (This may not achieve much with non-rectangular transformations.)
    2347           0 :     dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x;
    2348             :     // abcissa passed to Draw
    2349           0 :     offset[i] = dx;
    2350             :     // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
    2351             :     // pixel, so the bm values can include 1 row of faint pixels on each edge.
    2352             :     // Don't rely on this pixel as it can look like a gap.
    2353           0 :     if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) {
    2354           0 :       start[i] = dx + bm.leftBearing + oneDevPixel; // left join
    2355           0 :       end[i] = dx + bm.rightBearing - oneDevPixel; // right join
    2356             :     } else {
    2357             :       // To avoid overlaps, we don't add one pixel on each side when the part
    2358             :       // is too small.
    2359           0 :       start[i] = dx + bm.leftBearing; // left join
    2360           0 :       end[i] = dx + bm.rightBearing; // right join
    2361             :     }
    2362             :   }
    2363             : 
    2364             :   // If there are overlaps, then join at the mid point
    2365           0 :   for (i = 0; i < 2; ++i) {
    2366           0 :     if (end[i] > start[i+1]) {
    2367           0 :       end[i] = (end[i] + start[i+1]) / 2;
    2368           0 :       start[i+1] = end[i];
    2369             :     }
    2370             :   }
    2371             : 
    2372           0 :   nsRect unionRect = aRect;
    2373           0 :   unionRect.Inflate(oneDevPixel, oneDevPixel);
    2374             : 
    2375           0 :   gfxTextRun::DrawParams params(aThebesContext);
    2376             : 
    2377             :   ///////////////////////////
    2378             :   // draw left, middle, right
    2379           0 :   for (i = 0; i <= 2; ++i) {
    2380             :     // glue can be null
    2381           0 :     if (mGlyphs[i]) {
    2382           0 :       nscoord dx = offset[i];
    2383           0 :       nsRect clipRect = unionRect;
    2384             :       // Clip at the join to get a solid edge (without overlap or gap), when
    2385             :       // this won't change the glyph too much.  If the glyph is too small to
    2386             :       // clip then we'll overlap rather than have a gap.
    2387           0 :       nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing;
    2388           0 :       if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
    2389           0 :         if (0 == i) { // left
    2390           0 :           clipRect.width = end[i] - clipRect.x;
    2391             :         }
    2392           0 :         else if (2 == i) { // right
    2393           0 :           clipRect.width -= start[i] - clipRect.x;
    2394           0 :           clipRect.x = start[i];
    2395             :         }
    2396             :         else { // middle
    2397           0 :           clipRect.x = start[i];
    2398           0 :           clipRect.width = end[i] - start[i];
    2399             :         }
    2400             :       }
    2401           0 :       if (!clipRect.IsEmpty()) {
    2402           0 :         AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
    2403           0 :         mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params);
    2404             :       }
    2405             :     }
    2406             :   }
    2407             : 
    2408             :   ////////////////
    2409             :   // fill the gap between left and middle, and between middle and right.
    2410           0 :   if (!mGlyphs[3]) { // null glue : draw a rule
    2411             :     // figure out the dimensions of the rule to be drawn :
    2412             :     // set ascent to lowest ascent among the two current successive parts.
    2413             :     // set descent to highest descent among the two current successive parts.
    2414             :     // this satisfies the convention used for over/underbraces, and helps
    2415             :     // fix broken fonts.
    2416             :     nscoord ascent, descent;
    2417           0 :     int32_t first = 0, last = 1;
    2418           0 :     while (last <= 2) {
    2419           0 :       if (mGlyphs[last]) {
    2420           0 :         ascent = mBmData[last].ascent;
    2421           0 :         descent = mBmData[last].descent;
    2422           0 :         if (mGlyphs[first]) {
    2423           0 :           if (ascent > mBmData[first].ascent)
    2424           0 :             ascent = mBmData[first].ascent;
    2425           0 :           if (descent > mBmData[first].descent)
    2426           0 :             descent = mBmData[first].descent;
    2427             :         }
    2428             :       }
    2429           0 :       else if (mGlyphs[first]) {
    2430           0 :         ascent = mBmData[first].ascent;
    2431           0 :         descent = mBmData[first].descent;
    2432             :       }
    2433             :       else {
    2434           0 :         NS_ERROR("Cannot stretch - All parts missing");
    2435           0 :         return NS_ERROR_UNEXPECTED;
    2436             :       }
    2437             :       // paint the rule between the parts
    2438             :       nsRect rule(end[first], dy - ascent,
    2439           0 :                   start[last] - end[first], ascent + descent);
    2440           0 :       PaintRule(aDrawTarget, oneDevPixel, rule, aColor);
    2441           0 :       first = last;
    2442           0 :       last++;
    2443             :     }
    2444             :   }
    2445           0 :   else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) {
    2446             :     // glue is present
    2447           0 :     nsBoundingMetrics& bm = mBmData[3];
    2448             :     // Ensure the stride for the glue is not reduced to less than one pixel
    2449           0 :     if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
    2450             :       // To protect against gaps, pretend the glue is smaller than it is,
    2451             :       // in order to trim off ends and thus get a solid edge for the join.
    2452           0 :       bm.leftBearing += oneDevPixel;
    2453           0 :       bm.rightBearing -= oneDevPixel;
    2454             :     }
    2455             : 
    2456           0 :     nsRect clipRect = unionRect;
    2457             : 
    2458           0 :     for (i = 0; i < 2; ++i) {
    2459             :       // Make sure not to draw outside the character
    2460           0 :       nscoord dx = std::max(end[i], aRect.x);
    2461           0 :       nscoord fillEnd = std::min(start[i+1], aRect.XMost());
    2462           0 :       while (dx < fillEnd) {
    2463           0 :         clipRect.x = dx;
    2464           0 :         clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx);
    2465           0 :         AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
    2466           0 :         dx -= bm.leftBearing;
    2467           0 :         mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params);
    2468           0 :         dx += bm.rightBearing;
    2469             :       }
    2470             :     }
    2471             :   }
    2472             : #ifdef DEBUG
    2473             :   else { // no glue
    2474           0 :     for (i = 0; i < 2; ++i) {
    2475           0 :       NS_ASSERTION(end[i] >= start[i+1],
    2476             :                    "gap between parts with missing glue glyph");
    2477             :     }
    2478             :   }
    2479             : #endif
    2480           0 :   return NS_OK;
    2481             : }

Generated by: LCOV version 1.13