diff --git a/firefox-wayland.patch b/firefox-wayland.patch new file mode 100644 index 0000000..d5d1306 --- /dev/null +++ b/firefox-wayland.patch @@ -0,0 +1,8831 @@ +diff -up thunderbird-60.3.0/widget/gtk/gtk3drawing.cpp.wayland thunderbird-60.3.0/widget/gtk/gtk3drawing.cpp +--- thunderbird-60.3.0/widget/gtk/gtk3drawing.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/gtk3drawing.cpp 2018-11-20 12:04:43.731787368 +0100 +@@ -24,7 +24,7 @@ static gboolean checkbox_check_state; + static gboolean notebook_has_tab_gap; + + static ScrollbarGTKMetrics sScrollbarMetrics[2]; +-static ScrollbarGTKMetrics sScrollbarMetricsActive[2]; ++static ScrollbarGTKMetrics sActiveScrollbarMetrics[2]; + static ToggleGTKMetrics sCheckboxMetrics; + static ToggleGTKMetrics sRadioMetrics; + static ToolbarGTKMetrics sToolbarMetrics; +@@ -39,6 +39,28 @@ static ToolbarGTKMetrics sToolbarMetrics + #endif + + static GtkBorder ++operator-(const GtkBorder& first, const GtkBorder& second) ++{ ++ GtkBorder result; ++ result.left = first.left - second.left; ++ result.right = first.right - second.right; ++ result.top = first.top - second.top; ++ result.bottom = first.bottom - second.bottom; ++ return result; ++} ++ ++static GtkBorder ++operator+(const GtkBorder& first, const GtkBorder& second) ++{ ++ GtkBorder result; ++ result.left = first.left + second.left; ++ result.right = first.right + second.right; ++ result.top = first.top + second.top; ++ result.bottom = first.bottom + second.bottom; ++ return result; ++} ++ ++static GtkBorder + operator+=(GtkBorder& first, const GtkBorder& second) + { + first.left += second.left; +@@ -84,7 +106,7 @@ moz_gtk_add_style_border(GtkStyleContext + { + GtkBorder border; + +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); + + *left += border.left; + *right += border.right; +@@ -98,7 +120,7 @@ moz_gtk_add_style_padding(GtkStyleContex + { + GtkBorder padding; + +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + *left += padding.left; + *right += padding.right; +@@ -193,8 +215,8 @@ moz_gtk_refresh() + + sScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false; + sScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false; +- sScrollbarMetricsActive[GTK_ORIENTATION_HORIZONTAL].initialized = false; +- sScrollbarMetricsActive[GTK_ORIENTATION_VERTICAL].initialized = false; ++ sActiveScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false; ++ sActiveScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false; + sCheckboxMetrics.initialized = false; + sRadioMetrics.initialized = false; + sToolbarMetrics.initialized = false; +@@ -229,8 +251,8 @@ moz_gtk_get_focus_outline_size(GtkStyleC + { + GtkBorder border; + GtkBorder padding; +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + *focus_h_width = border.left + padding.left; + *focus_v_width = border.top + padding.top; + return MOZ_GTK_SUCCESS; +@@ -688,8 +710,8 @@ calculate_button_inner_rect(GtkWidget* b + style = gtk_widget_get_style_context(button); + + /* This mirrors gtkbutton's child positioning */ +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + inner_rect->x = rect->x + border.left + padding.left; + inner_rect->y = rect->y + padding.top + border.top; +@@ -1008,19 +1030,21 @@ moz_gtk_scrollbar_thumb_paint(WidgetNode + GtkTextDirection direction) + { + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); ++ GtkStyleContext* style = GetStyleContext(widget, direction, state_flags); ++ ++ GtkOrientation orientation = (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? ++ GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; + + GdkRectangle rect = *aRect; +- GtkStyleContext* style = GetStyleContext(widget, direction, state_flags); +- InsetByMargin(&rect, style); + +- gtk_render_slider(style, cr, +- rect.x, +- rect.y, +- rect.width, +- rect.height, +- (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? +- GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); ++ const ScrollbarGTKMetrics* metrics = ++ (state->depressed || state->active || state->inHover) ? ++ GetActiveScrollbarMetrics(orientation) : ++ GetScrollbarMetrics(orientation); ++ Inset(&rect, metrics->margin.thumb); + ++ gtk_render_slider(style, cr, rect.x, rect.y, rect.width, rect.height, ++ orientation); + + return MOZ_GTK_SUCCESS; + } +@@ -1217,7 +1241,7 @@ moz_gtk_entry_paint(cairo_t *cr, GdkRect + GtkStyleContext* style) + { + gint x = rect->x, y = rect->y, width = rect->width, height = rect->height; +- int draw_focus_outline_only = state->depressed; // NS_THEME_FOCUS_OUTLINE ++ int draw_focus_outline_only = state->depressed; // StyleAppearance::FocusOutline + + if (draw_focus_outline_only) { + // Inflate the given 'rect' with the focus outline size. +@@ -1621,7 +1645,7 @@ moz_gtk_toolbar_separator_paint(cairo_t + rect->height * (end_fraction - start_fraction)); + } else { + GtkBorder padding; +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + paint_width = padding.left; + if (paint_width > rect->width) +@@ -1790,7 +1814,7 @@ moz_gtk_get_tab_thickness(GtkStyleContex + return 0; /* tabs do not overdraw the tabpanel border with "no gap" style */ + + GtkBorder border; +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); + if (border.top < 2) + return 2; /* some themes don't set ythickness correctly */ + +@@ -2127,7 +2151,7 @@ moz_gtk_menu_separator_paint(cairo_t *cr + GtkBorder padding; + + style = GetStyleContext(MOZ_GTK_MENUSEPARATOR, direction); +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + x = rect->x; + y = rect->y; +@@ -2433,7 +2457,7 @@ moz_gtk_get_widget_border(WidgetNodeType + NULL); + + if (!wide_separators) { +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), + &border); + separator_width = border.left; + } +@@ -2606,13 +2630,13 @@ moz_gtk_get_tab_border(gint* left, gint* + } else { + GtkBorder margin; + +- gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); ++ gtk_style_context_get_margin(style, gtk_style_context_get_state(style), &margin); + *left += margin.left; + *right += margin.right; + + if (flags & MOZ_GTK_TAB_FIRST) { + style = GetStyleContext(MOZ_GTK_NOTEBOOK_HEADER, direction); +- gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); ++ gtk_style_context_get_margin(style, gtk_style_context_get_state(style), &margin); + *left += margin.left; + *right += margin.right; + } +@@ -2687,7 +2711,7 @@ moz_gtk_get_toolbar_separator_width(gint + "separator-width", &separator_width, + NULL); + /* Just in case... */ +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); + *size = MAX(*size, (wide_separators ? separator_width : border.left)); + return MOZ_GTK_SUCCESS; + } +@@ -2718,7 +2742,7 @@ moz_gtk_get_menu_separator_height(gint * + gint separator_height; + GtkBorder padding; + GtkStyleContext* style = GetStyleContext(MOZ_GTK_MENUSEPARATOR); +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SEPARATOR); +@@ -2750,8 +2774,8 @@ moz_gtk_get_entry_min_height(gint* heigh + + GtkBorder border; + GtkBorder padding; +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); +- gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); ++ gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border); ++ gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding); + + *height += (border.top + border.bottom + padding.top + padding.bottom); + } +@@ -2901,23 +2925,17 @@ GetToggleMetrics(bool isRadio) + return metrics; + } + +-const ScrollbarGTKMetrics* +-GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive) ++static void ++InitScrollbarMetrics(ScrollbarGTKMetrics* aMetrics, ++ GtkOrientation aOrientation, ++ GtkStateFlags aStateFlags) + { +- auto metrics = aActive ? &sScrollbarMetricsActive[aOrientation] : +- &sScrollbarMetrics[aOrientation]; +- if (metrics->initialized) +- return metrics; +- +- metrics->initialized = true; +- + WidgetNodeType scrollbar = aOrientation == GTK_ORIENTATION_HORIZONTAL ? + MOZ_GTK_SCROLLBAR_HORIZONTAL : MOZ_GTK_SCROLLBAR_VERTICAL; + + gboolean backward, forward, secondary_backward, secondary_forward; + GtkStyleContext* style = GetStyleContext(scrollbar, GTK_TEXT_DIR_NONE, +- aActive ? GTK_STATE_FLAG_PRELIGHT : +- GTK_STATE_FLAG_NORMAL); ++ aStateFlags); + gtk_style_context_get_style(style, + "has-backward-stepper", &backward, + "has-forward-stepper", &forward, +@@ -2938,15 +2956,15 @@ GetScrollbarMetrics(GtkOrientation aOrie + "min-slider-length", &min_slider_size, + nullptr); + +- metrics->size.thumb = ++ aMetrics->size.thumb = + SizeFromLengthAndBreadth(aOrientation, min_slider_size, slider_width); +- metrics->size.button = ++ aMetrics->size.button = + SizeFromLengthAndBreadth(aOrientation, stepper_size, slider_width); + // overall scrollbar + gint breadth = slider_width + 2 * trough_border; + // Require room for the slider in the track if we don't have buttons. + gint length = hasButtons ? 0 : min_slider_size + 2 * trough_border; +- metrics->size.scrollbar = ++ aMetrics->size.scrollbar = + SizeFromLengthAndBreadth(aOrientation, length, breadth); + + // Borders on the major axis are set on the outermost scrollbar +@@ -2956,23 +2974,24 @@ GetScrollbarMetrics(GtkOrientation aOrie + // receives mouse events, as in GTK. + // Other borders have been zero-initialized. + if (aOrientation == GTK_ORIENTATION_HORIZONTAL) { +- metrics->border.scrollbar.left = +- metrics->border.scrollbar.right = +- metrics->border.track.top = +- metrics->border.track.bottom = trough_border; ++ aMetrics->border.scrollbar.left = ++ aMetrics->border.scrollbar.right = ++ aMetrics->border.track.top = ++ aMetrics->border.track.bottom = trough_border; + } else { +- metrics->border.scrollbar.top = +- metrics->border.scrollbar.bottom = +- metrics->border.track.left = +- metrics->border.track.right = trough_border; ++ aMetrics->border.scrollbar.top = ++ aMetrics->border.scrollbar.bottom = ++ aMetrics->border.track.left = ++ aMetrics->border.track.right = trough_border; + } + +- return metrics; ++ // We're done here for Gtk+ < 3.20... ++ return; + } + + // GTK version > 3.20 + // scrollbar +- metrics->border.scrollbar = GetMarginBorderPadding(style); ++ aMetrics->border.scrollbar = GetMarginBorderPadding(style); + + WidgetNodeType contents, track, thumb; + if (aOrientation == GTK_ORIENTATION_HORIZONTAL) { +@@ -3003,72 +3022,102 @@ GetScrollbarMetrics(GtkOrientation aOrie + */ + + // thumb +- style = CreateStyleContextWithStates(thumb, GTK_TEXT_DIR_NONE, +- aActive ? GTK_STATE_FLAG_PRELIGHT : +- GTK_STATE_FLAG_NORMAL); +- metrics->size.thumb = GetMinMarginBox(style); ++ style = CreateStyleContextWithStates(thumb, GTK_TEXT_DIR_NONE, aStateFlags); ++ aMetrics->size.thumb = GetMinMarginBox(style); ++ gtk_style_context_get_margin(style, gtk_style_context_get_state(style), ++ &aMetrics->margin.thumb); + g_object_unref(style); + + // track +- style = CreateStyleContextWithStates(track, GTK_TEXT_DIR_NONE, +- aActive ? GTK_STATE_FLAG_PRELIGHT : +- GTK_STATE_FLAG_NORMAL); +- metrics->border.track = GetMarginBorderPadding(style); +- MozGtkSize trackMinSize = GetMinContentBox(style) + metrics->border.track; +- MozGtkSize trackSizeForThumb = metrics->size.thumb + metrics->border.track; ++ style = CreateStyleContextWithStates(track, GTK_TEXT_DIR_NONE, aStateFlags); ++ aMetrics->border.track = GetMarginBorderPadding(style); ++ MozGtkSize trackMinSize = GetMinContentBox(style) + aMetrics->border.track; ++ MozGtkSize trackSizeForThumb = aMetrics->size.thumb + aMetrics->border.track; + g_object_unref(style); + + // button + if (hasButtons) { + style = CreateStyleContextWithStates(MOZ_GTK_SCROLLBAR_BUTTON, +- GTK_TEXT_DIR_NONE, +- aActive ? GTK_STATE_FLAG_PRELIGHT : +- GTK_STATE_FLAG_NORMAL); +- metrics->size.button = GetMinMarginBox(style); ++ GTK_TEXT_DIR_NONE, aStateFlags); ++ aMetrics->size.button = GetMinMarginBox(style); + g_object_unref(style); + } else { +- metrics->size.button = {0, 0}; ++ aMetrics->size.button = {0, 0}; + } + if (aOrientation == GTK_ORIENTATION_HORIZONTAL) { +- metrics->size.button.Rotate(); ++ aMetrics->size.button.Rotate(); + // If the track is wider than necessary for the thumb, including when + // the buttons will cause Gecko to expand the track to fill + // available breadth, then add to the track border to prevent Gecko + // from expanding the thumb to fill available breadth. + gint extra = + std::max(trackMinSize.height, +- metrics->size.button.height) - trackSizeForThumb.height; ++ aMetrics->size.button.height) - trackSizeForThumb.height; + if (extra > 0) { + // If extra is odd, then the thumb is 0.5 pixels above + // center as in gtk_range_compute_slider_position(). +- metrics->border.track.top += extra / 2; +- metrics->border.track.bottom += extra - extra / 2; ++ aMetrics->border.track.top += extra / 2; ++ aMetrics->border.track.bottom += extra - extra / 2; + // Update size for change in border. + trackSizeForThumb.height += extra; + } + } else { + gint extra = + std::max(trackMinSize.width, +- metrics->size.button.width) - trackSizeForThumb.width; ++ aMetrics->size.button.width) - trackSizeForThumb.width; + if (extra > 0) { + // If extra is odd, then the thumb is 0.5 pixels to the left + // of center as in gtk_range_compute_slider_position(). +- metrics->border.track.left += extra / 2; +- metrics->border.track.right += extra - extra / 2; ++ aMetrics->border.track.left += extra / 2; ++ aMetrics->border.track.right += extra - extra / 2; + trackSizeForThumb.width += extra; + } + } + + style = CreateStyleContextWithStates(contents, GTK_TEXT_DIR_NONE, +- aActive ? GTK_STATE_FLAG_PRELIGHT : +- GTK_STATE_FLAG_NORMAL); ++ aStateFlags); + GtkBorder contentsBorder = GetMarginBorderPadding(style); + g_object_unref(style); + +- metrics->size.scrollbar = +- trackSizeForThumb + contentsBorder + metrics->border.scrollbar; ++ aMetrics->size.scrollbar = ++ trackSizeForThumb + contentsBorder + aMetrics->border.scrollbar; ++} ++ ++const ScrollbarGTKMetrics* ++GetScrollbarMetrics(GtkOrientation aOrientation) ++{ ++ auto metrics = &sScrollbarMetrics[aOrientation]; ++ if (!metrics->initialized) { ++ InitScrollbarMetrics(metrics, aOrientation, GTK_STATE_FLAG_NORMAL); ++ ++ // We calculate thumb margin here because it's composited from ++ // thumb class margin + difference margin between active and inactive ++ // scrollbars. It's a workaround which alows us to emulate ++ // overlay scrollbars for some Gtk+ themes (Ubuntu/Ambiance), ++ // when an inactive scrollbar thumb is smaller than the active one. ++ const ScrollbarGTKMetrics *metricsActive = ++ GetActiveScrollbarMetrics(aOrientation); ++ ++ if (metrics->size.thumb < metricsActive->size.thumb) { ++ metrics->margin.thumb += ++ (metrics->border.scrollbar + metrics->border.track) - ++ (metricsActive->border.scrollbar + metricsActive->border.track); ++ } + +- return metrics; ++ metrics->initialized = true; ++ } ++ return metrics; ++} ++ ++const ScrollbarGTKMetrics* ++GetActiveScrollbarMetrics(GtkOrientation aOrientation) ++{ ++ auto metrics = &sActiveScrollbarMetrics[aOrientation]; ++ if (!metrics->initialized) { ++ InitScrollbarMetrics(metrics, aOrientation, GTK_STATE_FLAG_PRELIGHT); ++ metrics->initialized = true; ++ } ++ return metrics; + } + + /* +@@ -3078,6 +3127,16 @@ GetScrollbarMetrics(GtkOrientation aOrie + bool + GetCSDDecorationSize(GtkWindow *aGtkWindow, GtkBorder* aDecorationSize) + { ++ // Available on GTK 3.20+. ++ static auto sGtkRenderBackgroundGetClip = ++ (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*)) ++ dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip"); ++ ++ if (!sGtkRenderBackgroundGetClip) { ++ *aDecorationSize = {0,0,0,0}; ++ return false; ++ } ++ + GtkStyleContext* context = gtk_widget_get_style_context(GTK_WIDGET(aGtkWindow)); + bool solidDecorations = gtk_style_context_has_class(context, "solid-csd"); + context = GetStyleContext(solidDecorations ? +@@ -3091,54 +3150,32 @@ GetCSDDecorationSize(GtkWindow *aGtkWind + gtk_style_context_get_padding(context, state, &padding); + *aDecorationSize += padding; + +- // Available on GTK 3.20+. +- static auto sGtkRenderBackgroundGetClip = +- (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*)) +- dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip"); + + GtkBorder margin; + gtk_style_context_get_margin(context, state, &margin); + +- GtkBorder extents = {0, 0, 0, 0}; +- if (sGtkRenderBackgroundGetClip) { +- /* Get shadow extents but combine with style margin; use the bigger value. +- */ +- GdkRectangle clip; +- sGtkRenderBackgroundGetClip(context, 0, 0, 0, 0, &clip); +- +- extents.top = -clip.y; +- extents.right = clip.width + clip.x; +- extents.bottom = clip.height + clip.y; +- extents.left = -clip.x; +- +- // Margin is used for resize grip size - it's not present on +- // popup windows. +- if (gtk_window_get_window_type(aGtkWindow) != GTK_WINDOW_POPUP) { +- extents.top = MAX(extents.top, margin.top); +- extents.right = MAX(extents.right, margin.right); +- extents.bottom = MAX(extents.bottom, margin.bottom); +- extents.left = MAX(extents.left, margin.left); +- } +- } else { +- /* If we can't get shadow extents use decoration-resize-handle instead +- * as a workaround. This is inspired by update_border_windows() +- * from gtkwindow.c although this is not 100% accurate as we emulate +- * the extents here. +- */ +- gint handle; +- gtk_widget_style_get(GetWidget(MOZ_GTK_WINDOW), +- "decoration-resize-handle", &handle, +- NULL); +- +- extents.top = handle + margin.top; +- extents.right = handle + margin.right; +- extents.bottom = handle + margin.bottom; +- extents.left = handle + margin.left; ++ /* Get shadow extents but combine with style margin; use the bigger value. ++ */ ++ GdkRectangle clip; ++ sGtkRenderBackgroundGetClip(context, 0, 0, 0, 0, &clip); ++ ++ GtkBorder extents; ++ extents.top = -clip.y; ++ extents.right = clip.width + clip.x; ++ extents.bottom = clip.height + clip.y; ++ extents.left = -clip.x; ++ ++ // Margin is used for resize grip size - it's not present on ++ // popup windows. ++ if (gtk_window_get_window_type(aGtkWindow) != GTK_WINDOW_POPUP) { ++ extents.top = MAX(extents.top, margin.top); ++ extents.right = MAX(extents.right, margin.right); ++ extents.bottom = MAX(extents.bottom, margin.bottom); ++ extents.left = MAX(extents.left, margin.left); + } + + *aDecorationSize += extents; +- +- return (sGtkRenderBackgroundGetClip != nullptr); ++ return true; + } + + /* cairo_t *cr argument has to be a system-cairo. */ +diff -up thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp.wayland thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp +--- thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/GtkCompositorWidget.cpp 2018-11-20 12:04:43.731787368 +0100 +@@ -40,7 +40,9 @@ GtkCompositorWidget::GtkCompositorWidget + + // Grab the window's visual and depth + XWindowAttributes windowAttrs; +- XGetWindowAttributes(mXDisplay, mXWindow, &windowAttrs); ++ if (!XGetWindowAttributes(mXDisplay, mXWindow, &windowAttrs)) { ++ NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!"); ++ } + + Visual* visual = windowAttrs.visual; + int depth = windowAttrs.depth; +@@ -50,8 +52,8 @@ GtkCompositorWidget::GtkCompositorWidget + mXDisplay, + mXWindow, + visual, +- depth +- ); ++ depth, ++ aInitData.Shaped()); + } + mClientSize = aInitData.InitialClientSize(); + } +diff -up thunderbird-60.3.0/widget/gtk/gtkdrawing.h.wayland thunderbird-60.3.0/widget/gtk/gtkdrawing.h +--- thunderbird-60.3.0/widget/gtk/gtkdrawing.h.wayland 2018-10-30 12:45:37.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/gtkdrawing.h 2018-11-20 12:04:43.731787368 +0100 +@@ -83,6 +83,9 @@ typedef struct { + GtkBorder scrollbar; + GtkBorder track; + } border; ++ struct { ++ GtkBorder thumb; ++ } margin; + } ScrollbarGTKMetrics; + + typedef struct { +@@ -502,11 +505,17 @@ moz_gtk_get_scalethumb_metrics(GtkOrient + /** + * Get the metrics in GTK pixels for a scrollbar. + * aOrientation: [IN] the scrollbar orientation +- * aActive: [IN] Metricts for scrollbar with mouse pointer over it. +- * + */ + const ScrollbarGTKMetrics* +-GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive = false); ++GetScrollbarMetrics(GtkOrientation aOrientation); ++ ++/** ++ * Get the metrics in GTK pixels for a scrollbar which is active ++ * (selected by mouse pointer). ++ * aOrientation: [IN] the scrollbar orientation ++ */ ++const ScrollbarGTKMetrics* ++GetActiveScrollbarMetrics(GtkOrientation aOrientation); + + /** + * Get the desired size of a dropdown arrow button +diff -up thunderbird-60.3.0/widget/gtk/IMContextWrapper.cpp.wayland thunderbird-60.3.0/widget/gtk/IMContextWrapper.cpp +--- thunderbird-60.3.0/widget/gtk/IMContextWrapper.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/IMContextWrapper.cpp 2018-11-20 12:04:43.732787365 +0100 +@@ -14,6 +14,7 @@ + #include "mozilla/Likely.h" + #include "mozilla/MiscEvents.h" + #include "mozilla/Preferences.h" ++#include "mozilla/Telemetry.h" + #include "mozilla/TextEventDispatcher.h" + #include "mozilla/TextEvents.h" + #include "WritingModes.h" +@@ -59,6 +60,73 @@ GetEventType(GdkEventKey* aKeyEvent) + } + } + ++class GetEventStateName : public nsAutoCString ++{ ++public: ++ explicit GetEventStateName(guint aState, ++ IMContextWrapper::IMContextID aIMContextID = ++ IMContextWrapper::IMContextID::eUnknown) ++ { ++ if (aState & GDK_SHIFT_MASK) { ++ AppendModifier("shift"); ++ } ++ if (aState & GDK_CONTROL_MASK) { ++ AppendModifier("control"); ++ } ++ if (aState & GDK_MOD1_MASK) { ++ AppendModifier("mod1"); ++ } ++ if (aState & GDK_MOD2_MASK) { ++ AppendModifier("mod2"); ++ } ++ if (aState & GDK_MOD3_MASK) { ++ AppendModifier("mod3"); ++ } ++ if (aState & GDK_MOD4_MASK) { ++ AppendModifier("mod4"); ++ } ++ if (aState & GDK_MOD4_MASK) { ++ AppendModifier("mod5"); ++ } ++ if (aState & GDK_MOD4_MASK) { ++ AppendModifier("mod5"); ++ } ++ switch (aIMContextID) { ++ case IMContextWrapper::IMContextID::eIBus: ++ static const guint IBUS_HANDLED_MASK = 1 << 24; ++ static const guint IBUS_IGNORED_MASK = 1 << 25; ++ if (aState & IBUS_HANDLED_MASK) { ++ AppendModifier("IBUS_HANDLED_MASK"); ++ } ++ if (aState & IBUS_IGNORED_MASK) { ++ AppendModifier("IBUS_IGNORED_MASK"); ++ } ++ break; ++ case IMContextWrapper::IMContextID::eFcitx: ++ static const guint FcitxKeyState_HandledMask = 1 << 24; ++ static const guint FcitxKeyState_IgnoredMask = 1 << 25; ++ if (aState & FcitxKeyState_HandledMask) { ++ AppendModifier("FcitxKeyState_HandledMask"); ++ } ++ if (aState & FcitxKeyState_IgnoredMask) { ++ AppendModifier("FcitxKeyState_IgnoredMask"); ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++private: ++ void AppendModifier(const char* aModifierName) ++ { ++ if (!IsEmpty()) { ++ AppendLiteral(" + "); ++ } ++ Append(aModifierName); ++ } ++}; ++ + class GetWritingModeName : public nsAutoCString + { + public: +@@ -281,12 +349,17 @@ IMContextWrapper::IMContextWrapper(nsWin + , mCompositionStart(UINT32_MAX) + , mProcessingKeyEvent(nullptr) + , mCompositionState(eCompositionState_NotComposing) ++ , mIMContextID(IMContextID::eUnknown) + , mIsIMFocused(false) ++ , mFallbackToKeyEvent(false) ++ , mKeyboardEventWasDispatched(false) + , mIsDeletingSurrounding(false) + , mLayoutChanged(false) + , mSetCursorPositionOnKeyEvent(true) + , mPendingResettingIMContext(false) + , mRetrieveSurroundingSignalReceived(false) ++ , mMaybeInDeadKeySequence(false) ++ , mIsIMInAsyncKeyHandlingMode(false) + { + static bool sFirstInstance = true; + if (sFirstInstance) { +@@ -299,15 +372,101 @@ IMContextWrapper::IMContextWrapper(nsWin + Init(); + } + ++static bool ++IsIBusInSyncMode() ++{ ++ // See ibus_im_context_class_init() in client/gtk2/ibusimcontext.c ++ // https://github.com/ibus/ibus/blob/86963f2f94d1e4fc213b01c2bc2ba9dcf4b22219/client/gtk2/ibusimcontext.c#L610 ++ const char* env = PR_GetEnv("IBUS_ENABLE_SYNC_MODE"); ++ ++ // See _get_boolean_env() in client/gtk2/ibusimcontext.c ++ // https://github.com/ibus/ibus/blob/86963f2f94d1e4fc213b01c2bc2ba9dcf4b22219/client/gtk2/ibusimcontext.c#L520-L537 ++ if (!env) { ++ return false; ++ } ++ nsDependentCString envStr(env); ++ if (envStr.IsEmpty() || ++ envStr.EqualsLiteral("0") || ++ envStr.EqualsLiteral("false") || ++ envStr.EqualsLiteral("False") || ++ envStr.EqualsLiteral("FALSE")) { ++ return false; ++ } ++ return true; ++} ++ ++static bool ++GetFcitxBoolEnv(const char* aEnv) ++{ ++ // See fcitx_utils_get_boolean_env in src/lib/fcitx-utils/utils.c ++ // https://github.com/fcitx/fcitx/blob/0c87840dc7d9460c2cb5feaeefec299d0d3d62ec/src/lib/fcitx-utils/utils.c#L721-L736 ++ const char* env = PR_GetEnv(aEnv); ++ if (!env) { ++ return false; ++ } ++ nsDependentCString envStr(env); ++ if (envStr.IsEmpty() || ++ envStr.EqualsLiteral("0") || ++ envStr.EqualsLiteral("false")) { ++ return false; ++ } ++ return true; ++} ++ ++static bool ++IsFcitxInSyncMode() ++{ ++ // See fcitx_im_context_class_init() in src/frontend/gtk2/fcitximcontext.c ++ // https://github.com/fcitx/fcitx/blob/78b98d9230dc9630e99d52e3172bdf440ffd08c4/src/frontend/gtk2/fcitximcontext.c#L395-L398 ++ return GetFcitxBoolEnv("IBUS_ENABLE_SYNC_MODE") || ++ GetFcitxBoolEnv("FCITX_ENABLE_SYNC_MODE"); ++} ++ ++nsDependentCSubstring ++IMContextWrapper::GetIMName() const ++{ ++ const char* contextIDChar = ++ gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(mContext)); ++ if (!contextIDChar) { ++ return nsDependentCSubstring(); ++ } ++ ++ nsDependentCSubstring im(contextIDChar, strlen(contextIDChar)); ++ ++ // If the context is XIM, actual engine must be specified with ++ // |XMODIFIERS=@im=foo|. ++ const char* xmodifiersChar = PR_GetEnv("XMODIFIERS"); ++ if (!im.EqualsLiteral("xim") || !xmodifiersChar) { ++ return im; ++ } ++ ++ nsDependentCString xmodifiers(xmodifiersChar); ++ int32_t atIMValueStart = xmodifiers.Find("@im=") + 4; ++ if (atIMValueStart < 4 || ++ xmodifiers.Length() <= static_cast(atIMValueStart)) { ++ return im; ++ } ++ ++ int32_t atIMValueEnd = ++ xmodifiers.Find("@", false, atIMValueStart); ++ if (atIMValueEnd > atIMValueStart) { ++ return nsDependentCSubstring(xmodifiersChar + atIMValueStart, ++ atIMValueEnd - atIMValueStart); ++ } ++ ++ if (atIMValueEnd == kNotFound) { ++ return nsDependentCSubstring(xmodifiersChar + atIMValueStart, ++ strlen(xmodifiersChar) - atIMValueStart); ++ } ++ ++ return im; ++} ++ + void + IMContextWrapper::Init() + { +- MOZ_LOG(gGtkIMLog, LogLevel::Info, +- ("0x%p Init(), mOwnerWindow=0x%p", +- this, mOwnerWindow)); +- + MozContainer* container = mOwnerWindow->GetMozContainer(); +- NS_PRECONDITION(container, "container is null"); ++ MOZ_ASSERT(container, "container is null"); + GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container)); + + // Overwrite selection colors of the window before associating the window +@@ -333,6 +492,47 @@ IMContextWrapper::Init() + G_CALLBACK(IMContextWrapper::OnStartCompositionCallback), this); + g_signal_connect(mContext, "preedit_end", + G_CALLBACK(IMContextWrapper::OnEndCompositionCallback), this); ++ nsDependentCSubstring im = GetIMName(); ++ if (im.EqualsLiteral("ibus")) { ++ mIMContextID = IMContextID::eIBus; ++ mIsIMInAsyncKeyHandlingMode = !IsIBusInSyncMode(); ++ // Although ibus has key snooper mode, it's forcibly disabled on Firefox ++ // in default settings by its whitelist since we always send key events ++ // to IME before handling shortcut keys. The whitelist can be ++ // customized with env, IBUS_NO_SNOOPER_APPS, but we don't need to ++ // support such rare cases for reducing maintenance cost. ++ mIsKeySnooped = false; ++ } else if (im.EqualsLiteral("fcitx")) { ++ mIMContextID = IMContextID::eFcitx; ++ mIsIMInAsyncKeyHandlingMode = !IsFcitxInSyncMode(); ++ // Although Fcitx has key snooper mode similar to ibus, it's also ++ // disabled on Firefox in default settings by its whitelist. The ++ // whitelist can be customized with env, IBUS_NO_SNOOPER_APPS or ++ // FCITX_NO_SNOOPER_APPS, but we don't need to support such rare cases ++ // for reducing maintenance cost. ++ mIsKeySnooped = false; ++ } else if (im.EqualsLiteral("uim")) { ++ mIMContextID = IMContextID::eUim; ++ mIsIMInAsyncKeyHandlingMode = false; ++ // We cannot know if uim uses key snooper since it's build option of ++ // uim. Therefore, we need to retrieve the consideration from the ++ // pref for making users and distributions allowed to choose their ++ // preferred value. ++ mIsKeySnooped = ++ Preferences::GetBool("intl.ime.hack.uim.using_key_snooper", true); ++ } else if (im.EqualsLiteral("scim")) { ++ mIMContextID = IMContextID::eScim; ++ mIsIMInAsyncKeyHandlingMode = false; ++ mIsKeySnooped = false; ++ } else if (im.EqualsLiteral("iiim")) { ++ mIMContextID = IMContextID::eIIIMF; ++ mIsIMInAsyncKeyHandlingMode = false; ++ mIsKeySnooped = false; ++ } else { ++ mIMContextID = IMContextID::eUnknown; ++ mIsIMInAsyncKeyHandlingMode = false; ++ mIsKeySnooped = false; ++ } + + // Simple context + if (sUseSimpleContext) { +@@ -361,6 +561,18 @@ IMContextWrapper::Init() + // Dummy context + mDummyContext = gtk_im_multicontext_new(); + gtk_im_context_set_client_window(mDummyContext, gdkWindow); ++ ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p Init(), mOwnerWindow=%p, mContext=%p (im=\"%s\"), " ++ "mIsIMInAsyncKeyHandlingMode=%s, mIsKeySnooped=%s, " ++ "mSimpleContext=%p, mDummyContext=%p, " ++ "gtk_im_multicontext_get_context_id()=\"%s\", " ++ "PR_GetEnv(\"XMODIFIERS\")=\"%s\"", ++ this, mOwnerWindow, mContext, nsAutoCString(im).get(), ++ ToChar(mIsIMInAsyncKeyHandlingMode), ToChar(mIsKeySnooped), ++ mSimpleContext, mDummyContext, ++ gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(mContext)), ++ PR_GetEnv("XMODIFIERS"))); + } + + /* static */ +@@ -469,7 +681,7 @@ IMContextWrapper::OnDestroyWindow(nsWind + "mOwnerWindow=0x%p, mLastFocusedModule=0x%p", + this, aWindow, mLastFocusedWindow, mOwnerWindow, sLastFocusedContext)); + +- NS_PRECONDITION(aWindow, "aWindow must not be null"); ++ MOZ_ASSERT(aWindow, "aWindow must not be null"); + + if (mLastFocusedWindow == aWindow) { + EndIMEComposition(aWindow); +@@ -524,46 +736,46 @@ IMContextWrapper::OnDestroyWindow(nsWind + mOwnerWindow = nullptr; + mLastFocusedWindow = nullptr; + mInputContext.mIMEState.mEnabled = IMEState::DISABLED; ++ mPostingKeyEvents.Clear(); + + MOZ_LOG(gGtkIMLog, LogLevel::Debug, + ("0x%p OnDestroyWindow(), succeeded, Completely destroyed", + this)); + } + +-// Work around gtk bug http://bugzilla.gnome.org/show_bug.cgi?id=483223: +-// (and the similar issue of GTK+ IIIM) +-// The GTK+ XIM and IIIM modules register handlers for the "closed" signal +-// on the display, but: +-// * The signal handlers are not disconnected when the module is unloaded. +-// +-// The GTK+ XIM module has another problem: +-// * When the signal handler is run (with the module loaded) it tries +-// XFree (and fails) on a pointer that did not come from Xmalloc. +-// +-// To prevent these modules from being unloaded, use static variables to +-// hold ref of GtkIMContext class. +-// For GTK+ XIM module, to prevent the signal handler from being run, +-// find the signal handlers and remove them. +-// +-// GtkIMContextXIMs share XOpenIM connections and display closed signal +-// handlers (where possible). +- + void + IMContextWrapper::PrepareToDestroyContext(GtkIMContext* aContext) + { +- GtkIMContext *slave = nullptr; //TODO GTK3 +- if (!slave) { +- return; +- } +- +- GType slaveType = G_TYPE_FROM_INSTANCE(slave); +- const gchar *im_type_name = g_type_name(slaveType); +- if (strcmp(im_type_name, "GtkIMContextIIIM") == 0) { +- // Add a reference to prevent the IIIM module from being unloaded +- static gpointer gtk_iiim_context_class = +- g_type_class_ref(slaveType); +- // Mute unused variable warning: +- (void)gtk_iiim_context_class; ++ if (mIMContextID == IMContextID::eIIIMF) { ++ // IIIM module registers handlers for the "closed" signal on the ++ // display, but the signal handler is not disconnected when the module ++ // is unloaded. To prevent the module from being unloaded, use static ++ // variable to hold reference of slave context class declared by IIIM. ++ // Note that this does not grab any instance, it grabs the "class". ++ static gpointer sGtkIIIMContextClass = nullptr; ++ if (!sGtkIIIMContextClass) { ++ // We retrieved slave context class with g_type_name() and actual ++ // slave context instance when our widget was GTK2. That must be ++ // _GtkIMContext::priv::slave in GTK3. However, _GtkIMContext::priv ++ // is an opacity struct named _GtkIMMulticontextPrivate, i.e., it's ++ // not exposed by GTK3. Therefore, we cannot access the instance ++ // safely. So, we need to retrieve the slave context class with ++ // g_type_from_name("GtkIMContextIIIM") directly (anyway, we needed ++ // to compare the class name with "GtkIMContextIIIM"). ++ GType IIMContextType = g_type_from_name("GtkIMContextIIIM"); ++ if (IIMContextType) { ++ sGtkIIIMContextClass = g_type_class_ref(IIMContextType); ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p PrepareToDestroyContext(), added to reference to " ++ "GtkIMContextIIIM class to prevent it from being unloaded", ++ this)); ++ } else { ++ MOZ_LOG(gGtkIMLog, LogLevel::Error, ++ ("0x%p PrepareToDestroyContext(), FAILED to prevent the " ++ "IIIM module from being uploaded", ++ this)); ++ } ++ } + } + } + +@@ -603,9 +815,9 @@ IMContextWrapper::OnBlurWindow(nsWindow* + bool + IMContextWrapper::OnKeyEvent(nsWindow* aCaller, + GdkEventKey* aEvent, +- bool aKeyDownEventWasSent /* = false */) ++ bool aKeyboardEventWasDispatched /* = false */) + { +- NS_PRECONDITION(aEvent, "aEvent must be non-null"); ++ MOZ_ASSERT(aEvent, "aEvent must be non-null"); + + if (!mInputContext.mIMEState.MaybeEditable() || + MOZ_UNLIKELY(IsDestroyed())) { +@@ -613,13 +825,24 @@ IMContextWrapper::OnKeyEvent(nsWindow* a + } + + MOZ_LOG(gGtkIMLog, LogLevel::Info, +- ("0x%p OnKeyEvent(aCaller=0x%p, aKeyDownEventWasSent=%s), " +- "mCompositionState=%s, current context=0x%p, active context=0x%p, " +- "aEvent(0x%p): { type=%s, keyval=%s, unicode=0x%X }", +- this, aCaller, ToChar(aKeyDownEventWasSent), ++ ("0x%p OnKeyEvent(aCaller=0x%p, " ++ "aEvent(0x%p): { type=%s, keyval=%s, unicode=0x%X, state=%s, " ++ "time=%u, hardware_keycode=%u, group=%u }, " ++ "aKeyboardEventWasDispatched=%s)", ++ this, aCaller, aEvent, GetEventType(aEvent), ++ gdk_keyval_name(aEvent->keyval), ++ gdk_keyval_to_unicode(aEvent->keyval), ++ GetEventStateName(aEvent->state, mIMContextID).get(), ++ aEvent->time, aEvent->hardware_keycode, aEvent->group, ++ ToChar(aKeyboardEventWasDispatched))); ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnKeyEvent(), mMaybeInDeadKeySequence=%s, " ++ "mCompositionState=%s, current context=%p, active context=%p, " ++ "mIMContextID=%s, mIsIMInAsyncKeyHandlingMode=%s", ++ this, ToChar(mMaybeInDeadKeySequence), + GetCompositionStateName(), GetCurrentContext(), GetActiveContext(), +- aEvent, GetEventType(aEvent), gdk_keyval_name(aEvent->keyval), +- gdk_keyval_to_unicode(aEvent->keyval))); ++ GetIMContextIDName(mIMContextID), ++ ToChar(mIsIMInAsyncKeyHandlingMode))); + + if (aCaller != mLastFocusedWindow) { + MOZ_LOG(gGtkIMLog, LogLevel::Error, +@@ -644,49 +867,158 @@ IMContextWrapper::OnKeyEvent(nsWindow* a + mSetCursorPositionOnKeyEvent = false; + } + +- mKeyDownEventWasSent = aKeyDownEventWasSent; +- mFilterKeyEvent = true; ++ // Let's support dead key event even if active keyboard layout also ++ // supports complicated composition like CJK IME. ++ bool isDeadKey = ++ KeymapWrapper::ComputeDOMKeyNameIndex(aEvent) == KEY_NAME_INDEX_Dead; ++ mMaybeInDeadKeySequence |= isDeadKey; ++ ++ // If current context is mSimpleContext, both ibus and fcitx handles key ++ // events synchronously. So, only when current context is mContext which ++ // is GtkIMMulticontext, the key event may be handled by IME asynchronously. ++ bool maybeHandledAsynchronously = ++ mIsIMInAsyncKeyHandlingMode && currentContext == mContext; ++ ++ // If IM is ibus or fcitx and it handles key events asynchronously, ++ // they mark aEvent->state as "handled by me" when they post key event ++ // to another process. Unfortunately, we need to check this hacky ++ // flag because it's difficult to store all pending key events by ++ // an array or a hashtable. ++ if (maybeHandledAsynchronously) { ++ switch (mIMContextID) { ++ case IMContextID::eIBus: ++ // ibus won't send back key press events in a dead key sequcne. ++ if (mMaybeInDeadKeySequence && aEvent->type == GDK_KEY_PRESS) { ++ maybeHandledAsynchronously = false; ++ break; ++ } ++ // ibus handles key events synchronously if focused editor is ++ // or |ime-mode: disabled;|. ++ if (mInputContext.mIMEState.mEnabled == IMEState::PASSWORD) { ++ maybeHandledAsynchronously = false; ++ break; ++ } ++ // See src/ibustypes.h ++ static const guint IBUS_IGNORED_MASK = 1 << 25; ++ // If IBUS_IGNORED_MASK was set to aEvent->state, the event ++ // has already been handled by another process and it wasn't ++ // used by IME. ++ if (aEvent->state & IBUS_IGNORED_MASK) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnKeyEvent(), aEvent->state has " ++ "IBUS_IGNORED_MASK, so, it won't be handled " ++ "asynchronously anymore. Removing posted events from " ++ "the queue", ++ this)); ++ maybeHandledAsynchronously = false; ++ mPostingKeyEvents.RemoveEvent(aEvent); ++ break; ++ } ++ break; ++ case IMContextID::eFcitx: ++ // fcitx won't send back key press events in a dead key sequcne. ++ if (mMaybeInDeadKeySequence && aEvent->type == GDK_KEY_PRESS) { ++ maybeHandledAsynchronously = false; ++ break; ++ } ++ ++ // fcitx handles key events asynchronously even if focused ++ // editor cannot use IME actually. ++ ++ // See src/lib/fcitx-utils/keysym.h ++ static const guint FcitxKeyState_IgnoredMask = 1 << 25; ++ // If FcitxKeyState_IgnoredMask was set to aEvent->state, ++ // the event has already been handled by another process and ++ // it wasn't used by IME. ++ if (aEvent->state & FcitxKeyState_IgnoredMask) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnKeyEvent(), aEvent->state has " ++ "FcitxKeyState_IgnoredMask, so, it won't be handled " ++ "asynchronously anymore. Removing posted events from " ++ "the queue", ++ this)); ++ maybeHandledAsynchronously = false; ++ mPostingKeyEvents.RemoveEvent(aEvent); ++ break; ++ } ++ break; ++ default: ++ MOZ_ASSERT_UNREACHABLE("IME may handle key event " ++ "asyncrhonously, but not yet confirmed if it comes agian " ++ "actually"); ++ } ++ } ++ ++ mKeyboardEventWasDispatched = aKeyboardEventWasDispatched; ++ mFallbackToKeyEvent = false; + mProcessingKeyEvent = aEvent; + gboolean isFiltered = + gtk_im_context_filter_keypress(currentContext, aEvent); +- mProcessingKeyEvent = nullptr; + +- // We filter the key event if the event was not committed (because +- // it's probably part of a composition) or if the key event was +- // committed _and_ changed. This way we still let key press +- // events go through as simple key press events instead of +- // composed characters. +- bool filterThisEvent = isFiltered && mFilterKeyEvent; +- +- if (IsComposingOnCurrentContext() && !isFiltered) { +- if (aEvent->type == GDK_KEY_PRESS) { +- if (!mDispatchedCompositionString.IsEmpty()) { +- // If there is composition string, we shouldn't dispatch +- // any keydown events during composition. +- filterThisEvent = true; +- } else { +- // A Hangul input engine for SCIM doesn't emit preedit_end +- // signal even when composition string becomes empty. On the +- // other hand, we should allow to make composition with empty +- // string for other languages because there *might* be such +- // IM. For compromising this issue, we should dispatch +- // compositionend event, however, we don't need to reset IM +- // actually. +- DispatchCompositionCommitEvent(currentContext, &EmptyString()); +- filterThisEvent = false; +- } +- } else { +- // Key release event may not be consumed by IM, however, we +- // shouldn't dispatch any keyup event during composition. +- filterThisEvent = true; ++ // The caller of this shouldn't handle aEvent anymore if we've dispatched ++ // composition events or modified content with other events. ++ bool filterThisEvent = isFiltered && !mFallbackToKeyEvent; ++ ++ if (IsComposingOnCurrentContext() && !isFiltered && ++ aEvent->type == GDK_KEY_PRESS && ++ mDispatchedCompositionString.IsEmpty()) { ++ // A Hangul input engine for SCIM doesn't emit preedit_end ++ // signal even when composition string becomes empty. On the ++ // other hand, we should allow to make composition with empty ++ // string for other languages because there *might* be such ++ // IM. For compromising this issue, we should dispatch ++ // compositionend event, however, we don't need to reset IM ++ // actually. ++ // NOTE: Don't dispatch key events as "processed by IME" since ++ // we need to dispatch keyboard events as IME wasn't handled it. ++ mProcessingKeyEvent = nullptr; ++ DispatchCompositionCommitEvent(currentContext, &EmptyString()); ++ mProcessingKeyEvent = aEvent; ++ // In this case, even though we handle the keyboard event here, ++ // but we should dispatch keydown event as ++ filterThisEvent = false; ++ } ++ ++ if (filterThisEvent && !mKeyboardEventWasDispatched) { ++ // If IME handled the key event but we've not dispatched eKeyDown nor ++ // eKeyUp event yet, we need to dispatch here unless the key event is ++ // now being handled by other IME process. ++ if (!maybeHandledAsynchronously) { ++ MaybeDispatchKeyEventAsProcessedByIME(eVoidEvent); ++ // Be aware, the widget might have been gone here. ++ } ++ // If we need to wait reply from IM, IM may send some signals to us ++ // without sending the key event again. In such case, we need to ++ // dispatch keyboard events with a copy of aEvent. Therefore, we ++ // need to use information of this key event to dispatch an KeyDown ++ // or eKeyUp event later. ++ else { ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnKeyEvent(), putting aEvent into the queue...", ++ this)); ++ mPostingKeyEvents.PutEvent(aEvent); + } + } + ++ mProcessingKeyEvent = nullptr; ++ ++ if (aEvent->type == GDK_KEY_PRESS && !filterThisEvent) { ++ // If the key event hasn't been handled by active IME nor keyboard ++ // layout, we can assume that the dead key sequence has been or was ++ // ended. Note that we should not reset it when the key event is ++ // GDK_KEY_RELEASE since it may not be filtered by active keyboard ++ // layout even in composition. ++ mMaybeInDeadKeySequence = false; ++ } ++ + MOZ_LOG(gGtkIMLog, LogLevel::Debug, + ("0x%p OnKeyEvent(), succeeded, filterThisEvent=%s " +- "(isFiltered=%s, mFilterKeyEvent=%s), mCompositionState=%s", ++ "(isFiltered=%s, mFallbackToKeyEvent=%s, " ++ "maybeHandledAsynchronously=%s), mCompositionState=%s, " ++ "mMaybeInDeadKeySequence=%s", + this, ToChar(filterThisEvent), ToChar(isFiltered), +- ToChar(mFilterKeyEvent), GetCompositionStateName())); ++ ToChar(mFallbackToKeyEvent), ToChar(maybeHandledAsynchronously), ++ GetCompositionStateName(), ToChar(mMaybeInDeadKeySequence))); + + return filterThisEvent; + } +@@ -1005,6 +1337,10 @@ IMContextWrapper::Focus() + + sLastFocusedContext = this; + ++ // Forget all posted key events when focus is moved since they shouldn't ++ // be fired in different editor. ++ mPostingKeyEvents.Clear(); ++ + gtk_im_context_focus_in(currentContext); + mIsIMFocused = true; + mSetCursorPositionOnKeyEvent = true; +@@ -1400,6 +1736,7 @@ IMContextWrapper::OnCommitCompositionNat + { + const gchar emptyStr = 0; + const gchar *commitString = aUTF8Char ? aUTF8Char : &emptyStr; ++ NS_ConvertUTF8toUTF16 utf16CommitString(commitString); + + MOZ_LOG(gGtkIMLog, LogLevel::Info, + ("0x%p OnCommitCompositionNative(aContext=0x%p), " +@@ -1422,7 +1759,7 @@ IMContextWrapper::OnCommitCompositionNat + // signal, we would dispatch compositionstart, text, compositionend + // events with empty string. Of course, they are unnecessary events + // for Web applications and our editor. +- if (!IsComposingOn(aContext) && !commitString[0]) { ++ if (!IsComposingOn(aContext) && utf16CommitString.IsEmpty()) { + MOZ_LOG(gGtkIMLog, LogLevel::Warning, + ("0x%p OnCommitCompositionNative(), Warning, does nothing " + "because has not started composition and commit string is empty", +@@ -1431,11 +1768,14 @@ IMContextWrapper::OnCommitCompositionNat + } + + // If IME doesn't change their keyevent that generated this commit, +- // don't send it through XIM - just send it as a normal key press +- // event. ++ // we should treat that IME didn't handle the key event because ++ // web applications want to receive "keydown" and "keypress" event ++ // in such case. + // NOTE: While a key event is being handled, this might be caused on + // current context. Otherwise, this may be caused on active context. +- if (!IsComposingOn(aContext) && mProcessingKeyEvent && ++ if (!IsComposingOn(aContext) && ++ mProcessingKeyEvent && ++ mProcessingKeyEvent->type == GDK_KEY_PRESS && + aContext == GetCurrentContext()) { + char keyval_utf8[8]; /* should have at least 6 bytes of space */ + gint keyval_utf8_len; +@@ -1445,12 +1785,80 @@ IMContextWrapper::OnCommitCompositionNat + keyval_utf8_len = g_unichar_to_utf8(keyval_unicode, keyval_utf8); + keyval_utf8[keyval_utf8_len] = '\0'; + ++ // If committing string is exactly same as a character which is ++ // produced by the key, eKeyDown and eKeyPress event should be ++ // dispatched by the caller of OnKeyEvent() normally. Note that ++ // mMaybeInDeadKeySequence will be set to false by OnKeyEvent() ++ // since we set mFallbackToKeyEvent to true here. + if (!strcmp(commitString, keyval_utf8)) { + MOZ_LOG(gGtkIMLog, LogLevel::Info, + ("0x%p OnCommitCompositionNative(), " + "we'll send normal key event", + this)); +- mFilterKeyEvent = false; ++ mFallbackToKeyEvent = true; ++ return; ++ } ++ ++ // If we're in a dead key sequence, commit string is a character in ++ // the BMP and mProcessingKeyEvent produces some characters but it's ++ // not same as committing string, we should dispatch an eKeyPress ++ // event from here. ++ WidgetKeyboardEvent keyDownEvent(true, eKeyDown, ++ mLastFocusedWindow); ++ KeymapWrapper::InitKeyEvent(keyDownEvent, mProcessingKeyEvent, false); ++ if (mMaybeInDeadKeySequence && ++ utf16CommitString.Length() == 1 && ++ keyDownEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) { ++ mKeyboardEventWasDispatched = true; ++ // Anyway, we're not in dead key sequence anymore. ++ mMaybeInDeadKeySequence = false; ++ ++ RefPtr dispatcher = GetTextEventDispatcher(); ++ nsresult rv = dispatcher->BeginNativeInputTransaction(); ++ if (NS_WARN_IF(NS_FAILED(rv))) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Error, ++ ("0x%p OnCommitCompositionNative(), FAILED, " ++ "due to BeginNativeInputTransaction() failure", ++ this)); ++ return; ++ } ++ ++ // First, dispatch eKeyDown event. ++ keyDownEvent.mKeyValue = utf16CommitString; ++ nsEventStatus status = nsEventStatus_eIgnore; ++ bool dispatched = ++ dispatcher->DispatchKeyboardEvent(eKeyDown, keyDownEvent, ++ status, mProcessingKeyEvent); ++ if (!dispatched || status == nsEventStatus_eConsumeNoDefault) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnCommitCompositionNative(), " ++ "doesn't dispatch eKeyPress event because the preceding " ++ "eKeyDown event was not dispatched or was consumed", ++ this)); ++ return; ++ } ++ if (mLastFocusedWindow != keyDownEvent.mWidget || ++ mLastFocusedWindow->Destroyed()) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p OnCommitCompositionNative(), Warning, " ++ "stop dispatching eKeyPress event because the preceding " ++ "eKeyDown event caused changing focused widget or " ++ "destroyed", ++ this)); ++ return; ++ } ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnCommitCompositionNative(), " ++ "dispatched eKeyDown event for the committed character", ++ this)); ++ ++ // Next, dispatch eKeyPress event. ++ dispatcher->MaybeDispatchKeypressEvents(keyDownEvent, status, ++ mProcessingKeyEvent); ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p OnCommitCompositionNative(), " ++ "dispatched eKeyPress event for the committed character", ++ this)); + return; + } + } +@@ -1470,7 +1878,7 @@ IMContextWrapper::GetCompositionString(G + gtk_im_context_get_preedit_string(aContext, &preedit_string, + &feedback_list, &cursor_pos); + if (preedit_string && *preedit_string) { +- CopyUTF8toUTF16(preedit_string, aCompositionString); ++ CopyUTF8toUTF16(MakeStringSpan(preedit_string), aCompositionString); + } else { + aCompositionString.Truncate(); + } +@@ -1485,6 +1893,173 @@ IMContextWrapper::GetCompositionString(G + } + + bool ++IMContextWrapper::MaybeDispatchKeyEventAsProcessedByIME( ++ EventMessage aFollowingEvent) ++{ ++ if (!mLastFocusedWindow) { ++ return false; ++ } ++ ++ if (!mIsKeySnooped && ++ ((!mProcessingKeyEvent && mPostingKeyEvents.IsEmpty()) || ++ (mProcessingKeyEvent && mKeyboardEventWasDispatched))) { ++ return true; ++ } ++ ++ // A "keydown" or "keyup" event handler may change focus with the ++ // following event. In such case, we need to cancel this composition. ++ // So, we need to store IM context now because mComposingContext may be ++ // overwritten with different context if calling this method recursively. ++ // Note that we don't need to grab the context here because |context| ++ // will be used only for checking if it's same as mComposingContext. ++ GtkIMContext* oldCurrentContext = GetCurrentContext(); ++ GtkIMContext* oldComposingContext = mComposingContext; ++ ++ RefPtr lastFocusedWindow(mLastFocusedWindow); ++ ++ if (mProcessingKeyEvent || !mPostingKeyEvents.IsEmpty()) { ++ if (mProcessingKeyEvent) { ++ mKeyboardEventWasDispatched = true; ++ } ++ // If we're not handling a key event synchronously, the signal may be ++ // sent by IME without sending key event to us. In such case, we ++ // should dispatch keyboard event for the last key event which was ++ // posted to other IME process. ++ GdkEventKey* sourceEvent = ++ mProcessingKeyEvent ? mProcessingKeyEvent : ++ mPostingKeyEvents.GetFirstEvent(); ++ ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(" ++ "aFollowingEvent=%s), dispatch %s %s " ++ "event: { type=%s, keyval=%s, unicode=0x%X, state=%s, " ++ "time=%u, hardware_keycode=%u, group=%u }", ++ this, ToChar(aFollowingEvent), ++ ToChar(sourceEvent->type == GDK_KEY_PRESS ? eKeyDown : eKeyUp), ++ mProcessingKeyEvent ? "processing" : "posted", ++ GetEventType(sourceEvent), gdk_keyval_name(sourceEvent->keyval), ++ gdk_keyval_to_unicode(sourceEvent->keyval), ++ GetEventStateName(sourceEvent->state, mIMContextID).get(), ++ sourceEvent->time, sourceEvent->hardware_keycode, ++ sourceEvent->group)); ++ ++ // Let's dispatch eKeyDown event or eKeyUp event now. Note that only ++ // when we're not in a dead key composition, we should mark the ++ // eKeyDown and eKeyUp event as "processed by IME" since we should ++ // expose raw keyCode and key value to web apps the key event is a ++ // part of a dead key sequence. ++ // FYI: We should ignore if default of preceding keydown or keyup ++ // event is prevented since even on the other browsers, web ++ // applications cannot cancel the following composition event. ++ // Spec bug: https://github.com/w3c/uievents/issues/180 ++ bool isCancelled; ++ lastFocusedWindow->DispatchKeyDownOrKeyUpEvent(sourceEvent, ++ !mMaybeInDeadKeySequence, ++ &isCancelled); ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(), keydown or keyup " ++ "event is dispatched", ++ this)); ++ ++ if (!mProcessingKeyEvent) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(), removing first " ++ "event from the queue", ++ this)); ++ mPostingKeyEvents.RemoveEvent(sourceEvent); ++ } ++ } else { ++ MOZ_ASSERT(mIsKeySnooped); ++ // Currently, we support key snooper mode of uim only. ++ MOZ_ASSERT(mIMContextID == IMContextID::eUim); ++ // uim sends "preedit_start" signal and "preedit_changed" separately ++ // at starting composition, "commit" and "preedit_end" separately at ++ // committing composition. ++ ++ // Currently, we should dispatch only fake eKeyDown event because ++ // we cannot decide which is the last signal of each key operation ++ // and Chromium also dispatches only "keydown" event in this case. ++ bool dispatchFakeKeyDown = false; ++ switch (aFollowingEvent) { ++ case eCompositionStart: ++ case eCompositionCommit: ++ case eCompositionCommitAsIs: ++ dispatchFakeKeyDown = true; ++ break; ++ // XXX Unfortunately, I don't have a good idea to prevent to ++ // dispatch redundant eKeyDown event for eCompositionStart ++ // immediately after "delete_surrounding" signal. However, ++ // not dispatching eKeyDown event is worse than dispatching ++ // redundant eKeyDown events. ++ case eContentCommandDelete: ++ dispatchFakeKeyDown = true; ++ break; ++ // We need to prevent to dispatch redundant eKeyDown event for ++ // eCompositionChange immediately after eCompositionStart. So, ++ // We should not dispatch eKeyDown event if dispatched composition ++ // string is still empty string. ++ case eCompositionChange: ++ dispatchFakeKeyDown = !mDispatchedCompositionString.IsEmpty(); ++ break; ++ default: ++ MOZ_ASSERT_UNREACHABLE("Do you forget to handle the case?"); ++ break; ++ } ++ ++ if (dispatchFakeKeyDown) { ++ WidgetKeyboardEvent fakeKeyDownEvent(true, eKeyDown, ++ lastFocusedWindow); ++ fakeKeyDownEvent.mKeyCode = NS_VK_PROCESSKEY; ++ fakeKeyDownEvent.mKeyNameIndex = KEY_NAME_INDEX_Process; ++ // It's impossible to get physical key information in this case but ++ // this should be okay since web apps shouldn't do anything with ++ // physical key information during composition. ++ fakeKeyDownEvent.mCodeNameIndex = CODE_NAME_INDEX_UNKNOWN; ++ ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(" ++ "aFollowingEvent=%s), dispatch fake eKeyDown event", ++ this, ToChar(aFollowingEvent))); ++ ++ bool isCancelled; ++ lastFocusedWindow->DispatchKeyDownOrKeyUpEvent(fakeKeyDownEvent, ++ &isCancelled); ++ MOZ_LOG(gGtkIMLog, LogLevel::Info, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(), " ++ "fake keydown event is dispatched", ++ this)); ++ } ++ } ++ ++ if (lastFocusedWindow->IsDestroyed() || ++ lastFocusedWindow != mLastFocusedWindow) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(), Warning, the " ++ "focused widget was destroyed/changed by a key event", ++ this)); ++ return false; ++ } ++ ++ // If the dispatched keydown event caused moving focus and that also ++ // caused changing active context, we need to cancel composition here. ++ if (GetCurrentContext() != oldCurrentContext) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p MaybeDispatchKeyEventAsProcessedByIME(), Warning, the key " ++ "event causes changing active IM context", ++ this)); ++ if (mComposingContext == oldComposingContext) { ++ // Only when the context is still composing, we should call ++ // ResetIME() here. Otherwise, it should've already been ++ // cleaned up. ++ ResetIME(); ++ } ++ return false; ++ } ++ ++ return true; ++} ++ ++bool + IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) + { + MOZ_LOG(gGtkIMLog, LogLevel::Info, +@@ -1528,50 +2103,16 @@ IMContextWrapper::DispatchCompositionSta + mCompositionStart = mSelection.mOffset; + mDispatchedCompositionString.Truncate(); + +- if (mProcessingKeyEvent && !mKeyDownEventWasSent && +- mProcessingKeyEvent->type == GDK_KEY_PRESS) { +- // A keydown event handler may change focus with the following keydown +- // event. In such case, we need to cancel this composition. So, we +- // need to store IM context now because mComposingContext may be +- // overwritten with different context if calling this method +- // recursively. +- // Note that we don't need to grab the context here because |context| +- // will be used only for checking if it's same as mComposingContext. +- GtkIMContext* context = mComposingContext; +- +- // If this composition is started by a native keydown event, we need to +- // dispatch our keydown event here (before composition start). +- bool isCancelled; +- mLastFocusedWindow->DispatchKeyDownEvent(mProcessingKeyEvent, +- &isCancelled); +- MOZ_LOG(gGtkIMLog, LogLevel::Debug, +- ("0x%p DispatchCompositionStart(), preceding keydown event is " +- "dispatched", ++ // If this composition is started by a key press, we need to dispatch ++ // eKeyDown or eKeyUp event before dispatching eCompositionStart event. ++ // Note that dispatching a keyboard event which is marked as "processed ++ // by IME" is okay since Chromium also dispatches keyboard event as so. ++ if (!MaybeDispatchKeyEventAsProcessedByIME(eCompositionStart)) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p DispatchCompositionStart(), Warning, " ++ "MaybeDispatchKeyEventAsProcessedByIME() returned false", + this)); +- if (lastFocusedWindow->IsDestroyed() || +- lastFocusedWindow != mLastFocusedWindow) { +- MOZ_LOG(gGtkIMLog, LogLevel::Warning, +- ("0x%p DispatchCompositionStart(), Warning, the focused " +- "widget was destroyed/changed by keydown event", +- this)); +- return false; +- } +- +- // If the dispatched keydown event caused moving focus and that also +- // caused changing active context, we need to cancel composition here. +- if (GetCurrentContext() != context) { +- MOZ_LOG(gGtkIMLog, LogLevel::Warning, +- ("0x%p DispatchCompositionStart(), Warning, the preceding " +- "keydown event causes changing active IM context", +- this)); +- if (mComposingContext == context) { +- // Only when the context is still composing, we should call +- // ResetIME() here. Otherwise, it should've already been +- // cleaned up. +- ResetIME(); +- } +- return false; +- } ++ return false; + } + + RefPtr dispatcher = GetTextEventDispatcher(); +@@ -1584,6 +2125,25 @@ IMContextWrapper::DispatchCompositionSta + return false; + } + ++ static bool sHasSetTelemetry = false; ++ if (!sHasSetTelemetry) { ++ sHasSetTelemetry = true; ++ NS_ConvertUTF8toUTF16 im(GetIMName()); ++ // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp ++ if (im.Length() > 72) { ++ if (NS_IS_LOW_SURROGATE(im[72 - 1]) && ++ NS_IS_HIGH_SURROGATE(im[72 - 2])) { ++ im.Truncate(72 - 2); ++ } else { ++ im.Truncate(72 - 1); ++ } ++ // U+2026 is "..." ++ im.Append(char16_t(0x2026)); ++ } ++ Telemetry::ScalarSet(Telemetry::ScalarID::WIDGET_IME_NAME_ON_LINUX, ++ im, true); ++ } ++ + MOZ_LOG(gGtkIMLog, LogLevel::Debug, + ("0x%p DispatchCompositionStart(), dispatching " + "compositionstart... (mCompositionStart=%u)", +@@ -1629,6 +2189,15 @@ IMContextWrapper::DispatchCompositionCha + return false; + } + } ++ // If this composition string change caused by a key press, we need to ++ // dispatch eKeyDown or eKeyUp before dispatching eCompositionChange event. ++ else if (!MaybeDispatchKeyEventAsProcessedByIME(eCompositionChange)) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p DispatchCompositionChangeEvent(), Warning, " ++ "MaybeDispatchKeyEventAsProcessedByIME() returned false", ++ this)); ++ return false; ++ } + + RefPtr dispatcher = GetTextEventDispatcher(); + nsresult rv = dispatcher->BeginNativeInputTransaction(); +@@ -1718,6 +2287,14 @@ IMContextWrapper::DispatchCompositionCom + return false; + } + ++ // TODO: We need special care to handle request to commit composition ++ // by content while we're committing composition because we have ++ // commit string information now but IME may not have composition ++ // anymore. Therefore, we may not be able to handle commit as ++ // expected. However, this is rare case because this situation ++ // never occurs with remote content. So, it's okay to fix this ++ // issue later. (Perhaps, TextEventDisptcher should do it for ++ // all platforms. E.g., creating WillCommitComposition()?) + if (!IsComposing()) { + if (!aCommitString || aCommitString->IsEmpty()) { + MOZ_LOG(gGtkIMLog, LogLevel::Error, +@@ -1734,6 +2311,17 @@ IMContextWrapper::DispatchCompositionCom + return false; + } + } ++ // If this commit caused by a key press, we need to dispatch eKeyDown or ++ // eKeyUp before dispatching composition events. ++ else if (!MaybeDispatchKeyEventAsProcessedByIME( ++ aCommitString ? eCompositionCommit : eCompositionCommitAsIs)) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p DispatchCompositionCommitEvent(), Warning, " ++ "MaybeDispatchKeyEventAsProcessedByIME() returned false", ++ this)); ++ mCompositionState = eCompositionState_NotComposing; ++ return false; ++ } + + RefPtr dispatcher = GetTextEventDispatcher(); + nsresult rv = dispatcher->BeginNativeInputTransaction(); +@@ -1755,6 +2343,11 @@ IMContextWrapper::DispatchCompositionCom + mSelection.mWritingMode); + + mCompositionState = eCompositionState_NotComposing; ++ // Reset dead key sequence too because GTK doesn't support dead key chain ++ // (i.e., a key press doesn't cause both producing some characters and ++ // restarting new dead key sequence at one time). So, committing ++ // composition means end of a dead key sequence. ++ mMaybeInDeadKeySequence = false; + mCompositionStart = UINT32_MAX; + mCompositionTargetRange.Clear(); + mDispatchedCompositionString.Truncate(); +@@ -2427,6 +3020,16 @@ IMContextWrapper::DeleteText(GtkIMContex + this)); + return NS_ERROR_FAILURE; + } ++ ++ // If this deleting text caused by a key press, we need to dispatch ++ // eKeyDown or eKeyUp before dispatching eContentCommandDelete event. ++ if (!MaybeDispatchKeyEventAsProcessedByIME(eContentCommandDelete)) { ++ MOZ_LOG(gGtkIMLog, LogLevel::Warning, ++ ("0x%p DeleteText(), Warning, " ++ "MaybeDispatchKeyEventAsProcessedByIME() returned false", ++ this)); ++ return NS_ERROR_FAILURE; ++ } + + // Delete the selection + WidgetContentCommandEvent contentCommandEvent(true, eContentCommandDelete, +diff -up thunderbird-60.3.0/widget/gtk/IMContextWrapper.h.wayland thunderbird-60.3.0/widget/gtk/IMContextWrapper.h +--- thunderbird-60.3.0/widget/gtk/IMContextWrapper.h.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/IMContextWrapper.h 2018-11-20 12:04:43.733787362 +0100 +@@ -70,13 +70,26 @@ public: + // OnThemeChanged is called when desktop theme is changed. + static void OnThemeChanged(); + +- // OnKeyEvent is called when aWindow gets a native key press event or a +- // native key release event. If this returns TRUE, the key event was +- // filtered by IME. Otherwise, this returns FALSE. +- // NOTE: When the keypress event starts composition, this returns TRUE but +- // this dispatches keydown event before compositionstart event. ++ /** ++ * OnKeyEvent() is called when aWindow gets a native key press event or a ++ * native key release event. If this returns true, the key event was ++ * filtered by IME. Otherwise, this returns false. ++ * NOTE: When the native key press event starts composition, this returns ++ * true but dispatches an eKeyDown event or eKeyUp event before ++ * dispatching composition events or content command event. ++ * ++ * @param aWindow A window on which user operate the ++ * key. ++ * @param aEvent A native key press or release ++ * event. ++ * @param aKeyboardEventWasDispatched true if eKeyDown or eKeyUp event ++ * for aEvent has already been ++ * dispatched. In this case, ++ * this class doesn't dispatch ++ * keyboard event anymore. ++ */ + bool OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent, +- bool aKeyDownEventWasSent = false); ++ bool aKeyboardEventWasDispatched = false); + + // IME related nsIWidget methods. + nsresult EndIMEComposition(nsWindow* aCaller); +@@ -89,6 +102,43 @@ public: + + TextEventDispatcher* GetTextEventDispatcher(); + ++ // TODO: Typically, new IM comes every several years. And now, our code ++ // becomes really IM behavior dependent. So, perhaps, we need prefs ++ // to control related flags for IM developers. ++ enum class IMContextID : uint8_t ++ { ++ eFcitx, ++ eIBus, ++ eIIIMF, ++ eScim, ++ eUim, ++ eUnknown, ++ }; ++ ++ static const char* GetIMContextIDName(IMContextID aIMContextID) ++ { ++ switch (aIMContextID) { ++ case IMContextID::eFcitx: ++ return "eFcitx"; ++ case IMContextID::eIBus: ++ return "eIBus"; ++ case IMContextID::eIIIMF: ++ return "eIIIMF"; ++ case IMContextID::eScim: ++ return "eScim"; ++ case IMContextID::eUim: ++ return "eUim"; ++ default: ++ return "eUnknown"; ++ } ++ } ++ ++ /** ++ * GetIMName() returns IM name associated with mContext. If the context is ++ * xim, this look for actual engine from XMODIFIERS environment variable. ++ */ ++ nsDependentCSubstring GetIMName() const; ++ + protected: + ~IMContextWrapper(); + +@@ -144,6 +194,100 @@ protected: + // event. + GdkEventKey* mProcessingKeyEvent; + ++ /** ++ * GdkEventKeyQueue stores *copy* of GdkEventKey instances. However, this ++ * must be safe to our usecase since it has |time| and the value should not ++ * be same as older event. ++ */ ++ class GdkEventKeyQueue final ++ { ++ public: ++ ~GdkEventKeyQueue() { Clear(); } ++ ++ void Clear() ++ { ++ if (!mEvents.IsEmpty()) { ++ RemoveEventsAt(0, mEvents.Length()); ++ } ++ } ++ ++ /** ++ * PutEvent() puts new event into the queue. ++ */ ++ void PutEvent(const GdkEventKey* aEvent) ++ { ++ GdkEventKey* newEvent = ++ reinterpret_cast( ++ gdk_event_copy(reinterpret_cast(aEvent))); ++ newEvent->state &= GDK_MODIFIER_MASK; ++ mEvents.AppendElement(newEvent); ++ } ++ ++ /** ++ * RemoveEvent() removes oldest same event and its preceding events ++ * from the queue. ++ */ ++ void RemoveEvent(const GdkEventKey* aEvent) ++ { ++ size_t index = IndexOf(aEvent); ++ if (NS_WARN_IF(index == mEvents.NoIndex)) { ++ return; ++ } ++ RemoveEventsAt(0, index + 1); ++ } ++ ++ /** ++ * FirstEvent() returns oldest event in the queue. ++ */ ++ GdkEventKey* GetFirstEvent() const ++ { ++ if (mEvents.IsEmpty()) { ++ return nullptr; ++ } ++ return mEvents[0]; ++ } ++ ++ bool IsEmpty() const { return mEvents.IsEmpty(); } ++ ++ private: ++ nsTArray mEvents; ++ ++ void RemoveEventsAt(size_t aStart, size_t aCount) ++ { ++ for (size_t i = aStart; i < aStart + aCount; i++) { ++ gdk_event_free(reinterpret_cast(mEvents[i])); ++ } ++ mEvents.RemoveElementsAt(aStart, aCount); ++ } ++ ++ size_t IndexOf(const GdkEventKey* aEvent) const ++ { ++ static_assert(!(GDK_MODIFIER_MASK & (1 << 24)), ++ "We assumes 25th bit is used by some IM, but used by GDK"); ++ static_assert(!(GDK_MODIFIER_MASK & (1 << 25)), ++ "We assumes 26th bit is used by some IM, but used by GDK"); ++ for (size_t i = 0; i < mEvents.Length(); i++) { ++ GdkEventKey* event = mEvents[i]; ++ // It must be enough to compare only type, time, keyval and ++ // part of state. Note that we cannot compaire two events ++ // simply since IME may have changed unused bits of state. ++ if (event->time == aEvent->time) { ++ if (NS_WARN_IF(event->type != aEvent->type) || ++ NS_WARN_IF(event->keyval != aEvent->keyval) || ++ NS_WARN_IF(event->state != ++ (aEvent->state & GDK_MODIFIER_MASK))) { ++ continue; ++ } ++ } ++ return i; ++ } ++ return mEvents.NoIndex; ++ } ++ }; ++ // OnKeyEvent() append mPostingKeyEvents when it believes that a key event ++ // is posted to other IME process. ++ GdkEventKeyQueue mPostingKeyEvents; ++ + struct Range + { + uint32_t mOffset; +@@ -167,7 +311,8 @@ protected: + Range mCompositionTargetRange; + + // mCompositionState indicates current status of composition. +- enum eCompositionState { ++ enum eCompositionState : uint8_t ++ { + eCompositionState_NotComposing, + eCompositionState_CompositionStartDispatched, + eCompositionState_CompositionChangeEventDispatched +@@ -219,6 +364,10 @@ protected: + } + } + ++ // mIMContextID indicates the ID of mContext. This is actually indicates ++ // IM which user selected. ++ IMContextID mIMContextID; ++ + struct Selection final + { + nsString mString; +@@ -268,16 +417,20 @@ protected: + // mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And + // it's set to FALSE when we call gtk_im_context_focus_out(). + bool mIsIMFocused; +- // mFilterKeyEvent is used by OnKeyEvent(). If the commit event should +- // be processed as simple key event, this is set to TRUE by the commit +- // handler. +- bool mFilterKeyEvent; +- // mKeyDownEventWasSent is used by OnKeyEvent() and +- // DispatchCompositionStart(). DispatchCompositionStart() dispatches +- // a keydown event if the composition start is caused by a native +- // keypress event. If this is true, the keydown event has been dispatched. +- // Then, DispatchCompositionStart() doesn't dispatch keydown event. +- bool mKeyDownEventWasSent; ++ // mFallbackToKeyEvent is set to false when this class starts to handle ++ // a native key event (at that time, mProcessingKeyEvent is set to the ++ // native event). If active IME just commits composition with a character ++ // which is produced by the key with current keyboard layout, this is set ++ // to true. ++ bool mFallbackToKeyEvent; ++ // mKeyboardEventWasDispatched is used by OnKeyEvent() and ++ // MaybeDispatchKeyEventAsProcessedByIME(). ++ // MaybeDispatchKeyEventAsProcessedByIME() dispatches an eKeyDown or ++ // eKeyUp event event if the composition is caused by a native ++ // key press event. If this is true, a keyboard event has been dispatched ++ // for the native event. If so, MaybeDispatchKeyEventAsProcessedByIME() ++ // won't dispatch keyboard event anymore. ++ bool mKeyboardEventWasDispatched; + // mIsDeletingSurrounding is true while OnDeleteSurroundingNative() is + // trying to delete the surrounding text. + bool mIsDeletingSurrounding; +@@ -298,6 +451,24 @@ protected: + // mRetrieveSurroundingSignalReceived is true after "retrieve_surrounding" + // signal is received until selection is changed in Gecko. + bool mRetrieveSurroundingSignalReceived; ++ // mMaybeInDeadKeySequence is set to true when we detect a dead key press ++ // and set to false when we're sure dead key sequence has been finished. ++ // Note that we cannot detect which key event causes ending a dead key ++ // sequence. For example, when you press dead key grave with ibus Spanish ++ // keyboard layout, it just consumes the key event when we call ++ // gtk_im_context_filter_keypress(). Then, pressing "Escape" key cancels ++ // the dead key sequence but we don't receive any signal and it's consumed ++ // by gtk_im_context_filter_keypress() normally. On the other hand, when ++ // pressing "Shift" key causes exactly same behavior but dead key sequence ++ // isn't finished yet. ++ bool mMaybeInDeadKeySequence; ++ // mIsIMInAsyncKeyHandlingMode is set to true if we know that IM handles ++ // key events asynchronously. I.e., filtered key event may come again ++ // later. ++ bool mIsIMInAsyncKeyHandlingMode; ++ // mIsKeySnooped is set to true if IM uses key snooper to listen key events. ++ // In such case, we won't receive key events if IME consumes the event. ++ bool mIsKeySnooped; + + // sLastFocusedContext is a pointer to the last focused instance of this + // class. When a instance is destroyed and sLastFocusedContext refers it, +@@ -448,12 +619,31 @@ protected: + * Following methods dispatch gecko events. Then, the focused widget + * can be destroyed, and also it can be stolen focus. If they returns + * FALSE, callers cannot continue the composition. ++ * - MaybeDispatchKeyEventAsProcessedByIME + * - DispatchCompositionStart + * - DispatchCompositionChangeEvent + * - DispatchCompositionCommitEvent + */ + + /** ++ * Dispatch an eKeyDown or eKeyUp event whose mKeyCode value is ++ * NS_VK_PROCESSKEY and mKeyNameIndex is KEY_NAME_INDEX_Process if ++ * we're not in a dead key sequence, mProcessingKeyEvent is nullptr ++ * but mPostingKeyEvents is not empty or mProcessingKeyEvent is not ++ * nullptr and mKeyboardEventWasDispatched is still false. If this ++ * dispatches a keyboard event, this sets mKeyboardEventWasDispatched ++ * to true. ++ * ++ * @param aFollowingEvent The following event message. ++ * @return If the caller can continue to handle ++ * composition, returns true. Otherwise, ++ * false. For example, if focus is moved ++ * by dispatched keyboard event, returns ++ * false. ++ */ ++ bool MaybeDispatchKeyEventAsProcessedByIME(EventMessage aFollowingEvent); ++ ++ /** + * Dispatches a composition start event. + * + * @param aContext A GtkIMContext which is being handled. +diff -up thunderbird-60.3.0/widget/gtk/InProcessGtkCompositorWidget.h.wayland thunderbird-60.3.0/widget/gtk/InProcessGtkCompositorWidget.h +--- thunderbird-60.3.0/widget/gtk/InProcessGtkCompositorWidget.h.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/InProcessGtkCompositorWidget.h 2018-11-20 12:04:43.733787362 +0100 +@@ -3,8 +3,8 @@ + * 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 widget_gtk_InProcessGtkCompositorWidgetParent_h +-#define widget_gtk_InProcessGtkCompositorWidgetParent_h ++#ifndef widget_gtk_InProcessGtkCompositorWidget_h ++#define widget_gtk_InProcessGtkCompositorWidget_h + + #include "GtkCompositorWidget.h" + +@@ -28,4 +28,4 @@ public: + } // namespace widget + } // namespace mozilla + +-#endif // widget_gtk_InProcessGtkCompositorWidgetParent_h ++#endif // widget_gtk_InProcessGtkCompositorWidget_h +diff -up thunderbird-60.3.0/widget/gtk/moz.build.wayland thunderbird-60.3.0/widget/gtk/moz.build +--- thunderbird-60.3.0/widget/gtk/moz.build.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/moz.build 2018-11-20 12:09:34.756903818 +0100 +@@ -123,6 +123,7 @@ include('/ipc/chromium/chromium-config.m + FINAL_LIBRARY = 'xul' + + LOCAL_INCLUDES += [ ++ '/layout/base', + '/layout/generic', + '/layout/xul', + '/other-licenses/atk-1.0', +diff -up thunderbird-60.3.0/widget/gtk/mozcontainer.cpp.wayland thunderbird-60.3.0/widget/gtk/mozcontainer.cpp +--- thunderbird-60.3.0/widget/gtk/mozcontainer.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/mozcontainer.cpp 2018-11-20 12:04:43.733787362 +0100 +@@ -10,6 +10,7 @@ + #ifdef MOZ_WAYLAND + #include + #include ++#include + #endif + #include + #include +@@ -207,6 +208,12 @@ moz_container_init (MozContainer *contai + + #if defined(MOZ_WAYLAND) + { ++ container->subcompositor = nullptr; ++ container->surface = nullptr; ++ container->subsurface = nullptr; ++ container->eglwindow = nullptr; ++ container->parent_surface_committed = false; ++ + GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container)); + if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) { + // Available as of GTK 3.8+ +@@ -225,12 +232,21 @@ moz_container_init (MozContainer *contai + } + + #if defined(MOZ_WAYLAND) ++static void ++moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container) ++{ ++ container->parent_surface_committed = true; ++ g_signal_handler_disconnect(clock, ++ container->parent_surface_committed_handler); ++ container->parent_surface_committed_handler = 0; ++} ++ + /* We want to draw to GdkWindow owned by mContainer from Compositor thread but + * Gtk+ can be used in main thread only. So we create wayland wl_surface + * and attach it as an overlay to GdkWindow. + * + * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c +-* for reference. ++ * for reference. + */ + static gboolean + moz_container_map_surface(MozContainer *container) +@@ -242,6 +258,9 @@ moz_container_map_surface(MozContainer * + static auto sGdkWaylandWindowGetWlSurface = + (wl_surface *(*)(GdkWindow *)) + dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface"); ++ static auto sGdkWindowGetFrameClock = ++ (GdkFrameClock *(*)(GdkWindow *)) ++ dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock"); + + GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container)); + if (GDK_IS_X11_DISPLAY(display)) +@@ -250,6 +269,18 @@ moz_container_map_surface(MozContainer * + if (container->subsurface && container->surface) + return true; + ++ if (!container->parent_surface_committed) { ++ if (!container->parent_surface_committed_handler) { ++ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); ++ GdkFrameClock *clock = sGdkWindowGetFrameClock(window); ++ container->parent_surface_committed_handler = ++ g_signal_connect_after(clock, "after-paint", ++ G_CALLBACK(moz_container_commited_handler), ++ container); ++ } ++ return false; ++ } ++ + if (!container->surface) { + struct wl_compositor *compositor; + compositor = sGdkWaylandDisplayGetWlCompositor(display); +@@ -289,8 +320,22 @@ moz_container_map_surface(MozContainer * + static void + moz_container_unmap_surface(MozContainer *container) + { ++ g_clear_pointer(&container->eglwindow, wl_egl_window_destroy); + g_clear_pointer(&container->subsurface, wl_subsurface_destroy); + g_clear_pointer(&container->surface, wl_surface_destroy); ++ ++ if (container->parent_surface_committed_handler) { ++ static auto sGdkWindowGetFrameClock = ++ (GdkFrameClock *(*)(GdkWindow *)) ++ dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock"); ++ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); ++ GdkFrameClock *clock = sGdkWindowGetFrameClock(window); ++ ++ g_signal_handler_disconnect(clock, ++ container->parent_surface_committed_handler); ++ container->parent_surface_committed_handler = 0; ++ } ++ container->parent_surface_committed = false; + } + + #endif +@@ -434,6 +479,11 @@ moz_container_size_allocate (GtkWidget + gdk_window_get_position(gtk_widget_get_window(widget), &x, &y); + wl_subsurface_set_position(container->subsurface, x, y); + } ++ if (container->eglwindow) { ++ wl_egl_window_resize(container->eglwindow, ++ allocation->width, allocation->height, ++ 0, 0); ++ } + #endif + } + +@@ -555,8 +605,40 @@ moz_container_get_wl_surface(MozContaine + return nullptr; + + moz_container_map_surface(container); ++ // Set the scale factor for the buffer right after we create it. ++ if (container->surface) { ++ static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) ++ dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor"); ++ if (sGdkWindowGetScaleFactorPtr && window) { ++ gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window); ++ wl_surface_set_buffer_scale(container->surface, scaleFactor); ++ } ++ } + } + + return container->surface; + } ++ ++struct wl_egl_window * ++moz_container_get_wl_egl_window(MozContainer *container) ++{ ++ if (!container->eglwindow) { ++ struct wl_surface *wlsurf = moz_container_get_wl_surface(container); ++ if (!wlsurf) ++ return nullptr; ++ ++ GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container)); ++ container->eglwindow ++ = wl_egl_window_create(wlsurf, ++ gdk_window_get_width(window), ++ gdk_window_get_height(window)); ++ } ++ return container->eglwindow; ++} ++ ++gboolean ++moz_container_has_wl_egl_window(MozContainer *container) ++{ ++ return container->eglwindow ? true : false; ++} + #endif +diff -up thunderbird-60.3.0/widget/gtk/mozcontainer.h.wayland thunderbird-60.3.0/widget/gtk/mozcontainer.h +--- thunderbird-60.3.0/widget/gtk/mozcontainer.h.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/mozcontainer.h 2018-11-20 12:04:43.733787362 +0100 +@@ -72,6 +72,9 @@ struct _MozContainer + struct wl_subcompositor *subcompositor; + struct wl_surface *surface; + struct wl_subsurface *subsurface; ++ struct wl_egl_window *eglwindow; ++ gboolean parent_surface_committed; ++ gulong parent_surface_committed_handler; + #endif + }; + +@@ -95,6 +98,8 @@ void moz_container_move ( + + #ifdef MOZ_WAYLAND + struct wl_surface* moz_container_get_wl_surface(MozContainer *container); ++struct wl_egl_window* moz_container_get_wl_egl_window(MozContainer *container); ++gboolean moz_container_has_wl_egl_window(MozContainer *container); + #endif + + #endif /* __MOZ_CONTAINER_H__ */ +diff -up thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c.wayland thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c +--- thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/mozgtk/mozgtk.c 2018-11-20 12:04:43.734787359 +0100 +@@ -135,6 +135,8 @@ STUB(gdk_x11_get_xatom_by_name) + STUB(gdk_x11_get_xatom_by_name_for_display) + STUB(gdk_x11_lookup_xdisplay) + STUB(gdk_x11_screen_get_xscreen) ++STUB(gdk_x11_screen_get_screen_number) ++STUB(gdk_x11_screen_lookup_visual) + STUB(gdk_x11_screen_supports_net_wm_hint) + STUB(gdk_x11_visual_get_xvisual) + STUB(gdk_x11_window_foreign_new_for_display) +@@ -266,6 +268,7 @@ STUB(gtk_im_context_set_client_window) + STUB(gtk_im_context_set_cursor_location) + STUB(gtk_im_context_set_surrounding) + STUB(gtk_im_context_simple_new) ++STUB(gtk_im_multicontext_get_context_id) + STUB(gtk_im_multicontext_get_type) + STUB(gtk_im_multicontext_new) + STUB(gtk_info_bar_get_type) +diff -up thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c.wayland thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c +--- thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/mozwayland/mozwayland.c 2018-11-20 12:04:43.734787359 +0100 +@@ -271,3 +271,21 @@ wl_log_set_handler_client(wl_log_func_t + { + } + ++MOZ_EXPORT struct wl_egl_window * ++wl_egl_window_create(struct wl_surface *surface, ++ int width, int height) ++{ ++ return NULL; ++} ++ ++MOZ_EXPORT void ++wl_egl_window_destroy(struct wl_egl_window *egl_window) ++{ ++} ++ ++MOZ_EXPORT void ++wl_egl_window_resize(struct wl_egl_window *egl_window, ++ int width, int height, ++ int dx, int dy) ++{ ++} +diff -up thunderbird-60.3.0/widget/gtk/nsAppShell.cpp.wayland thunderbird-60.3.0/widget/gtk/nsAppShell.cpp +--- thunderbird-60.3.0/widget/gtk/nsAppShell.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsAppShell.cpp 2018-11-20 12:04:43.734787359 +0100 +@@ -14,7 +14,8 @@ + #include "nsWindow.h" + #include "mozilla/Logging.h" + #include "prenv.h" +-#include "mozilla/HangMonitor.h" ++#include "mozilla/BackgroundHangMonitor.h" ++#include "mozilla/Hal.h" + #include "mozilla/Unused.h" + #include "mozilla/WidgetUtils.h" + #include "GeckoProfiler.h" +@@ -46,13 +47,14 @@ static GPollFunc sPollFunc; + static gint + PollWrapper(GPollFD *ufds, guint nfsd, gint timeout_) + { +- mozilla::HangMonitor::Suspend(); ++ mozilla::BackgroundHangMonitor().NotifyWait(); + gint result; + { ++ AUTO_PROFILER_LABEL("PollWrapper", IDLE); + AUTO_PROFILER_THREAD_SLEEP; + result = (*sPollFunc)(ufds, nfsd, timeout_); + } +- mozilla::HangMonitor::NotifyActivity(); ++ mozilla::BackgroundHangMonitor().NotifyActivity(); + return result; + } + +@@ -133,6 +135,8 @@ nsAppShell::EventProcessorCallback(GIOCh + + nsAppShell::~nsAppShell() + { ++ mozilla::hal::Shutdown(); ++ + if (mTag) + g_source_remove(mTag); + if (mPipeFDs[0]) +@@ -150,6 +154,8 @@ nsAppShell::Init() + // is a no-op. + g_type_init(); + ++ mozilla::hal::Init(); ++ + #ifdef MOZ_ENABLE_DBUS + if (XRE_IsParentProcess()) { + nsCOMPtr powerManagerService = +diff -up thunderbird-60.3.0/widget/gtk/nsClipboard.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboard.cpp +--- thunderbird-60.3.0/widget/gtk/nsClipboard.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsClipboard.cpp 2018-11-20 12:04:43.734787359 +0100 +@@ -671,11 +671,9 @@ void ConvertHTMLtoUCS2(const char* data, + *unicodeData = + reinterpret_cast + (moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t))); +- if (*unicodeData) { +- memcpy(*unicodeData, data + sizeof(char16_t), +- outUnicodeLen * sizeof(char16_t)); +- (*unicodeData)[outUnicodeLen] = '\0'; +- } ++ memcpy(*unicodeData, data + sizeof(char16_t), ++ outUnicodeLen * sizeof(char16_t)); ++ (*unicodeData)[outUnicodeLen] = '\0'; + } else if (charset.EqualsLiteral("UNKNOWN")) { + outUnicodeLen = 0; + return; +@@ -701,27 +699,25 @@ void ConvertHTMLtoUCS2(const char* data, + if (needed.value()) { + *unicodeData = reinterpret_cast( + moz_xmalloc((needed.value() + 1) * sizeof(char16_t))); +- if (*unicodeData) { +- uint32_t result; +- size_t read; +- size_t written; +- bool hadErrors; +- Tie(result, read, written, hadErrors) = +- decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)), +- MakeSpan(*unicodeData, needed.value()), +- true); +- MOZ_ASSERT(result == kInputEmpty); +- MOZ_ASSERT(read == size_t(dataLength)); +- MOZ_ASSERT(written <= needed.value()); +- Unused << hadErrors; ++ uint32_t result; ++ size_t read; ++ size_t written; ++ bool hadErrors; ++ Tie(result, read, written, hadErrors) = ++ decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)), ++ MakeSpan(*unicodeData, needed.value()), ++ true); ++ MOZ_ASSERT(result == kInputEmpty); ++ MOZ_ASSERT(read == size_t(dataLength)); ++ MOZ_ASSERT(written <= needed.value()); ++ Unused << hadErrors; + #ifdef DEBUG_CLIPBOARD +- if (read != dataLength) +- printf("didn't consume all the bytes\n"); ++ if (read != dataLength) ++ printf("didn't consume all the bytes\n"); + #endif +- outUnicodeLen = written; +- // null terminate. +- (*unicodeData)[outUnicodeLen] = '\0'; +- } ++ outUnicodeLen = written; ++ // null terminate. ++ (*unicodeData)[outUnicodeLen] = '\0'; + } // if valid length + } + } +diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp +--- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp 2018-11-20 12:04:43.735787356 +0100 +@@ -23,6 +23,7 @@ + #include "mozilla/Services.h" + #include "mozilla/RefPtr.h" + #include "mozilla/TimeStamp.h" ++#include "nsDragService.h" + + #include "imgIContainer.h" + +@@ -46,6 +47,44 @@ nsRetrievalContextWayland::sTextMimeType + "COMPOUND_TEXT" + }; + ++static inline GdkDragAction ++wl_to_gdk_actions(uint32_t dnd_actions) ++{ ++ GdkDragAction actions = GdkDragAction(0); ++ ++ if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) ++ actions = GdkDragAction(actions|GDK_ACTION_COPY); ++ if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) ++ actions = GdkDragAction(actions|GDK_ACTION_MOVE); ++ ++ return actions; ++} ++ ++static inline uint32_t ++gdk_to_wl_actions(GdkDragAction action) ++{ ++ uint32_t dnd_actions = 0; ++ ++ if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE)) ++ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; ++ if (action & GDK_ACTION_MOVE) ++ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; ++ ++ return dnd_actions; ++} ++ ++static GtkWidget* ++get_gtk_widget_for_wl_surface(struct wl_surface *surface) ++{ ++ GdkWindow *gdkParentWindow = ++ static_cast(wl_surface_get_user_data(surface)); ++ ++ gpointer user_data = nullptr; ++ gdk_window_get_user_data(gdkParentWindow, &user_data); ++ ++ return GTK_WIDGET(user_data); ++} ++ + void + DataOffer::AddMIMEType(const char *aMimeType) + { +@@ -114,7 +153,7 @@ DataOffer::GetData(wl_display* aDisplay, + + GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]); + GError* error = nullptr; +- char* clipboardData; ++ char* clipboardData = nullptr; + + g_io_channel_set_encoding(channel, nullptr, &error); + if (!error) { +@@ -155,6 +194,61 @@ WaylandDataOffer::RequestDataTransfer(co + return false; + } + ++void ++WaylandDataOffer::DragOfferAccept(const char* aMimeType, uint32_t aTime) ++{ ++ wl_data_offer_accept(mWaylandDataOffer, aTime, aMimeType); ++} ++ ++/* We follow logic of gdk_wayland_drag_context_commit_status()/gdkdnd-wayland.c ++ * here. ++ */ ++void ++WaylandDataOffer::SetDragStatus(GdkDragAction aAction, uint32_t aTime) ++{ ++ uint32_t dnd_actions = gdk_to_wl_actions(aAction); ++ uint32_t all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | ++ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; ++ ++ wl_data_offer_set_actions(mWaylandDataOffer, all_actions, dnd_actions); ++ ++ /* Workaround Wayland D&D architecture here. To get the data_device_drop() ++ signal (which routes to nsDragService::GetData() call) we need to ++ accept at least one mime type before data_device_leave(). ++ ++ Real wl_data_offer_accept() for actualy requested data mime type is ++ called from nsDragService::GetData(). ++ */ ++ if (mTargetMIMETypes[0]) { ++ wl_data_offer_accept(mWaylandDataOffer, aTime, ++ gdk_atom_name(mTargetMIMETypes[0])); ++ } ++} ++ ++void ++WaylandDataOffer::SetSelectedDragAction(uint32_t aWaylandAction) ++{ ++ mSelectedDragAction = aWaylandAction; ++} ++ ++GdkDragAction ++WaylandDataOffer::GetSelectedDragAction() ++{ ++ return wl_to_gdk_actions(mSelectedDragAction); ++} ++ ++void ++WaylandDataOffer::SetAvailableDragActions(uint32_t aWaylandActions) ++{ ++ mAvailableDragAction = aWaylandActions; ++} ++ ++GdkDragAction ++WaylandDataOffer::GetAvailableDragActions() ++{ ++ return wl_to_gdk_actions(mAvailableDragAction); ++} ++ + static void + data_offer_offer (void *data, + struct wl_data_offer *wl_data_offer, +@@ -164,25 +258,39 @@ data_offer_offer (void * + offer->AddMIMEType(type); + } + ++/* Advertise all available drag and drop actions from source. ++ * We don't use that but follow gdk_wayland_drag_context_commit_status() ++ * from gdkdnd-wayland.c here. ++ */ + static void + data_offer_source_actions(void *data, + struct wl_data_offer *wl_data_offer, + uint32_t source_actions) + { ++ auto *offer = static_cast(data); ++ offer->SetAvailableDragActions(source_actions); + } + ++/* Advertise recently selected drag and drop action by compositor, based ++ * on source actions and user choice (key modifiers, etc.). ++ */ + static void + data_offer_action(void *data, + struct wl_data_offer *wl_data_offer, + uint32_t dnd_action) + { ++ auto *offer = static_cast(data); ++ offer->SetSelectedDragAction(dnd_action); + } + + /* wl_data_offer callback description: + * + * data_offer_offer - Is called for each MIME type available at wl_data_offer. +- * data_offer_source_actions - Exposes all available D&D actions. +- * data_offer_action - Expose one actually selected D&D action. ++ * data_offer_source_actions - This event indicates the actions offered by ++ * the data source. ++ * data_offer_action - This event indicates the action selected by ++ * the compositor after matching the source/destination ++ * side actions. + */ + static const struct wl_data_offer_listener data_offer_listener = { + data_offer_offer, +@@ -246,12 +354,94 @@ PrimaryDataOffer::~PrimaryDataOffer(void + } + } + ++NS_IMPL_ISUPPORTS(nsWaylandDragContext, nsISupports); ++ ++nsWaylandDragContext::nsWaylandDragContext(WaylandDataOffer* aDataOffer, ++ wl_display *aDisplay) ++ : mDataOffer(aDataOffer) ++ , mDisplay(aDisplay) ++ , mTime(0) ++ , mGtkWidget(nullptr) ++ , mX(0) ++ , mY(0) ++{ ++} ++ ++void ++nsWaylandDragContext::DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime, ++ nscoord aX, nscoord aY) ++{ ++ mTime = aTime; ++ mGtkWidget = aGtkWidget; ++ mX = aX; ++ mY = aY; ++} ++ + void +-nsRetrievalContextWayland::RegisterDataOffer(wl_data_offer *aWaylandDataOffer) ++nsWaylandDragContext::DropMotion(uint32_t aTime, nscoord aX, nscoord aY) ++{ ++ mTime = aTime; ++ mX = aX; ++ mY = aY; ++} ++ ++void ++nsWaylandDragContext::GetLastDropInfo(uint32_t *aTime, nscoord *aX, nscoord *aY) ++{ ++ *aTime = mTime; ++ *aX = mX; ++ *aY = mY; ++} ++ ++void ++nsWaylandDragContext::SetDragStatus(GdkDragAction aAction) ++{ ++ mDataOffer->SetDragStatus(aAction, mTime); ++} ++ ++GdkDragAction ++nsWaylandDragContext::GetSelectedDragAction() ++{ ++ GdkDragAction gdkAction = mDataOffer->GetSelectedDragAction(); ++ ++ // We emulate gdk_drag_context_get_actions() here. ++ if (!gdkAction) { ++ gdkAction = mDataOffer->GetAvailableDragActions(); ++ } ++ ++ return gdkAction; ++} ++ ++GList* ++nsWaylandDragContext::GetTargets() ++{ ++ int targetNums; ++ GdkAtom *atoms = mDataOffer->GetTargets(&targetNums); ++ ++ GList* targetList = nullptr; ++ for (int i = 0; i < targetNums; i++) { ++ targetList = g_list_append(targetList, GDK_ATOM_TO_POINTER(atoms[i])); ++ } ++ ++ return targetList; ++} ++ ++char* ++nsWaylandDragContext::GetData(const char* aMimeType, uint32_t* aContentLength) ++{ ++ mDataOffer->DragOfferAccept(aMimeType, mTime); ++ return mDataOffer->GetData(mDisplay, aMimeType, aContentLength); ++} ++ ++void ++nsRetrievalContextWayland::RegisterNewDataOffer(wl_data_offer *aWaylandDataOffer) + { + DataOffer* dataOffer = + static_cast(g_hash_table_lookup(mActiveOffers, + aWaylandDataOffer)); ++ MOZ_ASSERT(dataOffer == nullptr, ++ "Registered WaylandDataOffer already exists. Wayland protocol error?"); ++ + if (!dataOffer) { + dataOffer = new WaylandDataOffer(aWaylandDataOffer); + g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer); +@@ -259,12 +449,15 @@ nsRetrievalContextWayland::RegisterDataO + } + + void +-nsRetrievalContextWayland::RegisterDataOffer( ++nsRetrievalContextWayland::RegisterNewDataOffer( + gtk_primary_selection_offer *aPrimaryDataOffer) + { + DataOffer* dataOffer = + static_cast(g_hash_table_lookup(mActiveOffers, + aPrimaryDataOffer)); ++ MOZ_ASSERT(dataOffer == nullptr, ++ "Registered PrimaryDataOffer already exists. Wayland protocol error?"); ++ + if (!dataOffer) { + dataOffer = new PrimaryDataOffer(aPrimaryDataOffer); + g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer); +@@ -274,6 +467,9 @@ nsRetrievalContextWayland::RegisterDataO + void + nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer) + { ++ // Delete existing clipboard data offer ++ mClipboardOffer = nullptr; ++ + DataOffer* dataOffer = + static_cast(g_hash_table_lookup(mActiveOffers, + aWaylandDataOffer)); +@@ -288,10 +484,12 @@ void + nsRetrievalContextWayland::SetPrimaryDataOffer( + gtk_primary_selection_offer *aPrimaryDataOffer) + { +- if (aPrimaryDataOffer == nullptr) { +- // Release any primary offer we have. +- mPrimaryOffer = nullptr; +- } else { ++ // Release any primary offer we have. ++ mPrimaryOffer = nullptr; ++ ++ // aPrimaryDataOffer can be null which means we lost ++ // the mouse selection. ++ if (aPrimaryDataOffer) { + DataOffer* dataOffer = + static_cast(g_hash_table_lookup(mActiveOffers, + aPrimaryDataOffer)); +@@ -304,12 +502,31 @@ nsRetrievalContextWayland::SetPrimaryDat + } + + void +-nsRetrievalContextWayland::ClearDataOffers(void) ++nsRetrievalContextWayland::AddDragAndDropDataOffer(wl_data_offer *aDropDataOffer) + { +- if (mClipboardOffer) +- mClipboardOffer = nullptr; +- if (mPrimaryOffer) +- mPrimaryOffer = nullptr; ++ // Remove any existing D&D contexts. ++ mDragContext = nullptr; ++ ++ WaylandDataOffer* dataOffer = ++ static_cast(g_hash_table_lookup(mActiveOffers, ++ aDropDataOffer)); ++ NS_ASSERTION(dataOffer, "We're missing drag and drop data offer!"); ++ if (dataOffer) { ++ g_hash_table_remove(mActiveOffers, aDropDataOffer); ++ mDragContext = new nsWaylandDragContext(dataOffer, mDisplay); ++ } ++} ++ ++nsWaylandDragContext* ++nsRetrievalContextWayland::GetDragContext(void) ++{ ++ return mDragContext; ++} ++ ++void ++nsRetrievalContextWayland::ClearDragAndDropDataOffer(void) ++{ ++ mDragContext = nullptr; + } + + // We have a new fresh data content. +@@ -321,7 +538,7 @@ data_device_data_offer (void + { + nsRetrievalContextWayland *context = + static_cast(data); +- context->RegisterDataOffer(offer); ++ context->RegisterNewDataOffer(offer); + } + + // The new fresh data content is clipboard. +@@ -341,31 +558,78 @@ data_device_enter (void + struct wl_data_device *data_device, + uint32_t time, + struct wl_surface *surface, +- int32_t x, +- int32_t y, ++ int32_t x_fixed, ++ int32_t y_fixed, + struct wl_data_offer *offer) + { ++ nsRetrievalContextWayland *context = ++ static_cast(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(data); ++ ++ nsWaylandDragContext* dropContext = context->GetDragContext(); ++ WindowDragLeaveHandler(dropContext->GetWidget()); ++ ++ context->ClearDragAndDropDataOffer(); + } + + static void + data_device_motion (void *data, + struct wl_data_device *data_device, + uint32_t time, +- int32_t x, +- int32_t y) ++ int32_t x_fixed, ++ int32_t y_fixed) + { ++ nsRetrievalContextWayland *context = ++ static_cast(data); ++ ++ nsWaylandDragContext* dropContext = context->GetDragContext(); ++ ++ nscoord x = wl_fixed_to_int(x_fixed); ++ nscoord y = wl_fixed_to_int(y_fixed); ++ dropContext->DropMotion(time, x, y); ++ ++ WindowDragMotionHandler(dropContext->GetWidget(), nullptr, ++ dropContext, x, y, time); + } + + static void + data_device_drop (void *data, + struct wl_data_device *data_device) + { ++ nsRetrievalContextWayland *context = ++ static_cast(data); ++ ++ nsWaylandDragContext* dropContext = context->GetDragContext(); ++ ++ uint32_t time; ++ nscoord x, y; ++ dropContext->GetLastDropInfo(&time, &x, &y); ++ ++ WindowDragDropHandler(dropContext->GetWidget(), nullptr, dropContext, ++ x, y, time); + } + + /* wl_data_device callback description: +@@ -405,7 +669,7 @@ primary_selection_data_offer (void + // create and add listener + nsRetrievalContextWayland *context = + static_cast(data); +- context->RegisterDataOffer(gtk_primary_offer); ++ context->RegisterNewDataOffer(gtk_primary_offer); + } + + static void +@@ -418,6 +682,18 @@ primary_selection_selection (void + context->SetPrimaryDataOffer(gtk_primary_offer); + } + ++/* gtk_primary_selection_device callback description: ++ * ++ * primary_selection_data_offer - It's called when there's a new ++ * gtk_primary_selection_offer available. ++ * We need to attach gtk_primary_selection_offer_listener ++ * to it to get available MIME types. ++ * ++ * primary_selection_selection - It's called when the new gtk_primary_selection_offer ++ * is a primary selection content. It can be also called with ++ * gtk_primary_selection_offer = null which means there's no ++ * primary selection. ++ */ + static const struct + gtk_primary_selection_device_listener primary_selection_device_listener = { + primary_selection_data_offer, +@@ -430,81 +706,6 @@ nsRetrievalContextWayland::HasSelectionS + return mPrimarySelectionDataDeviceManager != nullptr; + } + +-static void +-keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, +- uint32_t format, int fd, uint32_t size) +-{ +-} +- +-static void +-keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, +- uint32_t serial, struct wl_surface *surface, +- struct wl_array *keys) +-{ +-} +- +-static void +-keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, +- uint32_t serial, struct wl_surface *surface) +-{ +- // We lost focus so our clipboard data are outdated +- nsRetrievalContextWayland *context = +- static_cast(data); +- +- context->ClearDataOffers(); +-} +- +-static void +-keyboard_handle_key(void *data, struct wl_keyboard *keyboard, +- uint32_t serial, uint32_t time, uint32_t key, +- uint32_t state) +-{ +-} +- +-static void +-keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, +- uint32_t serial, uint32_t mods_depressed, +- uint32_t mods_latched, uint32_t mods_locked, +- uint32_t group) +-{ +-} +- +-static const struct wl_keyboard_listener keyboard_listener = { +- keyboard_handle_keymap, +- keyboard_handle_enter, +- keyboard_handle_leave, +- keyboard_handle_key, +- keyboard_handle_modifiers, +-}; +- +-void +-nsRetrievalContextWayland::ConfigureKeyboard(wl_seat_capability caps) +-{ +- // ConfigureKeyboard() is called when wl_seat configuration is changed. +- // We look for the keyboard only, get it when is't available and release it +- // when it's lost (we don't have focus for instance). +- if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { +- mKeyboard = wl_seat_get_keyboard(mSeat); +- wl_keyboard_add_listener(mKeyboard, &keyboard_listener, this); +- } else if (mKeyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { +- wl_keyboard_destroy(mKeyboard); +- mKeyboard = nullptr; +- } +-} +- +-static void +-seat_handle_capabilities(void *data, struct wl_seat *seat, +- unsigned int caps) +-{ +- nsRetrievalContextWayland *context = +- static_cast(data); +- context->ConfigureKeyboard((wl_seat_capability)caps); +-} +- +-static const struct wl_seat_listener seat_listener = { +- seat_handle_capabilities, +-}; +- + void + nsRetrievalContextWayland::InitDataDeviceManager(wl_registry *registry, + uint32_t id, +@@ -530,7 +731,6 @@ nsRetrievalContextWayland::InitSeat(wl_r + void *data) + { + mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1); +- wl_seat_add_listener(mSeat, &seat_listener, data); + } + + static void +@@ -573,6 +773,7 @@ nsRetrievalContextWayland::nsRetrievalCo + , mActiveOffers(g_hash_table_new(NULL, NULL)) + , mClipboardOffer(nullptr) + , mPrimaryOffer(nullptr) ++ , mDragContext(nullptr) + , mClipboardRequestNumber(0) + , mClipboardData(nullptr) + , mClipboardDataLength(0) +@@ -616,8 +817,21 @@ nsRetrievalContextWayland::nsRetrievalCo + mInitialized = true; + } + ++static gboolean ++offer_hash_remove(gpointer wl_offer, gpointer aDataOffer, gpointer user_data) ++{ ++#ifdef DEBUG ++ nsPrintfCString msg("nsRetrievalContextWayland(): leaked nsDataOffer %p\n", ++ aDataOffer); ++ NS_WARNING(msg.get()); ++#endif ++ delete static_cast(aDataOffer); ++ return true; ++} ++ + nsRetrievalContextWayland::~nsRetrievalContextWayland(void) + { ++ g_hash_table_foreach_remove(mActiveOffers, offer_hash_remove, nullptr); + g_hash_table_destroy(mActiveOffers); + } + +@@ -667,12 +881,14 @@ nsRetrievalContextWayland::TransferFastT + int aClipboardRequestNumber, GtkSelectionData *aSelectionData) + { + if (mClipboardRequestNumber == aClipboardRequestNumber) { +- mClipboardDataLength = gtk_selection_data_get_length(aSelectionData); +- if (mClipboardDataLength > 0) { ++ int dataLength = gtk_selection_data_get_length(aSelectionData); ++ if (dataLength > 0) { ++ mClipboardDataLength = dataLength; + mClipboardData = reinterpret_cast( +- g_malloc(sizeof(char)*mClipboardDataLength)); ++ g_malloc(sizeof(char)*(mClipboardDataLength+1))); + memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData), + sizeof(char)*mClipboardDataLength); ++ mClipboardData[mClipboardDataLength] = '\0'; + } + } else { + NS_WARNING("Received obsoleted clipboard data!"); +@@ -727,7 +943,7 @@ nsRetrievalContextWayland::GetClipboardT + if (!dataOffer) + return nullptr; + +- for (unsigned int i = 0; i < sizeof(sTextMimeTypes); i++) { ++ for (unsigned int i = 0; i < TEXT_MIME_TYPES_NUM; i++) { + if (dataOffer->HasTarget(sTextMimeTypes[i])) { + uint32_t unused; + return GetClipboardData(sTextMimeTypes[i], aWhichClipboard, +diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h +--- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h 2018-11-20 12:04:43.735787356 +0100 +@@ -32,6 +32,7 @@ public: + private: + virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0; + ++protected: + nsTArray mTargetMIMETypes; + }; + +@@ -40,25 +41,66 @@ class WaylandDataOffer : public DataOffe + public: + WaylandDataOffer(wl_data_offer* aWaylandDataOffer); + +-private: ++ void DragOfferAccept(const char* aMimeType, uint32_t aTime); ++ void SetDragStatus(GdkDragAction aAction, uint32_t aTime); ++ ++ GdkDragAction GetSelectedDragAction(); ++ void SetSelectedDragAction(uint32_t aWaylandAction); ++ ++ void SetAvailableDragActions(uint32_t aWaylandActions); ++ GdkDragAction GetAvailableDragActions(); ++ + virtual ~WaylandDataOffer(); ++private: + bool RequestDataTransfer(const char* aMimeType, int fd) override; + + wl_data_offer* mWaylandDataOffer; ++ uint32_t mSelectedDragAction; ++ uint32_t mAvailableDragAction; + }; + + class PrimaryDataOffer : public DataOffer + { + public: + PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer); ++ void SetAvailableDragActions(uint32_t aWaylandActions) {}; + +-private: + virtual ~PrimaryDataOffer(); ++private: + bool RequestDataTransfer(const char* aMimeType, int fd) override; + + gtk_primary_selection_offer* mPrimaryDataOffer; + }; + ++class nsWaylandDragContext : public nsISupports ++{ ++ NS_DECL_ISUPPORTS ++ ++public: ++ nsWaylandDragContext(WaylandDataOffer* aWaylandDataOffer, ++ wl_display *aDisplay); ++ ++ void DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime, ++ nscoord aX, nscoord aY); ++ void DropMotion(uint32_t aTime, nscoord aX, nscoord aY); ++ void GetLastDropInfo(uint32_t *aTime, nscoord *aX, nscoord *aY); ++ ++ void SetDragStatus(GdkDragAction action); ++ GdkDragAction GetSelectedDragAction(); ++ ++ GtkWidget* GetWidget() { return mGtkWidget; } ++ GList* GetTargets(); ++ char* GetData(const char* aMimeType, uint32_t* aContentLength); ++private: ++ virtual ~nsWaylandDragContext() {}; ++ ++ nsAutoPtr mDataOffer; ++ wl_display* mDisplay; ++ uint32_t mTime; ++ GtkWidget* mGtkWidget; ++ nscoord mX, mY; ++}; ++ + class nsRetrievalContextWayland : public nsRetrievalContext + { + public: +@@ -74,15 +116,16 @@ public: + int* aTargetNum) override; + virtual bool HasSelectionSupport(void) override; + +- void RegisterDataOffer(wl_data_offer *aWaylandDataOffer); +- void RegisterDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer); ++ void RegisterNewDataOffer(wl_data_offer *aWaylandDataOffer); ++ void RegisterNewDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer); + + void SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer); + void SetPrimaryDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer); ++ void AddDragAndDropDataOffer(wl_data_offer *aWaylandDataOffer); ++ nsWaylandDragContext* GetDragContext(); + +- void ClearDataOffers(); ++ void ClearDragAndDropDataOffer(); + +- void ConfigureKeyboard(wl_seat_capability caps); + void TransferFastTrackClipboard(int aClipboardRequestNumber, + GtkSelectionData *aSelectionData); + +@@ -103,6 +146,7 @@ private: + GHashTable* mActiveOffers; + nsAutoPtr mClipboardOffer; + nsAutoPtr mPrimaryOffer; ++ RefPtr mDragContext; + + int mClipboardRequestNumber; + char* mClipboardData; +diff -up thunderbird-60.3.0/widget/gtk/nsDeviceContextSpecG.cpp.wayland thunderbird-60.3.0/widget/gtk/nsDeviceContextSpecG.cpp +--- thunderbird-60.3.0/widget/gtk/nsDeviceContextSpecG.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsDeviceContextSpecG.cpp 2018-11-20 12:04:43.735787356 +0100 +@@ -33,6 +33,9 @@ + #include + #include + ++// To check if we need to use flatpak portal for printing ++#include "nsIGIOService.h" ++ + using namespace mozilla; + + using mozilla::gfx::IntSize; +@@ -355,6 +358,20 @@ NS_IMETHODIMP nsDeviceContextSpecGTK::En + // If you're not familiar with umasks, they contain the bits of what NOT to set in the permissions + // (thats because files and directories have different numbers of bits for their permissions) + destFile->SetPermissions(0666 & ~(mask)); ++ ++ // Notify flatpak printing portal that file is completely written ++ nsCOMPtr giovfs = ++ do_GetService(NS_GIOSERVICE_CONTRACTID); ++ bool shouldUsePortal; ++ if (giovfs) { ++ giovfs->ShouldUseFlatpakPortal(&shouldUsePortal); ++ if (shouldUsePortal) { ++ // Use the name of the file for printing to match with nsFlatpakPrintPortal ++ nsCOMPtr os = mozilla::services::GetObserverService(); ++ // Pass filename to be sure that observer process the right data ++ os->NotifyObservers(nullptr, "print-to-file-finished", targetPath.get()); ++ } ++ } + } + return NS_OK; + } +@@ -421,7 +438,7 @@ nsPrinterEnumeratorGTK::InitPrintSetting + } + + if (path) { +- CopyUTF8toUTF16(path, filename); ++ CopyUTF8toUTF16(MakeStringSpan(path), filename); + filename.AppendLiteral("/mozilla.pdf"); + } else { + filename.AssignLiteral("mozilla.pdf"); +diff -up thunderbird-60.3.0/widget/gtk/nsDragService.cpp.wayland thunderbird-60.3.0/widget/gtk/nsDragService.cpp +--- thunderbird-60.3.0/widget/gtk/nsDragService.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsDragService.cpp 2018-11-20 12:04:43.736787353 +0100 +@@ -34,7 +34,6 @@ + #include "nsPresContext.h" + #include "nsIContent.h" + #include "nsIDocument.h" +-#include "nsISelection.h" + #include "nsViewManager.h" + #include "nsIFrame.h" + #include "nsGtkUtils.h" +@@ -43,6 +42,9 @@ + #include "gfxPlatform.h" + #include "ScreenHelperGTK.h" + #include "nsArrayUtils.h" ++#ifdef MOZ_WAYLAND ++#include "nsClipboardWayland.h" ++#endif + + using namespace mozilla; + using namespace mozilla::gfx; +@@ -99,6 +101,10 @@ invisibleSourceDragDataGet(GtkWidget + nsDragService::nsDragService() + : mScheduledTask(eDragTaskNone) + , mTaskSource(0) ++#ifdef MOZ_WAYLAND ++ , mPendingWaylandDragContext(nullptr) ++ , mTargetWaylandDragContext(nullptr) ++#endif + { + // We have to destroy the hidden widget before the event loop stops + // running. +@@ -179,7 +185,7 @@ nsDragService::Observe(nsISupports *aSub + } + TargetResetData(); + } else { +- NS_NOTREACHED("unexpected topic"); ++ MOZ_ASSERT_UNREACHABLE("unexpected topic"); + return NS_ERROR_UNEXPECTED; + } + +@@ -269,13 +275,12 @@ OnSourceGrabEventAfter(GtkWidget *widget + } + + static GtkWindow* +-GetGtkWindow(nsIDOMDocument *aDocument) ++GetGtkWindow(nsIDocument *aDocument) + { +- nsCOMPtr doc = do_QueryInterface(aDocument); +- if (!doc) ++ if (!aDocument) + return nullptr; + +- nsCOMPtr presShell = doc->GetShell(); ++ nsCOMPtr presShell = aDocument->GetShell(); + if (!presShell) + return nullptr; + +@@ -304,10 +309,9 @@ GetGtkWindow(nsIDOMDocument *aDocument) + // nsIDragService + + NS_IMETHODIMP +-nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, ++nsDragService::InvokeDragSession(nsINode *aDOMNode, + const nsACString& aPrincipalURISpec, + nsIArray * aArrayTransferables, +- nsIScriptableRegion * aRegion, + uint32_t aActionType, + nsContentPolicyType aContentPolicyType = + nsIContentPolicy::TYPE_OTHER) +@@ -323,14 +327,14 @@ nsDragService::InvokeDragSession(nsIDOMN + + return nsBaseDragService::InvokeDragSession(aDOMNode, aPrincipalURISpec, + aArrayTransferables, +- aRegion, aActionType, ++ aActionType, + aContentPolicyType); + } + + // nsBaseDragService + nsresult + nsDragService::InvokeDragSessionImpl(nsIArray* aArrayTransferables, +- nsIScriptableRegion* aRegion, ++ const Maybe& aRegion, + uint32_t aActionType) + { + // make sure that we have an array of transferables to use +@@ -346,9 +350,6 @@ nsDragService::InvokeDragSessionImpl(nsI + if (!sourceList) + return NS_OK; + +- // stored temporarily until the drag-begin signal has been received +- mSourceRegion = aRegion; +- + // save our action type + GdkDragAction action = GDK_ACTION_DEFAULT; + +@@ -391,8 +392,6 @@ nsDragService::InvokeDragSessionImpl(nsI + 1, + &event); + +- mSourceRegion = nullptr; +- + nsresult rv; + if (context) { + StartDragSession(); +@@ -516,6 +515,9 @@ nsDragService::EndDragSession(bool aDone + + // We're done with the drag context. + mTargetDragContextForRemote = nullptr; ++#ifdef MOZ_WAYLAND ++ mTargetWaylandDragContextForRemote = nullptr; ++#endif + + return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers); + } +@@ -636,6 +638,14 @@ nsDragService::GetNumDropItems(uint32_t + return NS_OK; + } + ++#ifdef MOZ_WAYLAND ++ // TODO: Wayland implementation of text/uri-list. ++ if (!mTargetDragContext) { ++ *aNumItems = 1; ++ return NS_OK; ++ } ++#endif ++ + bool isList = IsTargetContextList(); + if (isList) + mSourceDataItems->GetLength(aNumItems); +@@ -1027,9 +1037,18 @@ nsDragService::IsDataFlavorSupported(con + } + + // check the target context vs. this flavor, one at a time +- GList *tmp; +- for (tmp = gdk_drag_context_list_targets(mTargetDragContext); +- tmp; tmp = tmp->next) { ++ GList *tmp = nullptr; ++ if (mTargetDragContext) { ++ tmp = gdk_drag_context_list_targets(mTargetDragContext); ++ } ++#ifdef MOZ_WAYLAND ++ else if (mTargetWaylandDragContext) { ++ tmp = mTargetWaylandDragContext->GetTargets(); ++ } ++ GList *tmp_head = tmp; ++#endif ++ ++ for (; tmp; tmp = tmp->next) { + /* Bug 331198 */ + GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data); + gchar *name = nullptr; +@@ -1074,6 +1093,15 @@ nsDragService::IsDataFlavorSupported(con + } + g_free(name); + } ++ ++#ifdef MOZ_WAYLAND ++ // mTargetWaylandDragContext->GetTargets allocates the list ++ // so we need to free it here. ++ if (!mTargetDragContext && tmp_head) { ++ g_list_free(tmp_head); ++ } ++#endif ++ + return NS_OK; + } + +@@ -1105,6 +1133,36 @@ nsDragService::ReplyToDragMotion(GdkDrag + gdk_drag_status(aDragContext, action, mTargetTime); + } + ++#ifdef MOZ_WAYLAND ++void ++nsDragService::ReplyToDragMotion(nsWaylandDragContext* aDragContext) ++{ ++ MOZ_LOG(sDragLm, LogLevel::Debug, ++ ("nsDragService::ReplyToDragMotion %d", mCanDrop)); ++ ++ GdkDragAction action = (GdkDragAction)0; ++ if (mCanDrop) { ++ // notify the dragger if we can drop ++ switch (mDragAction) { ++ case DRAGDROP_ACTION_COPY: ++ action = GDK_ACTION_COPY; ++ break; ++ case DRAGDROP_ACTION_LINK: ++ action = GDK_ACTION_LINK; ++ break; ++ case DRAGDROP_ACTION_NONE: ++ action = (GdkDragAction)0; ++ break; ++ default: ++ action = GDK_ACTION_MOVE; ++ break; ++ } ++ } ++ ++ aDragContext->SetDragStatus(action); ++} ++#endif ++ + void + nsDragService::TargetDataReceived(GtkWidget *aWidget, + GdkDragContext *aContext, +@@ -1136,6 +1194,12 @@ nsDragService::IsTargetContextList(void) + { + bool retval = false; + ++#ifdef MOZ_WAYLAND ++ // TODO: We need a wayland implementation here. ++ if (!mTargetDragContext) ++ return retval; ++#endif ++ + // gMimeListType drags only work for drags within a single process. The + // gtk_drag_get_source_widget() function will return nullptr if the source + // of the drag is another app, so we use it to check if a gMimeListType +@@ -1174,17 +1238,28 @@ nsDragService::GetTargetDragData(GdkAtom + mTargetDragContext.get())); + // reset our target data areas + TargetResetData(); +- gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime); + +- MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration.")); +- PRTime entryTime = PR_Now(); +- while (!mTargetDragDataReceived && mDoingDrag) { +- // check the number of iterations +- MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n")); +- PR_Sleep(20*PR_TicksPerSecond()/1000); /* sleep for 20 ms/iteration */ +- if (PR_Now()-entryTime > NS_DND_TIMEOUT) break; +- gtk_main_iteration(); ++ if (mTargetDragContext) { ++ gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime); ++ ++ MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration.")); ++ PRTime entryTime = PR_Now(); ++ while (!mTargetDragDataReceived && mDoingDrag) { ++ // check the number of iterations ++ MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n")); ++ PR_Sleep(20*PR_TicksPerSecond()/1000); /* sleep for 20 ms/iteration */ ++ if (PR_Now()-entryTime > NS_DND_TIMEOUT) break; ++ gtk_main_iteration(); ++ } ++ } ++#ifdef MOZ_WAYLAND ++ else { ++ mTargetDragData = ++ mTargetWaylandDragContext->GetData(gdk_atom_name(aFlavor), ++ &mTargetDragDataLen); ++ mTargetDragDataReceived = true; + } ++#endif + MOZ_LOG(sDragLm, LogLevel::Debug, ("finished inner iteration\n")); + } + +@@ -1435,7 +1510,7 @@ nsDragService::SourceEndDragSession(GdkD + } + + // Schedule the appropriate drag end dom events. +- Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0); ++ Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0); + } + + static void +@@ -1642,8 +1717,8 @@ void nsDragService::SetDragIcon(GdkDragC + LayoutDeviceIntRect dragRect; + nsPresContext* pc; + RefPtr surface; +- DrawDrag(mSourceNode, mSourceRegion, mScreenPosition, +- &dragRect, &surface, &pc); ++ DrawDrag(mSourceNode, mRegion, ++ mScreenPosition, &dragRect, &surface, &pc); + if (!pc) + return; + +@@ -1785,9 +1860,10 @@ invisibleSourceDragEnd(GtkWidget + gboolean + nsDragService::ScheduleMotionEvent(nsWindow *aWindow, + GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, + LayoutDeviceIntPoint aWindowPoint, guint aTime) + { +- if (mScheduledTask == eDragTaskMotion) { ++ if (aDragContext && mScheduledTask == eDragTaskMotion) { + // The drag source has sent another motion message before we've + // replied to the previous. That shouldn't happen with Xdnd. The + // spec for Motif drags is less clear, but we'll just update the +@@ -1798,7 +1874,7 @@ nsDragService::ScheduleMotionEvent(nsWin + + // Returning TRUE means we'll reply with a status message, unless we first + // get a leave. +- return Schedule(eDragTaskMotion, aWindow, aDragContext, ++ return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext, + aWindowPoint, aTime); + } + +@@ -1808,7 +1884,8 @@ nsDragService::ScheduleLeaveEvent() + // We don't know at this stage whether a drop signal will immediately + // follow. If the drop signal gets sent it will happen before we return + // to the main loop and the scheduled leave task will be replaced. +- if (!Schedule(eDragTaskLeave, nullptr, nullptr, LayoutDeviceIntPoint(), 0)) { ++ if (!Schedule(eDragTaskLeave, nullptr, nullptr, nullptr, ++ LayoutDeviceIntPoint(), 0)) { + NS_WARNING("Drag leave after drop"); + } + } +@@ -1816,10 +1893,11 @@ nsDragService::ScheduleLeaveEvent() + gboolean + nsDragService::ScheduleDropEvent(nsWindow *aWindow, + GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, + LayoutDeviceIntPoint aWindowPoint, guint aTime) + { + if (!Schedule(eDragTaskDrop, aWindow, +- aDragContext, aWindowPoint, aTime)) { ++ aDragContext, aWaylandDragContext, aWindowPoint, aTime)) { + NS_WARNING("Additional drag drop ignored"); + return FALSE; + } +@@ -1833,6 +1911,7 @@ nsDragService::ScheduleDropEvent(nsWindo + gboolean + nsDragService::Schedule(DragTask aTask, nsWindow *aWindow, + GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, + LayoutDeviceIntPoint aWindowPoint, guint aTime) + { + // If there is an existing leave or motion task scheduled, then that +@@ -1851,6 +1930,9 @@ nsDragService::Schedule(DragTask aTask, + mScheduledTask = aTask; + mPendingWindow = aWindow; + mPendingDragContext = aDragContext; ++#ifdef MOZ_WAYLAND ++ mPendingWaylandDragContext = aWaylandDragContext; ++#endif + mPendingWindowPoint = aWindowPoint; + mPendingTime = aTime; + +@@ -1927,6 +2009,9 @@ nsDragService::RunScheduledTask() + // succeeed. + mTargetWidget = mTargetWindow->GetMozContainerWidget(); + mTargetDragContext.steal(mPendingDragContext); ++#ifdef MOZ_WAYLAND ++ mTargetWaylandDragContext = mPendingWaylandDragContext.forget(); ++#endif + mTargetTime = mPendingTime; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model +@@ -1958,10 +2043,20 @@ nsDragService::RunScheduledTask() + if (task == eDragTaskMotion) { + if (TakeDragEventDispatchedToChildProcess()) { + mTargetDragContextForRemote = mTargetDragContext; ++#ifdef MOZ_WAYLAND ++ mTargetWaylandDragContextForRemote = mTargetWaylandDragContext; ++#endif + } else { + // Reply to tell the source whether we can drop and what + // action would be taken. +- ReplyToDragMotion(mTargetDragContext); ++ if (mTargetDragContext) { ++ ReplyToDragMotion(mTargetDragContext); ++ } ++#ifdef MOZ_WAYLAND ++ else if (mTargetWaylandDragContext) { ++ ReplyToDragMotion(mTargetWaylandDragContext); ++ } ++#endif + } + } + } +@@ -1972,8 +2067,10 @@ nsDragService::RunScheduledTask() + // Perhaps we should set the del parameter to TRUE when the drag + // action is move, but we don't know whether the data was successfully + // transferred. +- gtk_drag_finish(mTargetDragContext, success, +- /* del = */ FALSE, mTargetTime); ++ if (mTargetDragContext) { ++ gtk_drag_finish(mTargetDragContext, success, ++ /* del = */ FALSE, mTargetTime); ++ } + + // This drag is over, so clear out our reference to the previous + // window. +@@ -1986,6 +2083,9 @@ nsDragService::RunScheduledTask() + // We're done with the drag context. + mTargetWidget = nullptr; + mTargetDragContext = nullptr; ++#ifdef MOZ_WAYLAND ++ mTargetWaylandDragContext = nullptr; ++#endif + + // If we got another drag signal while running the sheduled task, that + // must have happened while running a nested event loop. Leave the task +@@ -2015,7 +2115,16 @@ nsDragService::UpdateDragAction() + + // default is to do nothing + int action = nsIDragService::DRAGDROP_ACTION_NONE; +- GdkDragAction gdkAction = gdk_drag_context_get_actions(mTargetDragContext); ++ GdkDragAction gdkAction = GDK_ACTION_DEFAULT; ++ if (mTargetDragContext) { ++ gdkAction = gdk_drag_context_get_actions(mTargetDragContext); ++ } ++#ifdef MOZ_WAYLAND ++ else if (mTargetWaylandDragContext) { ++ // We got the selected D&D action from compositor on Wayland. ++ gdkAction = mTargetWaylandDragContext->GetSelectedDragAction(); ++ } ++#endif + + // set the default just in case nothing matches below + if (gdkAction & GDK_ACTION_DEFAULT) +@@ -2044,6 +2153,12 @@ nsDragService::UpdateDragEffect() + ReplyToDragMotion(mTargetDragContextForRemote); + mTargetDragContextForRemote = nullptr; + } ++#ifdef MOZ_WAYLAND ++ else if (mTargetWaylandDragContextForRemote) { ++ ReplyToDragMotion(mTargetWaylandDragContextForRemote); ++ mTargetWaylandDragContextForRemote = nullptr; ++ } ++#endif + return NS_OK; + } + +diff -up thunderbird-60.3.0/widget/gtk/nsDragService.h.wayland thunderbird-60.3.0/widget/gtk/nsDragService.h +--- thunderbird-60.3.0/widget/gtk/nsDragService.h.wayland 2018-10-30 12:45:37.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsDragService.h 2018-11-20 12:04:43.736787353 +0100 +@@ -14,6 +14,7 @@ + #include + + class nsWindow; ++class nsWaylandDragContext; + + namespace mozilla { + namespace gfx { +@@ -60,13 +61,12 @@ public: + + // nsBaseDragService + virtual nsresult InvokeDragSessionImpl(nsIArray* anArrayTransferables, +- nsIScriptableRegion* aRegion, ++ const mozilla::Maybe& aRegion, + uint32_t aActionType) override; + // nsIDragService +- NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode, ++ NS_IMETHOD InvokeDragSession (nsINode *aDOMNode, + const nsACString& aPrincipalURISpec, + nsIArray * anArrayTransferables, +- nsIScriptableRegion * aRegion, + uint32_t aActionType, + nsContentPolicyType aContentPolicyType) override; + NS_IMETHOD StartDragSession() override; +@@ -98,11 +98,13 @@ public: + + gboolean ScheduleMotionEvent(nsWindow *aWindow, + GdkDragContext *aDragContext, ++ nsWaylandDragContext* aPendingWaylandDragContext, + mozilla::LayoutDeviceIntPoint aWindowPoint, + guint aTime); + void ScheduleLeaveEvent(); + gboolean ScheduleDropEvent(nsWindow *aWindow, + GdkDragContext *aDragContext, ++ nsWaylandDragContext* aPendingWaylandDragContext, + mozilla::LayoutDeviceIntPoint aWindowPoint, + guint aTime); + +@@ -158,6 +160,9 @@ private: + RefPtr mPendingWindow; + mozilla::LayoutDeviceIntPoint mPendingWindowPoint; + nsCountedRef mPendingDragContext; ++#ifdef MOZ_WAYLAND ++ RefPtr mPendingWaylandDragContext; ++#endif + guint mPendingTime; + + // mTargetWindow and mTargetWindowPoint record the position of the last +@@ -169,9 +174,15 @@ private: + // motion or drop events. mTime records the corresponding timestamp. + nsCountedRef mTargetWidget; + nsCountedRef mTargetDragContext; ++#ifdef MOZ_WAYLAND ++ RefPtr mTargetWaylandDragContext; ++#endif + // mTargetDragContextForRemote is set while waiting for a reply from + // a child process. + nsCountedRef mTargetDragContextForRemote; ++#ifdef MOZ_WAYLAND ++ RefPtr mTargetWaylandDragContextForRemote; ++#endif + guint mTargetTime; + + // is it OK to drop on us? +@@ -197,8 +208,6 @@ private: + // our source data items + nsCOMPtr mSourceDataItems; + +- nsCOMPtr mSourceRegion; +- + // get a list of the sources in gtk's format + GtkTargetList *GetSourceList(void); + +@@ -212,6 +221,7 @@ private: + + gboolean Schedule(DragTask aTask, nsWindow *aWindow, + GdkDragContext *aDragContext, ++ nsWaylandDragContext* aPendingWaylandDragContext, + mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime); + + // Callback for g_idle_add_full() to run mScheduledTask. +@@ -220,9 +230,11 @@ private: + void UpdateDragAction(); + void DispatchMotionEvents(); + void ReplyToDragMotion(GdkDragContext* aDragContext); ++#ifdef MOZ_WAYLAND ++ void ReplyToDragMotion(nsWaylandDragContext* aDragContext); ++#endif + gboolean DispatchDropEvent(); + static uint32_t GetCurrentModifiers(); + }; + + #endif // nsDragService_h__ +- +diff -up thunderbird-60.3.0/widget/gtk/nsFilePicker.cpp.wayland thunderbird-60.3.0/widget/gtk/nsFilePicker.cpp +--- thunderbird-60.3.0/widget/gtk/nsFilePicker.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsFilePicker.cpp 2018-11-20 12:04:43.736787353 +0100 +@@ -346,7 +346,7 @@ nsFilePicker::GetFiles(nsISimpleEnumerat + NS_ENSURE_ARG_POINTER(aFiles); + + if (mMode == nsIFilePicker::modeOpenMultiple) { +- return NS_NewArrayEnumerator(aFiles, mFiles); ++ return NS_NewArrayEnumerator(aFiles, mFiles, NS_GET_IID(nsIFile)); + } + + return NS_ERROR_FAILURE; +diff -up thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp.wayland thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp +--- thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.cpp 2018-11-20 12:04:43.736787353 +0100 +@@ -28,6 +28,10 @@ + #include "mozilla/MouseEvents.h" + #include "mozilla/TextEvents.h" + ++#ifdef MOZ_WAYLAND ++#include ++#endif ++ + namespace mozilla { + namespace widget { + +@@ -195,7 +199,11 @@ KeymapWrapper::Init() + memset(mModifierMasks, 0, sizeof(mModifierMasks)); + + if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) +- InitBySystemSettings(); ++ InitBySystemSettingsX11(); ++#ifdef MOZ_WAYLAND ++ else ++ InitBySystemSettingsWayland(); ++#endif + + gdk_window_add_filter(nullptr, FilterEvents, this); + +@@ -275,10 +283,10 @@ KeymapWrapper::InitXKBExtension() + } + + void +-KeymapWrapper::InitBySystemSettings() ++KeymapWrapper::InitBySystemSettingsX11() + { + MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, +- ("%p InitBySystemSettings, mGdkKeymap=%p", ++ ("%p InitBySystemSettingsX11, mGdkKeymap=%p", + this, mGdkKeymap)); + + Display* display = +@@ -439,6 +447,208 @@ KeymapWrapper::InitBySystemSettings() + XFree(xkeymap); + } + ++#ifdef MOZ_WAYLAND ++void ++KeymapWrapper::SetModifierMask(xkb_keymap *aKeymap, ModifierIndex aModifierIndex, ++ const char* aModifierName) ++{ ++ static auto sXkbKeymapModGetIndex = ++ (xkb_mod_index_t (*)(struct xkb_keymap *, const char *)) ++ dlsym(RTLD_DEFAULT, "xkb_keymap_mod_get_index"); ++ ++ xkb_mod_index_t index = sXkbKeymapModGetIndex(aKeymap, aModifierName); ++ if (index != XKB_MOD_INVALID) { ++ mModifierMasks[aModifierIndex] = (1 << index); ++ } ++} ++ ++void ++KeymapWrapper::SetModifierMasks(xkb_keymap *aKeymap) ++{ ++ KeymapWrapper* keymapWrapper = GetInstance(); ++ ++ // This mapping is derived from get_xkb_modifiers() at gdkkeys-wayland.c ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_NUM_LOCK, XKB_MOD_NAME_NUM); ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_ALT, XKB_MOD_NAME_ALT); ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_META, "Meta"); ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_SUPER, "Super"); ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_HYPER, "Hyper"); ++ ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_SCROLL_LOCK, "ScrollLock"); ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL3, "Level3"); ++ keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL5, "Level5"); ++ ++ MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, ++ ("%p KeymapWrapper::SetModifierMasks, CapsLock=0x%X, NumLock=0x%X, " ++ "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, " ++ "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X", ++ keymapWrapper, ++ keymapWrapper->GetModifierMask(CAPS_LOCK), ++ keymapWrapper->GetModifierMask(NUM_LOCK), ++ keymapWrapper->GetModifierMask(SCROLL_LOCK), ++ keymapWrapper->GetModifierMask(LEVEL3), ++ keymapWrapper->GetModifierMask(LEVEL5), ++ keymapWrapper->GetModifierMask(SHIFT), ++ keymapWrapper->GetModifierMask(CTRL), ++ keymapWrapper->GetModifierMask(ALT), ++ keymapWrapper->GetModifierMask(META), ++ keymapWrapper->GetModifierMask(SUPER), ++ keymapWrapper->GetModifierMask(HYPER))); ++} ++ ++/* This keymap routine is derived from weston-2.0.0/clients/simple-im.c ++*/ ++static void ++keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, ++ uint32_t format, int fd, uint32_t size) ++{ ++ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { ++ close(fd); ++ return; ++ } ++ ++ char *mapString = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); ++ if (mapString == MAP_FAILED) { ++ close(fd); ++ return; ++ } ++ ++ static auto sXkbContextNew = ++ (struct xkb_context *(*)(enum xkb_context_flags)) ++ dlsym(RTLD_DEFAULT, "xkb_context_new"); ++ static auto sXkbKeymapNewFromString = ++ (struct xkb_keymap *(*)(struct xkb_context *, const char *, ++ enum xkb_keymap_format, enum xkb_keymap_compile_flags)) ++ dlsym(RTLD_DEFAULT, "xkb_keymap_new_from_string"); ++ ++ struct xkb_context *xkb_context = sXkbContextNew(XKB_CONTEXT_NO_FLAGS); ++ struct xkb_keymap *keymap = ++ sXkbKeymapNewFromString(xkb_context, mapString, ++ XKB_KEYMAP_FORMAT_TEXT_V1, ++ XKB_KEYMAP_COMPILE_NO_FLAGS); ++ ++ munmap(mapString, size); ++ close(fd); ++ ++ if (!keymap) { ++ NS_WARNING("keyboard_handle_keymap(): Failed to compile keymap!\n"); ++ return; ++ } ++ ++ KeymapWrapper::SetModifierMasks(keymap); ++ ++ static auto sXkbKeymapUnRef = ++ (void(*)(struct xkb_keymap *)) ++ dlsym(RTLD_DEFAULT, "xkb_keymap_unref"); ++ sXkbKeymapUnRef(keymap); ++ ++ static auto sXkbContextUnref = ++ (void(*)(struct xkb_context *)) ++ dlsym(RTLD_DEFAULT, "xkb_context_unref"); ++ sXkbContextUnref(xkb_context); ++} ++ ++static void ++keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, ++ uint32_t serial, struct wl_surface *surface, ++ struct wl_array *keys) ++{ ++} ++ ++static void ++keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, ++ uint32_t serial, struct wl_surface *surface) ++{ ++} ++ ++static void ++keyboard_handle_key(void *data, struct wl_keyboard *keyboard, ++ uint32_t serial, uint32_t time, uint32_t key, ++ uint32_t state) ++{ ++} ++ ++static void ++keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, ++ uint32_t serial, uint32_t mods_depressed, ++ uint32_t mods_latched, uint32_t mods_locked, ++ uint32_t group) ++{ ++} ++ ++static const struct wl_keyboard_listener keyboard_listener = { ++ keyboard_handle_keymap, ++ keyboard_handle_enter, ++ keyboard_handle_leave, ++ keyboard_handle_key, ++ keyboard_handle_modifiers, ++}; ++ ++static void ++seat_handle_capabilities(void *data, struct wl_seat *seat, ++ unsigned int caps) ++{ ++ static wl_keyboard *keyboard = nullptr; ++ ++ if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { ++ keyboard = wl_seat_get_keyboard(seat); ++ wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr); ++ } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { ++ wl_keyboard_destroy(keyboard); ++ keyboard = nullptr; ++ } ++} ++ ++static const struct wl_seat_listener seat_listener = { ++ seat_handle_capabilities, ++}; ++ ++static void ++gdk_registry_handle_global(void *data, ++ struct wl_registry *registry, ++ uint32_t id, ++ const char *interface, ++ uint32_t version) ++{ ++ if (strcmp(interface, "wl_seat") == 0) { ++ wl_seat *seat = ++ (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1); ++ wl_seat_add_listener(seat, &seat_listener, data); ++ } ++} ++ ++static void ++gdk_registry_handle_global_remove(void *data, ++ struct wl_registry *registry, ++ uint32_t id) ++{ ++} ++ ++static const struct wl_registry_listener keyboard_registry_listener = { ++ gdk_registry_handle_global, ++ gdk_registry_handle_global_remove ++}; ++ ++void ++KeymapWrapper::InitBySystemSettingsWayland() ++{ ++ // Available as of GTK 3.8+ ++ static auto sGdkWaylandDisplayGetWlDisplay = ++ (wl_display *(*)(GdkDisplay *)) ++ dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display"); ++ ++ wl_display *display = ++ sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default()); ++ wl_registry_add_listener(wl_display_get_registry(display), ++ &keyboard_registry_listener, this); ++ ++ // Call wl_display_roundtrip() twice to make sure all ++ // callbacks are processed. ++ wl_display_roundtrip(display); ++ wl_display_roundtrip(display); ++} ++#endif ++ + KeymapWrapper::~KeymapWrapper() + { + gdk_window_remove_filter(nullptr, FilterEvents, this); +@@ -931,14 +1141,19 @@ KeymapWrapper::ComputeDOMCodeNameIndex(c + + /* static */ void + KeymapWrapper::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, +- GdkEventKey* aGdkKeyEvent) ++ GdkEventKey* aGdkKeyEvent, ++ bool aIsProcessedByIME) + { ++ MOZ_ASSERT(!aIsProcessedByIME || aKeyEvent.mMessage != eKeyPress, ++ "If the key event is handled by IME, keypress event shouldn't be fired"); ++ + KeymapWrapper* keymapWrapper = GetInstance(); + + aKeyEvent.mCodeNameIndex = ComputeDOMCodeNameIndex(aGdkKeyEvent); + MOZ_ASSERT(aKeyEvent.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING); + aKeyEvent.mKeyNameIndex = +- keymapWrapper->ComputeDOMKeyNameIndex(aGdkKeyEvent); ++ aIsProcessedByIME ? KEY_NAME_INDEX_Process : ++ keymapWrapper->ComputeDOMKeyNameIndex(aGdkKeyEvent); + if (aKeyEvent.mKeyNameIndex == KEY_NAME_INDEX_Unidentified) { + uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); + if (!charCode) { +@@ -951,10 +1166,11 @@ KeymapWrapper::InitKeyEvent(WidgetKeyboa + AppendUCS4ToUTF16(charCode, aKeyEvent.mKeyValue); + } + } +- aKeyEvent.mKeyCode = ComputeDOMKeyCode(aGdkKeyEvent); + +- if (aKeyEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING || +- aKeyEvent.mMessage != eKeyPress) { ++ if (aIsProcessedByIME) { ++ aKeyEvent.mKeyCode = NS_VK_PROCESSKEY; ++ } else if (aKeyEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING || ++ aKeyEvent.mMessage != eKeyPress) { + aKeyEvent.mKeyCode = ComputeDOMKeyCode(aGdkKeyEvent); + } else { + aKeyEvent.mKeyCode = 0; +@@ -1405,6 +1621,14 @@ void + KeymapWrapper::WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent, + GdkEventKey* aGdkKeyEvent) + { ++ if (!aGdkKeyEvent) { ++ // If aGdkKeyEvent is nullptr, we're trying to dispatch a fake keyboard ++ // event in such case, we don't need to set alternative char codes. ++ // So, we don't need to do nothing here. This case is typically we're ++ // dispatching eKeyDown or eKeyUp event during composition. ++ return; ++ } ++ + uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); + if (!charCode) { + MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, +diff -up thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h.wayland thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h +--- thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsGtkKeyUtils.h 2018-11-20 12:04:43.737787350 +0100 +@@ -13,6 +13,10 @@ + + #include + #include ++#ifdef MOZ_WAYLAND ++#include ++#include ++#endif + + namespace mozilla { + namespace widget { +@@ -131,9 +135,11 @@ public: + * @param aKeyEvent It's an WidgetKeyboardEvent which needs to be + * initialized. + * @param aGdkKeyEvent A native GDK key event. ++ * @param aIsProcessedByIME true if aGdkKeyEvent is handled by IME. + */ + static void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, +- GdkEventKey* aGdkKeyEvent); ++ GdkEventKey* aGdkKeyEvent, ++ bool aIsProcessedByIME); + + /** + * WillDispatchKeyboardEvent() is called via +@@ -148,6 +154,14 @@ public: + static void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent, + GdkEventKey* aGdkKeyEvent); + ++#ifdef MOZ_WAYLAND ++ /** ++ * Utility function to set all supported modifier masks ++ * from xkb_keymap. We call that from Wayland backend routines. ++ */ ++ static void SetModifierMasks(xkb_keymap *aKeymap); ++#endif ++ + /** + * Destroys the singleton KeymapWrapper instance, if it exists. + */ +@@ -172,7 +186,10 @@ protected: + */ + void Init(); + void InitXKBExtension(); +- void InitBySystemSettings(); ++ void InitBySystemSettingsX11(); ++#ifdef MOZ_WAYLAND ++ void InitBySystemSettingsWayland(); ++#endif + + /** + * mModifierKeys stores each hardware key information. +@@ -374,6 +391,15 @@ protected: + */ + void WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent, + GdkEventKey* aGdkKeyEvent); ++ ++#ifdef MOZ_WAYLAND ++ /** ++ * Utility function to set Xkb modifier key mask. ++ */ ++ void SetModifierMask(xkb_keymap *aKeymap, ++ ModifierIndex aModifierIndex, ++ const char* aModifierName); ++#endif + }; + + } // namespace widget +diff -up thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp.wayland thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp +--- thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsLookAndFeel.cpp 2018-11-20 12:04:43.737787350 +0100 +@@ -18,6 +18,7 @@ + + #include + #include "gfxPlatformGtk.h" ++#include "mozilla/FontPropertyTypes.h" + #include "ScreenHelperGTK.h" + + #include "gtkdrawing.h" +@@ -31,7 +32,9 @@ + #include + #include "WidgetStyleCache.h" + #include "prenv.h" ++#include "nsCSSColorUtils.h" + ++using namespace mozilla; + using mozilla::LookAndFeel; + + #define GDK_COLOR_TO_NS_RGB(c) \ +@@ -182,7 +185,7 @@ GetBorderColors(GtkStyleContext* aContex + // GTK has an initial value of zero for border-widths, and so themes + // need to explicitly set border-widths to make borders visible. + GtkBorder border; +- gtk_style_context_get_border(aContext, GTK_STATE_FLAG_NORMAL, &border); ++ gtk_style_context_get_border(aContext, state, &border); + visible = border.top != 0 || border.right != 0 || + border.bottom != 0 || border.left != 0; + } +@@ -213,6 +216,58 @@ GetBorderColors(GtkStyleContext* aContex + return ret; + } + ++// Finds ideal cell highlight colors used for unfocused+selected cells distinct ++// from both Highlight, used as focused+selected background, and the listbox ++// background which is assumed to be similar to -moz-field ++nsresult ++nsLookAndFeel::InitCellHighlightColors() { ++ // NS_SUFFICIENT_LUMINOSITY_DIFFERENCE is the a11y standard for text ++ // on a background. Use 20% of that standard since we have a background ++ // on top of another background ++ int32_t minLuminosityDifference = NS_SUFFICIENT_LUMINOSITY_DIFFERENCE / 5; ++ int32_t backLuminosityDifference = NS_LUMINOSITY_DIFFERENCE( ++ mMozWindowBackground, mMozFieldBackground); ++ if (backLuminosityDifference >= minLuminosityDifference) { ++ mMozCellHighlightBackground = mMozWindowBackground; ++ mMozCellHighlightText = mMozWindowText; ++ return NS_OK; ++ } ++ ++ uint16_t hue, sat, luminance; ++ uint8_t alpha; ++ mMozCellHighlightBackground = mMozFieldBackground; ++ mMozCellHighlightText = mMozFieldText; ++ ++ NS_RGB2HSV(mMozCellHighlightBackground, hue, sat, luminance, alpha); ++ ++ uint16_t step = 30; ++ // Lighten the color if the color is very dark ++ if (luminance <= step) { ++ luminance += step; ++ } ++ // Darken it if it is very light ++ else if (luminance >= 255 - step) { ++ luminance -= step; ++ } ++ // Otherwise, compute what works best depending on the text luminance. ++ else { ++ uint16_t textHue, textSat, textLuminance; ++ uint8_t textAlpha; ++ NS_RGB2HSV(mMozCellHighlightText, textHue, textSat, textLuminance, ++ textAlpha); ++ // Text is darker than background, use a lighter shade ++ if (textLuminance < luminance) { ++ luminance += step; ++ } ++ // Otherwise, use a darker shade ++ else { ++ luminance -= step; ++ } ++ } ++ NS_HSV2RGB(mMozCellHighlightBackground, hue, sat, luminance, alpha); ++ return NS_OK; ++} ++ + void + nsLookAndFeel::NativeInit() + { +@@ -269,7 +324,6 @@ nsLookAndFeel::NativeGetColor(ColorID aI + case eColorID_IMESelectedRawTextBackground: + case eColorID_IMESelectedConvertedTextBackground: + case eColorID__moz_dragtargetzone: +- case eColorID__moz_cellhighlight: + case eColorID__moz_html_cellhighlight: + case eColorID_highlight: // preference selected item, + aColor = mTextSelectedBackground; +@@ -279,10 +333,15 @@ nsLookAndFeel::NativeGetColor(ColorID aI + case eColorID_IMESelectedRawTextForeground: + case eColorID_IMESelectedConvertedTextForeground: + case eColorID_highlighttext: +- case eColorID__moz_cellhighlighttext: + case eColorID__moz_html_cellhighlighttext: + aColor = mTextSelectedText; + break; ++ case eColorID__moz_cellhighlight: ++ aColor = mMozCellHighlightBackground; ++ break; ++ case eColorID__moz_cellhighlighttext: ++ aColor = mMozCellHighlightText; ++ break; + case eColorID_Widget3DHighlight: + aColor = NS_RGB(0xa0,0xa0,0xa0); + break; +@@ -668,6 +727,17 @@ nsLookAndFeel::GetIntImpl(IntID aID, int + EnsureInit(); + aResult = mCSDCloseButton; + break; ++ case eIntID_PrefersReducedMotion: { ++ GtkSettings *settings; ++ gboolean enableAnimations; ++ ++ settings = gtk_settings_get_default(); ++ g_object_get(settings, ++ "gtk-enable-animations", ++ &enableAnimations, nullptr); ++ aResult = enableAnimations ? 0 : 1; ++ break; ++ } + default: + aResult = 0; + res = NS_ERROR_FAILURE; +@@ -708,7 +778,7 @@ GetSystemFontInfo(GtkStyleContext *aStyl + nsString *aFontName, + gfxFontStyle *aFontStyle) + { +- aFontStyle->style = NS_FONT_STYLE_NORMAL; ++ aFontStyle->style = FontSlantStyle::Normal(); + + // As in + // https://git.gnome.org/browse/gtk+/tree/gtk/gtkwidget.c?h=3.22.19#n10333 +@@ -722,10 +792,10 @@ GetSystemFontInfo(GtkStyleContext *aStyl + NS_ConvertUTF8toUTF16 family(pango_font_description_get_family(desc)); + *aFontName = quote + family + quote; + +- aFontStyle->weight = pango_font_description_get_weight(desc); ++ aFontStyle->weight = FontWeight(pango_font_description_get_weight(desc)); + + // FIXME: Set aFontStyle->stretch correctly! +- aFontStyle->stretch = NS_FONT_STRETCH_NORMAL; ++ aFontStyle->stretch = FontStretch::Normal(); + + float size = float(pango_font_description_get_size(desc)) / PANGO_SCALE; + +@@ -1009,6 +1079,9 @@ nsLookAndFeel::EnsureInit() + mOddCellBackground = GDK_RGBA_TO_NS_RGBA(color); + gtk_style_context_restore(style); + ++ // Compute cell highlight colors ++ InitCellHighlightColors(); ++ + // GtkFrame has a "border" subnode on which Adwaita draws the border. + // Some themes do not draw on this node but draw a border on the widget + // root node, so check the root node if no border is found on the border +diff -up thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h.wayland thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h +--- thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsLookAndFeel.h 2018-11-20 12:04:43.737787350 +0100 +@@ -77,6 +77,8 @@ protected: + nscolor mMozWindowActiveBorder; + nscolor mMozWindowInactiveBorder; + nscolor mMozWindowInactiveCaption; ++ nscolor mMozCellHighlightBackground; ++ nscolor mMozCellHighlightText; + nscolor mTextSelectedText; + nscolor mTextSelectedBackground; + nscolor mMozScrollbar; +@@ -91,6 +93,9 @@ protected: + bool mInitialized; + + void EnsureInit(); ++ ++private: ++ nsresult InitCellHighlightColors(); + }; + + #endif +diff -up thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.cpp.wayland thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.cpp +--- thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.cpp 2018-11-20 12:04:43.738787347 +0100 +@@ -4,7 +4,7 @@ + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + #include "nsNativeThemeGTK.h" +-#include "nsThemeConstants.h" ++#include "nsStyleConsts.h" + #include "gtkdrawing.h" + #include "ScreenHelperGTK.h" + +@@ -39,6 +39,8 @@ + #include "mozilla/gfx/HelpersCairo.h" + #include "mozilla/gfx/PathHelpers.h" + #include "mozilla/Preferences.h" ++#include "mozilla/layers/StackingContextHelper.h" ++#include "mozilla/StaticPrefs.h" + + #ifdef MOZ_X11 + # ifdef CAIRO_HAS_XLIB_SURFACE +@@ -119,7 +121,7 @@ nsNativeThemeGTK::Observe(nsISupports *a + if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) { + moz_gtk_shutdown(); + } else { +- NS_NOTREACHED("unexpected topic"); ++ MOZ_ASSERT_UNREACHABLE("unexpected topic"); + return NS_ERROR_UNEXPECTED; + } + +@@ -149,41 +151,43 @@ static bool IsFrameContentNodeInNamespac + return content->IsInNamespace(aNamespace); + } + +-static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) { +- MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT); +- return (aDisabledVector[aWidgetType >> 3] & (1 << (aWidgetType & 7))) != 0; ++static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, StyleAppearance aWidgetType) { ++ auto type = static_cast(aWidgetType); ++ MOZ_ASSERT(type < static_cast(mozilla::StyleAppearance::Count)); ++ return (aDisabledVector[type >> 3] & (1 << (type & 7))) != 0; + } + +-static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) { +- MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT); +- aDisabledVector[aWidgetType >> 3] |= (1 << (aWidgetType & 7)); ++static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, StyleAppearance aWidgetType) { ++ auto type = static_cast(aWidgetType); ++ MOZ_ASSERT(type < static_cast(mozilla::StyleAppearance::Count)); ++ aDisabledVector[type >> 3] |= (1 << (type & 7)); + } + + static inline uint16_t +-GetWidgetStateKey(uint8_t aWidgetType, GtkWidgetState *aWidgetState) ++GetWidgetStateKey(StyleAppearance aWidgetType, GtkWidgetState *aWidgetState) + { + return (aWidgetState->active | + aWidgetState->focused << 1 | + aWidgetState->inHover << 2 | + aWidgetState->disabled << 3 | + aWidgetState->isDefault << 4 | +- aWidgetType << 5); ++ static_cast(aWidgetType) << 5); + } + + static bool IsWidgetStateSafe(uint8_t* aSafeVector, +- uint8_t aWidgetType, +- GtkWidgetState *aWidgetState) ++ StyleAppearance aWidgetType, ++ GtkWidgetState *aWidgetState) + { +- MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT); ++ MOZ_ASSERT(static_cast(aWidgetType) < static_cast(mozilla::StyleAppearance::Count)); + uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState); + return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0; + } + + static void SetWidgetStateSafe(uint8_t *aSafeVector, +- uint8_t aWidgetType, ++ StyleAppearance aWidgetType, + GtkWidgetState *aWidgetState) + { +- MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT); ++ MOZ_ASSERT(static_cast(aWidgetType) < static_cast(mozilla::StyleAppearance::Count)); + uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState); + aSafeVector[key >> 3] |= (1 << (key & 7)); + } +@@ -213,33 +217,38 @@ nsNativeThemeGTK::GetTabMarginPixels(nsI + } + + static bool ShouldScrollbarButtonBeDisabled(int32_t aCurpos, int32_t aMaxpos, +- uint8_t aWidgetType) ++ StyleAppearance aWidgetType) + { +- return ((aCurpos == 0 && (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT)) +- || (aCurpos == aMaxpos && (aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT))); ++ return ((aCurpos == 0 && (aWidgetType == StyleAppearance::ScrollbarbuttonUp || ++ aWidgetType == StyleAppearance::ScrollbarbuttonLeft)) ++ || (aCurpos == aMaxpos && (aWidgetType == StyleAppearance::ScrollbarbuttonDown || ++ aWidgetType == StyleAppearance::ScrollbarbuttonRight))); + } + + bool +-nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame, ++nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aWidgetType, nsIFrame* aFrame, + WidgetNodeType& aGtkWidgetType, + GtkWidgetState* aState, + gint* aWidgetFlags) + { ++ if (aWidgetType == StyleAppearance::MenulistButton && ++ StaticPrefs::layout_css_webkit_appearance_enabled()) { ++ aWidgetType = StyleAppearance::Menulist; ++ } ++ + if (aState) { + // For XUL checkboxes and radio buttons, the state of the parent + // determines our state. + nsIFrame *stateFrame = aFrame; +- if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX || +- aWidgetType == NS_THEME_RADIO)) || +- aWidgetType == NS_THEME_CHECKBOX_LABEL || +- aWidgetType == NS_THEME_RADIO_LABEL)) { ++ if (aFrame && ((aWidgetFlags && (aWidgetType == StyleAppearance::Checkbox || ++ aWidgetType == StyleAppearance::Radio)) || ++ aWidgetType == StyleAppearance::CheckboxLabel || ++ aWidgetType == StyleAppearance::RadioLabel)) { + + nsAtom* atom = nullptr; + if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { +- if (aWidgetType == NS_THEME_CHECKBOX_LABEL || +- aWidgetType == NS_THEME_RADIO_LABEL) { ++ if (aWidgetType == StyleAppearance::CheckboxLabel || ++ aWidgetType == StyleAppearance::RadioLabel) { + // Adjust stateFrame so GetContentState finds the correct state. + stateFrame = aFrame = aFrame->GetParent()->GetParent(); + } else { +@@ -249,8 +258,8 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + if (aWidgetFlags) { + if (!atom) { +- atom = (aWidgetType == NS_THEME_CHECKBOX || +- aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked ++ atom = (aWidgetType == StyleAppearance::Checkbox || ++ aWidgetType == StyleAppearance::CheckboxLabel) ? nsGkAtoms::checked + : nsGkAtoms::selected; + } + *aWidgetFlags = CheckBooleanAttr(aFrame, atom); +@@ -258,7 +267,7 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } else { + if (aWidgetFlags) { + *aWidgetFlags = 0; +- HTMLInputElement* inputElt = HTMLInputElement::FromContent(aFrame->GetContent()); ++ HTMLInputElement* inputElt = HTMLInputElement::FromNode(aFrame->GetContent()); + if (inputElt && inputElt->Checked()) + *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; + +@@ -266,12 +275,12 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT; + } + } +- } else if (aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || +- aWidgetType == NS_THEME_TREEHEADERSORTARROW || +- aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS || +- aWidgetType == NS_THEME_BUTTON_ARROW_NEXT || +- aWidgetType == NS_THEME_BUTTON_ARROW_UP || +- aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) { ++ } else if (aWidgetType == StyleAppearance::ToolbarbuttonDropdown || ++ aWidgetType == StyleAppearance::Treeheadersortarrow || ++ aWidgetType == StyleAppearance::ButtonArrowPrevious || ++ aWidgetType == StyleAppearance::ButtonArrowNext || ++ aWidgetType == StyleAppearance::ButtonArrowUp || ++ aWidgetType == StyleAppearance::ButtonArrowDown) { + // The state of an arrow comes from its parent. + stateFrame = aFrame = aFrame->GetParent(); + } +@@ -287,7 +296,7 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + aState->canDefault = FALSE; // XXX fix me + aState->depressed = FALSE; + +- if (aWidgetType == NS_THEME_FOCUS_OUTLINE) { ++ if (aWidgetType == StyleAppearance::FocusOutline) { + aState->disabled = FALSE; + aState->active = FALSE; + aState->inHover = FALSE; +@@ -296,15 +305,16 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + + aState->focused = TRUE; + aState->depressed = TRUE; // see moz_gtk_entry_paint() +- } else if (aWidgetType == NS_THEME_BUTTON || +- aWidgetType == NS_THEME_TOOLBARBUTTON || +- aWidgetType == NS_THEME_DUALBUTTON || +- aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || +- aWidgetType == NS_THEME_MENULIST || +- aWidgetType == NS_THEME_MENULIST_BUTTON) { ++ } else if (aWidgetType == StyleAppearance::Button || ++ aWidgetType == StyleAppearance::Toolbarbutton || ++ aWidgetType == StyleAppearance::Dualbutton || ++ aWidgetType == StyleAppearance::ToolbarbuttonDropdown || ++ aWidgetType == StyleAppearance::Menulist || ++ aWidgetType == StyleAppearance::MenulistButton || ++ aWidgetType == StyleAppearance::MozMenulistButton) { + aState->active &= aState->inHover; +- } else if (aWidgetType == NS_THEME_TREETWISTY || +- aWidgetType == NS_THEME_TREETWISTYOPEN) { ++ } else if (aWidgetType == StyleAppearance::Treetwisty || ++ aWidgetType == StyleAppearance::Treetwistyopen) { + nsTreeBodyFrame *treeBodyFrame = do_QueryFrame(aFrame); + if (treeBodyFrame) { + const mozilla::AtomArray& atoms = +@@ -318,22 +328,22 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + // For these widget types, some element (either a child or parent) + // actually has element focus, so we check the focused attribute + // to see whether to draw in the focused state. +- if (aWidgetType == NS_THEME_NUMBER_INPUT || +- aWidgetType == NS_THEME_TEXTFIELD || +- aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || +- aWidgetType == NS_THEME_MENULIST_TEXTFIELD || +- aWidgetType == NS_THEME_SPINNER_TEXTFIELD || +- aWidgetType == NS_THEME_RADIO_CONTAINER || +- aWidgetType == NS_THEME_RADIO_LABEL) { ++ if (aWidgetType == StyleAppearance::NumberInput || ++ aWidgetType == StyleAppearance::Textfield || ++ aWidgetType == StyleAppearance::TextfieldMultiline || ++ aWidgetType == StyleAppearance::MenulistTextfield || ++ aWidgetType == StyleAppearance::SpinnerTextfield || ++ aWidgetType == StyleAppearance::RadioContainer || ++ aWidgetType == StyleAppearance::RadioLabel) { + aState->focused = IsFocused(aFrame); +- } else if (aWidgetType == NS_THEME_RADIO || +- aWidgetType == NS_THEME_CHECKBOX) { ++ } else if (aWidgetType == StyleAppearance::Radio || ++ aWidgetType == StyleAppearance::Checkbox) { + // In XUL, checkboxes and radios shouldn't have focus rings, their labels do + aState->focused = FALSE; + } + +- if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL || +- aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) { ++ if (aWidgetType == StyleAppearance::ScrollbarthumbVertical || ++ aWidgetType == StyleAppearance::ScrollbarthumbHorizontal) { + // for scrollbars we need to go up two to go from the thumb to + // the slider to the actual scrollbar object + nsIFrame *tmpFrame = aFrame->GetParent()->GetParent(); +@@ -348,10 +358,10 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + } + +- if (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) { ++ if (aWidgetType == StyleAppearance::ScrollbarbuttonUp || ++ aWidgetType == StyleAppearance::ScrollbarbuttonDown || ++ aWidgetType == StyleAppearance::ScrollbarbuttonLeft || ++ aWidgetType == StyleAppearance::ScrollbarbuttonRight) { + // set the state to disabled when the scrollbar is scrolled to + // the beginning or the end, depending on the button type. + int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); +@@ -369,7 +379,8 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + + if (aWidgetFlags) { + *aWidgetFlags = GetScrollbarButtonType(aFrame); +- if (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP < 2) ++ if (static_cast(aWidgetType) - ++ static_cast(StyleAppearance::ScrollbarbuttonUp) < 2) + *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL; + } + } +@@ -379,11 +390,11 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + // menus which are children of a menu bar are only marked as prelight + // if they are open, not on normal hover. + +- if (aWidgetType == NS_THEME_MENUITEM || +- aWidgetType == NS_THEME_CHECKMENUITEM || +- aWidgetType == NS_THEME_RADIOMENUITEM || +- aWidgetType == NS_THEME_MENUSEPARATOR || +- aWidgetType == NS_THEME_MENUARROW) { ++ if (aWidgetType == StyleAppearance::Menuitem || ++ aWidgetType == StyleAppearance::Checkmenuitem || ++ aWidgetType == StyleAppearance::Radiomenuitem || ++ aWidgetType == StyleAppearance::Menuseparator || ++ aWidgetType == StyleAppearance::Menuarrow) { + bool isTopLevel = false; + nsMenuFrame *menuFrame = do_QueryFrame(aFrame); + if (menuFrame) { +@@ -398,8 +409,8 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + + aState->active = FALSE; + +- if (aWidgetType == NS_THEME_CHECKMENUITEM || +- aWidgetType == NS_THEME_RADIOMENUITEM) { ++ if (aWidgetType == StyleAppearance::Checkmenuitem || ++ aWidgetType == StyleAppearance::Radiomenuitem) { + *aWidgetFlags = 0; + if (aFrame && aFrame->GetContent() && + aFrame->GetContent()->IsElement()) { +@@ -412,12 +423,13 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + + // A button with drop down menu open or an activated toggle button + // should always appear depressed. +- if (aWidgetType == NS_THEME_BUTTON || +- aWidgetType == NS_THEME_TOOLBARBUTTON || +- aWidgetType == NS_THEME_DUALBUTTON || +- aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || +- aWidgetType == NS_THEME_MENULIST || +- aWidgetType == NS_THEME_MENULIST_BUTTON) { ++ if (aWidgetType == StyleAppearance::Button || ++ aWidgetType == StyleAppearance::Toolbarbutton || ++ aWidgetType == StyleAppearance::Dualbutton || ++ aWidgetType == StyleAppearance::ToolbarbuttonDropdown || ++ aWidgetType == StyleAppearance::Menulist || ++ aWidgetType == StyleAppearance::MenulistButton || ++ aWidgetType == StyleAppearance::MozMenulistButton) { + bool menuOpen = IsOpenButton(aFrame); + aState->depressed = IsCheckedButton(aFrame) || menuOpen; + // we must not highlight buttons with open drop down menus on hover. +@@ -426,79 +438,81 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + + // When the input field of the drop down button has focus, some themes + // should draw focus for the drop down button as well. +- if (aWidgetType == NS_THEME_MENULIST_BUTTON && aWidgetFlags) { ++ if ((aWidgetType == StyleAppearance::MenulistButton || ++ aWidgetType == StyleAppearance::MozMenulistButton) && ++ aWidgetFlags) { + *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused); + } + } + } + + switch (aWidgetType) { +- case NS_THEME_BUTTON: ++ case StyleAppearance::Button: + if (aWidgetFlags) + *aWidgetFlags = GTK_RELIEF_NORMAL; + aGtkWidgetType = MOZ_GTK_BUTTON; + break; +- case NS_THEME_TOOLBARBUTTON: +- case NS_THEME_DUALBUTTON: ++ case StyleAppearance::Toolbarbutton: ++ case StyleAppearance::Dualbutton: + if (aWidgetFlags) + *aWidgetFlags = GTK_RELIEF_NONE; + aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON; + break; +- case NS_THEME_FOCUS_OUTLINE: ++ case StyleAppearance::FocusOutline: + aGtkWidgetType = MOZ_GTK_ENTRY; + break; +- case NS_THEME_CHECKBOX: +- case NS_THEME_RADIO: +- aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON; +- break; +- case NS_THEME_SCROLLBARBUTTON_UP: +- case NS_THEME_SCROLLBARBUTTON_DOWN: +- case NS_THEME_SCROLLBARBUTTON_LEFT: +- case NS_THEME_SCROLLBARBUTTON_RIGHT: ++ case StyleAppearance::Checkbox: ++ case StyleAppearance::Radio: ++ aGtkWidgetType = (aWidgetType == StyleAppearance::Radio) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON; ++ break; ++ case StyleAppearance::ScrollbarbuttonUp: ++ case StyleAppearance::ScrollbarbuttonDown: ++ case StyleAppearance::ScrollbarbuttonLeft: ++ case StyleAppearance::ScrollbarbuttonRight: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON; + break; +- case NS_THEME_SCROLLBAR_VERTICAL: ++ case StyleAppearance::ScrollbarVertical: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_VERTICAL; + if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) + *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE; + else + *aWidgetFlags = 0; + break; +- case NS_THEME_SCROLLBAR_HORIZONTAL: ++ case StyleAppearance::ScrollbarHorizontal: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_HORIZONTAL; + if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) + *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE; + else + *aWidgetFlags = 0; + break; +- case NS_THEME_SCROLLBARTRACK_HORIZONTAL: ++ case StyleAppearance::ScrollbartrackHorizontal: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL; + break; +- case NS_THEME_SCROLLBARTRACK_VERTICAL: ++ case StyleAppearance::ScrollbartrackVertical: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL; + break; +- case NS_THEME_SCROLLBARTHUMB_VERTICAL: ++ case StyleAppearance::ScrollbarthumbVertical: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL; + break; +- case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: ++ case StyleAppearance::ScrollbarthumbHorizontal: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL; + break; +- case NS_THEME_INNER_SPIN_BUTTON: ++ case StyleAppearance::InnerSpinButton: + aGtkWidgetType = MOZ_GTK_INNER_SPIN_BUTTON; + break; +- case NS_THEME_SPINNER: ++ case StyleAppearance::Spinner: + aGtkWidgetType = MOZ_GTK_SPINBUTTON; + break; +- case NS_THEME_SPINNER_UPBUTTON: ++ case StyleAppearance::SpinnerUpbutton: + aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP; + break; +- case NS_THEME_SPINNER_DOWNBUTTON: ++ case StyleAppearance::SpinnerDownbutton: + aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN; + break; +- case NS_THEME_SPINNER_TEXTFIELD: ++ case StyleAppearance::SpinnerTextfield: + aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY; + break; +- case NS_THEME_RANGE: ++ case StyleAppearance::Range: + { + if (IsRangeHorizontal(aFrame)) { + if (aWidgetFlags) +@@ -511,7 +525,7 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + break; + } +- case NS_THEME_RANGE_THUMB: ++ case StyleAppearance::RangeThumb: + { + if (IsRangeHorizontal(aFrame)) { + if (aWidgetFlags) +@@ -524,51 +538,51 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + break; + } +- case NS_THEME_SCALE_HORIZONTAL: ++ case StyleAppearance::ScaleHorizontal: + if (aWidgetFlags) + *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL; + aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL; + break; +- case NS_THEME_SCALETHUMB_HORIZONTAL: ++ case StyleAppearance::ScalethumbHorizontal: + if (aWidgetFlags) + *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL; + aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL; + break; +- case NS_THEME_SCALE_VERTICAL: ++ case StyleAppearance::ScaleVertical: + if (aWidgetFlags) + *aWidgetFlags = GTK_ORIENTATION_VERTICAL; + aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL; + break; +- case NS_THEME_SEPARATOR: ++ case StyleAppearance::Separator: + aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR; + break; +- case NS_THEME_SCALETHUMB_VERTICAL: ++ case StyleAppearance::ScalethumbVertical: + if (aWidgetFlags) + *aWidgetFlags = GTK_ORIENTATION_VERTICAL; + aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL; + break; +- case NS_THEME_TOOLBARGRIPPER: ++ case StyleAppearance::Toolbargripper: + aGtkWidgetType = MOZ_GTK_GRIPPER; + break; +- case NS_THEME_RESIZER: ++ case StyleAppearance::Resizer: + aGtkWidgetType = MOZ_GTK_RESIZER; + break; +- case NS_THEME_NUMBER_INPUT: +- case NS_THEME_TEXTFIELD: ++ case StyleAppearance::NumberInput: ++ case StyleAppearance::Textfield: + aGtkWidgetType = MOZ_GTK_ENTRY; + break; +- case NS_THEME_TEXTFIELD_MULTILINE: ++ case StyleAppearance::TextfieldMultiline: + #ifdef MOZ_WIDGET_GTK + aGtkWidgetType = MOZ_GTK_TEXT_VIEW; + #else + aGtkWidgetType = MOZ_GTK_ENTRY; + #endif + break; +- case NS_THEME_LISTBOX: +- case NS_THEME_TREEVIEW: ++ case StyleAppearance::Listbox: ++ case StyleAppearance::Treeview: + aGtkWidgetType = MOZ_GTK_TREEVIEW; + break; +- case NS_THEME_TREEHEADERCELL: ++ case StyleAppearance::Treeheadercell: + if (aWidgetFlags) { + // In this case, the flag denotes whether the header is the sorted one or not + if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural) +@@ -578,7 +592,7 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL; + break; +- case NS_THEME_TREEHEADERSORTARROW: ++ case StyleAppearance::Treeheadersortarrow: + if (aWidgetFlags) { + switch (GetTreeSortDirection(aFrame)) { + case eTreeSortDirection_Ascending: +@@ -598,74 +612,75 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW; + break; +- case NS_THEME_TREETWISTY: ++ case StyleAppearance::Treetwisty: + aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER; + if (aWidgetFlags) + *aWidgetFlags = GTK_EXPANDER_COLLAPSED; + break; +- case NS_THEME_TREETWISTYOPEN: ++ case StyleAppearance::Treetwistyopen: + aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER; + if (aWidgetFlags) + *aWidgetFlags = GTK_EXPANDER_EXPANDED; + break; +- case NS_THEME_MENULIST: ++ case StyleAppearance::Menulist: + aGtkWidgetType = MOZ_GTK_DROPDOWN; + if (aWidgetFlags) + *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML); + break; +- case NS_THEME_MENULIST_TEXT: ++ case StyleAppearance::MenulistText: + return false; // nothing to do, but prevents the bg from being drawn +- case NS_THEME_MENULIST_TEXTFIELD: ++ case StyleAppearance::MenulistTextfield: + aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY; + break; +- case NS_THEME_MENULIST_BUTTON: ++ case StyleAppearance::MenulistButton: ++ case StyleAppearance::MozMenulistButton: + aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW; + break; +- case NS_THEME_TOOLBARBUTTON_DROPDOWN: +- case NS_THEME_BUTTON_ARROW_DOWN: +- case NS_THEME_BUTTON_ARROW_UP: +- case NS_THEME_BUTTON_ARROW_NEXT: +- case NS_THEME_BUTTON_ARROW_PREVIOUS: ++ case StyleAppearance::ToolbarbuttonDropdown: ++ case StyleAppearance::ButtonArrowDown: ++ case StyleAppearance::ButtonArrowUp: ++ case StyleAppearance::ButtonArrowNext: ++ case StyleAppearance::ButtonArrowPrevious: + aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW; + if (aWidgetFlags) { + *aWidgetFlags = GTK_ARROW_DOWN; + +- if (aWidgetType == NS_THEME_BUTTON_ARROW_UP) ++ if (aWidgetType == StyleAppearance::ButtonArrowUp) + *aWidgetFlags = GTK_ARROW_UP; +- else if (aWidgetType == NS_THEME_BUTTON_ARROW_NEXT) ++ else if (aWidgetType == StyleAppearance::ButtonArrowNext) + *aWidgetFlags = GTK_ARROW_RIGHT; +- else if (aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS) ++ else if (aWidgetType == StyleAppearance::ButtonArrowPrevious) + *aWidgetFlags = GTK_ARROW_LEFT; + } + break; +- case NS_THEME_CHECKBOX_CONTAINER: ++ case StyleAppearance::CheckboxContainer: + aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER; + break; +- case NS_THEME_RADIO_CONTAINER: ++ case StyleAppearance::RadioContainer: + aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER; + break; +- case NS_THEME_CHECKBOX_LABEL: ++ case StyleAppearance::CheckboxLabel: + aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL; + break; +- case NS_THEME_RADIO_LABEL: ++ case StyleAppearance::RadioLabel: + aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL; + break; +- case NS_THEME_TOOLBAR: ++ case StyleAppearance::Toolbar: + aGtkWidgetType = MOZ_GTK_TOOLBAR; + break; +- case NS_THEME_TOOLTIP: ++ case StyleAppearance::Tooltip: + aGtkWidgetType = MOZ_GTK_TOOLTIP; + break; +- case NS_THEME_STATUSBARPANEL: +- case NS_THEME_RESIZERPANEL: ++ case StyleAppearance::Statusbarpanel: ++ case StyleAppearance::Resizerpanel: + aGtkWidgetType = MOZ_GTK_FRAME; + break; +- case NS_THEME_PROGRESSBAR: +- case NS_THEME_PROGRESSBAR_VERTICAL: ++ case StyleAppearance::Progressbar: ++ case StyleAppearance::ProgressbarVertical: + aGtkWidgetType = MOZ_GTK_PROGRESSBAR; + break; +- case NS_THEME_PROGRESSCHUNK: +- case NS_THEME_PROGRESSCHUNK_VERTICAL: ++ case StyleAppearance::Progresschunk: ++ case StyleAppearance::ProgresschunkVertical: + { + nsIFrame* stateFrame = aFrame->GetParent(); + EventStates eventStates = GetContentState(stateFrame, aWidgetType); +@@ -677,17 +692,17 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + : MOZ_GTK_PROGRESS_CHUNK; + } + break; +- case NS_THEME_TAB_SCROLL_ARROW_BACK: +- case NS_THEME_TAB_SCROLL_ARROW_FORWARD: ++ case StyleAppearance::TabScrollArrowBack: ++ case StyleAppearance::TabScrollArrowForward: + if (aWidgetFlags) +- *aWidgetFlags = aWidgetType == NS_THEME_TAB_SCROLL_ARROW_BACK ? ++ *aWidgetFlags = aWidgetType == StyleAppearance::TabScrollArrowBack ? + GTK_ARROW_LEFT : GTK_ARROW_RIGHT; + aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW; + break; +- case NS_THEME_TABPANELS: ++ case StyleAppearance::Tabpanels: + aGtkWidgetType = MOZ_GTK_TABPANELS; + break; +- case NS_THEME_TAB: ++ case StyleAppearance::Tab: + { + if (IsBottomTab(aFrame)) { + aGtkWidgetType = MOZ_GTK_TAB_BOTTOM; +@@ -709,19 +724,19 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + } + break; +- case NS_THEME_SPLITTER: ++ case StyleAppearance::Splitter: + if (IsHorizontal(aFrame)) + aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL; + else + aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL; + break; +- case NS_THEME_MENUBAR: ++ case StyleAppearance::Menubar: + aGtkWidgetType = MOZ_GTK_MENUBAR; + break; +- case NS_THEME_MENUPOPUP: ++ case StyleAppearance::Menupopup: + aGtkWidgetType = MOZ_GTK_MENUPOPUP; + break; +- case NS_THEME_MENUITEM: ++ case StyleAppearance::Menuitem: + { + nsMenuFrame *menuFrame = do_QueryFrame(aFrame); + if (menuFrame && menuFrame->IsOnMenuBar()) { +@@ -731,41 +746,41 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + } + aGtkWidgetType = MOZ_GTK_MENUITEM; + break; +- case NS_THEME_MENUSEPARATOR: ++ case StyleAppearance::Menuseparator: + aGtkWidgetType = MOZ_GTK_MENUSEPARATOR; + break; +- case NS_THEME_MENUARROW: ++ case StyleAppearance::Menuarrow: + aGtkWidgetType = MOZ_GTK_MENUARROW; + break; +- case NS_THEME_CHECKMENUITEM: ++ case StyleAppearance::Checkmenuitem: + aGtkWidgetType = MOZ_GTK_CHECKMENUITEM; + break; +- case NS_THEME_RADIOMENUITEM: ++ case StyleAppearance::Radiomenuitem: + aGtkWidgetType = MOZ_GTK_RADIOMENUITEM; + break; +- case NS_THEME_WINDOW: +- case NS_THEME_DIALOG: ++ case StyleAppearance::Window: ++ case StyleAppearance::Dialog: + aGtkWidgetType = MOZ_GTK_WINDOW; + break; +- case NS_THEME_GTK_INFO_BAR: ++ case StyleAppearance::MozGtkInfoBar: + aGtkWidgetType = MOZ_GTK_INFO_BAR; + break; +- case NS_THEME_WINDOW_TITLEBAR: ++ case StyleAppearance::MozWindowTitlebar: + aGtkWidgetType = MOZ_GTK_HEADER_BAR; + break; +- case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED: ++ case StyleAppearance::MozWindowTitlebarMaximized: + aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED; + break; +- case NS_THEME_WINDOW_BUTTON_CLOSE: ++ case StyleAppearance::MozWindowButtonClose: + aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE; + break; +- case NS_THEME_WINDOW_BUTTON_MINIMIZE: ++ case StyleAppearance::MozWindowButtonMinimize: + aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE; + break; +- case NS_THEME_WINDOW_BUTTON_MAXIMIZE: ++ case StyleAppearance::MozWindowButtonMaximize: + aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE; + break; +- case NS_THEME_WINDOW_BUTTON_RESTORE: ++ case StyleAppearance::MozWindowButtonRestore: + aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE; + break; + default: +@@ -1025,7 +1040,8 @@ DrawThemeWithCairo(gfxContext* aContext, + } + + bool +-nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType, ++nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, ++ StyleAppearance aWidgetType, + nsIntMargin* aExtra) + { + *aExtra = nsIntMargin(0,0,0,0); +@@ -1033,14 +1049,14 @@ nsNativeThemeGTK::GetExtraSizeForWidget( + // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least); + // We modify the frame's overflow area. See bug 297508. + switch (aWidgetType) { +- case NS_THEME_SCROLLBARTHUMB_VERTICAL: ++ case StyleAppearance::ScrollbarthumbVertical: + aExtra->top = aExtra->bottom = 1; + break; +- case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: ++ case StyleAppearance::ScrollbarthumbHorizontal: + aExtra->left = aExtra->right = 1; + break; + +- case NS_THEME_BUTTON : ++ case StyleAppearance::Button : + { + if (IsDefaultButton(aFrame)) { + // Some themes draw a default indicator outside the widget, +@@ -1055,14 +1071,14 @@ nsNativeThemeGTK::GetExtraSizeForWidget( + } + return false; + } +- case NS_THEME_FOCUS_OUTLINE: ++ case StyleAppearance::FocusOutline: + { + moz_gtk_get_focus_outline_size(&aExtra->left, &aExtra->top); + aExtra->right = aExtra->left; + aExtra->bottom = aExtra->top; + break; + } +- case NS_THEME_TAB : ++ case StyleAppearance::Tab : + { + if (!IsSelectedTab(aFrame)) + return false; +@@ -1097,7 +1113,7 @@ nsNativeThemeGTK::GetExtraSizeForWidget( + NS_IMETHODIMP + nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, + nsIFrame* aFrame, +- uint8_t aWidgetType, ++ StyleAppearance aWidgetType, + const nsRect& aRect, + const nsRect& aDirtyRect) + { +@@ -1191,8 +1207,8 @@ nsNativeThemeGTK::DrawWidgetBackground(g + #ifdef DEBUG + printf("GTK theme failed for widget type %d, error was %d, state was " + "[active=%d,focused=%d,inHover=%d,disabled=%d]\n", +- aWidgetType, gLastGdkError, state.active, state.focused, +- state.inHover, state.disabled); ++ static_cast(aWidgetType), gLastGdkError, state.active, ++ state.focused, state.inHover, state.disabled); + #endif + NS_WARNING("GTK theme failed; disabling unsafe widget"); + SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType); +@@ -1221,16 +1237,16 @@ nsNativeThemeGTK::CreateWebRenderCommand + const mozilla::layers::StackingContextHelper& aSc, + mozilla::layers::WebRenderLayerManager* aManager, + nsIFrame* aFrame, +- uint8_t aWidgetType, ++ StyleAppearance aWidgetType, + const nsRect& aRect) + { + nsPresContext* presContext = aFrame->PresContext(); +- wr::LayoutRect bounds = aSc.ToRelativeLayoutRect( ++ wr::LayoutRect bounds = wr::ToRoundedLayoutRect( + LayoutDeviceRect::FromAppUnits(aRect, presContext->AppUnitsPerDevPixel())); + + switch (aWidgetType) { +- case NS_THEME_WINDOW: +- case NS_THEME_DIALOG: ++ case StyleAppearance::Window: ++ case StyleAppearance::Dialog: + aBuilder.PushRect(bounds, bounds, true, + wr::ToColorF(Color::FromABGR( + LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground, +@@ -1243,7 +1259,7 @@ nsNativeThemeGTK::CreateWebRenderCommand + } + + WidgetNodeType +-nsNativeThemeGTK::NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame) ++nsNativeThemeGTK::NativeThemeToGtkTheme(StyleAppearance aWidgetType, nsIFrame* aFrame) + { + WidgetNodeType gtkWidgetType; + gint unusedFlags; +@@ -1258,9 +1274,10 @@ nsNativeThemeGTK::NativeThemeToGtkTheme( + } + + void +-nsNativeThemeGTK::GetCachedWidgetBorder(nsIFrame* aFrame, uint8_t aWidgetType, ++nsNativeThemeGTK::GetCachedWidgetBorder(nsIFrame* aFrame, ++ StyleAppearance aWidgetType, + GtkTextDirection aDirection, +- nsIntMargin* aResult) ++ LayoutDeviceIntMargin* aResult) + { + aResult->SizeTo(0, 0, 0, 0); + +@@ -1277,7 +1294,7 @@ nsNativeThemeGTK::GetCachedWidgetBorder( + } else { + moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top, + &aResult->right, &aResult->bottom, aDirection); +- if (aWidgetType != MOZ_GTK_DROPDOWN) { // depends on aDirection ++ if (gtkWidgetType != MOZ_GTK_DROPDOWN) { // depends on aDirection + mBorderCacheValid[cacheIndex] |= cacheBit; + mBorderCache[gtkWidgetType] = *aResult; + } +@@ -1285,49 +1302,52 @@ nsNativeThemeGTK::GetCachedWidgetBorder( + } + } + +-NS_IMETHODIMP +-nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame, +- uint8_t aWidgetType, nsIntMargin* aResult) ++LayoutDeviceIntMargin ++nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, ++ nsIFrame* aFrame, ++ StyleAppearance aWidgetType) + { ++ LayoutDeviceIntMargin result; + GtkTextDirection direction = GetTextDirection(aFrame); +- aResult->top = aResult->left = aResult->right = aResult->bottom = 0; + switch (aWidgetType) { +- case NS_THEME_SCROLLBAR_HORIZONTAL: +- case NS_THEME_SCROLLBAR_VERTICAL: ++ case StyleAppearance::ScrollbarHorizontal: ++ case StyleAppearance::ScrollbarVertical: + { + GtkOrientation orientation = +- aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ? ++ aWidgetType == StyleAppearance::ScrollbarHorizontal ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; +- const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true); ++ const ScrollbarGTKMetrics* metrics = ++ GetActiveScrollbarMetrics(orientation); + + const GtkBorder& border = metrics->border.scrollbar; +- aResult->top = border.top; +- aResult->right = border.right; +- aResult->bottom = border.bottom; +- aResult->left = border.left; ++ result.top = border.top; ++ result.right = border.right; ++ result.bottom = border.bottom; ++ result.left = border.left; + } + break; +- case NS_THEME_SCROLLBARTRACK_HORIZONTAL: +- case NS_THEME_SCROLLBARTRACK_VERTICAL: ++ case StyleAppearance::ScrollbartrackHorizontal: ++ case StyleAppearance::ScrollbartrackVertical: + { + GtkOrientation orientation = +- aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL ? ++ aWidgetType == StyleAppearance::ScrollbartrackHorizontal ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; +- const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true); ++ const ScrollbarGTKMetrics* metrics = ++ GetActiveScrollbarMetrics(orientation); + + const GtkBorder& border = metrics->border.track; +- aResult->top = border.top; +- aResult->right = border.right; +- aResult->bottom = border.bottom; +- aResult->left = border.left; ++ result.top = border.top; ++ result.right = border.right; ++ result.bottom = border.bottom; ++ result.left = border.left; + } + break; +- case NS_THEME_TOOLBOX: ++ case StyleAppearance::Toolbox: + // gtk has no toolbox equivalent. So, although we map toolbox to + // gtk's 'toolbar' for purposes of painting the widget background, + // we don't use the toolbar border for toolbox. + break; +- case NS_THEME_DUALBUTTON: ++ case StyleAppearance::Dualbutton: + // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw + // around the entire button + dropdown, and also an inner border if you're + // over the button part. But, we want the inner button to be right up +@@ -1335,23 +1355,23 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi + // To make this happen, we draw a button border for the outer button, + // but don't reserve any space for it. + break; +- case NS_THEME_TAB: ++ case StyleAppearance::Tab: + { + WidgetNodeType gtkWidgetType; + gint flags; + + if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, +- &flags)) +- return NS_OK; +- +- moz_gtk_get_tab_border(&aResult->left, &aResult->top, +- &aResult->right, &aResult->bottom, direction, ++ &flags)) { ++ return result; ++ } ++ moz_gtk_get_tab_border(&result.left, &result.top, ++ &result.right, &result.bottom, direction, + (GtkTabFlags)flags, gtkWidgetType); + } + break; +- case NS_THEME_MENUITEM: +- case NS_THEME_CHECKMENUITEM: +- case NS_THEME_RADIOMENUITEM: ++ case StyleAppearance::Menuitem: ++ case StyleAppearance::Checkmenuitem: ++ case StyleAppearance::Radiomenuitem: + // For regular menuitems, we will be using GetWidgetPadding instead of + // GetWidgetBorder to pad up the widget's internals; other menuitems + // will need to fall through and use the default case as before. +@@ -1360,50 +1380,57 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi + MOZ_FALLTHROUGH; + default: + { +- GetCachedWidgetBorder(aFrame, aWidgetType, direction, aResult); ++ GetCachedWidgetBorder(aFrame, aWidgetType, direction, &result); + } + } + + gint scale = GetMonitorScaleFactor(aFrame); +- aResult->top *= scale; +- aResult->right *= scale; +- aResult->bottom *= scale; +- aResult->left *= scale; +- return NS_OK; ++ result.top *= scale; ++ result.right *= scale; ++ result.bottom *= scale; ++ result.left *= scale; ++ return result; + } + + bool + nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext, +- nsIFrame* aFrame, uint8_t aWidgetType, +- nsIntMargin* aResult) +-{ ++ nsIFrame* aFrame, ++ StyleAppearance aWidgetType, ++ LayoutDeviceIntMargin* aResult) ++{ ++ if (aWidgetType == StyleAppearance::MenulistButton && ++ StaticPrefs::layout_css_webkit_appearance_enabled()) { ++ aWidgetType = StyleAppearance::Menulist; ++ } ++ + switch (aWidgetType) { +- case NS_THEME_BUTTON_FOCUS: +- case NS_THEME_TOOLBARBUTTON: +- case NS_THEME_WINDOW_BUTTON_CLOSE: +- case NS_THEME_WINDOW_BUTTON_MINIMIZE: +- case NS_THEME_WINDOW_BUTTON_MAXIMIZE: +- case NS_THEME_WINDOW_BUTTON_RESTORE: +- case NS_THEME_DUALBUTTON: +- case NS_THEME_TAB_SCROLL_ARROW_BACK: +- case NS_THEME_TAB_SCROLL_ARROW_FORWARD: +- case NS_THEME_MENULIST_BUTTON: +- case NS_THEME_TOOLBARBUTTON_DROPDOWN: +- case NS_THEME_BUTTON_ARROW_UP: +- case NS_THEME_BUTTON_ARROW_DOWN: +- case NS_THEME_BUTTON_ARROW_NEXT: +- case NS_THEME_BUTTON_ARROW_PREVIOUS: +- case NS_THEME_RANGE_THUMB: ++ case StyleAppearance::ButtonFocus: ++ case StyleAppearance::Toolbarbutton: ++ case StyleAppearance::MozWindowButtonClose: ++ case StyleAppearance::MozWindowButtonMinimize: ++ case StyleAppearance::MozWindowButtonMaximize: ++ case StyleAppearance::MozWindowButtonRestore: ++ case StyleAppearance::Dualbutton: ++ case StyleAppearance::TabScrollArrowBack: ++ case StyleAppearance::TabScrollArrowForward: ++ case StyleAppearance::MenulistButton: ++ case StyleAppearance::MozMenulistButton: ++ case StyleAppearance::ToolbarbuttonDropdown: ++ case StyleAppearance::ButtonArrowUp: ++ case StyleAppearance::ButtonArrowDown: ++ case StyleAppearance::ButtonArrowNext: ++ case StyleAppearance::ButtonArrowPrevious: ++ case StyleAppearance::RangeThumb: + // Radios and checkboxes return a fixed size in GetMinimumWidgetSize + // and have a meaningful baseline, so they can't have + // author-specified padding. +- case NS_THEME_CHECKBOX: +- case NS_THEME_RADIO: ++ case StyleAppearance::Checkbox: ++ case StyleAppearance::Radio: + aResult->SizeTo(0, 0, 0, 0); + return true; +- case NS_THEME_MENUITEM: +- case NS_THEME_CHECKMENUITEM: +- case NS_THEME_RADIOMENUITEM: ++ case StyleAppearance::Menuitem: ++ case StyleAppearance::Checkmenuitem: ++ case StyleAppearance::Radiomenuitem: + { + // Menubar and menulist have their padding specified in CSS. + if (!IsRegularMenuItem(aFrame)) +@@ -1413,8 +1440,7 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev + aResult); + + gint horizontal_padding; +- +- if (aWidgetType == NS_THEME_MENUITEM) ++ if (aWidgetType == StyleAppearance::Menuitem) + moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding); + else + moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding); +@@ -1430,6 +1456,8 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev + + return true; + } ++ default: ++ break; + } + + return false; +@@ -1437,7 +1465,8 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev + + bool + nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext, +- nsIFrame* aFrame, uint8_t aWidgetType, ++ nsIFrame* aFrame, ++ StyleAppearance aWidgetType, + nsRect* aOverflowRect) + { + nsIntMargin extraSize; +@@ -1456,37 +1485,43 @@ nsNativeThemeGTK::GetWidgetOverflow(nsDe + + NS_IMETHODIMP + nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext, +- nsIFrame* aFrame, uint8_t aWidgetType, ++ nsIFrame* aFrame, ++ StyleAppearance aWidgetType, + LayoutDeviceIntSize* aResult, + bool* aIsOverridable) + { + aResult->width = aResult->height = 0; + *aIsOverridable = true; + ++ if (aWidgetType == StyleAppearance::MenulistButton && ++ StaticPrefs::layout_css_webkit_appearance_enabled()) { ++ aWidgetType = StyleAppearance::Menulist; ++ } ++ + switch (aWidgetType) { +- case NS_THEME_SCROLLBARBUTTON_UP: +- case NS_THEME_SCROLLBARBUTTON_DOWN: ++ case StyleAppearance::ScrollbarbuttonUp: ++ case StyleAppearance::ScrollbarbuttonDown: + { + const ScrollbarGTKMetrics* metrics = +- GetScrollbarMetrics(GTK_ORIENTATION_VERTICAL, true); ++ GetActiveScrollbarMetrics(GTK_ORIENTATION_VERTICAL); + + aResult->width = metrics->size.button.width; + aResult->height = metrics->size.button.height; + *aIsOverridable = false; + } + break; +- case NS_THEME_SCROLLBARBUTTON_LEFT: +- case NS_THEME_SCROLLBARBUTTON_RIGHT: ++ case StyleAppearance::ScrollbarbuttonLeft: ++ case StyleAppearance::ScrollbarbuttonRight: + { + const ScrollbarGTKMetrics* metrics = +- GetScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL, true); ++ GetActiveScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL); + + aResult->width = metrics->size.button.width; + aResult->height = metrics->size.button.height; + *aIsOverridable = false; + } + break; +- case NS_THEME_SPLITTER: ++ case StyleAppearance::Splitter: + { + gint metrics; + if (IsHorizontal(aFrame)) { +@@ -1501,8 +1536,8 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + *aIsOverridable = false; + } + break; +- case NS_THEME_SCROLLBAR_HORIZONTAL: +- case NS_THEME_SCROLLBAR_VERTICAL: ++ case StyleAppearance::ScrollbarHorizontal: ++ case StyleAppearance::ScrollbarVertical: + { + /* While we enforce a minimum size for the thumb, this is ignored + * for the some scrollbars if buttons are hidden (bug 513006) because +@@ -1510,28 +1545,30 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + * or track. So add a minimum size to the track as well to prevent a + * 0-width scrollbar. */ + GtkOrientation orientation = +- aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ? ++ aWidgetType == StyleAppearance::ScrollbarHorizontal ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; +- const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true); ++ const ScrollbarGTKMetrics* metrics = ++ GetActiveScrollbarMetrics(orientation); + + aResult->width = metrics->size.scrollbar.width; + aResult->height = metrics->size.scrollbar.height; + } + break; +- case NS_THEME_SCROLLBARTHUMB_VERTICAL: +- case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: ++ case StyleAppearance::ScrollbarthumbVertical: ++ case StyleAppearance::ScrollbarthumbHorizontal: + { + GtkOrientation orientation = +- aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ? ++ aWidgetType == StyleAppearance::ScrollbarthumbHorizontal ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; +- const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true); ++ const ScrollbarGTKMetrics* metrics = ++ GetActiveScrollbarMetrics(orientation); + + aResult->width = metrics->size.thumb.width; + aResult->height = metrics->size.thumb.height; + *aIsOverridable = false; + } + break; +- case NS_THEME_RANGE_THUMB: ++ case StyleAppearance::RangeThumb: + { + gint thumb_length, thumb_height; + +@@ -1546,7 +1583,7 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + *aIsOverridable = false; + } + break; +- case NS_THEME_RANGE: ++ case StyleAppearance::Range: + { + gint scale_width, scale_height; + +@@ -1559,12 +1596,12 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + *aIsOverridable = true; + } + break; +- case NS_THEME_SCALETHUMB_HORIZONTAL: +- case NS_THEME_SCALETHUMB_VERTICAL: ++ case StyleAppearance::ScalethumbHorizontal: ++ case StyleAppearance::ScalethumbVertical: + { + gint thumb_length, thumb_height; + +- if (aWidgetType == NS_THEME_SCALETHUMB_VERTICAL) { ++ if (aWidgetType == StyleAppearance::ScalethumbVertical) { + moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height); + aResult->width = thumb_height; + aResult->height = thumb_length; +@@ -1577,21 +1614,22 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + *aIsOverridable = false; + } + break; +- case NS_THEME_TAB_SCROLL_ARROW_BACK: +- case NS_THEME_TAB_SCROLL_ARROW_FORWARD: ++ case StyleAppearance::TabScrollArrowBack: ++ case StyleAppearance::TabScrollArrowForward: + { + moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height); + *aIsOverridable = false; + } + break; +- case NS_THEME_MENULIST_BUTTON: ++ case StyleAppearance::MenulistButton: ++ case StyleAppearance::MozMenulistButton: + { + moz_gtk_get_combo_box_entry_button_size(&aResult->width, + &aResult->height); + *aIsOverridable = false; + } + break; +- case NS_THEME_MENUSEPARATOR: ++ case StyleAppearance::Menuseparator: + { + gint separator_height; + +@@ -1601,26 +1639,26 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + *aIsOverridable = false; + } + break; +- case NS_THEME_CHECKBOX: +- case NS_THEME_RADIO: ++ case StyleAppearance::Checkbox: ++ case StyleAppearance::Radio: + { +- const ToggleGTKMetrics* metrics = GetToggleMetrics(aWidgetType == NS_THEME_RADIO); ++ const ToggleGTKMetrics* metrics = GetToggleMetrics(aWidgetType == StyleAppearance::Radio); + aResult->width = metrics->minSizeWithBorder.width; + aResult->height = metrics->minSizeWithBorder.height; + } + break; +- case NS_THEME_TOOLBARBUTTON_DROPDOWN: +- case NS_THEME_BUTTON_ARROW_UP: +- case NS_THEME_BUTTON_ARROW_DOWN: +- case NS_THEME_BUTTON_ARROW_NEXT: +- case NS_THEME_BUTTON_ARROW_PREVIOUS: ++ case StyleAppearance::ToolbarbuttonDropdown: ++ case StyleAppearance::ButtonArrowUp: ++ case StyleAppearance::ButtonArrowDown: ++ case StyleAppearance::ButtonArrowNext: ++ case StyleAppearance::ButtonArrowPrevious: + { + moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW, + &aResult->width, &aResult->height); + *aIsOverridable = false; + } + break; +- case NS_THEME_WINDOW_BUTTON_CLOSE: ++ case StyleAppearance::MozWindowButtonClose: + { + const ToolbarButtonGTKMetrics* metrics = + GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE); +@@ -1628,7 +1666,7 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + aResult->height = metrics->minSizeWithBorderMargin.height; + break; + } +- case NS_THEME_WINDOW_BUTTON_MINIMIZE: ++ case StyleAppearance::MozWindowButtonMinimize: + { + const ToolbarButtonGTKMetrics* metrics = + GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE); +@@ -1636,8 +1674,8 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + aResult->height = metrics->minSizeWithBorderMargin.height; + break; + } +- case NS_THEME_WINDOW_BUTTON_MAXIMIZE: +- case NS_THEME_WINDOW_BUTTON_RESTORE: ++ case StyleAppearance::MozWindowButtonMaximize: ++ case StyleAppearance::MozWindowButtonRestore: + { + const ToolbarButtonGTKMetrics* metrics = + GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE); +@@ -1645,16 +1683,16 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + aResult->height = metrics->minSizeWithBorderMargin.height; + break; + } +- case NS_THEME_CHECKBOX_CONTAINER: +- case NS_THEME_RADIO_CONTAINER: +- case NS_THEME_CHECKBOX_LABEL: +- case NS_THEME_RADIO_LABEL: +- case NS_THEME_BUTTON: +- case NS_THEME_MENULIST: +- case NS_THEME_TOOLBARBUTTON: +- case NS_THEME_TREEHEADERCELL: ++ case StyleAppearance::CheckboxContainer: ++ case StyleAppearance::RadioContainer: ++ case StyleAppearance::CheckboxLabel: ++ case StyleAppearance::RadioLabel: ++ case StyleAppearance::Button: ++ case StyleAppearance::Menulist: ++ case StyleAppearance::Toolbarbutton: ++ case StyleAppearance::Treeheadercell: + { +- if (aWidgetType == NS_THEME_MENULIST) { ++ if (aWidgetType == StyleAppearance::Menulist) { + // Include the arrow size. + moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN, + &aResult->width, &aResult->height); +@@ -1663,21 +1701,21 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + // descendants; the value returned here will not be helpful, but the + // box model may consider border and padding with child minimum sizes. + +- nsIntMargin border; ++ LayoutDeviceIntMargin border; + GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame), &border); + aResult->width += border.left + border.right; + aResult->height += border.top + border.bottom; + } + break; + #ifdef MOZ_WIDGET_GTK +- case NS_THEME_NUMBER_INPUT: +- case NS_THEME_TEXTFIELD: ++ case StyleAppearance::NumberInput: ++ case StyleAppearance::Textfield: + { + moz_gtk_get_entry_min_height(&aResult->height); + } + break; + #endif +- case NS_THEME_SEPARATOR: ++ case StyleAppearance::Separator: + { + gint separator_width; + +@@ -1686,26 +1724,26 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + aResult->width = separator_width; + } + break; +- case NS_THEME_INNER_SPIN_BUTTON: +- case NS_THEME_SPINNER: ++ case StyleAppearance::InnerSpinButton: ++ case StyleAppearance::Spinner: + // hard code these sizes + aResult->width = 14; + aResult->height = 26; + break; +- case NS_THEME_TREEHEADERSORTARROW: +- case NS_THEME_SPINNER_UPBUTTON: +- case NS_THEME_SPINNER_DOWNBUTTON: ++ case StyleAppearance::Treeheadersortarrow: ++ case StyleAppearance::SpinnerUpbutton: ++ case StyleAppearance::SpinnerDownbutton: + // hard code these sizes + aResult->width = 14; + aResult->height = 13; + break; +- case NS_THEME_RESIZER: ++ case StyleAppearance::Resizer: + // same as Windows to make our lives easier + aResult->width = aResult->height = 15; + *aIsOverridable = false; + break; +- case NS_THEME_TREETWISTY: +- case NS_THEME_TREETWISTYOPEN: ++ case StyleAppearance::Treetwisty: ++ case StyleAppearance::Treetwistyopen: + { + gint expander_size; + +@@ -1714,6 +1752,8 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + *aIsOverridable = false; + } + break; ++ default: ++ break; + } + + *aResult = *aResult * GetMonitorScaleFactor(aFrame); +@@ -1722,41 +1762,42 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + } + + NS_IMETHODIMP +-nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, ++nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, ++ StyleAppearance aWidgetType, + nsAtom* aAttribute, bool* aShouldRepaint, + const nsAttrValue* aOldValue) + { + // Some widget types just never change state. +- if (aWidgetType == NS_THEME_TOOLBOX || +- aWidgetType == NS_THEME_TOOLBAR || +- aWidgetType == NS_THEME_STATUSBAR || +- aWidgetType == NS_THEME_STATUSBARPANEL || +- aWidgetType == NS_THEME_RESIZERPANEL || +- aWidgetType == NS_THEME_PROGRESSCHUNK || +- aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL || +- aWidgetType == NS_THEME_PROGRESSBAR || +- aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL || +- aWidgetType == NS_THEME_MENUBAR || +- aWidgetType == NS_THEME_MENUPOPUP || +- aWidgetType == NS_THEME_TOOLTIP || +- aWidgetType == NS_THEME_MENUSEPARATOR || +- aWidgetType == NS_THEME_WINDOW || +- aWidgetType == NS_THEME_DIALOG) { ++ if (aWidgetType == StyleAppearance::Toolbox || ++ aWidgetType == StyleAppearance::Toolbar || ++ aWidgetType == StyleAppearance::Statusbar || ++ aWidgetType == StyleAppearance::Statusbarpanel || ++ aWidgetType == StyleAppearance::Resizerpanel || ++ aWidgetType == StyleAppearance::Progresschunk || ++ aWidgetType == StyleAppearance::ProgresschunkVertical || ++ aWidgetType == StyleAppearance::Progressbar || ++ aWidgetType == StyleAppearance::ProgressbarVertical || ++ aWidgetType == StyleAppearance::Menubar || ++ aWidgetType == StyleAppearance::Menupopup || ++ aWidgetType == StyleAppearance::Tooltip || ++ aWidgetType == StyleAppearance::Menuseparator || ++ aWidgetType == StyleAppearance::Window || ++ aWidgetType == StyleAppearance::Dialog) { + *aShouldRepaint = false; + return NS_OK; + } + +- if ((aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL || +- aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) && ++ if ((aWidgetType == StyleAppearance::ScrollbarthumbVertical || ++ aWidgetType == StyleAppearance::ScrollbarthumbHorizontal) && + aAttribute == nsGkAtoms::active) { + *aShouldRepaint = true; + return NS_OK; + } + +- if ((aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) && ++ if ((aWidgetType == StyleAppearance::ScrollbarbuttonUp || ++ aWidgetType == StyleAppearance::ScrollbarbuttonDown || ++ aWidgetType == StyleAppearance::ScrollbarbuttonLeft || ++ aWidgetType == StyleAppearance::ScrollbarbuttonRight) && + (aAttribute == nsGkAtoms::curpos || + aAttribute == nsGkAtoms::maxpos)) { + // If 'curpos' has changed and we are passed its old value, we can +@@ -1820,120 +1861,135 @@ nsNativeThemeGTK::ThemeChanged() + NS_IMETHODIMP_(bool) + nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, + nsIFrame* aFrame, +- uint8_t aWidgetType) ++ StyleAppearance aWidgetType) + { + if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType)) + return false; + ++ if (IsWidgetScrollbarPart(aWidgetType)) { ++ ComputedStyle* cs = nsLayoutUtils::StyleForScrollbar(aFrame); ++ if (cs->StyleUI()->HasCustomScrollbars() || ++ // We cannot handle thin scrollbar on GTK+ widget directly as well. ++ cs->StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin) { ++ return false; ++ } ++ } ++ ++ if (aWidgetType == StyleAppearance::MenulistButton && ++ StaticPrefs::layout_css_webkit_appearance_enabled()) { ++ aWidgetType = StyleAppearance::Menulist; ++ } ++ + switch (aWidgetType) { + // Combobox dropdowns don't support native theming in vertical mode. +- case NS_THEME_MENULIST: +- case NS_THEME_MENULIST_TEXT: +- case NS_THEME_MENULIST_TEXTFIELD: ++ case StyleAppearance::Menulist: ++ case StyleAppearance::MenulistText: ++ case StyleAppearance::MenulistTextfield: + if (aFrame && aFrame->GetWritingMode().IsVertical()) { + return false; + } + MOZ_FALLTHROUGH; + +- case NS_THEME_BUTTON: +- case NS_THEME_BUTTON_FOCUS: +- case NS_THEME_RADIO: +- case NS_THEME_CHECKBOX: +- case NS_THEME_TOOLBOX: // N/A +- case NS_THEME_TOOLBAR: +- case NS_THEME_TOOLBARBUTTON: +- case NS_THEME_DUALBUTTON: // so we can override the border with 0 +- case NS_THEME_TOOLBARBUTTON_DROPDOWN: +- case NS_THEME_BUTTON_ARROW_UP: +- case NS_THEME_BUTTON_ARROW_DOWN: +- case NS_THEME_BUTTON_ARROW_NEXT: +- case NS_THEME_BUTTON_ARROW_PREVIOUS: +- case NS_THEME_SEPARATOR: +- case NS_THEME_TOOLBARGRIPPER: +- case NS_THEME_STATUSBAR: +- case NS_THEME_STATUSBARPANEL: +- case NS_THEME_RESIZERPANEL: +- case NS_THEME_RESIZER: +- case NS_THEME_LISTBOX: +- // case NS_THEME_LISTITEM: +- case NS_THEME_TREEVIEW: +- // case NS_THEME_TREEITEM: +- case NS_THEME_TREETWISTY: +- // case NS_THEME_TREELINE: +- // case NS_THEME_TREEHEADER: +- case NS_THEME_TREEHEADERCELL: +- case NS_THEME_TREEHEADERSORTARROW: +- case NS_THEME_TREETWISTYOPEN: +- case NS_THEME_PROGRESSBAR: +- case NS_THEME_PROGRESSCHUNK: +- case NS_THEME_PROGRESSBAR_VERTICAL: +- case NS_THEME_PROGRESSCHUNK_VERTICAL: +- case NS_THEME_TAB: +- // case NS_THEME_TABPANEL: +- case NS_THEME_TABPANELS: +- case NS_THEME_TAB_SCROLL_ARROW_BACK: +- case NS_THEME_TAB_SCROLL_ARROW_FORWARD: +- case NS_THEME_TOOLTIP: +- case NS_THEME_INNER_SPIN_BUTTON: +- case NS_THEME_SPINNER: +- case NS_THEME_SPINNER_UPBUTTON: +- case NS_THEME_SPINNER_DOWNBUTTON: +- case NS_THEME_SPINNER_TEXTFIELD: +- // case NS_THEME_SCROLLBAR: (n/a for gtk) +- // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk) +- case NS_THEME_SCROLLBARBUTTON_UP: +- case NS_THEME_SCROLLBARBUTTON_DOWN: +- case NS_THEME_SCROLLBARBUTTON_LEFT: +- case NS_THEME_SCROLLBARBUTTON_RIGHT: +- case NS_THEME_SCROLLBAR_HORIZONTAL: +- case NS_THEME_SCROLLBAR_VERTICAL: +- case NS_THEME_SCROLLBARTRACK_HORIZONTAL: +- case NS_THEME_SCROLLBARTRACK_VERTICAL: +- case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: +- case NS_THEME_SCROLLBARTHUMB_VERTICAL: +- case NS_THEME_NUMBER_INPUT: +- case NS_THEME_TEXTFIELD: +- case NS_THEME_TEXTFIELD_MULTILINE: +- case NS_THEME_RANGE: +- case NS_THEME_RANGE_THUMB: +- case NS_THEME_SCALE_HORIZONTAL: +- case NS_THEME_SCALETHUMB_HORIZONTAL: +- case NS_THEME_SCALE_VERTICAL: +- case NS_THEME_SCALETHUMB_VERTICAL: +- // case NS_THEME_SCALETHUMBSTART: +- // case NS_THEME_SCALETHUMBEND: +- // case NS_THEME_SCALETHUMBTICK: +- case NS_THEME_CHECKBOX_CONTAINER: +- case NS_THEME_RADIO_CONTAINER: +- case NS_THEME_CHECKBOX_LABEL: +- case NS_THEME_RADIO_LABEL: +- case NS_THEME_MENUBAR: +- case NS_THEME_MENUPOPUP: +- case NS_THEME_MENUITEM: +- case NS_THEME_MENUARROW: +- case NS_THEME_MENUSEPARATOR: +- case NS_THEME_CHECKMENUITEM: +- case NS_THEME_RADIOMENUITEM: +- case NS_THEME_SPLITTER: +- case NS_THEME_WINDOW: +- case NS_THEME_DIALOG: ++ case StyleAppearance::Button: ++ case StyleAppearance::ButtonFocus: ++ case StyleAppearance::Radio: ++ case StyleAppearance::Checkbox: ++ case StyleAppearance::Toolbox: // N/A ++ case StyleAppearance::Toolbar: ++ case StyleAppearance::Toolbarbutton: ++ case StyleAppearance::Dualbutton: // so we can override the border with 0 ++ case StyleAppearance::ToolbarbuttonDropdown: ++ case StyleAppearance::ButtonArrowUp: ++ case StyleAppearance::ButtonArrowDown: ++ case StyleAppearance::ButtonArrowNext: ++ case StyleAppearance::ButtonArrowPrevious: ++ case StyleAppearance::Separator: ++ case StyleAppearance::Toolbargripper: ++ case StyleAppearance::Statusbar: ++ case StyleAppearance::Statusbarpanel: ++ case StyleAppearance::Resizerpanel: ++ case StyleAppearance::Resizer: ++ case StyleAppearance::Listbox: ++ // case StyleAppearance::Listitem: ++ case StyleAppearance::Treeview: ++ // case StyleAppearance::Treeitem: ++ case StyleAppearance::Treetwisty: ++ // case StyleAppearance::Treeline: ++ // case StyleAppearance::Treeheader: ++ case StyleAppearance::Treeheadercell: ++ case StyleAppearance::Treeheadersortarrow: ++ case StyleAppearance::Treetwistyopen: ++ case StyleAppearance::Progressbar: ++ case StyleAppearance::Progresschunk: ++ case StyleAppearance::ProgressbarVertical: ++ case StyleAppearance::ProgresschunkVertical: ++ case StyleAppearance::Tab: ++ // case StyleAppearance::Tabpanel: ++ case StyleAppearance::Tabpanels: ++ case StyleAppearance::TabScrollArrowBack: ++ case StyleAppearance::TabScrollArrowForward: ++ case StyleAppearance::Tooltip: ++ case StyleAppearance::InnerSpinButton: ++ case StyleAppearance::Spinner: ++ case StyleAppearance::SpinnerUpbutton: ++ case StyleAppearance::SpinnerDownbutton: ++ case StyleAppearance::SpinnerTextfield: ++ // case StyleAppearance::Scrollbar: (n/a for gtk) ++ // case StyleAppearance::ScrollbarSmall: (n/a for gtk) ++ case StyleAppearance::ScrollbarbuttonUp: ++ case StyleAppearance::ScrollbarbuttonDown: ++ case StyleAppearance::ScrollbarbuttonLeft: ++ case StyleAppearance::ScrollbarbuttonRight: ++ case StyleAppearance::ScrollbarHorizontal: ++ case StyleAppearance::ScrollbarVertical: ++ case StyleAppearance::ScrollbartrackHorizontal: ++ case StyleAppearance::ScrollbartrackVertical: ++ case StyleAppearance::ScrollbarthumbHorizontal: ++ case StyleAppearance::ScrollbarthumbVertical: ++ case StyleAppearance::NumberInput: ++ case StyleAppearance::Textfield: ++ case StyleAppearance::TextfieldMultiline: ++ case StyleAppearance::Range: ++ case StyleAppearance::RangeThumb: ++ case StyleAppearance::ScaleHorizontal: ++ case StyleAppearance::ScalethumbHorizontal: ++ case StyleAppearance::ScaleVertical: ++ case StyleAppearance::ScalethumbVertical: ++ // case StyleAppearance::Scalethumbstart: ++ // case StyleAppearance::Scalethumbend: ++ // case StyleAppearance::Scalethumbtick: ++ case StyleAppearance::CheckboxContainer: ++ case StyleAppearance::RadioContainer: ++ case StyleAppearance::CheckboxLabel: ++ case StyleAppearance::RadioLabel: ++ case StyleAppearance::Menubar: ++ case StyleAppearance::Menupopup: ++ case StyleAppearance::Menuitem: ++ case StyleAppearance::Menuarrow: ++ case StyleAppearance::Menuseparator: ++ case StyleAppearance::Checkmenuitem: ++ case StyleAppearance::Radiomenuitem: ++ case StyleAppearance::Splitter: ++ case StyleAppearance::Window: ++ case StyleAppearance::Dialog: + #ifdef MOZ_WIDGET_GTK +- case NS_THEME_GTK_INFO_BAR: ++ case StyleAppearance::MozGtkInfoBar: + #endif + return !IsWidgetStyled(aPresContext, aFrame, aWidgetType); + +- case NS_THEME_WINDOW_BUTTON_CLOSE: +- case NS_THEME_WINDOW_BUTTON_MINIMIZE: +- case NS_THEME_WINDOW_BUTTON_MAXIMIZE: +- case NS_THEME_WINDOW_BUTTON_RESTORE: +- case NS_THEME_WINDOW_TITLEBAR: +- case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED: ++ case StyleAppearance::MozWindowButtonClose: ++ case StyleAppearance::MozWindowButtonMinimize: ++ case StyleAppearance::MozWindowButtonMaximize: ++ case StyleAppearance::MozWindowButtonRestore: ++ case StyleAppearance::MozWindowTitlebar: ++ case StyleAppearance::MozWindowTitlebarMaximized: + // GtkHeaderBar is available on GTK 3.10+, which is used for styling + // title bars and title buttons. + return gtk_check_version(3, 10, 0) == nullptr && + !IsWidgetStyled(aPresContext, aFrame, aWidgetType); + +- case NS_THEME_MENULIST_BUTTON: ++ case StyleAppearance::MenulistButton: ++ case StyleAppearance::MozMenulistButton: + if (aFrame && aFrame->GetWritingMode().IsVertical()) { + return false; + } +@@ -1942,37 +1998,50 @@ nsNativeThemeGTK::ThemeSupportsWidget(ns + return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) && + !IsWidgetStyled(aPresContext, aFrame, aWidgetType); + +- case NS_THEME_FOCUS_OUTLINE: ++ case StyleAppearance::FocusOutline: + return true; ++ default: ++ break; + } + + return false; + } + + NS_IMETHODIMP_(bool) +-nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType) ++nsNativeThemeGTK::WidgetIsContainer(StyleAppearance aWidgetType) + { ++ if (aWidgetType == StyleAppearance::MenulistButton && ++ StaticPrefs::layout_css_webkit_appearance_enabled()) { ++ aWidgetType = StyleAppearance::Menulist; ++ } ++ + // XXXdwh At some point flesh all of this out. +- if (aWidgetType == NS_THEME_MENULIST_BUTTON || +- aWidgetType == NS_THEME_RADIO || +- aWidgetType == NS_THEME_RANGE_THUMB || +- aWidgetType == NS_THEME_CHECKBOX || +- aWidgetType == NS_THEME_TAB_SCROLL_ARROW_BACK || +- aWidgetType == NS_THEME_TAB_SCROLL_ARROW_FORWARD || +- aWidgetType == NS_THEME_BUTTON_ARROW_UP || +- aWidgetType == NS_THEME_BUTTON_ARROW_DOWN || +- aWidgetType == NS_THEME_BUTTON_ARROW_NEXT || +- aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS) ++ if (aWidgetType == StyleAppearance::MenulistButton || ++ aWidgetType == StyleAppearance::MozMenulistButton || ++ aWidgetType == StyleAppearance::Radio || ++ aWidgetType == StyleAppearance::RangeThumb || ++ aWidgetType == StyleAppearance::Checkbox || ++ aWidgetType == StyleAppearance::TabScrollArrowBack || ++ aWidgetType == StyleAppearance::TabScrollArrowForward || ++ aWidgetType == StyleAppearance::ButtonArrowUp || ++ aWidgetType == StyleAppearance::ButtonArrowDown || ++ aWidgetType == StyleAppearance::ButtonArrowNext || ++ aWidgetType == StyleAppearance::ButtonArrowPrevious) + return false; + return true; + } + + bool +-nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType) ++nsNativeThemeGTK::ThemeDrawsFocusForWidget(StyleAppearance aWidgetType) + { +- if (aWidgetType == NS_THEME_MENULIST || +- aWidgetType == NS_THEME_BUTTON || +- aWidgetType == NS_THEME_TREEHEADERCELL) ++ if (aWidgetType == StyleAppearance::MenulistButton && ++ StaticPrefs::layout_css_webkit_appearance_enabled()) { ++ aWidgetType = StyleAppearance::Menulist; ++ } ++ ++ if (aWidgetType == StyleAppearance::Menulist || ++ aWidgetType == StyleAppearance::Button || ++ aWidgetType == StyleAppearance::Treeheadercell) + return true; + + return false; +@@ -1985,16 +2054,17 @@ nsNativeThemeGTK::ThemeNeedsComboboxDrop + } + + nsITheme::Transparency +-nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) ++nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, ++ StyleAppearance aWidgetType) + { + switch (aWidgetType) { + // These widgets always draw a default background. +- case NS_THEME_MENUPOPUP: +- case NS_THEME_WINDOW: +- case NS_THEME_DIALOG: ++ case StyleAppearance::Menupopup: ++ case StyleAppearance::Window: ++ case StyleAppearance::Dialog: + return eOpaque; +- case NS_THEME_SCROLLBAR_VERTICAL: +- case NS_THEME_SCROLLBAR_HORIZONTAL: ++ case StyleAppearance::ScrollbarVertical: ++ case StyleAppearance::ScrollbarHorizontal: + #ifdef MOZ_WIDGET_GTK + // Make scrollbar tracks opaque on the window's scroll frame to prevent + // leaf layers from overlapping. See bug 1179780. +@@ -2006,9 +2076,10 @@ nsNativeThemeGTK::GetWidgetTransparency( + return eOpaque; + // Tooltips use gtk_paint_flat_box() on Gtk2 + // but are shaped on Gtk3 +- case NS_THEME_TOOLTIP: ++ case StyleAppearance::Tooltip: + return eTransparent; ++ default: ++ return eUnknownTransparency; + } + +- return eUnknownTransparency; + } +diff -up thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.h.wayland thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.h +--- thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.h.wayland 2018-10-30 12:45:37.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsNativeThemeGTK.h 2018-11-20 12:04:43.739787343 +0100 +@@ -11,7 +11,7 @@ + #include "nsAtom.h" + #include "nsIObserver.h" + #include "nsNativeTheme.h" +-#include "nsThemeConstants.h" ++#include "nsStyleConsts.h" + + #include + #include "gtkdrawing.h" +@@ -26,7 +26,7 @@ public: + + // The nsITheme interface. + NS_IMETHOD DrawWidgetBackground(gfxContext* aContext, +- nsIFrame* aFrame, uint8_t aWidgetType, ++ nsIFrame* aFrame, WidgetType aWidgetType, + const nsRect& aRect, + const nsRect& aDirtyRect) override; + +@@ -35,29 +35,29 @@ public: + const mozilla::layers::StackingContextHelper& aSc, + mozilla::layers::WebRenderLayerManager* aManager, + nsIFrame* aFrame, +- uint8_t aWidgetType, ++ WidgetType aWidgetType, + const nsRect& aRect) override; + +- NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame, +- uint8_t aWidgetType, +- nsIntMargin* aResult) override; +- +- virtual bool GetWidgetPadding(nsDeviceContext* aContext, +- nsIFrame* aFrame, +- uint8_t aWidgetType, +- nsIntMargin* aResult) override; ++ MOZ_MUST_USE LayoutDeviceIntMargin GetWidgetBorder(nsDeviceContext* aContext, ++ nsIFrame* aFrame, ++ WidgetType aWidgetType) override; ++ ++ bool GetWidgetPadding(nsDeviceContext* aContext, ++ nsIFrame* aFrame, ++ WidgetType aWidgetType, ++ LayoutDeviceIntMargin* aResult) override; + + virtual bool GetWidgetOverflow(nsDeviceContext* aContext, + nsIFrame* aFrame, +- uint8_t aWidgetType, ++ WidgetType aWidgetType, + nsRect* aOverflowRect) override; + + NS_IMETHOD GetMinimumWidgetSize(nsPresContext* aPresContext, +- nsIFrame* aFrame, uint8_t aWidgetType, ++ nsIFrame* aFrame, WidgetType aWidgetType, + mozilla::LayoutDeviceIntSize* aResult, + bool* aIsOverridable) override; + +- NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, ++ NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, WidgetType aWidgetType, + nsAtom* aAttribute, + bool* aShouldRepaint, + const nsAttrValue* aOldValue) override; +@@ -66,16 +66,16 @@ public: + + NS_IMETHOD_(bool) ThemeSupportsWidget(nsPresContext* aPresContext, + nsIFrame* aFrame, +- uint8_t aWidgetType) override; ++ WidgetType aWidgetType) override; + +- NS_IMETHOD_(bool) WidgetIsContainer(uint8_t aWidgetType) override; ++ NS_IMETHOD_(bool) WidgetIsContainer(WidgetType aWidgetType) override; + +- NS_IMETHOD_(bool) ThemeDrawsFocusForWidget(uint8_t aWidgetType) override; ++ NS_IMETHOD_(bool) ThemeDrawsFocusForWidget(WidgetType aWidgetType) override; + + virtual bool ThemeNeedsComboboxDropmarker() override; + + virtual Transparency GetWidgetTransparency(nsIFrame* aFrame, +- uint8_t aWidgetType) override; ++ WidgetType aWidgetType) override; + nsNativeThemeGTK(); + + protected: +@@ -84,26 +84,27 @@ protected: + private: + GtkTextDirection GetTextDirection(nsIFrame* aFrame); + gint GetTabMarginPixels(nsIFrame* aFrame); +- bool GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame, ++ bool GetGtkWidgetAndState(WidgetType aWidgetType, nsIFrame* aFrame, + WidgetNodeType& aGtkWidgetType, + GtkWidgetState* aState, gint* aWidgetFlags); +- bool GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType, ++ bool GetExtraSizeForWidget(nsIFrame* aFrame, WidgetType aWidgetType, + nsIntMargin* aExtra); + + void RefreshWidgetWindow(nsIFrame* aFrame); +- WidgetNodeType NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame); ++ WidgetNodeType NativeThemeToGtkTheme(WidgetType aWidgetType, nsIFrame* aFrame); + +- uint8_t mDisabledWidgetTypes[(ThemeWidgetType_COUNT + 7) / 8]; +- uint8_t mSafeWidgetStates[ThemeWidgetType_COUNT * 4]; // 32 bits per widget ++ uint8_t mDisabledWidgetTypes[(static_cast(mozilla::StyleAppearance::Count) + 7) / 8]; ++ uint8_t mSafeWidgetStates[static_cast(mozilla::StyleAppearance::Count) * 4]; // 32 bits per widget + static const char* sDisabledEngines[]; + + // Because moz_gtk_get_widget_border can be slow, we cache its results + // by widget type. Each bit in mBorderCacheValid says whether the + // corresponding entry in mBorderCache is valid. +- void GetCachedWidgetBorder(nsIFrame* aFrame, uint8_t aWidgetType, +- GtkTextDirection aDirection, nsIntMargin* aResult); ++ void GetCachedWidgetBorder(nsIFrame* aFrame, WidgetType aWidgetType, ++ GtkTextDirection aDirection, ++ LayoutDeviceIntMargin* aResult); + uint8_t mBorderCacheValid[(MOZ_GTK_WIDGET_NODE_COUNT + 7) / 8]; +- nsIntMargin mBorderCache[MOZ_GTK_WIDGET_NODE_COUNT]; ++ LayoutDeviceIntMargin mBorderCache[MOZ_GTK_WIDGET_NODE_COUNT]; + }; + + #endif +diff -up thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp.wayland thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp +--- thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsPrintDialogGTK.cpp 2018-11-20 12:04:43.739787343 +0100 +@@ -24,7 +24,20 @@ + #include "nsIBaseWindow.h" + #include "nsIDocShellTreeItem.h" + #include "nsIDocShell.h" ++#include "nsIGIOService.h" + #include "WidgetUtils.h" ++#include "nsIObserverService.h" ++ ++// for gdk_x11_window_get_xid ++#include ++#include ++#include ++#include ++#include ++ ++// for dlsym ++#include ++#include "MainThreadUtils.h" + + using namespace mozilla; + using namespace mozilla::widget; +@@ -387,7 +400,7 @@ nsPrintDialogWidgetGTK::ExportHeaderFoot + nsresult + nsPrintDialogWidgetGTK::ImportSettings(nsIPrintSettings *aNSSettings) + { +- NS_PRECONDITION(aNSSettings, "aSettings must not be null"); ++ MOZ_ASSERT(aNSSettings, "aSettings must not be null"); + NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); + + nsCOMPtr aNSSettingsGTK(do_QueryInterface(aNSSettings)); +@@ -416,7 +429,7 @@ nsPrintDialogWidgetGTK::ImportSettings(n + nsresult + nsPrintDialogWidgetGTK::ExportSettings(nsIPrintSettings *aNSSettings) + { +- NS_PRECONDITION(aNSSettings, "aSettings must not be null"); ++ MOZ_ASSERT(aNSSettings, "aSettings must not be null"); + NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); + + GtkPrintSettings* settings = gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog)); +@@ -513,13 +526,522 @@ nsPrintDialogServiceGTK::Init() + return NS_OK; + } + ++// Used to obtain window handle. The portal use this handle ++// to ensure that print dialog is modal. ++typedef void (*WindowHandleExported) (GtkWindow *window, ++ const char *handle, ++ gpointer user_data); ++ ++typedef void (*GtkWindowHandleExported) (GtkWindow *window, ++ const char *handle, ++ gpointer user_data); ++#ifdef MOZ_WAYLAND ++typedef struct { ++ GtkWindow *window; ++ WindowHandleExported callback; ++ gpointer user_data; ++} WaylandWindowHandleExportedData; ++ ++static void ++wayland_window_handle_exported (GdkWindow *window, ++ const char *wayland_handle_str, ++ gpointer user_data) ++{ ++ WaylandWindowHandleExportedData *data = ++ static_cast(user_data); ++ char *handle_str; ++ ++ handle_str = g_strdup_printf ("wayland:%s", wayland_handle_str); ++ data->callback (data->window, handle_str, data->user_data); ++ g_free (handle_str); ++} ++#endif ++ ++// Get window handle for the portal, taken from gtk/gtkwindow.c ++// (currently not exported) ++static gboolean ++window_export_handle(GtkWindow *window, ++ GtkWindowHandleExported callback, ++ gpointer user_data) ++{ ++ if (GDK_IS_X11_DISPLAY(gtk_widget_get_display(GTK_WIDGET(window)))) ++ { ++ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); ++ char *handle_str; ++ guint32 xid = (guint32) gdk_x11_window_get_xid(gdk_window); ++ ++ handle_str = g_strdup_printf("x11:%x", xid); ++ callback(window, handle_str, user_data); ++ g_free(handle_str); ++ return true; ++ } ++#ifdef MOZ_WAYLAND ++ else ++ { ++ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); ++ WaylandWindowHandleExportedData *data; ++ ++ data = g_new0(WaylandWindowHandleExportedData, 1); ++ data->window = window; ++ data->callback = callback; ++ data->user_data = user_data; ++ ++ static auto s_gdk_wayland_window_export_handle = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "gdk_wayland_window_export_handle")); ++ if (!s_gdk_wayland_window_export_handle || ++ !s_gdk_wayland_window_export_handle(gdk_window, ++ wayland_window_handle_exported, ++ data, g_free)) { ++ g_free (data); ++ return false; ++ } else { ++ return true; ++ } ++ } ++#endif ++ ++ g_warning("Couldn't export handle, unsupported windowing system"); ++ ++ return false; ++} ++/** ++ * Communication class with the GTK print portal handler ++ * ++ * To print document from flatpak we need to use print portal because ++ * printers are not directly accessible in the sandboxed environment. ++ * ++ * At first we request portal to show the print dialog to let user choose ++ * printer settings. We use DBUS interface for that (PreparePrint method). ++ * ++ * Next we force application to print to temporary file and after the writing ++ * to the file is finished we pass its file descriptor to the portal. ++ * Portal will pass duplicate of the file descriptor to the printer which ++ * user selected before (by DBUS Print method). ++ * ++ * Since DBUS communication is done async while nsPrintDialogServiceGTK::Show ++ * is expecting sync execution, we need to create a new GMainLoop during the ++ * print portal dialog is running. The loop is stopped after the dialog ++ * is closed. ++ */ ++class nsFlatpakPrintPortal: public nsIObserver ++{ ++ NS_DECL_ISUPPORTS ++ NS_DECL_NSIOBSERVER ++ public: ++ explicit nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings); ++ nsresult PreparePrintRequest(GtkWindow* aWindow); ++ static void OnWindowExportHandleDone(GtkWindow *aWindow, ++ const char* aWindowHandleStr, ++ gpointer aUserData); ++ void PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr); ++ static void OnPreparePrintResponse(GDBusConnection *connection, ++ const char *sender_name, ++ const char *object_path, ++ const char *interface_name, ++ const char *signal_name, ++ GVariant *parameters, ++ gpointer data); ++ GtkPrintOperationResult GetResult(); ++ private: ++ virtual ~nsFlatpakPrintPortal(); ++ void FinishPrintDialog(GVariant* parameters); ++ nsCOMPtr mPrintAndPageSettings; ++ GDBusProxy* mProxy; ++ guint32 mToken; ++ GMainLoop* mLoop; ++ GtkPrintOperationResult mResult; ++ guint mResponseSignalId; ++ GtkWindow* mParentWindow; ++}; ++ ++NS_IMPL_ISUPPORTS(nsFlatpakPrintPortal, nsIObserver) ++ ++nsFlatpakPrintPortal::nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings): ++ mPrintAndPageSettings(aPrintSettings), ++ mProxy(nullptr), ++ mLoop(nullptr), ++ mResponseSignalId(0), ++ mParentWindow(nullptr) ++{ ++} ++ ++/** ++ * Creates GDBusProxy, query for window handle and create a new GMainLoop. ++ * ++ * The GMainLoop is to be run from GetResult() and be quitted during ++ * FinishPrintDialog. ++ * ++ * @param aWindow toplevel application window which is used as parent of print ++ * dialog ++ */ ++nsresult ++nsFlatpakPrintPortal::PreparePrintRequest(GtkWindow* aWindow) ++{ ++ MOZ_ASSERT(aWindow, "aWindow must not be null"); ++ MOZ_ASSERT(mPrintAndPageSettings, "mPrintAndPageSettings must not be null"); ++ ++ GError* error = nullptr; ++ mProxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_NONE, ++ nullptr, ++ "org.freedesktop.portal.Desktop", ++ "/org/freedesktop/portal/desktop", ++ "org.freedesktop.portal.Print", ++ nullptr, ++ &error); ++ if (mProxy == nullptr) { ++ NS_WARNING(nsPrintfCString("Unable to create dbus proxy: %s", error->message).get()); ++ g_error_free(error); ++ return NS_ERROR_FAILURE; ++ } ++ ++ // The window handler is returned async, we will continue by PreparePrint method ++ // when it is returned. ++ if (!window_export_handle(aWindow, ++ &nsFlatpakPrintPortal::OnWindowExportHandleDone, this)) { ++ NS_WARNING("Unable to get window handle for creating modal print dialog."); ++ return NS_ERROR_FAILURE; ++ } ++ ++ mLoop = g_main_loop_new (NULL, FALSE); ++ return NS_OK; ++} ++ ++void ++nsFlatpakPrintPortal::OnWindowExportHandleDone(GtkWindow* aWindow, ++ const char* aWindowHandleStr, ++ gpointer aUserData) ++{ ++ nsFlatpakPrintPortal* printPortal = static_cast(aUserData); ++ printPortal->PreparePrint(aWindow, aWindowHandleStr); ++} ++ ++/** ++ * Ask print portal to show the print dialog. ++ * ++ * Print and page settings and window handle are passed to the portal to prefill ++ * last used settings. ++ */ ++void ++nsFlatpakPrintPortal::PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr) ++{ ++ GtkPrintSettings* gtkSettings = mPrintAndPageSettings->GetGtkPrintSettings(); ++ GtkPageSetup* pageSetup = mPrintAndPageSettings->GetGtkPageSetup(); ++ ++ // We need to remember GtkWindow to unexport window handle after it is ++ // no longer needed by the portal dialog (apply only on non-X11 sessions). ++ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) { ++ mParentWindow = aWindow; ++ } ++ ++ GVariantBuilder opt_builder; ++ g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT); ++ char* token = g_strdup_printf("mozilla%d", g_random_int_range (0, G_MAXINT)); ++ g_variant_builder_add(&opt_builder, "{sv}", "handle_token", ++ g_variant_new_string(token)); ++ g_free(token); ++ GVariant* options = g_variant_builder_end(&opt_builder); ++ static auto s_gtk_print_settings_to_gvariant = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "gtk_print_settings_to_gvariant")); ++ static auto s_gtk_page_setup_to_gvariant = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "gtk_page_setup_to_gvariant")); ++ if (!s_gtk_print_settings_to_gvariant || !s_gtk_page_setup_to_gvariant) { ++ mResult = GTK_PRINT_OPERATION_RESULT_ERROR; ++ FinishPrintDialog(nullptr); ++ return; ++ } ++ ++ // Get translated window title ++ nsCOMPtr bundleSvc = ++ do_GetService(NS_STRINGBUNDLE_CONTRACTID); ++ nsCOMPtr printBundle; ++ bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties", ++ getter_AddRefs(printBundle)); ++ nsAutoString intlPrintTitle; ++ printBundle->GetStringFromName("printTitleGTK", intlPrintTitle); ++ ++ GError* error = nullptr; ++ GVariant *ret = g_dbus_proxy_call_sync(mProxy, ++ "PreparePrint", ++ g_variant_new ("(ss@a{sv}@a{sv}@a{sv})", ++ aWindowHandleStr, ++ NS_ConvertUTF16toUTF8(intlPrintTitle).get(), // Title of the window ++ s_gtk_print_settings_to_gvariant(gtkSettings), ++ s_gtk_page_setup_to_gvariant(pageSetup), ++ options), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ nullptr, ++ &error); ++ if (ret == nullptr) { ++ NS_WARNING(nsPrintfCString("Unable to call dbus proxy: %s", error->message).get()); ++ g_error_free (error); ++ mResult = GTK_PRINT_OPERATION_RESULT_ERROR; ++ FinishPrintDialog(nullptr); ++ return; ++ } ++ ++ const char* handle = nullptr; ++ g_variant_get (ret, "(&o)", &handle); ++ if (strcmp (aWindowHandleStr, handle) != 0) ++ { ++ aWindowHandleStr = g_strdup (handle); ++ if (mResponseSignalId) { ++ g_dbus_connection_signal_unsubscribe( ++ g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId); ++ } ++ } ++ mResponseSignalId = ++ g_dbus_connection_signal_subscribe( ++ g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), ++ "org.freedesktop.portal.Desktop", ++ "org.freedesktop.portal.Request", ++ "Response", ++ aWindowHandleStr, ++ NULL, ++ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, ++ &nsFlatpakPrintPortal::OnPreparePrintResponse, ++ this, NULL); ++ ++} ++ ++void ++nsFlatpakPrintPortal::OnPreparePrintResponse(GDBusConnection *connection, ++ const char *sender_name, ++ const char *object_path, ++ const char *interface_name, ++ const char *signal_name, ++ GVariant *parameters, ++ gpointer data) ++{ ++ nsFlatpakPrintPortal* printPortal = static_cast(data); ++ printPortal->FinishPrintDialog(parameters); ++} ++ ++/** ++ * When the dialog is accepted, read print and page settings and token. ++ * ++ * Token is later used for printing portal as print operation identifier. ++ * Print and page settings are modified in-place and stored to ++ * mPrintAndPageSettings. ++ */ ++void ++nsFlatpakPrintPortal::FinishPrintDialog(GVariant* parameters) ++{ ++ // This ends GetResult() method ++ if (mLoop) { ++ g_main_loop_quit (mLoop); ++ mLoop = nullptr; ++ } ++ ++ if (!parameters) { ++ // mResult should be already defined ++ return; ++ } ++ ++ guint32 response; ++ GVariant *options; ++ ++ g_variant_get (parameters, "(u@a{sv})", &response, &options); ++ mResult = GTK_PRINT_OPERATION_RESULT_CANCEL; ++ if (response == 0) { ++ GVariant *v; ++ ++ char *filename; ++ char *uri; ++ v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT); ++ static auto s_gtk_print_settings_new_from_gvariant = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "gtk_print_settings_new_from_gvariant")); ++ ++ GtkPrintSettings* printSettings = s_gtk_print_settings_new_from_gvariant(v); ++ g_variant_unref (v); ++ ++ v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT); ++ static auto s_gtk_page_setup_new_from_gvariant = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "gtk_page_setup_new_from_gvariant")); ++ GtkPageSetup* pageSetup = s_gtk_page_setup_new_from_gvariant(v); ++ g_variant_unref (v); ++ ++ g_variant_lookup (options, "token", "u", &mToken); ++ ++ // Force printing to file because only filedescriptor of the file ++ // can be passed to portal ++ int fd = g_file_open_tmp("gtkprintXXXXXX", &filename, NULL); ++ uri = g_filename_to_uri(filename, NULL, NULL); ++ gtk_print_settings_set(printSettings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri); ++ g_free (uri); ++ close (fd); ++ ++ // Save native settings in the session object ++ mPrintAndPageSettings->SetGtkPrintSettings(printSettings); ++ mPrintAndPageSettings->SetGtkPageSetup(pageSetup); ++ ++ // Portal consumes PDF file ++ mPrintAndPageSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatPDF); ++ ++ // We need to set to print to file ++ mPrintAndPageSettings->SetPrintToFile(true); ++ ++ mResult = GTK_PRINT_OPERATION_RESULT_APPLY; ++ } ++} ++ ++/** ++ * Get result of the print dialog. ++ * ++ * This call blocks until FinishPrintDialog is called. ++ * ++ */ ++GtkPrintOperationResult ++nsFlatpakPrintPortal::GetResult() { ++ // If the mLoop has not been initialized we haven't go thru PreparePrint method ++ if (!NS_IsMainThread() || !mLoop) { ++ return GTK_PRINT_OPERATION_RESULT_ERROR; ++ } ++ // Calling g_main_loop_run stops current code until g_main_loop_quit is called ++ g_main_loop_run(mLoop); ++ ++ // Free resources we've allocated in order to show print dialog. ++#ifdef MOZ_WAYLAND ++ if (mParentWindow) { ++ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(mParentWindow)); ++ static auto s_gdk_wayland_window_unexport_handle = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "gdk_wayland_window_unexport_handle")); ++ if (s_gdk_wayland_window_unexport_handle) { ++ s_gdk_wayland_window_unexport_handle(gdk_window); ++ } ++ } ++#endif ++ return mResult; ++} ++ ++/** ++ * Send file descriptor of the file which contains document to the portal to ++ * finish the print operation. ++ */ ++NS_IMETHODIMP ++nsFlatpakPrintPortal::Observe(nsISupports *aObject, const char * aTopic, ++ const char16_t * aData) ++{ ++ // Check that written file match to the stored filename in case multiple ++ // print operations are in progress. ++ nsAutoString filenameStr; ++ mPrintAndPageSettings->GetToFileName(filenameStr); ++ if (!nsDependentString(aData).Equals(filenameStr)) { ++ // Different file is finished, not for this instance ++ return NS_OK; ++ } ++ int fd, idx; ++ fd = open(NS_ConvertUTF16toUTF8(filenameStr).get(), O_RDONLY|O_CLOEXEC); ++ static auto s_g_unix_fd_list_new = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "g_unix_fd_list_new")); ++ NS_ASSERTION(s_g_unix_fd_list_new, "Cannot find g_unix_fd_list_new function."); ++ ++ GUnixFDList *fd_list = s_g_unix_fd_list_new(); ++ static auto s_g_unix_fd_list_append = ++ reinterpret_cast ++ (dlsym(RTLD_DEFAULT, "g_unix_fd_list_append")); ++ idx = s_g_unix_fd_list_append(fd_list, fd, NULL); ++ close(fd); ++ ++ GVariantBuilder opt_builder; ++ g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT); ++ g_variant_builder_add(&opt_builder, "{sv}", "token", ++ g_variant_new_uint32(mToken)); ++ g_dbus_proxy_call_with_unix_fd_list( ++ mProxy, ++ "Print", ++ g_variant_new("(ssh@a{sv})", ++ "", /* window */ ++ "Print", /* title */ ++ idx, ++ g_variant_builder_end(&opt_builder)), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ fd_list, ++ NULL, ++ NULL, // TODO portal result cb function ++ nullptr); // data ++ g_object_unref(fd_list); ++ ++ nsCOMPtr os = mozilla::services::GetObserverService(); ++ // Let the nsFlatpakPrintPortal instance die ++ os->RemoveObserver(this, "print-to-file-finished"); ++ return NS_OK; ++} ++ ++nsFlatpakPrintPortal::~nsFlatpakPrintPortal() { ++ if (mProxy) { ++ if (mResponseSignalId) { ++ g_dbus_connection_signal_unsubscribe( ++ g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId); ++ } ++ g_object_unref(mProxy); ++ } ++ if (mLoop) ++ g_main_loop_quit(mLoop); ++} ++ + NS_IMETHODIMP + nsPrintDialogServiceGTK::Show(nsPIDOMWindowOuter *aParent, + nsIPrintSettings *aSettings, + nsIWebBrowserPrint *aWebBrowserPrint) + { +- NS_PRECONDITION(aParent, "aParent must not be null"); +- NS_PRECONDITION(aSettings, "aSettings must not be null"); ++ MOZ_ASSERT(aParent, "aParent must not be null"); ++ MOZ_ASSERT(aSettings, "aSettings must not be null"); ++ ++ // Check for the flatpak portal first ++ nsCOMPtr giovfs = ++ do_GetService(NS_GIOSERVICE_CONTRACTID); ++ bool shouldUsePortal; ++ giovfs->ShouldUseFlatpakPortal(&shouldUsePortal); ++ if (shouldUsePortal && gtk_check_version(3, 22, 0) == nullptr) { ++ nsCOMPtr widget = WidgetUtils::DOMWindowToWidget(aParent); ++ NS_ASSERTION(widget, "Need a widget for dialog to be modal."); ++ GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget); ++ NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal."); ++ ++ ++ nsCOMPtr printSettingsGTK(do_QueryInterface(aSettings)); ++ RefPtr fpPrintPortal = ++ new nsFlatpakPrintPortal(printSettingsGTK); ++ ++ nsresult rv = fpPrintPortal->PreparePrintRequest(gtkParent); ++ NS_ENSURE_SUCCESS(rv, rv); ++ ++ // This blocks until nsFlatpakPrintPortal::FinishPrintDialog is called ++ GtkPrintOperationResult printDialogResult = fpPrintPortal->GetResult(); ++ ++ rv = NS_OK; ++ switch (printDialogResult) { ++ case GTK_PRINT_OPERATION_RESULT_APPLY: ++ { ++ nsCOMPtr observer = do_QueryInterface(fpPrintPortal); ++ nsCOMPtr os = mozilla::services::GetObserverService(); ++ NS_ENSURE_STATE(os); ++ // Observer waits until notified that the file with the content ++ // to print has been written. ++ rv = os->AddObserver(observer, "print-to-file-finished", false); ++ NS_ENSURE_SUCCESS(rv, rv); ++ break; ++ } ++ case GTK_PRINT_OPERATION_RESULT_CANCEL: ++ rv = NS_ERROR_ABORT; ++ break; ++ default: ++ NS_WARNING("Unexpected response"); ++ rv = NS_ERROR_ABORT; ++ } ++ return rv; ++ } + + nsPrintDialogWidgetGTK printDialog(aParent, aSettings); + nsresult rv = printDialog.ImportSettings(aSettings); +@@ -553,8 +1075,8 @@ NS_IMETHODIMP + nsPrintDialogServiceGTK::ShowPageSetup(nsPIDOMWindowOuter *aParent, + nsIPrintSettings *aNSSettings) + { +- NS_PRECONDITION(aParent, "aParent must not be null"); +- NS_PRECONDITION(aNSSettings, "aSettings must not be null"); ++ MOZ_ASSERT(aParent, "aParent must not be null"); ++ MOZ_ASSERT(aNSSettings, "aSettings must not be null"); + NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); + + nsCOMPtr widget = WidgetUtils::DOMWindowToWidget(aParent); +diff -up thunderbird-60.3.0/widget/gtk/nsSound.cpp.wayland thunderbird-60.3.0/widget/gtk/nsSound.cpp +--- thunderbird-60.3.0/widget/gtk/nsSound.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsSound.cpp 2018-11-20 12:04:43.739787343 +0100 +@@ -417,43 +417,3 @@ NS_IMETHODIMP nsSound::PlayEventSound(ui + } + return NS_OK; + } +- +-NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias) +-{ +- if (NS_IsMozAliasSound(aSoundAlias)) { +- NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead"); +- uint32_t eventId; +- if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG)) +- eventId = EVENT_ALERT_DIALOG_OPEN; +- else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG)) +- eventId = EVENT_CONFIRM_DIALOG_OPEN; +- else if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP)) +- eventId = EVENT_NEW_MAIL_RECEIVED; +- else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE)) +- eventId = EVENT_MENU_EXECUTE; +- else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP)) +- eventId = EVENT_MENU_POPUP; +- else +- return NS_OK; +- return PlayEventSound(eventId); +- } +- +- nsresult rv; +- nsCOMPtr fileURI; +- +- // create a nsIFile and then a nsIFileURL from that +- nsCOMPtr soundFile; +- rv = NS_NewLocalFile(aSoundAlias, true, +- getter_AddRefs(soundFile)); +- NS_ENSURE_SUCCESS(rv,rv); +- +- rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile); +- NS_ENSURE_SUCCESS(rv,rv); +- +- nsCOMPtr fileURL = do_QueryInterface(fileURI,&rv); +- NS_ENSURE_SUCCESS(rv,rv); +- +- rv = Play(fileURL); +- +- return rv; +-} +diff -up thunderbird-60.3.0/widget/gtk/nsWidgetFactory.cpp.wayland thunderbird-60.3.0/widget/gtk/nsWidgetFactory.cpp +--- thunderbird-60.3.0/widget/gtk/nsWidgetFactory.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsWidgetFactory.cpp 2018-11-20 12:04:43.739787343 +0100 +@@ -27,6 +27,7 @@ + #ifdef MOZ_WIDGET_GTK + #include "nsApplicationChooser.h" + #endif ++#include "TaskbarProgress.h" + #include "nsColorPicker.h" + #include "nsFilePicker.h" + #include "nsSound.h" +@@ -60,7 +61,6 @@ + using namespace mozilla; + using namespace mozilla::widget; + +-NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) + NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) + NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard) + NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) +@@ -72,6 +72,7 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR + NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISound, nsSound::GetInstance) + NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ScreenManager, ScreenManager::GetAddRefedSingleton) + NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf) ++NS_GENERIC_FACTORY_CONSTRUCTOR(TaskbarProgress) + + + // from nsWindow.cpp +@@ -196,14 +197,13 @@ nsClipboardConstructor(nsISupports *aOut + return inst->QueryInterface(aIID, aResult); + } + +-NS_DEFINE_NAMED_CID(NS_WINDOW_CID); +-NS_DEFINE_NAMED_CID(NS_CHILD_CID); + NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); + NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID); + NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID); + #ifdef MOZ_WIDGET_GTK + NS_DEFINE_NAMED_CID(NS_APPLICATIONCHOOSER_CID); + #endif ++NS_DEFINE_NAMED_CID(NS_GTK_TASKBARPROGRESS_CID); + NS_DEFINE_NAMED_CID(NS_SOUND_CID); + NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID); + #ifdef MOZ_X11 +@@ -230,14 +230,13 @@ NS_DEFINE_NAMED_CID(NS_GFXINFO_CID); + + + static const mozilla::Module::CIDEntry kWidgetCIDs[] = { +- { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor }, +- { &kNS_CHILD_CID, false, nullptr, nsWindowConstructor }, + { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor, Module::ALLOW_IN_GPU_PROCESS }, + { &kNS_COLORPICKER_CID, false, nullptr, nsColorPickerConstructor, Module::MAIN_PROCESS_ONLY }, + { &kNS_FILEPICKER_CID, false, nullptr, nsFilePickerConstructor, Module::MAIN_PROCESS_ONLY }, + #ifdef MOZ_WIDGET_GTK + { &kNS_APPLICATIONCHOOSER_CID, false, nullptr, nsApplicationChooserConstructor, Module::MAIN_PROCESS_ONLY }, + #endif ++ { &kNS_GTK_TASKBARPROGRESS_CID, false, nullptr, TaskbarProgressConstructor}, + { &kNS_SOUND_CID, false, nullptr, nsISoundConstructor, Module::MAIN_PROCESS_ONLY }, + { &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor }, + #ifdef MOZ_X11 +@@ -266,14 +265,13 @@ static const mozilla::Module::CIDEntry k + }; + + static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { +- { "@mozilla.org/widget/window/gtk;1", &kNS_WINDOW_CID }, +- { "@mozilla.org/widgets/child_window/gtk;1", &kNS_CHILD_CID }, + { "@mozilla.org/widget/appshell/gtk;1", &kNS_APPSHELL_CID, Module::ALLOW_IN_GPU_PROCESS }, + { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID, Module::MAIN_PROCESS_ONLY }, + { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID, Module::MAIN_PROCESS_ONLY }, + #ifdef MOZ_WIDGET_GTK + { "@mozilla.org/applicationchooser;1", &kNS_APPLICATIONCHOOSER_CID, Module::MAIN_PROCESS_ONLY }, + #endif ++ { "@mozilla.org/widget/taskbarprogress/gtk;1", &kNS_GTK_TASKBARPROGRESS_CID }, + { "@mozilla.org/sound;1", &kNS_SOUND_CID, Module::MAIN_PROCESS_ONLY }, + { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID }, + #ifdef MOZ_X11 +diff -up thunderbird-60.3.0/widget/gtk/nsWindow.cpp.wayland thunderbird-60.3.0/widget/gtk/nsWindow.cpp +--- thunderbird-60.3.0/widget/gtk/nsWindow.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsWindow.cpp 2018-11-20 12:04:43.741787337 +0100 +@@ -18,6 +18,7 @@ + #include "mozilla/TouchEvents.h" + #include "mozilla/UniquePtrExtensions.h" + #include "mozilla/WidgetUtils.h" ++#include "mozilla/dom/WheelEventBinding.h" + #include + + #include "GeckoProfiler.h" +@@ -25,7 +26,7 @@ + #include "prlink.h" + #include "nsGTKToolkit.h" + #include "nsIRollupListener.h" +-#include "nsIDOMNode.h" ++#include "nsINode.h" + + #include "nsWidgetsCID.h" + #include "nsDragService.h" +@@ -56,6 +57,7 @@ + + #if defined(MOZ_WAYLAND) + #include ++#include "nsView.h" + #endif + + #include "nsGkAtoms.h" +@@ -116,6 +118,7 @@ using namespace mozilla::widget; + #include "mozilla/layers/CompositorThread.h" + + #ifdef MOZ_X11 ++#include "GLContextGLX.h" // for GLContextGLX::FindVisual() + #include "GtkCompositorWidget.h" + #include "gfxXlibSurface.h" + #include "WindowSurfaceX11Image.h" +@@ -129,8 +132,6 @@ using namespace mozilla::widget; + #include "nsShmImage.h" + #include "gtkdrawing.h" + +-#include "nsIDOMWheelEvent.h" +- + #include "NativeKeyBindings.h" + + #include +@@ -140,6 +141,7 @@ using namespace mozilla::gfx; + using namespace mozilla::widget; + using namespace mozilla::layers; + using mozilla::gl::GLContext; ++using mozilla::gl::GLContextGLX; + + // Don't put more than this many rects in the dirty region, just fluff + // out to the bounding-box if there are more +@@ -155,7 +157,8 @@ const gint kEvents = GDK_EXPOSURE_MASK | + #endif + GDK_SCROLL_MASK | + GDK_POINTER_MOTION_MASK | +- GDK_PROPERTY_CHANGE_MASK; ++ GDK_PROPERTY_CHANGE_MASK | ++ GDK_FOCUS_CHANGE_MASK; + + /* utility functions */ + static bool is_mouse_in_window(GdkWindow* aWindow, +@@ -210,7 +213,7 @@ static void hierarchy_changed_cb + GtkWidget *previous_toplevel); + static gboolean window_state_event_cb (GtkWidget *widget, + GdkEventWindowState *event); +-static void theme_changed_cb (GtkSettings *settings, ++static void settings_changed_cb (GtkSettings *settings, + GParamSpec *pspec, + nsWindow *data); + static void check_resize_cb (GtkContainer* container, +@@ -481,6 +484,8 @@ nsWindow::nsWindow() + mPendingConfigures = 0; + mCSDSupportLevel = CSD_SUPPORT_NONE; + mDrawInTitlebar = false; ++ ++ mHasAlphaVisual = false; + } + + nsWindow::~nsWindow() +@@ -630,7 +635,7 @@ EnsureInvisibleContainer() + static void + CheckDestroyInvisibleContainer() + { +- NS_PRECONDITION(gInvisibleContainer, "oh, no"); ++ MOZ_ASSERT(gInvisibleContainer, "oh, no"); + + if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) { + // No children, so not in use. +@@ -731,7 +736,7 @@ nsWindow::Destroy() + ClearCachedResources(); + + g_signal_handlers_disconnect_by_func(gtk_settings_get_default(), +- FuncToGpointer(theme_changed_cb), ++ FuncToGpointer(settings_changed_cb), + this); + + nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); +@@ -838,11 +843,48 @@ nsWindow::GetDesktopToDeviceScale() + return DesktopToLayoutDeviceScale(1.0); + } + ++DesktopToLayoutDeviceScale ++nsWindow::GetDesktopToDeviceScaleByScreen() ++{ ++#ifdef MOZ_WAYLAND ++ GdkDisplay* gdkDisplay = gdk_display_get_default(); ++ // In Wayland there's no way to get absolute position of the window and use it to ++ // determine the screen factor of the monitor on which the window is placed. ++ // The window is notified of the current scale factor but not at this point, ++ // so the GdkScaleFactor can return wrong value which can lead to wrong popup ++ // placement. ++ // We need to use parent's window scale factor for the new one. ++ if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { ++ nsView* view = nsView::GetViewFor(this); ++ if (view) { ++ nsView* parentView = view->GetParent(); ++ if (parentView) { ++ nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr); ++ if (parentWidget) { ++ return DesktopToLayoutDeviceScale(parentWidget->RoundsWidgetCoordinatesTo()); ++ } else { ++ NS_WARNING("Widget has no parent"); ++ } ++ } ++ } else { ++ NS_WARNING("Cannot find widget view"); ++ } ++ } ++#endif ++ return nsBaseWidget::GetDesktopToDeviceScale(); ++} ++ + void + nsWindow::SetParent(nsIWidget *aNewParent) + { +- if (mContainer || !mGdkWindow) { +- NS_NOTREACHED("nsWindow::SetParent called illegally"); ++ if (!mGdkWindow) { ++ MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed"); ++ return; ++ } ++ ++ if (mContainer) { ++ // FIXME bug 1469183 ++ NS_ERROR("nsWindow should not have a container here"); + return; + } + +@@ -886,7 +928,7 @@ nsWindow::WidgetTypeSupportsAcceleration + void + nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) + { +- NS_PRECONDITION(aNewParent, ""); ++ MOZ_ASSERT(aNewParent, "null widget"); + NS_ASSERTION(!mIsDestroyed, ""); + NS_ASSERTION(!static_cast(aNewParent)->mIsDestroyed, ""); + +@@ -1517,7 +1559,7 @@ nsWindow::GetClientBounds() + void + nsWindow::UpdateClientOffset() + { +- AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", GRAPHICS); ++ AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffset", OTHER); + + if (!mIsTopLevel || !mShell || !mIsX11Display || + gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) { +@@ -1738,6 +1780,15 @@ nsWindow::GetNativeData(uint32_t aDataTy + case NS_NATIVE_COMPOSITOR_DISPLAY: + return gfxPlatformGtk::GetPlatform()->GetCompositorDisplay(); + #endif // MOZ_X11 ++ case NS_NATIVE_EGL_WINDOW: { ++ if (mIsX11Display) ++ return mGdkWindow ? (void*)GDK_WINDOW_XID(mGdkWindow) : nullptr; ++#ifdef MOZ_WAYLAND ++ if (mContainer) ++ return moz_container_get_wl_egl_window(mContainer); ++#endif ++ return nullptr; ++ } + default: + NS_WARNING("nsWindow::GetNativeData called with bad value"); + return nullptr; +@@ -2057,6 +2108,12 @@ nsWindow::OnExposeEvent(cairo_t *cr) + if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel) + return FALSE; + ++#ifdef MOZ_WAYLAND ++ // Window does not have visible wl_surface yet. ++ if (!mIsX11Display && !GetWaylandSurface()) ++ return FALSE; ++#endif ++ + nsIWidgetListener *listener = GetListener(); + if (!listener) + return FALSE; +@@ -2112,10 +2169,7 @@ nsWindow::OnExposeEvent(cairo_t *cr) + + bool shaped = false; + if (eTransparencyTransparent == GetTransparencyMode()) { +- GdkScreen *screen = gdk_window_get_screen(mGdkWindow); +- if (gdk_screen_is_composited(screen) && +- gdk_window_get_visual(mGdkWindow) == +- gdk_screen_get_rgba_visual(screen)) { ++ if (mHasAlphaVisual) { + // Remove possible shape mask from when window manger was not + // previously compositing. + static_cast(GetTopLevelWidget())-> +@@ -2216,12 +2270,9 @@ nsWindow::OnExposeEvent(cairo_t *cr) + bool painted = false; + { + if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { +- GdkScreen *screen = gdk_window_get_screen(mGdkWindow); + if (GetTransparencyMode() == eTransparencyTransparent && + layerBuffering == BufferMode::BUFFER_NONE && +- gdk_screen_is_composited(screen) && +- gdk_window_get_visual(mGdkWindow) == +- gdk_screen_get_rgba_visual(screen)) { ++ mHasAlphaVisual) { + // If our draw target is unbuffered and we use an alpha channel, + // clear the image beforehand to ensure we don't get artifacts from a + // reused SHM image. See bug 1258086. +@@ -2354,7 +2405,7 @@ nsWindow::OnConfigureEvent(GtkWidget *aW + // + // These windows should not be moved by the window manager, and so any + // change in position is a result of our direction. mBounds has +- // already been set in Move() or Resize(), and that is more ++ // already been set in std::move() or Resize(), and that is more + // up-to-date than the position in the ConfigureNotify event if the + // event is from an earlier window move. + // +@@ -2888,7 +2939,7 @@ nsWindow::OnContainerFocusOutEvent(GdkEv + bool shouldRollup = !dragSession; + if (!shouldRollup) { + // we also roll up when a drag is from a different application +- nsCOMPtr sourceNode; ++ nsCOMPtr sourceNode; + dragSession->GetSourceNode(getter_AddRefs(sourceNode)); + shouldRollup = (sourceNode == nullptr); + } +@@ -2915,8 +2966,8 @@ bool + nsWindow::DispatchCommandEvent(nsAtom* aCommand) + { + nsEventStatus status; +- WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this); +- DispatchEvent(&event, status); ++ WidgetCommandEvent appCommandEvent(true, aCommand, this); ++ DispatchEvent(&appCommandEvent, status); + return TRUE; + } + +@@ -2938,30 +2989,44 @@ IsCtrlAltTab(GdkEventKey *aEvent) + } + + bool +-nsWindow::DispatchKeyDownEvent(GdkEventKey *aEvent, bool *aCancelled) ++nsWindow::DispatchKeyDownOrKeyUpEvent(GdkEventKey* aEvent, ++ bool aIsProcessedByIME, ++ bool* aIsCancelled) + { +- NS_PRECONDITION(aCancelled, "aCancelled must not be null"); ++ MOZ_ASSERT(aIsCancelled, "aIsCancelled must not be nullptr"); + +- *aCancelled = false; ++ *aIsCancelled = false; + +- if (IsCtrlAltTab(aEvent)) { ++ if (aEvent->type == GDK_KEY_PRESS && IsCtrlAltTab(aEvent)) { + return false; + } + ++ EventMessage message = ++ aEvent->type == GDK_KEY_PRESS ? eKeyDown : eKeyUp; ++ WidgetKeyboardEvent keyEvent(true, message, this); ++ KeymapWrapper::InitKeyEvent(keyEvent, aEvent, aIsProcessedByIME); ++ return DispatchKeyDownOrKeyUpEvent(keyEvent, aIsCancelled); ++} ++bool ++nsWindow::DispatchKeyDownOrKeyUpEvent(WidgetKeyboardEvent& aKeyboardEvent, ++ bool* aIsCancelled) ++{ ++ MOZ_ASSERT(aIsCancelled, "aIsCancelled must not be nullptr"); ++ ++ *aIsCancelled = false; ++ + RefPtr dispatcher = GetTextEventDispatcher(); + nsresult rv = dispatcher->BeginNativeInputTransaction(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return FALSE; + } + +- WidgetKeyboardEvent keydownEvent(true, eKeyDown, this); +- KeymapWrapper::InitKeyEvent(keydownEvent, aEvent); + nsEventStatus status = nsEventStatus_eIgnore; + bool dispatched = +- dispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, +- status, aEvent); +- *aCancelled = (status == nsEventStatus_eConsumeNoDefault); +- return dispatched ? TRUE : FALSE; ++ dispatcher->DispatchKeyboardEvent(aKeyboardEvent.mMessage, ++ aKeyboardEvent, status, nullptr); ++ *aIsCancelled = (status == nsEventStatus_eConsumeNoDefault); ++ return dispatched; + } + + WidgetEventTime +@@ -3046,7 +3111,7 @@ nsWindow::OnKeyPressEvent(GdkEventKey *a + // KEYDOWN -> KEYPRESS -> KEYUP -> KEYDOWN -> KEYPRESS -> KEYUP... + + bool isKeyDownCancelled = false; +- if (DispatchKeyDownEvent(aEvent, &isKeyDownCancelled) && ++ if (DispatchKeyDownOrKeyUpEvent(aEvent, false, &isKeyDownCancelled) && + (MOZ_UNLIKELY(mIsDestroyed) || isKeyDownCancelled)) { + return TRUE; + } +@@ -3095,7 +3160,7 @@ nsWindow::OnKeyPressEvent(GdkEventKey *a + } + + WidgetKeyboardEvent keypressEvent(true, eKeyPress, this); +- KeymapWrapper::InitKeyEvent(keypressEvent, aEvent); ++ KeymapWrapper::InitKeyEvent(keypressEvent, aEvent, false); + + // before we dispatch a key, check if it's the context menu key. + // If so, send a context menu key event instead. +@@ -3179,7 +3244,7 @@ nsWindow::MaybeDispatchContextMenuEvent( + } + + gboolean +-nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent) ++nsWindow::OnKeyReleaseEvent(GdkEventKey* aEvent) + { + LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this)); + +@@ -3187,17 +3252,11 @@ nsWindow::OnKeyReleaseEvent(GdkEventKey + return TRUE; + } + +- RefPtr dispatcher = GetTextEventDispatcher(); +- nsresult rv = dispatcher->BeginNativeInputTransaction(); +- if (NS_WARN_IF(NS_FAILED(rv))) { +- return false; ++ bool isCancelled = false; ++ if (NS_WARN_IF(!DispatchKeyDownOrKeyUpEvent(aEvent, false, &isCancelled))) { ++ return FALSE; + } + +- WidgetKeyboardEvent keyupEvent(true, eKeyUp, this); +- KeymapWrapper::InitKeyEvent(keyupEvent, aEvent); +- nsEventStatus status = nsEventStatus_eIgnore; +- dispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status, aEvent); +- + return TRUE; + } + +@@ -3214,7 +3273,7 @@ nsWindow::OnScrollEvent(GdkEventScroll * + return; + #endif + WidgetWheelEvent wheelEvent(true, eWheel, this); +- wheelEvent.mDeltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE; ++ wheelEvent.mDeltaMode = dom::WheelEvent_Binding::DOM_DELTA_LINE; + switch (aEvent->direction) { + #if GTK_CHECK_VERSION(3,4,0) + case GDK_SCROLL_SMOOTH: +@@ -3318,6 +3377,33 @@ nsWindow::OnWindowStateEvent(GtkWidget * + } + // else the widget is a shell widget. + ++ // The block below is a bit evil. ++ // ++ // When a window is resized before it is shown, gtk_window_resize() delays ++ // resizes until the window is shown. If gtk_window_state_event() sees a ++ // GDK_WINDOW_STATE_MAXIMIZED change [1] before the window is shown, then ++ // gtk_window_compute_configure_request_size() ignores the values from the ++ // resize [2]. See bug 1449166 for an example of how this could happen. ++ // ++ // [1] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L7967 ++ // [2] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L9377 ++ // ++ // In order to provide a sensible size for the window when the user exits ++ // maximized state, we hide the GDK_WINDOW_STATE_MAXIMIZED change from ++ // gtk_window_state_event() so as to trick GTK into using the values from ++ // gtk_window_resize() in its configure request. ++ // ++ // We instead notify gtk_window_state_event() of the maximized state change ++ // once the window is shown. ++ if (!mIsShown) { ++ aEvent->changed_mask = static_cast ++ (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 ++ (aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED); ++ } ++ + // We don't care about anything but changes in the maximized/icon/fullscreen + // states + if ((aEvent->changed_mask +@@ -3404,6 +3490,7 @@ nsWindow::OnDPIChanged() + // Update menu's font size etc + presShell->ThemeChanged(); + } ++ mWidgetListener->UIResolutionChanged(); + } + } + +@@ -3611,6 +3698,8 @@ nsWindow::Create(nsIWidget* aParent, + nsWindow *parentnsWindow = nullptr; + GtkWidget *eventWidget = nullptr; + bool drawToContainer = false; ++ bool needsAlphaVisual = (mWindowType == eWindowType_popup && ++ aInitData->mSupportTranslucency); + + if (aParent) { + parentnsWindow = static_cast(aParent); +@@ -3661,23 +3750,47 @@ nsWindow::Create(nsIWidget* aParent, + } + mShell = gtk_window_new(type); + +- bool useAlphaVisual = (mWindowType == eWindowType_popup && +- aInitData->mSupportTranslucency); +- +- // mozilla.widget.use-argb-visuals is a hidden pref defaulting to false +- // to allow experimentation +- if (Preferences::GetBool("mozilla.widget.use-argb-visuals", false)) +- useAlphaVisual = true; +- +- // We need to select an ARGB visual here instead of in +- // SetTransparencyMode() because it has to be done before the +- // widget is realized. An ARGB visual is only useful if we +- // are on a compositing window manager. +- if (useAlphaVisual) { +- GdkScreen *screen = gtk_widget_get_screen(mShell); +- if (gdk_screen_is_composited(screen)) { +- GdkVisual *visual = gdk_screen_get_rgba_visual(screen); +- gtk_widget_set_visual(mShell, visual); ++#ifdef MOZ_X11 ++ // Ensure gfxPlatform is initialized, since that is what initializes ++ // gfxVars, used below. ++ Unused << gfxPlatform::GetPlatform(); ++ ++ bool useWebRender = gfx::gfxVars::UseWebRender() && ++ AllowWebRenderForThisWindow(); ++ ++ // If using WebRender on X11, we need to select a visual with a depth buffer, ++ // as well as an alpha channel if transparency is requested. This must be done ++ // before the widget is realized. ++ if (mIsX11Display && useWebRender) { ++ auto display = ++ GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(mShell)); ++ auto screen = gtk_widget_get_screen(mShell); ++ int screenNumber = GDK_SCREEN_XNUMBER(screen); ++ int visualId = 0; ++ if (GLContextGLX::FindVisual(display, screenNumber, ++ useWebRender, needsAlphaVisual, ++ &visualId)) { ++ // If we're using CSD, rendering will go through mContainer, but ++ // it will inherit this visual as it is a child of mShell. ++ gtk_widget_set_visual(mShell, ++ gdk_x11_screen_lookup_visual(screen, ++ visualId)); ++ mHasAlphaVisual = needsAlphaVisual; ++ } else { ++ NS_WARNING("We're missing X11 Visual for WebRender!"); ++ } ++ } else ++#endif // MOZ_X11 ++ { ++ if (needsAlphaVisual) { ++ GdkScreen *screen = gtk_widget_get_screen(mShell); ++ if (gdk_screen_is_composited(screen)) { ++ GdkVisual *visual = gdk_screen_get_rgba_visual(screen); ++ if (visual) { ++ gtk_widget_set_visual(mShell, visual); ++ mHasAlphaVisual = true; ++ } ++ } + } + } + +@@ -3784,7 +3897,7 @@ nsWindow::Create(nsIWidget* aParent, + // it explicitly now. + gtk_widget_realize(mShell); + +- /* There are two cases here: ++ /* There are several cases here: + * + * 1) We're running on Gtk+ without client side decorations. + * Content is rendered to mShell window and we listen +@@ -3859,17 +3972,7 @@ nsWindow::Create(nsIWidget* aParent, + // If the window were to get unredirected, there could be visible + // tearing because Gecko does not align its framebuffer updates with + // vblank. +- if (mIsX11Display) { +- gulong value = 2; // Opt out of unredirection +- GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL); +- gdk_property_change(gtk_widget_get_window(mShell), +- gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE), +- cardinal_atom, +- 32, // format +- GDK_PROP_MODE_REPLACE, +- (guchar*)&value, +- 1); +- } ++ SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); + #endif + } + break; +@@ -3949,10 +4052,13 @@ nsWindow::Create(nsIWidget* aParent, + GtkSettings* default_settings = gtk_settings_get_default(); + g_signal_connect_after(default_settings, + "notify::gtk-theme-name", +- G_CALLBACK(theme_changed_cb), this); ++ G_CALLBACK(settings_changed_cb), this); + g_signal_connect_after(default_settings, + "notify::gtk-font-name", +- G_CALLBACK(theme_changed_cb), this); ++ G_CALLBACK(settings_changed_cb), this); ++ g_signal_connect_after(default_settings, ++ "notify::gtk-enable-animations", ++ G_CALLBACK(settings_changed_cb), this); + } + + if (mContainer) { +@@ -4070,8 +4176,10 @@ nsWindow::Create(nsIWidget* aParent, + GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow); + mXVisual = gdk_x11_visual_get_xvisual(gdkVisual); + mXDepth = gdk_visual_get_depth(gdkVisual); ++ bool shaped = needsAlphaVisual && !mHasAlphaVisual; + +- mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth); ++ mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth, ++ shaped); + } + #ifdef MOZ_WAYLAND + else if (!mIsX11Display) { +@@ -4083,60 +4191,70 @@ nsWindow::Create(nsIWidget* aParent, + } + + void +-nsWindow::SetWindowClass(const nsAString &xulWinType) ++nsWindow::RefreshWindowClass(void) + { +- if (!mShell) +- return; ++ if (mGtkWindowTypeName.IsEmpty() || mGtkWindowRoleName.IsEmpty()) ++ return; + +- const char *res_class = gdk_get_program_class(); +- if (!res_class) +- return; ++ GdkWindow* gdkWindow = gtk_widget_get_window(mShell); ++ gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get()); + +- char *res_name = ToNewCString(xulWinType); +- if (!res_name) +- return; ++#ifdef MOZ_X11 ++ if (mIsX11Display) { ++ XClassHint *class_hint = XAllocClassHint(); ++ if (!class_hint) { ++ return; ++ } ++ const char *res_class = gdk_get_program_class(); ++ if (!res_class) ++ return; ++ ++ class_hint->res_name = const_cast(mGtkWindowTypeName.get()); ++ class_hint->res_class = const_cast(res_class); ++ ++ // Can't use gtk_window_set_wmclass() for this; it prints ++ // a warning & refuses to make the change. ++ GdkDisplay *display = gdk_display_get_default(); ++ XSetClassHint(GDK_DISPLAY_XDISPLAY(display), ++ gdk_x11_window_get_xid(gdkWindow), ++ class_hint); ++ XFree(class_hint); ++ } ++#endif /* MOZ_X11 */ ++} + +- const char *role = nullptr; ++void ++nsWindow::SetWindowClass(const nsAString &xulWinType) ++{ ++ if (!mShell) ++ return; + +- // Parse res_name into a name and role. Characters other than +- // [A-Za-z0-9_-] are converted to '_'. Anything after the first +- // colon is assigned to role; if there's no colon, assign the +- // whole thing to both role and res_name. +- for (char *c = res_name; *c; c++) { +- if (':' == *c) { +- *c = 0; +- role = c + 1; +- } +- else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) +- *c = '_'; +- } +- res_name[0] = toupper(res_name[0]); +- if (!role) role = res_name; ++ char *res_name = ToNewCString(xulWinType); ++ if (!res_name) ++ return; + +- GdkWindow* gdkWindow = gtk_widget_get_window(mShell); +- gdk_window_set_role(gdkWindow, role); ++ const char *role = nullptr; + +-#ifdef MOZ_X11 +- if (mIsX11Display) { +- XClassHint *class_hint = XAllocClassHint(); +- if (!class_hint) { +- free(res_name); +- return; ++ // Parse res_name into a name and role. Characters other than ++ // [A-Za-z0-9_-] are converted to '_'. Anything after the first ++ // colon is assigned to role; if there's no colon, assign the ++ // whole thing to both role and res_name. ++ for (char *c = res_name; *c; c++) { ++ if (':' == *c) { ++ *c = 0; ++ role = c + 1; + } +- class_hint->res_name = res_name; +- class_hint->res_class = const_cast(res_class); ++ else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) ++ *c = '_'; ++ } ++ res_name[0] = toupper(res_name[0]); ++ if (!role) role = res_name; + +- // Can't use gtk_window_set_wmclass() for this; it prints +- // a warning & refuses to make the change. +- GdkDisplay *display = gdk_display_get_default(); +- XSetClassHint(GDK_DISPLAY_XDISPLAY(display), +- gdk_x11_window_get_xid(gdkWindow), +- class_hint); +- XFree(class_hint); +- } +-#endif /* MOZ_X11 */ ++ mGtkWindowTypeName = res_name; ++ mGtkWindowRoleName = role; ++ free(res_name); + +- free(res_name); ++ RefreshWindowClass(); + } + + void +@@ -4162,6 +4280,8 @@ nsWindow::NativeResize() + size.width, size.height)); + + if (mIsTopLevel) { ++ MOZ_ASSERT(size.width > 0 && size.height > 0, ++ "Can't resize window smaller than 1x1."); + gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height); + } + else if (mContainer) { +@@ -4207,6 +4327,8 @@ nsWindow::NativeMoveResize() + NativeShow(false); + } + NativeMove(); ++ ++ return; + } + + GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size()); +@@ -4219,6 +4341,8 @@ nsWindow::NativeMoveResize() + // x and y give the position of the window manager frame top-left. + gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y); + // This sets the client window size. ++ MOZ_ASSERT(size.width > 0 && size.height > 0, ++ "Can't resize window smaller than 1x1."); + gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height); + } + else if (mContainer) { +@@ -4271,6 +4395,16 @@ nsWindow::NativeShow(bool aAction) + } + } + else { ++#ifdef MOZ_WAYLAND ++ if (mContainer && moz_container_has_wl_egl_window(mContainer)) { ++ // Because wl_egl_window is destroyed on moz_container_unmap(), ++ // the current compositor cannot use it anymore. To avoid crash, ++ // destroy the compositor & recreate a new compositor on next ++ // expose event. ++ DestroyLayerManager(); ++ } ++#endif ++ + if (mIsTopLevel) { + // Workaround window freezes on GTK versions before 3.21.2 by + // ensuring that configure events get dispatched to windows before +@@ -4946,6 +5080,8 @@ FullscreenTransitionWindow::FullscreenTr + gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect); + gtk_window_set_screen(gtkWin, screen); + gtk_window_move(gtkWin, monitorRect.x, monitorRect.y); ++ MOZ_ASSERT(monitorRect.width > 0 && monitorRect.height > 0, ++ "Can't resize window smaller than 1x1."); + gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height); + + GdkColor bgColor; +@@ -5951,7 +6087,7 @@ window_state_event_cb (GtkWidget *widget + } + + static void +-theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data) ++settings_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data) + { + RefPtr window = data; + window->ThemeChanged(); +@@ -6032,13 +6168,13 @@ nsWindow::InitDragEvent(WidgetDragEvent + KeymapWrapper::InitInputEvent(aEvent, modifierState); + } + +-static gboolean +-drag_motion_event_cb(GtkWidget *aWidget, +- GdkDragContext *aDragContext, +- gint aX, +- gint aY, +- guint aTime, +- gpointer aData) ++gboolean ++WindowDragMotionHandler(GtkWidget *aWidget, ++ GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, ++ gint aX, ++ gint aY, ++ guint aTime) + { + RefPtr window = get_window_for_gtk_widget(aWidget); + if (!window) +@@ -6063,15 +6199,24 @@ drag_motion_event_cb(GtkWidget *aWidget, + + RefPtr dragService = nsDragService::GetInstance(); + return dragService-> +- ScheduleMotionEvent(innerMostWindow, aDragContext, ++ ScheduleMotionEvent(innerMostWindow, aDragContext, aWaylandDragContext, + point, aTime); + } + +-static void +-drag_leave_event_cb(GtkWidget *aWidget, +- GdkDragContext *aDragContext, +- guint aTime, +- gpointer aData) ++static gboolean ++drag_motion_event_cb(GtkWidget *aWidget, ++ GdkDragContext *aDragContext, ++ gint aX, ++ gint aY, ++ guint aTime, ++ gpointer aData) ++{ ++ return WindowDragMotionHandler(aWidget, aDragContext, nullptr, ++ aX, aY, aTime); ++} ++ ++void ++WindowDragLeaveHandler(GtkWidget *aWidget) + { + RefPtr window = get_window_for_gtk_widget(aWidget); + if (!window) +@@ -6104,14 +6249,22 @@ drag_leave_event_cb(GtkWidget *aWidget, + dragService->ScheduleLeaveEvent(); + } + ++static void ++drag_leave_event_cb(GtkWidget *aWidget, ++ GdkDragContext *aDragContext, ++ guint aTime, ++ gpointer aData) ++{ ++ WindowDragLeaveHandler(aWidget); ++} + +-static gboolean +-drag_drop_event_cb(GtkWidget *aWidget, +- GdkDragContext *aDragContext, +- gint aX, +- gint aY, +- guint aTime, +- gpointer aData) ++gboolean ++WindowDragDropHandler(GtkWidget *aWidget, ++ GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, ++ gint aX, ++ gint aY, ++ guint aTime) + { + RefPtr window = get_window_for_gtk_widget(aWidget); + if (!window) +@@ -6136,10 +6289,21 @@ drag_drop_event_cb(GtkWidget *aWidget, + + RefPtr dragService = nsDragService::GetInstance(); + return dragService-> +- ScheduleDropEvent(innerMostWindow, aDragContext, ++ ScheduleDropEvent(innerMostWindow, aDragContext, aWaylandDragContext, + point, aTime); + } + ++static gboolean ++drag_drop_event_cb(GtkWidget *aWidget, ++ GdkDragContext *aDragContext, ++ gint aX, ++ gint aY, ++ guint aTime, ++ gpointer aData) ++{ ++ return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime); ++} ++ + static void + drag_data_received_event_cb(GtkWidget *aWidget, + GdkDragContext *aDragContext, +@@ -6554,12 +6718,6 @@ nsWindow::GetLayerManager(PLayerTransact + return mLayerManager; + } + +- if (!mLayerManager && !IsComposited() && +- eTransparencyTransparent == GetTransparencyMode()) +- { +- mLayerManager = CreateBasicLayerManager(); +- } +- + return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence); + } + +@@ -6601,6 +6759,13 @@ nsWindow::ClearCachedResources() + void + nsWindow::UpdateClientOffsetForCSDWindow() + { ++ // We update window offset on X11 as the window position is calculated ++ // relatively to mShell. We don't do that on Wayland as our wl_subsurface ++ // is attached to mContainer and mShell is ignored. ++ if (!mIsX11Display) { ++ return; ++ } ++ + // _NET_FRAME_EXTENTS is not set on client decorated windows, + // so we need to read offset between mContainer and toplevel mShell + // window. +@@ -6692,6 +6857,15 @@ nsWindow::SetDrawsInTitlebar(bool aState + mNeedsShow = true; + NativeResize(); + ++ // Label mShell toplevel window so property_notify_event_cb callback ++ // can find its way home. ++ g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), ++ "nsWindow", this); ++#ifdef MOZ_X11 ++ SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); ++#endif ++ RefreshWindowClass(); ++ + // When we use system titlebar setup managed by Gtk+ we also get + // _NET_FRAME_EXTENTS property for our toplevel window so we can't + // update the client offset it here. +@@ -6998,6 +7172,8 @@ nsWindow::GetSystemCSDSupportLevel() { + // KDE Plasma + } else if (strstr(currentDesktop, "KDE") != nullptr) { + sCSDSupportLevel = CSD_SUPPORT_CLIENT; ++ } else if (strstr(currentDesktop, "Enlightenment") != nullptr) { ++ sCSDSupportLevel = CSD_SUPPORT_CLIENT; + } else if (strstr(currentDesktop, "LXDE") != nullptr) { + sCSDSupportLevel = CSD_SUPPORT_CLIENT; + } else if (strstr(currentDesktop, "openbox") != nullptr) { +@@ -7014,6 +7190,8 @@ nsWindow::GetSystemCSDSupportLevel() { + sCSDSupportLevel = CSD_SUPPORT_SYSTEM; + } else if (strstr(currentDesktop, "LXQt") != nullptr) { + sCSDSupportLevel = CSD_SUPPORT_SYSTEM; ++ } else if (strstr(currentDesktop, "Deepin") != nullptr) { ++ sCSDSupportLevel = CSD_SUPPORT_SYSTEM; + } else { + // Release or beta builds are not supposed to be broken + // so disable titlebar rendering on untested/unknown systems. +@@ -7066,26 +7244,19 @@ nsWindow::RoundsWidgetCoordinatesTo() + + void nsWindow::GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) + { ++ // Make sure the window XID is propagated to X server, we can fail otherwise ++ // in GPU process (Bug 1401634). ++ if (mXDisplay && mXWindow != X11None) { ++ XFlush(mXDisplay); ++ } ++ + *aInitData = mozilla::widget::GtkCompositorWidgetInitData( + (mXWindow != X11None) ? mXWindow : (uintptr_t)nullptr, + mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(), ++ mIsTransparent && !mHasAlphaVisual, + GetClientSize()); + } + +-bool +-nsWindow::IsComposited() const +-{ +- if (!mGdkWindow) { +- NS_WARNING("nsWindow::HasARGBVisual called before realization!"); +- return false; +- } +- +- GdkScreen* gdkScreen = gdk_screen_get_default(); +- return gdk_screen_is_composited(gdkScreen) && +- (gdk_window_get_visual(mGdkWindow) +- == gdk_screen_get_rgba_visual(gdkScreen)); +-} +- + #ifdef MOZ_WAYLAND + wl_display* + nsWindow::GetWaylandDisplay() +@@ -7110,3 +7281,120 @@ nsWindow::GetWaylandSurface() + return nullptr; + } + #endif ++ ++#ifdef MOZ_X11 ++/* XApp progress support currently works by setting a property ++ * on a window with this Atom name. A supporting window manager ++ * will notice this and pass it along to whatever handling has ++ * been implemented on that end (e.g. passing it on to a taskbar ++ * widget.) There is no issue if WM support is lacking, this is ++ * simply ignored in that case. ++ * ++ * See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c ++ * for further details. ++ */ ++ ++#define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS" ++ ++static void ++set_window_hint_cardinal (Window xid, ++ const gchar *atom_name, ++ gulong cardinal) ++{ ++ GdkDisplay *display; ++ ++ display = gdk_display_get_default (); ++ ++ if (cardinal > 0) ++ { ++ XChangeProperty (GDK_DISPLAY_XDISPLAY (display), ++ xid, ++ gdk_x11_get_xatom_by_name_for_display (display, atom_name), ++ XA_CARDINAL, 32, ++ PropModeReplace, ++ (guchar *) &cardinal, 1); ++ } ++ else ++ { ++ XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), ++ xid, ++ gdk_x11_get_xatom_by_name_for_display (display, atom_name)); ++ } ++} ++#endif // MOZ_X11 ++ ++void ++nsWindow::SetProgress(unsigned long progressPercent) ++{ ++#ifdef MOZ_X11 ++ ++ if (!mIsX11Display) { ++ return; ++ } ++ ++ if (!mShell) { ++ return; ++ } ++ ++ progressPercent = MIN(progressPercent, 100); ++ ++ set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)), ++ PROGRESS_HINT, ++ progressPercent); ++#endif // MOZ_X11 ++} ++ ++#ifdef MOZ_X11 ++void ++nsWindow::SetCompositorHint(WindowComposeRequest aState) ++{ ++ if (mIsX11Display) { ++ gulong value = aState; ++ GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL); ++ gdk_property_change(gtk_widget_get_window(mShell), ++ gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE), ++ cardinal_atom, ++ 32, // format ++ GDK_PROP_MODE_REPLACE, ++ (guchar*)&value, ++ 1); ++ } ++} ++#endif ++ ++nsresult ++nsWindow::SetSystemFont(const nsCString& aFontName) ++{ ++ GtkSettings* settings = gtk_settings_get_default(); ++ g_object_set(settings, "gtk-font-name", aFontName.get(), nullptr); ++ return NS_OK; ++} ++ ++nsresult ++nsWindow::GetSystemFont(nsCString& aFontName) ++{ ++ GtkSettings* settings = gtk_settings_get_default(); ++ gchar* fontName = nullptr; ++ g_object_get(settings, ++ "gtk-font-name", &fontName, ++ nullptr); ++ if (fontName) { ++ aFontName.Assign(fontName); ++ g_free(fontName); ++ } ++ return NS_OK; ++} ++ ++already_AddRefed ++nsIWidget::CreateTopLevelWindow() ++{ ++ nsCOMPtr window = new nsWindow(); ++ return window.forget(); ++} ++ ++already_AddRefed ++nsIWidget::CreateChildWindow() ++{ ++ nsCOMPtr window = new nsWindow(); ++ return window.forget(); ++} +diff -up thunderbird-60.3.0/widget/gtk/nsWindow.h.wayland thunderbird-60.3.0/widget/gtk/nsWindow.h +--- thunderbird-60.3.0/widget/gtk/nsWindow.h.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/nsWindow.h 2018-11-20 12:04:43.741787337 +0100 +@@ -66,6 +66,21 @@ extern mozilla::LazyLogModule gWidgetDra + + #endif /* MOZ_LOGGING */ + ++#ifdef MOZ_WAYLAND ++class nsWaylandDragContext; ++ ++gboolean ++WindowDragMotionHandler(GtkWidget *aWidget, GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, ++ gint aX, gint aY, guint aTime); ++gboolean ++WindowDragDropHandler(GtkWidget *aWidget, GdkDragContext *aDragContext, ++ nsWaylandDragContext *aWaylandDragContext, gint aX, gint aY, ++ guint aTime); ++void ++WindowDragLeaveHandler(GtkWidget *aWidget); ++#endif ++ + class gfxPattern; + + namespace mozilla { +@@ -78,6 +93,7 @@ class nsWindow final : public nsBaseWidg + public: + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::WidgetEventTime WidgetEventTime; ++ typedef mozilla::WidgetKeyboardEvent WidgetKeyboardEvent; + typedef mozilla::widget::PlatformCompositorWidgetDelegate PlatformCompositorWidgetDelegate; + + nsWindow(); +@@ -108,6 +124,7 @@ public: + virtual float GetDPI() override; + virtual double GetDefaultScaleInternal() override; + mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override; ++ mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() override; + virtual void SetParent(nsIWidget* aNewParent) override; + virtual void SetModal(bool aModal) override; + virtual bool IsVisible() const override; +@@ -115,8 +132,7 @@ public: + int32_t *aX, + int32_t *aY) override; + virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override; +- virtual void Move(double aX, +- double aY) override; ++ virtual void Move(double aX, double aY) override; + virtual void Show (bool aState) override; + virtual void Resize (double aWidth, + double aHeight, +@@ -228,6 +244,8 @@ public: + virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget, + LayoutDeviceIntRegion& aInvalidRegion) override; + ++ void SetProgress(unsigned long progressPercent); ++ + private: + void UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface, nsIntRect aBoundsRect); + +@@ -278,10 +296,32 @@ public: + guint aTime); + static void UpdateDragStatus (GdkDragContext *aDragContext, + nsIDragService *aDragService); +- // If this dispatched the keydown event actually, this returns TRUE, +- // otherwise, FALSE. +- bool DispatchKeyDownEvent(GdkEventKey *aEvent, +- bool *aIsCancelled); ++ /** ++ * DispatchKeyDownOrKeyUpEvent() dispatches eKeyDown or eKeyUp event. ++ * ++ * @param aEvent A native GDK_KEY_PRESS or GDK_KEY_RELEASE ++ * event. ++ * @param aProcessedByIME true if the event is handled by IME. ++ * @param aIsCancelled [Out] true if the default is prevented. ++ * @return true if eKeyDown event is actually dispatched. ++ * Otherwise, false. ++ */ ++ bool DispatchKeyDownOrKeyUpEvent(GdkEventKey* aEvent, ++ bool aProcessedByIME, ++ bool* aIsCancelled); ++ ++ /** ++ * DispatchKeyDownOrKeyUpEvent() dispatches eKeyDown or eKeyUp event. ++ * ++ * @param aEvent An eKeyDown or eKeyUp event. This will be ++ * dispatched as is. ++ * @param aIsCancelled [Out] true if the default is prevented. ++ * @return true if eKeyDown event is actually dispatched. ++ * Otherwise, false. ++ */ ++ bool DispatchKeyDownOrKeyUpEvent(WidgetKeyboardEvent& aEvent, ++ bool* aIsCancelled); ++ + WidgetEventTime GetWidgetEventTime(guint32 aEventTime); + mozilla::TimeStamp GetEventTimeStamp(guint32 aEventTime); + mozilla::CurrentX11TimeGetter* GetCurrentTimeGetter(); +@@ -375,6 +415,9 @@ public: + + virtual bool WidgetTypeSupportsAcceleration() override; + ++ nsresult SetSystemFont(const nsCString& aFontName) override; ++ nsresult GetSystemFont(nsCString& aFontName) override; ++ + typedef enum { CSD_SUPPORT_SYSTEM, // CSD including shadows + CSD_SUPPORT_CLIENT, // CSD without shadows + CSD_SUPPORT_NONE, // WM does not support CSD at all +@@ -452,13 +495,24 @@ private: + gint* aRootX, gint* aRootY); + void ClearCachedResources(); + nsIWidgetListener* GetListener(); +- bool IsComposited() const; + + void UpdateClientOffsetForCSDWindow(); + + nsWindow* GetTransientForWindowIfPopup(); + bool IsHandlingTouchSequence(GdkEventSequence* aSequence); + ++#ifdef MOZ_X11 ++ typedef enum { GTK_WIDGET_COMPOSIDED_DEFAULT = 0, ++ GTK_WIDGET_COMPOSIDED_DISABLED = 1, ++ GTK_WIDGET_COMPOSIDED_ENABLED = 2 ++ } WindowComposeRequest; ++ ++ void SetCompositorHint(WindowComposeRequest aState); ++#endif ++ nsCString mGtkWindowTypeName; ++ nsCString mGtkWindowRoleName; ++ void RefreshWindowClass(); ++ + GtkWidget *mShell; + MozContainer *mContainer; + GdkWindow *mGdkWindow; +@@ -558,6 +612,9 @@ private: + // full translucency at this time; each pixel is either fully opaque + // or fully transparent. + gchar* mTransparencyBitmap; ++ // True when we're on compositing window manager and this ++ // window is using visual with alpha channel. ++ bool mHasAlphaVisual; + + // all of our DND stuff + void InitDragEvent(mozilla::WidgetDragEvent& aEvent); +diff -up thunderbird-60.3.0/widget/gtk/PlatformWidgetTypes.ipdlh.wayland thunderbird-60.3.0/widget/gtk/PlatformWidgetTypes.ipdlh +--- thunderbird-60.3.0/widget/gtk/PlatformWidgetTypes.ipdlh.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/PlatformWidgetTypes.ipdlh 2018-11-20 12:04:43.741787337 +0100 +@@ -15,6 +15,7 @@ struct GtkCompositorWidgetInitData + { + uintptr_t XWindow; + nsCString XDisplayString; ++ bool Shaped; + + LayoutDeviceIntSize InitialClientSize; + }; +diff -up thunderbird-60.3.0/widget/gtk/ScreenHelperGTK.cpp.wayland thunderbird-60.3.0/widget/gtk/ScreenHelperGTK.cpp +--- thunderbird-60.3.0/widget/gtk/ScreenHelperGTK.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/ScreenHelperGTK.cpp 2018-11-20 12:04:43.741787337 +0100 +@@ -197,7 +197,7 @@ ScreenHelperGTK::RefreshScreens() + } + + ScreenManager& screenManager = ScreenManager::GetSingleton(); +- screenManager.Refresh(Move(screenList)); ++ screenManager.Refresh(std::move(screenList)); + } + + } // namespace widget +diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.cpp.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.cpp +--- thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.cpp.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.cpp 2018-11-20 12:04:43.741787337 +0100 +@@ -31,6 +31,7 @@ WindowSurfaceProvider::WindowSurfaceProv + #ifdef MOZ_WAYLAND + , mWidget(nullptr) + #endif ++ , mIsShaped(false) + { + } + +@@ -38,7 +39,8 @@ void WindowSurfaceProvider::Initialize( + Display* aDisplay, + Window aWindow, + Visual* aVisual, +- int aDepth) ++ int aDepth, ++ bool aIsShaped) + { + // We should not be initialized + MOZ_ASSERT(!mXDisplay); +@@ -50,6 +52,7 @@ void WindowSurfaceProvider::Initialize( + mXWindow = aWindow; + mXVisual = aVisual; + mXDepth = aDepth; ++ mIsShaped = aIsShaped; + mIsX11Display = true; + } + +@@ -88,21 +91,22 @@ WindowSurfaceProvider::CreateWindowSurfa + // 3. XPutImage + + #ifdef MOZ_WIDGET_GTK +- if (gfxVars::UseXRender()) { ++ if (!mIsShaped && gfxVars::UseXRender()) { + LOGDRAW(("Drawing to nsWindow %p using XRender\n", (void*)this)); + return MakeUnique(mXDisplay, mXWindow, mXVisual, mXDepth); + } + #endif // MOZ_WIDGET_GTK + + #ifdef MOZ_HAVE_SHMIMAGE +- if (nsShmImage::UseShm()) { ++ if (!mIsShaped && nsShmImage::UseShm()) { + LOGDRAW(("Drawing to nsWindow %p using MIT-SHM\n", (void*)this)); + return MakeUnique(mXDisplay, mXWindow, mXVisual, mXDepth); + } + #endif // MOZ_HAVE_SHMIMAGE + + LOGDRAW(("Drawing to nsWindow %p using XPutImage\n", (void*)this)); +- return MakeUnique(mXDisplay, mXWindow, mXVisual, mXDepth); ++ return MakeUnique(mXDisplay, mXWindow, mXVisual, ++ mXDepth, mIsShaped); + } + + already_AddRefed +@@ -125,7 +129,7 @@ WindowSurfaceProvider::StartRemoteDrawin + // We can't use WindowSurfaceX11Image fallback on Wayland but + // Lock() call on WindowSurfaceWayland should never fail. + gfxWarningOnce() << "Failed to lock WindowSurface, falling back to XPutImage backend."; +- mWindowSurface = MakeUnique(mXDisplay, mXWindow, mXVisual, mXDepth); ++ mWindowSurface = MakeUnique(mXDisplay, mXWindow, mXVisual, mXDepth, mIsShaped); + dt = mWindowSurface->Lock(aInvalidRegion); + } + return dt.forget(); +diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h +--- thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h.wayland 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/WindowSurfaceProvider.h 2018-11-20 12:04:43.742787334 +0100 +@@ -17,6 +17,7 @@ + #include + #endif + #include // for Window, Display, Visual, etc. ++#include "X11UndefineNone.h" + + class nsWindow; + +@@ -43,7 +44,8 @@ public: + Display* aDisplay, + Window aWindow, + Visual* aVisual, +- int aDepth); ++ int aDepth, ++ bool aIsShaped); + + #ifdef MOZ_WAYLAND + void Initialize(nsWindow *aWidget); +@@ -75,6 +77,7 @@ private: + #ifdef MOZ_WAYLAND + nsWindow* mWidget; + #endif ++ bool mIsShaped; + }; + + } // namespace widget +diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp +--- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.cpp 2018-11-20 12:04:43.742787334 +0100 +@@ -151,8 +151,9 @@ static nsWaylandDisplay* WaylandDisplayG + static void WaylandDisplayRelease(wl_display *aDisplay); + static void WaylandDisplayLoop(wl_display *aDisplay); + +-// TODO: is the 60pfs loop correct? +-#define EVENT_LOOP_DELAY (1000/60) ++// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with ++// compositor event loop. ++#define EVENT_LOOP_DELAY (1000/240) + + // Get WaylandDisplay for given wl_display and actual calling thread. + static nsWaylandDisplay* +@@ -304,6 +305,7 @@ nsWaylandDisplay::nsWaylandDisplay(wl_di + : mThreadId(PR_GetCurrentThread()) + // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format + // and is always present. ++ // TODO: Provide also format without alpha (Bug 1470126). + , mFormat(gfx::SurfaceFormat::B8G8R8A8) + , mShm(nullptr) + , mDisplay(aDisplay) +@@ -522,7 +524,7 @@ WindowBackBuffer::Detach() + } + + bool +-WindowBackBuffer::SetImageDataFromBackBuffer( ++WindowBackBuffer::SetImageDataFromBuffer( + class WindowBackBuffer* aSourceBuffer) + { + if (!IsMatchingSize(aSourceBuffer)) { +@@ -535,11 +537,9 @@ WindowBackBuffer::SetImageDataFromBackBu + } + + already_AddRefed +-WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion) ++WindowBackBuffer::Lock() + { +- gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); +- gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); +- ++ gfx::IntSize lockSize(mWidth, mHeight); + return gfxPlatform::CreateDrawTargetForData(static_cast(mShmPool.GetImageData()), + lockSize, + BUFFER_BPP * mWidth, +@@ -551,6 +551,8 @@ frame_callback_handler(void *data, struc + { + auto surface = reinterpret_cast(data); + surface->FrameCallbackHandler(); ++ ++ gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(time); + } + + static const struct wl_callback_listener frame_listener = { +@@ -560,26 +562,40 @@ static const struct wl_callback_listener + WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow) + : mWindow(aWindow) + , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())) +- , mFrontBuffer(nullptr) +- , mBackBuffer(nullptr) ++ , mWaylandBuffer(nullptr) ++ , mBackupBuffer(nullptr) + , mFrameCallback(nullptr) +- , mFrameCallbackSurface(nullptr) ++ , mLastCommittedSurface(nullptr) + , mDisplayThreadMessageLoop(MessageLoop::current()) +- , mDelayedCommit(false) +- , mFullScreenDamage(false) ++ , mDelayedCommitHandle(nullptr) ++ , mDrawToWaylandBufferDirectly(true) ++ , mPendingCommit(false) ++ , mWaylandBufferFullScreenDamage(false) + , mIsMainThread(NS_IsMainThread()) ++ , mNeedScaleFactorUpdate(true) + { + } + + WindowSurfaceWayland::~WindowSurfaceWayland() + { +- delete mFrontBuffer; +- delete mBackBuffer; ++ if (mPendingCommit) { ++ NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!"); ++ } ++ ++ if (mDelayedCommitHandle) { ++ // Delete reference to this to prevent WaylandBufferDelayCommitHandler() ++ // operate on released this. mDelayedCommitHandle itself will ++ // be released at WaylandBufferDelayCommitHandler(). ++ *mDelayedCommitHandle = nullptr; ++ } + + if (mFrameCallback) { + wl_callback_destroy(mFrameCallback); + } + ++ delete mWaylandBuffer; ++ delete mBackupBuffer; ++ + if (!mIsMainThread) { + // We can be destroyed from main thread even though we was created/used + // in compositor thread. We have to unref/delete WaylandDisplay in compositor +@@ -593,162 +609,309 @@ WindowSurfaceWayland::~WindowSurfaceWayl + } + } + +-void +-WindowSurfaceWayland::UpdateScaleFactor() +-{ +- wl_surface* waylandSurface = mWindow->GetWaylandSurface(); +- if (waylandSurface) { +- wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); +- } +-} +- + WindowBackBuffer* +-WindowSurfaceWayland::GetBufferToDraw(int aWidth, int aHeight) ++WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight) + { +- if (!mFrontBuffer) { +- mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); +- mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); +- return mFrontBuffer; ++ if (!mWaylandBuffer) { ++ mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); ++ mBackupBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); ++ return mWaylandBuffer; + } + +- if (!mFrontBuffer->IsAttached()) { +- if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) { +- mFrontBuffer->Resize(aWidth, aHeight); ++ if (!mWaylandBuffer->IsAttached()) { ++ if (!mWaylandBuffer->IsMatchingSize(aWidth, aHeight)) { ++ mWaylandBuffer->Resize(aWidth, aHeight); + // There's a chance that scale factor has been changed + // when buffer size changed +- UpdateScaleFactor(); ++ mNeedScaleFactorUpdate = true; + } +- return mFrontBuffer; ++ return mWaylandBuffer; + } + + // Front buffer is used by compositor, draw to back buffer +- if (mBackBuffer->IsAttached()) { ++ if (mBackupBuffer->IsAttached()) { + NS_WARNING("No drawing buffer available"); + return nullptr; + } + +- MOZ_ASSERT(!mDelayedCommit, ++ MOZ_ASSERT(!mPendingCommit, + "Uncommitted buffer switch, screen artifacts ahead."); + +- WindowBackBuffer *tmp = mFrontBuffer; +- mFrontBuffer = mBackBuffer; +- mBackBuffer = tmp; ++ WindowBackBuffer *tmp = mWaylandBuffer; ++ mWaylandBuffer = mBackupBuffer; ++ mBackupBuffer = tmp; + +- if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) { ++ if (mBackupBuffer->IsMatchingSize(aWidth, aHeight)) { + // Former front buffer has the same size as a requested one. + // Gecko may expect a content already drawn on screen so copy + // existing data to the new buffer. +- mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer); ++ mWaylandBuffer->SetImageDataFromBuffer(mBackupBuffer); + // When buffer switches we need to damage whole screen + // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260) +- mFullScreenDamage = true; ++ mWaylandBufferFullScreenDamage = true; + } else { + // Former buffer has different size from the new request. Only resize + // the new buffer and leave gecko to render new whole content. +- mFrontBuffer->Resize(aWidth, aHeight); ++ mWaylandBuffer->Resize(aWidth, aHeight); ++ } ++ ++ return mWaylandBuffer; ++} ++ ++already_AddRefed ++WindowSurfaceWayland::LockWaylandBuffer(int aWidth, int aHeight) ++{ ++ WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight); ++ if (buffer) { ++ return buffer->Lock(); ++ } ++ ++ NS_WARNING("WindowSurfaceWayland::LockWaylandBuffer(): No buffer available"); ++ return nullptr; ++} ++ ++already_AddRefed ++WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize) ++{ ++ if (!mImageSurface || mImageSurface->CairoStatus() || ++ !(aLockSize <= mImageSurface->GetSize())) { ++ mImageSurface = new gfxImageSurface(aLockSize, ++ SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat())); ++ if (mImageSurface->CairoStatus()) { ++ return nullptr; ++ } + } + +- return mFrontBuffer; ++ return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(), ++ mImageSurface->GetSize(), ++ mImageSurface->Stride(), ++ mWaylandDisplay->GetSurfaceFormat()); + } + ++/* ++ There are some situations which can happen here: ++ ++ A) Lock() is called to whole surface. In that case we don't need ++ to clip/buffer the drawing and we can return wl_buffer directly ++ for drawing. ++ - mWaylandBuffer is available - that's an ideal situation. ++ - mWaylandBuffer is locked by compositor - flip buffers and draw. ++ - if we can't flip buffers - go B) ++ ++ B) Lock() is requested for part(s) of screen. We need to provide temporary ++ surface to draw into and copy result (clipped) to target wl_surface. ++ */ + already_AddRefed + WindowSurfaceWayland::Lock(const LayoutDeviceIntRegion& aRegion) + { + MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); + +- // We allocate back buffer to widget size but return only +- // portion requested by aRegion. +- LayoutDeviceIntRect rect = mWindow->GetBounds(); +- WindowBackBuffer* buffer = GetBufferToDraw(rect.width, +- rect.height); +- if (!buffer) { +- NS_WARNING("No drawing buffer available"); +- return nullptr; ++ LayoutDeviceIntRect screenRect = mWindow->GetBounds(); ++ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); ++ gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); ++ ++ // Are we asked for entire nsWindow to draw? ++ mDrawToWaylandBufferDirectly = (aRegion.GetNumRects() == 1 && ++ bounds.x == 0 && bounds.y == 0 && ++ lockSize.width == screenRect.width && ++ lockSize.height == screenRect.height); ++ ++ if (mDrawToWaylandBufferDirectly) { ++ RefPtr dt = LockWaylandBuffer(screenRect.width, ++ screenRect.height); ++ if (dt) { ++ return dt.forget(); ++ } ++ ++ // We don't have any front buffer available. Try indirect drawing ++ // to mImageSurface which is mirrored to front buffer at commit. ++ mDrawToWaylandBufferDirectly = false; + } + +- return buffer->Lock(aRegion); ++ 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 dt = LockWaylandBuffer(screenRect.width, ++ screenRect.height); ++ RefPtr surf = ++ gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(), ++ mImageSurface->GetSize(), ++ mImageSurface->Format()); ++ if (!dt || !surf) { ++ return false; ++ } ++ ++ uint32_t numRects = aRegion.GetNumRects(); ++ if (numRects != 1) { ++ AutoTArray rects; ++ rects.SetCapacity(numRects); ++ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { ++ rects.AppendElement(iter.Get().ToUnknownRect()); ++ } ++ dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length()); ++ } ++ ++ dt->DrawSurface(surf, rect, rect); ++ ++ if (numRects != 1) { ++ dt->PopClip(); ++ } ++ ++ return true; ++} ++ ++static void ++WaylandBufferDelayCommitHandler(WindowSurfaceWayland **aSurface) ++{ ++ if (*aSurface) { ++ (*aSurface)->DelayedCommitHandler(); ++ } else { ++ // Referenced WindowSurfaceWayland is already deleted. ++ // Do nothing but just release the mDelayedCommitHandle allocated at ++ // WindowSurfaceWayland::CommitWaylandBuffer(). ++ free(aSurface); ++ } + } + + void +-WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) ++WindowSurfaceWayland::CommitWaylandBuffer() + { +- MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); ++ MOZ_ASSERT(mPendingCommit, "Committing empty surface!"); + + wl_surface* waylandSurface = mWindow->GetWaylandSurface(); + if (!waylandSurface) { +- // Target window is already destroyed - don't bother to render there. ++ // Target window is not created yet - delay the commit. This can happen only ++ // when the window is newly created and there's no active ++ // frame callback pending. ++ MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface, ++ "Missing wayland surface at frame callback!"); ++ ++ // Do nothing if there's already mDelayedCommitHandle pending. ++ if (!mDelayedCommitHandle) { ++ mDelayedCommitHandle = static_cast( ++ moz_xmalloc(sizeof(*mDelayedCommitHandle))); ++ *mDelayedCommitHandle = this; ++ ++ MessageLoop::current()->PostDelayedTask( ++ NewRunnableFunction("WaylandBackBufferCommit", ++ &WaylandBufferDelayCommitHandler, ++ mDelayedCommitHandle), ++ EVENT_LOOP_DELAY); ++ } + return; + } + wl_proxy_set_queue((struct wl_proxy *)waylandSurface, + mWaylandDisplay->GetEventQueue()); + +- if (mFullScreenDamage) { ++ // We have an active frame callback request so handle it. ++ if (mFrameCallback) { ++ if (waylandSurface == mLastCommittedSurface) { ++ // We have an active frame callback pending from our recent surface. ++ // It means we should defer the commit to FrameCallbackHandler(). ++ return; ++ } ++ // If our stored wl_surface does not match the actual one it means the frame ++ // callback is no longer active and we should release it. ++ wl_callback_destroy(mFrameCallback); ++ mFrameCallback = nullptr; ++ mLastCommittedSurface = nullptr; ++ } ++ ++ if (mWaylandBufferFullScreenDamage) { + LayoutDeviceIntRect rect = mWindow->GetBounds(); + wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height); +- mFullScreenDamage = false; ++ mWaylandBufferFullScreenDamage = false; + } else { +- for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { ++ gint scaleFactor = mWindow->GdkScaleFactor(); ++ for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) { + const mozilla::LayoutDeviceIntRect &r = iter.Get(); +- wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height); ++ // We need to remove the scale factor because the wl_surface_damage ++ // also multiplies by current scale factor. ++ wl_surface_damage(waylandSurface, r.x/scaleFactor, r.y/scaleFactor, ++ r.width/scaleFactor, r.height/scaleFactor); + } + } + +- // Frame callback is always connected to actual wl_surface. When the surface +- // is unmapped/deleted the frame callback is never called. Unfortunatelly +- // we don't know if the frame callback is not going to be called. +- // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden +- // creates a new one when is visible. +- if (mFrameCallback && mFrameCallbackSurface == waylandSurface) { +- // Do nothing here - we have a valid wl_surface and the buffer will be +- // commited to compositor in next frame callback event. +- mDelayedCommit = true; +- return; +- } else { +- if (mFrameCallback) { +- // Delete frame callback connected to obsoleted wl_surface. +- wl_callback_destroy(mFrameCallback); +- } ++ // Clear all back buffer damage as we're committing ++ // all requested regions. ++ mWaylandBufferDamage.SetEmpty(); + +- mFrameCallback = wl_surface_frame(waylandSurface); +- wl_callback_add_listener(mFrameCallback, &frame_listener, this); +- mFrameCallbackSurface = waylandSurface; +- +- // There's no pending frame callback so we can draw immediately +- // and create frame callback for possible subsequent drawing. +- mFrontBuffer->Attach(waylandSurface); +- mDelayedCommit = false; ++ mFrameCallback = wl_surface_frame(waylandSurface); ++ wl_callback_add_listener(mFrameCallback, &frame_listener, this); ++ ++ if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) { ++ wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); ++ mNeedScaleFactorUpdate = false; ++ } ++ ++ mWaylandBuffer->Attach(waylandSurface); ++ mLastCommittedSurface = waylandSurface; ++ ++ // There's no pending commit, all changes are sent to compositor. ++ mPendingCommit = false; ++} ++ ++void ++WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) ++{ ++ MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); ++ ++ // We have new content at mImageSurface - copy data to mWaylandBuffer first. ++ if (!mDrawToWaylandBufferDirectly) { ++ CommitImageSurfaceToWaylandBuffer(aInvalidRegion); + } ++ ++ // If we're not at fullscreen damage add drawing area from aInvalidRegion ++ if (!mWaylandBufferFullScreenDamage) { ++ mWaylandBufferDamage.OrWith(aInvalidRegion); ++ } ++ ++ // We're ready to commit. ++ mPendingCommit = true; ++ CommitWaylandBuffer(); + } + + void + WindowSurfaceWayland::FrameCallbackHandler() + { + MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); ++ MOZ_ASSERT(mFrameCallback != nullptr, ++ "FrameCallbackHandler() called without valid frame callback!"); ++ MOZ_ASSERT(mLastCommittedSurface != nullptr, ++ "FrameCallbackHandler() called without valid wl_surface!"); + +- if (mFrameCallback) { +- wl_callback_destroy(mFrameCallback); +- mFrameCallback = nullptr; +- mFrameCallbackSurface = nullptr; ++ wl_callback_destroy(mFrameCallback); ++ mFrameCallback = nullptr; ++ ++ if (mPendingCommit) { ++ CommitWaylandBuffer(); + } ++} + +- if (mDelayedCommit) { +- wl_surface* waylandSurface = mWindow->GetWaylandSurface(); +- if (!waylandSurface) { +- // Target window is already destroyed - don't bother to render there. +- NS_WARNING("No drawing buffer available"); +- return; +- } +- wl_proxy_set_queue((struct wl_proxy *)waylandSurface, +- mWaylandDisplay->GetEventQueue()); ++void ++WindowSurfaceWayland::DelayedCommitHandler() ++{ ++ MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!"); + +- // Send pending surface to compositor and register frame callback +- // for possible subsequent drawing. +- mFrameCallback = wl_surface_frame(waylandSurface); +- wl_callback_add_listener(mFrameCallback, &frame_listener, this); +- mFrameCallbackSurface = waylandSurface; ++ *mDelayedCommitHandle = nullptr; ++ free(mDelayedCommitHandle); ++ mDelayedCommitHandle = nullptr; + +- mFrontBuffer->Attach(waylandSurface); +- mDelayedCommit = false; ++ if (mPendingCommit) { ++ CommitWaylandBuffer(); + } + } + +diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h +--- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h 2018-11-20 12:04:43.742787334 +0100 +@@ -8,6 +8,7 @@ + #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H + + #include ++#include "mozilla/gfx/Types.h" + + namespace mozilla { + namespace widget { +@@ -66,14 +67,14 @@ public: + WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight); + ~WindowBackBuffer(); + +- already_AddRefed Lock(const LayoutDeviceIntRegion& aRegion); ++ already_AddRefed Lock(); + + void Attach(wl_surface* aSurface); + void Detach(); + bool IsAttached() { return mAttached; } + + bool Resize(int aWidth, int aHeight); +- bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer); ++ bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer); + + bool IsMatchingSize(int aWidth, int aHeight) + { +@@ -110,22 +111,32 @@ public: + already_AddRefed 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 LockWaylandBuffer(int aWidth, int aHeight); ++ already_AddRefed LockImageSurface(const gfx::IntSize& aLockSize); ++ bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion); ++ void CommitWaylandBuffer(); + + // TODO: Do we need to hold a reference to nsWindow object? + nsWindow* mWindow; + nsWaylandDisplay* mWaylandDisplay; +- WindowBackBuffer* mFrontBuffer; +- WindowBackBuffer* mBackBuffer; ++ WindowBackBuffer* mWaylandBuffer; ++ LayoutDeviceIntRegion mWaylandBufferDamage; ++ WindowBackBuffer* mBackupBuffer; ++ RefPtr 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.3.0/widget/gtk/WindowSurfaceX11Image.cpp.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.cpp +--- thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.cpp.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.cpp 2018-11-20 12:04:43.743787331 +0100 +@@ -12,19 +12,42 @@ + #include "gfxPlatform.h" + #include "gfx2DGlue.h" + ++#include ++ + namespace mozilla { + namespace widget { + ++// gfxImageSurface pixel format configuration. ++#define SHAPED_IMAGE_SURFACE_BPP 4 ++#ifdef IS_BIG_ENDIAN ++ #define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 0 ++#else ++ #define SHAPED_IMAGE_SURFACE_ALPHA_INDEX 3 ++#endif ++ + WindowSurfaceX11Image::WindowSurfaceX11Image(Display* aDisplay, + Window aWindow, + Visual* aVisual, +- unsigned int aDepth) ++ unsigned int aDepth, ++ bool aIsShaped) + : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth) ++ , mTransparencyBitmap(nullptr) ++ , mTransparencyBitmapWidth(0) ++ , mTransparencyBitmapHeight(0) ++ , mIsShaped(aIsShaped) + { + } + + WindowSurfaceX11Image::~WindowSurfaceX11Image() + { ++ if (mTransparencyBitmap) { ++ delete[] mTransparencyBitmap; ++ ++ Display* xDisplay = mWindowSurface->XDisplay(); ++ Window xDrawable = mWindowSurface->XDrawable(); ++ ++ XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, X11None, ShapeSet); ++ } + } + + already_AddRefed +@@ -50,6 +73,13 @@ WindowSurfaceX11Image::Lock(const Layout + gfx::SurfaceFormat::X8R8G8B8_UINT32; + } + ++ // Use alpha image format for shaped window as we derive ++ // the shape bitmap from alpha channel. Must match SHAPED_IMAGE_SURFACE_BPP ++ // and SHAPED_IMAGE_SURFACE_ALPHA_INDEX. ++ if (mIsShaped) { ++ format = gfx::SurfaceFormat::A8R8G8B8_UINT32; ++ } ++ + mImageSurface = new gfxImageSurface(size, format); + if (mImageSurface->CairoStatus()) { + return nullptr; +@@ -82,6 +112,132 @@ WindowSurfaceX11Image::Lock(const Layout + ImageFormatToSurfaceFormat(format)); + } + ++// The transparency bitmap routines are derived form the ones at nsWindow.cpp. ++// The difference here is that we compose to RGBA image and then create ++// the shape mask from final image alpha channel. ++static inline int32_t ++GetBitmapStride(int32_t width) ++{ ++ return (width+7)/8; ++} ++ ++static bool ++ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight, ++ const nsIntRect& aRect, uint8_t* aImageData) ++{ ++ int32_t stride = aMaskWidth*SHAPED_IMAGE_SURFACE_BPP; ++ int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost(); ++ int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth); ++ for (y = aRect.y; y < yMax; y++) { ++ gchar* maskBytes = aMaskBits + y*maskBytesPerRow; ++ uint8_t* alphas = aImageData; ++ for (x = aRect.x; x < xMax; x++) { ++ bool newBit = *(alphas+SHAPED_IMAGE_SURFACE_ALPHA_INDEX) > 0x7f; ++ alphas += SHAPED_IMAGE_SURFACE_BPP; ++ ++ gchar maskByte = maskBytes[x >> 3]; ++ bool maskBit = (maskByte & (1 << (x & 7))) != 0; ++ ++ if (maskBit != newBit) { ++ return true; ++ } ++ } ++ aImageData += stride; ++ } ++ ++ return false; ++} ++ ++static void ++UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth, int32_t aMaskHeight, ++ const nsIntRect& aRect, uint8_t* aImageData) ++{ ++ int32_t stride = aMaskWidth*SHAPED_IMAGE_SURFACE_BPP; ++ int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost(); ++ int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth); ++ for (y = aRect.y; y < yMax; y++) { ++ gchar* maskBytes = aMaskBits + y*maskBytesPerRow; ++ uint8_t* alphas = aImageData; ++ for (x = aRect.x; x < xMax; x++) { ++ bool newBit = *(alphas+SHAPED_IMAGE_SURFACE_ALPHA_INDEX) > 0x7f; ++ alphas += SHAPED_IMAGE_SURFACE_BPP; ++ ++ gchar mask = 1 << (x & 7); ++ gchar maskByte = maskBytes[x >> 3]; ++ // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11 ++ maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask); ++ } ++ aImageData += stride; ++ } ++} ++ ++void ++WindowSurfaceX11Image::ResizeTransparencyBitmap(int aWidth, int aHeight) ++{ ++ int32_t actualSize = ++ GetBitmapStride(mTransparencyBitmapWidth)*mTransparencyBitmapHeight; ++ int32_t newSize = GetBitmapStride(aWidth)*aHeight; ++ ++ if (actualSize < newSize) { ++ delete[] mTransparencyBitmap; ++ mTransparencyBitmap = new gchar[newSize]; ++ } ++ ++ mTransparencyBitmapWidth = aWidth; ++ mTransparencyBitmapHeight = aHeight; ++} ++ ++void ++WindowSurfaceX11Image::ApplyTransparencyBitmap() ++{ ++ gfx::IntSize size = mWindowSurface->GetSize(); ++ bool maskChanged = true; ++ ++ if (!mTransparencyBitmap) { ++ mTransparencyBitmapWidth = size.width; ++ mTransparencyBitmapHeight = size.height; ++ ++ int32_t byteSize = ++ GetBitmapStride(mTransparencyBitmapWidth)*mTransparencyBitmapHeight; ++ mTransparencyBitmap = new gchar[byteSize]; ++ } else { ++ bool sizeChanged = (size.width != mTransparencyBitmapWidth || ++ size.height != mTransparencyBitmapHeight); ++ ++ if (sizeChanged) { ++ ResizeTransparencyBitmap(size.width, size.height); ++ } else { ++ maskChanged = ChangedMaskBits(mTransparencyBitmap, ++ mTransparencyBitmapWidth, mTransparencyBitmapHeight, ++ nsIntRect(0, 0, size.width, size.height), ++ (uint8_t*)mImageSurface->Data()); ++ } ++ } ++ ++ if (maskChanged) { ++ UpdateMaskBits(mTransparencyBitmap, ++ mTransparencyBitmapWidth, ++ mTransparencyBitmapHeight, ++ nsIntRect(0, 0, size.width, size.height), ++ (uint8_t*)mImageSurface->Data()); ++ ++ // We use X11 calls where possible, because GDK handles expose events ++ // for shaped windows in a way that's incompatible with us (Bug 635903). ++ // It doesn't occur when the shapes are set through X. ++ Display* xDisplay = mWindowSurface->XDisplay(); ++ Window xDrawable = mWindowSurface->XDrawable(); ++ Pixmap maskPixmap = XCreateBitmapFromData(xDisplay, ++ xDrawable, ++ mTransparencyBitmap, ++ mTransparencyBitmapWidth, ++ mTransparencyBitmapHeight); ++ XShapeCombineMask(xDisplay, xDrawable, ++ ShapeBounding, 0, 0, ++ maskPixmap, ShapeSet); ++ XFreePixmap(xDisplay, maskPixmap); ++ } ++} ++ + void + WindowSurfaceX11Image::Commit(const LayoutDeviceIntRegion& aInvalidRegion) + { +@@ -112,6 +268,10 @@ WindowSurfaceX11Image::Commit(const Layo + dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length()); + } + ++ if (mIsShaped) { ++ ApplyTransparencyBitmap(); ++ } ++ + dt->DrawSurface(surf, rect, rect); + + if (numRects != 1) { +diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.h +--- thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.h.wayland 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/gtk/WindowSurfaceX11Image.h 2018-11-20 12:04:43.743787331 +0100 +@@ -19,7 +19,7 @@ namespace widget { + class WindowSurfaceX11Image : public WindowSurfaceX11 { + public: + WindowSurfaceX11Image(Display* aDisplay, Window aWindow, Visual* aVisual, +- unsigned int aDepth); ++ unsigned int aDepth, bool aIsShaped); + ~WindowSurfaceX11Image(); + + already_AddRefed Lock(const LayoutDeviceIntRegion& aRegion) override; +@@ -27,8 +27,16 @@ public: + bool IsFallback() const override { return true; } + + private: ++ void ResizeTransparencyBitmap(int aWidth, int aHeight); ++ void ApplyTransparencyBitmap(); ++ + RefPtr mWindowSurface; + RefPtr mImageSurface; ++ ++ gchar* mTransparencyBitmap; ++ int32_t mTransparencyBitmapWidth; ++ int32_t mTransparencyBitmapHeight; ++ bool mIsShaped; + }; + + } // namespace widget diff --git a/thunderbird-dbus-remote.patch b/thunderbird-dbus-remote.patch new file mode 100644 index 0000000..856e64e --- /dev/null +++ b/thunderbird-dbus-remote.patch @@ -0,0 +1,188 @@ +diff -up thunderbird-60.3.0/toolkit/components/remote/nsDBusRemoteService.cpp.old thunderbird-60.3.0/toolkit/components/remote/nsDBusRemoteService.cpp +--- thunderbird-60.3.0/toolkit/components/remote/nsDBusRemoteService.cpp.old 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/toolkit/components/remote/nsDBusRemoteService.cpp 2018-11-14 13:37:32.223714689 +0100 +@@ -174,6 +174,7 @@ nsDBusRemoteService::Startup(const char* + return NS_ERROR_FAILURE; + } + dbus_connection_set_exit_on_disconnect(mConnection, false); ++ dbus_connection_setup_with_g_main(mConnection, nullptr); + + mAppName = aAppName; + ToLowerCase(mAppName); +diff -up thunderbird-60.3.0/toolkit/components/remote/nsRemoteService.cpp.old thunderbird-60.3.0/toolkit/components/remote/nsRemoteService.cpp +--- thunderbird-60.3.0/toolkit/components/remote/nsRemoteService.cpp.old 2018-11-20 10:55:35.584756422 +0100 ++++ thunderbird-60.3.0/toolkit/components/remote/nsRemoteService.cpp 2018-11-14 13:37:32.244714628 +0100 +@@ -34,20 +34,18 @@ NS_IMPL_ISUPPORTS(nsRemoteService, + NS_IMETHODIMP + nsRemoteService::Startup(const char* aAppName, const char* aProfileName) + { +-#if 0 ++#if defined(MOZ_ENABLE_DBUS) && defined(MOZ_WAYLAND) + nsresult rv; + mDBusRemoteService = new nsDBusRemoteService(); + rv = mDBusRemoteService->Startup(aAppName, aProfileName); + if (NS_FAILED(rv)) { + mDBusRemoteService = nullptr; + } ++#elif !defined(MOZ_WAYLAND) ++ mGtkRemoteService = new nsGTKRemoteService(); ++ mGtkRemoteService->Startup(aAppName, aProfileName); + #endif + +- if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) { +- mGtkRemoteService = new nsGTKRemoteService(); +- mGtkRemoteService->Startup(aAppName, aProfileName); +- } +- + if (!mDBusRemoteService && !mGtkRemoteService) + return NS_ERROR_FAILURE; + +@@ -73,7 +71,7 @@ nsRemoteService::RegisterWindow(mozIDOMW + NS_IMETHODIMP + nsRemoteService::Shutdown() + { +-#if defined(MOZ_ENABLE_DBUS) ++#if defined(MOZ_ENABLE_DBUS) && defined(MOZ_WAYLAND) + if (mDBusRemoteService) { + mDBusRemoteService->Shutdown(); + mDBusRemoteService = nullptr; +diff -up thunderbird-60.3.0/toolkit/components/remote/nsXRemoteService.cpp.old thunderbird-60.3.0/toolkit/components/remote/nsXRemoteService.cpp +--- thunderbird-60.3.0/toolkit/components/remote/nsXRemoteService.cpp.old 2018-10-30 12:45:34.000000000 +0100 ++++ thunderbird-60.3.0/toolkit/components/remote/nsXRemoteService.cpp 2018-10-31 01:08:14.000000000 +0100 +@@ -192,5 +192,5 @@ nsXRemoteService::EnsureAtoms(void) + sMozUserAtom = XAtoms[i++]; + sMozProfileAtom = XAtoms[i++]; + sMozProgramAtom = XAtoms[i++]; +- sMozCommandLineAtom = XAtoms[i++]; ++ sMozCommandLineAtom = XAtoms[i]; + } +diff -up thunderbird-60.3.0/widget/xremoteclient/DBusRemoteClient.cpp.old thunderbird-60.3.0/widget/xremoteclient/DBusRemoteClient.cpp +--- thunderbird-60.3.0/widget/xremoteclient/DBusRemoteClient.cpp.old 2018-10-30 12:45:35.000000000 +0100 ++++ thunderbird-60.3.0/widget/xremoteclient/DBusRemoteClient.cpp 2018-11-14 13:37:32.224714686 +0100 +@@ -12,6 +12,7 @@ + #include "mozilla/Base64.h" + #include "nsPrintfCString.h" + ++#include + #include + + using mozilla::LogLevel; +@@ -43,6 +44,7 @@ DBusRemoteClient::Init() + return NS_ERROR_FAILURE; + + dbus_connection_set_exit_on_disconnect(mConnection, false); ++ dbus_connection_setup_with_g_main(mConnection, nullptr); + + return NS_OK; + } +diff -up thunderbird-60.3.0/widget/xremoteclient/DBusRemoteClient.h.old thunderbird-60.3.0/widget/xremoteclient/DBusRemoteClient.h +diff -up thunderbird-60.3.0/widget/xremoteclient/moz.build.old thunderbird-60.3.0/widget/xremoteclient/moz.build +--- thunderbird-60.3.0/widget/xremoteclient/moz.build.old 2018-10-31 01:08:14.000000000 +0100 ++++ thunderbird-60.3.0/widget/xremoteclient/moz.build 2018-11-14 13:37:32.244714628 +0100 +@@ -11,7 +11,6 @@ FINAL_LIBRARY = 'xul' + + SOURCES += [ + 'RemoteUtils.cpp', +- 'XRemoteClient.cpp', + ] + + if CONFIG['MOZ_ENABLE_DBUS'] and CONFIG['MOZ_WAYLAND']: +@@ -20,3 +19,7 @@ if CONFIG['MOZ_ENABLE_DBUS'] and CONFIG[ + ] + CXXFLAGS += CONFIG['TK_CFLAGS'] + CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS'] ++else: ++ SOURCES += [ ++ 'XRemoteClient.cpp', ++ ] +diff -up thunderbird-60.3.0/widget/xremoteclient/XRemoteClient.cpp.old thunderbird-60.3.0/widget/xremoteclient/XRemoteClient.cpp +--- thunderbird-60.3.0/widget/xremoteclient/XRemoteClient.cpp.old 2018-10-30 12:45:37.000000000 +0100 ++++ thunderbird-60.3.0/widget/xremoteclient/XRemoteClient.cpp 2018-10-31 01:08:15.000000000 +0100 +@@ -9,6 +9,7 @@ + #include "mozilla/ArrayUtils.h" + #include "mozilla/IntegerPrintfMacros.h" + #include "mozilla/Sprintf.h" ++#include "mozilla/Unused.h" + #include "XRemoteClient.h" + #include "RemoteUtils.h" + #include "plstr.h" +@@ -41,7 +42,7 @@ + #else + #define TO_LITTLE_ENDIAN32(x) (x) + #endif +- ++ + #ifndef MAX_PATH + #ifdef PATH_MAX + #define MAX_PATH PATH_MAX +@@ -51,6 +52,7 @@ + #endif + + using mozilla::LogLevel; ++using mozilla::Unused; + + static mozilla::LazyLogModule sRemoteLm("XRemoteClient"); + +@@ -118,7 +120,7 @@ XRemoteClient::Init() + mMozUserAtom = XAtoms[i++]; + mMozProfileAtom = XAtoms[i++]; + mMozProgramAtom = XAtoms[i++]; +- mMozCommandLineAtom = XAtoms[i++]; ++ mMozCommandLineAtom = XAtoms[i]; + + mInitialized = true; + +@@ -472,12 +474,12 @@ XRemoteClient::FindBestWindow(const char + // pass in a program name and this window doesn't support that + // protocol, we don't include it in our list. + if (aProgram && strcmp(aProgram, "any")) { +- status = XGetWindowProperty(mDisplay, w, mMozProgramAtom, +- 0, (65536 / sizeof(long)), +- False, XA_STRING, +- &type, &format, &nitems, &bytesafter, +- &data_return); +- ++ Unused << XGetWindowProperty(mDisplay, w, mMozProgramAtom, ++ 0, (65536 / sizeof(long)), ++ False, XA_STRING, ++ &type, &format, &nitems, &bytesafter, ++ &data_return); ++ + // If the return name is not the same as what someone passed in, + // we don't want this window. + if (data_return) { +@@ -507,11 +509,11 @@ XRemoteClient::FindBestWindow(const char + } + + if (username) { +- status = XGetWindowProperty(mDisplay, w, mMozUserAtom, +- 0, (65536 / sizeof(long)), +- False, XA_STRING, +- &type, &format, &nitems, &bytesafter, +- &data_return); ++ Unused << XGetWindowProperty(mDisplay, w, mMozUserAtom, ++ 0, (65536 / sizeof(long)), ++ False, XA_STRING, ++ &type, &format, &nitems, &bytesafter, ++ &data_return); + + // if there's a username compare it with what we have + if (data_return) { +@@ -529,11 +531,11 @@ XRemoteClient::FindBestWindow(const char + // there is, then we need to make sure it matches what someone + // passed in. + if (aProfile) { +- status = XGetWindowProperty(mDisplay, w, mMozProfileAtom, +- 0, (65536 / sizeof(long)), +- False, XA_STRING, +- &type, &format, &nitems, &bytesafter, +- &data_return); ++ Unused << XGetWindowProperty(mDisplay, w, mMozProfileAtom, ++ 0, (65536 / sizeof(long)), ++ False, XA_STRING, ++ &type, &format, &nitems, &bytesafter, ++ &data_return); + + // If there's a profile compare it with what we have + if (data_return) { + diff --git a/thunderbird-mozconfig b/thunderbird-mozconfig index 0dce186..c6e3cc4 100644 --- a/thunderbird-mozconfig +++ b/thunderbird-mozconfig @@ -2,7 +2,7 @@ mk_add_options MOZ_CO_PROJECT=mail ac_add_options --enable-application=comm/mail mk_add_options AUTOCONF=autoconf-2.13 -#ac_add_options --with-system-png +ac_add_options --enable-default-toolkit=cairo-gtk3-wayland ac_add_options --prefix="$PREFIX" ac_add_options --libdir="$LIBDIR" diff --git a/thunderbird-wayland.desktop b/thunderbird-wayland.desktop new file mode 100644 index 0000000..23a7c24 --- /dev/null +++ b/thunderbird-wayland.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Version=1.0 +Name=Thunderbird on Wayland +GenericName=Email +Comment=Send and Receive Email +Exec=thunderbird %u +TryExec=thunderbird-wayland +Icon=thunderbird +Terminal=false +Type=Application +MimeType=message/rfc822;x-scheme-handler/mailto; +StartupNotify=true +Categories=Network;Email; +Name[cs]=Poštovní klient Thunderbird +Name[ca]=Client de correu Thunderbird +Name[fi]=Thunderbird-sähköposti +Name[fr]=Messagerie Thunderbird +Name[pl]=Klient poczty Thunderbird +Name[pt_BR]=Cliente de E-mail Thunderbird +Name[sv]=E-postklienten Thunderbird +Comment[ca]=Llegiu i escriviu correu +Comment[cs]=Čtení a psaní pošty +Comment[de]=Emails lesen und verfassen +Comment[fi]=Lue ja kirjoita sähköposteja +Comment[fr]=Lire et écrire des courriels +Comment[it]=Leggere e scrivere email +Comment[ja]=メールの読み書き +Comment[pl]=Czytanie i wysyłanie e-maili +Comment[pt_BR]=Ler e escrever suas mensagens +Comment[sv]=Läs och skriv e-post diff --git a/thunderbird-wayland.sh.in b/thunderbird-wayland.sh.in new file mode 100644 index 0000000..0bbbd9c --- /dev/null +++ b/thunderbird-wayland.sh.in @@ -0,0 +1,7 @@ +#!/bin/bash +# +# Run Thunderbird under Wayland +# + +export GDK_BACKEND=wayland +exec /usr/bin/thunderbird "$@" diff --git a/thunderbird.sh.in b/thunderbird.sh.in index b0ff04d..c178d2f 100644 --- a/thunderbird.sh.in +++ b/thunderbird.sh.in @@ -36,6 +36,13 @@ MOZ_LANGPACKS_DIR="$MOZ_DIST_BIN/langpacks" MOZ_EXTENSIONS_PROFILE_DIR="$HOME/.mozilla/extensions/{3550f703-e582-4d05-9a08-453d09bdfdc6}" MOZ_LAUNCHER="$MOZ_DIST_BIN/thunderbird" +## +## Enable X11 backend by default? +## +if ! [ "$GDK_BACKEND" ]; then + export GDK_BACKEND=x11 +fi + ## ## Set MOZ_ENABLE_PANGO is no longer used because Pango is enabled by default ## you may use MOZ_DISABLE_PANGO=1 to force disabling of pango diff --git a/thunderbird.spec b/thunderbird.spec index 3a8955c..0f8882c 100644 --- a/thunderbird.spec +++ b/thunderbird.spec @@ -9,7 +9,6 @@ %define system_sqlite 0 %define system_ffi 1 -%define use_gtk3 0 %define build_langpacks 1 @@ -55,23 +54,19 @@ %define big_endian 1 %endif -%if 0%{?fedora} >= 26 || 0%{?rhel} > 7 -%define use_gtk3 1 -%endif - %if %{?system_libvpx} %global libvpx_version 1.4.0 %endif %define tb_version 45.6.0 -%define thunderbird_app_id \{3550f703-e582-4d05-9a08-453d09bdfdc6\} +%define thunderbird_app_id \{3550f703-e582-4d05-9a08-453d09bdfdc6\} %global langpackdir %{mozappdir}/distribution/extensions # The tarball is pretty inconsistent with directory structure. # Sometimes there is a top level directory. That goes here. # -# IMPORTANT: If there is no top level directory, this should be +# IMPORTANT: If there is no top level directory, this should be # set to the cwd, ie: '.' %define objdir objdir %define mozappdir %{_libdir}/%{name} @@ -89,7 +84,7 @@ Summary: Mozilla Thunderbird mail/newsgroup client Name: thunderbird Version: 60.3.0 -Release: 3%{?dist} +Release: 4%{?dist} URL: http://www.mozilla.org/projects/thunderbird/ License: MPLv1.1 or GPLv2+ or LGPLv2+ Group: Applications/Internet @@ -106,6 +101,8 @@ Source11: thunderbird-mozconfig-branded Source12: thunderbird-redhat-default-prefs.js Source20: thunderbird.desktop Source21: thunderbird.sh.in +Source28: thunderbird-wayland.sh.in +Source29: thunderbird-wayland.desktop # Mozilla (XULRunner) patches Patch9: mozilla-build-arm.patch @@ -129,6 +126,8 @@ Patch309: mozilla-1460871-ldap-query.patch # Fedora specific patches Patch310: disable-dbus-remote.patch +Patch311: firefox-wayland.patch +Patch312: thunderbird-dbus-remote.patch # Upstream patches @@ -155,9 +154,7 @@ BuildRequires: zip BuildRequires: bzip2-devel BuildRequires: zlib-devel BuildRequires: libIDL-devel -%if %{?use_gtk3} BuildRequires: pkgconfig(gtk+-3.0) -%endif BuildRequires: pkgconfig(gtk+-2.0) BuildRequires: krb5-devel BuildRequires: pango-devel @@ -203,6 +200,16 @@ Suggests: u2f-hidraw-policy %description Mozilla Thunderbird is a standalone mail and newsgroup client. +%package wayland +Summary: Thunderbird Wayland launcher. +Requires: %{name} +%description wayland +The thunderbird-wayland package contains launcher and desktop file +to run Thunderbird natively on Wayland. +%files wayland +%{_bindir}/thunderbird-wayland +%attr(644,root,root) %{_datadir}/applications/mozilla-thunderbird-wayland.desktop + %if %{enable_mozilla_crashreporter} %global moz_debug_prefix %{_prefix}/lib/debug %global moz_debug_dir %{moz_debug_prefix}%{mozappdir} @@ -263,6 +270,11 @@ debug %{name}, you want to install %{name}-debuginfo instead. %endif #cd .. +# TODO - needs fixes +#%patch311 -p1 -b .wayland +#%patch312 -p1 -b .thunderbird-dbus-remote + + %if %{official_branding} # Required by Mozilla Corporation @@ -374,11 +386,6 @@ echo "ac_add_options --with-system-libvpx" >> .mozconfig %else echo "ac_add_options --without-system-libvpx" >> .mozconfig %endif -%if %{?use_gtk3} - echo "ac_add_options --enable-default-toolkit=cairo-gtk3" >> .mozconfig -%else - echo "ac_add_options --enable-default-toolkit=cairo-gtk2" >> .mozconfig -%endif %if %{enable_mozilla_crashreporter} echo "ac_add_options --enable-crashreporter" >> .mozconfig %else @@ -415,7 +422,7 @@ find ./ -name config.guess -exec cp /usr/lib/rpm/config.guess {} ';' # everywhere in the code; so, don't override that. # # Disable C++ exceptions since Mozilla code is not exception-safe -# +# MOZ_OPT_FLAGS=$(echo "$RPM_OPT_FLAGS -fpermissive" | \ %{__sed} -e 's/-Wall//') #rhbz#1037353 @@ -488,12 +495,17 @@ done desktop-file-install --vendor mozilla \ --dir $RPM_BUILD_ROOT%{_datadir}/applications \ %{SOURCE20} +desktop-file-install --vendor mozilla \ + --dir $RPM_BUILD_ROOT%{_datadir}/applications \ + %{SOURCE29} # set up the thunderbird start script rm -f $RPM_BUILD_ROOT/%{_bindir}/thunderbird %{__cat} %{SOURCE21} > $RPM_BUILD_ROOT%{_bindir}/thunderbird %{__chmod} 755 $RPM_BUILD_ROOT/%{_bindir}/thunderbird +%{__cat} %{SOURCE28} > %{buildroot}%{_bindir}/thunderbird-wayland +%{__chmod} 755 %{buildroot}%{_bindir}/thunderbird-wayland # set up our default preferences %{__cat} %{SOURCE12} | %{__sed} -e 's,THUNDERBIRD_RPM_VR,%{tb_version}-%{release},g' > \ @@ -556,7 +568,7 @@ ln -s %{_datadir}/myspell $RPM_BUILD_ROOT%{mozappdir}/dictionaries touch $RPM_BUILD_ROOT%{mozappdir}/components/compreg.dat touch $RPM_BUILD_ROOT%{mozappdir}/components/xpti.dat -# Add debuginfo for crash-stats.mozilla.com +# Add debuginfo for crash-stats.mozilla.com %if %{enable_mozilla_crashreporter} %{__mkdir_p} $RPM_BUILD_ROOT/%{moz_debug_dir} %{__cp} %{objdir}/dist/%{symbols_file_name} $RPM_BUILD_ROOT/%{moz_debug_dir} @@ -673,19 +685,18 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %endif %{mozappdir}/dependentlibs.list %{mozappdir}/distribution -%if !%{?system_libicu} -#%{mozappdir}/icudt*.dat -%endif %{mozappdir}/fonts %{mozappdir}/chrome.manifest %{mozappdir}/pingsender -%if %{?use_gtk3} %{mozappdir}/gtk2/libmozgtk.so -%endif #=============================================================================== %changelog +* Tue Nov 20 2018 Martin Stransky - 60.3.0-4 +- Build with Wayland support +- Enabled DBus remote for Wayland + * Tue Nov 13 2018 Caolán McNamara - 60.3.0-3 - rebuild for hunspell-1.7.0 @@ -986,7 +997,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : - Update to 24.3.0 * Mon Dec 16 2013 Martin Stransky - 24.2.0-4 -- Fixed rhbz#1024232 - thunderbird: squiggly lines used +- Fixed rhbz#1024232 - thunderbird: squiggly lines used for spelling correction disappear randomly * Fri Dec 13 2013 Martin Stransky - 24.2.0-3 @@ -1322,7 +1333,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : - Update to 3.0.1 * Mon Jan 18 2010 Martin Stransky - 3.0-5 -- Added fix for #480603 - thunderbird takes +- Added fix for #480603 - thunderbird takes unacceptably long time to start * Wed Dec 9 2009 Jan Horak - 3.0-4 @@ -1371,7 +1382,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : * Mon Mar 2 2009 Jan Horak - 3.0-1.beta2 - Update to 3.0 beta2 -- Added Patch2 to build correctly when building with --enable-shared option +- Added Patch2 to build correctly when building with --enable-shared option * Wed Feb 25 2009 Fedora Release Engineering - 2.0.0.18-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild @@ -1425,7 +1436,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : - Removed hardcoded MAX_PATH, PATH_MAX and MAXPATHLEN macros * Tue Sep 11 2007 Christopher Aillon 2.0.0.6-4 -- Fix crashes when using GTK+ themes containing a gtkrc which specify +- Fix crashes when using GTK+ themes containing a gtkrc which specify GtkOptionMenu::indicator_size and GtkOptionMenu::indicator_spacing * Mon Sep 10 2007 Martin Stransky 2.0.0.6-3 @@ -1471,7 +1482,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : * Mon Feb 12 2007 Martin Stransky 1.5.0.9-8 - added fix for #227406: garbage characters on some websites (when pango is disabled) - + * Tue Jan 30 2007 Christopher Aillon 1.5.0.9-7 - Updated cursor position patch from tagoh to fix issue with "jumping" cursor when in a textfield with tabs. @@ -1740,7 +1751,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : * Wed Sep 01 2004 David Hill 0.7.3-4 - remove all Xvfb-related hacks -* Wed Sep 01 2004 Warren Togami +* Wed Sep 01 2004 Warren Togami - actually apply psfonts - add mozilla gnome-uriloader patch to prevent build failure @@ -1784,7 +1795,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : - temporary workaround for enigmail skin "modern" bug * Mon May 10 2004 David Hill 0.6-0.fdr.4 -- update to Enigmail 0.84.0 +- update to Enigmail 0.84.0 - update launch script * Mon May 10 2004 David Hill 0.6-0.fdr.3 @@ -1921,4 +1932,3 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : * Mon Sep 01 2003 David Hill - initial RPM (based on the fedora MozillaFirebird-0.6.1 specfile) -