diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/thebes/SharedFontList-impl.h | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/gfx/thebes/SharedFontList-impl.h b/gfx/thebes/SharedFontList-impl.h new file mode 100644 index 0000000000..e39e4dd47d --- /dev/null +++ b/gfx/thebes/SharedFontList-impl.h @@ -0,0 +1,386 @@ +/* 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 "base/shared_memory.h" + +#include "gfxFontUtils.h" +#include "nsClassHashtable.h" +#include "nsTHashMap.h" +#include "nsXULAppAPI.h" +#include "mozilla/UniquePtr.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 { + +/** + * Data used to initialize a font family alias (a "virtual" family that refers + * to some or all of the faces of another family, used when alternate family + * names are found in the font resource for localization or for styled + * subfamilies). AliasData records are collected incrementally while scanning + * the fonts, and then used to set up the Aliases list in the shared font list. + */ +struct AliasData { + nsTArray<Pointer> mFaces; + nsCString mBaseFamily; + uint32_t mIndex = 0; + FontVisibility mVisibility = FontVisibility::Unknown; + bool mBundled = false; + bool mBadUnderline = false; + bool mForceClassic = false; + + void InitFromFamily(const Family* aFamily, const nsCString& aBaseFamily) { + mBaseFamily = aBaseFamily; + mIndex = aFamily->Index(); + mVisibility = aFamily->Visibility(); + mBundled = aFamily->IsBundled(); + mBadUnderline = aFamily->IsBadUnderlineFamily(); + mForceClassic = aFamily->IsForceClassic(); + } +}; + +/** + * 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. + * + * The passed-in array may be modified (to eliminate duplicates of bundled + * fonts, or restrict the available list to a specified subset), so if the + * caller intends to make further use of it this should be kept in mind. + * + * 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(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, AliasData>& 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( + nsTHashMap<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, bool aPrimaryNameOnly = false); + + /** + * 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); + + /** + * Return the localized name for the given family in the current system + * locale (if multiple localizations are available). + */ + nsCString LocalizedFamilyName(const Family* aFamily); + + bool Initialized() { return mBlocks.Length() > 0 && NumFamilies() > 0; } + + uint32_t NumFamilies() { return GetHeader().mFamilyCount; } + Family* Families() { + return GetHeader().mFamilies.ToArray<Family>(this, NumFamilies()); + } + + uint32_t NumAliases() { return GetHeader().mAliasCount; } + Family* AliasFamilies() { + return GetHeader().mAliases.ToArray<Family>(this, NumAliases()); + } + + uint32_t NumLocalFaces() { return GetHeader().mLocalFaceCount; } + LocalFaceRec* LocalFaces() { + return GetHeader().mLocalFaces.ToArray<LocalFaceRec>(this, NumLocalFaces()); + } + + /** + * 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 fields present in every shared-memory block. The mBlockSize field + * is not modified after initial block creation (before the block has been + * shared to any other process), so does not need to be std::atomic<>. + * The mAllocated field is checked during Pointer::ToPtr(), so we make that + * atomic to avoid data races. + */ + struct BlockHeader { + std::atomic<uint32_t> mAllocated; // Space allocated from this block. + uint32_t mBlockSize; // Total size of this block. + }; + + /** + * Header info that is stored at the beginning of the first shared-memory + * block for the font list. + * (Subsequent blocks have only the mBlockHeader.) + * 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 { + BlockHeader mBlockHeader; + 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. This is used when a child process requests + * an additional block that was not already passed to it (because the + * list has changed/grown since the child was first initialized). + */ + void ShareShmBlockToProcess(uint32_t aIndex, base::ProcessId aPid, + base::SharedMemoryHandle* aOut) { + MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); + if (aIndex >= mReadOnlyShmems.Length()) { + // Block index out of range + *aOut = base::SharedMemory::NULLHandle(); + return; + } + *aOut = mReadOnlyShmems[aIndex]->CloneHandle(); + if (!*aOut) { + MOZ_CRASH("failed to share block"); + } + } + + /** + * Collect an array of handles to all the shmem blocks, ready to be + * shared to the given process. This is used at child process startup + * to pass the complete list at once. + */ + void ShareBlocksToProcess(nsTArray<base::SharedMemoryHandle>* aBlocks, + base::ProcessId aPid); + + base::SharedMemoryHandle ShareBlockToProcess(uint32_t aIndex, + base::ProcessId aPid); + + void ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, + base::SharedMemoryHandle aHandle); + /** + * Support for memory reporter. + */ + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + size_t AllocatedShmemSize() const; + + /** + * Using a larger block size will speed up allocation, at the cost of more + * wasted space in the shared memory (on average). + */ +#if ANDROID + // Android devices usually have a much smaller number of fonts than desktop + // systems, and memory is more constrained, so use a smaller default block + // size. + static constexpr uint32_t SHM_BLOCK_SIZE = 64 * 1024; +#elif XP_LINUX + // On Linux, font face descriptors are rather large (serialized FcPatterns), + // so use a larger block size for efficiency. + static constexpr uint32_t SHM_BLOCK_SIZE = 1024 * 1024; +#else + // Default block size for Windows and macOS. + static constexpr uint32_t SHM_BLOCK_SIZE = 256 * 1024; +#endif + static_assert(SHM_BLOCK_SIZE <= (1 << Pointer::kBlockShift), + "SHM_BLOCK_SIZE too large"); + + private: + struct ShmBlock { + // Takes ownership of aShmem. Note that in a child process, aShmem will be + // mapped as read-only. + explicit ShmBlock(mozilla::UniquePtr<base::SharedMemory>&& aShmem) + : mShmem(std::move(aShmem)) {} + + // Get pointer to the mapped memory. + void* Memory() const { return mShmem->memory(); } + + // Only the parent process does allocation, so only it will update this + // field. Content processes read the value when checking Pointer validity. + uint32_t Allocated() const { + return static_cast<BlockHeader*>(Memory())->mAllocated; + } + + void StoreAllocated(uint32_t aSize) { + MOZ_ASSERT(XRE_IsParentProcess()); + static_cast<BlockHeader*>(Memory())->mAllocated.store(aSize); + } + + // This is stored by the parent process during block creation and never + // changes, so does not need to be atomic. + // Note that some blocks may be larger than SHM_BLOCK_SIZE, if needed for + // individual large allocations. + uint32_t& BlockSize() const { + MOZ_ASSERT(XRE_IsParentProcess()); + return static_cast<BlockHeader*>(Memory())->mBlockSize; + } + + mozilla::UniquePtr<base::SharedMemory> mShmem; + }; + + Header& GetHeader() const; + + /** + * Create a new shared memory block and append to the FontList's list + * of blocks. + * + * Only used in the parent process. + */ + bool AppendShmBlock(uint32_t aSizeNeeded); + + /** + * Used by child processes to ensure all the blocks are registered. + * Returns false on failure. + * Pass aMustLock=true to take the gfxPlatformFontList lock during the + * update (not required when calling from the constructor). + */ + [[nodiscard]] bool UpdateShmBlocks(bool aMustLock); + + /** + * 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; + + /** + * Auxiliary array, used only in the parent process; holds read-only copies + * of the shmem blocks; these are what will be shared to child processes. + */ + nsTArray<mozilla::UniquePtr<base::SharedMemory>> mReadOnlyShmems; +}; + +} // namespace fontlist +} // namespace mozilla + +#endif /* SharedFontList_impl_h */ |