Bug 387969. Use CSS 'text-rendering' property to control text quality. r=pavlov,r+sr=bzbarsky
authorroc+@cs.cmu.edu
Thu, 26 Jul 2007 02:47:43 -0700
changeset 4032 31b8a46f0c4c8fb0af384305ecb87881d571b464
parent 4031 eb71f420a4c046428c2115eb3656485d8968b739
child 4033 fa14c450f0b4036423555949a9553f149d3971e6
push idunknown
push userunknown
push dateunknown
reviewerspavlov, r
bugs387969
milestone1.9a7pre
Bug 387969. Use CSS 'text-rendering' property to control text quality. r=pavlov,r+sr=bzbarsky
gfx/thebes/public/gfxFont.h
gfx/thebes/src/gfxPangoFonts.cpp
gfx/thebes/src/gfxTextRunWordCache.cpp
gfx/thebes/src/gfxWindowsFonts.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/generic/nsTextFrameThebes.cpp
layout/svg/base/src/nsSVGGlyphFrame.cpp
modules/libpref/src/init/all.js
--- a/gfx/thebes/public/gfxFont.h
+++ b/gfx/thebes/public/gfxFont.h
@@ -484,17 +484,23 @@ public:
          * and advance width of the glyph). When not set, it may just be the
          * standard font-box even if glyphs overflow.
          */
         TEXT_NEED_BOUNDING_BOX       = 0x0200,
         /**
          * When set, optional ligatures are disabled. Ligatures that are
          * required for legible text should still be enabled.
          */
-        TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0400
+        TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0400,
+        /**
+         * When set, the textrun should favour speed of construction over
+         * quality. This may involve disabling ligatures and/or kerning or
+         * other effects.
+         */
+        TEXT_OPTIMIZE_SPEED          = 0x0800
     };
 
     /**
      * This record contains all the parameters needed to initialize a textrun.
      */
     struct Parameters {
         // A reference context suggesting where the textrun will be rendered
         gfxContext   *mContext;
--- a/gfx/thebes/src/gfxPangoFonts.cpp
+++ b/gfx/thebes/src/gfxPangoFonts.cpp
@@ -825,52 +825,64 @@ gfxPangoFontGroup::MakeTextRun(const PRU
         nsCAutoString utf8;
         PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8);
         AppendUTF16toUTF8(unicodeString, utf8);
         InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE);
     }
     return run;
 }
 
+static PRBool
+CanTakeFastPath(PRUint32 aFlags)
+{
+    // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't
+    // We need to always use Pango for RTL text, in case glyph mirroring is required
+    return (aFlags &
+            (gfxTextRunFactory::TEXT_OPTIMIZE_SPEED | gfxTextRunFactory::TEXT_IS_RTL)) ==
+        gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
+}
+
 gfxTextRun *
 gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
                                const Parameters *aParams, PRUint32 aFlags)
 {
     gfxTextRun *run = new gfxTextRun(aParams, aString, aLength, this, aFlags);
     if (!run)
         return nsnull;
 
     run->RecordSurrogates(aString);
 
     nsCAutoString utf8;
     PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8);
     AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8);
-    PRUint32 allBits = 0;
+    PRBool is8Bit = PR_FALSE;
 #if defined(ENABLE_XFT_FAST_PATH_8BIT)
