Blob Blame History Raw
diff -up thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp.wayland thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp
--- thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/GtkCompositorWidget.cpp	2019-02-05 14:26:16.973316654 +0100
@@ -38,7 +38,9 @@ GtkCompositorWidget::GtkCompositorWidget
 
     // Grab the window's visual and depth
     XWindowAttributes windowAttrs;
-    XGetWindowAttributes(mXDisplay, mXWindow, &windowAttrs);
+    if (!XGetWindowAttributes(mXDisplay, mXWindow, &windowAttrs)) {
+      NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!");
+    }
 
     Visual* visual = windowAttrs.visual;
     int depth = windowAttrs.depth;
diff -up thunderbird-60.5.0/widget/gtk/moz.build.wayland thunderbird-60.5.0/widget/gtk/moz.build
--- thunderbird-60.5.0/widget/gtk/moz.build.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/moz.build	2019-02-05 14:26:16.974316651 +0100
@@ -99,6 +99,7 @@ if CONFIG['MOZ_X11']:
 if CONFIG['MOZ_WAYLAND']:
     UNIFIED_SOURCES += [
         'nsClipboardWayland.cpp',
+        'nsWaylandDisplay.cpp',
         'WindowSurfaceWayland.cpp',
     ]
 
@@ -123,6 +124,7 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
+    '/layout/base',
     '/layout/generic',
     '/layout/xul',
     '/other-licenses/atk-1.0',
diff -up thunderbird-60.5.0/widget/gtk/mozcontainer.cpp.wayland thunderbird-60.5.0/widget/gtk/mozcontainer.cpp
--- thunderbird-60.5.0/widget/gtk/mozcontainer.cpp.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozcontainer.cpp	2019-02-05 15:09:24.116970135 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -7,9 +7,10 @@
 
 #include "mozcontainer.h"
 #include <gtk/gtk.h>
-#ifdef MOZ_WAYLAND
 #include <gdk/gdkx.h>
-#include <gdk/gdkwayland.h>
+#ifdef MOZ_WAYLAND
+#include "nsWaylandDisplay.h"
+#include <wayland-egl.h>
 #endif
 #include <stdio.h>
 #include <dlfcn.h>
@@ -19,12 +20,20 @@
 #include "maiRedundantObjectFactory.h"
 #endif
 
