Bug 1194751 - Part 5. Implement ScreenHelperGTK and delete old nsScreenManagerGtk/nsScreenGtk. r?karlt draft
authorKan-Ru Chen <kanru@kanru.info>
Thu, 09 Mar 2017 19:29:44 +0800
changeset 551528 54d15aa4e182397e3fe607bb1bc5ee5993c1d4f9
parent 551527 96a8407046ed436da88719438eb52f964f24671e
child 551529 17ba14ac6268c44e4f920ed04e29cdcaa527bed2
child 551537 4fd5f9bf5c51e20bc1b4db800addb17b32bda125
child 551544 2801ce20c65894e7ac9fdad3636b97a489d55e18
push id51072
push userkchen@mozilla.com
push dateSun, 26 Mar 2017 23:21:39 +0000
reviewerskarlt
bugs1194751
milestone55.0a1
Bug 1194751 - Part 5. Implement ScreenHelperGTK and delete old nsScreenManagerGtk/nsScreenGtk. r?karlt ScreenHelperGTK is the platform dependent part of the original nsScreenManagerGtk and nsScreenGtk. It registers monitors-changed event listener from gtk and pushes updates to ScreenManager. See patch part 4. for how ScreenManager works. MozReview-Commit-ID: KBo7ZLFTjM3
gfx/thebes/gfxXlibSurface.h
widget/gtk/ScreenHelperGTK.cpp
widget/gtk/ScreenHelperGTK.h
widget/gtk/moz.build
widget/gtk/nsAppShell.cpp
widget/gtk/nsDragService.cpp
widget/gtk/nsLookAndFeel.cpp
widget/gtk/nsNativeThemeGTK.cpp
widget/gtk/nsScreenGtk.cpp
widget/gtk/nsScreenGtk.h
widget/gtk/nsScreenManagerGtk.cpp
widget/gtk/nsScreenManagerGtk.h
widget/gtk/nsWidgetFactory.cpp
widget/gtk/nsWindow.cpp
--- a/gfx/thebes/gfxXlibSurface.h
+++ b/gfx/thebes/gfxXlibSurface.h
@@ -31,51 +31,51 @@ public:
     gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual);
 
     // construct a wrapper around the specified drawable with dpy/visual,
     // and known width/height.
     gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const mozilla::gfx::IntSize& size);
 
     // construct a wrapper around the specified drawable with dpy/format,
     // and known width/height.
-    gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format,
+    gfxXlibSurface(::Screen *screen, Drawable drawable, XRenderPictFormat *format,
                    const mozilla::gfx::IntSize& size);
 
     explicit gfxXlibSurface(cairo_surface_t *csurf);
 
     // create a new Pixmap and wrapper surface.
     // |relatedDrawable| provides a hint to the server for determining whether
     // the pixmap should be in video or system memory.  It must be on
     // |screen| (if specified).
     static already_AddRefed<gfxXlibSurface>
