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