+#ifdef MOZ_WAYLAND
+using namespace mozilla;
+using namespace mozilla::widget;
+#endif
+
 /* 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);
+#if defined(MOZ_WAYLAND)
+static gboolean moz_container_map_wayland(GtkWidget *widget, GdkEventAny *event);
+#endif
 static void moz_container_unmap(GtkWidget *widget);
 static void moz_container_realize(GtkWidget *widget);
 static void moz_container_size_allocate(GtkWidget *widget,
@@ -114,29 +123,6 @@ void moz_container_put(MozContainer *con
   gtk_widget_set_parent(child_widget, GTK_WIDGET(container));
 }
 
-void moz_container_move(MozContainer *container, GtkWidget *child_widget,
-                        gint x, gint y, gint width, gint height) {
-  MozContainerChild *child;
-  GtkAllocation new_allocation;
-
-  child = moz_container_get_child(container, child_widget);
-
-  child->x = x;
-  child->y = y;
-
-  new_allocation.x = x;
-  new_allocation.y = y;
-  new_allocation.width = width;
-  new_allocation.height = height;
-
-  /* printf("moz_container_move %p %p will allocate to %d %d %d %d\n",
-     (void *)container, (void *)child_widget,
-     new_allocation.x, new_allocation.y,
-     new_allocation.width, new_allocation.height); */
-
-  gtk_widget_size_allocate(child_widget, &new_allocation);
-}
-
 /* static methods */
 
 void moz_container_class_init(MozContainerClass *klass) {
@@ -146,6 +132,11 @@ void moz_container_class_init(MozContain
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
 
   widget_class->map = moz_container_map;
+#if defined(MOZ_WAYLAND)
+    if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+      widget_class->map_event = moz_container_map_wayland;
+    }
+#endif
   widget_class->unmap = moz_container_unmap;
   widget_class->realize = moz_container_realize;
   widget_class->size_allocate = moz_container_size_allocate;
@@ -155,109 +146,81 @@ void moz_container_class_init(MozContain
   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)
-  {
-    GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
-    if (GDK_IS_WAYLAND_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->frame_callback_handler = nullptr;
+  // We can draw to x11 window any time.
+  container->ready_to_draw = GDK_IS_X11_DISPLAY(gdk_display_get_default());
+  container->surface_needs_clear = true;
 #endif
 }
 
 #if defined(MOZ_WAYLAND)
-/* We want to draw to GdkWindow owned by mContainer from Compositor thread but
- * Gtk+ can be used in main thread only. So we create wayland wl_surface
- * and attach it as an overlay to GdkWindow.
- *
- * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c
- *  for reference.
- */
-static gboolean moz_container_map_surface(MozContainer *container) {
-  // Available as of GTK 3.8+
-  static auto sGdkWaylandDisplayGetWlCompositor =
-      (wl_compositor * (*)(GdkDisplay *))
-          dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
+static wl_surface *moz_container_get_gtk_container_surface(
+    MozContainer *container) {
   static auto sGdkWaylandWindowGetWlSurface = (wl_surface * (*)(GdkWindow *))
       dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
 
-  GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
-  if (GDK_IS_X11_DISPLAY(display)) return false;
+  GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
+  return sGdkWaylandWindowGetWlSurface(window);
+}
 
-  if (container->subsurface && container->surface) return true;
+static void frame_callback_handler(void *data, struct wl_callback *callback,
+                                   uint32_t time) {
+  MozContainer *container = MOZ_CONTAINER(data);
+  g_clear_pointer(&container->frame_callback_handler, wl_callback_destroy);
+  container->ready_to_draw = true;
+}
 
-  if (!container->surface) {
-    struct wl_compositor *compositor;
-    compositor = sGdkWaylandDisplayGetWlCompositor(display);
-    container->surface = wl_compositor_create_surface(compositor);
-  }
+static const struct wl_callback_listener frame_listener = {
+    frame_callback_handler};
 
-  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;
-    }
+static gboolean moz_container_map_wayland(GtkWidget *widget, GdkEventAny *event) {
+  MozContainer* container = MOZ_CONTAINER(widget);
 
-    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);
+  if (container->ready_to_draw || container->frame_callback_handler) {
+    return FALSE;
+  }
 
-    // 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);
+  wl_surface *gtk_container_surface =
+      moz_container_get_gtk_container_surface(container);
+
+  if (gtk_container_surface) {
+    container->frame_callback_handler = wl_surface_frame(gtk_container_surface);
+    wl_callback_add_listener(container->frame_callback_handler, &frame_listener,
+                             container);
   }
-  return true;
+
+  return FALSE;
 }
 
-static void moz_container_unmap_surface(MozContainer *container) {
+static void moz_container_unmap_wayland(MozContainer *container) {
   g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
   g_clear_pointer(&container->surface, wl_surface_destroy);
+  g_clear_pointer(&container->frame_callback_handler, wl_callback_destroy);
+
+  container->surface_needs_clear = true;
+  container->ready_to_draw = false;
 }
 
+static gint moz_container_get_scale(MozContainer *container) {
+    static auto sGdkWindowGetScaleFactorPtr = (gint(*)(GdkWindow *))dlsym(
+        RTLD_DEFAULT, "gdk_window_get_scale_factor");
+
+    if (sGdkWindowGetScaleFactorPtr) {
+      GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
+      return (*sGdkWindowGetScaleFactorPtr)(window);
+    }
+
+    return 1;
+}
 #endif
 
 void moz_container_map(GtkWidget *widget) {
@@ -283,7 +246,9 @@ void moz_container_map(GtkWidget *widget
   if (gtk_widget_get_has_window(widget)) {
     gdk_window_show(gtk_widget_get_window(widget));
 #if defined(MOZ_WAYLAND)
-    moz_container_map_surface(MOZ_CONTAINER(widget));
+    if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+      moz_container_map_wayland(widget, nullptr);
+    }
 #endif
   }
 }
@@ -296,7 +261,9 @@ void moz_container_unmap(GtkWidget *widg
   if (gtk_widget_get_has_window(widget)) {
     gdk_window_hide(gtk_widget_get_window(widget));
 #if defined(MOZ_WAYLAND)
-    moz_container_unmap_surface(MOZ_CONTAINER(widget));
+    if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+      moz_container_unmap_wayland(MOZ_CONTAINER(widget));
+    }
 #endif
   }
 }
@@ -485,13 +452,62 @@ static void moz_container_add(GtkContain
 
 #ifdef MOZ_WAYLAND
 struct wl_surface *moz_container_get_wl_surface(MozContainer *container) {
-  if (!container->subsurface || !container->surface) {
+  if (!container->surface) {
+    if (!container->ready_to_draw) {
+      return nullptr;
+    }
+    GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
+
+    // Available as of GTK 3.8+
+    static auto sGdkWaylandDisplayGetWlCompositor =
+        (wl_compositor * (*)(GdkDisplay *))
+            dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
+    struct wl_compositor *compositor =
+        sGdkWaylandDisplayGetWlCompositor(display);
+    container->surface = wl_compositor_create_surface(compositor);
+
+    nsWaylandDisplay *waylandDisplay = WaylandDisplayGet(display);
+    container->subsurface = wl_subcompositor_get_subsurface(
+        waylandDisplay->GetSubcompositor(), container->surface,
+        moz_container_get_gtk_container_surface(container));
+    WaylandDisplayRelease(waylandDisplay);
+
     GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
-    if (!gdk_window_is_visible(window)) return nullptr;
+    gint x, y;
+    gdk_window_get_position(window, &x, &y);
+    wl_subsurface_set_position(container->subsurface, x, y);
+    wl_subsurface_set_desync(container->subsurface);
 
-    moz_container_map_surface(container);
+    // Route input to parent wl_surface owned by Gtk+ so we get input
+    // events from Gtk+.
+    wl_region *region = wl_compositor_create_region(compositor);
+    wl_surface_set_input_region(container->surface, region);
+    wl_region_destroy(region);
+
+    wl_surface_set_buffer_scale(container->surface,
+                                moz_container_get_scale(container));
   }
 
   return container->surface;
 }
+
+struct wl_egl_window *moz_container_get_wl_egl_window(MozContainer *container) {
+  if (!container->eglwindow) {
+    wl_surface *surface = moz_container_get_wl_surface(container);
+    if (!surface) {
+      return nullptr;
+    }
+  }
+  return container->eglwindow;
+}
+
+gboolean moz_container_has_wl_egl_window(MozContainer *container) {
+  return container->eglwindow ? true : false;
+}
+
+gboolean moz_container_surface_needs_clear(MozContainer *container) {
+  gboolean state = container->surface_needs_clear;
+  container->surface_needs_clear = false;
+  return state;
+}
 #endif
diff -up thunderbird-60.5.0/widget/gtk/mozcontainer.h.wayland thunderbird-60.5.0/widget/gtk/mozcontainer.h
--- thunderbird-60.5.0/widget/gtk/mozcontainer.h.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozcontainer.h	2019-02-05 14:26:16.974316651 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -63,7 +63,6 @@ typedef struct _MozContainerClass MozCon
  * present in wayland-devel < 1.12
  */
 #ifdef MOZ_WAYLAND
-struct wl_subcompositor;
 struct wl_surface;
 struct wl_subsurface;
 #endif
@@ -73,9 +72,12 @@ struct _MozContainer {
   GList *children;
 
 #ifdef MOZ_WAYLAND
-  struct wl_subcompositor *subcompositor;
   struct wl_surface *surface;
   struct wl_subsurface *subsurface;
+  struct wl_egl_window *eglwindow;
+  struct wl_callback *frame_callback_handler;
+  gboolean surface_needs_clear;
+  gboolean ready_to_draw;
 #endif
 };
 
@@ -87,11 +89,13 @@ GType moz_container_get_type(void);
 GtkWidget *moz_container_new(void);
 void moz_container_put(MozContainer *container, GtkWidget *child_widget, gint x,
                        gint y);
-void moz_container_move(MozContainer *container, GtkWidget *child_widget,
-                        gint x, gint y, gint width, gint height);
 
 #ifdef MOZ_WAYLAND
 struct wl_surface *moz_container_get_wl_surface(MozContainer *container);
+struct wl_egl_window *moz_container_get_wl_egl_window(MozContainer *container);
+
+gboolean moz_container_has_wl_egl_window(MozContainer *container);
+gboolean moz_container_surface_needs_clear(MozContainer *container);
 #endif
 
 #endif /* __MOZ_CONTAINER_H__ */
diff -up thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c.wayland thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c
--- thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozgtk/mozgtk.c	2019-02-05 14:26:16.974316651 +0100
@@ -26,6 +26,7 @@ STUB(gdk_display_sync)
 STUB(gdk_display_warp_pointer)
 STUB(gdk_drag_context_get_actions)
 STUB(gdk_drag_context_get_dest_window)
+STUB(gdk_drag_context_get_source_window)
 STUB(gdk_drag_context_list_targets)
 STUB(gdk_drag_status)
 STUB(gdk_error_trap_pop)
@@ -55,6 +56,7 @@ STUB(gdk_pointer_grab)
 STUB(gdk_pointer_ungrab)
 STUB(gdk_property_change)
 STUB(gdk_property_get)
+STUB(gdk_property_delete)
 STUB(gdk_screen_get_default)
 STUB(gdk_screen_get_display)
 STUB(gdk_screen_get_font_options)
@@ -136,6 +138,8 @@ STUB(gdk_x11_get_xatom_by_name)
 STUB(gdk_x11_get_xatom_by_name_for_display)
 STUB(gdk_x11_lookup_xdisplay)
 STUB(gdk_x11_screen_get_xscreen)
+STUB(gdk_x11_screen_get_screen_number)
+STUB(gdk_x11_screen_lookup_visual)
 STUB(gdk_x11_screen_supports_net_wm_hint)
 STUB(gdk_x11_visual_get_xvisual)
 STUB(gdk_x11_window_foreign_new_for_display)
@@ -267,6 +271,7 @@ STUB(gtk_im_context_set_client_window)
 STUB(gtk_im_context_set_cursor_location)
 STUB(gtk_im_context_set_surrounding)
 STUB(gtk_im_context_simple_new)
+STUB(gtk_im_multicontext_get_context_id)
 STUB(gtk_im_multicontext_get_type)
 STUB(gtk_im_multicontext_new)
 STUB(gtk_info_bar_get_type)
@@ -411,6 +416,7 @@ STUB(gtk_table_get_type)
 STUB(gtk_table_new)
 STUB(gtk_target_list_add)
 STUB(gtk_target_list_add_image_targets)
+STUB(gtk_target_list_add_text_targets)
 STUB(gtk_target_list_new)
 STUB(gtk_target_list_unref)
 STUB(gtk_targets_include_image)
@@ -491,6 +497,7 @@ STUB(gtk_widget_unrealize)
 STUB(gtk_window_deiconify)
 STUB(gtk_window_fullscreen)
 STUB(gtk_window_get_group)
+STUB(gtk_window_get_modal)
 STUB(gtk_window_get_transient_for)
 STUB(gtk_window_get_type)
 STUB(gtk_window_get_type_hint)
diff -up thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c.wayland thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c
--- thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c.wayland	2019-01-22 20:44:02.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.c	2019-02-05 14:26:16.974316651 +0100
@@ -1,14 +1,23 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 <stdlib.h>
 #include "mozilla/Types.h"
 #include <gtk/gtk.h>
+#include <gtk/gtkx.h>
 #include <gdk/gdkwayland.h>
 
+union wl_argument;
+
+/* Those strucures are just placeholders and will be replaced by
+ * real symbols from libwayland during run-time linking. We need to make
+ * them explicitly visible.
+ */
+#pragma GCC visibility push(default)
 const struct wl_interface wl_buffer_interface;
 const struct wl_interface wl_callback_interface;
 const struct wl_interface wl_data_device_interface;
@@ -22,6 +31,7 @@ const struct wl_interface wl_seat_interf
 const struct wl_interface wl_surface_interface;
 const struct wl_interface wl_subsurface_interface;
 const struct wl_interface wl_subcompositor_interface;
+#pragma GCC visibility pop
 
 MOZ_EXPORT void wl_event_queue_destroy(struct wl_event_queue *queue) {}
 
@@ -75,6 +85,10 @@ MOZ_EXPORT const void *wl_proxy_get_list
   return NULL;
 }
 
+typedef int (*wl_dispatcher_func_t)(const void *, void *, uint32_t,
+                                    const struct wl_message *,
+                                    union wl_argument *);
+
 MOZ_EXPORT int wl_proxy_add_dispatcher(struct wl_proxy *proxy,
                                        wl_dispatcher_func_t dispatcher_func,
                                        const void *dispatcher_data,
@@ -160,3 +174,13 @@ MOZ_EXPORT void wl_display_cancel_read(s
 MOZ_EXPORT int wl_display_read_events(struct wl_display *display) { return -1; }
 
 MOZ_EXPORT void wl_log_set_handler_client(wl_log_func_t handler) {}
+
+MOZ_EXPORT struct wl_egl_window *wl_egl_window_create(
+    struct wl_surface *surface, int width, int height) {
+  return NULL;
+}
+
+MOZ_EXPORT void wl_egl_window_destroy(struct wl_egl_window *egl_window) {}
+
+MOZ_EXPORT void wl_egl_window_resize(struct wl_egl_window *egl_window,
+                                     int width, int height, int dx, int dy) {}
diff -up thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h.wayland thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h
--- thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h.wayland	2019-02-05 14:26:16.975316648 +0100
+++ thunderbird-60.5.0/widget/gtk/mozwayland/mozwayland.h	2019-02-05 14:26:16.975316648 +0100
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Wayland compatibility header, it makes Firefox build with
+   wayland-1.2 and Gtk+ 3.10.
+*/
+
+#ifndef __MozWayland_h_
+#define __MozWayland_h_
+
+#include "mozilla/Types.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkwayland.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOZ_EXPORT int wl_display_roundtrip_queue(struct wl_display *display,
+                                          struct wl_event_queue *queue);
+MOZ_EXPORT uint32_t wl_proxy_get_version(struct wl_proxy *proxy);
+MOZ_EXPORT struct wl_proxy *wl_proxy_marshal_constructor(
+    struct wl_proxy *proxy, uint32_t opcode,
+    const struct wl_interface *interface, ...);
+
+/* We need implement some missing functions from wayland-client-protocol.h
+ */
+#ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM
+enum wl_data_device_manager_dnd_action {
+  WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE = 0,
+  WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY = 1,
+  WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE = 2,
+  WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK = 4
+};
+#endif
+
+#ifndef WL_DATA_OFFER_SET_ACTIONS
+#define WL_DATA_OFFER_SET_ACTIONS 4
+
+struct moz_wl_data_offer_listener {
+  void (*offer)(void *data, struct wl_data_offer *wl_data_offer,
+                const char *mime_type);
+  void (*source_actions)(void *data, struct wl_data_offer *wl_data_offer,
+                         uint32_t source_actions);
+  void (*action)(void *data, struct wl_data_offer *wl_data_offer,
+                 uint32_t dnd_action);
+};
+
+static inline void wl_data_offer_set_actions(
+    struct wl_data_offer *wl_data_offer, uint32_t dnd_actions,
+    uint32_t preferred_action) {
+  wl_proxy_marshal((struct wl_proxy *)wl_data_offer, WL_DATA_OFFER_SET_ACTIONS,
+                   dnd_actions, preferred_action);
+}
+#else
+typedef struct wl_data_offer_listener moz_wl_data_offer_listener;
+#endif
+
+#ifndef WL_SUBCOMPOSITOR_GET_SUBSURFACE
+#define WL_SUBCOMPOSITOR_GET_SUBSURFACE 1
+struct wl_subcompositor;
+
+// Emulate what mozilla header wrapper does - make the
+// wl_subcompositor_interface always visible.
+#pragma GCC visibility push(default)
+extern const struct wl_interface wl_subsurface_interface;
+extern const struct wl_interface wl_subcompositor_interface;
+#pragma GCC visibility pop
+
+#define WL_SUBSURFACE_DESTROY 0
+#define WL_SUBSURFACE_SET_POSITION 1
+#define WL_SUBSURFACE_PLACE_ABOVE 2
+#define WL_SUBSURFACE_PLACE_BELOW 3
+#define WL_SUBSURFACE_SET_SYNC 4
+#define WL_SUBSURFACE_SET_DESYNC 5
+
+static inline struct wl_subsurface *wl_subcompositor_get_subsurface(
+    struct wl_subcompositor *wl_subcompositor, struct wl_surface *surface,
+    struct wl_surface *parent) {
+  struct wl_proxy *id;
+
+  id = wl_proxy_marshal_constructor(
+      (struct wl_proxy *)wl_subcompositor, WL_SUBCOMPOSITOR_GET_SUBSURFACE,
+      &wl_subsurface_interface, NULL, surface, parent);
+
+  return (struct wl_subsurface *)id;
+}
+
+static inline void wl_subsurface_set_position(
+    struct wl_subsurface *wl_subsurface, int32_t x, int32_t y) {
+  wl_proxy_marshal((struct wl_proxy *)wl_subsurface, WL_SUBSURFACE_SET_POSITION,
+                   x, y);
+}
+
+static inline void wl_subsurface_set_desync(
+    struct wl_subsurface *wl_subsurface) {
+  wl_proxy_marshal((struct wl_proxy *)wl_subsurface, WL_SUBSURFACE_SET_DESYNC);
+}
+
+static inline void wl_subsurface_destroy(struct wl_subsurface *wl_subsurface) {
+  wl_proxy_marshal((struct wl_proxy *)wl_subsurface, WL_SUBSURFACE_DESTROY);
+
+  wl_proxy_destroy((struct wl_proxy *)wl_subsurface);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MozWayland_h_ */
diff -up thunderbird-60.5.0/widget/gtk/nsClipboard.cpp.wayland thunderbird-60.5.0/widget/gtk/nsClipboard.cpp
--- thunderbird-60.5.0/widget/gtk/nsClipboard.cpp.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsClipboard.cpp	2019-02-05 14:26:16.975316648 +0100
@@ -72,7 +72,7 @@ nsClipboard::~nsClipboard() {
   }
 }
 
-NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
+NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard, nsIObserver)
 
 nsresult nsClipboard::Init(void) {
   GdkDisplay *display = gdk_display_get_default();
diff -up thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp.wayland thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp
--- thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsClipboardWayland.cpp	2019-02-05 14:26:16.975316648 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -20,9 +20,11 @@
 #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"
 
@@ -31,15 +33,43 @@
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
-#include <gtk/gtk.h>
-#include <gdk/gdkwayland.h>
 #include <errno.h>
 
-#include "wayland/gtk-primary-selection-client-protocol.h"
-
 const char *nsRetrievalContextWayland::sTextMimeTypes[TEXT_MIME_TYPES_NUM] = {
     "text/plain;charset=utf-8", "UTF8_STRING", "COMPOUND_TEXT"};
 
+static inline GdkDragAction wl_to_gdk_actions(uint32_t dnd_actions) {
+  GdkDragAction actions = GdkDragAction(0);
+
+  if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+    actions = GdkDragAction(actions | GDK_ACTION_COPY);
+  if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+    actions = GdkDragAction(actions | GDK_ACTION_MOVE);
+
+  return actions;
+}
+
+static inline uint32_t gdk_to_wl_actions(GdkDragAction action) {
+  uint32_t dnd_actions = 0;
+
+  if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
+    dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+  if (action & GDK_ACTION_MOVE)
+    dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+  return dnd_actions;
+}
+
+static GtkWidget *get_gtk_widget_for_wl_surface(struct wl_surface *surface) {
+  GdkWindow *gdkParentWindow =
+      static_cast<GdkWindow *>(wl_surface_get_user_data(surface));
+
+  gpointer user_data = nullptr;
+  gdk_window_get_user_data(gdkParentWindow, &user_data);
+
+  return GTK_WIDGET(user_data);
+}
+
 void DataOffer::AddMIMEType(const char *aMimeType) {
   GdkAtom atom = gdk_atom_intern(aMimeType, FALSE);
   mTargetMIMETypes.AppendElement(atom);
@@ -98,7 +128,7 @@ char *DataOffer::GetData(wl_display *aDi
 
   GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
   GError *error = nullptr;
-  char *clipboardData;
+  char *clipboardData = nullptr;
 
   g_io_channel_set_encoding(channel, nullptr, &error);
   if (!error) {
@@ -138,31 +168,92 @@ bool WaylandDataOffer::RequestDataTransf
   return false;
 }
 
+void WaylandDataOffer::DragOfferAccept(const char *aMimeType, uint32_t aTime) {
+  wl_data_offer_accept(mWaylandDataOffer, aTime, aMimeType);
+}
+
+/* We follow logic of gdk_wayland_drag_context_commit_status()/gdkdnd-wayland.c
+ * here.
+ */
+void WaylandDataOffer::SetDragStatus(GdkDragAction aAction, uint32_t aTime) {
+  uint32_t dnd_actions = gdk_to_wl_actions(aAction);
+  uint32_t all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
+                         WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+  wl_data_offer_set_actions(mWaylandDataOffer, all_actions, dnd_actions);
+
+  /* Workaround Wayland D&D architecture here. To get the data_device_drop()
+     signal (which routes to nsDragService::GetData() call) we need to
+     accept at least one mime type before data_device_leave().
+
+     Real wl_data_offer_accept() for actualy requested data mime type is
+     called from nsDragService::GetData().
+  */
+  if (mTargetMIMETypes[0]) {
+    wl_data_offer_accept(mWaylandDataOffer, aTime,
+                         gdk_atom_name(mTargetMIMETypes[0]));
+  }
+}
+
+void WaylandDataOffer::SetSelectedDragAction(uint32_t aWaylandAction) {
+  mSelectedDragAction = aWaylandAction;
+}
+
+GdkDragAction WaylandDataOffer::GetSelectedDragAction() {
+  return wl_to_gdk_actions(mSelectedDragAction);
+}
+
+void WaylandDataOffer::SetAvailableDragActions(uint32_t aWaylandActions) {
+  mAvailableDragAction = aWaylandActions;
+}
+
+GdkDragAction WaylandDataOffer::GetAvailableDragActions() {
+  return wl_to_gdk_actions(mAvailableDragAction);
+}
+
 static void data_offer_offer(void *data, struct wl_data_offer *wl_data_offer,
                              const char *type) {
   auto *offer = static_cast<DataOffer *>(data);
   offer->AddMIMEType(type);
 }
 
+/* Advertise all available drag and drop actions from source.
+ * We don't use that but follow gdk_wayland_drag_context_commit_status()
+ * from gdkdnd-wayland.c here.
+ */
 static void data_offer_source_actions(void *data,
                                       struct wl_data_offer *wl_data_offer,
-                                      uint32_t source_actions) {}
+                                      uint32_t source_actions) {
+  auto *offer = static_cast<WaylandDataOffer *>(data);
+  offer->SetAvailableDragActions(source_actions);
+}
 
+/* Advertise recently selected drag and drop action by compositor, based
+ * on source actions and user choice (key modifiers, etc.).
+ */
 static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer,
-                              uint32_t dnd_action) {}
+                              uint32_t dnd_action) {
+  auto *offer = static_cast<WaylandDataOffer *>(data);
+  offer->SetSelectedDragAction(dnd_action);
+}
 
 /* wl_data_offer callback description:
  *
  * data_offer_offer - Is called for each MIME type available at wl_data_offer.
- * data_offer_source_actions - Exposes all available D&D actions.
- * data_offer_action - Expose one actually selected D&D action.
+ * data_offer_source_actions - This event indicates the actions offered by
+ *                             the data source.
+ * data_offer_action - This event indicates the action selected by
+ *                     the compositor after matching the source/destination
+ *                     side actions.
  */
-static const struct wl_data_offer_listener data_offer_listener = {
+static const moz_wl_data_offer_listener data_offer_listener = {
     data_offer_offer, data_offer_source_actions, data_offer_action};
 
 WaylandDataOffer::WaylandDataOffer(wl_data_offer *aWaylandDataOffer)
     : mWaylandDataOffer(aWaylandDataOffer) {
-  wl_data_offer_add_listener(mWaylandDataOffer, &data_offer_listener, this);
+  wl_data_offer_add_listener(
+      mWaylandDataOffer, (struct wl_data_offer_listener *)&data_offer_listener,
+      this);
 }
 
 WaylandDataOffer::~WaylandDataOffer(void) {
@@ -207,20 +298,93 @@ PrimaryDataOffer::~PrimaryDataOffer(void
   }
 }
 
-void nsRetrievalContextWayland::RegisterDataOffer(
+NS_IMPL_ISUPPORTS(nsWaylandDragContext, nsISupports);
+
+nsWaylandDragContext::nsWaylandDragContext(WaylandDataOffer *aDataOffer,
+                                           wl_display *aDisplay)
+    : mDataOffer(aDataOffer),
+      mDisplay(aDisplay),
+      mTime(0),
+      mGtkWidget(nullptr),
+      mX(0),
+      mY(0) {}
+
+void nsWaylandDragContext::DropDataEnter(GtkWidget *aGtkWidget, uint32_t aTime,
+                                         nscoord aX, nscoord aY) {
+  mTime = aTime;
+  mGtkWidget = aGtkWidget;
+  mX = aX;
+  mY = aY;
+}
+
+void nsWaylandDragContext::DropMotion(uint32_t aTime, nscoord aX, nscoord aY) {
+  mTime = aTime;
+  mX = aX;
+  mY = aY;
+}
+
+void nsWaylandDragContext::GetLastDropInfo(uint32_t *aTime, nscoord *aX,
+                                           nscoord *aY) {
+  *aTime = mTime;
+  *aX = mX;
+  *aY = mY;
+}
+
+void nsWaylandDragContext::SetDragStatus(GdkDragAction aAction) {
+  mDataOffer->SetDragStatus(aAction, mTime);
+}
+
+GdkDragAction nsWaylandDragContext::GetSelectedDragAction() {
+  GdkDragAction gdkAction = mDataOffer->GetSelectedDragAction();
+
+  // We emulate gdk_drag_context_get_actions() here.
+  if (!gdkAction) {
+    gdkAction = mDataOffer->GetAvailableDragActions();
+  }
+
+  return gdkAction;
+}
+
+GList *nsWaylandDragContext::GetTargets() {
+  int targetNums;
+  GdkAtom *atoms = mDataOffer->GetTargets(&targetNums);
+
+  GList *targetList = nullptr;
+  for (int i = 0; i < targetNums; i++) {
+    targetList = g_list_append(targetList, GDK_ATOM_TO_POINTER(atoms[i]));
+  }
+
+  return targetList;
+}
+
+char *nsWaylandDragContext::GetData(const char *aMimeType,
+                                    uint32_t *aContentLength) {
+  mDataOffer->DragOfferAccept(aMimeType, mTime);
+  return mDataOffer->GetData(mDisplay, aMimeType, aContentLength);
+}
+
+void nsRetrievalContextWayland::RegisterNewDataOffer(
     wl_data_offer *aWaylandDataOffer) {
   DataOffer *dataOffer = static_cast<DataOffer *>(
       g_hash_table_lookup(mActiveOffers, aWaylandDataOffer));
+  MOZ_ASSERT(
+      dataOffer == nullptr,
+      "Registered WaylandDataOffer already exists. Wayland protocol error?");
+
   if (!dataOffer) {
     dataOffer = new WaylandDataOffer(aWaylandDataOffer);
     g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer);
   }
 }
 
-void nsRetrievalContextWayland::RegisterDataOffer(
+void nsRetrievalContextWayland::RegisterNewDataOffer(
     gtk_primary_selection_offer *aPrimaryDataOffer) {
   DataOffer *dataOffer = static_cast<DataOffer *>(
       g_hash_table_lookup(mActiveOffers, aPrimaryDataOffer));
+  MOZ_ASSERT(
+      dataOffer == nullptr,
+      "Registered PrimaryDataOffer already exists. Wayland protocol error?");
+
   if (!dataOffer) {
     dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
     g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
@@ -229,21 +393,30 @@ void nsRetrievalContextWayland::Register
 
 void nsRetrievalContextWayland::SetClipboardDataOffer(
     wl_data_offer *aWaylandDataOffer) {
-  DataOffer *dataOffer = static_cast<DataOffer *>(
-      g_hash_table_lookup(mActiveOffers, aWaylandDataOffer));
-  NS_ASSERTION(dataOffer, "We're missing clipboard data offer!");
-  if (dataOffer) {
-    g_hash_table_remove(mActiveOffers, aWaylandDataOffer);
-    mClipboardOffer = dataOffer;
+  // Delete existing clipboard data offer
+  mClipboardOffer = nullptr;
+
+  // null aWaylandDataOffer indicates that our clipboard content
+  // is no longer valid and should be release.
+  if (aWaylandDataOffer != nullptr) {
+    DataOffer *dataOffer = static_cast<DataOffer *>(
+        g_hash_table_lookup(mActiveOffers, aWaylandDataOffer));
+    NS_ASSERTION(dataOffer, "We're missing stored clipboard data offer!");
+    if (dataOffer) {
+      g_hash_table_remove(mActiveOffers, aWaylandDataOffer);
+      mClipboardOffer = dataOffer;
+    }
   }
 }
 
 void nsRetrievalContextWayland::SetPrimaryDataOffer(
     gtk_primary_selection_offer *aPrimaryDataOffer) {
-  if (aPrimaryDataOffer == nullptr) {
-    // Release any primary offer we have.
-    mPrimaryOffer = nullptr;
-  } else {
+  // Release any primary offer we have.
+  mPrimaryOffer = nullptr;
+
+  // aPrimaryDataOffer can be null which means we lost
+  // the mouse selection.
+  if (aPrimaryDataOffer) {
     DataOffer *dataOffer = static_cast<DataOffer *>(
         g_hash_table_lookup(mActiveOffers, aPrimaryDataOffer));
     NS_ASSERTION(dataOffer, "We're missing primary data offer!");
@@ -254,9 +427,26 @@ void nsRetrievalContextWayland::SetPrima
   }
 }
 
-void nsRetrievalContextWayland::ClearDataOffers(void) {
-  if (mClipboardOffer) mClipboardOffer = nullptr;
-  if (mPrimaryOffer) mPrimaryOffer = nullptr;
+void nsRetrievalContextWayland::AddDragAndDropDataOffer(
+    wl_data_offer *aDropDataOffer) {
+  // Remove any existing D&D contexts.
+  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->GetDisplay());
+  }
+}
+
+nsWaylandDragContext *nsRetrievalContextWayland::GetDragContext(void) {
+  return mDragContext;
+}
+
+void nsRetrievalContextWayland::ClearDragAndDropDataOffer(void) {
+  mDragContext = nullptr;
 }
 
 // We have a new fresh data content.
@@ -266,7 +456,7 @@ static void data_device_data_offer(void
                                    struct wl_data_offer *offer) {
   nsRetrievalContextWayland *context =
       static_cast<nsRetrievalContextWayland *>(data);
-  context->RegisterDataOffer(offer);
+  context->RegisterNewDataOffer(offer);
 }
 
 // The new fresh data content is clipboard.
@@ -281,15 +471,65 @@ static void data_device_selection(void *
 // The new fresh wayland data content is drag and drop.
 static void data_device_enter(void *data, struct wl_data_device *data_device,
                               uint32_t time, struct wl_surface *surface,
-                              int32_t x, int32_t y,
-                              struct wl_data_offer *offer) {}
+                              int32_t x_fixed, int32_t y_fixed,
+                              struct wl_data_offer *offer) {
+  nsRetrievalContextWayland *context =
+      static_cast<nsRetrievalContextWayland *>(data);
+  context->AddDragAndDropDataOffer(offer);
+
+  nsWaylandDragContext *dragContext = context->GetDragContext();
+
+  GtkWidget *gtkWidget = get_gtk_widget_for_wl_surface(surface);
+  if (!gtkWidget) {
+    NS_WARNING("DragAndDrop: Unable to get GtkWidget for wl_surface!");
+    return;
+  }
+
+  LOGDRAG(("nsWindow data_device_enter for GtkWidget %p\n", (void *)gtkWidget));
+
+  dragContext->DropDataEnter(gtkWidget, time, wl_fixed_to_int(x_fixed),
+                             wl_fixed_to_int(y_fixed));
+}
+
+static void data_device_leave(void *data, struct wl_data_device *data_device) {
+  nsRetrievalContextWayland *context =
+      static_cast<nsRetrievalContextWayland *>(data);
 
-static void data_device_leave(void *data, struct wl_data_device *data_device) {}
+  nsWaylandDragContext *dropContext = context->GetDragContext();
+  WindowDragLeaveHandler(dropContext->GetWidget());
+
+  context->ClearDragAndDropDataOffer();
+}
 
 static void data_device_motion(void *data, struct wl_data_device *data_device,
-                               uint32_t time, int32_t x, int32_t y) {}
+                               uint32_t time, int32_t x_fixed,
+                               int32_t y_fixed) {
+  nsRetrievalContextWayland *context =
+      static_cast<nsRetrievalContextWayland *>(data);
+
+  nsWaylandDragContext *dropContext = context->GetDragContext();
 
-static void data_device_drop(void *data, struct wl_data_device *data_device) {}
+  nscoord x = wl_fixed_to_int(x_fixed);
+  nscoord y = wl_fixed_to_int(y_fixed);
+  dropContext->DropMotion(time, x, y);
+
+  WindowDragMotionHandler(dropContext->GetWidget(), nullptr, dropContext, x, y,
+                          time);
+}
+
+static void data_device_drop(void *data, struct wl_data_device *data_device) {
+  nsRetrievalContextWayland *context =
+      static_cast<nsRetrievalContextWayland *>(data);
+
+  nsWaylandDragContext *dropContext = context->GetDragContext();
+
+  uint32_t time;
+  nscoord x, y;
+  dropContext->GetLastDropInfo(&time, &x, &y);
+
+  WindowDragDropHandler(dropContext->GetWidget(), nullptr, dropContext, x, y,
+                        time);
+}
 
 /* wl_data_device callback description:
  *
@@ -323,7 +563,7 @@ static void primary_selection_data_offer
   // create and add listener
   nsRetrievalContextWayland *context =
       static_cast<nsRetrievalContextWayland *>(data);
-  context->RegisterDataOffer(gtk_primary_offer);
+  context->RegisterNewDataOffer(gtk_primary_offer);
 }
 
 static void primary_selection_selection(
@@ -335,6 +575,19 @@ static void primary_selection_selection(
   context->SetPrimaryDataOffer(gtk_primary_offer);
 }
 
+/* gtk_primary_selection_device callback description:
+ *
+ * primary_selection_data_offer - It's called when there's a new
+ *                          gtk_primary_selection_offer available.  We need to
+ *                          attach gtk_primary_selection_offer_listener to it
+ *                          to get available MIME types.
+ *
+ * primary_selection_selection - It's called when the new
+ *                          gtk_primary_selection_offer is a primary selection
+ *                          content. It can be also called with
+ *                          gtk_primary_selection_offer = null which means
+ *                          there's no primary selection.
+ */
 static const struct gtk_primary_selection_device_listener
     primary_selection_device_listener = {
         primary_selection_data_offer,
@@ -342,149 +595,29 @@ static const struct gtk_primary_selectio
 };
 
 bool nsRetrievalContextWayland::HasSelectionSupport(void) {
-  return mPrimarySelectionDataDeviceManager != nullptr;
-}
-
-static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
-                                   uint32_t format, int fd, uint32_t size) {}
-
-static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
-                                  uint32_t serial, struct wl_surface *surface,
-                                  struct wl_array *keys) {}
-
-static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
-                                  uint32_t serial, struct wl_surface *surface) {
-  // We lost focus so our clipboard data are outdated
-  nsRetrievalContextWayland *context =
-      static_cast<nsRetrievalContextWayland *>(data);
-
-  context->ClearDataOffers();
+  return mDisplay->GetPrimarySelectionDeviceManager() != nullptr;
 }
 
-static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
-                                uint32_t serial, uint32_t time, uint32_t key,
-                                uint32_t state) {}
-
-static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
-                                      uint32_t serial, uint32_t mods_depressed,
-                                      uint32_t mods_latched,
-                                      uint32_t mods_locked, uint32_t group) {}
-
-static const struct wl_keyboard_listener keyboard_listener = {
-    keyboard_handle_keymap, keyboard_handle_enter,     keyboard_handle_leave,
-    keyboard_handle_key,    keyboard_handle_modifiers,
-};
-
-void nsRetrievalContextWayland::ConfigureKeyboard(wl_seat_capability caps) {
-  // ConfigureKeyboard() is called when wl_seat configuration is changed.
-  // We look for the keyboard only, get it when is't available and release it
-  // when it's lost (we don't have focus for instance).
-  if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
-    mKeyboard = wl_seat_get_keyboard(mSeat);
-    wl_keyboard_add_listener(mKeyboard, &keyboard_listener, this);
-  } else if (mKeyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
-    wl_keyboard_destroy(mKeyboard);
-    mKeyboard = nullptr;
-  }
-}
-
-static void seat_handle_capabilities(void *data, struct wl_seat *seat,
-                                     unsigned int caps) {
-  nsRetrievalContextWayland *context =
-      static_cast<nsRetrievalContextWayland *>(data);
-  context->ConfigureKeyboard((wl_seat_capability)caps);
-}
-
-static const struct wl_seat_listener seat_listener = {
-    seat_handle_capabilities,
-};
-
-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);
-  wl_seat_add_listener(mSeat, &seat_listener, data);
-}
-
-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),
-      mKeyboard(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");
-
-  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 *dataDevice = wl_data_device_manager_get_data_device(
+      mDisplay->GetDataDeviceManager(), mDisplay->GetSeat());
   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);
   }
