Merge inbound to mozilla-central r=merge a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Mon, 13 Nov 2017 11:53:28 +0200
changeset 391487 fc194660762d1b92e1679d860a8bf41116d0f54f
parent 391467 47752e9824da5125cb99c9d8160795b59adcc407 (current diff)
parent 391486 fc119ef7a5b0e1ef590e4a2a107e24e089ce0490 (diff)
child 391488 79f4abfff781c33f18da836272fd138d2949c7f9
child 391510 c6f1269dff84e733ee9085aad5313772d0ef7e7f
child 391567 7664017a8e916b6dbe7f66e86de09d1f685abb19
push id32888
push usernbeleuzu@mozilla.com
push dateMon, 13 Nov 2017 09:54:04 +0000
treeherdermozilla-central@fc194660762d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
fc194660762d / 59.0a1 / 20171113100232 / files
nightly linux64
fc194660762d / 59.0a1 / 20171113100232 / files
nightly mac
fc194660762d / 59.0a1 / 20171113100232 / files
nightly win32
fc194660762d / 59.0a1 / 20171113100232 / files
nightly win64
fc194660762d / 59.0a1 / 20171113100232 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central r=merge a=merge
browser/branding/aurora/content/icon48.png
browser/branding/aurora/content/icon64.png
browser/branding/aurora/mozicon128.png
browser/branding/nightly/content/icon48.png
browser/branding/nightly/content/icon64.png
browser/branding/nightly/mozicon128.png
browser/branding/official/content/icon48.png
browser/branding/official/content/icon64.png
browser/branding/official/mozicon128.png
browser/branding/unofficial/content/icon48.png
browser/branding/unofficial/content/icon64.png
browser/branding/unofficial/mozicon128.png
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -544,24 +544,24 @@ NS_INTERFACE_MAP_BEGIN(ContentChild)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
 NS_INTERFACE_MAP_END
 
 
 mozilla::ipc::IPCResult
 ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
                                             const StructuredCloneData& aInitialData,
                                             nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
-                                            nsTArray<FontFamilyListEntry>&& aFontFamilyList)
+                                            nsTArray<SystemFontListEntry>&& aFontList)
 {
   if (!sShutdownCanary) {
     return IPC_OK();
   }
 
   mLookAndFeelCache = Move(aLookAndFeelIntCache);
-  mFontFamilies = Move(aFontFamilyList);
+  mFontList = Move(aFontList);
   gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
   InitXPCOM(aXPCOMInit, aInitialData);
   InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
 
   return IPC_OK();
 }
 
 bool
@@ -2537,16 +2537,24 @@ mozilla::ipc::IPCResult
 ContentChild::RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries)
 {
   mAvailableDictionaries = aDictionaries;
   mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentChild::RecvUpdateFontList(InfallibleTArray<SystemFontListEntry>&& aFontList)
+{
+  mFontList = Move(aFontList);
+  gfxPlatform::GetPlatform()->UpdateFontList();
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentChild::RecvUpdateAppLocales(nsTArray<nsCString>&& aAppLocales)
 {
   LocaleService::GetInstance()->AssignAppLocales(aAppLocales);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvUpdateRequestedLocales(nsTArray<nsCString>&& aRequestedLocales)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -394,16 +394,18 @@ public:
                                                    const ClonedMessageData& aData) override;
 
   virtual mozilla::ipc::IPCResult RecvGeolocationUpdate(const GeoPosition& somewhere) override;
 
   virtual mozilla::ipc::IPCResult RecvGeolocationError(const uint16_t& errorCode) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
 
+  virtual mozilla::ipc::IPCResult RecvUpdateFontList(InfallibleTArray<SystemFontListEntry>&& aFontList) override;
+
   virtual mozilla::ipc::IPCResult RecvUpdateAppLocales(nsTArray<nsCString>&& aAppLocales) override;
   virtual mozilla::ipc::IPCResult RecvUpdateRequestedLocales(nsTArray<nsCString>&& aRequestedLocales) override;
 
   virtual mozilla::ipc::IPCResult RecvAddPermission(const IPC::Permission& permission) override;
 
   virtual mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason) override;
 
   virtual mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMainChromeTid,
@@ -598,17 +600,17 @@ public:
           const bool& anonymize,
           const bool& minimizeMemoryUsage,
           const MaybeFileDesc& DMDFile) override;
 
   virtual mozilla::ipc::IPCResult
   RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
                                 const StructuredCloneData& aInitialData,
                                 nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
-                                nsTArray<FontFamilyListEntry>&& aFontFamilyList) override;
+                                nsTArray<SystemFontListEntry>&& aFontList) override;
 
   virtual mozilla::ipc::IPCResult
   RecvProvideAnonymousTemporaryFile(const uint64_t& aID, const FileDescOrError& aFD) override;
 
   mozilla::ipc::IPCResult
   RecvSetPermissionsWithKey(const nsCString& aPermissionKey,
                             nsTArray<IPC::Permission>&& aPerms) override;
 
@@ -636,21 +638,21 @@ public:
   virtual mozilla::ipc::IPCResult
   RecvAddDynamicScalars(nsTArray<DynamicScalarDefinition>&& aDefs) override;
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   bool
   SendGetA11yContentId();
 #endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
-  // Get a reference to the font family list passed from the chrome process,
+  // Get a reference to the font list passed from the chrome process,
   // for use during gfx initialization.