-    Create(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size,
+    Create(::Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size,
            Drawable relatedDrawable = X11None);
     static cairo_surface_t *
-    CreateCairoSurface(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size,
+    CreateCairoSurface(::Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size,
                        Drawable relatedDrawable = X11None);
     static already_AddRefed<gfxXlibSurface>
-    Create(Screen* screen, XRenderPictFormat *format, const mozilla::gfx::IntSize& size,
+    Create(::Screen* screen, XRenderPictFormat *format, const mozilla::gfx::IntSize& size,
            Drawable relatedDrawable = X11None);
 
     virtual ~gfxXlibSurface();
 
     virtual already_AddRefed<gfxASurface>
     CreateSimilarSurface(gfxContentType aType,
                          const mozilla::gfx::IntSize& aSize) override;
     virtual void Finish() override;
 
     virtual const mozilla::gfx::IntSize GetSize() const override;
 
     Display* XDisplay() { return mDisplay; }
-    Screen* XScreen();
+    ::Screen* XScreen();
     Drawable XDrawable() { return mDrawable; }
     XRenderPictFormat* XRenderFormat();
 
-    static int DepthOfVisual(const Screen* screen, const Visual* visual);
-    static Visual* FindVisual(Screen* screen, gfxImageFormat format);
+    static int DepthOfVisual(const ::Screen* screen, const Visual* visual);
+    static Visual* FindVisual(::Screen* screen, gfxImageFormat format);
     static XRenderPictFormat *FindRenderFormat(Display *dpy, gfxImageFormat format);
     static bool GetColormapAndVisual(cairo_surface_t* aXlibSurface, Colormap* colormap, Visual **visual);
 
     // take ownership of a passed-in Pixmap, calling XFreePixmap on it
     // when the gfxXlibSurface is destroyed.
     void TakePixmap();
 
     // Release ownership of this surface's Pixmap.  This is only valid
rename from widget/gtk/nsScreenManagerGtk.cpp
rename to widget/gtk/ScreenHelperGTK.cpp
--- a/widget/gtk/nsScreenManagerGtk.cpp
+++ b/widget/gtk/ScreenHelperGTK.cpp
@@ -1,77 +1,113 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "nsScreenManagerGtk.h"
-
-#include "mozilla/RefPtr.h"
-#include "nsScreenGtk.h"
-#include "nsIComponentManager.h"
-#include "nsRect.h"
-#include "nsGtkUtils.h"
-
-#define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1)
+#include "ScreenHelperGTK.h"
 
 #ifdef MOZ_X11
+#include <X11/Xatom.h>
 #include <gdk/gdkx.h>
+// from Xinerama.h
+typedef struct {
+   int   screen_number;
+   short x_org;
+   short y_org;
+   short width;
+   short height;
+} XineramaScreenInfo;
 // prototypes from Xinerama.h
 typedef Bool (*_XnrmIsActive_fn)(Display *dpy);
 typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number);
 #endif
-
+#include <dlfcn.h>
 #include <gtk/gtk.h>
 
-void
+#include "gfxPlatformGtk.h"
+#include "mozilla/Logging.h"
+#include "nsGtkUtils.h"
+#include "nsTArray.h"
+
+#define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1)
+
+namespace mozilla {
+namespace widget {
+
+static LazyLogModule sScreenLog("WidgetScreen");
+
+static void
 monitors_changed(GdkScreen* aScreen, gpointer aClosure)
 {
-  nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure);
-  manager->Init();
+  MOZ_LOG(sScreenLog, LogLevel::Debug, ("Received monitors-changed event"));
+  ScreenHelperGTK* self = static_cast<ScreenHelperGTK*>(aClosure);
+  self->RefreshScreens();
 }
 
 static GdkFilterReturn
-root_window_event_filter(GdkXEvent *aGdkXEvent, GdkEvent *aGdkEvent,
+root_window_event_filter(GdkXEvent* aGdkXEvent, GdkEvent* aGdkEvent,
                          gpointer aClosure)
 {
-  nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure);
 #ifdef MOZ_X11
+  ScreenHelperGTK* self = static_cast<ScreenHelperGTK*>(aClosure);
   XEvent *xevent = static_cast<XEvent*>(aGdkXEvent);
 
-  // See comments in nsScreenGtk::Init below.
   switch (xevent->type) {
     case PropertyNotify:
       {
         XPropertyEvent *propertyEvent = &xevent->xproperty;
-        if (propertyEvent->atom == manager->NetWorkareaAtom()) {
-          manager->Init();
+        if (propertyEvent->atom == self->NetWorkareaAtom()) {
+          MOZ_LOG(sScreenLog, LogLevel::Debug, ("Work area size changed"));
+          self->RefreshScreens();
         }
       }
       break;
     default:
       break;
   }
 #endif
 
   return GDK_FILTER_CONTINUE;
 }
 
-nsScreenManagerGtk :: nsScreenManagerGtk ( )
+ScreenHelperGTK::ScreenHelperGTK()
   : mXineramalib(nullptr)
   , mRootWindow(nullptr)
   , mNetWorkareaAtom(0)
 {
-  // nothing else to do. I guess we could cache a bunch of information
-  // here, but we want to ask the device at runtime in case anything
-  // has changed.
+  MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperGTK created"));
+  mRootWindow = gdk_get_default_root_window();
+  if (!mRootWindow) {
+    // Sometimes we don't initial X (e.g., xpcshell)
+    MOZ_LOG(sScreenLog, LogLevel::Debug, ("mRootWindow is nullptr, running headless"));
+    return;
+  }
+
+  g_object_ref(mRootWindow);
+
+  // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
+  gdk_window_set_events(mRootWindow,
+                        GdkEventMask(gdk_window_get_events(mRootWindow) |
+                                     GDK_PROPERTY_CHANGE_MASK));
+
+  g_signal_connect(gdk_screen_get_default(), "monitors-changed",
+                   G_CALLBACK(monitors_changed), this);
+#ifdef MOZ_X11
+  gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
+  if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+    mNetWorkareaAtom =
+      XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False);
+  }
+#endif
+  RefreshScreens();
 }
 
-
-nsScreenManagerGtk :: ~nsScreenManagerGtk()
+ScreenHelperGTK::~ScreenHelperGTK()
 {
   g_signal_handlers_disconnect_by_func(gdk_screen_get_default(),
                                        FuncToGpointer(monitors_changed),
                                        this);
 
   if (mRootWindow) {
     gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
     g_object_unref(mRootWindow);
@@ -82,56 +118,172 @@ nsScreenManagerGtk :: ~nsScreenManagerGt
    * in X, which is to be called in XCloseDisplay(). This is the case
    * if Xinerama is active, even if only with one screen.
    *
    * We can't unload libXinerama.so.1 here because this will make
    * the address of close_display() registered in X to be invalid and
    * it will crash when XCloseDisplay() is called later. */
 }
 
+gint
+ScreenHelperGTK::GetGTKMonitorScaleFactor()
+{
+#if (MOZ_WIDGET_GTK >= 3)
+  // Since GDK 3.10
+  static auto sGdkScreenGetMonitorScaleFactorPtr = (gint (*)(GdkScreen*, gint))
+    dlsym(RTLD_DEFAULT, "gdk_screen_get_monitor_scale_factor");
+  if (sGdkScreenGetMonitorScaleFactorPtr) {
+    // FIXME: In the future, we'll want to fix this for GTK on Wayland which
+    // supports a variable scale factor per display.
+    GdkScreen *screen = gdk_screen_get_default();
+    return sGdkScreenGetMonitorScaleFactorPtr(screen, 0);
+  }
+#endif
+  return 1;
+}
 
-// addref, release, QI
-NS_IMPL_ISUPPORTS(nsScreenManagerGtk, nsIScreenManager)
+static float
+GetDefaultCssScale()
+{
+  double scale = nsIWidget::DefaultScaleOverride();
+  if (scale <= 0.0) {
+    scale = ScreenHelperGTK::GetGTKMonitorScaleFactor() * gfxPlatformGtk::GetDPIScale();
+  }
+  return scale;
+}
 
+float
+ScreenHelperGTK::GetSystemDefaultScale()
+{
+  return GetDefaultCssScale();
+}
+
+static uint32_t
+GetGTKPixelDepth()
+{
+  GdkVisual * visual = gdk_screen_get_system_visual(gdk_screen_get_default());
+  return gdk_visual_get_depth(visual);
+}
+
+static already_AddRefed<Screen>
+MakeScreen(GdkWindow* aRootWindow)
+{
+  RefPtr<Screen> screen;
 
-// this function will make sure that everything has been initialized.
-nsresult
-nsScreenManagerGtk :: EnsureInit()
-{
-  if (mCachedScreenArray.Count() > 0)
-    return NS_OK;
+  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+  gint width = gdk_screen_width() * scale;
+  gint height = gdk_screen_height() * scale;
+  uint32_t pixelDepth = GetGTKPixelDepth();
+  DesktopToLayoutDeviceScale contentsScale(1.0);
+  CSSToLayoutDeviceScale defaultCssScale(GetDefaultCssScale());
+
+  LayoutDeviceIntRect rect;
+  LayoutDeviceIntRect availRect;
+  rect = availRect = LayoutDeviceIntRect(0, 0, width, height);
+
+#ifdef MOZ_X11
+  // We need to account for the taskbar, etc in the available rect.
+  // See http://freedesktop.org/Standards/wm-spec/index.html#id2767771
+
+  // XXX do we care about _NET_WM_STRUT_PARTIAL?  That will
+  // add much more complexity to the code here (our screen
+  // could have a non-rectangular shape), but should
+  // lead to greater accuracy.
 
-  mRootWindow = gdk_get_default_root_window();
-  if (!mRootWindow) {
-    // Sometimes we don't initial X (e.g., xpcshell)
-    return NS_OK;
+  long *workareas;
+  GdkAtom type_returned;
+  int format_returned;
+  int length_returned;
+
+  GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
+
+  gdk_error_trap_push();
+
+  // gdk_property_get uses (length + 3) / 4, hence G_MAXLONG - 3 here.
+  if (!gdk_property_get(aRootWindow,
+                        gdk_atom_intern ("_NET_WORKAREA", FALSE),
+                        cardinal_atom,
+                        0, G_MAXLONG - 3, FALSE,
+                        &type_returned,
+                        &format_returned,
+                        &length_returned,
+                        (guchar **) &workareas)) {
+    // This window manager doesn't support the freedesktop standard.
+    // Nothing we can do about it, so assume full screen size.
+    MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%d %d %d %d %d %f]",
+                                          rect.x, rect.y, rect.width, rect.height,
+                                          pixelDepth, defaultCssScale.scale));
+    screen = new Screen(rect, availRect,
+                        pixelDepth, pixelDepth,
+                        contentsScale, defaultCssScale);
+    return screen.forget();
   }
 
-  g_object_ref(mRootWindow);
+  // Flush the X queue to catch errors now.
+  gdk_flush();
 
-  // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
-  gdk_window_set_events(mRootWindow,
-                        GdkEventMask(gdk_window_get_events(mRootWindow) |
-                                     GDK_PROPERTY_CHANGE_MASK));
+  if (!gdk_error_trap_pop() &&
+      type_returned == cardinal_atom &&
+      length_returned && (length_returned % 4) == 0 &&
+      format_returned == 32) {
+    int num_items = length_returned / sizeof(long);
 
-  g_signal_connect(gdk_screen_get_default(), "monitors-changed",
-                   G_CALLBACK(monitors_changed), this);
-#ifdef MOZ_X11
-  gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
-  if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
-      mNetWorkareaAtom =
-        XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False);
+    for (int i = 0; i < num_items; i += 4) {
+      LayoutDeviceIntRect workarea(workareas[i],     workareas[i + 1],
+                                   workareas[i + 2], workareas[i + 3]);
+      if (!rect.Contains(workarea)) {
+        // Note that we hit this when processing screen size changes,
+        // since we'll get the configure event before the toolbars have
+        // been moved.  We'll end up cleaning this up when we get the
+        // change notification to the _NET_WORKAREA property.  However,
+        // we still want to listen to both, so we'll handle changes
+        // properly for desktop environments that don't set the
+        // _NET_WORKAREA property.
+        NS_WARNING("Invalid bounds");
+        continue;
+      }
+
+      availRect.IntersectRect(availRect, workarea);
+    }
+  }
+  g_free(workareas);
 #endif
-
-  return Init();
+  MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%d %d %d %d %d %f]",
+                                        rect.x, rect.y, rect.width, rect.height,
+                                        pixelDepth, defaultCssScale.scale));
+  screen = new Screen(rect, availRect,
+                      pixelDepth, pixelDepth,
+                      contentsScale, defaultCssScale);
+  return screen.forget();
 }
 
