bug 825871 - refactor gfxTextRun and gfxShapedWord to share a common abstract base class (gfxShapedText) and shaping interface. r=roc
authorJonathan Kew <jkew@mozilla.com>
Fri, 04 Jan 2013 18:35:37 +0000
changeset 126757 39b72947ad7951787f07640cc44324287ab094d0
parent 126756 89bc8559149f355b564c2acc2efb65e98eb9ec21
child 126758 72fd8681d3a62202a9904b9c78374d7564c534af
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs825871
milestone20.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 825871 - refactor gfxTextRun and gfxShapedWord to share a common abstract base class (gfxShapedText) and shaping interface. r=roc
gfx/thebes/gfxCoreTextShaper.cpp
gfx/thebes/gfxCoreTextShaper.h
gfx/thebes/gfxDWriteShaper.cpp
gfx/thebes/gfxDWriteShaper.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFT2Fonts.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFont.h
gfx/thebes/gfxGDIShaper.cpp
gfx/thebes/gfxGDIShaper.h
gfx/thebes/gfxGraphiteShaper.cpp
gfx/thebes/gfxGraphiteShaper.h
gfx/thebes/gfxHarfBuzzShaper.cpp
gfx/thebes/gfxHarfBuzzShaper.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxMacFont.h
gfx/thebes/gfxPangoFonts.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxUniscribeShaper.cpp
gfx/thebes/gfxUniscribeShaper.h
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -58,33 +58,35 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
         ::CFRelease(mAttributesDict);
     }
     if (mCTFont) {
         ::CFRelease(mCTFont);
     }
 }
 
 bool
-gfxCoreTextShaper::ShapeWord(gfxContext      *aContext,
-                             gfxShapedWord   *aShapedWord,
-                             const PRUnichar *aText)
+gfxCoreTextShaper::ShapeText(gfxContext      *aContext,
+                             const PRUnichar *aText,
+                             uint32_t         aOffset,
+                             uint32_t         aLength,
+                             int32_t          aScript,
+                             gfxShapedText   *aShapedText)
 {
     // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
 
-    bool isRightToLeft = aShapedWord->IsRightToLeft();
-    uint32_t length = aShapedWord->Length();
+    bool isRightToLeft = aShapedText->IsRightToLeft();
+    uint32_t length = aLength;
 
     // we need to bidi-wrap the text if the run is RTL,
     // or if it is an LTR run but may contain (overridden) RTL chars
     bool bidiWrap = isRightToLeft;
-    if (!bidiWrap && !aShapedWord->TextIs8Bit()) {
-        const PRUnichar *text = aShapedWord->TextUnicode();
+    if (!bidiWrap && !aShapedText->TextIs8Bit()) {
         uint32_t i;
         for (i = 0; i < length; ++i) {
-            if (gfxFontUtils::PotentialRTLChar(text[i])) {
+            if (gfxFontUtils::PotentialRTLChar(aText[i])) {
                 bidiWrap = true;
                 break;
             }
         }
     }
 
     // If there's a possibility of any bidi, we wrap the text with direction overrides
     // to ensure neutrals or characters that were bidi-overridden in HTML behave properly.
@@ -110,17 +112,17 @@ gfxCoreTextShaper::ShapeWord(gfxContext 
     } else {
         startOffset = 0;
         stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
                                                          aText, length,
                                                          kCFAllocatorNull);
     }
 
     CFDictionaryRef attrObj;
-    if (aShapedWord->DisableLigatures()) {
+    if (aShapedText->DisableLigatures()) {
         // For letterspacing (or maybe other situations) we need to make a copy of the CTFont
         // with the ligature feature disabled
         CTFontRef ctFont =
             CreateCTFontWithDisabledLigatures(::CTFontGetSize(mCTFont));
 
         attrObj =
             ::CFDictionaryCreate(kCFAllocatorDefault,
                                  (const void**) &kCTFontAttributeName,
@@ -151,48 +153,50 @@ gfxCoreTextShaper::ShapeWord(gfxContext 
 
     // Iterate through the glyph runs.
     // Note that this includes the bidi wrapper, so we have to be careful
     // not to include the extra glyphs from there
     bool success = true;
     for (uint32_t runIndex = 0; runIndex < numRuns; runIndex++) {
         CTRunRef aCTRun =
             (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
-        if (SetGlyphsFromRun(aShapedWord, aCTRun, startOffset) != NS_OK) {
+        if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun, startOffset) != NS_OK) {
             success = false;
             break;
         }
     }
 
     ::CFRelease(line);
 
     return success;
 }
 
 #define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;
                             // some testing indicates that 90%+ of glyph runs will fit
                             // without requiring a separate allocation
 
 nsresult
-gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
-                                    CTRunRef aCTRun,
-                                    int32_t aStringOffset)
+gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
+                                    uint32_t       aOffset,
+                                    uint32_t       aLength,
+                                    CTRunRef       aCTRun,
+                                    int32_t        aStringOffset)
 {
     // The word has been bidi-wrapped; aStringOffset is the number
     // of chars at the beginning of the CTLine that we should skip.
     // aCTRun is a glyph run from the CoreText layout process.
 
-    int32_t direction = aShapedWord->IsRightToLeft() ? -1 : 1;
+    int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
 
     int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
     if (numGlyphs == 0) {
         return NS_OK;
     }
 
-    int32_t wordLength = aShapedWord->Length();
+    int32_t wordLength = aLength;
 
     // character offsets get really confusing here, as we have to keep track of
     // (a) the text in the actual textRun we're constructing
     // (c) the string that was handed to CoreText, which contains the text of the font run
     //     plus directional-override padding
     // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line
     //     (but may extend beyond the actual font run into the bidi wrapping text).
     //     aStringOffset tells us how many initial characters of the line to ignore.
@@ -252,18 +256,20 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
         }
         ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
         glyphToChar = glyphToCharArray.get();
     }
 
     double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
                                                   NULL, NULL, NULL);
 
-    nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
-    gfxTextRun::CompressedGlyph g;
+    nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
+    gfxShapedText::CompressedGlyph g;
+    gfxShapedText::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs() + aOffset;
 
     // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
     // to a source text character; we also need the charindex-to-glyphindex mapping to
     // find the glyph for a given char. Note that some chars may not map to any glyph
     // (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
     // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
     // record the last glyph index for cases where the char maps to several glyphs,
     // so that our clumping will include all the glyph fragments for the character.
@@ -297,17 +303,17 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
     // of the glyph array. That's a clump; then we update the starting positions and repeat.
     //
     // NB: In the case of RTL layouts, we iterate over the stringRange in reverse.
     //
 
     // This may find characters that fall outside the range 0:wordLength,
     // so we won't necessarily use everything we find here.
 
-    bool isRightToLeft = aShapedWord->IsRightToLeft();
+    bool isRightToLeft = aShapedText->IsRightToLeft();
     int32_t glyphStart = 0; // looking for a clump that starts at this glyph index
     int32_t charStart = isRightToLeft ?
         stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)
 
     while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
         bool inOrder = true;
         int32_t charEnd = glyphToChar[glyphStart] - stringRange.location;
         NS_WARN_IF_FALSE(charEnd >= 0 && charEnd < stringRange.length,
@@ -433,36 +439,35 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
             continue;
         }
         // Ensure we won't try to go beyond the valid length of the word's text
         baseCharIndex = NS_MAX(baseCharIndex, 0);
         endCharIndex = NS_MIN(endCharIndex, wordLength);
 
         // Now we're ready to set the glyph info in the textRun; measure the glyph width
         // of the first (perhaps only) glyph, to see if it is "Simple"
-        int32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
+        int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
         double toNextGlyph;
         if (glyphStart < numGlyphs-1) {
             toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
         } else {
             toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
         }
         int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
 
         // Check if it's a simple one-to-one mapping
         int32_t glyphsInClump = glyphEnd - glyphStart;
         if (glyphsInClump == 1 &&
             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
-            aShapedWord->IsClusterStart(baseCharIndex) &&
+            charGlyphs[baseCharIndex].IsClusterStart() &&
             positions[glyphStart].y == 0.0)
         {
-            aShapedWord->SetSimpleGlyph(baseCharIndex,
-                                        g.SetSimpleGlyph(advance,
-                                                         glyphs[glyphStart]));
+            charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
+                                                     glyphs[glyphStart]);
         } else {
             // collect all glyphs in a list to be assigned to the first char;
             // there must be at least one in the clump, and we already measured its advance,
             // hence the placement of the loop-exit test and the measurement of the next glyph
             while (1) {
                 gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
                 details->mGlyphID = glyphs[glyphStart];
                 details->mXOffset = 0;
@@ -475,28 +480,28 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
                     toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
                 } else {
                     toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
                 }
                 advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
             }
 
             gfxTextRun::CompressedGlyph g;
-            g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
+            g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
                          true, detailedGlyphs.Length());
-            aShapedWord->SetGlyphs(baseCharIndex, g, detailedGlyphs.Elements());
+            aShapedText->SetGlyphs(aOffset + baseCharIndex, g, detailedGlyphs.Elements());
 
             detailedGlyphs.Clear();
         }
 
         // the rest of the chars in the group are ligature continuations, no associated glyphs
         while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
-            g.SetComplex(inOrder && aShapedWord->IsClusterStart(baseCharIndex),
-                         false, 0);
-            aShapedWord->SetGlyphs(baseCharIndex, g, nullptr);
+            gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
+            NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+            g.SetComplex(inOrder && g.IsClusterStart(), false, 0);
         }
 
         glyphStart = glyphEnd;
         charStart = charEnd;
     }
 
     return NS_OK;
 }
--- a/gfx/thebes/gfxCoreTextShaper.h
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -17,30 +17,35 @@
 class gfxMacFont;
 
 class gfxCoreTextShaper : public gfxFontShaper {
 public:
     gfxCoreTextShaper(gfxMacFont *aFont);
 
     virtual ~gfxCoreTextShaper();
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aText);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
 
     // clean up static objects that may have been cached
     static void Shutdown();
 
 protected:
     CTFontRef mCTFont;
     CFDictionaryRef mAttributesDict;
 
-    nsresult SetGlyphsFromRun(gfxShapedWord *aShapedWord,
-                              CTRunRef aCTRun,
-                              int32_t aStringOffset);
+    nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
+                              uint32_t       aOffset,
+                              uint32_t       aLength,
+                              CTRunRef       aCTRun,
+                              int32_t        aStringOffset);
 
     CTFontRef CreateCTFontWithDisabledLigatures(CGFloat aSize);
 
     static void CreateDefaultFeaturesDescriptor();
 
     static CTFontDescriptorRef GetDefaultFeaturesDescriptor() {
         if (sDefaultFeaturesDescriptor == NULL) {
             CreateDefaultFeaturesDescriptor();
--- a/gfx/thebes/gfxDWriteShaper.cpp
+++ b/gfx/thebes/gfxDWriteShaper.cpp
@@ -8,55 +8,58 @@
 
 #include <dwrite.h>
 
 #include "gfxDWriteTextAnalysis.h"
 
 #include "nsCRT.h"
 
 bool
-gfxDWriteShaper::ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString)
+gfxDWriteShaper::ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText)
 {
     HRESULT hr;
-    // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
+    // TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES
 
     DWRITE_READING_DIRECTION readingDirection = 
-        aShapedWord->IsRightToLeft()
+        aShapedText->IsRightToLeft()
             ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
             : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
 
     gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
 
-    gfxShapedWord::CompressedGlyph g;
+    gfxShapedText::CompressedGlyph g;
 
     IDWriteTextAnalyzer *analyzer =
         gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer();
     if (!analyzer) {
         return false;
     }
 
     /**
      * There's an internal 16-bit limit on some things inside the analyzer,
-     * but we never attempt to shape a word longer than 64K characters
-     * in a single gfxShapedWord, so we cannot exceed that limit.
+     * but we never attempt to shape a word longer than 32K characters
+     * in a single call, so we cannot exceed that limit.
      */
-    UINT32 length = aShapedWord->Length();
+    UINT32 length = aLength;
 
-    TextAnalysis analysis(aString, length, NULL, readingDirection);
+    TextAnalysis analysis(aText, length, NULL, readingDirection);
     TextAnalysis::Run *runHead;
     hr = analysis.GenerateResults(analyzer, &runHead);
 
     if (FAILED(hr)) {
         NS_WARNING("Analyzer failed to generate results.");
         return false;
     }
 
-    uint32_t appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit();
+    uint32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
 
     UINT32 maxGlyphs = 0;
 trymoreglyphs:
     if ((UINT32_MAX - 3 * length / 2 + 16) < maxGlyphs) {
         // This isn't going to work, we're going to cross the UINT32 upper
         // limit. Give up.
         NS_WARNING("Shaper needs to generate more than 2^32 glyphs?!");
         return false;
@@ -72,17 +75,17 @@ trymoreglyphs:
         !textProperties.SetLength(maxGlyphs) ||
         !glyphProperties.SetLength(maxGlyphs)) {
         NS_WARNING("Shaper failed to allocate memory.");
         return false;
     }
 
     UINT32 actualGlyphs;
 
-    hr = analyzer->GetGlyphs(aString, length,
+    hr = analyzer->GetGlyphs(aText, length,
             font->GetFontFace(), FALSE, 
             readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
             &runHead->mScript, NULL, NULL, NULL, NULL, 0,
             maxGlyphs, clusters.Elements(), textProperties.Elements(),
             indices.Elements(), glyphProperties.Elements(), &actualGlyphs);
 
     if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
         // We increase the amount of glyphs and try again.
@@ -99,17 +102,17 @@ trymoreglyphs:
     if (!advances.SetLength(actualGlyphs) || 
         !glyphOffsets.SetLength(actualGlyphs)) {
         NS_WARNING("Shaper failed to allocate memory.");
         return false;
     }
 
     if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
         hr = analyzer->GetGdiCompatibleGlyphPlacements(
-                                          aString,
+                                          aText,
                                           clusters.Elements(),
                                           textProperties.Elements(),
                                           length,
                                           indices.Elements(),
                                           glyphProperties.Elements(),
                                           actualGlyphs,
                                           font->GetFontFace(),
                                           font->GetAdjustedSize(),
@@ -121,17 +124,17 @@ trymoreglyphs:
                                           &runHead->mScript,
                                           NULL,
                                           NULL,
                                           NULL,
                                           0,
                                           advances.Elements(),
                                           glyphOffsets.Elements());
     } else {
-        hr = analyzer->GetGlyphPlacements(aString,
+        hr = analyzer->GetGlyphPlacements(aText,
                                           clusters.Elements(),
                                           textProperties.Elements(),
                                           length,
                                           indices.Elements(),
                                           glyphProperties.Elements(),
                                           actualGlyphs,
                                           font->GetFontFace(),
                                           font->GetAdjustedSize(),
@@ -145,26 +148,29 @@ trymoreglyphs:
                                           advances.Elements(),
                                           glyphOffsets.Elements());
     }
     if (FAILED(hr)) {
         NS_WARNING("Analyzer failed to get glyph placements.");
         return false;
     }
 
-    nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
+    nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
+    gfxShapedText::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs();
 
     for (unsigned int c = 0; c < length; c++) {
         uint32_t k = clusters[c];
-        uint32_t absC = c;
+        uint32_t absC = aOffset + c;
 
         if (c > 0 && k == clusters[c - 1]) {
-            g.SetComplex(aShapedWord->IsClusterStart(absC), false, 0);
-            aShapedWord->SetGlyphs(absC, g, nullptr);
             // This is a cluster continuation. No glyph here.
+            gfxShapedText::CompressedGlyph &g = charGlyphs[absC];
+            NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+            g.SetComplex(g.IsClusterStart(), false, 0);
             continue;
         }
 
         // Count glyphs for this character
         uint32_t glyphCount = actualGlyphs - k;
         uint32_t nextClusterOffset;
         for (nextClusterOffset = c + 1; 
             nextClusterOffset < length; ++nextClusterOffset) {
@@ -172,22 +178,20 @@ trymoreglyphs:
                 glyphCount = clusters[nextClusterOffset] - k;
                 break;
             }
         }
         int32_t advance = (int32_t)(advances[k] * appUnitsPerDevPixel);
         if (glyphCount == 1 && advance >= 0 &&
             glyphOffsets[k].advanceOffset == 0 &&
             glyphOffsets[k].ascenderOffset == 0 &&
-            aShapedWord->IsClusterStart(absC) &&
-            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
-            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
-              aShapedWord->SetSimpleGlyph(absC, 
-                                          g.SetSimpleGlyph(advance, 
-                                                           indices[k]));
+            charGlyphs[absC].IsClusterStart() &&
+            gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
+            gfxShapedText::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
+              charGlyphs[absC].SetSimpleGlyph(advance, indices[k]);
         } else {
             if (detailedGlyphs.Length() < glyphCount) {
                 if (!detailedGlyphs.AppendElements(
                     glyphCount - detailedGlyphs.Length())) {
                     continue;
                 }
             }
             float totalAdvance = 0;
@@ -207,19 +211,19 @@ trymoreglyphs:
                         glyphOffsets[k + z].advanceOffset *
                         appUnitsPerDevPixel;
                 }
                 detailedGlyphs[z].mYOffset = 
                     -glyphOffsets[k + z].ascenderOffset *
                     appUnitsPerDevPixel;
                 totalAdvance += advances[k + z];
             }
-            aShapedWord->SetGlyphs(
+            aShapedText->SetGlyphs(
                 absC,
-                g.SetComplex(aShapedWord->IsClusterStart(absC),
+                g.SetComplex(charGlyphs[absC].IsClusterStart(),
                              true,
                              glyphCount),
                 detailedGlyphs.Elements());
         }
     }
 
     return true;
 }
--- a/gfx/thebes/gfxDWriteShaper.h
+++ b/gfx/thebes/gfxDWriteShaper.h
@@ -20,14 +20,17 @@ public:
         MOZ_COUNT_CTOR(gfxDWriteShaper);
     }
 
     virtual ~gfxDWriteShaper()
     {
         MOZ_COUNT_DTOR(gfxDWriteShaper);
     }
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
 };
 
 #endif /* GFX_DWRITESHAPER_H */
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -398,78 +398,82 @@ gfxFT2FontGroup::WhichSystemFontSupports
 
 #endif // !ANDROID
 
 /**
  * gfxFT2Font
  */
 
 bool
-gfxFT2Font::ShapeWord(gfxContext *aContext,
-                      gfxShapedWord *aShapedWord,
-                      const PRUnichar *aString,
-                      bool aPreferPlatformShaping)
+gfxFT2Font::ShapeText(gfxContext      *aContext,
+                      const PRUnichar *aText,
+                      uint32_t         aOffset,
+                      uint32_t         aLength,
+                      int32_t          aScript,
+                      gfxShapedText   *aShapedText,
+                      bool             aPreferPlatformShaping)
 {
     bool ok = false;
 
 #ifdef MOZ_GRAPHITE
     if (FontCanSupportGraphite()) {
         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
             if (!mGraphiteShaper) {
                 mGraphiteShaper = new gfxGraphiteShaper(this);
             }
-            ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString);
+            ok = mGraphiteShaper->ShapeText(aContext, aText,
+                                            aOffset, aLength,
+                                            aScript, aShapedText);
         }
     }
 #endif
 
-    if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
+    if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
         if (!mHarfBuzzShaper) {
             gfxFT2LockedFace face(this);
             mFUnitsConvFactor = face.XScale();
 
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
         }
-        ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString);
+        ok = mHarfBuzzShaper->ShapeText(aContext, aText,
+                                        aOffset, aLength,
+                                        aScript, aShapedText);
     }
 
     if (!ok) {
-        AddRange(aShapedWord, aString);
+        AddRange(aText, aOffset, aLength, aShapedText);
     }
 
-    if (IsSyntheticBold()) {
-        float synBoldOffset =
-            GetSyntheticBoldOffset() * CalcXScale(aContext);
-        aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
-    }
+    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
 
     return true;
 }
 
 void
-gfxFT2Font::AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str)
+gfxFT2Font::AddRange(const PRUnichar *aText, uint32_t aOffset,
+                     uint32_t aLength, gfxShapedText *aShapedText)
 {
-    const uint32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
+    const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
     // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
     gfxFT2LockedFace faceLock(this);
     FT_Face face = faceLock.get();
 
-    gfxShapedWord::CompressedGlyph g;
+    gfxShapedText::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs();
 
     const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
 
     FT_UInt spaceGlyph = GetSpaceGlyph();
 
-    uint32_t len = aShapedWord->Length();
-    for (uint32_t i = 0; i < len; i++) {
-        PRUnichar ch = str[i];
+    for (uint32_t i = 0; i < aLength; i++, aOffset++) {
+        PRUnichar ch = aText[i];
 
         if (ch == 0) {
             // treat this null byte as a missing glyph, don't create a glyph for it
-            aShapedWord->SetMissingGlyph(i, 0, this);
+            aShapedText->SetMissingGlyph(aOffset, 0, this);
             continue;
         }
 
         NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch), "Invalid char detected");
 
         if (cgdNext) {
             cgd = cgdNext;
             cgdNext = nullptr;
@@ -484,18 +488,18 @@ gfxFT2Font::AddRange(gfxShapedWord *aSha
             advance = -1; // trigger the missing glyphs case below
         } else {
             // find next character and its glyph -- in case they exist
             // and exist in the current font face -- to compute kerning
             PRUnichar chNext = 0;
             FT_UInt gidNext = 0;
             FT_Pos lsbDeltaNext = 0;
 
-            if (FT_HAS_KERNING(face) && i + 1 < len) {
-                chNext = str[i + 1];
+            if (FT_HAS_KERNING(face) && i + 1 < aLength) {
+                chNext = aText[i + 1];
                 if (chNext != 0) {
                     cgdNext = GetGlyphDataForChar(chNext);
                     gidNext = cgdNext->glyphIndex;
                     if (gidNext && gidNext != spaceGlyph)
                         lsbDeltaNext = cgdNext->lsbDelta;
                 }
             }
 
@@ -515,31 +519,33 @@ gfxFT2Font::AddRange(gfxShapedWord *aSha
 
             // convert 26.6 fixed point to app units
             // round rather than truncate to nearest pixel
             // because these advances are often scaled
             advance = ((advance * appUnitsPerDevUnit + 32) >> 6);
         }
 
         if (advance >= 0 &&
-            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
-            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gid)) {
-            aShapedWord->SetSimpleGlyph(i, g.SetSimpleGlyph(advance, gid));
+            gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
+            gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) {
+            charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
         } else if (gid == 0) {
             // gid = 0 only happens when the glyph is missing from the font
-            aShapedWord->SetMissingGlyph(i, ch, this);
+            aShapedText->SetMissingGlyph(aOffset, ch, this);
         } else {
             gfxTextRun::DetailedGlyph details;
             details.mGlyphID = gid;
-            NS_ASSERTION(details.mGlyphID == gid, "Seriously weird glyph ID detected!");
+            NS_ASSERTION(details.mGlyphID == gid,
+                         "Seriously weird glyph ID detected!");
             details.mAdvance = advance;
             details.mXOffset = 0;
             details.mYOffset = 0;
-            g.SetComplex(aShapedWord->IsClusterStart(i), true, 1);
-            aShapedWord->SetGlyphs(i, g, &details);
+            gfxShapedText::CompressedGlyph g;
+            g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
+            aShapedText->SetGlyphs(aOffset, g, &details);
         }
     }
 }
 
 gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
                        FT2FontEntry *aFontEntry,
                        const gfxFontStyle *aFontStyle,
                        bool aNeedsBold)
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -64,24 +64,30 @@ public: // new functions
     }
 
     virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontCacheSizes*   aSizes) const;
     virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontCacheSizes*   aSizes) const;
 
 protected:
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString,
-                           bool aPreferPlatformShaping = false);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText,
+                           bool             aPreferPlatformShaping);
 
     void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd);
 
-    void AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str);
+    void AddRange(const PRUnichar *aText,
+                  uint32_t         aOffset,
+                  uint32_t         aLength,
+                  gfxShapedText   *aShapedText);
 
     typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
     typedef nsTHashtable<CharGlyphMapEntryType> CharGlyphMap;
     CharGlyphMap mCharGlyphCache;
 };
 
 #ifndef ANDROID // not needed on Android, uses the standard gfxFontGroup directly
 class THEBES_API gfxFT2FontGroup : public gfxFontGroup {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2330,45 +2330,37 @@ gfxFont::Measure(gfxTextRun *aTextRun,
     if (isRTL) {
         metrics.mBoundingBox -= gfxPoint(x, 0);
     }
 
     metrics.mAdvanceWidth = x*direction;
     return metrics;
 }
 
-#define MAX_SHAPING_LENGTH  32760 // slightly less than 32K, trying to avoid
-                                  // over-stressing platform shapers
-
-#define BACKTRACK_LIMIT  1024 // If we can't find a space or a cluster start
-                              // within 1K chars, just chop arbitrarily.
-                              // Limiting backtrack here avoids pathological
-                              // behavior on long runs with no whitespace.
-
 static bool
 IsBoundarySpace(PRUnichar aChar, PRUnichar aNextChar)
 {
     return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
 }
 
 static inline uint32_t
 HashMix(uint32_t aHash, PRUnichar aCh)
 {
     return (aHash >> 28) ^ (aHash << 4) ^ aCh;
 }
 
 template<typename T>
 gfxShapedWord*
 gfxFont::GetShapedWord(gfxContext *aContext,
-                       const T *aText,
-                       uint32_t aLength,
-                       uint32_t aHash,
-                       int32_t aRunScript,
-                       int32_t aAppUnitsPerDevUnit,
-                       uint32_t aFlags)
+                       const T    *aText,
+                       uint32_t    aLength,
+                       uint32_t    aHash,
+                       int32_t     aRunScript,
+                       int32_t     aAppUnitsPerDevUnit,
+                       uint32_t    aFlags)
 {
     // if the cache is getting too big, flush it and start over
     if (mWordCache.Count() > 10000) {
         NS_WARNING("flushing shaped-word cache");
         ClearCachedWords();
     }
 
     // if there's a cached entry for this word, just return it
@@ -2395,52 +2387,34 @@ gfxFont::GetShapedWord(gfxContext *aCont
                                                     aRunScript,
                                                     aAppUnitsPerDevUnit,
                                                     aFlags);
     if (!sw) {
         NS_WARNING("failed to create gfxShapedWord - expect missing text");
         return nullptr;
     }
 
-    DebugOnly<bool> ok = false;
-    if (sizeof(T) == sizeof(PRUnichar)) {
-        ok = ShapeWord(aContext, sw, (const PRUnichar*)aText);
-    } else {
-        nsAutoString utf16;
-        AppendASCIItoUTF16(nsDependentCSubstring((const char*)aText, aLength),
-                           utf16);
-        if (utf16.Length() == aLength) {
-            ok = ShapeWord(aContext, sw, utf16.BeginReading());
-        }
-    }
+    DebugOnly<bool> ok =
+        ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
+
     NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
 
-    for (uint32_t i = 0; i < aLength; ++i) {
-        if (aText[i] == ' ') {
-            sw->SetIsSpace(i);
-        } else if (i > 0 &&
-                   NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
-                   NS_IS_LOW_SURROGATE(aText[i])) {
-            sw->SetIsLowSurrogate(i);
-        }
-    }
-
     return sw;
 }
 
 bool
 gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
 {
     const gfxShapedWord *sw = mShapedWord;
     if (!sw) {
         return false;
     }
-    if (sw->Length() != aKey->mLength ||
+    if (sw->GetLength() != aKey->mLength ||
         sw->Flags() != aKey->mFlags ||
-        sw->AppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
+        sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
         sw->Script() != aKey->mScript) {
         return false;
     }
     if (sw->TextIs8Bit()) {
         if (aKey->mTextIs8Bit) {
             return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
                                 aKey->mLength * sizeof(uint8_t)));
         }
@@ -2459,51 +2433,190 @@ gfxFont::CacheHashEntry::KeyEquals(const
     }
     NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
                  !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
     return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
                         aKey->mLength * sizeof(PRUnichar)));
 }
 
 bool
-gfxFont::ShapeWord(gfxContext *aContext,
-                   gfxShapedWord *aShapedWord,
+gfxFont::ShapeText(gfxContext    *aContext,
+                   const uint8_t *aText,
+                   uint32_t       aOffset,
+                   uint32_t       aLength,
+                   int32_t        aScript,
+                   gfxShapedText *aShapedText,
+                   bool           aPreferPlatformShaping)
+{
+    nsDependentCSubstring ascii((const char*)aText, aLength);
+    nsAutoString utf16;
+    AppendASCIItoUTF16(ascii, utf16);
+    if (utf16.Length() != aLength) {
+        return false;
+    }
+    return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
+                     aScript, aShapedText, aPreferPlatformShaping);
+}
+
+bool
+gfxFont::ShapeText(gfxContext      *aContext,
                    const PRUnichar *aText,
-                   bool aPreferPlatformShaping)
+                   uint32_t         aOffset,
+                   uint32_t         aLength,
+                   int32_t          aScript,
+                   gfxShapedText   *aShapedText,
+                   bool             aPreferPlatformShaping)
 {
     bool ok = false;
 
 #ifdef MOZ_GRAPHITE
     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
-        ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aText);
+        ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                        aScript, aShapedText);
     }
 #endif
 
     if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
-        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
-            ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aText);
+        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
+            ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                            aScript, aShapedText);
         }
     }
 
     if (!ok) {
         if (!mPlatformShaper) {
             CreatePlatformShaper();
             NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
         }
         if (mPlatformShaper) {
-            ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aText);
+            ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                            aScript, aShapedText);
         }
     }
 
-    if (ok && IsSyntheticBold()) {
+    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
+
+    return ok;
+}
+
+void
+gfxFont::PostShapingFixup(gfxContext      *aContext,
+                          const PRUnichar *aText,
+                          uint32_t         aOffset,
+                          uint32_t         aLength,
+                          gfxShapedText   *aShapedText)
+{
+    if (IsSyntheticBold()) {
         float synBoldOffset =
                 GetSyntheticBoldOffset() * CalcXScale(aContext);
-        aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
-    }
-
+        aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
+                                                    aOffset, aLength);
+    }
+}
+
+#define MAX_SHAPING_LENGTH  32760 // slightly less than 32K, trying to avoid
+                                  // over-stressing platform shapers
+#define BACKTRACK_LIMIT     16 // backtrack this far looking for a good place
+                               // to split into fragments for separate shaping
+
+template<typename T>
+bool
+gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
+                                       const T    *aText,
+                                       uint32_t    aOffset,
+                                       uint32_t    aLength,
+                                       int32_t     aScript,
+                                       gfxTextRun *aTextRun)
+{
+    aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
+
+    bool ok = true;
+
+    while (ok && aLength > 0) {
+        uint32_t fragLen = aLength;
+
+        // limit the length of text we pass to shapers in a single call
+        if (fragLen > MAX_SHAPING_LENGTH) {
+            fragLen = MAX_SHAPING_LENGTH;
+
+            // in the 8-bit case, there are no multi-char clusters,
+            // so we don't need to do this check
+            if (sizeof(T) == sizeof(PRUnichar)) {
+                uint32_t i;
+                for (i = 0; i < BACKTRACK_LIMIT; ++i) {
+                    if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
+                        fragLen -= i;
+                        break;
+                    }
+                }
+                if (i == BACKTRACK_LIMIT) {
+                    // if we didn't find any cluster start while backtracking,
+                    // just check that we're not in the middle of a surrogate
+                    // pair; back up by one code unit if we are.
+                    if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
+                        NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
+                        --fragLen;
+                    }
+                }
+            }
+        }
+
+        ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
+
+        aText += fragLen;
+        aOffset += fragLen;
+        aLength -= fragLen;
+    }
+
+    return ok;
+}
+
+template<typename T>
+bool
+gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
+                                   const T    *aText,
+                                   uint32_t    aOffset,
+                                   uint32_t    aLength,
+                                   int32_t     aScript,
+                                   gfxTextRun *aTextRun)
+{
+    uint32_t fragStart = 0;
+    bool ok = true;
+
+    for (uint32_t i = 0; i <= aLength && ok; ++i) {
+        T ch = (i < aLength) ? aText[i] : '\n';
+        bool invalid = gfxFontGroup::IsInvalidChar(ch);
+        uint32_t length = i - fragStart;
+
+        // break into separate fragments when we hit an invalid char
+        if (!invalid) {
+            continue;
+        }
+
+        if (length > 0) {
+            ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
+                                               aOffset + fragStart, length,
+                                               aScript, aTextRun);
+        }
+
+        if (i == aLength) {
+            break;
+        }
+
+        // fragment was terminated by an invalid char: skip it,
+        // but record where TAB or NEWLINE occur
+        if (ch == '\t') {
+            aTextRun->SetIsTab(aOffset + i);
+        } else if (ch == '\n') {
+            aTextRun->SetIsNewline(aOffset + i);
+        }
+        fragStart = i + 1;
+    }
+
+    NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
     return ok;
 }
 
 inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
 inline static bool IsChar8Bit(PRUnichar aCh) { return aCh < 0x100; }
 
 template<typename T>
 bool
@@ -2543,17 +2656,17 @@ gfxFont::SplitAndInitTextRun(gfxContext 
         uint32_t length = i - wordStart;
 
         // break into separate ShapedWords when we hit an invalid char,
         // or a boundary space (always handled individually),
         // or the first non-space after a space
         bool breakHere = boundary || invalid;
 
         if (!breakHere) {
-            // if we're approaching the max ShapedWord length, break anyway...
+            // if we're approaching the max length for shaping, break anyway...
             if (sizeof(T) == sizeof(uint8_t)) {
                 // in 8-bit text, no clusters or surrogates to worry about
                 if (length >= gfxShapedWord::kMaxLength) {
                     breakHere = true;
                 }
             } else {
                 // try to avoid breaking before combining mark or low surrogate
                 if (length >= gfxShapedWord::kMaxLength - 15) {
@@ -3763,17 +3876,17 @@ gfxFontGroup::InitScriptRun(gfxContext *
     nsAutoTArray<gfxTextRange,3> fontRanges;
     ComputeRanges(fontRanges, aString + aScriptRunStart,
                   aScriptRunEnd - aScriptRunStart, aRunScript);
     uint32_t numRanges = fontRanges.Length();
 
     for (uint32_t r = 0; r < numRanges; r++) {
         const gfxTextRange& range = fontRanges[r];
         uint32_t matchedLength = range.Length();
-        gfxFont *matchedFont = (range.font ? range.font.get() : nullptr);
+        gfxFont *matchedFont = range.font;
 
         // create the glyph run for this range
         if (matchedFont) {
             aTextRun->AddGlyphRun(matchedFont, range.matchType,
                                   runStart, (matchedLength > 0));
             // do glyph layout and record the resulting positioned glyphs
             if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString,
                                                   runStart, matchedLength,
@@ -3782,24 +3895,21 @@ gfxFontGroup::InitScriptRun(gfxContext *
                 matchedFont = nullptr;
             }
         } else {
             aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
                                   runStart, (matchedLength > 0));
         }
 
         if (!matchedFont) {
-            // for PRUnichar text, we need to set cluster boundaries so that
+            // We need to set cluster boundaries (and mark spaces) so that
             // surrogate pairs, combining characters, etc behave properly,
             // even if we don't have glyphs for them
-            if (sizeof(T) == sizeof(PRUnichar)) {
-                gfxShapedWord::SetupClusterBoundaries(aTextRun->GetCharacterGlyphs() + runStart,
-                                                      reinterpret_cast<const PRUnichar*>(aString) + runStart,
-                                                      matchedLength);
-            }
+            aTextRun->SetupClusterBoundaries(runStart, aString + runStart,
+                                             matchedLength);
 
             // various "missing" characters may need special handling,
             // so we check for them here
             uint32_t runLimit = runStart + matchedLength;
             for (uint32_t index = runStart; index < runLimit; index++) {
                 T ch = aString[index];
 
                 // tab and newline are not to be displayed as hexboxes,
@@ -3817,53 +3927,53 @@ gfxFontGroup::InitScriptRun(gfxContext *
                 // special Unicode spaces; omit these checks in 8-bit runs
                 if (sizeof(T) == sizeof(PRUnichar)) {
                     if (NS_IS_HIGH_SURROGATE(ch) &&
                         index + 1 < aScriptRunEnd &&
                         NS_IS_LOW_SURROGATE(aString[index + 1]))
                     {
                         aTextRun->SetMissingGlyph(index,
                                                   SURROGATE_TO_UCS4(ch,
-                                                                    aString[index + 1]));
+                                                                    aString[index + 1]),
+                                                  mainFont);
                         index++;
-                        aTextRun->SetIsLowSurrogate(index);
                         continue;
                     }
 
                     // check if this is a known Unicode whitespace character that
                     // we can render using the space glyph with a custom width
                     gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
                     if (wid >= 0.0) {
                         nscoord advance =
                             aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
-                        gfxTextRun::CompressedGlyph g;
-                        if (gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance)) {
-                            aTextRun->SetSimpleGlyph(index,
-                                                     g.SetSimpleGlyph(advance,
-                                                         mainFont->GetSpaceGlyph()));
+                        if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
+                            aTextRun->GetCharacterGlyphs()[index].
+                                SetSimpleGlyph(advance,
+                                               mainFont->GetSpaceGlyph());
                         } else {
                             gfxTextRun::DetailedGlyph detailedGlyph;
                             detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
                             detailedGlyph.mAdvance = advance;
                             detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
+                            gfxShapedText::CompressedGlyph g;
                             g.SetComplex(true, true, 1);
                             aTextRun->SetGlyphs(index,
                                                 g, &detailedGlyph);
                         }
                         continue;
                     }
                 }
 
                 if (IsInvalidChar(ch)) {
                     // invalid chars are left as zero-width/invisible
                     continue;
                 }
 
                 // record char code so we can draw a box with the Unicode value
-                aTextRun->SetMissingGlyph(index, ch);
+                aTextRun->SetMissingGlyph(index, ch, mainFont);
             }
         }
 
         runStart += matchedLength;
     }
 }
 
 already_AddRefed<gfxFont>
@@ -4324,140 +4434,173 @@ gfxFontStyle::ComputeWeight() const
     if (baseWeight < 0)
         baseWeight = 0;
     if (baseWeight > 9)
         baseWeight = 9;
 
     return baseWeight;
 }
 
