backout changeset 59b2dd6b5048 (bug 985220) due to MS Sans Serif regression with Thai system locale (see bug 1020826).
authorJonathan Kew <jkew@mozilla.com>
Fri, 06 Jun 2014 22:41:09 +0100
changeset 206564 5cf33b3e3d5e456df098d213056aff4754653579
parent 206563 6be2448e325c1ffbcb9c87dfd1cbf6730ed9c551
child 206565 6502442e61332740457eecde69db45aa0e9dc15b
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs985220, 1020826
milestone32.0a1
backs out59b2dd6b50483f5b443f98a7a145dc7b265d442c
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
backout changeset 59b2dd6b5048 (bug 985220) due to MS Sans Serif regression with Thai system locale (see bug 1020826).
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxDWriteFonts.h
gfx/thebes/gfxDWriteShaper.cpp
gfx/thebes/gfxDWriteShaper.h
gfx/thebes/gfxDWriteTextAnalysis.cpp
gfx/thebes/gfxDWriteTextAnalysis.h
gfx/thebes/gfxFT2FontBase.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/gfxUniscribeShaper.cpp
gfx/thebes/gfxUniscribeShaper.h
gfx/thebes/moz.build
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -2,23 +2,26 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxDWriteFonts.h"
 
 #include "mozilla/MemoryReporting.h"
 
+#include "gfxDWriteShaper.h"
 #include "gfxHarfBuzzShaper.h"
 #include <algorithm>
 #include "gfxGraphiteShaper.h"
 #include "gfxDWriteFontList.h"
 #include "gfxContext.h"
 #include <dwrite.h>
 
+#include "gfxDWriteTextAnalysis.h"
+
 #include "harfbuzz/hb.h"
 
 // Chosen this as to resemble DWrite's own oblique face style.
 #define OBLIQUE_SKEW_FACTOR 0.3
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
@@ -129,40 +132,20 @@ gfxDWriteFont::~gfxDWriteFont()
 
 gfxFont*
 gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
 {
     return new gfxDWriteFont(static_cast<gfxDWriteFontEntry*>(mFontEntry.get()),
                              &mStyle, mNeedsBold, anAAOption);
 }
 
-bool
-gfxDWriteFont::ShapeText(gfxContext      *aContext,
-                         const char16_t *aText,
-                         uint32_t         aOffset,
-                         uint32_t         aLength,
-                         int32_t          aScript,
-                         gfxShapedText   *aShapedText,
-                         bool             aPreferPlatformShaping)
+void
+gfxDWriteFont::CreatePlatformShaper()
 {
-    bool ok = false;
-
-    if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
-        ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
-                                        aScript, aShapedText);
-    }
-
-    if (!ok && mHarfBuzzShaper) {
-        ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
-                                        aScript, aShapedText);
-    }
-
-    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
-
-    return ok;
+    mPlatformShaper = new gfxDWriteShaper(this);
 }
 
 const gfxFont::Metrics&
 gfxDWriteFont::GetMetrics()
 {
     return *mMetrics;
 }
 
@@ -607,17 +590,17 @@ gfxDWriteFont::Measure(gfxTextRun *aText
         metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
         metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
     }
 
     return metrics;
 }
 
 bool