-nsresult
-nsScreenManagerGtk :: Init()
+static already_AddRefed<Screen>
+MakeScreen(const XineramaScreenInfo& aScreenInfo)
 {
+  LayoutDeviceIntRect xineRect(aScreenInfo.x_org, aScreenInfo.y_org,
+                               aScreenInfo.width, aScreenInfo.height);
+  uint32_t pixelDepth = GetGTKPixelDepth();
+  DesktopToLayoutDeviceScale contentsScale(1.0);
+  CSSToLayoutDeviceScale defaultCssScale(GetDefaultCssScale());
+
+  MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%d %d %d %d %d %f]",
+                                        xineRect.x, xineRect.y,
+                                        xineRect.width, xineRect.height,
+                                        pixelDepth, defaultCssScale.scale));
+  RefPtr<Screen> screen = new Screen(xineRect, xineRect,
+                                     pixelDepth, pixelDepth,
+                                     contentsScale, defaultCssScale);
+  return screen.forget();
+}
+
+void
+ScreenHelperGTK::RefreshScreens()
+{
+  MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
+  AutoTArray<RefPtr<Screen>, 4> screenList;
 #ifdef MOZ_X11
   XineramaScreenInfo *screenInfo = nullptr;
   int numScreens;
 
   bool useXinerama = GDK_IS_X11_DISPLAY(gdk_display_get_default());
 
   if (useXinerama && !mXineramalib) {
     mXineramalib = PR_LoadLibrary("libXinerama.so.1");
@@ -153,194 +305,34 @@ nsScreenManagerGtk :: Init()
     }
   }
 
   // screenInfo == nullptr if either Xinerama couldn't be loaded or
   // isn't running on the current display
   if (!screenInfo || numScreens == 1) {
     numScreens = 1;
 #endif
-    RefPtr<nsScreenGtk> screen;
-
-    if (mCachedScreenArray.Count() > 0) {
-      screen = static_cast<nsScreenGtk*>(mCachedScreenArray[0]);
-    } else {
-      screen = new nsScreenGtk();
-      if (!screen || !mCachedScreenArray.AppendObject(screen)) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-    }
-
-    screen->Init(mRootWindow);
+    MOZ_LOG(sScreenLog, LogLevel::Debug, ("Find only one screen available"));
+    // Get primary screen
+    screenList.AppendElement(MakeScreen(mRootWindow));
 #ifdef MOZ_X11
   }
   // If Xinerama is enabled and there's more than one screen, fill
   // in the info for all of the screens.  If that's not the case
