Blob Blame History Raw
diff -up thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp.wayland thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp
--- thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp	2018-11-21 13:42:00.757025666 +0100
@@ -40,7 +40,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;
@@ -50,8 +52,7 @@ GtkCompositorWidget::GtkCompositorWidget
       mXDisplay,
       mXWindow,
       visual,
-      depth
-      );
+      depth);
   }
   mClientSize = aInitData.InitialClientSize();
 }
diff -up thunderbird-60.3.0/widget/gtk/moz.build.wayland thunderbird-60.3.0/widget/gtk/moz.build
--- thunderbird-60.3.0/widget/gtk/moz.build.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/moz.build	2018-11-21 13:42:00.757025666 +0100
@@ -123,6 +123,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.3.0/widget/gtk/mozcontainer.cpp.wayland thunderbird-60.3.0/widget/gtk/mozcontainer.cpp
--- thunderbird-60.3.0/widget/gtk/mozcontainer.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/mozcontainer.cpp	2018-11-21 13:42:00.757025666 +0100
@@ -10,6 +10,7 @@
 #ifdef MOZ_WAYLAND
 #include <gdk/gdkx.h>
 #include <gdk/gdkwayland.h>
+#include <wayland-egl.h>
 #endif
 #include <stdio.h>
 #include <dlfcn.h>
@@ -207,6 +208,12 @@ moz_container_init (MozContainer *contai
 
 #if defined(MOZ_WAYLAND)
     {
+      container->subcompositor = nullptr;
+      container->surface = nullptr;
+      container->subsurface = nullptr;
+      container->eglwindow = nullptr;
+      container->parent_surface_committed = false;
+
       GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
       if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
           // Available as of GTK 3.8+
@@ -225,12 +232,21 @@ moz_container_init (MozContainer *contai
 }
 
 #if defined(MOZ_WAYLAND)
+static void
+moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container)
+{
+    container->parent_surface_committed = true;
+    g_signal_handler_disconnect(clock,
+                                container->parent_surface_committed_handler);
+    container->parent_surface_committed_handler = 0;
+}
+
 /* 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.
+ * for reference.
  */
 static gboolean
 moz_container_map_surface(MozContainer *container)
@@ -242,6 +258,9 @@ moz_container_map_surface(MozContainer *
     static auto sGdkWaylandWindowGetWlSurface =
         (wl_surface *(*)(GdkWindow *))
         dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
+    static auto sGdkWindowGetFrameClock =
+        (GdkFrameClock *(*)(GdkWindow *))
+        dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
 
     GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
     if (GDK_IS_X11_DISPLAY(display))
@@ -250,6 +269,18 @@ moz_container_map_surface(MozContainer *
     if (container->subsurface && container->surface)
         return true;
 
+    if (!container->parent_surface_committed) {
+        if (!container->parent_surface_committed_handler) {
+            GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
+            GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
+            container->parent_surface_committed_handler =
+                g_signal_connect_after(clock, "after-paint",
+                                       G_CALLBACK(moz_container_commited_handler),
+                                       container);
+        }
+        return false;
+    }
+
     if (!container->surface) {
         struct wl_compositor *compositor;
         compositor = sGdkWaylandDisplayGetWlCompositor(display);
@@ -289,8 +320,22 @@ moz_container_map_surface(MozContainer *
 static void
 moz_container_unmap_surface(MozContainer *container)
 {
+    //g_clear_pointer(&container->eglwindow, wl_egl_window_destroy);
     g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
     g_clear_pointer(&container->surface, wl_surface_destroy);
+
+    if (container->parent_surface_committed_handler) {
+        static auto sGdkWindowGetFrameClock =
+            (GdkFrameClock *(*)(GdkWindow *))
+            dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
+        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
+        GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
+
+        g_signal_handler_disconnect(clock,
+                                    container->parent_surface_committed_handler);
+        container->parent_surface_committed_handler = 0;
+    }
+    container->parent_surface_committed = false;
 }
 
 #endif
@@ -555,8 +605,40 @@ moz_container_get_wl_surface(MozContaine
             return nullptr;
 
         moz_container_map_surface(container);
+        // Set the scale factor for the buffer right after we create it.
+        if (container->surface) {
+            static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*))
+            dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
+            if (sGdkWindowGetScaleFactorPtr && window) {
+              gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window);
+              wl_surface_set_buffer_scale(container->surface, scaleFactor);
+            }
+        }
     }
 
     return container->surface;
 }
+
+struct wl_egl_window *
+moz_container_get_wl_egl_window(MozContainer *container)
+{ /*
+    if (!container->eglwindow) {
+        struct wl_surface *wlsurf = moz_container_get_wl_surface(container);
+        if (!wlsurf)
+            return nullptr;
+
+      GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
+      container->eglwindow
+            = wl_egl_window_create(wlsurf,
+                                   gdk_window_get_width(window),
+                                   gdk_window_get_height(window));
+    }
+    return container->eglwindow;*/ return nullptr;
+}
+
+gboolean
+moz_container_has_wl_egl_window(MozContainer *container)
+{
+    return container->eglwindow ? true : false;
+}
 #endif
diff -up thunderbird-60.3.0/widget/gtk/mozcontainer.h.wayland thunderbird-60.3.0/widget/gtk/mozcontainer.h
--- thunderbird-60.3.0/widget/gtk/mozcontainer.h.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/mozcontainer.h	2018-11-21 13:42:00.757025666 +0100
@@ -72,6 +72,9 @@ struct _MozContainer
     struct wl_subcompositor *subcompositor;
     struct wl_surface       *surface;
     struct wl_subsurface    *subsurface;
+    struct wl_egl_window    *eglwindow;
+    gboolean                 parent_surface_committed;
+    gulong                   parent_surface_committed_handler;
 #endif
 };
 
@@ -95,6 +98,8 @@ void       moz_container_move          (
 
 #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);
 #endif
 
 #endif /* __MOZ_CONTAINER_H__ */
diff -up thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c.wayland thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c
--- thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c	2018-11-21 13:42:00.757025666 +0100
@@ -135,6 +135,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)
@@ -266,6 +268,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)
diff -up thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c.wayland thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c
--- thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c	2018-11-21 13:42:00.758025661 +0100
@@ -271,3 +271,21 @@ wl_log_set_handler_client(wl_log_func_t
 {
 }
 
+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.3.0/widget/gtk/nsClipboard.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboard.cpp
--- thunderbird-60.3.0/widget/gtk/nsClipboard.cpp.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsClipboard.cpp	2018-11-21 13:42:00.758025661 +0100
@@ -671,11 +671,9 @@ void ConvertHTMLtoUCS2(const char* data,
         *unicodeData =
             reinterpret_cast<char16_t*>
             (moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t)));
-        if (*unicodeData) {
-            memcpy(*unicodeData, data + sizeof(char16_t),
-                   outUnicodeLen * sizeof(char16_t));
-            (*unicodeData)[outUnicodeLen] = '\0';
-        }
+        memcpy(*unicodeData, data + sizeof(char16_t),
+               outUnicodeLen * sizeof(char16_t));
+        (*unicodeData)[outUnicodeLen] = '\0';
     } else if (charset.EqualsLiteral("UNKNOWN")) {
         outUnicodeLen = 0;
         return;
@@ -701,27 +699,25 @@ void ConvertHTMLtoUCS2(const char* data,
         if (needed.value()) {
           *unicodeData = reinterpret_cast<char16_t*>(
             moz_xmalloc((needed.value() + 1) * sizeof(char16_t)));
-          if (*unicodeData) {
-            uint32_t result;
-            size_t read;
-            size_t written;
-            bool hadErrors;
-            Tie(result, read, written, hadErrors) =
-              decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)),
-                                     MakeSpan(*unicodeData, needed.value()),
-                                     true);
-            MOZ_ASSERT(result == kInputEmpty);
-            MOZ_ASSERT(read == size_t(dataLength));
-            MOZ_ASSERT(written <= needed.value());
-            Unused << hadErrors;
+          uint32_t result;
+          size_t read;
+          size_t written;
+          bool hadErrors;
+          Tie(result, read, written, hadErrors) =
+            decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)),
+                                   MakeSpan(*unicodeData, needed.value()),
+                                   true);
+          MOZ_ASSERT(result == kInputEmpty);
+          MOZ_ASSERT(read == size_t(dataLength));
+          MOZ_ASSERT(written <= needed.value());
+          Unused << hadErrors;
 #ifdef DEBUG_CLIPBOARD
-            if (read != dataLength)
-              printf("didn't consume all the bytes\n");
+          if (read != dataLength)
+            printf("didn't consume all the bytes\n");
 #endif
-            outUnicodeLen = written;
-            // null terminate.
-            (*unicodeData)[outUnicodeLen] = '\0';
-            }
+          outUnicodeLen = written;
+          // null terminate.
+          (*unicodeData)[outUnicodeLen] = '\0';
         } // if valid length
     }
 }
diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp
--- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp	2018-11-21 13:42:00.758025661 +0100
@@ -23,6 +23,7 @@
 #include "mozilla/Services.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
+#include "nsDragService.h"
 
 #include "imgIContainer.h"
 
@@ -46,6 +47,44 @@ nsRetrievalContextWayland::sTextMimeType
     "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)
 {
@@ -114,7 +153,7 @@ DataOffer::GetData(wl_display* aDisplay,
 
     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) {
@@ -155,6 +194,61 @@ WaylandDataOffer::RequestDataTransfer(co
     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,
@@ -164,25 +258,39 @@ data_offer_offer (void                 *
     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)
 {
+    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)
 {
+    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 = {
     data_offer_offer,
@@ -246,12 +354,94 @@ PrimaryDataOffer::~PrimaryDataOffer(void
     }
 }
 
+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
-nsRetrievalContextWayland::RegisterDataOffer(wl_data_offer *aWaylandDataOffer)
+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);
@@ -259,12 +449,15 @@ nsRetrievalContextWayland::RegisterDataO
 }
 
 void
-nsRetrievalContextWayland::RegisterDataOffer(
+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);
@@ -274,6 +467,9 @@ nsRetrievalContextWayland::RegisterDataO
 void
 nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer)
 {
+    // Delete existing clipboard data offer
+    mClipboardOffer = nullptr;
+
     DataOffer* dataOffer =
         static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
                                                     aWaylandDataOffer));
@@ -288,10 +484,12 @@ 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));
@@ -304,12 +502,31 @@ nsRetrievalContextWayland::SetPrimaryDat
 }
 
 void
-nsRetrievalContextWayland::ClearDataOffers(void)
+nsRetrievalContextWayland::AddDragAndDropDataOffer(wl_data_offer *aDropDataOffer)
 {
-    if (mClipboardOffer)
-        mClipboardOffer = nullptr;
-    if (mPrimaryOffer)
-        mPrimaryOffer = nullptr;
+    // 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);
+    }
+}
+
+nsWaylandDragContext*
+nsRetrievalContextWayland::GetDragContext(void)
+{
+    return mDragContext;
+}
+
+void
+nsRetrievalContextWayland::ClearDragAndDropDataOffer(void)
+{
+    mDragContext = nullptr;
 }
 
 // We have a new fresh data content.
@@ -321,7 +538,7 @@ data_device_data_offer (void
 {
     nsRetrievalContextWayland *context =
         static_cast<nsRetrievalContextWayland*>(data);
-    context->RegisterDataOffer(offer);
+    context->RegisterNewDataOffer(offer);
 }
 
 // The new fresh data content is clipboard.
@@ -341,31 +558,78 @@ data_device_enter (void
                    struct wl_data_device *data_device,
                    uint32_t               time,
                    struct wl_surface     *surface,
-                   int32_t                x,
-                   int32_t                y,
+                   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);
+
+    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)
+                    int32_t                x_fixed,
+                    int32_t                y_fixed)
 {
+    nsRetrievalContextWayland *context =
+        static_cast<nsRetrievalContextWayland*>(data);
+
+    nsWaylandDragContext* dropContext = context->GetDragContext();
+
+    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:
@@ -405,7 +669,7 @@ primary_selection_data_offer (void
     // create and add listener
     nsRetrievalContextWayland *context =
         static_cast<nsRetrievalContextWayland*>(data);
-    context->RegisterDataOffer(gtk_primary_offer);
+    context->RegisterNewDataOffer(gtk_primary_offer);
 }
 
 static void
@@ -418,6 +682,18 @@ primary_selection_selection (void
     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,
@@ -430,81 +706,6 @@ nsRetrievalContextWayland::HasSelectionS
     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();
-}
-
-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,
@@ -530,7 +731,6 @@ nsRetrievalContextWayland::InitSeat(wl_r
                                     void *data)
 {
     mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
-    wl_seat_add_listener(mSeat, &seat_listener, data);
 }
 
 static void
@@ -573,6 +773,7 @@ nsRetrievalContextWayland::nsRetrievalCo
   , mActiveOffers(g_hash_table_new(NULL, NULL))
   , mClipboardOffer(nullptr)
   , mPrimaryOffer(nullptr)
+  , mDragContext(nullptr)
   , mClipboardRequestNumber(0)
   , mClipboardData(nullptr)
   , mClipboardDataLength(0)
@@ -616,8 +817,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);
 }
 
@@ -667,12 +881,14 @@ nsRetrievalContextWayland::TransferFastT
     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!");
@@ -727,7 +943,7 @@ nsRetrievalContextWayland::GetClipboardT
     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,
diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h
--- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h	2018-11-21 13:42:00.759025657 +0100
@@ -32,6 +32,7 @@ public:
 private:
     virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0;
 
+protected:
     nsTArray<GdkAtom> mTargetMIMETypes;
 };
 
@@ -40,25 +41,66 @@ class WaylandDataOffer : public DataOffe
 public:
     WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
 
-private:
+    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();
+
     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);
+    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:
@@ -74,15 +116,16 @@ 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);
 
@@ -103,6 +146,7 @@ private:
     GHashTable*                 mActiveOffers;
     nsAutoPtr<DataOffer>        mClipboardOffer;
     nsAutoPtr<DataOffer>        mPrimaryOffer;
+    RefPtr<nsWaylandDragContext> mDragContext;
 
     int                         mClipboardRequestNumber;
     char*                       mClipboardData;
diff -up thunderbird-60.3.0/widget/gtk/nsDragService.cpp.wayland thunderbird-60.3.0/widget/gtk/nsDragService.cpp
--- thunderbird-60.3.0/widget/gtk/nsDragService.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsDragService.cpp	2018-11-21 13:42:00.759025657 +0100
@@ -34,7 +34,6 @@
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
-#include "nsISelection.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
 #include "nsGtkUtils.h"
@@ -43,6 +42,9 @@
 #include "gfxPlatform.h"
 #include "ScreenHelperGTK.h"
 #include "nsArrayUtils.h"
+#ifdef MOZ_WAYLAND
+#include "nsClipboardWayland.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
@@ -99,6 +101,10 @@ invisibleSourceDragDataGet(GtkWidget
 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.
@@ -516,6 +522,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);
 }
@@ -636,6 +645,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);
@@ -1027,9 +1044,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;
@@ -1074,6 +1100,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;
 }
 
@@ -1105,6 +1140,36 @@ nsDragService::ReplyToDragMotion(GdkDrag
     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,
@@ -1136,6 +1201,12 @@ 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
@@ -1174,17 +1245,28 @@ nsDragService::GetTargetDragData(GdkAtom
                                    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"));
 }
 
@@ -1435,7 +1517,7 @@ nsDragService::SourceEndDragSession(GdkD
     }
 
     // Schedule the appropriate drag end dom events.
-    Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
+    Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
 }
 
 static void
@@ -1785,9 +1867,10 @@ invisibleSourceDragEnd(GtkWidget
 gboolean
 nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
                                    GdkDragContext *aDragContext,
+                                   nsWaylandDragContext *aWaylandDragContext,
                                    LayoutDeviceIntPoint aWindowPoint, guint aTime)
 {
-    if (mScheduledTask == eDragTaskMotion) {
+    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
@@ -1798,7 +1881,7 @@ nsDragService::ScheduleMotionEvent(nsWin
 
     // Returning TRUE means we'll reply with a status message, unless we first
     // get a leave.
-    return Schedule(eDragTaskMotion, aWindow, aDragContext,
+    return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext,
                     aWindowPoint, aTime);
 }
 
@@ -1808,7 +1891,8 @@ 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");
     }
 }
@@ -1816,10 +1900,11 @@ nsDragService::ScheduleLeaveEvent()
 gboolean
 nsDragService::ScheduleDropEvent(nsWindow *aWindow,
                                  GdkDragContext *aDragContext,
+                                 nsWaylandDragContext *aWaylandDragContext,
                                  LayoutDeviceIntPoint aWindowPoint, guint aTime)
 {
     if (!Schedule(eDragTaskDrop, aWindow,
-                  aDragContext, aWindowPoint, aTime)) {
+                  aDragContext, aWaylandDragContext, aWindowPoint, aTime)) {
         NS_WARNING("Additional drag drop ignored");
         return FALSE;
     }