-// This is not a member function of gfxShapedWord because it is also used
-// by gfxFontGroup on missing-glyph runs, where we don't actually "shape"
-// anything but still need to set cluster info.
-/*static*/ void
-gfxShapedWord::SetupClusterBoundaries(CompressedGlyph *aGlyphs,
-                                      const PRUnichar *aString, uint32_t aLength)
-{
+void
+gfxShapedText::SetupClusterBoundaries(uint32_t         aOffset,
+                                      const PRUnichar *aString,
+                                      uint32_t         aLength)
+{
+    CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
+
     gfxTextRun::CompressedGlyph extendCluster;
     extendCluster.SetComplex(false, true, 0);
 
     ClusterIterator iter(aString, aLength);
 
     // the ClusterIterator won't be able to tell us if the string
     // _begins_ with a cluster-extender, so we handle that here
     if (aLength && IsClusterExtender(*aString)) {
-        *aGlyphs = extendCluster;
+        *glyphs = extendCluster;
     }
 
     while (!iter.AtEnd()) {
+        if (*iter == PRUnichar(' ')) {
+            glyphs->SetIsSpace();
+        }
         // advance iter to the next cluster-start (or end of text)
         iter.Next();
         // step past the first char of the cluster
         aString++;
-        aGlyphs++;
+        glyphs++;
         // mark all the rest as cluster-continuations
         while (aString < iter) {
-            *aGlyphs++ = extendCluster;
+            *glyphs = extendCluster;
+            if (NS_IS_LOW_SURROGATE(*aString)) {
+                glyphs->SetIsLowSurrogate();
+            }
+            glyphs++;
             aString++;
         }
     }
 }
 
-gfxShapedWord::DetailedGlyph *
-gfxShapedWord::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
-{
-    NS_ASSERTION(aIndex < Length(), "Index out of range");
+void
+gfxShapedText::SetupClusterBoundaries(uint32_t       aOffset,
+                                      const uint8_t *aString,
+                                      uint32_t       aLength)
+{
+    CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
+    const uint8_t *limit = aString + aLength;
+
+    while (aString < limit) {
+        if (*aString == uint8_t(' ')) {
+            glyphs->SetIsSpace();
+        }
+        aString++;
+        glyphs++;
+    }
+}
+
+gfxShapedText::DetailedGlyph *
+gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
+{
+    NS_ASSERTION(aIndex < GetLength(), "Index out of range");
 
     if (!mDetailedGlyphs) {
         mDetailedGlyphs = new DetailedGlyphStore();
     }
 
     DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
     if (!details) {
-        mCharacterGlyphs[aIndex].SetMissing(0);
+        GetCharacterGlyphs()[aIndex].SetMissing(0);
         return nullptr;
     }
 
     return details;
 }
 
 void
-gfxShapedWord::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
+gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
                          const DetailedGlyph *aGlyphs)
 {
     NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
     NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
                  "First character can't be a ligature continuation!");
 
     uint32_t glyphCount = aGlyph.GetGlyphCount();
     if (glyphCount > 0) {
         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
         if (!details) {
             return;
         }
         memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
     }
-    mCharacterGlyphs[aIndex] = aGlyph;
+    GetCharacterGlyphs()[aIndex] = aGlyph;
 }
 
 #define ZWNJ 0x200C
 #define ZWJ  0x200D
 static inline bool
 IsDefaultIgnorable(uint32_t aChar)
 {
     return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
            aChar == ZWNJ || aChar == ZWJ;
 }
 
 void
