Bug 1507475 - [Wayland] Implement global wayland registry, r=jhorak
☠☠ backed out by 499f78411793 ☠ ☠
authorMartin Stransky <stransky@redhat.com>
Thu, 22 Nov 2018 13:24:09 +0000
changeset 504143 bafd708f5788ec19c3eda5a4068f2a56fb9f4e7a
parent 504142 e6377178d0937fd4fd4a56374c8a5e370b6f66e0
child 504144 e23085e6b51f7d2ab2e9c11fedbc1ef0177f30ed
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1507475
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1507475 - [Wayland] Implement global wayland registry, r=jhorak Differential Revision: https://phabricator.services.mozilla.com/D12255
widget/gtk/WindowSurfaceProvider.cpp
widget/gtk/WindowSurfaceWayland.cpp
widget/gtk/WindowSurfaceWayland.h
widget/gtk/WindowSurfaceX11Image.cpp
widget/gtk/WindowSurfaceX11Image.h
widget/gtk/moz.build
widget/gtk/mozcontainer.cpp
widget/gtk/mozcontainer.h
widget/gtk/nsClipboardWayland.cpp
widget/gtk/nsClipboardWayland.h
widget/gtk/nsGtkKeyUtils.cpp
widget/gtk/nsLookAndFeel.h
widget/gtk/nsWaylandDisplay.cpp
widget/gtk/nsWaylandDisplay.h
widget/gtk/nsWindow.cpp
--- a/widget/gtk/WindowSurfaceProvider.cpp
+++ b/widget/gtk/WindowSurfaceProvider.cpp
@@ -54,19 +54,16 @@ void WindowSurfaceProvider::Initialize(
   mXDepth = aDepth;
   mIsShaped = aIsShaped;
   mIsX11Display = true;
 }
 
 #ifdef MOZ_WAYLAND
 void WindowSurfaceProvider::Initialize(nsWindow *aWidget)
 {
-  MOZ_ASSERT(aWidget->GetWaylandDisplay(),
-             "We are supposed to have a Wayland display!");
-
   mWidget = aWidget;
   mIsX11Display = false;
 }
 #endif
 
 void WindowSurfaceProvider::CleanupResources()
 {
   mWindowSurface = nullptr;
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -1,32 +1,33 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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 "nsWaylandDisplay.h"
 #include "WindowSurfaceWayland.h"
 
-#include "base/message_loop.h"          // for MessageLoop
-#include "base/task.h"                  // for NewRunnableMethod, etc
 #include "nsPrintfCString.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Tools.h"
 #include "gfxPlatform.h"
 #include "mozcontainer.h"
 #include "nsTArray.h"
-#include "mozilla/StaticMutex.h"
-#include "mozwayland/mozwayland.h"
+#include "base/message_loop.h"          // for MessageLoop
+#include "base/task.h"                  // for NewRunnableMethod, etc
 
 #include <sys/mman.h>
-#include <assert.h>
 #include <fcntl.h>
 #include <errno.h>
 
+namespace mozilla {
+namespace widget {
+
 /*
   Wayland multi-thread rendering scheme
 
   Every rendering thread (main thread, compositor thread) contains its own
   nsWaylandDisplay object connected to Wayland compositor (Mutter, Weston, etc.)
 
   WindowSurfaceWayland implements WindowSurface class and draws nsWindow by
   WindowSurface interface (Lock, Commit) to screen through nsWaylandDisplay.
@@ -126,212 +127,18 @@ WaylandShmPool acts as a manager of shar
 Allocates it, holds reference to it and releases it.
 
 We allocate shared memory (shm) by mmap(..., MAP_SHARED,...) as an interface
 between us and wayland compositor. We draw our graphics data to the shm and
 handle to wayland compositor by WindowBackBuffer/WindowSurfaceWayland
 (wl_buffer/wl_surface).
 */
 
-namespace mozilla {
-namespace widget {
-
 #define BUFFER_BPP 4
-#define MAX_DISPLAY_CONNECTIONS 2
-
-static nsWaylandDisplay* gWaylandDisplays[MAX_DISPLAY_CONNECTIONS];
-static StaticMutex gWaylandDisplaysMutex;
-
-// Each thread which is using wayland connection (wl_display) has to operate
-// its own wl_event_queue. Main Firefox thread wl_event_queue is handled
-// by Gtk main loop, other threads/wl_event_queue has to be handled by us.
-//
-// nsWaylandDisplay is our interface to wayland compositor. It provides wayland
-// global objects as we need (wl_display, wl_shm) and operates wl_event_queue on
-// compositor (not the main) thread.
-static nsWaylandDisplay* WaylandDisplayGet(wl_display *aDisplay);
-static void WaylandDisplayRelease(wl_display *aDisplay);
-static void WaylandDisplayLoop(wl_display *aDisplay);
-
-// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with
-// compositor event loop.
-#define EVENT_LOOP_DELAY (1000/240)
-
-// Get WaylandDisplay for given wl_display and actual calling thread.
-static nsWaylandDisplay*
-WaylandDisplayGetLocked(wl_display *aDisplay, const StaticMutexAutoLock&)
-{
-  for (auto& display: gWaylandDisplays) {
-    if (display && display->Matches(aDisplay)) {
-      NS_ADDREF(display);
-      return display;
-    }
-  }
-
-  for (auto& display: gWaylandDisplays) {
-    if (display == nullptr) {
-      display = new nsWaylandDisplay(aDisplay);
-      NS_ADDREF(display);
-      return display;
-    }
-  }
-
-  MOZ_CRASH("There's too many wayland display conections!");
-  return nullptr;
-}
-
-static nsWaylandDisplay*
-WaylandDisplayGet(wl_display *aDisplay)
-{
-  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
-  return WaylandDisplayGetLocked(aDisplay, lock);
-}
-
-static bool
-WaylandDisplayReleaseLocked(wl_display *aDisplay,
-                            const StaticMutexAutoLock&)
-{
-  for (auto& display: gWaylandDisplays) {
-    if (display && display->Matches(aDisplay)) {
-      int rc = display->Release();
-      if (rc == 0) {
-        display = nullptr;
-      }
-      return true;
-    }
-  }
-  MOZ_ASSERT(false, "Missing nsWaylandDisplay for this thread!");
-  return false;
-}
-
-static void
-WaylandDisplayRelease(wl_display *aDisplay)
-{
-  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
-  WaylandDisplayReleaseLocked(aDisplay, lock);
-}
-
-static void
-WaylandDisplayLoopLocked(wl_display* aDisplay,
-                         const StaticMutexAutoLock&)
-{
-  for (auto& display: gWaylandDisplays) {
-    if (display && display->Matches(aDisplay)) {
-      if (display->DisplayLoop()) {
-        MessageLoop::current()->PostDelayedTask(
-            NewRunnableFunction("WaylandDisplayLoop",
-                               &WaylandDisplayLoop,
-                               aDisplay),
-            EVENT_LOOP_DELAY);
-      }
-      break;
-    }
-  }
-}
-
-static void
-WaylandDisplayLoop(wl_display* aDisplay)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
-  WaylandDisplayLoopLocked(aDisplay, lock);
-}
-
-static void
-global_registry_handler(void *data, wl_registry *registry, uint32_t id,
-                        const char *interface, uint32_t version)
-{
-  if (strcmp(interface, "wl_shm") == 0) {
-    auto interface = reinterpret_cast<nsWaylandDisplay *>(data);
-    auto shm = static_cast<wl_shm*>(
-        wl_registry_bind(registry, id, &wl_shm_interface, 1));
-    wl_proxy_set_queue((struct wl_proxy *)shm, interface->GetEventQueue());
-    interface->SetShm(shm);
-  }
-}
-
-static void
-global_registry_remover(void *data, wl_registry *registry, uint32_t id)
-{
-}
-
-static const struct wl_registry_listener registry_listener = {
-  global_registry_handler,
-  global_registry_remover
-};
-
-wl_shm*
-nsWaylandDisplay::GetShm()
-{
-  MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
-
-  if (!mShm) {
-    // wl_shm is not provided by Gtk so we need to query wayland directly
-    // See weston/simple-shm.c and create_display() for reference.
-    wl_registry* registry = wl_display_get_registry(mDisplay);
-    wl_registry_add_listener(registry, &registry_listener, this);
-
-    wl_proxy_set_queue((struct wl_proxy *)registry, mEventQueue);
-    if (mEventQueue) {
-      wl_display_roundtrip_queue(mDisplay, mEventQueue);
-    } else {
-      wl_display_roundtrip(mDisplay);
-    }
-
-    MOZ_RELEASE_ASSERT(mShm, "Wayland registry query failed!");
-  }
-
-  return(mShm);
-}
-
-bool
-nsWaylandDisplay::DisplayLoop()
-{
-  wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
-  return true;
-}
-
-bool
-nsWaylandDisplay::Matches(wl_display *aDisplay)
-{
-  return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
-}
-
-NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
-
-nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
-  : mThreadId(PR_GetCurrentThread())
-  // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format
-  // and is always present.
-  // TODO: Provide also format without alpha (Bug 1470126).
-  , mFormat(gfx::SurfaceFormat::B8G8R8A8)
-  , mShm(nullptr)
-  , mDisplay(aDisplay)
-{
-  if (NS_IsMainThread()) {
-    // Use default event queue in main thread operated by Gtk+.
-    mEventQueue = nullptr;
-  } else {
-    mEventQueue = wl_display_create_queue(mDisplay);
-    MessageLoop::current()->PostTask(NewRunnableFunction(
-        "WaylandDisplayLoop", &WaylandDisplayLoop, mDisplay));
-  }
-}
-
-nsWaylandDisplay::~nsWaylandDisplay()
-{
-  MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
-  // Owned by Gtk+, we don't need to release
-  mDisplay = nullptr;
-
-  if (mEventQueue) {
-    wl_event_queue_destroy(mEventQueue);
-    mEventQueue = nullptr;
-  }
-}
+gfx::SurfaceFormat WindowBackBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8;
 
 int
 WaylandShmPool::CreateTemporaryFile(int aSize)
 {
   const char* tmppath = getenv("XDG_RUNTIME_DIR");
   MOZ_RELEASE_ASSERT(tmppath, "Missing XDG_RUNTIME_DIR env variable.");
 
   nsPrintfCString tmpname("%s/mozilla-shared-XXXXXX", tmppath);
@@ -539,17 +346,17 @@ WindowBackBuffer::SetImageDataFromBuffer
 
 already_AddRefed<gfx::DrawTarget>
 WindowBackBuffer::Lock()
 {
   gfx::IntSize lockSize(mWidth, mHeight);
   return gfxPlatform::CreateDrawTargetForData(static_cast<unsigned char*>(mShmPool.GetImageData()),
                                               lockSize,
                                               BUFFER_BPP * mWidth,
-                                              mWaylandDisplay->GetSurfaceFormat());
+                                              mFormat);
 }
 
 static void
 frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time)
 {
   auto surface = reinterpret_cast<WindowSurfaceWayland*>(data);
   surface->FrameCallbackHandler();
 
@@ -557,17 +364,17 @@ frame_callback_handler(void *data, struc
 }
 
 static const struct wl_callback_listener frame_listener = {
   frame_callback_handler
 };
 
 WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
   : mWindow(aWindow)
-  , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay()))
+  , mWaylandDisplay(WaylandDisplayGet())
   , mWaylandBuffer(nullptr)
   , mFrameCallback(nullptr)
   , mLastCommittedSurface(nullptr)
   , mDisplayThreadMessageLoop(MessageLoop::current())
   , mDelayedCommitHandle(nullptr)
   , mDrawToWaylandBufferDirectly(true)
   , mPendingCommit(false)
   , mWaylandBufferFullScreenDamage(false)
@@ -605,19 +412,19 @@ WindowSurfaceWayland::~WindowSurfaceWayl
 
   if (!mIsMainThread) {
     // We can be destroyed from main thread even though we was created/used
     // in compositor thread. We have to unref/delete WaylandDisplay in compositor
     // thread then and we can't use MessageLoop::current() here.
     mDisplayThreadMessageLoop->PostTask(
       NewRunnableFunction("WaylandDisplayRelease",
                           &WaylandDisplayRelease,
-                          mWaylandDisplay->GetDisplay()));
+                          mWaylandDisplay));
   } else {
-    WaylandDisplayRelease(mWaylandDisplay->GetDisplay());
+    WaylandDisplayRelease(mWaylandDisplay);
   }
 }
 
 WindowBackBuffer*
 WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight)
 {
   if (!mWaylandBuffer) {
     mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
@@ -695,26 +502,26 @@ WindowSurfaceWayland::LockWaylandBuffer(
 }
 
 already_AddRefed<gfx::DrawTarget>
 WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize)
 {
   if (!mImageSurface || mImageSurface->CairoStatus() ||
       !(aLockSize <= mImageSurface->GetSize())) {
     mImageSurface = new gfxImageSurface(aLockSize,
-        SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat()));
+        SurfaceFormatToImageFormat(WindowBackBuffer::GetSurfaceFormat()));
     if (mImageSurface->CairoStatus()) {
       return nullptr;
     }
   }
 
   return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(),
                                               mImageSurface->GetSize(),
                                               mImageSurface->Stride(),
