Bug 1514869 - patch 3 - Implement macOS backend for the shared font list. r=jwatt
authorJonathan Kew <jkew@mozilla.com>
Sat, 27 Apr 2019 15:38:26 +0000
changeset 471755 c154853c599ae8fe77062a06475f6b7c5e6da226
parent 471754 095b3edec3c8ba9318c71d0acc91e10ce57818dc
child 471756 d51c979e9930e3f20d601149c0cab64d748b872c
push id35934
push usershindli@mozilla.com
push dateMon, 29 Apr 2019 21:53:38 +0000
treeherdermozilla-central@f6766ba4ac77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1514869
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1514869 - patch 3 - Implement macOS backend for the shared font list. r=jwatt Differential Revision: https://phabricator.services.mozilla.com/D22939
gfx/thebes/gfxMacPlatformFontList.h
gfx/thebes/gfxMacPlatformFontList.mm
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -181,16 +181,17 @@ class gfxMacPlatformFontList : public gf
  private:
   friend class gfxPlatformMac;
 
   gfxMacPlatformFontList();
   virtual ~gfxMacPlatformFontList();
 
   // initialize font lists
   nsresult InitFontListForPlatform() override;
+  void InitSharedFontListForPlatform() override;
 
   // special case font faces treated as font families (set via prefs)
   void InitSingleFaceList();
 
   // initialize system fonts
   void InitSystemFontNames();
 
   // helper function to lookup in both hidden system fonts and normal fonts
@@ -217,16 +218,27 @@ class gfxMacPlatformFontList : public gf
   // But CFStringRef and NSString* are the same thing anyway (they're
   // toll-free bridged).
   void AddFamily(CFStringRef aFamily);
 
   void AddFamily(const nsACString& aFamilyName, bool aSystemFont);
 
   void ActivateFontsFromDir(nsIFile* aDir);
 
+  gfxFontEntry* CreateFontEntry(
+      mozilla::fontlist::Face* aFace,
+      const mozilla::fontlist::Family* aFamily) override;
+
+  void GetFacesInitDataForFamily(
+      const mozilla::fontlist::Family* aFamily,
+      nsTArray<mozilla::fontlist::Face::InitData>& aFaces) const override;
+
+  void ReadFaceNamesForFamily(mozilla::fontlist::Family* aFamily,
+                              bool aNeedFullnamePostscriptNames) override;
+
 #ifdef MOZ_BUNDLED_FONTS
   void ActivateBundledFonts();
 #endif
 
   enum { kATSGenerationInitial = -1 };
 
   // default font for use with system-wide font fallback
   CTFontRef mDefaultFont;
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -39,34 +39,35 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/Logging.h"
 
 #include <algorithm>
 
 #import <AppKit/AppKit.h>
 
+#include "gfxFontConstants.h"
 #include "gfxPlatformMac.h"
 #include "gfxMacPlatformFontList.h"
 #include "gfxMacFont.h"
 #include "gfxUserFontSet.h"
+#include "SharedFontList-impl.h"
+
 #include "harfbuzz/hb.h"
 
 #include "MainThreadUtils.h"
-#include "nsServiceManagerUtils.h"
-#include "nsTArray.h"
-
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsCharTraits.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
-#include "gfxFontConstants.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
@@ -139,17 +140,17 @@ static NSString* GetNSStringForString(co
 // the characters, but lacks OpenType support).
 
 // TODO: consider whether we should move this to gfxFontEntry and do similar
 // cmap-masking on other platforms to avoid using fonts that won't shape
 // properly.
 
 nsresult MacOSFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
   // attempt this once, if errors occur leave a blank cmap
-  if (mCharacterMap) {
+  if (mCharacterMap || mShmemCharacterMap) {
     return NS_OK;
   }
 
   RefPtr<gfxCharacterMap> charmap;
   nsresult rv;
 
   if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
     rv = NS_OK;
@@ -231,17 +232,27 @@ nsresult MacOSFontEntry::ReadCMAP(FontIn
       charmap->ClearRange(0x06ee, 0x06ef);
       charmap->clear(0x06ff);
     }
   }
 
   mHasCmapTable = NS_SUCCEEDED(rv);
   if (mHasCmapTable) {
     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
-    mCharacterMap = pfl->FindCharMap(charmap);
+    fontlist::FontList* sharedFontList = pfl->SharedFontList();
+    if (!IsUserFont() && mShmemFace) {
+      mShmemFace->SetCharacterMap(sharedFontList, charmap); // async
+      if (!TrySetShmemCharacterMap()) {
+        // Temporarily retain charmap, until the shared version is
+        // ready for use.
+        mCharacterMap = charmap;
+      }
+    } else {
+      mCharacterMap = pfl->FindCharMap(charmap);
+    }
   } else {
     // if error occurred, initialize to null cmap
     mCharacterMap = new gfxCharacterMap();
   }
 
   LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n", mName.get(),
                 charmap->SizeOfIncludingThis(moz_malloc_size_of), charmap->mHash,
                 mCharacterMap == charmap ? " new" : ""));