-gfxShapedWord::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
-{
+gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
+{
+    uint8_t category = GetGeneralCategory(aChar);
+    if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
+        category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    {
+        GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
+    }
+
     DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
     if (!details) {
         return;
     }
 
     details->mGlyphID = aChar;
     if (IsDefaultIgnorable(aChar)) {
         // Setting advance width to zero will prevent drawing the hexbox
         details->mAdvance = 0;
     } else {
         gfxFloat width = NS_MAX(aFont->GetMetrics().aveCharWidth,
                                 gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
         details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
     }
     details->mXOffset = 0;
     details->mYOffset = 0;
-    mCharacterGlyphs[aIndex].SetMissing(1);
+    GetCharacterGlyphs()[aIndex].SetMissing(1);
 }
 
 bool
-gfxShapedWord::FilterIfIgnorable(uint32_t aIndex)
-{
-    uint32_t ch = GetCharAt(aIndex);
-    if (IsDefaultIgnorable(ch)) {
+gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
+{
+    if (IsDefaultIgnorable(aCh)) {
         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
         if (details) {
-            details->mGlyphID = ch;
+            details->mGlyphID = aCh;
             details->mAdvance = 0;
             details->mXOffset = 0;
             details->mYOffset = 0;
-            mCharacterGlyphs[aIndex].SetMissing(1);
+            GetCharacterGlyphs()[aIndex].SetMissing(1);
             return true;
         }
     }
     return false;
 }
 
 void
-gfxShapedWord::AdjustAdvancesForSyntheticBold(float aSynBoldOffset)
+gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
+                                              uint32_t aOffset,
+                                              uint32_t aLength)
 {
     uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
-    for (uint32_t i = 0; i < Length(); ++i) {
-         CompressedGlyph *glyphData = &mCharacterGlyphs[i];
+    CompressedGlyph *charGlyphs = GetCharacterGlyphs();
+    for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
+         CompressedGlyph *glyphData = charGlyphs + i;
          if (glyphData->IsSimpleGlyph()) {
              // simple glyphs ==> just add the advance
              int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
              if (CompressedGlyph::IsSimpleAdvance(advance)) {
                  glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
              } else {
                  // rare case, tested by making this the default
                  uint32_t glyphIndex = glyphData->GetSimpleGlyph();
@@ -4488,17 +4631,17 @@ gfxTextRun::GlyphRunIterator::NextRun() 
     if (mNextIndex >= mTextRun->mGlyphRuns.Length())
         return false;
     mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
     if (mGlyphRun->mCharacterOffset >= mEndOffset)
         return false;
 
     mStringStart = NS_MAX(mStartOffset, mGlyphRun->mCharacterOffset);
     uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
-        ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->mCharacterCount;
+        ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
     mStringEnd = NS_MIN(mEndOffset, last);
 
     ++mNextIndex;
     return true;
 }
 
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
 static void
@@ -4548,20 +4691,19 @@ gfxTextRun::Create(const gfxTextRunFacto
         return nullptr;
     }
 
     return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
 }
 
 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
                        uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
-  : mUserData(aParams->mUserData),
-    mFontGroup(aFontGroup),
-    mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
-    mFlags(aFlags), mCharacterCount(aLength)
+    : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
+    , mUserData(aParams->mUserData)
+    , mFontGroup(aFontGroup)
 {
     NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
     MOZ_COUNT_CTOR(gfxTextRun);
     NS_ADDREF(mFontGroup);
 
     mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
 
     if (aParams->mSkipChars) {
@@ -4589,17 +4731,17 @@ gfxTextRun::~gfxTextRun()
     MOZ_COUNT_DTOR(gfxTextRun);
 }
 
 bool
 gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
                                    uint8_t *aBreakBefore,
                                    gfxContext *aRefContext)
 {
-    NS_ASSERTION(aStart + aLength <= mCharacterCount, "Overflow");
+    NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
 
     uint32_t changed = 0;
     uint32_t i;
     CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
     for (i = 0; i < aLength; ++i) {
         uint8_t canBreak = aBreakBefore[i];
         if (canBreak && !charGlyphs[i].IsClusterStart()) {
             // This can happen ... there is no guarantee that our linebreaking rules
@@ -4612,27 +4754,27 @@ gfxTextRun::SetPotentialLineBreaks(uint3
     return changed != 0;
 }
 
 gfxTextRun::LigatureData
 gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
                                 PropertyProvider *aProvider)
 {
     NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
-    NS_ASSERTION(aPartEnd <= mCharacterCount, "Character length overflow");
+    NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
   
     LigatureData result;
     CompressedGlyph *charGlyphs = mCharacterGlyphs;
 
     uint32_t i;
     for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
         NS_ASSERTION(i > 0, "Ligature at the start of the run??");
     }
     result.mLigatureStart = i;
-    for (i = aPartStart + 1; i < mCharacterCount && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
+    for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
     }
     result.mLigatureEnd = i;
 
     int32_t ligatureWidth =
         GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
     // Count the number of started clusters we have seen
     uint32_t totalClusterCount = 0;
     uint32_t partClusterIndex = 0;
@@ -4777,17 +4919,17 @@ gfxTextRun::ShrinkToLigatureBoundaries(u
     if (*aStart >= *aEnd)
         return;
   
     CompressedGlyph *charGlyphs = mCharacterGlyphs;
 
     while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
         ++(*aStart);
     }
-    if (*aEnd < mCharacterCount) {
+    if (*aEnd < GetLength()) {
         while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
             --(*aEnd);
         }
     }
 }
 
 void
 gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
@@ -4942,17 +5084,17 @@ struct BufferAlphaColor {
 
 void
 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode,
                  uint32_t aStart, uint32_t aLength,
                  PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
                  gfxTextObjectPaint *aObjectPaint,
                  gfxTextRun::DrawCallbacks *aCallbacks)
 {
-    NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
+    NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
     NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
     NS_ASSERTION(aDrawMode == gfxFont::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
 
     gfxFloat direction = GetDirection();
 
     if (mSkipDrawing) {
         // We're waiting for a user font to finish downloading;
         // but if the caller wants advance width, we need to compute it here
@@ -5080,17 +5222,17 @@ gfxTextRun::AccumulatePartialLigatureMet
 }
 
 gfxTextRun::Metrics
 gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
                         gfxFont::BoundingBoxType aBoundingBoxType,
                         gfxContext *aRefContext,
                         PropertyProvider *aProvider)
 {
-    NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
+    NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
 
     Metrics accumulatedMetrics;
     GlyphRunIterator iter(this, aStart, aLength);
     while (iter.NextRun()) {
         gfxFont *font = iter.GetGlyphRun()->mFont;
         uint32_t start = iter.GetStringStart();
         uint32_t end = iter.GetStringEnd();
         uint32_t ligatureRunStart = start;
@@ -5128,19 +5270,19 @@ gfxTextRun::BreakAndMeasureText(uint32_t
                                 Metrics *aMetrics,
                                 gfxFont::BoundingBoxType aBoundingBoxType,
                                 gfxContext *aRefContext,
                                 bool *aUsedHyphenation,
                                 uint32_t *aLastBreak,
                                 bool aCanWordWrap,
                                 gfxBreakPriority *aBreakPriority)
 {
-    aMaxLength = NS_MIN(aMaxLength, mCharacterCount - aStart);
-
-    NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range");
+    aMaxLength = NS_MIN(aMaxLength, GetLength() - aStart);
+
+    NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
 
     uint32_t bufferStart = aStart;
     uint32_t bufferLength = NS_MIN<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
     PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
     bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
     if (haveSpacing) {
         GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
                            spacingBuffer);
@@ -5289,17 +5431,17 @@ gfxTextRun::BreakAndMeasureText(uint32_t
 
     return charsFit;
 }
 
 gfxFloat
 gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
                             PropertyProvider *aProvider)
 {
-    NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
+    NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
 
     uint32_t ligatureRunStart = aStart;
     uint32_t ligatureRunEnd = aStart + aLength;
     ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
 
     gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
                       ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
 
@@ -5333,20 +5475,20 @@ gfxTextRun::SetLineBreaks(uint32_t aStar
         *aAdvanceWidthDelta = 0;
     }
     return false;
 }
 
 uint32_t
 gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
 {
-    NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun");
-    NS_ASSERTION(mCharacterCount == 0 || mGlyphRuns.Length() > 0,
+    NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
+    NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
                  "non-empty text but no glyph runs present!");
-    if (aOffset == mCharacterCount)
+    if (aOffset == GetLength())
         return mGlyphRuns.Length();
     uint32_t start = 0;
     uint32_t end = mGlyphRuns.Length();
     while (end - start > 1) {
         uint32_t mid = (start + end)/2;
         if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
             start = mid;
         } else {
@@ -5454,129 +5596,83 @@ gfxTextRun::SanitizeGlyphRuns()
     // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
     // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
     // it appear as if a ligature has been formed)
     int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
     const CompressedGlyph *charGlyphs = mCharacterGlyphs;
     for (i = lastRunIndex; i >= 0; --i) {
         GlyphRun& run = mGlyphRuns[i];
         while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
-               run.mCharacterOffset < mCharacterCount) {
+               run.mCharacterOffset < GetLength()) {
             run.mCharacterOffset++;
         }
         // if the run has become empty, eliminate it
         if ((i < lastRunIndex &&
              run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
-            (i == lastRunIndex && run.mCharacterOffset == mCharacterCount)) {
+            (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
             mGlyphRuns.RemoveElementAt(i);
             --lastRunIndex;
         }
     }
 }
 
 uint32_t
 gfxTextRun::CountMissingGlyphs()
 {
     uint32_t i;
     uint32_t count = 0;
-    for (i = 0; i < mCharacterCount; ++i) {
+    for (i = 0; i < GetLength(); ++i) {
         if (mCharacterGlyphs[i].IsMissing()) {
             ++count;
         }
     }
     return count;
 }
 
 gfxTextRun::DetailedGlyph *
 gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
 {
-    NS_ASSERTION(aIndex < mCharacterCount, "Index out of range");
+    NS_ASSERTION(aIndex < GetLength(), "Index out of range");
 
     if (!mDetailedGlyphs) {
         mDetailedGlyphs = new DetailedGlyphStore();
     }
 
     DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
     if (!details) {
         mCharacterGlyphs[aIndex].SetMissing(0);
         return nullptr;
     }
 
     return details;
 }
 
 void
-gfxTextRun::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
-                      const DetailedGlyph *aGlyphs)
-{
-    NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
-    NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
-                 "First character can't be a ligature continuation!");
-
-    uint32_t glyphCount = aGlyph.GetGlyphCount();
-    if (glyphCount > 0) {
-        DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
-        if (!details)
-            return;
-        memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
-    }
-    mCharacterGlyphs[aIndex] = aGlyph;
-}
-
-void
-gfxTextRun::SetMissingGlyph(uint32_t aIndex, uint32_t aChar)
-{
-    uint8_t category = GetGeneralCategory(aChar);
-    if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
-        category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
-    {
-        mCharacterGlyphs[aIndex].SetComplex(false, true, 0);
-    }
-
-    DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
-    if (!details)
-        return;
-
-    details->mGlyphID = aChar;
-    GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)];
-    if (IsDefaultIgnorable(aChar)) {
-        // Setting advance width to zero will prevent drawing the hexbox
-        details->mAdvance = 0;
-    } else {
-        gfxFloat width = NS_MAX(glyphRun->mFont->GetMetrics().aveCharWidth,
-                                gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
-        details->mAdvance = uint32_t(width*GetAppUnitsPerDevUnit());
-    }
-    details->mXOffset = 0;
-    details->mYOffset = 0;
-    mCharacterGlyphs[aIndex].SetMissing(1);
-}
-
-void
-gfxTextRun::CopyGlyphDataFrom(const gfxShapedWord *aShapedWord, uint32_t aOffset)
-{
-    uint32_t wordLen = aShapedWord->Length();
+gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
+{
+    uint32_t wordLen = aShapedWord->GetLength();
     NS_ASSERTION(aOffset + wordLen <= GetLength(),
                  "word overruns end of textrun!");
 
+    CompressedGlyph *charGlyphs = GetCharacterGlyphs();
     const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
     if (aShapedWord->HasDetailedGlyphs()) {
         for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
             const CompressedGlyph& g = wordGlyphs[i];
             if (g.IsSimpleGlyph()) {
-                SetSimpleGlyph(aOffset, g);
+                charGlyphs[aOffset] = g;
             } else {
                 const DetailedGlyph *details =
                     g.GetGlyphCount() > 0 ?
                         aShapedWord->GetDetailedGlyphs(i) : nullptr;
                 SetGlyphs(aOffset, g, details);
             }
         }
     } else {
-        memcpy(GetCharacterGlyphs() + aOffset, wordGlyphs,
+        memcpy(charGlyphs + aOffset, wordGlyphs,
                wordLen * sizeof(CompressedGlyph));
     }
 }
 
 void
 gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
                               uint32_t aLength, uint32_t aDest)
 {
@@ -5691,17 +5787,17 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFon
     }
 
     AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
     CompressedGlyph g;
     g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
     if (aSpaceChar == ' ') {
         g.SetIsSpace();
     }
-    SetSimpleGlyph(aCharIndex, g);
+    GetCharacterGlyphs()[aCharIndex] = g;
     return true;
 }
 
 void
 gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
 {
     bool needsGlyphExtents = NeedsGlyphExtents(this);
     if (!needsGlyphExtents && !mDetailedGlyphs)
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -36,16 +36,17 @@ typedef struct _cairo_scaled_font cairo_
 
 class gfxContext;
 class gfxTextRun;
 class gfxFont;
 class gfxFontFamily;
 class gfxFontGroup;
 class gfxUserFontSet;
 class gfxUserFontData;
+class gfxShapedText;
 class gfxShapedWord;
 class gfxSVGGlyphs;
 class gfxTextObjectPaint;
 
 class nsILanguageAtomService;
 
 typedef struct hb_blob_t hb_blob_t;
 
@@ -1121,19 +1122,25 @@ public:
     gfxFontShaper(gfxFont *aFont)
         : mFont(aFont)
     {
         NS_ASSERTION(aFont, "shaper requires a valid font!");
     }
 
     virtual ~gfxFontShaper() { }
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aText) = 0;
+    // Shape a piece of text and store the resulting glyph data into
+    // aShapedText. Parameters aOffset/aLength indicate the range of
+    // aShapedText to be updated; aLength is also the length of aText.
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText) = 0;
 
     gfxFont *GetFont() const { return mFont; }
 
     // returns true if features exist in output, false otherwise
     static bool
     MergeFontFeatures(const nsTArray<gfxFontFeature>& aStyleRuleFeatures,
                       const nsTArray<gfxFontFeature>& aFontFeatures,
                       bool aDisableLigatures,
@@ -1556,23 +1563,71 @@ public:
     } FontType;
 
     virtual FontType GetType() const = 0;
 
     virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont> GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
     { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
 
 protected:
+    // For 8-bit text, expand to 16-bit and then call the following method.
+    bool ShapeText(gfxContext    *aContext,
+                   const uint8_t *aText,
+                   uint32_t       aOffset, // dest offset in gfxShapedText
+                   uint32_t       aLength,
+                   int32_t        aScript,
+                   gfxShapedText *aShapedText, // where to store the result
+                   bool           aPreferPlatformShaping = false);
+
     // Call the appropriate shaper to generate glyphs for aText and store
-    // them into aShapedWord.
-    // The length of the text is aShapedWord->Length().
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
+    // them into aShapedText.
+    virtual bool ShapeText(gfxContext      *aContext,
                            const PRUnichar *aText,
-                           bool aPreferPlatformShaping = false);
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText,
+                           bool             aPreferPlatformShaping = false);
+
+    // Helper to adjust for synthetic bold and set character-type flags
+    // in the shaped text; implementations of ShapeText should call this
+    // after glyph shaping has been completed.
+    void PostShapingFixup(gfxContext      *aContext,
+                          const PRUnichar *aText,
+                          uint32_t         aOffset, // position within aShapedText
+                          uint32_t         aLength,
+                          gfxShapedText   *aShapedText);
+
+    // Shape text directly into a range within a textrun, without using the
+    // font's word cache. Intended for use when the font has layout features
+    // that involve space, and therefore require shaping complete runs rather
+    // than isolated words, or for long strings that are inefficient to cache.
+    // This will split the text on "invalid" characters (tab/newline) that are
+    // not handled via normal shaping, but does not otherwise divide up the
+    // text.
+    template<typename T>
+    bool ShapeTextWithoutWordCache(gfxContext *aContext,
+                                   const T    *aText,
+                                   uint32_t    aOffset,
+                                   uint32_t    aLength,
+                                   int32_t     aScript,
+                                   gfxTextRun *aTextRun);
+
+    // Shape a fragment of text (a run that is known to contain only
+    // "valid" characters, no newlines/tabs/other control chars).
+    // All non-wordcache shaping goes through here; this is the function
+    // that will ensure we don't pass excessively long runs to the various
+    // platform shapers.
+    template<typename T>
+    bool ShapeFragmentWithoutWordCache(gfxContext *aContext,
+                                       const T    *aText,
+                                       uint32_t    aOffset,
+                                       uint32_t    aLength,
+                                       int32_t     aScript,
+                                       gfxTextRun *aTextRun);
 
     nsRefPtr<gfxFontEntry> mFontEntry;
 
     struct CacheHashKey {
         union {
             const uint8_t   *mSingle;
             const PRUnichar *mDouble;
         }                mText;
@@ -1719,105 +1774,45 @@ protected:
     // synthetic-bold offset.
     static double CalcXScale(gfxContext *aContext);
 };
 
 // proportion of ascent used for x-height, if unable to read value from font
 #define DEFAULT_XHEIGHT_FACTOR 0.56f
 
 /*
- * gfxShapedWord stores a list of zero or more glyphs for each character. For each
- * glyph we store the glyph ID, the advance, and possibly an xoffset and yoffset.
+ * gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun.
+ * These are objects that store a list of zero or more glyphs for each character.
+ * For each glyph we store the glyph ID, the advance, and possibly x/y-offsets.
  * The idea is that a string is rendered by a loop that draws each glyph
  * at its designated offset from the current point, then advances the current
  * point by the glyph's advance in the direction of the textrun (LTR or RTL).
  * Each glyph advance is always rounded to the nearest appunit; this ensures
  * consistent results when dividing the text in a textrun into multiple text
  * frames (frame boundaries are always aligned to appunits). We optimize
  * for the case where a character has a single glyph and zero xoffset and yoffset,
  * and the glyph ID and advance are in a reasonable range so we can pack all
  * necessary data into 32 bits.
  *
- * This glyph data is copied into gfxTextRuns as needed from the cache of
- * ShapedWords associated with each gfxFont instance.
- *
- * gfxTextRun methods that measure or draw substrings will associate all the
- * glyphs in a cluster with the first character of the cluster; if that character
- * is in the substring, the glyphs will be measured or drawn, otherwise they
- * won't.
+ * gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont)
+ * or directly into a gfxTextRun (for cases where we want to shape textruns in
+ * their entirety rather than using cached words, because there may be layout
+ * features that depend on the inter-word spaces).
  */
-class gfxShapedWord
+class gfxShapedText
 {
 public:
-    static const uint32_t kMaxLength = 0x7fff;
-
-    // Create a ShapedWord that can hold glyphs for aLength characters,
-    // with mCharacterGlyphs sized appropriately.
-    //
-    // Returns null on allocation failure (does NOT use infallible alloc)
-    // so caller must check for success.
-    //
-    // This does NOT perform shaping, so the returned word contains no
-    // glyph data; the caller must call gfxFont::Shape() with appropriate
-    // parameters to set up the glyphs.
-    static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
-                                 int32_t aRunScript,
-                                 int32_t aAppUnitsPerDevUnit,
-                                 uint32_t aFlags) {
-        NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
-
-        // Compute size needed including the mCharacterGlyphs array
-        // and a copy of the original text
-        uint32_t size =
-            offsetof(gfxShapedWord, mCharacterGlyphs) +
-            aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
-        void *storage = moz_malloc(size);
-        if (!storage) {
-            return nullptr;
-        }
-
-        // Construct in the pre-allocated storage, using placement new
-        return new (storage) gfxShapedWord(aText, aLength, aRunScript,
-                                           aAppUnitsPerDevUnit, aFlags);
-    }
-
-    static gfxShapedWord* Create(const PRUnichar *aText, uint32_t aLength,
-                                 int32_t aRunScript,
-                                 int32_t aAppUnitsPerDevUnit,
-                                 uint32_t aFlags) {
-        NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
-
-        // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
-        // then we convert the text to an 8-bit version and call the 8-bit
-        // Create function instead.
-        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-            nsAutoCString narrowText;
-            LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
-                                    narrowText);
-            return Create((const uint8_t*)(narrowText.BeginReading()),
-                          aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
-        }
-
-        uint32_t size =
-            offsetof(gfxShapedWord, mCharacterGlyphs) +
-            aLength * (sizeof(CompressedGlyph) + sizeof(PRUnichar));
-        void *storage = moz_malloc(size);
-        if (!storage) {
-            return nullptr;
-        }
-
-        return new (storage) gfxShapedWord(aText, aLength, aRunScript,
-                                           aAppUnitsPerDevUnit, aFlags);
-    }
-
-    // Override operator delete to properly free the object that was
-    // allocated via moz_malloc.
-    void operator delete(void* p) {
-        moz_free(p);
-    }
+    gfxShapedText(uint32_t aLength, uint32_t aFlags,
+                  uint32_t aAppUnitsPerDevUnit)
+        : mLength(aLength)
+        , mFlags(aFlags)
+        , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
+    { }
+
+    virtual ~gfxShapedText() { }
 
     /**
      * This class records the information associated with a character in the
      * input string. It's optimized for the case where there is one glyph
      * representing that character alone.
      * 
      * A character can have zero or more associated glyphs. Each glyph
      * has an advance width and an x and y offset.
@@ -2007,61 +2002,89 @@ public:
             NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
             mValue |= FLAG_CHAR_IS_LOW_SURROGATE;
         }
 
     private:
         uint32_t mValue;
     };
 
+    // Accessor for the array of CompressedGlyph records, which will be in
+    // a different place in gfxShapedWord vs gfxTextRun
+    virtual CompressedGlyph *GetCharacterGlyphs() = 0;
+
     /**
      * When the glyphs for a character don't fit into a CompressedGlyph record
      * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
      */
     struct DetailedGlyph {
         /** The glyphID, or the Unicode character
          * if this is a missing glyph */
         uint32_t mGlyphID;
         /** The advance, x-offset and y-offset of the glyph, in appunits
          *  mAdvance is in the text direction (RTL or LTR)
          *  mXOffset is always from left to right
          *  mYOffset is always from top to bottom */   
         int32_t  mAdvance;
         float    mXOffset, mYOffset;
     };
 
+    void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
+                   const DetailedGlyph *aGlyphs);
+
+    void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
+
+    void SetIsSpace(uint32_t aIndex) {
+        GetCharacterGlyphs()[aIndex].SetIsSpace();
+    }
+
+    void SetIsLowSurrogate(uint32_t aIndex) {
+        SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
+        GetCharacterGlyphs()[aIndex].SetIsLowSurrogate();
+    }
+
+    bool HasDetailedGlyphs() const {
+        return mDetailedGlyphs != nullptr;
+    }
+
     bool IsClusterStart(uint32_t aPos) {
-        NS_ASSERTION(aPos < Length(), "aPos out of range");
-        return mCharacterGlyphs[aPos].IsClusterStart();
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        return GetCharacterGlyphs()[aPos].IsClusterStart();
     }
 
     bool IsLigatureGroupStart(uint32_t aPos) {
-        NS_ASSERTION(aPos < Length(), "aPos out of range");
-        return mCharacterGlyphs[aPos].IsLigatureGroupStart();
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        return GetCharacterGlyphs()[aPos].IsLigatureGroupStart();
     }
 
-    uint32_t Length() const {
-        return mLength;
-    }
-
-    const uint8_t* Text8Bit() const {
-        NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
-        return reinterpret_cast<const uint8_t*>(&mCharacterGlyphs[Length()]);
+    // NOTE that this must not be called for a character offset that does
+    // not have any DetailedGlyph records; callers must have verified that
+    // GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero.
+    DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) {
+        NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() &&
+                     !GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() &&
+                     GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0,
+                     "invalid use of GetDetailedGlyphs; check the caller!");
+        return mDetailedGlyphs->Get(aCharIndex);
     }
 
-    const PRUnichar* TextUnicode() const {
-        NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
-        return reinterpret_cast<const PRUnichar*>(&mCharacterGlyphs[Length()]);
-    }
-
-    PRUnichar GetCharAt(uint32_t aOffset) const {
-        NS_ASSERTION(aOffset < Length(), "aOffset out of range");
-        return TextIs8Bit() ?
-            PRUnichar(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
-    }
+    void AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
+                                        uint32_t aOffset, uint32_t aLength);
+
+    // Mark clusters in the CompressedGlyph records, starting at aOffset,
+    // based on the Unicode properties of the text in aString.
+    // This is also responsible to set the IsSpace flag for space characters.
+    void SetupClusterBoundaries(uint32_t         aOffset,
+                                const PRUnichar *aString,
+                                uint32_t         aLength);
+    // In 8-bit text, there won't actually be any clusters, but we still need
+    // the space-marking functionality.
+    void SetupClusterBoundaries(uint32_t       aOffset,
+                                const uint8_t *aString,
+                                uint32_t       aLength);
 
     uint32_t Flags() const {
         return mFlags;
     }
 
     bool IsRightToLeft() const {
         return (Flags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
     }
@@ -2073,115 +2096,27 @@ public:
     bool DisableLigatures() const {
         return (Flags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
     }
 
     bool TextIs8Bit() const {
         return (Flags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
     }
 
-    int32_t Script() const {
-        return mScript;
-    }
-
-    int32_t AppUnitsPerDevUnit() const {
+    uint32_t GetAppUnitsPerDevUnit() const {
         return mAppUnitsPerDevUnit;
     }
 
-    void ResetAge() {
-        mAgeCounter = 0;
-    }
-    uint32_t IncrementAge() {
-        return ++mAgeCounter;
-    }
-
-    void SetSimpleGlyph(uint32_t aCharIndex, CompressedGlyph aGlyph) {
-        NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
-        NS_ASSERTION(mCharacterGlyphs, "mCharacterGlyphs pointer is null!");
-        mCharacterGlyphs[aCharIndex] = aGlyph;
-    }
-
-    void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
-                   const DetailedGlyph *aGlyphs);
-
-    void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
-
-    void SetIsSpace(uint32_t aIndex) {
-        mCharacterGlyphs[aIndex].SetIsSpace();
-    }
-
-    void SetIsLowSurrogate(uint32_t aIndex) {
-        SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
-        mCharacterGlyphs[aIndex].SetIsLowSurrogate();
-    }
-
-    bool FilterIfIgnorable(uint32_t aIndex);
-
-    const CompressedGlyph *GetCharacterGlyphs() const {
-        return &mCharacterGlyphs[0];
-    }
-
-    bool HasDetailedGlyphs() const {
-        return mDetailedGlyphs != nullptr;
+    uint32_t GetLength() const {
+        return mLength;
     }
 
-    // NOTE that this must not be called for a character offset that does
-    // not have any DetailedGlyph records; callers must have verified that
-    // mCharacterGlyphs[aCharIndex].GetGlyphCount() is greater than zero.
-    DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) const {
-        NS_ASSERTION(HasDetailedGlyphs() &&
-                     !mCharacterGlyphs[aCharIndex].IsSimpleGlyph() &&
-                     mCharacterGlyphs[aCharIndex].GetGlyphCount() > 0,
-                     "invalid use of GetDetailedGlyphs; check the caller!");
-        return mDetailedGlyphs->Get(aCharIndex);
-    }
-
-    void AdjustAdvancesForSyntheticBold(float aSynBoldOffset);
-
-    // this is a public static method in order to make it available
-    // for gfxTextRun to use directly on its own CompressedGlyph array,
-    // in addition to the use within ShapedWord
-    static void
-    SetupClusterBoundaries(CompressedGlyph *aGlyphs,
-                           const PRUnichar *aString, uint32_t aLength);
-
-private:
-    // so that gfxTextRun can share our DetailedGlyphStore class
-    friend class gfxTextRun;
-
-    // Construct storage for a ShapedWord, ready to receive glyph data
-    gfxShapedWord(const uint8_t *aText, uint32_t aLength,
-                  int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
-                  uint32_t aFlags)
-        : mLength(aLength)
-        , mFlags(aFlags | gfxTextRunFactory::TEXT_IS_8BIT)
-        , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
-        , mScript(aRunScript)
-        , mAgeCounter(0)
-    {
-        memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
-        uint8_t *text = reinterpret_cast<uint8_t*>(&mCharacterGlyphs[aLength]);
-        memcpy(text, aText, aLength * sizeof(uint8_t));
-    }
-
-    gfxShapedWord(const PRUnichar *aText, uint32_t aLength,
-                  int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
-                  uint32_t aFlags)
-        : mLength(aLength)
-        , mFlags(aFlags)
-        , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
-        , mScript(aRunScript)
-        , mAgeCounter(0)
-    {
-        memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
-        PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharacterGlyphs[aLength]);
-        memcpy(text, aText, aLength * sizeof(PRUnichar));
-        SetupClusterBoundaries(&mCharacterGlyphs[0], aText, aLength);
-    }
-
+    bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh);
+
+protected:
     // Allocate aCount DetailedGlyphs for the given index
     DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex,
                                           uint32_t aCount);
 
     // For characters whose glyph data does not fit the "simple" glyph criteria
     // in CompressedGlyph, we use a sorted array to store the association
     // between the source character offset and an index into an array 
     // DetailedGlyphs. The CompressedGlyph record includes a count of
@@ -2298,35 +2233,174 @@ private:
         // Records the most recently used index into mOffsetToIndex, so that
         // we can support sequential access more quickly than just doing
         // a binary search each time.
         nsTArray<DGRec>::index_type mLastUsed;
     };
 
     nsAutoPtr<DetailedGlyphStore>   mDetailedGlyphs;
 
-    // Number of PRUnichar characters and CompressedGlyph glyph records;
-    // note that gfx font code will never attempt to create a ShapedWord
-    // with a huge number of characters, so we could limit this to 16 bits
-    // to minimize memory usage for large numbers of cached words.
+    // Number of PRUnichar characters and CompressedGlyph glyph records
     uint32_t                        mLength;
 
+    // Shaping flags (direction, ligature-suppression)
     uint32_t                        mFlags;
 
-    int32_t                         mAppUnitsPerDevUnit;
-    int32_t                         mScript;
-
-    uint32_t                        mAgeCounter;
-
-    // The mCharacterGlyphs array is actually a variable-size member;
+    uint32_t                        mAppUnitsPerDevUnit;
+};
+
+/*
+ * gfxShapedWord: an individual (space-delimited) run of text shaped with a
+ * particular font, without regard to external context.
+ *
+ * The glyph data is copied into gfxTextRuns as needed from the cache of
+ * ShapedWords associated with each gfxFont instance.
+ */
+class gfxShapedWord : public gfxShapedText
+{
+public:
+    static const uint32_t kMaxLength = 0x7fff;
+
+    // Create a ShapedWord that can hold glyphs for aLength characters,
+    // with mCharacterGlyphs sized appropriately.
+    //
+    // Returns null on allocation failure (does NOT use infallible alloc)
+    // so caller must check for success.
+    //
+    // This does NOT perform shaping, so the returned word contains no
+    // glyph data; the caller must call gfxFont::ShapeText() with appropriate
+    // parameters to set up the glyphs.
+    static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
+                                 int32_t aRunScript,
+                                 int32_t aAppUnitsPerDevUnit,
+                                 uint32_t aFlags) {
+        NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
+
+        // Compute size needed including the mCharacterGlyphs array
+        // and a copy of the original text
+        uint32_t size =
+            offsetof(gfxShapedWord, mCharGlyphsStorage) +
+            aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
+        void *storage = moz_malloc(size);
+        if (!storage) {
+            return nullptr;
+        }
+
+        // Construct in the pre-allocated storage, using placement new
+        return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+                                           aAppUnitsPerDevUnit, aFlags);
+    }
+
+    static gfxShapedWord* Create(const PRUnichar *aText, uint32_t aLength,
+                                 int32_t aRunScript,
+                                 int32_t aAppUnitsPerDevUnit,
+                                 uint32_t aFlags) {
+        NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
+
+        // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
+        // then we convert the text to an 8-bit version and call the 8-bit
+        // Create function instead.
+        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+            nsAutoCString narrowText;
+            LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
+                                    narrowText);
+            return Create((const uint8_t*)(narrowText.BeginReading()),
+                          aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
+        }
+
+        uint32_t size =
+            offsetof(gfxShapedWord, mCharGlyphsStorage) +
+            aLength * (sizeof(CompressedGlyph) + sizeof(PRUnichar));
+        void *storage = moz_malloc(size);
+        if (!storage) {
+            return nullptr;
+        }
+
+        return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+                                           aAppUnitsPerDevUnit, aFlags);
+    }
+
+    // Override operator delete to properly free the object that was
+    // allocated via moz_malloc.
+    void operator delete(void* p) {
+        moz_free(p);
+    }
+
+    CompressedGlyph *GetCharacterGlyphs() {
+        return &mCharGlyphsStorage[0];
+    }
+
+    const uint8_t* Text8Bit() const {
+        NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
+        return reinterpret_cast<const uint8_t*>(mCharGlyphsStorage + GetLength());
+    }
+
+    const PRUnichar* TextUnicode() const {
+        NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
+        return reinterpret_cast<const PRUnichar*>(mCharGlyphsStorage + GetLength());
+    }
+
+    PRUnichar GetCharAt(uint32_t aOffset) const {
+        NS_ASSERTION(aOffset < GetLength(), "aOffset out of range");
+        return TextIs8Bit() ?
+            PRUnichar(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
+    }
+
+    int32_t Script() const {
+        return mScript;
+    }
+
+    void ResetAge() {
+        mAgeCounter = 0;
+    }
+    uint32_t IncrementAge() {
+        return ++mAgeCounter;
+    }
+
+private:
+    // so that gfxTextRun can share our DetailedGlyphStore class
+    friend class gfxTextRun;
+
+    // Construct storage for a ShapedWord, ready to receive glyph data
+    gfxShapedWord(const uint8_t *aText, uint32_t aLength,
+                  int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
+                  uint32_t aFlags)
+        : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT,
+                        aAppUnitsPerDevUnit)
+        , mScript(aRunScript)
+        , mAgeCounter(0)
+    {
+        memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
+        uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]);
+        memcpy(text, aText, aLength * sizeof(uint8_t));
+    }
+
+    gfxShapedWord(const PRUnichar *aText, uint32_t aLength,
+                  int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
+                  uint32_t aFlags)
+        : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit)
+        , mScript(aRunScript)
+        , mAgeCounter(0)
+    {
+        memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
+        PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharGlyphsStorage[aLength]);
+        memcpy(text, aText, aLength * sizeof(PRUnichar));
+        SetupClusterBoundaries(0, aText, aLength);
+    }
+
+    int32_t          mScript;
+
+    uint32_t         mAgeCounter;
+
+    // The mCharGlyphsStorage array is actually a variable-size member;
     // when the ShapedWord is created, its size will be increased as necessary
     // to allow the proper number of glyphs to be stored.
     // The original text, in either 8-bit or 16-bit form, will be stored
     // immediately following the CompressedGlyphs.