-    PRUint32 i;
-    for (i = 0; i < aLength; ++i) {
-        allBits |= aString[i];
+    if (CanTakeFastPath(aFlags)) {
+        PRUint32 allBits;
+        PRUint32 i;
+        for (i = 0; i < aLength; ++i) {
+            allBits |= aString[i];
+        }
+        is8Bit = (allBits & 0xFF00) == 0;
     }
 #endif
-    PRBool is8Bit = (allBits & 0xFF00) == 0;
     InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit);
     return run;
 }
 
 void
 gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
                                PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
                                PRBool aTake8BitPath)
 {
 #if defined(ENABLE_XFT_FAST_PATH_ALWAYS)
     CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
 #else
 #if defined(ENABLE_XFT_FAST_PATH_8BIT)
-    // We need to always use Pango for RTL text, in case glyph mirroring is required
-    if (aTake8BitPath && !aTextRun->IsRightToLeft()) {
+    if (aTake8BitPath && CanTakeFastPath(aTextRun->GetFlags())) {
         CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
         return;
     }
 #endif
 
     pango_context_set_base_dir(GetFontAt(0)->GetPangoContext(),
                                (aTextRun->IsRightToLeft()
                                   ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR));
--- a/gfx/thebes/src/gfxTextRunWordCache.cpp
+++ b/gfx/thebes/src/gfxTextRunWordCache.cpp
@@ -88,26 +88,28 @@ protected:
         void        *mFontOrGroup;
         const void  *mString;
         PRUint32     mLength;
         PRUint32     mAppUnitsPerDevUnit;
         PRUint32     mStringHash;
         PRPackedBool mIsDoubleByteText;
         PRPackedBool mIsRTL;
         PRPackedBool mEnabledOptionalLigatures;
+        PRPackedBool mOptimizeSpeed;
         
         CacheHashKey(gfxTextRun *aBaseTextRun, void *aFontOrGroup,
                      PRUint32 aStart, PRUint32 aLength, PRUint32 aHash)
             : mFontOrGroup(aFontOrGroup), mString(aBaseTextRun->GetTextAt(aStart)),
               mLength(aLength),
               mAppUnitsPerDevUnit(aBaseTextRun->GetAppUnitsPerDevUnit()),
               mStringHash(aHash),
               mIsDoubleByteText((aBaseTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) == 0),
               mIsRTL(aBaseTextRun->IsRightToLeft()),
-              mEnabledOptionalLigatures((aBaseTextRun->GetFlags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) == 0)
+              mEnabledOptionalLigatures((aBaseTextRun->GetFlags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) == 0),
+              mOptimizeSpeed((aBaseTextRun->GetFlags() & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED) != 0)
         {
         }
     };
 
     class CacheHashEntry : public PLDHashEntryHdr {
     public:
         typedef const CacheHashKey &KeyType;
         typedef const CacheHashKey *KeyTypePointer;
@@ -581,17 +583,18 @@ TextRunWordCache::CacheHashEntry::KeyEqu
         return PR_FALSE;
 
     PRUint32 length = aKey->mLength;
     gfxFontGroup *fontGroup = mTextRun->GetFontGroup();
     if (!IsWordEnd(mTextRun, mWordOffset + length) ||
         GetFontOrGroup(fontGroup, mHashedByFont) != aKey->mFontOrGroup ||
         aKey->mAppUnitsPerDevUnit != mTextRun->GetAppUnitsPerDevUnit() ||
         aKey->mIsRTL != mTextRun->IsRightToLeft() ||
-        aKey->mEnabledOptionalLigatures != ((mTextRun->GetFlags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) == 0))
+        aKey->mEnabledOptionalLigatures != ((mTextRun->GetFlags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) == 0) ||
+        aKey->mOptimizeSpeed != ((mTextRun->GetFlags() & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED) != 0))
         return PR_FALSE;
 
     if (mTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
         const PRUint8 *text = mTextRun->GetText8Bit() + mWordOffset;
         if (!aKey->mIsDoubleByteText)
             return memcmp(text, aKey->mString, length) == 0;
         return CompareDifferentWidthStrings(text,
                                             static_cast<const PRUnichar *>(aKey->mString), length);
@@ -603,17 +606,18 @@ TextRunWordCache::CacheHashEntry::KeyEqu
                                             text, length);
     }
 }
 
 PLDHashNumber
 TextRunWordCache::CacheHashEntry::HashKey(const KeyTypePointer aKey)
 {
     return aKey->mStringHash + (long)aKey->mFontOrGroup + aKey->mAppUnitsPerDevUnit +
-        aKey->mIsDoubleByteText + aKey->mIsRTL*2 + aKey->mEnabledOptionalLigatures*4;
+        aKey->mIsDoubleByteText + aKey->mIsRTL*2 + aKey->mEnabledOptionalLigatures*4 +
+        aKey->mOptimizeSpeed*8;
 }
 
 static TextRunWordCache *gTextRunWordCache = nsnull;
 
 nsresult
 gfxTextRunWordCache::Init()
 {
     gTextRunWordCache = new TextRunWordCache();
--- a/gfx/thebes/src/gfxWindowsFonts.cpp
+++ b/gfx/thebes/src/gfxWindowsFonts.cpp
@@ -499,16 +499,26 @@ gfxWindowsFontGroup::GetFontAt(PRInt32 i
 }
 
 gfxFontGroup *
 gfxWindowsFontGroup::Copy(const gfxFontStyle *aStyle)
 {
     return new gfxWindowsFontGroup(mFamilies, aStyle);
 }
 
+static PRBool
+CanTakeFastPath(PRUint32 aFlags)
+{
+    // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't
+    // We need to always use Uniscribe for RTL text, in case glyph mirroring is required
+    return (aFlags &
+            (gfxTextRunFactory::TEXT_OPTIMIZE_SPEED | gfxTextRunFactory::TEXT_IS_RTL)) ==
+        gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
+}
+
 gfxTextRun *
 gfxWindowsFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
                                  const Parameters *aParams, PRUint32 aFlags)
 {
     // XXX comment out the assertion for now since it fires too much
     //    NS_ASSERTION(!(mFlags & TEXT_NEED_BOUNDING_BOX),
     //                 "Glyph extents not yet supported");
 
@@ -517,18 +527,18 @@ gfxWindowsFontGroup::MakeTextRun(const P
         return nsnull;
     NS_ASSERTION(aParams->mContext, "MakeTextRun called without a gfxContext");
 
     textRun->RecordSurrogates(aString);
     
 #ifdef FORCE_UNISCRIBE
     const PRBool isComplex = PR_TRUE;
 #else
-    const PRBool isComplex = ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK ||
-                             textRun->IsRightToLeft();
+    const PRBool isComplex = !CanTakeFastPath(aFlags) ||
+                             ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK;
 #endif
     if (isComplex)
         InitTextRunUniscribe(aParams->mContext, textRun, aString, aLength);
     else
         InitTextRunGDI(aParams->mContext, textRun, aString, aLength);
 
     return textRun;
 }
@@ -542,17 +552,17 @@ gfxWindowsFontGroup::MakeTextRun(const P
     gfxTextRun *textRun = new gfxTextRun(aParams, aString, aLength, this, aFlags);
     if (!textRun)
         return nsnull;
     NS_ASSERTION(aParams->mContext, "MakeTextRun called without a gfxContext");
 
 #ifdef FORCE_UNISCRIBE
     const PRBool isComplex = PR_TRUE;
 #else
-    const PRBool isComplex = textRun->IsRightToLeft();
+    const PRBool isComplex = !CanTakeFastPath(aFlags);
 #endif
 
     /* We can only call GDI "A" functions if this is a true 7bit ASCII string,
        because they interpret code points from 0x80-0xFF as if they were
        in the system code page. */
     if (!isComplex && (aFlags & TEXT_IS_ASCII)) {
         InitTextRunGDI(aParams->mContext, textRun,
                        reinterpret_cast<const char*>(aString), aLength);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -60,19 +60,21 @@
 #include "nsRegion.h"
 #include "nsFrameManager.h"
 #include "nsBlockFrame.h"
 #include "nsBidiPresUtils.h"
 #include "gfxIImageFrame.h"
 #include "imgIContainer.h"
 #include "gfxRect.h"
 #include "gfxContext.h"
+#include "gfxFont.h"
 #include "nsIImage.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsCSSRendering.h"
+#include "nsContentUtils.h"
 
 #ifdef MOZ_SVG_FOREIGNOBJECT
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGUtils.h"
 #include "nsSVGOuterSVGFrame.h"
 #endif
 
 /**
@@ -2328,8 +2330,43 @@ nsLayoutUtils::FrameHasTransparency(nsIF
   if (bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT)
     return PR_TRUE;
   if (NS_GET_A(bg->mBackgroundColor) < 255)
     return PR_TRUE;
   if (bg->mBackgroundClip != NS_STYLE_BG_CLIP_BORDER)
     return PR_TRUE;
   return PR_FALSE;
 }
+
+static PRBool
+IsNonzeroCoord(const nsStyleCoord& aCoord)
+{
+  if (eStyleUnit_Coord == aCoord.GetUnit())
+    return aCoord.GetCoordValue() != 0;
+  return PR_FALSE;
+}
+
+/* static */ PRUint32
+nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
+                                       const nsStyleText* aStyleText,
+                                       const nsStyleFont* aStyleFont)
+{
+  PRUint32 result = 0;
+  if (IsNonzeroCoord(aStyleText->mLetterSpacing)) {
+    result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
+  }
+#ifdef MOZ_SVG
+  switch (aStyleContext->GetStyleSVG()->mTextRendering) {
+  case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
+    result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
+    break;
+  case NS_STYLE_TEXT_RENDERING_AUTO:
+    if (aStyleFont->mFont.size <
+        aStyleContext->PresContext()->GetAutoQualityMinFontSize()) {
+      result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
+    }
+    break;
+  default:
+    break;
+  }
+#endif
+  return result;
+}
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -695,11 +695,22 @@ public:
   static PRBool HasNonZeroSide(const nsStyleSides& aSides);
 
   /**
    * Determine if a widget is likely to require transparency or translucency.
    *   @param aFrame the frame of a <window>, <popup> or <menupopup> element.
    *   @return a value suitable for passing to SetWindowTranslucency
    */
   static PRBool FrameHasTransparency(nsIFrame* aFrame);
+
+  /**
+   * Get textrun construction flags determined by a given style; in particular
+   * some combination of:
+   * -- TEXT_DISABLE_OPTIONAL_LIGATURES if letter-spacing is in use
+   * -- TEXT_OPTIMIZE_SPEED if the text-rendering CSS property and font size
+   * and prefs indicate we should be optimizing for speed over quality
+   */
+  static PRUint32 GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
+                                          const nsStyleText* aStyleText,
+                                          const nsStyleFont* aStyleFont);
 };
 
 #endif // nsLayoutUtils_h__
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -664,16 +664,18 @@ nsPresContext::ClearStyleDataAndReflow()
                                                   &changeList, nsChangeHint(0));
     // Tell the style set it's safe to destroy the old rule tree
     mShell->StyleSet()->EndReconstruct();
     // Tell the frame constructor to process the required changes
     mShell->FrameConstructor()->ProcessRestyledFrames(changeList);
   }
 }
 