@@ -397,16 +408,30 @@ gfxFontEntry* MacOSFontEntry::Clone() co
   MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
   MacOSFontEntry* fe = new MacOSFontEntry(Name(), Weight(), mStandardFace, mSizeHint);
   fe->mStyleRange = mStyleRange;
   fe->mStretchRange = mStretchRange;
   fe->mFixedPitch = mFixedPitch;
   return fe;
 }
 
+gfxFontEntry* gfxMacPlatformFontList::CreateFontEntry(fontlist::Face* aFace,
+                                                      const fontlist::Family* aFamily) {
+  MacOSFontEntry* fe =
+      new MacOSFontEntry(aFace->mDescriptor.AsString(SharedFontList()), aFace->mWeight, false,
+                         0.0);  // XXX standardFace, sizeHint
+  fe->mStyleRange = aFace->mStyle;
+  fe->mStretchRange = aFace->mStretch;
+  fe->mFixedPitch = aFace->mFixedPitch;
+  fe->mIsBadUnderlineFont = aFamily->IsBadUnderlineFamily();
+  fe->mShmemFace = aFace;
+  fe->mFamilyName = aFamily->DisplayName().AsString(SharedFontList());
+  return fe;
+}
+
 CGFontRef MacOSFontEntry::GetFontRef() {
   if (!mFontRefInitialized) {
     mFontRefInitialized = true;
     NSString* psname = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
     mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
     if (!mFontRef) {
       // This happens on macOS 10.12 for font entry names that start with
       // .AppleSystemUIFont. For those fonts, we need to go through NSFont
@@ -1096,16 +1121,38 @@ nsresult gfxMacPlatformFontList::InitFon
   PreloadNamesList();
 
   // start the delayed cmap loader
   GetPrefsAndStartLoader();
 
   return NS_OK;
 }
 
+void gfxMacPlatformFontList::InitSharedFontListForPlatform() {
+  InitSystemFontNames();
+  if (XRE_IsParentProcess()) {
+    CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
+    nsTArray<fontlist::Family::InitData> families;
+    for (NSString* familyName in (NSArray*)familyNames) {
+      nsAutoString name16;
+      GetStringForNSString(familyName, name16);
+      NS_ConvertUTF16toUTF8 name(name16);
+      nsAutoCString key;
+      GenerateFontListKey(name, key);
+      bool isHidden = key.EqualsLiteral("lastresort") || key[0] == '.';
+      families.AppendElement(fontlist::Family::InitData(key, name, 0, isHidden));
+    }
+    CFRelease(familyNames);
+    ApplyWhitelist(families);
+    families.Sort();
+    SharedFontList()->SetFamilyNames(families);
+    GetPrefsAndStartLoader();
+  }
+}
+
 void gfxMacPlatformFontList::InitSingleFaceList() {
   AutoTArray<nsCString, 10> singleFaceFonts;
   gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
 
   for (auto& familyName : singleFaceFonts) {
     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
     // Each entry in the "single face families" list is expected to be a
     // colon-separated pair of FaceName:Family,
@@ -1314,25 +1361,43 @@ gfxFontEntry* gfxMacPlatformFontList::Pl
       AutoTArray<UniChar, 1024> buffer;
       CFIndex familyNameLen = ::CFStringGetLength(familyNameRef);
       buffer.SetLength(familyNameLen + 1);
       ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen), buffer.Elements());
       buffer[familyNameLen] = 0;
       NS_ConvertUTF16toUTF8 familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()),
                                              familyNameLen);
 