-gfxDWriteFont::ProvidesGlyphWidths() const
+gfxDWriteFont::ProvidesGlyphWidths()
 {
     return !mUseSubpixelPositions ||
            (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
 }
 
 int32_t
 gfxDWriteFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
 {
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -48,17 +48,17 @@ public:
 
     /* override Measure to add padding for antialiasing */
     virtual RunMetrics Measure(gfxTextRun *aTextRun,
                                uint32_t aStart, uint32_t aEnd,
                                BoundingBoxType aBoundingBoxType,
                                gfxContext *aContextForTightBoundingBox,
                                Spacing *aSpacing);
 
-    virtual bool ProvidesGlyphWidths() const;
+    virtual bool ProvidesGlyphWidths();
 
     virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
 
     virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> GetGlyphRenderingOptions();
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
@@ -66,23 +66,19 @@ public:
 
     virtual FontType GetType() const { return FONT_TYPE_DWRITE; }
 
     virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont> GetScaledFont(mozilla::gfx::DrawTarget *aTarget);
 
     virtual cairo_scaled_font_t *GetCairoScaledFont();
 
 protected:
-    virtual bool ShapeText(gfxContext     *aContext,
-                           const char16_t *aText,
-                           uint32_t        aOffset,
-                           uint32_t        aLength,
-                           int32_t         aScript,
-                           gfxShapedText  *aShapedText,
-                           bool            aPreferPlatformShaping = false);
+    friend class gfxDWriteShaper;
+
+    virtual void CreatePlatformShaper();
 
     bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics);
 
     void ComputeMetrics(AntialiasOption anAAOption);
 
     bool HasBitmapStrikeForSize(uint32_t aSize);
 
     cairo_font_face_t *CairoFontFace();
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxDWriteShaper.cpp
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxDWriteShaper.h"
+#include "gfxWindowsPlatform.h"
+
+#include <dwrite.h>
+
+#include "gfxDWriteTextAnalysis.h"
+
+#include "nsCRT.h"
+
+bool
+gfxDWriteShaper::ShapeText(gfxContext      *aContext,
+                           const char16_t *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText)
+{
+    HRESULT hr;
+    // TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES
+
+    DWRITE_READING_DIRECTION readingDirection = 
+        aShapedText->IsRightToLeft()
+            ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
+            : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
+
+    gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
+
+    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 32K characters
+     * in a single call, so we cannot exceed that limit.
+     */
+    UINT32 length = aLength;
+    char16ptr_t text = aText;
+
+    TextAnalysis analysis(text, length, nullptr, readingDirection);
+    TextAnalysis::Run *runHead;
+    hr = analysis.GenerateResults(analyzer, &runHead);
+
+    if (FAILED(hr)) {
+        NS_WARNING("Analyzer failed to generate results.");
+        return false;
+    }
+
+    int32_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;
+    }
+    maxGlyphs += 3 * length / 2 + 16;
+
+    AutoFallibleTArray<UINT16, 400> clusters;
+    AutoFallibleTArray<UINT16, 400> indices;
+    AutoFallibleTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
+    AutoFallibleTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
+    if (!clusters.SetLength(length) ||
+        !indices.SetLength(maxGlyphs) || 
+        !textProperties.SetLength(maxGlyphs) ||
+        !glyphProperties.SetLength(maxGlyphs)) {
+        NS_WARNING("Shaper failed to allocate memory.");
+        return false;
+    }
+
+    UINT32 actualGlyphs;
+
+    hr = analyzer->GetGlyphs(text, length,
+            font->GetFontFace(), FALSE, 
+            readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
+            &runHead->mScript, nullptr, nullptr, nullptr, nullptr, 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.
+        goto trymoreglyphs;
+    }
+    if (FAILED(hr)) {
+        NS_WARNING("Analyzer failed to get glyphs.");
+        return false;
+    }
+
+    WORD gID = indices[0];
+    AutoFallibleTArray<FLOAT, 400> advances;
+    AutoFallibleTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
+    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(
+                                          text,
+                                          clusters.Elements(),
+                                          textProperties.Elements(),
+                                          length,
+                                          indices.Elements(),
+                                          glyphProperties.Elements(),
+                                          actualGlyphs,
+                                          font->GetFontFace(),
+                                          font->GetAdjustedSize(),
+                                          1.0,
+                                          nullptr,
+                                          FALSE,
+                                          FALSE,
+                                          FALSE,
+                                          &runHead->mScript,
+                                          nullptr,
+                                          nullptr,
+                                          nullptr,
+                                          0,
+                                          advances.Elements(),
+                                          glyphOffsets.Elements());
+    } else {
+        hr = analyzer->GetGlyphPlacements(text,
+                                          clusters.Elements(),
+                                          textProperties.Elements(),
+                                          length,
+                                          indices.Elements(),
+                                          glyphProperties.Elements(),
+                                          actualGlyphs,
+                                          font->GetFontFace(),
+                                          font->GetAdjustedSize(),
+                                          FALSE,
+                                          FALSE,
+                                          &runHead->mScript,
+                                          nullptr,
+                                          nullptr,
+                                          nullptr,
+                                          0,
+                                          advances.Elements(),
+                                          glyphOffsets.Elements());
+    }
+    if (FAILED(hr)) {
+        NS_WARNING("Analyzer failed to get glyph placements.");
+        return false;
+    }
+
+    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 = aOffset + c;
+
+        if (c > 0 && k == clusters[c - 1]) {
+            // 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) {
+            if (clusters[nextClusterOffset] > k) {
+                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 &&
+            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;
+            for (unsigned int z = 0; z < glyphCount; z++) {
+                detailedGlyphs[z].mGlyphID = indices[k + z];
+                detailedGlyphs[z].mAdvance = 
+                    (int32_t)(advances[k + z]
+                       * appUnitsPerDevPixel);
+                if (readingDirection == 
+                    DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
+                    detailedGlyphs[z].mXOffset = 
+                        (totalAdvance + 
+                          glyphOffsets[k + z].advanceOffset)
+                        * appUnitsPerDevPixel;
+                } else {
+                    detailedGlyphs[z].mXOffset = 
+                        glyphOffsets[k + z].advanceOffset *
+                        appUnitsPerDevPixel;
+                }
+                detailedGlyphs[z].mYOffset = 
+                    -glyphOffsets[k + z].ascenderOffset *
+                    appUnitsPerDevPixel;
+                totalAdvance += advances[k + z];
+            }
+            aShapedText->SetGlyphs(
+                absC,
+                g.SetComplex(charGlyphs[absC].IsClusterStart(),
+                             true,
+                             glyphCount),
+                detailedGlyphs.Elements());
+        }
+    }
+
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxDWriteShaper.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DWRITESHAPER_H
+#define GFX_DWRITESHAPER_H
+
+#include "gfxDWriteFonts.h"
+
+/**
+ * \brief Class representing a DWrite font shaper.
+ */
+class gfxDWriteShaper : public gfxFontShaper
+{
+public:
+    gfxDWriteShaper(gfxDWriteFont *aFont)
+        : gfxFontShaper(aFont)
+    {
+        MOZ_COUNT_CTOR(gfxDWriteShaper);
+    }
+
+    virtual ~gfxDWriteShaper()
+    {
+        MOZ_COUNT_DTOR(gfxDWriteShaper);
+    }
+
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const char16_t *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
+};
+
+#endif /* GFX_DWRITESHAPER_H */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxDWriteTextAnalysis.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxDWriteTextAnalysis.h"
+
+TextAnalysis::TextAnalysis(const wchar_t* text,
+                           UINT32 textLength,
+                           const wchar_t* localeName,
+                           DWRITE_READING_DIRECTION readingDirection)
+  : mText(text)
+  , mTextLength(textLength)
+  , mLocaleName(localeName)
+  , mReadingDirection(readingDirection)
+  , mCurrentRun(nullptr)
+{
+}
+
+TextAnalysis::~TextAnalysis()
+{
+    // delete runs, except mRunHead which is part of the TextAnalysis object
+    for (Run *run = mRunHead.nextRun; run;) {
+        Run *origRun = run;
+        run = run->nextRun;
+        delete origRun;
+    }
+}
+
+STDMETHODIMP 
+TextAnalysis::GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
+                              OUT Run **runHead)
+{
+    // Analyzes the text using the script analyzer and returns
+    // the result as a series of runs.
+
+    HRESULT hr = S_OK;
+
+    // Initially start out with one result that covers the entire range.
+    // This result will be subdivided by the analysis processes.
+    mRunHead.mTextStart = 0;
+    mRunHead.mTextLength = mTextLength;
+    mRunHead.mBidiLevel = 
+        (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
+    mRunHead.nextRun = nullptr;
+    mCurrentRun = &mRunHead;
+
+    // Call each of the analyzers in sequence, recording their results.
+    if (SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this,
+                                                   0,
+                                                   mTextLength,
+                                                   this))) {
+        *runHead = &mRunHead;
+    }
+
+    return hr;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// IDWriteTextAnalysisSource source implementation
+
+IFACEMETHODIMP 
+TextAnalysis::GetTextAtPosition(UINT32 textPosition,
+                                OUT WCHAR const** textString,
+                                OUT UINT32* textLength)
+{
+    if (textPosition >= mTextLength) {
+        // No text at this position, valid query though.
+        *textString = nullptr;
+        *textLength = 0;
+    } else {
+        *textString = mText + textPosition;
+        *textLength = mTextLength - textPosition;
+    }
+    return S_OK;
+}
+
+
+IFACEMETHODIMP 
+TextAnalysis::GetTextBeforePosition(UINT32 textPosition,
+                                    OUT WCHAR const** textString,
+                                    OUT UINT32* textLength)
+{
+    if (textPosition == 0 || textPosition > mTextLength) {
+        // Either there is no text before here (== 0), or this
+        // is an invalid position. The query is considered valid thouh.
+        *textString = nullptr;
+        *textLength = 0;
+    } else {
+        *textString = mText;
+        *textLength = textPosition;
+    }
+    return S_OK;
+}
+
+
+DWRITE_READING_DIRECTION STDMETHODCALLTYPE 
+TextAnalysis::GetParagraphReadingDirection()
+{
+    // We support only a single reading direction.
+    return mReadingDirection;
+}
+
+
+IFACEMETHODIMP 
+TextAnalysis::GetLocaleName(UINT32 textPosition,
+                            OUT UINT32* textLength,
+                            OUT WCHAR const** localeName)
+{
+    // Single locale name is used, valid until the end of the string.
+    *localeName = mLocaleName;
+    *textLength = mTextLength - textPosition;
+
+    return S_OK;
+}
+
+
+IFACEMETHODIMP 
+TextAnalysis::GetNumberSubstitution(UINT32 textPosition,
+                                    OUT UINT32* textLength,
+                                    OUT IDWriteNumberSubstitution** numberSubstitution)
+{
+    // We do not support number substitution.
+    *numberSubstitution = nullptr;
+    *textLength = mTextLength - textPosition;
+
+    return S_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// IDWriteTextAnalysisSink implementation
+
+IFACEMETHODIMP 
+TextAnalysis::SetLineBreakpoints(UINT32 textPosition,
+                                 UINT32 textLength,
+                                 DWRITE_LINE_BREAKPOINT const* lineBreakpoints)
+{
+    // We don't use this for now.
+    return S_OK;
+}
+
+
+IFACEMETHODIMP 
+TextAnalysis::SetScriptAnalysis(UINT32 textPosition,
+                                UINT32 textLength,
+                                DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
+{
+    SetCurrentRun(textPosition);
+    SplitCurrentRun(textPosition);
+    while (textLength > 0) {
+        Run *run = FetchNextRun(&textLength);
+        run->mScript = *scriptAnalysis;
+    }
+
+    return S_OK;
+}
+
+
+IFACEMETHODIMP 
+TextAnalysis::SetBidiLevel(UINT32 textPosition,
+                           UINT32 textLength,
+                           UINT8 explicitLevel,
+                           UINT8 resolvedLevel)
+{
+    // We don't use this for now.
+    return S_OK;
+}
+
+
+IFACEMETHODIMP 
+TextAnalysis::SetNumberSubstitution(UINT32 textPosition,
+                                    UINT32 textLength,
+                                    IDWriteNumberSubstitution* numberSubstitution)
+{
+    // We don't use this for now.
+    return S_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Run modification.
+
+TextAnalysis::Run *
+TextAnalysis::FetchNextRun(IN OUT UINT32* textLength)
+{
+    // Used by the sink setters, this returns a reference to the next run.
+    // Position and length are adjusted to now point after the current run
+    // being returned.
+
+    Run *origRun = mCurrentRun;
+    // Split the tail if needed (the length remaining is less than the
+    // current run's size).
+    if (*textLength < mCurrentRun->mTextLength) {
+        SplitCurrentRun(mCurrentRun->mTextStart + *textLength);
+    } else {
+        // Just advance the current run.
+        mCurrentRun = mCurrentRun->nextRun;
+    }
+    *textLength -= origRun->mTextLength;
+
+    // Return a reference to the run that was just current.
+    return origRun;
+}
+
+
+void TextAnalysis::SetCurrentRun(UINT32 textPosition)
+{
+    // Move the current run to the given position.
+    // Since the analyzers generally return results in a forward manner,
+    // this will usually just return early. If not, find the
+    // corresponding run for the text position.
+
+    if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) {
+        return;
+    }
+
+    for (Run *run = &mRunHead; run; run = run->nextRun) {
+        if (run->ContainsTextPosition(textPosition)) {
+            mCurrentRun = run;
+            return;
+        }
+    }
+    NS_NOTREACHED("We should always be able to find the text position in one \
+        of our runs");
+}
+
+
+void TextAnalysis::SplitCurrentRun(UINT32 splitPosition)
+{
+    if (!mCurrentRun) {
+        NS_ASSERTION(false, "SplitCurrentRun called without current run.");
+        // Shouldn't be calling this when no current run is set!
+        return;
+    }
+    // Split the current run.
+    if (splitPosition <= mCurrentRun->mTextStart) {
+        // No need to split, already the start of a run
+        // or before it. Usually the first.
+        return;
+    }
+    Run *newRun = new Run;
+
+    *newRun = *mCurrentRun;
+
+    // Insert the new run in our linked list.
+    newRun->nextRun = mCurrentRun->nextRun;
+    mCurrentRun->nextRun = newRun;
+
+    // Adjust runs' text positions and lengths.
+    UINT32 splitPoint = splitPosition - mCurrentRun->mTextStart;
+    newRun->mTextStart += splitPoint;
+    newRun->mTextLength -= splitPoint;
+    mCurrentRun->mTextLength = splitPoint;
+    mCurrentRun = newRun;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxDWriteTextAnalysis.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_DWRITETEXTANALYSIS_H
+#define GFX_DWRITETEXTANALYSIS_H
+
+#include "gfxDWriteCommon.h"
+
+// Helper source/sink class for text analysis.
+class TextAnalysis
+    :   public IDWriteTextAnalysisSource,
+        public IDWriteTextAnalysisSink        
+{
+public:
+
+    // IUnknown interface
+    IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
+    {
+        if (iid == __uuidof(IDWriteTextAnalysisSource)) {
+            *ppObject = static_cast<IDWriteTextAnalysisSource*>(this);
+            return S_OK;
+        } else if (iid == __uuidof(IDWriteTextAnalysisSink)) {
+            *ppObject = static_cast<IDWriteTextAnalysisSink*>(this);
+            return S_OK;
+        } else if (iid == __uuidof(IUnknown)) {
+            *ppObject = 
+                static_cast<IUnknown*>(static_cast<IDWriteTextAnalysisSource*>(this));
+            return S_OK;
+        } else {
+            return E_NOINTERFACE;
+        }
+    }
+
+    IFACEMETHOD_(ULONG, AddRef)()
+    {
+        return 1;
+    }
+
+    IFACEMETHOD_(ULONG, Release)()
+    {
+        return 1;
+    }
+
+    // A single contiguous run of characters containing the same analysis 
+    // results.
+    struct Run
+    {
+        UINT32 mTextStart;   // starting text position of this run
+        UINT32 mTextLength;  // number of contiguous code units covered
+        UINT32 mGlyphStart;  // starting glyph in the glyphs array
+        UINT32 mGlyphCount;  // number of glyphs associated with this run of 
+                             // text
+        DWRITE_SCRIPT_ANALYSIS mScript;
+        UINT8 mBidiLevel;
+        bool mIsSideways;
+
+        inline bool ContainsTextPosition(UINT32 aTextPosition) const
+        {
+            return aTextPosition >= mTextStart
+                && aTextPosition <  mTextStart + mTextLength;
+        }
+
+        Run *nextRun;
+    };
+
+public:
+    TextAnalysis(const wchar_t* text,
+                 UINT32 textLength,
+                 const wchar_t* localeName,
+                 DWRITE_READING_DIRECTION readingDirection);
+
+    ~TextAnalysis();
+
+    STDMETHODIMP GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
+                                 Run **runHead);
+
+    // IDWriteTextAnalysisSource implementation
+
+    IFACEMETHODIMP GetTextAtPosition(UINT32 textPosition,
+                                     OUT WCHAR const** textString,
+                                     OUT UINT32* textLength);
+
+    IFACEMETHODIMP GetTextBeforePosition(UINT32 textPosition,
+                                         OUT WCHAR const** textString,
+                                         OUT UINT32* textLength);
+
+    IFACEMETHODIMP_(DWRITE_READING_DIRECTION) 
+        GetParagraphReadingDirection() throw();
+
+    IFACEMETHODIMP GetLocaleName(UINT32 textPosition,
+                                 OUT UINT32* textLength,
+                                 OUT WCHAR const** localeName);
+
+    IFACEMETHODIMP 
+        GetNumberSubstitution(UINT32 textPosition,
+                              OUT UINT32* textLength,
+                              OUT IDWriteNumberSubstitution** numberSubstitution);
+
+    // IDWriteTextAnalysisSink implementation
+
+    IFACEMETHODIMP 
+        SetScriptAnalysis(UINT32 textPosition,
+                          UINT32 textLength,
+                          DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis);
+
+    IFACEMETHODIMP 
+        SetLineBreakpoints(UINT32 textPosition,
+                           UINT32 textLength,
+                           const DWRITE_LINE_BREAKPOINT* lineBreakpoints);
+
+    IFACEMETHODIMP SetBidiLevel(UINT32 textPosition,
+                                UINT32 textLength,
+                                UINT8 explicitLevel,
+                                UINT8 resolvedLevel);
+
+    IFACEMETHODIMP 
+        SetNumberSubstitution(UINT32 textPosition,
+                              UINT32 textLength,
+                              IDWriteNumberSubstitution* numberSubstitution);
+
+protected:
+    Run *FetchNextRun(IN OUT UINT32* textLength);
+
+    void SetCurrentRun(UINT32 textPosition);
+
+    void SplitCurrentRun(UINT32 splitPosition);
+
+protected:
+    // Input
+    // (weak references are fine here, since this class is a transient
+    //  stack-based helper that doesn't need to copy data)
+    UINT32 mTextLength;
+    const wchar_t* mText;
+    const wchar_t* mLocaleName;
+    DWRITE_READING_DIRECTION mReadingDirection;
+
+    // Current processing state.
+    Run *mCurrentRun;
+
+    // Output is a list of runs starting here
+    Run  mRunHead;
+};
+
+#endif /* GFX_DWRITETEXTANALYSIS_H */
--- a/gfx/thebes/gfxFT2FontBase.h
+++ b/gfx/thebes/gfxFT2FontBase.h
@@ -20,17 +20,17 @@ public:
 
     uint32_t GetGlyph(uint32_t aCharCode);
     void GetGlyphExtents(uint32_t aGlyph,
                          cairo_text_extents_t* aExtents);
     virtual const gfxFont::Metrics& GetMetrics();
     virtual uint32_t GetSpaceGlyph();
     virtual bool ProvidesGetGlyph() const { return true; }
     virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector);
-    virtual bool ProvidesGlyphWidths() const { return true; }
+    virtual bool ProvidesGlyphWidths() { return true; }
     virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
 
     cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
     virtual bool SetupCairoFont(gfxContext *aContext);
 
     virtual FontType GetType() const { return FONT_TYPE_FT2; }
 
     mozilla::gfx::FontOptions* GetFontOptions() { return &mFontOptions; }
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3978,16 +3978,17 @@ gfxFont::ShapeText(gfxContext      *aCon
             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->ShapeText(aContext, aText, aOffset, aLength,
                                             aScript, aShapedText);
         }
     }
 
     PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1947,17 +1947,17 @@ protected:
     // Default implementation relies on gfxFontEntry::CreateFontInstance;
     // backends that don't implement that will need to override this and use
     // an alternative technique. (gfxPangoFonts, I'm looking at you...)
     virtual already_AddRefed<gfxFont> GetSmallCapsFont();
 
     // subclasses may provide (possibly hinted) glyph widths (in font units);
     // if they do not override this, harfbuzz will use unhinted widths
     // derived from the font tables