-    CompressedGlyph                 mCharacterGlyphs[1];
+    CompressedGlyph  mCharGlyphsStorage[1];
 };
 
 /**
  * gfxTextRun is an abstraction for drawing and measuring substrings of a run
  * of text. It stores runs of positioned glyph data, each run having a single
  * gfxFont. The glyphs are associated with a string of source text, and the
  * gfxTextRun APIs take parameters that are offsets into that source text.
  * 
@@ -2338,73 +2412,68 @@ private:
  * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does
  * not actually do anything to explicitly account for line breaks). Initially
  * there are no line breaks. The textrun can record line breaks before or after
  * any given cluster. (Line breaks specified inside clusters are ignored.)
  * 
  * It is important that zero-length substrings are handled correctly. This will
  * be on the test!
  */
-class THEBES_API gfxTextRun {
+class THEBES_API gfxTextRun : public gfxShapedText {
 public:
-    // we use the same glyph storage as gfxShapedWord, to facilitate copying
-    // glyph data from shaped words into text runs as needed
-    typedef gfxShapedWord::CompressedGlyph    CompressedGlyph;
-    typedef gfxShapedWord::DetailedGlyph      DetailedGlyph;
-    typedef gfxShapedWord::DetailedGlyphStore DetailedGlyphStore;
 
     // Override operator delete to properly free the object that was
     // allocated via moz_malloc.
     void operator delete(void* p) {
         moz_free(p);
     }
 
     virtual ~gfxTextRun();
 
     typedef gfxFont::RunMetrics Metrics;
 
     // Public textrun API for general use
 
     bool IsClusterStart(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].IsClusterStart();
     }
     bool IsLigatureGroupStart(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].IsLigatureGroupStart();
     }
     bool CanBreakLineBefore(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CanBreakBefore() ==
             CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
     }
     bool CanHyphenateBefore(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CanBreakBefore() ==
             CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
     }
 
     bool CharIsSpace(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CharIsSpace();
     }
     bool CharIsTab(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CharIsTab();
     }
     bool CharIsNewline(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CharIsNewline();
     }
     bool CharIsLowSurrogate(uint32_t aPos) {
-        NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CharIsLowSurrogate();
     }
 
-    uint32_t GetLength() { return mCharacterCount; }
+    uint32_t GetLength() { return mLength; }
 
     // All uint32_t aStart, uint32_t aLength ranges below are restricted to
     // grapheme cluster boundaries! All offsets are in terms of the string
     // passed into MakeTextRun.
     
     // All coordinates are in layout/app units
 
     /**
@@ -2650,33 +2719,31 @@ public:
      * Update the reference context.
      * XXX this is a hack. New text frame does not call this. Use only
      * temporarily for old text frame.
      */
     void SetContext(gfxContext *aContext) {}
 
     // Utility getters
 
-    bool IsRightToLeft() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0; }
     gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; }
     void *GetUserData() const { return mUserData; }
     void SetUserData(void *aUserData) { mUserData = aUserData; }
     uint32_t GetFlags() const { return mFlags; }
     void SetFlagBits(uint32_t aFlags) {
       NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
                    "Only user flags should be mutable");
       mFlags |= aFlags;
     }
     void ClearFlagBits(uint32_t aFlags) {
       NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
                    "Only user flags should be mutable");
       mFlags &= ~aFlags;
     }
     const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
-    uint32_t GetAppUnitsPerDevUnit() const { return mAppUnitsPerDevUnit; }
     gfxFontGroup *GetFontGroup() const { return mFontGroup; }
 
 
     // Call this, don't call "new gfxTextRun" directly. This does custom
     // allocation and initialization
     static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams,
                               uint32_t aLength, gfxFontGroup *aFontGroup,
                               uint32_t aFlags);
@@ -2742,30 +2809,21 @@ public:
      * TextRun.
      */
     nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
                          uint32_t aStartCharIndex, bool aForceNewRun);
     void ResetGlyphRuns() { mGlyphRuns.Clear(); }
     void SortGlyphRuns();
     void SanitizeGlyphRuns();
 
-    // Call the following glyph-setters during initialization or during reshaping
-    // only. It is OK to overwrite existing data for a character.
-    void SetSimpleGlyph(uint32_t aCharIndex, CompressedGlyph aGlyph) {
-        NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
-        mCharacterGlyphs[aCharIndex] = aGlyph;
+    CompressedGlyph* GetCharacterGlyphs() {
+        NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
+        return mCharacterGlyphs;
     }
-    /**
-     * Set the glyph data for a character. aGlyphs may be null if aGlyph is a
-     * simple glyph or has no associated glyphs. If non-null the data is copied,
-     * the caller retains ownership.
-     */
-    void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
-                   const DetailedGlyph *aGlyphs);
-    void SetMissingGlyph(uint32_t aCharIndex, uint32_t aUnicodeChar);
+
     void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex);
 
     // Set the glyph data for the given character index to the font's
     // space glyph, IF this can be done as a "simple" glyph record
     // (not requiring a DetailedGlyph entry). This avoids the need to call
     // the font shaper and go through the shaped-word cache for most spaces.
     //
     // The parameter aSpaceChar is the original character code for which
@@ -2815,43 +2873,27 @@ public:
     /**
      * Prefetch all the glyph extents needed to ensure that Measure calls
      * on this textrun not requesting tight boundingBoxes will succeed. Note
      * that some glyph extents might not be fetched due to OOM or other
      * errors.
      */
     void FetchGlyphExtents(gfxContext *aRefContext);
 
-    // API for access to the raw glyph data, needed by gfxFont::Draw
-    // and gfxFont::GetBoundingBox
-    CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
-
-    // NOTE that this must not be called for a character offset that does
-    // not have any DetailedGlyph records; callers must have verified that
-    // mCharacterGlyphs[aCharIndex].GetGlyphCount() is greater than zero.
-    DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) {
-        NS_ASSERTION(mDetailedGlyphs != nullptr &&
-                     !mCharacterGlyphs[aCharIndex].IsSimpleGlyph() &&
-                     mCharacterGlyphs[aCharIndex].GetGlyphCount() > 0,
-                     "invalid use of GetDetailedGlyphs; check the caller!");
-        return mDetailedGlyphs->Get(aCharIndex);
-    }
-
-    bool HasDetailedGlyphs() { return mDetailedGlyphs != nullptr; }
     uint32_t CountMissingGlyphs();
     const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) {
         *aNumGlyphRuns = mGlyphRuns.Length();
         return mGlyphRuns.Elements();
     }
     // Returns the index of the GlyphRun containing the given offset.
     // Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
     uint32_t FindFirstGlyphRunContaining(uint32_t aOffset);
 
     // Copy glyph data from a ShapedWord into this textrun.
-    void CopyGlyphDataFrom(const gfxShapedWord *aSource, uint32_t aStart);
+    void CopyGlyphDataFrom(gfxShapedWord *aSource, uint32_t aStart);
 
     // Copy glyph data for a range of characters from aSource to this
     // textrun.
     void CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
                            uint32_t aLength, uint32_t aDest);
 
     nsExpirationState *GetExpirationState() { return &mExpirationState; }
 
@@ -2906,21 +2948,19 @@ protected:
 
     /**
      * Helper for the Create() factory method to allocate the required
      * glyph storage for a textrun object with the basic size aSize,
      * plus room for aLength glyph records.
      */
     static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength);
 
-    // All our glyph data is in logical order, not visual.
-    // Space for mCharacterGlyphs is allocated fused with the textrun object,
-    // and then the constructor sets the pointer to the beginning of this
-    // storage area. Thus, this pointer must NOT be freed!
-    CompressedGlyph  *mCharacterGlyphs;
+    // Pointer to the array of CompressedGlyph records; must be initialized
+    // when the object is constructed.
+    CompressedGlyph *mCharacterGlyphs;
 
 private:
     // **** general helpers **** 
 
     // Allocate aCount DetailedGlyphs for the given index
     DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex, uint32_t aCount);
 
     // Get the total advance for a range of glyphs.
@@ -2970,29 +3010,24 @@ private:
 
     // **** drawing helper ****
     void DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
                     gfxFont::DrawMode aDrawMode, gfxPoint *aPt,
                     gfxTextObjectPaint *aObjectPaint, uint32_t aStart,
                     uint32_t aEnd, PropertyProvider *aProvider,
                     uint32_t aSpacingStart, uint32_t aSpacingEnd);
 
-    nsAutoPtr<DetailedGlyphStore>   mDetailedGlyphs;
-
     // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
     // for smaller size especially in the super-common one-glyphrun case
     nsAutoTArray<GlyphRun,1>        mGlyphRuns;
 
     void             *mUserData;
     gfxFontGroup     *mFontGroup; // addrefed
     gfxSkipChars      mSkipChars;
     nsExpirationState mExpirationState;
-    uint32_t          mAppUnitsPerDevUnit;
-    uint32_t          mFlags;
-    uint32_t          mCharacterCount;
 
     bool              mSkipDrawing; // true if the font group we used had a user font
                                     // download that's in progress, so we should hide text
                                     // until the download completes (or timeout fires)
 };
 
 class THEBES_API gfxFontGroup : public gfxTextRunFactory {
 public:
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -77,43 +77,47 @@ gfxGDIFont::CreatePlatformShaper()
 gfxFont*
 gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
 {
     return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
                           &mStyle, mNeedsBold, anAAOption);
 }
 
 static bool
-UseUniscribe(gfxShapedWord *aShapedWord,
-             const PRUnichar *aString)
+UseUniscribe(gfxShapedText *aShapedText,
+             const PRUnichar *aText,
+             uint32_t aLength)
 {
-    uint32_t flags = aShapedWord->Flags();
+    uint32_t flags = aShapedText->Flags();
     bool useGDI;
 
     bool isXP = (gfxWindowsPlatform::WindowsOSVersion() 
                        < gfxWindowsPlatform::kWindowsVista);
 
     // bug 561304 - Uniscribe bug produces bad positioning at certain
     // font sizes on XP, so default to GDI on XP using logic of 3.6
 
     useGDI = isXP &&
              (flags &
                (gfxTextRunFactory::TEXT_OPTIMIZE_SPEED | 
                 gfxTextRunFactory::TEXT_IS_RTL)
              ) == gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
 
     return !useGDI ||
-        ScriptIsComplex(aString, aShapedWord->Length(), SIC_COMPLEX) == S_OK;
+        ScriptIsComplex(aText, aLength, SIC_COMPLEX) == S_OK;
 }
 
 bool
-gfxGDIFont::ShapeWord(gfxContext *aContext,
-                      gfxShapedWord *aShapedWord,
-                      const PRUnichar *aString,
-                      bool aPreferPlatformShaping)
+gfxGDIFont::ShapeText(gfxContext      *aContext,
+                      const PRUnichar *aText,
+                      uint32_t         aOffset,
+                      uint32_t         aLength,
+                      int32_t          aScript,
+                      gfxShapedText   *aShapedText,
+                      bool             aPreferPlatformShaping)
 {
     if (!mMetrics) {
         Initialize();
     }
     if (!mIsValid) {
         NS_WARNING("invalid font! expect incorrect text rendering");
         return false;
     }
@@ -125,87 +129,87 @@ gfxGDIFont::ShapeWord(gfxContext *aConte
     // We must check that this succeeded, otherwise we risk cairo creating the
     // wrong kind of font internally as a fallback (bug 744480).
     if (!SetupCairoFont(aContext)) {
         return false;
     }
 
 #ifdef MOZ_GRAPHITE
     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
-        ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString);
+        ok = mGraphiteShaper->ShapeText(aContext, aText,
+                                        aOffset, aLength,
+                                        aScript, aShapedText);
     }
 #endif
 
     if (!ok && mHarfBuzzShaper) {
-        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
-            ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString);
+        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
+            ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                            aScript, aShapedText);
         }
     }
 
     if (!ok) {
         GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
         bool preferUniscribe =
             (!fe->IsTrueType() || fe->IsSymbolFont()) && !fe->mForceGDI;
 
-        if (preferUniscribe || UseUniscribe(aShapedWord, aString)) {
+        if (preferUniscribe || UseUniscribe(aShapedText, aText, aLength)) {
             // first try Uniscribe
             if (!mUniscribeShaper) {
                 mUniscribeShaper = new gfxUniscribeShaper(this);
             }
 
-            ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString);
-            if (ok) {
-                return true;
-            }
+            ok = mUniscribeShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                             aScript, aShapedText);
+            if (!ok) {
+                // fallback to GDI shaping
+                if (!mPlatformShaper) {
+                    CreatePlatformShaper();
+                }
 
-            // fallback to GDI shaping
-            if (!mPlatformShaper) {
-                CreatePlatformShaper();
+                ok = mPlatformShaper->ShapeText(aContext, aText, aOffset,
+                                                aLength, aScript, aShapedText);
             }
-
-            ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString);
         } else {
             // first use GDI
             if (!mPlatformShaper) {
                 CreatePlatformShaper();
             }
 
-            ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString);
-            if (ok) {
-                return true;
-            }
+            ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                            aScript, aShapedText);
+            if (!ok) {
+                // try Uniscribe if GDI failed
+                if (!mUniscribeShaper) {
+                    mUniscribeShaper = new gfxUniscribeShaper(this);
+                }
 
-            // try Uniscribe if GDI failed
-            if (!mUniscribeShaper) {
-                mUniscribeShaper = new gfxUniscribeShaper(this);
+                // use Uniscribe shaping
+                ok = mUniscribeShaper->ShapeText(aContext, aText,
+                                                 aOffset, aLength,
+                                                 aScript, aShapedText);
             }
-
-            // use Uniscribe shaping
-            ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString);
         }
 
 #if DEBUG
         if (!ok) {
             NS_ConvertUTF16toUTF8 name(GetName());
             char msg[256];
 
             sprintf(msg, 
                     "text shaping with both uniscribe and GDI failed for"
                     " font: %s",
                     name.get());
             NS_WARNING(msg);
         }
 #endif
     }
 
-    if (ok && IsSyntheticBold()) {
-        float synBoldOffset =
-                GetSyntheticBoldOffset() * CalcXScale(aContext);
-        aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
-    }
+    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
 
     return ok;
 }
 
 const gfxFont::Metrics&
 gfxGDIFont::GetMetrics()
 {
     if (!mMetrics) {
@@ -416,24 +420,21 @@ gfxGDIFont::Initialize()
         // does not say what the failure modes for GetTextExtentPoint32 are -
         // is it safe to assume it will fail iff the font has no '0'?
         if (GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size)) {
             mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
         } else {
             mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
         }
 
-        mSpaceGlyph = 0;
-        if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
-            WORD glyph;
-            DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
-                                         GGI_MARK_NONEXISTING_GLYPHS);
-            if (ret != GDI_ERROR && glyph != 0xFFFF) {
-                mSpaceGlyph = glyph;
-            }
+        WORD glyph;
+        DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
+                                     GGI_MARK_NONEXISTING_GLYPHS);
+        if (ret != GDI_ERROR && glyph != 0xFFFF) {
+            mSpaceGlyph = glyph;
         }
 
         SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
     }
 
     if (IsSyntheticBold()) {
         mMetrics->aveCharWidth += GetSyntheticBoldOffset();
         mMetrics->maxAdvance += GetSyntheticBoldOffset();
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -59,20 +59,23 @@ public:
                                      FontCacheSizes*   aSizes) const;
 
     virtual FontType GetType() const { return FONT_TYPE_GDI; }
 
 protected:
     virtual void CreatePlatformShaper();
 
     /* override to check for uniscribe failure and fall back to GDI */
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString,
-                           bool aPreferPlatformShaping = false);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText,
+                           bool             aPreferPlatformShaping);
 
     void Initialize(); // creates metrics and Cairo fonts
 
     // Fill the given LOGFONT record according to our style, but don't adjust
     // the lfItalic field if we're going to use a cairo transform for fake
     // italics.
     void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
 
--- a/gfx/thebes/gfxGDIShaper.cpp
+++ b/gfx/thebes/gfxGDIShaper.cpp
@@ -9,31 +9,34 @@
 
 /**********************************************************************
  *
  * class gfxGDIShaper
  *
  **********************************************************************/
 
 bool