-      gfxFontFamily* family = FindSystemFontFamily(familyNameString);
-      if (family) {
-        fontEntry = family->FindFontForStyle(*aMatchStyle);
-        if (fontEntry) {
-          if (fontEntry->HasCharacter(aCh)) {
-            *aMatchedFamily = FontFamily(family);
-          } else {
-            fontEntry = nullptr;
-            cantUseFallbackFont = true;
+      if (SharedFontList()) {
+        fontlist::Family* family = FindSharedFamily(familyNameString);
+        if (family) {
+          fontlist::Face* face = family->FindFaceForStyle(SharedFontList(), *aMatchStyle);
+          if (face) {
+            fontEntry = GetOrCreateFontEntry(face, family);
+          }
+          if (fontEntry) {
+            if (fontEntry->HasCharacter(aCh)) {
+              *aMatchedFamily = FontFamily(family);
+            } else {
+              fontEntry = nullptr;
+              cantUseFallbackFont = true;
+            }
+          }
+        }
+      } else {
+        gfxFontFamily* family = FindSystemFontFamily(familyNameString);
+        if (family) {
+          fontEntry = family->FindFontForStyle(*aMatchStyle);
+          if (fontEntry) {
+            if (fontEntry->HasCharacter(aCh)) {
+              *aMatchedFamily = FontFamily(family);
+            } else {
+              fontEntry = nullptr;
+              cantUseFallbackFont = true;
+            }
           }
         }
       }
     }
 
     if (familyNameRef) {
       ::CFRelease(familyNameRef);
     }
@@ -1437,24 +1502,36 @@ gfxFontEntry* gfxMacPlatformFontList::Ma
 static const char kSystemFont_system[] = "-apple-system";
 
 bool gfxMacPlatformFontList::FindAndAddFamilies(mozilla::StyleGenericFontFamily aGeneric,
                                                 const nsACString& aFamily,
                                                 nsTArray<FamilyAndGeneric>* aOutput,
                                                 FindFamiliesFlags aFlags, gfxFontStyle* aStyle,
                                                 gfxFloat aDevToCssSize) {
   // search for special system font name, -apple-system
-  if (aFamily.EqualsLiteral(kSystemFont_system)) {
-    if (mUseSizeSensitiveSystemFont && aStyle &&
-        (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
-      aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName));
+  if (SharedFontList()) {
+    if (aFamily.EqualsLiteral(kSystemFont_system)) {
+      if (mUseSizeSensitiveSystemFont && aStyle &&
+          (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
+        return gfxPlatformFontList::FindAndAddFamilies(aGeneric, mSystemDisplayFontFamilyName,
+                                                       aOutput, aFlags, aStyle, aDevToCssSize);
+      }
+      return gfxPlatformFontList::FindAndAddFamilies(aGeneric, mSystemTextFontFamilyName, aOutput,
+                                                     aFlags, aStyle, aDevToCssSize);
+    }
+  } else {
+    if (aFamily.EqualsLiteral(kSystemFont_system)) {
+      if (mUseSizeSensitiveSystemFont && aStyle &&
+          (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
+        aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName));
+        return true;
+      }
+      aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName));
       return true;
     }
-    aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName));
-    return true;
   }
 
   return gfxPlatformFontList::FindAndAddFamilies(aGeneric, aFamily, aOutput, aFlags, aStyle,
                                                  aDevToCssSize);
 }
 
 void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
                                               nsACString& aSystemFontName,
@@ -1680,16 +1757,132 @@ void gfxMacPlatformFontList::ActivateFon
       ::CFRelease(fontURL);
     }
   }
 
   ::CTFontManagerRegisterFontsForURLs(urls, kCTFontManagerScopeProcess, nullptr);
   ::CFRelease(urls);
 }
 
