tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }