Bug 1261552 - Introduce StaticPresData and hoist some shared functionality into it. r=heycam
authorBobby Holley <bobbyholley@gmail.com>
Thu, 31 Mar 2016 12:47:20 -0700
changeset 347751 74a65e4f87f0dc77bd550c117442173420fbe2a0
parent 347750 f3c202b18fa268a30d4782653f4d2a76ccfeda74
child 347752 767a3ddd89e46554e84b51ee9f80ff1cbd2b91ad
push id14653
push userolivier@olivieryiptong.com
push dateTue, 05 Apr 2016 19:21:01 +0000
reviewersheycam
bugs1261552
milestone48.0a1
Bug 1261552 - Introduce StaticPresData and hoist some shared functionality into it. r=heycam The complexity around the font pref cache stuff is really annoying. If we think it's unnecessary, we could remove it in a followup.
layout/base/StaticPresData.cpp
layout/base/StaticPresData.h
layout/base/moz.build
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/build/nsLayoutStatics.cpp
layout/style/nsStyleStruct.cpp
new file mode 100644
--- /dev/null
+++ b/layout/base/StaticPresData.cpp
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "mozilla/StaticPresData.h"
+
+#include "mozilla/Preferences.h"
+#include "nsPresContext.h"
+
+namespace mozilla {
+
+static StaticPresData* sSingleton = nullptr;
+
+void
+StaticPresData::Init()
+{
+  MOZ_ASSERT(!sSingleton);
+  sSingleton = new StaticPresData();
+}
+
+void
+StaticPresData::Shutdown()
+{
+  MOZ_ASSERT(sSingleton);
+  delete sSingleton;
+  sSingleton = nullptr;
+}
+
+StaticPresData*
+StaticPresData::Get()
+{
+  MOZ_ASSERT(sSingleton);
+  return sSingleton;
+}
+
+StaticPresData::StaticPresData()
+{
+  mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
+
+  mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THIN] = nsPresContext::CSSPixelsToAppUnits(1);
+  mBorderWidthTable[NS_STYLE_BORDER_WIDTH_MEDIUM] = nsPresContext::CSSPixelsToAppUnits(3);
+  mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THICK] = nsPresContext::CSSPixelsToAppUnits(5);
+}
+
+#define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
+ _pref.Assign(_s0); \
+ _pref.Append(_s1);
+
+static const char* const kGenericFont[] = {
+  ".variable.",
+  ".fixed.",
+  ".serif.",
+  ".sans-serif.",
+  ".monospace.",
+  ".cursive.",
+  ".fantasy."
+};
+
+// These are private, use the list in nsFont.h if you want a public list.
+enum {
+  eDefaultFont_Variable,
+  eDefaultFont_Fixed,
+  eDefaultFont_Serif,
+  eDefaultFont_SansSerif,
+  eDefaultFont_Monospace,
+  eDefaultFont_Cursive,
+  eDefaultFont_Fantasy,
+  eDefaultFont_COUNT
+};
+
+const LangGroupFontPrefs*
+StaticPresData::GetFontPrefsForLangHelper(nsIAtom *aLanguage,
+                                          const LangGroupFontPrefs* aPrefs) const
+{
+  // Get language group for aLanguage:
+  MOZ_ASSERT(aLanguage);
+  MOZ_ASSERT(mLangService);
+  MOZ_ASSERT(aPrefs);
+
+  nsresult rv = NS_OK;
+  nsIAtom *langGroupAtom = nullptr;
+  langGroupAtom = mLangService->GetLanguageGroup(aLanguage, &rv);
+  if (NS_FAILED(rv) || !langGroupAtom) {
+    langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe...
+  }
+
+  LangGroupFontPrefs *prefs = const_cast<LangGroupFontPrefs*>(aPrefs);
+  if (prefs->mLangGroup) { // if initialized
+    DebugOnly<uint32_t> count = 0;
+    for (;;) {
+      NS_ASSERTION(++count < 35, "Lang group count exceeded!!!");
+      if (prefs->mLangGroup == langGroupAtom) {
+        return prefs;
+      }
+      if (!prefs->mNext) {
+        break;
+      }
+      prefs = prefs->mNext;
+    }
+
+    // nothing cached, so go on and fetch the prefs for this lang group:
+    prefs = prefs->mNext = new LangGroupFontPrefs;
+  }
+
+  prefs->mLangGroup = langGroupAtom;
+
+  /* Fetch the font prefs to be used -- see bug 61883 for details.
+     Not all prefs are needed upfront. Some are fallback prefs intended
+     for the GFX font sub-system...
+
+  1) unit : assumed to be the same for all language groups -------------
+  font.size.unit = px | pt    XXX could be folded in the size... bug 90440
+
+  2) attributes for generic fonts --------------------------------------
+  font.default.[langGroup] = serif | sans-serif - fallback generic font
+  font.name.[generic].[langGroup] = current user' selected font on the pref dialog
+  font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list]
+  font.size.[generic].[langGroup] = integer - settable by the user
+  font.size-adjust.[generic].[langGroup] = "float" - settable by the user
+  font.minimum-size.[langGroup] = integer - settable by the user
+  */
+
+  nsAutoCString langGroup;
+  langGroupAtom->ToUTF8String(langGroup);
+
+  prefs->mDefaultVariableFont.size = nsPresContext::CSSPixelsToAppUnits(16);
+  prefs->mDefaultFixedFont.size = nsPresContext::CSSPixelsToAppUnits(13);
+
+  nsAutoCString pref;
+
+  // get the current applicable font-size unit
+  enum {eUnit_unknown = -1, eUnit_px, eUnit_pt};
+  int32_t unit = eUnit_px;
+
+  nsAdoptingCString cvalue =
+    Preferences::GetCString("font.size.unit");
+
+  if (!cvalue.IsEmpty()) {
+    if (cvalue.EqualsLiteral("px")) {
+      unit = eUnit_px;
+    }
+    else if (cvalue.EqualsLiteral("pt")) {
+      unit = eUnit_pt;
+    }
+    else {
+      // XXX should really send this warning to the user (Error Console?).
+      // And just default to unit = eUnit_px?
+      NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'");
+      unit = eUnit_unknown;
+    }
+  }
+
+  // get font.minimum-size.[langGroup]
+
+  MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup);
+
+  int32_t size = Preferences::GetInt(pref.get());
+  if (unit == eUnit_px) {
+    prefs->mMinimumFontSize = nsPresContext::CSSPixelsToAppUnits(size);
+  }
+  else if (unit == eUnit_pt) {
+    prefs->mMinimumFontSize = nsPresContext::CSSPointsToAppUnits(size);
+  }
+
+  nsFont* fontTypes[] = {
+    &prefs->mDefaultVariableFont,
+    &prefs->mDefaultFixedFont,
+    &prefs->mDefaultSerifFont,
+    &prefs->mDefaultSansSerifFont,
+    &prefs->mDefaultMonospaceFont,
+    &prefs->mDefaultCursiveFont,
+    &prefs->mDefaultFantasyFont
+  };
+  static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT,
+                "FontTypes array count is not correct");
+
+  // Get attributes specific to each generic font. We do not get the user's
+  // generic-font-name-to-specific-family-name preferences because its the
+  // generic name that should be fed into the cascade. It is up to the GFX
+  // code to look up the font prefs to convert generic names to specific
+  // family names as necessary.
+  nsAutoCString generic_dot_langGroup;
+  for (uint32_t eType = 0; eType < ArrayLength(fontTypes); ++eType) {
+    generic_dot_langGroup.Assign(kGenericFont[eType]);
+    generic_dot_langGroup.Append(langGroup);
+
+    nsFont* font = fontTypes[eType];
+
+    // set the default variable font (the other fonts are seen as 'generic' fonts
+    // in GFX and will be queried there when hunting for alternative fonts)
+    if (eType == eDefaultFont_Variable) {
+      MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup);
+
+      nsAdoptingString value = Preferences::GetString(pref.get());
+      if (!value.IsEmpty()) {
+        FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
+        FontFamilyType defaultType = defaultVariableName.mType;
+        NS_ASSERTION(defaultType == eFamily_serif ||
+                     defaultType == eFamily_sans_serif,
+                     "default type must be serif or sans-serif");
+        prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType);
+      }
+      else {
+        MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup);
+        value = Preferences::GetString(pref.get());
+        if (!value.IsEmpty()) {
+          FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
+          FontFamilyType defaultType = defaultVariableName.mType;
+          NS_ASSERTION(defaultType == eFamily_serif ||
+                       defaultType == eFamily_sans_serif,
+                       "default type must be serif or sans-serif");
+          prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType);
+        }
+      }
+    }
+    else {
+      if (eType == eDefaultFont_Monospace) {
+        // This takes care of the confusion whereby people often expect "monospace"
+        // to have the same default font-size as "-moz-fixed" (this tentative
+        // size may be overwritten with the specific value for "monospace" when
+        // "font.size.monospace.[langGroup]" is read -- see below)
+        prefs->mDefaultMonospaceFont.size = prefs->mDefaultFixedFont.size;
+      }
+      else if (eType != eDefaultFont_Fixed) {
+        // all the other generic fonts are initialized with the size of the
+        // variable font, but their specific size can supersede later -- see below
+        font->size = prefs->mDefaultVariableFont.size;
+      }
+    }
+
+    // Bug 84398: for spec purists, a different font-size only applies to the
+    // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|.
+    // The problem is that only GfxWin has the support for |font-size-adjust|. So for
+    // parity, we enable the ability to set a different font-size on all platforms.
+
+    // get font.size.[generic].[langGroup]
+    // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font
+    MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup);
+    size = Preferences::GetInt(pref.get());
+    if (size > 0) {
+      if (unit == eUnit_px) {
+        font->size = nsPresContext::CSSPixelsToAppUnits(size);
+      }
+      else if (unit == eUnit_pt) {
+        font->size = nsPresContext::CSSPointsToAppUnits(size);
+      }
+    }
+
+    // get font.size-adjust.[generic].[langGroup]
+    // XXX only applicable on GFX ports that handle |font-size-adjust|
+    MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup);
+    cvalue = Preferences::GetCString(pref.get());
+    if (!cvalue.IsEmpty()) {
+      font->sizeAdjust = (float)atof(cvalue.get());
+    }
+
+#ifdef DEBUG_rbs
+    printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n",
+           generic_dot_langGroup.get(),
+           NS_ConvertUTF16toUTF8(font->name).get(), font->size,
+           font->sizeAdjust);
+#endif
+  }
+
+  return prefs;
+}
+
+const nsFont*
+StaticPresData::GetDefaultFontHelper(uint8_t aFontID, nsIAtom *aLanguage,
+                                     const LangGroupFontPrefs* aPrefs) const
+{
+  MOZ_ASSERT(aLanguage);
+  MOZ_ASSERT(aPrefs);
+
+  const nsFont *font;
+  switch (aFontID) {
+    // Special (our default variable width font and fixed width font)
+    case kPresContext_DefaultVariableFont_ID:
+      font = &aPrefs->mDefaultVariableFont;
+      break;
+    case kPresContext_DefaultFixedFont_ID:
+      font = &aPrefs->mDefaultFixedFont;
+      break;
+    // CSS
+    case kGenericFont_serif:
+      font = &aPrefs->mDefaultSerifFont;
+      break;
+    case kGenericFont_sans_serif:
+      font = &aPrefs->mDefaultSansSerifFont;
+      break;
+    case kGenericFont_monospace:
+      font = &aPrefs->mDefaultMonospaceFont;
+      break;
+    case kGenericFont_cursive:
+      font = &aPrefs->mDefaultCursiveFont;
+      break;
+    case kGenericFont_fantasy:
+      font = &aPrefs->mDefaultFantasyFont;
+      break;
+    default:
+      font = nullptr;
+      NS_ERROR("invalid arg");
+      break;
+  }
+  return font;
+}
+
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/StaticPresData.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_StaticPresData_h
+#define mozilla_StaticPresData_h
+
+#include "nsAutoPtr.h"
+#include "nsCoord.h"
+#include "nsCOMPtr.h"
+#include "nsFont.h"
+#include "nsIAtom.h"
+#include "nsILanguageAtomService.h"
+
+namespace mozilla {
+
+struct LangGroupFontPrefs {
+  // Font sizes default to zero; they will be set in GetFontPreferences
+  LangGroupFontPrefs()
+    : mLangGroup(nullptr)
+    , mMinimumFontSize(0)
+    , mDefaultVariableFont(mozilla::eFamily_serif, 0)
+    , mDefaultFixedFont(mozilla::eFamily_monospace, 0)
+    , mDefaultSerifFont(mozilla::eFamily_serif, 0)
+    , mDefaultSansSerifFont(mozilla::eFamily_sans_serif, 0)
+    , mDefaultMonospaceFont(mozilla::eFamily_monospace, 0)
+    , mDefaultCursiveFont(mozilla::eFamily_cursive, 0)
+    , mDefaultFantasyFont(mozilla::eFamily_fantasy, 0)
+  {}
+
+  void Reset()
+  {
+    // Throw away any other LangGroupFontPrefs objects:
+    mNext = nullptr;
+
+    // Make GetFontPreferences reinitialize mLangGroupFontPrefs:
+    mLangGroup = nullptr;
+  }
+
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+    size_t n = 0;
+    LangGroupFontPrefs* curr = mNext;
+    while (curr) {
+      n += aMallocSizeOf(curr);
+
+      // Measurement of the following members may be added later if DMD finds
+      // it is worthwhile:
+      // - mLangGroup
+      // - mDefault*Font
+
+      curr = curr->mNext;
+    }
+    return n;
+  }
+
+  nsCOMPtr<nsIAtom> mLangGroup;
+  nscoord mMinimumFontSize;
+  nsFont mDefaultVariableFont;
+  nsFont mDefaultFixedFont;
+  nsFont mDefaultSerifFont;
+  nsFont mDefaultSansSerifFont;
+  nsFont mDefaultMonospaceFont;
+  nsFont mDefaultCursiveFont;
+  nsFont mDefaultFantasyFont;
+  nsAutoPtr<LangGroupFontPrefs> mNext;
+};
+
+/**
+ * Some functionality that has historically lived on nsPresContext does not
+ * actually need to be per-document. This singleton class serves as a host
+ * for that functionality. We delegate to it from nsPresContext where
+ * appropriate, and use it standalone in some cases as well.
+ */
+class StaticPresData
+{
+public:
+  // Initialization and shutdown of the singleton. Called exactly once.
+  static void Init();
+  static void Shutdown();
+
+  // Gets an instance of the singleton. Infallible between the calls to Init
+  // and Shutdown.
+  static StaticPresData* Get();
+
+  /**
+   * This table maps border-width enums 'thin', 'medium', 'thick'
+   * to actual nscoord values.
+   */
+  const nscoord* GetBorderWidthTable() { return mBorderWidthTable; }
+
+  /**
+   * Fetch the user's font preferences for the given aLanguage's
+   * langugage group.
+   *
+   * The original code here is pretty old, and includes an optimization
+   * whereby language-specific prefs are read per-document, and the
+   * results are stored in a linked list, which is assumed to be very short
+   * since most documents only ever use one language.
+   *
+   * Storing this per-session rather than per-document would almost certainly
+   * be fine. But just to be on the safe side, we leave the old mechanism as-is,
+   * with an additional per-session cache that new callers can use if they don't
+   * have a PresContext.
+   */
+  const LangGroupFontPrefs* GetFontPrefsForLangHelper(nsIAtom* aLanguage,
+                                                      const LangGroupFontPrefs* aPrefs) const;
+  /**
+   * Get the default font for the given language and generic font ID.
+   * aLanguage may not be nullptr.
+   *
+   * This object is read-only, you must copy the font to modify it.
+   *
+   * When aFontID is kPresContext_DefaultVariableFontID or
+   * kPresContext_DefaultFixedFontID (which equals
+   * kGenericFont_moz_fixed, which is used for the -moz-fixed generic),
+   * the nsFont returned has its name as a CSS generic family (serif or
+   * sans-serif for the former, monospace for the latter), and its size
+   * as the default font size for variable or fixed fonts for the
+   * language group.
+   *
+   * For aFontID corresponding to a CSS Generic, the nsFont returned has
+   * its name set to that generic font's name, and its size set to
+   * the user's preference for font size for that generic and the
+   * given language.
+   */
+  const nsFont* GetDefaultFontHelper(uint8_t aFontID,
+                                     nsIAtom* aLanguage,
+                                     const LangGroupFontPrefs* aPrefs) const;
+
+  /*
+   * These versions operate on the font pref cache on StaticPresData.
+   */
+
+  const nsFont* GetDefaultFont(uint8_t aFontID, nsIAtom* aLanguage) const
+  {
+    MOZ_ASSERT(aLanguage);
+    return GetDefaultFontHelper(aFontID, aLanguage, GetFontPrefsForLang(aLanguage));
+  }
+  const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom* aLanguage) const
+  {
+    MOZ_ASSERT(aLanguage);
+    return GetFontPrefsForLangHelper(aLanguage, &mStaticLangGroupFontPrefs);
+  }
+
+  void ResetCachedFontPrefs() { mStaticLangGroupFontPrefs.Reset(); }
+
+private:
+  StaticPresData();
+  ~StaticPresData() {}
+
+  nsCOMPtr<nsILanguageAtomService> mLangService;
+  nscoord mBorderWidthTable[3];
+  LangGroupFontPrefs mStaticLangGroupFontPrefs;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_StaticPresData_h
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -104,16 +104,17 @@ EXPORTS.mozilla += [
     'ArenaRefPtrInlines.h',
     'GeometryUtils.h',
     'PaintTracker.h',
     'RestyleLogging.h',
     'RestyleManager.h',
     'RestyleManagerHandle.h',
     'RestyleManagerHandleInlines.h',
     'ServoRestyleManager.h',
+    'StaticPresData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleCaret.cpp',
     'AccessibleCaretEventHub.cpp',
     'AccessibleCaretManager.cpp',
     'ActiveLayerTracker.cpp',
     'DisplayItemClip.cpp',
@@ -149,16 +150,17 @@ UNIFIED_SOURCES += [
     'nsStyleSheetService.cpp',
     'PaintTracker.cpp',
     'PositionedEventTargeting.cpp',
     'RestyleManager.cpp',
     'RestyleTracker.cpp',
     'ScrollbarStyles.cpp',
     'ServoRestyleManager.cpp',
     'StackArena.cpp',
+    'StaticPresData.cpp',
     'TouchManager.cpp',
     'ZoomConstraintsClient.cpp',
 ]
 
 # nsPresArena.cpp needs to be built separately because it uses plarena.h.
 # nsRefreshDriver.cpp needs to be built separately because of name clashes in the OS X headers
 SOURCES += [
     'nsPresArena.cpp',
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -392,251 +392,30 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   // NS_RELEASE(tmp->mLanguage); // an atom
   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
 
   tmp->Destroy();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-
-#define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
- _pref.Assign(_s0); \
- _pref.Append(_s1);
-
-static const char* const kGenericFont[] = {
-  ".variable.",
-  ".fixed.",
-  ".serif.",
-  ".sans-serif.",
-  ".monospace.",
-  ".cursive.",
-  ".fantasy."
-};
-
 // whether no native theme service exists;
 // if this gets set to true, we'll stop asking for it.
 static bool sNoTheme = false;
 
 // Set to true when LookAndFeelChanged needs to be called.  This is used
 // because the look and feel is a service, so there's no need to notify it from
 // more than one prescontext.
 static bool sLookAndFeelChanged;
 
 // Set to true when ThemeChanged needs to be called on mTheme.  This is used
 // because mTheme is a service, so there's no need to notify it from more than
 // one prescontext.
 static bool sThemeChanged;
 
-const nsPresContext::LangGroupFontPrefs*
-nsPresContext::GetFontPrefsForLang(nsIAtom *aLanguage) const
-{
-  // Get language group for aLanguage:
-
-  nsresult rv = NS_OK;
-  nsIAtom *langGroupAtom = nullptr;
-  if (!aLanguage) {
-    aLanguage = mLanguage;
-  }
-  if (aLanguage && mLangService) {
-    langGroupAtom = mLangService->GetLanguageGroup(aLanguage, &rv);
-  }
-  if (NS_FAILED(rv) || !langGroupAtom) {
-    langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe...
-  }
-
-  // Look for cached prefs for this lang group.
-  // Most documents will only use one (or very few) language groups. Rather
-  // than have the overhead of a hash lookup, we simply look along what will
-  // typically be a very short (usually of length 1) linked list. There are 31
-  // language groups, so in the worst case scenario we'll need to traverse 31
-  // link items.
-
-  LangGroupFontPrefs *prefs =
-    const_cast<LangGroupFontPrefs*>(&mLangGroupFontPrefs);
-  if (prefs->mLangGroup) { // if initialized
-    DebugOnly<uint32_t> count = 0;
-    for (;;) {
-      NS_ASSERTION(++count < 35, "Lang group count exceeded!!!");
-      if (prefs->mLangGroup == langGroupAtom) {
-        return prefs;
-      }
-      if (!prefs->mNext) {
-        break;
-      }
-      prefs = prefs->mNext;
-    }
-
-    // nothing cached, so go on and fetch the prefs for this lang group:
-    prefs = prefs->mNext = new LangGroupFontPrefs;
-  }
-
-  prefs->mLangGroup = langGroupAtom;
-
-  /* Fetch the font prefs to be used -- see bug 61883 for details.
-     Not all prefs are needed upfront. Some are fallback prefs intended
-     for the GFX font sub-system...
-
-  1) unit : assumed to be the same for all language groups -------------
-  font.size.unit = px | pt    XXX could be folded in the size... bug 90440
-
-  2) attributes for generic fonts --------------------------------------
-  font.default.[langGroup] = serif | sans-serif - fallback generic font
-  font.name.[generic].[langGroup] = current user' selected font on the pref dialog
-  font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list]
-  font.size.[generic].[langGroup] = integer - settable by the user
-  font.size-adjust.[generic].[langGroup] = "float" - settable by the user
-  font.minimum-size.[langGroup] = integer - settable by the user
-  */
-
-  nsAutoCString langGroup;
-  langGroupAtom->ToUTF8String(langGroup);
-
-  prefs->mDefaultVariableFont.size = CSSPixelsToAppUnits(16);
-  prefs->mDefaultFixedFont.size = CSSPixelsToAppUnits(13);
-
-  nsAutoCString pref;
-
-  // get the current applicable font-size unit
-  enum {eUnit_unknown = -1, eUnit_px, eUnit_pt};
-  int32_t unit = eUnit_px;
-
-  nsAdoptingCString cvalue =
-    Preferences::GetCString("font.size.unit");
-
-  if (!cvalue.IsEmpty()) {
-    if (cvalue.EqualsLiteral("px")) {
-      unit = eUnit_px;
-    }
-    else if (cvalue.EqualsLiteral("pt")) {
-      unit = eUnit_pt;
-    }
-    else {
-      // XXX should really send this warning to the user (Error Console?).
-      // And just default to unit = eUnit_px?
-      NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'");
-      unit = eUnit_unknown;
-    }
-  }
-
-  // get font.minimum-size.[langGroup]
-
-  MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup);
-
-  int32_t size = Preferences::GetInt(pref.get());
-  if (unit == eUnit_px) {
-    prefs->mMinimumFontSize = CSSPixelsToAppUnits(size);
-  }
-  else if (unit == eUnit_pt) {
-    prefs->mMinimumFontSize = CSSPointsToAppUnits(size);
-  }
-
-  nsFont* fontTypes[] = {
-    &prefs->mDefaultVariableFont,
-    &prefs->mDefaultFixedFont,
-    &prefs->mDefaultSerifFont,
-    &prefs->mDefaultSansSerifFont,
-    &prefs->mDefaultMonospaceFont,
-    &prefs->mDefaultCursiveFont,
-    &prefs->mDefaultFantasyFont
-  };
-  static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT,
-                "FontTypes array count is not correct");
-
-  // Get attributes specific to each generic font. We do not get the user's
-  // generic-font-name-to-specific-family-name preferences because its the
-  // generic name that should be fed into the cascade. It is up to the GFX
-  // code to look up the font prefs to convert generic names to specific
-  // family names as necessary.
-  nsAutoCString generic_dot_langGroup;
-  for (uint32_t eType = 0; eType < ArrayLength(fontTypes); ++eType) {
-    generic_dot_langGroup.Assign(kGenericFont[eType]);
-    generic_dot_langGroup.Append(langGroup);
-
-    nsFont* font = fontTypes[eType];
-
-    // set the default variable font (the other fonts are seen as 'generic' fonts
-    // in GFX and will be queried there when hunting for alternative fonts)
-    if (eType == eDefaultFont_Variable) {
-      MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup);
-
-      nsAdoptingString value = Preferences::GetString(pref.get());
-      if (!value.IsEmpty()) {
-        FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
-        FontFamilyType defaultType = defaultVariableName.mType;
-        NS_ASSERTION(defaultType == eFamily_serif ||
-                     defaultType == eFamily_sans_serif,
-                     "default type must be serif or sans-serif");
-        prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType);
-      }
-      else {
-        MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup);
-        value = Preferences::GetString(pref.get());
-        if (!value.IsEmpty()) {
-          FontFamilyName defaultVariableName = FontFamilyName::Convert(value);
-          FontFamilyType defaultType = defaultVariableName.mType;
-          NS_ASSERTION(defaultType == eFamily_serif ||
-                       defaultType == eFamily_sans_serif,
-                       "default type must be serif or sans-serif");
-          prefs->mDefaultVariableFont.fontlist = FontFamilyList(defaultType);
-        }
-      }
-    }
-    else {
-      if (eType == eDefaultFont_Monospace) {
-        // This takes care of the confusion whereby people often expect "monospace"
-        // to have the same default font-size as "-moz-fixed" (this tentative
-        // size may be overwritten with the specific value for "monospace" when
-        // "font.size.monospace.[langGroup]" is read -- see below)
-        prefs->mDefaultMonospaceFont.size = prefs->mDefaultFixedFont.size;
-      }
-      else if (eType != eDefaultFont_Fixed) {
-        // all the other generic fonts are initialized with the size of the
-        // variable font, but their specific size can supersede later -- see below
-        font->size = prefs->mDefaultVariableFont.size;
-      }
-    }
-
-    // Bug 84398: for spec purists, a different font-size only applies to the
-    // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|.
-    // The problem is that only GfxWin has the support for |font-size-adjust|. So for
-    // parity, we enable the ability to set a different font-size on all platforms.
-
-    // get font.size.[generic].[langGroup]
-    // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font
-    MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup);
-    size = Preferences::GetInt(pref.get());
-    if (size > 0) {
-      if (unit == eUnit_px) {
-        font->size = CSSPixelsToAppUnits(size);
-      }
-      else if (unit == eUnit_pt) {
-        font->size = CSSPointsToAppUnits(size);
-      }
-    }
-
-    // get font.size-adjust.[generic].[langGroup]
-    // XXX only applicable on GFX ports that handle |font-size-adjust|
-    MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup);
-    cvalue = Preferences::GetCString(pref.get());
-    if (!cvalue.IsEmpty()) {
-      font->sizeAdjust = (float)atof(cvalue.get());
-    }
-
-#ifdef DEBUG_rbs
-    printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n",
-           generic_dot_langGroup.get(),
-           NS_ConvertUTF16toUTF8(font->name).get(), font->size,
-           font->sizeAdjust);
-#endif
-  }
-
-  return prefs;
-}
-
 void
 nsPresContext::GetDocumentColorPreferences()
 {
   // Make sure the preferences are initialized.  In the normal run,
   // they would already be, because gfxPlatform would have been created,
   // but in some reference tests, that is not the case.
   gfxPrefs::GetSingleton();
 
@@ -795,17 +574,18 @@ nsPresContext::GetUserPreferences()
   mBodyTextColor = mDefaultColor;
 
   // * use fonts?
   mUseDocumentFonts =
     Preferences::GetInt("browser.display.use_document_fonts") != 0;
 
   mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
 
-  ResetCachedFontPrefs();
+  mLangGroupFontPrefs.Reset();
+  StaticPresData::Get()->ResetCachedFontPrefs();
 
   // * image animation
   const nsAdoptingCString& animatePref =
     Preferences::GetCString("image.animation_mode");
   if (animatePref.EqualsLiteral("normal"))
     mImageAnimationModePref = imgIContainer::kNormalAnimMode;
   else if (animatePref.EqualsLiteral("none"))
     mImageAnimationModePref = imgIContainer::kDontAnimMode;
@@ -1231,17 +1011,17 @@ nsPresContext::UpdateCharSet(const nsCSt
   if (mLangService) {
     mLanguage = mLangService->LookupCharSet(aCharSet);
     // this will be a language group (or script) code rather than a true language code
 
     // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
     if (mLanguage == nsGkAtoms::Unicode) {
       mLanguage = mLangService->GetLocaleLanguage();
     }
-    ResetCachedFontPrefs();
+    mLangGroupFontPrefs.Reset();
   }
 
   switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
 
     case IBMBIDI_TEXTTYPE_LOGICAL:
       SetVisualMode(false);
       break;
 
@@ -1470,54 +1250,16 @@ nsPresContext::SetImageAnimationModeInte
 }
 
 void
 nsPresContext::SetImageAnimationModeExternal(uint16_t aMode)
 {
   SetImageAnimationModeInternal(aMode);
 }
 
-const nsFont*
-nsPresContext::GetDefaultFont(uint8_t aFontID, nsIAtom *aLanguage) const
-{
-  const LangGroupFontPrefs *prefs = GetFontPrefsForLang(aLanguage);
-
-  const nsFont *font;
-  switch (aFontID) {
-    // Special (our default variable width font and fixed width font)
-    case kPresContext_DefaultVariableFont_ID:
-      font = &prefs->mDefaultVariableFont;
-      break;
-    case kPresContext_DefaultFixedFont_ID:
-      font = &prefs->mDefaultFixedFont;
-      break;
-    // CSS
-    case kGenericFont_serif:
-      font = &prefs->mDefaultSerifFont;
-      break;
-    case kGenericFont_sans_serif:
-      font = &prefs->mDefaultSansSerifFont;
-      break;
-    case kGenericFont_monospace:
-      font = &prefs->mDefaultMonospaceFont;
-      break;
-    case kGenericFont_cursive:
-      font = &prefs->mDefaultCursiveFont;
-      break;
-    case kGenericFont_fantasy:
-      font = &prefs->mDefaultFantasyFont;
-      break;
-    default:
-      font = nullptr;
-      NS_ERROR("invalid arg");
-      break;
-  }
-  return font;
-}
-
 already_AddRefed<nsIAtom>
 nsPresContext::GetContentLanguage() const
 {
   nsAutoString language;
   Document()->GetContentLanguage(language);
   language.StripWhitespace();
 
   // Content-Language may be a comma-separated list of language codes,
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -37,16 +37,17 @@
 #include "prclist.h"
 #include "nsThreadUtils.h"
 #include "ScrollbarStyles.h"
 #include "nsIMessageManager.h"
 #include "mozilla/RestyleLogging.h"
 #include "Units.h"
 #include "mozilla/RestyleManagerHandle.h"
 #include "prenv.h"
+#include "mozilla/StaticPresData.h"
 
 class nsAString;
 class nsIPrintSettings;
 class nsDocShell;
 class nsIDocShell;
 class nsIDocument;
 class nsILanguageAtomService;
 class nsITheme;
@@ -130,17 +131,19 @@ public:
 class nsRootPresContext;
 
 // An interface for presentation contexts. Presentation contexts are
 // objects that provide an outer context for a presentation shell.
 
 class nsPresContext : public nsIObserver {
 public:
   typedef mozilla::FramePropertyTable FramePropertyTable;
+  typedef mozilla::LangGroupFontPrefs LangGroupFontPrefs;
   typedef mozilla::ScrollbarStyles ScrollbarStyles;
+  typedef mozilla::StaticPresData StaticPresData;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
   NS_DECL_CYCLE_COLLECTION_CLASS(nsPresContext)
 
   enum nsPresContextType {
     eContext_Galley,       // unpaginated screen presentation
@@ -360,33 +363,25 @@ public:
     if (mShell)
       mShell->FreeMisc(aSize, aFreeChunk);
   }
 
   /**
    * Get the default font for the given language and generic font ID.
    * If aLanguage is nullptr, the document's language is used.
    *
-   * This object is read-only, you must copy the font to modify it.
-   *
-   * When aFontID is kPresContext_DefaultVariableFontID or
-   * kPresContext_DefaultFixedFontID (which equals
-   * kGenericFont_moz_fixed, which is used for the -moz-fixed generic),
-   * the nsFont returned has its name as a CSS generic family (serif or
-   * sans-serif for the former, monospace for the latter), and its size
-   * as the default font size for variable or fixed fonts for the
-   * language group.
-   *
-   * For aFontID corresponding to a CSS Generic, the nsFont returned has
-   * its name set to that generic font's name, and its size set to
-   * the user's preference for font size for that generic and the
-   * given language.
+   * See the comment in StaticPresData::GetDefaultFont.
    */
   const nsFont* GetDefaultFont(uint8_t aFontID,
-                                           nsIAtom *aLanguage) const;
+                               nsIAtom *aLanguage) const
+  {
+    nsIAtom* lang = aLanguage ? aLanguage : mLanguage.get();
+    return StaticPresData::Get()->GetDefaultFontHelper(aFontID, lang,
+                                                       GetFontPrefsForLang(lang));
+  }
 
   /** Get a cached boolean pref, by its type */
   // *  - initially created for bugs 31816, 20760, 22963
   bool GetCachedBoolPref(nsPresContext_CachedBoolPrefType aPrefType) const
   {
     // If called with a constant parameter, the compiler should optimize
     // this switch statement away.
     switch (aPrefType) {
@@ -1120,74 +1115,24 @@ protected:
   void PreferenceChanged(const char* aPrefName);
   static void PrefChangedCallback(const char*, void*);
 
   void UpdateAfterPreferencesChanged();
   static void PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure);
 
   void GetUserPreferences();
 
-  // Allow nsAutoPtr<LangGroupFontPrefs> dtor to access this protected struct's
-  // dtor:
-  struct LangGroupFontPrefs;
-  friend class nsAutoPtr<LangGroupFontPrefs>;
-  struct LangGroupFontPrefs {
-    // Font sizes default to zero; they will be set in GetFontPreferences
-    LangGroupFontPrefs()
-      : mLangGroup(nullptr)
-      , mMinimumFontSize(0)
-      , mDefaultVariableFont(mozilla::eFamily_serif, 0)
-      , mDefaultFixedFont(mozilla::eFamily_monospace, 0)
-      , mDefaultSerifFont(mozilla::eFamily_serif, 0)
-      , mDefaultSansSerifFont(mozilla::eFamily_sans_serif, 0)
-      , mDefaultMonospaceFont(mozilla::eFamily_monospace, 0)
-      , mDefaultCursiveFont(mozilla::eFamily_cursive, 0)
-      , mDefaultFantasyFont(mozilla::eFamily_fantasy, 0)
-    {}
-
-    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
-      size_t n = 0;
-      LangGroupFontPrefs *curr = mNext;
-      while (curr) {
-        n += aMallocSizeOf(curr);
-
-        // Measurement of the following members may be added later if DMD finds
-        // it is worthwhile:
-        // - mLangGroup
-        // - mDefault*Font
-
-        curr = curr->mNext;
-      }
-      return n;
-    }
-
-    nsCOMPtr<nsIAtom> mLangGroup;
-    nscoord mMinimumFontSize;
-    nsFont mDefaultVariableFont;
-    nsFont mDefaultFixedFont;
-    nsFont mDefaultSerifFont;
-    nsFont mDefaultSansSerifFont;
-    nsFont mDefaultMonospaceFont;
-    nsFont mDefaultCursiveFont;
-    nsFont mDefaultFantasyFont;
-    nsAutoPtr<LangGroupFontPrefs> mNext;
-  };
-
   /**
    * Fetch the user's font preferences for the given aLanguage's
    * langugage group.
    */
-  const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom *aLanguage) const;
-
-  void ResetCachedFontPrefs() {
-    // Throw away any other LangGroupFontPrefs objects:
-    mLangGroupFontPrefs.mNext = nullptr;
-
-    // Make GetFontPreferences reinitialize mLangGroupFontPrefs:
-    mLangGroupFontPrefs.mLangGroup = nullptr;
+  const LangGroupFontPrefs* GetFontPrefsForLang(nsIAtom *aLanguage) const
+  {
+    nsIAtom* lang = aLanguage ? aLanguage : mLanguage.get();
+    return StaticPresData::Get()->GetFontPrefsForLangHelper(lang, &mLangGroupFontPrefs);
   }
 
   void UpdateCharSet(const nsCString& aCharSet);
 
 public:
   void DoChangeCharSet(const nsCString& aCharSet);
 
   /**
@@ -1327,16 +1272,21 @@ protected:
   ScrollbarStyles       mViewportStyleScrollbar;
   uint8_t               mFocusRingWidth;
 
   bool mExistThrottledUpdates;
 
   uint16_t              mImageAnimationMode;
   uint16_t              mImageAnimationModePref;
 
+  // Most documents will only use one (or very few) language groups. Rather
+  // than have the overhead of a hash lookup, we simply look along what will
+  // typically be a very short (usually of length 1) linked list. There are 31
+  // language groups, so in the worst case scenario we'll need to traverse 31
+  // link items.
   LangGroupFontPrefs    mLangGroupFontPrefs;
 
   nscoord               mBorderWidthTable[3];
 
   uint32_t              mInterruptChecksToSkip;
 
   // Counters for tests and tools that want to detect frame construction
   // or reflow.
@@ -1428,28 +1378,16 @@ protected:
   bool                  mInitialized;
 #endif
 
 
 protected:
 
   virtual ~nsPresContext();
 
-  // these are private, use the list in nsFont.h if you want a public list
-  enum {
-    eDefaultFont_Variable,
-    eDefaultFont_Fixed,
-    eDefaultFont_Serif,
-    eDefaultFont_SansSerif,
-    eDefaultFont_Monospace,
-    eDefaultFont_Cursive,
-    eDefaultFont_Fantasy,
-    eDefaultFont_COUNT
-  };
-
   nscolor MakeColorPref(const nsString& aColor);
 
   void LastRelease();
 
 #ifdef DEBUG
 private:
   friend struct nsAutoLayoutPhase;
   uint32_t mLayoutPhaseCount[eLayoutPhase_COUNT];
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -126,16 +126,17 @@ using namespace mozilla::system;
 #include "mozilla/IMEStateManager.h"
 #include "nsDocument.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CameraPreferences.h"
 #include "TouchManager.h"
 #include "MediaDecoder.h"
 #include "mozilla/layers/CompositorLRU.h"
 #include "mozilla/dom/devicestorage/DeviceStorageStatics.h"
+#include "mozilla/StaticPresData.h"
 
 #ifdef MOZ_B2G_BT
 #include "mozilla/dom/BluetoothUUID.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::net;
 using namespace mozilla::dom;
@@ -192,16 +193,17 @@ nsLayoutStatics::Initialize()
   rv = nsTextFragment::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsTextFragment");
     return rv;
   }
 
   nsCellMap::Init();
 
+  StaticPresData::Init();
   nsCSSRendering::Init();
 
   nsTextFrameTextRunCache::Init();
 
   rv = nsHTMLDNSPrefetch::Initialize();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize HTML DNS prefetch");
     return rv;
@@ -342,16 +344,17 @@ nsLayoutStatics::Shutdown()
   Attr::Shutdown();
   EventListenerManager::Shutdown();
   IMEStateManager::Shutdown();
   nsCSSParser::Shutdown();
   nsCSSRuleProcessor::Shutdown();
   nsTextFrameTextRunCache::Shutdown();
   nsHTMLDNSPrefetch::Shutdown();
   nsCSSRendering::Shutdown();
+  StaticPresData::Shutdown();
 #ifdef DEBUG
   nsFrame::DisplayReflowShutdown();
 #endif
   nsCellMap::Shutdown();
   ActiveLayerTracker::Shutdown();
 
   // Release all of our atoms
   nsColorNames::ReleaseTable();
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -104,17 +104,17 @@ nsStyleFont::nsStyleFont(const nsFont& a
   , mGenericID(kGenericFont_NONE)
   , mScriptLevel(0)
   , mMathVariant(NS_MATHML_MATHVARIANT_NONE)
   , mMathDisplay(NS_MATHML_DISPLAYSTYLE_INLINE)
   , mMinFontSizeRatio(100) // 100%
   , mExplicitLanguage(false)
   , mAllowZoom(true)
   , mScriptUnconstrainedSize(mSize)
-  , mScriptMinSize(aPresContext->CSSTwipsToAppUnits(
+  , mScriptMinSize(nsPresContext::CSSTwipsToAppUnits(
       NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT)))
   , mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER)
   , mLanguage(GetLanguage(aPresContext))
 {
   MOZ_COUNT_CTOR(nsStyleFont);
   mFont.size = mSize;
 }
 
@@ -368,17 +368,17 @@ nsStyleBorder::nsStyleBorder(nsPresConte
 {
   MOZ_COUNT_CTOR(nsStyleBorder);
 
   NS_FOR_CSS_HALF_CORNERS (corner) {
     mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor));
   }
 
   nscoord medium =
-    (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM];
+    (StaticPresData::Get()->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM];
   NS_FOR_CSS_SIDES(side) {
     mBorderImageSlice.Set(side, nsStyleCoord(1.0f, eStyleUnit_Percent));
     mBorderImageWidth.Set(side, nsStyleCoord(1.0f, eStyleUnit_Factor));
     mBorderImageOutset.Set(side, nsStyleCoord(0.0f, eStyleUnit_Factor));
 
     mBorder.Side(side) = medium;
     mBorderStyle[side] = NS_STYLE_BORDER_STYLE_NONE | BORDER_COLOR_FOREGROUND;
     mBorderColor[side] = NS_RGB(0, 0, 0);
@@ -755,17 +755,18 @@ nsChangeHint nsStyleXUL::CalcDifference(
 nsStyleColumn::nsStyleColumn(nsPresContext* aPresContext)
 {
   MOZ_COUNT_CTOR(nsStyleColumn);
   mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO;
   mColumnWidth.SetAutoValue();
   mColumnGap.SetNormalValue();
   mColumnFill = NS_STYLE_COLUMN_FILL_BALANCE;
 
-  mColumnRuleWidth = (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM];
+  mColumnRuleWidth =
+    (StaticPresData::Get()->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM];
   mColumnRuleStyle = NS_STYLE_BORDER_STYLE_NONE;
   mColumnRuleColor = NS_RGB(0, 0, 0);
   mColumnRuleColorIsForeground = true;
 
   mTwipsPerPixel = aPresContext->AppUnitsPerDevPixel();
 }
 
 nsStyleColumn::~nsStyleColumn()