gfxMacPlatformFontList.mm (14240B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "mozilla/Components.h" 7 #include "mozilla/Logging.h" 8 9 #include <algorithm> 10 11 #import <AppKit/AppKit.h> 12 13 #include "gfxFontConstants.h" 14 #include "gfxPlatformMac.h" 15 #include "gfxMacPlatformFontList.h" 16 #include "gfxMacFont.h" 17 #include "gfxUserFontSet.h" 18 #include "SharedFontList-impl.h" 19 20 #include "harfbuzz/hb.h" 21 22 #include "AppleUtils.h" 23 #include "MainThreadUtils.h" 24 #include "nsDirectoryServiceUtils.h" 25 #include "nsDirectoryServiceDefs.h" 26 #include "nsAppDirectoryServiceDefs.h" 27 #include "nsIDirectoryEnumerator.h" 28 #include "nsCharTraits.h" 29 #include "nsCocoaUtils.h" 30 #include "nsComponentManagerUtils.h" 31 #include "nsServiceManagerUtils.h" 32 #include "nsTArray.h" 33 34 #include "mozilla/dom/ContentChild.h" 35 #include "mozilla/dom/ContentParent.h" 36 #include "mozilla/FontPropertyTypes.h" 37 #include "mozilla/Preferences.h" 38 #include "mozilla/ProfilerLabels.h" 39 #include "mozilla/Sprintf.h" 40 #include "mozilla/StaticPrefs_gfx.h" 41 #include "mozilla/Telemetry.h" 42 #include "mozilla/gfx/2D.h" 43 44 #include <unistd.h> 45 #include <time.h> 46 #include <dlfcn.h> 47 48 #define StandardFonts 49 #include "StandardFonts-macos-bb.inc" 50 #undef StandardFonts 51 52 using namespace mozilla; 53 using namespace mozilla::gfx; 54 55 // cache Cocoa's "shared font manager" for performance 56 static NSFontManager* sFontManager; 57 58 static void GetStringForNSString(const NSString* aSrc, nsAString& aDest) { 59 aDest.SetLength(aSrc.length); 60 [aSrc getCharacters:reinterpret_cast<unichar*>(aDest.BeginWriting()) 61 range:NSMakeRange(0, aSrc.length)]; 62 } 63 64 static NSString* GetNSStringForString(const nsAString& aSrc) { 65 return [NSString 66 stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading()) 67 length:aSrc.Length()]; 68 } 69 70 #define LOG_FONTLIST(args) \ 71 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args) 72 #define LOG_FONTLIST_ENABLED() \ 73 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug) 74 #define LOG_CMAPDATA_ENABLED() \ 75 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug) 76 77 /* gfxSingleFaceMacFontFamily */ 78 79 class gfxSingleFaceMacFontFamily final : public gfxFontFamily { 80 public: 81 gfxSingleFaceMacFontFamily(const nsACString& aName, 82 FontVisibility aVisibility) 83 : gfxFontFamily(aName, aVisibility) { 84 mFaceNamesInitialized = true; // omit from face name lists 85 } 86 87 virtual ~gfxSingleFaceMacFontFamily() = default; 88 89 void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr) 90 MOZ_REQUIRES(mLock) override {}; 91 92 void LocalizedName(nsACString& aLocalizedName) override; 93 94 void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) override; 95 96 bool IsSingleFaceFamily() const override { return true; } 97 }; 98 99 void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) { 100 nsAutoreleasePool localPool; 101 102 AutoReadLock lock(mLock); 103 104 if (!HasOtherFamilyNames()) { 105 aLocalizedName = mName; 106 return; 107 } 108 109 gfxFontEntry* fe = mAvailableFonts[0]; 110 NSFont* font = [NSFont 111 fontWithName:GetNSStringForString(NS_ConvertUTF8toUTF16(fe->Name())) 112 size:0.0]; 113 if (font) { 114 NSString* localized = [font displayName]; 115 if (localized) { 116 nsAutoString locName; 117 GetStringForNSString(localized, locName); 118 CopyUTF16toUTF8(locName, aLocalizedName); 119 return; 120 } 121 } 122 123 // failed to get localized name, just use the canonical one 124 aLocalizedName = mName; 125 } 126 127 void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames( 128 gfxPlatformFontList* aPlatformFontList) { 129 AutoWriteLock lock(mLock); 130 if (mOtherFamilyNamesInitialized) { 131 return; 132 } 133 134 gfxFontEntry* fe = mAvailableFonts[0]; 135 if (!fe) { 136 return; 137 } 138 139 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e'); 140 141 gfxFontEntry::AutoTable nameTable(fe, kNAME); 142 if (!nameTable) { 143 return; 144 } 145 146 mHasOtherFamilyNames = 147 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true); 148 149 mOtherFamilyNamesInitialized = true; 150 } 151 152 /* gfxMacPlatformFontList */ 153 #pragma mark - 154 155 gfxMacPlatformFontList::gfxMacPlatformFontList() : CoreTextFontList() { 156 CheckFamilyList(kBaseFonts); 157 #ifndef BASE_BROWSER_VERSION 158 CheckFamilyList(kBaseFonts_13_Higher); 159 #endif 160 161 // cache this in a static variable so that gfxMacFontFamily objects 162 // don't have to repeatedly look it up 163 sFontManager = [NSFontManager sharedFontManager]; 164 165 // Load the font-list preferences now, so that we don't have to do it from 166 // Init[Shared]FontListForPlatform, which may be called off-main-thread. 167 gfxFontUtils::GetPrefsFontList("font.single-face-list", mSingleFaceFonts); 168 } 169 170 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination; 171 Device GetFontVisibilityDevice() { 172 if (!NS_IsMainThread()) { 173 return Device::MacOS_Unknown; 174 } 175 static Device fontVisibilityDevice = Device::Unassigned; 176 if (fontVisibilityDevice == Device::Unassigned) { 177 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); 178 NS_ENSURE_SUCCESS( 179 gfxInfo->GetFontVisibilityDetermination(&fontVisibilityDevice), 180 Device::MacOS_Unknown); 181 } 182 183 return fontVisibilityDevice; 184 } 185 186 FontVisibility gfxMacPlatformFontList::GetVisibilityForFamily( 187 const nsACString& aName) const { 188 if (aName[0] == '.' || aName.LowerCaseEqualsLiteral("lastresort")) { 189 return FontVisibility::Hidden; 190 } 191 if (FamilyInList(aName, kBaseFonts)) { 192 return FontVisibility::Base; 193 } 194 #ifndef BASE_BROWSER_VERSION 195 if (GetFontVisibilityDevice() == Device::MacOS_13_plus && 196 FamilyInList(aName, kBaseFonts_13_Higher)) { 197 return FontVisibility::Base; 198 } 199 #endif 200 #ifdef MOZ_BUNDLED_FONTS 201 if (mBundledFamilies.Contains(aName)) { 202 return FontVisibility::Base; 203 } 204 #endif 205 return FontVisibility::User; 206 } 207 208 nsTArray<std::pair<const char**, uint32_t>> 209 gfxMacPlatformFontList::GetFilteredPlatformFontLists() { 210 nsTArray<std::pair<const char**, uint32_t>> fontLists; 211 212 fontLists.AppendElement(std::make_pair(kBaseFonts, std::size(kBaseFonts))); 213 214 #ifndef BASE_BROWSER_VERSION 215 if (GetFontVisibilityDevice() == Device::MacOS_13_plus) { 216 fontLists.AppendElement( 217 std::make_pair(kBaseFonts_13_Higher, std::size(kBaseFonts_13_Higher))); 218 } 219 #endif 220 221 return fontLists; 222 } 223 224 bool gfxMacPlatformFontList::DeprecatedFamilyIsAvailable( 225 const nsACString& aName) { 226 NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(aName)); 227 return [[sFontManager availableMembersOfFontFamily:family] count] > 0; 228 } 229 230 void gfxMacPlatformFontList::InitAliasesForSingleFaceList() { 231 for (const auto& familyName : mSingleFaceFonts) { 232 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get())); 233 // Each entry in the "single face families" list is expected to be a 234 // colon-separated pair of FaceName:Family, 235 // where FaceName is the individual face name (psname) of a font 236 // that should be exposed as a separate family name, 237 // and Family is the standard family to which that face belongs. 238 // The only such face listed by default is 239 // Osaka-Mono:Osaka 240 auto colon = familyName.FindChar(':'); 241 if (colon == kNotFound) { 242 continue; 243 } 244 245 // Look for the parent family in the main font family list, 246 // and ensure we have loaded its list of available faces. 247 nsAutoCString key; 248 GenerateFontListKey(Substring(familyName, colon + 1), key); 249 fontlist::Family* family = SharedFontList()->FindFamily(key); 250 if (!family) { 251 // The parent family is not present, so just ignore this entry. 252 continue; 253 } 254 if (!family->IsInitialized()) { 255 if (!gfxPlatformFontList::InitializeFamily(family)) { 256 // This shouldn't ever fail, but if it does, we can safely ignore it. 257 MOZ_ASSERT(false, "failed to initialize font family"); 258 continue; 259 } 260 } 261 262 // Truncate the entry from prefs at the colon, so now it is just the 263 // desired single-face-family name. 264 nsAutoCString aliasName(Substring(familyName, 0, colon)); 265 266 // Look through the family's faces to see if this one is present. 267 fontlist::FontList* list = SharedFontList(); 268 const fontlist::Pointer* facePtrs = family->Faces(list); 269 for (size_t i = 0; i < family->NumFaces(); i++) { 270 if (facePtrs[i].IsNull()) { 271 continue; 272 } 273 auto* face = facePtrs[i].ToPtr<const fontlist::Face>(list); 274 if (face->mDescriptor.AsString(list).Equals(aliasName)) { 275 // Found it! Create an entry in the Alias table. 276 GenerateFontListKey(aliasName, key); 277 if (SharedFontList()->FindFamily(key) || mAliasTable.Get(key)) { 278 // If the family name is already known, something's misconfigured; 279 // just ignore it. 280 MOZ_ASSERT(false, "single-face family already known"); 281 break; 282 } 283 auto aliasData = mAliasTable.GetOrInsertNew(key); 284 // The "alias" here isn't based on an existing family, so we don't call 285 // aliasData->InitFromFamily(); the various flags are left as defaults. 286 aliasData->mFaces.AppendElement(facePtrs[i]); 287 aliasData->mBaseFamily = aliasName; 288 aliasData->mVisibility = family->Visibility(); 289 break; 290 } 291 } 292 } 293 if (!mAliasTable.IsEmpty()) { 294 // This will be updated when the font loader completes, but we require 295 // at least the Osaka-Mono alias to be available immediately. 296 SharedFontList()->SetAliases(mAliasTable); 297 } 298 } 299 300 void gfxMacPlatformFontList::InitSingleFaceList() { 301 for (const auto& familyName : mSingleFaceFonts) { 302 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get())); 303 // Each entry in the "single face families" list is expected to be a 304 // colon-separated pair of FaceName:Family, 305 // where FaceName is the individual face name (psname) of a font 306 // that should be exposed as a separate family name, 307 // and Family is the standard family to which that face belongs. 308 // The only such face listed by default is 309 // Osaka-Mono:Osaka 310 auto colon = familyName.FindChar(':'); 311 if (colon == kNotFound) { 312 continue; 313 } 314 315 // Look for the parent family in the main font family list, 316 // and ensure we have loaded its list of available faces. 317 nsAutoCString key(Substring(familyName, colon + 1)); 318 ToLowerCase(key); 319 gfxFontFamily* family = mFontFamilies.GetWeak(key); 320 if (!family || family->IsHidden()) { 321 continue; 322 } 323 family->FindStyleVariations(); 324 325 // Truncate the entry from prefs at the colon, so now it is just the 326 // desired single-face-family name. 327 nsAutoCString aliasName(Substring(familyName, 0, colon)); 328 329 // Look through the family's faces to see if this one is present. 330 const gfxFontEntry* fe = nullptr; 331 family->ReadLock(); 332 for (const auto& face : family->GetFontList()) { 333 if (face->Name().Equals(aliasName)) { 334 fe = face; 335 break; 336 } 337 } 338 family->ReadUnlock(); 339 if (!fe) { 340 continue; 341 } 342 343 // We found the correct face, so create the single-face family record. 344 GenerateFontListKey(aliasName, key); 345 LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n", 346 aliasName.get(), key.get())); 347 348 // add only if doesn't exist already 349 if (!mFontFamilies.GetWeak(key)) { 350 RefPtr<gfxFontFamily> familyEntry = 351 new gfxSingleFaceMacFontFamily(aliasName, family->Visibility()); 352 // We need a separate font entry, because its family name will 353 // differ from the one we found in the main list. 354 CTFontEntry* fontEntry = 355 new CTFontEntry(fe->Name(), fe->Weight(), true, 356 static_cast<const CTFontEntry*>(fe)->mSizeHint); 357 familyEntry->AddFontEntry(fontEntry); 358 familyEntry->SetHasStyles(true); 359 mFontFamilies.InsertOrUpdate(key, std::move(familyEntry)); 360 LOG_FONTLIST(("(fontlist-singleface) added new family: %s, key: %s\n", 361 aliasName.get(), key.get())); 362 } 363 } 364 } 365 366 void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID, 367 nsACString& aSystemFontName, 368 gfxFontStyle& aFontStyle) { 369 // Provide a local pool because we may be called from stylo threads. 370 nsAutoreleasePool localPool; 371 372 // code moved here from widget/cocoa/nsLookAndFeel.mm 373 NSFont* font = nullptr; 374 switch (aSystemFontID) { 375 case LookAndFeel::FontID::MessageBox: 376 case LookAndFeel::FontID::StatusBar: 377 case LookAndFeel::FontID::MozList: 378 case LookAndFeel::FontID::MozField: 379 case LookAndFeel::FontID::MozButton: 380 font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize]; 381 break; 382 383 case LookAndFeel::FontID::SmallCaption: 384 font = [NSFont boldSystemFontOfSize:NSFont.smallSystemFontSize]; 385 break; 386 387 case LookAndFeel::FontID::Icon: // used in urlbar; tried labelFont, but too 388 // small 389 font = [NSFont controlContentFontOfSize:0.0]; 390 break; 391 392 case LookAndFeel::FontID::MozPullDownMenu: 393 font = [NSFont menuBarFontOfSize:0.0]; 394 break; 395 396 case LookAndFeel::FontID::Caption: 397 case LookAndFeel::FontID::Menu: 398 default: 399 font = [NSFont systemFontOfSize:0.0]; 400 break; 401 } 402 NS_ASSERTION(font, "system font not set"); 403 404 aSystemFontName.AssignASCII("-apple-system"); 405 406 NSFontSymbolicTraits traits = font.fontDescriptor.symbolicTraits; 407 aFontStyle.style = (traits & NSFontItalicTrait) ? FontSlantStyle::ITALIC 408 : FontSlantStyle::NORMAL; 409 aFontStyle.weight = 410 (traits & NSFontBoldTrait) ? FontWeight::BOLD : FontWeight::NORMAL; 411 aFontStyle.stretch = (traits & NSFontExpandedTrait) ? FontStretch::EXPANDED 412 : (traits & NSFontCondensedTrait) 413 ? FontStretch::CONDENSED 414 : FontStretch::NORMAL; 415 aFontStyle.size = font.pointSize; 416 aFontStyle.systemFont = true; 417 }