-    virtual bool ProvidesGlyphWidths() const {
+    virtual bool ProvidesGlyphWidths() {
         return false;
     }
 
     // The return value is interpreted as a horizontal advance in 16.16 fixed
     // point format.
     virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) {
         return -1;
     }
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxGDIFont.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/WindowsVersion.h"
 
+#include "gfxGDIShaper.h"
+#include "gfxUniscribeShaper.h"
 #include "gfxHarfBuzzShaper.h"
 #include <algorithm>
 #include "gfxGraphiteShaper.h"
 #include "gfxWindowsPlatform.h"
 #include "gfxContext.h"
 #include "mozilla/Preferences.h"
 #include "nsUnicodeProperties.h"
 #include "gfxFontConstants.h"
@@ -49,40 +51,71 @@ gfxGDIFont::gfxGDIFont(GDIFontEntry *aFo
       mFontFace(nullptr),
       mMetrics(nullptr),
       mSpaceGlyph(0),
       mNeedsBold(aNeedsBold)
 {
     if (FontCanSupportGraphite()) {
         mGraphiteShaper = new gfxGraphiteShaper(this);
     }
-    mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+    if (FontCanSupportHarfBuzz()) {
+        mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+    }
 }
 
 gfxGDIFont::~gfxGDIFont()
 {
     if (mScaledFont) {
         cairo_scaled_font_destroy(mScaledFont);
     }
     if (mFontFace) {
         cairo_font_face_destroy(mFontFace);
     }
     if (mFont) {
         ::DeleteObject(mFont);
     }
     delete mMetrics;
 }
 
+void
+gfxGDIFont::CreatePlatformShaper()
+{
+    mPlatformShaper = new gfxGDIShaper(this);
+}
+
 gfxFont*
 gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
 {
     return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
                           &mStyle, mNeedsBold, anAAOption);
 }
 
