Bug 1514869 - patch 1 - Basic implementation of a cross-process sharable font list, using shared memory to store the list of families & faces, and per-font character maps. r=jwatt,jld
authorJonathan Kew <jkew@mozilla.com>
Sat, 27 Apr 2019 15:37:29 +0000
changeset 471762 21ef00977ab69cd330d727d582fef291276391c4
parent 471761 87be514024ac53ab6362ffc26610c063d50abe07
child 471763 095b3edec3c8ba9318c71d0acc91e10ce57818dc
push id84266
push userjkew@mozilla.com
push dateMon, 29 Apr 2019 15:21:39 +0000
treeherderautoland@7de7d6a0be86 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt, jld
bugs1514869
milestone68.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 1514869 - patch 1 - Basic implementation of a cross-process sharable font list, using shared memory to store the list of families & faces, and per-font character maps. r=jwatt,jld Differential Revision: https://phabricator.services.mozilla.com/D22937
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
gfx/thebes/SharedFontList-impl.h
gfx/thebes/SharedFontList.cpp
gfx/thebes/SharedFontList.h
gfx/thebes/gfxFontEntry.h
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
gfx/thebes/moz.build
ipc/ipdl/sync-messages.ini
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -198,16 +198,17 @@
 #include "BrowserParent.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 "gfxPlatformFontList.h"
 #include "gfxPrefs.h"
 #include "prio.h"
 #include "private/pprio.h"
 #include "ContentProcessManager.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/psm/PSMContentListener.h"