@@ -1833,6 +1918,7 @@ nsDragService::ScheduleDropEvent(nsWindo
 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
@@ -1851,6 +1937,9 @@ nsDragService::Schedule(DragTask aTask,
     mScheduledTask = aTask;
     mPendingWindow = aWindow;
     mPendingDragContext = aDragContext;
+#ifdef MOZ_WAYLAND
+    mPendingWaylandDragContext = aWaylandDragContext;
+#endif
     mPendingWindowPoint = aWindowPoint;
     mPendingTime = aTime;
 
@@ -1927,6 +2016,9 @@ 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
@@ -1958,10 +2050,20 @@ 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
           }
         }
     }
@@ -1972,8 +2074,10 @@ 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.
@@ -1986,6 +2090,9 @@ 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
@@ -2015,7 +2122,16 @@ 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)
@@ -2044,6 +2160,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.3.0/widget/gtk/nsDragService.h.wayland thunderbird-60.3.0/widget/gtk/nsDragService.h
--- thunderbird-60.3.0/widget/gtk/nsDragService.h.wayland	2018-10-30 12:45:37.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsDragService.h	2018-11-21 13:42:00.759025657 +0100
@@ -14,6 +14,7 @@
 #include <gtk/gtk.h>
 
 class nsWindow;
+class nsWaylandDragContext;
 
 namespace mozilla {
 namespace gfx {
@@ -98,11 +99,13 @@ public:
 
     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);
 
@@ -158,6 +161,9 @@ private:
     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
@@ -169,9 +175,15 @@ private:
     // 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?
@@ -212,6 +224,7 @@ private:
 
     gboolean Schedule(DragTask aTask, nsWindow *aWindow,
                       GdkDragContext *aDragContext,
+                      nsWaylandDragContext* aPendingWaylandDragContext,
                       mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
 
     // Callback for g_idle_add_full() to run mScheduledTask.
@@ -220,9 +233,11 @@ private:
     void UpdateDragAction();
     void DispatchMotionEvents();
     void ReplyToDragMotion(GdkDragContext* aDragContext);
+#ifdef MOZ_WAYLAND
+    void ReplyToDragMotion(nsWaylandDragContext* aDragContext);
+#endif
     gboolean DispatchDropEvent();
     static uint32_t GetCurrentModifiers();
 };
 
 #endif // nsDragService_h__
-
diff -up thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp.wayland thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp
--- thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp	2018-11-21 13:42:00.760025653 +0100
@@ -28,6 +28,10 @@
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
+#ifdef MOZ_WAYLAND
+#include <sys/mman.h>
+#endif
+
 namespace mozilla {
 namespace widget {
 
@@ -195,7 +199,11 @@ KeymapWrapper::Init()
     memset(mModifierMasks, 0, sizeof(mModifierMasks));
 
     if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
-        InitBySystemSettings();
+        InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+    else
+        InitBySystemSettingsWayland();
+#endif
 
     gdk_window_add_filter(nullptr, FilterEvents, this);
 
@@ -275,10 +283,10 @@ KeymapWrapper::InitXKBExtension()
 }
 
 void
-KeymapWrapper::InitBySystemSettings()
+KeymapWrapper::InitBySystemSettingsX11()
 {
     MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
-        ("%p InitBySystemSettings, mGdkKeymap=%p",
+        ("%p InitBySystemSettingsX11, mGdkKeymap=%p",
          this, mGdkKeymap));
 
     Display* display =
@@ -439,6 +447,208 @@ 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 = wl_seat_get_keyboard(seat);
+        wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
+    } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+        wl_keyboard_destroy(keyboard);
+        keyboard = nullptr;
+    }
+}
+
+static const struct wl_seat_listener seat_listener = {
+      seat_handle_capabilities,
+};
+
+static void
+gdk_registry_handle_global(void               *data,
+                           struct wl_registry *registry,
+                           uint32_t            id,
+                           const char         *interface,
+                           uint32_t            version)
+{
+    if (strcmp(interface, "wl_seat") == 0) {
+        wl_seat *seat =
+            (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
+        wl_seat_add_listener(seat, &seat_listener, data);
+    }
+}
+
+static void
+gdk_registry_handle_global_remove(void               *data,
+                                 struct wl_registry *registry,
+                                 uint32_t            id)
+{
+}
+
+static const struct wl_registry_listener keyboard_registry_listener = {
+    gdk_registry_handle_global,
+    gdk_registry_handle_global_remove
+};
+
+void
+KeymapWrapper::InitBySystemSettingsWayland()
+{
+    // Available as of GTK 3.8+
+    static auto sGdkWaylandDisplayGetWlDisplay =
+        (wl_display *(*)(GdkDisplay *))
+        dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+
+    wl_display *display =
+        sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
+    wl_registry_add_listener(wl_display_get_registry(display),
+                             &keyboard_registry_listener, this);
+
+    // Call wl_display_roundtrip() twice to make sure all
+    // callbacks are processed.
+    wl_display_roundtrip(display);
+    wl_display_roundtrip(display);
+}
+#endif
+
 KeymapWrapper::~KeymapWrapper()
 {
     gdk_window_remove_filter(nullptr, FilterEvents, this);
@@ -1405,6 +1615,14 @@ 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.3.0/widget/gtk/nsGtkKeyUtils.h.wayland thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h
--- thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h	2018-11-21 13:42:00.760025653 +0100
@@ -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 {
@@ -131,6 +135,7 @@ public:
      * @param aKeyEvent         It's an WidgetKeyboardEvent which needs to be
      *                          initialized.
      * @param aGdkKeyEvent      A native GDK key event.
+     * @param aIsProcessedByIME true if aGdkKeyEvent is handled by IME.
      */
     static void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                              GdkEventKey* aGdkKeyEvent);
@@ -148,6 +153,14 @@ public:
     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.
      */
@@ -172,7 +185,10 @@ protected:
      */
     void Init();
     void InitXKBExtension();
-    void InitBySystemSettings();
+    void InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+    void InitBySystemSettingsWayland();
+#endif
 
     /**
      * mModifierKeys stores each hardware key information.
@@ -374,6 +390,15 @@ protected:
      */
     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.3.0/widget/gtk/nsLookAndFeel.cpp.wayland thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp
--- thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp	2018-11-21 13:42:00.760025653 +0100
@@ -31,7 +31,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) \
@@ -182,7 +184,7 @@ GetBorderColors(GtkStyleContext* aContex
         // 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;
     }
@@ -213,6 +215,58 @@ GetBorderColors(GtkStyleContext* aContex
     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()
 {
@@ -269,7 +323,6 @@ nsLookAndFeel::NativeGetColor(ColorID aI
     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;
@@ -279,10 +332,15 @@ nsLookAndFeel::NativeGetColor(ColorID aI
     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;
@@ -1009,6 +1067,9 @@ 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.3.0/widget/gtk/nsLookAndFeel.h.wayland thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h
--- thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h	2018-11-21 13:42:00.760025653 +0100
@@ -77,6 +77,8 @@ protected:
     nscolor mMozWindowActiveBorder;
     nscolor mMozWindowInactiveBorder;
     nscolor mMozWindowInactiveCaption;
+    nscolor mMozCellHighlightBackground;
+    nscolor mMozCellHighlightText;
     nscolor mTextSelectedText;
     nscolor mTextSelectedBackground;
     nscolor mMozScrollbar;
@@ -91,6 +93,9 @@ protected:
     bool    mInitialized;
 
     void EnsureInit();
+
+private:
+    nsresult InitCellHighlightColors();
 };
 
 #endif
diff -up thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp.wayland thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp
--- thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp	2018-11-21 13:45:42.405091067 +0100
@@ -24,7 +24,20 @@
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShell.h"
+#include "nsIGIOService.h"
 #include "WidgetUtils.h"
+#include "nsIObserverService.h"
+
+// for gdk_x11_window_get_xid
+#include <gdk/gdkx.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gio/gunixfdlist.h>
+
+// for dlsym
+#include <dlfcn.h>
+#include "MainThreadUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
@@ -387,7 +400,7 @@ nsPrintDialogWidgetGTK::ExportHeaderFoot
 nsresult
 nsPrintDialogWidgetGTK::ImportSettings(nsIPrintSettings *aNSSettings)
 {
-  NS_PRECONDITION(aNSSettings, "aSettings must not be null");
+  MOZ_ASSERT(aNSSettings, "aSettings must not be null");
   NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
@@ -416,7 +429,7 @@ nsPrintDialogWidgetGTK::ImportSettings(n
 nsresult
 nsPrintDialogWidgetGTK::ExportSettings(nsIPrintSettings *aNSSettings)
 {
-  NS_PRECONDITION(aNSSettings, "aSettings must not be null");
+  MOZ_ASSERT(aNSSettings, "aSettings must not be null");
   NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
 
   GtkPrintSettings* settings = gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
@@ -513,13 +526,521 @@ nsPrintDialogServiceGTK::Init()
   return NS_OK;
 }
 
+// Used to obtain window handle. The portal use this handle
+// to ensure that print dialog is modal.
+typedef void (*WindowHandleExported) (GtkWindow  *window,
+                                      const char *handle,
+                                      gpointer    user_data);
+
+typedef void (*GtkWindowHandleExported) (GtkWindow  *window,
+                                         const char *handle,
+                                         gpointer    user_data);
+#ifdef MOZ_WAYLAND
+typedef struct {
+    GtkWindow *window;
+    WindowHandleExported callback;
+    gpointer user_data;
+} WaylandWindowHandleExportedData;
+
+static void
+wayland_window_handle_exported (GdkWindow  *window,
+                                const char *wayland_handle_str,
+                                gpointer    user_data)
+{
+    WaylandWindowHandleExportedData *data =
+        static_cast<WaylandWindowHandleExportedData*>(user_data);
+    char *handle_str;
+
+    handle_str = g_strdup_printf ("wayland:%s", wayland_handle_str);
+    data->callback (data->window, handle_str, data->user_data);
+    g_free (handle_str);
+}
+#endif
+
+// Get window handle for the portal, taken from gtk/gtkwindow.c
+// (currently not exported)
+static gboolean
+window_export_handle(GtkWindow               *window,
+                     GtkWindowHandleExported  callback,
+                     gpointer                 user_data)
+{
+  if (GDK_IS_X11_DISPLAY(gtk_widget_get_display(GTK_WIDGET(window))))
+    {
+      GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+      char *handle_str;
+      guint32 xid = (guint32) gdk_x11_window_get_xid(gdk_window);
+
+      handle_str = g_strdup_printf("x11:%x", xid);
+      callback(window, handle_str, user_data);
+      g_free(handle_str);
+      return true;
+    }
+#ifdef MOZ_WAYLAND
+  else
+    {
+      GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+      WaylandWindowHandleExportedData *data;
+
+      data = g_new0(WaylandWindowHandleExportedData, 1);
+      data->window = window;
+      data->callback = callback;
+      data->user_data = user_data;
+
+      static auto s_gdk_wayland_window_export_handle =
+        reinterpret_cast<gboolean (*)(GdkWindow*, GdkWaylandWindowExported,
+                                      gpointer, GDestroyNotify)>
+        (dlsym(RTLD_DEFAULT, "gdk_wayland_window_export_handle"));
+      if (!s_gdk_wayland_window_export_handle ||
+          !s_gdk_wayland_window_export_handle(gdk_window,
+                                              wayland_window_handle_exported,
+                                              data, g_free)) {
+          g_free (data);
+          return false;
+        } else  {
+          return true;
+        }
+    }
+#endif
+
+  g_warning("Couldn't export handle, unsupported windowing system");
+
+  return false;
+}
+/**
+ * Communication class with the GTK print portal handler
+ *
+ * To print document from flatpak we need to use print portal because
+ * printers are not directly accessible in the sandboxed environment.
+ *
+ * At first we request portal to show the print dialog to let user choose
+ * printer settings. We use DBUS interface for that (PreparePrint method).
+ *
+ * Next we force application to print to temporary file and after the writing
+ * to the file is finished we pass its file descriptor to the portal.
+ * Portal will pass duplicate of the file descriptor to the printer which
+ * user selected before (by DBUS Print method).
+ *
+ * Since DBUS communication is done async while nsPrintDialogServiceGTK::Show
+ * is expecting sync execution, we need to create a new GMainLoop during the
+ * print portal dialog is running. The loop is stopped after the dialog
+ * is closed.
+ */
+class nsFlatpakPrintPortal: public nsIObserver
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  public:
+    explicit nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings);
+    nsresult PreparePrintRequest(GtkWindow* aWindow);
+    static void OnWindowExportHandleDone(GtkWindow *aWindow,
+                                         const char* aWindowHandleStr,
+                                         gpointer aUserData);
+    void PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr);
+    static void OnPreparePrintResponse(GDBusConnection *connection,
+                                       const char      *sender_name,
+                                       const char      *object_path,
+                                       const char      *interface_name,
+                                       const char      *signal_name,
+                                       GVariant        *parameters,
+                                       gpointer         data);
+    GtkPrintOperationResult GetResult();
+  private:
+    virtual ~nsFlatpakPrintPortal();
+    void FinishPrintDialog(GVariant* parameters);
+    nsCOMPtr<nsPrintSettingsGTK> mPrintAndPageSettings;
+    GDBusProxy* mProxy;
+    guint32 mToken;
+    GMainLoop* mLoop;
+    GtkPrintOperationResult mResult;
+    guint mResponseSignalId;
+    GtkWindow* mParentWindow;
+};
+
+NS_IMPL_ISUPPORTS(nsFlatpakPrintPortal, nsIObserver)
+
+nsFlatpakPrintPortal::nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings):
+  mPrintAndPageSettings(aPrintSettings),
+  mProxy(nullptr),
+  mLoop(nullptr),
+  mResponseSignalId(0),
+  mParentWindow(nullptr)
+{
+}
+
+/**
+ * Creates GDBusProxy, query for window handle and create a new GMainLoop.
+ *
+ * The GMainLoop is to be run from GetResult() and be quitted during
+ * FinishPrintDialog.
+ *
+ * @param aWindow toplevel application window which is used as parent of print
+ *                dialog
+ */
+nsresult
+nsFlatpakPrintPortal::PreparePrintRequest(GtkWindow* aWindow)
+{
+  MOZ_ASSERT(aWindow, "aWindow must not be null");
+  MOZ_ASSERT(mPrintAndPageSettings, "mPrintAndPageSettings must not be null");
+
+  GError* error = nullptr;
+  mProxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+      G_DBUS_PROXY_FLAGS_NONE,
+      nullptr,
+      "org.freedesktop.portal.Desktop",
+      "/org/freedesktop/portal/desktop",
+      "org.freedesktop.portal.Print",
+      nullptr,
+      &error);
+  if (mProxy == nullptr) {
+    NS_WARNING(nsPrintfCString("Unable to create dbus proxy: %s", error->message).get());
+    g_error_free(error);
+    return NS_ERROR_FAILURE;
+  }
+
+  // The window handler is returned async, we will continue by PreparePrint method
+  // when it is returned.
+  if (!window_export_handle(aWindow,
+        &nsFlatpakPrintPortal::OnWindowExportHandleDone, this)) {
+    NS_WARNING("Unable to get window handle for creating modal print dialog.");
+    return NS_ERROR_FAILURE;
+  }
+
+  mLoop = g_main_loop_new (NULL, FALSE);
+  return NS_OK;
+}
+
+void
+nsFlatpakPrintPortal::OnWindowExportHandleDone(GtkWindow* aWindow,
+                                               const char* aWindowHandleStr,
+                                               gpointer aUserData)
+{
+  nsFlatpakPrintPortal* printPortal = static_cast<nsFlatpakPrintPortal*>(aUserData);
+  printPortal->PreparePrint(aWindow, aWindowHandleStr);
+}
+
+/**
+ * Ask print portal to show the print dialog.
+ *
+ * Print and page settings and window handle are passed to the portal to prefill
+ * last used settings.
+ */
+void
+nsFlatpakPrintPortal::PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr)
+{
+  GtkPrintSettings* gtkSettings = mPrintAndPageSettings->GetGtkPrintSettings();
+  GtkPageSetup* pageSetup = mPrintAndPageSettings->GetGtkPageSetup();
+
+  // We need to remember GtkWindow to unexport window handle after it is
+  // no longer needed by the portal dialog (apply only on non-X11 sessions).
+  if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+    mParentWindow = aWindow;
+  }
+
+  GVariantBuilder opt_builder;
+  g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT);
+  char* token = g_strdup_printf("mozilla%d", g_random_int_range (0, G_MAXINT));
+  g_variant_builder_add(&opt_builder, "{sv}", "handle_token",
+      g_variant_new_string(token));
+  g_free(token);
+  GVariant* options = g_variant_builder_end(&opt_builder);
+  static auto s_gtk_print_settings_to_gvariant =
+    reinterpret_cast<GVariant* (*)(GtkPrintSettings*)>
+    (dlsym(RTLD_DEFAULT, "gtk_print_settings_to_gvariant"));
+  static auto s_gtk_page_setup_to_gvariant =
+    reinterpret_cast<GVariant* (*)(GtkPageSetup *)>
+    (dlsym(RTLD_DEFAULT, "gtk_page_setup_to_gvariant"));
+  if (!s_gtk_print_settings_to_gvariant || !s_gtk_page_setup_to_gvariant) {
+    mResult = GTK_PRINT_OPERATION_RESULT_ERROR;
+    FinishPrintDialog(nullptr);
+    return;
+  }
+
+  // Get translated window title
+  nsCOMPtr<nsIStringBundleService> bundleSvc =
+       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+  nsCOMPtr<nsIStringBundle> printBundle;
+  bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
+      getter_AddRefs(printBundle));
+  nsAutoString intlPrintTitle;
+  printBundle->GetStringFromName("printTitleGTK", intlPrintTitle);
+
+  GError* error = nullptr;
+  GVariant *ret = g_dbus_proxy_call_sync(mProxy,
+      "PreparePrint",
+      g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
+        aWindowHandleStr,
+        NS_ConvertUTF16toUTF8(intlPrintTitle).get(), // Title of the window
+        s_gtk_print_settings_to_gvariant(gtkSettings),
+        s_gtk_page_setup_to_gvariant(pageSetup),
+        options),
+      G_DBUS_CALL_FLAGS_NONE,
+      -1,
+      nullptr,
+      &error);
+  if (ret == nullptr) {
+    NS_WARNING(nsPrintfCString("Unable to call dbus proxy: %s", error->message).get());
+    g_error_free (error);
+    mResult = GTK_PRINT_OPERATION_RESULT_ERROR;
+    FinishPrintDialog(nullptr);
+    return;
+  }
+
+  const char* handle = nullptr;
+  g_variant_get (ret, "(&o)", &handle);
+  if (strcmp (aWindowHandleStr, handle) != 0)
+  {
+    aWindowHandleStr = g_strdup (handle);
+    if (mResponseSignalId) {
+      g_dbus_connection_signal_unsubscribe(
+          g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId);
+    }
+  }
+  mResponseSignalId =
+    g_dbus_connection_signal_subscribe(
+        g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)),
+        "org.freedesktop.portal.Desktop",
+        "org.freedesktop.portal.Request",
+        "Response",
+        aWindowHandleStr,
+        NULL,
+        G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+        &nsFlatpakPrintPortal::OnPreparePrintResponse,
+        this, NULL);
+
+}
+
+void
+nsFlatpakPrintPortal::OnPreparePrintResponse(GDBusConnection *connection,
+                                             const char      *sender_name,
+                                             const char      *object_path,
+                                             const char      *interface_name,
+                                             const char      *signal_name,
+                                             GVariant        *parameters,
+                                             gpointer         data)
+{
+  nsFlatpakPrintPortal* printPortal = static_cast<nsFlatpakPrintPortal*>(data);
+  printPortal->FinishPrintDialog(parameters);
+}
+
+/**
+ * When the dialog is accepted, read print and page settings and token.
+ *
+ * Token is later used for printing portal as print operation identifier.
+ * Print and page settings are modified in-place and stored to
+ * mPrintAndPageSettings.
+ */
+void
+nsFlatpakPrintPortal::FinishPrintDialog(GVariant* parameters)
+{
+  // This ends GetResult() method
+  if (mLoop) {
+    g_main_loop_quit (mLoop);
+    mLoop = nullptr;
+  }
+
+  if (!parameters) {
+    // mResult should be already defined
+    return;
+  }
+
+  guint32 response;
+  GVariant *options;
+
+  g_variant_get (parameters, "(u@a{sv})", &response, &options);
+  mResult = GTK_PRINT_OPERATION_RESULT_CANCEL;
+  if (response == 0) {
+    GVariant *v;
+
+    char *filename;
+    char *uri;
+    v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
+    static auto s_gtk_print_settings_new_from_gvariant =
+      reinterpret_cast<GtkPrintSettings* (*)(GVariant*)>
+      (dlsym(RTLD_DEFAULT, "gtk_print_settings_new_from_gvariant"));
+
+    GtkPrintSettings* printSettings = s_gtk_print_settings_new_from_gvariant(v);
+    g_variant_unref (v);
+
+    v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT);
+    static auto s_gtk_page_setup_new_from_gvariant =
+      reinterpret_cast<GtkPageSetup* (*)(GVariant*)>
+      (dlsym(RTLD_DEFAULT, "gtk_page_setup_new_from_gvariant"));
+    GtkPageSetup* pageSetup = s_gtk_page_setup_new_from_gvariant(v);
+    g_variant_unref (v);
+
+    g_variant_lookup (options, "token", "u", &mToken);
+
+    // Force printing to file because only filedescriptor of the file
+    // can be passed to portal
+    int fd = g_file_open_tmp("gtkprintXXXXXX", &filename, NULL);
+    uri = g_filename_to_uri(filename, NULL, NULL);
+    gtk_print_settings_set(printSettings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
+    g_free (uri);
+    close (fd);
+
+    // Save native settings in the session object
+    mPrintAndPageSettings->SetGtkPrintSettings(printSettings);
+    mPrintAndPageSettings->SetGtkPageSetup(pageSetup);
+
+    // Portal consumes PDF file
+    mPrintAndPageSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatPDF);
+
+    // We need to set to print to file
+    mPrintAndPageSettings->SetPrintToFile(true);
+
+    mResult = GTK_PRINT_OPERATION_RESULT_APPLY;
+  }
+}
+
+/**
+ * Get result of the print dialog.
+ *
+ * This call blocks until FinishPrintDialog is called.
+ *
+ */
+GtkPrintOperationResult
+nsFlatpakPrintPortal::GetResult() {
+  // If the mLoop has not been initialized we haven't go thru PreparePrint method
+  if (!NS_IsMainThread() || !mLoop) {
+    return GTK_PRINT_OPERATION_RESULT_ERROR;
+  }
+  // Calling g_main_loop_run stops current code until g_main_loop_quit is called
+  g_main_loop_run(mLoop);
+
+  // Free resources we've allocated in order to show print dialog.
+#ifdef MOZ_WAYLAND
+  if (mParentWindow) {
+    GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(mParentWindow));
+    static auto s_gdk_wayland_window_unexport_handle =
+      reinterpret_cast<void (*)(GdkWindow*)>
+      (dlsym(RTLD_DEFAULT, "gdk_wayland_window_unexport_handle"));
+    if (s_gdk_wayland_window_unexport_handle) {
+      s_gdk_wayland_window_unexport_handle(gdk_window);
+    }
+  }
+#endif
+  return mResult;
+}
+
+/**
+ * Send file descriptor of the file which contains document to the portal to
+ * finish the print operation.
+ */
+NS_IMETHODIMP
+nsFlatpakPrintPortal::Observe(nsISupports *aObject, const char * aTopic,
+                              const char16_t * aData)
+{
+  // Check that written file match to the stored filename in case multiple
+  // print operations are in progress.
+  nsAutoString filenameStr;
+  mPrintAndPageSettings->GetToFileName(filenameStr);
+  if (!nsDependentString(aData).Equals(filenameStr)) {
+    // Different file is finished, not for this instance
+    return NS_OK;
+  }
+  int fd, idx;
+  fd = open(NS_ConvertUTF16toUTF8(filenameStr).get(), O_RDONLY|O_CLOEXEC);
+  static auto s_g_unix_fd_list_new =
+    reinterpret_cast<GUnixFDList* (*)(void)>
+    (dlsym(RTLD_DEFAULT, "g_unix_fd_list_new"));
+  NS_ASSERTION(s_g_unix_fd_list_new, "Cannot find g_unix_fd_list_new function.");
+
+  GUnixFDList *fd_list = s_g_unix_fd_list_new();
+  static auto s_g_unix_fd_list_append =
+    reinterpret_cast<gint (*)(GUnixFDList*, gint, GError**)>
+    (dlsym(RTLD_DEFAULT, "g_unix_fd_list_append"));
+  idx = s_g_unix_fd_list_append(fd_list, fd, NULL);
+  close(fd);
+
+  GVariantBuilder opt_builder;
+  g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add(&opt_builder, "{sv}",  "token",
+      g_variant_new_uint32(mToken));
+  g_dbus_proxy_call_with_unix_fd_list(
+      mProxy,
+      "Print",
+      g_variant_new("(ssh@a{sv})",
+                     "", /* window */
+                     "Print", /* title */
+                     idx,
+                     g_variant_builder_end(&opt_builder)),
+      G_DBUS_CALL_FLAGS_NONE,
+      -1,
+      fd_list,
+      NULL,
+      NULL, // TODO portal result cb function
+      nullptr); // data
+  g_object_unref(fd_list);
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  // Let the nsFlatpakPrintPortal instance die
+  os->RemoveObserver(this, "print-to-file-finished");
+  return NS_OK;
+}
+
+nsFlatpakPrintPortal::~nsFlatpakPrintPortal() {
+  if (mProxy) {
+    if (mResponseSignalId) {
+      g_dbus_connection_signal_unsubscribe(
+          g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId);
+    }
+    g_object_unref(mProxy);
+  }
+  if (mLoop)
+    g_main_loop_quit(mLoop);
+}
+
 NS_IMETHODIMP
 nsPrintDialogServiceGTK::Show(nsPIDOMWindowOuter *aParent,
                               nsIPrintSettings *aSettings,
                               nsIWebBrowserPrint *aWebBrowserPrint)
 {
-  NS_PRECONDITION(aParent, "aParent must not be null");
-  NS_PRECONDITION(aSettings, "aSettings must not be null");
+  MOZ_ASSERT(aParent, "aParent must not be null");
+  MOZ_ASSERT(aSettings, "aSettings must not be null");
+
+  // Check for the flatpak portal first
+  nsCOMPtr<nsIGIOService> giovfs =
+    do_GetService(NS_GIOSERVICE_CONTRACTID);
+  bool shouldUsePortal = false;
+  if (shouldUsePortal && gtk_check_version(3, 22, 0) == nullptr) {
+    nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
+    NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
+    GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
+    NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
+
+
+    nsCOMPtr<nsPrintSettingsGTK> printSettingsGTK(do_QueryInterface(aSettings));
+    RefPtr<nsFlatpakPrintPortal> fpPrintPortal =
+      new nsFlatpakPrintPortal(printSettingsGTK);
+
+    nsresult rv = fpPrintPortal->PreparePrintRequest(gtkParent);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // This blocks until nsFlatpakPrintPortal::FinishPrintDialog is called
+    GtkPrintOperationResult printDialogResult = fpPrintPortal->GetResult();
+
+    rv = NS_OK;
+    switch (printDialogResult) {
+      case GTK_PRINT_OPERATION_RESULT_APPLY:
+        {
+          nsCOMPtr<nsIObserver> observer = do_QueryInterface(fpPrintPortal);
+          nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+          NS_ENSURE_STATE(os);
+          // Observer waits until notified that the file with the content
+          // to print has been written.
+          rv = os->AddObserver(observer, "print-to-file-finished", false);
+          NS_ENSURE_SUCCESS(rv, rv);
+          break;
+        }
+      case GTK_PRINT_OPERATION_RESULT_CANCEL:
+        rv = NS_ERROR_ABORT;
+        break;
+      default:
+        NS_WARNING("Unexpected response");
+        rv = NS_ERROR_ABORT;
+    }
+    return rv;
+  }
 
   nsPrintDialogWidgetGTK printDialog(aParent, aSettings);
   nsresult rv = printDialog.ImportSettings(aSettings);
@@ -553,8 +1074,8 @@ NS_IMETHODIMP
 nsPrintDialogServiceGTK::ShowPageSetup(nsPIDOMWindowOuter *aParent,
                                        nsIPrintSettings *aNSSettings)
 {
-  NS_PRECONDITION(aParent, "aParent must not be null");
-  NS_PRECONDITION(aNSSettings, "aSettings must not be null");
+  MOZ_ASSERT(aParent, "aParent must not be null");
+  MOZ_ASSERT(aNSSettings, "aSettings must not be null");
   NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
diff -up thunderbird-60.3.0/widget/gtk/nsWindow.cpp.wayland thunderbird-60.3.0/widget/gtk/nsWindow.cpp
--- thunderbird-60.3.0/widget/gtk/nsWindow.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsWindow.cpp	2018-11-21 13:42:00.762025644 +0100
@@ -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,7 +26,7 @@
 #include "prlink.h"
 #include "nsGTKToolkit.h"
 #include "nsIRollupListener.h"
-#include "nsIDOMNode.h"
+#include "nsINode.h"
 
 #include "nsWidgetsCID.h"
 #include "nsDragService.h"
@@ -56,6 +57,7 @@
 
 #if defined(MOZ_WAYLAND)
 #include <gdk/gdkwayland.h>
+#include "nsView.h"
 #endif
 
 #include "nsGkAtoms.h"
@@ -116,6 +118,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 +132,6 @@ using namespace mozilla::widget;
 #include "nsShmImage.h"
 #include "gtkdrawing.h"
 
-#include "nsIDOMWheelEvent.h"
-
 #include "NativeKeyBindings.h"
 
 #include <dlfcn.h>
@@ -140,6 +141,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
@@ -155,7 +157,8 @@ const gint kEvents = GDK_EXPOSURE_MASK |
 #endif
                      GDK_SCROLL_MASK |
                      GDK_POINTER_MOTION_MASK |
-                     GDK_PROPERTY_CHANGE_MASK;
+                     GDK_PROPERTY_CHANGE_MASK |
+                     GDK_FOCUS_CHANGE_MASK;
 
 /* utility functions */
 static bool       is_mouse_in_window(GdkWindow* aWindow,
@@ -210,7 +213,7 @@ static void     hierarchy_changed_cb
                                            GtkWidget *previous_toplevel);
 static gboolean window_state_event_cb     (GtkWidget *widget,
                                            GdkEventWindowState *event);
-static void     theme_changed_cb          (GtkSettings *settings,
+static void     settings_changed_cb       (GtkSettings *settings,
                                            GParamSpec *pspec,
                                            nsWindow *data);
 static void     check_resize_cb           (GtkContainer* container,
@@ -481,6 +484,8 @@ nsWindow::nsWindow()
     mPendingConfigures = 0;
     mCSDSupportLevel = CSD_SUPPORT_NONE;
     mDrawInTitlebar = false;
+
+    mHasAlphaVisual = false;
 }
 
 nsWindow::~nsWindow()
@@ -630,7 +635,7 @@ EnsureInvisibleContainer()
 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.
@@ -731,7 +736,7 @@ nsWindow::Destroy()
     ClearCachedResources();
 
     g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
-                                         FuncToGpointer(theme_changed_cb),
+                                         FuncToGpointer(settings_changed_cb),
                                          this);
 
     nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
@@ -841,8 +846,14 @@ nsWindow::GetDesktopToDeviceScale()
 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;
     }
 
@@ -886,7 +897,7 @@ nsWindow::WidgetTypeSupportsAcceleration
 void
 nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
 {
-    NS_PRECONDITION(aNewParent, "");
+    MOZ_ASSERT(aNewParent, "null widget");
     NS_ASSERTION(!mIsDestroyed, "");
     NS_ASSERTION(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
 
@@ -1517,7 +1528,7 @@ nsWindow::GetClientBounds()
 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) {
@@ -2057,6 +2068,12 @@ nsWindow::OnExposeEvent(cairo_t *cr)
     if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel)
         return FALSE;
 
+#ifdef MOZ_WAYLAND
+    // Window does not have visible wl_surface yet.
+    if (!mIsX11Display && !GetWaylandSurface())
+        return FALSE;
+#endif
+
     nsIWidgetListener *listener = GetListener();
     if (!listener)
         return FALSE;
@@ -3318,6 +3335,33 @@ nsWindow::OnWindowStateEvent(GtkWidget *
     }
     // 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
@@ -3404,6 +3448,7 @@ nsWindow::OnDPIChanged()
       // Update menu's font size etc
       presShell->ThemeChanged();
     }
+    mWidgetListener->UIResolutionChanged();
   }
 }
 