+static const char sMinFontSizePref[] = "browser.display.auto_quality_min_font_size";
+
 void
 nsPresContext::PreferenceChanged(const char* aPrefName)
 {
   if (!nsCRT::strcmp(aPrefName, "layout.css.dpi")) {
     nsRect bounds(mVisibleArea);
     bounds *= 1.0f / AppUnitsPerDevPixel();
     if (mDeviceContext->CheckDPIChange() && mShell) {
       mDeviceContext->FlushFontCache();
@@ -682,16 +684,21 @@ nsPresContext::PreferenceChanged(const c
       nscoord width = DevPixelsToAppUnits(bounds.width);
       nscoord height = DevPixelsToAppUnits(bounds.height);
       vm->SetWindowDimensions(width, height);
 
       ClearStyleDataAndReflow();
     }
     return;
   }
+  if (!nsCRT::strcmp(aPrefName, sMinFontSizePref)) {
+    mAutoQualityMinFontSizePixelsPref = nsContentUtils::GetIntPref(sMinFontSizePref);
+    ClearStyleDataAndReflow();
+    return;
+  }
   // we use a zero-delay timer to coalesce multiple pref updates
   if (!mPrefChangedTimer)
   {
     mPrefChangedTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (!mPrefChangedTimer)
       return;
     mPrefChangedTimer->InitWithFuncCallback(nsPresContext::PrefChangedUpdateTimerCallback, (void*)this, 0, nsITimer::TYPE_ONE_SHOT);
   }
@@ -775,16 +782,19 @@ nsPresContext::Init(nsIDeviceContext* aD
 #ifdef IBMBIDI
   nsContentUtils::RegisterPrefCallback("bidi.", PrefChangedCallback,
                                        this);
 #endif
   nsContentUtils::RegisterPrefCallback("layout.css.dpi",
                                        nsPresContext::PrefChangedCallback,
                                        this);
 
+  // This is observed thanks to the browser.display. observer above.
+  mAutoQualityMinFontSizePixelsPref = nsContentUtils::GetIntPref(sMinFontSizePref);
+
   rv = mEventManager->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mEventManager->SetPresContext(this);
 
 #ifdef DEBUG
   mInitialized = PR_TRUE;
 #endif
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -465,16 +465,20 @@ public:
   void SetTextZoom(float aZoom) {
     mTextZoom = aZoom;
     ClearStyleDataAndReflow();
   }
 
   float GetFullZoom() {return mDeviceContext->GetPixelScale();}
   void SetFullZoom(float aZoom);
 
+  nscoord GetAutoQualityMinFontSize() {
+    return DevPixelsToAppUnits(mAutoQualityMinFontSizePixelsPref);
+  }
+  
   static PRInt32 AppUnitsPerCSSPixel() { return nsIDeviceContext::AppUnitsPerCSSPixel(); }
   PRInt32 AppUnitsPerDevPixel() const  { return mDeviceContext->AppUnitsPerDevPixel(); }
   PRInt32 AppUnitsPerInch() const      { return mDeviceContext->AppUnitsPerInch(); }
 
   static nscoord CSSPixelsToAppUnits(PRInt32 aPixels)
   { return NSIntPixelsToAppUnits(aPixels,
                                  nsIDeviceContext::AppUnitsPerCSSPixel()); }
 
@@ -750,16 +754,17 @@ protected:
 
   nsILinkHandler*       mLinkHandler;   // [WEAK]
   nsIAtom*              mLangGroup;     // [STRONG]
 
   nsInterfaceHashtable<nsVoidPtrHashKey, nsImageLoader> mImageLoaders;
   nsWeakPtr             mContainer;
 
   float                 mTextZoom;      // Text zoom, defaults to 1.0
+  PRInt32               mAutoQualityMinFontSizePixelsPref;
 
 #ifdef IBMBIDI
   nsBidiPresUtils*      mBidiUtils;
 #endif
 
   nsCOMPtr<nsITheme> mTheme;
   nsCOMPtr<nsILanguageAtomService> mLangService;
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -1280,22 +1280,16 @@ static nscoord StyleToCoord(const nsStyl
   if (eStyleUnit_Coord == aCoord.GetUnit()) {
     return aCoord.GetCoordValue();
   } else {
     return 0;
   }
 }
 
 static PRBool
-ShouldDisableOptionalLigatures(const nsStyleText* aTextStyle)
-{
-  return StyleToCoord(aTextStyle->mLetterSpacing) != 0;
-}
-
-static PRBool
 HasTerminalNewline(const nsTextFrame* aFrame)
 {
   if (aFrame->GetContentLength() == 0)
     return PR_FALSE;
   const nsTextFragment* frag = aFrame->GetContent()->GetText();
   return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n';
 }
 
@@ -1315,19 +1309,23 @@ BuildTextRunsScanner::ContinueTextRunAcr
   // until after reflow has broken up the frame into one (or more) frames per
   // line. That's OK though.
   if (textStyle1->WhiteSpaceIsSignificant() && HasTerminalNewline(aFrame1))
     return PR_FALSE;
 
   nsStyleContext* sc2 = aFrame2->GetStyleContext();
   if (sc1 == sc2)
     return PR_TRUE;
-  return sc1->GetStyleFont()->mFont.BaseEquals(sc2->GetStyleFont()->mFont) &&
+  const nsStyleFont* fontStyle1 = sc1->GetStyleFont();
+  const nsStyleFont* fontStyle2 = sc2->GetStyleFont();
+  const nsStyleText* textStyle2 = sc2->GetStyleText();
+  return fontStyle1->mFont.BaseEquals(fontStyle2->mFont) &&
     sc1->GetStyleVisibility()->mLangGroup == sc2->GetStyleVisibility()->mLangGroup &&
-    ShouldDisableOptionalLigatures(textStyle1) == ShouldDisableOptionalLigatures(sc2->GetStyleText());
+    nsLayoutUtils::GetTextRunFlagsForStyle(sc1, textStyle1, fontStyle1) ==
+      nsLayoutUtils::GetTextRunFlagsForStyle(sc2, textStyle2, fontStyle2);
 }
 
 void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
 {
   // First check if we can extend the current mapped frame block. This is common.
   if (mMappedFlows.Length() > 0) {
     MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
     if (mappedFlow->mEndFrame == aFrame) {
@@ -1526,34 +1524,38 @@ BuildTextRunsScanner::BuildTextRunForFra
 
   PRUint32 finalMappedFlowCount = 0;
   PRUint32 currentTransformedTextOffset = 0;
 
   PRUint32 nextBreakIndex = 0;
   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
 
   PRUint32 i;
+  const nsStyleText* textStyle = nsnull;
+  const nsStyleFont* fontStyle = nsnull;
+  nsStyleContext* lastStyleContext = nsnull;
   for (i = 0; i < mMappedFlows.Length(); ++i) {
     MappedFlow* mappedFlow = &mMappedFlows[i];
     nsTextFrame* f = mappedFlow->mStartFrame;
 
     mappedFlow->mTransformedTextOffset = currentTransformedTextOffset;
 
+    lastStyleContext = f->GetStyleContext();
     // Detect use of text-transform or font-variant anywhere in the run
-    const nsStyleText* textStyle = f->GetStyleText();
+    textStyle = f->GetStyleText();
     if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) {
       anyTextTransformStyle = PR_TRUE;
     }
     textFlags |= GetSpacingFlags(textStyle->mLetterSpacing);
     textFlags |= GetSpacingFlags(textStyle->mWordSpacing);
     PRBool compressWhitespace = !textStyle->WhiteSpaceIsSignificant();
     if (NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign && compressWhitespace) {
       textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
     }
-    const nsStyleFont* fontStyle = f->GetStyleFont();
+    fontStyle = f->GetStyleFont();
     if (NS_STYLE_FONT_VARIANT_SMALL_CAPS == fontStyle->mFont.variant) {
       anySmallcapsStyle = PR_TRUE;
     }
 
     // Figure out what content is included in this flow.
     nsIContent* content = f->GetContent();
     const nsTextFragment* frag = content->GetText();
     PRInt32 contentStart = mappedFlow->mContentOffset;
@@ -1703,20 +1705,21 @@ BuildTextRunsScanner::BuildTextRunForFra
     textFlags |= gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS;
   }
   if (mBidiEnabled && (NS_GET_EMBEDDING_LEVEL(firstFrame) & 1)) {
     textFlags |= gfxTextRunFactory::TEXT_IS_RTL;
   }
   if (mTrimNextRunLeadingWhitespace) {
     textFlags |= nsTextFrameUtils::TEXT_TRAILING_WHITESPACE;
   }
-  if (ShouldDisableOptionalLigatures(firstFrame->GetStyleText())) {
-    textFlags |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
-  }
- 
+  // ContinueTextRunAcrossFrames guarantees that it doesn't matter which
+  // frame's style is used, so use the last frame's
+  textFlags |= nsLayoutUtils::GetTextRunFlagsForStyle(lastStyleContext,
+      textStyle, fontStyle);
+
   gfxSkipChars skipChars;
   skipChars.TakeFrom(&builder);
   // Convert linebreak coordinates to transformed string offsets
   NS_ASSERTION(nextBreakIndex == mLineBreakBeforeFrames.Length(),
                "Didn't find all the frames to break-before...");
   gfxSkipCharsIterator iter(skipChars);
   for (i = 0; i < nextBreakIndex; ++i) {
     PRUint32* breakPoint = &textBreakPoints[i];
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -1346,18 +1346,20 @@ nsSVGGlyphFrame::GetTextRun(gfxContext *
   // coordinates into thebes and pulling metrics out.
   //
   // References:
   //   https://bugzilla.mozilla.org/show_bug.cgi?id=375141
   //   http://weblogs.mozillazine.org/roc/archives/2007/03/text_text_text.html
   if (!mFontGroup)
     return nsnull;
 
+  PRUint32 flags = nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(),
+      GetStyleText(), GetStyleFont());
   return gfxTextRunCache::MakeTextRun(aText.get(), aText.Length(),
-      mFontGroup, aCtx, 1, 0);
+      mFontGroup, aCtx, 1, flags);
 }
 
 //----------------------------------------------------------------------
 // helper class
 
 nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::nsSVGAutoGlyphHelperContext(
     nsSVGGlyphFrame *aSource,
     const nsString &aText,
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -81,16 +81,18 @@ pref("browser.display.use_system_colors"
 pref("browser.display.foreground_color",    "#000000");
 pref("browser.display.background_color",    "#FFFFFF");
 pref("browser.display.force_inline_alttext", false); // true = force ALT text for missing images to be layed out inline
 // 0 = no external leading, 
 // 1 = use external leading only when font provides, 
 // 2 = add extra leading both internal leading and external leading are zero
 pref("browser.display.normal_lineheight_calc_control", 2);
 pref("browser.display.show_image_placeholders", true); // true = show image placeholders while image is loaded and when image is broken
+// min font device pixel size at which to turn on high quality
+pref("browser.display.auto_quality_min_font_size", 60);
 pref("browser.anchor_color",                "#0000EE");
 pref("browser.active_color",                "#EE0000");
 pref("browser.visited_color",               "#551A8B");
 pref("browser.underline_anchors",           true);
 pref("browser.blink_allowed",               true);
 pref("browser.enable_automatic_image_resizing", false);
 
 // See http://whatwg.org/specs/web-apps/current-work/#ping