@@ -4979,16 +4980,47 @@ mozilla::ipc::IPCResult ContentParent::R
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvGetGraphicsDeviceInitData(
     ContentDeviceData* aOut) {
   gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentParent::RecvGetFontListShmBlock(
+    const uint32_t& aGeneration, const uint32_t& aIndex,
+    mozilla::ipc::SharedMemoryBasic::Handle* aOut) {
+  gfxPlatformFontList::PlatformFontList()->ShareFontListShmBlockToProcess(
+      aGeneration, aIndex, Pid(), aOut);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitializeFamily(
+    const uint32_t& aGeneration, const uint32_t& aFamilyIndex) {
+  gfxPlatformFontList::PlatformFontList()->InitializeFamily(aGeneration,
+                                                            aFamilyIndex);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetCharacterMap(
+    const uint32_t& aGeneration, const mozilla::fontlist::Pointer& aFacePtr,
+    const gfxSparseBitSet& aMap) {
+  gfxPlatformFontList::PlatformFontList()->SetCharacterMap(aGeneration,
+                                                           aFacePtr, aMap);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitOtherFamilyNames(
+    const uint32_t& aGeneration, const bool& aDefer, bool* aLoaded) {
+  gfxPlatformFontList::PlatformFontList()->InitOtherFamilyNames(aGeneration,
+                                                                aDefer);
+  *aLoaded = true;
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult ContentParent::RecvGraphicsError(
     const nsCString& aError) {
   gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder();
   if (lf) {
     std::stringstream message;
     message << "CP+" << aError.get();
     lf->UpdateStringsVector(message.str());
   }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1100,16 +1100,31 @@ class ContentParent final : public PCont
   mozilla::ipc::IPCResult RecvUpdateDropEffect(const uint32_t& aDragAction,
                                                const uint32_t& aDropEffect);
 
   mozilla::ipc::IPCResult RecvShutdownProfile(const nsCString& aProfile);
 
   mozilla::ipc::IPCResult RecvGetGraphicsDeviceInitData(
       ContentDeviceData* aOut);
 
+  mozilla::ipc::IPCResult RecvGetFontListShmBlock(
+      const uint32_t& aGeneration, const uint32_t& aIndex,
+      mozilla::ipc::SharedMemoryBasic::Handle* aOut);
+
+  mozilla::ipc::IPCResult RecvInitializeFamily(const uint32_t& aGeneration,
+                                               const uint32_t& aFamilyIndex);
+
+  mozilla::ipc::IPCResult RecvSetCharacterMap(
+      const uint32_t& aGeneration, const mozilla::fontlist::Pointer& aFacePtr,
+      const gfxSparseBitSet& aMap);
+
+  mozilla::ipc::IPCResult RecvInitOtherFamilyNames(const uint32_t& aGeneration,
+                                                   const bool& aDefer,
+                                                   bool* aLoaded);
+
   mozilla::ipc::IPCResult RecvNotifyBenchmarkResult(const nsString& aCodecName,
                                                     const uint32_t& aDecodeFPS);
 
   mozilla::ipc::IPCResult RecvNotifyPushObservers(
       const nsCString& aScope, const IPC::Principal& aPrincipal,
       const nsString& aMessageId);
 
   mozilla::ipc::IPCResult RecvNotifyPushObserversWithData(
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -97,16 +97,19 @@ using mozilla::Telemetry::DiscardedData 
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 using refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
 using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h";
 using mozilla::dom::BrowsingContextTransaction from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::BrowsingContextFieldEpochs from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::BrowsingContextInitializer from "mozilla/dom/BrowsingContext.h";
 using base::SharedMemoryHandle from "base/shared_memory.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::fontlist::Pointer from "SharedFontList.h";
+using gfxSparseBitSet from "gfxFontUtils.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
 
@@ -1118,16 +1121,158 @@ parent:
     async ShutdownProfile(nsCString aProfile);
 
     /**
      * Request graphics initialization information from the parent.
      */
     sync GetGraphicsDeviceInitData()
         returns (ContentDeviceData aData);
 
+    /**
+     * A shared font list (see gfx/thebes/SharedFontList.*) contains a list
+     * of shared-memory blocks that are used to store all the font list data.
+     * The font list created in the parent process is the only one that can
+     * create or store objects into the shared memory; content processes font
+     * lists have read-only access to it.
+     *
+     * To minimize the cost of record allocations, the shared font list
+     * bump-allocates new objects that it adds to the shared memory blocks
+     * (i.e. the records stored in the shared memory blocks are only ever
+     * appended, and never freed except when the entire font list is
+     * reconstructed).
+     *
+     * When initially created by the parent process, the font list may contain
+     * nothing except a header, and the list of the system's installed font
+     * family names. Additional data about the families (styled faces available
+     * and character coverage) is appended to the font list during the session
+     * as a given font is considered for use, because loading all data for all
+     * installed fonts during startup is too expensive/slow.
+     *
+     * During content process launch, a content process's first step in
+     * gaining access to the font list is to call GetFontListShmBlock,
+     * passing index zero in order to get access to the first block, which
+     * contains the font list header and the list of font-family records
+     * (which may be virtually all uninitialized at this time, containing
+     * nothing but the family names). Once a content process determines a
+     * font-family name it wants to use (e.g. from a CSS font-family list, or
+     * from preferences), if that Family record has not yet been initialized,
+     * it will call InitializeFamily (below) to have the parent process
+     * populate Face records in the shared memory with the family's styles.
+     * The content process can then pick the face with best style match from
+     * the available faces according to the CSS font matching algorithm, load
+     * its character map, then send the map to the parent process using
+     * SetCharacterMap (so that the parent process can share the map with all
+     * processes to avoid duplication of work).
+     *
+     * At some point, as the parent process adds data to the font list, a new
+     * shared-memory block will probably be needed. At that point the parent
+     * will create a new block and append it to its share memory block list.
+     * The new Block index will start to appear in Pointer records in the
+     * shared memory, and the content process's can then fetch those other
+     * blocks using this function as needed.
+     *
+     * @param aGeneration
+     *   The font list has a Generation ID stored in its Header, and any time
+     *   the parent process needs to reinitialize the list (because of a change
+     *   in the available font repertoire) a new Generation ID is assigned.
+     *   Content processes pass the Generation of the list they're using in
+     *   all messages, so that the parent can recognize if they're out of date
+     *   and safely ignore such messages. (When the parent rebuilds the list,
+     *   it will notify all content processes, but they may still send a few
+     *   messages that relate to the obsolete list before they have processed
+     *   this notification.)
+     * @param aIndex
+     *   (Zero-based) index of the shared-memory block to be mapped.
+     *   In a typical case, there will be a handful of blocks altogether, so
+     *   each content process only needs to make this request a few times.
+     * @returns aHandle
+     *   Handle that can be used to construct a SharedMemoryBasic that maps the
+     *   requested block of memory.
+     *   If aGeneration does not match the parent's font list generation ID, or
+     *   if requesting a block that does not exist (i.e. with aIndex greater
+     *   than or equal to the number of blocks actually in existence), returns
+     *   a null handle.
+     *
+     * This is a sync message because the content process needs font data in
+     * order to perform font-matching (e.g. during reflow), and cannot continue
+     * until it has mapped the font-list memory.
+     */
+    sync GetFontListShmBlock(uint32_t aGeneration, uint32_t aIndex)
+        returns (Handle aHandle);
+
+    /**
+     * Ask the parent to initialize a given font family, so that face metadata
+     * will be available. Content processes will only call this for families
+     * where the Face data has not yet been populated, so it will generally be
+     * called no more than once per family. (It may not be needed at all, if
+     * the parent process has already initialized the families that content
+     * wants to use.)
+     *
+     * @param aGeneration
+     *   Font-list generation, so requests relating to an obsolete list can be
+     *   ignored (see comments for GetFontListShmBlock).
+     * @param aFamilyIndex
+     *   The 0-based index of the Family within the font-list that a content
+     *   process needs to use.
+     *
+     * This is a sync message because the content process cannot complete its
+     * font-matching until the family is fully populated with Face records.
+     * If we make it async, content processes will reflow using fallbacks,
+     * and then have to reflow again once all the font information needed
+     * becomes available.
+     */
+    sync InitializeFamily(uint32_t aGeneration, uint32_t aFamilyIndex);
+
+    /**
+     * Record the character map of a given Face in the font list.
+     *
+     * @param aGeneration
+     *   Font-list generation, so requests relating to an obsolete list can be
+     *   ignored (see comments for GetFontListShmBlock).
+     * @param aFacePtr
+     *   Font-list shared-memory "pointer" to the Face record to be updated.
+     *   A Pointer is a record of a shared-memory block index and an offset
+     *   within that block, which each process that maps the block can convert
+     *   into a real pointer in its address space.
+     * @param aMap
+     *   The character coverage map of the face. (This will be stored as a
+     *   SharedBitSet record within the shared font list, and the Face record
+     *   will be updated to reference it.)
+     *
+     * This is initially a sync message because the content process needs the
+     * Face record to be updated with its character-map reference before it can
+     * proceed with its font-matching work.
+     *
+     * NOTE: a later patch in the series makes this async.
+     */
+    sync SetCharacterMap(uint32_t aGeneration, Pointer aFacePtr, gfxSparseBitSet aMap);
+
+    /**
+     * Ask the parent to try and complete the InitOtherFamilyNames task, because
+     * we're trying to look up a localized font name. This is a sync method so that
+     * the update will be available before the child continues reflow; however, it
+     * is possible the task will have timed-out in the parent and not actually
+     * completed during this call.
+     *
+     * @param aGeneration
+     *   Font-list generation, so requests relating to an obsolete list can be
+     *   ignored (see comments for GetFontListShmBlock).
+     * @param aDefer
+     *   Parameter aDeferOtherFamilyNamesLoading to be passed to
+     *   gfxPlatformFontList::InitOtherFamilyNames, to determine whether name
+     *   loading should be deferred to a background task or run immediately.
+     * @param aLoaded
+     *   Returns whether the font name loading process has completed.
+     *
+     * TODO: This is currently a sync message but can probably be made async,
+     * at the cost of an increased chance of some testcases failing because
+     * they depend on lazily-loaded font names.
+     */
+    sync InitOtherFamilyNames(uint32_t aGeneration, bool aDefer) returns (bool aLoaded);
+
     async CreateWindow(nullable PBrowser aThisTab,
                        PBrowser aNewTab,
                        uint32_t aChromeFlags,
                        bool aCalledFromJS,
                        bool aPositionSpecified,
                        bool aSizeSpecified,
                        URIParams? aURIToLoad,
                        nsCString aFeatures,
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/SharedFontList-impl.h
@@ -0,0 +1,307 @@
+/* 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 SharedFontList_impl_h
+#define SharedFontList_impl_h
+
+#include "SharedFontList.h"
+
+#include "mozilla/ipc/SharedMemoryBasic.h"
+
+#include "gfxFontUtils.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+
+// This is split out from SharedFontList.h because that header is included
+// quite widely (via gfxPlatformFontList.h, gfxTextRun.h, etc), and other code
+// such as the generated DOM bindings code gets upset at (indirect) inclusion
+// of <windows.h> via SharedMemoryBasic.h. So this header, which defines the
+// actual shared-memory FontList class, is included only by the .cpp files that
+// implement or directly interface with the font list, to avoid polluting other
+// headers.
+
+namespace mozilla {
+namespace fontlist {
+
+/**
+ * The Shared Font List is a collection of data that lives in shared memory
+ * so that all processes can use it, rather than maintaining their own copies,
+ * and provides the metadata needed for CSS font-matching (a list of all the
+ * available font families and their faces, style properties, etc, as well as
+ * character coverage).
+ *
+ * An important assumption is that all processes see the same collection of
+ * installed fonts; therefore it is valid for them all to share the same set
+ * of font metadata. The data is updated only by the parent process; content
+ * processes have read-only access to it.
+ *
+ * The total size of this data varies greatly depending on the user's installed
+ * fonts; and it is not known at startup because we load a lot of the font data
+ * on first use rather than preloading during initialization (because that's
+ * too expensive/slow).
+ *
+ * Therefore, the shared memory area needs to be able to grow during the
+ * session; we can't predict how much space will be needed, and we can't afford
+ * to pre-allocate such a huge block that it could never overflow. To handle
+ * this, we maintain a (generally short) array of blocks of shared memory,
+ * and then allocate our Family, Face, etc. objects within these. Because we
+ * only ever add data (never delete what we've already stored), we can use a
+ * simplified allocator that doesn't ever need to free blocks; the only time
+ * the memory is released during a session is the (rare) case where a font is
+ * installed or deleted while the browser is running, and in this case we just
+ * delete the entire shared font list and start afresh.
+ */
+class FontList {
+ public:
+  friend struct Pointer;
+
+  explicit FontList(uint32_t aGeneration);
+  ~FontList();
+
+  /**
+   * Initialize the master list of installed font families. This must be
+   * set during font-list creation, before the list is shared with any
+   * content processes. All installed font families known to the browser
+   * appear in this list, although some may be marked as "hidden" so that
+   * they are not exposed to the font-family property.
+   *
+   * Once initialized, the master family list is immutable; in the (rare)
+   * event that the system's collection of installed fonts changes, we discard
+   * the FontList and create a new one.
+   *
+   * In some cases, a font family may be known by multiple names (e.g.
+   * localizations in multiple languages, or there may be legacy family names
+   * that correspond to specific styled faces like "Arial Black"). Such names
+   * do not appear in this master list, but are referred to as aliases (see
+   * SetAliases below); the alias list need not be populated before the font
+   * list is shared to content processes and used.
+   * 
+   * Only used in the parent process.
+   */
+  void SetFamilyNames(const nsTArray<Family::InitData>& aFamilies);
+
+  /**
+   * Aliases are Family records whose Face entries are already part of another
+   * family (either because the family has multiple localized names, or because
+   * the alias family is a legacy name like "Arial Narrow" that is a subset of
+   * the faces in the main "Arial" family). The table of aliases is initialized
+   * from a hash of alias family name -> array of Face records.
+   *
+   * Like the master family list, the list of family aliases is immutable once
+   * initialized.
+   *
+   * Only used in the parent process.
+   */
+  void SetAliases(
+      nsClassHashtable<nsCStringHashKey, nsTArray<Pointer>>& aAliasTable);
+
+  /**
+   * Local names are PostScript or Full font names of individual faces, used
+   * to look up faces for @font-face { src: local(...) } rules. Some platforms
+   * (e.g. macOS) can look up local names directly using platform font APIs,
+   * in which case the local names table here is unused.
+   *
+   * The list of local names is immutable once initialized. Local font name
+   * lookups may occur before this list has been set up, in which case they
+   * will use the SearchForLocalFace method.
+   *
+   * Only used in the parent process.
+   */
+  void SetLocalNames(nsDataHashtable<nsCStringHashKey, LocalFaceRec::InitData>&
+                         aLocalNameTable);
+
+  /**
+   * Look up a Family record by name, typically to satisfy the font-family
+   * property or a font family listed in preferences.
+   */
+  Family* FindFamily(const nsCString& aName);
+
+  /**
+   * Look up an individual Face by PostScript or Full name, for @font-face
+   * rules using src:local(...). This requires the local names list to have
+   * been initialized.
+   */
+  LocalFaceRec* FindLocalFace(const nsCString& aName);
+
+  /**
+   * Search families for a face with local name aName; should only be used if
+   * the mLocalFaces array has not yet been set up, as this will be a more
+   * expensive search than FindLocalFace.
+   */
+  void SearchForLocalFace(const nsACString& aName, Family** aFamily,
+                          Face** aFace);
+
+  bool Initialized() { return mBlocks.Length() > 0 && NumFamilies() > 0; }
+
+  uint32_t NumFamilies() { return GetHeader().mFamilyCount; }
+  Family* Families() {
+    return static_cast<Family*>(GetHeader().mFamilies.ToPtr(this));
+  }
+
+  uint32_t NumAliases() { return GetHeader().mAliasCount; }
+  Family* AliasFamilies() {
+    return static_cast<Family*>(GetHeader().mAliases.ToPtr(this));
+  }
+
+  uint32_t NumLocalFaces() { return GetHeader().mLocalFaceCount; }
+  LocalFaceRec* LocalFaces() {
+    return static_cast<LocalFaceRec*>(GetHeader().mLocalFaces.ToPtr(this));
+  }
+
+  /**
+   * Ask the font list to initialize the character map for a given face.
+   */
+  void LoadCharMapFor(Face& aFace, const Family* aFamily);
+
+  /**
+   * Allocate shared-memory space for a record of aSize bytes. The returned
+   * pointer will be 32-bit aligned. (This method may trigger the allocation of
+   * a new shared memory block, if required.)
+   *
+   * Only used in the parent process.
+   */
+  Pointer Alloc(uint32_t aSize);
+
+  /**
+   * Convert a native pointer to a shared-memory Pointer record that can be
+   * passed between processes.
+   */
+  Pointer ToSharedPointer(const void* aPtr);
+
+  uint32_t GetGeneration() { return GetHeader().mGeneration; }
+
+  /**
+   * Header info that is stored at the beginning of the first shared-memory
+   * block for the font list.
+   * (Subsequent blocks have only the mAllocated field, accessed via the
+   * Block::Allocated() method.)
+   * The mGeneration and mFamilyCount fields are set by the parent process
+   * during font-list construction, before the list has been shared with any
+   * other process, and subsequently never change; therefore, we don't need
+   * to use std::atomic<> for these.
+   */
+  struct Header {
+    std::atomic<uint32_t> mAllocated;    // Space allocated from this block;
+                                         // must be first field in Header
+    uint32_t mGeneration;                // Font-list generation ID
+    uint32_t mFamilyCount;               // Number of font families in the list
+    std::atomic<uint32_t> mBlockCount;   // Total number of blocks that exist
+    std::atomic<uint32_t> mAliasCount;   // Number of family aliases
+    std::atomic<uint32_t> mLocalFaceCount;  // Number of local face names
+    Pointer mFamilies;    // Pointer to array of |mFamilyCount| families
+    Pointer mAliases;     // Pointer to array of |mAliasCount| aliases
+    Pointer mLocalFaces;  // Pointer to array of |mLocalFaceCount| face records
+  };
+
+  /**
+   * Used by the parent process to pass a handle to a shared block to a
+   * specific child process.
+   */
+  void ShareShmBlockToProcess(uint32_t aIndex, base::ProcessId aPid,
+                              mozilla::ipc::SharedMemoryBasic::Handle* aOut) {
+    if (aIndex >= mBlocks.Length()) {
+      // Block index out of range
+      *aOut = mozilla::ipc::SharedMemoryBasic::NULLHandle();
+    }
+    if (!mBlocks[aIndex]->mShmem->ShareToProcess(aPid, aOut)) {
+      MOZ_CRASH("failed to share block");
+    }
+  }
+
+  /**
+   * Support for memory reporter.
+   */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+    size_t result = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
+    for (const auto& b : mBlocks) {
+      result += aMallocSizeOf(b.get()) + aMallocSizeOf(b->mShmem);
+    }
+    return result;
+  }
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  size_t AllocatedShmemSize() const {
+    return mBlocks.Length() * SHM_BLOCK_SIZE;
+  }
+
+  /**
+   * This must be large enough that we can allocate the largest possible
+   * SharedBitSet (around 140K, see comments on SharedBitSet in gfxFontUtils.h)
+   * within a single ShmBlock.
+   * Using a larger block size will speed up allocation, at the cost of more
+   * wasted space in the shared memory (on average).
+   */
+  static const size_t SHM_BLOCK_SIZE = 256 * 1024;
+  static_assert(SHM_BLOCK_SIZE <= (1 << Pointer::kBlockShift),
+                "SHM_BLOCK_SIZE too large");
+  static_assert(SHM_BLOCK_SIZE >= SharedBitSet::kMaxSize + 4,
+                "SHM_BLOCK_SIZE too small");
+
+ private:
+  struct ShmBlock {
+    ShmBlock(mozilla::ipc::SharedMemoryBasic* aShmem, void* aAddr)
+        : mShmem(aShmem), mAddr(aAddr) {}
+
+    // The first 32-bit word of each block holds the current amount allocated
+    // in that block; this is updated whenever a new record is stored in the
+    // block.
+    std::atomic<uint32_t>& Allocated() const {
+      return *static_cast<std::atomic<uint32_t>*>(mAddr);
+    }
+
+    RefPtr<mozilla::ipc::SharedMemoryBasic> mShmem;
+    void* mAddr; // Address where the shared memory block is mapped in this
+                 // process; avoids virtual call to mShmem->memory() each time
+                 // we need to convert between Pointer and a real C++ pointer.
+  };
+
+  Header& GetHeader() {
+    // It's invalid to try and access this before the first block exists.
+    MOZ_ASSERT(mBlocks.Length() > 0);
+    return *static_cast<Header*>(Pointer(0, 0).ToPtr(this));
+  }
+
+  /**
+   * Create a new shared memory block and append to the FontList's list
+   * of blocks.
+   *
+   * Only used in the parent process.
+   */
+  bool AppendShmBlock();
+
+  /**
+   * Used by child processes to ensure all the blocks are registered.
+   * Returns false on failure.
+   */
+  MOZ_MUST_USE bool UpdateShmBlocks();
+
+  /**
+   * This makes a *sync* IPC call to get a shared block from the parent.
+   * As such, it may block for a while if the parent is busy; fortunately,
+   * we'll generally only call this a handful of times in the course of an
+   * entire session. If the requested block does not yet exist (because the
+   * child is wanting to allocate an object, and there wasn't room in any
+   * existing block), the parent will create a new shared block and return it.
+   * This may (in rare cases) return null, if the parent has recreated the
+   * font list and we actually need to reinitialize.
+   */
+  ShmBlock* GetBlockFromParent(uint32_t aIndex);
+
+  void DetachShmBlocks();
+
+  /**
+   * Array of pointers to the shared-memory block records.
+   * NOTE: if mBlocks.Length() < GetHeader().mBlockCount, then the parent has
+   * added a block (or blocks) to the list, and we need to update!
+   */
+  nsTArray<mozilla::UniquePtr<ShmBlock>> mBlocks;
+};
+
+}  // namespace fontlist
+}  // namespace mozilla
+
+#endif /* SharedFontList_impl_h */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/SharedFontList.cpp
@@ -0,0 +1,709 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedFontList-impl.h"
+#include "gfxPlatformFontList.h"
+#include "gfxFontUtils.h"
+#include "gfxFont.h"
+#include "prerror.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Logging.h"
+
+#define LOG_FONTLIST(args) \
+  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
+#define LOG_FONTLIST_ENABLED() \
+  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
+
+namespace mozilla {
+namespace fontlist {
+
+static double WSSDistance(const Face* aFace, const gfxFontStyle& aStyle) {
+  double stretchDist = StretchDistance(aFace->mStretch, aStyle.stretch);
+  double styleDist = StyleDistance(aFace->mStyle, aStyle.style);
+  double weightDist = WeightDistance(aFace->mWeight, aStyle.weight);
+
+  // Sanity-check that the distances are within the expected range
+  // (update if implementation of the distance functions is changed).
+  MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
+  MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
+  MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
+
+  // weight/style/stretch priority: stretch >> style >> weight
+  // so we multiply the stretch and style values to make them dominate
+  // the result
+  return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist;
+}
+
+void* Pointer::ToPtr(FontList* aFontList) const {
+  if (IsNull()) {
+    return nullptr;
+  }
+  uint32_t block = Block();
+  // If the Pointer refers to a block we have not yet mapped in this process,
+  // we first need to retrieve new block handle(s) from the parent and update
+  // our mBlocks list.
+  if (block >= aFontList->mBlocks.Length()) {
+    if (XRE_IsParentProcess()) {
+      // Shouldn't happen! A content process tried to pass a bad Pointer?
+      return nullptr;
+    }
+    // UpdateShmBlocks can fail, if the parent has replaced the font list with
+    // a new generation. In that case we just return null, and whatever font
+    // the content process was trying to use will appear unusable for now. It's
+    // about to receive a notification of the new font list anyhow, at which
+    // point it will flush its caches and reflow everything, so the temporary
+    // failure of this font will be forgotten.
+    if (!aFontList->UpdateShmBlocks()) {
+      return nullptr;
+    }
+    MOZ_ASSERT(block < aFontList->mBlocks.Length());
+  }
+  return static_cast<char*>(aFontList->mBlocks[block]->mAddr) + Offset();
+}
+
+void String::Assign(const nsACString& aString, FontList* aList) {
+  // We only assign to previously-empty strings; they are never modified
+  // after initial assignment.
+  MOZ_ASSERT(mPointer.IsNull());
+  mLength = aString.Length();
+  mPointer = aList->Alloc(mLength + 1);
+  char* p = static_cast<char*>(mPointer.ToPtr(aList));
+  std::memcpy(p, aString.BeginReading(), mLength);
+  p[mLength] = '\0';
+}
+
+Family::Family(FontList* aList, const InitData& aData)
+    : mFaceCount(0),
+      mKey(aList, aData.mKey),
+      mName(aList, aData.mName),
+      mCharacterMap(Pointer::Null()),
+      mFaces(Pointer::Null()),
+      mIndex(aData.mIndex),
+      mIsHidden(aData.mHidden),
+      mIsBadUnderlineFamily(aData.mBadUnderline),
+      mIsForceClassic(aData.mForceClassic),
+      mIsSimple(false) {
+  MOZ_ASSERT(aData.mIndex <= 0x7fffffffu);
+  mIndex = aData.mIndex | (aData.mBundled ? 0x80000000u : 0u);
+}
+
+void Face::SetCharacterMap(FontList* aList, const gfxSparseBitSet* aCharMap) {
+  if (!XRE_IsParentProcess()) {
+    Pointer ptr = aList->ToSharedPointer(this);
+    dom::ContentChild::GetSingleton()->SendSetCharacterMap(
+        aList->GetGeneration(), ptr, *aCharMap);
+    return;
+  }
+  auto pfl = gfxPlatformFontList::PlatformFontList();
+  mCharacterMap = pfl->GetShmemCharMap(aCharMap);
+}
+
+void Family::AddFaces(FontList* aList, const nsTArray<Face::InitData>& aFaces) {
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if (mFaceCount > 0) {
+    // Already initialized!
+    MOZ_ASSERT(mFaceCount == aFaces.Length());
+    return;
+  }
+  uint32_t count = aFaces.Length();
+  // Allocate space for the face records, and initialize them
+  Pointer p = aList->Alloc(aFaces.Length() * sizeof(Pointer));
+  auto facePtrs = static_cast<Pointer*>(p.ToPtr(aList));
+  for (size_t i = 0; i < aFaces.Length(); i++) {
+    Pointer fp = aList->Alloc(sizeof(Face));
+    auto face = static_cast<Face*>(fp.ToPtr(aList));
+    (void)new (face) Face(aList, aFaces[i]);
+    facePtrs[i] = fp;
+  }
+  mFaces = p;
+  mFaceCount.store(count);
+  if (LOG_FONTLIST_ENABLED()) {
+    const nsCString& fam = DisplayName().AsString(aList);
+    for (unsigned j = 0; j < aFaces.Length(); j++) {
+      nsAutoCString weight, style, stretch;
+      aFaces[j].mWeight.ToString(weight);
+      aFaces[j].mStyle.ToString(style);
+      aFaces[j].mStretch.ToString(stretch);
+      LOG_FONTLIST(
+          ("(shared-fontlist) family (%s) added face (%s) index %u, weight "
+           "%s, style %s, stretch %s",
+           fam.get(), aFaces[j].mDescriptor.get(), aFaces[j].mIndex,
+           weight.get(), style.get(), stretch.get()));
+    }
+  }
+}
+
+void Family::FindAllFacesForStyle(FontList* aList, const gfxFontStyle& aStyle,
+                                  nsTArray<Face*>& aFaceList,
+                                  bool aIgnoreSizeTolerance) const {
+  MOZ_ASSERT(aFaceList.IsEmpty());
+  if (!IsInitialized()) {
+    return;
+  }
+
+  Pointer* facePtrs = Faces(aList);
+
+  // If the family has only one face, we simply return it; no further
+  // checking needed.
+  if (NumFaces() == 1) {
+    MOZ_ASSERT(!facePtrs->IsNull());
+    aFaceList.AppendElement(static_cast<Face*>(facePtrs[0].ToPtr(aList)));
+    return;
+  }
+
+  // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
+  // or some subset of these. In this case, we have exactly 4 entries in
+  // mAvailableFonts, stored in the above order; note that some of the entries
+  // may be nullptr. We can then pick the required entry based on whether the
+  // request is for bold or non-bold, italic or non-italic, without running the
+  // more complex matching algorithm used for larger families with many weights
+  // and/or widths.
+
+  if (mIsSimple) {
+    // Family has no more than the "standard" 4 faces, at fixed indexes;
+    // calculate which one we want.
+    // Note that we cannot simply return it as not all 4 faces are necessarily
+    // present.
+    bool wantBold = aStyle.weight >= FontWeight(600);
+    bool wantItalic = !aStyle.style.IsNormal();
+    uint8_t faceIndex =
+        (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
+
+    // if the desired style is available, return it directly
+    Face* face = static_cast<Face*>(facePtrs[faceIndex].ToPtr(aList));
+    if (face->HasValidDescriptor()) {
+      aFaceList.AppendElement(face);
+      return;
+    }
+
+    // order to check fallback faces in a simple family, depending on requested
+    // style
+    static const uint8_t simpleFallbacks[4][3] = {
+        {kBoldFaceIndex, kItalicFaceIndex,
+         kBoldItalicFaceIndex},  // fallback sequence for Regular
+        {kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex},  // Bold
+        {kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex},    // Italic
+        {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex}  // BoldItalic
+    };
+    const uint8_t* order = simpleFallbacks[faceIndex];
+
+    for (uint8_t trial = 0; trial < 3; ++trial) {
+      // check remaining faces in order of preference to find the first that
+      // actually exists
+      face = static_cast<Face*>(facePtrs[order[trial]].ToPtr(aList));
+      if (face->HasValidDescriptor()) {
+        aFaceList.AppendElement(face);
+        return;
+      }
+    }
+
+    // this can't happen unless we have totally broken the font-list manager!
+    MOZ_ASSERT_UNREACHABLE("no face found in simple font family!");
+  }
+
+  // Pick the font(s) that are closest to the desired weight, style, and
+  // stretch. Iterate over all fonts, measuring the weight/style distance.
+  // Because of unicode-range values, there may be more than one font for a
+  // given but the 99% use case is only a single font entry per
+  // weight/style/stretch distance value. To optimize this, only add entries
+  // to the matched font array when another entry already has the same
+  // weight/style/stretch distance and add the last matched font entry. For
+  // normal platform fonts with a single font entry for each
+  // weight/style/stretch combination, only the last matched font entry will
+  // be added.
+
+  double minDistance = INFINITY;
+  Face* matched = nullptr;
+  for (uint32_t i = 0; i < NumFaces(); i++) {
+    Face* face = static_cast<Face*>(facePtrs[i].ToPtr(aList));
+    // weight/style/stretch priority: stretch >> style >> weight
+    double distance = WSSDistance(face, aStyle);
+    if (distance < minDistance) {
+      matched = face;
+      if (!aFaceList.IsEmpty()) {
+        aFaceList.Clear();
+      }
+      minDistance = distance;
+    } else if (distance == minDistance) {
+      if (matched) {
+        aFaceList.AppendElement(matched);
+      }
+      matched = face;
+    }
+  }
+
+  MOZ_ASSERT(matched, "didn't match a font within a family");
+  if (matched) {
+    aFaceList.AppendElement(matched);
+  }
+}
+
+Face* Family::FindFaceForStyle(FontList* aList, const gfxFontStyle& aStyle,
+                               bool aIgnoreSizeTolerance) const {
+  AutoTArray<Face*, 4> faces;
+  FindAllFacesForStyle(aList, aStyle, faces, aIgnoreSizeTolerance);
+  return faces.IsEmpty() ? nullptr : faces[0];
+}
+
+void Family::SearchAllFontsForChar(FontList* aList,
+                                   GlobalFontMatch* aMatchData) {
+  const SharedBitSet* charmap =
+      static_cast<const SharedBitSet*>(mCharacterMap.ToPtr(aList));
+  if (charmap && !charmap->test(aMatchData->mCh)) {
+    return;
+  }
+  if (!IsInitialized()) {
+    if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this)) {
+      return;
+    }
+  }
+  uint32_t numFaces = NumFaces();
+  uint32_t charMapsLoaded = 0; // number of faces whose charmap is loaded
+  Pointer* facePtrs = Faces(aList);
+  for (uint32_t i = 0; i < numFaces; i++) {
+    Face* face = static_cast<Face*>(facePtrs[i].ToPtr(aList));
+    MOZ_ASSERT(face->HasValidDescriptor());
+    // Get the face's character map, if available (may be null!)
+    charmap =
+        static_cast<const SharedBitSet*>(face->mCharacterMap.ToPtr(aList));
+    if (charmap) {
+      ++charMapsLoaded;
+    }
+    // Check style distance if the char is supported, or if charmap not known
+    if (!charmap || charmap->test(aMatchData->mCh)) {
+      double distance = WSSDistance(face, aMatchData->mStyle);
+      if (distance < aMatchData->mMatchDistance) {
+        // It's a better style match: if we didn't already check charmap,
+        // load it and do that now
+        if (!charmap) {
+          aList->LoadCharMapFor(*face, this);
+          charmap = static_cast<const SharedBitSet*>(
+              face->mCharacterMap.ToPtr(aList));
+          if (charmap) {
+            ++charMapsLoaded;
+          }
+          if (!charmap || !charmap->test(aMatchData->mCh)) {
+            continue;
+          }
+        }
+        aMatchData->mBestMatch =
+            gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face,
+                                                                          this);
+        aMatchData->mMatchDistance = distance;
+        aMatchData->mMatchedSharedFamily = this;
+      }
+    }
+  }
+  if (mCharacterMap.IsNull() && charMapsLoaded == numFaces) {
+    SetupFamilyCharMap(aList);
+  }
+}
+
+void Family::SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces) {
+  size_t size = aFaces.Length() * sizeof(Pointer);
+  // XXX TODO: [in patch 7]
+  // Check whether the faces meet the criteria for a "simple" family; if so,
+  // store them at the appropriate positions and set the mIsSimple flag to
+  // accelerate font-matching.
+  mFaces = aList->Alloc(size);
+  memcpy(mFaces.ToPtr(aList), aFaces.Elements(), size);
+  mFaceCount.store(aFaces.Length());
+}
+
+void Family::SetupFamilyCharMap(FontList* aList) {
+  // XXX TODO: [in patch 6]
+  // Set the character map of the family to the union of all the face cmaps,
+  // to allow font fallback searches to more rapidly reject the family.
+}
+
+FontList::FontList(uint32_t aGeneration) {
+  if (XRE_IsParentProcess()) {
+    // Create the initial shared block, and initialize Header
+    if (AppendShmBlock()) {
+      Header& header = GetHeader();
+      header.mAllocated.store(sizeof(Header));
+      header.mGeneration = aGeneration;
+      header.mFamilyCount = 0;
+      header.mBlockCount.store(1);
+      header.mAliasCount.store(0);
+      header.mLocalFaceCount.store(0);
+      header.mFamilies = Pointer::Null();
+      header.mAliases = Pointer::Null();
+      header.mLocalFaces = Pointer::Null();
+    } else {
+      MOZ_CRASH("parent: failed to initialize FontList");
+    }
+  } else {
+    for (unsigned retryCount = 0; retryCount < 3; ++retryCount) {
+      if (UpdateShmBlocks()) {
+        return;
+      }
+      // The only reason for UpdateShmBlocks to fail is if the parent recreated
+      // the list after we read its first block, but before we finished getting
+      // them all, and so the generation check failed on a subsequent request.
+      // Discarding whatever we've got and retrying should get us a new,
+      // consistent set of memory blocks in this case. If this doesn't work
+      // after a couple of retries, bail out.
+      DetachShmBlocks();
+    }
+    NS_WARNING("child: failed to initialize shared FontList");
+  }
+}
+
+FontList::~FontList() { DetachShmBlocks(); }
+
+bool FontList::AppendShmBlock() {
+  MOZ_ASSERT(XRE_IsParentProcess());
+  ipc::SharedMemoryBasic* newShm = new ipc::SharedMemoryBasic();
+  if (!newShm->Create(SHM_BLOCK_SIZE)) {
+    MOZ_CRASH("failed to create shared memory");
+    return false;
+  }
+  if (!newShm->Map(SHM_BLOCK_SIZE)) {
+    MOZ_CRASH("failed to map shared memory");
+  }
+
+  char* addr = static_cast<char*>(newShm->memory());
+  if (!addr) {
+    MOZ_CRASH("null shared memory?");
+    return false;
+  }
+
+  ShmBlock* block = new ShmBlock(newShm, addr);
+  // Allocate space for the Allocated() header field present in all blocks
+  block->Allocated().store(4);
+
+  mBlocks.AppendElement(block);
+  GetHeader().mBlockCount.store(mBlocks.Length());
+
+  return true;
+}
+
+void FontList::DetachShmBlocks() {
+  for (auto& i : mBlocks) {
+    i->mShmem = nullptr;
+  }
+  mBlocks.SetLength(0);
+}
+
+FontList::ShmBlock* FontList::GetBlockFromParent(uint32_t aIndex) {
+  MOZ_ASSERT(!XRE_IsParentProcess());
+  // If we have no existing blocks, we don't want a generation check yet;
+  // the header in the first block will define the generation of this list
+  uint32_t generation = aIndex == 0 ? 0 : GetGeneration();
+  ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle();
+  if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
+          generation, aIndex, &handle)) {
+    return nullptr;
+  }
+  RefPtr<ipc::SharedMemoryBasic> newShm = new ipc::SharedMemoryBasic();
+  if (!newShm->IsHandleValid(handle)) {
+    return nullptr;
+  }
+  if (!newShm->SetHandle(handle,
+                         mozilla::ipc::SharedMemoryBasic::RightsReadOnly)) {
+    MOZ_CRASH("failed to set shm handle");
+  }
+  if (!newShm->Map(SHM_BLOCK_SIZE)) {
+    MOZ_CRASH("failed to map shared memory");
+  }
+  char* addr = static_cast<char*>(newShm->memory());
+  if (!addr) {
+    MOZ_CRASH("null shared memory?");
+  }
+  return new ShmBlock(newShm, addr);
+}
+
+bool FontList::UpdateShmBlocks() {
+  MOZ_ASSERT(!XRE_IsParentProcess());
+  while (!mBlocks.Length() || mBlocks.Length() < GetHeader().mBlockCount) {
+    ShmBlock* newBlock = GetBlockFromParent(mBlocks.Length());
+    if (!newBlock) {
+      return false;
+    }
+    mBlocks.AppendElement(newBlock);
+  }
+  return true;
+}
+
+// The block size MUST be sufficient to allocate the largest possible
+// SharedBitSet in a single contiguous block, following its own
+// Allocated() field.
+static_assert(FontList::SHM_BLOCK_SIZE >= 4 + SharedBitSet::kMaxSize,
+              "may not be able to allocate a SharedBitSet");
+
+Pointer FontList::Alloc(uint32_t aSize) {
+  // Only the parent process does allocation.
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  // 4-byte alignment is good enough for anything we allocate in the font list,
+  // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
+  auto align = [](uint32_t aSize) -> size_t { return (aSize + 3u) & ~3u; };
+
+  // There's a limit to the size of object we can allocate: the block size,
+  // minus the 4-byte mAllocated header field at the start of the block.
+  MOZ_DIAGNOSTIC_ASSERT(aSize <= SHM_BLOCK_SIZE - 4);
+
+  aSize = align(aSize);
+
+  int32_t blockIndex;
+  uint32_t curAlloc;
+  while (true) {
+    // Try to allocate in the most recently added block first, as this is
+    // highly likely to succeed; if not, try earlier blocks (to fill gaps).
+    const int32_t blockCount = mBlocks.Length();
+    for (blockIndex = blockCount - 1; blockIndex >= 0; --blockIndex) {
+      curAlloc = mBlocks[blockIndex]->Allocated();
+      if (SHM_BLOCK_SIZE - curAlloc >= aSize) {
+        break;
+      }
+    }
+
+    if (blockIndex < 0) {
+      // Couldn't find enough space: create a new block, and retry.
+      if (!AppendShmBlock()) {
+        return Pointer::Null();
+      }
+      continue;  // retry; this will check the newly-added block first,
+                 // which must succeed because it's empty
+    }
+
+    // We've found a block; allocate space from it, and return
+    mBlocks[blockIndex]->Allocated() = curAlloc + aSize;
+    break;
+  }
+
+  return Pointer(blockIndex, curAlloc);
+}
+
+void FontList::LoadCharMapFor(Face& aFace, const Family* aFamily) {
+  RefPtr<gfxFontEntry> fe =
+      gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(&aFace,
+                                                                    aFamily);
+  if (fe) {
+    fe->ReadCMAP();
+  }
+}
+
+void FontList::SetFamilyNames(const nsTArray<Family::InitData>& aFamilies) {
+  // Only the parent process should ever assign the list of families.
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  Header& header = GetHeader();
+  MOZ_ASSERT(!header.mFamilyCount);
+
+  size_t count = aFamilies.Length();
+  header.mFamilies = Alloc(count * sizeof(Family));
+  if (header.mFamilies.IsNull()) {
+    return;
+  }
+
+  Family* families = static_cast<Family*>(header.mFamilies.ToPtr(this));
+  for (size_t i = 0; i < count; i++) {
+    (void)new (&families[i]) Family(this, aFamilies[i]);
+    LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i,
+                  aFamilies[i].mName.get()));
+  }
+
+  header.mFamilyCount = count;
+}
+
+void FontList::SetAliases(
+    nsClassHashtable<nsCStringHashKey, nsTArray<Pointer>>& aAliasTable) {
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  Header& header = GetHeader();
+
+  nsTArray<Family::InitData> aliasArray;
+  aliasArray.SetCapacity(aAliasTable.Count());
+  for (auto i = aAliasTable.Iter(); !i.Done(); i.Next()) {
+    nsAutoCString key(i.Key());
+    ToLowerCase(key);
+    aliasArray.AppendElement(Family::InitData(key, i.Key()));
+  }
+  aliasArray.Sort();
+
+  size_t count = aliasArray.Length();
+  if (count < header.mAliasCount) {
+    // This shouldn't happen, but handle it safely by just bailing out.
+    NS_WARNING("cannot reduce number of aliases");
+    return;
+  }
+  fontlist::Pointer ptr = Alloc(count * sizeof(Family));
+  Family* aliases = static_cast<Family*>(ptr.ToPtr(this));
+  for (size_t i = 0; i < count; i++) {
+    (void)new (&aliases[i]) Family(this, aliasArray[i]);
+    LOG_FONTLIST(("(shared-fontlist) alias family %u (%s)", (unsigned)i,
+                  aliasArray[i].mName.get()));
+    aliases[i].SetFacePtrs(this, *aAliasTable.Get(aliasArray[i].mName));
+    if (LOG_FONTLIST_ENABLED()) {
+      const auto& faces = *aAliasTable.Get(aliasArray[i].mName);
+      for (unsigned j = 0; j < faces.Length(); j++) {
+        auto face = static_cast<const fontlist::Face*>(faces[j].ToPtr(this));
+        const nsCString& desc = face->mDescriptor.AsString(this);
+        nsAutoCString weight, style, stretch;
+        face->mWeight.ToString(weight);
+        face->mStyle.ToString(style);
+        face->mStretch.ToString(stretch);
+        LOG_FONTLIST(
+            ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
+             "stretch %s",
+             desc.get(), face->mIndex, weight.get(), style.get(),
+             stretch.get()));
+      }
+    }
+  }
+
+  // Set the pointer before the count, so that any other process trying to read
+  // will not risk out-of-bounds access to the array.
+  header.mAliases = ptr;
+  header.mAliasCount.store(count);
+}
+
+void FontList::SetLocalNames(
+    nsDataHashtable<nsCStringHashKey, LocalFaceRec::InitData>&
+        aLocalNameTable) {
+  MOZ_ASSERT(XRE_IsParentProcess());
+  Header& header = GetHeader();
+  if (header.mLocalFaceCount > 0) {
+    return;  // already been done!
+  }
+  nsTArray<nsCString> faceArray;
+  faceArray.SetCapacity(aLocalNameTable.Count());
+  for (auto i = aLocalNameTable.Iter(); !i.Done(); i.Next()) {
+    faceArray.AppendElement(i.Key());
+  }
+  faceArray.Sort();
+  size_t count = faceArray.Length();
+  Family* families = Families();
+  fontlist::Pointer ptr = Alloc(count * sizeof(LocalFaceRec));
+  LocalFaceRec* faces = static_cast<LocalFaceRec*>(ptr.ToPtr(this));
+  for (size_t i = 0; i < count; i++) {
+    (void)new (&faces[i]) LocalFaceRec();
+    const auto& rec = aLocalNameTable.Get(faceArray[i]);
+    faces[i].mKey.Assign(faceArray[i], this);
+    faces[i].mFamilyIndex = FindFamily(rec.mFamilyName) - families;
+    faces[i].mFaceIndex = rec.mFaceIndex;
+  }
+  header.mLocalFaces = ptr;
+  header.mLocalFaceCount.store(count);
+}
+
+Family* FontList::FindFamily(const nsCString& aName) {
+  struct FamilyNameComparator {
+    FamilyNameComparator(FontList* aList, const nsCString& aTarget)
+        : mList(aList), mTarget(aTarget) {}
+
+    int operator()(const Family& aVal) const {
+      return mTarget.Compare(aVal.Key().BeginReading(mList));
+    }
+
+   private:
+    FontList* mList;
+    const nsCString& mTarget;
+  };
+
+  Header& header = GetHeader();
+
+  Family* families = Families();
+  size_t match;
+  if (BinarySearchIf(families, 0, header.mFamilyCount,
+                     FamilyNameComparator(this, aName), &match)) {
+    return &families[match];
+  }
+
+  if (header.mAliasCount) {
+    families = AliasFamilies();
+    size_t match;
+    if (BinarySearchIf(families, 0, header.mAliasCount,
+                       FamilyNameComparator(this, aName), &match)) {
+      return &families[match];
+    }
+  }
+
+  return nullptr;
+}
+
+LocalFaceRec* FontList::FindLocalFace(const nsCString& aName) {
+  struct FaceNameComparator {
+    FaceNameComparator(FontList* aList, const nsCString& aTarget)
+        : mList(aList), mTarget(aTarget) {}
+
+    int operator()(const LocalFaceRec& aVal) const {
+      return mTarget.Compare(aVal.mKey.BeginReading(mList));
+    }
+
+   private:
+    FontList* mList;
+    const nsCString& mTarget;
+  };
+
+  Header& header = GetHeader();
+
+  LocalFaceRec* faces = LocalFaces();
+  size_t match;
+  if (BinarySearchIf(faces, 0, header.mLocalFaceCount,
+                     FaceNameComparator(this, aName), &match)) {
+    return &faces[match];
+  }
+
+  return nullptr;
+}
+
+void FontList::SearchForLocalFace(const nsACString& aName, Family** aFamily,
+                                  Face** aFace) {
+  Header& header = GetHeader();
+  MOZ_ASSERT(header.mLocalFaceCount == 0,
+             "do not use when local face names are already set up!");
+  LOG_FONTLIST(
+      ("(shared-fontlist) local face search for (%s)", aName.BeginReading()));
+  char initial = aName[0];
+  Family* families = Families();
+  for (uint32_t i = 0; i < header.mFamilyCount; i++) {
+    Family* family = &families[i];
+    if (family->Key().AsString(this)[0] != initial) {
+      continue;
+    }
+    LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
+                  family->Key().AsString(this).BeginReading()));
+    if (!family->IsInitialized()) {
+      if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family)) {
+        continue;
+      }
+    }
+    Pointer* faces = family->Faces(this);
+    for (uint32_t j = 0; j < family->NumFaces(); j++) {
+      Face* face = static_cast<Face*>(faces[j].ToPtr(this));
+      nsAutoCString psname, fullname;
+      if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
+              family, face, psname, fullname)) {
+        LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
+                      psname.get(), fullname.get()));
+        ToLowerCase(psname);
+        ToLowerCase(fullname);
+        if (aName == psname || aName == fullname) {
+          *aFamily = family;
+          *aFace = face;
+          return;
+        }
+      }
+    }
+  }
+}
+
+Pointer FontList::ToSharedPointer(const void* aPtr) {
+  const char* p = (const char*)aPtr;
+  const uint32_t blockCount = mBlocks.Length();
+  for (uint32_t i = 0; i < blockCount; ++i) {
+    const char* blockAddr = (const char*)mBlocks[i]->mAddr;
+    if (p >= blockAddr && p < blockAddr + SHM_BLOCK_SIZE) {
+      return Pointer(i, p - blockAddr);
+    }
+  }
+  MOZ_ASSERT_UNREACHABLE("invalid shared-memory pointer");
+  return Pointer::Null();
+}
+
+}  // namespace fontlist
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/SharedFontList.h
@@ -0,0 +1,331 @@
+/* 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 SharedFontList_h
+#define SharedFontList_h
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/FontPropertyTypes.h"
+#include <atomic>
+
+class gfxSparseBitSet;
+struct gfxFontStyle;
+class gfxFontEntry;
+struct GlobalFontMatch;
+
+namespace mozilla {
+namespace fontlist {
+
+class FontList;  // See the separate SharedFontList-impl.h header
+
+/**
+ * A Pointer in the shared font list contains a packed index/offset pair,
+ * with a 12-bit index into the array of shared-memory blocks, and a 20-bit
+ * offset into the block.
+ * The maximum size of each block is therefore 2^20 bytes (1048576).
+ */
+struct Pointer {
+ private:
+  friend class FontList;
+  static const uint32_t kIndexBits = 12u;
+  static const uint32_t kBlockShift = 20u;
+  static_assert(kIndexBits + kBlockShift == 32u, "bad Pointer bit count");
+
+  static const uint32_t kNullValue = 0xffffffffu;
+  static const uint32_t kOffsetMask = (1u << kBlockShift) - 1;
+
+ public:
+  static Pointer Null() { return Pointer(); }
+
+  Pointer() : mBlockAndOffset(kNullValue) {}
+
+  Pointer(uint32_t aBlock, uint32_t aOffset)
+      : mBlockAndOffset((aBlock << kBlockShift) | aOffset) {
+    MOZ_ASSERT(aBlock < (1u << kIndexBits) && aOffset < (1u << kBlockShift));
+  }
+
+  Pointer(const Pointer& aOther) {
+    mBlockAndOffset.store(aOther.mBlockAndOffset);
+  }
+
+  bool IsNull() const { return mBlockAndOffset == kNullValue; }
+
+  uint32_t Block() const { return mBlockAndOffset >> kBlockShift; }
+
+  uint32_t Offset() const { return mBlockAndOffset & kOffsetMask; }
+
+  /**
+   * Convert a fontlist::Pointer to a standard C++ pointer. This requires the
+   * FontList, which will know where the shared memory block is mapped in
+   * the current process's address space.
+   */
+  void* ToPtr(FontList* aFontList) const;
+
+  Pointer& operator=(const Pointer& aOther) {
+    mBlockAndOffset.store(aOther.mBlockAndOffset);
+    return *this;
+  }
+
+  // We store the block index and the offset within the block as a single
+  // atomic 32-bit value so we can safely modify a Pointer without other
+  // processes seeing a broken (partially-updated) value.
+  std::atomic<uint32_t> mBlockAndOffset;
+};
+
+/**
+ * Family and face names are stored as String records, which hold a length
+ * (in utf-8 code units) and a Pointer to the string's UTF-8 characters.
+ */
+struct String {
+  String() : mPointer(Pointer::Null()), mLength(0) {}
+
+  String(FontList* aList, const nsACString& aString)
+      : mPointer(Pointer::Null()) {
+    Assign(aString, aList);
+  }
+
+  const nsCString AsString(FontList* aList) const {
+    MOZ_ASSERT(!mPointer.IsNull());
+    nsCString res;
+    res.AssignLiteral(static_cast<const char*>(mPointer.ToPtr(aList)), mLength);
+    return res;
+  }
+
+  void Assign(const nsACString& aString, FontList* aList);
+
+  const char* BeginReading(FontList* aList) const {
+    MOZ_ASSERT(!mPointer.IsNull());
+    return static_cast<const char*>(mPointer.ToPtr(aList));
+  }
+
+  uint32_t Length() const { return mLength; }
+
+  bool IsNull() const { return mPointer.IsNull(); }
+
+ private:
+  Pointer mPointer;
+  uint32_t mLength;
+};
+
+/**
+ * A Face record represents an individual font resource; it has the style
+ * properties needed for font matching, as well as a pointer to a character
+ * map that records the supported character set. This may be Null if we have
+ * not yet loaded the data.
+ * The mDescriptor and mIndex fields provide the information needed to
+ * instantiate a (platform-specific) font reference that can be used with
+ * platform font APIs; their content depends on the requirements of the
+ * platform APIs (e.g. font PostScript name, file pathname, serialized
+ * fontconfig pattern, etc).
+ */
+struct Face {
+  // Data required to initialize a Face
+  struct InitData {
+    nsCString mDescriptor; // descriptor that can be used to instantiate a
+                           // platform font reference
+    uint16_t mIndex;       // an index used with descriptor (on some platforms)
+    bool mFixedPitch;      // is the face fixed-pitch (monospaced)?
+    mozilla::WeightRange mWeight;    // CSS font-weight value
+    mozilla::StretchRange mStretch;  // CSS font-stretch value
+    mozilla::SlantStyleRange mStyle; // CSS font-style value
+  };
+
+  Face(FontList* aList, const InitData& aData)
+      : mDescriptor(aList, aData.mDescriptor),
+        mIndex(aData.mIndex),
+        mFixedPitch(aData.mFixedPitch),
+        mWeight(aData.mWeight),
+        mStretch(aData.mStretch),
+        mStyle(aData.mStyle),
+        mCharacterMap(Pointer::Null()) {}
+
+  bool HasValidDescriptor() const {
+    return !mDescriptor.IsNull() && mIndex != uint16_t(-1);
+  }
+
+  void SetCharacterMap(FontList* aList, const gfxSparseBitSet* aCharMap);
+
+  String mDescriptor;
+  uint16_t mIndex;
+  bool mFixedPitch;
+  mozilla::WeightRange mWeight;
+  mozilla::StretchRange mStretch;
+  mozilla::SlantStyleRange mStyle;
+  Pointer mCharacterMap;
+};
+
+/**
+ * A Family record represents an available (installed) font family; it has
+ * a name (for display purposes) and a key (lowercased, for case-insensitive
+ * lookups), as well as a pointer to an array of Faces. Depending on the
+ * platform, the array of faces may be lazily initialized the first time we
+ * want to use the family.
+ */
+struct Family {
+  // Data required to initialize a Family
+  struct InitData {
+    InitData(const nsACString& aKey, // lookup key (lowercased)
+             const nsACString& aName, // display name
+             uint32_t aIndex = 0, // [win] index in the system font collection
+             bool aHidden = false, // [mac] hidden font (e.g. .SFNSText)?
+             bool aBundled = false, // [win] font was bundled with the app
+                                    // rather than system-installed
+             bool aBadUnderline = false, // underline-position in font is bad
+             bool aForceClassic = false // [win] use "GDI classic" rendering
+            )
+        : mKey(aKey),
+          mName(aName),
+          mIndex(aIndex),
+          mHidden(aHidden),
+          mBundled(aBundled),
+          mBadUnderline(aBadUnderline),
+          mForceClassic(aForceClassic) {}
+    bool operator<(const InitData& aRHS) const { return mKey < aRHS.mKey; }
+    bool operator==(const InitData& aRHS) const {
+      return mKey == aRHS.mKey && mName == aRHS.mName &&
+             mHidden == aRHS.mHidden && mBundled == aRHS.mBundled &&
+             mBadUnderline == aRHS.mBadUnderline;
+    }
+    const nsCString mKey;
+    const nsCString mName;
+    uint32_t mIndex;
+    bool mHidden;
+    bool mBundled;
+    bool mBadUnderline;
+    bool mForceClassic;
+  };
+
+  /**
+   * Font families are considered "simple" if they contain only 4 faces with
+   * style attributes corresponding to Regular, Bold, Italic, and BoldItalic
+   * respectively, or a subset of these (e.g. only Regular and Bold). In this
+   * case, the faces are stored at predefined positions in the mFaces array,
+   * and a simplified (faster) style-matching algorithm can be used.
+   */
+  enum {
+    // Indexes into mFaces for families where mIsSimple is true
+    kRegularFaceIndex = 0,
+    kBoldFaceIndex = 1,
+    kItalicFaceIndex = 2,
+    kBoldItalicFaceIndex = 3,
+    // mask values for selecting face with bold and/or italic attributes
+    kBoldMask = 0x01,
+    kItalicMask = 0x02
+  };
+
+  Family(FontList* aList, const InitData& aData);
+
+  void AddFaces(FontList* aList, const nsTArray<Face::InitData>& aFaces);
+
+  void SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces);
+
+  const String& Key() const { return mKey; }
+
+  const String& DisplayName() const { return mName; }
+
+  uint32_t Index() const { return mIndex & 0x7fffffffu; }
+  bool IsBundled() const { return mIndex & 0x80000000u; }
+
+  uint32_t NumFaces() const {
+    MOZ_ASSERT(IsInitialized());
+    return mFaceCount;
+  }
+
+  Pointer* Faces(FontList* aList) const {
+    MOZ_ASSERT(IsInitialized());
+    return static_cast<Pointer*>(mFaces.ToPtr(aList));
+  }
+
+  bool IsHidden() const { return mIsHidden; }
+
+  bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
+  bool IsForceClassic() const { return mIsForceClassic; }
+
+  bool IsInitialized() const { return !mFaces.IsNull(); }
+
+  void FindAllFacesForStyle(FontList* aList, const gfxFontStyle& aStyle,
+                            nsTArray<Face*>& aFaceList,
+                            bool aIgnoreSizeTolerance = false) const;
+
+  Face* FindFaceForStyle(FontList* aList, const gfxFontStyle& aStyle,
+                         bool aIgnoreSizeTolerance = false) const;
+
+  void SearchAllFontsForChar(FontList* aList, GlobalFontMatch* aMatchData);
+
+  void SetupFamilyCharMap(FontList* aList);
+
+ private:
+  std::atomic<uint32_t> mFaceCount;
+  String mKey;
+  String mName;
+  Pointer mCharacterMap; // If non-null, union of character coverage of all
+                         // faces in the family
+  Pointer mFaces; // Pointer to array of |mFaceCount| face pointers
+  uint32_t mIndex; // [win] Top bit set indicates app-bundled font family
+  bool mIsHidden;
+  bool mIsBadUnderlineFamily;
+  bool mIsForceClassic;
+  bool mIsSimple; // family allows simplified style matching: mFaces contains
+                  // exactly 4 entries [Regular, Bold, Italic, BoldItalic].
+};
+
+/**
+ * For platforms where we build an index of local font face names (PS-name
+ * and fullname of the font) to support @font-face{src:local(...)}, we map
+ * each face name to an index into the family list, and an index into the
+ * family's list of faces.
+ */
+struct LocalFaceRec {
+  /**
+   * The InitData struct needs to record the family name rather than index,
+   * as we may be collecting these records at the same time as building the
+   * family list, so we don't yet know the final family index.
+   * When actually recorded in the FontList's mLocalFaces array, the family
+   * will be stored as a simple index into the mFamilies array.
+   */
+  struct InitData {
+    nsCString mFamilyName;
+    uint32_t mFaceIndex;
+    InitData(const nsACString& aFamily, uint32_t aFace)
+        : mFamilyName(aFamily), mFaceIndex(aFace) {}
+    InitData() = default;
+  };
+  String mKey;
+  uint32_t mFamilyIndex;  // Index into the font list's Families array
+  uint32_t mFaceIndex;    // Index into the family's Faces array
+};
+
+}  // namespace fontlist
+}  // namespace mozilla
+
+#include "ipc/IPCMessageUtils.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::fontlist::Pointer> {
+  typedef mozilla::fontlist::Pointer paramType;
+  static void Write(Message* aMsg, const paramType& aParam) {
+    uint32_t v = aParam.mBlockAndOffset;
+    WriteParam(aMsg, v);
+  }
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    uint32_t v;
+    if (ReadParam(aMsg, aIter, &v)) {
+      aResult->mBlockAndOffset.store(v);
+      return true;
+    }
+    return false;
+  }
+};
+
+}  // namespace IPC
+
+#undef ERROR // This is defined via Windows.h, but conflicts with some bindings
+             // code when this gets included in the same compilation unit.
+
+#endif /* SharedFontList_h */
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -677,29 +677,26 @@ inline bool gfxFontEntry::SupportsBold()
   return Weight().Max().IsBold() ||
          ((mRangeFlags & RangeFlags::eAutoWeight) == RangeFlags::eAutoWeight &&
           HasBoldVariableWeight());
 }
 
 // used when iterating over all fonts looking for a match for a given character
 struct GlobalFontMatch {
   GlobalFontMatch(const uint32_t aCharacter, const gfxFontStyle& aStyle)
-      : mStyle(aStyle),
-        mCh(aCharacter),
-        mCount(0),
-        mCmapsTested(0),
-        mMatchDistance(INFINITY) {}
+      : mStyle(aStyle), mCh(aCharacter) {}
 
   RefPtr<gfxFontEntry> mBestMatch;       // current best match
   RefPtr<gfxFontFamily> mMatchedFamily;  // the family it belongs to
-  const gfxFontStyle& mStyle;            // style to match
-  const uint32_t mCh;                    // codepoint to be matched
-  uint32_t mCount;                       // number of fonts matched
-  uint32_t mCmapsTested;                 // number of cmaps tested
-  float mMatchDistance;                  // metric indicating closest match
+  mozilla::fontlist::Family* mMatchedSharedFamily = nullptr;
+  const gfxFontStyle& mStyle;       // style to match
+  const uint32_t mCh;               // codepoint to be matched
+  uint32_t mCount = 0;              // number of fonts matched
+  uint32_t mCmapsTested = 0;        // number of cmaps tested
+  float mMatchDistance = INFINITY;  // metric indicating closest match
 };
 
 class gfxFontFamily {
  public:
   // Used by stylo
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontFamily)
 
   explicit gfxFontFamily(const nsACString& aName)
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -5,32 +5,37 @@
 
 #ifndef GFX_FONT_UTILS_H
 #define GFX_FONT_UTILS_H
 
 #include "gfxFontVariations.h"
 #include "gfxPlatform.h"
 #include "nsComponentManagerUtils.h"
 #include "nsTArray.h"