-gfxGDIShaper::ShapeWord(gfxContext *aContext,
-                        gfxShapedWord *aShapedWord,
-                        const PRUnichar *aString)
+gfxGDIShaper::ShapeText(gfxContext      *aContext,
+                        const PRUnichar *aText,
+                        uint32_t         aOffset,
+                        uint32_t         aLength,
+                        int32_t          aScript,
+                        gfxShapedText   *aShapedText)
 {
     DCFromContext dc(aContext);
     AutoSelectFont selectFont(dc, static_cast<gfxGDIFont*>(mFont)->GetHFONT());
 
-    uint32_t length = aShapedWord->Length();
+    uint32_t length = aLength;
     nsAutoTArray<WORD,500> glyphArray;
     if (!glyphArray.SetLength(length)) {
         return false;
     }
     WORD *glyphs = glyphArray.Elements();
 
-    DWORD ret = ::GetGlyphIndicesW(dc, aString, length,
+    DWORD ret = ::GetGlyphIndicesW(dc, aText, length,
                                    glyphs, GGI_MARK_NONEXISTING_GLYPHS);
     if (ret == GDI_ERROR) {
         return false;
     }
 
     for (int k = 0; k < length; k++) {
         if (glyphs[k] == 0xFFFF)
             return false;
@@ -52,41 +55,42 @@ gfxGDIShaper::ShapeWord(gfxContext *aCon
                                            NULL,
                                            partialWidthArray.Elements(),
                                            &size);
     if (!success) {
         return false;
     }
 
     gfxTextRun::CompressedGlyph g;
+    gfxTextRun::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs();
     uint32_t i;
     int32_t lastWidth = 0;
-    uint32_t appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit();
+    uint32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
     for (i = 0; i < length; ++i) {
-        uint32_t offset = i;
+        uint32_t offset = aOffset + i;
         int32_t advancePixels = partialWidthArray[i] - lastWidth;
         lastWidth = partialWidthArray[i];
         int32_t advanceAppUnits = advancePixels * appUnitsPerDevPixel;
         WCHAR glyph = glyphs[i];
-        NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aShapedWord->GetCharAt(offset)),
+        NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aText[i]),
                      "Invalid character detected!");
-        bool atClusterStart = aShapedWord->IsClusterStart(offset);
+        bool atClusterStart = charGlyphs[offset].IsClusterStart();
         if (advanceAppUnits >= 0 &&
             gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
             gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
             atClusterStart)
         {
-            aShapedWord->SetSimpleGlyph(offset,
-                                        g.SetSimpleGlyph(advanceAppUnits, glyph));
+            charGlyphs[offset].SetSimpleGlyph(advanceAppUnits, glyph);
         } else {
-            gfxShapedWord::DetailedGlyph details;
+            gfxShapedText::DetailedGlyph details;
             details.mGlyphID = glyph;
             details.mAdvance = advanceAppUnits;
             details.mXOffset = 0;
             details.mYOffset = 0;
-            aShapedWord->SetGlyphs(offset,
+            aShapedText->SetGlyphs(offset,
                                    g.SetComplex(atClusterStart, true, 1),
                                    &details);
         }
     }
 
     return true;
 }
--- a/gfx/thebes/gfxGDIShaper.h
+++ b/gfx/thebes/gfxGDIShaper.h
@@ -17,14 +17,17 @@ public:
         MOZ_COUNT_CTOR(gfxGDIShaper);
     }
 
     virtual ~gfxGDIShaper()
     {
         MOZ_COUNT_DTOR(gfxGDIShaper);
     }
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
 };
 
 #endif /* GFX_GDISHAPER_H */
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -133,19 +133,22 @@ AddFeature(const uint32_t& aTag, uint32_
     const gr_feature_ref* fref = gr_face_find_fref(f->mFace, aTag);
     if (fref) {
         gr_fref_set_feature_value(fref, aValue, f->mFeatures);
     }
     return PL_DHASH_NEXT;
 }
 
 bool
-gfxGraphiteShaper::ShapeWord(gfxContext      *aContext,
-                             gfxShapedWord   *aShapedWord,
-                             const PRUnichar *aText)
+gfxGraphiteShaper::ShapeText(gfxContext      *aContext,
+                             const PRUnichar *aText,
+                             uint32_t         aOffset,
+                             uint32_t         aLength,
+                             int32_t          aScript,
+                             gfxShapedText   *aShapedText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     if (!mFont->SetupCairoFont(aContext)) {
         return false;
     }
 
     mCallbackData.mContext = aContext;
 
@@ -178,33 +181,34 @@ gfxGraphiteShaper::ShapeWord(gfxContext 
         style->language->ToUTF8String(langString);
         grLang = GetGraphiteTagForLang(langString);
     }
     gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
 
     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
 
     if (MergeFontFeatures(style->featureSettings, entry->mFeatureSettings,
-                          aShapedWord->DisableLigatures(), mergedFeatures)) {
+                          aShapedText->DisableLigatures(), mergedFeatures)) {
         // enumerate result and insert into Graphite feature list
         GrFontFeatures f = {mGrFace, grFeatures};
         mergedFeatures.Enumerate(AddFeature, &f);
     }
 
     gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
-                                  gr_utf16, aText, aShapedWord->Length(),
-                                  aShapedWord->IsRightToLeft());
+                                  gr_utf16, aText, aLength,
+                                  aShapedText->IsRightToLeft());
 
     gr_featureval_destroy(grFeatures);
 
     if (!seg) {
         return false;
     }
 
-    nsresult rv = SetGlyphsFromSegment(aShapedWord, seg);
+    nsresult rv = SetGlyphsFromSegment(aShapedText, aOffset, aLength,
+                                       aText, seg);
 
     gr_seg_destroy(seg);
 
     return NS_SUCCEEDED(rv);
 }
 
 #define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
                             // for short (typical) runs up to this length
@@ -213,31 +217,34 @@ struct Cluster {
     uint32_t baseChar;
     uint32_t baseGlyph;
     uint32_t nChars;
     uint32_t nGlyphs;
     Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
 };
 
 nsresult
-gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
-                                        gr_segment *aSegment)
+gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText   *aShapedText,
+                                        uint32_t         aOffset,
+                                        uint32_t         aLength,
+                                        const PRUnichar *aText,
+                                        gr_segment      *aSegment)
 {
-    int32_t dev2appUnits = aShapedWord->AppUnitsPerDevUnit();
-    bool rtl = aShapedWord->IsRightToLeft();
+    int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
+    bool rtl = aShapedText->IsRightToLeft();
 
     uint32_t glyphCount = gr_seg_n_slots(aSegment);
 
     // identify clusters; graphite may have reordered/expanded/ligated glyphs.
     nsAutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
     nsAutoTArray<uint16_t,SMALL_GLYPH_RUN> gids;
     nsAutoTArray<float,SMALL_GLYPH_RUN> xLocs;
     nsAutoTArray<float,SMALL_GLYPH_RUN> yLocs;
 
-    if (!clusters.SetLength(aShapedWord->Length()) ||
+    if (!clusters.SetLength(aLength) ||
         !gids.SetLength(glyphCount) ||
         !xLocs.SetLength(glyphCount) ||
         !yLocs.SetLength(glyphCount))
     {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // walk through the glyph slots and check which original character
@@ -263,35 +270,38 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
             --cIndex;
         }
 
         // if there's a gap between the current cluster's base character and
         // this glyph's, extend the cluster to include the intervening chars
         if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
             before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
         {
-            NS_ASSERTION(cIndex < aShapedWord->Length() - 1, "cIndex at end of word");
+            NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word");
             Cluster& c = clusters[cIndex + 1];
             c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
             c.nChars = before - c.baseChar;
             c.baseGlyph = gIndex;
             c.nGlyphs = 0;
             ++cIndex;
         }
 
         // increment cluster's glyph count to include current slot
-        NS_ASSERTION(cIndex < aShapedWord->Length(), "cIndex beyond word length");
+        NS_ASSERTION(cIndex < aLength, "cIndex beyond word length");
         ++clusters[cIndex].nGlyphs;
 
         // extend cluster if necessary to reach the glyph's "after" index
         if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
             clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
         }
     }
 
+    gfxShapedText::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs() + aOffset;
+
     // now put glyphs into the textrun, one cluster at a time
     for (uint32_t i = 0; i <= cIndex; ++i) {
         const Cluster& c = clusters[i];
 
         float adv; // total advance of the cluster
         if (rtl) {
             if (i == 0) {
                 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
@@ -304,66 +314,62 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
             } else {
                 adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
             }
         }
 
         // Check for default-ignorable char that didn't get filtered, combined,
         // etc by the shaping process, and skip it.
         uint32_t offs = gr_cinfo_base(gr_seg_cinfo(aSegment, c.baseChar));
-        NS_ASSERTION(offs >= c.baseChar && offs < aShapedWord->Length(),
+        NS_ASSERTION(offs >= c.baseChar && offs < aLength,
                      "unexpected offset");
         if (c.nGlyphs == 1 && c.nChars == 1 &&
-            aShapedWord->FilterIfIgnorable(offs))
-        {
+            aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
             continue;
         }
 
         uint32_t appAdvance = adv * dev2appUnits;
         if (c.nGlyphs == 1 &&
-            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
-            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
+            gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
+            gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
             yLocs[c.baseGlyph] == 0)
         {
-            gfxShapedWord::CompressedGlyph g;
-            aShapedWord->SetSimpleGlyph(offs,
-                                        g.SetSimpleGlyph(appAdvance,
-                                                         gids[c.baseGlyph]));
+            charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
         } else {
             // not a one-to-one mapping with simple metrics: use DetailedGlyph
-            nsAutoTArray<gfxShapedWord::DetailedGlyph,8> details;
+            nsAutoTArray<gfxShapedText::DetailedGlyph,8> details;
             float clusterLoc;
             for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
-                gfxShapedWord::DetailedGlyph* d = details.AppendElement();
+                gfxShapedText::DetailedGlyph* d = details.AppendElement();
                 d->mGlyphID = gids[j];
                 d->mYOffset = -yLocs[j] * dev2appUnits;
                 if (j == c.baseGlyph) {
                     d->mXOffset = 0;
                     d->mAdvance = appAdvance;
                     clusterLoc = xLocs[j];
                 } else {
                     d->mXOffset = dev2appUnits *
                         (rtl ? (xLocs[j] - clusterLoc) :
                                (xLocs[j] - clusterLoc - adv));
                     d->mAdvance = 0;
                 }
             }
-            gfxShapedWord::CompressedGlyph g;
-            g.SetComplex(aShapedWord->IsClusterStart(offs),
+            gfxShapedText::CompressedGlyph g;
+            g.SetComplex(charGlyphs[offs].IsClusterStart(),
                          true, details.Length());
-            aShapedWord->SetGlyphs(offs, g, details.Elements());
+            aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
         }
 
         for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
             offs = gr_cinfo_base(gr_seg_cinfo(aSegment, j));
-            NS_ASSERTION(offs >= j && offs < aShapedWord->Length(),
+            NS_ASSERTION(offs >= j && offs < aLength,
                          "unexpected offset");
-            gfxShapedWord::CompressedGlyph g;
-            g.SetComplex(aShapedWord->IsClusterStart(offs), false, 0);
-            aShapedWord->SetGlyphs(offs, g, nullptr);
+            gfxShapedText::CompressedGlyph &g = charGlyphs[offs];
+            NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+            g.SetComplex(g.IsClusterStart(), false, 0);
         }
     }
 
     return NS_OK;
 }
 
 // for language tag validation - include list of tags from the IANA registry
 #include "gfxLanguageTagList.cpp"
--- a/gfx/thebes/gfxGraphiteShaper.h
+++ b/gfx/thebes/gfxGraphiteShaper.h
@@ -15,19 +15,22 @@ struct gr_face;
 struct gr_font;
 struct gr_segment;
 
 class gfxGraphiteShaper : public gfxFontShaper {
 public:
     gfxGraphiteShaper(gfxFont *aFont);
     virtual ~gfxGraphiteShaper();
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aText);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
 
     const void* GetTable(uint32_t aTag, size_t *aLength);
 
     static void Shutdown();
 
     struct CallbackData {
         gfxFont           *mFont;
         gfxGraphiteShaper *mShaper;
@@ -36,18 +39,21 @@ public:
 
     struct TableRec {
         hb_blob_t  *mBlob;
         const void *mData;
         uint32_t    mLength;
     };
 
 protected:
-    nsresult SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
-                                  gr_segment *aSegment);
+    nsresult SetGlyphsFromSegment(gfxShapedText   *aShapedText,
+                                  uint32_t         aOffset,
+                                  uint32_t         aLength,
+                                  const PRUnichar *aText,
+                                  gr_segment      *aSegment);
 
     gr_face *mGrFace;
     gr_font *mGrFont;
 
     CallbackData mCallbackData;
 
     nsDataHashtable<nsUint32HashKey,TableRec> mTables;
 
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -111,53 +111,65 @@ struct FontCallbackData {
 };
 
 #define UNICODE_BMP_LIMIT 0x10000
 
 hb_codepoint_t
 gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode,
                             hb_codepoint_t variation_selector) const
 {
+    hb_codepoint_t gid;
+
     if (mUseFontGetGlyph) {
-        return mFont->GetGlyph(unicode, variation_selector);
+        gid = mFont->GetGlyph(unicode, variation_selector);
+    } else {
+        // we only instantiate a harfbuzz shaper if there's a cmap available
+        NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
+                     "we cannot be using this font!");
+
+        NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
+                     "cmap data not correctly set up, expect disaster");
+
+        const uint8_t* data =
+            (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
+
+        switch (mCmapFormat) {
+        case 4:
+            gid = unicode < UNICODE_BMP_LIMIT ?
+                gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
+                                                    unicode) : 0;
+            break;
+        case 12:
+            gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset,
+                                                       unicode);
+            break;
+        default:
+            NS_WARNING("unsupported cmap format, glyphs will be missing");
+            gid = 0;
+            break;
+        }
+
+        if (gid && variation_selector && mUVSTableOffset) {
+            hb_codepoint_t varGID =
+                gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
+                                                    unicode,
+                                                    variation_selector);
+            if (varGID) {
+                gid = varGID;
+            }
+            // else the variation sequence was not supported, use default
+            // mapping of the character code alone
+        }
     }
 
-    // we only instantiate a harfbuzz shaper if there's a cmap available
-    NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
-                 "we cannot be using this font!");
-
-    NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
-                 "cmap data not correctly set up, expect disaster");
-
-    const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
-
-    hb_codepoint_t gid;
-    switch (mCmapFormat) {
-    case 4:
-        gid = unicode < UNICODE_BMP_LIMIT ?
-            gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset, unicode) : 0;
-        break;
-    case 12:
-        gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset, unicode);
-        break;
-    default:
-        NS_WARNING("unsupported cmap format, glyphs will be missing");
-        gid = 0;
-        break;
-    }
-
-    if (gid && variation_selector && mUVSTableOffset) {
-        hb_codepoint_t varGID =
-            gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
-                                                unicode, variation_selector);
-        if (varGID) {
-            gid = varGID;
+    if (!gid) {
+        // if there's no glyph for &nbsp;, just use the space glyph instead
+        if (unicode == 0xA0) {
+            gid = mFont->GetSpaceGlyph();
         }
-        // else the variation sequence was not supported, use default mapping
-        // of the character code alone
     }
 
     return gid;
 }
 
 static hb_bool_t
 HBGetGlyph(hb_font_t *font, void *font_data,
            hb_codepoint_t unicode, hb_codepoint_t variation_selector,
@@ -825,19 +837,22 @@ AddFeature(const uint32_t& aTag, uint32_
 /*
  * gfxFontShaper override to initialize the text run using HarfBuzz
  */
 
 static hb_font_funcs_t * sHBFontFuncs = nullptr;
 static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
 
 bool
-gfxHarfBuzzShaper::ShapeWord(gfxContext      *aContext,
-                             gfxShapedWord   *aShapedWord,
-                             const PRUnichar *aText)
+gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
+                             const PRUnichar *aText,
+                             uint32_t         aOffset,
+                             uint32_t         aLength,
+                             int32_t          aScript,
+                             gfxShapedText   *aShapedText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     if (!mFont->SetupCairoFont(aContext)) {
         return false;
     }
 
     if (!mHBFace) {
 
@@ -955,60 +970,60 @@ gfxHarfBuzzShaper::ShapeWord(gfxContext 
 
     gfxFontEntry *entry = mFont->GetFontEntry();
     const gfxFontStyle *style = mFont->GetStyle();
 
     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
 
     if (MergeFontFeatures(style->featureSettings,
                       mFont->GetFontEntry()->mFeatureSettings,
-                      aShapedWord->DisableLigatures(), mergedFeatures)) {
+                      aShapedText->DisableLigatures(), mergedFeatures)) {
         // enumerate result and insert into hb_feature array
         mergedFeatures.Enumerate(AddFeature, &features);
     }
 
-    bool isRightToLeft = aShapedWord->IsRightToLeft();
+    bool isRightToLeft = aShapedText->IsRightToLeft();
     hb_buffer_t *buffer = hb_buffer_create();
     hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
     hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
                                                     HB_DIRECTION_LTR);
     // For unresolved "common" or "inherited" runs, default to Latin for now.
     // (Should we somehow use the language or locale to try and infer
     // a better default?)
-    int32_t scriptCode = aShapedWord->Script();
-    hb_script_t scriptTag = (scriptCode <= MOZ_SCRIPT_INHERITED) ?
+    hb_script_t scriptTag = (aScript <= MOZ_SCRIPT_INHERITED) ?
         HB_SCRIPT_LATIN :
-        hb_script_t(GetScriptTagForCode(scriptCode));
+        hb_script_t(GetScriptTagForCode(aScript));
     hb_buffer_set_script(buffer, scriptTag);
 
     hb_language_t language;
     if (style->languageOverride) {
         language = hb_ot_tag_to_language(style->languageOverride);
     } else if (entry->mLanguageOverride) {
         language = hb_ot_tag_to_language(entry->mLanguageOverride);
     } else {
         nsCString langString;
         style->language->ToUTF8String(langString);
         language =
             hb_language_from_string(langString.get(), langString.Length());
     }
     hb_buffer_set_language(buffer, language);
 
-    uint32_t length = aShapedWord->Length();
+    uint32_t length = aLength;
     hb_buffer_add_utf16(buffer,
                         reinterpret_cast<const uint16_t*>(aText),
                         length, 0, length);
 
     hb_shape(font, buffer, features.Elements(), features.Length());
 
     if (isRightToLeft) {
         hb_buffer_reverse(buffer);
     }
 
-    nsresult rv = SetGlyphsFromRun(aContext, aShapedWord, buffer);
+    nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
+                                   aText, buffer);
 
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
     hb_buffer_destroy(buffer);
     hb_font_destroy(font);
 
     return NS_SUCCEEDED(rv);
 }
 
@@ -1086,29 +1101,32 @@ GetRoundOffsetsToPixels(gfxContext *aCon
     return;
 }
 
 #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
                             // will fit without requiring separate allocation
                             // for charToGlyphArray
 
 nsresult
-gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
-                                    gfxShapedWord *aShapedWord,
-                                    hb_buffer_t *aBuffer)
+gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext      *aContext,
+                                    gfxShapedText   *aShapedText,
+                                    uint32_t         aOffset,
+                                    uint32_t         aLength,
+                                    const PRUnichar *aText,
+                                    hb_buffer_t     *aBuffer)
 {
     uint32_t numGlyphs;
     const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
     if (numGlyphs == 0) {
         return NS_OK;
     }
 
     nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
 
-    uint32_t wordLength = aShapedWord->Length();
+    uint32_t wordLength = aLength;
     static const int32_t NO_GLYPH = -1;
     nsAutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
     if (!charToGlyphArray.SetLength(wordLength)) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     int32_t *charToGlyph = charToGlyphArray.Elements();
     for (uint32_t offset = 0; offset < wordLength; ++offset) {
@@ -1124,21 +1142,23 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
 
     int32_t glyphStart = 0; // looking for a clump that starts at this glyph
     int32_t charStart = 0; // and this char index within the range of the run
 
     bool roundX;
     bool roundY;
     GetRoundOffsetsToPixels(aContext, &roundX, &roundY);
 
-    int32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
+    int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
+    gfxShapedText::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs() + aOffset;
 
     // factor to convert 16.16 fixed-point pixels to app units
     // (only used if not rounding)
-    double hb2appUnits = FixedToFloat(aShapedWord->AppUnitsPerDevUnit());
+    double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
 
     // Residual from rounding of previous advance, for use in rounding the
     // subsequent offset or advance appropriately.  16.16 fixed-point
     //
     // When rounding, the goal is to make the distance between glyphs and
     // their base glyph equal to the integral number of pixels closest to that
     // suggested by that shaper.
     // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
@@ -1231,17 +1251,18 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
 
         // Now we're ready to set the glyph info in the textRun
         int32_t glyphsInClump = glyphEnd - glyphStart;
 
         // Check for default-ignorable char that didn't get filtered, combined,
         // etc by the shaping process, and remove from the run.
         // (This may be done within harfbuzz eventually.)
         if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
-            aShapedWord->FilterIfIgnorable(baseCharIndex)) {
+            aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
+                                           aText[baseCharIndex])) {
             glyphStart = glyphEnd;
             charStart = charEnd;
             continue;
         }
 
         hb_position_t x_offset = posInfo[glyphStart].x_offset;
         hb_position_t x_advance = posInfo[glyphStart].x_advance;
         nscoord xOffset, advance;
@@ -1256,24 +1277,22 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
         } else {
             xOffset = floor(hb2appUnits * x_offset + 0.5);
             advance = floor(hb2appUnits * x_advance + 0.5);
         }
         // Check if it's a simple one-to-one mapping
         if (glyphsInClump == 1 &&
             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
-            aShapedWord->IsClusterStart(baseCharIndex) &&
+            charGlyphs[baseCharIndex].IsClusterStart() &&
             xOffset == 0 &&
             posInfo[glyphStart].y_offset == 0 && yPos == 0)
         {
-            gfxTextRun::CompressedGlyph g;
-            aShapedWord->SetSimpleGlyph(baseCharIndex,
-                                     g.SetSimpleGlyph(advance,
-                                         ginfo[glyphStart].codepoint));
+            charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
+                                                     ginfo[glyphStart].codepoint);
         } else {
             // collect all glyphs in a list to be assigned to the first char;
             // there must be at least one in the clump, and we already measured
             // its advance, hence the placement of the loop-exit test and the
             // measurement of the next glyph
             while (1) {
                 gfxTextRun::DetailedGlyph* details =
                     detailedGlyphs.AppendElement();
@@ -1312,33 +1331,32 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
                     x_residual = x_advance - FloatToFixed(intAdvance);
                     advance = appUnitsPerDevUnit * intAdvance;
                 } else {
                     xOffset = floor(hb2appUnits * x_offset + 0.5);
                     advance = floor(hb2appUnits * x_advance + 0.5);
                 }
             }
 
-            gfxTextRun::CompressedGlyph g;
-            g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
+            gfxShapedText::CompressedGlyph g;
+            g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
                          true, detailedGlyphs.Length());
-            aShapedWord->SetGlyphs(baseCharIndex,
-                                g, detailedGlyphs.Elements());
+            aShapedText->SetGlyphs(aOffset + baseCharIndex,
+                                   g, detailedGlyphs.Elements());
 
             detailedGlyphs.Clear();
         }
 
         // the rest of the chars in the group are ligature continuations,
         // no associated glyphs
         while (++baseCharIndex != endCharIndex &&
                baseCharIndex < int32_t(wordLength)) {
-            gfxTextRun::CompressedGlyph g;
-            g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
-                         false, 0);
-            aShapedWord->SetGlyphs(baseCharIndex, g, nullptr);
+            gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
+            NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+            g.SetComplex(g.IsClusterStart(), false, 0);
         }
 
         glyphStart = glyphEnd;
         charStart = charEnd;
     }
 
     return NS_OK;
 }
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -13,38 +13,44 @@
 
 #include "harfbuzz/hb.h"
 
 class gfxHarfBuzzShaper : public gfxFontShaper {
 public:
     gfxHarfBuzzShaper(gfxFont *aFont);
     virtual ~gfxHarfBuzzShaper();
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aText);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
 
     // get a given font table in harfbuzz blob form
     hb_blob_t * GetFontTable(hb_tag_t aTag) const;
 
     // map unicode character to glyph ID
     hb_codepoint_t GetGlyph(hb_codepoint_t unicode,
                             hb_codepoint_t variation_selector) const;
 
     // get harfbuzz glyph advance, in font design units
     hb_position_t GetGlyphHAdvance(gfxContext *aContext,
                                    hb_codepoint_t glyph) const;
 
     hb_position_t GetHKerning(uint16_t aFirstGlyph,
                               uint16_t aSecondGlyph) const;
 
 protected:
-    nsresult SetGlyphsFromRun(gfxContext *aContext,
-                              gfxShapedWord *aShapedWord,
-                              hb_buffer_t *aBuffer);
+    nsresult SetGlyphsFromRun(gfxContext      *aContext,
+                              gfxShapedText   *aShapedText,
+                              uint32_t         aOffset,
+                              uint32_t         aLength,
+                              const PRUnichar *aText,
+                              hb_buffer_t     *aBuffer);
 
     // retrieve glyph positions, applying advance adjustments and attachments
     // returns results in appUnits
     nscoord GetGlyphPositions(gfxContext *aContext,
                               hb_buffer_t *aBuffer,
                               nsTArray<nsPoint>& aPositions,
                               uint32_t aAppUnitsPerDevUnit);
 
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -115,29 +115,33 @@ gfxMacFont::~gfxMacFont()
         cairo_scaled_font_destroy(mScaledFont);
     }
     if (mFontFace) {
         cairo_font_face_destroy(mFontFace);
     }
 }
 
 bool
-gfxMacFont::ShapeWord(gfxContext *aContext,
-                      gfxShapedWord *aShapedWord,
+gfxMacFont::ShapeText(gfxContext      *aContext,
                       const PRUnichar *aText,
-                      bool aPreferPlatformShaping)
+                      uint32_t         aOffset,
+                      uint32_t         aLength,
+                      int32_t          aScript,
+                      gfxShapedText   *aShapedText,
+                      bool             aPreferPlatformShaping)
 {
     if (!mIsValid) {
         NS_WARNING("invalid font! expect incorrect text rendering");
         return false;
     }
 
     bool requiresAAT =
         static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout();
-    return gfxFont::ShapeWord(aContext, aShapedWord, aText, requiresAAT);
+    return gfxFont::ShapeText(aContext, aText, aOffset, aLength,
+                              aScript, aShapedText, requiresAAT);
 }
 
 void
 gfxMacFont::CreatePlatformShaper()
 {
     mPlatformShaper = new gfxCoreTextShaper(this);
 }
 
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -52,20 +52,23 @@ public:
                                      FontCacheSizes*   aSizes) const;
 
     virtual FontType GetType() const { return FONT_TYPE_MAC; }
 
 protected:
     virtual void CreatePlatformShaper();
 
     // override to prefer CoreText shaping with fonts that depend on AAT
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
+    virtual bool ShapeText(gfxContext      *aContext,
                            const PRUnichar *aText,
-                           bool aPreferPlatformShaping = false);
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText,
+                           bool             aPreferPlatformShaping = false);
 
     void InitMetrics();
     void InitMetricsFromPlatform();
 
     // Get width and glyph ID for a character; uses aConvFactor
     // to convert font units as returned by CG to actual dimensions
     gfxFloat GetCharWidth(CFDataRef aCmap, PRUnichar aUniChar,
                           uint32_t *aGlyphID, gfxFloat aConvFactor);
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -78,17 +78,17 @@ struct gfxPangoFcFont;
 int moz_pango_units_from_double(double d) {
     return NS_lround(d * FLOAT_PANGO_SCALE);
 }
 
 static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
 
 static cairo_scaled_font_t *
 CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
-static void SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
+static void SetMissingGlyphs(gfxShapedText *aShapedText, const gchar *aUTF8,
                              uint32_t aUTF8Length, uint32_t *aUTF16Offset,
                              gfxFont *aFont);
 
 static PangoFontMap *gPangoFontMap;
 static PangoFontMap *GetPangoFontMap();
 static bool gUseFontMapProperty;
 
 static FT_Library gFTLibrary;
@@ -281,17 +281,17 @@ gfxFcFontEntry::ShouldUseHarfBuzz(int32_
         FcPatternGetString(mPatterns[0],
                            FC_CAPABILITY, 0, &capability) == FcResultNoMatch ||
         !FcStrStr(capability, gfxFontconfigUtils::ToFcChar8("ttable:Silf")))
     {
         mSkipGraphiteCheck = true;
         return true;
     }
 
-    // Mimicing gfxHarfBuzzShaper::ShapeWord
+    // Mimicing gfxHarfBuzzShaper::ShapeText
     hb_script_t script = (aRunScript <= MOZ_SCRIPT_INHERITED) ?
         HB_SCRIPT_LATIN :
         hb_script_t(GetScriptTagForCode(aRunScript));
 
     // Prefer HarfBuzz if the font also has support for OpenType shaping of
     // this script.
     const FcChar8 otCapTemplate[] = "otlayout:XXXX";
     FcChar8 otCap[NS_ARRAY_LENGTH(otCapTemplate)];
@@ -799,23 +799,29 @@ public:
         if (name) {
             return NS_ConvertUTF8toUTF16(name);
         } else {
             return GetFontEntry()->FamilyName();
         }
     }
 
 protected:
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString,
-                           bool aPreferPlatformShaping);
-
-    bool InitGlyphRunWithPango(gfxShapedWord *aTextRun,
-                               const PRUnichar *aString);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText,
+                           bool             aPreferPlatformShaping);
+
+    bool InitGlyphRunWithPango(const PRUnichar *aString,
+                               uint32_t         aOffset,
+                               uint32_t         aLength,
+                               int32_t          aScript,
+                               gfxShapedText   *aShapedText);
 
 private:
     gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
               const gfxFontStyle *aFontStyle);
 
     void MakePangoFont();
 
     PangoFont *mPangoFont;
@@ -2242,55 +2248,65 @@ gfxFcFont::~gfxFcFont()
     cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, NULL, NULL);
     if (mPangoFont) {
         g_object_remove_toggle_ref(G_OBJECT(mPangoFont),
                                    PangoFontToggleNotify, this);
     }
 }
 
 bool
-gfxFcFont::ShapeWord(gfxContext *aContext,
-                     gfxShapedWord *aShapedWord,
-                     const PRUnichar *aString,
-                     bool aPreferPlatformShaping)
+gfxFcFont::ShapeText(gfxContext      *aContext,
+                     const PRUnichar *aText,
+                     uint32_t         aOffset,
+                     uint32_t         aLength,
+                     int32_t          aScript,
+                     gfxShapedText   *aShapedText,
+                     bool             aPreferPlatformShaping)
 {
     gfxFcFontEntry *fontEntry = static_cast<gfxFcFontEntry*>(GetFontEntry());
 
+    bool ok = false;
+
 #ifdef MOZ_GRAPHITE
     if (FontCanSupportGraphite()) {
         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
             if (!mGraphiteShaper) {
                 mGraphiteShaper = new gfxGraphiteShaper(this);
             }
-            if (mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString)) {
-                return true;
-            }
+            ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                            aScript, aShapedText);
         }
     }
 #endif
 
-    if (fontEntry->ShouldUseHarfBuzz(aShapedWord->Script())) {
+    if (!ok && fontEntry->ShouldUseHarfBuzz(aScript)) {
         if (!mHarfBuzzShaper) {
             gfxFT2LockedFace face(this);
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
             // Used by gfxHarfBuzzShaper, currently only for kerning
             mFUnitsConvFactor = face.XScale();
         }
-        if (mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString)) {
-            return true;
+        ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                        aScript, aShapedText);
+        if (!ok) {
+            // Wrong font type for HarfBuzz
+            fontEntry->SkipHarfBuzz();
+            mHarfBuzzShaper = nullptr;
         }
-
-        // Wrong font type for HarfBuzz
-        fontEntry->SkipHarfBuzz();
-        mHarfBuzzShaper = nullptr;
     }
 
-    bool ok = InitGlyphRunWithPango(aShapedWord, aString);
+    if (!ok) {
+        ok = InitGlyphRunWithPango(aText, aOffset, aLength, aScript,
+                                   aShapedText);
+    }
 
     NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