+static bool
+UseUniscribe(gfxShapedText *aShapedText,
+             char16ptr_t aText,
+             uint32_t aLength)
+{
+    uint32_t flags = aShapedText->Flags();
+    bool useGDI;
+
+    bool isXP = !IsVistaOrLater();
+
+    // 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(aText, aLength, SIC_COMPLEX) == S_OK;
+}
+
 bool
 gfxGDIFont::ShapeText(gfxContext      *aContext,
                       const char16_t *aText,
                       uint32_t         aOffset,
                       uint32_t         aLength,
                       int32_t          aScript,
                       gfxShapedText   *aShapedText,
                       bool             aPreferPlatformShaping)
@@ -90,26 +123,103 @@ gfxGDIFont::ShapeText(gfxContext      *a
     if (!mMetrics) {
         Initialize();
     }
     if (!mIsValid) {
         NS_WARNING("invalid font! expect incorrect text rendering");
         return false;
     }
 
+    bool ok = false;
+
     // Ensure the cairo font is set up, so there's no risk it'll fall back to
     // creating a "toy" font internally (see bug 544617).
     // 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;
     }
 
-    return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
-                              aShapedText, aPreferPlatformShaping);
+    if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+        ok = mGraphiteShaper->ShapeText(aContext, aText,
+                                        aOffset, aLength,
+                                        aScript, aShapedText);
+    }
+
+    if (!ok && mHarfBuzzShaper) {
+        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript) ||
+            (!IsVistaOrLater() &&
+             ScriptShapingType(aScript) == SHAPING_INDIC &&
+             !Preferences::GetBool("gfx.font_rendering.winxp-indic-uniscribe",
+                                   false))) {
+            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(aShapedText, aText, aLength)) {
+            // first try Uniscribe
+            if (!mUniscribeShaper) {
+                mUniscribeShaper = new gfxUniscribeShaper(this);
+            }
+
+            ok = mUniscribeShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                             aScript, aShapedText);
+            if (!ok) {
+                // fallback to GDI shaping
+                if (!mPlatformShaper) {
+                    CreatePlatformShaper();
+                }
+
+                ok = mPlatformShaper->ShapeText(aContext, aText, aOffset,
+                                                aLength, aScript, aShapedText);
+            }
+        } else {
+            // first use GDI
+            if (!mPlatformShaper) {
+                CreatePlatformShaper();
+            }
+
+            ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
+                                            aScript, aShapedText);
+            if (!ok) {
+                // try Uniscribe if GDI failed
+                if (!mUniscribeShaper) {
+                    mUniscribeShaper = new gfxUniscribeShaper(this);
+                }
+
+                // use Uniscribe shaping
+                ok = mUniscribeShaper->ShapeText(aContext, aText,
+                                                 aOffset, aLength,
+                                                 aScript, aShapedText);
+            }
+        }
+
+#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
+    }
+
+    PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
+
+    return ok;
 }
 
 const gfxFont::Metrics&
 gfxGDIFont::GetMetrics()
 {
     if (!mMetrics) {
         Initialize();
     }
@@ -422,52 +532,16 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFo
                     (mAntialiasOption == kAntialiasSubpixel) ? true : false);
 
     // If GDI synthetic italic is wanted, force the lfItalic field to true
     if (aUseGDIFakeItalic) {
         aLogFont.lfItalic = 1;
     }
 }
 
-uint32_t
-gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
-{
-    // Callback used only for fonts that lack a 'cmap' table.
-
-    // We don't support variation selector sequences or non-BMP characters
-    // in the legacy bitmap, vector or postscript fonts that might use
-    // this code path.
-    if (aUnicode > 0xffff || aVarSelector) {
-        return 0;
-    }
-
-    if (!mGlyphIDs) {
-        mGlyphIDs = new nsDataHashtable<nsUint32HashKey,uint32_t>(128);
-    }
-
-    uint32_t gid;
-    if (mGlyphIDs->Get(aUnicode, &gid)) {
-        return gid;
-    }
-
-    AutoDC dc;
-    AutoSelectFont fs(dc.GetDC(), GetHFONT());
-
-    wchar_t ch = aUnicode;
-    WORD glyph;
-    DWORD ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
-                                 GGI_MARK_NONEXISTING_GLYPHS);
-    if (ret == GDI_ERROR || glyph == 0xFFFF) {
-        return 0;
-    }
-
-    mGlyphIDs->Put(aUnicode, glyph);
-    return glyph;
-}
-
 int32_t
 gfxGDIFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
 {
     if (!mGlyphWidths) {
         mGlyphWidths = new nsDataHashtable<nsUint32HashKey,int32_t>(200);
     }
 
     int32_t width;
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -44,61 +44,56 @@ public:
                                uint32_t aStart, uint32_t aEnd,
                                BoundingBoxType aBoundingBoxType,
                                gfxContext *aContextForTightBoundingBox,
                                Spacing *aSpacing);
 
     /* required for MathML to suppress effects of ClearType "padding" */
     virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption);
 
-    // If the font has a cmap table, we handle it purely with harfbuzz;
-    // but if not (e.g. .fon fonts), we'll use a GDI callback to get glyphs.
-    virtual bool ProvidesGetGlyph() const {
-        return !mFontEntry->HasCmapTable();
-    }
-
-    virtual uint32_t GetGlyph(uint32_t aUnicode, uint32_t aVarSelector);
-
-    virtual bool ProvidesGlyphWidths() const { return true; }
+    virtual bool ProvidesGlyphWidths() { return true; }
 
     // get hinted glyph width in pixels as 16.16 fixed-point value
     virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
 
     virtual FontType GetType() const { return FONT_TYPE_GDI; }
 
 protected:
-    /* override to ensure the cairo font is set up properly */
+    virtual void CreatePlatformShaper();
+
+    /* override to check for uniscribe failure and fall back to GDI */
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *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);
 
+    // mPlatformShaper is used for the GDI shaper, mUniscribeShaper
+    // for the Uniscribe version if needed
+    nsAutoPtr<gfxFontShaper>   mUniscribeShaper;
+
     HFONT                 mFont;
     cairo_font_face_t    *mFontFace;
 
     Metrics              *mMetrics;
     uint32_t              mSpaceGlyph;
 
     bool                  mNeedsBold;
 
-    // cache of glyph IDs (used for non-sfnt fonts only)
-    nsAutoPtr<nsDataHashtable<nsUint32HashKey,uint32_t> > mGlyphIDs;
-
     // cache of glyph widths in 16.16 fixed-point pixels
     nsAutoPtr<nsDataHashtable<nsUint32HashKey,int32_t> > mGlyphWidths;
 };
 
 #endif /* GFX_GDIFONT_H */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxGDIShaper.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//#define FORCE_PR_LOG
