8832 lines
339 KiB
Diff
8832 lines
339 KiB
Diff
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
|