-                                              mWaylandDisplay->GetSurfaceFormat());
+                                              WindowBackBuffer::GetSurfaceFormat());
 }
 
 /*
   There are some situations which can happen here:
 
   A) Lock() is called to whole surface. In that case we don't need
      to clip/buffer the drawing and we can return wl_buffer directly
      for drawing.
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -4,49 +4,23 @@
  * 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 _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
 #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
 
 #include <prthread.h>
 #include "mozilla/gfx/Types.h"
+#include "nsWaylandDisplay.h"
 
 #define  BACK_BUFFER_NUM 2
 
 namespace mozilla {
 namespace widget {
 
-// Our general connection to Wayland display server,
-// holds our display connection and runs event loop.
-class nsWaylandDisplay : public nsISupports {
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-public:
-  explicit nsWaylandDisplay(wl_display *aDisplay);
-
-  wl_shm*             GetShm();
-  void                SetShm(wl_shm* aShm)   { mShm = aShm; };
-
-  wl_display*         GetDisplay()           { return mDisplay; };
-  wl_event_queue*     GetEventQueue()        { return mEventQueue; };
-  gfx::SurfaceFormat  GetSurfaceFormat()     { return mFormat; };
-  bool                DisplayLoop();
-  bool                Matches(wl_display *aDisplay);
-
-private:
-  virtual ~nsWaylandDisplay();
-
-  PRThread*           mThreadId;
-  gfx::SurfaceFormat  mFormat;
-  wl_shm*             mShm;
-  wl_event_queue*     mEventQueue;
-  wl_display*         mDisplay;
-};
-
 // Allocates and owns shared memory for Wayland drawing surface
 class WaylandShmPool {
 public:
   WaylandShmPool(nsWaylandDisplay* aDisplay, int aSize);
   ~WaylandShmPool();
 
   bool                Resize(int aSize);
   wl_shm_pool*        GetShmPool()    { return mShmPool;   };
@@ -83,30 +57,36 @@ public:
   {
     return aWidth == mWidth && aHeight == mHeight;
   }
   bool IsMatchingSize(class WindowBackBuffer *aBuffer)
   {
     return aBuffer->mWidth == mWidth && aBuffer->mHeight == mHeight;
   }
 
+  static gfx::SurfaceFormat GetSurfaceFormat()
+  {
+    return mFormat;
+  }
+
 private:
   void Create(int aWidth, int aHeight);
   void Release();
 
   // WaylandShmPool provides actual shared memory we draw into
   WaylandShmPool      mShmPool;
 
   // wl_buffer is a wayland object that encapsulates the shared memory
   // and passes it to wayland compositor by wl_surface object.
   wl_buffer*          mWaylandBuffer;
   int                 mWidth;
   int                 mHeight;
   bool                mAttached;
   nsWaylandDisplay*   mWaylandDisplay;
+  static gfx::SurfaceFormat mFormat;
 };
 
 // WindowSurfaceWayland is an abstraction for wl_surface
 // and related management
 class WindowSurfaceWayland : public WindowSurface {
 public:
   explicit WindowSurfaceWayland(nsWindow *aWindow);
   ~WindowSurfaceWayland();
--- a/widget/gtk/WindowSurfaceX11Image.cpp
+++ b/widget/gtk/WindowSurfaceX11Image.cpp
@@ -12,16 +12,18 @@
 #include "gfxPlatform.h"
 #include "gfx2DGlue.h"
 
 #include <X11/extensions/shape.h>
 
 namespace mozilla {
 namespace widget {
 
+using namespace mozilla::gfx;
+
 // gfxImageSurface pixel format configuration.
 #define SHAPED_IMAGE_SURFACE_BPP            4
 #ifdef IS_BIG_ENDIAN
   #define SHAPED_IMAGE_SURFACE_ALPHA_INDEX  0
 #else
   #define SHAPED_IMAGE_SURFACE_ALPHA_INDEX  3
 #endif
 
--- a/widget/gtk/WindowSurfaceX11Image.h
+++ b/widget/gtk/WindowSurfaceX11Image.h
@@ -4,16 +4,17 @@
  * 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 _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
 #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
 
 #ifdef MOZ_X11
 
+#include <glib.h>
 #include "WindowSurfaceX11.h"
 #include "gfxXlibSurface.h"
 #include "gfxImageSurface.h"
 
 namespace mozilla {
 namespace widget {
 
 class WindowSurfaceX11Image : public WindowSurfaceX11 {
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -92,16 +92,17 @@ if CONFIG['MOZ_X11']:
     ]
     EXPORTS.mozilla.widget += [
         'WindowSurfaceProvider.h',
     ]
 
 if CONFIG['MOZ_WAYLAND']:
     UNIFIED_SOURCES += [
         'nsClipboardWayland.cpp',
+        'nsWaylandDisplay.cpp',
         'WindowSurfaceWayland.cpp',
     ]
 
 if CONFIG['ACCESSIBILITY']:
     UNIFIED_SOURCES += [
         'maiRedundantObjectFactory.c',
     ]
 
--- a/widget/gtk/mozcontainer.cpp
+++ b/widget/gtk/mozcontainer.cpp
@@ -4,27 +4,30 @@
 /* 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 "mozcontainer.h"
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #ifdef MOZ_WAYLAND
-#include "mozwayland/mozwayland.h"
+#include "nsWaylandDisplay.h"
 #include <wayland-egl.h>
 #endif
 #include <stdio.h>
 #include <dlfcn.h>
 
 #ifdef ACCESSIBILITY
 #include <atk/atk.h>
 #include "maiRedundantObjectFactory.h"
 #endif
 
+using namespace mozilla;
+using namespace mozilla::widget;
+
 /* init methods */
 static void moz_container_class_init          (MozContainerClass *klass);
 static void moz_container_init                (MozContainer      *container);
 
 /* widget class methods */
 static void moz_container_map                 (GtkWidget         *widget);
 static void moz_container_unmap               (GtkWidget         *widget);
 static void moz_container_realize             (GtkWidget         *widget);