@@ -3611,6 +3656,7 @@ nsWindow::Create(nsIWidget* aParent,
     nsWindow       *parentnsWindow = nullptr;
     GtkWidget      *eventWidget = nullptr;
     bool            drawToContainer = false;
+    bool            useAlphaVisual = false;
 
     if (aParent) {
         parentnsWindow = static_cast<nsWindow*>(aParent);
@@ -3661,8 +3707,8 @@ nsWindow::Create(nsIWidget* aParent,
         }
         mShell = gtk_window_new(type);
 
-        bool useAlphaVisual = (mWindowType == eWindowType_popup &&
-                               aInitData->mSupportTranslucency);
+        useAlphaVisual = (mWindowType == eWindowType_popup &&
+                          aInitData->mSupportTranslucency);
 
         // mozilla.widget.use-argb-visuals is a hidden pref defaulting to false
         // to allow experimentation
@@ -3784,7 +3830,7 @@ nsWindow::Create(nsIWidget* aParent,
         // it explicitly now.
         gtk_widget_realize(mShell);
 
-        /* There are two cases here:
+        /* There are several cases here:
          *
          * 1) We're running on Gtk+ without client side decorations.
          *    Content is rendered to mShell window and we listen
@@ -3859,17 +3905,7 @@ nsWindow::Create(nsIWidget* aParent,
         // If the window were to get unredirected, there could be visible
         // tearing because Gecko does not align its framebuffer updates with
         // vblank.
-        if (mIsX11Display) {
-            gulong value = 2; // Opt out of unredirection
-            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);
-        }
+        SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
 #endif
     }
         break;
@@ -3949,10 +3985,13 @@ nsWindow::Create(nsIWidget* aParent,
         GtkSettings* default_settings = gtk_settings_get_default();
         g_signal_connect_after(default_settings,
                                "notify::gtk-theme-name",
-                               G_CALLBACK(theme_changed_cb), this);
+                               G_CALLBACK(settings_changed_cb), this);
         g_signal_connect_after(default_settings,
                                "notify::gtk-font-name",
-                               G_CALLBACK(theme_changed_cb), this);
+                               G_CALLBACK(settings_changed_cb), this);
+        g_signal_connect_after(default_settings,
+                               "notify::gtk-enable-animations",
+                               G_CALLBACK(settings_changed_cb), this);
     }
 
     if (mContainer) {
@@ -4083,60 +4122,70 @@ nsWindow::Create(nsIWidget* aParent,
 }
 
 void
-nsWindow::SetWindowClass(const nsAString &xulWinType)
+nsWindow::RefreshWindowClass(void)
 {
-  if (!mShell)
-    return;
+    if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty())
+        return;
 
-  const char *res_class = gdk_get_program_class();
-  if (!res_class)
-    return;
+    GdkWindow* gdkWindow = gtk_widget_get_window(mShell);
+    gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get());
 
-  char *res_name = ToNewCString(xulWinType);
-  if (!res_name)
-    return;
+#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 */
+}
 
-  const char *role = nullptr;
+void
+nsWindow::SetWindowClass(const nsAString &xulWinType)
+{
+    if (!mShell)
+      return;
 
-  // Parse res_name into a name and role. Characters other than
-  // [A-Za-z0-9_-] are converted to '_'. Anything after the first
-  // colon is assigned to role; if there's no colon, assign the
-  // whole thing to both role and res_name.
-  for (char *c = res_name; *c; c++) {
-    if (':' == *c) {
-      *c = 0;
-      role = c + 1;
-    }
-    else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
-      *c = '_';
-  }
-  res_name[0] = toupper(res_name[0]);
-  if (!role) role = res_name;
+    char *res_name = ToNewCString(xulWinType);
+    if (!res_name)
+      return;
 
-  GdkWindow* gdkWindow = gtk_widget_get_window(mShell);
-  gdk_window_set_role(gdkWindow, role);
+    const char *role = nullptr;
 
-#ifdef MOZ_X11
-  if (mIsX11Display) {
-      XClassHint *class_hint = XAllocClassHint();
-      if (!class_hint) {
-        free(res_name);
-        return;
+    // Parse res_name into a name and role. Characters other than
+    // [A-Za-z0-9_-] are converted to '_'. Anything after the first
+    // colon is assigned to role; if there's no colon, assign the
+    // whole thing to both role and res_name.
+    for (char *c = res_name; *c; c++) {
+      if (':' == *c) {
+        *c = 0;
+        role = c + 1;
       }
-      class_hint->res_name = res_name;
-      class_hint->res_class = const_cast<char*>(res_class);
+      else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
+        *c = '_';
+    }
+    res_name[0] = toupper(res_name[0]);
+    if (!role) role = res_name;
 
-      // 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);
 
-  free(res_name);
+    RefreshWindowClass();
 }
 
 void
@@ -4162,6 +4211,8 @@ nsWindow::NativeResize()
          size.width, size.height));
 
     if (mIsTopLevel) {
+        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) {
@@ -4207,6 +4258,8 @@ nsWindow::NativeMoveResize()
             NativeShow(false);
         }
         NativeMove();
+
+        return;
     }
 
     GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
@@ -4219,6 +4272,8 @@ 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) {
@@ -4271,6 +4326,16 @@ nsWindow::NativeShow(bool aAction)
         }
     }
     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
