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