Bug 1430623 - Move the lang font prefs to Document instead of nsPresContext. r=jfkthame
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 18 Jan 2019 13:43:06 +0000
changeset 454441 e902aece9ed7f640a7be789ff2cb5ccdfa8a5719
parent 454440 7d151a27de64cce8b0ed96443c1888656341e420
child 454442 160cc7a53e2142dd5165c7ed5d28c2d224cf451c
push id35397
push useropoprus@mozilla.com
push dateSat, 19 Jan 2019 03:35:41 +0000
treeherdermozilla-central@57dc8bbbc38f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1430623, 1490401, 1471231
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1430623 - Move the lang font prefs to Document instead of nsPresContext. r=jfkthame This will allow me to (in different patches): * Make the default style structs constructible without a pres context (default color and co. would need to be faked or moved to Document as well, but that's ok, since those cannot affect media queries, the default font-size does). * Remove the nsPresContext pointer from ComputedStyle (moving it to nsFrame, probably). That would in turn allow me to have the default style computed without a pres context, which allows us to fix both bug 1490401 and bug 1471231. Differential Revision: https://phabricator.services.mozilla.com/D16926
dom/base/Document.cpp
dom/base/Document.h
layout/base/StaticPresData.cpp
layout/base/StaticPresData.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/generic/nsSimplePageSequenceFrame.cpp
layout/generic/nsTextFrame.cpp
layout/style/GeckoBindings.cpp
layout/style/MappedDeclarations.cpp
layout/style/ServoStyleSet.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1203,16 +1203,17 @@ Document::Document(const char* aContentT
       mCharacterSetSource(0),
       mParentDocument(nullptr),
       mCachedRootElement(nullptr),
       mNodeInfoManager(nullptr),
 #ifdef DEBUG
       mStyledLinksCleared(false),
 #endif
       mBidiEnabled(false),
+      mFontGroupCacheDirty(true),
       mMathMLEnabled(false),
       mIsInitialDocumentInWindow(false),
       mIgnoreDocGroupMismatches(false),
       mLoadedAsData(false),
       mLoadedAsInteractiveData(false),
       mMayStartLayout(true),
       mHaveFiredTitleChange(false),
       mIsShowing(false),
@@ -1351,16 +1352,18 @@ Document::Document(const char* aContentT
 
   SetContentTypeInternal(nsDependentCString(aContentType));
 
   // Start out mLastStyleSheetSet as null, per spec
   SetDOMStringToNull(mLastStyleSheetSet);
 
   // void state used to differentiate an empty source from an unselected source
   mPreloadPictureFoundSource.SetIsVoid(true);
+
+  RecomputeLanguageFromCharset();
 }
 
 void Document::ClearAllBoxObjects() {
   if (mBoxObjectTable) {
     for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
       nsPIBoxObject* boxObject = iter.UserData();
       if (boxObject) {
         boxObject->Clear();
@@ -3406,16 +3409,17 @@ URLExtraData* Document::DefaultStyleAttr
   }
   return mCachedURLData;
 }
 
 void Document::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding) {
   if (mCharacterSet != aEncoding) {
     mCharacterSet = aEncoding;
     mEncodingMenuDisabled = aEncoding == UTF_8_ENCODING;
+    RecomputeLanguageFromCharset();
 
     if (nsPresContext* context = GetPresContext()) {
       context->DispatchCharSetChange(aEncoding);
     }
   }
 }
 
 void Document::GetSandboxFlagsAsString(nsAString& aFlags) {
@@ -3469,16 +3473,17 @@ void Document::SetHeaderData(nsAtom* aHe
     if (!aData.IsEmpty() && !found) {
       // didn't find, append
       *lastPtr = new DocHeaderData(aHeaderField, aData);
     }
   }
 
   if (aHeaderField == nsGkAtoms::headerContentLanguage) {
     CopyUTF16toUTF8(aData, mContentLanguage);
+    ResetLangPrefs();
     if (auto* presContext = GetPresContext()) {
       presContext->ContentLanguageChanged();
     }
   }
 
   if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
     SetPreferredStyleSheetSet(aData);
   }
@@ -10884,16 +10889,19 @@ void Document::DocAddSizeOfExcludingThis
   //
   // Therefore, the measurement of the Document superclass must happen after
   // the measurement of DOM nodes (above), because Document contains the
   // PresShell, which contains the frame tree.
   if (mPresShell) {
     mPresShell->AddSizeOfIncludingThis(aWindowSizes);
   }
 
+  aWindowSizes.mDOMOtherSize += mLangGroupFontPrefs.SizeOfExcludingThis(
+      aWindowSizes.mState.mMallocSizeOf);
+
   aWindowSizes.mPropertyTablesSize +=
       mPropertyTable.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
 
   if (EventListenerManager* elm = GetExistingListenerManager()) {
     aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
   }
 
   if (mNodeInfoManager) {
@@ -12493,10 +12501,67 @@ void Document::ReportShadowDOMUsage() {
   mHasReportedShadowDOMUsage = true;
 }
 
 bool Document::StorageAccessSandboxed() const {
   return StaticPrefs::dom_storage_access_enabled() &&
          (GetSandboxFlags() & SANDBOXED_STORAGE_ACCESS) != 0;
 }
 
+already_AddRefed<nsAtom> Document::GetContentLanguageAsAtomForStyle() const {
+  nsAutoString contentLang;
+  GetContentLanguage(contentLang);
+  contentLang.StripWhitespace();
+
+  // Content-Language may be a comma-separated list of language codes,
+  // in which case the HTML5 spec says to treat it as unknown
+  if (!contentLang.IsEmpty() && !contentLang.Contains(char16_t(','))) {
+    return NS_Atomize(contentLang);
+  }
+
+  return nullptr;
+}
+
+already_AddRefed<nsAtom> Document::GetLanguageForStyle() const {
+  RefPtr<nsAtom> lang = GetContentLanguageAsAtomForStyle();
+  if (!lang) {
+    lang = mLanguageFromCharset;
+  }
+  return lang.forget();
+}
+
+const LangGroupFontPrefs* Document::GetFontPrefsForLang(
+    nsAtom* aLanguage, bool* aNeedsToCache) const {
+  nsAtom* lang = aLanguage ? aLanguage : mLanguageFromCharset.get();
+  return StaticPresData::Get()->GetFontPrefsForLangHelper(
+      lang, &mLangGroupFontPrefs, aNeedsToCache);
+}
+
+void Document::DoCacheAllKnownLangPrefs() {
+  MOZ_ASSERT(mFontGroupCacheDirty);
+  RefPtr<nsAtom> lang = GetLanguageForStyle();
+  GetFontPrefsForLang(lang.get());
+  GetFontPrefsForLang(nsGkAtoms::x_math);
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
+  GetFontPrefsForLang(nsGkAtoms::Unicode);
+  for (auto iter = mLanguagesUsed.Iter(); !iter.Done(); iter.Next()) {
+    GetFontPrefsForLang(iter.Get()->GetKey());
+  }
+  mFontGroupCacheDirty = false;
+}
+
+void Document::RecomputeLanguageFromCharset() {
+  nsLanguageAtomService* service = nsLanguageAtomService::GetService();
+  RefPtr<nsAtom> language = service->LookupCharSet(mCharacterSet);
+  if (language == nsGkAtoms::Unicode) {
+    language = service->GetLocaleLanguage();
+  }
+
+  if (language == mLanguageFromCharset) {
+    return;
+  }
+
+  ResetLangPrefs();
+  mLanguageFromCharset = language.forget();
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -38,16 +38,17 @@
 #include "nsPropertyTable.h"         // for member
 #include "nsStringFwd.h"
 #include "nsStubMutationObserver.h"
 #include "nsTHashtable.h"  // for member
 #include "nsURIHashKey.h"
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
+#include "mozilla/StaticPresData.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/ContentBlockingLog.h"
 #include "mozilla/dom/DispatcherTrait.h"
@@ -118,16 +119,17 @@ class nsPresContext;
 class nsRange;
 class nsSimpleContentList;
 class nsTextNode;
 class nsWindowSizes;
 class nsDOMCaretPosition;
 class nsViewportInfo;
 class nsIGlobalObject;
 class nsIXULWindow;
+struct nsFont;
 
 namespace mozilla {
 class AbstractThread;
 class CSSStyleSheet;
 class Encoding;
 class ErrorResult;
 class EventStates;
 class EventListenerManager;
@@ -3453,17 +3455,49 @@ class Document : public nsINode,
 
   // The application cache that this document is associated with, if
   // any.  This can change during the lifetime of the document.
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
 
  public:
   bool IsThirdPartyForFlashClassifier();
 
-  bool IsScopedStyleEnabled();
+ private:
+  void DoCacheAllKnownLangPrefs();
+  void RecomputeLanguageFromCharset();
+
+ public:
+  void ResetLangPrefs() {
+    mLangGroupFontPrefs.Reset();
+    mFontGroupCacheDirty = true;
+  }
+
+  already_AddRefed<nsAtom> GetContentLanguageAsAtomForStyle() const;
+  already_AddRefed<nsAtom> GetLanguageForStyle() const;
+
+  /**
+   * Fetch the user's font preferences for the given aLanguage's
+   * language group.
+   */
+  const LangGroupFontPrefs* GetFontPrefsForLang(
+      nsAtom* aLanguage, bool* aNeedsToCache = nullptr) const;
+
+  void ForceCacheLang(nsAtom* aLanguage) {
+    if (!mLanguagesUsed.EnsureInserted(aLanguage)) {
+      return;
+    }
+    GetFontPrefsForLang(aLanguage);
+  }
+
+  void CacheAllKnownLangPrefs() {
+    if (!mFontGroupCacheDirty) {
+      return;
+    }
+    DoCacheAllKnownLangPrefs();
+  }
 
   nsINode* GetServoRestyleRoot() const { return mServoRestyleRoot; }
 
   uint32_t GetServoRestyleRootDirtyBits() const {
     MOZ_ASSERT(mServoRestyleRoot);
     MOZ_ASSERT(mServoRestyleRootDirtyBits);
     return mServoRestyleRootDirtyBits;
   }
@@ -3799,16 +3833,18 @@ class Document : public nsINode,
   mozilla::EventStates mDocumentState;
 
   RefPtr<mozilla::dom::Promise> mReadyForIdle;
 
   RefPtr<mozilla::dom::FeaturePolicy> mFeaturePolicy;
 
   // True if BIDI is enabled.
   bool mBidiEnabled : 1;
+  // True if mLangGroupFontPrefs is not initialized or dirty in some other way.
+  bool mFontGroupCacheDirty : 1;
   // True if a MathML element has ever been owned by this document.
   bool mMathMLEnabled : 1;
 
   // True if this document is the initial document for a window.  This should
   // basically be true only for documents that exist in newly-opened windows or
   // documents created to satisfy a GetDocument() on a window when there's no
   // document in it.
   bool mIsInitialDocumentInWindow : 1;
@@ -4428,16 +4464,28 @@ class Document : public nsINode,
 
   RefPtr<DOMStyleSheetSetList> mStyleSheetSetList;
 
   // We lazily calculate declaration blocks for SVG elements with mapped
   // attributes in Servo mode. This list contains all elements which need lazy
   // resolution.
   nsTHashtable<nsPtrHashKey<SVGElement>> mLazySVGPresElements;
 
+  // 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;
+
+  nsTHashtable<nsRefPtrHashKey<nsAtom>> mLanguagesUsed;
+
+  // TODO(emilio): Is this hot enough to warrant to be cached?
+  RefPtr<nsAtom> mLanguageFromCharset;
+
   // Restyle root for servo's style system.
   //
   // We store this as an nsINode, rather than as an Element, so that we can
   // store the Document node as the restyle root if the entire document (along
   // with all document-level native-anonymous content) needs to be restyled.
   //
   // We also track which "descendant" bits (normal/animation-only/lazy-fc) the
   // root corresponds to.
--- a/layout/base/StaticPresData.cpp
+++ b/layout/base/StaticPresData.cpp
@@ -277,48 +277,9 @@ const LangGroupFontPrefs* StaticPresData
   }
 
   AssertIsMainThreadOrServoFontMetricsLocked();
   prefs->Initialize(langGroupAtom);
 
   return prefs;
 }
 
-const nsFont* StaticPresData::GetDefaultFontHelper(
-    uint8_t aFontID, nsAtom* 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
--- a/layout/base/StaticPresData.h
+++ b/layout/base/StaticPresData.h
@@ -56,16 +56,60 @@ struct LangGroupFontPrefs {
       curr = curr->mNext.get();
     }
     return n;
   }
 
   // Initialize this with the data for a given language
   void Initialize(nsAtom* aLangGroupAtom);
 
+  /**
+   * 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* GetDefaultFont(uint8_t aFontID) const {
+    switch (aFontID) {
+      // Special (our default variable width font and fixed width font)
+      case kGenericFont_moz_variable:
+        return &mDefaultVariableFont;
+      case kGenericFont_moz_fixed:
+        return &mDefaultFixedFont;
+      // CSS
+      case kGenericFont_serif:
+        return &mDefaultSerifFont;
+      case kGenericFont_sans_serif:
+        return &mDefaultSansSerifFont;
+      case kGenericFont_monospace:
+        return &mDefaultMonospaceFont;
+      case kGenericFont_cursive:
+        return &mDefaultCursiveFont;
+      case kGenericFont_fantasy:
+        return &mDefaultFantasyFont;
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("invalid font id");
+        return nullptr;
+    }
+  }
+
   RefPtr<nsAtom> mLangGroup;
   nscoord mMinimumFontSize;
   nsFont mDefaultVariableFont;
   nsFont mDefaultFixedFont;
   nsFont mDefaultSerifFont;
   nsFont mDefaultSansSerifFont;
   nsFont mDefaultMonospaceFont;
   nsFont mDefaultCursiveFont;
@@ -129,59 +173,21 @@ class StaticPresData {
    * with an additional per-session cache that new callers can use if they don't
    * have a PresContext.
    *
    * See comment on GetLangGroup for the usage of aNeedsToCache.
    */
   const LangGroupFontPrefs* GetFontPrefsForLangHelper(
       nsAtom* aLanguage, const LangGroupFontPrefs* aPrefs,
       bool* aNeedsToCache = nullptr) 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, nsAtom* aLanguage,
                                      const LangGroupFontPrefs* aPrefs) const;
 
-  /*
-   * These versions operate on the font pref cache on StaticPresData.
-   */
-
-  const nsFont* GetDefaultFont(uint8_t aFontID, nsAtom* aLanguage) const {
-    MOZ_ASSERT(aLanguage);
-    return GetDefaultFontHelper(aFontID, aLanguage,
-                                GetFontPrefsForLang(aLanguage));
-  }
-  const LangGroupFontPrefs* GetFontPrefsForLang(
-      nsAtom* aLanguage, bool* aNeedsToCache = nullptr) const {
-    MOZ_ASSERT(aLanguage);
-    return GetFontPrefsForLangHelper(aLanguage, &mStaticLangGroupFontPrefs,
-                                     aNeedsToCache);
-  }
-
-  void ResetCachedFontPrefs() { mStaticLangGroupFontPrefs.Reset(); }
-
  private:
   StaticPresData();
   ~StaticPresData() {}
 
   nsLanguageAtomService* mLangService;
-  LangGroupFontPrefs mStaticLangGroupFontPrefs;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_StaticPresData_h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -170,17 +170,16 @@ nsPresContext::nsPresContext(dom::Docume
       mSystemFontScale(1.0),
       mTextZoom(1.0),
       mEffectiveTextZoom(1.0),
       mFullZoom(1.0),
       mOverrideDPPX(0.0),
       mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
       mCurAppUnitsPerDevPixel(0),
       mAutoQualityMinFontSizePixelsPref(0),
-      mLangService(nsLanguageAtomService::GetService()),
       mPageSize(-1, -1),
       mPageScale(0.0),
       mPPScale(1.0f),
       mDefaultColor(NS_RGBA(0, 0, 0, 0)),
       mBackgroundColor(NS_RGB(0xFF, 0xFF, 0xFF)),
       mLinkColor(NS_RGB(0x00, 0x00, 0xEE)),
       mActiveLinkColor(NS_RGB(0xEE, 0x00, 0x00)),
       mVisitedLinkColor(NS_RGB(0x55, 0x1A, 0x8B)),
@@ -188,17 +187,16 @@ nsPresContext::nsPresContext(dom::Docume
       mFocusTextColor(mDefaultColor),
       mBodyTextColor(mDefaultColor),
       mViewportScrollOverrideElement(nullptr),
       mViewportScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto),
       mFocusRingWidth(1),
       mExistThrottledUpdates(false),
       // mImageAnimationMode is initialised below, in constructor body
       mImageAnimationModePref(imgIContainer::kNormalAnimMode),
-      mFontGroupCacheDirty(true),
       mInterruptChecksToSkip(0),
       mElementsRestyled(0),
       mFramesConstructed(0),
       mFramesReflowed(0),
       mInteractionTimeEnabled(true),
       mHasPendingInterrupt(false),
       mPendingInterruptFromTest(false),
       mInterruptsEnabled(false),
@@ -334,28 +332,26 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
 
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
-  // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext);  // worth bothering?
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
   // 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
 
 // whether no native theme service exists;
 // if this gets set to true, we'll stop asking for it.
 static bool sNoTheme = false;
@@ -535,19 +531,17 @@ void nsPresContext::GetUserPreferences()
   mBodyTextColor = mDefaultColor;
 
   // * use fonts?
   mUseDocumentFonts =
       Preferences::GetInt("browser.display.use_document_fonts") != 0;
 
   mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
 
-  mLangGroupFontPrefs.Reset();
-  mFontGroupCacheDirty = true;
-  StaticPresData::Get()->ResetCachedFontPrefs();
+  Document()->ResetLangPrefs();
 
   // * image animation
   nsAutoCString animatePref;
   Preferences::GetCString("image.animation_mode", animatePref);
   if (animatePref.EqualsLiteral("normal"))
     mImageAnimationModePref = imgIContainer::kNormalAnimMode;
   else if (animatePref.EqualsLiteral("none"))
     mImageAnimationModePref = imgIContainer::kDontAnimMode;
@@ -916,27 +910,16 @@ void nsPresContext::DoChangeCharSet(NotN
   // might happen earlier than the UpdateCharSet(), so we need to restyle
   // descendants to make their style data up-to-date.
   //
   // FIXME(emilio): Revisit whether this is true after bug 1438911.
   RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_ForceDescendants);
 }
 
 void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
-  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();
-  }
-  mLangGroupFontPrefs.Reset();
-  mFontGroupCacheDirty = true;
-
   switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
     case IBMBIDI_TEXTTYPE_LOGICAL:
       SetVisualMode(false);
       break;
 
     case IBMBIDI_TEXTTYPE_VISUAL:
       SetVisualMode(true);
       break;
@@ -1125,31 +1108,16 @@ void nsPresContext::SetImageAnimationMod
       }
       SetSMILAnimations(doc, aMode, mImageAnimationMode);
     }
   }
 
   mImageAnimationMode = aMode;
 }
 