-  InfallibleTArray<mozilla::dom::FontFamilyListEntry>&
-  SystemFontFamilyList() {
-    return mFontFamilies;
+  InfallibleTArray<mozilla::dom::SystemFontListEntry>&
+  SystemFontList() {
+    return mFontList;
   }
 
   // PURLClassifierChild
   virtual PURLClassifierChild*
   AllocPURLClassifierChild(const Principal& aPrincipal,
                            const bool& aUseTrackingProtection,
                            bool* aSuccess) override;
   virtual bool
@@ -744,20 +746,20 @@ private:
 
   InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
   RefPtr<ConsoleListener> mConsoleListener;
 
   nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
 
   InfallibleTArray<nsString> mAvailableDictionaries;
 
-  // Temporary storage for a list of available font families, passed from the
+  // Temporary storage for a list of available fonts, passed from the
   // parent process and used to initialize gfx in the child. Currently used
-  // only on MacOSX.
-  InfallibleTArray<mozilla::dom::FontFamilyListEntry> mFontFamilies;
+  // only on MacOSX and Linux.
+  InfallibleTArray<mozilla::dom::SystemFontListEntry> mFontList;
   // Temporary storage for nsXPLookAndFeel flags.
   nsTArray<LookAndFeelInt> mLookAndFeelCache;
 
   /**
    * An ID unique to the process containing our corresponding
    * content parent.
    *
    * We expect our content parent to set this ID immediately after opening a
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -166,16 +166,17 @@
 #include "SourceSurfaceRawData.h"
 #include "TabParent.h"
 #include "URIUtils.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIDocShell.h"
 #include "nsDocShell.h"
 #include "nsOpenURIInFrameParams.h"
 #include "mozilla/net/NeckoMessageUtils.h"
+#include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "prio.h"
 #include "private/pprio.h"
 #include "ContentProcessManager.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "nsPluginHost.h"
 #include "nsPluginTags.h"
@@ -2219,19 +2220,20 @@ ContentParent::InitInternal(ProcessPrior
       ErrorResult rv;
       initialData.Write(jsapi.cx(), init, rv);
       if (NS_WARN_IF(rv.Failed())) {
         rv.SuppressException();
         MOZ_CRASH();
       }
     }
   }
-  // This is only implemented (returns a non-empty list) by MacOSX at present.
-  nsTArray<FontFamilyListEntry> fontFamilies;
-  gfxPlatform::GetPlatform()->GetSystemFontFamilyList(&fontFamilies);
+  // This is only implemented (returns a non-empty list) by MacOSX and Linux
+  // at present.
+  nsTArray<SystemFontListEntry> fontList;
+  gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
   nsTArray<LookAndFeelInt> lnfCache = LookAndFeel::GetIntCache();
 
   // Content processes have no permission to access profile directory, so we
   // send the file URL instead.
   StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
   if (ucs) {
     SerializeURI(ucs->GetSheetURI(), xpcomInit.userContentSheetURL());
   } else {
@@ -2264,17 +2266,17 @@ ContentParent::InitInternal(ProcessPrior
   // Send the dynamic scalar definitions to the new process.
   TelemetryIPC::GetDynamicScalarDefinitions(xpcomInit.dynamicScalarDefs());
 
   // Must send screen info before send initialData
   ScreenManager& screenManager = ScreenManager::GetSingleton();
   screenManager.CopyScreensToRemote(this);
 
   Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache,
-                                          fontFamilies);
+                                          fontList);
 
   if (aSendRegisteredChrome) {
     nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
     nsChromeRegistryChrome* chromeRegistry =
       static_cast<nsChromeRegistryChrome*>(registrySvc.get());
     chromeRegistry->SendRegisteredChrome(this);
   }
 
@@ -4257,16 +4259,27 @@ ContentParent::NotifyUpdatedDictionaries
   InfallibleTArray<nsString> dictionaries;
   spellChecker->GetDictionaryList(&dictionaries);
 
   for (auto* cp : AllProcesses(eLive)) {
     Unused << cp->SendUpdateDictionaryList(dictionaries);
   }
 }
 
+void
+ContentParent::NotifyUpdatedFonts()
+{
+  InfallibleTArray<SystemFontListEntry> fontList;
+  gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    Unused << cp->SendUpdateFontList(fontList);
+  }
+}
+
 /*static*/ void
 ContentParent::UnregisterRemoteFrame(const TabId& aTabId,
                                const ContentParentId& aCpId,
                                bool aMarkedDestroying)
 {
   if (XRE_IsParentProcess()) {
     ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
     ContentParent* cp = cpm->GetContentProcessById(aCpId);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -264,16 +264,18 @@ public:
       sContentParents ? sContentParents->getFirst() : nullptr;
     return ContentParentIterator(aPolicy, first);
   }
 
   static bool IgnoreIPCPrincipal();
 
   static void NotifyUpdatedDictionaries();
 
+  static void NotifyUpdatedFonts();
+
 #if defined(XP_WIN)
   /**
    * Windows helper for firing off an update window request to a plugin
    * instance.
    *
    * aWidget - the eWindowType_plugin_ipc_chrome widget associated with
    *           this plugin window.
    */
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -127,16 +127,29 @@ struct FontListEntry {
 // from chrome to content processes.
 // The entryType field distinguishes several types of font family
 // record; see gfxMacPlatformFontList.h for values and meaning.
 struct FontFamilyListEntry {
     nsString familyName;
     uint8_t  entryType;
 };
 
+// Used on Linux to pass list of font patterns from chrome to content.
+struct FontPatternListEntry {
+    nsCString pattern;
+    bool      appFontFamily;
+};
+
+// Wrap the Font*ListEntry records in a union so the SetXPCOMProcessAttributes
+// message can pass an array of either type.
+union SystemFontListEntry {
+    FontFamilyListEntry;
+    FontPatternListEntry;
+};
+
 union PrefValue {
   nsCString;
   int32_t;
   bool;
 };
 
 union MaybePrefValue {
   PrefValue;
@@ -424,16 +437,18 @@ child:
     async NotifyAlertsObserver(nsCString topic, nsString data);
 
     async GeolocationUpdate(GeoPosition somewhere);
 
     async GeolocationError(uint16_t errorCode);
 
     async UpdateDictionaryList(nsString[] dictionaries);
 
+    async UpdateFontList(SystemFontListEntry[] fontList);
+
     async UpdateAppLocales(nsCString[] appLocales);
     async UpdateRequestedLocales(nsCString[] requestedLocales);
 
     // nsIPermissionManager messages
     async AddPermission(Permission permission);
 
     async FlushMemory(nsString reason);
 
@@ -471,18 +486,18 @@ child:
     /**
      * Send BlobURLRegistrationData to child process.
      */
     async InitBlobURLs(BlobURLRegistrationData[] registrations);
 
     async SetXPCOMProcessAttributes(XPCOMInitData xpcomInit,
                                     StructuredCloneData initialData,
                                     LookAndFeelInt[] lookAndFeelIntCache,
-                                    /* used on MacOSX only: */
-                                    FontFamilyListEntry[] fontFamilyList);
+                                    /* used on MacOSX and Linux only: */
+                                    SystemFontListEntry[] systemFontList);
 
     // Notify child that last-pb-context-exited notification was observed
     async LastPrivateDocShellDestroyed();
 
     async NotifyProcessPriorityChanged(ProcessPriority priority);
     async MinimizeMemoryUsage();
 
     /**
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -7,44 +7,56 @@
 
 #include "gfxFcPlatformFontList.h"
 #include "gfxFont.h"
 #include "gfxFontConstants.h"
 #include "gfxFontFamilyList.h"
 #include "gfxFT2Utils.h"
 #include "gfxPlatform.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
 #include "nsGkAtoms.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeRange.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCharSeparatedTokenizer.h"
+#include "nsXULAppAPI.h"
 
 #include "mozilla/gfx/HelpersCairo.h"
 
 #include <fontconfig/fcfreetype.h>
+#include <unistd.h>
 
 #ifdef MOZ_WIDGET_GTK
 #include <gdk/gdk.h>
 #include "gfxPlatformGtk.h"
 #endif
 
 #ifdef MOZ_X11
 #include "mozilla/X11Util.h"
 #endif
 
+#ifdef MOZ_CONTENT_SANDBOX
+#include "mozilla/SandboxBrokerPolicyFactory.h"
+#include "mozilla/SandboxSettings.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 
+using mozilla::dom::SystemFontListEntry;
+using mozilla::dom::FontPatternListEntry;
+
 #ifndef FC_POSTSCRIPT_NAME
 #define FC_POSTSCRIPT_NAME  "postscriptname"      /* String */
 #endif
 
 #define PRINTING_FC_PROPERTY "gfx.printing"
 
 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
                                LogLevel::Debug, args)
@@ -1200,16 +1212,35 @@ gfxFontconfigFontFamily::SupportsLangGro
 
 /* virtual */
 gfxFontconfigFontFamily::~gfxFontconfigFontFamily()
  {
     // Should not be dropped by stylo
     MOZ_ASSERT(NS_IsMainThread());
 }
 
+template<typename Func>
+void
+gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc)
+{
+    if (HasStyles()) {
+        for (auto& fe : mAvailableFonts) {
+            if (!fe) {
+                continue;
+            }
+            auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
+            aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
+        }
+    } else {
+        for (auto& pat : mFontPatterns) {
+            aAddPatternFunc(pat, mContainsAppFonts);
+        }
+    }
+}
+
 gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
                                      cairo_scaled_font_t *aScaledFont,
                                      FcPattern *aPattern,
                                      gfxFloat aAdjustedSize,
                                      gfxFontEntry *aFontEntry,
                                      const gfxFontStyle *aFontStyle,
                                      bool aNeedsBold)
     : gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
@@ -1239,28 +1270,30 @@ gfxFontconfigFont::GetScaledFont(mozilla
 
 gfxFcPlatformFontList::gfxFcPlatformFontList()
     : mLocalNames(64)
     , mGenericMappings(32)
     , mFcSubstituteCache(64)
     , mLastConfig(nullptr)
     , mAlwaysUseFontconfigGenerics(true)
 {
-    // if the rescan interval is set, start the timer
-    int rescanInterval = FcConfigGetRescanInterval(nullptr);
-    if (rescanInterval) {
-        mLastConfig = FcConfigGetCurrent();
-        NS_NewTimerWithFuncCallback(getter_AddRefs(mCheckFontUpdatesTimer),
-                                    CheckFontUpdates,
-                                    this,
-                                    (rescanInterval + 1) * 1000,
-                                    nsITimer::TYPE_REPEATING_SLACK,
-                                    "gfxFcPlatformFontList::gfxFcPlatformFontList");
-        if (!mCheckFontUpdatesTimer) {
-            NS_WARNING("Failure to create font updates timer");
+    if (XRE_IsParentProcess()) {
+        // if the rescan interval is set, start the timer
+        int rescanInterval = FcConfigGetRescanInterval(nullptr);
+        if (rescanInterval) {
+            mLastConfig = FcConfigGetCurrent();
+            NS_NewTimerWithFuncCallback(getter_AddRefs(mCheckFontUpdatesTimer),
+                                        CheckFontUpdates,
+                                        this,
+                                        (rescanInterval + 1) * 1000,
+                                        nsITimer::TYPE_REPEATING_SLACK,
+                                        "gfxFcPlatformFontList::gfxFcPlatformFontList");
+            if (!mCheckFontUpdatesTimer) {
+                NS_WARNING("Failure to create font updates timer");
+            }
         }
     }
 
 #ifdef MOZ_BUNDLED_FONTS
     mBundledFontsInitialized = false;
 #endif
 }
 
@@ -1268,121 +1301,268 @@ gfxFcPlatformFontList::~gfxFcPlatformFon
 {
     if (mCheckFontUpdatesTimer) {
         mCheckFontUpdatesTimer->Cancel();
         mCheckFontUpdatesTimer = nullptr;
     }
 }
 
 void
-gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet, bool aAppFonts)
+gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
+                                          const SandboxPolicy* aPolicy,
+                                          bool aAppFonts)
 {
     // This iterates over the fonts in a font set and adds in gfxFontFamily
-    // objects for each family. The patterns for individual fonts are not
-    // copied here. When a family is actually used, the fonts in the family
-    // are enumerated and the patterns copied. Note that we're explicitly
-    // excluding non-scalable fonts such as X11 bitmap fonts, which
-    // Chrome Skia/Webkit code does also.
+    // objects for each family. Individual gfxFontEntry objects for each face
+    // are not created here; the patterns are just stored in the family. When
+    // a family is actually used, it will be populated with gfxFontEntry
+    // records and the patterns moved to those.
 
     if (!aFontSet) {
         NS_WARNING("AddFontSetFamilies called with a null font set.");
         return;
     }
 
     FcChar8* lastFamilyName = (FcChar8*)"";
     RefPtr<gfxFontconfigFontFamily> fontFamily;
     nsAutoString familyName;
     for (int f = 0; f < aFontSet->nfont; f++) {
-        FcPattern* font = aFontSet->fonts[f];
+        FcPattern* pattern = aFontSet->fonts[f];
 
-        // get canonical name
-        uint32_t cIndex = FindCanonicalNameIndex(font, FC_FAMILYLANG);
-        FcChar8* canonical = nullptr;
-        FcPatternGetString(font, FC_FAMILY, cIndex, &canonical);
-        if (!canonical) {
+        // Skip any fonts that aren't readable for us (e.g. due to restrictive
+        // file ownership/permissions).
+        FcChar8* path;
+        if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
+            continue;
+        }
+        if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
             continue;
         }
 
-        // same as the last one? no need to add a new family, skip
-        if (FcStrCmp(canonical, lastFamilyName) != 0) {
-            lastFamilyName = canonical;
+#ifdef MOZ_CONTENT_SANDBOX
+        // Skip any fonts that will be blocked by the content-process sandbox
+        // policy.
+        if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
+                         SandboxBroker::Perms::MAY_READ)) {
+            continue;
+        }
+#endif
+
+        AddPatternToFontList(pattern, lastFamilyName,
+                             familyName, fontFamily, aAppFonts);
+    }
+}
 