@@ -4946,6 +5011,8 @@ FullscreenTransitionWindow::FullscreenTr
     gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect);
     gtk_window_set_screen(gtkWin, screen);
     gtk_window_move(gtkWin, monitorRect.x, monitorRect.y);
+    MOZ_ASSERT(monitorRect.width > 0 && monitorRect.height > 0,
+               "Can't resize window smaller than 1x1.");
     gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height);
 
     GdkColor bgColor;
@@ -5951,7 +6018,7 @@ window_state_event_cb (GtkWidget *widget
 }
 
 static void
-theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
+settings_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
 {
     RefPtr<nsWindow> window = data;
     window->ThemeChanged();
@@ -6032,13 +6099,13 @@ nsWindow::InitDragEvent(WidgetDragEvent
     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)
@@ -6063,15 +6130,24 @@ drag_motion_event_cb(GtkWidget *aWidget,
 
     RefPtr<nsDragService> dragService = nsDragService::GetInstance();
     return dragService->
-        ScheduleMotionEvent(innerMostWindow, aDragContext,
+        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)
@@ -6104,14 +6180,22 @@ drag_leave_event_cb(GtkWidget *aWidget,
     dragService->ScheduleLeaveEvent();
 }
 
+static void
+drag_leave_event_cb(GtkWidget *aWidget,
+                    GdkDragContext *aDragContext,
+                    guint aTime,
+                    gpointer aData)
+{
+    WindowDragLeaveHandler(aWidget);
+}
 
-static gboolean
-drag_drop_event_cb(GtkWidget *aWidget,
-                   GdkDragContext *aDragContext,
-                   gint aX,
-                   gint aY,
-                   guint aTime,
-                   gpointer aData)
+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)
@@ -6136,10 +6220,21 @@ drag_drop_event_cb(GtkWidget *aWidget,
 
     RefPtr<nsDragService> dragService = nsDragService::GetInstance();
     return dragService->
-        ScheduleDropEvent(innerMostWindow, aDragContext,
+        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,
                             GdkDragContext *aDragContext,
@@ -6554,12 +6649,6 @@ nsWindow::GetLayerManager(PLayerTransact
       return mLayerManager;
     }
 
-    if (!mLayerManager && !IsComposited() &&
-        eTransparencyTransparent == GetTransparencyMode())
-    {
-        mLayerManager = CreateBasicLayerManager();
-    }
-
     return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence);
 }
 
@@ -6601,6 +6690,13 @@ nsWindow::ClearCachedResources()
 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.
@@ -6692,6 +6788,15 @@ nsWindow::SetDrawsInTitlebar(bool aState
         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.
@@ -6998,6 +7103,8 @@ nsWindow::GetSystemCSDSupportLevel() {
         // 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) {
@@ -7014,6 +7121,8 @@ nsWindow::GetSystemCSDSupportLevel() {
             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.
@@ -7066,26 +7175,18 @@ nsWindow::RoundsWidgetCoordinatesTo()
 
 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()
@@ -7110,3 +7211,85 @@ nsWindow::GetWaylandSurface()
   return nullptr;
 }
 #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) {
+        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.3.0/widget/gtk/nsWindow.h.wayland thunderbird-60.3.0/widget/gtk/nsWindow.h
--- thunderbird-60.3.0/widget/gtk/nsWindow.h.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsWindow.h	2018-11-21 13:42:00.762025644 +0100
@@ -66,6 +66,21 @@ 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 {
@@ -78,6 +93,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;
 
     nsWindow();
@@ -115,8 +131,7 @@ public:
                                          int32_t *aX,
                                          int32_t *aY) override;
     virtual void       SetSizeConstraints(const SizeConstraints& aConstraints) override;
-    virtual void       Move(double aX,
-                            double aY) override;
+    virtual void       Move(double aX, double aY) override;
     virtual void       Show             (bool aState) override;
     virtual void       Resize           (double aWidth,
                                          double aHeight,
@@ -228,6 +243,8 @@ public:
     virtual void       EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget,
                                                 LayoutDeviceIntRegion& aInvalidRegion) override;
 
+    void               SetProgress(unsigned long progressPercent);
+
 private:
     void               UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface, nsIntRect aBoundsRect);
 
@@ -452,13 +469,24 @@ private:
                                    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;
@@ -558,6 +586,9 @@ private:
     // full translucency at this time; each pixel is either fully opaque
     // or fully transparent.
     gchar*       mTransparencyBitmap;
+    // True when we're on compositing window manager and this
+    // window is using visual with alpha channel.
+    bool         mHasAlphaVisual;
 
     // all of our DND stuff
     void   InitDragEvent(mozilla::WidgetDragEvent& aEvent);
diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h
--- thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h.wayland	2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h	2018-11-21 13:42:00.762025644 +0100
@@ -17,6 +17,7 @@
 #include <gdk/gdkwayland.h>
 #endif
 #include <X11/Xlib.h> // for Window, Display, Visual, etc.
+#include "X11UndefineNone.h"
 
 class nsWindow;
 
diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp
--- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp	2018-11-21 13:42:00.763025640 +0100
@@ -151,8 +151,9 @@ static nsWaylandDisplay* WaylandDisplayG
 static void WaylandDisplayRelease(wl_display *aDisplay);
 static void WaylandDisplayLoop(wl_display *aDisplay);
 
-// TODO: is the 60pfs loop correct?
-#define EVENT_LOOP_DELAY (1000/60)
+// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with
+// compositor event loop.
+#define EVENT_LOOP_DELAY (1000/240)
 
 // Get WaylandDisplay for given wl_display and actual calling thread.
 static nsWaylandDisplay*
@@ -304,6 +305,7 @@ nsWaylandDisplay::nsWaylandDisplay(wl_di
   : mThreadId(PR_GetCurrentThread())
   // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format
   // and is always present.
+  // TODO: Provide also format without alpha (Bug 1470126).
   , mFormat(gfx::SurfaceFormat::B8G8R8A8)
   , mShm(nullptr)
   , mDisplay(aDisplay)
@@ -522,7 +524,7 @@ WindowBackBuffer::Detach()
 }
 
 bool
-WindowBackBuffer::SetImageDataFromBackBuffer(
+WindowBackBuffer::SetImageDataFromBuffer(
   class WindowBackBuffer* aSourceBuffer)
 {
   if (!IsMatchingSize(aSourceBuffer)) {
@@ -535,11 +537,9 @@ WindowBackBuffer::SetImageDataFromBackBu
 }
 
 already_AddRefed<gfx::DrawTarget>
-WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion)
+WindowBackBuffer::Lock()
 {
-  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
-  gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
-
+  gfx::IntSize lockSize(mWidth, mHeight);
   return gfxPlatform::CreateDrawTargetForData(static_cast<unsigned char*>(mShmPool.GetImageData()),
                                               lockSize,
                                               BUFFER_BPP * mWidth,
@@ -560,26 +560,40 @@ static const struct wl_callback_listener
 WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
   : mWindow(aWindow)
   , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay()))
-  , mFrontBuffer(nullptr)
-  , mBackBuffer(nullptr)
+  , mWaylandBuffer(nullptr)
+  , mBackupBuffer(nullptr)
   , mFrameCallback(nullptr)
-  , mFrameCallbackSurface(nullptr)
+  , mLastCommittedSurface(nullptr)
   , mDisplayThreadMessageLoop(MessageLoop::current())
-  , mDelayedCommit(false)
-  , mFullScreenDamage(false)
+  , mDelayedCommitHandle(nullptr)
+  , mDrawToWaylandBufferDirectly(true)
+  , mPendingCommit(false)
+  , mWaylandBufferFullScreenDamage(false)
   , mIsMainThread(NS_IsMainThread())
+  , mNeedScaleFactorUpdate(true)
 {
 }
 
 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;
+  delete mBackupBuffer;
+
   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
@@ -593,162 +607,309 @@ WindowSurfaceWayland::~WindowSurfaceWayl
   }
 }
 
-void
-WindowSurfaceWayland::UpdateScaleFactor()
-{
-  wl_surface* waylandSurface = mWindow->GetWaylandSurface();
-  if (waylandSurface) {
-    wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
-  }
-}
-
 WindowBackBuffer*
-WindowSurfaceWayland::GetBufferToDraw(int aWidth, int aHeight)
+WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight)
 {
-  if (!mFrontBuffer) {
-    mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
-    mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
-    return mFrontBuffer;
+  if (!mWaylandBuffer) {
+    mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+    mBackupBuffer = 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 mFrontBuffer;
+    return mWaylandBuffer;
   }
 
   // Front buffer is used by compositor, draw to back buffer
-  if (mBackBuffer->IsAttached()) {
+  if (mBackupBuffer->IsAttached()) {
     NS_WARNING("No drawing buffer available");
     return nullptr;
   }
 
-  MOZ_ASSERT(!mDelayedCommit,
+  MOZ_ASSERT(!mPendingCommit,
              "Uncommitted buffer switch, screen artifacts ahead.");
 
-  WindowBackBuffer *tmp = mFrontBuffer;
-  mFrontBuffer = mBackBuffer;
-  mBackBuffer = tmp;
+  WindowBackBuffer *tmp = mWaylandBuffer;
+  mWaylandBuffer = mBackupBuffer;
+  mBackupBuffer = tmp;
 
-  if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) {
+  if (mBackupBuffer->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(mBackupBuffer);
     // 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 mWaylandBuffer;
+}
+
+already_AddRefed<gfx::DrawTarget>
+WindowSurfaceWayland::LockWaylandBuffer(int aWidth, int aHeight)
+{
+  WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight);
+  if (buffer) {
+    return buffer->Lock();
   }
 
-  return mFrontBuffer;
+  NS_WARNING("WindowSurfaceWayland::LockWaylandBuffer(): No buffer available");
+  return nullptr;
 }
 
 already_AddRefed<gfx::DrawTarget>
+WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize)
+{
+  if (!mImageSurface || mImageSurface->CairoStatus() ||
+      !(aLockSize <= mImageSurface->GetSize())) {
+    mImageSurface = new gfxImageSurface(aLockSize,
+        SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat()));
+    if (mImageSurface->CairoStatus()) {
+      return nullptr;
+    }
+  }
+
+  return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(),
+                                              mImageSurface->GetSize(),
+                                              mImageSurface->Stride(),
+                                              mWaylandDisplay->GetSurfaceFormat());
+}
+
+/*
+  There are some situations which can happen here:
+
+  A) Lock() is called to whole surface. In that case we don't need
+     to clip/buffer the drawing and we can return wl_buffer directly
+     for drawing.
+       - 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());
 
-  // 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);
-  if (!buffer) {
-    NS_WARNING("No drawing buffer available");
-    return nullptr;
+  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);
+    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);
+  RefPtr<gfx::SourceSurface> surf =
+    gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(),
+                                                     mImageSurface->GetSize(),
+                                                     mImageSurface->Format());
+  if (!dt || !surf) {
+    return false;
   }
 
-  return buffer->Lock(aRegion);
+  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::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
+WindowSurfaceWayland::CommitWaylandBuffer()
 {
-  MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+  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,
                      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()) {
+    gint scaleFactor = mWindow->GdkScaleFactor();
+    for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) {
       const mozilla::LayoutDeviceIntRect &r = iter.Get();
-      wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
+      // 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.3.0/widget/gtk/WindowSurfaceWayland.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h
--- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h.wayland	2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h	2018-11-21 13:42:00.763025640 +0100
@@ -8,6 +8,7 @@
 #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
 
 #include <prthread.h>
+#include "mozilla/gfx/Types.h"
 
 namespace mozilla {
 namespace widget {
@@ -66,14 +67,14 @@ public:
   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; }
 
   bool Resize(int aWidth, int aHeight);
-  bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
+  bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
 
   bool IsMatchingSize(int aWidth, int aHeight)
   {
@@ -110,22 +111,32 @@ public:
   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);
+  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;
+  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