410 lines
14 KiB
Text
410 lines
14 KiB
Text
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/Components.h"
|
|
#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 "AppleUtils.h"
|
|
#include "MainThreadUtils.h"
|
|
#include "nsDirectoryServiceUtils.h"
|
|
#include "nsDirectoryServiceDefs.h"
|
|
#include "nsAppDirectoryServiceDefs.h"
|
|
#include "nsIDirectoryEnumerator.h"
|
|
#include "nsCharTraits.h"
|
|
#include "nsCocoaUtils.h"
|
|
#include "nsComponentManagerUtils.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/ProfilerLabels.h"
|
|
#include "mozilla/Sprintf.h"
|
|
#include "mozilla/StaticPrefs_gfx.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include "StandardFonts-macos.inc"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::gfx;
|
|
|
|
// cache Cocoa's "shared font manager" for performance
|
|
static NSFontManager* sFontManager;
|
|
|
|
static void GetStringForNSString(const NSString* aSrc, nsAString& aDest) {
|
|
aDest.SetLength(aSrc.length);
|
|
[aSrc getCharacters:reinterpret_cast<unichar*>(aDest.BeginWriting())
|
|
range:NSMakeRange(0, aSrc.length)];
|
|
}
|
|
|
|
static NSString* GetNSStringForString(const nsAString& aSrc) {
|
|
return [NSString
|
|
stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
|
|
length:aSrc.Length()];
|
|
}
|
|
|
|
#define LOG_FONTLIST(args) \
|
|
MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args)
|
|
#define LOG_FONTLIST_ENABLED() \
|
|
MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug)
|
|
#define LOG_CMAPDATA_ENABLED() \
|
|
MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug)
|
|
|
|
/* gfxSingleFaceMacFontFamily */
|
|
|
|
class gfxSingleFaceMacFontFamily final : public gfxFontFamily {
|
|
public:
|
|
gfxSingleFaceMacFontFamily(const nsACString& aName,
|
|
FontVisibility aVisibility)
|
|
: gfxFontFamily(aName, aVisibility) {
|
|
mFaceNamesInitialized = true; // omit from face name lists
|
|
}
|
|
|
|
virtual ~gfxSingleFaceMacFontFamily() = default;
|
|
|
|
void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
|
|
MOZ_REQUIRES(mLock) override {};
|
|
|
|
void LocalizedName(nsACString& aLocalizedName) override;
|
|
|
|
void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) override;
|
|
|
|
bool IsSingleFaceFamily() const override { return true; }
|
|
};
|
|
|
|
void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
|
|
nsAutoreleasePool localPool;
|
|
|
|
AutoReadLock lock(mLock);
|
|
|
|
if (!HasOtherFamilyNames()) {
|
|
aLocalizedName = mName;
|
|
return;
|
|
}
|
|
|
|
gfxFontEntry* fe = mAvailableFonts[0];
|
|
NSFont* font = [NSFont
|
|
fontWithName:GetNSStringForString(NS_ConvertUTF8toUTF16(fe->Name()))
|
|
size:0.0];
|
|
if (font) {
|
|
NSString* localized = [font displayName];
|
|
if (localized) {
|
|
nsAutoString locName;
|
|
GetStringForNSString(localized, locName);
|
|
CopyUTF16toUTF8(locName, aLocalizedName);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// failed to get localized name, just use the canonical one
|
|
aLocalizedName = mName;
|
|
}
|
|
|
|
void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(
|
|
gfxPlatformFontList* aPlatformFontList) {
|
|
AutoWriteLock lock(mLock);
|
|
if (mOtherFamilyNamesInitialized) {
|
|
return;
|
|
}
|
|
|
|
gfxFontEntry* fe = mAvailableFonts[0];
|
|
if (!fe) {
|
|
return;
|
|
}
|
|
|
|
const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
|
|
|
|
gfxFontEntry::AutoTable nameTable(fe, kNAME);
|
|
if (!nameTable) {
|
|
return;
|
|
}
|
|
|
|
mHasOtherFamilyNames =
|
|
ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true);
|
|
|
|
mOtherFamilyNamesInitialized = true;
|
|
}
|
|
|
|
/* gfxMacPlatformFontList */
|
|
#pragma mark -
|
|
|
|
gfxMacPlatformFontList::gfxMacPlatformFontList() : CoreTextFontList() {
|
|
CheckFamilyList(kBaseFonts);
|
|
CheckFamilyList(kBaseFonts_13_Higher);
|
|
|
|
// cache this in a static variable so that gfxMacFontFamily objects
|
|
// don't have to repeatedly look it up
|
|
sFontManager = [NSFontManager sharedFontManager];
|
|
|
|
// Load the font-list preferences now, so that we don't have to do it from
|
|
// Init[Shared]FontListForPlatform, which may be called off-main-thread.
|
|
gfxFontUtils::GetPrefsFontList("font.single-face-list", mSingleFaceFonts);
|
|
}
|
|
|
|
using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
|
|
Device GetFontVisibilityDevice() {
|
|
if (!NS_IsMainThread()) {
|
|
return Device::MacOS_Unknown;
|
|
}
|
|
static Device fontVisibilityDevice = Device::Unassigned;
|
|
if (fontVisibilityDevice == Device::Unassigned) {
|
|
nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
|
|
NS_ENSURE_SUCCESS(
|
|
gfxInfo->GetFontVisibilityDetermination(&fontVisibilityDevice),
|
|
Device::MacOS_Unknown);
|
|
}
|
|
|
|
return fontVisibilityDevice;
|
|
}
|
|
|
|
FontVisibility gfxMacPlatformFontList::GetVisibilityForFamily(
|
|
const nsACString& aName) const {
|
|
if (aName[0] == '.' || aName.LowerCaseEqualsLiteral("lastresort")) {
|
|
return FontVisibility::Hidden;
|
|
}
|
|
if (FamilyInList(aName, kBaseFonts)) {
|
|
return FontVisibility::Base;
|
|
}
|
|
if (GetFontVisibilityDevice() == Device::MacOS_13_plus &&
|
|
FamilyInList(aName, kBaseFonts_13_Higher)) {
|
|
return FontVisibility::Base;
|
|
}
|
|
#ifdef MOZ_BUNDLED_FONTS
|
|
if (mBundledFamilies.Contains(aName)) {
|
|
return FontVisibility::Base;
|
|
}
|
|
#endif
|
|
return FontVisibility::User;
|
|
}
|
|
|
|
nsTArray<std::pair<const char**, uint32_t>>
|
|
gfxMacPlatformFontList::GetFilteredPlatformFontLists() {
|
|
nsTArray<std::pair<const char**, uint32_t>> fontLists;
|
|
|
|
fontLists.AppendElement(std::make_pair(kBaseFonts, std::size(kBaseFonts)));
|
|
|
|
if (GetFontVisibilityDevice() == Device::MacOS_13_plus) {
|
|
fontLists.AppendElement(
|
|
std::make_pair(kBaseFonts_13_Higher, std::size(kBaseFonts_13_Higher)));
|
|
}
|
|
|
|
return fontLists;
|
|
}
|
|
|
|
bool gfxMacPlatformFontList::DeprecatedFamilyIsAvailable(
|
|
const nsACString& aName) {
|
|
NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(aName));
|
|
return [[sFontManager availableMembersOfFontFamily:family] count] > 0;
|
|
}
|
|
|
|
void gfxMacPlatformFontList::InitAliasesForSingleFaceList() {
|
|
for (const auto& familyName : mSingleFaceFonts) {
|
|
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,
|
|
// where FaceName is the individual face name (psname) of a font
|
|
// that should be exposed as a separate family name,
|
|
// and Family is the standard family to which that face belongs.
|
|
// The only such face listed by default is
|
|
// Osaka-Mono:Osaka
|
|
auto colon = familyName.FindChar(':');
|
|
if (colon == kNotFound) {
|
|
continue;
|
|
}
|
|
|
|
// Look for the parent family in the main font family list,
|
|
// and ensure we have loaded its list of available faces.
|
|
nsAutoCString key;
|
|
GenerateFontListKey(Substring(familyName, colon + 1), key);
|
|
fontlist::Family* family = SharedFontList()->FindFamily(key);
|
|
if (!family) {
|
|
// The parent family is not present, so just ignore this entry.
|
|
continue;
|
|
}
|
|
if (!family->IsInitialized()) {
|
|
if (!gfxPlatformFontList::InitializeFamily(family)) {
|
|
// This shouldn't ever fail, but if it does, we can safely ignore it.
|
|
MOZ_ASSERT(false, "failed to initialize font family");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Truncate the entry from prefs at the colon, so now it is just the
|
|
// desired single-face-family name.
|
|
nsAutoCString aliasName(Substring(familyName, 0, colon));
|
|
|
|
// Look through the family's faces to see if this one is present.
|
|
fontlist::FontList* list = SharedFontList();
|
|
const fontlist::Pointer* facePtrs = family->Faces(list);
|
|
for (size_t i = 0; i < family->NumFaces(); i++) {
|
|
if (facePtrs[i].IsNull()) {
|
|
continue;
|
|
}
|
|
auto* face = facePtrs[i].ToPtr<const fontlist::Face>(list);
|
|
if (face->mDescriptor.AsString(list).Equals(aliasName)) {
|
|
// Found it! Create an entry in the Alias table.
|
|
GenerateFontListKey(aliasName, key);
|
|
if (SharedFontList()->FindFamily(key) || mAliasTable.Get(key)) {
|
|
// If the family name is already known, something's misconfigured;
|
|
// just ignore it.
|
|
MOZ_ASSERT(false, "single-face family already known");
|
|
break;
|
|
}
|
|
auto aliasData = mAliasTable.GetOrInsertNew(key);
|
|
// The "alias" here isn't based on an existing family, so we don't call
|
|
// aliasData->InitFromFamily(); the various flags are left as defaults.
|
|
aliasData->mFaces.AppendElement(facePtrs[i]);
|
|
aliasData->mBaseFamily = aliasName;
|
|
aliasData->mVisibility = family->Visibility();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!mAliasTable.IsEmpty()) {
|
|
// This will be updated when the font loader completes, but we require
|
|
// at least the Osaka-Mono alias to be available immediately.
|
|
SharedFontList()->SetAliases(mAliasTable);
|
|
}
|
|
}
|
|
|
|
void gfxMacPlatformFontList::InitSingleFaceList() {
|
|
for (const auto& familyName : mSingleFaceFonts) {
|
|
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,
|
|
// where FaceName is the individual face name (psname) of a font
|
|
// that should be exposed as a separate family name,
|
|
// and Family is the standard family to which that face belongs.
|
|
// The only such face listed by default is
|
|
// Osaka-Mono:Osaka
|
|
auto colon = familyName.FindChar(':');
|
|
if (colon == kNotFound) {
|
|
continue;
|
|
}
|
|
|
|
// Look for the parent family in the main font family list,
|
|
// and ensure we have loaded its list of available faces.
|
|
nsAutoCString key(Substring(familyName, colon + 1));
|
|
ToLowerCase(key);
|
|
gfxFontFamily* family = mFontFamilies.GetWeak(key);
|
|
if (!family || family->IsHidden()) {
|
|
continue;
|
|
}
|
|
family->FindStyleVariations();
|
|
|
|
// Truncate the entry from prefs at the colon, so now it is just the
|
|
// desired single-face-family name.
|
|
nsAutoCString aliasName(Substring(familyName, 0, colon));
|
|
|
|
// Look through the family's faces to see if this one is present.
|
|
const gfxFontEntry* fe = nullptr;
|
|
family->ReadLock();
|
|
for (const auto& face : family->GetFontList()) {
|
|
if (face->Name().Equals(aliasName)) {
|
|
fe = face;
|
|
break;
|
|
}
|
|
}
|
|
family->ReadUnlock();
|
|
if (!fe) {
|
|
continue;
|
|
}
|
|
|
|
// We found the correct face, so create the single-face family record.
|
|
GenerateFontListKey(aliasName, key);
|
|
LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
|
|
aliasName.get(), key.get()));
|
|
|
|
// add only if doesn't exist already
|
|
if (!mFontFamilies.GetWeak(key)) {
|
|
RefPtr<gfxFontFamily> familyEntry =
|
|
new gfxSingleFaceMacFontFamily(aliasName, family->Visibility());
|
|
// We need a separate font entry, because its family name will
|
|
// differ from the one we found in the main list.
|
|
CTFontEntry* fontEntry =
|
|
new CTFontEntry(fe->Name(), fe->Weight(), true,
|
|
static_cast<const CTFontEntry*>(fe)->mSizeHint);
|
|
familyEntry->AddFontEntry(fontEntry);
|
|
familyEntry->SetHasStyles(true);
|
|
mFontFamilies.InsertOrUpdate(key, std::move(familyEntry));
|
|
LOG_FONTLIST(("(fontlist-singleface) added new family: %s, key: %s\n",
|
|
aliasName.get(), key.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
|
|
nsACString& aSystemFontName,
|
|
gfxFontStyle& aFontStyle) {
|
|
// Provide a local pool because we may be called from stylo threads.
|
|
nsAutoreleasePool localPool;
|
|
|
|
// code moved here from widget/cocoa/nsLookAndFeel.mm
|
|
NSFont* font = nullptr;
|
|
switch (aSystemFontID) {
|
|
case LookAndFeel::FontID::MessageBox:
|
|
case LookAndFeel::FontID::StatusBar:
|
|
case LookAndFeel::FontID::MozList:
|
|
case LookAndFeel::FontID::MozField:
|
|
case LookAndFeel::FontID::MozButton:
|
|
font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize];
|
|
break;
|
|
|
|
case LookAndFeel::FontID::SmallCaption:
|
|
font = [NSFont boldSystemFontOfSize:NSFont.smallSystemFontSize];
|
|
break;
|
|
|
|
case LookAndFeel::FontID::Icon: // used in urlbar; tried labelFont, but too
|
|
// small
|
|
font = [NSFont controlContentFontOfSize:0.0];
|
|
break;
|
|
|
|
case LookAndFeel::FontID::MozPullDownMenu:
|
|
font = [NSFont menuBarFontOfSize:0.0];
|
|
break;
|
|
|
|
case LookAndFeel::FontID::Caption:
|
|
case LookAndFeel::FontID::Menu:
|
|
default:
|
|
font = [NSFont systemFontOfSize:0.0];
|
|
break;
|
|
}
|
|
NS_ASSERTION(font, "system font not set");
|
|
|
|
aSystemFontName.AssignASCII("-apple-system");
|
|
|
|
NSFontSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
|
|
aFontStyle.style = (traits & NSFontItalicTrait) ? FontSlantStyle::ITALIC
|
|
: FontSlantStyle::NORMAL;
|
|
aFontStyle.weight =
|
|
(traits & NSFontBoldTrait) ? FontWeight::BOLD : FontWeight::NORMAL;
|
|
aFontStyle.stretch = (traits & NSFontExpandedTrait) ? FontStretch::EXPANDED
|
|
: (traits & NSFontCondensedTrait)
|
|
? FontStretch::CONDENSED
|
|
: FontStretch::NORMAL;
|
|
aFontStyle.size = font.pointSize;
|
|
aFontStyle.systemFont = true;
|
|
}
|