+#include "ipc/IPCMessageUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/UniquePtr.h"
 
 #include "zlib.h"
 #include <algorithm>
 
 /* Bug 341128 - w32api defines min/max which causes problems with <bitset> */
 #ifdef __MINGW32__
 #  undef min
 #  undef max
 #endif
 
+#undef ERROR /* defined by Windows.h, conflicts with some generated bindings \
+                code when this gets indirectly included via shared font list \
+              */
+
 typedef struct hb_blob_t hb_blob_t;
 
 class gfxSparseBitSet {
  private:
   friend class SharedBitSet;
 
   enum { BLOCK_SIZE = 32 };  // ==> 256 codepoints per block
   enum { BLOCK_SIZE_BITS = BLOCK_SIZE * 8 };
@@ -306,20 +311,50 @@ class gfxSparseBitSet {
         adler32(0, reinterpret_cast<const uint8_t*>(mBlockIndex.Elements()),
                 mBlockIndex.Length() * sizeof(uint16_t));
     check = adler32(check, reinterpret_cast<const uint8_t*>(mBlocks.Elements()),
                     mBlocks.Length() * sizeof(Block));
     return check;
   }
 
  private:
+  friend struct IPC::ParamTraits<gfxSparseBitSet>;
+  friend struct IPC::ParamTraits<gfxSparseBitSet::Block>;
   nsTArray<uint16_t> mBlockIndex;
   nsTArray<Block> mBlocks;
 };
 
+namespace IPC {
+template <>
+struct ParamTraits<gfxSparseBitSet> {
+  typedef gfxSparseBitSet paramType;
+  static void Write(Message* aMsg, const paramType& aParam) {
+    WriteParam(aMsg, aParam.mBlockIndex);
+    WriteParam(aMsg, aParam.mBlocks);
+  }
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    return ReadParam(aMsg, aIter, &aResult->mBlockIndex) &&
+           ReadParam(aMsg, aIter, &aResult->mBlocks);
+  }
+};
+
+template <>
+struct ParamTraits<gfxSparseBitSet::Block> {
+  typedef gfxSparseBitSet::Block paramType;
+  static void Write(Message* aMsg, const paramType& aParam) {
+    aMsg->WriteBytes(&aParam, sizeof(aParam));
+  }
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
+  }
+};
+}  // namespace IPC
+
 /**
  * SharedBitSet is a version of gfxSparseBitSet that is intended to be used
  * in a shared-memory block, and can be used regardless of the address at which
  * the block has been mapped. The SharedBitSet cannot be modified once it has
  * been created.
  *
  * Max size of a SharedBitSet = 4352 * 32  ; blocks
  *                              + 4352 * 2 ; index
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -6,16 +6,17 @@
 #include "mozilla/Logging.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/intl/MozLocale.h"
 #include "mozilla/intl/OSPreferences.h"
 
 #include "gfxPlatformFontList.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
+#include "SharedFontList-impl.h"
 
 #include "nsCRT.h"
 #include "nsGkAtoms.h"
 #include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsUnicodeProperties.h"
 #include "nsXULAppAPI.h"
 
@@ -787,16 +788,26 @@ gfxFontEntry* gfxPlatformFontList::FindF
     const nsACString& aFamily, const gfxFontStyle* aStyle) {
   gfxFontFamily* familyEntry = FindFamily(aFamily);
 
   if (familyEntry) return familyEntry->FindFontForStyle(*aStyle);
 
   return nullptr;
 }
 
+gfxFontEntry* gfxPlatformFontList::GetOrCreateFontEntry(
+    fontlist::Face* aFace, const fontlist::Family* aFamily) {
+  gfxFontEntry* fe = mFontEntries.GetWeak(aFace);
+  if (!fe) {
+    fe = CreateFontEntry(aFace, aFamily);
+    mFontEntries.Put(aFace, fe);
+  }
+  return fe;
+}
+
 void gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily* aFamilyEntry,
                                              nsCString& aOtherFamilyName) {
   nsAutoCString key;
   GenerateFontListKey(aOtherFamilyName, key);
 
   if (!mOtherFamilyNames.GetWeak(key)) {
     mOtherFamilyNames.Put(key, aFamilyEntry);
     LOG_FONTLIST(
@@ -1696,10 +1707,54 @@ void gfxPlatformFontList::InitOtherFamil
 
 void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() {
   if (mPendingOtherFamilyNameTask) {
     mPendingOtherFamilyNameTask->Cancel();
     mPendingOtherFamilyNameTask = nullptr;
   }
 }
 
+void gfxPlatformFontList::ShareFontListShmBlockToProcess(
+    uint32_t aGeneration, uint32_t aIndex, /*base::ProcessId*/ uint32_t aPid,
+    /*mozilla::ipc::SharedMemoryBasic::Handle*/ void* aOut) {
+  MOZ_ASSERT(SharedFontList());
+  auto out = static_cast<mozilla::ipc::SharedMemoryBasic::Handle*>(aOut);
+  if (!aGeneration || SharedFontList()->GetGeneration() == aGeneration) {
+    SharedFontList()->ShareShmBlockToProcess(aIndex, aPid, out);
+  } else {
+    *out = mozilla::ipc::SharedMemoryBasic::NULLHandle();
+  }
+}
+
+void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
+                                           uint32_t aFamilyIndex) {
+  MOZ_ASSERT(SharedFontList());
+  if (SharedFontList()->GetGeneration() != aGeneration) {
+    return;
+  }
+  if (aFamilyIndex >= SharedFontList()->NumFamilies()) {
+    return;
+  }
+  Unused << InitializeFamily(SharedFontList()->Families() + aFamilyIndex);
+}
+
+void gfxPlatformFontList::SetCharacterMap(uint32_t aGeneration,
+                                          const fontlist::Pointer& aFacePtr,
+                                          const gfxSparseBitSet& aMap) {
+  MOZ_ASSERT(SharedFontList());
+  if (SharedFontList()->GetGeneration() != aGeneration) {
+    return;
+  }
+  fontlist::Face* face =
+      static_cast<fontlist::Face*>(aFacePtr.ToPtr(SharedFontList()));
+  face->SetCharacterMap(SharedFontList(), &aMap);
+}
+
+void gfxPlatformFontList::InitOtherFamilyNames(uint32_t aGeneration,
+                                               bool aDefer) {
+  if (SharedFontList()->GetGeneration() != aGeneration) {
+    return;
+  }
+  InitOtherFamilyNames(aDefer);
+}
+
 #undef LOG
 #undef LOG_ENABLED
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -11,16 +11,17 @@
 #include "nsTHashtable.h"
 
 #include "gfxFontUtils.h"
 #include "gfxFontInfoLoader.h"
 #include "gfxFont.h"
 #include "gfxFontConstants.h"
 #include "gfxPlatform.h"
 #include "gfxFontFamilyList.h"
+#include "SharedFontList.h"
 
 #include "nsIMemoryReporter.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/RangedArray.h"
 #include "nsLanguageAtomService.h"
@@ -168,28 +169,64 @@ class gfxPlatformFontList : public gfxFo
                                   nsTArray<FamilyAndGeneric>* aOutput,
                                   FindFamiliesFlags aFlags,
                                   gfxFontStyle* aStyle = nullptr,
                                   gfxFloat aDevToCssSize = 1.0);
 
   gfxFontEntry* FindFontForFamily(const nsACString& aFamily,
                                   const gfxFontStyle* aStyle);
 
+  mozilla::fontlist::FontList* SharedFontList() const {
+    return mSharedFontList.get();
+  }
+
+  // The aPid and aOut parameters are declared here with generic types
+  // because (on Windows) we cannot include the proper headers, as they
+  // result in build failure due to (indirect) inclusion of windows.h
+  // in generated bindings code.
+  void ShareFontListShmBlockToProcess(
+      uint32_t aGeneration, uint32_t aIndex, /*base::ProcessId*/ uint32_t aPid,
+      /*mozilla::ipc::SharedMemoryBasic::Handle*/ void* aOut);
+
+  void SetCharacterMap(uint32_t aGeneration,
+                       const mozilla::fontlist::Pointer& aFacePtr,
+                       const gfxSparseBitSet& aMap);
+
+  MOZ_MUST_USE bool InitializeFamily(mozilla::fontlist::Family* aFamily) {
+    // To be implemented in patch 2 of the SharedFontList series.
+    return false;
+  }
+  void InitializeFamily(uint32_t aGeneration, uint32_t aFamilyIndex);
+
   // name lookup table methods
 
   void AddOtherFamilyName(gfxFontFamily* aFamilyEntry,
                           nsCString& aOtherFamilyName);
 
   void AddFullname(gfxFontEntry* aFontEntry, const nsCString& aFullname);
 
   void AddPostscriptName(gfxFontEntry* aFontEntry,
                          const nsCString& aPostscriptName);
 
   bool NeedFullnamePostscriptNames() { return mExtraNames != nullptr; }
 
