thunderbird/firefox-wayland.patch

8832 lines
339 KiB
Diff
Raw Normal View History

2018-11-20 12:38:58 +00:00
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<size_t>(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
+ // <input type="password"> 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<TextEventDispatcher> 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<nsWindow> 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<TextEventDispatcher> 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<TextEventDispatcher> 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<TextEventDispatcher> 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<GdkEventKey*>(
+ gdk_event_copy(reinterpret_cast<const GdkEvent*>(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<GdkEventKey*> mEvents;
+
+ void RemoveEventsAt(size_t aStart, size_t aCount)
+ {
+ for (size_t i = aStart; i < aStart + aCount; i++) {
+ gdk_event_free(reinterpret_cast<GdkEvent*>(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 <gdk/gdkx.h>
#include <gdk/gdkwayland.h>
+#include <wayland-egl.h>
#endif
#include <stdio.h>
#include <dlfcn.h>
@@ -207,6 +208,12 @@ moz_container_init (MozContainer *contai
#if defined(MOZ_WAYLAND)
{
+ container->subcompositor = nullptr;
+ container->surface = nullptr;
+ container->subsurface = nullptr;
+ container->eglwindow = nullptr;
+ container->parent_surface_committed = false;
+
GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
// Available as of GTK 3.8+
@@ -225,12 +232,21 @@ moz_container_init (MozContainer *contai
}
#if defined(MOZ_WAYLAND)
+static void
+moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container)
+{
+ container->parent_surface_committed = true;
+ g_signal_handler_disconnect(clock,
+ container->parent_surface_committed_handler);
+ container->parent_surface_committed_handler = 0;
+}
+
/* We want to draw to GdkWindow owned by mContainer from Compositor thread but
* Gtk+ can be used in main thread only. So we create wayland wl_surface
* and attach it as an overlay to GdkWindow.
*
* see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c
-* for reference.
+ * for reference.
*/
static gboolean
moz_container_map_surface(MozContainer *container)
@@ -242,6 +258,9 @@ moz_container_map_surface(MozContainer *
static auto sGdkWaylandWindowGetWlSurface =
(wl_surface *(*)(GdkWindow *))
dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
+ static auto sGdkWindowGetFrameClock =
+ (GdkFrameClock *(*)(GdkWindow *))
+ dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
if (GDK_IS_X11_DISPLAY(display))
@@ -250,6 +269,18 @@ moz_container_map_surface(MozContainer *
if (container->subsurface && container->surface)
return true;
+ if (!container->parent_surface_committed) {
+ if (!container->parent_surface_committed_handler) {
+ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
+ GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
+ container->parent_surface_committed_handler =
+ g_signal_connect_after(clock, "after-paint",
+ G_CALLBACK(moz_container_commited_handler),
+ container);
+ }
+ return false;
+ }
+
if (!container->surface) {
struct wl_compositor *compositor;
compositor = sGdkWaylandDisplayGetWlCompositor(display);
@@ -289,8 +320,22 @@ moz_container_map_surface(MozContainer *
static void
moz_container_unmap_surface(MozContainer *container)
{
+ g_clear_pointer(&container->eglwindow, wl_egl_window_destroy);
g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
g_clear_pointer(&container->surface, wl_surface_destroy);
+
+ if (container->parent_surface_committed_handler) {
+ static auto sGdkWindowGetFrameClock =
+ (GdkFrameClock *(*)(GdkWindow *))
+ dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
+ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
+ GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
+
+ g_signal_handler_disconnect(clock,
+ container->parent_surface_committed_handler);
+ container->parent_surface_committed_handler = 0;
+ }
+ container->parent_surface_committed = false;
}
#endif
@@ -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<nsIPowerManagerService> 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<char16_t*>
(moz_xmalloc((outUnicodeLen + sizeof('\0')) * sizeof(char16_t)));
- if (*unicodeData) {
- memcpy(*unicodeData, data + sizeof(char16_t),
- outUnicodeLen * sizeof(char16_t));
- (*unicodeData)[outUnicodeLen] = '\0';
- }
+ memcpy(*unicodeData, data + sizeof(char16_t),
+ outUnicodeLen * sizeof(char16_t));
+ (*unicodeData)[outUnicodeLen] = '\0';
} else if (charset.EqualsLiteral("UNKNOWN")) {
outUnicodeLen = 0;
return;
@@ -701,27 +699,25 @@ void ConvertHTMLtoUCS2(const char* data,
if (needed.value()) {
*unicodeData = reinterpret_cast<char16_t*>(
moz_xmalloc((needed.value() + 1) * sizeof(char16_t)));
- if (*unicodeData) {
- uint32_t result;
- size_t read;
- size_t written;
- bool hadErrors;
- Tie(result, read, written, hadErrors) =
- decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)),
- MakeSpan(*unicodeData, needed.value()),
- true);
- MOZ_ASSERT(result == kInputEmpty);
- MOZ_ASSERT(read == size_t(dataLength));
- MOZ_ASSERT(written <= needed.value());
- Unused << hadErrors;
+ uint32_t result;
+ size_t read;
+ size_t written;
+ bool hadErrors;
+ Tie(result, read, written, hadErrors) =
+ decoder->DecodeToUTF16(AsBytes(MakeSpan(data, dataLength)),
+ MakeSpan(*unicodeData, needed.value()),
+ true);
+ MOZ_ASSERT(result == kInputEmpty);
+ MOZ_ASSERT(read == size_t(dataLength));
+ MOZ_ASSERT(written <= needed.value());
+ Unused << hadErrors;
#ifdef DEBUG_CLIPBOARD
- if (read != dataLength)
- printf("didn't consume all the bytes\n");
+ if (read != dataLength)
+ printf("didn't consume all the bytes\n");
#endif
- outUnicodeLen = written;
- // null terminate.
- (*unicodeData)[outUnicodeLen] = '\0';
- }
+ outUnicodeLen = written;
+ // null terminate.
+ (*unicodeData)[outUnicodeLen] = '\0';
} // if valid length
}
}
diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp
--- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp.wayland 2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.cpp 2018-11-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<GdkWindow*>(wl_surface_get_user_data(surface));
+
+ gpointer user_data = nullptr;
+ gdk_window_get_user_data(gdkParentWindow, &user_data);
+
+ return GTK_WIDGET(user_data);
+}
+
void
DataOffer::AddMIMEType(const char *aMimeType)
{
@@ -114,7 +153,7 @@ DataOffer::GetData(wl_display* aDisplay,
GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
GError* error = nullptr;
- char* clipboardData;
+ char* clipboardData = nullptr;
g_io_channel_set_encoding(channel, nullptr, &error);
if (!error) {
@@ -155,6 +194,61 @@ WaylandDataOffer::RequestDataTransfer(co
return false;
}
+void
+WaylandDataOffer::DragOfferAccept(const char* aMimeType, uint32_t aTime)
+{
+ wl_data_offer_accept(mWaylandDataOffer, aTime, aMimeType);
+}
+
+/* We follow logic of gdk_wayland_drag_context_commit_status()/gdkdnd-wayland.c
+ * here.
+ */
+void
+WaylandDataOffer::SetDragStatus(GdkDragAction aAction, uint32_t aTime)
+{
+ uint32_t dnd_actions = gdk_to_wl_actions(aAction);
+ uint32_t all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+ wl_data_offer_set_actions(mWaylandDataOffer, all_actions, dnd_actions);
+
+ /* Workaround Wayland D&D architecture here. To get the data_device_drop()
+ signal (which routes to nsDragService::GetData() call) we need to
+ accept at least one mime type before data_device_leave().
+
+ Real wl_data_offer_accept() for actualy requested data mime type is
+ called from nsDragService::GetData().
+ */
+ if (mTargetMIMETypes[0]) {
+ wl_data_offer_accept(mWaylandDataOffer, aTime,
+ gdk_atom_name(mTargetMIMETypes[0]));
+ }
+}
+
+void
+WaylandDataOffer::SetSelectedDragAction(uint32_t aWaylandAction)
+{
+ mSelectedDragAction = aWaylandAction;
+}
+
+GdkDragAction
+WaylandDataOffer::GetSelectedDragAction()
+{
+ return wl_to_gdk_actions(mSelectedDragAction);
+}
+
+void
+WaylandDataOffer::SetAvailableDragActions(uint32_t aWaylandActions)
+{
+ mAvailableDragAction = aWaylandActions;
+}
+
+GdkDragAction
+WaylandDataOffer::GetAvailableDragActions()
+{
+ return wl_to_gdk_actions(mAvailableDragAction);
+}
+
static void
data_offer_offer (void *data,
struct wl_data_offer *wl_data_offer,
@@ -164,25 +258,39 @@ data_offer_offer (void *
offer->AddMIMEType(type);
}
+/* Advertise all available drag and drop actions from source.
+ * We don't use that but follow gdk_wayland_drag_context_commit_status()
+ * from gdkdnd-wayland.c here.
+ */
static void
data_offer_source_actions(void *data,
struct wl_data_offer *wl_data_offer,
uint32_t source_actions)
{
+ auto *offer = static_cast<WaylandDataOffer*>(data);
+ offer->SetAvailableDragActions(source_actions);
}
+/* Advertise recently selected drag and drop action by compositor, based
+ * on source actions and user choice (key modifiers, etc.).
+ */
static void
data_offer_action(void *data,
struct wl_data_offer *wl_data_offer,
uint32_t dnd_action)
{
+ auto *offer = static_cast<WaylandDataOffer*>(data);
+ offer->SetSelectedDragAction(dnd_action);
}
/* wl_data_offer callback description:
*
* data_offer_offer - Is called for each MIME type available at wl_data_offer.
- * data_offer_source_actions - Exposes all available D&D actions.
- * data_offer_action - Expose one actually selected D&D action.
+ * data_offer_source_actions - This event indicates the actions offered by
+ * the data source.
+ * data_offer_action - This event indicates the action selected by
+ * the compositor after matching the source/destination
+ * side actions.
*/
static const struct wl_data_offer_listener data_offer_listener = {
data_offer_offer,
@@ -246,12 +354,94 @@ PrimaryDataOffer::~PrimaryDataOffer(void
}
}
+NS_IMPL_ISUPPORTS(nsWaylandDragContext, nsISupports);
+
+nsWaylandDragContext::nsWaylandDragContext(WaylandDataOffer* aDataOffer,
+ wl_display *aDisplay)
+ : mDataOffer(aDataOffer)
+ , mDisplay(aDisplay)
+ , mTime(0)
+ , mGtkWidget(nullptr)
+ , mX(0)
+ , mY(0)
+{
+}
+
+void
+nsWaylandDragContext::DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime,
+ nscoord aX, nscoord aY)
+{
+ mTime = aTime;
+ mGtkWidget = aGtkWidget;
+ mX = aX;
+ mY = aY;
+}
+
void
-nsRetrievalContextWayland::RegisterDataOffer(wl_data_offer *aWaylandDataOffer)
+nsWaylandDragContext::DropMotion(uint32_t aTime, nscoord aX, nscoord aY)
+{
+ mTime = aTime;
+ mX = aX;
+ mY = aY;
+}
+
+void
+nsWaylandDragContext::GetLastDropInfo(uint32_t *aTime, nscoord *aX, nscoord *aY)
+{
+ *aTime = mTime;
+ *aX = mX;
+ *aY = mY;
+}
+
+void
+nsWaylandDragContext::SetDragStatus(GdkDragAction aAction)
+{
+ mDataOffer->SetDragStatus(aAction, mTime);
+}
+
+GdkDragAction
+nsWaylandDragContext::GetSelectedDragAction()
+{
+ GdkDragAction gdkAction = mDataOffer->GetSelectedDragAction();
+
+ // We emulate gdk_drag_context_get_actions() here.
+ if (!gdkAction) {
+ gdkAction = mDataOffer->GetAvailableDragActions();
+ }
+
+ return gdkAction;
+}
+
+GList*
+nsWaylandDragContext::GetTargets()
+{
+ int targetNums;
+ GdkAtom *atoms = mDataOffer->GetTargets(&targetNums);
+
+ GList* targetList = nullptr;
+ for (int i = 0; i < targetNums; i++) {
+ targetList = g_list_append(targetList, GDK_ATOM_TO_POINTER(atoms[i]));
+ }
+
+ return targetList;
+}
+
+char*
+nsWaylandDragContext::GetData(const char* aMimeType, uint32_t* aContentLength)
+{
+ mDataOffer->DragOfferAccept(aMimeType, mTime);
+ return mDataOffer->GetData(mDisplay, aMimeType, aContentLength);
+}
+
+void
+nsRetrievalContextWayland::RegisterNewDataOffer(wl_data_offer *aWaylandDataOffer)
{
DataOffer* dataOffer =
static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
aWaylandDataOffer));
+ MOZ_ASSERT(dataOffer == nullptr,
+ "Registered WaylandDataOffer already exists. Wayland protocol error?");
+
if (!dataOffer) {
dataOffer = new WaylandDataOffer(aWaylandDataOffer);
g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer);
@@ -259,12 +449,15 @@ nsRetrievalContextWayland::RegisterDataO
}
void
-nsRetrievalContextWayland::RegisterDataOffer(
+nsRetrievalContextWayland::RegisterNewDataOffer(
gtk_primary_selection_offer *aPrimaryDataOffer)
{
DataOffer* dataOffer =
static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
aPrimaryDataOffer));
+ MOZ_ASSERT(dataOffer == nullptr,
+ "Registered PrimaryDataOffer already exists. Wayland protocol error?");
+
if (!dataOffer) {
dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
@@ -274,6 +467,9 @@ nsRetrievalContextWayland::RegisterDataO
void
nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer)
{
+ // Delete existing clipboard data offer
+ mClipboardOffer = nullptr;
+
DataOffer* dataOffer =
static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
aWaylandDataOffer));
@@ -288,10 +484,12 @@ void
nsRetrievalContextWayland::SetPrimaryDataOffer(
gtk_primary_selection_offer *aPrimaryDataOffer)
{
- if (aPrimaryDataOffer == nullptr) {
- // Release any primary offer we have.
- mPrimaryOffer = nullptr;
- } else {
+ // Release any primary offer we have.
+ mPrimaryOffer = nullptr;
+
+ // aPrimaryDataOffer can be null which means we lost
+ // the mouse selection.
+ if (aPrimaryDataOffer) {
DataOffer* dataOffer =
static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
aPrimaryDataOffer));
@@ -304,12 +502,31 @@ nsRetrievalContextWayland::SetPrimaryDat
}
void
-nsRetrievalContextWayland::ClearDataOffers(void)
+nsRetrievalContextWayland::AddDragAndDropDataOffer(wl_data_offer *aDropDataOffer)
{
- if (mClipboardOffer)
- mClipboardOffer = nullptr;
- if (mPrimaryOffer)
- mPrimaryOffer = nullptr;
+ // Remove any existing D&D contexts.
+ mDragContext = nullptr;
+
+ WaylandDataOffer* dataOffer =
+ static_cast<WaylandDataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aDropDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing drag and drop data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aDropDataOffer);
+ mDragContext = new nsWaylandDragContext(dataOffer, mDisplay);
+ }
+}
+
+nsWaylandDragContext*
+nsRetrievalContextWayland::GetDragContext(void)
+{
+ return mDragContext;
+}
+
+void
+nsRetrievalContextWayland::ClearDragAndDropDataOffer(void)
+{
+ mDragContext = nullptr;
}
// We have a new fresh data content.
@@ -321,7 +538,7 @@ data_device_data_offer (void
{
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland*>(data);
- context->RegisterDataOffer(offer);
+ context->RegisterNewDataOffer(offer);
}
// The new fresh data content is clipboard.
@@ -341,31 +558,78 @@ data_device_enter (void
struct wl_data_device *data_device,
uint32_t time,
struct wl_surface *surface,
- int32_t x,
- int32_t y,
+ int32_t x_fixed,
+ int32_t y_fixed,
struct wl_data_offer *offer)
{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+ context->AddDragAndDropDataOffer(offer);
+
+ nsWaylandDragContext* dragContext = context->GetDragContext();
+
+ GtkWidget* gtkWidget = get_gtk_widget_for_wl_surface(surface);
+ if (!gtkWidget) {
+ NS_WARNING("DragAndDrop: Unable to get GtkWidget for wl_surface!");
+ return;
+ }
+
+ LOGDRAG(("nsWindow data_device_enter for GtkWidget %p\n",
+ (void*)gtkWidget));
+
+ dragContext->DropDataEnter(gtkWidget, time,
+ wl_fixed_to_int(x_fixed),
+ wl_fixed_to_int(y_fixed));
}
static void
data_device_leave (void *data,
struct wl_data_device *data_device)
{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+
+ nsWaylandDragContext* dropContext = context->GetDragContext();
+ WindowDragLeaveHandler(dropContext->GetWidget());
+
+ context->ClearDragAndDropDataOffer();
}
static void
data_device_motion (void *data,
struct wl_data_device *data_device,
uint32_t time,
- int32_t x,
- int32_t y)
+ int32_t x_fixed,
+ int32_t y_fixed)
{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+
+ nsWaylandDragContext* dropContext = context->GetDragContext();
+
+ nscoord x = wl_fixed_to_int(x_fixed);
+ nscoord y = wl_fixed_to_int(y_fixed);
+ dropContext->DropMotion(time, x, y);
+
+ WindowDragMotionHandler(dropContext->GetWidget(), nullptr,
+ dropContext, x, y, time);
}
static void
data_device_drop (void *data,
struct wl_data_device *data_device)
{
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+
+ nsWaylandDragContext* dropContext = context->GetDragContext();
+
+ uint32_t time;
+ nscoord x, y;
+ dropContext->GetLastDropInfo(&time, &x, &y);
+
+ WindowDragDropHandler(dropContext->GetWidget(), nullptr, dropContext,
+ x, y, time);
}
/* wl_data_device callback description:
@@ -405,7 +669,7 @@ primary_selection_data_offer (void
// create and add listener
nsRetrievalContextWayland *context =
static_cast<nsRetrievalContextWayland*>(data);
- context->RegisterDataOffer(gtk_primary_offer);
+ context->RegisterNewDataOffer(gtk_primary_offer);
}
static void
@@ -418,6 +682,18 @@ primary_selection_selection (void
context->SetPrimaryDataOffer(gtk_primary_offer);
}
+/* gtk_primary_selection_device callback description:
+ *
+ * primary_selection_data_offer - It's called when there's a new
+ * gtk_primary_selection_offer available.
+ * We need to attach gtk_primary_selection_offer_listener
+ * to it to get available MIME types.
+ *
+ * primary_selection_selection - It's called when the new gtk_primary_selection_offer
+ * is a primary selection content. It can be also called with
+ * gtk_primary_selection_offer = null which means there's no
+ * primary selection.
+ */
static const struct
gtk_primary_selection_device_listener primary_selection_device_listener = {
primary_selection_data_offer,
@@ -430,81 +706,6 @@ nsRetrievalContextWayland::HasSelectionS
return mPrimarySelectionDataDeviceManager != nullptr;
}
-static void
-keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
- uint32_t format, int fd, uint32_t size)
-{
-}
-
-static void
-keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface,
- struct wl_array *keys)
-{
-}
-
-static void
-keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface)
-{
- // We lost focus so our clipboard data are outdated
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland*>(data);
-
- context->ClearDataOffers();
-}
-
-static void
-keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t time, uint32_t key,
- uint32_t state)
-{
-}
-
-static void
-keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched, uint32_t mods_locked,
- uint32_t group)
-{
-}
-
-static const struct wl_keyboard_listener keyboard_listener = {
- keyboard_handle_keymap,
- keyboard_handle_enter,
- keyboard_handle_leave,
- keyboard_handle_key,
- keyboard_handle_modifiers,
-};
-
-void
-nsRetrievalContextWayland::ConfigureKeyboard(wl_seat_capability caps)
-{
- // ConfigureKeyboard() is called when wl_seat configuration is changed.
- // We look for the keyboard only, get it when is't available and release it
- // when it's lost (we don't have focus for instance).
- if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
- mKeyboard = wl_seat_get_keyboard(mSeat);
- wl_keyboard_add_listener(mKeyboard, &keyboard_listener, this);
- } else if (mKeyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
- wl_keyboard_destroy(mKeyboard);
- mKeyboard = nullptr;
- }
-}
-
-static void
-seat_handle_capabilities(void *data, struct wl_seat *seat,
- unsigned int caps)
-{
- nsRetrievalContextWayland *context =
- static_cast<nsRetrievalContextWayland*>(data);
- context->ConfigureKeyboard((wl_seat_capability)caps);
-}
-
-static const struct wl_seat_listener seat_listener = {
- seat_handle_capabilities,
-};
-
void
nsRetrievalContextWayland::InitDataDeviceManager(wl_registry *registry,
uint32_t id,
@@ -530,7 +731,6 @@ nsRetrievalContextWayland::InitSeat(wl_r
void *data)
{
mSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
- wl_seat_add_listener(mSeat, &seat_listener, data);
}
static void
@@ -573,6 +773,7 @@ nsRetrievalContextWayland::nsRetrievalCo
, mActiveOffers(g_hash_table_new(NULL, NULL))
, mClipboardOffer(nullptr)
, mPrimaryOffer(nullptr)
+ , mDragContext(nullptr)
, mClipboardRequestNumber(0)
, mClipboardData(nullptr)
, mClipboardDataLength(0)
@@ -616,8 +817,21 @@ nsRetrievalContextWayland::nsRetrievalCo
mInitialized = true;
}
+static gboolean
+offer_hash_remove(gpointer wl_offer, gpointer aDataOffer, gpointer user_data)
+{
+#ifdef DEBUG
+ nsPrintfCString msg("nsRetrievalContextWayland(): leaked nsDataOffer %p\n",
+ aDataOffer);
+ NS_WARNING(msg.get());
+#endif
+ delete static_cast<DataOffer*>(aDataOffer);
+ return true;
+}
+
nsRetrievalContextWayland::~nsRetrievalContextWayland(void)
{
+ g_hash_table_foreach_remove(mActiveOffers, offer_hash_remove, nullptr);
g_hash_table_destroy(mActiveOffers);
}
@@ -667,12 +881,14 @@ nsRetrievalContextWayland::TransferFastT
int aClipboardRequestNumber, GtkSelectionData *aSelectionData)
{
if (mClipboardRequestNumber == aClipboardRequestNumber) {
- mClipboardDataLength = gtk_selection_data_get_length(aSelectionData);
- if (mClipboardDataLength > 0) {
+ int dataLength = gtk_selection_data_get_length(aSelectionData);
+ if (dataLength > 0) {
+ mClipboardDataLength = dataLength;
mClipboardData = reinterpret_cast<char*>(
- g_malloc(sizeof(char)*mClipboardDataLength));
+ g_malloc(sizeof(char)*(mClipboardDataLength+1)));
memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData),
sizeof(char)*mClipboardDataLength);
+ mClipboardData[mClipboardDataLength] = '\0';
}
} else {
NS_WARNING("Received obsoleted clipboard data!");
@@ -727,7 +943,7 @@ nsRetrievalContextWayland::GetClipboardT
if (!dataOffer)
return nullptr;
- for (unsigned int i = 0; i < sizeof(sTextMimeTypes); i++) {
+ for (unsigned int i = 0; i < TEXT_MIME_TYPES_NUM; i++) {
if (dataOffer->HasTarget(sTextMimeTypes[i])) {
uint32_t unused;
return GetClipboardData(sTextMimeTypes[i], aWhichClipboard,
diff -up thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h
--- thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h.wayland 2018-10-30 12:45:34.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/nsClipboardWayland.h 2018-11-20 12:04:43.735787356 +0100
@@ -32,6 +32,7 @@ public:
private:
virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0;
+protected:
nsTArray<GdkAtom> mTargetMIMETypes;
};
@@ -40,25 +41,66 @@ class WaylandDataOffer : public DataOffe
public:
WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
-private:
+ void DragOfferAccept(const char* aMimeType, uint32_t aTime);
+ void SetDragStatus(GdkDragAction aAction, uint32_t aTime);
+
+ GdkDragAction GetSelectedDragAction();
+ void SetSelectedDragAction(uint32_t aWaylandAction);
+
+ void SetAvailableDragActions(uint32_t aWaylandActions);
+ GdkDragAction GetAvailableDragActions();
+
virtual ~WaylandDataOffer();
+private:
bool RequestDataTransfer(const char* aMimeType, int fd) override;
wl_data_offer* mWaylandDataOffer;
+ uint32_t mSelectedDragAction;
+ uint32_t mAvailableDragAction;
};
class PrimaryDataOffer : public DataOffer
{
public:
PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
+ void SetAvailableDragActions(uint32_t aWaylandActions) {};
-private:
virtual ~PrimaryDataOffer();
+private:
bool RequestDataTransfer(const char* aMimeType, int fd) override;
gtk_primary_selection_offer* mPrimaryDataOffer;
};
+class nsWaylandDragContext : public nsISupports
+{
+ NS_DECL_ISUPPORTS
+
+public:
+ nsWaylandDragContext(WaylandDataOffer* aWaylandDataOffer,
+ wl_display *aDisplay);
+
+ void DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime,
+ nscoord aX, nscoord aY);
+ void DropMotion(uint32_t aTime, nscoord aX, nscoord aY);
+ void GetLastDropInfo(uint32_t *aTime, nscoord *aX, nscoord *aY);
+
+ void SetDragStatus(GdkDragAction action);
+ GdkDragAction GetSelectedDragAction();
+
+ GtkWidget* GetWidget() { return mGtkWidget; }
+ GList* GetTargets();
+ char* GetData(const char* aMimeType, uint32_t* aContentLength);
+private:
+ virtual ~nsWaylandDragContext() {};
+
+ nsAutoPtr<WaylandDataOffer> mDataOffer;
+ wl_display* mDisplay;
+ uint32_t mTime;
+ GtkWidget* mGtkWidget;
+ nscoord mX, mY;
+};
+
class nsRetrievalContextWayland : public nsRetrievalContext
{
public:
@@ -74,15 +116,16 @@ public:
int* aTargetNum) override;
virtual bool HasSelectionSupport(void) override;
- void RegisterDataOffer(wl_data_offer *aWaylandDataOffer);
- void RegisterDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer);
+ void RegisterNewDataOffer(wl_data_offer *aWaylandDataOffer);
+ void RegisterNewDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer);
void SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer);
void SetPrimaryDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer);
+ void AddDragAndDropDataOffer(wl_data_offer *aWaylandDataOffer);
+ nsWaylandDragContext* GetDragContext();
- void ClearDataOffers();
+ void ClearDragAndDropDataOffer();
- void ConfigureKeyboard(wl_seat_capability caps);
void TransferFastTrackClipboard(int aClipboardRequestNumber,
GtkSelectionData *aSelectionData);
@@ -103,6 +146,7 @@ private:
GHashTable* mActiveOffers;
nsAutoPtr<DataOffer> mClipboardOffer;
nsAutoPtr<DataOffer> mPrimaryOffer;
+ RefPtr<nsWaylandDragContext> mDragContext;
int mClipboardRequestNumber;
char* mClipboardData;
diff -up thunderbird-60.3.0/widget/gtk/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 <sys/types.h>
#include <sys/stat.h>
+// 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<nsIGIOService> 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<nsIObserverService> 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<nsIDocument> doc = do_QueryInterface(aDocument);
- if (!doc)
+ if (!aDocument)
return nullptr;
- nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
+ nsCOMPtr<nsIPresShell> 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<CSSIntRegion>& 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<SourceSurface> 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 <gtk/gtk.h>
class nsWindow;
+class nsWaylandDragContext;
namespace mozilla {
namespace gfx {
@@ -60,13 +61,12 @@ public:
// nsBaseDragService
virtual nsresult InvokeDragSessionImpl(nsIArray* anArrayTransferables,
- nsIScriptableRegion* aRegion,
+ const mozilla::Maybe<mozilla::CSSIntRegion>& 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<nsWindow> mPendingWindow;
mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
nsCountedRef<GdkDragContext> mPendingDragContext;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mPendingWaylandDragContext;
+#endif
guint mPendingTime;
// mTargetWindow and mTargetWindowPoint record the position of the last
@@ -169,9 +174,15 @@ private:
// motion or drop events. mTime records the corresponding timestamp.
nsCountedRef<GtkWidget> mTargetWidget;
nsCountedRef<GdkDragContext> mTargetDragContext;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mTargetWaylandDragContext;
+#endif
// mTargetDragContextForRemote is set while waiting for a reply from
// a child process.
nsCountedRef<GdkDragContext> mTargetDragContextForRemote;
+#ifdef MOZ_WAYLAND
+ RefPtr<nsWaylandDragContext> mTargetWaylandDragContextForRemote;
+#endif
guint mTargetTime;
// is it OK to drop on us?
@@ -197,8 +208,6 @@ private:
// our source data items
nsCOMPtr<nsIArray> mSourceDataItems;
- nsCOMPtr<nsIScriptableRegion> 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 <sys/mman.h>
+#endif
+
namespace mozilla {
namespace widget {
@@ -195,7 +199,11 @@ KeymapWrapper::Init()
memset(mModifierMasks, 0, sizeof(mModifierMasks));
if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
- InitBySystemSettings();
+ InitBySystemSettingsX11();
+#ifdef MOZ_WAYLAND
+ else
+ InitBySystemSettingsWayland();
+#endif
gdk_window_add_filter(nullptr, FilterEvents, this);
@@ -275,10 +283,10 @@ KeymapWrapper::InitXKBExtension()
}
void
-KeymapWrapper::InitBySystemSettings()
+KeymapWrapper::InitBySystemSettingsX11()
{
MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
- ("%p InitBySystemSettings, mGdkKeymap=%p",
+ ("%p InitBySystemSettingsX11, mGdkKeymap=%p",
this, mGdkKeymap));
Display* display =
@@ -439,6 +447,208 @@ KeymapWrapper::InitBySystemSettings()
XFree(xkeymap);
}
+#ifdef MOZ_WAYLAND
+void
+KeymapWrapper::SetModifierMask(xkb_keymap *aKeymap, ModifierIndex aModifierIndex,
+ const char* aModifierName)
+{
+ static auto sXkbKeymapModGetIndex =
+ (xkb_mod_index_t (*)(struct xkb_keymap *, const char *))
+ dlsym(RTLD_DEFAULT, "xkb_keymap_mod_get_index");
+
+ xkb_mod_index_t index = sXkbKeymapModGetIndex(aKeymap, aModifierName);
+ if (index != XKB_MOD_INVALID) {
+ mModifierMasks[aModifierIndex] = (1 << index);
+ }
+}
+
+void
+KeymapWrapper::SetModifierMasks(xkb_keymap *aKeymap)
+{
+ KeymapWrapper* keymapWrapper = GetInstance();
+
+ // This mapping is derived from get_xkb_modifiers() at gdkkeys-wayland.c
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_NUM_LOCK, XKB_MOD_NAME_NUM);
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_ALT, XKB_MOD_NAME_ALT);
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_META, "Meta");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_SUPER, "Super");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_HYPER, "Hyper");
+
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_SCROLL_LOCK, "ScrollLock");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL3, "Level3");
+ keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL5, "Level5");
+
+ MOZ_LOG(gKeymapWrapperLog, LogLevel::Info,
+ ("%p KeymapWrapper::SetModifierMasks, CapsLock=0x%X, NumLock=0x%X, "
+ "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, "
+ "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X",
+ keymapWrapper,
+ keymapWrapper->GetModifierMask(CAPS_LOCK),
+ keymapWrapper->GetModifierMask(NUM_LOCK),
+ keymapWrapper->GetModifierMask(SCROLL_LOCK),
+ keymapWrapper->GetModifierMask(LEVEL3),
+ keymapWrapper->GetModifierMask(LEVEL5),
+ keymapWrapper->GetModifierMask(SHIFT),
+ keymapWrapper->GetModifierMask(CTRL),
+ keymapWrapper->GetModifierMask(ALT),
+ keymapWrapper->GetModifierMask(META),
+ keymapWrapper->GetModifierMask(SUPER),
+ keymapWrapper->GetModifierMask(HYPER)));
+}
+
+/* This keymap routine is derived from weston-2.0.0/clients/simple-im.c
+*/
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ return;
+ }
+
+ char *mapString = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (mapString == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+
+ static auto sXkbContextNew =
+ (struct xkb_context *(*)(enum xkb_context_flags))
+ dlsym(RTLD_DEFAULT, "xkb_context_new");
+ static auto sXkbKeymapNewFromString =
+ (struct xkb_keymap *(*)(struct xkb_context *, const char *,
+ enum xkb_keymap_format, enum xkb_keymap_compile_flags))
+ dlsym(RTLD_DEFAULT, "xkb_keymap_new_from_string");
+
+ struct xkb_context *xkb_context = sXkbContextNew(XKB_CONTEXT_NO_FLAGS);
+ struct xkb_keymap *keymap =
+ sXkbKeymapNewFromString(xkb_context, mapString,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ munmap(mapString, size);
+ close(fd);
+
+ if (!keymap) {
+ NS_WARNING("keyboard_handle_keymap(): Failed to compile keymap!\n");
+ return;
+ }
+
+ KeymapWrapper::SetModifierMasks(keymap);
+
+ static auto sXkbKeymapUnRef =
+ (void(*)(struct xkb_keymap *))
+ dlsym(RTLD_DEFAULT, "xkb_keymap_unref");
+ sXkbKeymapUnRef(keymap);
+
+ static auto sXkbContextUnref =
+ (void(*)(struct xkb_context *))
+ dlsym(RTLD_DEFAULT, "xkb_context_unref");
+ sXkbContextUnref(xkb_context);
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface,
+ struct wl_array *keys)
+{
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state)
+{
+}
+
+static void
+keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat,
+ unsigned int caps)
+{
+ static wl_keyboard *keyboard = nullptr;
+
+ if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
+ keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr);
+ } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+ wl_keyboard_destroy(keyboard);
+ keyboard = nullptr;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void
+gdk_registry_handle_global(void *data,
+ struct wl_registry *registry,
+ uint32_t id,
+ const char *interface,
+ uint32_t version)
+{
+ if (strcmp(interface, "wl_seat") == 0) {
+ wl_seat *seat =
+ (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1);
+ wl_seat_add_listener(seat, &seat_listener, data);
+ }
+}
+
+static void
+gdk_registry_handle_global_remove(void *data,
+ struct wl_registry *registry,
+ uint32_t id)
+{
+}
+
+static const struct wl_registry_listener keyboard_registry_listener = {
+ gdk_registry_handle_global,
+ gdk_registry_handle_global_remove
+};
+
+void
+KeymapWrapper::InitBySystemSettingsWayland()
+{
+ // Available as of GTK 3.8+
+ static auto sGdkWaylandDisplayGetWlDisplay =
+ (wl_display *(*)(GdkDisplay *))
+ dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+
+ wl_display *display =
+ sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default());
+ wl_registry_add_listener(wl_display_get_registry(display),
+ &keyboard_registry_listener, this);
+
+ // Call wl_display_roundtrip() twice to make sure all
+ // callbacks are processed.
+ wl_display_roundtrip(display);
+ wl_display_roundtrip(display);
+}
+#endif
+
KeymapWrapper::~KeymapWrapper()
{
gdk_window_remove_filter(nullptr, FilterEvents, this);
@@ -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 <gdk/gdk.h>
#include <X11/XKBlib.h>
+#ifdef MOZ_WAYLAND
+#include <gdk/gdkwayland.h>
+#include <xkbcommon/xkbcommon.h>
+#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 <fontconfig/fontconfig.h>
#include "gfxPlatformGtk.h"
+#include "mozilla/FontPropertyTypes.h"
#include "ScreenHelperGTK.h"
#include "gtkdrawing.h"
@@ -31,7 +32,9 @@
#include <cairo-gobject.h>
#include "WidgetStyleCache.h"
#include "prenv.h"
+#include "nsCSSColorUtils.h"
+using namespace mozilla;
using mozilla::LookAndFeel;
#define GDK_COLOR_TO_NS_RGB(c) \
@@ -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<size_t>(aWidgetType);
+ MOZ_ASSERT(type < static_cast<size_t>(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<size_t>(aWidgetType);
+ MOZ_ASSERT(type < static_cast<size_t>(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<uint16_t>(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<size_t>(aWidgetType) < static_cast<size_t>(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<size_t>(aWidgetType) < static_cast<size_t>(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<uint8_t>(aWidgetType) -
+ static_cast<uint8_t>(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<int>(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 <gtk/gtk.h>
#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<size_t>(mozilla::StyleAppearance::Count) + 7) / 8];
+ uint8_t mSafeWidgetStates[static_cast<size_t>(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 <gdk/gdkx.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gio/gunixfdlist.h>
+
+// for dlsym
+#include <dlfcn.h>
+#include "MainThreadUtils.h"
using namespace mozilla;
using namespace mozilla::widget;
@@ -387,7 +400,7 @@ nsPrintDialogWidgetGTK::ExportHeaderFoot
nsresult
nsPrintDialogWidgetGTK::ImportSettings(nsIPrintSettings *aNSSettings)
{
- NS_PRECONDITION(aNSSettings, "aSettings must not be null");
+ MOZ_ASSERT(aNSSettings, "aSettings must not be null");
NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
nsCOMPtr<nsPrintSettingsGTK> aNSSettingsGTK(do_QueryInterface(aNSSettings));
@@ -416,7 +429,7 @@ nsPrintDialogWidgetGTK::ImportSettings(n
nsresult
nsPrintDialogWidgetGTK::ExportSettings(nsIPrintSettings *aNSSettings)
{
- NS_PRECONDITION(aNSSettings, "aSettings must not be null");
+ MOZ_ASSERT(aNSSettings, "aSettings must not be null");
NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE);
GtkPrintSettings* settings = gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
@@ -513,13 +526,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<WaylandWindowHandleExportedData*>(user_data);
+ char *handle_str;
+
+ handle_str = g_strdup_printf ("wayland:%s", wayland_handle_str);
+ data->callback (data->window, handle_str, data->user_data);
+ g_free (handle_str);
+}
+#endif
+
+// Get window handle for the portal, taken from gtk/gtkwindow.c
+// (currently not exported)
+static gboolean
+window_export_handle(GtkWindow *window,
+ GtkWindowHandleExported callback,
+ gpointer user_data)
+{
+ if (GDK_IS_X11_DISPLAY(gtk_widget_get_display(GTK_WIDGET(window))))
+ {
+ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+ char *handle_str;
+ guint32 xid = (guint32) gdk_x11_window_get_xid(gdk_window);
+
+ handle_str = g_strdup_printf("x11:%x", xid);
+ callback(window, handle_str, user_data);
+ g_free(handle_str);
+ return true;
+ }
+#ifdef MOZ_WAYLAND
+ else
+ {
+ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
+ WaylandWindowHandleExportedData *data;
+
+ data = g_new0(WaylandWindowHandleExportedData, 1);
+ data->window = window;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ static auto s_gdk_wayland_window_export_handle =
+ reinterpret_cast<gboolean (*)(GdkWindow*, GdkWaylandWindowExported,
+ gpointer, GDestroyNotify)>
+ (dlsym(RTLD_DEFAULT, "gdk_wayland_window_export_handle"));
+ if (!s_gdk_wayland_window_export_handle ||
+ !s_gdk_wayland_window_export_handle(gdk_window,
+ wayland_window_handle_exported,
+ data, g_free)) {
+ g_free (data);
+ return false;
+ } else {
+ return true;
+ }
+ }
+#endif
+
+ g_warning("Couldn't export handle, unsupported windowing system");
+
+ return false;
+}
+/**
+ * Communication class with the GTK print portal handler
+ *
+ * To print document from flatpak we need to use print portal because
+ * printers are not directly accessible in the sandboxed environment.
+ *
+ * At first we request portal to show the print dialog to let user choose
+ * printer settings. We use DBUS interface for that (PreparePrint method).
+ *
+ * Next we force application to print to temporary file and after the writing
+ * to the file is finished we pass its file descriptor to the portal.
+ * Portal will pass duplicate of the file descriptor to the printer which
+ * user selected before (by DBUS Print method).
+ *
+ * Since DBUS communication is done async while nsPrintDialogServiceGTK::Show
+ * is expecting sync execution, we need to create a new GMainLoop during the
+ * print portal dialog is running. The loop is stopped after the dialog
+ * is closed.
+ */
+class nsFlatpakPrintPortal: public nsIObserver
+{
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ public:
+ explicit nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings);
+ nsresult PreparePrintRequest(GtkWindow* aWindow);
+ static void OnWindowExportHandleDone(GtkWindow *aWindow,
+ const char* aWindowHandleStr,
+ gpointer aUserData);
+ void PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr);
+ static void OnPreparePrintResponse(GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer data);
+ GtkPrintOperationResult GetResult();
+ private:
+ virtual ~nsFlatpakPrintPortal();
+ void FinishPrintDialog(GVariant* parameters);
+ nsCOMPtr<nsPrintSettingsGTK> mPrintAndPageSettings;
+ GDBusProxy* mProxy;
+ guint32 mToken;
+ GMainLoop* mLoop;
+ GtkPrintOperationResult mResult;
+ guint mResponseSignalId;
+ GtkWindow* mParentWindow;
+};
+
+NS_IMPL_ISUPPORTS(nsFlatpakPrintPortal, nsIObserver)
+
+nsFlatpakPrintPortal::nsFlatpakPrintPortal(nsPrintSettingsGTK* aPrintSettings):
+ mPrintAndPageSettings(aPrintSettings),
+ mProxy(nullptr),
+ mLoop(nullptr),
+ mResponseSignalId(0),
+ mParentWindow(nullptr)
+{
+}
+
+/**
+ * Creates GDBusProxy, query for window handle and create a new GMainLoop.
+ *
+ * The GMainLoop is to be run from GetResult() and be quitted during
+ * FinishPrintDialog.
+ *
+ * @param aWindow toplevel application window which is used as parent of print
+ * dialog
+ */
+nsresult
+nsFlatpakPrintPortal::PreparePrintRequest(GtkWindow* aWindow)
+{
+ MOZ_ASSERT(aWindow, "aWindow must not be null");
+ MOZ_ASSERT(mPrintAndPageSettings, "mPrintAndPageSettings must not be null");
+
+ GError* error = nullptr;
+ mProxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ nullptr,
+ "org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.Print",
+ nullptr,
+ &error);
+ if (mProxy == nullptr) {
+ NS_WARNING(nsPrintfCString("Unable to create dbus proxy: %s", error->message).get());
+ g_error_free(error);
+ return NS_ERROR_FAILURE;
+ }
+
+ // The window handler is returned async, we will continue by PreparePrint method
+ // when it is returned.
+ if (!window_export_handle(aWindow,
+ &nsFlatpakPrintPortal::OnWindowExportHandleDone, this)) {
+ NS_WARNING("Unable to get window handle for creating modal print dialog.");
+ return NS_ERROR_FAILURE;
+ }
+
+ mLoop = g_main_loop_new (NULL, FALSE);
+ return NS_OK;
+}
+
+void
+nsFlatpakPrintPortal::OnWindowExportHandleDone(GtkWindow* aWindow,
+ const char* aWindowHandleStr,
+ gpointer aUserData)
+{
+ nsFlatpakPrintPortal* printPortal = static_cast<nsFlatpakPrintPortal*>(aUserData);
+ printPortal->PreparePrint(aWindow, aWindowHandleStr);
+}
+
+/**
+ * Ask print portal to show the print dialog.
+ *
+ * Print and page settings and window handle are passed to the portal to prefill
+ * last used settings.
+ */
+void
+nsFlatpakPrintPortal::PreparePrint(GtkWindow* aWindow, const char* aWindowHandleStr)
+{
+ GtkPrintSettings* gtkSettings = mPrintAndPageSettings->GetGtkPrintSettings();
+ GtkPageSetup* pageSetup = mPrintAndPageSettings->GetGtkPageSetup();
+
+ // We need to remember GtkWindow to unexport window handle after it is
+ // no longer needed by the portal dialog (apply only on non-X11 sessions).
+ if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
+ mParentWindow = aWindow;
+ }
+
+ GVariantBuilder opt_builder;
+ g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT);
+ char* token = g_strdup_printf("mozilla%d", g_random_int_range (0, G_MAXINT));
+ g_variant_builder_add(&opt_builder, "{sv}", "handle_token",
+ g_variant_new_string(token));
+ g_free(token);
+ GVariant* options = g_variant_builder_end(&opt_builder);
+ static auto s_gtk_print_settings_to_gvariant =
+ reinterpret_cast<GVariant* (*)(GtkPrintSettings*)>
+ (dlsym(RTLD_DEFAULT, "gtk_print_settings_to_gvariant"));
+ static auto s_gtk_page_setup_to_gvariant =
+ reinterpret_cast<GVariant* (*)(GtkPageSetup *)>
+ (dlsym(RTLD_DEFAULT, "gtk_page_setup_to_gvariant"));
+ if (!s_gtk_print_settings_to_gvariant || !s_gtk_page_setup_to_gvariant) {
+ mResult = GTK_PRINT_OPERATION_RESULT_ERROR;
+ FinishPrintDialog(nullptr);
+ return;
+ }
+
+ // Get translated window title
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+ nsCOMPtr<nsIStringBundle> printBundle;
+ bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties",
+ getter_AddRefs(printBundle));
+ nsAutoString intlPrintTitle;
+ printBundle->GetStringFromName("printTitleGTK", intlPrintTitle);
+
+ GError* error = nullptr;
+ GVariant *ret = g_dbus_proxy_call_sync(mProxy,
+ "PreparePrint",
+ g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
+ aWindowHandleStr,
+ NS_ConvertUTF16toUTF8(intlPrintTitle).get(), // Title of the window
+ s_gtk_print_settings_to_gvariant(gtkSettings),
+ s_gtk_page_setup_to_gvariant(pageSetup),
+ options),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ nullptr,
+ &error);
+ if (ret == nullptr) {
+ NS_WARNING(nsPrintfCString("Unable to call dbus proxy: %s", error->message).get());
+ g_error_free (error);
+ mResult = GTK_PRINT_OPERATION_RESULT_ERROR;
+ FinishPrintDialog(nullptr);
+ return;
+ }
+
+ const char* handle = nullptr;
+ g_variant_get (ret, "(&o)", &handle);
+ if (strcmp (aWindowHandleStr, handle) != 0)
+ {
+ aWindowHandleStr = g_strdup (handle);
+ if (mResponseSignalId) {
+ g_dbus_connection_signal_unsubscribe(
+ g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId);
+ }
+ }
+ mResponseSignalId =
+ g_dbus_connection_signal_subscribe(
+ g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)),
+ "org.freedesktop.portal.Desktop",
+ "org.freedesktop.portal.Request",
+ "Response",
+ aWindowHandleStr,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+ &nsFlatpakPrintPortal::OnPreparePrintResponse,
+ this, NULL);
+
+}
+
+void
+nsFlatpakPrintPortal::OnPreparePrintResponse(GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer data)
+{
+ nsFlatpakPrintPortal* printPortal = static_cast<nsFlatpakPrintPortal*>(data);
+ printPortal->FinishPrintDialog(parameters);
+}
+
+/**
+ * When the dialog is accepted, read print and page settings and token.
+ *
+ * Token is later used for printing portal as print operation identifier.
+ * Print and page settings are modified in-place and stored to
+ * mPrintAndPageSettings.
+ */
+void
+nsFlatpakPrintPortal::FinishPrintDialog(GVariant* parameters)
+{
+ // This ends GetResult() method
+ if (mLoop) {
+ g_main_loop_quit (mLoop);
+ mLoop = nullptr;
+ }
+
+ if (!parameters) {
+ // mResult should be already defined
+ return;
+ }
+
+ guint32 response;
+ GVariant *options;
+
+ g_variant_get (parameters, "(u@a{sv})", &response, &options);
+ mResult = GTK_PRINT_OPERATION_RESULT_CANCEL;
+ if (response == 0) {
+ GVariant *v;
+
+ char *filename;
+ char *uri;
+ v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
+ static auto s_gtk_print_settings_new_from_gvariant =
+ reinterpret_cast<GtkPrintSettings* (*)(GVariant*)>
+ (dlsym(RTLD_DEFAULT, "gtk_print_settings_new_from_gvariant"));
+
+ GtkPrintSettings* printSettings = s_gtk_print_settings_new_from_gvariant(v);
+ g_variant_unref (v);
+
+ v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT);
+ static auto s_gtk_page_setup_new_from_gvariant =
+ reinterpret_cast<GtkPageSetup* (*)(GVariant*)>
+ (dlsym(RTLD_DEFAULT, "gtk_page_setup_new_from_gvariant"));
+ GtkPageSetup* pageSetup = s_gtk_page_setup_new_from_gvariant(v);
+ g_variant_unref (v);
+
+ g_variant_lookup (options, "token", "u", &mToken);
+
+ // Force printing to file because only filedescriptor of the file
+ // can be passed to portal
+ int fd = g_file_open_tmp("gtkprintXXXXXX", &filename, NULL);
+ uri = g_filename_to_uri(filename, NULL, NULL);
+ gtk_print_settings_set(printSettings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
+ g_free (uri);
+ close (fd);
+
+ // Save native settings in the session object
+ mPrintAndPageSettings->SetGtkPrintSettings(printSettings);
+ mPrintAndPageSettings->SetGtkPageSetup(pageSetup);
+
+ // Portal consumes PDF file
+ mPrintAndPageSettings->SetOutputFormat(nsIPrintSettings::kOutputFormatPDF);
+
+ // We need to set to print to file
+ mPrintAndPageSettings->SetPrintToFile(true);
+
+ mResult = GTK_PRINT_OPERATION_RESULT_APPLY;
+ }
+}
+
+/**
+ * Get result of the print dialog.
+ *
+ * This call blocks until FinishPrintDialog is called.
+ *
+ */
+GtkPrintOperationResult
+nsFlatpakPrintPortal::GetResult() {
+ // If the mLoop has not been initialized we haven't go thru PreparePrint method
+ if (!NS_IsMainThread() || !mLoop) {
+ return GTK_PRINT_OPERATION_RESULT_ERROR;
+ }
+ // Calling g_main_loop_run stops current code until g_main_loop_quit is called
+ g_main_loop_run(mLoop);
+
+ // Free resources we've allocated in order to show print dialog.
+#ifdef MOZ_WAYLAND
+ if (mParentWindow) {
+ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(mParentWindow));
+ static auto s_gdk_wayland_window_unexport_handle =
+ reinterpret_cast<void (*)(GdkWindow*)>
+ (dlsym(RTLD_DEFAULT, "gdk_wayland_window_unexport_handle"));
+ if (s_gdk_wayland_window_unexport_handle) {
+ s_gdk_wayland_window_unexport_handle(gdk_window);
+ }
+ }
+#endif
+ return mResult;
+}
+
+/**
+ * Send file descriptor of the file which contains document to the portal to
+ * finish the print operation.
+ */
+NS_IMETHODIMP
+nsFlatpakPrintPortal::Observe(nsISupports *aObject, const char * aTopic,
+ const char16_t * aData)
+{
+ // Check that written file match to the stored filename in case multiple
+ // print operations are in progress.
+ nsAutoString filenameStr;
+ mPrintAndPageSettings->GetToFileName(filenameStr);
+ if (!nsDependentString(aData).Equals(filenameStr)) {
+ // Different file is finished, not for this instance
+ return NS_OK;
+ }
+ int fd, idx;
+ fd = open(NS_ConvertUTF16toUTF8(filenameStr).get(), O_RDONLY|O_CLOEXEC);
+ static auto s_g_unix_fd_list_new =
+ reinterpret_cast<GUnixFDList* (*)(void)>
+ (dlsym(RTLD_DEFAULT, "g_unix_fd_list_new"));
+ NS_ASSERTION(s_g_unix_fd_list_new, "Cannot find g_unix_fd_list_new function.");
+
+ GUnixFDList *fd_list = s_g_unix_fd_list_new();
+ static auto s_g_unix_fd_list_append =
+ reinterpret_cast<gint (*)(GUnixFDList*, gint, GError**)>
+ (dlsym(RTLD_DEFAULT, "g_unix_fd_list_append"));
+ idx = s_g_unix_fd_list_append(fd_list, fd, NULL);
+ close(fd);
+
+ GVariantBuilder opt_builder;
+ g_variant_builder_init(&opt_builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add(&opt_builder, "{sv}", "token",
+ g_variant_new_uint32(mToken));
+ g_dbus_proxy_call_with_unix_fd_list(
+ mProxy,
+ "Print",
+ g_variant_new("(ssh@a{sv})",
+ "", /* window */
+ "Print", /* title */
+ idx,
+ g_variant_builder_end(&opt_builder)),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ fd_list,
+ NULL,
+ NULL, // TODO portal result cb function
+ nullptr); // data
+ g_object_unref(fd_list);
+
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ // Let the nsFlatpakPrintPortal instance die
+ os->RemoveObserver(this, "print-to-file-finished");
+ return NS_OK;
+}
+
+nsFlatpakPrintPortal::~nsFlatpakPrintPortal() {
+ if (mProxy) {
+ if (mResponseSignalId) {
+ g_dbus_connection_signal_unsubscribe(
+ g_dbus_proxy_get_connection(G_DBUS_PROXY(mProxy)), mResponseSignalId);
+ }
+ g_object_unref(mProxy);
+ }
+ if (mLoop)
+ g_main_loop_quit(mLoop);
+}
+
NS_IMETHODIMP
nsPrintDialogServiceGTK::Show(nsPIDOMWindowOuter *aParent,
nsIPrintSettings *aSettings,
nsIWebBrowserPrint *aWebBrowserPrint)
{
- NS_PRECONDITION(aParent, "aParent must not be null");
- NS_PRECONDITION(aSettings, "aSettings must not be null");
+ MOZ_ASSERT(aParent, "aParent must not be null");
+ MOZ_ASSERT(aSettings, "aSettings must not be null");
+
+ // Check for the flatpak portal first
+ nsCOMPtr<nsIGIOService> giovfs =
+ do_GetService(NS_GIOSERVICE_CONTRACTID);
+ bool shouldUsePortal;
+ giovfs->ShouldUseFlatpakPortal(&shouldUsePortal);
+ if (shouldUsePortal && gtk_check_version(3, 22, 0) == nullptr) {
+ nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(aParent);
+ NS_ASSERTION(widget, "Need a widget for dialog to be modal.");
+ GtkWindow* gtkParent = get_gtk_window_for_nsiwidget(widget);
+ NS_ASSERTION(gtkParent, "Need a GTK window for dialog to be modal.");
+
+
+ nsCOMPtr<nsPrintSettingsGTK> printSettingsGTK(do_QueryInterface(aSettings));
+ RefPtr<nsFlatpakPrintPortal> fpPrintPortal =
+ new nsFlatpakPrintPortal(printSettingsGTK);
+
+ nsresult rv = fpPrintPortal->PreparePrintRequest(gtkParent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // This blocks until nsFlatpakPrintPortal::FinishPrintDialog is called
+ GtkPrintOperationResult printDialogResult = fpPrintPortal->GetResult();
+
+ rv = NS_OK;
+ switch (printDialogResult) {
+ case GTK_PRINT_OPERATION_RESULT_APPLY:
+ {
+ nsCOMPtr<nsIObserver> observer = do_QueryInterface(fpPrintPortal);
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ NS_ENSURE_STATE(os);
+ // Observer waits until notified that the file with the content
+ // to print has been written.
+ rv = os->AddObserver(observer, "print-to-file-finished", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ case GTK_PRINT_OPERATION_RESULT_CANCEL:
+ rv = NS_ERROR_ABORT;
+ break;
+ default:
+ NS_WARNING("Unexpected response");
+ rv = NS_ERROR_ABORT;
+ }
+ return rv;
+ }
nsPrintDialogWidgetGTK printDialog(aParent, aSettings);
nsresult rv = printDialog.ImportSettings(aSettings);
@@ -553,8 +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<nsIWidget> 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 <nsIURI> fileURI;
-
- // create a nsIFile and then a nsIFileURL from that
- nsCOMPtr <nsIFile> 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<nsIFileURL> 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 <algorithm>
#include "GeckoProfiler.h"
@@ -25,7 +26,7 @@
#include "prlink.h"
#include "nsGTKToolkit.h"
#include "nsIRollupListener.h"
-#include "nsIDOMNode.h"
+#include "nsINode.h"
#include "nsWidgetsCID.h"
#include "nsDragService.h"
@@ -56,6 +57,7 @@
#if defined(MOZ_WAYLAND)
#include <gdk/gdkwayland.h>
+#include "nsView.h"
#endif
#include "nsGkAtoms.h"
@@ -116,6 +118,7 @@ using namespace mozilla::widget;
#include "mozilla/layers/CompositorThread.h"
#ifdef MOZ_X11
+#include "GLContextGLX.h" // for GLContextGLX::FindVisual()
#include "GtkCompositorWidget.h"
#include "gfxXlibSurface.h"
#include "WindowSurfaceX11Image.h"
@@ -129,8 +132,6 @@ using namespace mozilla::widget;
#include "nsShmImage.h"
#include "gtkdrawing.h"
-#include "nsIDOMWheelEvent.h"
-
#include "NativeKeyBindings.h"
#include <dlfcn.h>
@@ -140,6 +141,7 @@ using namespace mozilla::gfx;
using namespace mozilla::widget;
using namespace mozilla::layers;
using mozilla::gl::GLContext;
+using mozilla::gl::GLContextGLX;
// Don't put more than this many rects in the dirty region, just fluff
// out to the bounding-box if there are more
@@ -155,7 +157,8 @@ const gint kEvents = GDK_EXPOSURE_MASK |
#endif
GDK_SCROLL_MASK |
GDK_POINTER_MOTION_MASK |
- GDK_PROPERTY_CHANGE_MASK;
+ GDK_PROPERTY_CHANGE_MASK |
+ GDK_FOCUS_CHANGE_MASK;
/* utility functions */
static bool is_mouse_in_window(GdkWindow* aWindow,
@@ -210,7 +213,7 @@ static void hierarchy_changed_cb
GtkWidget *previous_toplevel);
static gboolean window_state_event_cb (GtkWidget *widget,
GdkEventWindowState *event);
-static void theme_changed_cb (GtkSettings *settings,
+static void settings_changed_cb (GtkSettings *settings,
GParamSpec *pspec,
nsWindow *data);
static void check_resize_cb (GtkContainer* container,
@@ -481,6 +484,8 @@ nsWindow::nsWindow()
mPendingConfigures = 0;
mCSDSupportLevel = CSD_SUPPORT_NONE;
mDrawInTitlebar = false;
+
+ mHasAlphaVisual = false;
}
nsWindow::~nsWindow()
@@ -630,7 +635,7 @@ EnsureInvisibleContainer()
static void
CheckDestroyInvisibleContainer()
{
- NS_PRECONDITION(gInvisibleContainer, "oh, no");
+ MOZ_ASSERT(gInvisibleContainer, "oh, no");
if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
// No children, so not in use.
@@ -731,7 +736,7 @@ nsWindow::Destroy()
ClearCachedResources();
g_signal_handlers_disconnect_by_func(gtk_settings_get_default(),
- FuncToGpointer(theme_changed_cb),
+ FuncToGpointer(settings_changed_cb),
this);
nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
@@ -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<nsWindow*>(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<nsWindow*>(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<nsIDOMNode> sourceNode;
+ nsCOMPtr<nsINode> 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<TextEventDispatcher> 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<TextEventDispatcher> 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<GdkWindowState>
+ (aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
+ } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
+ aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
+ aEvent->changed_mask = static_cast<GdkWindowState>
+ (aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
+ }
+
// We don't care about anything but changes in the maximized/icon/fullscreen
// states
if ((aEvent->changed_mask
@@ -3404,6 +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<nsWindow*>(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<char*>(mGtkWindowTypeName.get());
+ class_hint->res_class = const_cast<char*>(res_class);
+
+ // Can't use gtk_window_set_wmclass() for this; it prints
+ // a warning & refuses to make the change.
+ GdkDisplay *display = gdk_display_get_default();
+ XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
+ gdk_x11_window_get_xid(gdkWindow),
+ class_hint);
+ XFree(class_hint);
+ }
+#endif /* MOZ_X11 */
+}
- const char *role = nullptr;
+void
+nsWindow::SetWindowClass(const nsAString &xulWinType)
+{
+ if (!mShell)
+ return;
- // Parse res_name into a name and role. Characters other than
- // [A-Za-z0-9_-] are converted to '_'. Anything after the first
- // colon is assigned to role; if there's no colon, assign the
- // whole thing to both role and res_name.
- for (char *c = res_name; *c; c++) {
- if (':' == *c) {
- *c = 0;
- role = c + 1;
- }
- else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
- *c = '_';
- }
- res_name[0] = toupper(res_name[0]);
- if (!role) role = res_name;
+ char *res_name = ToNewCString(xulWinType);
+ if (!res_name)
+ return;
- GdkWindow* gdkWindow = gtk_widget_get_window(mShell);
- gdk_window_set_role(gdkWindow, role);
+ const char *role = nullptr;
-#ifdef MOZ_X11
- if (mIsX11Display) {
- XClassHint *class_hint = XAllocClassHint();
- if (!class_hint) {
- free(res_name);
- return;
+ // Parse res_name into a name and role. Characters other than
+ // [A-Za-z0-9_-] are converted to '_'. Anything after the first
+ // colon is assigned to role; if there's no colon, assign the
+ // whole thing to both role and res_name.
+ for (char *c = res_name; *c; c++) {
+ if (':' == *c) {
+ *c = 0;
+ role = c + 1;
}
- class_hint->res_name = res_name;
- class_hint->res_class = const_cast<char*>(res_class);
+ else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
+ *c = '_';
+ }
+ res_name[0] = toupper(res_name[0]);
+ if (!role) role = res_name;
- // Can't use gtk_window_set_wmclass() for this; it prints
- // a warning & refuses to make the change.
- GdkDisplay *display = gdk_display_get_default();
- XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
- gdk_x11_window_get_xid(gdkWindow),
- class_hint);
- XFree(class_hint);
- }
-#endif /* MOZ_X11 */
+ mGtkWindowTypeName = res_name;
+ mGtkWindowRoleName = role;
+ free(res_name);
- free(res_name);
+ RefreshWindowClass();
}
void
@@ -4162,6 +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<nsWindow> 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<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window)
@@ -6063,15 +6199,24 @@ drag_motion_event_cb(GtkWidget *aWidget,
RefPtr<nsDragService> dragService = nsDragService::GetInstance();
return dragService->
- ScheduleMotionEvent(innerMostWindow, aDragContext,
+ ScheduleMotionEvent(innerMostWindow, aDragContext, aWaylandDragContext,
point, aTime);
}
-static void
-drag_leave_event_cb(GtkWidget *aWidget,
- GdkDragContext *aDragContext,
- guint aTime,
- gpointer aData)
+static gboolean
+drag_motion_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ guint aTime,
+ gpointer aData)
+{
+ return WindowDragMotionHandler(aWidget, aDragContext, nullptr,
+ aX, aY, aTime);
+}
+
+void
+WindowDragLeaveHandler(GtkWidget *aWidget)
{
RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window)
@@ -6104,14 +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<nsWindow> window = get_window_for_gtk_widget(aWidget);
if (!window)
@@ -6136,10 +6289,21 @@ drag_drop_event_cb(GtkWidget *aWidget,
RefPtr<nsDragService> dragService = nsDragService::GetInstance();
return dragService->
- ScheduleDropEvent(innerMostWindow, aDragContext,
+ ScheduleDropEvent(innerMostWindow, aDragContext, aWaylandDragContext,
point, aTime);
}
+static gboolean
+drag_drop_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ guint aTime,
+ gpointer aData)
+{
+ return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
+}
+
static void
drag_data_received_event_cb(GtkWidget *aWidget,
GdkDragContext *aDragContext,
@@ -6554,12 +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>
+nsIWidget::CreateTopLevelWindow()
+{
+ nsCOMPtr<nsIWidget> window = new nsWindow();
+ return window.forget();
+}
+
+already_AddRefed<nsIWidget>
+nsIWidget::CreateChildWindow()
+{
+ nsCOMPtr<nsIWidget> 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<WindowSurfaceXRender>(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<WindowSurfaceX11SHM>(mXDisplay, mXWindow, mXVisual, mXDepth);
}
#endif // MOZ_HAVE_SHMIMAGE
LOGDRAW(("Drawing to nsWindow %p using XPutImage\n", (void*)this));
- return MakeUnique<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual, mXDepth);
+ return MakeUnique<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual,
+ mXDepth, mIsShaped);
}
already_AddRefed<gfx::DrawTarget>
@@ -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<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual, mXDepth);
+ mWindowSurface = MakeUnique<WindowSurfaceX11Image>(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 <gdk/gdkwayland.h>
#endif
#include <X11/Xlib.h> // 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<gfx::DrawTarget>
-WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion)
+WindowBackBuffer::Lock()
{
- gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
- gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
-
+ gfx::IntSize lockSize(mWidth, mHeight);
return gfxPlatform::CreateDrawTargetForData(static_cast<unsigned char*>(mShmPool.GetImageData()),
lockSize,
BUFFER_BPP * mWidth,
@@ -551,6 +551,8 @@ frame_callback_handler(void *data, struc
{
auto surface = reinterpret_cast<WindowSurfaceWayland*>(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<gfx::DrawTarget>
+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<gfx::DrawTarget>
+WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize)
+{
+ if (!mImageSurface || mImageSurface->CairoStatus() ||
+ !(aLockSize <= mImageSurface->GetSize())) {
+ mImageSurface = new gfxImageSurface(aLockSize,
+ SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat()));
+ if (mImageSurface->CairoStatus()) {
+ return nullptr;
+ }
}
- return 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<gfx::DrawTarget>
WindowSurfaceWayland::Lock(const LayoutDeviceIntRegion& aRegion)
{
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
- // We allocate back buffer to widget size but return only
- // portion requested by aRegion.
- LayoutDeviceIntRect rect = mWindow->GetBounds();
- WindowBackBuffer* buffer = GetBufferToDraw(rect.width,
- rect.height);
- if (!buffer) {
- NS_WARNING("No drawing buffer available");
- return nullptr;
+ LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+ gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+ // Are we asked for entire nsWindow to draw?
+ mDrawToWaylandBufferDirectly = (aRegion.GetNumRects() == 1 &&
+ bounds.x == 0 && bounds.y == 0 &&
+ lockSize.width == screenRect.width &&
+ lockSize.height == screenRect.height);
+
+ if (mDrawToWaylandBufferDirectly) {
+ RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(screenRect.width,
+ screenRect.height);
+ if (dt) {
+ return dt.forget();
+ }
+
+ // We don't have any front buffer available. Try indirect drawing
+ // to mImageSurface which is mirrored to front buffer at commit.
+ mDrawToWaylandBufferDirectly = false;
}
- return 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<gfx::DrawTarget> dt = LockWaylandBuffer(screenRect.width,
+ screenRect.height);
+ RefPtr<gfx::SourceSurface> surf =
+ gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(),
+ mImageSurface->GetSize(),
+ mImageSurface->Format());
+ if (!dt || !surf) {
+ return false;
+ }
+
+ uint32_t numRects = aRegion.GetNumRects();
+ if (numRects != 1) {
+ AutoTArray<IntRect, 32> rects;
+ rects.SetCapacity(numRects);
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ rects.AppendElement(iter.Get().ToUnknownRect());
+ }
+ dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+ }
+
+ dt->DrawSurface(surf, rect, rect);
+
+ if (numRects != 1) {
+ dt->PopClip();
+ }
+
+ return true;
+}
+
+static void
+WaylandBufferDelayCommitHandler(WindowSurfaceWayland **aSurface)
+{
+ if (*aSurface) {
+ (*aSurface)->DelayedCommitHandler();
+ } else {
+ // Referenced WindowSurfaceWayland is already deleted.
+ // Do nothing but just release the mDelayedCommitHandle allocated at
+ // WindowSurfaceWayland::CommitWaylandBuffer().
+ free(aSurface);
+ }
}
void
-WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
+WindowSurfaceWayland::CommitWaylandBuffer()
{
- MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+ MOZ_ASSERT(mPendingCommit, "Committing empty surface!");
wl_surface* waylandSurface = mWindow->GetWaylandSurface();
if (!waylandSurface) {
- // Target window is already destroyed - don't bother to render there.
+ // Target window is not created yet - delay the commit. This can happen only
+ // when the window is newly created and there's no active
+ // frame callback pending.
+ MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface,
+ "Missing wayland surface at frame callback!");
+
+ // Do nothing if there's already mDelayedCommitHandle pending.
+ if (!mDelayedCommitHandle) {
+ mDelayedCommitHandle = static_cast<WindowSurfaceWayland**>(
+ moz_xmalloc(sizeof(*mDelayedCommitHandle)));
+ *mDelayedCommitHandle = this;
+
+ MessageLoop::current()->PostDelayedTask(
+ NewRunnableFunction("WaylandBackBufferCommit",
+ &WaylandBufferDelayCommitHandler,
+ mDelayedCommitHandle),
+ EVENT_LOOP_DELAY);
+ }
return;
}
wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
mWaylandDisplay->GetEventQueue());
- if (mFullScreenDamage) {
+ // We have an active frame callback request so handle it.
+ if (mFrameCallback) {
+ if (waylandSurface == mLastCommittedSurface) {
+ // We have an active frame callback pending from our recent surface.
+ // It means we should defer the commit to FrameCallbackHandler().
+ return;
+ }
+ // If our stored wl_surface does not match the actual one it means the frame
+ // callback is no longer active and we should release it.
+ wl_callback_destroy(mFrameCallback);
+ mFrameCallback = nullptr;
+ mLastCommittedSurface = nullptr;
+ }
+
+ if (mWaylandBufferFullScreenDamage) {
LayoutDeviceIntRect rect = mWindow->GetBounds();
wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height);
- mFullScreenDamage = false;
+ mWaylandBufferFullScreenDamage = false;
} else {
- for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ gint scaleFactor = mWindow->GdkScaleFactor();
+ for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) {
const mozilla::LayoutDeviceIntRect &r = iter.Get();
- wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
+ // We need to remove the scale factor because the wl_surface_damage
+ // also multiplies by current scale factor.
+ wl_surface_damage(waylandSurface, r.x/scaleFactor, r.y/scaleFactor,
+ r.width/scaleFactor, r.height/scaleFactor);
}
}
- // Frame callback is always connected to actual wl_surface. When the surface
- // is unmapped/deleted the frame callback is never called. Unfortunatelly
- // we don't know if the frame callback is not going to be called.
- // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden
- // creates a new one when is visible.
- if (mFrameCallback && mFrameCallbackSurface == waylandSurface) {
- // Do nothing here - we have a valid wl_surface and the buffer will be
- // commited to compositor in next frame callback event.
- mDelayedCommit = true;
- return;
- } else {
- if (mFrameCallback) {
- // Delete frame callback connected to obsoleted wl_surface.
- wl_callback_destroy(mFrameCallback);
- }
+ // Clear all back buffer damage as we're committing
+ // all requested regions.
+ mWaylandBufferDamage.SetEmpty();
- mFrameCallback = wl_surface_frame(waylandSurface);
- wl_callback_add_listener(mFrameCallback, &frame_listener, this);
- mFrameCallbackSurface = waylandSurface;
-
- // There's no pending frame callback so we can draw immediately
- // and create frame callback for possible subsequent drawing.
- mFrontBuffer->Attach(waylandSurface);
- mDelayedCommit = false;
+ mFrameCallback = wl_surface_frame(waylandSurface);
+ wl_callback_add_listener(mFrameCallback, &frame_listener, this);
+
+ if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) {
+ wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+ mNeedScaleFactorUpdate = false;
+ }
+
+ mWaylandBuffer->Attach(waylandSurface);
+ mLastCommittedSurface = waylandSurface;
+
+ // There's no pending commit, all changes are sent to compositor.
+ mPendingCommit = false;
+}
+
+void
+WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
+{
+ MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+
+ // We have new content at mImageSurface - copy data to mWaylandBuffer first.
+ if (!mDrawToWaylandBufferDirectly) {
+ CommitImageSurfaceToWaylandBuffer(aInvalidRegion);
}
+
+ // If we're not at fullscreen damage add drawing area from aInvalidRegion
+ if (!mWaylandBufferFullScreenDamage) {
+ mWaylandBufferDamage.OrWith(aInvalidRegion);
+ }
+
+ // We're ready to commit.
+ mPendingCommit = true;
+ CommitWaylandBuffer();
}
void
WindowSurfaceWayland::FrameCallbackHandler()
{
MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
+ MOZ_ASSERT(mFrameCallback != nullptr,
+ "FrameCallbackHandler() called without valid frame callback!");
+ MOZ_ASSERT(mLastCommittedSurface != nullptr,
+ "FrameCallbackHandler() called without valid wl_surface!");
- if (mFrameCallback) {
- wl_callback_destroy(mFrameCallback);
- mFrameCallback = nullptr;
- mFrameCallbackSurface = nullptr;
+ wl_callback_destroy(mFrameCallback);
+ mFrameCallback = nullptr;
+
+ if (mPendingCommit) {
+ CommitWaylandBuffer();
}
+}
- if (mDelayedCommit) {
- wl_surface* waylandSurface = mWindow->GetWaylandSurface();
- if (!waylandSurface) {
- // Target window is already destroyed - don't bother to render there.
- NS_WARNING("No drawing buffer available");
- return;
- }
- wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
- mWaylandDisplay->GetEventQueue());
+void
+WindowSurfaceWayland::DelayedCommitHandler()
+{
+ MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!");
- // Send pending surface to compositor and register frame callback
- // for possible subsequent drawing.
- mFrameCallback = wl_surface_frame(waylandSurface);
- wl_callback_add_listener(mFrameCallback, &frame_listener, this);
- mFrameCallbackSurface = waylandSurface;
+ *mDelayedCommitHandle = nullptr;
+ free(mDelayedCommitHandle);
+ mDelayedCommitHandle = nullptr;
- mFrontBuffer->Attach(waylandSurface);
- mDelayedCommit = false;
+ if (mPendingCommit) {
+ CommitWaylandBuffer();
}
}
diff -up thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h.wayland thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h
--- thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h.wayland 2018-10-30 12:45:35.000000000 +0100
+++ thunderbird-60.3.0/widget/gtk/WindowSurfaceWayland.h 2018-11-20 12:04:43.742787334 +0100
@@ -8,6 +8,7 @@
#define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H
#include <prthread.h>
+#include "mozilla/gfx/Types.h"
namespace mozilla {
namespace widget {
@@ -66,14 +67,14 @@ public:
WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight);
~WindowBackBuffer();
- already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion);
+ already_AddRefed<gfx::DrawTarget> Lock();
void Attach(wl_surface* aSurface);
void Detach();
bool IsAttached() { return mAttached; }
bool Resize(int aWidth, int aHeight);
- bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
+ bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
bool IsMatchingSize(int aWidth, int aHeight)
{
@@ -110,22 +111,32 @@ public:
already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
void FrameCallbackHandler();
+ void DelayedCommitHandler();
private:
- WindowBackBuffer* GetBufferToDraw(int aWidth, int aHeight);
- void UpdateScaleFactor();
+ WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight);
+
+ already_AddRefed<gfx::DrawTarget> LockWaylandBuffer(int aWidth, int aHeight);
+ already_AddRefed<gfx::DrawTarget> LockImageSurface(const gfx::IntSize& aLockSize);
+ bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion);
+ void CommitWaylandBuffer();
// TODO: Do we need to hold a reference to nsWindow object?
nsWindow* mWindow;
nsWaylandDisplay* mWaylandDisplay;
- WindowBackBuffer* mFrontBuffer;
- WindowBackBuffer* mBackBuffer;
+ WindowBackBuffer* mWaylandBuffer;
+ LayoutDeviceIntRegion mWaylandBufferDamage;
+ WindowBackBuffer* mBackupBuffer;
+ RefPtr<gfxImageSurface> mImageSurface;
wl_callback* mFrameCallback;
- wl_surface* mFrameCallbackSurface;
+ wl_surface* mLastCommittedSurface;
MessageLoop* mDisplayThreadMessageLoop;
- bool mDelayedCommit;
- bool mFullScreenDamage;
+ WindowSurfaceWayland** mDelayedCommitHandle;
+ bool mDrawToWaylandBufferDirectly;
+ bool mPendingCommit;
+ bool mWaylandBufferFullScreenDamage;
bool mIsMainThread;
+ bool mNeedScaleFactorUpdate;
};
} // namespace widget
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 <X11/extensions/shape.h>
+
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<gfx::DrawTarget>
@@ -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<gfx::DrawTarget> 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<gfxXlibSurface> mWindowSurface;
RefPtr<gfxImageSurface> mImageSurface;
+
+ gchar* mTransparencyBitmap;
+ int32_t mTransparencyBitmapWidth;
+ int32_t mTransparencyBitmapHeight;
+ bool mIsShaped;
};
} // namespace widget