Bug 663740 - Migrate nsMathMLChar measuring and drawing from nsRenderingContext to gfx/thebes classes. r=karlt
authorFrédéric Wang <fred.wang@free.fr>
Thu, 13 Feb 2014 09:53:52 -0500
changeset 168580 ea9b012bfe3e15eb3569ce98e01be304f89652be
parent 168579 a802f84d9c943e62bb698de4277736e3dcc113ae
child 168581 3faa29f938c547c5e4be01bfbb93c02033d6fb73
push id26210
push userkwierso@gmail.com
push dateFri, 14 Feb 2014 00:52:58 +0000
treeherdermozilla-central@0f18070eb5d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs663740
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 663740 - Migrate nsMathMLChar measuring and drawing from nsRenderingContext to gfx/thebes classes. r=karlt
layout/mathml/nsMathMLChar.cpp
layout/mathml/nsMathMLChar.h
layout/mathml/nsMathMLmrootFrame.cpp
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -28,32 +28,30 @@
 #include "nsMathMLOperators.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 //#define NOISY_SEARCH 1
 
 // -----------------------------------------------------------------------------
-static const nsGlyphCode kNullGlyph = {{0, 0}, 0};
+static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
 typedef enum {eExtension_base, eExtension_variants, eExtension_parts}
   nsMathfontPrefExtension;
 
 // -----------------------------------------------------------------------------
 // nsGlyphTable is a class that provides an interface for accessing glyphs
 // of stretchy chars. It acts like a table that stores the variants of bigger
 // sizes (if any) and the partial glyphs needed to build extensible symbols.
 // An instance of nsGlyphTable is associated to one primary font. Extra glyphs
 // can be taken in other additional fonts when stretching certain characters.
 // These supplementary fonts are referred to as "external" fonts to the table.
 //
-// A char for which nsGlyphTable::Has(aChar) is true means that the table
-// contains some glyphs (bigger and/or partial) that can be used to render
-// the char. Bigger sizes (if any) of the char can then be retrieved with
-// BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
+// Bigger sizes (if any) of the char can then be retrieved with
+// BigOf(aSize). Partial glyphs can be retrieved with ElementAt()
 //
 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
 // points or as direct glyph indices, depending on the type of the table.
 // XXX The latter is not yet supported.
 
 // General format of MathFont Property Files from which glyph data are
 // retrieved:
 // -----------------------------------------------------------------------------
@@ -121,56 +119,38 @@ public:
 
   const nsAString& PrimaryFontName() const
   {
     return mFontName[0];
   }
 
   const nsAString& FontNameFor(const nsGlyphCode& aGlyphCode) const
   {
+    NS_ASSERTION(!aGlyphCode.IsGlyphID(),
+                 "nsGlyphTable can only access glyphs by Unicode code point");
     return mFontName[aGlyphCode.font];
   }
 
-  // True if this table contains some glyphs (variants and/or parts)
-  // or contains child chars that can be used to render this char
-  bool Has(nsPresContext* aPresContext, nsMathMLChar* aChar);
-
-  // True if this table contains variants of larger sizes to render this char
-  bool HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
+  // Getters for the parts
+  nsGlyphCode ElementAt(char16_t aChar, uint32_t aPosition);
+  nsGlyphCode BigOf(char16_t aChar, int32_t aSize) {
+    return ElementAt(aChar, 4 + aSize);
+  }
 
   // True if this table contains parts to render this char
-  bool HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
-
-  // Getters for the parts
-  nsGlyphCode TopOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
-    return ElementAt(aPresContext, aChar, 0);
-  }
-  nsGlyphCode MiddleOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
-    return ElementAt(aPresContext, aChar, 1);
-  }
-  nsGlyphCode BottomOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
-    return ElementAt(aPresContext, aChar, 2);
-  }
-  nsGlyphCode GlueOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
-    return ElementAt(aPresContext, aChar, 3);
-  }
-  nsGlyphCode BigOf(nsPresContext* aPresContext, nsMathMLChar* aChar,
-                    int32_t aSize) {
-    return ElementAt(aPresContext, aChar, 4 + aSize);
-  }
-  nsGlyphCode LeftOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
-    return ElementAt(aPresContext, aChar, 0);
-  }
-  nsGlyphCode RightOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
-    return ElementAt(aPresContext, aChar, 2);
+  bool HasPartsOf(char16_t aChar) {
+    return (ElementAt(aChar, 0).Exists() || ElementAt(aChar, 1).Exists() ||
+            ElementAt(aChar, 2).Exists() || ElementAt(aChar, 3).Exists());
   }
 
+  gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                          int32_t            aAppUnitsPerDevPixel,
+                          gfxFontGroup*      aFontGroup,
+                          const nsGlyphCode& aGlyph);
 private:
-  nsGlyphCode ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar,
-                        uint32_t aPosition);
 
   // mFontName[0] is the primary font associated to this table. The others 
   // are possible "external" fonts for glyphs not in the primary font
   // but which are needed to stretch certain characters in the table
   nsTArray<nsString> mFontName;
 
   // Tri-state variable for error/empty/ready
   int32_t mState;
@@ -197,18 +177,17 @@ private:
   // A font identifier of '0' means the default primary font associated to this
   // table. Other digits map to the "external" fonts that may have been
   // specified in the MathFont Property File.
   nsString  mGlyphCache;
   char16_t mCharCache;
 };
 
 nsGlyphCode
-nsGlyphTable::ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar,
-                        uint32_t aPosition)
+nsGlyphTable::ElementAt(char16_t aChar, uint32_t aPosition)
 {
   if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
   // Load glyph properties if this is the first time we have been here
   if (mState == NS_TABLE_STATE_EMPTY) {
     nsresult rv = LoadProperties(mFontName[0], mGlyphProperties);
 #ifdef DEBUG
     nsAutoCString uriStr;
     uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
@@ -234,21 +213,20 @@ nsGlyphTable::ElementAt(nsPresContext* a
       rv = mGlyphProperties->GetStringProperty(key, value);
       if (NS_FAILED(rv)) break;
       Clean(value);
       mFontName.AppendElement(value); // i.e., mFontName[i] holds this font name
     }
   }
 
   // Update our cache if it is not associated to this character
-  char16_t uchar = aChar->mData[0];
-  if (mCharCache != uchar) {
+  if (mCharCache != aChar) {
     // The key in the property file is interpreted as ASCII and kept
     // as such ...
-    char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", uchar);
+    char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", aChar);
     nsAutoString value;
     nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key),
                                                       value);
     if (NS_FAILED(rv)) return kNullGlyph;
     Clean(value);
     // See if this char uses external fonts; e.g., if the 2nd glyph is taken
     // from the external font '1', the property line looks like
     // \uNNNN = \uNNNN\uNNNN@1\uNNNN.
@@ -290,49 +268,39 @@ nsGlyphTable::ElementAt(nsPresContext* a
         if (!mFontName[font].Length()) {
           return kNullGlyph;
         }
       }
       buffer.Append(font);
     }
     // update our cache with the new settings
     mGlyphCache.Assign(buffer);
-    mCharCache = uchar;
+    mCharCache = aChar;
   }
 
   // 3* is to account for the code@font pairs
   uint32_t index = 3*aPosition;
   if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
   nsGlyphCode ch;
   ch.code[0] = mGlyphCache.CharAt(index);
   ch.code[1] = mGlyphCache.CharAt(index + 1);
   ch.font = mGlyphCache.CharAt(index + 2);
   return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
 }
 
-bool
-nsGlyphTable::Has(nsPresContext* aPresContext, nsMathMLChar* aChar)
-{
-  return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
-}
-
-bool
-nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
+gfxTextRun*
+nsGlyphTable::MakeTextRun(gfxContext*        aThebesContext,
+                          int32_t            aAppUnitsPerDevPixel,
+                          gfxFontGroup*      aFontGroup,
+                          const nsGlyphCode& aGlyph)
 {
-  //XXXkt all variants must be in the same file as size 1
-  return BigOf(aPresContext, aChar, 1).Exists();
-}
-
-bool
-nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
-{
-  return GlueOf(aPresContext, aChar).Exists() ||
-    TopOf(aPresContext, aChar).Exists() ||
-    BottomOf(aPresContext, aChar).Exists() ||
-    MiddleOf(aPresContext, aChar).Exists();
+  NS_ASSERTION(!aGlyph.IsGlyphID(), "not yet implemented");
+  return aFontGroup->
+    MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext,
+                aAppUnitsPerDevPixel, 0);
 }
 
 // -----------------------------------------------------------------------------
 // This is the list of all the applicable glyph tables.
 // We will maintain a single global instance that will only reveal those
 // glyph tables that are associated to fonts currently installed on the
 // user' system. The class is an XPCOM shutdown observer to allow us to
 // free its allocated data at shutdown