-            // add new family if one doesn't already exist
-            familyName.Truncate();
-            AppendUTF8toUTF16(ToCharPtr(canonical), familyName);
-            nsAutoString keyName(familyName);
-            ToLowerCase(keyName);
+void
+gfxFcPlatformFontList::AddPatternToFontList(FcPattern* aFont,
+                                            FcChar8*& aLastFamilyName,
+                                            nsAString& aFamilyName,
+                                            RefPtr<gfxFontconfigFontFamily>& aFontFamily,
+                                            bool aAppFonts)
+{
+    // get canonical name
+    uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
+    FcChar8* canonical = nullptr;
+    FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
+    if (!canonical) {
+        return;
+    }
 
-            fontFamily = static_cast<gfxFontconfigFontFamily*>
-                             (mFontFamilies.GetWeak(keyName));
-            if (!fontFamily) {
-                fontFamily = new gfxFontconfigFontFamily(familyName);
-                mFontFamilies.Put(keyName, fontFamily);
-            }
-            // Record if the family contains fonts from the app font set
-            // (in which case we won't rely on fontconfig's charmap, due to
-            // bug 1276594).
-            if (aAppFonts) {
-                fontFamily->SetFamilyContainsAppFonts(true);
-            }
+    // same as the last one? no need to add a new family, skip
+    if (FcStrCmp(canonical, aLastFamilyName) != 0) {
+        aLastFamilyName = canonical;
+
+        // add new family if one doesn't already exist
+        aFamilyName.Truncate();
+        AppendUTF8toUTF16(ToCharPtr(canonical), aFamilyName);
+        nsAutoString keyName(aFamilyName);
+        ToLowerCase(keyName);
 
-            // Add pointers to other localized family names. Most fonts
-            // only have a single name, so the first call to GetString
-            // will usually not match
-            FcChar8* otherName;
-            int n = (cIndex == 0 ? 1 : 0);
-            while (FcPatternGetString(font, FC_FAMILY, n, &otherName) == FcResultMatch) {
-                NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
-                AddOtherFamilyName(fontFamily, otherFamilyName);
-                n++;
-                if (n == int(cIndex)) {
-                    n++; // skip over canonical name
-                }
+        aFontFamily = static_cast<gfxFontconfigFontFamily*>
+            (mFontFamilies.GetWeak(keyName));
+        if (!aFontFamily) {
+            aFontFamily = new gfxFontconfigFontFamily(aFamilyName);
+            mFontFamilies.Put(keyName, aFontFamily);
+        }
+        // Record if the family contains fonts from the app font set
+        // (in which case we won't rely on fontconfig's charmap, due to
+        // bug 1276594).
+        if (aAppFonts) {
+            aFontFamily->SetFamilyContainsAppFonts(true);
+        }
+
+        // Add pointers to other localized family names. Most fonts
+        // only have a single name, so the first call to GetString
+        // will usually not match
+        FcChar8* otherName;
+        int n = (cIndex == 0 ? 1 : 0);
+        while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) ==
+               FcResultMatch) {
+            NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
+            AddOtherFamilyName(aFontFamily, otherFamilyName);
+            n++;
+            if (n == int(cIndex)) {
+                n++; // skip over canonical name
             }
         }
+    }
 
-        NS_ASSERTION(fontFamily, "font must belong to a font family");
-        fontFamily->AddFontPattern(font);
+    MOZ_ASSERT(aFontFamily, "font must belong to a font family");
+    aFontFamily->AddFontPattern(aFont);
 
-        // map the psname, fullname ==> font family for local font lookups
-        nsAutoString psname, fullname;
-        GetFaceNames(font, familyName, psname, fullname);
-        if (!psname.IsEmpty()) {
-            ToLowerCase(psname);
-            mLocalNames.Put(psname, font);
-        }
-        if (!fullname.IsEmpty()) {
-            ToLowerCase(fullname);
-            mLocalNames.Put(fullname, font);
-        }
+    // map the psname, fullname ==> font family for local font lookups
+    nsAutoString psname, fullname;
+    GetFaceNames(aFont, aFamilyName, psname, fullname);
+    if (!psname.IsEmpty()) {
+        ToLowerCase(psname);
+        mLocalNames.Put(psname, aFont);
+    }
+    if (!fullname.IsEmpty()) {
+        ToLowerCase(fullname);
+        mLocalNames.Put(fullname, aFont);
     }
 }
 
 nsresult
 gfxFcPlatformFontList::InitFontListForPlatform()
 {
-    mLastConfig = FcConfigGetCurrent();
+#ifdef MOZ_BUNDLED_FONTS
+    ActivateBundledFonts();
+#endif
 
     mLocalNames.Clear();
     mFcSubstituteCache.Clear();
 
+    mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
+    mOtherFamilyNamesInitialized = true;
+
+    if (XRE_IsContentProcess()) {
+        // Content process: use the font list passed from the chrome process,
+        // because we can't rely on fontconfig in the presence of sandboxing;
+        // it may report fonts that we can't actually access.
+
+        FcChar8* lastFamilyName = (FcChar8*)"";
+        RefPtr<gfxFontconfigFontFamily> fontFamily;
+        nsAutoString familyName;
+
+        // Get font list that was passed during XPCOM startup
+        // or in an UpdateFontList message.
+        auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
+
+        // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
+        // we need to escape any leading space in the charset element,
+        // otherwise FcNameParse will fail. :(
+        //
+        // The bug was introduced on 2013-05-24 by
+        //   https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
+        //   "Bug 64906 - FcNameParse() should ignore leading whitespace in parameters"
+        // because ignoring a leading space in the encoded value of charset
+        // causes erroneous decoding of the whole element.
+        // This first shipped in version 2.10.94, and was eventually fixed as
+        // a side-effect of switching to the "human-readable" representation of
+        // charsets on 2014-07-03 in
+        //   https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
+        //   "Change charset parse/unparse format to be human readable"
+        // (with a followup fix next day) which means a leading space is no
+        // longer significant. This fix landed after 2.11.1 had been shipped,
+        // so the first version tag without the bug is 2.11.91.
+        int fcVersion = FcGetVersion();
+        bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
+
+        for (SystemFontListEntry& fle : fontList) {
+            MOZ_ASSERT(fle.type() ==
+                       SystemFontListEntry::Type::TFontPatternListEntry);
+            FontPatternListEntry& fpe(fle);
+            nsCString& patternStr = fpe.pattern();
+            if (fcCharsetParseBug) {
+                int32_t index = patternStr.Find(":charset= ");
+                if (index != kNotFound) {
+                    // insert backslash after the =, before the space
+                    patternStr.Insert('\\', index + 9);
+                }
+            }
+            FcPattern* pattern =
+                FcNameParse((const FcChar8*)patternStr.get());
+            AddPatternToFontList(pattern, lastFamilyName, familyName,
+                                 fontFamily, fpe.appFontFamily());
+            FcPatternDestroy(pattern);
+        }
+
+        LOG_FONTLIST(("got font list from chrome process: "
+                      "%u faces in %u families",
+                      (unsigned)fontList.Length(), mFontFamilies.Count()));
+
+        fontList.Clear();
+
+        return NS_OK;
+    }
+
+    mLastConfig = FcConfigGetCurrent();
+
+    UniquePtr<SandboxPolicy> policy;
+
+#ifdef MOZ_CONTENT_SANDBOX
+    // Create a temporary SandboxPolicy to check font paths; use a fake PID
+    // to avoid picking up any PID-specific rules by accident.
+    SandboxBrokerPolicyFactory policyFactory;
+    if (GetEffectiveContentSandboxLevel() > 0 &&
+        !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+        policy = policyFactory.GetContentPolicy(-1, false);
+    }
+#endif
+
     // iterate over available fonts
     FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
-    AddFontSetFamilies(systemFonts, /* aAppFonts = */ false);
-    mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
+    AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
 
 #ifdef MOZ_BUNDLED_FONTS
-    ActivateBundledFonts();
     FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
-    AddFontSetFamilies(appFonts, /* aAppFonts = */ true);
+    AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
 #endif
 
-    mOtherFamilyNamesInitialized = true;
+    return NS_OK;
+}
 