@@ -492,8 +625,21 @@ nsRetrievalContextWayland::nsRetrievalCo
   mInitialized = true;
 }
 
+static gboolean offer_hash_remove(gpointer wl_offer, gpointer aDataOffer,
+                                  gpointer user_data) {
+#ifdef DEBUG
+  nsPrintfCString msg("nsRetrievalContextWayland(): leaked nsDataOffer %p\n",
+                      aDataOffer);
+  NS_WARNING(msg.get());
+#endif
+  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,
@@ -533,12 +679,14 @@ static void wayland_clipboard_contents_r
 void nsRetrievalContextWayland::TransferFastTrackClipboard(
     int aClipboardRequestNumber, GtkSelectionData *aSelectionData) {
   if (mClipboardRequestNumber == aClipboardRequestNumber) {
-    mClipboardDataLength = gtk_selection_data_get_length(aSelectionData);
-    if (mClipboardDataLength > 0) {
+    int dataLength = gtk_selection_data_get_length(aSelectionData);
+    if (dataLength > 0) {
+      mClipboardDataLength = dataLength;
       mClipboardData = reinterpret_cast<char *>(
-          g_malloc(sizeof(char) * mClipboardDataLength));
+          g_malloc(sizeof(char) * (mClipboardDataLength + 1)));
       memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData),
              sizeof(char) * mClipboardDataLength);
+      mClipboardData[mClipboardDataLength] = '\0';
     }
   } else {
     NS_WARNING("Received obsoleted clipboard data!");
@@ -572,8 +720,8 @@ const char *nsRetrievalContextWayland::G
       mClipboardData = nullptr;
       mClipboardDataLength = 0;
     } else {
-      mClipboardData =
-          dataOffer->GetData(mDisplay, aMimeType, &mClipboardDataLength);
+      mClipboardData = dataOffer->GetData(mDisplay->GetDisplay(), aMimeType,
+                                          &mClipboardDataLength);
     }
   }
 
@@ -588,7 +736,7 @@ const char *nsRetrievalContextWayland::G
       (selection == GDK_SELECTION_PRIMARY) ? mPrimaryOffer : mClipboardOffer;
   if (!dataOffer) return nullptr;
 
