1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "gfxPlatformMac.h"
7 
8 #include "gfxQuartzSurface.h"
9 #include "mozilla/gfx/2D.h"
10 #include "mozilla/gfx/MacIOSurface.h"
11 
12 #include "gfxMacPlatformFontList.h"
13 #include "gfxMacFont.h"
14 #include "gfxCoreTextShaper.h"
15 #include "gfxTextRun.h"
16 #include "gfxUserFontSet.h"
17 
18 #include "nsTArray.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/VsyncDispatcher.h"
21 #include "qcms.h"
22 #include "gfx2DGlue.h"
23 
24 #include <dlfcn.h>
25 #include <CoreVideo/CoreVideo.h>
26 
27 #include "mozilla/layers/CompositorBridgeParent.h"
28 #include "VsyncSource.h"
29 
30 using namespace mozilla;
31 using namespace mozilla::gfx;
32 
33 // cribbed from CTFontManager.h
34 enum {
35    kAutoActivationDisabled = 1
36 };
37 typedef uint32_t AutoActivationSetting;
38 
39 // bug 567552 - disable auto-activation of fonts
40 
41 static void
DisableFontActivation()42 DisableFontActivation()
43 {
44     // get the main bundle identifier
45     CFBundleRef mainBundle = ::CFBundleGetMainBundle();
46     CFStringRef mainBundleID = nullptr;
47 
48     if (mainBundle) {
49         mainBundleID = ::CFBundleGetIdentifier(mainBundle);
50     }
51 
52     // bug 969388 and bug 922590 - mainBundlID as null is sometimes problematic
53     if (!mainBundleID) {
54         NS_WARNING("missing bundle ID, packaging set up incorrectly");
55         return;
56     }
57 
58     // if possible, fetch CTFontManagerSetAutoActivationSetting
59     void (*CTFontManagerSetAutoActivationSettingPtr)
60             (CFStringRef, AutoActivationSetting);
61     CTFontManagerSetAutoActivationSettingPtr =
62         (void (*)(CFStringRef, AutoActivationSetting))
63         dlsym(RTLD_DEFAULT, "CTFontManagerSetAutoActivationSetting");
64 
65     // bug 567552 - disable auto-activation of fonts
66     if (CTFontManagerSetAutoActivationSettingPtr) {
67         CTFontManagerSetAutoActivationSettingPtr(mainBundleID,
68                                                  kAutoActivationDisabled);
69     }
70 }
71 
gfxPlatformMac()72 gfxPlatformMac::gfxPlatformMac()
73 {
74     DisableFontActivation();
75     mFontAntiAliasingThreshold = ReadAntiAliasingThreshold();
76 
77     uint32_t canvasMask = BackendTypeBit(BackendType::SKIA);
78     uint32_t contentMask = BackendTypeBit(BackendType::SKIA);
79     InitBackendPrefs(canvasMask, BackendType::SKIA,
80                      contentMask, BackendType::SKIA);
81 
82     // XXX: Bug 1036682 - we run out of fds on Mac when using tiled layers because
83     // with 256x256 tiles we can easily hit the soft limit of 800 when using double
84     // buffered tiles in e10s, so let's bump the soft limit to the hard limit for the OS
85     // up to a new cap of OPEN_MAX.
86     struct rlimit limits;
87     if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
88         limits.rlim_cur = std::min(rlim_t(OPEN_MAX), limits.rlim_max);
89         if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
90             NS_WARNING("Unable to bump RLIMIT_NOFILE to the maximum number on this OS");
91         }
92     }
93 
94     MacIOSurfaceLib::LoadLibrary();
95 }
96 
~gfxPlatformMac()97 gfxPlatformMac::~gfxPlatformMac()
98 {
99     gfxCoreTextShaper::Shutdown();
100 }
101 
102 gfxPlatformFontList*
CreatePlatformFontList()103 gfxPlatformMac::CreatePlatformFontList()
104 {
105     gfxPlatformFontList* list = new gfxMacPlatformFontList();
106     if (NS_SUCCEEDED(list->InitFontList())) {
107         return list;
108     }
109     gfxPlatformFontList::Shutdown();
110     return nullptr;
111 }
112 
113 already_AddRefed<gfxASurface>
CreateOffscreenSurface(const IntSize & aSize,gfxImageFormat aFormat)114 gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize,
115                                        gfxImageFormat aFormat)
116 {
117     if (!Factory::AllowedSurfaceSize(aSize)) {
118         return nullptr;
119     }
120 
121     RefPtr<gfxASurface> newSurface =
122       new gfxQuartzSurface(aSize, aFormat);
123     return newSurface.forget();
124 }
125 
126 already_AddRefed<ScaledFont>
GetScaledFontForFont(DrawTarget * aTarget,gfxFont * aFont)127 gfxPlatformMac::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
128 {
129     gfxMacFont *font = static_cast<gfxMacFont*>(aFont);
130     return font->GetScaledFont(aTarget);
131 }
132 
133 gfxFontGroup *
CreateFontGroup(const FontFamilyList & aFontFamilyList,const gfxFontStyle * aStyle,gfxTextPerfMetrics * aTextPerf,gfxUserFontSet * aUserFontSet,gfxFloat aDevToCssSize)134 gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList,
135                                 const gfxFontStyle *aStyle,
136                                 gfxTextPerfMetrics* aTextPerf,
137                                 gfxUserFontSet *aUserFontSet,
138                                 gfxFloat aDevToCssSize)
139 {
140     return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
141                             aUserFontSet, aDevToCssSize);
142 }
143 
144 bool
IsFontFormatSupported(nsIURI * aFontURI,uint32_t aFormatFlags)145 gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
146 {
147     // check for strange format flags
148     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
149                  "strange font format hint set");
150 
151     // accept supported formats
152     if (aFormatFlags & (gfxUserFontSet::FLAG_FORMATS_COMMON |
153                         gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT)) {
154         return true;
155     }
156 
157     // reject all other formats, known and unknown
158     if (aFormatFlags != 0) {
159         return false;
160     }
161 
162     // no format hint set, need to look at data
163     return true;
164 }
165 
166 static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
167 static const char kFontAppleBraille[] = "Apple Braille";
168 static const char kFontAppleColorEmoji[] = "Apple Color Emoji";
169 static const char kFontAppleSymbols[] = "Apple Symbols";
170 static const char kFontDevanagariSangamMN[] = "Devanagari Sangam MN";
171 static const char kFontEuphemiaUCAS[] = "Euphemia UCAS";
172 static const char kFontGeneva[] = "Geneva";
173 static const char kFontGeezaPro[] = "Geeza Pro";
174 static const char kFontGujaratiSangamMN[] = "Gujarati Sangam MN";
175 static const char kFontGurmukhiMN[] = "Gurmukhi MN";
176 static const char kFontHiraginoKakuGothic[] = "Hiragino Kaku Gothic ProN";
177 static const char kFontHiraginoSansGB[] = "Hiragino Sans GB";
178 static const char kFontKefa[] = "Kefa";
179 static const char kFontKhmerMN[] = "Khmer MN";
180 static const char kFontLaoMN[] = "Lao MN";
181 static const char kFontLucidaGrande[] = "Lucida Grande";
182 static const char kFontMenlo[] = "Menlo";
183 static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
184 static const char kFontMingLiUExtB[] = "MingLiU-ExtB";
185 static const char kFontMyanmarMN[] = "Myanmar MN";
186 static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
187 static const char kFontSimSunExtB[] = "SimSun-ExtB";
188 static const char kFontSongtiSC[] = "Songti SC";
189 static const char kFontSTHeiti[] = "STHeiti";
190 static const char kFontSTIXGeneral[] = "STIXGeneral";
191 static const char kFontTamilMN[] = "Tamil MN";
192 
193 void
GetCommonFallbackFonts(uint32_t aCh,uint32_t aNextCh,Script aRunScript,nsTArray<const char * > & aFontList)194 gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
195                                        Script aRunScript,
196                                        nsTArray<const char*>& aFontList)
197 {
198     if (aNextCh == 0xfe0f) {
199         aFontList.AppendElement(kFontAppleColorEmoji);
200     }
201 
202     aFontList.AppendElement(kFontLucidaGrande);
203 
204     if (!IS_IN_BMP(aCh)) {
205         uint32_t p = aCh >> 16;
206         uint32_t b = aCh >> 8;
207         if (p == 1) {
208             if (b >= 0x1f0 && b < 0x1f7) {
209                 aFontList.AppendElement(kFontAppleColorEmoji);
210             } else {
211                 aFontList.AppendElement(kFontAppleSymbols);
212                 aFontList.AppendElement(kFontSTIXGeneral);
213                 aFontList.AppendElement(kFontGeneva);
214             }
215         } else if (p == 2) {
216             // OSX installations with MS Office may have these fonts
217             aFontList.AppendElement(kFontMingLiUExtB);
218             aFontList.AppendElement(kFontSimSunExtB);
219         }
220     } else {
221         uint32_t b = (aCh >> 8) & 0xff;
222 
223         switch (b) {
224         case 0x03:
225         case 0x05:
226             aFontList.AppendElement(kFontGeneva);
227             break;
228         case 0x07:
229             aFontList.AppendElement(kFontGeezaPro);
230             break;
231         case 0x09:
232             aFontList.AppendElement(kFontDevanagariSangamMN);
233             break;
234         case 0x0a:
235             aFontList.AppendElement(kFontGurmukhiMN);
236             aFontList.AppendElement(kFontGujaratiSangamMN);
237             break;
238         case 0x0b:
239             aFontList.AppendElement(kFontTamilMN);
240             break;
241         case 0x0e:
242             aFontList.AppendElement(kFontLaoMN);
243             break;
244         case 0x0f:
245             aFontList.AppendElement(kFontSongtiSC);
246             break;
247         case 0x10:
248             aFontList.AppendElement(kFontMenlo);
249             aFontList.AppendElement(kFontMyanmarMN);
250             break;
251         case 0x13:  // Cherokee
252             aFontList.AppendElement(kFontPlantagenetCherokee);
253             aFontList.AppendElement(kFontKefa);
254             break;
255         case 0x14:  // Unified Canadian Aboriginal Syllabics
256         case 0x15:
257         case 0x16:
258             aFontList.AppendElement(kFontEuphemiaUCAS);
259             aFontList.AppendElement(kFontGeneva);
260             break;
261         case 0x18:  // Mongolian, UCAS
262             aFontList.AppendElement(kFontSTHeiti);
263             aFontList.AppendElement(kFontEuphemiaUCAS);
264             break;
265         case 0x19:  // Khmer
266             aFontList.AppendElement(kFontKhmerMN);
267             aFontList.AppendElement(kFontMicrosoftTaiLe);
268             break;
269         case 0x1d:
270         case 0x1e:
271             aFontList.AppendElement(kFontGeneva);
272             break;
273         case 0x20:  // Symbol ranges
274         case 0x21:
275         case 0x22:
276         case 0x23:
277         case 0x24:
278         case 0x25:
279         case 0x26:
280         case 0x27:
281         case 0x29:
282         case 0x2a:
283         case 0x2b:
284         case 0x2e:
285             aFontList.AppendElement(kFontHiraginoKakuGothic);
286             aFontList.AppendElement(kFontAppleSymbols);
287             aFontList.AppendElement(kFontMenlo);
288             aFontList.AppendElement(kFontSTIXGeneral);
289             aFontList.AppendElement(kFontGeneva);
290             aFontList.AppendElement(kFontAppleColorEmoji);
291             break;
292         case 0x2c:
293             aFontList.AppendElement(kFontGeneva);
294             break;
295         case 0x2d:
296             aFontList.AppendElement(kFontKefa);
297             aFontList.AppendElement(kFontGeneva);
298             break;
299         case 0x28:  // Braille
300             aFontList.AppendElement(kFontAppleBraille);
301             break;
302         case 0x31:
303             aFontList.AppendElement(kFontHiraginoSansGB);
304             break;
305         case 0x4d:
306             aFontList.AppendElement(kFontAppleSymbols);
307             break;
308         case 0xa0:  // Yi
309         case 0xa1:
310         case 0xa2:
311         case 0xa3:
312         case 0xa4:
313             aFontList.AppendElement(kFontSTHeiti);
314             break;
315         case 0xa6:
316         case 0xa7:
317             aFontList.AppendElement(kFontGeneva);
318             aFontList.AppendElement(kFontAppleSymbols);
319             break;
320         case 0xab:
321             aFontList.AppendElement(kFontKefa);
322             break;
323         case 0xfc:
324         case 0xff:
325             aFontList.AppendElement(kFontAppleSymbols);
326             break;
327         default:
328             break;
329         }
330     }
331 
332     // Arial Unicode MS has lots of glyphs for obscure, use it as a last resort
333     aFontList.AppendElement(kFontArialUnicodeMS);
334 }
335 
336 /*static*/ void
LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,nsAString & aSystemFontName,gfxFontStyle & aFontStyle,float aDevPixPerCSSPixel)337 gfxPlatformMac::LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
338                                  nsAString& aSystemFontName,
339                                  gfxFontStyle& aFontStyle,
340                                  float aDevPixPerCSSPixel)
341 {
342     gfxMacPlatformFontList* pfl = gfxMacPlatformFontList::PlatformFontList();
343     return pfl->LookupSystemFont(aSystemFontID, aSystemFontName, aFontStyle,
344                                  aDevPixPerCSSPixel);
345 }
346 
347 uint32_t
ReadAntiAliasingThreshold()348 gfxPlatformMac::ReadAntiAliasingThreshold()
349 {
350     uint32_t threshold = 0;  // default == no threshold
351 
352     // first read prefs flag to determine whether to use the setting or not
353     bool useAntiAliasingThreshold = Preferences::GetBool("gfx.use_text_smoothing_setting", false);
354 
355     // if the pref setting is disabled, return 0 which effectively disables this feature
356     if (!useAntiAliasingThreshold)
357         return threshold;
358 
359     // value set via Appearance pref panel, "Turn off text smoothing for font sizes xxx and smaller"
360     CFNumberRef prefValue = (CFNumberRef)CFPreferencesCopyAppValue(CFSTR("AppleAntiAliasingThreshold"), kCFPreferencesCurrentApplication);
361 
362     if (prefValue) {
363         if (!CFNumberGetValue(prefValue, kCFNumberIntType, &threshold)) {
364             threshold = 0;
365         }
366         CFRelease(prefValue);
367     }
368 
369     return threshold;
370 }
371 
372 bool
AccelerateLayersByDefault()373 gfxPlatformMac::AccelerateLayersByDefault()
374 {
375   return true;
376 }
377 
378 // This is the renderer output callback function, called on the vsync thread
379 static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
380                               const CVTimeStamp* aNow,
381                               const CVTimeStamp* aOutputTime,
382                               CVOptionFlags aFlagsIn,
383                               CVOptionFlags* aFlagsOut,
384                               void* aDisplayLinkContext);
385 
386 class OSXVsyncSource final : public VsyncSource
387 {
388 public:
OSXVsyncSource()389   OSXVsyncSource()
390   {
391   }
392 
GetGlobalDisplay()393   virtual Display& GetGlobalDisplay() override
394   {
395     return mGlobalDisplay;
396   }
397 
398   class OSXDisplay final : public VsyncSource::Display
399   {
400   public:
OSXDisplay()401     OSXDisplay()
402       : mDisplayLink(nullptr)
403     {
404       MOZ_ASSERT(NS_IsMainThread());
405       mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
406     }
407 
~OSXDisplay()408     ~OSXDisplay()
409     {
410       MOZ_ASSERT(NS_IsMainThread());
411     }
412 
RetryEnableVsync(nsITimer * aTimer,void * aOsxDisplay)413     static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
414     {
415       MOZ_ASSERT(NS_IsMainThread());
416       OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
417       MOZ_ASSERT(osxDisplay);
418       osxDisplay->EnableVsync();
419     }
420 
EnableVsync()421     virtual void EnableVsync() override
422     {
423       MOZ_ASSERT(NS_IsMainThread());
424       if (IsVsyncEnabled()) {
425         return;
426       }
427 
428       // Create a display link capable of being used with all active displays
429       // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
430       // situations. According to the docs, it is compatible with all displays running on the computer
431       // But if we have different monitors at different display rates, we may hit issues.
432       if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
433         NS_WARNING("Could not create a display link with all active displays. Retrying");
434         CVDisplayLinkRelease(mDisplayLink);
435         mDisplayLink = nullptr;
436 
437         // bug 1142708 - When coming back from sleep,
438         // or when changing displays, active displays may not be ready yet,
439         // even if listening for the kIOMessageSystemHasPoweredOn event
440         // from OS X sleep notifications.
441         // Active displays are those that are drawable.
442         // bug 1144638 - When changing display configurations and getting
443         // notifications from CGDisplayReconfigurationCallBack, the
444         // callback gets called twice for each active display
445         // so it's difficult to know when all displays are active.
446         // Instead, try again soon. The delay is arbitrary. 100ms chosen
447         // because on a late 2013 15" retina, it takes about that
448         // long to come back up from sleep.
449         uint32_t delay = 100;
450         mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT);
451         return;
452       }
453 
454       if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
455         NS_WARNING("Could not set displaylink output callback");
456         CVDisplayLinkRelease(mDisplayLink);
457         mDisplayLink = nullptr;
458         return;
459       }
460 
461       mPreviousTimestamp = TimeStamp::Now();
462       if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
463         NS_WARNING("Could not activate the display link");
464         CVDisplayLinkRelease(mDisplayLink);
465         mDisplayLink = nullptr;
466       }
467 
468       CVTime vsyncRate = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(mDisplayLink);
469       if (vsyncRate.flags & kCVTimeIsIndefinite) {
470         NS_WARNING("Could not get vsync rate, setting to 60.");
471         mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
472       } else {
473         int64_t timeValue = vsyncRate.timeValue;
474         int64_t timeScale = vsyncRate.timeScale;
475         const int milliseconds = 1000;
476         float rateInMs = ((double) timeValue / (double) timeScale) * milliseconds;
477         mVsyncRate = TimeDuration::FromMilliseconds(rateInMs);
478       }
479     }
480 
DisableVsync()481     virtual void DisableVsync() override
482     {
483       MOZ_ASSERT(NS_IsMainThread());
484       if (!IsVsyncEnabled()) {
485         return;
486       }
487 
488       // Release the display link
489       if (mDisplayLink) {
490         CVDisplayLinkRelease(mDisplayLink);
491         mDisplayLink = nullptr;
492       }
493     }
494 
IsVsyncEnabled()495     virtual bool IsVsyncEnabled() override
496     {
497       MOZ_ASSERT(NS_IsMainThread());
498       return mDisplayLink != nullptr;
499     }
500 
GetVsyncRate()501     virtual TimeDuration GetVsyncRate() override
502     {
503       return mVsyncRate;
504     }
505 
Shutdown()506     virtual void Shutdown() override
507     {
508       MOZ_ASSERT(NS_IsMainThread());
509       mTimer->Cancel();
510       mTimer = nullptr;
511       DisableVsync();
512     }
513 
514     // The vsync timestamps given by the CVDisplayLinkCallback are
515     // in the future for the NEXT frame. Large parts of Gecko, such
516     // as animations assume a timestamp at either now or in the past.
517     // Normalize the timestamps given to the VsyncDispatchers to the vsync
518     // that just occured, not the vsync that is upcoming.
519     TimeStamp mPreviousTimestamp;
520 
521   private:
522     // Manages the display link render thread
523     CVDisplayLinkRef   mDisplayLink;
524     RefPtr<nsITimer> mTimer;
525     TimeDuration mVsyncRate;
526   }; // OSXDisplay
527 
528 private:
~OSXVsyncSource()529   virtual ~OSXVsyncSource()
530   {
531   }
532 
533   OSXDisplay mGlobalDisplay;
534 }; // OSXVsyncSource
535 
VsyncCallback(CVDisplayLinkRef aDisplayLink,const CVTimeStamp * aNow,const CVTimeStamp * aOutputTime,CVOptionFlags aFlagsIn,CVOptionFlags * aFlagsOut,void * aDisplayLinkContext)536 static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
537                               const CVTimeStamp* aNow,
538                               const CVTimeStamp* aOutputTime,
539                               CVOptionFlags aFlagsIn,
540                               CVOptionFlags* aFlagsOut,
541                               void* aDisplayLinkContext)
542 {
543   // Executed on OS X hardware vsync thread
544   OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
545   int64_t nextVsyncTimestamp = aOutputTime->hostTime;
546 
547   mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
548   mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
549   mozilla::TimeStamp now = TimeStamp::Now();
550 
551   // Snow leopard sometimes sends vsync timestamps very far in the past.
552   // Normalize the vsync timestamps to now.
553   if (nextVsync <= previousVsync) {
554     nextVsync = now;
555     previousVsync = now;
556   } else if (now < previousVsync) {
557     // Bug 1158321 - The VsyncCallback can sometimes execute before the reported
558     // vsync time. In those cases, normalize the timestamp to Now() as sending
559     // timestamps in the future has undefined behavior. See the comment above
560     // OSXDisplay::mPreviousTimestamp
561     previousVsync = now;
562   }
563 
564   display->mPreviousTimestamp = nextVsync;
565 
566   display->NotifyVsync(previousVsync);
567   return kCVReturnSuccess;
568 }
569 
570 already_AddRefed<mozilla::gfx::VsyncSource>
CreateHardwareVsyncSource()571 gfxPlatformMac::CreateHardwareVsyncSource()
572 {
573   RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource();
574   VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay();
575   primaryDisplay.EnableVsync();
576   if (!primaryDisplay.IsVsyncEnabled()) {
577     NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.");
578     return gfxPlatform::CreateHardwareVsyncSource();
579   }
580 
581   primaryDisplay.DisableVsync();
582   return osxVsyncSource.forget();
583 }
584 
585 void
GetPlatformCMSOutputProfile(void * & mem,size_t & size)586 gfxPlatformMac::GetPlatformCMSOutputProfile(void* &mem, size_t &size)
587 {
588     mem = nullptr;
589     size = 0;
590 
591     CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
592     if (!cspace) {
593         cspace = ::CGColorSpaceCreateDeviceRGB();
594     }
595     if (!cspace) {
596         return;
597     }
598 
599     CFDataRef iccp = ::CGColorSpaceCopyICCProfile(cspace);
600 
601     ::CFRelease(cspace);
602 
603     if (!iccp) {
604         return;
605     }
606 
607     // copy to external buffer
608     size = static_cast<size_t>(::CFDataGetLength(iccp));
609     if (size > 0) {
610         void *data = malloc(size);
611         if (data) {
612             memcpy(data, ::CFDataGetBytePtr(iccp), size);
613             mem = data;
614         } else {
615             size = 0;
616         }
617     }
618 
619     ::CFRelease(iccp);
620 }
621