+
+#include "gfxGDIShaper.h"
+
+/**********************************************************************
+ *
+ * class gfxGDIShaper
+ *
+ **********************************************************************/
+
+bool
+gfxGDIShaper::ShapeText(gfxContext      *aContext,
+                        const char16_t *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 = aLength;
+    AutoFallibleTArray<WORD,500> glyphArray;
+    if (!glyphArray.SetLength(length)) {
+        return false;
+    }
+    WORD *glyphs = glyphArray.Elements();
+
+    DWORD ret = ::GetGlyphIndicesW(dc, char16ptr_t(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;
+    }
+ 
+    SIZE size;
+    AutoFallibleTArray<int,500> partialWidthArray;
+    if (!partialWidthArray.SetLength(length)) {
+        return false;
+    }
+
+    BOOL success = ::GetTextExtentExPointI(dc,
+                                           glyphs,
+                                           length,
+                                           INT_MAX,
+                                           nullptr,
+                                           partialWidthArray.Elements(),
+                                           &size);
+    if (!success) {
+        return false;
+    }
+
+    gfxTextRun::CompressedGlyph g;
+    gfxTextRun::CompressedGlyph *charGlyphs =
+        aShapedText->GetCharacterGlyphs();
+    uint32_t i;
+    int32_t lastWidth = 0;
+    int32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
+    for (i = 0; i < length; ++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(aText[i]),
+                     "Invalid character detected!");
+        bool atClusterStart = charGlyphs[offset].IsClusterStart();
+        if (advanceAppUnits >= 0 &&
+            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
+            atClusterStart)
+        {
+            charGlyphs[offset].SetSimpleGlyph(advanceAppUnits, glyph);
+        } else {
+            gfxShapedText::DetailedGlyph details;
+            details.mGlyphID = glyph;
+            details.mAdvance = advanceAppUnits;
+            details.mXOffset = 0;
+            details.mYOffset = 0;
+            aShapedText->SetGlyphs(offset,
+                                   g.SetComplex(atClusterStart, true, 1),
+                                   &details);
+        }
+    }
+
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxGDIShaper.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_GDISHAPER_H
+#define GFX_GDISHAPER_H
+
+#include "gfxGDIFont.h"
+
+class gfxGDIShaper : public gfxFontShaper
+{
+public:
+    gfxGDIShaper(gfxGDIFont *aFont)
+        : gfxFontShaper(aFont)
+    {
+        MOZ_COUNT_CTOR(gfxGDIShaper);
+    }
+
+    virtual ~gfxGDIShaper()
+    {
+        MOZ_COUNT_DTOR(gfxGDIShaper);
+    }
+
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const char16_t *aText,
+                           uint32_t         aOffset,
+                           uint32_t         aLength,
+                           int32_t          aScript,
+                           gfxShapedText   *aShapedText);
+};
+
+#endif /* GFX_GDISHAPER_H */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxUniscribeShaper.cpp
@@ -0,0 +1,527 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxTypes.h"
+
+#include "gfxContext.h"
+#include "gfxUniscribeShaper.h"
+#include "gfxWindowsPlatform.h"
+
+#include "gfxFontTest.h"
+
+#include "cairo.h"
+#include "cairo-win32.h"
+
+#include <windows.h>
+
+#include "nsTArray.h"
+
+#include "prinit.h"
+
+/**********************************************************************
+ *
+ * class gfxUniscribeShaper
+ *
+ **********************************************************************/
+
+#define ESTIMATE_MAX_GLYPHS(L) (((3 * (L)) >> 1) + 16)
+
+class UniscribeItem
+{
+public:
+    UniscribeItem(gfxContext *aContext, HDC aDC,
+                  gfxUniscribeShaper *aShaper,
+                  const char16_t *aString, uint32_t aLength,
+                  SCRIPT_ITEM *aItem, uint32_t aIVS) :
+        mContext(aContext), mDC(aDC),
+        mShaper(aShaper),
+        mItemString(aString), mItemLength(aLength), 
+        mAlternativeString(nullptr), mScriptItem(aItem),
+        mScript(aItem->a.eScript),
+        mNumGlyphs(0), mMaxGlyphs(ESTIMATE_MAX_GLYPHS(aLength)),
+        mFontSelected(false), mIVS(aIVS)
+    {
+        // See bug 394751 for details.
+        NS_ASSERTION(mMaxGlyphs < 65535,
+                     "UniscribeItem is too big, ScriptShape() will fail!");
+    }
+
+    ~UniscribeItem() {
+        free(mAlternativeString);
+    }
+
+    bool AllocateBuffers() {
+        return (mGlyphs.SetLength(mMaxGlyphs) &&
+                mClusters.SetLength(mItemLength + 1) &&
+                mAttr.SetLength(mMaxGlyphs));
+    }
+
+    /* possible return values:
+     * S_OK - things succeeded
+     * GDI_ERROR - things failed to shape.  Might want to try again after calling DisableShaping()
+     */
+
+    HRESULT Shape() {
+        HRESULT rv;
+        HDC shapeDC = nullptr;
+
+        char16ptr_t str = mAlternativeString ? mAlternativeString : mItemString;
+
+        mScriptItem->a.fLogicalOrder = true; 
+        SCRIPT_ANALYSIS sa = mScriptItem->a;
+
+        while (true) {
+
+            rv = ScriptShape(shapeDC, mShaper->ScriptCache(),
+                             str, mItemLength,
+                             mMaxGlyphs, &sa,
+                             mGlyphs.Elements(), mClusters.Elements(),
+                             mAttr.Elements(), &mNumGlyphs);
+
+            if (rv == E_OUTOFMEMORY) {
+                mMaxGlyphs *= 2;
+                if (!mGlyphs.SetLength(mMaxGlyphs) ||
+                    !mAttr.SetLength(mMaxGlyphs)) {
+                    return E_OUTOFMEMORY;
+                }
+                continue;
+            }
+
+            // Uniscribe can't do shaping with some fonts, so it sets the 
+            // fNoGlyphIndex flag in the SCRIPT_ANALYSIS structure to indicate
+            // this.  This occurs with CFF fonts loaded with 
+            // AddFontMemResourceEx but it's not clear what the other cases
+            // are. We return an error so our caller can try fallback shaping.
+            // see http://msdn.microsoft.com/en-us/library/ms776520(VS.85).aspx
+
+            if (sa.fNoGlyphIndex) {
+                return GDI_ERROR;
+            }
+
+            if (rv == E_PENDING) {
+                if (shapeDC == mDC) {
+                    // we already tried this once, something failed, give up
+                    return E_PENDING;
+                }
+
+                SelectFont();
+
+                shapeDC = mDC;
+                continue;
+            }
+
+            // http://msdn.microsoft.com/en-us/library/dd368564(VS.85).aspx:
+            // Uniscribe will return this if "the font corresponding to the
+            // DC does not support the script required by the run...".
+            // In this case, we'll set the script code to SCRIPT_UNDEFINED
+            // and try again, so that we'll at least get glyphs even though
+            // they won't necessarily have proper shaping.
+            // (We probably shouldn't have selected this font at all,
+            // but it's too late to fix that here.)
+            if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
+                sa.eScript = SCRIPT_UNDEFINED;
+                NS_WARNING("Uniscribe says font does not support script needed");
+                continue;
+            }
+
+            // Prior to Windows 7, Uniscribe didn't support Ideographic Variation
+            // Selectors. Replace the UVS glyph manually.
+            if (mIVS) {
+                uint32_t lastChar = str[mItemLength - 1];
+                if (NS_IS_LOW_SURROGATE(lastChar)
+                    && NS_IS_HIGH_SURROGATE(str[mItemLength - 2])) {
+                    lastChar = SURROGATE_TO_UCS4(str[mItemLength - 2], lastChar);
+                }
+                uint16_t glyphId = mShaper->GetFont()->GetUVSGlyph(lastChar, mIVS);
+                if (glyphId) {
+                    mGlyphs[mNumGlyphs - 1] = glyphId;
+                }
+            }
+
+            return rv;
+        }
+    }
+
+    bool ShapingEnabled() {
+        return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
+    }
+    void DisableShaping() {
+        mScriptItem->a.eScript = SCRIPT_UNDEFINED;
+        // Note: If we disable the shaping by using SCRIPT_UNDEFINED and
+        // the string has the surrogate pair, ScriptShape API is
+        // *sometimes* crashed. Therefore, we should replace the surrogate
+        // pair to U+FFFD. See bug 341500.
+        GenerateAlternativeString();
+    }
+    void EnableShaping() {
+        mScriptItem->a.eScript = mScript;
+        if (mAlternativeString) {
+            free(mAlternativeString);
+            mAlternativeString = nullptr;
+        }
+    }
+
+    bool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, uint32_t aGlyphIndex) {
+        return (mGlyphs[aGlyphIndex] == aSFP->wgDefault);
+    }
+
+
+    HRESULT Place() {
+        HRESULT rv;
+        HDC placeDC = nullptr;
+
+        if (!mOffsets.SetLength(mNumGlyphs) ||
+            !mAdvances.SetLength(mNumGlyphs)) {
+            return E_OUTOFMEMORY;
+        }
+
+        SCRIPT_ANALYSIS sa = mScriptItem->a;
+
+        while (true) {
+            rv = ScriptPlace(placeDC, mShaper->ScriptCache(),
+                             mGlyphs.Elements(), mNumGlyphs,
+                             mAttr.Elements(), &sa,
+                             mAdvances.Elements(), mOffsets.Elements(), nullptr);
+
+            if (rv == E_PENDING) {
+                SelectFont();
+                placeDC = mDC;
+                continue;
+            }
+
+            if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
+                sa.eScript = SCRIPT_UNDEFINED;
+                continue;
+            }
+
+            break;
+        }
+
+        return rv;
+    }
+
+    void ScriptFontProperties(SCRIPT_FONTPROPERTIES *sfp) {
+        HRESULT rv;
+
+        memset(sfp, 0, sizeof(SCRIPT_FONTPROPERTIES));
+        sfp->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
+        rv = ScriptGetFontProperties(nullptr, mShaper->ScriptCache(),
+                                     sfp);
+        if (rv == E_PENDING) {
+            SelectFont();
+            rv = ScriptGetFontProperties(mDC, mShaper->ScriptCache(),
+                                         sfp);
+        }
+    }
+
+    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<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
+        gfxShapedText::CompressedGlyph g;
+        gfxShapedText::CompressedGlyph *charGlyphs =
+            aShapedText->GetCharacterGlyphs();
+        const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
+        while (offset < mItemLength) {
+            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);
+            } 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) {
+                        glyphCount = mClusters[nextClusterOffset] - k;
+                        break;
+                    }
+                }
+                uint32_t j;
+                for (j = 1; j < glyphCount; ++j) {
+                    if (IsGlyphMissing(&sfp, k + j)) {
+                        missing = true;
+                    }
+                }
+                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])) {
+                        aShapedText->SetMissingGlyph(runOffset,
+                                                     SURROGATE_TO_UCS4(mItemString[offset],
+                                                                       mItemString[offset + 1]),
+                                                     mShaper->GetFont());
+                    } else {
+                        aShapedText->SetMissingGlyph(runOffset, mItemString[offset],
+                                                     mShaper->GetFont());
+                    }
+                } else if (glyphCount == 1 && advance >= 0 &&
+                    mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
+                    gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
+                    gfxShapedText::CompressedGlyph::IsSimpleGlyphID(glyph) &&
+                    atClusterStart)
+                {
+                    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 *
+                            aShapedText->GetDirection();
+                        details->mYOffset = - float(mOffsets[k + i].dv) * appUnitsPerDevUnit;
+                    }
+                    aShapedText->SetGlyphs(runOffset,
+                                           g.SetComplex(atClusterStart, true,
+                                                        glyphCount),
+                                           detailedGlyphs.Elements());
+                }
+            }
+            ++offset;
+        }
+    }
+
+    void SelectFont() {
+        if (mFontSelected)
+            return;
+
+        cairo_t *cr = mContext->GetCairo();
+
+        cairo_set_font_face(cr, mShaper->GetFont()->CairoFontFace());
+        cairo_set_font_size(cr, mShaper->GetFont()->GetAdjustedSize());
+        cairo_scaled_font_t *scaledFont = mShaper->GetFont()->CairoScaledFont();
+        cairo_win32_scaled_font_select_font(scaledFont, mDC);
+
+        mFontSelected = true;
+    }
+
+private:
+
+    void GenerateAlternativeString() {
+        if (mAlternativeString)
+            free(mAlternativeString);
+        mAlternativeString = (char16_t *)malloc(mItemLength * sizeof(char16_t));
+        if (!mAlternativeString)
+            return;
+        memcpy((void *)mAlternativeString, (const void *)mItemString,
+               mItemLength * sizeof(char16_t));
+        for (uint32_t i = 0; i < mItemLength; i++) {
+            if (NS_IS_HIGH_SURROGATE(mItemString[i]) || NS_IS_LOW_SURROGATE(mItemString[i]))
+                mAlternativeString[i] = char16_t(0xFFFD);
+        }
+    }
+
+private:
+    nsRefPtr<gfxContext> mContext;
+    HDC mDC;
+    gfxUniscribeShaper *mShaper;
+
+    SCRIPT_ITEM *mScriptItem;
+    WORD mScript;
+
+public:
+    // these point to the full string/length of the item
+    const char16_t *mItemString;
+    const uint32_t mItemLength;
+
+private:
+    char16_t *mAlternativeString;
+
+#define AVERAGE_ITEM_LENGTH 40
+
+    AutoFallibleTArray<WORD, uint32_t(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mGlyphs;
+    AutoFallibleTArray<WORD, AVERAGE_ITEM_LENGTH + 1> mClusters;
+    AutoFallibleTArray<SCRIPT_VISATTR, uint32_t(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mAttr;
+ 
+    AutoFallibleTArray<GOFFSET, 2 * AVERAGE_ITEM_LENGTH> mOffsets;
+    AutoFallibleTArray<int, 2 * AVERAGE_ITEM_LENGTH> mAdvances;
+
+#undef AVERAGE_ITEM_LENGTH
+
+    int mMaxGlyphs;
+    int mNumGlyphs;
+    uint32_t mIVS;
+
+    bool mFontSelected;
+};
+
+class Uniscribe
+{
+public:
+    Uniscribe(const char16_t *aString,
+              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 = 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, mLength,
+                                   maxItems, &mControl, &mState,
+                                   mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
+            maxItems *= 2;
+            if (!mItems.SetLength(maxItems + 1)) {
+                return 0;
+            }
+            Init();
+        }
+
+        return mNumItems;
+    }
+
+    SCRIPT_ITEM *ScriptItem(uint32_t i) {
+        NS_ASSERTION(i <= (uint32_t)mNumItems, "Trying to get out of bounds item");
+        return &mItems[i];
+    }
+
+private:
+    char16ptr_t      mString;
+    gfxShapedText   *mShapedText;
+    uint32_t         mOffset;
+    uint32_t         mLength;
+
+    SCRIPT_CONTROL mControl;
+    SCRIPT_STATE   mState;
+    FallibleTArray<SCRIPT_ITEM> mItems;
+    int mNumItems;
+};
+
+
+bool
+gfxUniscribeShaper::ShapeText(gfxContext      *aContext,
+                              const char16_t *aText,
+                              uint32_t         aOffset,
+                              uint32_t         aLength,
+                              int32_t          aScript,
+                              gfxShapedText   *aShapedText)
+{
+    DCFromContext aDC(aContext);
+ 
+    bool result = true;
+    HRESULT rv;
+
+    Uniscribe us(aText, aShapedText, aOffset, aLength);
+
+    /* itemize the string */
+    int numItems = us.Itemize();
+
+    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
+            && aText[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
+            && uint32_t(aText[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
+            <= L_SURROGATE(kUnicodeVS256) - L_SURROGATE(kUnicodeVS17)) {
+
+            ivs = SURROGATE_TO_UCS4(aText[iCharPosNext],
+                                    aText[iCharPosNext + 1]);
+        } else {
+            ivs = 0;
+        }
+
+        UniscribeItem item(aContext, aDC, this,
+                           aText + iCharPos,
+                           iCharPosNext - iCharPos,
+                           us.ScriptItem(i), ivs);
+        if (!item.AllocateBuffers()) {
+            result = false;
+            break;
+        }
+
+        if (!item.ShapingEnabled()) {
+            item.EnableShaping();
+        }
+
+        rv = item.Shape();
+        if (FAILED(rv)) {
+            // we know we have the glyphs to display this font already
+            // so Uniscribe just doesn't know how to shape the script.
+            // Render the glyphs without shaping.
+            item.DisableShaping();
+            rv = item.Shape();
+        }
+#ifdef DEBUG
+        if (FAILED(rv)) {
+            NS_WARNING("Uniscribe failed to shape with font");
+        }
+#endif
+
+        if (SUCCEEDED(rv)) {
+            rv = item.Place();
+#ifdef DEBUG
+            if (FAILED(rv)) {
+                // crap fonts may fail when placing (e.g. funky free fonts)
+                NS_WARNING("Uniscribe failed to place with font");
+            }
+#endif
+        }
+
+        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(aShapedText, aOffset);
+    }
+
+    RestoreDC(aDC, -1);
+
+    return result;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxUniscribeShaper.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_UNISCRIBESHAPER_H
+#define GFX_UNISCRIBESHAPER_H
+
+#include "gfxTypes.h"
+#include "gfxGDIFont.h"
+
+#include <usp10.h>
+#include <cairo-win32.h>
+
+
+class gfxUniscribeShaper : public gfxFontShaper
+{
+public:
+    gfxUniscribeShaper(gfxGDIFont *aFont)
+        : gfxFontShaper(aFont)
+        , mScriptCache(nullptr)
+    {
+        MOZ_COUNT_CTOR(gfxUniscribeShaper);
+    }
+
+    virtual ~gfxUniscribeShaper()
+    {
+        MOZ_COUNT_DTOR(gfxUniscribeShaper);
+    }
+
+    virtual bool ShapeText(gfxContext      *aContext,
+                           const char16_t *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;
+
+    enum {
+        kUnicodeVS17 = gfxFontUtils::kUnicodeVS17,
+        kUnicodeVS256 = gfxFontUtils::kUnicodeVS256
+    };
+};
+
+#endif /* GFX_UNISCRIBESHAPER_H */
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -166,32 +166,36 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
         'gfxGDIFont.h',
         'gfxGDIFontList.h',
         'gfxPDFSurface.h',
         'gfxPlatformFontList.h',
         'gfxWindowsNativeDrawing.h',
         'gfxWindowsPlatform.h',
         'gfxWindowsSurface.h',
     ]
-    # gfxGDIFontList.cpp forces NSPR logging, so it cannot be built in unified mode.
+    # gfxGDIFontList.cpp and gfxGDIShaper.cpp force NSPR logging, so they cannot be built in unified mode.
     SOURCES += [
         'gfxGDIFont.cpp',
         'gfxGDIFontList.cpp',
+        'gfxGDIShaper.cpp',
         'gfxPDFSurface.cpp',
+        'gfxUniscribeShaper.cpp',
         'gfxWindowsNativeDrawing.cpp',
         'gfxWindowsPlatform.cpp',
         'gfxWindowsSurface.cpp',
     ]
     if CONFIG['MOZ_ENABLE_DWRITE_FONT']:
         # gfxDWriteFontList.cpp forces NSPR logging, so it cannot be built in unified mode.
         SOURCES += [
             'gfxD2DSurface.cpp',
             'gfxDWriteCommon.cpp',
             'gfxDWriteFontList.cpp',
             'gfxDWriteFonts.cpp',
+            'gfxDWriteShaper.cpp',
+            'gfxDWriteTextAnalysis.cpp',
         ]
 
 # Are we targeting x86 or x64?  If so, build gfxAlphaRecoverySSE2.cpp.
 if CONFIG['INTEL_ARCHITECTURE']:
     SOURCES += ['gfxAlphaRecoverySSE2.cpp']
     # The file uses SSE2 intrinsics, so it needs special compile flags on some
     # compilers.
     SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']