diff options
Diffstat (limited to 'gfx/thebes/gfxPlatformMac.cpp')
-rw-r--r-- | gfx/thebes/gfxPlatformMac.cpp | 1014 |
1 files changed, 1014 insertions, 0 deletions
diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp new file mode 100644 index 0000000000..02ec1a7b28 --- /dev/null +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -0,0 +1,1014 @@ +/* -*- 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 "gfxPlatformMac.h" + +#include "gfxQuartzSurface.h" +#include "mozilla/DataMutex.h" +#include "mozilla/gfx/2D.h" + +#include "gfxMacPlatformFontList.h" +#include "gfxMacFont.h" +#include "gfxCoreTextShaper.h" +#include "gfxTextRun.h" +#include "gfxUserFontSet.h" +#include "gfxConfig.h" + +#include "AppleUtils.h" +#include "nsTArray.h" +#include "mozilla/Preferences.h" +#include "mozilla/VsyncDispatcher.h" +#include "nsCocoaFeatures.h" +#include "nsComponentManagerUtils.h" +#include "nsIFile.h" +#include "nsUnicodeProperties.h" +#include "qcms.h" +#include "gfx2DGlue.h" +#include "GeckoProfiler.h" +#include "nsThreadUtils.h" + +#ifdef MOZ_BUNDLED_FONTS +# include "mozilla/Telemetry.h" +# include "nsDirectoryServiceDefs.h" +# include "mozilla/StaticPrefs_gfx.h" +#endif + +#include <dlfcn.h> +#include <CoreVideo/CoreVideo.h> + +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/SurfacePool.h" +#include "VsyncSource.h" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::unicode; + +using mozilla::dom::SystemFontList; + +// A bunch of fonts for "additional language support" are shipped in a +// "Language Support" directory, and don't show up in the standard font +// list returned by CTFontManagerCopyAvailableFontFamilyNames unless +// we explicitly activate them. +static const nsLiteralCString kLangFontsDirs[] = { + "/Library/Application Support/Apple/Fonts/Language Support"_ns, + "/System/Library/Fonts/Supplemental"_ns}; + +/* static */ +void gfxPlatformMac::FontRegistrationCallback(void* aUnused) { + AUTO_PROFILER_REGISTER_THREAD("RegisterFonts"); + PR_SetCurrentThreadName("RegisterFonts"); + + for (const auto& dir : kLangFontsDirs) { + gfxMacPlatformFontList::ActivateFontsFromDir(dir); + } +} + +PRThread* gfxPlatformMac::sFontRegistrationThread = nullptr; + +/* This is called from XPCOM_Init during startup (before gfxPlatform has been + initialized), so that it can kick off the font activation on a secondary + thread, and hope that it'll be finished by the time we're ready to build + our font list. */ +/* static */ +void gfxPlatformMac::RegisterSupplementalFonts() { + if (XRE_GetProcessType() == GeckoProcessType_Default) { + // We activate the fonts on a separate thread, to minimize the startup- + // time cost. + sFontRegistrationThread = PR_CreateThread( + PR_USER_THREAD, FontRegistrationCallback, nullptr, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + } +} + +/* static */ +void gfxPlatformMac::WaitForFontRegistration() { + if (sFontRegistrationThread) { + PR_JoinThread(sFontRegistrationThread); + sFontRegistrationThread = nullptr; + } +} + +gfxPlatformMac::gfxPlatformMac() { + mFontAntiAliasingThreshold = ReadAntiAliasingThreshold(); + + InitBackendPrefs(GetBackendPrefs()); +} + +gfxPlatformMac::~gfxPlatformMac() { gfxCoreTextShaper::Shutdown(); } + +BackendPrefsData gfxPlatformMac::GetBackendPrefs() const { + BackendPrefsData data; + + data.mCanvasBitmask = BackendTypeBit(BackendType::SKIA); + data.mContentBitmask = BackendTypeBit(BackendType::SKIA); + data.mCanvasDefault = BackendType::SKIA; + data.mContentDefault = BackendType::SKIA; + + return data; +} + +bool gfxPlatformMac::CreatePlatformFontList() { + return gfxPlatformFontList::Initialize(new gfxMacPlatformFontList); +} + +void gfxPlatformMac::ReadSystemFontList(SystemFontList* aFontList) { + gfxMacPlatformFontList::PlatformFontList()->ReadSystemFontList(aFontList); +} + +already_AddRefed<gfxASurface> gfxPlatformMac::CreateOffscreenSurface( + const IntSize& aSize, gfxImageFormat aFormat) { + if (!Factory::AllowedSurfaceSize(aSize)) { + return nullptr; + } + + RefPtr<gfxASurface> newSurface = new gfxQuartzSurface(aSize, aFormat); + return newSurface.forget(); +} + +void gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, Script aRunScript, + eFontPresentation aPresentation, + nsTArray<const char*>& aFontList) { + if (PrefersColor(aPresentation)) { + aFontList.AppendElement("Apple Color Emoji"); + } + + switch (aRunScript) { + case Script::INVALID: + case Script::NUM_SCRIPT_CODES: + // Ensure the switch covers all the Script enum values. + MOZ_ASSERT_UNREACHABLE("bad script code"); + break; + + case Script::COMMON: + case Script::INHERITED: + // In most cases, COMMON and INHERITED characters will be merged into + // their context, but if they occur without any specific script context + // we'll just try common default fonts here. + case Script::LATIN: + case Script::CYRILLIC: + case Script::GREEK: + aFontList.AppendElement("Lucida Grande"); + break; + + case Script::MATHEMATICAL_NOTATION: + case Script::SYMBOLS: + case Script::SYMBOLS_EMOJI: + // Not currently returned by script run resolution (but see below, after + // the switch). + break; + + // CJK-related script codes are a bit troublesome because of unification; + // we'll probably just get HAN much of the time, so the choice of which + // language font to try for fallback is rather arbitrary. Usually, though, + // we hope that font prefs will have handled this earlier. + case Script::BOPOMOFO: + case Script::HAN_WITH_BOPOMOFO: + case Script::SIMPLIFIED_HAN: + case Script::HAN: + aFontList.AppendElement("Songti SC"); + if (aCh > 0x10000) { + // macOS installations with MS Office may have these -ExtB fonts + aFontList.AppendElement("SimSun-ExtB"); + } + break; + + // Currently, we don't resolve script runs to this value, but we may do so + // in future if we get better at handling things like `lang=zh-Hant`, not + // just resolving based on the Unicode text. + case Script::TRADITIONAL_HAN: + aFontList.AppendElement("Songti TC"); + if (aCh > 0x10000) { + // macOS installations with MS Office may have these -ExtB fonts + aFontList.AppendElement("MingLiU-ExtB"); + } + break; + + case Script::HIRAGANA: + case Script::KATAKANA: + case Script::KATAKANA_OR_HIRAGANA: + case Script::JAPANESE: + aFontList.AppendElement("Hiragino Sans"); + aFontList.AppendElement("Hiragino Kaku Gothic ProN"); + break; + + case Script::JAMO: + case Script::KOREAN: + case Script::HANGUL: + aFontList.AppendElement("Nanum Gothic"); + aFontList.AppendElement("Apple SD Gothic Neo"); + break; + + // For most other scripts, macOS comes with a default font we can use. + case Script::ARABIC: + aFontList.AppendElement("Geeza Pro"); + break; + case Script::ARMENIAN: + aFontList.AppendElement("Mshtakan"); + break; + case Script::BENGALI: + aFontList.AppendElement("Bangla Sangam MN"); + break; + case Script::CHEROKEE: + aFontList.AppendElement("Plantagenet Cherokee"); + break; + case Script::COPTIC: + aFontList.AppendElement("Noto Sans Coptic"); + break; + case Script::DESERET: + aFontList.AppendElement("Baskerville"); + break; + case Script::DEVANAGARI: + aFontList.AppendElement("Devanagari Sangam MN"); + break; + case Script::ETHIOPIC: + aFontList.AppendElement("Kefa"); + break; + case Script::GEORGIAN: + aFontList.AppendElement("Helvetica"); + break; + case Script::GOTHIC: + aFontList.AppendElement("Noto Sans Gothic"); + break; + case Script::GUJARATI: + aFontList.AppendElement("Gujarati Sangam MN"); + break; + case Script::GURMUKHI: + aFontList.AppendElement("Gurmukhi MN"); + break; + case Script::HEBREW: + aFontList.AppendElement("Lucida Grande"); + break; + case Script::KANNADA: + aFontList.AppendElement("Kannada MN"); + break; + case Script::KHMER: + aFontList.AppendElement("Khmer MN"); + break; + case Script::LAO: + aFontList.AppendElement("Lao MN"); + break; + case Script::MALAYALAM: + aFontList.AppendElement("Malayalam Sangam MN"); + break; + case Script::MONGOLIAN: + aFontList.AppendElement("Noto Sans Mongolian"); + break; + case Script::MYANMAR: + aFontList.AppendElement("Myanmar MN"); + break; + case Script::OGHAM: + aFontList.AppendElement("Noto Sans Ogham"); + break; + case Script::OLD_ITALIC: + aFontList.AppendElement("Noto Sans Old Italic"); + break; + case Script::ORIYA: + aFontList.AppendElement("Oriya Sangam MN"); + break; + case Script::RUNIC: + aFontList.AppendElement("Noto Sans Runic"); + break; + case Script::SINHALA: + aFontList.AppendElement("Sinhala Sangam MN"); + break; + case Script::SYRIAC: + aFontList.AppendElement("Noto Sans Syriac"); + break; + case Script::TAMIL: + aFontList.AppendElement("Tamil MN"); + break; + case Script::TELUGU: + aFontList.AppendElement("Telugu MN"); + break; + case Script::THAANA: + aFontList.AppendElement("Noto Sans Thaana"); + break; + case Script::THAI: + aFontList.AppendElement("Thonburi"); + break; + case Script::TIBETAN: + aFontList.AppendElement("Kailasa"); + break; + case Script::CANADIAN_ABORIGINAL: + aFontList.AppendElement("Euphemia UCAS"); + break; + case Script::YI: + aFontList.AppendElement("Noto Sans Yi"); + aFontList.AppendElement("STHeiti"); + break; + case Script::TAGALOG: + aFontList.AppendElement("Noto Sans Tagalog"); + break; + case Script::HANUNOO: + aFontList.AppendElement("Noto Sans Hanunoo"); + break; + case Script::BUHID: + aFontList.AppendElement("Noto Sans Buhid"); + break; + case Script::TAGBANWA: + aFontList.AppendElement("Noto Sans Tagbanwa"); + break; + case Script::BRAILLE: + aFontList.AppendElement("Apple Braille"); + break; + case Script::CYPRIOT: + aFontList.AppendElement("Noto Sans Cypriot"); + break; + case Script::LIMBU: + aFontList.AppendElement("Noto Sans Limbu"); + break; + case Script::LINEAR_B: + aFontList.AppendElement("Noto Sans Linear B"); + break; + case Script::OSMANYA: + aFontList.AppendElement("Noto Sans Osmanya"); + break; + case Script::SHAVIAN: + aFontList.AppendElement("Noto Sans Shavian"); + break; + case Script::TAI_LE: + aFontList.AppendElement("Noto Sans Tai Le"); + break; + case Script::UGARITIC: + aFontList.AppendElement("Noto Sans Ugaritic"); + break; + case Script::BUGINESE: + aFontList.AppendElement("Noto Sans Buginese"); + break; + case Script::GLAGOLITIC: + aFontList.AppendElement("Noto Sans Glagolitic"); + break; + case Script::KHAROSHTHI: + aFontList.AppendElement("Noto Sans Kharoshthi"); + break; + case Script::SYLOTI_NAGRI: + aFontList.AppendElement("Noto Sans Syloti Nagri"); + break; + case Script::NEW_TAI_LUE: + aFontList.AppendElement("Noto Sans New Tai Lue"); + break; + case Script::TIFINAGH: + aFontList.AppendElement("Noto Sans Tifinagh"); + break; + case Script::OLD_PERSIAN: + aFontList.AppendElement("Noto Sans Old Persian"); + break; + case Script::BALINESE: + aFontList.AppendElement("Noto Sans Balinese"); + break; + case Script::BATAK: + aFontList.AppendElement("Noto Sans Batak"); + break; + case Script::BRAHMI: + aFontList.AppendElement("Noto Sans Brahmi"); + break; + case Script::CHAM: + aFontList.AppendElement("Noto Sans Cham"); + break; + case Script::EGYPTIAN_HIEROGLYPHS: + aFontList.AppendElement("Noto Sans Egyptian Hieroglyphs"); + break; + case Script::PAHAWH_HMONG: + aFontList.AppendElement("Noto Sans Pahawh Hmong"); + break; + case Script::OLD_HUNGARIAN: + aFontList.AppendElement("Noto Sans Old Hungarian"); + break; + case Script::JAVANESE: + aFontList.AppendElement("Noto Sans Javanese"); + break; + case Script::KAYAH_LI: + aFontList.AppendElement("Noto Sans Kayah Li"); + break; + case Script::LEPCHA: + aFontList.AppendElement("Noto Sans Lepcha"); + break; + case Script::LINEAR_A: + aFontList.AppendElement("Noto Sans Linear A"); + break; + case Script::MANDAIC: + aFontList.AppendElement("Noto Sans Mandaic"); + break; + case Script::NKO: + aFontList.AppendElement("Noto Sans NKo"); + break; + case Script::OLD_TURKIC: + aFontList.AppendElement("Noto Sans Old Turkic"); + break; + case Script::OLD_PERMIC: + aFontList.AppendElement("Noto Sans Old Permic"); + break; + case Script::PHAGS_PA: + aFontList.AppendElement("Noto Sans PhagsPa"); + break; + case Script::PHOENICIAN: + aFontList.AppendElement("Noto Sans Phoenician"); + break; + case Script::MIAO: + aFontList.AppendElement("Noto Sans Miao"); + break; + case Script::VAI: + aFontList.AppendElement("Noto Sans Vai"); + break; + case Script::CUNEIFORM: + aFontList.AppendElement("Noto Sans Cuneiform"); + break; + case Script::CARIAN: + aFontList.AppendElement("Noto Sans Carian"); + break; + case Script::TAI_THAM: + aFontList.AppendElement("Noto Sans Tai Tham"); + break; + case Script::LYCIAN: + aFontList.AppendElement("Noto Sans Lycian"); + break; + case Script::LYDIAN: + aFontList.AppendElement("Noto Sans Lydian"); + break; + case Script::OL_CHIKI: + aFontList.AppendElement("Noto Sans Ol Chiki"); + break; + case Script::REJANG: + aFontList.AppendElement("Noto Sans Rejang"); + break; + case Script::SAURASHTRA: + aFontList.AppendElement("Noto Sans Saurashtra"); + break; + case Script::SUNDANESE: + aFontList.AppendElement("Noto Sans Sundanese"); + break; + case Script::MEETEI_MAYEK: + aFontList.AppendElement("Noto Sans Meetei Mayek"); + break; + case Script::IMPERIAL_ARAMAIC: + aFontList.AppendElement("Noto Sans Imperial Aramaic"); + break; + case Script::AVESTAN: + aFontList.AppendElement("Noto Sans Avestan"); + break; + case Script::CHAKMA: + aFontList.AppendElement("Noto Sans Chakma"); + break; + case Script::KAITHI: + aFontList.AppendElement("Noto Sans Kaithi"); + break; + case Script::MANICHAEAN: + aFontList.AppendElement("Noto Sans Manichaean"); + break; + case Script::INSCRIPTIONAL_PAHLAVI: + aFontList.AppendElement("Noto Sans Inscriptional Pahlavi"); + break; + case Script::PSALTER_PAHLAVI: + aFontList.AppendElement("Noto Sans Psalter Pahlavi"); + break; + case Script::INSCRIPTIONAL_PARTHIAN: + aFontList.AppendElement("Noto Sans Inscriptional Parthian"); + break; + case Script::SAMARITAN: + aFontList.AppendElement("Noto Sans Samaritan"); + break; + case Script::TAI_VIET: + aFontList.AppendElement("Noto Sans Tai Viet"); + break; + case Script::BAMUM: + aFontList.AppendElement("Noto Sans Bamum"); + break; + case Script::LISU: + aFontList.AppendElement("Noto Sans Lisu"); + break; + case Script::OLD_SOUTH_ARABIAN: + aFontList.AppendElement("Noto Sans Old South Arabian"); + break; + case Script::BASSA_VAH: + aFontList.AppendElement("Noto Sans Bassa Vah"); + break; + case Script::DUPLOYAN: + aFontList.AppendElement("Noto Sans Duployan"); + break; + case Script::ELBASAN: + aFontList.AppendElement("Noto Sans Elbasan"); + break; + case Script::GRANTHA: + aFontList.AppendElement("Noto Sans Grantha"); + break; + case Script::MENDE_KIKAKUI: + aFontList.AppendElement("Noto Sans Mende Kikakui"); + break; + case Script::MEROITIC_CURSIVE: + case Script::MEROITIC_HIEROGLYPHS: + aFontList.AppendElement("Noto Sans Meroitic"); + break; + case Script::OLD_NORTH_ARABIAN: + aFontList.AppendElement("Noto Sans Old North Arabian"); + break; + case Script::NABATAEAN: + aFontList.AppendElement("Noto Sans Nabataean"); + break; + case Script::PALMYRENE: + aFontList.AppendElement("Noto Sans Palmyrene"); + break; + case Script::KHUDAWADI: + aFontList.AppendElement("Noto Sans Khudawadi"); + break; + case Script::WARANG_CITI: + aFontList.AppendElement("Noto Sans Warang Citi"); + break; + case Script::MRO: + aFontList.AppendElement("Noto Sans Mro"); + break; + case Script::SHARADA: + aFontList.AppendElement("Noto Sans Sharada"); + break; + case Script::SORA_SOMPENG: + aFontList.AppendElement("Noto Sans Sora Sompeng"); + break; + case Script::TAKRI: + aFontList.AppendElement("Noto Sans Takri"); + break; + case Script::KHOJKI: + aFontList.AppendElement("Noto Sans Khojki"); + break; + case Script::TIRHUTA: + aFontList.AppendElement("Noto Sans Tirhuta"); + break; + case Script::CAUCASIAN_ALBANIAN: + aFontList.AppendElement("Noto Sans Caucasian Albanian"); + break; + case Script::MAHAJANI: + aFontList.AppendElement("Noto Sans Mahajani"); + break; + case Script::AHOM: + aFontList.AppendElement("Noto Serif Ahom"); + break; + case Script::HATRAN: + aFontList.AppendElement("Noto Sans Hatran"); + break; + case Script::MODI: + aFontList.AppendElement("Noto Sans Modi"); + break; + case Script::MULTANI: + aFontList.AppendElement("Noto Sans Multani"); + break; + case Script::PAU_CIN_HAU: + aFontList.AppendElement("Noto Sans Pau Cin Hau"); + break; + case Script::SIDDHAM: + aFontList.AppendElement("Noto Sans Siddham"); + break; + case Script::ADLAM: + aFontList.AppendElement("Noto Sans Adlam"); + break; + case Script::BHAIKSUKI: + aFontList.AppendElement("Noto Sans Bhaiksuki"); + break; + case Script::MARCHEN: + aFontList.AppendElement("Noto Sans Marchen"); + break; + case Script::NEWA: + aFontList.AppendElement("Noto Sans Newa"); + break; + case Script::OSAGE: + aFontList.AppendElement("Noto Sans Osage"); + break; + case Script::HANIFI_ROHINGYA: + aFontList.AppendElement("Noto Sans Hanifi Rohingya"); + break; + case Script::WANCHO: + aFontList.AppendElement("Noto Sans Wancho"); + break; + + // Script codes for which no commonly-installed font is currently known. + // Probably future macOS versions will add Noto fonts for many of these, + // so we should watch for updates. + case Script::OLD_CHURCH_SLAVONIC_CYRILLIC: + case Script::DEMOTIC_EGYPTIAN: + case Script::HIERATIC_EGYPTIAN: + case Script::BLISSYMBOLS: + case Script::CIRTH: + case Script::KHUTSURI: + case Script::HARAPPAN_INDUS: + case Script::LATIN_FRAKTUR: + case Script::LATIN_GAELIC: + case Script::MAYAN_HIEROGLYPHS: + case Script::RONGORONGO: + case Script::SARATI: + case Script::ESTRANGELO_SYRIAC: + case Script::WESTERN_SYRIAC: + case Script::EASTERN_SYRIAC: + case Script::TENGWAR: + case Script::VISIBLE_SPEECH: + case Script::UNWRITTEN_LANGUAGES: + case Script::UNKNOWN: + case Script::SIGNWRITING: + case Script::MOON: + case Script::BOOK_PAHLAVI: + case Script::NAKHI_GEBA: + case Script::KPELLE: + case Script::LOMA: + case Script::AFAKA: + case Script::JURCHEN: + case Script::NUSHU: + case Script::TANGUT: + case Script::WOLEAI: + case Script::ANATOLIAN_HIEROGLYPHS: + case Script::MASARAM_GONDI: + case Script::SOYOMBO: + case Script::ZANABAZAR_SQUARE: + case Script::DOGRA: + case Script::GUNJALA_GONDI: + case Script::MAKASAR: + case Script::MEDEFAIDRIN: + case Script::SOGDIAN: + case Script::OLD_SOGDIAN: + case Script::ELYMAIC: + case Script::NYIAKENG_PUACHUE_HMONG: + case Script::NANDINAGARI: + case Script::CHORASMIAN: + case Script::DIVES_AKURU: + case Script::KHITAN_SMALL_SCRIPT: + case Script::YEZIDI: + case Script::CYPRO_MINOAN: + case Script::OLD_UYGHUR: + case Script::TANGSA: + case Script::TOTO: + case Script::VITHKUQI: + case Script::KAWI: + case Script::NAG_MUNDARI: + break; + } + + // Symbols/dingbats are generally Script=COMMON but may be resolved to any + // surrounding script run. So we'll always append a couple of likely fonts + // for such characters. + const uint32_t b = aCh >> 8; + if (aRunScript == Script::COMMON || // Stray COMMON chars not resolved + (b >= 0x20 && b <= 0x2b) || b == 0x2e || // BMP symbols/punctuation/etc + GetGenCategory(aCh) == nsUGenCategory::kSymbol || + GetGenCategory(aCh) == nsUGenCategory::kPunctuation) { + if (b == 0x27) { + aFontList.AppendElement("Zapf Dingbats"); + } + aFontList.AppendElement("Geneva"); + aFontList.AppendElement("STIXGeneral"); + aFontList.AppendElement("Apple Symbols"); + // Japanese fonts also cover a lot of miscellaneous symbols + aFontList.AppendElement("Hiragino Sans"); + aFontList.AppendElement("Hiragino Kaku Gothic ProN"); + } + + // Arial Unicode MS has lots of glyphs for obscure characters; try it as a + // last resort. + aFontList.AppendElement("Arial Unicode MS"); +} + +/*static*/ +void gfxPlatformMac::LookupSystemFont( + mozilla::LookAndFeel::FontID aSystemFontID, nsACString& aSystemFontName, + gfxFontStyle& aFontStyle) { + return gfxMacPlatformFontList::LookupSystemFont(aSystemFontID, + aSystemFontName, aFontStyle); +} + +uint32_t gfxPlatformMac::ReadAntiAliasingThreshold() { + uint32_t threshold = 0; // default == no threshold + + // first read prefs flag to determine whether to use the setting or not + bool useAntiAliasingThreshold = + Preferences::GetBool("gfx.use_text_smoothing_setting", false); + + // if the pref setting is disabled, return 0 which effectively disables this + // feature + if (!useAntiAliasingThreshold) return threshold; + + // value set via Appearance pref panel, "Turn off text smoothing for font + // sizes xxx and smaller" + CFNumberRef prefValue = (CFNumberRef)CFPreferencesCopyAppValue( + CFSTR("AppleAntiAliasingThreshold"), kCFPreferencesCurrentApplication); + + if (prefValue) { + if (!CFNumberGetValue(prefValue, kCFNumberIntType, &threshold)) { + threshold = 0; + } + CFRelease(prefValue); + } + + return threshold; +} + +bool gfxPlatformMac::AccelerateLayersByDefault() { return true; } + +// This is the renderer output callback function, called on the vsync thread +static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink, + const CVTimeStamp* aNow, + const CVTimeStamp* aOutputTime, + CVOptionFlags aFlagsIn, CVOptionFlags* aFlagsOut, + void* aDisplayLinkContext); + +class OSXVsyncSource final : public VsyncSource { + public: + OSXVsyncSource() + : mDisplayLink(nullptr, "OSXVsyncSource::OSXDisplay::mDisplayLink") { + MOZ_ASSERT(NS_IsMainThread()); + mTimer = NS_NewTimer(); + CGDisplayRegisterReconfigurationCallback(DisplayReconfigurationCallback, + this); + } + + virtual ~OSXVsyncSource() { + MOZ_ASSERT(NS_IsMainThread()); + CGDisplayRemoveReconfigurationCallback(DisplayReconfigurationCallback, + this); + } + + static void RetryEnableVsync(nsITimer* aTimer, void* aOsxVsyncSource) { + MOZ_ASSERT(NS_IsMainThread()); + OSXVsyncSource* osxVsyncSource = + static_cast<OSXVsyncSource*>(aOsxVsyncSource); + MOZ_ASSERT(osxVsyncSource); + osxVsyncSource->EnableVsync(); + } + + void EnableVsync() override { + MOZ_ASSERT(NS_IsMainThread()); + if (IsVsyncEnabled()) { + return; + } + + auto displayLink = mDisplayLink.Lock(); + + // Create a display link capable of being used with all active displays + // TODO: See if we need to create an active DisplayLink for each monitor + // in multi-monitor situations. According to the docs, it is compatible + // with all displays running on the computer But if we have different + // monitors at different display rates, we may hit issues. + CVReturn retval = CVDisplayLinkCreateWithActiveCGDisplays(&*displayLink); + + // Workaround for bug 1201401: CVDisplayLinkCreateWithCGDisplays() + // (called by CVDisplayLinkCreateWithActiveCGDisplays()) sometimes + // creates a CVDisplayLinkRef with an uninitialized (nulled) internal + // pointer. If we continue to use this CVDisplayLinkRef, we will + // eventually crash in CVCGDisplayLink::getDisplayTimes(), where the + // internal pointer is dereferenced. Fortunately, when this happens + // another internal variable is also left uninitialized (zeroed), + // which is accessible via CVDisplayLinkGetCurrentCGDisplay(). In + // normal conditions the current display is never zero. + if ((retval == kCVReturnSuccess) && + (CVDisplayLinkGetCurrentCGDisplay(*displayLink) == 0)) { + retval = kCVReturnInvalidDisplay; + } + + if (retval != kCVReturnSuccess) { + NS_WARNING( + "Could not create a display link with all active displays. " + "Retrying"); + CVDisplayLinkRelease(*displayLink); + *displayLink = nullptr; + + // bug 1142708 - When coming back from sleep, + // or when changing displays, active displays may not be ready yet, + // even if listening for the kIOMessageSystemHasPoweredOn event + // from OS X sleep notifications. + // Active displays are those that are drawable. + // bug 1144638 - When changing display configurations and getting + // notifications from CGDisplayReconfigurationCallBack, the + // callback gets called twice for each active display + // so it's difficult to know when all displays are active. + // Instead, try again soon. The delay is arbitrary. 100ms chosen + // because on a late 2013 15" retina, it takes about that + // long to come back up from sleep. + uint32_t delay = 100; + mTimer->InitWithNamedFuncCallback(RetryEnableVsync, this, delay, + nsITimer::TYPE_ONE_SHOT, + "RetryEnableVsync"); + return; + } + + if (CVDisplayLinkSetOutputCallback(*displayLink, &VsyncCallback, this) != + kCVReturnSuccess) { + NS_WARNING("Could not set displaylink output callback"); + CVDisplayLinkRelease(*displayLink); + *displayLink = nullptr; + return; + } + + mPreviousTimestamp = TimeStamp::Now(); + if (CVDisplayLinkStart(*displayLink) != kCVReturnSuccess) { + NS_WARNING("Could not activate the display link"); + CVDisplayLinkRelease(*displayLink); + *displayLink = nullptr; + } + + CVTime vsyncRate = + CVDisplayLinkGetNominalOutputVideoRefreshPeriod(*displayLink); + if (vsyncRate.flags & kCVTimeIsIndefinite) { + NS_WARNING("Could not get vsync rate, setting to 60."); + mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0); + } else { + int64_t timeValue = vsyncRate.timeValue; + int64_t timeScale = vsyncRate.timeScale; + const int milliseconds = 1000; + float rateInMs = ((double)timeValue / (double)timeScale) * milliseconds; + mVsyncRate = TimeDuration::FromMilliseconds(rateInMs); + } + } + + void DisableVsync() override { + MOZ_ASSERT(NS_IsMainThread()); + if (!IsVsyncEnabled()) { + return; + } + + // Release the display link + auto displayLink = mDisplayLink.Lock(); + if (*displayLink) { + CVDisplayLinkRelease(*displayLink); + *displayLink = nullptr; + } + } + + bool IsVsyncEnabled() override { + MOZ_ASSERT(NS_IsMainThread()); + auto displayLink = mDisplayLink.Lock(); + return *displayLink != nullptr; + } + + TimeDuration GetVsyncRate() override { return mVsyncRate; } + + void Shutdown() override { + MOZ_ASSERT(NS_IsMainThread()); + mTimer->Cancel(); + mTimer = nullptr; + DisableVsync(); + } + + // The vsync timestamps given by the CVDisplayLinkCallback are + // in the future for the NEXT frame. Large parts of Gecko, such + // as animations assume a timestamp at either now or in the past. + // Normalize the timestamps given to the VsyncDispatchers to the vsync + // that just occured, not the vsync that is upcoming. + TimeStamp mPreviousTimestamp; + + private: + static void DisplayReconfigurationCallback(CGDirectDisplayID aDisplay, + CGDisplayChangeSummaryFlags aFlags, + void* aUserInfo) { + static_cast<OSXVsyncSource*>(aUserInfo)->OnDisplayReconfiguration(aDisplay, + aFlags); + } + + void OnDisplayReconfiguration(CGDirectDisplayID aDisplay, + CGDisplayChangeSummaryFlags aFlags) { + // Display reconfiguration notifications are fired in two phases: Before + // the reconfiguration and after the reconfiguration. + // All displays are notified before (with a "BeginConfiguration" flag), + // and the reconfigured displays are notified again after the + // configuration. + if (aFlags & kCGDisplayBeginConfigurationFlag) { + // We're only interested in the "after" notification, for the display + // link's current display. + return; + } + + if (!NS_IsMainThread()) { + return; + } + + bool didReconfigureCurrentDisplayLinkDisplay = false; + { // scope for lock + auto displayLink = mDisplayLink.Lock(); + didReconfigureCurrentDisplayLinkDisplay = + *displayLink && + CVDisplayLinkGetCurrentCGDisplay(*displayLink) == aDisplay; + } + + if (didReconfigureCurrentDisplayLinkDisplay) { + // The link's current display has been reconfigured. + // Recreate the display link, because otherwise it may be stuck with a + // "removed" display forever and never notify us again. + DisableVsync(); + EnableVsync(); + } + } + + // Accessed from main thread and from display reconfiguration callback + // thread... which also happens to be the main thread. + DataMutex<CVDisplayLinkRef> mDisplayLink; + + // Accessed only from the main thread. + RefPtr<nsITimer> mTimer; + TimeDuration mVsyncRate; +}; // OSXVsyncSource + +static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink, + const CVTimeStamp* aNow, + const CVTimeStamp* aOutputTime, + CVOptionFlags aFlagsIn, CVOptionFlags* aFlagsOut, + void* aDisplayLinkContext) { + // Executed on OS X hardware vsync thread + OSXVsyncSource* vsyncSource = (OSXVsyncSource*)aDisplayLinkContext; + + mozilla::TimeStamp outputTime = + mozilla::TimeStamp::FromSystemTime(aOutputTime->hostTime); + mozilla::TimeStamp nextVsync = outputTime; + mozilla::TimeStamp previousVsync = vsyncSource->mPreviousTimestamp; + mozilla::TimeStamp now = TimeStamp::Now(); + + // Snow leopard sometimes sends vsync timestamps very far in the past. + // Normalize the vsync timestamps to now. + if (nextVsync <= previousVsync) { + nextVsync = now; + previousVsync = now; + } else if (now < previousVsync) { + // Bug 1158321 - The VsyncCallback can sometimes execute before the reported + // vsync time. In those cases, normalize the timestamp to Now() as sending + // timestamps in the future has undefined behavior. See the comment above + // OSXVsyncSource::mPreviousTimestamp + previousVsync = now; + } + + vsyncSource->mPreviousTimestamp = nextVsync; + + vsyncSource->NotifyVsync(previousVsync, outputTime); + return kCVReturnSuccess; +} + +already_AddRefed<mozilla::gfx::VsyncSource> +gfxPlatformMac::CreateGlobalHardwareVsyncSource() { + RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource(); + osxVsyncSource->EnableVsync(); + if (!osxVsyncSource->IsVsyncEnabled()) { + NS_WARNING( + "OS X Vsync source not enabled. Falling back to software vsync."); + return GetSoftwareVsyncSource(); + } + + osxVsyncSource->DisableVsync(); + return osxVsyncSource.forget(); +} + +bool gfxPlatformMac::SupportsHDR() { + // HDR has 3 requirements: + // 1) high peak brightness + // 2) high contrast ratio + // 3) color depth > 24 + if (GetScreenDepth() <= 24) { + return false; + } + + // Screen is capable. Is the OS capable? +#ifdef EARLY_BETA_OR_EARLIER + // More-or-less supported in Catalina. + return true; +#else + // Definitely supported in Big Sur. + return nsCocoaFeatures::OnBigSurOrLater(); +#endif +} + +nsTArray<uint8_t> gfxPlatformMac::GetPlatformCMSOutputProfileData() { + nsTArray<uint8_t> prefProfileData = GetPrefCMSOutputProfileData(); + if (!prefProfileData.IsEmpty()) { + return prefProfileData; + } + + CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); + if (!cspace) { + cspace = ::CGColorSpaceCreateDeviceRGB(); + } + if (!cspace) { + return nsTArray<uint8_t>(); + } + + CFDataRef iccp = ::CGColorSpaceCopyICCData(cspace); + + ::CFRelease(cspace); + + if (!iccp) { + return nsTArray<uint8_t>(); + } + + // copy to external buffer + size_t size = static_cast<size_t>(::CFDataGetLength(iccp)); + + nsTArray<uint8_t> result; + + if (size > 0) { + result.AppendElements(::CFDataGetBytePtr(iccp), size); + } + + ::CFRelease(iccp); + + return result; +} + +bool gfxPlatformMac::CheckVariationFontSupport() { return true; } + +void gfxPlatformMac::InitPlatformGPUProcessPrefs() { + FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS); + gpuProc.ForceDisable(FeatureStatus::Blocked, + "GPU process does not work on Mac", + "FEATURE_FAILURE_MAC_GPU_PROC"_ns); +} |