+void gfxMacPlatformFontList::GetFacesInitDataForFamily(
+    const fontlist::Family* aFamily,
+    nsTArray<fontlist::Face::InitData>& aFaces) const {
+  nsAutoreleasePool localPool;
+
+  NS_ConvertUTF8toUTF16 name(aFamily->Key().AsString(SharedFontList()));
+  NSString* family = GetNSStringForString(name);
+
+  // returns an array of [psname, style name, weight, traits] elements, goofy api
+  NSArray* fontfaces = [sFontManager availableMembersOfFontFamily:family];
+  int faceCount = [fontfaces count];
+  for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) {
+    NSArray* face = [fontfaces objectAtIndex:faceIndex];
+    NSString* psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
+    int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
+    uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
+    NSString* facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
+    bool isStandardFace = false;
+
+    if (appKitWeight == kAppleExtraLightWeight) {
+      // if the facename contains UltraLight, set the weight to the ultralight weight value
+      NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
+      if (range.location != NSNotFound) {
+        appKitWeight = kAppleUltraLightWeight;
+      }
+    }
+
+    // make a nsString
+    nsAutoString postscriptFontName;
+    GetStringForNSString(psname, postscriptFontName);
+
+    int32_t cssWeight = GetWeightOverride(postscriptFontName);
+    if (cssWeight) {
+      // scale down and clamp, to get a value from 1..9
+      cssWeight = ((cssWeight + 50) / 100);
+      cssWeight = std::max(1, std::min(cssWeight, 9));
+    } else {
+      cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
+    }
+    cssWeight *= 100;  // scale up to CSS values
+
+    if ([facename isEqualToString:@"Regular"] || [facename isEqualToString:@"Bold"] ||
+        [facename isEqualToString:@"Italic"] || [facename isEqualToString:@"Oblique"] ||
+        [facename isEqualToString:@"Bold Italic"] || [facename isEqualToString:@"Bold Oblique"]) {
+      isStandardFace = true;
+    }
+
+    StretchRange stretch(FontStretch::Normal());
+    if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
+      stretch = StretchRange(FontStretch::Condensed());
+    } else if (macTraits & NSExpandedFontMask) {
+      stretch = StretchRange(FontStretch::Expanded());
+    }
+    // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
+    // at least (see bug 611855), so check for style name endings as well
+    SlantStyleRange slantStyle(FontSlantStyle::Normal());
+    if ((macTraits & NSItalicFontMask) || [facename hasSuffix:@"Italic"] ||
+        [facename hasSuffix:@"Oblique"]) {
+      slantStyle = SlantStyleRange(FontSlantStyle::Italic());
+    }
+
+    bool fixedPitch = (macTraits & NSFixedPitchFontMask) ? true : false;
+
+    aFaces.AppendElement(fontlist::Face::InitData{
+        NS_ConvertUTF16toUTF8(postscriptFontName),
+        0,
+        fixedPitch,
+        WeightRange(FontWeight(cssWeight)),
+        stretch,
+        slantStyle,
+    });
+  }
+}
+
+void gfxMacPlatformFontList::ReadFaceNamesForFamily(fontlist::Family* aFamily,
+                                                    bool aNeedFullnamePostscriptNames) {
+  if (!aFamily->IsInitialized()) {
+    if (!InitializeFamily(aFamily)) {
+      return;
+    }
+  }
+  const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
+  fontlist::FontList* list = SharedFontList();
+  nsAutoCString canonicalName(aFamily->DisplayName().AsString(list));
+  const fontlist::Pointer* facePtrs = aFamily->Faces(list);
+  for (uint32_t i = 0, n = aFamily->NumFaces(); i < n; i++) {
+    auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
+    nsAutoCString name(face->mDescriptor.AsString(list));
+    // We create a temporary MacOSFontEntry just to read family names from the
+    // 'name' table in the font resource. The style attributes here are ignored
+    // as this entry is not used for font style matching.
+    // The size hint might be used to select which face is accessed in the case
+    // of the macOS UI font; see MacOSFontEntry::GetFontRef(). We pass 16.0 in
+    // order to get a standard text-size face in this case, although it's
+    // unlikely to matter for the purpose of just reading family names.
+    auto fe = MakeUnique<MacOSFontEntry>(name, WeightRange(FontWeight::Normal()),
+                                         false, 16.0);
+    if (!fe) {
+      continue;
+    }
+    gfxFontEntry::AutoTable nameTable(fe.get(), kNAME);
+    if (!nameTable) {
+      continue;
+    }
+    uint32_t dataLength;
+    const char* nameData = hb_blob_get_data(nameTable, &dataLength);
+    AutoTArray<nsCString, 4> otherFamilyNames;
+    gfxFontUtils::ReadOtherFamilyNamesForFace(canonicalName, nameData, dataLength,
+                                              otherFamilyNames, false);
+    for (const auto& alias : otherFamilyNames) {
+      auto af = mAliasTable.LookupOrAdd(alias);
+      af->AppendElement(facePtrs[i]);
+    }
+  }
+}
+
 #ifdef MOZ_BUNDLED_FONTS
 
 void gfxMacPlatformFontList::ActivateBundledFonts() {
   nsCOMPtr<nsIFile> localDir;
   if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)))) {
     return;
   }
   if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {