Switch some RELEASE_ASSERTS to plain debug ASSERTS in PlatformScreenMac.mm
[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 static PlatformDisplayID displayID(NSScreen *screen)
50 {
51     RELEASE_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     RELEASE_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     RELEASE_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     RELEASE_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 HashMap<PlatformDisplayID, ScreenProperties>& screenProperties()
100 {
101     static NeverDestroyed<HashMap<PlatformDisplayID, ScreenProperties>> screenProperties;
102     return screenProperties;
103 }
104
105 static PlatformDisplayID& primaryScreenDisplayID()
106 {
107     static PlatformDisplayID primaryScreenDisplayID = 0;
108     return primaryScreenDisplayID;
109 }
110
111 std::pair<PlatformDisplayID, HashMap<PlatformDisplayID, ScreenProperties>> getScreenProperties()
112 {
113     RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
114
115     HashMap<PlatformDisplayID, ScreenProperties> screenProperties;
116     std::optional<PlatformDisplayID> firstScreen;
117
118     for (NSScreen *screen in [NSScreen screens]) {
119         auto displayID = WebCore::displayID(screen);
120         FloatRect screenAvailableRect = screen.visibleFrame;
121         screenAvailableRect.setY(NSMaxY(screen.frame) - (screenAvailableRect.y() + screenAvailableRect.height())); // flip
122         FloatRect screenRect = screen.frame;
123
124         RetainPtr<CGColorSpaceRef> colorSpace = screen.colorSpace.CGColorSpace;
125
126         int screenDepth = NSBitsPerPixelFromDepth(screen.depth);
127         int screenDepthPerComponent = NSBitsPerSampleFromDepth(screen.depth);
128         bool screenSupportsExtendedColor = [screen canRepresentDisplayGamut:NSDisplayGamutP3];
129         bool screenHasInvertedColors = CGDisplayUsesInvertedPolarity();
130         bool screenIsMonochrome = CGDisplayUsesForceToGray();
131
132         screenProperties.set(displayID, ScreenProperties { screenAvailableRect, screenRect, colorSpace, screenDepth, screenDepthPerComponent, screenSupportsExtendedColor, screenHasInvertedColors, screenIsMonochrome });
133
134         if (!firstScreen)
135             firstScreen = displayID;
136     }
137
138     return { WTFMove(*firstScreen), WTFMove(screenProperties) };
139 }
140
141 void setScreenProperties(PlatformDisplayID primaryScreenID, const HashMap<PlatformDisplayID, ScreenProperties>& properties)
142 {
143     primaryScreenDisplayID() = primaryScreenID;
144     screenProperties() = properties;
145 }
146
147 ScreenProperties screenProperties(PlatformDisplayID screendisplayID)
148 {
149     RELEASE_ASSERT(!screenProperties().isEmpty());
150
151     // Return property of the first screen if the screen is not found in the map.
152     auto displayID = screendisplayID ? screendisplayID : primaryScreenDisplayID();
153     if (displayID) {
154         auto screenPropertiesForDisplay = screenProperties().find(displayID);
155         if (screenPropertiesForDisplay != screenProperties().end())
156             return screenPropertiesForDisplay->value;
157     }
158
159     // Last resort: use the first item in the screen list.
160     return screenProperties().begin()->value;
161 }
162
163 static ScreenProperties getScreenProperties(Widget* widget)
164 {
165     return screenProperties(displayID(widget));
166 }
167
168 bool screenIsMonochrome(Widget* widget)
169 {
170     if (!screenProperties().isEmpty())
171         return getScreenProperties(widget).screenIsMonochrome;
172
173     // This is a system-wide accessibility setting, same on all screens.
174     RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
175     return CGDisplayUsesForceToGray();
176 }
177
178 bool screenHasInvertedColors()
179 {
180     if (!screenProperties().isEmpty())
181         return screenProperties(primaryScreenDisplayID()).screenHasInvertedColors;
182
183     // This is a system-wide accessibility setting, same on all screens.
184     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
185     return CGDisplayUsesInvertedPolarity();
186 }
187
188 int screenDepth(Widget* widget)
189 {
190     if (!screenProperties().isEmpty()) {
191         auto screenDepth = getScreenProperties(widget).screenDepth;
192         ASSERT(screenDepth);
193         return screenDepth;
194     }
195
196     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
197     return NSBitsPerPixelFromDepth(screen(widget).depth);
198 }
199
200 int screenDepthPerComponent(Widget* widget)
201 {
202     if (!screenProperties().isEmpty()) {
203         auto depthPerComponent = getScreenProperties(widget).screenDepthPerComponent;
204         ASSERT(depthPerComponent);
205         return depthPerComponent;
206     }
207
208     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
209     return NSBitsPerSampleFromDepth(screen(widget).depth);
210 }
211
212 FloatRect screenRectForDisplay(PlatformDisplayID displayID)
213 {
214     if (!screenProperties().isEmpty()) {
215         auto screenRect = screenProperties(displayID).screenRect;
216         ASSERT(!screenRect.isEmpty());
217         return screenRect;
218     }
219
220     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
221     return screen(displayID).frame;
222 }
223
224 FloatRect screenRect(Widget* widget)
225 {
226     if (!screenProperties().isEmpty())
227         return getScreenProperties(widget).screenRect;
228
229     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
230     return toUserSpace([screen(widget) frame], window(widget));
231 }
232
233 FloatRect screenAvailableRect(Widget* widget)
234 {
235     if (!screenProperties().isEmpty())
236         return getScreenProperties(widget).screenAvailableRect;
237
238     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
239     return toUserSpace([screen(widget) visibleFrame], window(widget));
240 }
241
242 NSScreen *screen(NSWindow *window)
243 {
244     RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
245     return [window screen] ?: firstScreen();
246 }
247
248 NSScreen *screen(PlatformDisplayID displayID)
249 {
250     RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
251     for (NSScreen *screen in [NSScreen screens]) {
252         if (WebCore::displayID(screen) == displayID)
253             return screen;
254     }
255     return firstScreen();
256 }
257
258 CGColorSpaceRef screenColorSpace(Widget* widget)
259 {
260     if (!screenProperties().isEmpty())
261         return getScreenProperties(widget).colorSpace.get();
262
263     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
264     return screen(widget).colorSpace.CGColorSpace;
265 }
266
267 bool screenSupportsExtendedColor(Widget* widget)
268 {
269     if (!screenProperties().isEmpty())
270         return getScreenProperties(widget).screenSupportsExtendedColor;
271
272     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
273     return [screen(widget) canRepresentDisplayGamut:NSDisplayGamutP3];
274 }
275
276 FloatRect toUserSpace(const NSRect& rect, NSWindow *destination)
277 {
278     FloatRect userRect = rect;
279     userRect.setY(NSMaxY([screen(destination) frame]) - (userRect.y() + userRect.height())); // flip
280     return userRect;
281 }
282
283 FloatRect toUserSpaceForPrimaryScreen(const NSRect& rect)
284 {
285     FloatRect userRect = rect;
286     userRect.setY(NSMaxY(screenRectForDisplay(primaryScreenDisplayID())) - (userRect.y() + userRect.height())); // flip
287     return userRect;
288 }
289
290 NSRect toDeviceSpace(const FloatRect& rect, NSWindow *source)
291 {
292     FloatRect deviceRect = rect;
293     deviceRect.setY(NSMaxY([screen(source) frame]) - (deviceRect.y() + deviceRect.height())); // flip
294     return deviceRect;
295 }
296
297 NSPoint flipScreenPoint(const NSPoint& screenPoint, NSScreen *screen)
298 {
299     NSPoint flippedPoint = screenPoint;
300     flippedPoint.y = NSMaxY([screen frame]) - flippedPoint.y;
301     return flippedPoint;
302 }
303
304 } // namespace WebCore
305
306 #endif // PLATFORM(MAC)