1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #ifndef SharedFontList_impl_h
6 #define SharedFontList_impl_h
7 
8 #include "SharedFontList.h"
9 
10 #include "base/shared_memory.h"
11 
12 #include "gfxFontUtils.h"
13 #include "nsClassHashtable.h"
14 #include "nsTHashMap.h"
15 #include "nsXULAppAPI.h"
16 #include "mozilla/UniquePtr.h"
17 
18 // This is split out from SharedFontList.h because that header is included
19 // quite widely (via gfxPlatformFontList.h, gfxTextRun.h, etc), and other code
20 // such as the generated DOM bindings code gets upset at (indirect) inclusion
21 // of <windows.h> via SharedMemoryBasic.h. So this header, which defines the
22 // actual shared-memory FontList class, is included only by the .cpp files that
23 // implement or directly interface with the font list, to avoid polluting other
24 // headers.
25 
26 namespace mozilla {
27 namespace fontlist {
28 
29 /**
30  * Data used to initialize a font family alias (a "virtual" family that refers
31  * to some or all of the faces of another family, used when alternate family
32  * names are found in the font resource for localization or for styled
33  * subfamilies). AliasData records are collected incrementally while scanning
34  * the fonts, and then used to set up the Aliases list in the shared font list.
35  */
36 struct AliasData {
37   nsTArray<Pointer> mFaces;
38   nsCString mBaseFamily;
39   uint32_t mIndex = 0;
40   FontVisibility mVisibility = FontVisibility::Unknown;
41   bool mBundled = false;
42   bool mBadUnderline = false;
43   bool mForceClassic = false;
44 
InitFromFamilyAliasData45   void InitFromFamily(const Family* aFamily, const nsCString& aBaseFamily) {
46     mBaseFamily = aBaseFamily;
47     mIndex = aFamily->Index();
48     mVisibility = aFamily->Visibility();
49     mBundled = aFamily->IsBundled();
50     mBadUnderline = aFamily->IsBadUnderlineFamily();
51     mForceClassic = aFamily->IsForceClassic();
52   }
53 };
54 
55 /**
56  * The Shared Font List is a collection of data that lives in shared memory
57  * so that all processes can use it, rather than maintaining their own copies,
58  * and provides the metadata needed for CSS font-matching (a list of all the
59  * available font families and their faces, style properties, etc, as well as
60  * character coverage).
61  *
62  * An important assumption is that all processes see the same collection of
63  * installed fonts; therefore it is valid for them all to share the same set
64  * of font metadata. The data is updated only by the parent process; content
65  * processes have read-only access to it.
66  *
67  * The total size of this data varies greatly depending on the user's installed
68  * fonts; and it is not known at startup because we load a lot of the font data
69  * on first use rather than preloading during initialization (because that's
70  * too expensive/slow).
71  *
72  * Therefore, the shared memory area needs to be able to grow during the
73  * session; we can't predict how much space will be needed, and we can't afford
74  * to pre-allocate such a huge block that it could never overflow. To handle
75  * this, we maintain a (generally short) array of blocks of shared memory,
76  * and then allocate our Family, Face, etc. objects within these. Because we
77  * only ever add data (never delete what we've already stored), we can use a
78  * simplified allocator that doesn't ever need to free blocks; the only time
79  * the memory is released during a session is the (rare) case where a font is
80  * installed or deleted while the browser is running, and in this case we just
81  * delete the entire shared font list and start afresh.
82  */
83 class FontList {
84  public:
85   friend struct Pointer;
86 
87   explicit FontList(uint32_t aGeneration);
88   ~FontList();
89 
90   /**
91    * Initialize the master list of installed font families. This must be
92    * set during font-list creation, before the list is shared with any
93    * content processes. All installed font families known to the browser
94    * appear in this list, although some may be marked as "hidden" so that
95    * they are not exposed to the font-family property.
96    *
97    * The passed-in array may be modified (to eliminate duplicates of bundled
98    * fonts, or restrict the available list to a specified subset), so if the
99    * caller intends to make further use of it this should be kept in mind.
100    *
101    * Once initialized, the master family list is immutable; in the (rare)
102    * event that the system's collection of installed fonts changes, we discard
103    * the FontList and create a new one.
104    *
105    * In some cases, a font family may be known by multiple names (e.g.
106    * localizations in multiple languages, or there may be legacy family names
107    * that correspond to specific styled faces like "Arial Black"). Such names
108    * do not appear in this master list, but are referred to as aliases (see
109    * SetAliases below); the alias list need not be populated before the font
110    * list is shared to content processes and used.
111    *
112    * Only used in the parent process.
113    */
114   void SetFamilyNames(nsTArray<Family::InitData>& aFamilies);
115 
116   /**
117    * Aliases are Family records whose Face entries are already part of another
118    * family (either because the family has multiple localized names, or because
119    * the alias family is a legacy name like "Arial Narrow" that is a subset of
120    * the faces in the main "Arial" family). The table of aliases is initialized
121    * from a hash of alias family name -> array of Face records.
122    *
123    * Like the master family list, the list of family aliases is immutable once
124    * initialized.
125    *
126    * Only used in the parent process.
127    */
128   void SetAliases(nsClassHashtable<nsCStringHashKey, AliasData>& aAliasTable);
129 
130   /**
131    * Local names are PostScript or Full font names of individual faces, used
132    * to look up faces for @font-face { src: local(...) } rules. Some platforms
133    * (e.g. macOS) can look up local names directly using platform font APIs,
134    * in which case the local names table here is unused.
135    *
136    * The list of local names is immutable once initialized. Local font name
137    * lookups may occur before this list has been set up, in which case they
138    * will use the SearchForLocalFace method.
139    *
140    * Only used in the parent process.
141    */
142   void SetLocalNames(
143       nsTHashMap<nsCStringHashKey, LocalFaceRec::InitData>& aLocalNameTable);
144 
145   /**
146    * Look up a Family record by name, typically to satisfy the font-family
147    * property or a font family listed in preferences.
148    */
149   Family* FindFamily(const nsCString& aName, bool aPrimaryNameOnly = false);
150 
151   /**
152    * Look up an individual Face by PostScript or Full name, for @font-face
153    * rules using src:local(...). This requires the local names list to have
154    * been initialized.
155    */
156   LocalFaceRec* FindLocalFace(const nsCString& aName);
157 
158   /**
159    * Search families for a face with local name aName; should only be used if
160    * the mLocalFaces array has not yet been set up, as this will be a more
161    * expensive search than FindLocalFace.
162    */
163   void SearchForLocalFace(const nsACString& aName, Family** aFamily,
164                           Face** aFace);
165 
166   /**
167    * Return the localized name for the given family in the current system
168    * locale (if multiple localizations are available).
169    */
170   nsCString LocalizedFamilyName(const Family* aFamily);
171 
Initialized()172   bool Initialized() { return mBlocks.Length() > 0 && NumFamilies() > 0; }
173 
NumFamilies()174   uint32_t NumFamilies() { return GetHeader().mFamilyCount; }
Families()175   Family* Families() {
176     return static_cast<Family*>(GetHeader().mFamilies.ToPtr(this));
177   }
178 
NumAliases()179   uint32_t NumAliases() { return GetHeader().mAliasCount; }
AliasFamilies()180   Family* AliasFamilies() {
181     return static_cast<Family*>(GetHeader().mAliases.ToPtr(this));
182   }
183 
NumLocalFaces()184   uint32_t NumLocalFaces() { return GetHeader().mLocalFaceCount; }
LocalFaces()185   LocalFaceRec* LocalFaces() {
186     return static_cast<LocalFaceRec*>(GetHeader().mLocalFaces.ToPtr(this));
187   }
188 
189   /**
190    * Ask the font list to initialize the character map for a given face.
191    */
192   void LoadCharMapFor(Face& aFace, const Family* aFamily);
193 
194   /**
195    * Allocate shared-memory space for a record of aSize bytes. The returned
196    * pointer will be 32-bit aligned. (This method may trigger the allocation of
197    * a new shared memory block, if required.)
198    *
199    * Only used in the parent process.
200    */
201   Pointer Alloc(uint32_t aSize);
202 
203   /**
204    * Convert a native pointer to a shared-memory Pointer record that can be
205    * passed between processes.
206    */
207   Pointer ToSharedPointer(const void* aPtr);
208 
GetGeneration()209   uint32_t GetGeneration() { return GetHeader().mGeneration; }
210 
211   /**
212    * Header fields present in every shared-memory block. The mBlockSize field
213    * is not modified after initial block creation (before the block has been
214    * shared to any other process), and the mAllocated field is used only by
215    * the parent process, so neither of these needs to be std::atomic<>.
216    */
217   struct BlockHeader {
218     uint32_t mAllocated;  // Space allocated from this block.
219     uint32_t mBlockSize;  // Total size of this block.
220   };
221 
222   /**
223    * Header info that is stored at the beginning of the first shared-memory
224    * block for the font list.
225    * (Subsequent blocks have only the mBlockHeader.)
226    * The mGeneration and mFamilyCount fields are set by the parent process
227    * during font-list construction, before the list has been shared with any
228    * other process, and subsequently never change; therefore, we don't need
229    * to use std::atomic<> for these.
230    */
231   struct Header {
232     BlockHeader mBlockHeader;
233     uint32_t mGeneration;               // Font-list generation ID
234     uint32_t mFamilyCount;              // Number of font families in the list
235     std::atomic<uint32_t> mBlockCount;  // Total number of blocks that exist
236     std::atomic<uint32_t> mAliasCount;  // Number of family aliases
237     std::atomic<uint32_t> mLocalFaceCount;  // Number of local face names
238     Pointer mFamilies;    // Pointer to array of |mFamilyCount| families
239     Pointer mAliases;     // Pointer to array of |mAliasCount| aliases
240     Pointer mLocalFaces;  // Pointer to array of |mLocalFaceCount| face records
241   };
242 
243   /**
244    * Used by the parent process to pass a handle to a shared block to a
245    * specific child process. This is used when a child process requests
246    * an additional block that was not already passed to it (because the
247    * list has changed/grown since the child was first initialized).
248    */
ShareShmBlockToProcess(uint32_t aIndex,base::ProcessId aPid,base::SharedMemoryHandle * aOut)249   void ShareShmBlockToProcess(uint32_t aIndex, base::ProcessId aPid,
250                               base::SharedMemoryHandle* aOut) {
251     MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
252     if (aIndex >= mReadOnlyShmems.Length()) {
253       // Block index out of range
254       *aOut = base::SharedMemory::NULLHandle();
255     }
256     if (!mReadOnlyShmems[aIndex]->ShareToProcess(aPid, aOut)) {
257       MOZ_CRASH("failed to share block");
258     }
259   }
260 
261   /**
262    * Collect an array of handles to all the shmem blocks, ready to be
263    * shared to the given process. This is used at child process startup
264    * to pass the complete list at once.
265    */
266   void ShareBlocksToProcess(nsTArray<base::SharedMemoryHandle>* aBlocks,
267                             base::ProcessId aPid);
268 
269   base::SharedMemoryHandle ShareBlockToProcess(uint32_t aIndex,
270                                                base::ProcessId aPid);
271 
272   void ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
273                      base::SharedMemoryHandle aHandle);
274   /**
275    * Support for memory reporter.
276    */
277   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
278   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
279   size_t AllocatedShmemSize() const;
280 
281   /**
282    * Using a larger block size will speed up allocation, at the cost of more
283    * wasted space in the shared memory (on average).
284    */
285 #if ANDROID
286   // Android devices usually have a much smaller number of fonts than desktop
287   // systems, and memory is more constrained, so use a smaller default block
288   // size.
289   static constexpr uint32_t SHM_BLOCK_SIZE = 64 * 1024;
290 #elif XP_LINUX
291   // On Linux, font face descriptors are rather large (serialized FcPatterns),
292   // so use a larger block size for efficiency.
293   static constexpr uint32_t SHM_BLOCK_SIZE = 1024 * 1024;
294 #else
295   // Default block size for Windows and macOS.
296   static constexpr uint32_t SHM_BLOCK_SIZE = 256 * 1024;
297 #endif
298   static_assert(SHM_BLOCK_SIZE <= (1 << Pointer::kBlockShift),
299                 "SHM_BLOCK_SIZE too large");
300 
301  private:
302   struct ShmBlock {
303     // Takes ownership of aShmem. Note that in a child process, aShmem will be
304     // mapped as read-only.
ShmBlockShmBlock305     explicit ShmBlock(mozilla::UniquePtr<base::SharedMemory>&& aShmem)
306         : mShmem(std::move(aShmem)) {}
307 
308     // Get pointer to the mapped memory.
MemoryShmBlock309     void* Memory() const { return mShmem->memory(); }
310 
311     // Because only the parent process does allocation, this doesn't need to
312     // be an atomic.
313     // Further allocations may happen within the block after it has been shared
314     // to content processes, but as their access is read-only and they cannot
315     // do allocations themselves, they never look at this field; only the parent
316     // reads or updates it.
AllocatedShmBlock317     uint32_t& Allocated() const {
318       MOZ_ASSERT(XRE_IsParentProcess());
319       return static_cast<BlockHeader*>(Memory())->mAllocated;
320     }
321 
322     // This is stored by the parent process during block creation and never
323     // changes, so does not need to be atomic.
324     // Note that some blocks may be larger than SHM_BLOCK_SIZE, if needed for
325     // individual large allocations.
BlockSizeShmBlock326     uint32_t& BlockSize() const {
327       MOZ_ASSERT(XRE_IsParentProcess());
328       return static_cast<BlockHeader*>(Memory())->mBlockSize;
329     }
330 
331     mozilla::UniquePtr<base::SharedMemory> mShmem;
332   };
333 
GetHeader()334   Header& GetHeader() {
335     // It's invalid to try and access this before the first block exists.
336     MOZ_ASSERT(mBlocks.Length() > 0);
337     return *static_cast<Header*>(Pointer(0, 0).ToPtr(this));
338   }
339 
340   /**
341    * Create a new shared memory block and append to the FontList's list
342    * of blocks.
343    *
344    * Only used in the parent process.
345    */
346   bool AppendShmBlock(uint32_t aSizeNeeded);
347 
348   /**
349    * Used by child processes to ensure all the blocks are registered.
350    * Returns false on failure.
351    */
352   [[nodiscard]] bool UpdateShmBlocks();
353 
354   /**
355    * This makes a *sync* IPC call to get a shared block from the parent.
356    * As such, it may block for a while if the parent is busy; fortunately,
357    * we'll generally only call this a handful of times in the course of an
358    * entire session. If the requested block does not yet exist (because the
359    * child is wanting to allocate an object, and there wasn't room in any
360    * existing block), the parent will create a new shared block and return it.
361    * This may (in rare cases) return null, if the parent has recreated the
362    * font list and we actually need to reinitialize.
363    */
364   ShmBlock* GetBlockFromParent(uint32_t aIndex);
365 
366   void DetachShmBlocks();
367 
368   /**
369    * Array of pointers to the shared-memory block records.
370    * NOTE: if mBlocks.Length() < GetHeader().mBlockCount, then the parent has
371    * added a block (or blocks) to the list, and we need to update!
372    */
373   nsTArray<mozilla::UniquePtr<ShmBlock>> mBlocks;
374 
375   /**
376    * Auxiliary array, used only in the parent process; holds read-only copies
377    * of the shmem blocks; these are what will be shared to child processes.
378    */
379   nsTArray<mozilla::UniquePtr<base::SharedMemory>> mReadOnlyShmems;
380 };
381 
382 }  // namespace fontlist
383 }  // namespace mozilla
384 
385 #endif /* SharedFontList_impl_h */
386