-    return NS_OK;
+void
+gfxFcPlatformFontList::ReadSystemFontList(
+    InfallibleTArray<SystemFontListEntry>* retValue)
+{
+    // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
+    // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
+    // an older version, we manually append it to the unparsed pattern.
+    if (FcGetVersion() < 20900) {
+        for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+            auto family =
+                static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
+            family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
+                char* s = (char*)FcNameUnparse(aPat);
+                nsAutoCString patternStr(s);
+                free(s);
+                if (FcResultMatch ==
+                    FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&s)) {
+                    patternStr.Append(":file=");
+                    patternStr.Append(s);
+                }
+                retValue->AppendElement(FontPatternListEntry(patternStr,
+                                                             aAppFonts));
+            });
+        }
+    } else {
+        for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+            auto family =
+                static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
+            family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
+                char* s = (char*)FcNameUnparse(aPat);
+                nsDependentCString patternStr(s);
+                retValue->AppendElement(FontPatternListEntry(patternStr,
+                                                             aAppFonts));
+                free(s);
+            });
+        }
+    }
 }
 
 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
 // FcFontList results in the list containing the localized names as dictated
 // by system defaults.
 static void
 GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsAtom *aLangGroup)
 {
@@ -1973,25 +2153,31 @@ gfxFcPlatformFontList::PrefFontListsUseO
         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
     }
     return prefFontsUseOnlyGenerics;
 }
 
 /* static */ void
 gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis)
 {
+    // A content process is not supposed to check this directly;
+    // it will be notified by the parent when the font list changes.
+    MOZ_ASSERT(XRE_IsParentProcess());
+
     // check for font updates
     FcInitBringUptoDate();
 
     // update fontlist if current config changed
     gfxFcPlatformFontList *pfl = static_cast<gfxFcPlatformFontList*>(aThis);
     FcConfig* current = FcConfigGetCurrent();
     if (current != pfl->GetLastConfig()) {
         pfl->UpdateFontList();
         pfl->ForceGlobalReflow();
+
+        mozilla::dom::ContentParent::NotifyUpdatedFonts();
     }
 }
 
 gfxFontFamily*
 gfxFcPlatformFontList::CreateFontFamily(const nsAString& aName) const
 {
     return new gfxFontconfigFontFamily(aName);
 }
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -16,16 +16,26 @@
 
 #include <fontconfig/fontconfig.h>
 #include "ft2build.h"
 #include FT_FREETYPE_H
 #include FT_TRUETYPE_TABLES_H
 #include <cairo.h>
 #include <cairo-ft.h>
 
+#ifdef MOZ_CONTENT_SANDBOX
+#include "mozilla/SandboxBroker.h"
+#endif
+
+namespace mozilla {
+    namespace dom {
+        class SystemFontListEntry;
+    };
+};
+
 template <>
 class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
 {
 public:
     static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
     static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
 };
 
@@ -177,16 +187,19 @@ class gfxFontconfigFontFamily : public g
 public:
     explicit gfxFontconfigFontFamily(const nsAString& aName) :
         gfxFontFamily(aName),
         mContainsAppFonts(false),
         mHasNonScalableFaces(false),
         mForceScalable(false)
     { }
 
+    template<typename Func>
+    void AddFacesToFontList(Func aAddPatternFunc);
+
     void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) override;
 
     // Families are constructed initially with just references to patterns.
     // When necessary, these are enumerated within FindStyleVariations.
     void AddFontPattern(FcPattern* aFontPattern);
 
     void SetFamilyContainsAppFonts(bool aContainsAppFonts)
     {
@@ -249,16 +262,18 @@ public:
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
 
     void GetFontList(nsAtom *aLangGroup,
                      const nsACString& aGenericFamily,
                      nsTArray<nsString>& aListOfFonts) override;
 
+    void ReadSystemFontList(
+        InfallibleTArray<mozilla::dom::SystemFontListEntry>* retValue);
 
     gfxFontEntry*
     LookupLocalFont(const nsAString& aFontName, uint16_t aWeight,
                     int16_t aStretch, uint8_t aStyle) override;
 
     gfxFontEntry*
     MakePlatformFont(const nsAString& aFontName, uint16_t aWeight,
                      int16_t aStretch,
@@ -289,19 +304,33 @@ public:
         mGenericMappings.Clear();
     }
 
     static FT_Library GetFTLibrary();
 
 protected:
     virtual ~gfxFcPlatformFontList();
 
+#ifdef MOZ_CONTENT_SANDBOX
+    typedef mozilla::SandboxBroker::Policy SandboxPolicy;
+#else
+    // Dummy type just so we can still have a SandboxPolicy* parameter.
+    struct SandboxPolicy {};
+#endif
+
     // Add all the font families found in a font set.
     // aAppFonts indicates whether this is the system or application fontset.
-    void AddFontSetFamilies(FcFontSet* aFontSet, bool aAppFonts);
+    void AddFontSetFamilies(FcFontSet* aFontSet, const SandboxPolicy* aPolicy,
+                            bool aAppFonts);
+
+    // Helper for above, to add a single font pattern.
+    void AddPatternToFontList(FcPattern* aFont, FcChar8*& aLastFamilyName,
+                              nsAString& aFamilyName,
+                              RefPtr<gfxFontconfigFontFamily>& aFontFamily,
+                              bool aAppFonts);
 
     // figure out which families fontconfig maps a generic to
     // (aGeneric assumed already lowercase)
     PrefFontList* FindGenericFamilies(const nsAString& aGeneric,
                                       nsAtom* aLanguage);
 
     // are all pref font settings set to use fontconfig generics?
     bool PrefFontListsUseOnlyGenerics();
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -154,18 +154,18 @@ public:
     // Values for the entryType field in FontFamilyListEntry records passed
     // from chrome to content process.
     enum FontFamilyEntryType {
         kStandardFontFamily = 0, // a standard installed font family
         kHiddenSystemFontFamily = 1, // hidden system family, not exposed to UI
         kTextSizeSystemFontFamily = 2, // name of 'system' font at text sizes
         kDisplaySizeSystemFontFamily = 3 // 'system' font at display sizes
     };
-    void GetSystemFontFamilyList(
-        InfallibleTArray<mozilla::dom::FontFamilyListEntry>* aList);
+    void ReadSystemFontList(
+        InfallibleTArray<mozilla::dom::SystemFontListEntry>* aList);
 
 protected:
     gfxFontFamily*
     GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
 
 private:
     friend class gfxPlatformMac;
 
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -59,28 +59,30 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
 #include "nsCharTraits.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "gfxFontConstants.h"
 
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/gfx/2D.h"
 
 #include <unistd.h>
 #include <time.h>
 #include <dlfcn.h>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using mozilla::dom::SystemFontListEntry;
 using mozilla::dom::FontFamilyListEntry;
 
 // indexes into the NSArray objects that the Cocoa font manager returns
 // as the available members of a family
 #define INDEX_FONT_POSTSCRIPT_NAME 0
 #define INDEX_FONT_FACE_NAME 1
 #define INDEX_FONT_WEIGHT 2
 #define INDEX_FONT_TRAITS 3
@@ -982,34 +984,42 @@ gfxMacPlatformFontList::gfxMacPlatformFo
     nsCOMPtr<nsIFile> langFonts(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
     if (NS_SUCCEEDED(rv)) {
         rv = langFonts->InitWithNativePath(NS_LITERAL_CSTRING(LANG_FONTS_DIR));
         if (NS_SUCCEEDED(rv)) {
             ActivateFontsFromDir(langFonts);
         }
     }
 
-    ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
-                                      this,
-                                      RegisteredFontsChangedNotificationCallback,
-                                      kCTFontManagerRegisteredFontsChangedNotification,
-                                      0,
-                                      CFNotificationSuspensionBehaviorDeliverImmediately);
+    // Only the parent process listens for OS font-changed notifications;
+    // after rebuilding its list, it will update the content processes.
+    if (XRE_IsParentProcess()) {
+        ::CFNotificationCenterAddObserver(
+            ::CFNotificationCenterGetLocalCenter(),
+            this,
+            RegisteredFontsChangedNotificationCallback,
+            kCTFontManagerRegisteredFontsChangedNotification,
+            0,
+            CFNotificationSuspensionBehaviorDeliverImmediately);
+    }
 
     // cache this in a static variable so that MacOSFontFamily objects
     // don't have to repeatedly look it up
     sFontManager = [NSFontManager sharedFontManager];
 }
 
 gfxMacPlatformFontList::~gfxMacPlatformFontList()
 {
-    ::CFNotificationCenterRemoveObserver(::CFNotificationCenterGetLocalCenter(),
-                                         this,
-                                         kCTFontManagerRegisteredFontsChangedNotification,
-                                         0);
+    if (XRE_IsParentProcess()) {
+        ::CFNotificationCenterRemoveObserver(
+            ::CFNotificationCenterGetLocalCenter(),
+            this,
+            kCTFontManagerRegisteredFontsChangedNotification,
+            0);
+    }
 
     if (mDefaultFont) {
         ::CFRelease(mDefaultFont);
     }
 }
 
 void
 gfxMacPlatformFontList::AddFamily(const nsAString& aFamilyName,
@@ -1051,18 +1061,18 @@ gfxMacPlatformFontList::AddFamily(CFStri
     nsAutoString familyName;
     nsCocoaUtils::GetStringForNSString(family, familyName);
 
     bool isHiddenSystemFont = familyName[0] == '.';
     AddFamily(familyName, isHiddenSystemFont);
 }
 
 void
-gfxMacPlatformFontList::GetSystemFontFamilyList(
-    InfallibleTArray<FontFamilyListEntry>* aList)
+gfxMacPlatformFontList::ReadSystemFontList(
+    InfallibleTArray<SystemFontListEntry>* aList)
 {
     // Note: We rely on the records for mSystemTextFontFamilyName and
     // mSystemDisplayFontFamilyName (if present) being *before* the main
     // font list, so that those names are known in the content process
     // by the time we add the actual family records to the font list.
     aList->AppendElement(FontFamilyListEntry(mSystemTextFontFamilyName,
                                              kTextSizeSystemFontFamily));
     if (mUseSizeSensitiveSystemFont) {
@@ -1094,42 +1104,41 @@ gfxMacPlatformFontList::InitFontListForP
 
     // reset system font list
     mSystemFontFamilies.Clear();
 
     if (XRE_IsContentProcess()) {
         // Content process: use font list passed from the chrome process via
         // the GetXPCOMProcessAttributes message, because it's much faster than
         // querying Core Text again in the child.
-        mozilla::dom::ContentChild* cc =
-            mozilla::dom::ContentChild::GetSingleton();
-        for (auto f : cc->SystemFontFamilyList()) {
-            switch (f.entryType()) {
+        auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
+        for (SystemFontListEntry& fle : fontList) {
+            MOZ_ASSERT(fle.type() ==
+                       SystemFontListEntry::Type::TFontFamilyListEntry);
+            FontFamilyListEntry& ffe(fle);
+            switch (ffe.entryType()) {
             case kStandardFontFamily:
-                AddFamily(f.familyName(), false);
+                AddFamily(ffe.familyName(), false);
                 break;
             case kHiddenSystemFontFamily:
-                AddFamily(f.familyName(), true);
+                AddFamily(ffe.familyName(), true);
                 break;
             case kTextSizeSystemFontFamily:
-                mSystemTextFontFamilyName = f.familyName();
+                mSystemTextFontFamilyName = ffe.familyName();
                 break;
             case kDisplaySizeSystemFontFamily:
-                mSystemDisplayFontFamilyName = f.familyName();
+                mSystemDisplayFontFamilyName = ffe.familyName();
                 mUseSizeSensitiveSystemFont = true;
                 break;
             }
         }
-        // The ContentChild doesn't need the font list any longer.
-        cc->SystemFontFamilyList().Clear();
-    }
-
-    // If this is the chrome process, or if for some reason we failed to get
-    // a usable list above, get the available fonts from Core Text.
-    if (!mFontFamilies.Count()) {
+        fontList.Clear();
+    } else {
+        // We're not a content process, so get the available fonts directly
+        // from Core Text.
         InitSystemFontNames();
         CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
         for (NSString* familyName in (NSArray*)familyNames) {
             AddFamily((CFStringRef)familyName);
         }
         CFRelease(familyNames);
     }
 
@@ -1338,16 +1347,18 @@ gfxMacPlatformFontList::RegisteredFontsC
 
     gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
 
     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
     fl->UpdateFontList();
 
     // modify a preference that will trigger reflow everywhere
     fl->ForceGlobalReflow();
+
+    mozilla::dom::ContentParent::NotifyUpdatedFonts();
 }
 
 gfxFontEntry*
 gfxMacPlatformFontList::PlatformGlobalFontFallback(const uint32_t aCh,
                                                    Script aRunScript,
                                                    const gfxFontStyle* aMatchStyle,
                                                    gfxFontFamily** aMatchedFamily)
 {
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -59,17 +59,17 @@ class FeatureState;
 inline uint32_t
 BackendTypeBit(BackendType b)
 {
   return 1 << uint8_t(b);
 }
 
 } // namespace gfx
 namespace dom {
-class FontFamilyListEntry;
+class SystemFontListEntry;
 }
 } // namespace mozilla
 
 #define MOZ_PERFORMANCE_WARNING(module, ...) \
   do { \
     if (gfxPlatform::PerfWarnings()) { \
       printf_stderr("[" module "] " __VA_ARGS__); \
     } \
@@ -332,22 +332,22 @@ public:
      * that correspond to the given language group or generic font family
      * (or both, or neither).
      */
     virtual nsresult GetFontList(nsAtom *aLangGroup,
                                  const nsACString& aGenericFamily,
                                  nsTArray<nsString>& aListOfFonts);
 
     /**
-     * Fill aFontFamilies with a list of FontFamilyListEntry records for the
+     * Fill aFontList with a list of SystemFontListEntry records for the
      * available fonts on the platform; used to pass the list from chrome to
-     * content process. Currently implemented only on MacOSX.
+     * content process. Currently implemented only on MacOSX and Linux.
      */
-    virtual void GetSystemFontFamilyList(
-      InfallibleTArray<mozilla::dom::FontFamilyListEntry>* aFontFamilies)
+    virtual void ReadSystemFontList(
+      InfallibleTArray<mozilla::dom::SystemFontListEntry>* aFontList)
     { }
 
     /**
      * Rebuilds the any cached system font lists
      */
     virtual nsresult UpdateFontList();
 
     /**
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -12,24 +12,26 @@
 #include "gfxUserFontSet.h"
 
 #include "nsCRT.h"
 #include "nsGkAtoms.h"
 #include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsUnicodeRange.h"
 #include "nsUnicodeProperties.h"
+#include "nsXULAppAPI.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/gfx/2D.h"
 
 #include <locale.h>
 
 using namespace mozilla;
 using mozilla::intl::LocaleService;
 using mozilla::intl::OSPreferences;
 
@@ -195,33 +197,49 @@ gfxPlatformFontList::gfxPlatformFontList
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
     Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
 
-    Preferences::RegisterCallback(FontWhitelistPrefChanged,
-                                  kFontSystemWhitelistPref);
+    // Only the parent process listens for whitelist changes; it will then
+    // notify its children to rebuild their font lists.
+    if (XRE_IsParentProcess()) {
+        Preferences::RegisterCallback(FontWhitelistPrefChanged,
+                                      kFontSystemWhitelistPref);
+    }
 
     RegisterStrongMemoryReporter(new MemoryReporter());
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
     mSharedCmaps.Clear();
     ClearLangGroupPrefFonts();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
     Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
-    Preferences::UnregisterCallback(FontWhitelistPrefChanged,
-                                    kFontSystemWhitelistPref);
+    if (XRE_IsParentProcess()) {
+        Preferences::UnregisterCallback(FontWhitelistPrefChanged,
+                                        kFontSystemWhitelistPref);
+    }
     NS_RELEASE(gFontListPrefObserver);
 }
 
+/* static */
+void
+gfxPlatformFontList::FontWhitelistPrefChanged(const char *aPref,
+                                              void *aClosure)
+{
+    MOZ_ASSERT(XRE_IsParentProcess());
+    gfxPlatformFontList::PlatformFontList()->UpdateFontList();
+    mozilla::dom::ContentParent::NotifyUpdatedFonts();
+}
+
 // number of CSS generic font families
 const uint32_t kNumGenerics = 5;
 
 void
 gfxPlatformFontList::ApplyWhitelist()
 {
     nsTArray<nsString> list;
     gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, list);
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -278,19 +278,17 @@ public:
 
     // map lang group ==> lang string
     void GetSampleLangForGroup(nsAtom* aLanguage, nsACString& aLangStr,
                                bool aCheckEnvironment = true);
 
     // Returns true if the font family whitelist is not empty.
     bool IsFontFamilyWhitelistActive();
 
-    static void FontWhitelistPrefChanged(const char *aPref, void *aClosure) {
-        gfxPlatformFontList::PlatformFontList()->UpdateFontList();
-    }
+    static void FontWhitelistPrefChanged(const char *aPref, void *aClosure);
 
     bool AddWithLegacyFamilyName(const nsAString& aLegacyName,
                                  gfxFontEntry* aFontEntry);
 
 protected:
     class InitOtherFamilyNamesRunnable : public mozilla::CancelableRunnable
     {
     public:
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -60,16 +60,17 @@
 
 #define GDK_PIXMAP_SIZE_MAX 32767
 
 #define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS "gfx.font_rendering.fontconfig.max_generic_substitutions"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::unicode;
+using mozilla::dom::SystemFontListEntry;
 
 #if (MOZ_WIDGET_GTK == 2)
 static cairo_user_data_key_t cairo_gdk_drawable_key;
 #endif
 
 gfxPlatformGtk::gfxPlatformGtk()
 {
     if (!gfxPlatform::IsHeadless()) {
@@ -244,16 +245,23 @@ gfxPlatformGtk::GetCommonFallbackFonts(u
          ((aCh >> 16) == 2))) {
         aFontList.AppendElement(kFontTakaoPGothic);
         aFontList.AppendElement(kFontDroidSansFallback);
         aFontList.AppendElement(kFontWenQuanYiMicroHei);
         aFontList.AppendElement(kFontNanumGothic);
     }
 }
 
+void
+gfxPlatformGtk::ReadSystemFontList(
+    InfallibleTArray<SystemFontListEntry>* retValue)
+{
+    gfxFcPlatformFontList::PlatformFontList()->ReadSystemFontList(retValue);
+}
+
 gfxPlatformFontList*
 gfxPlatformGtk::CreatePlatformFontList()
 {
     gfxPlatformFontList* list = new gfxFcPlatformFontList();
     if (NS_SUCCEEDED(list->InitFontList())) {
         return list;
     }
     gfxPlatformFontList::Shutdown();
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -17,25 +17,34 @@ extern "C" {
 }
 #endif
 
 #ifdef MOZ_X11
 struct _XDisplay;
 typedef struct _XDisplay Display;
 #endif // MOZ_X11
 
+namespace mozilla {
+    namespace dom {
+        class SystemFontListEntry;
+    };
+};
+
 class gfxPlatformGtk : public gfxPlatform {
 public:
     gfxPlatformGtk();
     virtual ~gfxPlatformGtk();
 
     static gfxPlatformGtk *GetPlatform() {
         return (gfxPlatformGtk*) gfxPlatform::GetPlatform();
     }
 
+    void ReadSystemFontList(
+        InfallibleTArray<mozilla::dom::SystemFontListEntry>* retValue) override;
+
     virtual already_AddRefed<gfxASurface>
       CreateOffscreenSurface(const IntSize& aSize,
                              gfxImageFormat aFormat) override;
 
     virtual nsresult GetFontList(nsAtom *aLangGroup,
                                  const nsACString& aGenericFamily,
                                  nsTArray<nsString>& aListOfFonts) override;
 
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -25,17 +25,17 @@
 #include <CoreVideo/CoreVideo.h>
 
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "VsyncSource.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
-using mozilla::dom::FontFamilyListEntry;
+using mozilla::dom::SystemFontListEntry;
 
 // cribbed from CTFontManager.h
 enum {
    kAutoActivationDisabled = 1
 };
 typedef uint32_t AutoActivationSetting;
 
 // bug 567552 - disable auto-activation of fonts
@@ -108,21 +108,20 @@ gfxPlatformMac::CreatePlatformFontList()
     if (NS_SUCCEEDED(list->InitFontList())) {
         return list;
     }
     gfxPlatformFontList::Shutdown();
     return nullptr;
 }
 
 void
-gfxPlatformMac::GetSystemFontFamilyList(
-    InfallibleTArray<FontFamilyListEntry>* aFontFamilies)
+gfxPlatformMac::ReadSystemFontList(
+    InfallibleTArray<SystemFontListEntry>* aFontList)
 {
-    gfxMacPlatformFontList::PlatformFontList()->
-        GetSystemFontFamilyList(aFontFamilies);
+    gfxMacPlatformFontList::PlatformFontList()->ReadSystemFontList(aFontList);
 }
 
 already_AddRefed<gfxASurface>
 gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize,
                                        gfxImageFormat aFormat)
 {
     if (!Factory::AllowedSurfaceSize(aSize)) {
         return nullptr;
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -35,18 +35,18 @@ public:
                     const gfxFontStyle *aStyle,
                     gfxTextPerfMetrics* aTextPerf,
                     gfxUserFontSet *aUserFontSet,
                     gfxFloat aDevToCssSize) override;
 
     virtual gfxPlatformFontList* CreatePlatformFontList() override;
 
     void
-    GetSystemFontFamilyList(InfallibleTArray<mozilla::dom::FontFamilyListEntry>*
-                            aFontFamilies) override;
+    ReadSystemFontList(InfallibleTArray<mozilla::dom::SystemFontListEntry>*
+                       aFontList) override;
 
     bool IsFontFormatSupported(uint32_t aFormatFlags) override;
 
     virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
                                         Script aRunScript,
                                         nsTArray<const char*>& aFontList) override;
 
     // lookup the system font for a particular system font type and set
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -51,17 +51,17 @@ MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY,     0
 MSG_DEF(JSMSG_NOT_FUNCTION,            1, JSEXN_TYPEERR, "{0} is not a function")
 MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         1, JSEXN_TYPEERR, "{0} is not a constructor")
 MSG_DEF(JSMSG_CANT_CONVERT_TO,         2, JSEXN_TYPEERR, "can't convert {0} to {1}")
 MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] property is not a function")
 MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
 MSG_DEF(JSMSG_NO_PROPERTIES,           1, JSEXN_TYPEERR, "{0} has no properties")
 MSG_DEF(JSMSG_BAD_REGEXP_FLAG,         1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")
 MSG_DEF(JSMSG_INVALID_DATA_VIEW_LENGTH, 0, JSEXN_RANGEERR, "invalid data view length")
-MSG_DEF(JSMSG_ARG_INDEX_OUT_OF_RANGE,  1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range")
+MSG_DEF(JSMSG_OFFSET_LARGER_THAN_FILESIZE, 0, JSEXN_RANGEERR, "offset is larger than filesize")
 MSG_DEF(JSMSG_OFFSET_OUT_OF_BUFFER,    0, JSEXN_RANGEERR, "start offset is outside the bounds of the buffer")
 MSG_DEF(JSMSG_OFFSET_OUT_OF_DATAVIEW,  0, JSEXN_RANGEERR, "offset is outside the bounds of the DataView")
 MSG_DEF(JSMSG_SPREAD_TOO_LARGE,        0, JSEXN_RANGEERR, "array too large due to spread operand(s)")
 MSG_DEF(JSMSG_BAD_WEAKMAP_KEY,         0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
 MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER,    1, JSEXN_TYPEERR, "invalid {0} usage")
 MSG_DEF(JSMSG_BAD_ARRAY_LENGTH,        0, JSEXN_RANGEERR, "invalid array length")
 MSG_DEF(JSMSG_REDECLARED_PREV,         2, JSEXN_NOTE, "Previously declared at line {0}, column {1}")
 MSG_DEF(JSMSG_REDECLARED_VAR,          2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1152,17 +1152,17 @@ CreateMappedArrayBuffer(JSContext* cx, u
     if (!sizeGiven) {
         struct stat st;
         if (fstat(fileno(file), &st) < 0) {
             JS_ReportErrorASCII(cx, "Unable to stat file");
             return false;
         }
         if (off_t(offset) >= st.st_size) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                      JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
+                                      JSMSG_OFFSET_LARGER_THAN_FILESIZE);
             return false;
         }
         size = st.st_size - offset;
     }
 
     void* contents = JS_CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file), offset, size);
     if (!contents) {
         JS_ReportErrorASCII(cx, "failed to allocate mapped array buffer contents (possibly due to bad alignment)");
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -278,27 +278,31 @@ RetainedDisplayListBuilder::IncrementSub
 
   nsIPresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
   MOZ_ASSERT(presShell);
 
   mBuilder.IncrementPresShellPaintCount(presShell);
 }
 
 void UpdateASR(nsDisplayItem* aItem,
-               const ActiveScrolledRoot* aContainerASR)
+               Maybe<const ActiveScrolledRoot*>& aContainerASR)
 {
+  if (!aContainerASR) {
+    return;
+  }
+
   nsDisplayWrapList* wrapList = aItem->AsDisplayWrapList();
   if (!wrapList) {
-    aItem->SetActiveScrolledRoot(aContainerASR);
+    aItem->SetActiveScrolledRoot(aContainerASR.value());
     return;
   }
 
   wrapList->SetActiveScrolledRoot(
     ActiveScrolledRoot::PickAncestor(wrapList->GetFrameActiveScrolledRoot(),
-                                     aContainerASR));
+                                     aContainerASR.value()));
 }
 
 /**
  * Takes two display lists and merges them into an output list.
  *
  * The basic algorithm is:
  *
  * For-each item in the new list:
@@ -342,35 +346,37 @@ void UpdateASR(nsDisplayItem* aItem,
  * instance of A.
  *
  * Merged List: A, B
  */
 void
 RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
                                               nsDisplayList* aOldList,
                                               nsDisplayList* aOutList,
-                                              const ActiveScrolledRoot** aOutContainerASR)
+                                              Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
 {
   nsDisplayList merged(&mBuilder);
-  const ActiveScrolledRoot* containerASR = nullptr;
-
-  const auto ReuseItem = [&](nsDisplayItem* aItem) {
+  const auto UseItem = [&](nsDisplayItem* aItem) {
     const ActiveScrolledRoot* itemClipASR =
       aItem->GetClipChain() ? aItem->GetClipChain()->mASR : nullptr;
 
     const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
       itemClipASR, aItem->GetActiveScrolledRoot());
-    if (merged.IsEmpty()) {
-      containerASR = finiteBoundsASR;
+    if (!aOutContainerASR) {
+      aOutContainerASR = Some(finiteBoundsASR);
     } else {
-      containerASR =
-        ActiveScrolledRoot::PickAncestor(containerASR, finiteBoundsASR);
+      aOutContainerASR =
+        Some(ActiveScrolledRoot::PickAncestor(aOutContainerASR.value(), finiteBoundsASR));
     }
 
     merged.AppendToTop(aItem);
+  };
+
+  const auto ReuseItem = [&](nsDisplayItem* aItem) {
+    UseItem(aItem);
     aItem->SetReused(true);
 
     if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
       IncrementSubDocPresShellPaintCount(aItem);
     }
   };
 
   // Build a hashtable of items in the old list so we can look for them quickly.
@@ -403,19 +409,19 @@ RetainedDisplayListBuilder::MergeDisplay
     if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) {
       if (oldItem->IsReused()) {
         // If there's a matching item in the old list, but we've already put it into the
         // merged list then stick with that one. Merge any child lists, and then delete the
         // new item. This solves example 2 from above.
 
         if (oldItem->GetChildren()) {
           MOZ_ASSERT(newItem->GetChildren());
-          const ActiveScrolledRoot* containerASRForChildren;
+          Maybe<const ActiveScrolledRoot*> containerASRForChildren;
           MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
-                            oldItem->GetChildren(), &containerASRForChildren);
+                            oldItem->GetChildren(), containerASRForChildren);
           UpdateASR(oldItem, containerASRForChildren);
           oldItem->UpdateBounds(&mBuilder);
         }
         if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
           MergeLayerEventRegions(oldItem, newItem);
         }
         newItem->Destroy(&mBuilder);
       } else {
@@ -423,19 +429,19 @@ RetainedDisplayListBuilder::MergeDisplay
         // items from the old list into the merged list until we get to the matched item.
         nsDisplayItem* old = nullptr;
         while ((old = aOldList->RemoveBottom()) && !IsSameItem(newItem, old)) {
           if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
             // Recurse into the child list (without a matching new list) to
             // ensure that we find and remove any invalidated items.
             if (old->GetChildren()) {
               nsDisplayList empty(&mBuilder);
-              const ActiveScrolledRoot* containerASRForChildren;
+              Maybe<const ActiveScrolledRoot*> containerASRForChildren;
               MergeDisplayLists(&empty, old->GetChildren(),
-                                old->GetChildren(), &containerASRForChildren);
+                                old->GetChildren(), containerASRForChildren);
               UpdateASR(old, containerASRForChildren);
               old->UpdateBounds(&mBuilder);
             }
             ReuseItem(old);
           } else {
             oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
             old->Destroy(&mBuilder);
           }
@@ -453,63 +459,60 @@ RetainedDisplayListBuilder::MergeDisplay
           // likely to have the bigger lists and merging will be quicker.
           MergeLayerEventRegions(old, newItem);
           ReuseItem(old);
           newItem->Destroy(&mBuilder);
         } else {
           if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
               old->GetChildren()) {
             MOZ_ASSERT(newItem->GetChildren());
-            const ActiveScrolledRoot* containerASRForChildren;
+            Maybe<const ActiveScrolledRoot*> containerASRForChildren;
             MergeDisplayLists(newItem->GetChildren(), old->GetChildren(),
-                              newItem->GetChildren(), &containerASRForChildren);
+                              newItem->GetChildren(), containerASRForChildren);
             UpdateASR(newItem, containerASRForChildren);
             newItem->UpdateBounds(&mBuilder);
           }
 
           old->Destroy(&mBuilder);
-          merged.AppendToTop(newItem);
+          UseItem(newItem);
         }
       }
     } else {
       // If there was no matching item in the old list, then we only need to
       // add the new item to the merged list.
-      merged.AppendToTop(newItem);
+      UseItem(newItem);
     }
   }
 
   // Reuse the remaining valid items from the old display list.
   while (nsDisplayItem* old = aOldList->RemoveBottom()) {
     if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
       if (old->GetChildren()) {
         // We are calling MergeDisplayLists() to ensure that the display items
         // with modified or deleted children will be correctly handled.
         // Passing an empty new display list as an argument skips the merging
         // loop above and jumps back here.
         nsDisplayList empty(&mBuilder);
-        const ActiveScrolledRoot* containerASRForChildren;
+        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
 
         MergeDisplayLists(&empty, old->GetChildren(),
-                          old->GetChildren(), &containerASRForChildren);
+                          old->GetChildren(), containerASRForChildren);
         UpdateASR(old, containerASRForChildren);
         old->UpdateBounds(&mBuilder);
       }
       if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
         MergeLayerEventRegions(old, nullptr);
       }
       ReuseItem(old);
     } else {
       old->Destroy(&mBuilder);
     }
   }
 
   aOutList->AppendToTop(&merged);
-  if (aOutContainerASR) {
-    *aOutContainerASR = containerASR;
-  }
 }
 
 static void
 TakeAndAddModifiedFramesFromRootFrame(nsTArray<nsIFrame*>& aFrames,
                                       nsIFrame* aRootFrame)
 {
   MOZ_ASSERT(aRootFrame);
 
@@ -841,17 +844,18 @@ RetainedDisplayListBuilder::AttemptParti
       // printf_stderr("Skipping display list building since nothing needed to be done\n");
     }
 
     // |modifiedDL| can sometimes be empty here. We still perform the
     // display list merging to prune unused items (for example, items that
     // are not visible anymore) from the old list.
     // TODO: Optimization opportunity. In this case, MergeDisplayLists()
     // unnecessarily creates a hashtable of the old items.
-    MergeDisplayLists(&modifiedDL, &mList, &mList, nullptr);
+    Maybe<const ActiveScrolledRoot*> dummy;
+    MergeDisplayLists(&modifiedDL, &mList, &mList, dummy);
 
     //printf_stderr("Painting --- Merged list:\n");
     //nsFrame::PrintDisplayList(&mBuilder, mList);
 
     merged = true;
   }
 
   mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -3,16 +3,17 @@
 /* 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 RETAINEDDISPLAYLISTBUILDER_H_
 #define RETAINEDDISPLAYLISTBUILDER_H_
 
 #include "nsDisplayList.h"
+#include "mozilla/Maybe.h"
 
 struct RetainedDisplayListBuilder {
   RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
                              nsDisplayListBuilderMode aMode,
                              bool aBuildCaret)
     : mBuilder(aReferenceFrame, aMode, aBuildCaret, true)
     , mList(&mBuilder)
   {}
@@ -30,17 +31,17 @@ struct RetainedDisplayListBuilder {
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 
 private:
   void PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
 
   void MergeDisplayLists(nsDisplayList* aNewList,
                          nsDisplayList* aOldList,
                          nsDisplayList* aOutList,
-                         const mozilla::ActiveScrolledRoot** aOutContainerASR = nullptr);
+                         mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                             nsRect* aOutDirty,
                             AnimatedGeometryRoot** aOutModifiedAGR,
                             nsTArray<nsIFrame*>* aOutFramesWithProps);
 
   void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
 
--- a/mfbt/Maybe.h
+++ b/mfbt/Maybe.h
@@ -220,17 +220,21 @@ public:
   }
 
   /* Methods that check whether this Maybe contains a value */
   explicit operator bool() const { return isSome(); }
   bool isSome() const { return mIsSome; }
   bool isNothing() const { return !mIsSome; }
 
   /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
-  T value() const;
+  T value() const
+  {
+    MOZ_ASSERT(mIsSome);
+    return ref();
+  }
 
   /*
    * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
    * the default value provided.
    */
   template<typename V>
   T valueOr(V&& aDefault) const
   {
@@ -249,18 +253,27 @@ public:
   {
     if (isSome()) {
       return ref();
     }
     return aFunc();
   }
 
   /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
-  T* ptr();
-  const T* ptr() const;
+  T* ptr()
+  {
+    MOZ_ASSERT(mIsSome);
+    return &ref();
+  }
+
+  const T* ptr() const
+  {
+    MOZ_ASSERT(mIsSome);
+    return &ref();
+  }
 
   /*
    * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
    * returns the default value provided.
    */
   T* ptrOr(T* aDefault)
   {
     if (isSome()) {
@@ -294,22 +307,40 @@ public:
   const T* ptrOrFrom(F&& aFunc) const
   {
     if (isSome()) {
       return ptr();
     }
     return aFunc();
   }
 
-  T* operator->();
-  const T* operator->() const;
+  T* operator->()
+  {
+    MOZ_ASSERT(mIsSome);
+    return ptr();
+  }
+
+  const T* operator->() const
+  {
+    MOZ_ASSERT(mIsSome);
+    return ptr();
+  }
 
   /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
-  T& ref();
-  const T& ref() const;
+  T& ref()
+  {
+    MOZ_ASSERT(mIsSome);
+    return *static_cast<T*>(data());
+  }
+
+  const T& ref() const
+  {
+    MOZ_ASSERT(mIsSome);
+    return *static_cast<const T*>(data());
+  }
 
   /*
    * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
    * the default value provided.
    */
   T& refOr(T& aDefault)
   {
     if (isSome()) {
@@ -343,18 +374,27 @@ public:
   const T& refOrFrom(F&& aFunc) const
   {
     if (isSome()) {
       return ref();
     }
     return aFunc();
   }
 
-  T& operator*();
-  const T& operator*() const;
+  T& operator*()
+  {
+    MOZ_ASSERT(mIsSome);
+    return ref();
+  }
+
+  const T& operator*() const
+  {
+    MOZ_ASSERT(mIsSome);
+    return ref();
+  }
 
   /* If |isSome()|, runs the provided function or functor on the contents of
    * this Maybe. */
   template<typename Func>
   Maybe& apply(Func aFunc)
   {
     if (isSome()) {
       aFunc(ref());
@@ -408,112 +448,35 @@ public:
     }
   }
 
   /*
    * Constructs a T value in-place in this empty Maybe<T>'s storage. The
    * arguments to |emplace()| are the parameters to T's constructor.
    */
   template<typename... Args>
-  void emplace(Args&&... aArgs);
+  void emplace(Args&&... aArgs)
+  {
+    MOZ_ASSERT(!mIsSome);
+    ::new (KnownNotNull, data()) T(Forward<Args>(aArgs)...);
+    mIsSome = true;
+  }
 
   friend std::ostream&
   operator<<(std::ostream& aStream, const Maybe<T>& aMaybe)
   {
     if (aMaybe) {
       aStream << aMaybe.ref();
     } else {
       aStream << "<Nothing>";
     }
     return aStream;
   }
 };
 
-template<typename T>
-T
-Maybe<T>::value() const
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return ref();
-}
-
-template<typename T>
-T*
-Maybe<T>::ptr()
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return &ref();
-}
-
-template<typename T>
-const T*
-Maybe<T>::ptr() const
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return &ref();
-}
-
-template<typename T>
-T*
-Maybe<T>::operator->()
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return ptr();
-}
-
-template<typename T>
-const T*
-Maybe<T>::operator->() const
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return ptr();
-}
-
-template<typename T>
-T&
-Maybe<T>::ref()
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return *static_cast<T*>(data());
-}
-
-template<typename T>
-const T&
-Maybe<T>::ref() const
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return *static_cast<const T*>(data());
-}
-
-template<typename T>
-T&
-Maybe<T>::operator*()
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return ref();
-}
-
-template<typename T>
-const T&
-Maybe<T>::operator*() const
-{
-  MOZ_RELEASE_ASSERT(mIsSome);
-  return ref();
-}
-
-template<typename T>
-template<typename... Args>
-void
-Maybe<T>::emplace(Args&&... aArgs)
-{
-  MOZ_RELEASE_ASSERT(!mIsSome);
-  ::new (KnownNotNull, data()) T(Forward<Args>(aArgs)...);
-  mIsSome = true;
-}
-
 /*
  * Some() creates a Maybe<T> value containing the provided T value. If T has a
  * move constructor, it's used to make this as efficient as possible.
  *
  * Some() selects the type of Maybe it returns by removing any const, volatile,
  * or reference qualifiers from the type of the value you pass to it. This gives
  * it more intuitive behavior when used in expressions, but it also means that
  * if you need to construct a Maybe value that holds a const, volatile, or
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3128,18 +3128,18 @@ pref("layout.interruptible-reflow.enable
 pref("layout.frame_rate", -1);
 
 // pref to dump the display list to the log. Useful for debugging drawing.
 pref("layout.display-list.dump", false);
 pref("layout.display-list.dump-content", false);
 pref("layout.display-list.dump-parent", false);
 
 // Toggle retaining display lists between paints
-#ifdef ANDROID
-pref("layout.display-list.retain", false);
+#if !defined(ANDROID) && defined(NIGHTLY_BUILD)
+pref("layout.display-list.retain", true);
 #else
 pref("layout.display-list.retain", false);
 #endif
 
 // Set the maximum amount of modified frames allowed before doing a full
 // display list rebuild.
 pref("layout.display-list.rebuild-frame-limit", 500);
 
--- a/storage/mozStorageConnection.cpp
+++ b/storage/mozStorageConnection.cpp
@@ -543,17 +543,19 @@ Connection::Connection(Service *aService
 {
   MOZ_ASSERT(!mIgnoreLockingMode || mFlags & SQLITE_OPEN_READONLY,
              "Can't ignore locking for a non-readonly connection!");
   mStorageService->registerConnection(this);
 }
 
 Connection::~Connection()
 {
-  Unused << Close();
+  // Failsafe Close() occurs in our custom Release method because of
+  // complications related to Close() potentially invoking AsyncClose() which
+  // will increment our refcount.
   MOZ_ASSERT(!mAsyncExecutionThread,
              "The async thread has not been shutdown properly!");
 }
 
 NS_IMPL_ADDREF(Connection)
 
 NS_INTERFACE_MAP_BEGIN(Connection)
   NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
@@ -567,18 +569,65 @@ NS_INTERFACE_MAP_END
 NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void)
 {
   NS_PRECONDITION(0 != mRefCnt, "dup release");
   nsrefcnt count = --mRefCnt;
   NS_LOG_RELEASE(this, count, "Connection");
   if (1 == count) {
     // If the refcount is 1, the single reference must be from
     // gService->mConnections (in class |Service|).  Which means we can
-    // unregister it safely.
-    mStorageService->unregisterConnection(this);
+    // perform our failsafe Close() and unregister...
+    //
+    // HOWEVER, there is an edge-case where our failsafe Close() may trigger
+    // a call to AsyncClose() which obtains a strong reference.  This reference
+    // will be released via NS_ReleaseOnMainThreadSystemGroup() before Close()
+    // returns, which can potentially result in reentrancy into this method and
+    // this branch a second time.  (It may also be deferred if we're not in
+    // that event target ourselves.)  To avoid reentrancy madness, we explicitly
+    // bump our refcount up to 2 without going through AddRef().
+    ++mRefCnt;
+    // Okay, now our refcount is 2, we trigger Close().
+    Unused << Close();
+    // Now our refcount should either be at 2 (because nothing happened, or the
+    // addref and release pair happened due to SpinningSynchronousClose) or
+    // 3 (because SpinningSynchronousClose happened but didn't release yet).
+    //
+    // We *really* want to avoid re-entrancy, and we have potentially two strong
+    // references remaining that will invoke Release() and potentially trigger
+    // a transition to 1 again.  Since the second reference would be just a
+    // proxy release of an already-closed connection, it's not a big deal for us
+    // to unregister the connection now.  We do need to take care to avoid a
+    // strong refcount transition to 1 from 2 because that would induce
+    // reentrancy.  Note that we do not have any concerns about other threads
+    // being involved here; we MUST be the main thread if AsyncClose() is
+    // involved.
+    //
+    // Note: While Close() potentially spins the nested event loop, it is
+    // conceivable that Service::CollectReports or Service::minimizeMemory might
+    // be invoked.  These call Service::getConnections() and will perform
+    // matching AddRef and Release calls but will definitely not retain any
+    // references.  (Because connectionReady() will return false so both loops
+    // will immediately "continue" to bypass the connection in question.)
+    // Because our refcount is at least 2 at the lowest point, these do not pose
+    // a problem.
+    if (mRefCnt == 3) {
+      // pending proxy release, strong release to 2
+      mStorageService->unregisterConnection(this);
+      // now weak release to 1, the outstanding refcount will strong release to
+      // 0 and result in destruction later.
+      --mRefCnt;
+    } else if (mRefCnt == 2) {
+      // weak release to 1
+      --mRefCnt;
+      // strong release to 0, destruction will happen, we must NOT touch
+      // `this` after this point.
+      mStorageService->unregisterConnection(this);
+    } else {
+      MOZ_ASSERT(false, "Connection refcount invariant violated.");
+    }
   } else if (0 == count) {
     mRefCnt = 1; /* stabilize */
 #if 0 /* enable this to find non-threadsafe destructors: */
     NS_ASSERT_OWNINGTHREAD(Connection);
 #endif
     delete (this);
     return 0;
   }
new file mode 100644
--- /dev/null
+++ b/storage/test/unit/test_connection_failsafe_close.js
@@ -0,0 +1,34 @@
+/* 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/. */
+
+/*
+ * This file tests edge-cases related to mozStorageService::unregisterConnection
+ * in the face of failsafe closing at destruction time which results in
+ * SpinningSynchronousClose being invoked which can "resurrect" the connection
+ * and result in a second call to unregisterConnection.
+ *
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=1413501 for more context.
+ */
+
+add_task(async function test_failsafe_close_of_async_connection() {
+  // get the db
+  let db = getOpenedDatabase();
+
+  // do something async
+  let callbackInvoked = new Promise((resolve) => {
+    db.executeSimpleSQLAsync("CREATE TABLE test (id INTEGER)",
+                             { handleCompletion: resolve });
+  });
+
+  // drop our reference and force a GC so the only live reference is owned by
+  // the async statement.
+  db = gDBConn = null;
+  // (we don't need to cycle collect)
+  Components.utils.forceGC();
+
+  // now we need to wait for that callback to have completed.
+  await callbackInvoked;
+
+  Assert.ok(true, "if we shutdown cleanly and do not crash, then we succeeded");
+});
--- a/storage/test/unit/xpcshell.ini
+++ b/storage/test/unit/xpcshell.ini
@@ -13,16 +13,20 @@ support-files =
 [test_bug-444233.js]
 [test_cache_size.js]
 [test_chunk_growth.js]
 # Bug 676981: test fails consistently on Android
 fail-if = os == "android"
 [test_connection_asyncClose.js]
 [test_connection_executeAsync.js]
 [test_connection_executeSimpleSQLAsync.js]
+[test_connection_failsafe_close.js]
+# The failsafe close mechanism asserts when performing SpinningSynchronousClose
+# on debug builds, so we can only test on non-debug builds.
+skip-if = debug
 [test_connection_interrupt.js]
 [test_js_helpers.js]
 [test_levenshtein.js]
 [test_like.js]
 [test_like_escape.js]
 [test_locale_collation.js]
 [test_page_size_is_32k.js]
 [test_sqlite_secure_delete.js]
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -26,17 +26,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   MessageChannel: "resource://gre/modules/MessageChannel.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   NativeApp: "resource://gre/modules/NativeMessaging.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   Schemas: "resource://gre/modules/Schemas.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetters(this, {
-  gAddonPolicyService: ["@mozilla.org/addons/policy-service;1", "nsIAddonPolicyService"],
   aomStartup: ["@mozilla.org/addons/addon-manager-startup;1", "amIAddonManagerStartup"],
 });
 
 Cu.import("resource://gre/modules/ExtensionCommon.jsm");
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
   BaseContext,
@@ -1473,18 +1472,16 @@ StartupCache = {
                             createFunc);
   },
 
   delete(extension, path) {
     return this.general.delete([extension.id, extension.version, ...path]);
   },
 };
 
-// void StartupCache.dataPromise;
-
 Services.obs.addObserver(StartupCache, "startupcache-invalidate");
 
 class CacheStore {
   constructor(storeName) {
     this.storeName = storeName;
   }
 
   async getStore(path = null) {