-  // then nsScreenGTK() defaults to the screen width + height
+  // then defaults to the screen width + height
   else {
-#ifdef DEBUG
-    printf("Xinerama superpowers activated for %d screens!\n", numScreens);
-#endif
+    MOZ_LOG(sScreenLog, LogLevel::Debug,
+            ("Xinerama enabled for %d screens", numScreens));
     for (int i = 0; i < numScreens; ++i) {
-      RefPtr<nsScreenGtk> screen;
-      if (mCachedScreenArray.Count() > i) {
-        screen = static_cast<nsScreenGtk*>(mCachedScreenArray[i]);
-      } else {
-        screen = new nsScreenGtk();
-        if (!screen || !mCachedScreenArray.AppendObject(screen)) {
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-      }
-
-      // initialize this screen object
-      screen->Init(&screenInfo[i]);
+      screenList.AppendElement(MakeScreen(screenInfo[i]));
     }
   }
-  // Remove any screens that are no longer present.
-  while (mCachedScreenArray.Count() > numScreens) {
-    mCachedScreenArray.RemoveObjectAt(mCachedScreenArray.Count() - 1);
-  }
 
   if (screenInfo) {
     XFree(screenInfo);
   }
 #endif
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScreenManagerGtk :: ScreenForId ( uint32_t aId, nsIScreen **outScreen )
-{
-  *outScreen = nullptr;
-
-  nsresult rv;
-  rv = EnsureInit();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForId");
-    return rv;
-  }
-
-  for (int32_t i = 0, i_end = mCachedScreenArray.Count(); i < i_end; ++i) {
-    uint32_t id;
-    rv = mCachedScreenArray[i]->GetId(&id);
-    if (NS_SUCCEEDED(rv) && id == aId) {
-      NS_IF_ADDREF(*outScreen = mCachedScreenArray[i]);
-      return NS_OK;
-    }
-  }
-
-  return NS_ERROR_FAILURE;
+  ScreenManager& screenManager = ScreenManager::GetSingleton();
+  screenManager.Refresh(Move(screenList));
 }
 
-
-//
-// ScreenForRect
-//
-// Returns the screen that contains the rectangle. If the rect overlaps
-// multiple screens, it picks the screen with the greatest area of intersection.
-//
-// The coordinates are in desktop pixels.
-//
-NS_IMETHODIMP
-nsScreenManagerGtk::ScreenForRect(int32_t aX, int32_t aY,
-                                  int32_t aWidth, int32_t aHeight,
-                                  nsIScreen **aOutScreen)
-{
-  nsresult rv;
-  rv = EnsureInit();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForRect");
-    return rv;
-  }
-
-  // which screen ( index from zero ) should we return?
-  uint32_t which = 0;
-  // Optimize for the common case.  If the number of screens is only
-  // one then this will fall through with which == 0 and will get the
-  // primary screen.
-  if (mCachedScreenArray.Count() > 1) {
-    // walk the list of screens and find the one that has the most
-    // surface area.
-    uint32_t area = 0;
-    nsIntRect windowRect(aX, aY, aWidth, aHeight);
-    for (int32_t i = 0, i_end = mCachedScreenArray.Count(); i < i_end; ++i) {
-      int32_t  x, y, width, height;
-      x = y = width = height = 0;
-      mCachedScreenArray[i]->GetRect(&x, &y, &width, &height);
-      // calculate the surface area
-      nsIntRect screenRect(x, y, width, height);
-      screenRect.IntersectRect(screenRect, windowRect);
-      uint32_t tempArea = screenRect.width * screenRect.height;
-      if (tempArea >= area) {
-        which = i;
-        area = tempArea;
-      }
-    }
-  }
-  *aOutScreen = mCachedScreenArray.SafeObjectAt(which);
-  NS_IF_ADDREF(*aOutScreen);
-  return NS_OK;
-
-} // ScreenForRect
-
-
-//
-// GetPrimaryScreen
-//
-// The screen with the menubar/taskbar. This shouldn't be needed very
-// often.
-//
-NS_IMETHODIMP
-nsScreenManagerGtk :: GetPrimaryScreen(nsIScreen * *aPrimaryScreen)
-{
-  nsresult rv;
-  rv =  EnsureInit();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetPrimaryScreen");
-    return rv;
-  }
-  *aPrimaryScreen = mCachedScreenArray.SafeObjectAt(0);
-  NS_IF_ADDREF(*aPrimaryScreen);
-  return NS_OK;
-
-} // GetPrimaryScreen
-
-NS_IMETHODIMP
-nsScreenManagerGtk::GetSystemDefaultScale(float *aDefaultScale)
-{
-  *aDefaultScale = nsScreenGtk::GetDPIScale();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScreenManagerGtk :: ScreenForNativeWidget (void *aWidget, nsIScreen **outScreen)
-{
-  nsresult rv;
-  rv = EnsureInit();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForNativeWidget");
-    return rv;
-  }
-
-  if (mCachedScreenArray.Count() > 1) {
-    // I don't know how to go from GtkWindow to nsIScreen, especially
-    // given xinerama and stuff, so let's just do this
-    gint x, y, width, height;
-#if (MOZ_WIDGET_GTK == 2)
-    gint depth;
-#endif
-    x = y = width = height = 0;
-
-#if (MOZ_WIDGET_GTK == 2)
-    gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height,
-                            &depth);
-#else
-    gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height);
-#endif
-    gdk_window_get_origin(GDK_WINDOW(aWidget), &x, &y);
-    rv = ScreenForRect(x, y, width, height, outScreen);
-  } else {
-    rv = GetPrimaryScreen(outScreen);
-  }
-
-  return rv;
-}
+} // namespace widget
+} // namespace mozilla
rename from widget/gtk/nsScreenManagerGtk.h
rename to widget/gtk/ScreenHelperGTK.h
--- a/widget/gtk/nsScreenManagerGtk.h
+++ b/widget/gtk/ScreenHelperGTK.h
@@ -1,52 +1,49 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
-#ifndef nsScreenManagerGtk_h___
-#define nsScreenManagerGtk_h___
+#ifndef mozilla_widget_gtk_ScreenHelperGTK_h
+#define mozilla_widget_gtk_ScreenHelperGTK_h
 
-#include "nsIScreenManager.h"
-#include "nsIScreen.h"
-#include "nsCOMPtr.h"
-#include "nsCOMArray.h"
+#include "mozilla/widget/ScreenManager.h"
+
 #include "prlink.h"
 #include "gdk/gdk.h"
 #ifdef MOZ_X11
 #include <X11/Xlib.h>
 #endif
 
-//------------------------------------------------------------------------
+namespace mozilla {
+namespace widget {
 
-class nsScreenManagerGtk : public nsIScreenManager
+class ScreenHelperGTK final : public ScreenManager::Helper
 {
 public:
-  nsScreenManagerGtk ( );
+  ScreenHelperGTK();
+  ~ScreenHelperGTK() override;
 
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSISCREENMANAGER
+  float GetSystemDefaultScale() override;
+
+  static gint GetGTKMonitorScaleFactor();
 
 #ifdef MOZ_X11
   Atom NetWorkareaAtom() { return mNetWorkareaAtom; }
 #endif
-  
-  // For internal use, or reinitialization from change notification.
-  nsresult Init();
+
+  // For internal use from signal callback functions
+  void RefreshScreens();
 
 private:
-  virtual ~nsScreenManagerGtk();
-
-  nsresult EnsureInit();
-
-  // Cached screen array.  Its length is the number of screens we have.
-  nsCOMArray<nsIScreen> mCachedScreenArray;
-
-  PRLibrary *mXineramalib;
-
-  GdkWindow *mRootWindow;
+  PRLibrary* mXineramalib;
+  GdkWindow* mRootWindow;
 #ifdef MOZ_X11
   Atom mNetWorkareaAtom;
 #endif
 };
 
-#endif  // nsScreenManagerGtk_h___
+} // namespace widget
+} // namespace mozilla
+
+#endif // mozilla_widget_gtk_ScreenHelperGTK_h
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -24,21 +24,20 @@ UNIFIED_SOURCES += [
     'nsAppShell.cpp',
     'nsBidiKeyboard.cpp',
     'nsColorPicker.cpp',
     'nsFilePicker.cpp',
     'nsGtkKeyUtils.cpp',
     'nsImageToPixbuf.cpp',
     'nsLookAndFeel.cpp',
     'nsNativeThemeGTK.cpp',
-    'nsScreenGtk.cpp',
-    'nsScreenManagerGtk.cpp',
     'nsSound.cpp',
     'nsToolkit.cpp',
     'nsWidgetFactory.cpp',
+    'ScreenHelperGTK.cpp',
     'WakeLockListener.cpp',
     'WidgetTraceEvent.cpp',
     'WidgetUtilsGtk.cpp',
 ]
 
 SOURCES += [
     'nsWindow.cpp', # conflicts with X11 headers
 ]
--- a/widget/gtk/nsAppShell.cpp
+++ b/widget/gtk/nsAppShell.cpp
@@ -16,18 +16,22 @@
 #include "prenv.h"
 #include "mozilla/HangMonitor.h"
 #include "mozilla/Unused.h"
 #include "GeckoProfiler.h"
 #include "nsIPowerManagerService.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "WakeLockListener.h"
 #endif
+#include "ScreenHelperGTK.h"
+#include "mozilla/widget/ScreenManager.h"
 
 using mozilla::Unused;
+using mozilla::widget::ScreenHelperGTK;
+using mozilla::widget::ScreenManager;
 
 #define NOTIFY_TOKEN 0xFA
 
 PRLogModuleInfo *gWidgetLog = nullptr;
 PRLogModuleInfo *gWidgetFocusLog = nullptr;
 PRLogModuleInfo *gWidgetDragLog = nullptr;
 PRLogModuleInfo *gWidgetDrawLog = nullptr;
 
@@ -159,16 +163,21 @@ nsAppShell::Init()
     }
 #endif
 
     if (!sPollFunc) {
         sPollFunc = g_main_context_get_poll_func(nullptr);
         g_main_context_set_poll_func(nullptr, &PollWrapper);
     }
 
+    if (XRE_IsParentProcess()) {
+        ScreenManager& screenManager = ScreenManager::GetSingleton();
+        screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperGTK>());
+    }
+
 #if MOZ_WIDGET_GTK == 3
     if (!sReal_gtk_window_check_resize &&
         gtk_check_version(3,8,0) != nullptr) { // GTK 3.0 to GTK 3.6.
         // GtkWindow is a static class and so will leak anyway but this ref
         // makes sure it isn't recreated.
         gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_WINDOW);
         auto check_resize = &GTK_CONTAINER_CLASS(gtk_plug_class)->check_resize;
         sReal_gtk_window_check_resize = *check_resize;
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -35,17 +35,17 @@
 #include "nsIDocument.h"
 #include "nsISelection.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
 #include "nsGtkUtils.h"
 #include "nsGtkKeyUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "gfxPlatform.h"
-#include "nsScreenGtk.h"
+#include "ScreenHelperGTK.h"
 #include "nsArrayUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 // This sets how opaque the drag image is
 #define DRAG_IMAGE_ALPHA_LEVEL 0.5
 
@@ -234,17 +234,17 @@ OnSourceGrabEventAfter(GtkWidget *widget
         if (sMotionEvent) {
             gdk_event_free(sMotionEvent);
         }
         sMotionEvent = gdk_event_copy(event);
 
         // Update the cursor position.  The last of these recorded gets used for
         // the eDragEnd event.
         nsDragService *dragService = static_cast<nsDragService*>(user_data);
-        gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+        gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
         auto p = LayoutDeviceIntPoint::Round(event->motion.x_root * scale,
                                              event->motion.y_root * scale);
         dragService->SetDragEndPoint(p);
     } else if (sMotionEvent && (event->type == GDK_KEY_PRESS ||
                                 event->type == GDK_KEY_RELEASE)) {
         // Update modifier state from key events.
         sMotionEvent->motion.state = event->key.state;
     } else {
@@ -500,17 +500,17 @@ nsDragService::SetAlphaPixmap(SourceSurf
     cairo_surface_mark_dirty(surf);
     cairo_surface_set_device_offset(surf, -aXOffset, -aYOffset);
 
     // Ensure that the surface is drawn at the correct scale on HiDPI displays.
     static auto sCairoSurfaceSetDeviceScalePtr =
         (void (*)(cairo_surface_t*,double,double))
         dlsym(RTLD_DEFAULT, "cairo_surface_set_device_scale");
     if (sCairoSurfaceSetDeviceScalePtr) {
-        gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+        gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
         sCairoSurfaceSetDeviceScalePtr(surf, scale, scale);
     }
 
     gtk_drag_set_icon_surface(aContext, surf);
     cairo_surface_destroy(surf);
     return true;
 #endif
 }
@@ -1412,17 +1412,17 @@ nsDragService::SourceEndDragSession(GdkD
         // or SourceEndDragSession on drag-failed
         return;
 
     if (mEndDragPoint.x < 0) {
         // We don't have a drag end point, so guess
         gint x, y;
         GdkDisplay* display = gdk_display_get_default();
         if (display) {
-            gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+            gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
             gdk_display_get_pointer(display, nullptr, &x, &y, nullptr);
             SetDragEndPoint(LayoutDeviceIntPoint(x * scale, y * scale));
         }
     }
 
     // Either the drag was aborted or the drop occurred outside the app.
     // The dropEffect of mDataTransfer is not updated for motion outside the
     // app, but is needed for the dragend event, so set it now.
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -13,17 +13,17 @@
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 
 #include <pango/pango.h>
 #include <pango/pango-fontmap.h>
 
 #include <fontconfig/fontconfig.h>
 #include "gfxPlatformGtk.h"
-#include "nsScreenGtk.h"
+#include "ScreenHelperGTK.h"
 
 #include "gtkdrawing.h"
 #include "nsStyleConsts.h"
 #include "gfxFontConstants.h"
 #include "WidgetUtils.h"
 
 #include <dlfcn.h>
 
@@ -920,17 +920,17 @@ GetSystemFontInfo(GtkWidget *aWidget,
     if (!pango_font_description_get_size_is_absolute(desc)) {
         // |size| is in pango-points, so convert to pixels.
         size *= float(gfxPlatformGtk::GetDPI()) / POINTS_PER_INCH_FLOAT;
     }
 
     // Scale fonts up on HiDPI displays.
     // This would be done automatically with cairo, but we manually manage
     // the display scale for platform consistency.
-    size *= nsScreenGtk::GetGtkMonitorScaleFactor();
+    size *= ScreenHelperGTK::GetGTKMonitorScaleFactor();
 
     // |size| is now pixels
 
     aFontStyle->size = size;
 
     pango_font_description_free(desc);
 }
 
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; 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 "nsNativeThemeGTK.h"
 #include "nsThemeConstants.h"
 #include "gtkdrawing.h"
-#include "nsScreenGtk.h"
+#include "ScreenHelperGTK.h"
 
 #include "gfx2DGlue.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsIFrame.h"
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsViewManager.h"
@@ -1085,17 +1085,17 @@ nsNativeThemeGTK::GetExtraSizeForWidget(
       } else {
         aExtra->bottom = extra;
       }
       return false;
     }
   default:
     return false;
   }
-  gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
   aExtra->top *= scale;
   aExtra->right *= scale;
   aExtra->bottom *= scale;
   aExtra->left *= scale;
   return true;
 }
 
 NS_IMETHODIMP
@@ -1113,17 +1113,17 @@ nsNativeThemeGTK::DrawWidgetBackground(n
                             &flags))
     return NS_OK;
 
   gfxContext* ctx = aContext->ThebesContext();
   nsPresContext *presContext = aFrame->PresContext();
 
   gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
   gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
-  gint scaleFactor = nsScreenGtk::GetGtkMonitorScaleFactor();
+  gint scaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor();
 
   // Align to device pixels where sensible
   // to provide crisper and faster drawing.
   // Don't snap if it's a non-unit scale factor. We're going to have to take
   // slow paths then in any case.
   bool snapped = ctx->UserToDevicePixelSnapped(rect);
   if (snapped) {
     // Leave rect in device coords but make dirtyRect consistent.
@@ -1309,17 +1309,17 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi
                                &unusedFlags)) {
         moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
                                   &aResult->right, &aResult->bottom, direction,
                                   IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
       }
     }
   }
 
-  gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
   aResult->top *= scale;
   aResult->right *= scale;
   aResult->bottom *= scale;
   aResult->left *= scale;
   return NS_OK;
 }
 
 bool
@@ -1369,17 +1369,17 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev
         if (aWidgetType == NS_THEME_MENUITEM)
           moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
         else
           moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
 
         aResult->left += horizontal_padding;
         aResult->right += horizontal_padding;
 
-        gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+        gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
         aResult->top *= scale;
         aResult->right *= scale;
         aResult->bottom *= scale;
         aResult->left *= scale;
 
         return true;
       }
   }
@@ -1697,17 +1697,17 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
 
       moz_gtk_get_treeview_expander_size(&expander_size);
       aResult->width = aResult->height = expander_size;
       *aIsOverridable = false;
     }
     break;
   }
 
-  *aResult = *aResult * nsScreenGtk::GetGtkMonitorScaleFactor();
+  *aResult = *aResult * ScreenHelperGTK::GetGTKMonitorScaleFactor();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
                                      nsIAtom* aAttribute, bool* aShouldRepaint,
                                      const nsAttrValue* aOldValue)
deleted file mode 100644
--- a/widget/gtk/nsScreenGtk.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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 "nsScreenGtk.h"
-
-#include "nsIWidget.h"
-
-#include <gdk/gdk.h>
-#ifdef MOZ_X11
-#include <gdk/gdkx.h>
-#include <X11/Xatom.h>
-#endif
-#include <gtk/gtk.h>
-#include <dlfcn.h>
-#include "gfxPlatformGtk.h"
-
-static uint32_t sScreenId = 0;
-
-
-nsScreenGtk :: nsScreenGtk (  )
-  : mScreenNum(0),
-    mRect(0, 0, 0, 0),
-    mAvailRect(0, 0, 0, 0),
-    mId(++sScreenId)
-{
-}
-
-
-nsScreenGtk :: ~nsScreenGtk()
-{
-}
-
-
-NS_IMETHODIMP
-nsScreenGtk :: GetId(uint32_t *aId)
-{
-  *aId = mId;
-  return NS_OK;
-} // GetId
-
-
-NS_IMETHODIMP
-nsScreenGtk :: GetRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight)
-{
-  *outLeft = mRect.x;
-  *outTop = mRect.y;
-  *outWidth = mRect.width;
-  *outHeight = mRect.height;
-
-  return NS_OK;
-  
-} // GetRect
-
-
-NS_IMETHODIMP
-nsScreenGtk :: GetAvailRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight)
-{
-  *outLeft = mAvailRect.x;
-  *outTop = mAvailRect.y;
-  *outWidth = mAvailRect.width;
-  *outHeight = mAvailRect.height;
-
-  return NS_OK;
-  
-} // GetAvailRect
-
-gint
-nsScreenGtk :: GetGtkMonitorScaleFactor()
-{
-#if (MOZ_WIDGET_GTK >= 3)
-  // Since GDK 3.10
-  static auto sGdkScreenGetMonitorScaleFactorPtr = (gint (*)(GdkScreen*, gint))
-      dlsym(RTLD_DEFAULT, "gdk_screen_get_monitor_scale_factor");
-  if (sGdkScreenGetMonitorScaleFactorPtr) {
-      // FIXME: In the future, we'll want to fix this for GTK on Wayland which
-      // supports a variable scale factor per display.
-      GdkScreen *screen = gdk_screen_get_default();
-      return sGdkScreenGetMonitorScaleFactorPtr(screen, 0);
-  }
-#endif
-    return 1;
-}
-
-double
-nsScreenGtk :: GetDPIScale()
-{
-  double dpiScale = nsIWidget::DefaultScaleOverride();
-  if (dpiScale <= 0.0) {
-    dpiScale = GetGtkMonitorScaleFactor() * gfxPlatformGtk::GetDPIScale();
-  }
-  return dpiScale;
-}
-
-NS_IMETHODIMP 
-nsScreenGtk :: GetPixelDepth(int32_t *aPixelDepth)
-{
-  GdkVisual * visual = gdk_screen_get_system_visual(gdk_screen_get_default());
-  *aPixelDepth = gdk_visual_get_depth(visual);
-
-  return NS_OK;
-
-} // GetPixelDepth
-
-NS_IMETHODIMP 
-nsScreenGtk :: GetColorDepth(int32_t *aColorDepth)
-{
-  return GetPixelDepth ( aColorDepth );
-
-} // GetColorDepth
-
-NS_IMETHODIMP
-nsScreenGtk::GetDefaultCSSScaleFactor(double* aScaleFactor)
-{
-  *aScaleFactor = GetDPIScale();
-  return NS_OK;
-}
-
-void
-nsScreenGtk :: Init (GdkWindow *aRootWindow)
-{
-  gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
-  gint width = gdk_screen_width()*scale;
-  gint height = gdk_screen_height()*scale;
-
-  // We listen for configure events on the root window to pick up
-  // changes to this rect.  We could listen for "size_changed" signals
-  // on the default screen to do this, except that doesn't work with
-  // versions of GDK predating the GdkScreen object.  See bug 256646.
-  mAvailRect = mRect = nsIntRect(0, 0, width, height);
-
-#ifdef MOZ_X11
-  // We need to account for the taskbar, etc in the available rect.
-  // See http://freedesktop.org/Standards/wm-spec/index.html#id2767771
-
-  // XXX do we care about _NET_WM_STRUT_PARTIAL?  That will
-  // add much more complexity to the code here (our screen
-  // could have a non-rectangular shape), but should
-  // lead to greater accuracy.
-
-  long *workareas;
-  GdkAtom type_returned;
-  int format_returned;
-  int length_returned;
-
-  GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
-
-  gdk_error_trap_push();
-
-  // gdk_property_get uses (length + 3) / 4, hence G_MAXLONG - 3 here.
-  if (!gdk_property_get(aRootWindow,
-                        gdk_atom_intern ("_NET_WORKAREA", FALSE),
-                        cardinal_atom,
-                        0, G_MAXLONG - 3, FALSE,
-                        &type_returned,
-                        &format_returned,
-                        &length_returned,
-                        (guchar **) &workareas)) {
-    // This window manager doesn't support the freedesktop standard.
-    // Nothing we can do about it, so assume full screen size.
-    return;
-  }
-
-  // Flush the X queue to catch errors now.
-  gdk_flush();
-
-  if (!gdk_error_trap_pop() &&
-      type_returned == cardinal_atom &&
-      length_returned && (length_returned % 4) == 0 &&
-      format_returned == 32) {
-    int num_items = length_returned / sizeof(long);
-
-    for (int i = 0; i < num_items; i += 4) {
-      nsIntRect workarea(workareas[i],     workareas[i + 1],
-                         workareas[i + 2], workareas[i + 3]);
-      if (!mRect.Contains(workarea)) {
-        // Note that we hit this when processing screen size changes,
-        // since we'll get the configure event before the toolbars have
-        // been moved.  We'll end up cleaning this up when we get the
-        // change notification to the _NET_WORKAREA property.  However,
-        // we still want to listen to both, so we'll handle changes
-        // properly for desktop environments that don't set the
-        // _NET_WORKAREA property.
-        NS_WARNING("Invalid bounds");
-        continue;
-      }
-
-      mAvailRect.IntersectRect(mAvailRect, workarea);
-    }
-  }
-  g_free (workareas);
-#endif
-}
-
-#ifdef MOZ_X11
-void
-nsScreenGtk :: Init (XineramaScreenInfo *aScreenInfo)
-{
-  nsIntRect xineRect(aScreenInfo->x_org, aScreenInfo->y_org,
-                     aScreenInfo->width, aScreenInfo->height);
-
-  mScreenNum = aScreenInfo->screen_number;
-
-  mAvailRect = mRect = xineRect;
-}
-#endif
deleted file mode 100644
--- a/widget/gtk/nsScreenGtk.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef nsScreenGtk_h___
-#define nsScreenGtk_h___
-
-#include "nsBaseScreen.h"
-#include "nsRect.h"
-#include "gdk/gdk.h"
-#ifdef MOZ_X11
-#include <X11/Xlib.h>
-
-// from Xinerama.h
-typedef struct {
-   int   screen_number;
-   short x_org;
-   short y_org;
-   short width;
-   short height;
-} XineramaScreenInfo;
-#endif /* MOZ_X11 */
-
-//------------------------------------------------------------------------
-
-class nsScreenGtk : public nsBaseScreen
-{
-public:
-  nsScreenGtk();
-  ~nsScreenGtk();
-
-  NS_IMETHOD GetId(uint32_t* aId) override;
-  NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop,
-                     int32_t* aWidth, int32_t* aHeight) override;
-  NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop,
-                          int32_t* aWidth, int32_t* aHeight) override;
-  NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override;
-  NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override;
-  NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) override;
-
-  void Init(GdkWindow *aRootWindow);
-#ifdef MOZ_X11
-  void Init(XineramaScreenInfo *aScreenInfo);
-#endif /* MOZ_X11 */
-
-  static gint    GetGtkMonitorScaleFactor();
-  static double  GetDPIScale();
-
-private:
-  uint32_t mScreenNum;
-  nsIntRect mRect;
-  nsIntRect mAvailRect;
-  uint32_t mId;
-};
-
-#endif  // nsScreenGtk_h___
--- a/widget/gtk/nsWidgetFactory.cpp
+++ b/widget/gtk/nsWidgetFactory.cpp
@@ -24,17 +24,16 @@
 #endif
 #if (MOZ_WIDGET_GTK == 3)
 #include "nsApplicationChooser.h"
 #endif
 #include "nsColorPicker.h"
 #include "nsFilePicker.h"
 #include "nsSound.h"
 #include "nsBidiKeyboard.h"
-#include "nsScreenManagerGtk.h"
 #include "nsGTKToolkit.h"
 #include "WakeLockListener.h"
 
 #ifdef NS_PRINTING
 #include "nsPrintOptionsGTK.h"
 #include "nsPrintSession.h"
 #include "nsDeviceContextSpecG.h"
 #endif
@@ -49,16 +48,17 @@
 #include "GfxInfoX11.h"
 #endif
 
 #include "nsNativeThemeGTK.h"
 
 #include "nsIComponentRegistrar.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/widget/ScreenManager.h"
 #include <gtk/gtk.h>
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 /* from nsFilePicker.js */
 #define XULFILEPICKER_CID \
   { 0x54ae32f8, 0x1dd2, 0x11b2, \
@@ -72,17 +72,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKey
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 #ifdef MOZ_X11
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceGTK, nsIdleServiceGTK::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerGtk)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ScreenManager, ScreenManager::GetAddRefedSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf)
 
 
 // from nsWindow.cpp
 extern bool gDisableNativeTheme;
 
 static nsresult
 nsNativeThemeGTKConstructor(nsISupports *aOuter, REFNSIID aIID,
@@ -238,17 +238,17 @@ static const mozilla::Module::CIDEntry k
     { &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor },
 #ifdef MOZ_X11
     { &kNS_CLIPBOARD_CID, false, nullptr, nsClipboardConstructor, Module::MAIN_PROCESS_ONLY },
     { &kNS_CLIPBOARDHELPER_CID, false, nullptr, nsClipboardHelperConstructor },
     { &kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceConstructor, Module::MAIN_PROCESS_ONLY },
 #endif
     { &kNS_HTMLFORMATCONVERTER_CID, false, nullptr, nsHTMLFormatConverterConstructor },
     { &kNS_BIDIKEYBOARD_CID, false, nullptr, nsBidiKeyboardConstructor },
-    { &kNS_SCREENMANAGER_CID, false, nullptr, nsScreenManagerGtkConstructor,
+    { &kNS_SCREENMANAGER_CID, false, nullptr, ScreenManagerConstructor,
       Module::MAIN_PROCESS_ONLY },
     { &kNS_THEMERENDERER_CID, false, nullptr, nsNativeThemeGTKConstructor },
 #ifdef NS_PRINTING
     { &kNS_PRINTSETTINGSSERVICE_CID, false, nullptr, nsPrintOptionsGTKConstructor },
     { &kNS_PRINTER_ENUMERATOR_CID, false, nullptr, nsPrinterEnumeratorGTKConstructor },
     { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor },
     { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecGTKConstructor },
     { &kNS_PRINTDIALOGSERVICE_CID, false, nullptr, nsPrintDialogServiceGTKConstructor },
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -29,17 +29,17 @@
 #include "nsWidgetsCID.h"
 #include "nsDragService.h"
 #include "nsIWidgetListener.h"
 #include "nsIScreenManager.h"
 #include "SystemTimeConverter.h"
 
 #include "nsGtkKeyUtils.h"
 #include "nsGtkCursors.h"
-#include "nsScreenGtk.h"
+#include "ScreenHelperGTK.h"
 
 #include <gtk/gtk.h>
 #if (MOZ_WIDGET_GTK == 3)
 #include <gtk/gtkx.h>
 #endif
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #include <X11/Xatom.h>
@@ -6434,17 +6434,17 @@ nsWindow::GdkScaleFactor()
 {
 #if (MOZ_WIDGET_GTK >= 3)
     // Available as of GTK 3.10+
     static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*))
         dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
     if (sGdkWindowGetScaleFactorPtr && mGdkWindow)
         return (*sGdkWindowGetScaleFactorPtr)(mGdkWindow);
 #endif
-    return nsScreenGtk::GetGtkMonitorScaleFactor();
+    return ScreenHelperGTK::GetGTKMonitorScaleFactor();
 }
 
 
 gint
 nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) {
     gint scale = GdkScaleFactor();
     return (pixels + scale - 1) / scale;
 }