+
+    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
+
     return ok;
 }
 
 /* static */ void
 gfxPangoFontGroup::Shutdown()
 {
     if (gPangoFontMap) {
         g_object_unref(gPangoFontMap);
@@ -2781,78 +2797,78 @@ ConvertPangoToAppUnits(int32_t aCoordina
 /**
  * Given a run of Pango glyphs that should be treated as a single
  * cluster/ligature, store them in the textrun at the appropriate character
  * and set the other characters involved to be ligature/cluster continuations
  * as appropriate.
  */ 
 static nsresult
 SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, uint32_t aGlyphCount,
-                           gfxShapedWord *aShapedWord,
+                           gfxShapedText *aShapedText,
                            const gchar *aUTF8, uint32_t aUTF8Length,
                            uint32_t *aUTF16Offset,
                            PangoGlyphUnit aOverrideSpaceWidth)
 {
     uint32_t utf16Offset = *aUTF16Offset;
-    uint32_t wordLength = aShapedWord->Length();
-    const uint32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
+    uint32_t limit = aShapedText->GetLength();
+    const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
+    gfxShapedText::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs();
+    bool atClusterStart = charGlyphs[utf16Offset].IsClusterStart();
 
     // Override the width of a space, but only for spaces that aren't
     // clustered with something else (like a freestanding diacritical mark)
     PangoGlyphUnit width = aGlyphs[0].geometry.width;
     if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
-        (utf16Offset + 1 == wordLength ||
-         aShapedWord->IsClusterStart(utf16Offset))) {
+        (utf16Offset + 1 == limit || atClusterStart)) {
         width = aOverrideSpaceWidth;
     }
     int32_t advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
 
-    gfxShapedWord::CompressedGlyph g;
-    bool atClusterStart = aShapedWord->IsClusterStart(utf16Offset);
+    gfxShapedText::CompressedGlyph g;
     // See if we fit in the compressed area.
     if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
         aGlyphs[0].geometry.x_offset == 0 &&
         aGlyphs[0].geometry.y_offset == 0 &&
         !IS_EMPTY_GLYPH(aGlyphs[0].glyph) &&
-        gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
-        gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
-        aShapedWord->SetSimpleGlyph(utf16Offset,
-                                    g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
+        gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
+        gfxShapedText::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
+        charGlyphs[utf16Offset].SetSimpleGlyph(advance, aGlyphs[0].glyph);
     } else {
-        nsAutoTArray<gfxShapedWord::DetailedGlyph,10> detailedGlyphs;
+        nsAutoTArray<gfxShapedText::DetailedGlyph,10> detailedGlyphs;
         if (!detailedGlyphs.AppendElements(aGlyphCount))
             return NS_ERROR_OUT_OF_MEMORY;
 
-        int32_t direction = aShapedWord->IsRightToLeft() ? -1 : 1;
+        int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
         uint32_t pangoIndex = direction > 0 ? 0 : aGlyphCount - 1;
         uint32_t detailedIndex = 0;
         for (uint32_t i = 0; i < aGlyphCount; ++i) {
             const PangoGlyphInfo &glyph = aGlyphs[pangoIndex];
             pangoIndex += direction;
             // The zero width characters return empty glyph ID at
             // shaping; we should skip these.
             if (IS_EMPTY_GLYPH(glyph.glyph))
                 continue;
 
-            gfxShapedWord::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
+            gfxShapedText::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
             ++detailedIndex;
 
             details->mGlyphID = glyph.glyph;
             NS_ASSERTION(details->mGlyphID == glyph.glyph,
                          "Seriously weird glyph ID detected!");
             details->mAdvance =
                 ConvertPangoToAppUnits(glyph.geometry.width,
                                        appUnitsPerDevUnit);
             details->mXOffset =
                 float(glyph.geometry.x_offset)*appUnitsPerDevUnit/PANGO_SCALE;
             details->mYOffset =
                 float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
         }
         g.SetComplex(atClusterStart, true, detailedIndex);
-        aShapedWord->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
+        aShapedText->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
     }
 
     // Check for ligatures and set *aUTF16Offset.
     const gchar *p = aUTF8;
     const gchar *end = aUTF8 + aUTF8Length;
     while (1) {
         // Skip the CompressedGlyph that we have added, but check if the
         // character was supposed to be ignored. If it's supposed to be ignored,
@@ -2867,30 +2883,31 @@ SetGlyphsForCharacterGroup(const PangoGl
                      "Invalid character detected");
         ++utf16Offset;
 
         // We produced this UTF8 so we don't need to worry about malformed stuff
         p = g_utf8_next_char(p);
         if (p >= end)
             break;
 
-        if (utf16Offset >= wordLength) {
+        if (utf16Offset >= limit) {
             NS_ERROR("Someone has added too many glyphs!");
             return NS_ERROR_FAILURE;
         }
 
-        g.SetComplex(aShapedWord->IsClusterStart(utf16Offset), false, 0);
-        aShapedWord->SetGlyphs(utf16Offset, g, nullptr);
+        gfxShapedText::CompressedGlyph &g = charGlyphs[utf16Offset];
+        NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
+        g.SetComplex(g.IsClusterStart(), false, 0);
     }
     *aUTF16Offset = utf16Offset;
     return NS_OK;
 }
 
 static nsresult
-SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, uint32_t aUTF8Length,
+SetGlyphs(gfxShapedText *aShapedText, const gchar *aUTF8, uint32_t aUTF8Length,
           uint32_t *aUTF16Offset, PangoGlyphString *aGlyphs,
           PangoGlyphUnit aOverrideSpaceWidth,
           gfxFont *aFont)
 {
     gint numGlyphs = aGlyphs->num_glyphs;
     PangoGlyphInfo *glyphs = aGlyphs->glyphs;
     const gint *logClusters = aGlyphs->log_clusters;
     // We cannot make any assumptions about the order of glyph clusters
@@ -2916,31 +2933,31 @@ SetGlyphs(gfxShapedWord *aShapedWord, co
             lastCluster = thisCluster;
             NS_ASSERTION(0 <= thisCluster && thisCluster < gint(aUTF8Length),
                          "garbage from pango_shape - this is bad");
             logGlyphs[thisCluster] = glyphIndex;
         }
     }
 
     uint32_t utf16Offset = *aUTF16Offset;
-    uint32_t wordLength = aShapedWord->Length();
+    uint32_t limit = aShapedText->GetLength();
     utf8Index = 0;
     // The next glyph cluster in logical order. 
     gint nextGlyphClusterStart = logGlyphs[utf8Index];
     NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
     while (utf8Index < aUTF8Length) {
-        if (utf16Offset >= wordLength) {
+        if (utf16Offset >= limit) {
           NS_ERROR("Someone has added too many glyphs!");
           return NS_ERROR_FAILURE;
         }
         gint glyphClusterStart = nextGlyphClusterStart;
         // Find the utf8 text associated with this glyph cluster.
         uint32_t clusterUTF8Start = utf8Index;
         // Check whether we are consistent with pango_break data.
-        NS_WARN_IF_FALSE(aShapedWord->IsClusterStart(utf16Offset),
+        NS_WARN_IF_FALSE(aShapedText->IsClusterStart(utf16Offset),
                          "Glyph cluster not aligned on character cluster.");
         do {
             ++utf8Index;
             nextGlyphClusterStart = logGlyphs[utf8Index];
         } while (nextGlyphClusterStart < 0);
         const gchar *clusterUTF8 = &aUTF8[clusterUTF8Start];
         uint32_t clusterUTF8Length = utf8Index - clusterUTF8Start;
 
@@ -2956,87 +2973,90 @@ SetGlyphs(gfxShapedWord *aShapedWord, co
                 haveMissingGlyph = true;
             }
             glyphIndex++;
         } while (glyphIndex < numGlyphs && 
                  logClusters[glyphIndex] == gint(clusterUTF8Start));
 
         nsresult rv;
         if (haveMissingGlyph) {
-            SetMissingGlyphs(aShapedWord, clusterUTF8, clusterUTF8Length,
+            SetMissingGlyphs(aShapedText, clusterUTF8, clusterUTF8Length,
                              &utf16Offset, aFont);
         } else {
             rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
                                             glyphIndex - glyphClusterStart,
-                                            aShapedWord,
+                                            aShapedText,
                                             clusterUTF8, clusterUTF8Length,
                                             &utf16Offset, aOverrideSpaceWidth);
             NS_ENSURE_SUCCESS(rv,rv);
         }
     }
     *aUTF16Offset = utf16Offset;
     return NS_OK;
 }
 
 static void
-SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
+SetMissingGlyphs(gfxShapedText *aShapedText, const gchar *aUTF8,
                  uint32_t aUTF8Length, uint32_t *aUTF16Offset,
                  gfxFont *aFont)
 {
     uint32_t utf16Offset = *aUTF16Offset;
-    uint32_t wordLength = aShapedWord->Length();
+    uint32_t limit = aShapedText->GetLength();
     for (uint32_t index = 0; index < aUTF8Length;) {
-        if (utf16Offset >= wordLength) {
+        if (utf16Offset >= limit) {
             NS_ERROR("Someone has added too many glyphs!");
             break;
         }
         gunichar ch = g_utf8_get_char(aUTF8 + index);
-        aShapedWord->SetMissingGlyph(utf16Offset, ch, aFont);
+        aShapedText->SetMissingGlyph(utf16Offset, ch, aFont);
 
         ++utf16Offset;
         NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
         if (ch >= 0x10000)
             ++utf16Offset;
         // We produced this UTF8 so we don't need to worry about malformed stuff
         index = g_utf8_next_char(aUTF8 + index) - aUTF8;
     }
 
     *aUTF16Offset = utf16Offset;
 }
 
 static void
-InitGlyphRunWithPangoAnalysis(gfxShapedWord *aShapedWord,
+InitGlyphRunWithPangoAnalysis(gfxShapedText *aShapedText,
+                              uint32_t aOffset, uint32_t aLength,
                               const gchar *aUTF8, uint32_t aUTF8Length,
                               PangoAnalysis *aAnalysis,
                               PangoGlyphUnit aOverrideSpaceWidth,
                               gfxFont *aFont)
 {
-    uint32_t utf16Offset = 0;
+    uint32_t utf16Offset = aOffset;
     PangoGlyphString *glyphString = pango_glyph_string_new();
 
     const gchar *p = aUTF8;
     const gchar *end = p + aUTF8Length;
     while (p < end) {
+        NS_ASSERTION(utf16Offset < aOffset + aLength,
+                     "overrun expected range of aShapedText");
         if (*p == 0) {
-            aShapedWord->SetMissingGlyph(utf16Offset, 0, aFont);
+            aShapedText->SetMissingGlyph(utf16Offset, 0, aFont);
             ++p;
             ++utf16Offset;
             continue;
         }
 
         // It's necessary to loop over pango_shape as it treats
         // NULs as string terminators
         const gchar *text = p;
         do {
             ++p;
         } while(p < end && *p != 0);
         gint len = p - text;
 
         pango_shape(text, len, aAnalysis, glyphString);
-        SetGlyphs(aShapedWord, text, len, &utf16Offset, glyphString,
+        SetGlyphs(aShapedText, text, len, &utf16Offset, glyphString,
                   aOverrideSpaceWidth, aFont);
     }
 
     pango_glyph_string_free(glyphString);
 }
 
 // PangoAnalysis is part of Pango's ABI but over time extra fields have been
 // inserted into padding.  This union is used so that the code here can be
@@ -3054,21 +3074,24 @@ typedef union {
         guint8 flags;
         guint8 script; /* PangoScript */
         PangoLanguage *language;
         GSList *extra_attrs;
     } local;
 } PangoAnalysisUnion;
 
 bool
-gfxFcFont::InitGlyphRunWithPango(gfxShapedWord *aShapedWord,
-                                 const PRUnichar *aString)
+gfxFcFont::InitGlyphRunWithPango(const PRUnichar *aString,
+                                 uint32_t         aOffset,
+                                 uint32_t         aLength,
+                                 int32_t          aScript,
+                                 gfxShapedText   *aShapedText)
 {
-    const PangoScript script = static_cast<PangoScript>(aShapedWord->Script());
-    NS_ConvertUTF16toUTF8 utf8(aString, aShapedWord->Length());
+    const PangoScript script = static_cast<PangoScript>(aScript);
+    NS_ConvertUTF16toUTF8 utf8(aString, aLength);
 
     PangoFont *font = GetPangoFont();
 
     hb_language_t languageOverride = NULL;
     if (GetStyle()->languageOverride) {
         languageOverride =
             hb_ot_tag_to_language(GetStyle()->languageOverride);
     } else if (GetFontEntry()->mLanguageOverride) {
@@ -3164,17 +3187,17 @@ gfxFcFont::InitGlyphRunWithPango(gfxShap
 
     // For pango_shape
     analysis.local.shape_engine = shapeEngine;
     // For pango_break
     analysis.local.lang_engine =
         PANGO_ENGINE_LANG(pango_map_get_engine(langMap, script));
 
     analysis.local.font = font;
-    analysis.local.level = aShapedWord->IsRightToLeft() ? 1 : 0;
+    analysis.local.level = aShapedText->IsRightToLeft() ? 1 : 0;
     // gravity and flags are used in Pango 1.14.10 and newer.
     //
     // PANGO_GRAVITY_SOUTH is what we want for upright horizontal text.  The
     // constant is not available when compiling with older Pango versions, but
     // is zero so the zero memset initialization is sufficient.
     //
     // Pango uses non-zero flags for vertical gravities only
     // (up to version 1.28 at least), so using zero is fine for flags too.
@@ -3187,17 +3210,18 @@ gfxFcFont::InitGlyphRunWithPango(gfxShap
 
     analysis.local.language = language;
     // Non-font attributes.  Not used here.
     analysis.local.extra_attrs = NULL;
 
     PangoGlyphUnit spaceWidth =
         moz_pango_units_from_double(GetMetrics().spaceWidth);
 
-    InitGlyphRunWithPangoAnalysis(aShapedWord, utf8.get(), utf8.Length(),
+    InitGlyphRunWithPangoAnalysis(aShapedText, aOffset, aLength,
+                                  utf8.get(), utf8.Length(),
                                   &analysis.pango, spaceWidth, this);
     return true;
 }
 
 /* static */
 PangoLanguage *
 GuessPangoLanguage(nsIAtom *aLanguage)
 {
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1596,18 +1596,17 @@ gfxPlatform::SetupClusterBoundaries(gfxT
         // XXX is this true in all languages???
         // behdad: don't think so.  Czech for example IIRC has a
         // 'ch' grapheme.
         // jfkthame: but that's not expected to behave as a grapheme cluster
         // for selection/editing/etc.
         return;
     }
 
-    gfxShapedWord::SetupClusterBoundaries(aTextRun->GetCharacterGlyphs(),
-                                          aString, aTextRun->GetLength());
+    aTextRun->SetupClusterBoundaries(0, aString, aTextRun->GetLength());
 }
 
 int32_t
 gfxPlatform::GetBidiNumeralOption()
 {
     if (mBidiNumeralOption == UNINITIALIZED_VALUE) {
         mBidiNumeralOption = Preferences::GetInt(BIDI_NUMERAL_PREF, 0);
     }
--- a/gfx/thebes/gfxUniscribeShaper.cpp
+++ b/gfx/thebes/gfxUniscribeShaper.cpp
@@ -212,33 +212,36 @@ public:
                                      sfp);
         if (rv == E_PENDING) {
             SelectFont();
             rv = ScriptGetFontProperties(mDC, mShaper->ScriptCache(),
                                          sfp);
         }
     }
 
-    void SaveGlyphs(gfxShapedWord *aShapedWord) {
+    void SaveGlyphs(gfxShapedText *aShapedText, uint32_t aOffset) {
         uint32_t offsetInRun = mScriptItem->iCharPos;
 
         // XXX We should store this in the item and only fetch it once
         SCRIPT_FONTPROPERTIES sfp;
         ScriptFontProperties(&sfp);
 
         uint32_t offset = 0;
-        nsAutoTArray<gfxShapedWord::DetailedGlyph,1> detailedGlyphs;
-        gfxShapedWord::CompressedGlyph g;
-        const uint32_t appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
+        nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
+        gfxShapedText::CompressedGlyph g;
+        gfxShapedText::CompressedGlyph *charGlyphs =
+            aShapedText->GetCharacterGlyphs();
+        const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
         while (offset < mItemLength) {
-            uint32_t runOffset = offsetInRun + offset;
-            bool atClusterStart = aShapedWord->IsClusterStart(runOffset);
+            uint32_t runOffset = aOffset + offsetInRun + offset;
+            bool atClusterStart = charGlyphs[runOffset].IsClusterStart();
             if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
+                gfxShapedText::CompressedGlyph &g = charGlyphs[runOffset];
+                NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
                 g.SetComplex(atClusterStart, false, 0);
-                aShapedWord->SetGlyphs(runOffset, g, nullptr);
             } else {
                 // Count glyphs for this character
                 uint32_t k = mClusters[offset];
                 uint32_t glyphCount = mNumGlyphs - k;
                 uint32_t nextClusterOffset;
                 bool missing = IsGlyphMissing(&sfp, k);
                 for (nextClusterOffset = offset + 1; nextClusterOffset < mItemLength; ++nextClusterOffset) {
                     if (mClusters[nextClusterOffset] > k) {
@@ -255,46 +258,46 @@ public:
                 int32_t advance = mAdvances[k]*appUnitsPerDevUnit;
                 WORD glyph = mGlyphs[k];
                 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(mItemString[offset]),
                              "invalid character detected");
                 if (missing) {
                     if (NS_IS_HIGH_SURROGATE(mItemString[offset]) &&
                         offset + 1 < mItemLength &&
                         NS_IS_LOW_SURROGATE(mItemString[offset + 1])) {
-                        aShapedWord->SetMissingGlyph(runOffset,
+                        aShapedText->SetMissingGlyph(runOffset,
                                                      SURROGATE_TO_UCS4(mItemString[offset],
                                                                        mItemString[offset + 1]),
                                                      mShaper->GetFont());
                     } else {
-                        aShapedWord->SetMissingGlyph(runOffset, mItemString[offset],
+                        aShapedText->SetMissingGlyph(runOffset, mItemString[offset],
                                                      mShaper->GetFont());
                     }
                 } else if (glyphCount == 1 && advance >= 0 &&
                     mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
-                    gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
-                    gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
+                    gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
+                    gfxShapedText::CompressedGlyph::IsSimpleGlyphID(glyph) &&
                     atClusterStart)
                 {
-                    aShapedWord->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
+                    charGlyphs[runOffset].SetSimpleGlyph(advance, glyph);
                 } else {
                     if (detailedGlyphs.Length() < glyphCount) {
                         if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
                             return;
                     }
                     uint32_t i;
                     for (i = 0; i < glyphCount; ++i) {
                         gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
                         details->mGlyphID = mGlyphs[k + i];
                         details->mAdvance = mAdvances[k + i] * appUnitsPerDevUnit;
                         details->mXOffset = float(mOffsets[k + i].du) * appUnitsPerDevUnit *
-                            aShapedWord->GetDirection();
+                            aShapedText->GetDirection();
                         details->mYOffset = - float(mOffsets[k + i].dv) * appUnitsPerDevUnit;
                     }
-                    aShapedWord->SetGlyphs(runOffset,
+                    aShapedText->SetGlyphs(runOffset,
                                            g.SetComplex(atClusterStart, true,
                                                         glyphCount),
                                            detailedGlyphs.Elements());
                 }
             }
             ++offset;
         }
     }
@@ -362,44 +365,46 @@ private:
 
     bool mFontSelected;
 };
 
 class Uniscribe
 {
 public:
     Uniscribe(const PRUnichar *aString,
-              gfxShapedWord *aShapedWord):
-        mString(aString), mShapedWord(aShapedWord)
+              gfxShapedText *aShapedText,
+              uint32_t aOffset, uint32_t aLength):
+        mString(aString), mShapedText(aShapedText),
+        mOffset(aOffset), mLength(aLength)
     {
     }
 
     void Init() {
         memset(&mControl, 0, sizeof(SCRIPT_CONTROL));
         memset(&mState, 0, sizeof(SCRIPT_STATE));
         // Lock the direction. Don't allow the itemizer to change directions
         // based on character type.
-        mState.uBidiLevel = mShapedWord->IsRightToLeft() ? 1 : 0;
+        mState.uBidiLevel = mShapedText->IsRightToLeft() ? 1 : 0;
         mState.fOverrideDirection = true;
     }
 
 public:
     int Itemize() {
         HRESULT rv;
 
         int maxItems = 5;
 
         Init();
 
         // Allocate space for one more item than expected, to handle a rare
         // overflow in ScriptItemize (pre XP SP2). See bug 366643.
         if (!mItems.SetLength(maxItems + 1)) {
             return 0;
         }
-        while ((rv = ScriptItemize(mString, mShapedWord->Length(),
+        while ((rv = ScriptItemize(mString, mLength,
                                    maxItems, &mControl, &mState,
                                    mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
             maxItems *= 2;
             if (!mItems.SetLength(maxItems + 1)) {
                 return 0;
             }
             Init();
         }
@@ -409,68 +414,73 @@ public:
 
     SCRIPT_ITEM *ScriptItem(uint32_t i) {
         NS_ASSERTION(i <= (uint32_t)mNumItems, "Trying to get out of bounds item");
         return &mItems[i];
     }
 
 private:
     const PRUnichar *mString;
-    gfxShapedWord   *mShapedWord;
+    gfxShapedText   *mShapedText;
+    uint32_t         mOffset;
+    uint32_t         mLength;
 
     SCRIPT_CONTROL mControl;
     SCRIPT_STATE   mState;
     nsTArray<SCRIPT_ITEM> mItems;
     int mNumItems;
 };
 
 
 bool
-gfxUniscribeShaper::ShapeWord(gfxContext *aContext,
-                              gfxShapedWord *aShapedWord,
-                              const PRUnichar *aString)
+gfxUniscribeShaper::ShapeText(gfxContext      *aContext,
+                              const PRUnichar *aText,
+                              uint32_t         aOffset,
+                              uint32_t         aLength,
+                              int32_t          aScript,
+                              gfxShapedText   *aShapedText)
 {
     DCFromContext aDC(aContext);
  
     bool result = true;
     HRESULT rv;
 
-    Uniscribe us(aString, aShapedWord);
+    Uniscribe us(aText, aShapedText, aOffset, aLength);
 
     /* itemize the string */
     int numItems = us.Itemize();
 
-    uint32_t length = aShapedWord->Length();
+    uint32_t length = aLength;
     SaveDC(aDC);
     uint32_t ivs = 0;
     for (int i = 0; i < numItems; ++i) {
         int iCharPos = us.ScriptItem(i)->iCharPos;
         int iCharPosNext = us.ScriptItem(i+1)->iCharPos;
 
         if (ivs) {
             iCharPos += 2;
             if (iCharPos >= iCharPosNext) {
                 ivs = 0;
                 continue;
             }
         }
 
         if (i+1 < numItems && iCharPosNext <= length - 2
-            && aString[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
-            && uint32_t(aString[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
+            && aText[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
+            && uint32_t(aText[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
             <= L_SURROGATE(kUnicodeVS256) - L_SURROGATE(kUnicodeVS17)) {
 
-            ivs = SURROGATE_TO_UCS4(aString[iCharPosNext],
-                                    aString[iCharPosNext + 1]);
+            ivs = SURROGATE_TO_UCS4(aText[iCharPosNext],
+                                    aText[iCharPosNext + 1]);
         } else {
             ivs = 0;
         }
 
         UniscribeItem item(aContext, aDC, this,
-                           aString + iCharPos,
+                           aText + iCharPos,
                            iCharPosNext - iCharPos,
                            us.ScriptItem(i), ivs);
         if (!item.AllocateBuffers()) {
             result = false;
             break;
         }
 
         if (!item.ShapingEnabled()) {
@@ -504,15 +514,15 @@ gfxUniscribeShaper::ShapeWord(gfxContext
         if (FAILED(rv)) {
             // Uniscribe doesn't like this font for some reason.
             // Returning FALSE will make the gfxGDIFont retry with the
             // "dumb" GDI shaper, unless useUniscribeOnly was set.
             result = false;
             break;
         }
 
-        item.SaveGlyphs(aShapedWord);
+        item.SaveGlyphs(aShapedText, aOffset);
     }
 
     RestoreDC(aDC, -1);
 
     return result;
 }
--- a/gfx/thebes/gfxUniscribeShaper.h
+++ b/gfx/thebes/gfxUniscribeShaper.h
@@ -24,19 +24,22 @@ public:
         MOZ_COUNT_CTOR(gfxUniscribeShaper);
     }
 
     virtual ~gfxUniscribeShaper()
     {
         MOZ_COUNT_DTOR(gfxUniscribeShaper);
     }
 
-    virtual bool ShapeWord(gfxContext *aContext,
-                           gfxShapedWord *aShapedWord,
-                           const PRUnichar *aString);
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const PRUnichar *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
 
     SCRIPT_CACHE *ScriptCache() { return &mScriptCache; }
 
     gfxGDIFont *GetFont() { return static_cast<gfxGDIFont*>(mFont); }
 
 private:
     SCRIPT_CACHE mScriptCache;