-already_AddRefed<nsAtom> nsPresContext::GetContentLanguage() const {
-  nsAutoString language;
-  Document()->GetContentLanguage(language);
-  language.StripWhitespace();
-
-  // Content-Language may be a comma-separated list of language codes,
-  // in which case the HTML5 spec says to treat it as unknown
-  if (!language.IsEmpty() && !language.Contains(char16_t(','))) {
-    return NS_Atomize(language);
-    // NOTE:  This does *not* count as an explicit language; in other
-    // words, it doesn't trigger language-specific hyphenation.
-  }
-  return nullptr;
-}
-
 void nsPresContext::UpdateEffectiveTextZoom() {
   float newZoom = mSystemFontScale * mTextZoom;
   float minZoom = nsLayoutUtils::MinZoom();
   float maxZoom = nsLayoutUtils::MaxZoom();
 
   if (newZoom < minZoom) {
     newZoom = minZoom;
   } else if (newZoom > maxZoom) {
@@ -1689,38 +1657,17 @@ void nsPresContext::EmulateMedium(const 
 void nsPresContext::StopEmulatingMedium() {
   nsAtom* previousMedium = Medium();
   mIsEmulatingMedia = false;
   if (Medium() != previousMedium) {
     MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange});
   }
 }
 
-void nsPresContext::ForceCacheLang(nsAtom* aLanguage) {
-  // force it to be cached
-  GetDefaultFont(kPresContext_DefaultVariableFont_ID, aLanguage);
-  mLanguagesUsed.PutEntry(aLanguage);
-}
-
-void nsPresContext::CacheAllLangs() {
-  if (mFontGroupCacheDirty) {
-    RefPtr<nsAtom> thisLang = nsStyleFont::GetLanguage(this);
-    GetDefaultFont(kPresContext_DefaultVariableFont_ID, thisLang.get());
-    GetDefaultFont(kPresContext_DefaultVariableFont_ID, nsGkAtoms::x_math);
-    // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
-    GetDefaultFont(kPresContext_DefaultVariableFont_ID, nsGkAtoms::Unicode);
-    for (auto iter = mLanguagesUsed.Iter(); !iter.Done(); iter.Next()) {
-      GetDefaultFont(kPresContext_DefaultVariableFont_ID, iter.Get()->GetKey());
-    }
-  }
-  mFontGroupCacheDirty = false;
-}
-
 void nsPresContext::ContentLanguageChanged() {
-  mFontGroupCacheDirty = true;
   PostRebuildAllStyleDataEvent(nsChangeHint(0), eRestyle_ForceDescendants);
 }
 
 void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
                                         nsRestyleHint aRestyleHint) {
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
@@ -2556,20 +2503,18 @@ nsIFrame* nsPresContext::GetPrimaryFrame
   if (GetPresShell() &&
       GetPresShell()->GetDocument() == aContent->GetComposedDoc()) {
     return aContent->GetPrimaryFrame();
   }
   return nullptr;
 }
 
 size_t nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
