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