-  for (unsigned int i = 0; i < sizeof(sTextMimeTypes); i++) {
+  for (unsigned int i = 0; i < TEXT_MIME_TYPES_NUM; i++) {
     if (dataOffer->HasTarget(sTextMimeTypes[i])) {
       uint32_t unused;
       return GetClipboardData(sTextMimeTypes[i], aWhichClipboard, &unused);
diff -up thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h.wayland thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h
--- thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsClipboardWayland.h	2019-02-05 14:26:16.975316648 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -9,6 +9,7 @@
 #define __nsClipboardWayland_h_
 
 #include "nsIClipboard.h"
+#include "mozwayland/mozwayland.h"
 #include "wayland/gtk-primary-selection-client-protocol.h"
 
 #include <gtk/gtk.h>
@@ -32,31 +33,75 @@ class DataOffer {
  private:
   virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0;
 
+ protected:
   nsTArray<GdkAtom> mTargetMIMETypes;
 };
 
 class WaylandDataOffer : public DataOffer {
  public:
-  WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
+  explicit WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
+
+  void DragOfferAccept(const char* aMimeType, uint32_t aTime);
+  void SetDragStatus(GdkDragAction aAction, uint32_t aTime);
+
+  GdkDragAction GetSelectedDragAction();
+  void SetSelectedDragAction(uint32_t aWaylandAction);
+
+  void SetAvailableDragActions(uint32_t aWaylandActions);
+  GdkDragAction GetAvailableDragActions();
 
- private:
   virtual ~WaylandDataOffer();
+
+ private:
   bool RequestDataTransfer(const char* aMimeType, int fd) override;
 
   wl_data_offer* mWaylandDataOffer;
+  uint32_t mSelectedDragAction;
+  uint32_t mAvailableDragAction;
 };
 
 class PrimaryDataOffer : public DataOffer {
  public:
-  PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+  explicit PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+  void SetAvailableDragActions(uint32_t aWaylandActions){};
 
- private:
   virtual ~PrimaryDataOffer();
+
+ private:
   bool RequestDataTransfer(const char* aMimeType, int fd) override;
 
   gtk_primary_selection_offer* mPrimaryDataOffer;
 };
 
+class nsWaylandDragContext : public nsISupports {
+  NS_DECL_ISUPPORTS
+
+ public:
+  nsWaylandDragContext(WaylandDataOffer* aWaylandDataOffer,
+                       wl_display* aDisplay);
+
+  void DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime, nscoord aX,
+                     nscoord aY);
+  void DropMotion(uint32_t aTime, nscoord aX, nscoord aY);
+  void GetLastDropInfo(uint32_t* aTime, nscoord* aX, nscoord* aY);
+
+  void SetDragStatus(GdkDragAction action);
+  GdkDragAction GetSelectedDragAction();
+
+  GtkWidget* GetWidget() { return mGtkWidget; }
+  GList* GetTargets();
+  char* GetData(const char* aMimeType, uint32_t* aContentLength);
+
+ private:
+  virtual ~nsWaylandDragContext(){};
+
+  nsAutoPtr<WaylandDataOffer> mDataOffer;
+  wl_display* mDisplay;
+  uint32_t mTime;
+  GtkWidget* mGtkWidget;
+  nscoord mX, mY;
+};
+
 class nsRetrievalContextWayland : public nsRetrievalContext {
  public:
   nsRetrievalContextWayland();
@@ -71,38 +116,30 @@ class nsRetrievalContextWayland : public
                               int* aTargetNum) override;
   virtual bool HasSelectionSupport(void) override;
 
-  void RegisterDataOffer(wl_data_offer* aWaylandDataOffer);
-  void RegisterDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+  void RegisterNewDataOffer(wl_data_offer* aWaylandDataOffer);
+  void RegisterNewDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
 
   void SetClipboardDataOffer(wl_data_offer* aWaylandDataOffer);
   void SetPrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+  void AddDragAndDropDataOffer(wl_data_offer* aWaylandDataOffer);
+  nsWaylandDragContext* GetDragContext();
 
-  void ClearDataOffers();
+  void ClearDragAndDropDataOffer();
 
-  void ConfigureKeyboard(wl_seat_capability caps);
   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;
-  wl_keyboard* mKeyboard;
+  nsWaylandDisplay* mDisplay;
 
   // Data offers provided by Wayland data device
   GHashTable* mActiveOffers;
   nsAutoPtr<DataOffer> mClipboardOffer;
   nsAutoPtr<DataOffer> mPrimaryOffer;
+  RefPtr<nsWaylandDragContext> mDragContext;
 
   int mClipboardRequestNumber;
   char* mClipboardData;
diff -up thunderbird-60.5.0/widget/gtk/nsDragService.cpp.wayland thunderbird-60.5.0/widget/gtk/nsDragService.cpp
--- thunderbird-60.5.0/widget/gtk/nsDragService.cpp.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsDragService.cpp	2019-02-05 14:26:16.976316645 +0100
@@ -1,5 +1,5 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=4 et sw=4 tw=80: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=4 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/. */
@@ -9,6 +9,7 @@
 #include "nsIObserverService.h"
 #include "nsWidgetsCID.h"
 #include "nsWindow.h"
+#include "nsSystemInfo.h"
 #include "nsIServiceManager.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
@@ -34,7 +35,6 @@
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
-#include "nsISelection.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
 #include "nsGtkUtils.h"
@@ -43,10 +43,15 @@
 #include "gfxPlatform.h"
 #include "ScreenHelperGTK.h"
 #include "nsArrayUtils.h"
+#ifdef MOZ_WAYLAND
+#include "nsClipboardWayland.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
+#define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
+
 // This sets how opaque the drag image is
 #define DRAG_IMAGE_ALPHA_LEVEL 0.5
 
@@ -68,6 +73,7 @@ static const char gMimeListType[] = "app
 static const char gMozUrlType[] = "_NETSCAPE_URL";
 static const char gTextUriListType[] = "text/uri-list";
 static const char gTextPlainUTF8Type[] = "text/plain;charset=utf-8";
+static const char gXdndDirectSaveType[] = "XdndDirectSave0";
 
 static void invisibleSourceDragBegin(GtkWidget *aWidget,
                                      GdkDragContext *aContext, gpointer aData);
@@ -85,7 +91,15 @@ static void invisibleSourceDragDataGet(G
                                        guint aInfo, guint32 aTime,
                                        gpointer aData);
 
-nsDragService::nsDragService() : mScheduledTask(eDragTaskNone), mTaskSource(0) {
+nsDragService::nsDragService()
+    : mScheduledTask(eDragTaskNone),
+      mTaskSource(0)
+#ifdef MOZ_WAYLAND
+      ,
+      mPendingWaylandDragContext(nullptr),
+      mTargetWaylandDragContext(nullptr)
+#endif
+{
   // We have to destroy the hidden widget before the event loop stops
   // running.
   nsCOMPtr<nsIObserverService> obsServ =
@@ -159,7 +173,7 @@ nsDragService::Observe(nsISupports *aSub
     }
     TargetResetData();
   } else {
-    NS_NOTREACHED("unexpected topic");
+    MOZ_ASSERT_UNREACHABLE("unexpected topic");
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -457,6 +471,9 @@ nsDragService::EndDragSession(bool aDone
 
   // We're done with the drag context.
   mTargetDragContextForRemote = nullptr;
+#ifdef MOZ_WAYLAND
+  mTargetWaylandDragContextForRemote = nullptr;
+#endif
 
   return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
 }
@@ -550,6 +567,14 @@ nsDragService::GetNumDropItems(uint32_t
     return NS_OK;
   }
 
+#ifdef MOZ_WAYLAND
+  // TODO: Wayland implementation of text/uri-list.
+  if (!mTargetDragContext) {
+    *aNumItems = 1;
+    return NS_OK;
+  }
+#endif
+
   bool isList = IsTargetContextList();
   if (isList)
     mSourceDataItems->GetLength(aNumItems);
@@ -907,9 +932,18 @@ nsDragService::IsDataFlavorSupported(con
   }
 
   // check the target context vs. this flavor, one at a time
-  GList *tmp;
-  for (tmp = gdk_drag_context_list_targets(mTargetDragContext); tmp;
-       tmp = tmp->next) {
+  GList *tmp = nullptr;
+  if (mTargetDragContext) {
+    tmp = gdk_drag_context_list_targets(mTargetDragContext);
+  }
+#ifdef MOZ_WAYLAND
+  else if (mTargetWaylandDragContext) {
+    tmp = mTargetWaylandDragContext->GetTargets();
+  }
+  GList *tmp_head = tmp;
+#endif
+
+  for (; tmp; tmp = tmp->next) {
     /* Bug 331198 */
     GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
     gchar *name = nullptr;
@@ -946,6 +980,15 @@ nsDragService::IsDataFlavorSupported(con
     }
     g_free(name);
   }
+
+#ifdef MOZ_WAYLAND
+  // mTargetWaylandDragContext->GetTargets allocates the list
+  // so we need to free it here.
+  if (!mTargetDragContext && tmp_head) {
+    g_list_free(tmp_head);
+  }
+#endif
+
   return NS_OK;
 }
 
@@ -975,6 +1018,34 @@ void nsDragService::ReplyToDragMotion(Gd
   gdk_drag_status(aDragContext, action, mTargetTime);
 }
 
+#ifdef MOZ_WAYLAND
+void nsDragService::ReplyToDragMotion(nsWaylandDragContext *aDragContext) {
+  MOZ_LOG(sDragLm, LogLevel::Debug,
+          ("nsDragService::ReplyToDragMotion %d", mCanDrop));
+
+  GdkDragAction action = (GdkDragAction)0;
+  if (mCanDrop) {
+    // notify the dragger if we can drop
+    switch (mDragAction) {
+      case DRAGDROP_ACTION_COPY:
+        action = GDK_ACTION_COPY;
+        break;
+      case DRAGDROP_ACTION_LINK:
+        action = GDK_ACTION_LINK;
+        break;
+      case DRAGDROP_ACTION_NONE:
+        action = (GdkDragAction)0;
+        break;
+      default:
+        action = GDK_ACTION_MOVE;
+        break;
+    }
+  }
+
+  aDragContext->SetDragStatus(action);
+}
+#endif
+
 void nsDragService::TargetDataReceived(GtkWidget *aWidget,
                                        GdkDragContext *aContext, gint aX,
                                        gint aY,
@@ -999,6 +1070,11 @@ void nsDragService::TargetDataReceived(G
 bool nsDragService::IsTargetContextList(void) {
   bool retval = false;
 
+#ifdef MOZ_WAYLAND
+  // TODO: We need a wayland implementation here.
+  if (!mTargetDragContext) return retval;
+#endif
+
   // gMimeListType drags only work for drags within a single process. The
   // gtk_drag_get_source_widget() function will return nullptr if the source
   // of the drag is another app, so we use it to check if a gMimeListType
@@ -1032,17 +1108,27 @@ void nsDragService::GetTargetDragData(Gd
            mTargetDragContext.get()));
   // reset our target data areas
   TargetResetData();
-  gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
 
-  MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
-  PRTime entryTime = PR_Now();
-  while (!mTargetDragDataReceived && mDoingDrag) {
-    // check the number of iterations
-    MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
-    PR_Sleep(20 * PR_TicksPerSecond() / 1000); /* sleep for 20 ms/iteration */
-    if (PR_Now() - entryTime > NS_DND_TIMEOUT) break;
-    gtk_main_iteration();
+  if (mTargetDragContext) {
+    gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
+
+    MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
+    PRTime entryTime = PR_Now();
+    while (!mTargetDragDataReceived && mDoingDrag) {
+      // check the number of iterations
+      MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
+      PR_Sleep(20 * PR_TicksPerSecond() / 1000); /* sleep for 20 ms/iteration */
+      if (PR_Now() - entryTime > NS_DND_TIMEOUT) break;
+      gtk_main_iteration();
+    }
   }
+#ifdef MOZ_WAYLAND
+  else {
+    mTargetDragData = mTargetWaylandDragContext->GetData(gdk_atom_name(aFlavor),
+                                                         &mTargetDragDataLen);
+    mTargetDragDataReceived = true;
+  }
+#endif
   MOZ_LOG(sDragLm, LogLevel::Debug, ("finished inner iteration\n"));
 }
 
@@ -1218,6 +1304,10 @@ void nsDragService::SourceEndDragSession
   // this just releases the list of data items that we provide
   mSourceDataItems = nullptr;
 
+  // Remove this property, if it exists, to satisfy the Direct Save Protocol.
+  GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE);
+  gdk_property_delete(gdk_drag_context_get_source_window(aContext), property);
+
   if (!mDoingDrag || mScheduledTask == eDragTaskSourceEnd)
     // EndDragSession() was already called on drop
     // or SourceEndDragSession on drag-failed
@@ -1276,7 +1366,7 @@ void nsDragService::SourceEndDragSession
   }
 
   // Schedule the appropriate drag end dom events.
-  Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
+  Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
 }
 
 static void CreateUriList(nsIArray *items, gchar **text, gint *length) {
@@ -1585,11 +1675,11 @@ static void invisibleSourceDragEnd(GtkWi
 // Gecko drag events are in flight.  This helps event handlers that may not
 // expect nested events, while accessing an event's dataTransfer for example.
 
-gboolean nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
-                                            GdkDragContext *aDragContext,
-                                            LayoutDeviceIntPoint aWindowPoint,
-                                            guint aTime) {
-  if (mScheduledTask == eDragTaskMotion) {
+gboolean nsDragService::ScheduleMotionEvent(
+    nsWindow *aWindow, GdkDragContext *aDragContext,
+    nsWaylandDragContext *aWaylandDragContext,
+    LayoutDeviceIntPoint aWindowPoint, guint aTime) {
+  if (aDragContext && mScheduledTask == eDragTaskMotion) {
     // The drag source has sent another motion message before we've
     // replied to the previous.  That shouldn't happen with Xdnd.  The
     // spec for Motif drags is less clear, but we'll just update the
@@ -1600,23 +1690,26 @@ gboolean nsDragService::ScheduleMotionEv
 
   // Returning TRUE means we'll reply with a status message, unless we first
   // get a leave.
-  return Schedule(eDragTaskMotion, aWindow, aDragContext, aWindowPoint, aTime);
+  return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext,
+                  aWindowPoint, aTime);
 }
 
 void nsDragService::ScheduleLeaveEvent() {
   // We don't know at this stage whether a drop signal will immediately
   // follow.  If the drop signal gets sent it will happen before we return
   // to the main loop and the scheduled leave task will be replaced.
-  if (!Schedule(eDragTaskLeave, nullptr, nullptr, LayoutDeviceIntPoint(), 0)) {
+  if (!Schedule(eDragTaskLeave, nullptr, nullptr, nullptr,
+                LayoutDeviceIntPoint(), 0)) {
     NS_WARNING("Drag leave after drop");
   }
 }
 
-gboolean nsDragService::ScheduleDropEvent(nsWindow *aWindow,
-                                          GdkDragContext *aDragContext,
-                                          LayoutDeviceIntPoint aWindowPoint,
-                                          guint aTime) {
-  if (!Schedule(eDragTaskDrop, aWindow, aDragContext, aWindowPoint, aTime)) {
+gboolean nsDragService::ScheduleDropEvent(
+    nsWindow *aWindow, GdkDragContext *aDragContext,
+    nsWaylandDragContext *aWaylandDragContext,
+    LayoutDeviceIntPoint aWindowPoint, guint aTime) {
+  if (!Schedule(eDragTaskDrop, aWindow, aDragContext, aWaylandDragContext,
+                aWindowPoint, aTime)) {
     NS_WARNING("Additional drag drop ignored");
     return FALSE;
   }
@@ -1629,6 +1722,7 @@ gboolean nsDragService::ScheduleDropEven
 
 gboolean nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
                                  GdkDragContext *aDragContext,
+                                 nsWaylandDragContext *aWaylandDragContext,
                                  LayoutDeviceIntPoint aWindowPoint,
                                  guint aTime) {
   // If there is an existing leave or motion task scheduled, then that
@@ -1647,6 +1741,9 @@ gboolean nsDragService::Schedule(DragTas
   mScheduledTask = aTask;
   mPendingWindow = aWindow;
   mPendingDragContext = aDragContext;
+#ifdef MOZ_WAYLAND
+  mPendingWaylandDragContext = aWaylandDragContext;
+#endif
   mPendingWindowPoint = aWindowPoint;
   mPendingTime = aTime;
 
@@ -1717,6 +1814,9 @@ gboolean nsDragService::RunScheduledTask
   // succeeed.
   mTargetWidget = mTargetWindow->GetMozContainerWidget();
   mTargetDragContext.steal(mPendingDragContext);
+#ifdef MOZ_WAYLAND
+  mTargetWaylandDragContext = mPendingWaylandDragContext.forget();
+#endif
   mTargetTime = mPendingTime;
 
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
@@ -1748,10 +1848,20 @@ gboolean nsDragService::RunScheduledTask
     if (task == eDragTaskMotion) {
       if (TakeDragEventDispatchedToChildProcess()) {
         mTargetDragContextForRemote = mTargetDragContext;
+#ifdef MOZ_WAYLAND
+        mTargetWaylandDragContextForRemote = mTargetWaylandDragContext;
+#endif
       } else {
         // Reply to tell the source whether we can drop and what
         // action would be taken.
-        ReplyToDragMotion(mTargetDragContext);
+        if (mTargetDragContext) {
+          ReplyToDragMotion(mTargetDragContext);
+        }
+#ifdef MOZ_WAYLAND
+        else if (mTargetWaylandDragContext) {
+          ReplyToDragMotion(mTargetWaylandDragContext);
+        }
+#endif
       }
     }
   }
@@ -1762,8 +1872,10 @@ gboolean nsDragService::RunScheduledTask
     // Perhaps we should set the del parameter to TRUE when the drag
     // action is move, but we don't know whether the data was successfully
     // transferred.
-    gtk_drag_finish(mTargetDragContext, success,
-                    /* del = */ FALSE, mTargetTime);
+    if (mTargetDragContext) {
+      gtk_drag_finish(mTargetDragContext, success,
+                      /* del = */ FALSE, mTargetTime);
+    }
 
     // This drag is over, so clear out our reference to the previous
     // window.
@@ -1776,6 +1888,9 @@ gboolean nsDragService::RunScheduledTask
   // We're done with the drag context.
   mTargetWidget = nullptr;
   mTargetDragContext = nullptr;
+#ifdef MOZ_WAYLAND
+  mTargetWaylandDragContext = nullptr;
+#endif
 
   // If we got another drag signal while running the sheduled task, that
   // must have happened while running a nested event loop.  Leave the task
@@ -1802,7 +1917,16 @@ void nsDragService::UpdateDragAction() {
 
   // default is to do nothing
   int action = nsIDragService::DRAGDROP_ACTION_NONE;
-  GdkDragAction gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
+  GdkDragAction gdkAction = GDK_ACTION_DEFAULT;
+  if (mTargetDragContext) {
+    gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
+  }
+#ifdef MOZ_WAYLAND
+  else if (mTargetWaylandDragContext) {
+    // We got the selected D&D action from compositor on Wayland.
+    gdkAction = mTargetWaylandDragContext->GetSelectedDragAction();
+  }
+#endif
 
   // set the default just in case nothing matches below
   if (gdkAction & GDK_ACTION_DEFAULT)
@@ -1830,6 +1954,12 @@ nsDragService::UpdateDragEffect() {
     ReplyToDragMotion(mTargetDragContextForRemote);
     mTargetDragContextForRemote = nullptr;
   }
+#ifdef MOZ_WAYLAND
+  else if (mTargetWaylandDragContextForRemote) {
+    ReplyToDragMotion(mTargetWaylandDragContextForRemote);
+    mTargetWaylandDragContextForRemote = nullptr;
+  }
+#endif
   return NS_OK;
 }
 
diff -up thunderbird-60.5.0/widget/gtk/nsDragService.h.wayland thunderbird-60.5.0/widget/gtk/nsDragService.h
--- thunderbird-60.5.0/widget/gtk/nsDragService.h.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsDragService.h	2019-02-05 14:26:16.976316645 +0100
@@ -1,5 +1,5 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=4 et sw=4 tw=80: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=4 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/. */
@@ -14,6 +14,7 @@
 #include <gtk/gtk.h>
 
 class nsWindow;
+class nsWaylandDragContext;
 
 namespace mozilla {
 namespace gfx {
@@ -91,10 +92,12 @@ class nsDragService final : public nsBas
                           guint aInfo, guint32 aTime);
 
   gboolean ScheduleMotionEvent(nsWindow *aWindow, GdkDragContext *aDragContext,
+                               nsWaylandDragContext *aPendingWaylandDragContext,
                                mozilla::LayoutDeviceIntPoint aWindowPoint,
                                guint aTime);
   void ScheduleLeaveEvent();
   gboolean ScheduleDropEvent(nsWindow *aWindow, GdkDragContext *aDragContext,
+                             nsWaylandDragContext *aPendingWaylandDragContext,
                              mozilla::LayoutDeviceIntPoint aWindowPoint,
                              guint aTime);
 
@@ -111,6 +114,8 @@ class nsDragService final : public nsBas
   void SourceDataGet(GtkWidget *widget, GdkDragContext *context,
                      GtkSelectionData *selection_data, guint32 aTime);
 
+  void SourceBeginDrag(GdkDragContext *aContext);
+
   // set the drag icon during drag-begin
   void SetDragIcon(GdkDragContext *aContext);
 
@@ -144,6 +149,9 @@ class nsDragService final : public nsBas
   RefPtr<nsWindow> mPendingWindow;
   mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
   nsCountedRef<GdkDragContext> mPendingDragContext;
+#ifdef MOZ_WAYLAND
+  RefPtr<nsWaylandDragContext> mPendingWaylandDragContext;
+#endif
   guint mPendingTime;
 
   // mTargetWindow and mTargetWindowPoint record the position of the last
@@ -155,9 +163,15 @@ class nsDragService final : public nsBas
   // motion or drop events.  mTime records the corresponding timestamp.
   nsCountedRef<GtkWidget> mTargetWidget;
   nsCountedRef<GdkDragContext> mTargetDragContext;
+#ifdef MOZ_WAYLAND
+  RefPtr<nsWaylandDragContext> mTargetWaylandDragContext;
+#endif
   // mTargetDragContextForRemote is set while waiting for a reply from
   // a child process.
   nsCountedRef<GdkDragContext> mTargetDragContextForRemote;
+#ifdef MOZ_WAYLAND
+  RefPtr<nsWaylandDragContext> mTargetWaylandDragContextForRemote;
+#endif
   guint mTargetTime;
 
   // is it OK to drop on us?
@@ -196,6 +210,7 @@ class nsDragService final : public nsBas
 
   gboolean Schedule(DragTask aTask, nsWindow *aWindow,
                     GdkDragContext *aDragContext,
+                    nsWaylandDragContext *aPendingWaylandDragContext,
                     mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
 
   // Callback for g_idle_add_full() to run mScheduledTask.
@@ -204,6 +219,9 @@ class nsDragService final : public nsBas
   void UpdateDragAction();
   void DispatchMotionEvents();
   void ReplyToDragMotion(GdkDragContext *aDragContext);
+#ifdef MOZ_WAYLAND
+  void ReplyToDragMotion(nsWaylandDragContext *aDragContext);
+#endif
   gboolean DispatchDropEvent();
   static uint32_t GetCurrentModifiers();
 };
diff -up thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp.wayland thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp
--- thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.cpp	2019-02-05 14:26:16.976316645 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -28,6 +28,10 @@
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
+#ifdef MOZ_WAYLAND
+#include <sys/mman.h>
+#endif
+
 namespace mozilla {
 namespace widget {
 
@@ -200,7 +204,11 @@ void KeymapWrapper::Init() {
   mModifierKeys.Clear();
   memset(mModifierMasks, 0, sizeof(mModifierMasks));
 
-  if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) InitBySystemSettings();
+  if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+  else
+    InitBySystemSettingsWayland();
+#endif
 
   gdk_window_add_filter(nullptr, FilterEvents, this);
 
@@ -276,9 +284,9 @@ void KeymapWrapper::InitXKBExtension() {
           ("%p InitXKBExtension, Succeeded", this));
 }
 
-void KeymapWrapper::InitBySystemSettings() {
+void KeymapWrapper::InitBySystemSettingsX11() {
   MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
-          ("%p InitBySystemSettings, mGdkKeymap=%p", this, mGdkKeymap));
+          ("%p InitBySystemSettingsX11, mGdkKeymap=%p", this, mGdkKeymap));
 
   Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
 
@@ -439,6 +447,163 @@ void KeymapWrapper::InitBySystemSettings
   XFree(xkeymap);
 }
 
+#ifdef MOZ_WAYLAND
+void KeymapWrapper::SetModifierMask(xkb_keymap* aKeymap,
+                                    ModifierIndex aModifierIndex,
+                                    const char* aModifierName) {
+  static auto sXkbKeymapModGetIndex =
+      (xkb_mod_index_t(*)(struct xkb_keymap*, const char*))dlsym(
+          RTLD_DEFAULT, "xkb_keymap_mod_get_index");
+
+  xkb_mod_index_t index = sXkbKeymapModGetIndex(aKeymap, aModifierName);
+  if (index != XKB_MOD_INVALID) {
+    mModifierMasks[aModifierIndex] = (1 << index);
+  }
+}
+
+void KeymapWrapper::SetModifierMasks(xkb_keymap* aKeymap) {
+  KeymapWrapper* keymapWrapper = GetInstance();
+
+  // This mapping is derived from get_xkb_modifiers() at gdkkeys-wayland.c
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_NUM_LOCK, XKB_MOD_NAME_NUM);
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_ALT, XKB_MOD_NAME_ALT);
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_META, "Meta");
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_SUPER, "Super");
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_HYPER, "Hyper");
+
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_SCROLL_LOCK, "ScrollLock");
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL3, "Level3");
+  keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL5, "Level5");
+
+  MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
+          ("%p KeymapWrapper::SetModifierMasks, CapsLock=0x%X, NumLock=0x%X, "
+           "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, "
+           "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X",
+           keymapWrapper, keymapWrapper->GetModifierMask(CAPS_LOCK),
+           keymapWrapper->GetModifierMask(NUM_LOCK),
+           keymapWrapper->GetModifierMask(SCROLL_LOCK),
+           keymapWrapper->GetModifierMask(LEVEL3),
+           keymapWrapper->GetModifierMask(LEVEL5),
+           keymapWrapper->GetModifierMask(SHIFT),
+           keymapWrapper->GetModifierMask(CTRL),
+           keymapWrapper->GetModifierMask(ALT),
+           keymapWrapper->GetModifierMask(META),
+           keymapWrapper->GetModifierMask(SUPER),
+           keymapWrapper->GetModifierMask(HYPER)));
+}
+
+/* This keymap routine is derived from weston-2.0.0/clients/simple-im.c
+ */
+static void keyboard_handle_keymap(void* data, struct wl_keyboard* wl_keyboard,
+                                   uint32_t format, int fd, uint32_t size) {
+  if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+    close(fd);
+    return;
+  }
+
+  char* mapString = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+  if (mapString == MAP_FAILED) {
+    close(fd);
+    return;
+  }
+
+  static auto sXkbContextNew =
+      (struct xkb_context * (*)(enum xkb_context_flags))
+          dlsym(RTLD_DEFAULT, "xkb_context_new");
+  static auto sXkbKeymapNewFromString =
+      (struct xkb_keymap * (*)(struct xkb_context*, const char*,
+                               enum xkb_keymap_format,
+                               enum xkb_keymap_compile_flags))
+          dlsym(RTLD_DEFAULT, "xkb_keymap_new_from_string");
+
+  struct xkb_context* xkb_context = sXkbContextNew(XKB_CONTEXT_NO_FLAGS);
+  struct xkb_keymap* keymap =
+      sXkbKeymapNewFromString(xkb_context, mapString, XKB_KEYMAP_FORMAT_TEXT_V1,
+                              XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+  munmap(mapString, size);
+  close(fd);
+
+  if (!keymap) {
+    NS_WARNING("keyboard_handle_keymap(): Failed to compile keymap!\n");
+    return;
+  }
+
+  KeymapWrapper::SetModifierMasks(keymap);
+
+  static auto sXkbKeymapUnRef =
+      (void (*)(struct xkb_keymap*))dlsym(RTLD_DEFAULT, "xkb_keymap_unref");
+  sXkbKeymapUnRef(keymap);
+
+  static auto sXkbContextUnref =
+      (void (*)(struct xkb_context*))dlsym(RTLD_DEFAULT, "xkb_context_unref");
+  sXkbContextUnref(xkb_context);
+}
+
+static void keyboard_handle_enter(void* data, struct wl_keyboard* keyboard,
+                                  uint32_t serial, struct wl_surface* surface,
+                                  struct wl_array* keys) {}
+static void keyboard_handle_leave(void* data, struct wl_keyboard* keyboard,
+                                  uint32_t serial, struct wl_surface* surface) {
+}
+static void keyboard_handle_key(void* data, struct wl_keyboard* keyboard,
+                                uint32_t serial, uint32_t time, uint32_t key,
+                                uint32_t state) {}
+static void keyboard_handle_modifiers(void* data, struct wl_keyboard* keyboard,
+                                      uint32_t serial, uint32_t mods_depressed,
+                                      uint32_t mods_latched,
+                                      uint32_t mods_locked, uint32_t group) {}
+
+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) {
+    keyboard = wl_seat_get_keyboard(seat);
+    wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
+  } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && 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");
+  wl_display* display =
+      sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
+  wl_registry_add_listener(wl_display_get_registry(display),
+                           &keyboard_registry_listener, this);
+}
+#endif
+
 KeymapWrapper::~KeymapWrapper() {
   gdk_window_remove_filter(nullptr, FilterEvents, this);
   g_signal_handlers_disconnect_by_func(mGdkKeymap,
@@ -1473,6 +1638,14 @@ void KeymapWrapper::WillDispatchKeyboard
 
 void KeymapWrapper::WillDispatchKeyboardEventInternal(
     WidgetKeyboardEvent& aKeyEvent, GdkEventKey* aGdkKeyEvent) {
+  if (!aGdkKeyEvent) {
+    // If aGdkKeyEvent is nullptr, we're trying to dispatch a fake keyboard
+    // event in such case, we don't need to set alternative char codes.
+    // So, we don't need to do nothing here.  This case is typically we're
+    // dispatching eKeyDown or eKeyUp event during composition.
+    return;
+  }
+
   uint32_t charCode = GetCharCodeFor(aGdkKeyEvent);
   if (!charCode) {
     MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
diff -up thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h.wayland thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h
--- thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsGtkKeyUtils.h	2019-02-05 14:26:16.976316645 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -13,6 +13,10 @@
 
 #include <gdk/gdk.h>
 #include <X11/XKBlib.h>
+#ifdef MOZ_WAYLAND
+#include <gdk/gdkwayland.h>
+#include <xkbcommon/xkbcommon.h>
+#endif
 
 namespace mozilla {
 namespace widget {
@@ -145,6 +149,14 @@ class KeymapWrapper {
   static void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent,
                                         GdkEventKey* aGdkKeyEvent);
 
+#ifdef MOZ_WAYLAND
+  /**
+   * Utility function to set all supported modifier masks
+   * from xkb_keymap. We call that from Wayland backend routines.
+   */
+  static void SetModifierMasks(xkb_keymap* aKeymap);
+#endif
+
   /**
    * Destroys the singleton KeymapWrapper instance, if it exists.
    */
@@ -168,7 +180,10 @@ class KeymapWrapper {
    */
   void Init();
   void InitXKBExtension();
-  void InitBySystemSettings();
+  void InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+  void InitBySystemSettingsWayland();
+#endif
 
   /**
    * mModifierKeys stores each hardware key information.
@@ -360,6 +375,14 @@ class KeymapWrapper {
    */
   void WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent,
                                          GdkEventKey* aGdkKeyEvent);
+
+#ifdef MOZ_WAYLAND
+  /**
+   * Utility function to set Xkb modifier key mask.
+   */
+  void SetModifierMask(xkb_keymap* aKeymap, ModifierIndex aModifierIndex,
+                       const char* aModifierName);
+#endif
 };
 
 }  // namespace widget
diff -up thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp.wayland thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp
--- thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsLookAndFeel.cpp	2019-02-05 14:26:16.977316642 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -18,6 +18,7 @@
 
 #include <fontconfig/fontconfig.h>
 #include "gfxPlatformGtk.h"
+//#include "mozilla/FontPropertyTypes.h"
 #include "ScreenHelperGTK.h"
 
 #include "gtkdrawing.h"
@@ -31,7 +32,9 @@
 #include <cairo-gobject.h>
 #include "WidgetStyleCache.h"
 #include "prenv.h"
+#include "nsCSSColorUtils.h"
 
+using namespace mozilla;
 using mozilla::LookAndFeel;
 
 #define GDK_COLOR_TO_NS_RGB(c) \
@@ -170,7 +173,7 @@ static bool GetBorderColors(GtkStyleCont
     // GTK has an initial value of zero for border-widths, and so themes
     // need to explicitly set border-widths to make borders visible.
     GtkBorder border;
-    gtk_style_context_get_border(aContext, GTK_STATE_FLAG_NORMAL, &border);
+    gtk_style_context_get_border(aContext, state, &border);
     visible = border.top != 0 || border.right != 0 || border.bottom != 0 ||
               border.left != 0;
   }
@@ -199,6 +202,57 @@ static bool GetBorderColors(GtkStyleCont
   return ret;
 }
 
+// Finds ideal cell highlight colors used for unfocused+selected cells distinct
+// from both Highlight, used as focused+selected background, and the listbox
+// background which is assumed to be similar to -moz-field
+nsresult nsLookAndFeel::InitCellHighlightColors() {
+  // NS_SUFFICIENT_LUMINOSITY_DIFFERENCE is the a11y standard for text
+  // on a background. Use 20% of that standard since we have a background
+  // on top of another background
+  int32_t minLuminosityDifference = NS_SUFFICIENT_LUMINOSITY_DIFFERENCE / 5;
+  int32_t backLuminosityDifference =
+      NS_LUMINOSITY_DIFFERENCE(mMozWindowBackground, mMozFieldBackground);
+  if (backLuminosityDifference >= minLuminosityDifference) {
+    mMozCellHighlightBackground = mMozWindowBackground;
+    mMozCellHighlightText = mMozWindowText;
+    return NS_OK;
+  }
+
+  uint16_t hue, sat, luminance;
+  uint8_t alpha;
+  mMozCellHighlightBackground = mMozFieldBackground;
+  mMozCellHighlightText = mMozFieldText;
+
+  NS_RGB2HSV(mMozCellHighlightBackground, hue, sat, luminance, alpha);
+
+  uint16_t step = 30;
+  // Lighten the color if the color is very dark
+  if (luminance <= step) {
+    luminance += step;
+  }
+  // Darken it if it is very light
+  else if (luminance >= 255 - step) {
+    luminance -= step;
+  }
+  // Otherwise, compute what works best depending on the text luminance.
+  else {
+    uint16_t textHue, textSat, textLuminance;
+    uint8_t textAlpha;
+    NS_RGB2HSV(mMozCellHighlightText, textHue, textSat, textLuminance,
+               textAlpha);
+    // Text is darker than background, use a lighter shade
+    if (textLuminance < luminance) {
+      luminance += step;
+    }
+    // Otherwise, use a darker shade
+    else {
+      luminance -= step;
+    }
+  }
+  NS_HSV2RGB(mMozCellHighlightBackground, hue, sat, luminance, alpha);
+  return NS_OK;
+}
+
 void nsLookAndFeel::NativeInit() { EnsureInit(); }
 
 void nsLookAndFeel::RefreshImpl() {
@@ -248,7 +302,6 @@ nsresult nsLookAndFeel::NativeGetColor(C
     case eColorID_IMESelectedRawTextBackground:
     case eColorID_IMESelectedConvertedTextBackground:
     case eColorID__moz_dragtargetzone:
-    case eColorID__moz_cellhighlight:
     case eColorID__moz_html_cellhighlight:
     case eColorID_highlight:  // preference selected item,
       aColor = mTextSelectedBackground;
@@ -258,10 +311,15 @@ nsresult nsLookAndFeel::NativeGetColor(C
     case eColorID_IMESelectedRawTextForeground:
     case eColorID_IMESelectedConvertedTextForeground:
     case eColorID_highlighttext:
-    case eColorID__moz_cellhighlighttext:
     case eColorID__moz_html_cellhighlighttext:
       aColor = mTextSelectedText;
       break;
+    case eColorID__moz_cellhighlight:
+      aColor = mMozCellHighlightBackground;
+      break;
+    case eColorID__moz_cellhighlighttext:
+      aColor = mMozCellHighlightText;
+      break;
     case eColorID_Widget3DHighlight:
       aColor = NS_RGB(0xa0, 0xa0, 0xa0);
       break;
@@ -961,6 +1019,9 @@ void nsLookAndFeel::EnsureInit() {
   mOddCellBackground = GDK_RGBA_TO_NS_RGBA(color);
   gtk_style_context_restore(style);
 
+  // Compute cell highlight colors
+  InitCellHighlightColors();
+
   // GtkFrame has a "border" subnode on which Adwaita draws the border.
   // Some themes do not draw on this node but draw a border on the widget
   // root node, so check the root node if no border is found on the border
diff -up thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h.wayland thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h
--- thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsLookAndFeel.h	2019-02-05 14:26:16.977316642 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -8,6 +8,7 @@
 #ifndef __nsLookAndFeel
 #define __nsLookAndFeel
 
+#include "X11UndefineNone.h"
 #include "nsXPLookAndFeel.h"
 #include "nsCOMPtr.h"
 #include "gfxFont.h"
@@ -75,6 +76,8 @@ class nsLookAndFeel final : public nsXPL
   nscolor mMozWindowActiveBorder;
   nscolor mMozWindowInactiveBorder;
   nscolor mMozWindowInactiveCaption;
+  nscolor mMozCellHighlightBackground;
+  nscolor mMozCellHighlightText;
   nscolor mTextSelectedText;
   nscolor mTextSelectedBackground;
   nscolor mMozScrollbar;
@@ -89,6 +92,9 @@ class nsLookAndFeel final : public nsXPL
   bool mInitialized;
 
   void EnsureInit();
+
+ private:
+  nsresult InitCellHighlightColors();
 };
 
 #endif
diff -up thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp.wayland thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp
--- thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp.wayland	2019-02-05 14:26:16.977316642 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.cpp	2019-02-05 14:26:16.977316642 +0100
@@ -0,0 +1,222 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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
diff -up thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h.wayland thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h
--- thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h.wayland	2019-02-05 14:26:16.977316642 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWaylandDisplay.h	2019-02-05 14:26:16.977316642 +0100
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.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__
diff -up thunderbird-60.5.0/widget/gtk/nsWindow.cpp.wayland thunderbird-60.5.0/widget/gtk/nsWindow.cpp
--- thunderbird-60.5.0/widget/gtk/nsWindow.cpp.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWindow.cpp	2019-02-05 14:26:16.978316639 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -18,6 +18,7 @@
 #include "mozilla/TouchEvents.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/WidgetUtils.h"
+#include "mozilla/dom/WheelEventBinding.h"
 #include <algorithm>
 
 #include "GeckoProfiler.h"
@@ -25,13 +26,15 @@
 #include "prlink.h"
 #include "nsGTKToolkit.h"
 #include "nsIRollupListener.h"
-#include "nsIDOMNode.h"
+#include "nsINode.h"
 
 #include "nsWidgetsCID.h"
 #include "nsDragService.h"
 #include "nsIWidgetListener.h"
 #include "nsIScreenManager.h"
 #include "SystemTimeConverter.h"
+#include "nsIPresShell.h"
+#include "nsViewManager.h"
 
 #include "nsGtkKeyUtils.h"
 #include "nsGtkCursors.h"
@@ -56,6 +59,7 @@
 
 #if defined(MOZ_WAYLAND)
 #include <gdk/gdkwayland.h>
+#include "nsView.h"
 #endif
 
 #include "nsGkAtoms.h"
@@ -116,6 +120,7 @@ using namespace mozilla::widget;
 #include "mozilla/layers/CompositorThread.h"
 
 #ifdef MOZ_X11
+#include "GLContextGLX.h"  // for GLContextGLX::FindVisual()
 #include "GtkCompositorWidget.h"
 #include "gfxXlibSurface.h"
 #include "WindowSurfaceX11Image.h"
@@ -129,8 +134,6 @@ using namespace mozilla::widget;
 #include "nsShmImage.h"
 #include "gtkdrawing.h"
 
-#include "nsIDOMWheelEvent.h"
-
 #include "NativeKeyBindings.h"
 
 #include <dlfcn.h>
@@ -140,6 +143,7 @@ using namespace mozilla::gfx;
 using namespace mozilla::widget;
 using namespace mozilla::layers;
 using mozilla::gl::GLContext;
+using mozilla::gl::GLContextGLX;
 
 // Don't put more than this many rects in the dirty region, just fluff
 // out to the bounding-box if there are more
@@ -152,9 +156,12 @@ const gint kEvents =
 #if GTK_CHECK_VERSION(3, 4, 0)
     GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK |
 #endif
-    GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_PROPERTY_CHANGE_MASK;
+    GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_PROPERTY_CHANGE_MASK |
+    GDK_FOCUS_CHANGE_MASK;
 
 /* utility functions */
+static void theme_changed_cb(GtkSettings *settings, GParamSpec *pspec,
+                             nsWindow *data);
 static bool is_mouse_in_window(GdkWindow *aWindow, gdouble aMouseX,
                                gdouble aMouseY);
 static nsWindow *get_window_for_gtk_widget(GtkWidget *widget);
@@ -196,8 +203,6 @@ static void hierarchy_changed_cb(GtkWidg
                                  GtkWidget *previous_toplevel);
 static gboolean window_state_event_cb(GtkWidget *widget,
                                       GdkEventWindowState *event);
-static void theme_changed_cb(GtkSettings *settings, GParamSpec *pspec,
-                             nsWindow *data);
 static void check_resize_cb(GtkContainer *container, gpointer user_data);
 static void screen_composited_changed_cb(GdkScreen *screen, gpointer user_data);
 static void widget_composited_changed_cb(GtkWidget *widget, gpointer user_data);
@@ -550,7 +555,7 @@ static GtkWidget *EnsureInvisibleContain
 }
 
 static void CheckDestroyInvisibleContainer() {
-  NS_PRECONDITION(gInvisibleContainer, "oh, no");
+  MOZ_ASSERT(gInvisibleContainer, "oh, no");
 
   if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
     // No children, so not in use.
@@ -639,9 +644,6 @@ void nsWindow::Destroy() {
 
   ClearCachedResources();
 
-  g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
-                                       FuncToGpointer(theme_changed_cb), this);
-
   nsIRollupListener *rollupListener = nsBaseWidget::GetActiveRollupListener();
   if (rollupListener) {
     nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
@@ -725,7 +727,7 @@ double nsWindow::GetDefaultScaleInternal
 DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScale() {
 #ifdef MOZ_WAYLAND
   GdkDisplay *gdkDisplay = gdk_display_get_default();
-  if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) {
+  if (!GDK_IS_X11_DISPLAY(gdkDisplay)) {
     return DesktopToLayoutDeviceScale(GdkScaleFactor());
   }
 #endif
@@ -735,8 +737,14 @@ DesktopToLayoutDeviceScale nsWindow::Get
 }
 
 void nsWindow::SetParent(nsIWidget *aNewParent) {
-  if (mContainer || !mGdkWindow) {
-    NS_NOTREACHED("nsWindow::SetParent called illegally");
+  if (!mGdkWindow) {
+    MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed");
+    return;
+  }
+
+  if (mContainer) {
+    // FIXME bug 1469183
+    NS_ERROR("nsWindow should not have a container here");
     return;
   }
 
@@ -774,7 +782,7 @@ void nsWindow::SetParent(nsIWidget *aNew
 bool nsWindow::WidgetTypeSupportsAcceleration() { return !IsSmallPopup(); }
 
 void nsWindow::ReparentNativeWidget(nsIWidget *aNewParent) {
-  NS_PRECONDITION(aNewParent, "");
+  MOZ_ASSERT(aNewParent, "null widget");
   NS_ASSERTION(!mIsDestroyed, "");
   NS_ASSERTION(!static_cast<nsWindow *>(aNewParent)->mIsDestroyed, "");
 
@@ -1331,7 +1339,7 @@ LayoutDeviceIntRect nsWindow::GetClientB
 }
 
 void nsWindow::UpdateClientOffset() {
-  AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", GRAPHICS);
+  AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", OTHER);
 
   if (!mIsTopLevel || !mShell || !mIsX11Display ||
       gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
@@ -1373,9 +1381,7 @@ LayoutDeviceIntPoint nsWindow::GetClient
 }
 
 gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget *aWidget,
-                                         GdkEventProperty *aEvent)
-
-{
+                                         GdkEventProperty *aEvent) {
   if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) {
     UpdateClientOffset();
 
@@ -1820,6 +1826,9 @@ gboolean nsWindow::OnExposeEvent(cairo_t
 
   // Windows that are not visible will be painted after they become visible.
   if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) return FALSE;
+#ifdef MOZ_WAYLAND
+  if (mContainer && !mContainer->ready_to_draw) return FALSE;
+#endif
 
   nsIWidgetListener *listener = GetListener();
   if (!listener) return FALSE;
@@ -3000,6 +3009,33 @@ void nsWindow::OnWindowStateEvent(GtkWid
   }
   // else the widget is a shell widget.
 
+  // The block below is a bit evil.
+  //
+  // When a window is resized before it is shown, gtk_window_resize() delays
+  // resizes until the window is shown.  If gtk_window_state_event() sees a
+  // GDK_WINDOW_STATE_MAXIMIZED change [1] before the window is shown, then
+  // gtk_window_compute_configure_request_size() ignores the values from the
+  // resize [2].  See bug 1449166 for an example of how this could happen.
+  //
+  // [1] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L7967
+  // [2] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L9377
+  //
+  // In order to provide a sensible size for the window when the user exits
+  // maximized state, we hide the GDK_WINDOW_STATE_MAXIMIZED change from
+  // gtk_window_state_event() so as to trick GTK into using the values from
+  // gtk_window_resize() in its configure request.
+  //
+  // We instead notify gtk_window_state_event() of the maximized state change
+  // once the window is shown.
+  if (!mIsShown) {
+    aEvent->changed_mask = static_cast<GdkWindowState>(
+        aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
+  } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
+             aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+    aEvent->changed_mask = static_cast<GdkWindowState>(
+        aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
+  }
+
   // We don't care about anything but changes in the maximized/icon/fullscreen
   // states
   if ((aEvent->changed_mask &
@@ -3075,6 +3111,7 @@ void nsWindow::OnDPIChanged() {
       // Update menu's font size etc
       presShell->ThemeChanged();
     }
+    mWidgetListener->UIResolutionChanged();
   }
 }
 
@@ -3443,13 +3480,15 @@ nsresult nsWindow::Create(nsIWidget *aPa
                         gtk_style_context_has_class(style, "csd");
       eventWidget = (drawToContainer) ? container : mShell;
 
-      gtk_widget_add_events(eventWidget, kEvents);
-      if (drawToContainer)
-        gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
-
       // Prevent GtkWindow from painting a background to avoid flickering.
       gtk_widget_set_app_paintable(eventWidget, TRUE);
 
+      gtk_widget_add_events(eventWidget, kEvents);
+      if (drawToContainer) {
+        gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
+        gtk_widget_set_app_paintable(mShell, TRUE);
+      }
+
       // If we draw to mContainer window then configure it now because
       // gtk_container_add() realizes the child widget.
       gtk_widget_set_has_window(container, drawToContainer);
@@ -3698,6 +3737,15 @@ nsresult nsWindow::Create(nsIWidget *aPa
     mXDepth = gdk_visual_get_depth(gdkVisual);
 
     mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth);
+
+    if (mIsTopLevel) {
+      // Set window manager hint to keep fullscreen windows composited.
+      //
+      // If the window were to get unredirected, there could be visible
+      // tearing because Gecko does not align its framebuffer updates with
+      // vblank.
+      // SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
+    }
   }
 #ifdef MOZ_WAYLAND
   else if (!mIsX11Display) {
@@ -3708,12 +3756,37 @@ nsresult nsWindow::Create(nsIWidget *aPa
   return NS_OK;
 }
 
+void nsWindow::RefreshWindowClass(void) {
+  if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty()) return;
+
+  GdkWindow *gdkWindow = gtk_widget_get_window(mShell);
+  gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get());
+
+#ifdef MOZ_X11
+  if (mIsX11Display) {
+    XClassHint *class_hint = XAllocClassHint();
+    if (!class_hint) {
+      return;
+    }
+    const char *res_class = gdk_get_program_class();
+    if (!res_class) return;
+
+    class_hint->res_name = const_cast<char *>(mGtkWindowTypeName.get());
+    class_hint->res_class = const_cast<char *>(res_class);
+
+    // Can't use gtk_window_set_wmclass() for this; it prints
+    // a warning & refuses to make the change.
+    GdkDisplay *display = gdk_display_get_default();
+    XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
+                  gdk_x11_window_get_xid(gdkWindow), class_hint);
+    XFree(class_hint);
+  }
+#endif /* MOZ_X11 */
+}
+
 void nsWindow::SetWindowClass(const nsAString &xulWinType) {
   if (!mShell) return;
 
-  const char *res_class = gdk_get_program_class();
-  if (!res_class) return;
-
   char *res_name = ToNewCString(xulWinType);
   if (!res_name) return;
 
@@ -3733,29 +3806,11 @@ void nsWindow::SetWindowClass(const nsAS
   res_name[0] = toupper(res_name[0]);
   if (!role) role = res_name;
 
-  GdkWindow *gdkWindow = gtk_widget_get_window(mShell);
-  gdk_window_set_role(gdkWindow, role);
-
-#ifdef MOZ_X11
-  if (mIsX11Display) {
-    XClassHint *class_hint = XAllocClassHint();
-    if (!class_hint) {
-      free(res_name);
-      return;
-    }
-    class_hint->res_name = res_name;
-    class_hint->res_class = const_cast<char *>(res_class);
-
-    // Can't use gtk_window_set_wmclass() for this; it prints
-    // a warning & refuses to make the change.
-    GdkDisplay *display = gdk_display_get_default();
-    XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
-                  gdk_x11_window_get_xid(gdkWindow), class_hint);
-    XFree(class_hint);
-  }
-#endif /* MOZ_X11 */
-
+  mGtkWindowTypeName = res_name;
+  mGtkWindowRoleName = role;
   free(res_name);
+
+  RefreshWindowClass();
 }
 
 void nsWindow::NativeResize() {
@@ -3820,6 +3875,8 @@ void nsWindow::NativeMoveResize() {
       NativeShow(false);
     }
     NativeMove();
+
+    return;
   }
 
   GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
@@ -3832,6 +3889,8 @@ void nsWindow::NativeMoveResize() {
     // x and y give the position of the window manager frame top-left.
     gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
     // This sets the client window size.
+    MOZ_ASSERT(size.width > 0 && size.height > 0,
+               "Can't resize window smaller than 1x1.");
     gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
   } else if (mContainer) {
     GtkAllocation allocation;
@@ -3877,6 +3936,16 @@ void nsWindow::NativeShow(bool aAction)
       gdk_window_show_unraised(mGdkWindow);
     }
   } else {
+#ifdef MOZ_WAYLAND
+    if (mContainer && moz_container_has_wl_egl_window(mContainer)) {
+      // Because wl_egl_window is destroyed on moz_container_unmap(),
+      // the current compositor cannot use it anymore. To avoid crash,
+      // destroy the compositor & recreate a new compositor on next
+      // expose event.
+      DestroyLayerManager();
+    }
+#endif
+
     if (mIsTopLevel) {
       // Workaround window freezes on GTK versions before 3.21.2 by
       // ensuring that configure events get dispatched to windows before
@@ -5436,9 +5505,10 @@ void nsWindow::InitDragEvent(WidgetDragE
   KeymapWrapper::InitInputEvent(aEvent, modifierState);
 }
 
-static gboolean drag_motion_event_cb(GtkWidget *aWidget,
-                                     GdkDragContext *aDragContext, gint aX,
-                                     gint aY, guint aTime, gpointer aData) {
+gboolean WindowDragMotionHandler(GtkWidget *aWidget,
+                                 GdkDragContext *aDragContext,
+                                 nsWaylandDragContext *aWaylandDragContext,
+                                 gint aX, gint aY, guint aTime) {
   RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
   if (!window) return FALSE;
 
@@ -5459,13 +5529,17 @@ static gboolean drag_motion_event_cb(Gtk
   LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({retx, rety});
 
   RefPtr<nsDragService> dragService = nsDragService::GetInstance();
-  return dragService->ScheduleMotionEvent(innerMostWindow, aDragContext, point,
-                                          aTime);
+  return dragService->ScheduleMotionEvent(innerMostWindow, aDragContext,
+                                          aWaylandDragContext, point, aTime);
 }
 
-static void drag_leave_event_cb(GtkWidget *aWidget,
-                                GdkDragContext *aDragContext, guint aTime,
-                                gpointer aData) {
+static gboolean drag_motion_event_cb(GtkWidget *aWidget,
+                                     GdkDragContext *aDragContext, gint aX,
+                                     gint aY, guint aTime, gpointer aData) {
+  return WindowDragMotionHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
+}
+
+void WindowDragLeaveHandler(GtkWidget *aWidget) {
   RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
   if (!window) return;
 
@@ -5495,9 +5569,15 @@ static void drag_leave_event_cb(GtkWidge
   dragService->ScheduleLeaveEvent();
 }
 
-static gboolean drag_drop_event_cb(GtkWidget *aWidget,
-                                   GdkDragContext *aDragContext, gint aX,
-                                   gint aY, guint aTime, gpointer aData) {
+static void drag_leave_event_cb(GtkWidget *aWidget,
+                                GdkDragContext *aDragContext, guint aTime,
+                                gpointer aData) {
+  WindowDragLeaveHandler(aWidget);
+}
+
+gboolean WindowDragDropHandler(GtkWidget *aWidget, GdkDragContext *aDragContext,
+                               nsWaylandDragContext *aWaylandDragContext,
+                               gint aX, gint aY, guint aTime) {
   RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
   if (!window) return FALSE;
 
@@ -5518,8 +5598,14 @@ static gboolean drag_drop_event_cb(GtkWi
   LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({retx, rety});
 
   RefPtr<nsDragService> dragService = nsDragService::GetInstance();
-  return dragService->ScheduleDropEvent(innerMostWindow, aDragContext, point,
-                                        aTime);
+  return dragService->ScheduleDropEvent(innerMostWindow, aDragContext,
+                                        aWaylandDragContext, point, aTime);
+}
+
+static gboolean drag_drop_event_cb(GtkWidget *aWidget,
+                                   GdkDragContext *aDragContext, gint aX,
+                                   gint aY, guint aTime, gpointer aData) {
+  return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
 }
 
 static void drag_data_received_event_cb(GtkWidget *aWidget,
@@ -5877,11 +5963,6 @@ nsIWidget::LayerManager *nsWindow::GetLa
     return mLayerManager;
   }
 
-  if (!mLayerManager && !IsComposited() &&
-      eTransparencyTransparent == GetTransparencyMode()) {
-    mLayerManager = CreateBasicLayerManager();
-  }
-
   return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
                                        aPersistence);
 }
@@ -5919,6 +6000,13 @@ void nsWindow::ClearCachedResources() {
  * It works only for CSD decorated GtkWindow.
  */
 void nsWindow::UpdateClientOffsetForCSDWindow() {
+  // We update window offset on X11 as the window position is calculated
+  // relatively to mShell. We don't do that on Wayland as our wl_subsurface
+  // is attached to mContainer and mShell is ignored.
+  if (!mIsX11Display) {
+    return;
+  }
+
   // _NET_FRAME_EXTENTS is not set on client decorated windows,
   // so we need to read offset between mContainer and toplevel mShell
   // window.
@@ -6005,6 +6093,15 @@ void nsWindow::SetDrawsInTitlebar(bool a
     mNeedsShow = true;
     NativeResize();
 
+    // Label mShell toplevel window so property_notify_event_cb callback
+    // can find its way home.
+    g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), "nsWindow",
+                      this);
+#ifdef MOZ_X11
+    // SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
+#endif
+    RefreshWindowClass();
+
     // When we use system titlebar setup managed by Gtk+ we also get
     // _NET_FRAME_EXTENTS property for our toplevel window so we can't
     // update the client offset it here.
@@ -6019,13 +6116,11 @@ void nsWindow::SetDrawsInTitlebar(bool a
 }
 
 gint 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 ScreenHelperGTK::GetGTKMonitorScaleFactor();
 }
 
@@ -6287,6 +6382,8 @@ nsWindow::CSDSupportLevel nsWindow::GetS
       // KDE Plasma
     } else if (strstr(currentDesktop, "KDE") != nullptr) {
       sCSDSupportLevel = CSD_SUPPORT_CLIENT;
+    } else if (strstr(currentDesktop, "Enlightenment") != nullptr) {
+      sCSDSupportLevel = CSD_SUPPORT_CLIENT;
     } else if (strstr(currentDesktop, "LXDE") != nullptr) {
       sCSDSupportLevel = CSD_SUPPORT_CLIENT;
     } else if (strstr(currentDesktop, "openbox") != nullptr) {
@@ -6303,6 +6400,8 @@ nsWindow::CSDSupportLevel nsWindow::GetS
       sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
     } else if (strstr(currentDesktop, "LXQt") != nullptr) {
       sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
+    } else if (strstr(currentDesktop, "Deepin") != nullptr) {
+      sCSDSupportLevel = CSD_SUPPORT_SYSTEM;
     } else {
 // Release or beta builds are not supposed to be broken
 // so disable titlebar rendering on untested/unknown systems.
@@ -6351,34 +6450,19 @@ int32_t nsWindow::RoundsWidgetCoordinate
 
 void nsWindow::GetCompositorWidgetInitData(
     mozilla::widget::CompositorWidgetInitData *aInitData) {
+  // Make sure the window XID is propagated to X server, we can fail otherwise
+  // in GPU process (Bug 1401634).
+  if (mXDisplay && mXWindow != X11None) {
+    XFlush(mXDisplay);
+  }
+
   *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
       (mXWindow != X11None) ? mXWindow : (uintptr_t) nullptr,
       mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(),
       GetClientSize());
 }
 
-bool nsWindow::IsComposited() const {
-  if (!mGdkWindow) {
-    NS_WARNING("nsWindow::HasARGBVisual called before realization!");
-    return false;
-  }
-
-  GdkScreen *gdkScreen = gdk_screen_get_default();
-  return gdk_screen_is_composited(gdkScreen) &&
-         (gdk_window_get_visual(mGdkWindow) ==
-          gdk_screen_get_rgba_visual(gdkScreen));
-}
-
 #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));
@@ -6388,4 +6472,80 @@ wl_surface *nsWindow::GetWaylandSurface(
       "drawing!");
   return nullptr;
 }
+
+bool nsWindow::WaylandSurfaceNeedsClear() {
+  if (mContainer) {
+    return moz_container_surface_needs_clear(MOZ_CONTAINER(mContainer));
+  }
+
+  NS_WARNING(
+      "nsWindow::WaylandSurfaceNeedsClear(): We don't have any mContainer!");
+  return false;
+}
 #endif
+
+#ifdef MOZ_X11
+/* XApp progress support currently works by setting a property
+ * on a window with this Atom name.  A supporting window manager
+ * will notice this and pass it along to whatever handling has
+ * been implemented on that end (e.g. passing it on to a taskbar
+ * widget.)  There is no issue if WM support is lacking, this is
+ * simply ignored in that case.
+ *
+ * See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c
+ * for further details.
+ */
+
+#define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS"
+
+static void set_window_hint_cardinal(Window xid, const gchar *atom_name,
+                                     gulong cardinal) {
+  GdkDisplay *display;
+
+  display = gdk_display_get_default();
+
+  if (cardinal > 0) {
+    XChangeProperty(GDK_DISPLAY_XDISPLAY(display), xid,
+                    gdk_x11_get_xatom_by_name_for_display(display, atom_name),
+                    XA_CARDINAL, 32, PropModeReplace, (guchar *)&cardinal, 1);
+  } else {
+    XDeleteProperty(GDK_DISPLAY_XDISPLAY(display), xid,
+                    gdk_x11_get_xatom_by_name_for_display(display, atom_name));
+  }
+}
+#endif  // MOZ_X11
+
+void nsWindow::SetProgress(unsigned long progressPercent) {
+#ifdef MOZ_X11
+
+  if (!mIsX11Display) {
+    return;
+  }
+
+  if (!mShell) {
+    return;
+  }
+
+  progressPercent = MIN(progressPercent, 100);
+
+  set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)),
+                           PROGRESS_HINT, progressPercent);
+#endif  // MOZ_X11
+}
+
+#ifdef MOZ_X11
+void nsWindow::SetCompositorHint(WindowComposeRequest aState) {
+  if (mIsX11Display &&
+      (!GetLayerManager() ||
+       GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC)) {
+    gulong value = aState;
+    GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
+    gdk_property_change(gtk_widget_get_window(mShell),
+                        gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE),
+                        cardinal_atom,
+                        32,  // format
+                        GDK_PROP_MODE_REPLACE, (guchar *)&value, 1);
+  }
+}
+#endif
+
diff -up thunderbird-60.5.0/widget/gtk/nsWindow.h.wayland thunderbird-60.5.0/widget/gtk/nsWindow.h
--- thunderbird-60.5.0/widget/gtk/nsWindow.h.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/nsWindow.h	2019-02-05 14:26:16.978316639 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=4:tabstop=4:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -8,19 +8,8 @@
 #ifndef __nsWindow_h__
 #define __nsWindow_h__
 
-#include "mozcontainer.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/UniquePtr.h"
-#include "nsIDragService.h"
-#include "nsITimer.h"
-#include "nsGkAtoms.h"
-#include "nsRefPtrHashtable.h"
-
-#include "nsBaseWidget.h"
-#include "CompositorWidget.h"
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
-
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #include "X11UndefineNone.h"
@@ -28,7 +17,16 @@
 #ifdef MOZ_WAYLAND
 #include <gdk/gdkwayland.h>
 #endif
-
+#include "mozcontainer.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIDragService.h"
+#include "nsITimer.h"
+#include "nsGkAtoms.h"
+#include "nsRefPtrHashtable.h"
+#include "nsIFrame.h"
+#include "nsBaseWidget.h"
+#include "CompositorWidget.h"
 #include "mozilla/widget/WindowSurface.h"
 #include "mozilla/widget/WindowSurfaceProvider.h"
 
@@ -66,6 +64,19 @@ extern mozilla::LazyLogModule gWidgetDra
 
 #endif /* MOZ_LOGGING */
 
+#ifdef MOZ_WAYLAND
+class nsWaylandDragContext;
+
+gboolean WindowDragMotionHandler(GtkWidget* aWidget,
+                                 GdkDragContext* aDragContext,
+                                 nsWaylandDragContext* aWaylandDragContext,
+                                 gint aX, gint aY, guint aTime);
+gboolean WindowDragDropHandler(GtkWidget* aWidget, GdkDragContext* aDragContext,
+                               nsWaylandDragContext* aWaylandDragContext,
+                               gint aX, gint aY, guint aTime);
+void WindowDragLeaveHandler(GtkWidget* aWidget);
+#endif
+
 class gfxPattern;
 
 namespace mozilla {
@@ -77,6 +88,7 @@ class nsWindow final : public nsBaseWidg
  public:
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::WidgetEventTime WidgetEventTime;
+  typedef mozilla::WidgetKeyboardEvent WidgetKeyboardEvent;
   typedef mozilla::widget::PlatformCompositorWidgetDelegate
       PlatformCompositorWidgetDelegate;
 
@@ -216,6 +228,8 @@ class nsWindow final : public nsBaseWidg
       mozilla::gfx::DrawTarget* aDrawTarget,
       LayoutDeviceIntRegion& aInvalidRegion) override;
 
+  void SetProgress(unsigned long progressPercent);
+
  private:
   void UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface,
                    nsIntRect aBoundsRect);
@@ -335,6 +349,7 @@ class nsWindow final : public nsBaseWidg
 #ifdef MOZ_WAYLAND
   wl_display* GetWaylandDisplay();
   wl_surface* GetWaylandSurface();
+  bool WaylandSurfaceNeedsClear();
 #endif
   virtual void GetCompositorWidgetInitData(
       mozilla::widget::CompositorWidgetInitData* aInitData) override;
@@ -436,13 +451,23 @@ class nsWindow final : public nsBaseWidg
                    gint* aButton, gint* aRootX, gint* aRootY);
   void ClearCachedResources();
   nsIWidgetListener* GetListener();
-  bool IsComposited() const;
 
   void UpdateClientOffsetForCSDWindow();
 
   nsWindow* GetTransientForWindowIfPopup();
   bool IsHandlingTouchSequence(GdkEventSequence* aSequence);
 
+#ifdef MOZ_X11
+  typedef enum {GTK_WIDGET_COMPOSIDED_DEFAULT = 0,
+                GTK_WIDGET_COMPOSIDED_DISABLED = 1,
+                GTK_WIDGET_COMPOSIDED_ENABLED = 2} WindowComposeRequest;
+
+  void SetCompositorHint(WindowComposeRequest aState);
+#endif
+  nsCString mGtkWindowTypeName;
+  nsCString mGtkWindowRoleName;
+  void RefreshWindowClass();
+
   GtkWidget* mShell;
   MozContainer* mContainer;
   GdkWindow* mGdkWindow;
diff -up thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h.wayland thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h
--- thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/WindowSurfaceProvider.h	2019-02-05 14:26:16.978316639 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; 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
@@ -17,6 +17,7 @@
 #include <gdk/gdkwayland.h>
 #endif
 #include <X11/Xlib.h>  // for Window, Display, Visual, etc.
+#include "X11UndefineNone.h"
 
 class nsWindow;
 
@@ -70,6 +71,7 @@ class WindowSurfaceProvider final {
 #ifdef MOZ_WAYLAND
   nsWindow* mWidget;
 #endif
+  bool mIsShaped;
 };
 
 }  // namespace widget
diff -up thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp.wayland thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp
--- thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp.wayland	2019-01-22 20:44:04.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.cpp	2019-02-05 14:26:16.979316635 +0100
@@ -1,27 +1,28 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; 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 "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 "nsCOMArray.h"
-#include "mozilla/StaticMutex.h"
+#include "nsTArray.h"
+#include "base/message_loop.h"  // for MessageLoop
+#include "base/task.h"          // for NewRunnableMethod, etc
 
-#include <gdk/gdkwayland.h>
 #include <sys/mman.h>
-#include <assert.h>
 #include <fcntl.h>
 #include <errno.h>
 
+namespace mozilla {
+namespace widget {
+
 /*
   Wayland multi-thread rendering scheme
 
@@ -131,188 +132,16 @@ handle to wayland compositor by WindowBa
 (wl_buffer/wl_surface).
 */
 
-namespace mozilla {
-namespace widget {
-
 #define BUFFER_BPP 4
-
-// TODO: How many rendering threads do we actualy handle?
-static nsCOMArray<nsWaylandDisplay> gWaylandDisplays;
-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: is the 60pfs loop correct?
-#define EVENT_LOOP_DELAY (1000 / 60)
-
-// Get WaylandDisplay for given wl_display and actual calling thread.
-static nsWaylandDisplay *WaylandDisplayGetLocked(wl_display *aDisplay,
-                                                 const StaticMutexAutoLock &) {
-  nsWaylandDisplay *waylandDisplay = nullptr;
-
-  int len = gWaylandDisplays.Count();
-  for (int i = 0; i < len; i++) {
-    if (gWaylandDisplays[i]->Matches(aDisplay)) {
-      waylandDisplay = gWaylandDisplays[i];
-      break;
-    }
-  }
-
-  if (!waylandDisplay) {
-    waylandDisplay = new nsWaylandDisplay(aDisplay);
-    gWaylandDisplays.AppendObject(waylandDisplay);
-  }
-
-  NS_ADDREF(waylandDisplay);
-  return waylandDisplay;
-}
-
-static nsWaylandDisplay *WaylandDisplayGet(wl_display *aDisplay) {
-  StaticMutexAutoLock lock(gWaylandDisplaysMutex);
-  return WaylandDisplayGetLocked(aDisplay, lock);
-}
-
-static bool WaylandDisplayReleaseLocked(wl_display *aDisplay,
-                                        const StaticMutexAutoLock &) {
-  int len = gWaylandDisplays.Count();
-  for (int i = 0; i < len; i++) {
-    if (gWaylandDisplays[i]->Matches(aDisplay)) {
-      int rc = gWaylandDisplays[i]->Release();
-      // nsCOMArray::AppendObject()/RemoveObjectAt() also call
-      // AddRef()/Release() so remove WaylandDisplay when ref count is 1.
-      if (rc == 1) {
-        gWaylandDisplays.RemoveObjectAt(i);
-      }
-      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 &) {
-  int len = gWaylandDisplays.Count();
-  for (int i = 0; i < len; i++) {
-    if (gWaylandDisplays[i]->Matches(aDisplay)) {
-      if (gWaylandDisplays[i]->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.
-      ,
-      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");
+  const char* tmppath = getenv("XDG_RUNTIME_DIR");
   MOZ_RELEASE_ASSERT(tmppath, "Missing XDG_RUNTIME_DIR env variable.");
 
   nsPrintfCString tmpname("%s/mozilla-shared-XXXXXX", tmppath);
 
-  char *filename;
+  char* filename;
   int fd = -1;
   int ret = 0;
 
@@ -353,7 +182,7 @@ int WaylandShmPool::CreateTemporaryFile(
   return fd;
 }
 
-WaylandShmPool::WaylandShmPool(nsWaylandDisplay *aWaylandDisplay, int aSize)
+WaylandShmPool::WaylandShmPool(nsWaylandDisplay* aWaylandDisplay, int aSize)
     : mAllocatedSize(aSize) {
   mShmPoolFd = CreateTemporaryFile(mAllocatedSize);
   mImageData = mmap(nullptr, mAllocatedSize, PROT_READ | PROT_WRITE, MAP_SHARED,
@@ -365,7 +194,7 @@ WaylandShmPool::WaylandShmPool(nsWayland
       wl_shm_create_pool(aWaylandDisplay->GetShm(), mShmPoolFd, mAllocatedSize);
 
   // We set our queue to get mShmPool events at compositor thread.
-  wl_proxy_set_queue((struct wl_proxy *)mShmPool,
+  wl_proxy_set_queue((struct wl_proxy*)mShmPool,
                      aWaylandDisplay->GetEventQueue());
 }
 
@@ -394,7 +223,7 @@ bool WaylandShmPool::Resize(int aSize) {
   return true;
 }
 
-void WaylandShmPool::SetImageDataFromPool(class WaylandShmPool *aSourcePool,
+void WaylandShmPool::SetImageDataFromPool(class WaylandShmPool* aSourcePool,
                                           int aImageDataSize) {
   MOZ_ASSERT(mAllocatedSize >= aImageDataSize, "WaylandShmPool overflows!");
   memcpy(mImageData, aSourcePool->GetImageData(), aImageDataSize);
@@ -406,8 +235,8 @@ WaylandShmPool::~WaylandShmPool() {
   close(mShmPoolFd);
 }
 
-static void buffer_release(void *data, wl_buffer *buffer) {
-  auto surface = reinterpret_cast<WindowBackBuffer *>(data);
+static void buffer_release(void* data, wl_buffer* buffer) {
+  auto surface = reinterpret_cast<WindowBackBuffer*>(data);
   surface->Detach();
 }
 
@@ -422,7 +251,7 @@ void WindowBackBuffer::Create(int aWidth
   mWaylandBuffer =
       wl_shm_pool_create_buffer(mShmPool.GetShmPool(), 0, aWidth, aHeight,
                                 aWidth * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);
-  wl_proxy_set_queue((struct wl_proxy *)mWaylandBuffer,
+  wl_proxy_set_queue((struct wl_proxy*)mWaylandBuffer,
                      mWaylandDisplay->GetEventQueue());
   wl_buffer_add_listener(mWaylandBuffer, &buffer_listener, this);
 
@@ -435,7 +264,11 @@ void WindowBackBuffer::Release() {
   mWidth = mHeight = 0;
 }
 
-WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay *aWaylandDisplay,
+void WindowBackBuffer::Clear() {
+  memset(mShmPool.GetImageData(), 0, mHeight * mWidth * BUFFER_BPP);
+}
+
+WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay* aWaylandDisplay,
                                    int aWidth, int aHeight)
     : mShmPool(aWaylandDisplay, aWidth * aHeight * BUFFER_BPP),
       mWaylandBuffer(nullptr),
@@ -457,7 +290,7 @@ bool WindowBackBuffer::Resize(int aWidth
   return (mWaylandBuffer != nullptr);
 }
 
-void WindowBackBuffer::Attach(wl_surface *aSurface) {
+void WindowBackBuffer::Attach(wl_surface* aSurface) {
   wl_surface_attach(aSurface, mWaylandBuffer, 0, 0);
   wl_surface_commit(aSurface);
   wl_display_flush(mWaylandDisplay->GetDisplay());
@@ -466,8 +299,8 @@ void WindowBackBuffer::Attach(wl_surface
 
 void WindowBackBuffer::Detach() { mAttached = false; }
 
-bool WindowBackBuffer::SetImageDataFromBackBuffer(
-    class WindowBackBuffer *aSourceBuffer) {
+bool WindowBackBuffer::SetImageDataFromBuffer(
+    class WindowBackBuffer* aSourceBuffer) {
   if (!IsMatchingSize(aSourceBuffer)) {
     Resize(aSourceBuffer->mWidth, aSourceBuffer->mHeight);
   }
@@ -478,204 +311,381 @@ bool WindowBackBuffer::SetImageDataFromB
   return true;
 }
 
-already_AddRefed<gfx::DrawTarget> WindowBackBuffer::Lock(
-    const LayoutDeviceIntRegion &aRegion) {
-  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
-  gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
-
+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());
+      static_cast<unsigned char*>(mShmPool.GetImageData()), lockSize,
+      BUFFER_BPP * mWidth, mFormat);
 }
 
-static void frame_callback_handler(void *data, struct wl_callback *callback,
+static void frame_callback_handler(void* data, struct wl_callback* callback,
                                    uint32_t time) {
-  auto surface = reinterpret_cast<WindowSurfaceWayland *>(data);
+  auto surface = reinterpret_cast<WindowSurfaceWayland*>(data);
   surface->FrameCallbackHandler();
 }
 
 static const struct wl_callback_listener frame_listener = {
     frame_callback_handler};
 
-WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
+WindowSurfaceWayland::WindowSurfaceWayland(nsWindow* aWindow)
     : mWindow(aWindow),
-      mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())),
-      mFrontBuffer(nullptr),
-      mBackBuffer(nullptr),
+      mWaylandDisplay(WaylandDisplayGet()),
+      mWaylandBuffer(nullptr),
       mFrameCallback(nullptr),
-      mFrameCallbackSurface(nullptr),
+      mLastCommittedSurface(nullptr),
       mDisplayThreadMessageLoop(MessageLoop::current()),
-      mDelayedCommit(false),
-      mFullScreenDamage(false),
-      mIsMainThread(NS_IsMainThread()) {}
+      mDelayedCommitHandle(nullptr),
+      mDrawToWaylandBufferDirectly(true),
+      mPendingCommit(false),
+      mWaylandBufferFullScreenDamage(false),
+      mIsMainThread(NS_IsMainThread()),
+      mNeedScaleFactorUpdate(true) {
+  for (int i = 0; i < BACK_BUFFER_NUM; i++) mBackupBuffer[i] = nullptr;
+}
 
 WindowSurfaceWayland::~WindowSurfaceWayland() {
-  delete mFrontBuffer;
-  delete mBackBuffer;
+  if (mPendingCommit) {
+    NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!");
+  }
+
+  if (mDelayedCommitHandle) {
+    // Delete reference to this to prevent WaylandBufferDelayCommitHandler()
+    // operate on released this. mDelayedCommitHandle itself will
+    // be released at WaylandBufferDelayCommitHandler().
+    *mDelayedCommitHandle = nullptr;
+  }
 
   if (mFrameCallback) {
     wl_callback_destroy(mFrameCallback);
   }
 
+  delete mWaylandBuffer;
+
+  for (int i = 0; i < BACK_BUFFER_NUM; i++) {
+    if (mBackupBuffer[i]) {
+      delete mBackupBuffer[i];
+    }
+  }
+
   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()));
+    mDisplayThreadMessageLoop->PostTask(NewRunnableFunction(
+        "WaylandDisplayRelease", &WaylandDisplayRelease, mWaylandDisplay));
   } else {
-    WaylandDisplayRelease(mWaylandDisplay->GetDisplay());
-  }
-}
-
-void WindowSurfaceWayland::UpdateScaleFactor() {
-  wl_surface *waylandSurface = mWindow->GetWaylandSurface();
-  if (waylandSurface) {
-    wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+    WaylandDisplayRelease(mWaylandDisplay);
   }
 }
 
-WindowBackBuffer *WindowSurfaceWayland::GetBufferToDraw(int aWidth,
-                                                        int aHeight) {
-  if (!mFrontBuffer) {
-    mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
-    mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
-    return mFrontBuffer;
+WindowBackBuffer* WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth,
+                                                               int aHeight) {
+  if (!mWaylandBuffer) {
+    mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+    return mWaylandBuffer;
   }
 
-  if (!mFrontBuffer->IsAttached()) {
-    if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) {
-      mFrontBuffer->Resize(aWidth, aHeight);
+  if (!mWaylandBuffer->IsAttached()) {
+    if (!mWaylandBuffer->IsMatchingSize(aWidth, aHeight)) {
+      mWaylandBuffer->Resize(aWidth, aHeight);
       // There's a chance that scale factor has been changed
       // when buffer size changed
-      UpdateScaleFactor();
+      mNeedScaleFactorUpdate = true;
+    }
+    return mWaylandBuffer;
+  }
+
+  MOZ_ASSERT(!mPendingCommit,
+             "Uncommitted buffer switch, screen artifacts ahead.");
+
+  // Front buffer is used by compositor, select a back buffer
+  int availableBuffer;
+  for (availableBuffer = 0; availableBuffer < BACK_BUFFER_NUM;
+       availableBuffer++) {
+    if (!mBackupBuffer[availableBuffer]) {
+      mBackupBuffer[availableBuffer] =
+          new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+      break;
+    }
+
+    if (!mBackupBuffer[availableBuffer]->IsAttached()) {
+      break;
     }
-    return mFrontBuffer;
   }
 
-  // Front buffer is used by compositor, draw to back buffer
-  if (mBackBuffer->IsAttached()) {
+  if (MOZ_UNLIKELY(availableBuffer == BACK_BUFFER_NUM)) {
     NS_WARNING("No drawing buffer available");
     return nullptr;
   }
 
-  MOZ_ASSERT(!mDelayedCommit,
-             "Uncommitted buffer switch, screen artifacts ahead.");
-
-  WindowBackBuffer *tmp = mFrontBuffer;
-  mFrontBuffer = mBackBuffer;
-  mBackBuffer = tmp;
+  WindowBackBuffer* lastWaylandBuffer = mWaylandBuffer;
+  mWaylandBuffer = mBackupBuffer[availableBuffer];
+  mBackupBuffer[availableBuffer] = lastWaylandBuffer;
 
-  if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) {
+  if (lastWaylandBuffer->IsMatchingSize(aWidth, aHeight)) {
     // Former front buffer has the same size as a requested one.
     // Gecko may expect a content already drawn on screen so copy
     // existing data to the new buffer.
-    mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer);
+    mWaylandBuffer->SetImageDataFromBuffer(lastWaylandBuffer);
     // When buffer switches we need to damage whole screen
     // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260)
-    mFullScreenDamage = true;
+    mWaylandBufferFullScreenDamage = true;
   } else {
     // Former buffer has different size from the new request. Only resize
     // the new buffer and leave gecko to render new whole content.
-    mFrontBuffer->Resize(aWidth, aHeight);
+    mWaylandBuffer->Resize(aWidth, aHeight);
   }
 
-  return mFrontBuffer;
+  return mWaylandBuffer;
 }
 
-already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
-    const LayoutDeviceIntRegion &aRegion) {
-  MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
-
-  // We allocate back buffer to widget size but return only
-  // portion requested by aRegion.
-  LayoutDeviceIntRect rect = mWindow->GetBounds();
-  WindowBackBuffer *buffer = GetBufferToDraw(rect.width, rect.height);
+already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::LockWaylandBuffer(
+    int aWidth, int aHeight, bool aClearBuffer) {
+  WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight);
   if (!buffer) {
-    NS_WARNING("No drawing buffer available");
+    NS_WARNING(
+        "WindowSurfaceWayland::LockWaylandBuffer(): No buffer available");
     return nullptr;
   }
 
-  return buffer->Lock(aRegion);
+  if (aClearBuffer) {
+    buffer->Clear();
+  }
+
+  return buffer->Lock();
+}
+
+already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::LockImageSurface(
+    const gfx::IntSize& aLockSize) {
+  if (!mImageSurface || mImageSurface->CairoStatus() ||
+      !(aLockSize <= mImageSurface->GetSize())) {
+    mImageSurface = new gfxImageSurface(
+        aLockSize,
+        SurfaceFormatToImageFormat(WindowBackBuffer::GetSurfaceFormat()));
+    if (mImageSurface->CairoStatus()) {
+      return nullptr;
+    }
+  }
+
+  return gfxPlatform::CreateDrawTargetForData(
+      mImageSurface->Data(), mImageSurface->GetSize(), mImageSurface->Stride(),
+      WindowBackBuffer::GetSurfaceFormat());
 }
 
-void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion &aInvalidRegion) {
+/*
+  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.
+       - mWaylandBuffer is available - that's an ideal situation.
+       - mWaylandBuffer is locked by compositor - flip buffers and draw.
+          - if we can't flip buffers - go B)
+
+  B) Lock() is requested for part(s) of screen. We need to provide temporary
+     surface to draw into and copy result (clipped) to target wl_surface.
+ */
+already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
+    const LayoutDeviceIntRegion& aRegion) {
   MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
 
-  wl_surface *waylandSurface = mWindow->GetWaylandSurface();
+  LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+  gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+  // Are we asked for entire nsWindow to draw?
+  mDrawToWaylandBufferDirectly =
+      (aRegion.GetNumRects() == 1 && bounds.x == 0 && bounds.y == 0 &&
+       lockSize.width == screenRect.width &&
+       lockSize.height == screenRect.height);
+
+  if (mDrawToWaylandBufferDirectly) {
+    RefPtr<gfx::DrawTarget> dt =
+        LockWaylandBuffer(screenRect.width, screenRect.height,
+                          mWindow->WaylandSurfaceNeedsClear());
+    if (dt) {
+      return dt.forget();
+    }
+
+    // We don't have any front buffer available. Try indirect drawing
+    // to mImageSurface which is mirrored to front buffer at commit.
+    mDrawToWaylandBufferDirectly = false;
+  }
+
+  return LockImageSurface(lockSize);
+}
+
+bool WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(
+    const LayoutDeviceIntRegion& aRegion) {
+  MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
+
+  LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+
+  gfx::Rect rect(bounds);
+  if (rect.IsEmpty()) {
+    return false;
+  }
+
+  RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(
+      screenRect.width, screenRect.height, mWindow->WaylandSurfaceNeedsClear());
+  RefPtr<gfx::SourceSurface> surf =
+      gfx::Factory::CreateSourceSurfaceForCairoSurface(
+          mImageSurface->CairoSurface(), mImageSurface->GetSize(),
+          mImageSurface->Format());
+  if (!dt || !surf) {
+    return false;
+  }
+
+  uint32_t numRects = aRegion.GetNumRects();
+  if (numRects != 1) {
+    AutoTArray<IntRect, 32> rects;
+    rects.SetCapacity(numRects);
+    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+      rects.AppendElement(iter.Get().ToUnknownRect());
+    }
+    dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+  }
+
+  dt->DrawSurface(surf, rect, rect);
+
+  if (numRects != 1) {
+    dt->PopClip();
+  }
+
+  return true;
+}
+
+static void WaylandBufferDelayCommitHandler(WindowSurfaceWayland** aSurface) {
+  if (*aSurface) {
+    (*aSurface)->DelayedCommitHandler();
+  } else {
+    // Referenced WindowSurfaceWayland is already deleted.
+    // Do nothing but just release the mDelayedCommitHandle allocated at
+    // WindowSurfaceWayland::CommitWaylandBuffer().
+    free(aSurface);
+  }
+}
+
+void WindowSurfaceWayland::CommitWaylandBuffer() {
+  MOZ_ASSERT(mPendingCommit, "Committing empty surface!");
+
+  wl_surface* waylandSurface = mWindow->GetWaylandSurface();
   if (!waylandSurface) {
-    // Target window is already destroyed - don't bother to render there.
+    // Target window is not created yet - delay the commit. This can happen only
+    // when the window is newly created and there's no active
+    // frame callback pending.
+    MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface,
+               "Missing wayland surface at frame callback!");
+
+    // Do nothing if there's already mDelayedCommitHandle pending.
+    if (!mDelayedCommitHandle) {
+      mDelayedCommitHandle = static_cast<WindowSurfaceWayland**>(
+          moz_xmalloc(sizeof(*mDelayedCommitHandle)));
+      *mDelayedCommitHandle = this;
+
+      MessageLoop::current()->PostDelayedTask(
+          NewRunnableFunction("WaylandBackBufferCommit",
+                              &WaylandBufferDelayCommitHandler,
+                              mDelayedCommitHandle),
+          EVENT_LOOP_DELAY);
+    }
     return;
   }
-  wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
+  wl_proxy_set_queue((struct wl_proxy*)waylandSurface,
                      mWaylandDisplay->GetEventQueue());
 
-  if (mFullScreenDamage) {
+  // We have an active frame callback request so handle it.
+  if (mFrameCallback) {
+    if (waylandSurface == mLastCommittedSurface) {
+      // We have an active frame callback pending from our recent surface.
+      // It means we should defer the commit to FrameCallbackHandler().
+      return;
+    }
+    // If our stored wl_surface does not match the actual one it means the frame
+    // callback is no longer active and we should release it.
+    wl_callback_destroy(mFrameCallback);
+    mFrameCallback = nullptr;
+    mLastCommittedSurface = nullptr;
+  }
+
+  if (mWaylandBufferFullScreenDamage) {
     LayoutDeviceIntRect rect = mWindow->GetBounds();
     wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height);
-    mFullScreenDamage = false;
+    mWaylandBufferFullScreenDamage = false;
   } else {
-    for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
-      const mozilla::LayoutDeviceIntRect &r = iter.Get();
-      wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
+    gint scaleFactor = mWindow->GdkScaleFactor();
+    for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done();
+         iter.Next()) {
+      const mozilla::LayoutDeviceIntRect& r = iter.Get();
+      // We need to remove the scale factor because the wl_surface_damage
+      // also multiplies by current  scale factor.
+      wl_surface_damage(waylandSurface, r.x / scaleFactor, r.y / scaleFactor,
+                        r.width / scaleFactor, r.height / scaleFactor);
     }
   }
 
-  // Frame callback is always connected to actual wl_surface. When the surface
-  // is unmapped/deleted the frame callback is never called. Unfortunatelly
-  // we don't know if the frame callback is not going to be called.
-  // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden
-  // creates a new one when is visible.
-  if (mFrameCallback && mFrameCallbackSurface == waylandSurface) {
-    // Do nothing here - we have a valid wl_surface and the buffer will be
-    // commited to compositor in next frame callback event.
-    mDelayedCommit = true;
-    return;
-  } else {
-    if (mFrameCallback) {
-      // Delete frame callback connected to obsoleted wl_surface.
-      wl_callback_destroy(mFrameCallback);
-    }
+  // Clear all back buffer damage as we're committing
+  // all requested regions.
+  mWaylandBufferDamage.SetEmpty();
 
-    mFrameCallback = wl_surface_frame(waylandSurface);
-    wl_callback_add_listener(mFrameCallback, &frame_listener, this);
-    mFrameCallbackSurface = waylandSurface;
-
-    // There's no pending frame callback so we can draw immediately
-    // and create frame callback for possible subsequent drawing.
-    mFrontBuffer->Attach(waylandSurface);
-    mDelayedCommit = false;
+  mFrameCallback = wl_surface_frame(waylandSurface);
+  wl_callback_add_listener(mFrameCallback, &frame_listener, this);
+
+  if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) {
+    wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+    mNeedScaleFactorUpdate = false;
   }
+
+  mWaylandBuffer->Attach(waylandSurface);
+  mLastCommittedSurface = waylandSurface;
+
+  // There's no pending commit, all changes are sent to compositor.
+  mPendingCommit = false;
+}
+
+void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) {
+  MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+
+  // We have new content at mImageSurface - copy data to mWaylandBuffer first.
+  if (!mDrawToWaylandBufferDirectly) {
+    CommitImageSurfaceToWaylandBuffer(aInvalidRegion);
+  }
+
+  // If we're not at fullscreen damage add drawing area from aInvalidRegion
+  if (!mWaylandBufferFullScreenDamage) {
+    mWaylandBufferDamage.OrWith(aInvalidRegion);
+  }
+
+  // We're ready to commit.
+  mPendingCommit = true;
+  CommitWaylandBuffer();
 }
 
 void WindowSurfaceWayland::FrameCallbackHandler() {
   MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+  MOZ_ASSERT(mFrameCallback != nullptr,
+             "FrameCallbackHandler() called without valid frame callback!");
+  MOZ_ASSERT(mLastCommittedSurface != nullptr,
+             "FrameCallbackHandler() called without valid wl_surface!");
 
-  if (mFrameCallback) {
-    wl_callback_destroy(mFrameCallback);
-    mFrameCallback = nullptr;
-    mFrameCallbackSurface = nullptr;
+  wl_callback_destroy(mFrameCallback);
+  mFrameCallback = nullptr;
+
+  if (mPendingCommit) {
+    CommitWaylandBuffer();
   }
+}
 
-  if (mDelayedCommit) {
-    wl_surface *waylandSurface = mWindow->GetWaylandSurface();
-    if (!waylandSurface) {
-      // Target window is already destroyed - don't bother to render there.
-      NS_WARNING("No drawing buffer available");
-      return;
-    }
-    wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
-                       mWaylandDisplay->GetEventQueue());
+void WindowSurfaceWayland::DelayedCommitHandler() {
+  MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!");
 
-    // Send pending surface to compositor and register frame callback
-    // for possible subsequent drawing.
-    mFrameCallback = wl_surface_frame(waylandSurface);
-    wl_callback_add_listener(mFrameCallback, &frame_listener, this);
-    mFrameCallbackSurface = waylandSurface;
+  *mDelayedCommitHandle = nullptr;
+  free(mDelayedCommitHandle);
+  mDelayedCommitHandle = nullptr;
 
-    mFrontBuffer->Attach(waylandSurface);
-    mDelayedCommit = false;
+  if (mPendingCommit) {
+    CommitWaylandBuffer();
   }
 }
 
diff -up thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h.wayland thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h
--- thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h.wayland	2019-01-22 20:44:03.000000000 +0100
+++ thunderbird-60.5.0/widget/gtk/WindowSurfaceWayland.h	2019-02-05 14:26:16.979316635 +0100
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; 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
@@ -8,37 +8,14 @@
 #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:
-  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:
@@ -66,14 +43,15 @@ class WindowBackBuffer {
   WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
   ~WindowBackBuffer();
 
-  already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion);
+  already_AddRefed<gfx::DrawTarget> Lock();
 
   void Attach(wl_surface* aSurface);
   void Detach();
   bool IsAttached() { return mAttached; }
 
+  void Clear();
   bool Resize(int aWidth, int aHeight);
-  bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
+  bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
 
   bool IsMatchingSize(int aWidth, int aHeight) {
     return aWidth == mWidth && aHeight == mHeight;
@@ -82,6 +60,8 @@ class WindowBackBuffer {
     return aBuffer->mWidth == mWidth && aBuffer->mHeight == mHeight;
   }
 
+  static gfx::SurfaceFormat GetSurfaceFormat() { return mFormat; }
+
  private:
   void Create(int aWidth, int aHeight);
   void Release();
@@ -96,35 +76,48 @@ class WindowBackBuffer {
   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:
-  WindowSurfaceWayland(nsWindow* aWindow);
+  explicit WindowSurfaceWayland(nsWindow* aWindow);
   ~WindowSurfaceWayland();
 
   already_AddRefed<gfx::DrawTarget> Lock(
       const LayoutDeviceIntRegion& aRegion) override;
   void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
   void FrameCallbackHandler();
+  void DelayedCommitHandler();
 
  private:
-  WindowBackBuffer* GetBufferToDraw(int aWidth, int aHeight);
-  void UpdateScaleFactor();
+  WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight);
+
+  already_AddRefed<gfx::DrawTarget> LockWaylandBuffer(int aWidth, int aHeight,
+                                                      bool aClearBuffer);
+  already_AddRefed<gfx::DrawTarget> LockImageSurface(
+      const gfx::IntSize& aLockSize);
+  bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion);
+  void CommitWaylandBuffer();
 
   // TODO: Do we need to hold a reference to nsWindow object?
   nsWindow* mWindow;
   nsWaylandDisplay* mWaylandDisplay;
-  WindowBackBuffer* mFrontBuffer;
-  WindowBackBuffer* mBackBuffer;
+  WindowBackBuffer* mWaylandBuffer;
+  LayoutDeviceIntRegion mWaylandBufferDamage;
+  WindowBackBuffer* mBackupBuffer[BACK_BUFFER_NUM];
+  RefPtr<gfxImageSurface> mImageSurface;
   wl_callback* mFrameCallback;
-  wl_surface* mFrameCallbackSurface;
+  wl_surface* mLastCommittedSurface;
   MessageLoop* mDisplayThreadMessageLoop;
-  bool mDelayedCommit;
-  bool mFullScreenDamage;
+  WindowSurfaceWayland** mDelayedCommitHandle;
+  bool mDrawToWaylandBufferDirectly;
+  bool mPendingCommit;
+  bool mWaylandBufferFullScreenDamage;
   bool mIsMainThread;
+  bool mNeedScaleFactorUpdate;
 };
 
 }  // namespace widget
diff -up thunderbird-60.6.1/widget/gtk/WindowSurfaceProvider.cpp.old thunderbird-60.6.1/widget/gtk/WindowSurfaceProvider.cpp
--- thunderbird-60.6.1/widget/gtk/WindowSurfaceProvider.cpp.old	2019-05-14 21:11:50.219841534 +0200
+++ thunderbird-60.6.1/widget/gtk/WindowSurfaceProvider.cpp	2019-05-14 21:11:58.228755117 +0200
@@ -52,9 +52,6 @@ void WindowSurfaceProvider::Initialize(D
 
 #ifdef MOZ_WAYLAND
 void WindowSurfaceProvider::Initialize(nsWindow* aWidget) {
-  MOZ_ASSERT(aWidget->GetWaylandDisplay(),
-             "We are supposed to have a Wayland display!");
-
   mWidget = aWidget;
   mIsX11Display = false;
 }