+  /**
+   * Read PSName and FullName of the given face, for src:local lookup,
+   * returning true if actually implemented and succeeded.
+   */
+  virtual bool ReadFaceNames(mozilla::fontlist::Family* aFamily,
+                             mozilla::fontlist::Face* aFace, nsCString& aPSName,
+                             nsCString& aFullName) {
+    return false;
+  }
+
+  // initialize localized family names
+  void InitOtherFamilyNames(bool aDeferOtherFamilyNamesLoading);
+  void InitOtherFamilyNames(uint32_t aGeneration, bool aDefer);
+
   // pure virtual functions, to be provided by concrete subclasses
 
   // get the system default font family
   FontFamily GetDefaultFont(const gfxFontStyle* aStyle);
 
   /**
    * Look up a font by name on the host platform.
    *
@@ -231,16 +268,21 @@ class gfxPlatformFontList : public gfxFo
   gfxFontFamily* GetDefaultFontFamily(const nsACString& aLangGroup,
                                       const nsACString& aGenericFamily);
 
   virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                       FontListSizes* aSizes) const;
   virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                       FontListSizes* aSizes) const;
 
+  mozilla::fontlist::Pointer GetShmemCharMap(const gfxSparseBitSet* aCharMap) {
+    // To be implemented in patch 2 of the SharedFontList series.
+    return mozilla::fontlist::Pointer::Null();
+  }
+
   // search for existing cmap that matches the input
   // return the input if no match is found
   gfxCharacterMap* FindCharMap(gfxCharacterMap* aCmap);
 
   // add a cmap to the shared cmap set
   gfxCharacterMap* AddCmap(const gfxCharacterMap* aCharMap);
 
   // remove the cmap from the shared cmap set
@@ -261,16 +303,25 @@ class gfxPlatformFontList : public gfxFo
     aNumInits = mFontlistInitCount;
     aLoaderState = (uint32_t)mState;
   }
 
   virtual void AddGenericFonts(mozilla::StyleGenericFontFamily aGenericType,
                                nsAtom* aLanguage,
                                nsTArray<FamilyAndGeneric>& aFamilyList);
 
+  /**
+   * Given a Face from the shared font list, return a gfxFontEntry usable
+   * by the current process. This returns a cached entry if available,
+   * otherwise it calls the (platform-specific) CreateFontEntry method to
+   * make one, and adds it to the cache.
+   */
+  gfxFontEntry* GetOrCreateFontEntry(mozilla::fontlist::Face* aFace,
+                                     const mozilla::fontlist::Family* aFamily);
+
   PrefFontList* GetPrefFontsLangGroup(
       mozilla::StyleGenericFontFamily aGenericType, eFontPrefLang aPrefLang);
 
   // in some situations, need to make decisions about ambiguous characters, may
   // need to look at multiple pref langs
   void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t& aLen,
                     eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
@@ -444,17 +495,16 @@ class gfxPlatformFontList : public gfxFo
 
   void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t& aLen,
                           eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
   // verifies that a family contains a non-zero font count
   gfxFontFamily* CheckFamily(gfxFontFamily* aFamily);
 
   // initialize localized family names
-  void InitOtherFamilyNames(bool aDeferOtherFamilyNamesLoading);
   void InitOtherFamilyNamesInternal(bool aDeferOtherFamilyNamesLoading);
   void CancelInitOtherFamilyNamesTask();
 
   // search through font families, looking for a given name, initializing
   // facename lists along the way. first checks all families with names
   // close to face name, then searchs all families if not found.
   gfxFontEntry* SearchFamiliesForFaceName(const nsACString& aFaceName);
 
@@ -498,16 +548,22 @@ class gfxPlatformFontList : public gfxFo
   void ResolveEmojiFontNames(PrefFontList* aGenericFamilies);
 
   void GetFontFamiliesFromGenericFamilies(nsTArray<nsCString>& aGenericFamilies,
                                           nsAtom* aLangGroup,
                                           PrefFontList* aFontFamilies);
 
   virtual nsresult InitFontListForPlatform() = 0;
 
+  virtual gfxFontEntry* CreateFontEntry(
+      mozilla::fontlist::Face* aFace,
+      const mozilla::fontlist::Family* aFamily) {
+    return nullptr;
+  }
+
   void ApplyWhitelist();
 
   // Create a new gfxFontFamily of the appropriate subclass for the platform,
   // used when AddWithLegacyFamilyName needs to create a new family.
   virtual gfxFontFamily* CreateFontFamily(const nsACString& aName) const = 0;
 
   typedef nsRefPtrHashtable<nsCStringHashKey, gfxFontFamily> FontFamilyTable;
   typedef nsRefPtrHashtable<nsCStringHashKey, gfxFontEntry> FontEntryTable;
