[macOS] Frequent leaks seen under WebCore::gpuIDForDisplayMask
[WebKit-https.git] / Source / WebCore / platform / mac / PlatformScreenMac.mm
1 /*
2  * Copyright (C) 2006-2018 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "PlatformScreen.h"
28
29 #if PLATFORM(MAC)
30
31 #import "FloatRect.h"
32 #import "FrameView.h"
33 #import "HostWindow.h"
34 #import "ScreenProperties.h"
35 #import <ColorSync/ColorSync.h>
36 #import <pal/spi/cg/CoreGraphicsSPI.h>
37 #import <wtf/ProcessPrivilege.h>
38
39 extern "C" {
40 bool CGDisplayUsesInvertedPolarity(void);
41 bool CGDisplayUsesForceToGray(void);
42 }
43
44 namespace WebCore {
45
46 // These functions scale between screen and page coordinates because JavaScript/DOM operations
47 // assume that the screen and the page share the same coordinate system.
48
49 PlatformDisplayID displayID(NSScreen *screen)
50 {
51     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
52     return [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
53 }
54
55 static PlatformDisplayID displayID(Widget* widget)
56 {
57     if (!widget)
58         return 0;
59
60     auto* view = widget->root();
61     if (!view)
62         return 0;
63
64     auto* hostWindow = view->hostWindow();
65     if (!hostWindow)
66         return 0;
67
68     return hostWindow->displayID();
69 }
70
71 // Screen containing the menubar.
72 static NSScreen *firstScreen()
73 {
74     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
75     NSArray *screens = [NSScreen screens];
76     if (![screens count])
77         return nil;
78     return [screens objectAtIndex:0];
79 }
80
81 static NSWindow *window(Widget* widget)
82 {
83     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
84     if (!widget)
85         return nil;
86     return widget->platformWidget().window;
87 }
88
89 static NSScreen *screen(Widget* widget)
90 {
91     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
92     // If the widget is in a window, use that, otherwise use the display ID from the host window.
93     // First case is for when the NSWindow is in the same process, second case for when it's not.
94     if (auto screenFromWindow = window(widget).screen)
95         return screenFromWindow;
96     return screen(displayID(widget));
97 }
98
99 static ScreenProperties& screenProperties()
100 {
101     static NeverDestroyed<ScreenProperties> screenProperties;
102     return screenProperties;
103 }
104
105 PlatformDisplayID primaryScreenDisplayID()
106 {
107     return screenProperties().primaryDisplayID;
108 }
109
110 ScreenProperties collectScreenProperties()
111 {
112     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
113
114     ScreenProperties screenProperties;
115
116     for (NSScreen *screen in [NSScreen screens]) {
117         auto displayID = WebCore::displayID(screen);
118         FloatRect screenAvailableRect = screen.visibleFrame;
119         screenAvailableRect.setY(NSMaxY(screen.frame) - (screenAvailableRect.y() + screenAvailableRect.height())); // flip
120         FloatRect screenRect = screen.frame;
121
122         RetainPtr<CGColorSpaceRef> colorSpace = screen.colorSpace.CGColorSpace;
123
124         int screenDepth = NSBitsPerPixelFromDepth(screen.depth);
125         int screenDepthPerComponent = NSBitsPerSampleFromDepth(screen.depth);
126         bool screenSupportsExtendedColor = [screen canRepresentDisplayGamut:NSDisplayGamutP3];
127         bool screenHasInvertedColors = CGDisplayUsesInvertedPolarity();
128         bool screenIsMonochrome = CGDisplayUsesForceToGray();
129         uint32_t displayMask = CGDisplayIDToOpenGLDisplayMask(displayID);
130         IORegistryGPUID gpuID = 0;
131
132 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
133         if (displayMask)
134             gpuID = gpuIDForDisplayMask(displayMask);
135 #endif
136
137         screenProperties.screenDataMap.set(displayID, ScreenData { screenAvailableRect, screenRect, colorSpace, screenDepth, screenDepthPerComponent, screenSupportsExtendedColor, screenHasInvertedColors, screenIsMonochrome, displayMask, gpuID });
138
139         if (!screenProperties.primaryDisplayID)
140             screenProperties.primaryDisplayID = displayID;
141     }
142
143     return screenProperties;
144 }
145
146 void setScreenProperties(const ScreenProperties& properties)
147 {
148     screenProperties() = properties;
149 }
150
151 static ScreenData screenData(PlatformDisplayID screendisplayID)
152 {
153     RELEASE_ASSERT(!screenProperties().screenDataMap.isEmpty());
154
155     // Return property of the first screen if the screen is not found in the map.
156     auto displayID = screendisplayID ? screendisplayID : primaryScreenDisplayID();
157     if (displayID) {
158         auto screenPropertiesForDisplay = screenProperties().screenDataMap.find(displayID);
159         if (screenPropertiesForDisplay != screenProperties().screenDataMap.end())
160             return screenPropertiesForDisplay->value;
161     }
162
163     // Last resort: use the first item in the screen list.
164     return screenProperties().screenDataMap.begin()->value;
165 }
166
167 uint32_t primaryOpenGLDisplayMask()
168 {
169     if (!screenProperties().screenDataMap.isEmpty())
170         return screenData(primaryScreenDisplayID()).displayMask;
171     
172     return 0;
173 }
174
175 uint32_t displayMaskForDisplay(PlatformDisplayID displayID)
176 {
177     if (!screenProperties().screenDataMap.isEmpty())
178         return screenData(displayID).displayMask;
179     
180     ASSERT_NOT_REACHED();
181     return 0;
182 }
183
184 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
185 IORegistryGPUID primaryGPUID()
186 {
187     return gpuIDForDisplay(screenProperties().primaryDisplayID);
188 }
189
190 IORegistryGPUID gpuIDForDisplay(PlatformDisplayID displayID)
191 {
192 #if ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
193     if (!screenProperties().screenDataMap.isEmpty())
194         return screenData(displayID).gpuID;
195 #else
196     return gpuIDForDisplayMask(CGDisplayIDToOpenGLDisplayMask(displayID));
197 #endif
198     return 0;
199 }
200
201 IORegistryGPUID gpuIDForDisplayMask(GLuint displayMask)
202 {
203     GLint numRenderers = 0;
204     CGLRendererInfoObj rendererInfo = nullptr;
205     CGLError error = CGLQueryRendererInfo(displayMask, &rendererInfo, &numRenderers);
206     if (!numRenderers || !rendererInfo || error != kCGLNoError)
207         return 0;
208
209     // The 0th renderer should not be the software renderer.
210     GLint isAccelerated;
211     error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPAccelerated, &isAccelerated);
212     if (!isAccelerated || error != kCGLNoError) {
213         CGLDestroyRendererInfo(rendererInfo);
214         return 0;
215     }
216
217     GLint gpuIDLow = 0;
218     GLint gpuIDHigh = 0;
219
220     error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow);
221     if (error != kCGLNoError) {
222         CGLDestroyRendererInfo(rendererInfo);
223         return 0;
224     }
225
226     error = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh);
227     if (error != kCGLNoError) {
228         CGLDestroyRendererInfo(rendererInfo);
229         return 0;
230     }
231
232     CGLDestroyRendererInfo(rendererInfo);
233     return (IORegistryGPUID) gpuIDHigh << 32 | gpuIDLow;
234 }
235 #endif // !__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
236
237 static ScreenData getScreenProperties(Widget* widget)
238 {
239     return screenData(displayID(widget));
240 }
241
242 bool screenIsMonochrome(Widget* widget)
243 {
244     if (!screenProperties().screenDataMap.isEmpty())
245         return getScreenProperties(widget).screenIsMonochrome;
246
247     // This is a system-wide accessibility setting, same on all screens.
248     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
249     return CGDisplayUsesForceToGray();
250 }
251
252 bool screenHasInvertedColors()
253 {
254     if (!screenProperties().screenDataMap.isEmpty())
255         return screenData(primaryScreenDisplayID()).screenHasInvertedColors;
256
257     // This is a system-wide accessibility setting, same on all screens.
258     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
259     return CGDisplayUsesInvertedPolarity();
260 }
261
262 int screenDepth(Widget* widget)
263 {
264     if (!screenProperties().screenDataMap.isEmpty()) {
265         auto screenDepth = getScreenProperties(widget).screenDepth;
266         ASSERT(screenDepth);
267         return screenDepth;
268     }
269
270     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
271     return NSBitsPerPixelFromDepth(screen(widget).depth);
272 }
273
274 int screenDepthPerComponent(Widget* widget)
275 {
276     if (!screenProperties().screenDataMap.isEmpty()) {
277         auto depthPerComponent = getScreenProperties(widget).screenDepthPerComponent;
278         ASSERT(depthPerComponent);
279         return depthPerComponent;
280     }
281
282     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
283     return NSBitsPerSampleFromDepth(screen(widget).depth);
284 }
285
286 FloatRect screenRectForDisplay(PlatformDisplayID displayID)
287 {
288     if (!screenProperties().screenDataMap.isEmpty()) {
289         auto screenRect = screenData(displayID).screenRect;
290         ASSERT(!screenRect.isEmpty());
291         return screenRect;
292     }
293
294     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
295     return screen(displayID).frame;
296 }
297
298 FloatRect screenRectForPrimaryScreen()
299 {
300     return screenRectForDisplay(primaryScreenDisplayID());
301 }
302
303 FloatRect screenRect(Widget* widget)
304 {
305     if (!screenProperties().screenDataMap.isEmpty())
306         return getScreenProperties(widget).screenRect;
307
308     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
309     return toUserSpace([screen(widget) frame], window(widget));
310 }
311
312 FloatRect screenAvailableRect(Widget* widget)
313 {
314     if (!screenProperties().screenDataMap.isEmpty())
315         return getScreenProperties(widget).screenAvailableRect;
316
317     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
318     return toUserSpace([screen(widget) visibleFrame], window(widget));
319 }
320
321 NSScreen *screen(NSWindow *window)
322 {
323     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
324     return [window screen] ?: firstScreen();
325 }
326
327 NSScreen *screen(PlatformDisplayID displayID)
328 {
329     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
330     for (NSScreen *screen in [NSScreen screens]) {
331         if (WebCore::displayID(screen) == displayID)
332             return screen;
333     }
334     return firstScreen();
335 }
336
337 CGColorSpaceRef screenColorSpace(Widget* widget)
338 {
339     if (!screenProperties().screenDataMap.isEmpty())
340         return getScreenProperties(widget).colorSpace.get();
341
342     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
343     return screen(widget).colorSpace.CGColorSpace;
344 }
345
346 bool screenSupportsExtendedColor(Widget* widget)
347 {
348     if (!screenProperties().screenDataMap.isEmpty())
349         return getScreenProperties(widget).screenSupportsExtendedColor;
350
351     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
352     return [screen(widget) canRepresentDisplayGamut:NSDisplayGamutP3];
353 }
354
355 FloatRect toUserSpace(const NSRect& rect, NSWindow *destination)
356 {
357     FloatRect userRect = rect;
358     userRect.setY(NSMaxY([screen(destination) frame]) - (userRect.y() + userRect.height())); // flip
359     return userRect;
360 }
361
362 FloatRect toUserSpaceForPrimaryScreen(const NSRect& rect)
363 {
364     FloatRect userRect = rect;
365     userRect.setY(NSMaxY(screenRectForDisplay(primaryScreenDisplayID())) - (userRect.y() + userRect.height())); // flip
366     return userRect;
367 }
368
369 NSRect toDeviceSpace(const FloatRect& rect, NSWindow *source)
370 {
371     FloatRect deviceRect = rect;
372     deviceRect.setY(NSMaxY([screen(source) frame]) - (deviceRect.y() + deviceRect.height())); // flip
373     return deviceRect;
374 }
375
376 NSPoint flipScreenPoint(const NSPoint& screenPoint, NSScreen *screen)
377 {
378     NSPoint flippedPoint = screenPoint;
379     flippedPoint.y = NSMaxY([screen frame]) - flippedPoint.y;
380     return flippedPoint;
381 }
382
383 } // namespace WebCore
384
385 #endif // PLATFORM(MAC)