@@ -358,21 +326,16 @@ public:
 
   nsresult Initialize();
   nsresult Finalize();
 
   // Add a glyph table in the list, return the new table that was added
   nsGlyphTable*
   AddGlyphTable(const nsString& aPrimaryFontName);
 
-  // Find a glyph table in the list that has a glyph for the given char
-  nsGlyphTable*
-  GetGlyphTableFor(nsPresContext* aPresContext,
-                   nsMathMLChar*  aChar);
-
   // Find the glyph table in the list corresponding to the given font family.
   nsGlyphTable*
   GetGlyphTableFor(const nsAString& aFamily);
 
 private:
   nsGlyphTable* TableAt(int32_t aIndex) {
     return &mTableList.ElementAt(aIndex);
   }
@@ -442,33 +405,16 @@ nsGlyphTableList::AddGlyphTable(const ns
     return glyphTable;
 
   // allocate a table
   glyphTable = mTableList.AppendElement(aPrimaryFontName);
   return glyphTable;
 }
 
 nsGlyphTable*
-nsGlyphTableList::GetGlyphTableFor(nsPresContext* aPresContext, 
-                                   nsMathMLChar*   aChar)
-{
-  if (mUnicodeTable.Has(aPresContext, aChar))
-    return &mUnicodeTable;
-
-  int32_t i;
-  for (i = 0; i < Count(); i++) {
-    nsGlyphTable* glyphTable = TableAt(i);
-    if (glyphTable->Has(aPresContext, aChar)) {
-      return glyphTable;
-    }
-  }
-  return nullptr;
-}
-
-nsGlyphTable*
 nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
 {
   for (int32_t i = 0; i < Count(); i++) {
     nsGlyphTable* glyphTable = TableAt(i);
     const nsAString& fontName = glyphTable->PrimaryFontName();
     // TODO: would be nice to consider StripWhitespace and other aliasing
     if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
       return glyphTable;
@@ -640,23 +586,21 @@ nsMathMLChar::SetData(nsPresContext* aPr
   if (!gGlyphTableInitialized) {
     InitGlobals(aPresContext);
   }
   mData = aData;
   // some assumptions until proven otherwise
   // note that mGlyph is not initialized
   mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
   mBoundingMetrics = nsBoundingMetrics();
-  mGlyphTable = nullptr;
   // check if stretching is applicable ...
   if (gGlyphTableList && (1 == mData.Length())) {
     mDirection = nsMathMLOperators::GetStretchyDirection(mData);
     // default tentative table (not the one that is necessarily going
     // to be used)
-    mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
   }
 }
 
 // -----------------------------------------------------------------------------
 /*
  The Stretch:
  @param aContainerSize - suggested size for the stretched char
  @param aDesiredStretchSize - OUT parameter. The desired size
@@ -901,64 +845,83 @@ insert:
     aFontName.Insert(aFallbackFamilies + NS_LITERAL_STRING(","),
                      p_name - p_begin);
   }
   else { // whitespace or empty
     aFontName = aFallbackFamilies;
   }
 }
 
-// Update the font and rendering context if there is a family change
-static bool
-SetFontFamily(nsStyleContext*      aStyleContext,
-              nsRenderingContext&  aRenderingContext,
-              nsFont&              aFont,
-              const nsGlyphTable*  aGlyphTable,
-              const nsGlyphCode&   aGlyphCode,
-              const nsAString&     aDefaultFamily)
+// Update the font if there is a family change and returns the font group.
+bool
+nsMathMLChar::SetFontFamily(nsPresContext*          aPresContext,
+                            const nsGlyphTable*     aGlyphTable,
+                            const nsGlyphCode&      aGlyphCode,
+                            const nsAString&        aDefaultFamily,
+                            nsFont&                 aFont,
+                            nsRefPtr<gfxFontGroup>* aFontGroup)
 {
   const nsAString& family =
     aGlyphCode.font ? aGlyphTable->FontNameFor(aGlyphCode) : aDefaultFamily;
-  if (! family.Equals(aFont.name)) {
+  if (!*aFontGroup || !family.Equals(aFont.name)) {
     nsFont font = aFont;
     font.name = family;
     nsRefPtr<nsFontMetrics> fm;
-    aRenderingContext.DeviceContext()->GetMetricsFor(font,
-      aStyleContext->StyleFont()->mLanguage,
-      aStyleContext->PresContext()->GetUserFontSet(),
-      aStyleContext->PresContext()->GetTextPerfMetrics(),
-      *getter_AddRefs(fm));
+    aPresContext->DeviceContext()->
+      GetMetricsFor(font,
+                    mStyleContext->StyleFont()->mLanguage,
+                    aPresContext->GetUserFontSet(),
+                    aPresContext->GetTextPerfMetrics(),
+                    *getter_AddRefs(fm));
     // Set the font if it is an unicode table
     // or if the same family name has been found
     if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
         fm->GetThebesFontGroup()->GetFontAt(0)->GetFontEntry()->
         FamilyName() == family) {
       aFont.name = family;
-      aRenderingContext.SetFont(fm);
+      *aFontGroup = fm->GetThebesFontGroup();
     } else {
       return false; // We did not set the font
     }
   }
   return true;
 }
 
+static nsBoundingMetrics
+MeasureTextRun(gfxContext* aThebesContext, gfxTextRun* aTextRun)
+{
+  gfxTextRun::Metrics metrics =
+    aTextRun->MeasureText(0, aTextRun->GetLength(),
+                          gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
+                          aThebesContext, nullptr);
+
+  nsBoundingMetrics bm;
+  bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
+  bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
+  bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
+  bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
+  bm.width = NSToCoordRound(metrics.mAdvanceWidth);
+
+  return bm;
+}
+
 class nsMathMLChar::StretchEnumContext {
 public:
   StretchEnumContext(nsMathMLChar*        aChar,
                      nsPresContext*       aPresContext,
-                     nsRenderingContext& aRenderingContext,
+                     gfxContext*          aThebesContext,
                      nsStretchDirection   aStretchDirection,
                      nscoord              aTargetSize,
                      uint32_t             aStretchHint,
                      nsBoundingMetrics&   aStretchedMetrics,
                      const nsAString&     aFamilies,
                      bool&              aGlyphFound)
     : mChar(aChar),
       mPresContext(aPresContext),
-      mRenderingContext(aRenderingContext),
+      mThebesContext(aThebesContext),
       mDirection(aStretchDirection),
       mTargetSize(aTargetSize),
       mStretchHint(aStretchHint),
       mBoundingMetrics(aStretchedMetrics),
       mFamilies(aFamilies),
       mTryVariants(true),
       mTryParts(true),
       mGlyphFound(aGlyphFound) {}
@@ -967,46 +930,45 @@ public:
   EnumCallback(const nsString& aFamily, bool aGeneric, void *aData);
 
 private:
   bool TryVariants(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
   bool TryParts(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
 
   nsMathMLChar* mChar;
   nsPresContext* mPresContext;
-  nsRenderingContext& mRenderingContext;
+  gfxContext* mThebesContext;
   const nsStretchDirection mDirection;
   const nscoord mTargetSize;
   const uint32_t mStretchHint;
   nsBoundingMetrics& mBoundingMetrics;
   // Font families to search
   const nsAString& mFamilies;
 
 public:
   bool mTryVariants;
   bool mTryParts;
 
 private:
   nsAutoTArray<nsGlyphTable*,16> mTablesTried;
-  nsGlyphTable* mGlyphTable; // for this callback
   bool&       mGlyphFound;
 };
 
 
 // 2. See if there are any glyphs of the appropriate size.
 // Returns true if the size is OK, false to keep searching.
 // Always updates the char if a better match is found.
 bool
 nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable*    aGlyphTable,
                                               const nsAString& aFamily)
 {
   // Use our stretchy style context now that stretching is in progress
   nsStyleContext *sc = mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
-  // Ensure mRenderingContext.SetFont will be called:
+  // Ensure SetFontFamily will set the font
   font.name.Truncate();
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
   bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
   bool largeopOnly =
     largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
 
@@ -1018,32 +980,33 @@ nsMathMLChar::StretchEnumContext::TryVar
   // start at size = 1 (size = 0 is the char at its normal size)
   int32_t size = 1;
 #ifdef NOISY_SEARCH
   printf("  searching in %s ...\n",
            NS_LossyConvertUTF16toASCII(aFamily).get());
 #endif
 
   nsGlyphCode ch;
-  while ((ch = aGlyphTable->BigOf(mPresContext, mChar, size)).Exists()) {
+  nsRefPtr<gfxFontGroup> fontGroup;
+  while ((ch = aGlyphTable->BigOf(mChar->mData[0], size)).Exists()) {
 
-    if(!SetFontFamily(sc, mRenderingContext, font, aGlyphTable, ch, aFamily)) {
+    if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
+                              &fontGroup)) {
       // if largeopOnly is set, break now
       if (largeopOnly) break;
       ++size;
       continue;
     }
 
-    NS_ASSERTION(maxWidth || ch.code[0] != mChar->mGlyph.code[0] ||
-                 ch.code[1] != mChar->mGlyph.code[1] ||
-                 !font.name.Equals(mChar->mFamily),
-                 "glyph table incorrectly set -- duplicate found");
+    nsAutoPtr<gfxTextRun> textRun;
+    textRun = aGlyphTable->MakeTextRun(mThebesContext,
+                                       mPresContext->AppUnitsPerDevPixel(),
+                                       fontGroup, ch);
+    nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
 
-    nsBoundingMetrics bm = mRenderingContext.GetBoundingMetrics(ch.code,
-                                                                ch.Length());
     nscoord charSize =
       isVertical ? bm.ascent + bm.descent
       : bm.rightBearing - bm.leftBearing;
 
     if (largeopOnly ||
         IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
       mGlyphFound = true;
       if (maxWidth) {
@@ -1057,19 +1020,18 @@ nsMathMLChar::StretchEnumContext::TryVar
           mBoundingMetrics.rightBearing = bm.rightBearing;
         // Continue to check other sizes unless largeopOnly
         haveBetter = largeopOnly;
       }
       else {
         mBoundingMetrics = bm;
         haveBetter = true;
         bestSize = charSize;
-        mChar->mGlyphTable = aGlyphTable;
-        mChar->mGlyph = ch;
-        mChar->mFamily = font.name;
+        mChar->mGlyphs[0] = textRun;
+        mChar->mDraw = DRAW_VARIANT;
       }
 #ifdef NOISY_SEARCH
       printf("    size:%d Current best\n", size);
 #endif
     }
     else {
 #ifdef NOISY_SEARCH
       printf("    size:%d Rejected!\n", size);
@@ -1090,65 +1052,61 @@ nsMathMLChar::StretchEnumContext::TryVar
 
 // 3. Build by parts.
 // Returns true if the size is OK, false to keep searching.
 // Always updates the char if a better match is found.
 bool
 nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable*    aGlyphTable,
                                            const nsAString& aFamily)
 {
-  if (!aGlyphTable->HasPartsOf(mPresContext, mChar))
+  if (!aGlyphTable->HasPartsOf(mChar->mData[0]))
     return false; // to next table
 
   // See if the parts of this table fit in the desired space //////////////////
 
   // Use our stretchy style context now that stretching is in progress
   nsFont font = mChar->mStyleContext->StyleFont()->mFont;
-  // Ensure mRenderingContext.SetFont will be called:
+  // Ensure SetFontFamily will set the font
   font.name.Truncate();
 
   // Compute the bounding metrics of all partial glyphs
+  nsAutoPtr<gfxTextRun> textRun[4];
   nsGlyphCode chdata[4];
   nsBoundingMetrics bmdata[4];
   nscoord sizedata[4];
-  nsGlyphCode glue = aGlyphTable->GlueOf(mPresContext, mChar);
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
+  nsRefPtr<gfxFontGroup> fontGroup;
 
   for (int32_t i = 0; i < 4; i++) {
-    nsGlyphCode ch;
-    switch (i) {
-    case 0: ch = aGlyphTable->TopOf(mPresContext, mChar);    break;
-    case 1: ch = aGlyphTable->MiddleOf(mPresContext, mChar); break;
-    case 2: ch = aGlyphTable->BottomOf(mPresContext, mChar); break;
-    case 3: ch = glue;                                       break;
-    }
-    // empty slots are filled with the glue if it is not null
-    if (!ch.Exists()) ch = glue;
+    nsGlyphCode ch = aGlyphTable->ElementAt(mChar->mData[0], i);
     chdata[i] = ch;
-    if (!ch.Exists()) {
-      // Null glue indicates that a rule will be drawn, which can stretch to
-      // fill any space.  Leave bounding metrics at 0.
-      sizedata[i] = mTargetSize;
-    }
-    else {
-      if (!SetFontFamily(mChar->mStyleContext, mRenderingContext,
-                         font, aGlyphTable, ch, aFamily))
+    if (ch.Exists()) {
+      if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
+                                &fontGroup))
         return false;
 
-      nsBoundingMetrics bm = mRenderingContext.GetBoundingMetrics(ch.code,
-                                                                  ch.Length());
+      textRun[i] = aGlyphTable->MakeTextRun(mThebesContext,
+                                            mPresContext->AppUnitsPerDevPixel(),
+                                            fontGroup, ch);
+      nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun[i]);
 
       // TODO: For the generic Unicode table, ideally we should check that the
       // glyphs are actually found and that they each come from the same
       // font.
       bmdata[i] = bm;
       sizedata[i] = isVertical ? bm.ascent + bm.descent
                                : bm.rightBearing - bm.leftBearing;
+    } else {
+      // Null glue indicates that a rule will be drawn, which can stretch to
+      // fill any space.
+      textRun[i] = nullptr;
+      bmdata[i] = nsBoundingMetrics();
+      sizedata[i] = i == 3 ? mTargetSize : 0;
     }
   }
 
   // Build by parts if we have successfully computed the
   // bounding metrics of all parts.
   nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
                                               mTargetSize);
 
@@ -1168,69 +1126,81 @@ nsMathMLChar::StretchEnumContext::TryPar
   printf("    Font %s Current best!\n",
          NS_LossyConvertUTF16toASCII(fontName).get());
 #endif
 
   // The computed size is the best we have found so far...
   // now is the time to compute and cache our bounding metrics
   if (isVertical) {
     int32_t i;
-    nscoord lbearing;
-    nscoord rbearing;
-    nscoord width;
-    if (maxWidth) {
-      lbearing = mBoundingMetrics.leftBearing;
-      rbearing = mBoundingMetrics.rightBearing;
-      width = mBoundingMetrics.width;
-      i = 0;
+    // Try and find the first existing part and then determine the extremal
+    // horizontal metrics of the parts.
+    for (i = 0; i <= 3 && !textRun[i]; i++);
+    if (i == 4) {
+      NS_ERROR("Cannot stretch - All parts missing");
+      return false;
     }
-    else {
-      lbearing = bmdata[0].leftBearing;
-      rbearing = bmdata[0].rightBearing;
-      width = bmdata[0].width;
-      i = 1;
+    nscoord lbearing = bmdata[i].leftBearing;
+    nscoord rbearing = bmdata[i].rightBearing;
+    nscoord width = bmdata[i].width;
+    i++;
+    for (; i <= 3; i++) {
+      if (!textRun[i]) continue;
+      lbearing = std::min(lbearing, bmdata[i].leftBearing);
+      rbearing = std::max(rbearing, bmdata[i].rightBearing);
+      width = std::max(width, bmdata[i].width);
     }
-    for (; i < 4; i++) {
-      const nsBoundingMetrics& bm = bmdata[i];
-      if (width < bm.width) width = bm.width;
-      if (lbearing > bm.leftBearing) lbearing = bm.leftBearing;
-      if (rbearing < bm.rightBearing) rbearing = bm.rightBearing;
+    if (maxWidth) {
+      lbearing = std::min(lbearing, mBoundingMetrics.leftBearing);
+      rbearing = std::max(rbearing, mBoundingMetrics.rightBearing);
+      width = std::max(width, mBoundingMetrics.width);
     }
     mBoundingMetrics.width = width;
     // When maxWidth, updating ascent and descent indicates that no characters
     // larger than this character's minimum size need to be checked as they
     // will not be used.
     mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent
                                                 // for height
     mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
     mBoundingMetrics.leftBearing = lbearing;
     mBoundingMetrics.rightBearing = rbearing;
   }
   else {
-    nscoord ascent = bmdata[0].ascent;
-    nscoord descent = bmdata[0].descent;
-    for (int32_t i = 1; i < 4; i++) {
-      const nsBoundingMetrics& bm = bmdata[i];
-      if (ascent < bm.ascent) ascent = bm.ascent;
-      if (descent < bm.descent) descent = bm.descent;
+    int32_t i;
+    // Try and find the first existing part and then determine the extremal
+    // vertical metrics of the parts.
+    for (i = 0; i <= 3 && !textRun[i]; i++);
+    if (i == 4) {
+      NS_ERROR("Cannot stretch - All parts missing");
+      return false;
+    }
+    nscoord ascent = bmdata[i].ascent;
+    nscoord descent = bmdata[i].descent;
+    i++;
+    for (; i <= 3; i++) {
+      if (!textRun[i]) continue;
+      ascent = std::max(ascent, bmdata[i].ascent);
+      descent = std::max(descent, bmdata[i].descent);
     }
     mBoundingMetrics.width = computedSize;
     mBoundingMetrics.ascent = ascent;
     mBoundingMetrics.descent = descent;
     mBoundingMetrics.leftBearing = 0;
     mBoundingMetrics.rightBearing = computedSize;
   }
   mGlyphFound = true;
   if (maxWidth)
     return false; // Continue to check other sizes
 
   // reset
-  mChar->mGlyph = kNullGlyph; // this will tell paint to build by parts
-  mChar->mGlyphTable = aGlyphTable;
-  mChar->mFamily = aFamily;
+  mChar->mDraw = DRAW_PARTS;
+  for (int32_t i = 0; i < 4; i++) {
+    mChar->mGlyphs[i] = textRun[i];
+    mChar->mBmData[i] = bmdata[i];
+  }
 
   return IsSizeOK(mPresContext, computedSize, mTargetSize, mStretchHint);
 }
 
 // This is called for each family, whether it exists or not
 bool
 nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily,
                                                bool aGeneric, void *aData)
@@ -1245,22 +1215,22 @@ nsMathMLChar::StretchEnumContext::EnumCa
 
   if (context->mTablesTried.Contains(glyphTable))
     return true; // already tried this one
 
   // Check font family if it is not a generic one
   // We test with the kNullGlyph
   nsStyleContext *sc = context->mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
-  if (!aGeneric && !SetFontFamily(sc, context->mRenderingContext,
-                                  font, nullptr, kNullGlyph, aFamily))
+  nsRefPtr<gfxFontGroup> fontGroup;
+  if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
+                                                  nullptr, kNullGlyph, aFamily,
+                                                  font, &fontGroup))
      return true; // Could not set the family
 
-  context->mGlyphTable = glyphTable;
-
   // Now see if the table has a glyph that matches the container
 
   // Only try this table once.
   context->mTablesTried.AppendElement(glyphTable);
 
   // If the unicode table is being used, then search all font families.  If a
   // special table is being used then the font in this family should have the
   // specified glyphs.
@@ -1271,17 +1241,17 @@ nsMathMLChar::StretchEnumContext::EnumCa
      (context->mTryParts && context->TryParts(glyphTable, family)))
     return false; // no need to continue
 
   return true; // true means continue
 }
 
 nsresult
 nsMathMLChar::StretchInternal(nsPresContext*           aPresContext,
-                              nsRenderingContext&     aRenderingContext,
+                              gfxContext*              aThebesContext,
                               nsStretchDirection&      aStretchDirection,
                               const nsBoundingMetrics& aContainerSize,
                               nsBoundingMetrics&       aDesiredStretchSize,
                               uint32_t                 aStretchHint,
                               // These are currently only used when
                               // aStretchHint & NS_STRETCH_MAXWIDTH:
                               float                    aMaxSize,
                               bool                     aMaxSizeIsAbsolute)
@@ -1299,30 +1269,31 @@ nsMathMLChar::StretchInternal(nsPresCont
   // Override with specific fonts if applicable for this character
   nsAutoString families;
   if (GetFontExtensionPref(mData[0], eExtension_base, families)) {
     font.name = families;
   }
 
   // Don't modify this nsMathMLChar when doing GetMaxWidth()
   bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
-  if (!maxWidth) {
-    // Record the families in case there is no stretch.  But don't bother
-    // storing families when they are just those from the StyleContext.
-    mFamily = families;
-  }
 
   nsRefPtr<nsFontMetrics> fm;
-  aRenderingContext.DeviceContext()->GetMetricsFor(font,
-    mStyleContext->StyleFont()->mLanguage,
-    aPresContext->GetUserFontSet(),
-    aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm));
-  aRenderingContext.SetFont(fm);
-  aDesiredStretchSize =
-    aRenderingContext.GetBoundingMetrics(mData.get(), uint32_t(mData.Length()));
+  aPresContext->DeviceContext()->
+    GetMetricsFor(font,
+                  mStyleContext->StyleFont()->mLanguage,
+                  aPresContext->GetUserFontSet(),
+                  aPresContext->GetTextPerfMetrics(),
+                  *getter_AddRefs(fm));
+  uint32_t len = uint32_t(mData.Length());
+  nsAutoPtr<gfxTextRun> textRun;
+  textRun = fm->GetThebesFontGroup()->
+    MakeTextRun(mData.get(), len, aThebesContext,
+                aPresContext->AppUnitsPerDevPixel(), 0);
+  aDesiredStretchSize = MeasureTextRun(aThebesContext, textRun);
+  mGlyphs[0] = textRun;
 
   if (!maxWidth) {
     mUnscaledAscent = aDesiredStretchSize.ascent;
   }
 
   //////////////////////////////////////////////////////////////////////////////
   // 1. Check the common situations where stretching is not actually needed
   //////////////////////////////////////////////////////////////////////////////
@@ -1389,19 +1360,19 @@ nsMathMLChar::StretchInternal(nsPresCont
     }
   }
 
   nsBoundingMetrics initialSize = aDesiredStretchSize;
   nscoord charSize =
     isVertical ? initialSize.ascent + initialSize.descent
     : initialSize.rightBearing - initialSize.leftBearing;
 
-  bool done = (mGlyphTable ? false : true);
+  bool done = false;
 
-  if (!done && !maxWidth && !largeop) {
+  if (!maxWidth && !largeop) {
     // Doing Stretch() not GetMaxWidth(),
     // and not a largeop in display mode; we're done if size fits
     if ((targetSize <= 0) || 
         ((isVertical && charSize >= targetSize) ||
          IsSizeOK(aPresContext, charSize, targetSize, aStretchHint)))
       done = true;
   }
 
@@ -1416,30 +1387,30 @@ nsMathMLChar::StretchInternal(nsPresCont
     font = mStyleContext->StyleFont()->mFont;
     cssFamilies = font.name;
   }
 
   // See if there are preferred fonts for the variants of this char
   if (!done && GetFontExtensionPref(mData[0], eExtension_variants, families)) {
     font.name = families;
 
-    StretchEnumContext enumData(this, aPresContext, aRenderingContext,
+    StretchEnumContext enumData(this, aPresContext, aThebesContext,
                                 aStretchDirection, targetSize, aStretchHint,
                                 aDesiredStretchSize, font.name, glyphFound);
     enumData.mTryParts = false;
 
     done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
   }
 
   // See if there are preferred fonts for the parts of this char
   if (!done && !largeopOnly
       && GetFontExtensionPref(mData[0], eExtension_parts, families)) {
     font.name = families;
 
-    StretchEnumContext enumData(this, aPresContext, aRenderingContext,
+    StretchEnumContext enumData(this, aPresContext, aThebesContext,
                                 aStretchDirection, targetSize, aStretchHint,
                                 aDesiredStretchSize, font.name, glyphFound);
     enumData.mTryVariants = false;
 
     done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
   }
 
   if (!done) { // normal case
@@ -1450,28 +1421,27 @@ nsMathMLChar::StretchInternal(nsPresCont
     if (!fallbackFonts.IsEmpty()) {
       AddFallbackFonts(font.name, fallbackFonts);
     }
 
 #ifdef NOISY_SEARCH
     printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
            font.name, mData[0], mData[0]&0x00FF);
 #endif
-    StretchEnumContext enumData(this, aPresContext, aRenderingContext,
+    StretchEnumContext enumData(this, aPresContext, aThebesContext,
                                 aStretchDirection, targetSize, aStretchHint,
                                 aDesiredStretchSize, font.name, glyphFound);
     enumData.mTryParts = !largeopOnly;
 
     font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
   }
 
   if (!maxWidth) {
     // Now, we know how we are going to draw the char. Update the member
     // variables accordingly.
-    mDrawNormal = !glyphFound;
     mUnscaledAscent = aDesiredStretchSize.ascent;
   }
     
   // stretchy character
   if (stretchy) {
     if (isVertical) {
       float scale =
         float(aContainerSize.ascent + aContainerSize.descent) /
@@ -1552,22 +1522,22 @@ nsMathMLChar::Stretch(nsPresContext*    
                       uint32_t                 aStretchHint,
                       bool                     aRTL)
 {
   NS_ASSERTION(!(aStretchHint &
                  ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
                    NS_STRETCH_INTEGRAL)),
                "Unexpected stretch flags");
 
-  mDrawNormal = true;
+  mDraw = DRAW_NORMAL;
   mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
   mScaleY = mScaleX = 1.0;
   mDirection = aStretchDirection;
   nsresult rv =
-    StretchInternal(aPresContext, aRenderingContext, mDirection,
+    StretchInternal(aPresContext, aRenderingContext.ThebesContext(), mDirection,
                     aContainerSize, aDesiredStretchSize, aStretchHint);
 
   // Record the metrics
   mBoundingMetrics = aDesiredStretchSize;
 
   return rv;
 }
 
@@ -1588,18 +1558,18 @@ nsMathMLChar::GetMaxWidth(nsPresContext*
                           nsRenderingContext& aRenderingContext,
                           uint32_t aStretchHint,
                           float aMaxSize, bool aMaxSizeIsAbsolute)
 {
   nsBoundingMetrics bm;
   nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
   const nsBoundingMetrics container; // zero target size
 
-  StretchInternal(aPresContext, aRenderingContext, direction, container,
-                  bm, aStretchHint | NS_STRETCH_MAXWIDTH);
+  StretchInternal(aPresContext, aRenderingContext.ThebesContext(), direction,
+                  container, bm, aStretchHint | NS_STRETCH_MAXWIDTH);
 
   return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
 }
 
 class nsDisplayMathMLSelectionRect : public nsDisplayItem {
 public:
   nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aFrame, const nsRect& aRect)
@@ -1775,17 +1745,17 @@ nsMathMLChar::Display(nsDisplayListBuild
                       nsIFrame*               aForFrame,
                       const nsDisplayListSet& aLists,
                       uint32_t                aIndex,
                       const nsRect*           aSelectedRect)
 {
   nsStyleContext* parentContext = mStyleContext->GetParent();
   nsStyleContext* styleContext = mStyleContext;
 
-  if (mDrawNormal) {
+  if (mDraw == DRAW_NORMAL) {
     // normal drawing if there is nothing special about this char
     // Set default context to the parent context
     styleContext = parentContext;
   }
 
   if (!styleContext->StyleVisibility()->IsVisible())
     return;
 
@@ -1817,25 +1787,33 @@ nsMathMLChar::Display(nsDisplayListBuild
   aLists.Content()->AppendNewToTop(new (aBuilder)
     nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
                                   aIndex,
                                   aSelectedRect &&
                                   !aSelectedRect->IsEmpty()));
 }
 
 void
-nsMathMLChar::ApplyTransforms(nsRenderingContext& aRenderingContext, nsRect &r)
+nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext,
+                              int32_t aAppUnitsPerGfxUnit,
+                              nsRect &r)
 {
   // apply the transforms
   if (mMirrored) {
-    aRenderingContext.Translate(r.TopRight());
-    aRenderingContext.Scale(-mScaleX, mScaleY);
+    nsPoint pt = r.TopRight();
+    aThebesContext->
+      Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
+                         NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)));
+    aThebesContext->Scale(-mScaleX, mScaleY);
   } else {
-    aRenderingContext.Translate(r.TopLeft());
-    aRenderingContext.Scale(mScaleX, mScaleY);
+    nsPoint pt = r.TopLeft();
+    aThebesContext->
+      Translate(gfxPoint(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
+                         NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)));
+    aThebesContext->Scale(mScaleX, mScaleY);
   }
 
   // update the bounding rectangle.
   r.x = r.y = 0;
   r.width /= mScaleX;
   r.height /= mScaleY;
 }
 
@@ -1843,504 +1821,438 @@ void
 nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
                               nsRenderingContext& aRenderingContext,
                               nsPoint aPt,
                               bool aIsSelected)
 {
   nsStyleContext* parentContext = mStyleContext->GetParent();
   nsStyleContext* styleContext = mStyleContext;
 
-  if (mDrawNormal) {
+  if (mDraw == DRAW_NORMAL) {
     // normal drawing if there is nothing special about this char
     // Set default context to the parent context
     styleContext = parentContext;
   }
 
+  nsRefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
+
   // Set color ...
   nscolor fgColor = styleContext->GetVisitedDependentColor(eCSSProperty_color);
   if (aIsSelected) {
     // get color to use for selection from the look&feel object
     fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
                                     fgColor);
   }
-  aRenderingContext.SetColor(fgColor);
-
-  nsFont theFont(styleContext->StyleFont()->mFont);
-  if (! mFamily.IsEmpty()) {
-    theFont.name = mFamily;
-  }
-  nsRefPtr<nsFontMetrics> fm;
-  aRenderingContext.DeviceContext()->GetMetricsFor(theFont,
-    styleContext->StyleFont()->mLanguage,
-    aPresContext->GetUserFontSet(), aPresContext->GetTextPerfMetrics(),
-    *getter_AddRefs(fm));
-  aRenderingContext.SetFont(fm);
-
-  aRenderingContext.PushState();
+  thebesContext->SetColor(fgColor);
+  thebesContext->Save();
   nsRect r = mRect + aPt;
-  ApplyTransforms(aRenderingContext, r);
+  ApplyTransforms(thebesContext, aPresContext->AppUnitsPerDevPixel(), r);
 
-  if (mDrawNormal) {
-    // normal drawing if there is nothing special about this char ...
-    // Grab some metrics to adjust the placements ...
-    uint32_t len = uint32_t(mData.Length());
-    aRenderingContext.DrawString(mData.get(), len, 0, mUnscaledAscent);
-  }
-  else {
-    // Grab some metrics to adjust the placements ...
-    // if there is a glyph of appropriate size, paint that glyph
-    if (mGlyph.Exists()) {
-      aRenderingContext.DrawString(mGlyph.code, mGlyph.Length(),
-                                   0, mUnscaledAscent);
+  switch(mDraw)
+  {
+    case DRAW_NORMAL:
+    case DRAW_VARIANT:
+      // draw a single glyph (base size or size variant)
+      mGlyphs[0]->Draw(thebesContext, gfxPoint(0.0, mUnscaledAscent),
+                       DrawMode::GLYPH_FILL, 0, mGlyphs[0]->GetLength(),
+                       nullptr, nullptr, nullptr);
+      break;
+    case DRAW_PARTS: {
+      // paint by parts
+      if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
+        PaintVertically(aPresContext, thebesContext, r);
+      else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
+        PaintHorizontally(aPresContext, thebesContext, r);
+      break;
     }
-    else { // paint by parts
-      if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
-        PaintVertically(aPresContext, aRenderingContext, theFont, styleContext,
-                        mGlyphTable, r);
-      else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
-        PaintHorizontally(aPresContext, aRenderingContext, theFont,
-                          styleContext, mGlyphTable, r);
-    }
+    default:
+      NS_NOTREACHED("Unknown drawing method");
+      break;
   }
 
-  aRenderingContext.PopState();
+  thebesContext->Restore();
 }
 
 /* =============================================================================
   Helper routines that actually do the job of painting the char by parts
  */
 
 class AutoPushClipRect {
-  nsRenderingContext& mCtx;
+  gfxContext* mThebesContext;
 public:
-  AutoPushClipRect(nsRenderingContext& aCtx, const nsRect& aRect)
-    : mCtx(aCtx) {
-    mCtx.PushState();
-    mCtx.IntersectClip(aRect);
+  AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
+                   const nsRect& aRect)
+    : mThebesContext(aThebesContext) {
+    mThebesContext->Save();
+    mThebesContext->NewPath();
+    gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
+    mThebesContext->SnappedRectangle(clip);
+    mThebesContext->Clip();
   }
   ~AutoPushClipRect() {
-    mCtx.PopState();
+    mThebesContext->Restore();
   }
 };
 
 static nsPoint
 SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
                 const nsPoint& aPt)
 {
   gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
               NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
   pt = aThebesContext->UserToDevice(pt);
   pt.Round();
   pt = aThebesContext->DeviceToUser(pt);
   return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
                  NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
 }
 
+static void
+PaintRule(gfxContext* aThebesContext,
+          int32_t     aAppUnitsPerGfxUnit,
+          nsRect&     aRect)
+{
+  aThebesContext->NewPath();
+  gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
+  aThebesContext->SnappedRectangle(rect);
+  aThebesContext->Fill();
+}
+
 // paint a stretchy char by assembling glyphs vertically
 nsresult
-nsMathMLChar::PaintVertically(nsPresContext*      aPresContext,
-                              nsRenderingContext& aRenderingContext,
-                              nsFont&              aFont,
-                              nsStyleContext*      aStyleContext,
-                              nsGlyphTable*        aGlyphTable,
-                              nsRect&              aRect)
+nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
+                              gfxContext*    aThebesContext,
+                              nsRect&        aRect)
 {
   // Get the device pixel size in the vertical direction.
   // (This makes no effort to optimize for non-translation transformations.)
   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
 
   // get metrics data to be re-used later
   int32_t i = 0;
-  nsGlyphCode ch, chdata[4];
-  nsBoundingMetrics bmdata[4];
-  int32_t glue, bottom;
-  nsGlyphCode chGlue = aGlyphTable->GlueOf(aPresContext, this);
-  for (int32_t j = 0; j < 4; ++j) {
-    switch (j) {
-      case 0:
-        ch = aGlyphTable->TopOf(aPresContext, this);
-        break;
-      case 1:
-        ch = aGlyphTable->MiddleOf(aPresContext, this);
-        if (!ch.Exists())
-          continue; // no middle
-        break;
-      case 2:
-        ch = aGlyphTable->BottomOf(aPresContext, this);
-        bottom = i;
-        break;
-      case 3:
-        ch = chGlue;
-        glue = i;
-        break;
-    }
-    // empty slots are filled with the glue if it is not null
-    if (!ch.Exists()) ch = chGlue;
-    // if (!ch.Exists()) glue is null, leave bounding metrics at 0
-    if (ch.Exists()) {
-      SetFontFamily(aStyleContext, aRenderingContext,
-                    aFont, aGlyphTable, ch, mFamily);
-      bmdata[i] = aRenderingContext.GetBoundingMetrics(ch.code, ch.Length());
-    }
-    chdata[i] = ch;
-    ++i;
-  }
   nscoord dx = aRect.x;
   nscoord offset[3], start[3], end[3];
-  nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
-  for (i = 0; i <= bottom; ++i) {
-    ch = chdata[i];
-    const nsBoundingMetrics& bm = bmdata[i];
+  for (i = 0; i <= 2; ++i) {
+    const nsBoundingMetrics& bm = mBmData[i];
     nscoord dy;
     if (0 == i) { // top
       dy = aRect.y + bm.ascent;
     }
-    else if (bottom == i) { // bottom
+    else if (2 == i) { // bottom
       dy = aRect.y + aRect.height - bm.descent;
     }
     else { // middle
       dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
     }
     // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
     // Do this now so that we can get the other dimensions right.
     // (This may not achieve much with non-rectangular transformations.)
-    dy = SnapToDevPixels(ctx, oneDevPixel, nsPoint(dx, dy)).y;
-    // abcissa passed to DrawString
+    dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y;
+    // abcissa passed to Draw
     offset[i] = dy;
     // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
     // pixel, so the bm values can include 1 row of faint pixels on each edge.
     // Don't rely on this pixel as it can look like a gap.
-    start[i] = dy - bm.ascent + oneDevPixel; // top join
-    end[i] = dy + bm.descent - oneDevPixel; // bottom join
+    if (bm.ascent + bm.descent >= 2 * oneDevPixel) {
+      start[i] = dy - bm.ascent + oneDevPixel; // top join
+      end[i] = dy + bm.descent - oneDevPixel; // bottom join
+    } else {
+      // To avoid overlaps, we don't add one pixel on each side when the part
+      // is too small.
+      start[i] = dy - bm.ascent; // top join
+      end[i] = dy + bm.descent; // bottom join
+    }
   }
 
   // If there are overlaps, then join at the mid point
-  for (i = 0; i < bottom; ++i) {
+  for (i = 0; i < 2; ++i) {
     if (end[i] > start[i+1]) {
       end[i] = (end[i] + start[i+1]) / 2;
       start[i+1] = end[i];
     }
   }
 
   nsRect unionRect = aRect;
   unionRect.x += mBoundingMetrics.leftBearing;
   unionRect.width =
     mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
   unionRect.Inflate(oneDevPixel, oneDevPixel);
 
   /////////////////////////////////////
   // draw top, middle, bottom
-  for (i = 0; i <= bottom; ++i) {
-    ch = chdata[i];
-    // glue can be null, and other parts could have been set to glue
-    if (ch.Exists()) {
+  for (i = 0; i <= 2; ++i) {
+    // glue can be null
+    if (mGlyphs[i]) {
       nscoord dy = offset[i];
       // Draw a glyph in a clipped area so that we don't have hairy chars
       // pending outside
       nsRect clipRect = unionRect;
       // Clip at the join to get a solid edge (without overlap or gap), when
       // this won't change the glyph too much.  If the glyph is too small to
       // clip then we'll overlap rather than have a gap.
-      nscoord height = bmdata[i].ascent + bmdata[i].descent;
-      if (ch == chGlue ||
-          height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
+      nscoord height = mBmData[i].ascent + mBmData[i].descent;
+      if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
         if (0 == i) { // top
           clipRect.height = end[i] - clipRect.y;
         }
-        else if (bottom == i) { // bottom
+        else if (2 == i) { // bottom
           clipRect.height -= start[i] - clipRect.y;
           clipRect.y = start[i];
         }
         else { // middle
           clipRect.y = start[i];
           clipRect.height = end[i] - start[i];
         }
       }
       if (!clipRect.IsEmpty()) {
-        AutoPushClipRect clip(aRenderingContext, clipRect);
-        SetFontFamily(aStyleContext, aRenderingContext,
-                      aFont, aGlyphTable, ch, mFamily);
-        aRenderingContext.DrawString(ch.code, ch.Length(), dx, dy);
+        AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
+        mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy),
+                         DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(),
+                         nullptr, nullptr, nullptr);
       }
     }
   }
 
   ///////////////
   // fill the gap between top and middle, and between middle and bottom.
-  if (!chGlue.Exists()) { // null glue : draw a rule
+  if (!mGlyphs[3]) { // null glue : draw a rule
     // figure out the dimensions of the rule to be drawn :
     // set lbearing to rightmost lbearing among the two current successive
     // parts.
     // set rbearing to leftmost rbearing among the two current successive parts.
     // this not only satisfies the convention used for over/underbraces
     // in TeX, but also takes care of broken fonts like the stretchy integral
     // in Symbol for small font sizes in unix.
     nscoord lbearing, rbearing;
     int32_t first = 0, last = 1;
-    while (last <= bottom) {
-      if (chdata[last].Exists()) {
-        lbearing = bmdata[last].leftBearing;
-        rbearing = bmdata[last].rightBearing;
-        if (chdata[first].Exists()) {
-          if (lbearing < bmdata[first].leftBearing)
-            lbearing = bmdata[first].leftBearing;
-          if (rbearing > bmdata[first].rightBearing)
-            rbearing = bmdata[first].rightBearing;
+    while (last <= 2) {
+      if (mGlyphs[last]) {
+        lbearing = mBmData[last].leftBearing;
+        rbearing = mBmData[last].rightBearing;
+        if (mGlyphs[first]) {
+          if (lbearing < mBmData[first].leftBearing)
+            lbearing = mBmData[first].leftBearing;
+          if (rbearing > mBmData[first].rightBearing)
+            rbearing = mBmData[first].rightBearing;
         }
       }
-      else if (chdata[first].Exists()) {
-        lbearing = bmdata[first].leftBearing;
-        rbearing = bmdata[first].rightBearing;
+      else if (mGlyphs[first]) {
+        lbearing = mBmData[first].leftBearing;
+        rbearing = mBmData[first].rightBearing;
       }
       else {
         NS_ERROR("Cannot stretch - All parts missing");
         return NS_ERROR_UNEXPECTED;
       }
       // paint the rule between the parts
       nsRect rule(aRect.x + lbearing, end[first],
                   rbearing - lbearing, start[last] - end[first]);
-      if (!rule.IsEmpty())
-        aRenderingContext.FillRect(rule);
+      PaintRule(aThebesContext, oneDevPixel, rule);
       first = last;
       last++;
     }
   }
-  else if (bmdata[glue].ascent + bmdata[glue].descent > 0) {
+  else if (mBmData[3].ascent + mBmData[3].descent > 0) {
     // glue is present
-    nsBoundingMetrics& bm = bmdata[glue];
+    nsBoundingMetrics& bm = mBmData[3];
     // Ensure the stride for the glue is not reduced to less than one pixel
     if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
       // To protect against gaps, pretend the glue is smaller than it is,
       // in order to trim off ends and thus get a solid edge for the join.
       bm.ascent -= oneDevPixel;
       bm.descent -= oneDevPixel;
     }
 
-    SetFontFamily(aStyleContext, aRenderingContext,
-                  aFont, aGlyphTable, chGlue, mFamily);
     nsRect clipRect = unionRect;
 
-    for (i = 0; i < bottom; ++i) {
+    for (i = 0; i < 2; ++i) {
       // Make sure not to draw outside the character
       nscoord dy = std::max(end[i], aRect.y);
       nscoord fillEnd = std::min(start[i+1], aRect.YMost());
       while (dy < fillEnd) {
         clipRect.y = dy;
         clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy);
-        AutoPushClipRect clip(aRenderingContext, clipRect);
+        AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
         dy += bm.ascent;
-        aRenderingContext.DrawString(chGlue.code, chGlue.Length(), dx, dy);
+        mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy),
+                            DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(),
+                            nullptr, nullptr, nullptr);
         dy += bm.descent;
       }
     }
   }
 #ifdef DEBUG
   else {
-    for (i = 0; i < bottom; ++i) {
+    for (i = 0; i < 2; ++i) {
       NS_ASSERTION(end[i] >= start[i+1],
                    "gap between parts with missing glue glyph");
     }
   }
 #endif
   return NS_OK;
 }
 
 // paint a stretchy char by assembling glyphs horizontally
 nsresult
-nsMathMLChar::PaintHorizontally(nsPresContext*      aPresContext,
-                                nsRenderingContext& aRenderingContext,
-                                nsFont&              aFont,
-                                nsStyleContext*      aStyleContext,
-                                nsGlyphTable*        aGlyphTable,
-                                nsRect&              aRect)
+nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
+                                gfxContext*    aThebesContext,
+                                nsRect&        aRect)
 {
   // Get the device pixel size in the horizontal direction.
   // (This makes no effort to optimize for non-translation transformations.)
   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
 
   // get metrics data to be re-used later
   int32_t i = 0;
-  nsGlyphCode ch, chdata[4];
-  nsBoundingMetrics bmdata[4];
-  int32_t glue, right;
-  nsGlyphCode chGlue = aGlyphTable->GlueOf(aPresContext, this);
-  for (int32_t j = 0; j < 4; ++j) {
-    switch (j) {
-      case 0:
-        ch = aGlyphTable->LeftOf(aPresContext, this);
-        break;
-      case 1:
-        ch = aGlyphTable->MiddleOf(aPresContext, this);
-        if (!ch.Exists())
-          continue; // no middle
-        break;
-      case 2:
-        ch = aGlyphTable->RightOf(aPresContext, this);
-        right = i;
-        break;
-      case 3:
-        ch = chGlue;
-        glue = i;
-        break;
-    }
-    // empty slots are filled with the glue if it is not null
-    if (!ch.Exists()) ch = chGlue;
-    // if (!ch.Exists()) glue is null, leave bounding metrics at 0.
-    if (ch.Exists()) {
-      SetFontFamily(aStyleContext, aRenderingContext,
-                    aFont, aGlyphTable, ch, mFamily);
-      bmdata[i] = aRenderingContext.GetBoundingMetrics(ch.code, ch.Length());
-    }
-    chdata[i] = ch;
-    ++i;
-  }
   nscoord dy = aRect.y + mBoundingMetrics.ascent;
   nscoord offset[3], start[3], end[3];
-  nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
-  for (i = 0; i <= right; ++i) {
-    ch = chdata[i];
-    const nsBoundingMetrics& bm = bmdata[i];
+  for (i = 0; i <= 2; ++i) {
+    const nsBoundingMetrics& bm = mBmData[i];
     nscoord dx;
     if (0 == i) { // left
       dx = aRect.x - bm.leftBearing;
     }
-    else if (right == i) { // right
+    else if (2 == i) { // right
       dx = aRect.x + aRect.width - bm.rightBearing;
     }
     else { // middle
       dx = aRect.x + (aRect.width - bm.width)/2;
     }
     // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
     // Do this now so that we can get the other dimensions right.
     // (This may not achieve much with non-rectangular transformations.)
-    dx = SnapToDevPixels(ctx, oneDevPixel, nsPoint(dx, dy)).x;
-    // abcissa passed to DrawString
+    dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x;
+    // abcissa passed to Draw
     offset[i] = dx;
     // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
     // pixel, so the bm values can include 1 row of faint pixels on each edge.
     // Don't rely on this pixel as it can look like a gap.
-    start[i] = dx + bm.leftBearing + oneDevPixel; // left join
-    end[i] = dx + bm.rightBearing - oneDevPixel; // right join
+    if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) { 
+      start[i] = dx + bm.leftBearing + oneDevPixel; // left join
+      end[i] = dx + bm.rightBearing - oneDevPixel; // right join
+    } else {
+      // To avoid overlaps, we don't add one pixel on each side when the part
+      // is too small.
+      start[i] = dx + bm.leftBearing; // left join
+      end[i] = dx + bm.rightBearing; // right join
+    }
   }
 
   // If there are overlaps, then join at the mid point
-  for (i = 0; i < right; ++i) {
+  for (i = 0; i < 2; ++i) {
     if (end[i] > start[i+1]) {
       end[i] = (end[i] + start[i+1]) / 2;
       start[i+1] = end[i];
     }
   }
 
   nsRect unionRect = aRect;
   unionRect.Inflate(oneDevPixel, oneDevPixel);
 
   ///////////////////////////
   // draw left, middle, right
-  for (i = 0; i <= right; ++i) {
-    ch = chdata[i];
-    // glue can be null, and other parts could have been set to glue
-    if (ch.Exists()) {
+  for (i = 0; i <= 2; ++i) {
+    // glue can be null
+    if (mGlyphs[i]) {
       nscoord dx = offset[i];
       nsRect clipRect = unionRect;
       // Clip at the join to get a solid edge (without overlap or gap), when
       // this won't change the glyph too much.  If the glyph is too small to
       // clip then we'll overlap rather than have a gap.
-      nscoord width = bmdata[i].rightBearing - bmdata[i].leftBearing;
-      if (ch == chGlue ||
-          width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
+      nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing;
+      if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
         if (0 == i) { // left
           clipRect.width = end[i] - clipRect.x;
         }
-        else if (right == i) { // right
+        else if (2 == i) { // right
           clipRect.width -= start[i] - clipRect.x;
           clipRect.x = start[i];
         }
         else { // middle
           clipRect.x = start[i];
           clipRect.width = end[i] - start[i];
         }
       }
       if (!clipRect.IsEmpty()) {
-        AutoPushClipRect clip(aRenderingContext, clipRect);
-        SetFontFamily(aStyleContext, aRenderingContext,
-                      aFont, aGlyphTable, ch, mFamily);
-        aRenderingContext.DrawString(ch.code, ch.Length(), dx, dy);
+        AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
+        mGlyphs[i]->Draw(aThebesContext, gfxPoint(dx, dy),
+                         DrawMode::GLYPH_FILL, 0, mGlyphs[i]->GetLength(),
+                         nullptr, nullptr, nullptr);
       }
     }
   }
 
   ////////////////
   // fill the gap between left and middle, and between middle and right.
-  if (!chGlue.Exists()) { // null glue : draw a rule
+  if (!mGlyphs[3]) { // null glue : draw a rule
     // figure out the dimensions of the rule to be drawn :
     // set ascent to lowest ascent among the two current successive parts.
     // set descent to highest descent among the two current successive parts.
     // this satisfies the convention used for over/underbraces, and helps
     // fix broken fonts.
     nscoord ascent, descent;
     int32_t first = 0, last = 1;
-    while (last <= right) {
-      if (chdata[last].Exists()) {
-        ascent = bmdata[last].ascent;
-        descent = bmdata[last].descent;
-        if (chdata[first].Exists()) {
-          if (ascent > bmdata[first].ascent)
-            ascent = bmdata[first].ascent;
-          if (descent > bmdata[first].descent)
-            descent = bmdata[first].descent;
+    while (last <= 2) {
+      if (mGlyphs[last]) {
+        ascent = mBmData[last].ascent;
+        descent = mBmData[last].descent;
+        if (mGlyphs[first]) {
+          if (ascent > mBmData[first].ascent)
+            ascent = mBmData[first].ascent;
+          if (descent > mBmData[first].descent)
+            descent = mBmData[first].descent;
         }
       }
-      else if (chdata[first].Exists()) {
-        ascent = bmdata[first].ascent;
-        descent = bmdata[first].descent;
+      else if (mGlyphs[first]) {
+        ascent = mBmData[first].ascent;
+        descent = mBmData[first].descent;
       }
       else {
         NS_ERROR("Cannot stretch - All parts missing");
         return NS_ERROR_UNEXPECTED;
       }
       // paint the rule between the parts
       nsRect rule(end[first], dy - ascent,
                   start[last] - end[first], ascent + descent);
-      if (!rule.IsEmpty())
-        aRenderingContext.FillRect(rule);
+      PaintRule(aThebesContext, oneDevPixel, rule);
       first = last;
       last++;
     }
   }
-  else if (bmdata[glue].rightBearing - bmdata[glue].leftBearing > 0) {
+  else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) {
     // glue is present
-    nsBoundingMetrics& bm = bmdata[glue];
+    nsBoundingMetrics& bm = mBmData[3];
     // Ensure the stride for the glue is not reduced to less than one pixel
     if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
       // To protect against gaps, pretend the glue is smaller than it is,
       // in order to trim off ends and thus get a solid edge for the join.
       bm.leftBearing += oneDevPixel;
       bm.rightBearing -= oneDevPixel;
     }
 
-    SetFontFamily(aStyleContext, aRenderingContext,
-                  aFont, aGlyphTable, chGlue, mFamily);
     nsRect clipRect = unionRect;
 
-    for (i = 0; i < right; ++i) {
+    for (i = 0; i < 2; ++i) {
       // Make sure not to draw outside the character
       nscoord dx = std::max(end[i], aRect.x);
       nscoord fillEnd = std::min(start[i+1], aRect.XMost());
       while (dx < fillEnd) {
         clipRect.x = dx;
         clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx);
-        AutoPushClipRect clip(aRenderingContext, clipRect);
+        AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
         dx -= bm.leftBearing;
-        aRenderingContext.DrawString(chGlue.code, chGlue.Length(), dx, dy);
+        mGlyphs[3]->Draw(aThebesContext, gfxPoint(dx, dy),
+                            DrawMode::GLYPH_FILL, 0, mGlyphs[3]->GetLength(),
+                            nullptr, nullptr, nullptr);
         dx += bm.rightBearing;
       }
     }
   }
 #ifdef DEBUG
   else { // no glue
-    for (i = 0; i < right; ++i) {
+    for (i = 0; i < 2; ++i) {
       NS_ASSERTION(end[i] >= start[i+1],
                    "gap between parts with missing glue glyph");
     }
   }
 #endif
   return NS_OK;
 }
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -1,21 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsMathMLChar_h___
 #define nsMathMLChar_h___
 
+#include "nsAutoPtr.h"
 #include "nsMathMLOperators.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsString.h"
 #include "nsBoundingMetrics.h"
+#include "gfxFont.h"
 
 class nsGlyphTable;
 class nsIFrame;
 class nsDisplayListBuilder;
 class nsDisplayListSet;
 class nsPresContext;
 class nsRenderingContext;
 class nsBoundingMetrics;
@@ -36,34 +38,45 @@ enum {
   NS_STRETCH_LARGEOP  = 0x10,
   NS_STRETCH_INTEGRAL  = 0x20,
 
   // Intended for internal use:
   // Find the widest metrics that might be returned from a vertical stretch
   NS_STRETCH_MAXWIDTH = 0x40
 };
 
-// A single glyph in our internal representation is characterized by a
-// 'code@font' pair. The 'code' is interpreted as a Unicode point or as the
-// direct glyph index (depending on the type of nsGlyphTable where this comes
-// from). The 'font' is a numeric identifier given to the font to which the
-// glyph belongs.
+// A single glyph in our internal representation is either
+// 1) a 'code@font' pair from the mathfontFONTFAMILY.properties table. The
+// 'code' is interpreted as a Unicode point. The 'font' is a numeric
+// identifier given to the font to which the glyph belongs, which is 0 for the
+// FONTFAMILY and > 0 for 'external' fonts.
+// 2) a glyph index from the Open Type MATH table. In that case, all the glyphs
+// come from the font containing that table and 'font' is just set to -1.
 struct nsGlyphCode {
-  char16_t code[2]; 
-  int32_t   font;
+  union {
+    char16_t code[2];
+    uint32_t glyphID;
+  };
+  int8_t   font;
 
-  int32_t Length() { return (code[1] == char16_t('\0') ? 1 : 2); }
+  bool IsGlyphID() const { return font == -1; }
+
+  int32_t Length() const {
+    return (IsGlyphID() || code[1] == PRUnichar('\0') ? 1 : 2);
+  }
   bool Exists() const
   {
-    return (code[0] != 0);
+    return IsGlyphID() ? glyphID != 0 : code[0] != 0;
   }
   bool operator==(const nsGlyphCode& other) const
   {
-    return (other.code[0] == code[0] && other.code[1] == code[1] && 
-            other.font == font);
+    return (other.font == font &&
+            ((IsGlyphID() && other.glyphID == glyphID) ||
+             (!IsGlyphID() && other.code[0] == code[0] &&
+              other.code[1] == code[1])));
   }
   bool operator!=(const nsGlyphCode& other) const
   {
     return ! operator==(other);
   }
 };
 
 // Class used to handle stretchy symbols (accent, delimiter and boundary
@@ -72,17 +85,17 @@ class nsMathMLChar
 {
 public:
   // constructor and destructor
   nsMathMLChar() {
     MOZ_COUNT_CTOR(nsMathMLChar);
     mStyleContext = nullptr;
     mUnscaledAscent = 0;
     mScaleX = mScaleY = 1.0;
-    mDrawNormal = true;
+    mDraw = DRAW_NORMAL;
     mMirrored = false;
   }
 
   // not a virtual destructor: this class is not intended to be subclassed
   ~nsMathMLChar();
 
   void Display(nsDisplayListBuilder*   aBuilder,
                nsIFrame*               aForFrame,
@@ -188,57 +201,68 @@ protected:
   friend class nsGlyphTable;
   nsString           mData;
 
 private:
   nsRect             mRect;
   nsStretchDirection mDirection;
   nsBoundingMetrics  mBoundingMetrics;
   nsStyleContext*    mStyleContext;
-  nsGlyphTable*      mGlyphTable;
-  nsGlyphCode        mGlyph;
-  // mFamily is non-empty when the family for the current size is different
-  // from the family in the nsStyleContext.
-  nsString           mFamily;
+  // mGlyphs/mBmData are arrays describing the glyphs used to draw the operator.
+  // See the drawing methods below.
+  nsAutoPtr<gfxTextRun> mGlyphs[4];
+  nsBoundingMetrics     mBmData[4];
   // mUnscaledAscent is the actual ascent of the char.
   nscoord            mUnscaledAscent;
   // mScaleX, mScaleY are the factors by which we scale the char.
   float              mScaleX, mScaleY;
-  // mDrawNormal indicates whether we use special glyphs or not.
-  bool               mDrawNormal;
+
+  // mDraw indicates how we draw the stretchy operator:
+  // - DRAW_NORMAL: we render the mData string normally.
+  // - DRAW_VARIANT: we draw a larger size variant given by mGlyphs[0].
+  // - DRAW_PARTS: we assemble several parts given by mGlyphs[0], ... mGlyphs[4]
+  // XXXfredw: the MATH table can have any numbers of parts and extenders.
+  enum DrawingMethod {
+    DRAW_NORMAL, DRAW_VARIANT, DRAW_PARTS
+  };
+  DrawingMethod mDraw;
+
   // mMirrored indicates whether the character is mirrored. 
   bool               mMirrored;
 
   class StretchEnumContext;
   friend class StretchEnumContext;
 
   // helper methods
+  bool
+  SetFontFamily(nsPresContext*          aPresContext,
+                const nsGlyphTable*     aGlyphTable,
+                const nsGlyphCode&      aGlyphCode,
+                const nsAString&        aDefaultFamily,
+                nsFont&                 aFont,
+                nsRefPtr<gfxFontGroup>* aFontGroup);
+
   nsresult
   StretchInternal(nsPresContext*           aPresContext,
-                  nsRenderingContext&     aRenderingContext,
+                  gfxContext*              aThebesContext,
                   nsStretchDirection&      aStretchDirection,
                   const nsBoundingMetrics& aContainerSize,
                   nsBoundingMetrics&       aDesiredStretchSize,
                   uint32_t                 aStretchHint,
                   float           aMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY,
                   bool            aMaxSizeIsAbsolute = false);
 
   nsresult
-  PaintVertically(nsPresContext*       aPresContext,
-                  nsRenderingContext& aRenderingContext,
-                  nsFont&              aFont,
-                  nsStyleContext*      aStyleContext,
-                  nsGlyphTable*        aGlyphTable,
-                  nsRect&              aRect);
+  PaintVertically(nsPresContext* aPresContext,
+                  gfxContext*    aThebesContext,
+                  nsRect&        aRect);
 
   nsresult
-  PaintHorizontally(nsPresContext*       aPresContext,
-                    nsRenderingContext& aRenderingContext,
-                    nsFont&              aFont,
-                    nsStyleContext*      aStyleContext,
-                    nsGlyphTable*        aGlyphTable,
-                    nsRect&              aRect);
+  PaintHorizontally(nsPresContext* aPresContext,
+                    gfxContext*    aThebesContext,
+                    nsRect&        aRect);
 
   void
-  ApplyTransforms(nsRenderingContext& aRenderingContext, nsRect &r);
+  ApplyTransforms(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
+                  nsRect &r);
 };
 
 #endif /* nsMathMLChar_h___ */
--- a/layout/mathml/nsMathMLmrootFrame.cpp
+++ b/layout/mathml/nsMathMLmrootFrame.cpp
@@ -370,18 +370,19 @@ nsMathMLmrootFrame::GetIntrinsicWidthMet
     nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
                                          nsLayoutUtils::PREF_WIDTH);
   nscoord indexWidth =
     nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
                                          nsLayoutUtils::PREF_WIDTH);
   nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
 
   nscoord dxSqr;
-  GetRadicalXOffsets(indexWidth, sqrWidth, aRenderingContext->FontMetrics(),
-                     nullptr, &dxSqr);
+  nsRefPtr<nsFontMetrics> fm;
+  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+  GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
 
   nscoord width = dxSqr + sqrWidth + baseWidth;
 
   aDesiredSize.Width() = width;
   aDesiredSize.mBoundingMetrics.width = width;
   aDesiredSize.mBoundingMetrics.leftBearing = 0;
   aDesiredSize.mBoundingMetrics.rightBearing = width;
 }