@@ -592,14 +648,19 @@ class gfxPlatformFontList : public gfxFo
 
   nsTHashtable<nsPtrHashKey<gfxUserFontSet>> mUserFontSetList;
 
   nsLanguageAtomService* mLangService;
 
   nsTArray<uint32_t> mCJKPrefLangs;
   nsTArray<mozilla::StyleGenericFontFamily> mDefaultGenericsLangGroup;
 
+  mozilla::UniquePtr<mozilla::fontlist::FontList> mSharedFontList;
+
+  nsRefPtrHashtable<nsPtrHashKey<mozilla::fontlist::Face>, gfxFontEntry>
+      mFontEntries;
+
   bool mFontFamilyWhitelistActive;
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxPlatformFontList::FindFamiliesFlags)
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -48,16 +48,17 @@ EXPORTS += [
     'gfxRect.h',
     'gfxSharedImageSurface.h',
     'gfxSkipChars.h',
     'gfxSVGGlyphs.h',
     'gfxTextRun.h',
     'gfxTypes.h',
     'gfxUserFontSet.h',
     'gfxUtils.h',
+    'SharedFontList.h',
     'SoftwareVsyncSource.h',
     'VsyncSource.h',
 ]
 
 EXPORTS.mozilla.gfx += [
     'D3D11Checks.h',
     'DeviceManagerDx.h',
     'PrintTarget.h',
@@ -206,16 +207,17 @@ UNIFIED_SOURCES += [
     'gfxPattern.cpp',
     'gfxPlatformFontList.cpp',
     'gfxScriptItemizer.cpp',
     'gfxSkipChars.cpp',
     'gfxSVGGlyphs.cpp',
     'gfxTextRun.cpp',
     'gfxUserFontSet.cpp',
     'gfxUtils.cpp',
+    'SharedFontList.cpp',
     'SoftwareVsyncSource.cpp',
     'VsyncSource.cpp',
 ]
 
 if CONFIG['MOZ_ENABLE_SKIA']:
     UNIFIED_SOURCES += [
         'SkMemoryReporter.cpp',
     ]
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -879,16 +879,24 @@ description =
 [PContent::EndDriverCrashGuard]
 description =
 [PContent::KeygenProcessValue]
 description =
 [PContent::KeygenProvideContent]
 description =
 [PContent::GetGraphicsDeviceInitData]
 description =
+[PContent::GetFontListShmBlock]
+description = for bug 1514869 - layout code needs synchronous access to font list, but this is used only once per block, after which content directly reads the shared memory
+[PContent::InitializeFamily]
+description = for bug 1514869 - layout is blocked on needing sync access to a specific font family - used once per family, then the data is cached in shared memory
+[PContent::InitOtherFamilyNames]
+description = for bug 1514869 - layout is blocked on font lookup, needs complete family-name information - not used after loading is complete
+[PContent::SetCharacterMap]
+description = for bug 1514869 - changed to async later in patch series
 [PContent::UngrabPointer]
 description =
 [PContent::RemovePermission]
 description =
 [PContent::GetA11yContentId]
 description =
 [PGMP::StartPlugin]
 description =