-  return mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf);
-
-  // Measurement of other members may be added later if DMD finds it is
-  // worthwhile.
+  // Measurement may be added later if DMD finds it is worthwhile.
+  return 0;
 }
 
 bool nsPresContext::IsRootContentDocument() const {
   // We are a root content document if: we are not a resource doc, we are
   // not chrome, and we either have no parent or our parent is chrome.
   if (mDocument->IsResourceDoc()) {
     return false;
   }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -23,17 +23,16 @@
 #include "nsRect.h"
 #include "nsStringFwd.h"
 #include "nsFont.h"
 #include "gfxFontConstants.h"
 #include "nsAtom.h"
 #include "nsITimer.h"
 #include "nsCRT.h"
 #include "nsIWidgetListener.h"
-#include "nsLanguageAtomService.h"
 #include "nsGkAtoms.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsChangeHint.h"
 #include <algorithm>
 #include "gfxTypes.h"
 #include "gfxRect.h"
 #include "nsTArray.h"
 #include "mozilla/MemoryReporting.h"
@@ -131,17 +130,16 @@ class nsRootPresContext;
 // objects that provide an outer context for a presentation shell.
 
 class nsPresContext : public nsISupports,
                       public mozilla::SupportsWeakPtr<nsPresContext> {
  public:
   using Encoding = mozilla::Encoding;
   template <typename T>
   using NotNull = mozilla::NotNull<T>;
-  typedef mozilla::LangGroupFontPrefs LangGroupFontPrefs;
   typedef mozilla::ScrollStyles ScrollStyles;
   typedef mozilla::StaticPresData StaticPresData;
   using TransactionId = mozilla::layers::TransactionId;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL
   NS_DECL_CYCLE_COLLECTION_CLASS(nsPresContext)
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsPresContext)
 
@@ -356,35 +354,16 @@ class nsPresContext : public nsISupports
    */
   void EmulateMedium(const nsAString& aMediaType);
 
   /*
    * Restore the viewer's natural medium
    */
   void StopEmulatingMedium();
 
-  /**
-   * Get the default font for the given language and generic font ID.
-   * If aLanguage is nullptr, the document's language is used.
-   *
-   * See the comment in StaticPresData::GetDefaultFont.
-   */
-  const nsFont* GetDefaultFont(uint8_t aFontID, nsAtom* aLanguage,
-                               bool* aNeedsToCache = nullptr) const {
-    nsAtom* lang = aLanguage ? aLanguage : mLanguage.get();
-    const LangGroupFontPrefs* prefs = GetFontPrefsForLang(lang, aNeedsToCache);
-    if (aNeedsToCache && *aNeedsToCache) {
-      return nullptr;
-    }
-    return StaticPresData::Get()->GetDefaultFontHelper(aFontID, lang, prefs);
-  }
-
-  void ForceCacheLang(nsAtom* aLanguage);
-  void CacheAllLangs();
-
   /** 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) {
       case kPresContext_UseDocumentFonts:
         return mUseDocumentFonts;
@@ -527,18 +506,16 @@ class nsPresContext : public nsISupports
    * in print preview.
    * XXX Temporary: see http://wiki.mozilla.org/Gecko:PrintPreview
    */
   float GetPrintPreviewScale() { return mPPScale; }
   void SetPrintPreviewScale(float aScale) { mPPScale = aScale; }
 
   nsDeviceContext* DeviceContext() const { return mDeviceContext; }
   mozilla::EventStateManager* EventStateManager() { return mEventManager; }
-  nsAtom* GetLanguageFromCharset() const { return mLanguage; }
-  already_AddRefed<nsAtom> GetContentLanguage() const;
 
   /**
    * Get/set a text zoom factor that is applied on top of the normal text zoom
    * set by the front-end/user.
    */
   float GetSystemFontScale() const { return mSystemFontScale; }
   void SetSystemFontScale(float aFontScale) {
     MOZ_ASSERT(aFontScale > 0.0f, "invalid font scale");
@@ -584,18 +561,18 @@ class nsPresContext : public nsISupports
 
   /**
    * Get the minimum font size for the specified language. If aLanguage
    * is nullptr, then the document's language is used.  This combines
    * the language-specific global preference with the per-presentation
    * base minimum font size.
    */
   int32_t MinFontSize(nsAtom* aLanguage, bool* aNeedsToCache = nullptr) const {
-    const LangGroupFontPrefs* prefs =
-        GetFontPrefsForLang(aLanguage, aNeedsToCache);
+    const auto* prefs =
+        Document()->GetFontPrefsForLang(aLanguage, aNeedsToCache);
     if (aNeedsToCache && *aNeedsToCache) {
       return 0;
     }
     return std::max(mBaseMinFontSize, prefs->mMinimumFontSize);
   }
 
   /**
    * Get the per-presentation base minimum font size.  This size is
@@ -1166,27 +1143,16 @@ class nsPresContext : public nsISupports
 
   void PreferenceChanged(const char* aPrefName);
 
   void UpdateAfterPreferencesChanged();
   void DispatchPrefChangedRunnableIfNeeded();
 
   void GetUserPreferences();
 
-  /**
-   * Fetch the user's font preferences for the given aLanguage's
-   * langugage group.
-   */
-  const LangGroupFontPrefs* GetFontPrefsForLang(
-      nsAtom* aLanguage, bool* aNeedsToCache = nullptr) const {
-    nsAtom* lang = aLanguage ? aLanguage : mLanguage.get();
-    return StaticPresData::Get()->GetFontPrefsForLangHelper(
-        lang, &mLangGroupFontPrefs, aNeedsToCache);
-  }
-
   void UpdateCharSet(NotNull<const Encoding*> aCharSet);
 
   static bool NotifyDidPaintSubdocumentCallback(
       mozilla::dom::Document* aDocument, void* aData);
   static bool NotifyRevokingDidPaintSubdocumentCallback(
       mozilla::dom::Document* aDocument, void* aData);
 
  public:
@@ -1256,23 +1222,16 @@ class nsPresContext : public nsISupports
       "always a static atom") mMedium;  // initialized by subclass ctors
   RefPtr<nsAtom> mMediaEmulated;
   RefPtr<gfxFontFeatureValueSet> mFontFeatureValuesLookup;
 
   // This pointer is nulled out through SetLinkHandler() in the destructors of
   // the classes which set it. (using SetLinkHandler() again).
   nsILinkHandler* MOZ_NON_OWNING_REF mLinkHandler;
 
-  // Formerly mLangGroup; moving from charset-oriented langGroup to
-  // maintaining actual language settings everywhere (see bug 524107).
-  // This may in fact hold a langGroup such as x-western rather than
-  // a specific language, however (e.g, if it is inferred from the
-  // charset rather than explicitly specified as a lang attribute).
-  RefPtr<nsAtom> mLanguage;
-
  public:
   // The following are public member variables so that we can use them
   // with mozilla::AutoToggle or mozilla::AutoRestore.
 
   // Should we disable font size inflation because we're inside of
   // shrink-wrapping calculations on an inflation container?
   bool mInflationDisabledForShrinkWrap;
 
@@ -1288,17 +1247,16 @@ class nsPresContext : public nsISupports
   float mFullZoom;           // Page zoom, defaults to 1.0
   float mOverrideDPPX;       // DPPX overrided, defaults to 0.0
   gfxSize mLastFontInflationScreenSize;
 
   int32_t mCurAppUnitsPerDevPixel;
   int32_t mAutoQualityMinFontSizePixelsPref;
 
   nsCOMPtr<nsITheme> mTheme;
-  nsLanguageAtomService* mLangService;
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
 
   mozilla::UniquePtr<nsBidi> mBidiEngine;
 
   AutoTArray<TransactionInvalidations, 4> mTransactions;
 
   // text performance metrics
   mozilla::UniquePtr<gfxTextPerfMetrics> mTextPerf;
@@ -1334,26 +1292,16 @@ class nsPresContext : public nsISupports
 
   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;
-
-  bool mFontGroupCacheDirty;
-  nsTHashtable<nsRefPtrHashKey<nsAtom>> mLanguagesUsed;
-
   uint32_t mInterruptChecksToSkip;
 
   // Counters for tests and tools that want to detect frame construction
   // or reflow.
   uint64_t mElementsRestyled;
   uint64_t mFramesConstructed;
   uint64_t mFramesReflowed;
 
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -48,18 +48,21 @@ nsSimplePageSequenceFrame::nsSimplePageS
       mTotalPages(-1),
       mCalledBeginPage(false),
       mCurrentCanvasListSetup(false) {
   nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
   mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
 
   // XXX Unsafe to assume successful allocation
   mPageData = new nsSharedPageData();
-  mPageData->mHeadFootFont = *PresContext()->GetDefaultFont(
-      kGenericFont_serif, aStyle->StyleFont()->mLanguage);
+  mPageData->mHeadFootFont =
+      *PresContext()
+           ->Document()
+           ->GetFontPrefsForLang(aStyle->StyleFont()->mLanguage)
+           ->GetDefaultFont(kGenericFont_serif);
   mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10);
 
   // Doing this here so we only have to go get these formats once
   SetPageNumberFormat("pagenumber", "%1$d", true);
   SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
 }
 
 nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame() {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5728,18 +5728,19 @@ gfxFloat nsTextFrame::ComputeSelectionUn
     case SelectionType::eSpellCheck: {
       // The thickness of the spellchecker underline shouldn't honor the font
       // metrics.  It should be constant pixels value which is decided from the
       // default font size.  Note that if the actual font size is smaller than
       // the default font size, we should use the actual font size because the
       // computed value from the default font size can be too thick for the
       // current font size.
       nscoord defaultFontSize =
-          aPresContext
-              ->GetDefaultFont(kPresContext_DefaultVariableFont_ID, nullptr)
+          aPresContext->Document()
+              ->GetFontPrefsForLang(nullptr)
+              ->GetDefaultFont(kPresContext_DefaultVariableFont_ID)
               ->size;
       int32_t zoomedFontSize = aPresContext->AppUnitsToDevPixels(
           nsStyleFont::ZoomText(aPresContext, defaultFontSize));
       gfxFloat fontSize =
           std::min(gfxFloat(zoomedFontSize), aFontMetrics.emHeight);
       fontSize = std::max(fontSize, 1.0);
       return ceil(fontSize / 20);
     }
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -101,26 +101,32 @@ ServoTraversalStatistics ServoTraversalS
 
 static RWLock* sServoFFILock = nullptr;
 
 static const nsFont* ThreadSafeGetDefaultFontHelper(
     const nsPresContext* aPresContext, nsAtom* aLanguage, uint8_t aGenericId) {
   bool needsCache = false;
   const nsFont* retval;
 
+  auto GetDefaultFont = [&](bool* aNeedsToCache) {
+    auto* prefs =
+        aPresContext->Document()->GetFontPrefsForLang(aLanguage, aNeedsToCache);
+    return prefs ? prefs->GetDefaultFont(aGenericId) : nullptr;
+  };
+
   {
     AutoReadLock guard(*sServoFFILock);
-    retval = aPresContext->GetDefaultFont(aGenericId, aLanguage, &needsCache);
+    retval = GetDefaultFont(&needsCache);
   }
   if (!needsCache) {
     return retval;
   }
   {
     AutoWriteLock guard(*sServoFFILock);
-    retval = aPresContext->GetDefaultFont(aGenericId, aLanguage, nullptr);
+    retval = GetDefaultFont(nullptr);
   }
   return retval;
 }
 
 /*
  * Does this child count as significant for selector matching?
  *
  * See nsStyleUtil::IsSignificantChild for details.
--- a/layout/style/MappedDeclarations.cpp
+++ b/layout/style/MappedDeclarations.cpp
@@ -18,19 +18,17 @@ void MappedDeclarations::SetIdentAtomVal
   Servo_DeclarationBlock_SetIdentStringValue(mDecl, aId, aValue);
   if (aId == eCSSProperty__x_lang) {
     // This forces the lang prefs result to be cached so that we can access them
     // off main thread during traversal.
     //
     // FIXME(emilio): Can we move mapped attribute declarations across
     // documents? Isn't this wrong in that case? This is pretty out of place
     // anyway.
-    if (nsPresContext* pc = mDocument->GetPresContext()) {
-      pc->ForceCacheLang(aValue);
-    }
+    mDocument->ForceCacheLang(aValue);
   }
 }
 
 void MappedDeclarations::SetBackgroundImage(const nsAttrValue& aValue) {
   if (aValue.Type() != nsAttrValue::eURL) {
     return;
   }
   // FIXME(emilio): Going through URL parsing again seems slightly wasteful.
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -382,31 +382,33 @@ void ServoStyleSet::PreTraverseSync() {
   mozilla::Unused << mDocument->GetRootElement();
 
   ResolveMappedAttrDeclarationBlocks();
 
   nsMediaFeatures::InitSystemMetrics();
 
   LookAndFeel::NativeInit();
 
-  nsPresContext* presContext = GetPresContext();
-  MOZ_ASSERT(presContext,
-             "For now, we don't call into here without a pres context");
+  mDocument->CacheAllKnownLangPrefs();
+
   if (gfxUserFontSet* userFontSet = mDocument->GetUserFontSet()) {
+    nsPresContext* presContext = GetPresContext();
+    MOZ_ASSERT(presContext,
+               "For now, we don't call into here without a pres context");
+
     // Ensure that the @font-face data is not stale
     uint64_t generation = userFontSet->GetGeneration();
     if (generation != mUserFontSetUpdateGeneration) {
       mDocument->GetFonts()->CacheFontLoadability();
       presContext->DeviceContext()->UpdateFontCacheUserFonts(userFontSet);
       mUserFontSetUpdateGeneration = generation;
     }
   }
 
   MOZ_ASSERT(!StylistNeedsUpdate());
-  presContext->CacheAllLangs();
 }
 
 void ServoStyleSet::PreTraverse(ServoTraversalFlags aFlags, Element* aRoot) {
   PreTraverseSync();
 
   // Process animation stuff that we should avoid doing during the parallel
   // traversal.
   SMILAnimationController* smilController =
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -105,34 +105,34 @@ nsStyleFont::nsStyleFont(const nsStyleFo
       mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize),
       mScriptMinSize(aSrc.mScriptMinSize),
       mScriptSizeMultiplier(aSrc.mScriptSizeMultiplier),
       mLanguage(aSrc.mLanguage) {
   MOZ_COUNT_CTOR(nsStyleFont);
 }
 
 nsStyleFont::nsStyleFont(const nsPresContext* aContext)
-    : mFont(*aContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID,
-                                      nullptr)),
+    : mFont(*aContext->Document()->GetFontPrefsForLang(nullptr)->GetDefaultFont(
+          kPresContext_DefaultVariableFont_ID)),
       mSize(ZoomText(aContext, mFont.size)),
       mFontSizeFactor(1.0),
       mFontSizeOffset(0),
       mFontSizeKeyword(NS_STYLE_FONT_SIZE_MEDIUM),
       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(nsPresContext::CSSTwipsToAppUnits(
           NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT))),
       mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER),
-      mLanguage(GetLanguage(aContext)) {
+      mLanguage(aContext->Document()->GetLanguageForStyle()) {
   MOZ_COUNT_CTOR(nsStyleFont);
   MOZ_ASSERT(NS_IsMainThread());
   nscoord minimumFontSize = aContext->MinFontSize(mLanguage);
   if (minimumFontSize > 0 && !aContext->IsChrome()) {
     mFont.size = std::max(mSize, minimumFontSize);
   } else {
     mFont.size = mSize;
   }
@@ -175,30 +175,16 @@ nsChangeHint nsStyleFont::CalcDifference
 /* static */ nscoord nsStyleFont::ZoomText(const nsPresContext* aPresContext,
                                            nscoord aSize) {
   // aSize can be negative (e.g.: calc(-1px)) so we can't assert that here.
   // The caller is expected deal with that.
   return NSToCoordTruncClamped(float(aSize) *
                                aPresContext->EffectiveTextZoom());
 }
 
-/* static */ already_AddRefed<nsAtom> nsStyleFont::GetLanguage(
-    const nsPresContext* aPresContext) {
-  RefPtr<nsAtom> language = aPresContext->GetContentLanguage();
-  if (!language) {
-    // we didn't find a (usable) Content-Language, so we fall back
-    // to whatever the presContext guessed from the charset
-    // NOTE this should not be used elsewhere, because we want websites
-    // to use UTF-8 with proper language tag, instead of relying on
-    // deriving language from charset. See bug 1040668 comment 67.
-    language = aPresContext->GetLanguageFromCharset();
-  }
-  return language.forget();
-}
-
 nsStyleMargin::nsStyleMargin(const nsPresContext* aContext) {
   MOZ_COUNT_CTOR(nsStyleMargin);
   nsStyleCoord zero(0, nsStyleCoord::CoordConstructor);
   NS_FOR_CSS_SIDES(side) { mMargin.Set(side, zero); }
 }
 
 nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc)
     : mMargin(aSrc.mMargin) {
@@ -3820,17 +3806,18 @@ nsStyleText::nsStyleText(const nsPresCon
       mTabSize(float(NS_STYLE_TABSIZE_INITIAL), eStyleUnit_Factor),
       mWordSpacing(0, nsStyleCoord::CoordConstructor),
       mLetterSpacing(eStyleUnit_Normal),
       mLineHeight(eStyleUnit_Normal),
       mTextIndent(0, nsStyleCoord::CoordConstructor),
       mWebkitTextStrokeWidth(0),
       mTextShadow(nullptr) {
   MOZ_COUNT_CTOR(nsStyleText);
-  RefPtr<nsAtom> language = aContext->GetContentLanguage();
+  RefPtr<nsAtom> language =
+      aContext->Document()->GetContentLanguageAsAtomForStyle();
   mTextEmphasisPosition =
       language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
           ? NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH
           : NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT;
 }
 
 nsStyleText::nsStyleText(const nsStyleText& aSource)
     : mTextAlign(aSource.mTextAlign),
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -94,18 +94,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsChangeHint CalcDifference(const nsStyleFont& aNewData) const;
 
   /**
    * Return aSize multiplied by the current text zoom factor (in aPresContext).
    * aSize is allowed to be negative, but the caller is expected to deal with
    * negative results.  The result is clamped to nscoord_MIN .. nscoord_MAX.
    */
   static nscoord ZoomText(const nsPresContext* aPresContext, nscoord aSize);
-  static already_AddRefed<nsAtom> GetLanguage(
-      const nsPresContext* aPresContext);
 
   nsFont mFont;
   nscoord mSize;  // Our "computed size". Can be different
                   // from mFont.size which is our "actual size" and is
                   // enforced to be >= the user's preferred min-size.
                   // mFont.size should be used for display purposes
                   // while mSize is the value to return in
                   // getComputedStyle() for example.