Bug 1734943 [wpt PR 31170] - Correct scrolling contents cull rect, a=testonly
[gecko.git] / widget / ScreenManager.cpp
blob7c8bab31c3af6f4dc654de80116bee9ca54a93e8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScreenManager.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "mozilla/dom/ContentParent.h"
11 #include "mozilla/dom/DOMTypes.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/StaticPtr.h"
14 #ifdef MOZ_WAYLAND
15 # include "mozilla/WidgetUtilsGtk.h"
16 #endif /* MOZ_WAYLAND */
18 static mozilla::LazyLogModule sScreenLog("WidgetScreen");
20 namespace mozilla::widget {
22 NS_IMPL_ISUPPORTS(ScreenManager, nsIScreenManager)
24 ScreenManager::ScreenManager() = default;
26 ScreenManager::~ScreenManager() = default;
28 static StaticRefPtr<ScreenManager> sSingleton;
30 ScreenManager& ScreenManager::GetSingleton() {
31 if (!sSingleton) {
32 sSingleton = new ScreenManager();
33 ClearOnShutdown(&sSingleton);
35 return *sSingleton;
38 already_AddRefed<ScreenManager> ScreenManager::GetAddRefedSingleton() {
39 RefPtr<ScreenManager> sm = &GetSingleton();
40 return sm.forget();
43 void ScreenManager::SetHelper(UniquePtr<Helper> aHelper) {
44 mHelper = std::move(aHelper);
47 void ScreenManager::Refresh(nsTArray<RefPtr<Screen>>&& aScreens) {
48 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens"));
50 mScreenList = std::move(aScreens);
52 CopyScreensToAllRemotesIfIsParent();
55 void ScreenManager::Refresh(nsTArray<mozilla::dom::ScreenDetails>&& aScreens) {
56 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refresh screens from IPC"));
58 mScreenList.Clear();
59 for (auto& screen : aScreens) {
60 mScreenList.AppendElement(new Screen(screen));
63 CopyScreensToAllRemotesIfIsParent();
66 template <class Range>
67 void ScreenManager::CopyScreensToRemoteRange(Range aRemoteRange) {
68 AutoTArray<dom::ScreenDetails, 4> screens;
69 for (auto& screen : mScreenList) {
70 screens.AppendElement(screen->ToScreenDetails());
72 for (auto cp : aRemoteRange) {
73 MOZ_LOG(sScreenLog, LogLevel::Debug,
74 ("Send screens to [Pid %d]", cp->Pid()));
75 if (!cp->SendRefreshScreens(screens)) {
76 MOZ_LOG(sScreenLog, LogLevel::Error,
77 ("SendRefreshScreens to [Pid %d] failed", cp->Pid()));
82 void ScreenManager::CopyScreensToRemote(dom::ContentParent* aContentParent) {
83 MOZ_ASSERT(aContentParent);
84 MOZ_ASSERT(XRE_IsParentProcess());
86 auto range = {aContentParent};
87 CopyScreensToRemoteRange(range);
90 void ScreenManager::CopyScreensToAllRemotesIfIsParent() {
91 if (XRE_IsContentProcess()) {
92 return;
95 MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing all ContentParents"));
97 CopyScreensToRemoteRange(
98 dom::ContentParent::AllProcesses(dom::ContentParent::eLive));
101 // Returns the screen that contains the rectangle. If the rect overlaps
102 // multiple screens, it picks the screen with the greatest area of intersection.
104 // The coordinates are in desktop pixels.
106 NS_IMETHODIMP
107 ScreenManager::ScreenForRect(int32_t aX, int32_t aY, int32_t aWidth,
108 int32_t aHeight, nsIScreen** aOutScreen) {
109 #if defined(MOZ_WAYLAND) && defined(MOZ_LOGGING)
110 static bool inWayland = mozilla::widget::GdkIsWaylandDisplay();
111 if (inWayland) {
112 MOZ_LOG(sScreenLog, LogLevel::Warning,
113 ("Getting screen in wayland, primary display will be returned."));
115 #endif
117 if (mScreenList.IsEmpty()) {
118 MOZ_LOG(sScreenLog, LogLevel::Warning,
119 ("No screen available. This can happen in xpcshell."));
120 RefPtr<Screen> ret = new Screen(
121 LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0,
122 DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */);
123 ret.forget(aOutScreen);
124 return NS_OK;
127 // Optimize for the common case. If the number of screens is only
128 // one then just return the primary screen.
129 if (mScreenList.Length() == 1) {
130 return GetPrimaryScreen(aOutScreen);
133 // which screen should we return?
134 Screen* which = mScreenList[0].get();
136 // walk the list of screens and find the one that has the most
137 // surface area.
138 uint32_t area = 0;
139 DesktopIntRect windowRect(aX, aY, aWidth, aHeight);
140 for (auto& screen : mScreenList) {
141 int32_t x, y, width, height;
142 x = y = width = height = 0;
143 screen->GetRectDisplayPix(&x, &y, &width, &height);
144 // calculate the surface area
145 DesktopIntRect screenRect(x, y, width, height);
146 screenRect.IntersectRect(screenRect, windowRect);
147 uint32_t tempArea = screenRect.Area();
148 if (tempArea > area) {
149 which = screen.get();
150 area = tempArea;
154 // If the rect intersects one or more screen,
155 // return the screen that has the largest intersection.
156 if (area > 0) {
157 RefPtr<Screen> ret = which;
158 ret.forget(aOutScreen);
159 return NS_OK;
162 // If the rect does not intersect a screen, find
163 // a screen that is nearest to the rect.
164 uint32_t distance = UINT32_MAX;
165 for (auto& screen : mScreenList) {
166 int32_t x, y, width, height;
167 x = y = width = height = 0;
168 screen->GetRectDisplayPix(&x, &y, &width, &height);
170 uint32_t distanceX = 0;
171 if (aX > (x + width)) {
172 distanceX = aX - (x + width);
173 } else if ((aX + aWidth) < x) {
174 distanceX = x - (aX + aWidth);
177 uint32_t distanceY = 0;
178 if (aY > (y + height)) {
179 distanceY = aY - (y + height);
180 } else if ((aY + aHeight) < y) {
181 distanceY = y - (aY + aHeight);
184 uint32_t tempDistance = distanceX * distanceX + distanceY * distanceY;
185 if (tempDistance < distance) {
186 which = screen.get();
187 distance = tempDistance;
188 if (distance == 0) {
189 break;
194 RefPtr<Screen> ret = which;
195 ret.forget(aOutScreen);
196 return NS_OK;
199 // The screen with the menubar/taskbar. This shouldn't be needed very
200 // often.
202 NS_IMETHODIMP
203 ScreenManager::GetPrimaryScreen(nsIScreen** aPrimaryScreen) {
204 if (mScreenList.IsEmpty()) {
205 MOZ_LOG(sScreenLog, LogLevel::Warning,
206 ("No screen available. This can happen in xpcshell."));
207 RefPtr<Screen> ret = new Screen(
208 LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0,
209 DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */);
210 ret.forget(aPrimaryScreen);
211 return NS_OK;
214 RefPtr<Screen> ret = mScreenList[0];
215 ret.forget(aPrimaryScreen);
216 return NS_OK;
219 NS_IMETHODIMP
220 ScreenManager::GetTotalScreenPixels(int64_t* aTotalScreenPixels) {
221 MOZ_ASSERT(aTotalScreenPixels);
223 if (mScreenList.IsEmpty()) {
224 MOZ_LOG(sScreenLog, LogLevel::Warning,
225 ("No screen available. This can happen in xpcshell."));
226 *aTotalScreenPixels = 0;
227 return NS_OK;
230 int64_t pixels = 0;
231 for (auto& screen : mScreenList) {
232 int32_t x, y, width, height;
233 x = y = width = height = 0;
234 screen->GetRect(&x, &y, &width, &height);
235 pixels += width * height;
238 *aTotalScreenPixels = pixels;
239 return NS_OK;
242 } // namespace mozilla::widget