@@ -163,77 +166,29 @@ moz_container_class_init (MozContainerCl
     widget_class->realize = moz_container_realize;
     widget_class->size_allocate = moz_container_size_allocate;
 
     container_class->remove = moz_container_remove;
     container_class->forall = moz_container_forall;
     container_class->add = moz_container_add;
 }
 
-#if defined(MOZ_WAYLAND)
-static void
-registry_handle_global (void *data,
-                        struct wl_registry *registry,
-                        uint32_t name,
-                        const char *interface,
-                        uint32_t version)
-{
-    MozContainer *container = MOZ_CONTAINER(data);
-    if(strcmp(interface, "wl_subcompositor") == 0) {
-        container->subcompositor =
-            static_cast<wl_subcompositor*>(wl_registry_bind(registry,
-                                           name,
-                                           &wl_subcompositor_interface,
-                                           1));
-    }
-}
-
-static void
-registry_handle_global_remove (void *data,
-                               struct wl_registry *registry,
-                               uint32_t name)
-{
-}
-
-static const struct wl_registry_listener registry_listener = {
-    registry_handle_global,
-    registry_handle_global_remove
-};
-#endif
-
 void
 moz_container_init (MozContainer *container)
 {
     gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
     gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
     gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
 
 #if defined(MOZ_WAYLAND)
-    {
-      container->subcompositor = nullptr;
-      container->surface = nullptr;
-      container->subsurface = nullptr;
-      container->eglwindow = nullptr;
-      container->parent_surface_committed = false;
-      container->needs_clear = true;
-
-      GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
-      if (!GDK_IS_X11_DISPLAY(gdk_display)) {
-          // Available as of GTK 3.8+
-          static auto sGdkWaylandDisplayGetWlDisplay =
-              (wl_display *(*)(GdkDisplay *))
-              dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
-
-          wl_display* display = sGdkWaylandDisplayGetWlDisplay(gdk_display);
-          wl_registry* registry = wl_display_get_registry(display);
-          wl_registry_add_listener(registry, &registry_listener, container);
-          wl_display_dispatch(display);
-          wl_display_roundtrip(display);
-        }
-    }
+    container->surface = nullptr;
+    container->subsurface = nullptr;
+    container->eglwindow = nullptr;
+    container->parent_surface_committed = false;
+    container->needs_clear = true;
 #endif
 }
 
 #if defined(MOZ_WAYLAND)
 static void
 moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container)
 {
     container->parent_surface_committed = true;
@@ -279,43 +234,47 @@ moz_container_map_surface(MozContainer *
                 g_signal_connect_after(clock, "after-paint",
                                        G_CALLBACK(moz_container_commited_handler),
                                        container);
         }
         return false;
     }
 
     if (!container->surface) {
+        // TODO - use nsWaylandDisplay::compositor for compositor thread.
         struct wl_compositor *compositor;
         compositor = sGdkWaylandDisplayGetWlCompositor(display);
         container->surface = wl_compositor_create_surface(compositor);
     }
 
     if (!container->subsurface) {
         GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
         wl_surface* gtk_surface = sGdkWaylandWindowGetWlSurface(window);
         if (!gtk_surface) {
           // We requested the underlying wl_surface too early when container
           // is not realized yet. We'll try again before first rendering
           // to mContainer.
           return false;
         }
+        GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(container));
+        nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(display);
+        wl_subcompositor* subcompositor = waylandDisplay->GetSubcompositor();
+         container->subsurface =
+            wl_subcompositor_get_subsurface(subcompositor,
+                                            container->surface,
+                                            gtk_surface);
+        WaylandDisplayRelease(waylandDisplay);
 
-        container->subsurface =
-          wl_subcompositor_get_subsurface (container->subcompositor,
-                                           container->surface,
-                                           gtk_surface);
         gint x, y;
         gdk_window_get_position(window, &x, &y);
         wl_subsurface_set_position(container->subsurface, x, y);
         wl_subsurface_set_desync(container->subsurface);
 
         // Route input to parent wl_surface owned by Gtk+ so we get input
         // events from Gtk+.
-        GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET (container));
         wl_compositor* compositor = sGdkWaylandDisplayGetWlCompositor(display);
         wl_region* region = wl_compositor_create_region(compositor);
         wl_surface_set_input_region(container->surface, region);
         wl_region_destroy(region);
 
         container->needs_clear = true;
     }
     return true;
--- a/widget/gtk/mozcontainer.h
+++ b/widget/gtk/mozcontainer.h
@@ -53,28 +53,26 @@
 
 typedef struct _MozContainer      MozContainer;
 typedef struct _MozContainerClass MozContainerClass;
 
 /* Workaround for bug at wayland-util.h,
  * present in wayland-devel < 1.12
  */
 #ifdef MOZ_WAYLAND
-struct wl_subcompositor;
 struct wl_surface;
 struct wl_subsurface;
 #endif
 
 struct _MozContainer
 {
     GtkContainer   container;
     GList         *children;
 
 #ifdef MOZ_WAYLAND
-    struct wl_subcompositor *subcompositor;
     struct wl_surface       *surface;
     struct wl_subsurface    *subsurface;
     struct wl_egl_window    *eglwindow;
     gboolean                 needs_clear;
     gboolean                 parent_surface_committed;
     gulong                   parent_surface_committed_handler;
 #endif
 };
--- a/widget/gtk/nsClipboardWayland.cpp
+++ b/widget/gtk/nsClipboardWayland.cpp
@@ -20,16 +20,17 @@
 #include "nsImageToPixbuf.h"
 #include "nsStringStream.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "nsDragService.h"
 #include "mozwayland/mozwayland.h"
+#include "nsWaylandDisplay.h"
 
 #include "imgIContainer.h"
 
 #include <gtk/gtk.h>
 #include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
@@ -509,17 +510,17 @@ nsRetrievalContextWayland::AddDragAndDro
     mDragContext = nullptr;
 
     WaylandDataOffer* dataOffer =
         static_cast<WaylandDataOffer*>(g_hash_table_lookup(mActiveOffers,
                                                            aDropDataOffer));
     NS_ASSERTION(dataOffer, "We're missing drag and drop data offer!");
     if (dataOffer) {
         g_hash_table_remove(mActiveOffers, aDropDataOffer);
-        mDragContext = new nsWaylandDragContext(dataOffer, mDisplay);
+        mDragContext = new nsWaylandDragContext(dataOffer, mDisplay->GetDisplay());
     }
 }
 
 nsWaylandDragContext*
 nsRetrievalContextWayland::GetDragContext(void)
 {
     return mDragContext;
 }
@@ -699,122 +700,42 @@ static const struct
 gtk_primary_selection_device_listener primary_selection_device_listener = {
     primary_selection_data_offer,
     primary_selection_selection,
 };
 
 bool
 nsRetrievalContextWayland::HasSelectionSupport(void)
 {
-    return mPrimarySelectionDataDeviceManager != nullptr;
-}
-
-void
-nsRetrievalContextWayland::InitDataDeviceManager(wl_registry *registry,
-                                                 uint32_t id,
-                                                 uint32_t version)
-{
-    int data_device_manager_version = MIN (version, 3);
-    mDataDeviceManager = (wl_data_device_manager *)wl_registry_bind(registry, id,
-        &wl_data_device_manager_interface, data_device_manager_version);
-}
-
-void
-nsRetrievalContextWayland::InitPrimarySelectionDataDeviceManager(
-  wl_registry *registry, uint32_t id)
-{
-    mPrimarySelectionDataDeviceManager =
-        (gtk_primary_selection_device_manager *)wl_registry_bind(registry, id,
-            &gtk_primary_selection_device_manager_interface, 1);
-}
-
-void
-nsRetrievalContextWayland::InitSeat(wl_registry *registry,
-                                    uint32_t id, uint32_t version,
-                                    void *data)
-{
-    mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
+    return mDisplay->GetPrimarySelectionDeviceManager() != nullptr;
 }
 
-static void
-gdk_registry_handle_global(void               *data,
-                           struct wl_registry *registry,
-                           uint32_t            id,
-                           const char         *interface,
-                           uint32_t            version)
-{
-    nsRetrievalContextWayland *context =
-        static_cast<nsRetrievalContextWayland*>(data);
-
-    if (strcmp (interface, "wl_data_device_manager") == 0) {
-        context->InitDataDeviceManager(registry, id, version);
-    } else if (strcmp(interface, "wl_seat") == 0) {
-        context->InitSeat(registry, id, version, data);
-    } else if (strcmp (interface, "gtk_primary_selection_device_manager") == 0) {
-        context->InitPrimarySelectionDataDeviceManager(registry, id);
-    }
-}
-
-static void
-gdk_registry_handle_global_remove(void               *data,
-                                 struct wl_registry *registry,
-                                 uint32_t            id)
-{
-}
-
-static const struct wl_registry_listener clipboard_registry_listener = {
-    gdk_registry_handle_global,
-    gdk_registry_handle_global_remove
-};
-
 nsRetrievalContextWayland::nsRetrievalContextWayland(void)
   : mInitialized(false)
-  , mSeat(nullptr)
-  , mDataDeviceManager(nullptr)
-  , mPrimarySelectionDataDeviceManager(nullptr)
+  , mDisplay(WaylandDisplayGet())
   , mActiveOffers(g_hash_table_new(NULL, NULL))
   , mClipboardOffer(nullptr)
   , mPrimaryOffer(nullptr)
   , mDragContext(nullptr)
   , mClipboardRequestNumber(0)
   , mClipboardData(nullptr)
   , mClipboardDataLength(0)
 {
-    // Available as of GTK 3.8+
-    static auto sGdkWaylandDisplayGetWlDisplay =
-        (wl_display *(*)(GdkDisplay *))
-        dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+    wl_data_device *dataDevice =
+        wl_data_device_manager_get_data_device(mDisplay->GetDataDeviceManager(),
+                                               mDisplay->GetSeat());
+    wl_data_device_add_listener(dataDevice, &data_device_listener, this);
 
-    mDisplay = sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
-    wl_registry_add_listener(wl_display_get_registry(mDisplay),
-                             &clipboard_registry_listener, this);
-    // Call wl_display_roundtrip() twice to make sure all
-    // callbacks are processed.
-    wl_display_roundtrip(mDisplay);
-    wl_display_roundtrip(mDisplay);
 
-    // mSeat/mDataDeviceManager should be set now by
-    // gdk_registry_handle_global() as a response to
-    // wl_registry_add_listener() call.
-    if (!mDataDeviceManager || !mSeat)
-        return;
-
-    wl_data_device *dataDevice =
-        wl_data_device_manager_get_data_device(mDataDeviceManager, mSeat);
-    wl_data_device_add_listener(dataDevice, &data_device_listener, this);
-    // We have to call wl_display_roundtrip() twice otherwise data_offer_listener
-    // may not be processed because it's called from data_device_data_offer
-    // callback.
-    wl_display_roundtrip(mDisplay);
-    wl_display_roundtrip(mDisplay);
-
-    if (mPrimarySelectionDataDeviceManager) {
+    gtk_primary_selection_device_manager* manager =
+        mDisplay->GetPrimarySelectionDeviceManager();
+    if (manager) {
         gtk_primary_selection_device *primaryDataDevice =
-            gtk_primary_selection_device_manager_get_device(mPrimarySelectionDataDeviceManager,
-                                                            mSeat);
+            gtk_primary_selection_device_manager_get_device(manager,
+                                                            mDisplay->GetSeat());
         gtk_primary_selection_device_add_listener(primaryDataDevice,
             &primary_selection_device_listener, this);
     }
 
     mInitialized = true;
 }
 
 static gboolean
@@ -828,16 +749,17 @@ offer_hash_remove(gpointer wl_offer, gpo
     delete static_cast<DataOffer*>(aDataOffer);
     return true;
 }
 
 nsRetrievalContextWayland::~nsRetrievalContextWayland(void)
 {
     g_hash_table_foreach_remove(mActiveOffers, offer_hash_remove, nullptr);
     g_hash_table_destroy(mActiveOffers);
+    WaylandDisplayRelease(mDisplay);
 }
 
 GdkAtom*
 nsRetrievalContextWayland::GetTargets(int32_t aWhichClipboard,
                                       int* aTargetNum)
 {
     if (GetSelectionAtom(aWhichClipboard) == GDK_SELECTION_CLIPBOARD) {
         if (mClipboardOffer) {
@@ -920,17 +842,17 @@ nsRetrievalContextWayland::GetClipboardD
                                   mPrimaryOffer : mClipboardOffer;
         if (!dataOffer) {
             // Something went wrong. We're requested to provide clipboard data
             // but we haven't got any from wayland.
             NS_WARNING("Requested data without valid DataOffer!");
             mClipboardData = nullptr;
             mClipboardDataLength = 0;
         } else {
-            mClipboardData = dataOffer->GetData(mDisplay,
+            mClipboardData = dataOffer->GetData(mDisplay->GetDisplay(),
                 aMimeType, &mClipboardDataLength);
         }
     }
 
     *aContentLength = mClipboardDataLength;
     return reinterpret_cast<const char*>(mClipboardData);
 }
 
--- a/widget/gtk/nsClipboardWayland.h
+++ b/widget/gtk/nsClipboardWayland.h
@@ -125,27 +125,21 @@ public:
     void AddDragAndDropDataOffer(wl_data_offer *aWaylandDataOffer);
     nsWaylandDragContext* GetDragContext();
 
     void ClearDragAndDropDataOffer();
 
     void TransferFastTrackClipboard(int aClipboardRequestNumber,
                                     GtkSelectionData *aSelectionData);
 
-    void InitDataDeviceManager(wl_registry *registry, uint32_t id, uint32_t version);
-    void InitPrimarySelectionDataDeviceManager(wl_registry *registry, uint32_t id);
-    void InitSeat(wl_registry *registry, uint32_t id, uint32_t version, void *data);
     virtual ~nsRetrievalContextWayland() override;
 
 private:
     bool                        mInitialized;
-    wl_display                 *mDisplay;
-    wl_seat                    *mSeat;
-    wl_data_device_manager     *mDataDeviceManager;
-    gtk_primary_selection_device_manager *mPrimarySelectionDataDeviceManager;
+    nsWaylandDisplay*           mDisplay;
 
     // Data offers provided by Wayland data device
     GHashTable*                 mActiveOffers;
     nsAutoPtr<DataOffer>        mClipboardOffer;
     nsAutoPtr<DataOffer>        mPrimaryOffer;
     RefPtr<nsWaylandDragContext> mDragContext;
 
     int                         mClipboardRequestNumber;
--- a/widget/gtk/nsGtkKeyUtils.cpp
+++ b/widget/gtk/nsGtkKeyUtils.cpp
@@ -579,78 +579,47 @@ keyboard_handle_modifiers(void *data, st
 static const struct wl_keyboard_listener keyboard_listener = {
     keyboard_handle_keymap,
     keyboard_handle_enter,
     keyboard_handle_leave,
     keyboard_handle_key,
     keyboard_handle_modifiers,
 };
 
-static void
-seat_handle_capabilities(void *data, struct wl_seat *seat,
-                         unsigned int caps)
-{
-    static wl_keyboard *keyboard = nullptr;
-
-    if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
-        keyboard = wl_seat_get_keyboard(seat);
-        wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
-    } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
-        wl_keyboard_destroy(keyboard);
-        keyboard = nullptr;
-    }
-}
-
-static const struct wl_seat_listener seat_listener = {
-      seat_handle_capabilities,
-};
-
-static void
-gdk_registry_handle_global(void               *data,
-                           struct wl_registry *registry,
-                           uint32_t            id,
-                           const char         *interface,
-                           uint32_t            version)
-{
-    if (strcmp(interface, "wl_seat") == 0) {
-        wl_seat *seat =
-            (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
-        wl_seat_add_listener(seat, &seat_listener, data);
-    }
-}
-
-static void
-gdk_registry_handle_global_remove(void               *data,
-                                 struct wl_registry *registry,
-                                 uint32_t            id)
-{
-}
-
-static const struct wl_registry_listener keyboard_registry_listener = {
-    gdk_registry_handle_global,
-    gdk_registry_handle_global_remove
-};
-
 void
 KeymapWrapper::InitBySystemSettingsWayland()
 {
-    // Available as of GTK 3.8+
-    static auto sGdkWaylandDisplayGetWlDisplay =
-        (wl_display *(*)(GdkDisplay *))
-        dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+    GdkDeviceManager* manager =
+        gdk_display_get_device_manager(gdk_display_get_default());
+    GList* devices =
+        gdk_device_manager_list_devices(manager, GDK_DEVICE_TYPE_MASTER);
+    GdkDevice* device = nullptr;
 
-    wl_display *display =
-        sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
-    wl_registry_add_listener(wl_display_get_registry(display),
-                             &keyboard_registry_listener, this);
+    GList* list = devices;
+    while (devices) {
+        device = static_cast<GdkDevice*>(devices->data);
+        if (gdk_device_get_source(device) == GDK_SOURCE_KEYBOARD) {
+            break;
+        }
+        devices = devices->next;
+   }
 
-    // Call wl_display_roundtrip() twice to make sure all
-    // callbacks are processed.
-    wl_display_roundtrip(display);
-    wl_display_roundtrip(display);
+   if (list) {
+       g_list_free(list);
+   }
+
+   if (device) {
+      // Present in Gtk+ 3.10
+      static auto sGdkWaylandDeviceGetWlKeyboard =
+          (struct wl_keyboard * (*)(GdkDevice *device))
+          dlsym(RTLD_DEFAULT, "gdk_wayland_device_get_wl_keyboard");
+
+      wl_keyboard_add_listener(sGdkWaylandDeviceGetWlKeyboard(device),
+                               &keyboard_listener, nullptr);
+   }
 }
 #endif
 
 KeymapWrapper::~KeymapWrapper()
 {
     gdk_window_remove_filter(nullptr, FilterEvents, this);
     g_signal_handlers_disconnect_by_func(mGdkKeymap,
                                          FuncToGpointer(OnKeysChanged), this);
--- a/widget/gtk/nsLookAndFeel.h
+++ b/widget/gtk/nsLookAndFeel.h
@@ -3,16 +3,17 @@
  */
 /* 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 __nsLookAndFeel
 #define __nsLookAndFeel
 
+#include "X11UndefineNone.h"
 #include "nsXPLookAndFeel.h"
 #include "nsCOMPtr.h"
 #include "gfxFont.h"
 
 struct _GtkStyle;
 
 class nsLookAndFeel final : public nsXPLookAndFeel
 {
new file mode 100644
--- /dev/null
+++ b/widget/gtk/nsWaylandDisplay.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* 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 "nsWaylandDisplay.h"
+
+#include "base/message_loop.h"          // for MessageLoop
+#include "base/task.h"                  // for NewRunnableMethod, etc
+#include "mozilla/StaticMutex.h"
+
+namespace mozilla {
+namespace widget {
+
+#define MAX_DISPLAY_CONNECTIONS 2
+
+static nsWaylandDisplay* gWaylandDisplays[MAX_DISPLAY_CONNECTIONS];
+static StaticMutex gWaylandDisplaysMutex;
+
+// Each thread which is using wayland connection (wl_display) has to operate
+// its own wl_event_queue. Main Firefox thread wl_event_queue is handled
+// by Gtk main loop, other threads/wl_event_queue has to be handled by us.
+//
+// nsWaylandDisplay is our interface to wayland compositor. It provides wayland
+// global objects as we need (wl_display, wl_shm) and operates wl_event_queue on
+// compositor (not the main) thread.
+static void WaylandDisplayLoop(wl_display *aDisplay);
+
+// Get WaylandDisplay for given wl_display and actual calling thread.
+static nsWaylandDisplay*
+WaylandDisplayGetLocked(wl_display *aDisplay, const StaticMutexAutoLock&)
+{
+  for (auto& display: gWaylandDisplays) {
+    if (display && display->Matches(aDisplay)) {
+      NS_ADDREF(display);
+      return display;
+    }
+  }
+
+  for (auto& display: gWaylandDisplays) {
+    if (display == nullptr) {
+      display = new nsWaylandDisplay(aDisplay);
+      NS_ADDREF(display);
+      return display;
+    }
+  }
+
+  MOZ_CRASH("There's too many wayland display conections!");
+  return nullptr;
+}
+
+nsWaylandDisplay*
+WaylandDisplayGet(GdkDisplay* aGdkDisplay)
+{
+  if (!aGdkDisplay) {
+    aGdkDisplay = gdk_display_get_default();
+  }
+
+  // Available as of GTK 3.8+
+  static auto sGdkWaylandDisplayGetWlDisplay =
+      (wl_display *(*)(GdkDisplay *))
+      dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+
+  wl_display *display = sGdkWaylandDisplayGetWlDisplay(aGdkDisplay);
+
+  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+  return WaylandDisplayGetLocked(display, lock);
+}
+
+static bool
+WaylandDisplayReleaseLocked(nsWaylandDisplay* aDisplay,
+                            const StaticMutexAutoLock&)
+{
+  for (auto& display: gWaylandDisplays) {
+    if (display == aDisplay) {
+      int rc = display->Release();
+      if (rc == 0) {
+        display = nullptr;
+      }
+      return true;
+    }
+  }
+  MOZ_ASSERT(false, "Missing nsWaylandDisplay for this thread!");
+  return false;
+}
+
+void
+WaylandDisplayRelease(nsWaylandDisplay* aDisplay)
+{
+  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+  WaylandDisplayReleaseLocked(aDisplay, lock);
+}
+
+static void
+WaylandDisplayLoopLocked(wl_display* aDisplay,
+                         const StaticMutexAutoLock&)
+{
+  for (auto& display: gWaylandDisplays) {
+    if (display && display->Matches(aDisplay)) {
+      if (display->DisplayLoop()) {
+        MessageLoop::current()->PostDelayedTask(
+            NewRunnableFunction("WaylandDisplayLoop",
+                               &WaylandDisplayLoop,
+                               aDisplay),
+            EVENT_LOOP_DELAY);
+      }
+      break;
+    }
+  }
+}
+
+static void
+WaylandDisplayLoop(wl_display* aDisplay)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
+  WaylandDisplayLoopLocked(aDisplay, lock);
+}
+
+void
+nsWaylandDisplay::SetShm(wl_shm* aShm)
+{
+  mShm = aShm;
+}
+
+void
+nsWaylandDisplay::SetSubcompositor(wl_subcompositor* aSubcompositor)
+{
+  mSubcompositor = aSubcompositor;
+}
+
+void
+nsWaylandDisplay::SetDataDeviceManager(wl_data_device_manager* aDataDeviceManager)
+{
+  mDataDeviceManager = aDataDeviceManager;
+}
+
+void
+nsWaylandDisplay::SetSeat(wl_seat* aSeat)
+{
+  mSeat = aSeat;
+}
+
+void
+nsWaylandDisplay::SetPrimarySelectionDeviceManager(
+    gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager)
+{
+  mPrimarySelectionDeviceManager = aPrimarySelectionDeviceManager;
+}
+
+static void
+global_registry_handler(void *data, wl_registry *registry, uint32_t id,
+                        const char *interface, uint32_t version)
+{
+  auto display = reinterpret_cast<nsWaylandDisplay *>(data);
+
+  if (strcmp(interface, "wl_shm") == 0) {
+    auto shm = static_cast<wl_shm*>(
+        wl_registry_bind(registry, id, &wl_shm_interface, 1));
+    wl_proxy_set_queue((struct wl_proxy *)shm,
+                        display->GetEventQueue());
+    display->SetShm(shm);
+  } else if (strcmp(interface, "wl_data_device_manager") == 0) {
+    int data_device_manager_version = MIN(version, 3);
+    auto data_device_manager =
+        static_cast<wl_data_device_manager *>(wl_registry_bind(registry, id,
+                                              &wl_data_device_manager_interface,
+                                              data_device_manager_version));
+    wl_proxy_set_queue((struct wl_proxy *)data_device_manager,
+                       display->GetEventQueue());
+    display->SetDataDeviceManager(data_device_manager);
+  } else if (strcmp(interface, "wl_seat") == 0) {
+    auto seat =
+        static_cast<wl_seat*>(wl_registry_bind(registry, id,
+                              &wl_seat_interface, 1));
+    wl_proxy_set_queue((struct wl_proxy *)seat,
+                       display->GetEventQueue());
+    display->SetSeat(seat);
+  } else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
+    auto primary_selection_device_manager =
+        static_cast<gtk_primary_selection_device_manager *>(
+                              wl_registry_bind(registry, id,
+                              &gtk_primary_selection_device_manager_interface, 1));
+    wl_proxy_set_queue((struct wl_proxy *)primary_selection_device_manager,
+                       display->GetEventQueue());
+    display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
+  } else if(strcmp(interface, "wl_subcompositor") == 0) {
+    auto subcompositor =
+        static_cast<wl_subcompositor*>(wl_registry_bind(registry,
+                                       id,
+                                       &wl_subcompositor_interface,
+                                       1));
+    wl_proxy_set_queue((struct wl_proxy *)subcompositor,
+                       display->GetEventQueue());
+    display->SetSubcompositor(subcompositor);
+  }
+}
+
+static void
+global_registry_remover(void *data, wl_registry *registry, uint32_t id)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+  global_registry_handler,
+  global_registry_remover
+};
+
+bool
+nsWaylandDisplay::DisplayLoop()
+{
+  wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
+  return true;
+}
+
+bool
+nsWaylandDisplay::Matches(wl_display *aDisplay)
+{
+  return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
+}
+
+NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
+
+nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
+  : mThreadId(PR_GetCurrentThread())
+  , mDisplay(aDisplay)
+  , mEventQueue(nullptr)
+  , mDataDeviceManager(nullptr)
+  , mSubcompositor(nullptr)
+  , mSeat(nullptr)
+  , mShm(nullptr)
+  , mPrimarySelectionDeviceManager(nullptr)
+{
+  wl_registry* registry = wl_display_get_registry(mDisplay);
+  wl_registry_add_listener(registry, &registry_listener, this);
+
+  if (NS_IsMainThread()) {
+    // Use default event queue in main thread operated by Gtk+.
+    mEventQueue = nullptr;
+    wl_display_roundtrip(mDisplay);
+    wl_display_roundtrip(mDisplay);
+  } else {
+    mEventQueue = wl_display_create_queue(mDisplay);
+    MessageLoop::current()->PostTask(NewRunnableFunction(
+        "WaylandDisplayLoop", &WaylandDisplayLoop, mDisplay));
+    wl_proxy_set_queue((struct wl_proxy *)registry, mEventQueue);
+    wl_display_roundtrip_queue(mDisplay, mEventQueue);
+    wl_display_roundtrip_queue(mDisplay, mEventQueue);
+  }
+}
+
+nsWaylandDisplay::~nsWaylandDisplay()
+{
+  MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
+  // Owned by Gtk+, we don't need to release
+  mDisplay = nullptr;
+
+  if (mEventQueue) {
+    wl_event_queue_destroy(mEventQueue);
+    mEventQueue = nullptr;
+  }
+}
+
+}  // namespace widget
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/gtk/nsWaylandDisplay.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* 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 __MOZ_WAYLAND_REGISTRY_H__
+#define __MOZ_WAYLAND_REGISTRY_H__
+
+#include "mozwayland/mozwayland.h"
+#include "wayland/gtk-primary-selection-client-protocol.h"
+
+namespace mozilla {
+namespace widget {
+
+// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with
+// compositor event loop.
+#define EVENT_LOOP_DELAY (1000/240)
+
+// Our general connection to Wayland display server,
+// holds our display connection and runs event loop.
+class nsWaylandDisplay : public nsISupports {
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+public:
+  explicit nsWaylandDisplay(wl_display *aDisplay);
+
+  bool                     DisplayLoop();
+  bool                     Matches(wl_display *aDisplay);
+
+  wl_display*              GetDisplay()             { return mDisplay; };
+  wl_event_queue*          GetEventQueue()          { return mEventQueue; };
+  wl_subcompositor*        GetSubcompositor(void)   { return mSubcompositor; };
+  wl_data_device_manager*  GetDataDeviceManager(void) { return mDataDeviceManager; };
+  wl_seat*                 GetSeat(void)            { return mSeat; };
+  wl_shm*                  GetShm(void)             { return mShm; };
+  gtk_primary_selection_device_manager*
+  GetPrimarySelectionDeviceManager(void)            { return mPrimarySelectionDeviceManager; };
+
+public:
+  void SetShm(wl_shm* aShm);
+  void SetSubcompositor(wl_subcompositor* aSubcompositor);
+  void SetDataDeviceManager(wl_data_device_manager* aDataDeviceManager);
+  void SetSeat(wl_seat* aSeat);
+  void SetPrimarySelectionDeviceManager(
+      gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager);
+
+private:
+  virtual ~nsWaylandDisplay();
+
+  PRThread*           mThreadId;
+  wl_display*         mDisplay;
+  wl_event_queue*     mEventQueue;
+  wl_data_device_manager* mDataDeviceManager;
+  wl_subcompositor*   mSubcompositor;
+  wl_seat*            mSeat;
+  wl_shm*             mShm;
+  gtk_primary_selection_device_manager* mPrimarySelectionDeviceManager;
+};
+
+nsWaylandDisplay* WaylandDisplayGet(GdkDisplay* aGdkDisplay = nullptr);
+void WaylandDisplayRelease(nsWaylandDisplay* aDisplay);
+
+}  // namespace widget
+}  // namespace mozilla
+
+#endif // __MOZ_WAYLAND_REGISTRY_H__
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -7298,29 +7298,16 @@ void nsWindow::GetCompositorWidgetInitDa
   *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
                                 (mXWindow != X11None) ? mXWindow : (uintptr_t)nullptr,
                                 mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(),
                                 mIsTransparent && !mHasAlphaVisual,
                                 GetClientSize());
 }
 
 #ifdef MOZ_WAYLAND
-wl_display*
-nsWindow::GetWaylandDisplay()
-{
-  // Available as of GTK 3.8+
-  static auto sGdkWaylandDisplayGetWlDisplay =
-      (wl_display *(*)(GdkDisplay *))
-      dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
-
-  GdkDisplay* gdkDisplay = gdk_display_get_default();
-  return mIsX11Display ? nullptr :
-                         sGdkWaylandDisplayGetWlDisplay(gdkDisplay);
-}
-
 wl_surface*
 nsWindow::GetWaylandSurface()
 {
   if (mContainer)
     return moz_container_get_wl_surface(MOZ_CONTAINER(mContainer));
 
   NS_WARNING("nsWindow::GetWaylandSurfaces(): We don't have any mContainer for drawing!");
   return nullptr;