diff options
-rw-r--r-- | firefox.spec | 12 | ||||
-rw-r--r-- | widget-rebase.patch | 3962 |
2 files changed, 3973 insertions, 1 deletions
diff --git a/firefox.spec b/firefox.spec index 8355468..89e67e7 100644 --- a/firefox.spec +++ b/firefox.spec @@ -94,7 +94,7 @@ Summary: Mozilla Firefox Web browser Name: firefox Version: 50.0 -Release: 1%{?pre_tag}%{?dist} +Release: 2%{?pre_tag}%{?dist} URL: https://www.mozilla.org/firefox/ License: MPLv1.1 or GPLv2+ or LGPLv2+ Group: Applications/Internet @@ -136,6 +136,9 @@ Patch225: mozilla-1005640-accept-lang.patch Patch304: mozilla-1253216.patch Patch402: mozilla-1196777.patch Patch406: mozilla-256180.patch +# Rebase Gtk3 widget code to latest trunk to +# fix various rendering problems +Patch407: widget-rebase.patch # Debian patches Patch500: mozilla-440908.patch @@ -278,6 +281,9 @@ cd %{tarballdir} %patch304 -p1 -b .1253216 %patch402 -p1 -b .1196777 %patch406 -p1 -b .256180 +# Rebase Gtk3 widget code to latest trunk to +# fix various rendering problems +%patch407 -p1 -b .widget-rebase # Debian extension patch %patch500 -p1 -b .440908 @@ -786,6 +792,10 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : #--------------------------------------------------------------------- %changelog +* Thu Nov 24 2016 Martin Stransky <stransky@redhat.com> - 50.0-2 +- Rebase Gtk3 widget code to latest trunk to fix + various rendering problems (rhbz#1397290) + * Thu Nov 10 2016 Martin Stransky <stransky@redhat.com> - 50.0-1 - Update to 50.0 diff --git a/widget-rebase.patch b/widget-rebase.patch new file mode 100644 index 0000000..85dcbc8 --- /dev/null +++ b/widget-rebase.patch @@ -0,0 +1,3962 @@ +diff -up firefox-50.0/widget/gtk/gtk3drawing.cpp.widget-rebase firefox-50.0/widget/gtk/gtk3drawing.cpp +--- firefox-50.0/widget/gtk/gtk3drawing.cpp.widget-rebase 2016-10-31 21:15:38.000000000 +0100 ++++ firefox-50.0/widget/gtk/gtk3drawing.cpp 2016-11-24 11:57:52.033064782 +0100 +@@ -18,27 +18,6 @@ + + #include <math.h> + +-static GtkWidget* gProtoLayout; +-static GtkWidget* gHScaleWidget; +-static GtkWidget* gVScaleWidget; +-static GtkWidget* gComboBoxWidget; +-static GtkWidget* gComboBoxButtonWidget; +-static GtkWidget* gComboBoxArrowWidget; +-static GtkWidget* gComboBoxSeparatorWidget; +-static GtkWidget* gComboBoxEntryWidget; +-static GtkWidget* gComboBoxEntryTextareaWidget; +-static GtkWidget* gComboBoxEntryButtonWidget; +-static GtkWidget* gComboBoxEntryArrowWidget; +-static GtkWidget* gTabWidget; +-static GtkWidget* gImageMenuItemWidget; +-static GtkWidget* gCheckMenuItemWidget; +-static GtkWidget* gTreeViewWidget; +-static GtkTreeViewColumn* gMiddleTreeViewColumn; +-static GtkWidget* gTreeHeaderCellWidget; +-static GtkWidget* gTreeHeaderSortArrowWidget; +-static GtkWidget* gHPanedWidget; +-static GtkWidget* gVPanedWidget; +- + static style_prop_t style_prop_func; + static gboolean have_arrow_scaling; + static gboolean checkbox_check_state; +@@ -54,6 +33,13 @@ static gboolean is_initialized; + #define GTK_STATE_FLAG_CHECKED (1 << 11) + #endif + ++static gint ++moz_gtk_get_tab_thickness(GtkStyleContext *style); ++ ++static gint ++moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect, ++ GtkWidgetState* state, GtkTextDirection direction); ++ + static GtkStateFlags + GetStateFlagsFromGtkWidgetState(GtkWidgetState* state) + { +@@ -73,338 +59,17 @@ GetStateFlagsFromGtkWidgetState(GtkWidge + return stateFlags; + } + +-gint +-moz_gtk_enable_style_props(style_prop_t styleGetProp) +-{ +- style_prop_func = styleGetProp; +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-setup_widget_prototype(GtkWidget* widget) +-{ +- if (!gProtoLayout) { +- gProtoLayout = GetWidget(MOZ_GTK_WINDOW_CONTAINER); +- } +- gtk_container_add(GTK_CONTAINER(gProtoLayout), widget); +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_hpaned_widget() +-{ +- if (!gHPanedWidget) { +- gHPanedWidget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); +- setup_widget_prototype(gHPanedWidget); +- } +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_vpaned_widget() +-{ +- if (!gVPanedWidget) { +- gVPanedWidget = gtk_paned_new(GTK_ORIENTATION_VERTICAL); +- setup_widget_prototype(gVPanedWidget); +- } +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_scale_widget() +-{ +- if (!gHScaleWidget) { +- gHScaleWidget = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, NULL); +- setup_widget_prototype(gHScaleWidget); +- } +- if (!gVScaleWidget) { +- gVScaleWidget = gtk_scale_new(GTK_ORIENTATION_VERTICAL, NULL); +- setup_widget_prototype(gVScaleWidget); +- } +- return MOZ_GTK_SUCCESS; +-} +- +-/* We need to have pointers to the inner widgets (button, separator, arrow) +- * of the ComboBox to get the correct rendering from theme engines which +- * special cases their look. Since the inner layout can change, we ask GTK +- * to NULL our pointers when they are about to become invalid because the +- * corresponding widgets don't exist anymore. It's the role of +- * g_object_add_weak_pointer(). +- * Note that if we don't find the inner widgets (which shouldn't happen), we +- * fallback to use generic "non-inner" widgets, and they don't need that kind +- * of weak pointer since they are explicit children of gProtoLayout and as +- * such GTK holds a strong reference to them. */ +-static void +-moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) +-{ +- if (GTK_IS_TOGGLE_BUTTON(widget)) { +- gComboBoxButtonWidget = widget; +- g_object_add_weak_pointer(G_OBJECT(widget), +- (gpointer *) &gComboBoxButtonWidget); +- gtk_widget_realize(widget); +- } +-} +- +-static void +-moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, +- gpointer client_data) +-{ +- if (GTK_IS_SEPARATOR(widget)) { +- gComboBoxSeparatorWidget = widget; +- g_object_add_weak_pointer(G_OBJECT(widget), +- (gpointer *) &gComboBoxSeparatorWidget); +- } else if (GTK_IS_ARROW(widget)) { +- gComboBoxArrowWidget = widget; +- g_object_add_weak_pointer(G_OBJECT(widget), +- (gpointer *) &gComboBoxArrowWidget); +- } else +- return; +- gtk_widget_realize(widget); +-} +- +-static gint +-ensure_combo_box_widgets() +-{ +- GtkWidget* buttonChild; +- +- if (gComboBoxButtonWidget && gComboBoxArrowWidget) +- return MOZ_GTK_SUCCESS; +- +- /* Create a ComboBox if needed */ +- if (!gComboBoxWidget) { +- gComboBoxWidget = gtk_combo_box_new(); +- setup_widget_prototype(gComboBoxWidget); +- } +- +- /* Get its inner Button */ +- gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), +- moz_gtk_get_combo_box_inner_button, +- NULL); +- +- if (gComboBoxButtonWidget) { +- /* Get the widgets inside the Button */ +- buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget)); +- if (GTK_IS_BOX(buttonChild)) { +- /* appears-as-list = FALSE, cell-view = TRUE; the button +- * contains an hbox. This hbox is there because the ComboBox +- * needs to place a cell renderer, a separator, and an arrow in +- * the button when appears-as-list is FALSE. */ +- gtk_container_forall(GTK_CONTAINER(buttonChild), +- moz_gtk_get_combo_box_button_inner_widgets, +- NULL); +- } else if(GTK_IS_ARROW(buttonChild)) { +- /* appears-as-list = TRUE, or cell-view = FALSE; +- * the button only contains an arrow */ +- gComboBoxArrowWidget = buttonChild; +- g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *) +- &gComboBoxArrowWidget); +- gtk_widget_realize(gComboBoxArrowWidget); +- } +- } else { +- /* Shouldn't be reached with current internal gtk implementation; we +- * use a generic toggle button as last resort fallback to avoid +- * crashing. */ +- gComboBoxButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON); +- } +- +- if (!gComboBoxArrowWidget) { +- /* Shouldn't be reached with current internal gtk implementation; +- * we gButtonArrowWidget as last resort fallback to avoid +- * crashing. */ +- gComboBoxArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW); +- } +- +- /* We don't test the validity of gComboBoxSeparatorWidget since there +- * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it +- * is invalid we just won't paint it. */ +- +- return MOZ_GTK_SUCCESS; +-} +- +-/* We need to have pointers to the inner widgets (entry, button, arrow) of +- * the ComboBoxEntry to get the correct rendering from theme engines which +- * special cases their look. Since the inner layout can change, we ask GTK +- * to NULL our pointers when they are about to become invalid because the +- * corresponding widgets don't exist anymore. It's the role of +- * g_object_add_weak_pointer(). +- * Note that if we don't find the inner widgets (which shouldn't happen), we +- * fallback to use generic "non-inner" widgets, and they don't need that kind +- * of weak pointer since they are explicit children of gProtoLayout and as +- * such GTK holds a strong reference to them. */ +-static void +-moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, +- gpointer client_data) +-{ +- if (GTK_IS_TOGGLE_BUTTON(widget)) { +- gComboBoxEntryButtonWidget = widget; +- g_object_add_weak_pointer(G_OBJECT(widget), +- (gpointer *) &gComboBoxEntryButtonWidget); +- } else if (GTK_IS_ENTRY(widget)) { +- gComboBoxEntryTextareaWidget = widget; +- g_object_add_weak_pointer(G_OBJECT(widget), +- (gpointer *) &gComboBoxEntryTextareaWidget); +- } else +- return; +- gtk_widget_realize(widget); +-} +- +-static void +-moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) +-{ +- if (GTK_IS_ARROW(widget)) { +- gComboBoxEntryArrowWidget = widget; +- g_object_add_weak_pointer(G_OBJECT(widget), +- (gpointer *) &gComboBoxEntryArrowWidget); +- gtk_widget_realize(widget); +- } +-} +- +-static gint +-ensure_combo_box_entry_widgets() +-{ +- GtkWidget* buttonChild; +- +- if (gComboBoxEntryTextareaWidget && +- gComboBoxEntryButtonWidget && +- gComboBoxEntryArrowWidget) +- return MOZ_GTK_SUCCESS; +- +- /* Create a ComboBoxEntry if needed */ +- if (!gComboBoxEntryWidget) { +- gComboBoxEntryWidget = gtk_combo_box_new_with_entry(); +- setup_widget_prototype(gComboBoxEntryWidget); +- } +- +- /* Get its inner Entry and Button */ +- gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), +- moz_gtk_get_combo_box_entry_inner_widgets, +- NULL); +- +- if (!gComboBoxEntryTextareaWidget) { +- gComboBoxEntryTextareaWidget = GetWidget(MOZ_GTK_ENTRY); +- } +- +- if (gComboBoxEntryButtonWidget) { +- /* Get the Arrow inside the Button */ +- buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget)); +- if (GTK_IS_BOX(buttonChild)) { +- /* appears-as-list = FALSE, cell-view = TRUE; the button +- * contains an hbox. This hbox is there because the ComboBox +- * needs to place a cell renderer, a separator, and an arrow in +- * the button when appears-as-list is FALSE. */ +- gtk_container_forall(GTK_CONTAINER(buttonChild), +- moz_gtk_get_combo_box_entry_arrow, +- NULL); +- } else if(GTK_IS_ARROW(buttonChild)) { +- /* appears-as-list = TRUE, or cell-view = FALSE; +- * the button only contains an arrow */ +- gComboBoxEntryArrowWidget = buttonChild; +- g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *) +- &gComboBoxEntryArrowWidget); +- gtk_widget_realize(gComboBoxEntryArrowWidget); +- } +- } else { +- /* Shouldn't be reached with current internal gtk implementation; +- * we use a generic toggle button as last resort fallback to avoid +- * crashing. */ +- gComboBoxEntryButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON); +- } +- +- if (!gComboBoxEntryArrowWidget) { +- /* Shouldn't be reached with current internal gtk implementation; +- * we gButtonArrowWidget as last resort fallback to avoid +- * crashing. */ +- gComboBoxEntryArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW); +- } +- +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_tab_widget() +-{ +- if (!gTabWidget) { +- gTabWidget = gtk_notebook_new(); +- setup_widget_prototype(gTabWidget); +- } +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_image_menu_item_widget() +-{ +- if (!gImageMenuItemWidget) { +- gImageMenuItemWidget = gtk_image_menu_item_new(); +- gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)), +- gImageMenuItemWidget); +- gtk_widget_realize(gImageMenuItemWidget); +- } +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_check_menu_item_widget() +-{ +- if (!gCheckMenuItemWidget) { +- gCheckMenuItemWidget = gtk_check_menu_item_new(); +- gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)), +- gCheckMenuItemWidget); +- gtk_widget_realize(gCheckMenuItemWidget); +- } +- return MOZ_GTK_SUCCESS; +-} +- +-static gint +-ensure_tree_view_widget() ++static GtkStateFlags ++GetStateFlagsFromGtkTabFlags(GtkTabFlags flags) + { +- if (!gTreeViewWidget) { +- gTreeViewWidget = gtk_tree_view_new(); +- setup_widget_prototype(gTreeViewWidget); +- } +- return MOZ_GTK_SUCCESS; ++ return ((flags & MOZ_GTK_TAB_SELECTED) == 0) ? ++ GTK_STATE_FLAG_NORMAL : GTK_STATE_FLAG_ACTIVE; + } + +-static gint +-ensure_tree_header_cell_widget() ++gint ++moz_gtk_enable_style_props(style_prop_t styleGetProp) + { +- if(!gTreeHeaderCellWidget) { +- /* +- * Some GTK engines paint the first and last cell +- * of a TreeView header with a highlight. +- * Since we do not know where our widget will be relative +- * to the other buttons in the TreeView header, we must +- * paint it as a button that is between two others, +- * thus ensuring it is neither the first or last button +- * in the header. +- * GTK doesn't give us a way to do this explicitly, +- * so we must paint with a button that is between two +- * others. +- */ +- +- GtkTreeViewColumn* firstTreeViewColumn; +- GtkTreeViewColumn* lastTreeViewColumn; +- +- ensure_tree_view_widget(); +- +- /* Create and append our three columns */ +- firstTreeViewColumn = gtk_tree_view_column_new(); +- gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); +- gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn); +- +- gMiddleTreeViewColumn = gtk_tree_view_column_new(); +- gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M"); +- gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), +- gMiddleTreeViewColumn); +- +- lastTreeViewColumn = gtk_tree_view_column_new(); +- gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); +- gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn); +- +- /* Use the middle column's header for our button */ +- gTreeHeaderCellWidget = gtk_tree_view_column_get_button(gMiddleTreeViewColumn); +- /* TODO, but it can't be NULL */ +- gTreeHeaderSortArrowWidget = gtk_button_new(); +- } ++ style_prop_func = styleGetProp; + return MOZ_GTK_SUCCESS; + } + +@@ -423,13 +88,17 @@ moz_gtk_init() + else + checkbox_check_state = GTK_STATE_FLAG_ACTIVE; + +- if(!gtk_check_version(3, 12, 0)) { +- ensure_tab_widget(); +- gtk_style_context_get_style(gtk_widget_get_style_context(gTabWidget), ++ if (gtk_check_version(3, 12, 0) == nullptr && ++ gtk_check_version(3, 20, 0) != nullptr) ++ { ++ // Deprecated for Gtk >= 3.20+ ++ GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_TAB_TOP); ++ gtk_style_context_get_style(style, + "has-tab-gap", ¬ebook_has_tab_gap, NULL); ++ ReleaseStyleContext(style); + } + else { +- notebook_has_tab_gap = TRUE; ++ notebook_has_tab_gap = true; + } + + return MOZ_GTK_SUCCESS; +@@ -455,16 +124,24 @@ moz_gtk_radio_get_metrics(gint* indicato + return MOZ_GTK_SUCCESS; + } + +-gint +-moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width) ++static gint ++moz_gtk_get_focus_outline_size(GtkStyleContext* style, ++ gint* focus_h_width, gint* focus_v_width) + { + GtkBorder border; + GtkBorder padding; +- GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_ENTRY); + gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); + gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); + *focus_h_width = border.left + padding.left; + *focus_v_width = border.top + padding.top; ++ return MOZ_GTK_SUCCESS; ++} ++ ++gint ++moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width) ++{ ++ GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_ENTRY); ++ moz_gtk_get_focus_outline_size(style, focus_h_width, focus_v_width); + ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } +@@ -472,22 +149,22 @@ moz_gtk_get_focus_outline_size(gint* foc + gint + moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding) + { +- gtk_widget_style_get(GetWidget(MOZ_GTK_MENUITEM), +- "horizontal-padding", horizontal_padding, +- nullptr); +- ++ GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_MENUITEM); ++ gtk_style_context_get_style(style, ++ "horizontal-padding", horizontal_padding, ++ nullptr); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + + gint + moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding) + { +- ensure_check_menu_item_widget(); +- +- gtk_style_context_get_style(gtk_widget_get_style_context(gCheckMenuItemWidget), ++ GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_CHECKMENUITEM); ++ gtk_style_context_get_style(style, + "horizontal-padding", horizontal_padding, +- NULL); +- ++ nullptr); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -543,15 +220,14 @@ moz_gtk_button_get_default_border(gint* + gint + moz_gtk_splitter_get_metrics(gint orientation, gint* size) + { ++ GtkStyleContext *style; + if (orientation == GTK_ORIENTATION_HORIZONTAL) { +- ensure_hpaned_widget(); +- gtk_style_context_get_style(gtk_widget_get_style_context(gHPanedWidget), +- "handle_size", size, NULL); ++ style = ClaimStyleContext(MOZ_GTK_SPLITTER_HORIZONTAL); + } else { +- ensure_vpaned_widget(); +- gtk_style_context_get_style(gtk_widget_get_style_context(gVPanedWidget), +- "handle_size", size, NULL); ++ style = ClaimStyleContext(MOZ_GTK_SPLITTER_VERTICAL); + } ++ gtk_style_context_get_style(style, "handle_size", size, NULL); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -756,8 +432,66 @@ calculate_arrow_rect(GtkWidget* arrow, G + return MOZ_GTK_SUCCESS; + } + ++void ++moz_gtk_get_widget_min_size(WidgetNodeType aGtkWidgetType, int* width, ++ int* height) ++{ ++ GtkStyleContext* style = ClaimStyleContext(aGtkWidgetType); ++ GtkStateFlags state_flags = gtk_style_context_get_state(style); ++ gtk_style_context_get(style, state_flags, ++ "min-height", height, ++ "min-width", width, ++ nullptr); ++ ++ GtkBorder border, padding, margin; ++ gtk_style_context_get_border(style, state_flags, &border); ++ gtk_style_context_get_padding(style, state_flags, &padding); ++ gtk_style_context_get_margin(style, state_flags, &margin); ++ ReleaseStyleContext(style); ++ ++ *width += border.left + border.right + margin.left + margin.right + ++ padding.left + padding.right; ++ *height += border.top + border.bottom + margin.top + margin.bottom + ++ padding.top + padding.bottom; ++} ++ ++static void ++Inset(GdkRectangle* rect, GtkBorder& aBorder) ++{ ++ MOZ_ASSERT(rect); ++ rect->x += aBorder.left; ++ rect->y += aBorder.top; ++ rect->width -= aBorder.left + aBorder.right; ++ rect->height -= aBorder.top + aBorder.bottom; ++} ++ ++// Inset a rectangle by the margins specified in a style context. ++static void ++InsetByMargin(GdkRectangle* rect, GtkStyleContext* style) ++{ ++ MOZ_ASSERT(rect); ++ GtkBorder margin; ++ ++ gtk_style_context_get_margin(style, gtk_style_context_get_state(style), ++ &margin); ++ Inset(rect, margin); ++} ++ ++// Inset a rectangle by the border and padding specified in a style context. ++static void ++InsetByBorderPadding(GdkRectangle* rect, GtkStyleContext* style) ++{ ++ GtkStateFlags state = gtk_style_context_get_state(style); ++ GtkBorder padding, border; ++ ++ gtk_style_context_get_padding(style, state, &padding); ++ Inset(rect, padding); ++ gtk_style_context_get_border(style, state, &border); ++ Inset(rect, border); ++} ++ + static gint +-moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, ++moz_gtk_scrollbar_button_paint(cairo_t *cr, const GdkRectangle* aRect, + GtkWidgetState* state, + GtkScrollbarButtonFlags flags, + GtkTextDirection direction) +@@ -795,32 +529,38 @@ moz_gtk_scrollbar_button_paint(cairo_t * + gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOP); + } + +- /* Scrollbar button has to be inset by trough_border because its DOM element +- * is filling width of vertical scrollbar's track (or height in case +- * of horizontal scrollbars). */ +- +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); +- if (flags & MOZ_GTK_STEPPER_VERTICAL) { +- rect->x += metrics.trough_border; +- rect->width = metrics.slider_width; ++ GdkRectangle rect = *aRect; ++ if (gtk_check_version(3,20,0) == nullptr) { ++ // The "trough-border" is not used since GTK 3.20. The stepper margin ++ // box occupies the full width of the "contents" gadget content box. ++ InsetByMargin(&rect, style); + } else { +- rect->y += metrics.trough_border; +- rect->height = metrics.slider_width; ++ // Scrollbar button has to be inset by trough_border because its DOM ++ // element is filling width of vertical scrollbar's track (or height ++ // in case of horizontal scrollbars). ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); ++ if (flags & MOZ_GTK_STEPPER_VERTICAL) { ++ rect.x += metrics.trough_border; ++ rect.width = metrics.slider_width; ++ } else { ++ rect.y += metrics.trough_border; ++ rect.height = metrics.slider_width; ++ } + } + +- gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); +- gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); ++ gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); ++ gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); + +- arrow_rect.width = rect->width / 2; +- arrow_rect.height = rect->height / 2; ++ arrow_rect.width = rect.width / 2; ++ arrow_rect.height = rect.height / 2; + + gfloat arrow_scaling; + gtk_style_context_get_style(style, "arrow-scaling", &arrow_scaling, NULL); + +- gdouble arrow_size = MIN(rect->width, rect->height) * arrow_scaling; +- arrow_rect.x = rect->x + (rect->width - arrow_size) / 2; +- arrow_rect.y = rect->y + (rect->height - arrow_size) / 2; ++ gdouble arrow_size = MIN(rect.width, rect.height) * arrow_scaling; ++ arrow_rect.x = rect.x + (rect.width - arrow_size) / 2; ++ arrow_rect.y = rect.y + (rect.height - arrow_size) / 2; + + if (state_flags & GTK_STATE_FLAG_ACTIVE) { + gtk_style_context_get_style(style, +@@ -862,19 +602,23 @@ moz_gtk_update_scrollbar_style(GtkStyleC + + static void + moz_gtk_draw_styled_frame(GtkStyleContext* style, cairo_t *cr, +- GdkRectangle* rect, bool drawFocus) ++ const GdkRectangle* aRect, bool drawFocus) + { +- gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); +- gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); ++ GdkRectangle rect = *aRect; ++ if (gtk_check_version(3, 6, 0) == nullptr) { ++ InsetByMargin(&rect, style); ++ } ++ gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); ++ gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); + if (drawFocus) { + gtk_render_focus(style, cr, +- rect->x, rect->y, rect->width, rect->height); ++ rect.x, rect.y, rect.width, rect.height); + } + } + + static gint + moz_gtk_scrollbar_trough_paint(WidgetNodeType widget, +- cairo_t *cr, GdkRectangle* rect, ++ cairo_t *cr, const GdkRectangle* rect, + GtkWidgetState* state, + GtkScrollbarTrackFlags flags, + GtkTextDirection direction) +@@ -886,26 +630,28 @@ moz_gtk_scrollbar_trough_paint(WidgetNod + ReleaseStyleContext(style); + } + +- bool isHorizontal = (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL); +- GtkStyleContext* style; ++ GtkStyleContext* style = ClaimStyleContext(widget, direction); ++ moz_gtk_draw_styled_frame(style, cr, rect, state->focused); ++ ReleaseStyleContext(style); + +- // Draw all child CSS Nodes for Gtk >= 3.20 +- if (gtk_check_version(3, 20, 0) == nullptr) { +- style = ClaimStyleContext(widget, direction); +- moz_gtk_update_scrollbar_style(style, widget, direction); +- moz_gtk_draw_styled_frame(style, cr, rect, state->focused); +- ReleaseStyleContext(style); ++ return MOZ_GTK_SUCCESS; ++} + +- style = ClaimStyleContext(isHorizontal ? +- MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL : +- MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL, +- direction); +- moz_gtk_draw_styled_frame(style, cr, rect, state->focused); +- ReleaseStyleContext(style); +- } +- style = ClaimStyleContext(isHorizontal ? +- MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL : +- MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL, ++static gint ++moz_gtk_scrollbar_paint(WidgetNodeType widget, ++ cairo_t *cr, const GdkRectangle* rect, ++ GtkWidgetState* state, ++ GtkTextDirection direction) ++{ ++ GtkStyleContext* style = ClaimStyleContext(widget, direction); ++ moz_gtk_update_scrollbar_style(style, widget, direction); ++ ++ moz_gtk_draw_styled_frame(style, cr, rect, state->focused); ++ ++ ReleaseStyleContext(style); ++ style = ClaimStyleContext((widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) ? ++ MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL : ++ MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL, + direction); + moz_gtk_draw_styled_frame(style, cr, rect, state->focused); + ReleaseStyleContext(style); +@@ -915,21 +661,21 @@ moz_gtk_scrollbar_trough_paint(WidgetNod + + static gint + moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget, +- cairo_t *cr, GdkRectangle* rect, ++ cairo_t *cr, const GdkRectangle* aRect, + GtkWidgetState* state, + GtkTextDirection direction) + { + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); +- GtkBorder margin; + ++ GdkRectangle rect = *aRect; + GtkStyleContext* style = ClaimStyleContext(widget, direction, state_flags); +- gtk_style_context_get_margin (style, state_flags, &margin); ++ InsetByMargin(&rect, style); + + gtk_render_slider(style, cr, +- rect->x + margin.left, +- rect->y + margin.top, +- rect->width - margin.left - margin.right, +- rect->height - margin.top - margin.bottom, ++ rect.x, ++ rect.y, ++ rect.width, ++ rect.height, + (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + +@@ -987,18 +733,15 @@ moz_gtk_scale_paint(cairo_t *cr, GdkRect + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + gint x, y, width, height, min_width, min_height; + GtkStyleContext* style; +- GtkWidget* widget; + GtkBorder margin; + +- ensure_scale_widget(); +- widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); +- gtk_widget_set_direction(widget, direction); + moz_gtk_get_scale_metrics(flags, &min_width, &min_height); + +- style = gtk_widget_get_style_context(widget); +- gtk_style_context_save(style); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); +- gtk_style_context_get_margin(style, state_flags, &margin); ++ WidgetNodeType widget = (flags == GTK_ORIENTATION_HORIZONTAL) ? ++ MOZ_GTK_SCALE_TROUGH_HORIZONTAL : ++ MOZ_GTK_SCALE_TROUGH_VERTICAL; ++ style = ClaimStyleContext(widget, direction, state_flags); ++ gtk_style_context_get_margin(style, state_flags, &margin); + + // Clamp the dimension perpendicular to the direction that the slider crosses + // to the minimum size. +@@ -1020,7 +763,8 @@ moz_gtk_scale_paint(cairo_t *cr, GdkRect + if (state->focused) + gtk_render_focus(style, cr, + rect->x, rect->y, rect->width, rect->height); +- gtk_style_context_restore(style); ++ ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -1031,17 +775,8 @@ moz_gtk_scale_thumb_paint(cairo_t *cr, G + { + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; +- GtkWidget* widget; + gint thumb_width, thumb_height, x, y; + +- ensure_scale_widget(); +- widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); +- gtk_widget_set_direction(widget, direction); +- +- style = gtk_widget_get_style_context(widget); +- gtk_style_context_save(style); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER); +- gtk_style_context_set_state(style, state_flags); + /* determine the thumb size, and position the thumb in the center in the opposite axis + */ + if (flags == GTK_ORIENTATION_HORIZONTAL) { +@@ -1055,8 +790,13 @@ moz_gtk_scale_thumb_paint(cairo_t *cr, G + y = rect->y; + } + ++ WidgetNodeType widget = (flags == GTK_ORIENTATION_HORIZONTAL) ? ++ MOZ_GTK_SCALE_THUMB_HORIZONTAL : ++ MOZ_GTK_SCALE_THUMB_VERTICAL; ++ style = ClaimStyleContext(widget, direction, state_flags); + gtk_render_slider(style, cr, x, y, thumb_width, thumb_height, flags); +- gtk_style_context_restore(style); ++ ReleaseStyleContext(style); ++ + return MOZ_GTK_SUCCESS; + } + +@@ -1078,17 +818,13 @@ static gint + moz_gtk_hpaned_paint(cairo_t *cr, GdkRectangle* rect, + GtkWidgetState* state) + { +- GtkStyleContext* style; +- +- ensure_hpaned_widget(); +- style = gtk_widget_get_style_context(gHPanedWidget); +- gtk_style_context_save(style); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); +- gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); ++ GtkStyleContext* style = ++ ClaimStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL, ++ GTK_TEXT_DIR_LTR, ++ GetStateFlagsFromGtkWidgetState(state)); + gtk_render_handle(style, cr, + rect->x, rect->y, rect->width, rect->height); +- gtk_style_context_restore(style); +- ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -1096,17 +832,13 @@ static gint + moz_gtk_vpaned_paint(cairo_t *cr, GdkRectangle* rect, + GtkWidgetState* state) + { +- GtkStyleContext* style; +- +- ensure_vpaned_widget(); +- style = gtk_widget_get_style_context(gVPanedWidget); +- gtk_style_context_save(style); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); +- gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); ++ GtkStyleContext* style = ++ ClaimStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL, ++ GTK_TEXT_DIR_LTR, ++ GetStateFlagsFromGtkWidgetState(state)); + gtk_render_handle(style, cr, + rect->x, rect->y, rect->width, rect->height); +- gtk_style_context_restore(style); +- ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -1114,60 +846,26 @@ moz_gtk_vpaned_paint(cairo_t *cr, GdkRec + static gint + moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect, + GtkWidgetState* state, +- GtkWidget* widget, GtkTextDirection direction) ++ GtkStyleContext* style) + { + gint x = rect->x, y = rect->y, width = rect->width, height = rect->height; +- GtkStyleContext* style; + int draw_focus_outline_only = state->depressed; // NS_THEME_FOCUS_OUTLINE + +- gtk_widget_set_direction(widget, direction); +- +- style = gtk_widget_get_style_context(widget); +- + if (draw_focus_outline_only) { + // Inflate the given 'rect' with the focus outline size. + gint h, v; +- moz_gtk_get_focus_outline_size(&h, &v); ++ moz_gtk_get_focus_outline_size(style, &h, &v); + rect->x -= h; + rect->width += 2 * h; + rect->y -= v; + rect->height += 2 * v; + width = rect->width; + height = rect->height; +- } +- +- /* gtkentry.c uses two windows, one for the entire widget and one for the +- * text area inside it. The background of both windows is set to the "base" +- * color of the new state in gtk_entry_state_changed, but only the inner +- * textarea window uses gtk_paint_flat_box when exposed */ +- +- /* This gets us a lovely greyish disabledish look */ +- gtk_widget_set_sensitive(widget, !state->disabled); +- +- gtk_style_context_save(style); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_ENTRY); +- +- /* Now paint the shadow and focus border. +- * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad +- * smaller when focused if the focus is not interior, then the focus. */ +- +- if (state->focused && !state->disabled) { +- /* This will get us the lit borders that focused textboxes enjoy on +- * some themes. */ +- gtk_style_context_set_state(style, GTK_STATE_FLAG_FOCUSED); +- } +- +- if (state->disabled) { +- gtk_style_context_set_state(style, GTK_STATE_FLAG_INSENSITIVE); +- } +- +- if (!draw_focus_outline_only) { ++ } else { + gtk_render_background(style, cr, x, y, width, height); + } + gtk_render_frame(style, cr, x, y, width, height); + +- gtk_style_context_restore(style); +- + return MOZ_GTK_SUCCESS; + } + +@@ -1214,9 +912,6 @@ moz_gtk_treeview_paint(cairo_t *cr, GdkR + GtkStateFlags state_flags; + GtkBorder border; + +- ensure_tree_view_widget(); +- gtk_widget_set_direction(gTreeViewWidget, direction); +- + /* only handle disabled and normal states, otherwise the whole background + * area will be painted differently with other states */ + state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; +@@ -1225,19 +920,19 @@ moz_gtk_treeview_paint(cairo_t *cr, GdkR + gtk_style_context_get_border(style, state_flags, &border); + xthickness = border.left; + ythickness = border.top; ++ ReleaseStyleContext(style); + +- style_tree = gtk_widget_get_style_context(gTreeViewWidget); +- gtk_style_context_save(style_tree); +- gtk_style_context_add_class(style_tree, GTK_STYLE_CLASS_VIEW); +- ++ style_tree = ClaimStyleContext(MOZ_GTK_TREEVIEW_VIEW, direction); + gtk_render_background(style_tree, cr, + rect->x + xthickness, rect->y + ythickness, + rect->width - 2 * xthickness, + rect->height - 2 * ythickness); ++ ReleaseStyleContext(style_tree); ++ ++ style = ClaimStyleContext(MOZ_GTK_SCROLLED_WINDOW, direction); + gtk_render_frame(style, cr, + rect->x, rect->y, rect->width, rect->height); + ReleaseStyleContext(style); +- gtk_style_context_restore(style_tree); + return MOZ_GTK_SUCCESS; + } + +@@ -1247,7 +942,7 @@ moz_gtk_tree_header_cell_paint(cairo_t * + gboolean isSorted, GtkTextDirection direction) + { + moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, +- gTreeHeaderCellWidget, direction); ++ GetWidget(MOZ_GTK_TREE_HEADER_CELL), direction); + return MOZ_GTK_SUCCESS; + } + +@@ -1260,18 +955,13 @@ moz_gtk_tree_header_sort_arrow_paint(cai + gdouble arrow_angle; + GtkStyleContext* style; + +- ensure_tree_header_cell_widget(); +- gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction); +- + /* hard code these values */ + arrow_rect.width = 11; + arrow_rect.height = 11; + arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; +- style = gtk_widget_get_style_context(gTreeHeaderSortArrowWidget); +- gtk_style_context_save(style); +- gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); +- ++ style = ClaimStyleContext(MOZ_GTK_TREE_HEADER_SORTARROW, direction, ++ GetStateFlagsFromGtkWidgetState(state)); + switch (arrow_type) { + case GTK_ARROW_LEFT: + arrow_angle = ARROW_LEFT; +@@ -1290,7 +980,7 @@ moz_gtk_tree_header_sort_arrow_paint(cai + gtk_render_arrow(style, cr, arrow_angle, + arrow_rect.x, arrow_rect.y, + arrow_rect.width); +- gtk_style_context_restore(style); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -1302,37 +992,28 @@ moz_gtk_treeview_expander_paint(cairo_t + GtkExpanderStyle expander_state, + GtkTextDirection direction) + { +- GtkStyleContext *style; +- GtkStateFlags state_flags; +- +- ensure_tree_view_widget(); +- gtk_widget_set_direction(gTreeViewWidget, direction); +- +- style = gtk_widget_get_style_context(gTreeViewWidget); +- gtk_style_context_save(style); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_EXPANDER); +- + /* Because the frame we get is of the entire treeview, we can't get the precise + * event state of one expander, thus rendering hover and active feedback useless. */ +- state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; ++ GtkStateFlags state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : ++ GTK_STATE_FLAG_NORMAL; + + /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering + * in gtk_render_expander() + */ + if (expander_state == GTK_EXPANDER_EXPANDED) +- state_flags = static_cast<GtkStateFlags>(state_flags|GTK_STATE_FLAG_ACTIVE); ++ state_flags = static_cast<GtkStateFlags>(state_flags|checkbox_check_state); + else +- state_flags = static_cast<GtkStateFlags>(state_flags&~(GTK_STATE_FLAG_ACTIVE)); +- +- gtk_style_context_set_state(style, state_flags); ++ state_flags = static_cast<GtkStateFlags>(state_flags&~(checkbox_check_state)); + ++ GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_TREEVIEW_EXPANDER, ++ direction, state_flags); + gtk_render_expander(style, cr, + rect->x, + rect->y, + rect->width, + rect->height); + +- gtk_style_context_restore(style); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -1349,34 +1030,37 @@ moz_gtk_combo_box_paint(cairo_t *cr, Gdk + GtkStyleContext* style; + GtkRequisition arrow_req; + +- ensure_combo_box_widgets(); ++ GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON); ++ GtkWidget* comboBoxArrow = GetWidget(MOZ_GTK_COMBOBOX_ARROW); + + /* Also sets the direction on gComboBoxButtonWidget, which is then + * inherited by the separator and arrow */ + moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, +- gComboBoxButtonWidget, direction); ++ comboBoxButton, direction); + +- calculate_button_inner_rect(gComboBoxButtonWidget, +- rect, &arrow_rect, direction); ++ calculate_button_inner_rect(comboBoxButton, rect, &arrow_rect, direction); + /* Now arrow_rect contains the inner rect ; we want to correct the width + * to what the arrow needs (see gtk_combo_box_size_allocate) */ +- gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req); ++ gtk_widget_get_preferred_size(comboBoxArrow, NULL, &arrow_req); ++ + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x += arrow_rect.width - arrow_req.width; + arrow_rect.width = arrow_req.width; + +- calculate_arrow_rect(gComboBoxArrowWidget, ++ calculate_arrow_rect(comboBoxArrow, + &arrow_rect, &real_arrow_rect, direction); + +- style = gtk_widget_get_style_context(gComboBoxArrowWidget); ++ style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ARROW); + gtk_render_arrow(style, cr, ARROW_DOWN, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width); ++ ReleaseStyleContext(style); + + /* If there is no separator in the theme, there's nothing left to do. */ +- if (!gComboBoxSeparatorWidget) ++ GtkWidget* widget = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR); ++ if (!widget) + return MOZ_GTK_SUCCESS; +- style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); ++ style = gtk_widget_get_style_context(widget); + gtk_style_context_get_style(style, + "wide-separators", &wide_separators, + "separator-width", &separator_width, +@@ -1460,15 +1144,14 @@ moz_gtk_combo_box_entry_button_paint(cai + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + +- ensure_combo_box_entry_widgets(); +- ++ GtkWidget* comboBoxEntry = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON); + moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, +- gComboBoxEntryButtonWidget, direction); ++ comboBoxEntry, direction); ++ calculate_button_inner_rect(comboBoxEntry, rect, &arrow_rect, direction); + +- calculate_button_inner_rect(gComboBoxEntryButtonWidget, +- rect, &arrow_rect, direction); + if (state_flags & GTK_STATE_FLAG_ACTIVE) { +- gtk_style_context_get_style(gtk_widget_get_style_context(gComboBoxEntryButtonWidget), ++ style = gtk_widget_get_style_context(comboBoxEntry); ++ gtk_style_context_get_style(style, + "child-displacement-x", &x_displacement, + "child-displacement-y", &y_displacement, + NULL); +@@ -1476,15 +1159,14 @@ moz_gtk_combo_box_entry_button_paint(cai + arrow_rect.y += y_displacement; + } + +- calculate_arrow_rect(gComboBoxEntryArrowWidget, ++ calculate_arrow_rect(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_ARROW), + &arrow_rect, &real_arrow_rect, direction); + +- style = gtk_widget_get_style_context(gComboBoxEntryArrowWidget); +- ++ style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_ARROW); + gtk_render_arrow(style, cr, ARROW_DOWN, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width); +- ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -1504,7 +1186,6 @@ moz_gtk_container_paint(cairo_t *cr, Gdk + } + + ReleaseStyleContext(style); +- + return MOZ_GTK_SUCCESS; + } + +@@ -1590,12 +1271,53 @@ moz_gtk_toolbar_separator_paint(cairo_t + } + + static gint +-moz_gtk_tooltip_paint(cairo_t *cr, GdkRectangle* rect, ++moz_gtk_tooltip_paint(cairo_t *cr, const GdkRectangle* aRect, + GtkTextDirection direction) + { ++ // Tooltip widget is made in GTK3 as following tree: ++ // Tooltip window ++ // Horizontal Box ++ // Icon (not supported by Firefox) ++ // Label ++ // Each element can be fully styled by CSS of GTK theme. ++ // We have to draw all elements with appropriate offset and right dimensions. ++ ++ // Tooltip drawing + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TOOLTIP, direction); +- gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); +- gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); ++ GdkRectangle rect = *aRect; ++ gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); ++ gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); ++ ++ // Horizontal Box drawing ++ // ++ // The box element has hard-coded 6px margin-* GtkWidget properties, which ++ // are added between the window dimensions and the CSS margin box of the ++ // horizontal box. The frame of the tooltip window is drawn in the ++ // 6px margin. ++ // For drawing Horizontal Box we have to inset drawing area by that 6px ++ // plus its CSS margin. ++ GtkStyleContext* boxStyle = ++ CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), style); ++ ++ rect.x += 6; ++ rect.y += 6; ++ rect.width -= 12; ++ rect.height -= 12; ++ ++ InsetByMargin(&rect, boxStyle); ++ gtk_render_background(boxStyle, cr, rect.x, rect.y, rect.width, rect.height); ++ gtk_render_frame(boxStyle, cr, rect.x, rect.y, rect.width, rect.height); ++ ++ // Label drawing ++ InsetByBorderPadding(&rect, boxStyle); ++ ++ GtkStyleContext* labelStyle = ++ CreateStyleForWidget(gtk_label_new(nullptr), boxStyle); ++ moz_gtk_draw_styled_frame(labelStyle, cr, &rect, false); ++ g_object_unref(labelStyle); ++ ++ g_object_unref(boxStyle); ++ + ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } +@@ -1721,46 +1443,35 @@ moz_gtk_progress_chunk_paint(cairo_t *cr + return MOZ_GTK_SUCCESS; + } + +-gint +-moz_gtk_get_tab_thickness(void) ++static gint ++moz_gtk_get_tab_thickness(GtkStyleContext *style) + { +- GtkBorder border; +- GtkStyleContext * style; +- +- ensure_tab_widget(); + if (!notebook_has_tab_gap) + return 0; /* tabs do not overdraw the tabpanel border with "no gap" style */ + +- style = gtk_widget_get_style_context(gTabWidget); +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); ++ GtkBorder border; + gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); +- + if (border.top < 2) + return 2; /* some themes don't set ythickness correctly */ + + return border.top; + } + +-static void +-moz_gtk_tab_prepare_style_context(GtkStyleContext *style, +- GtkTabFlags flags) +-{ +- gtk_style_context_set_state(style, ((flags & MOZ_GTK_TAB_SELECTED) == 0) ? +- GTK_STATE_FLAG_NORMAL : +- GTK_STATE_FLAG_ACTIVE); +- gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, +- (flags & MOZ_GTK_TAB_FIRST) ? +- GTK_REGION_FIRST : static_cast<GtkRegionFlags>(0)); +- gtk_style_context_add_class(style, (flags & MOZ_GTK_TAB_BOTTOM) ? +- GTK_STYLE_CLASS_BOTTOM : +- GTK_STYLE_CLASS_TOP); ++gint ++moz_gtk_get_tab_thickness(WidgetNodeType aNodeType) ++{ ++ GtkStyleContext *style = ClaimStyleContext(aNodeType); ++ int thickness = moz_gtk_get_tab_thickness(style); ++ ReleaseStyleContext(style); ++ return thickness; + } + + /* actual small tabs */ + static gint + moz_gtk_tab_paint(cairo_t *cr, GdkRectangle* rect, + GtkWidgetState* state, +- GtkTabFlags flags, GtkTextDirection direction) ++ GtkTabFlags flags, GtkTextDirection direction, ++ WidgetNodeType widget) + { + /* When the tab isn't selected, we just draw a notebook extension. + * When it is selected, we overwrite the adjacent border of the tabpanel +@@ -1772,14 +1483,10 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan + GdkRectangle focusRect; + GdkRectangle backRect; + int initial_gap = 0; ++ bool isBottomTab = (widget == MOZ_GTK_TAB_BOTTOM); + +- ensure_tab_widget(); +- gtk_widget_set_direction(gTabWidget, direction); +- +- style = gtk_widget_get_style_context(gTabWidget); +- gtk_style_context_save(style); +- moz_gtk_tab_prepare_style_context(style, flags); +- ++ style = ClaimStyleContext(widget, direction, ++ GetStateFlagsFromGtkTabFlags(flags)); + tabRect = *rect; + + if (flags & MOZ_GTK_TAB_FIRST) { +@@ -1798,8 +1505,7 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan + /* Only draw the tab */ + gtk_render_extension(style, cr, + tabRect.x, tabRect.y, tabRect.width, tabRect.height, +- (flags & MOZ_GTK_TAB_BOTTOM) ? +- GTK_POS_TOP : GTK_POS_BOTTOM ); ++ isBottomTab ? GTK_POS_TOP : GTK_POS_BOTTOM ); + } else { + /* Draw the tab and the gap + * We want the gap to be positioned exactly on the tabpanel top +@@ -1840,7 +1546,7 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan + gint gap_loffset, gap_roffset, gap_voffset, gap_height; + + /* Get height needed by the gap */ +- gap_height = moz_gtk_get_tab_thickness(); ++ gap_height = moz_gtk_get_tab_thickness(style); + + /* Extract gap_voffset from the first bits of flags */ + gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK; +@@ -1856,7 +1562,7 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan + gap_loffset = initial_gap; + } + +- if (flags & MOZ_GTK_TAB_BOTTOM) { ++ if (isBottomTab) { + /* Draw the tab on bottom */ + focusRect.y += gap_voffset; + focusRect.height -= gap_voffset; +@@ -1920,15 +1626,9 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan + gtk_render_frame(style, cr, tabRect.x, tabRect.y, tabRect.width, tabRect.height); + } + +- gtk_style_context_restore(style); +- + if (state->focused) { + /* Paint the focus ring */ + GtkBorder padding; +- +- gtk_style_context_save(style); +- moz_gtk_tab_prepare_style_context(style, flags); +- + gtk_style_context_get_padding(style, GetStateFlagsFromGtkWidgetState(state), &padding); + + focusRect.x += padding.left; +@@ -1938,10 +1638,8 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan + + gtk_render_focus(style, cr, + focusRect.x, focusRect.y, focusRect.width, focusRect.height); +- +- gtk_style_context_restore(style); + } +- ++ ReleaseStyleContext(style); + + return MOZ_GTK_SUCCESS; + } +@@ -1951,14 +1649,7 @@ static gint + moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect, + GtkTextDirection direction) + { +- GtkStyleContext* style; +- +- ensure_tab_widget(); +- gtk_widget_set_direction(gTabWidget, direction); +- +- style = gtk_widget_get_style_context(gTabWidget); +- gtk_style_context_save(style); +- ++ GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TABPANELS, direction); + gtk_render_background(style, cr, rect->x, rect->y, + rect->width, rect->height); + /* +@@ -1982,7 +1673,7 @@ moz_gtk_tabpanels_paint(cairo_t *cr, Gdk + rect->width, rect->height, + GTK_POS_TOP, rect->width - 1, rect->width); + cairo_restore(cr); +- ++ + /* right side */ + cairo_save(cr); + cairo_rectangle(cr, rect->x + rect->width / 2, rect->y, +@@ -1995,7 +1686,7 @@ moz_gtk_tabpanels_paint(cairo_t *cr, Gdk + GTK_POS_TOP, 0, 1); + cairo_restore(cr); + +- gtk_style_context_restore(style); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -2005,17 +1696,12 @@ moz_gtk_tab_scroll_arrow_paint(cairo_t * + GtkArrowType arrow_type, + GtkTextDirection direction) + { +- GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + gdouble arrow_angle; + gint arrow_size = MIN(rect->width, rect->height); + gint x = rect->x + (rect->width - arrow_size) / 2; + gint y = rect->y + (rect->height - arrow_size) / 2; + +- ensure_tab_widget(); +- +- style = gtk_widget_get_style_context(gTabWidget); +- gtk_style_context_save(style); + if (direction == GTK_TEXT_DIR_RTL) { + arrow_type = (arrow_type == GTK_ARROW_LEFT) ? + GTK_ARROW_RIGHT : GTK_ARROW_LEFT; +@@ -2035,12 +1721,12 @@ moz_gtk_tab_scroll_arrow_paint(cairo_t * + break; + } + if (arrow_type != GTK_ARROW_NONE) { +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); /* TODO TEST */ +- gtk_style_context_set_state(style, state_flags); ++ style = ClaimStyleContext(MOZ_GTK_TAB_SCROLLARROW, direction, ++ GetStateFlagsFromGtkWidgetState(state)); + gtk_render_arrow(style, cr, arrow_angle, + x, y, arrow_size); ++ ReleaseStyleContext(style); + } +- gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; + } + +@@ -2092,22 +1778,25 @@ static gint + moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect, + GtkTextDirection direction) + { ++ GtkWidgetState defaultState = { 0 }; ++ moz_gtk_menu_item_paint(MOZ_GTK_MENUSEPARATOR, cr, rect, ++ &defaultState, direction); ++ ++ if (gtk_get_minor_version() >= 20) ++ return MOZ_GTK_SUCCESS; ++ + GtkStyleContext* style; + gboolean wide_separators; + gint separator_height; +- guint border_width; + gint x, y, w; + GtkBorder padding; + +- border_width = +- gtk_container_get_border_width(GTK_CONTAINER( +- GetWidget(MOZ_GTK_MENUSEPARATOR))); + style = ClaimStyleContext(MOZ_GTK_MENUSEPARATOR, direction); + gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); + +- x = rect->x + border_width; +- y = rect->y + border_width; +- w = rect->width - border_width * 2; ++ x = rect->x; ++ y = rect->y; ++ w = rect->width; + + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SEPARATOR); +@@ -2144,37 +1833,32 @@ moz_gtk_menu_item_paint(WidgetNodeType w + { + gint x, y, w, h; + +- if (state->inHover && !state->disabled) { +- guint border_width = +- gtk_container_get_border_width(GTK_CONTAINER(GetWidget(widget))); +- GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); +- GtkStyleContext* style = +- ClaimStyleContext(widget, direction, state_flags); +- +- bool pre_3_6 = gtk_check_version(3, 6, 0) != nullptr; +- if (pre_3_6) { +- // GTK+ 3.4 saves the style context and adds the menubar class to +- // menubar children, but does each of these only when drawing, not +- // during layout. +- gtk_style_context_save(style); +- if (widget == MOZ_GTK_MENUBARITEM) { +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); +- } ++ GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); ++ GtkStyleContext* style = ClaimStyleContext(widget, direction, state_flags); ++ ++ bool pre_3_6 = gtk_check_version(3, 6, 0) != nullptr; ++ if (pre_3_6) { ++ // GTK+ 3.4 saves the style context and adds the menubar class to ++ // menubar children, but does each of these only when drawing, not ++ // during layout. ++ gtk_style_context_save(style); ++ if (widget == MOZ_GTK_MENUBARITEM) { ++ gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); + } ++ } + +- x = rect->x + border_width; +- y = rect->y + border_width; +- w = rect->width - border_width * 2; +- h = rect->height - border_width * 2; ++ x = rect->x; ++ y = rect->y; ++ w = rect->width; ++ h = rect->height; + +- gtk_render_background(style, cr, x, y, w, h); +- gtk_render_frame(style, cr, x, y, w, h); ++ gtk_render_background(style, cr, x, y, w, h); ++ gtk_render_frame(style, cr, x, y, w, h); + +- if (pre_3_6) { +- gtk_style_context_restore(style); +- } +- ReleaseStyleContext(style); ++ if (pre_3_6) { ++ gtk_style_context_restore(style); + } ++ ReleaseStyleContext(style); + + return MOZ_GTK_SUCCESS; + } +@@ -2194,63 +1878,68 @@ moz_gtk_menu_arrow_paint(cairo_t *cr, Gd + return MOZ_GTK_SUCCESS; + } + +-// See gtk_real_check_menu_item_draw_indicator() for reference. ++// For reference, see gtk_check_menu_item_size_allocate() in GTK versions after ++// 3.20 and gtk_real_check_menu_item_draw_indicator() in earlier versions. + static gint +-moz_gtk_check_menu_item_paint(cairo_t *cr, GdkRectangle* rect, ++moz_gtk_check_menu_item_paint(WidgetNodeType widgetType, ++ cairo_t *cr, GdkRectangle* rect, + GtkWidgetState* state, +- gboolean checked, gboolean isradio, +- GtkTextDirection direction) ++ gboolean checked, GtkTextDirection direction) + { + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; +- GtkBorder padding; +- gint offset; + gint indicator_size, horizontal_padding; + gint x, y; + + moz_gtk_menu_item_paint(MOZ_GTK_MENUITEM, cr, rect, state, direction); + +- ensure_check_menu_item_widget(); +- gtk_widget_set_direction(gCheckMenuItemWidget, direction); +- +- style = gtk_widget_get_style_context(gCheckMenuItemWidget); +- gtk_style_context_save(style); ++ if (checked) { ++ state_flags = static_cast<GtkStateFlags>(state_flags|checkbox_check_state); ++ } + ++ bool pre_3_20 = gtk_get_minor_version() < 20; ++ gint offset; ++ style = ClaimStyleContext(widgetType, direction); + gtk_style_context_get_style(style, + "indicator-size", &indicator_size, + "horizontal-padding", &horizontal_padding, + NULL); +- +- if (isradio) { +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); ++ if (pre_3_20) { ++ GtkBorder padding; ++ gtk_style_context_get_padding(style, state_flags, &padding); ++ offset = horizontal_padding + padding.left + 2; + } else { +- gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); +- } +- +- if (checked) { +- state_flags = static_cast<GtkStateFlags>(state_flags|checkbox_check_state); ++ GdkRectangle r = { 0 }; ++ InsetByMargin(&r, style); ++ InsetByBorderPadding(&r, style); ++ offset = r.x; + } +- +- gtk_style_context_set_state(style, state_flags); +- gtk_style_context_get_padding(style, state_flags, &padding); ++ ReleaseStyleContext(style); + +- offset = gtk_container_get_border_width(GTK_CONTAINER(gCheckMenuItemWidget)) + +- padding.left + 2; ++ bool isRadio = (widgetType == MOZ_GTK_RADIOMENUITEM); ++ WidgetNodeType indicatorType = isRadio ? MOZ_GTK_RADIOMENUITEM_INDICATOR ++ : MOZ_GTK_CHECKMENUITEM_INDICATOR; ++ style = ClaimStyleContext(indicatorType, direction, state_flags); + + if (direction == GTK_TEXT_DIR_RTL) { +- x = rect->width - indicator_size - offset - horizontal_padding; ++ x = rect->width - indicator_size - offset; + } + else { +- x = rect->x + offset + horizontal_padding; ++ x = rect->x + offset; + } + y = rect->y + (rect->height - indicator_size) / 2; + +- if (isradio) { ++ if (!pre_3_20) { ++ gtk_render_background(style, cr, x, y, indicator_size, indicator_size); ++ gtk_render_frame(style, cr, x, y, indicator_size, indicator_size); ++ } ++ ++ if (isRadio) { + gtk_render_option(style, cr, x, y, indicator_size, indicator_size); + } else { + gtk_render_check(style, cr, x, y, indicator_size, indicator_size); + } +- gtk_style_context_restore(style); ++ ReleaseStyleContext(style); + + return MOZ_GTK_SUCCESS; + } +@@ -2271,6 +1960,20 @@ moz_gtk_info_bar_paint(cairo_t *cr, GdkR + } + + static void ++moz_gtk_add_style_margin(GtkStyleContext* style, ++ gint* left, gint* top, gint* right, gint* bottom) ++{ ++ GtkBorder margin; ++ ++ gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); ++ ++ *left += margin.left; ++ *right += margin.right; ++ *top += margin.top; ++ *bottom += margin.bottom; ++} ++ ++static void + moz_gtk_add_style_border(GtkStyleContext* style, + gint* left, gint* top, gint* right, gint* bottom) + { +@@ -2298,6 +2001,15 @@ moz_gtk_add_style_padding(GtkStyleContex + *bottom += padding.bottom; + } + ++static void moz_gtk_add_margin_border_padding(GtkStyleContext *style, ++ gint* left, gint* top, ++ gint* right, gint* bottom) ++{ ++ moz_gtk_add_style_margin(style, left, top, right, bottom); ++ moz_gtk_add_style_border(style, left, top, right, bottom); ++ moz_gtk_add_style_padding(style, left, top, right, bottom); ++} ++ + gint + moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, + gint* right, gint* bottom, GtkTextDirection direction, +@@ -2362,26 +2074,24 @@ moz_gtk_get_widget_border(WidgetNodeType + * assigned. + * That is why the following code is the same as for MOZ_GTK_BUTTON. + * */ +- ensure_tree_header_cell_widget(); +- *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gTreeHeaderCellWidget)); +- +- style = gtk_widget_get_style_context(gTreeHeaderCellWidget); ++ *left = *top = *right = *bottom = ++ gtk_container_get_border_width(GTK_CONTAINER( ++ GetWidget(MOZ_GTK_TREE_HEADER_CELL))); + ++ style = ClaimStyleContext(MOZ_GTK_TREE_HEADER_CELL); + moz_gtk_add_style_border(style, left, top, right, bottom); + moz_gtk_add_style_padding(style, left, top, right, bottom); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_TREE_HEADER_SORTARROW: +- ensure_tree_header_cell_widget(); +- w = gTreeHeaderSortArrowWidget; ++ w = GetWidget(MOZ_GTK_TREE_HEADER_SORTARROW); + break; + case MOZ_GTK_DROPDOWN_ENTRY: +- ensure_combo_box_entry_widgets(); +- w = gComboBoxEntryTextareaWidget; ++ w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA); + break; + case MOZ_GTK_DROPDOWN_ARROW: +- ensure_combo_box_entry_widgets(); +- w = gComboBoxEntryButtonWidget; ++ w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON); + break; + case MOZ_GTK_DROPDOWN: + { +@@ -2393,32 +2103,33 @@ moz_gtk_get_widget_border(WidgetNodeType + GtkRequisition arrow_req; + GtkBorder border; + +- ensure_combo_box_widgets(); +- +- *left = *top = *right = *bottom = +- gtk_container_get_border_width(GTK_CONTAINER(gComboBoxButtonWidget)); +- +- style = gtk_widget_get_style_context(gComboBoxButtonWidget); +- ++ *left = *top = *right = *bottom = ++ gtk_container_get_border_width(GTK_CONTAINER( ++ GetWidget(MOZ_GTK_COMBOBOX_BUTTON))); ++ style = ClaimStyleContext(MOZ_GTK_COMBOBOX_BUTTON); + moz_gtk_add_style_padding(style, left, top, right, bottom); + moz_gtk_add_style_border(style, left, top, right, bottom); ++ ReleaseStyleContext(style); + + /* If there is no separator, don't try to count its width. */ + separator_width = 0; +- if (gComboBoxSeparatorWidget) { +- style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); ++ GtkWidget* comboBoxSeparator = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR); ++ if (comboBoxSeparator) { ++ style = gtk_widget_get_style_context(comboBoxSeparator); + gtk_style_context_get_style(style, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (!wide_separators) { +- gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); ++ gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, ++ &border); + separator_width = border.left; + } + } + +- gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req); ++ gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ARROW), ++ NULL, &arrow_req); + + if (direction == GTK_TEXT_DIR_RTL) + *left += separator_width + arrow_req.width; +@@ -2428,8 +2139,7 @@ moz_gtk_get_widget_border(WidgetNodeType + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_TABPANELS: +- ensure_tab_widget(); +- w = gTabWidget; ++ w = GetWidget(MOZ_GTK_TABPANELS); + break; + case MOZ_GTK_PROGRESSBAR: + w = GetWidget(MOZ_GTK_PROGRESSBAR); +@@ -2440,12 +2150,8 @@ moz_gtk_get_widget_border(WidgetNodeType + w = GetWidget(MOZ_GTK_SPINBUTTON); + break; + case MOZ_GTK_SCALE_HORIZONTAL: +- ensure_scale_widget(); +- w = gHScaleWidget; +- break; + case MOZ_GTK_SCALE_VERTICAL: +- ensure_scale_widget(); +- w = gVScaleWidget; ++ w = GetWidget(widget); + break; + case MOZ_GTK_FRAME: + w = GetWidget(MOZ_GTK_FRAME); +@@ -2471,17 +2177,18 @@ moz_gtk_get_widget_border(WidgetNodeType + case MOZ_GTK_CHECKMENUITEM: + case MOZ_GTK_RADIOMENUITEM: + { +- if (widget == MOZ_GTK_MENUBARITEM || widget == MOZ_GTK_MENUITEM) { +- // Bug 1274143 for MOZ_GTK_MENUBARITEM +- w = GetWidget(MOZ_GTK_MENUITEM); ++ // Bug 1274143 for MOZ_GTK_MENUBARITEM ++ WidgetNodeType type = ++ widget == MOZ_GTK_MENUBARITEM ? MOZ_GTK_MENUITEM : widget; ++ style = ClaimStyleContext(type); ++ ++ if (gtk_get_minor_version() < 20) { ++ moz_gtk_add_style_padding(style, left, top, right, bottom); + } else { +- ensure_check_menu_item_widget(); +- w = gCheckMenuItemWidget; ++ moz_gtk_add_margin_border_padding(style, ++ left, top, right, bottom); + } +- +- *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w)); +- moz_gtk_add_style_padding(gtk_widget_get_style_context(w), +- left, top, right, bottom); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_INFO_BAR: +@@ -2490,11 +2197,86 @@ moz_gtk_get_widget_border(WidgetNodeType + case MOZ_GTK_TOOLTIP: + { + style = ClaimStyleContext(MOZ_GTK_TOOLTIP); +- moz_gtk_add_style_border(style, left, top, right, bottom); +- moz_gtk_add_style_padding(style, left, top, right, bottom); ++ // In GTK 3 there are 6 pixels of additional margin around the box. ++ // See details there: ++ // https://github.com/GNOME/gtk/blob/5ea69a136bd7e4970b3a800390e20314665aaed2/gtk/ui/gtktooltipwindow.ui#L11 ++ *left = *right = *top = *bottom = 6; ++ ++ // We also need to add margin/padding/borders from Tooltip content. ++ // Tooltip contains horizontal box, where icon and label is put. ++ // We ignore icon as long as we don't have support for it. ++ GtkStyleContext* boxStyle = ++ CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), ++ style); ++ moz_gtk_add_margin_border_padding(boxStyle, ++ left, top, right, bottom); ++ ++ GtkStyleContext* labelStyle = ++ CreateStyleForWidget(gtk_label_new(nullptr), boxStyle); ++ moz_gtk_add_margin_border_padding(labelStyle, ++ left, top, right, bottom); ++ ++ g_object_unref(labelStyle); ++ g_object_unref(boxStyle); ++ + ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } ++ case MOZ_GTK_SCROLLBAR_VERTICAL: ++ case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL: ++ { ++ if (gtk_check_version(3,20,0) == nullptr) { ++ style = ClaimStyleContext(widget); ++ moz_gtk_add_margin_border_padding(style, left, top, right, bottom); ++ ReleaseStyleContext(style); ++ if (widget == MOZ_GTK_SCROLLBAR_VERTICAL) { ++ style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL); ++ moz_gtk_add_margin_border_padding(style, left, top, right, bottom); ++ ReleaseStyleContext(style); ++ } ++ } else { ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); ++ /* Top and bottom border for whole vertical scrollbar, top and bottom ++ * border for horizontal track - to correctly position thumb element */ ++ *top = *bottom = metrics.trough_border; ++ } ++ return MOZ_GTK_SUCCESS; ++ } ++ break; ++ ++ case MOZ_GTK_SCROLLBAR_HORIZONTAL: ++ case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL: ++ { ++ if (gtk_check_version(3,20,0) == nullptr) { ++ style = ClaimStyleContext(widget); ++ moz_gtk_add_margin_border_padding(style, left, top, right, bottom); ++ ReleaseStyleContext(style); ++ if (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) { ++ style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL); ++ moz_gtk_add_margin_border_padding(style, left, top, right, bottom); ++ ReleaseStyleContext(style); ++ } ++ } else { ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); ++ *left = *right = metrics.trough_border; ++ } ++ return MOZ_GTK_SUCCESS; ++ } ++ break; ++ ++ case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: ++ case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: ++ { ++ if (gtk_check_version(3,20,0) == nullptr) { ++ style = ClaimStyleContext(widget); ++ moz_gtk_add_margin_border_padding(style, left, top, right, bottom); ++ ReleaseStyleContext(style); ++ } ++ return MOZ_GTK_SUCCESS; ++ } ++ break; + /* These widgets have no borders, since they are not containers. */ + case MOZ_GTK_CHECKBUTTON_LABEL: + case MOZ_GTK_RADIOBUTTON_LABEL: +@@ -2503,10 +2285,6 @@ moz_gtk_get_widget_border(WidgetNodeType + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + case MOZ_GTK_SCROLLBAR_BUTTON: +- case MOZ_GTK_SCROLLBAR_HORIZONTAL: +- case MOZ_GTK_SCROLLBAR_VERTICAL: +- case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: +- case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + case MOZ_GTK_SCALE_THUMB_HORIZONTAL: + case MOZ_GTK_SCALE_THUMB_VERTICAL: + case MOZ_GTK_GRIPPER: +@@ -2540,35 +2318,48 @@ moz_gtk_get_widget_border(WidgetNodeType + + gint + moz_gtk_get_tab_border(gint* left, gint* top, gint* right, gint* bottom, +- GtkTextDirection direction, GtkTabFlags flags) ++ GtkTextDirection direction, GtkTabFlags flags, ++ WidgetNodeType widget) + { +- GtkStyleContext* style; +- int tab_curvature; +- +- ensure_tab_widget(); +- +- style = gtk_widget_get_style_context(gTabWidget); +- gtk_style_context_save(style); +- moz_gtk_tab_prepare_style_context(style, flags); ++ GtkStyleContext* style = ClaimStyleContext(widget, direction, ++ GetStateFlagsFromGtkTabFlags(flags)); + + *left = *top = *right = *bottom = 0; + moz_gtk_add_style_padding(style, left, top, right, bottom); + +- gtk_style_context_get_style(style, "tab-curvature", &tab_curvature, NULL); +- *left += tab_curvature; +- *right += tab_curvature; ++ // Gtk >= 3.20 does not use those styles ++ if (gtk_check_version(3, 20, 0) != nullptr) { ++ int tab_curvature; + +- if (flags & MOZ_GTK_TAB_FIRST) { +- int initial_gap; +- gtk_style_context_get_style(style, "initial-gap", &initial_gap, NULL); +- if (direction == GTK_TEXT_DIR_RTL) +- *right += initial_gap; +- else +- *left += initial_gap; +- } ++ gtk_style_context_get_style(style, "tab-curvature", &tab_curvature, NULL); ++ *left += tab_curvature; ++ *right += tab_curvature; ++ ++ if (flags & MOZ_GTK_TAB_FIRST) { ++ int initial_gap = 0; ++ gtk_style_context_get_style(style, "initial-gap", &initial_gap, NULL); ++ if (direction == GTK_TEXT_DIR_RTL) ++ *right += initial_gap; ++ else ++ *left += initial_gap; ++ } ++ } else { ++ GtkBorder margin; + +- gtk_style_context_restore(style); ++ gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); ++ *left += margin.left; ++ *right += margin.right; + ++ if (flags & MOZ_GTK_TAB_FIRST) { ++ ReleaseStyleContext(style); ++ style = ClaimStyleContext(MOZ_GTK_NOTEBOOK_HEADER, direction); ++ gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); ++ *left += margin.left; ++ *right += margin.right; ++ } ++ } ++ ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -2581,9 +2372,9 @@ moz_gtk_get_combo_box_entry_button_size( + * as well as the minimum arrow size and its padding + * */ + GtkRequisition requisition; +- ensure_combo_box_entry_widgets(); + +- gtk_widget_get_preferred_size(gComboBoxEntryButtonWidget, NULL, &requisition); ++ gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON), ++ NULL, &requisition); + *width = requisition.width; + *height = requisition.height; + +@@ -2595,10 +2386,11 @@ moz_gtk_get_tab_scroll_arrow_size(gint* + { + gint arrow_size; + +- ensure_tab_widget(); +- gtk_style_context_get_style(gtk_widget_get_style_context(gTabWidget), ++ GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_TABPANELS); ++ gtk_style_context_get_style(style, + "scroll-arrow-hlength", &arrow_size, + NULL); ++ ReleaseStyleContext(style); + + *height = *width = arrow_size; + +@@ -2611,8 +2403,7 @@ moz_gtk_get_arrow_size(WidgetNodeType wi + GtkWidget* widget; + switch (widgetType) { + case MOZ_GTK_DROPDOWN: +- ensure_combo_box_widgets(); +- widget = gComboBoxArrowWidget; ++ widget = GetWidget(MOZ_GTK_COMBOBOX_ARROW); + break; + default: + widget = GetWidget(MOZ_GTK_BUTTON_ARROW); +@@ -2659,11 +2450,9 @@ moz_gtk_get_expander_size(gint* size) + gint + moz_gtk_get_treeview_expander_size(gint* size) + { +- ensure_tree_view_widget(); +- gtk_style_context_get_style(gtk_widget_get_style_context(gTreeViewWidget), +- "expander-size", size, +- NULL); +- ++ GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TREEVIEW); ++ gtk_style_context_get_style(style, "expander-size", size, NULL); ++ ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; + } + +@@ -2674,14 +2463,7 @@ moz_gtk_get_menu_separator_height(gint * + gboolean wide_separators; + gint separator_height; + GtkBorder padding; +- GtkStyleContext* style; +- guint border_width; +- +- border_width = +- gtk_container_get_border_width(GTK_CONTAINER( +- GetWidget(MOZ_GTK_MENUSEPARATOR))); +- +- style = ClaimStyleContext(MOZ_GTK_MENUSEPARATOR); ++ GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_MENUSEPARATOR); + gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); + + gtk_style_context_save(style); +@@ -2695,7 +2477,7 @@ moz_gtk_get_menu_separator_height(gint * + gtk_style_context_restore(style); + ReleaseStyleContext(style); + +- *size = padding.top + padding.bottom + border_width*2; ++ *size = padding.top + padding.bottom; + *size += (wide_separators) ? separator_height : 1; + + return MOZ_GTK_SUCCESS; +@@ -2726,34 +2508,60 @@ void + moz_gtk_get_scale_metrics(GtkOrientation orient, gint* scale_width, + gint* scale_height) + { +- gint thumb_length, thumb_height, trough_border; +- GtkWidget* widget = orient == GTK_ORIENTATION_HORIZONTAL ? +- gHScaleWidget : gVScaleWidget; +- moz_gtk_get_scalethumb_metrics(orient, &thumb_length, &thumb_height); +- gtk_style_context_get_style(gtk_widget_get_style_context(widget), +- "trough-border", &trough_border, NULL); +- +- if (orient == GTK_ORIENTATION_HORIZONTAL) { +- *scale_width = thumb_length + trough_border * 2; +- *scale_height = thumb_height + trough_border * 2; ++ WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ? ++ MOZ_GTK_SCALE_HORIZONTAL : ++ MOZ_GTK_SCALE_VERTICAL; ++ ++ if (gtk_check_version(3, 20, 0) != nullptr) { ++ gint thumb_length, thumb_height, trough_border; ++ moz_gtk_get_scalethumb_metrics(orient, &thumb_length, &thumb_height); ++ ++ GtkStyleContext* style = ClaimStyleContext(widget); ++ gtk_style_context_get_style(style, "trough-border", &trough_border, NULL); ++ ++ if (orient == GTK_ORIENTATION_HORIZONTAL) { ++ *scale_width = thumb_length + trough_border * 2; ++ *scale_height = thumb_height + trough_border * 2; ++ } else { ++ *scale_width = thumb_height + trough_border * 2; ++ *scale_height = thumb_length + trough_border * 2; ++ } ++ ReleaseStyleContext(style); + } else { +- *scale_width = thumb_height + trough_border * 2; +- *scale_height = thumb_length + trough_border * 2; ++ GtkStyleContext* style = ClaimStyleContext(widget); ++ gtk_style_context_get(style, gtk_style_context_get_state(style), ++ "min-width", scale_width, ++ "min-height", scale_height, ++ nullptr); ++ ReleaseStyleContext(style); + } + } + + gint + moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height) + { +- GtkWidget* widget; + +- ensure_scale_widget(); +- widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); +- +- gtk_style_context_get_style(gtk_widget_get_style_context(widget), +- "slider_length", thumb_length, +- "slider_width", thumb_height, +- NULL); ++ if (gtk_check_version(3, 20, 0) != nullptr) { ++ WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ? ++ MOZ_GTK_SCALE_HORIZONTAL: ++ MOZ_GTK_SCALE_VERTICAL; ++ GtkStyleContext* style = ClaimStyleContext(widget); ++ gtk_style_context_get_style(style, ++ "slider_length", thumb_length, ++ "slider_width", thumb_height, ++ NULL); ++ ReleaseStyleContext(style); ++ } else { ++ WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ? ++ MOZ_GTK_SCALE_THUMB_HORIZONTAL: ++ MOZ_GTK_SCALE_THUMB_VERTICAL; ++ GtkStyleContext* style = ClaimStyleContext(widget); ++ gtk_style_context_get(style, gtk_style_context_get_state(style), ++ "min-width", thumb_length, ++ "min-height", thumb_height, ++ nullptr); ++ ReleaseStyleContext(style); ++ } + + return MOZ_GTK_SUCCESS; + } +@@ -2761,6 +2569,9 @@ moz_gtk_get_scalethumb_metrics(GtkOrient + gint + moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) + { ++ // For Gtk >= 3.20 scrollbar metrics are ignored ++ MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr); ++ + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_VERTICAL); + gtk_style_context_get_style(style, + "slider_width", &metrics->slider_width, +@@ -2771,41 +2582,9 @@ moz_gtk_get_scrollbar_metrics(MozGtkScro + nullptr); + ReleaseStyleContext(style); + +- if(!gtk_check_version(3, 20, 0)) { +- style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_THUMB_VERTICAL); +- gtk_style_context_get(style, gtk_style_context_get_state(style), +- "min-height", &metrics->min_slider_size, nullptr); +- ReleaseStyleContext(style); +- } +- + return MOZ_GTK_SUCCESS; + } + +-gboolean +-moz_gtk_images_in_menus() +-{ +- gboolean result; +- GtkSettings* settings; +- +- ensure_image_menu_item_widget(); +- settings = gtk_widget_get_settings(gImageMenuItemWidget); +- +- g_object_get(settings, "gtk-menu-images", &result, NULL); +- return result; +-} +- +-gboolean +-moz_gtk_images_in_buttons() +-{ +- gboolean result; +- GtkSettings* settings; +- +- settings = gtk_widget_get_settings(GetWidget(MOZ_GTK_BUTTON)); +- +- g_object_get(settings, "gtk-button-images", &result, NULL); +- return result; +-} +- + /* cairo_t *cr argument has to be a system-cairo. */ + gint + moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr, +@@ -2846,10 +2625,25 @@ moz_gtk_widget_paint(WidgetNodeType widg + break; + case MOZ_GTK_SCROLLBAR_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_VERTICAL: +- return moz_gtk_scrollbar_trough_paint(widget, cr, rect, +- state, +- (GtkScrollbarTrackFlags) flags, +- direction); ++ if (gtk_check_version(3,20,0) == nullptr) { ++ return moz_gtk_scrollbar_paint(widget, cr, rect, state, direction); ++ } else { ++ WidgetNodeType trough_widget = (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) ? ++ MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL : MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL; ++ return moz_gtk_scrollbar_trough_paint(trough_widget, cr, rect, ++ state, ++ (GtkScrollbarTrackFlags) flags, ++ direction); ++ } ++ break; ++ case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL: ++ case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL: ++ if (gtk_check_version(3,20,0) == nullptr) { ++ return moz_gtk_scrollbar_trough_paint(widget, cr, rect, ++ state, ++ (GtkScrollbarTrackFlags) flags, ++ direction); ++ } + break; + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: +@@ -2876,9 +2670,13 @@ moz_gtk_widget_paint(WidgetNodeType widg + state, direction); + break; + case MOZ_GTK_SPINBUTTON_ENTRY: +- // TODO - use MOZ_GTK_SPINBUTTON_ENTRY style directly +- return moz_gtk_entry_paint(cr, rect, state, +- GetWidget(MOZ_GTK_SPINBUTTON), direction); ++ { ++ GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SPINBUTTON_ENTRY, ++ direction, GetStateFlagsFromGtkWidgetState(state)); ++ gint ret = moz_gtk_entry_paint(cr, rect, state, style); ++ ReleaseStyleContext(style); ++ return ret; ++ } + break; + case MOZ_GTK_GRIPPER: + return moz_gtk_gripper_paint(cr, rect, state, +@@ -2903,9 +2701,13 @@ moz_gtk_widget_paint(WidgetNodeType widg + (GtkExpanderStyle) flags, direction); + break; + case MOZ_GTK_ENTRY: +- return moz_gtk_entry_paint(cr, rect, state, GetWidget(MOZ_GTK_ENTRY), +- direction); +- break; ++ { ++ GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_ENTRY, ++ direction, GetStateFlagsFromGtkWidgetState(state)); ++ gint ret = moz_gtk_entry_paint(cr, rect, state, style); ++ ReleaseStyleContext(style); ++ return ret; ++ } + case MOZ_GTK_TEXT_VIEW: + return moz_gtk_text_view_paint(cr, rect, state, direction); + break; +@@ -2917,9 +2719,13 @@ moz_gtk_widget_paint(WidgetNodeType widg + state, flags, direction); + break; + case MOZ_GTK_DROPDOWN_ENTRY: +- ensure_combo_box_entry_widgets(); +- return moz_gtk_entry_paint(cr, rect, state, +- gComboBoxEntryTextareaWidget, direction); ++ { ++ GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA, ++ direction, GetStateFlagsFromGtkWidgetState(state)); ++ gint ret = moz_gtk_entry_paint(cr, rect, state, style); ++ ReleaseStyleContext(style); ++ return ret; ++ } + break; + case MOZ_GTK_CHECKBUTTON_CONTAINER: + case MOZ_GTK_RADIOBUTTON_CONTAINER: +@@ -2957,9 +2763,10 @@ moz_gtk_widget_paint(WidgetNodeType widg + return moz_gtk_progress_chunk_paint(cr, rect, + direction, widget); + break; +- case MOZ_GTK_TAB: ++ case MOZ_GTK_TAB_TOP: ++ case MOZ_GTK_TAB_BOTTOM: + return moz_gtk_tab_paint(cr, rect, state, +- (GtkTabFlags) flags, direction); ++ (GtkTabFlags) flags, direction, widget); + break; + case MOZ_GTK_TABPANELS: + return moz_gtk_tabpanels_paint(cr, rect, direction); +@@ -2992,10 +2799,8 @@ moz_gtk_widget_paint(WidgetNodeType widg + break; + case MOZ_GTK_CHECKMENUITEM: + case MOZ_GTK_RADIOMENUITEM: +- return moz_gtk_check_menu_item_paint(cr, rect, state, +- (gboolean) flags, +- (widget == MOZ_GTK_RADIOMENUITEM), +- direction); ++ return moz_gtk_check_menu_item_paint(widget, cr, rect, state, ++ (gboolean) flags, direction); + break; + case MOZ_GTK_SPLITTER_HORIZONTAL: + return moz_gtk_vpaned_paint(cr, rect, state); +@@ -3043,31 +2848,6 @@ moz_gtk_shutdown() + /* This will destroy all of our widgets */ + ResetWidgetCache(); + +- /* TODO - replace it with appropriate widget */ +- if (gTreeHeaderSortArrowWidget) +- gtk_widget_destroy(gTreeHeaderSortArrowWidget); +- +- gProtoLayout = NULL; +- gHScaleWidget = NULL; +- gVScaleWidget = NULL; +- gComboBoxWidget = NULL; +- gComboBoxButtonWidget = NULL; +- gComboBoxSeparatorWidget = NULL; +- gComboBoxArrowWidget = NULL; +- gComboBoxEntryWidget = NULL; +- gComboBoxEntryButtonWidget = NULL; +- gComboBoxEntryArrowWidget = NULL; +- gComboBoxEntryTextareaWidget = NULL; +- gTabWidget = NULL; +- gImageMenuItemWidget = NULL; +- gCheckMenuItemWidget = NULL; +- gTreeViewWidget = NULL; +- gMiddleTreeViewColumn = NULL; +- gTreeHeaderCellWidget = NULL; +- gTreeHeaderSortArrowWidget = NULL; +- gHPanedWidget = NULL; +- gVPanedWidget = NULL; +- + is_initialized = FALSE; + + return MOZ_GTK_SUCCESS; +diff -up firefox-50.0/widget/gtk/gtkdrawing.h.widget-rebase firefox-50.0/widget/gtk/gtkdrawing.h +--- firefox-50.0/widget/gtk/gtkdrawing.h.widget-rebase 2016-10-31 21:15:38.000000000 +0100 ++++ firefox-50.0/widget/gtk/gtkdrawing.h 2016-11-24 11:57:52.034064779 +0100 +@@ -61,8 +61,6 @@ typedef enum { + typedef enum { + /* first eight bits are used to pass a margin */ + MOZ_GTK_TAB_MARGIN_MASK = 0xFF, +- /* bottom tabs */ +- MOZ_GTK_TAB_BOTTOM = 1 << 8, + /* the first tab in the group */ + MOZ_GTK_TAB_FIRST = 1 << 9, + /* the selected tab */ +@@ -128,6 +126,11 @@ typedef enum { + /* Paints a GtkScale. */ + MOZ_GTK_SCALE_HORIZONTAL, + MOZ_GTK_SCALE_VERTICAL, ++ /* Paints a GtkScale trough. */ ++ MOZ_GTK_SCALE_CONTENTS_HORIZONTAL, ++ MOZ_GTK_SCALE_CONTENTS_VERTICAL, ++ MOZ_GTK_SCALE_TROUGH_HORIZONTAL, ++ MOZ_GTK_SCALE_TROUGH_VERTICAL, + /* Paints a GtkScale thumb. */ + MOZ_GTK_SCALE_THUMB_HORIZONTAL, + MOZ_GTK_SCALE_THUMB_VERTICAL, +@@ -173,14 +176,22 @@ typedef enum { + MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE, + /* Paints a progress chunk of a vertical indeterminated GtkProgressBar. */ + MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE, ++ /* Used as root style of whole GtkNotebook widget */ ++ MOZ_GTK_NOTEBOOK, ++ /* Used as root style of active GtkNotebook area which contains tabs and arrows. */ ++ MOZ_GTK_NOTEBOOK_HEADER, + /* Paints a tab of a GtkNotebook. flags is a GtkTabFlags, defined above. */ +- MOZ_GTK_TAB, ++ MOZ_GTK_TAB_TOP, ++ /* Paints a tab of a GtkNotebook. flags is a GtkTabFlags, defined above. */ ++ MOZ_GTK_TAB_BOTTOM, + /* Paints the background and border of a GtkNotebook. */ + MOZ_GTK_TABPANELS, + /* Paints a GtkArrow for a GtkNotebook. flags is a GtkArrowType. */ + MOZ_GTK_TAB_SCROLLARROW, +- /* Paints the background and border of a GtkTreeView */ ++ /* Paints the expander and border of a GtkTreeView */ + MOZ_GTK_TREEVIEW, ++ /* Paints the border of a GtkTreeView */ ++ MOZ_GTK_TREEVIEW_VIEW, + /* Paints treeheader cells */ + MOZ_GTK_TREE_HEADER_CELL, + /* Paints sort arrows in treeheader cells */ +@@ -199,19 +210,44 @@ typedef enum { + MOZ_GTK_MENUBARITEM, + /* Paints items of popup menus. */ + MOZ_GTK_MENUITEM, ++ /* Paints a menuitem with check indicator, or the gets the style context for ++ a menuitem that contains a checkbox. */ + MOZ_GTK_CHECKMENUITEM, ++ /* Gets the style context for a checkbox in a check menuitem. */ ++ MOZ_GTK_CHECKMENUITEM_INDICATOR, + MOZ_GTK_RADIOMENUITEM, ++ MOZ_GTK_RADIOMENUITEM_INDICATOR, + MOZ_GTK_MENUSEPARATOR, +- /* Paints a GtkVPaned separator */ ++ /* GtkVPaned base class */ + MOZ_GTK_SPLITTER_HORIZONTAL, +- /* Paints a GtkHPaned separator */ ++ /* GtkHPaned base class */ + MOZ_GTK_SPLITTER_VERTICAL, ++ /* Paints a GtkVPaned separator */ ++ MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL, ++ /* Paints a GtkHPaned separator */ ++ MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL, + /* Paints the background of a window, dialog or page. */ + MOZ_GTK_WINDOW, + /* Window container for all widgets */ + MOZ_GTK_WINDOW_CONTAINER, + /* Paints a GtkInfoBar, for notifications. */ + MOZ_GTK_INFO_BAR, ++ /* Used for widget tree construction. */ ++ MOZ_GTK_COMBOBOX, ++ /* Paints a GtkComboBox button widget. */ ++ MOZ_GTK_COMBOBOX_BUTTON, ++ /* Paints a GtkComboBox arrow widget. */ ++ MOZ_GTK_COMBOBOX_ARROW, ++ /* Paints a GtkComboBox separator widget. */ ++ MOZ_GTK_COMBOBOX_SEPARATOR, ++ /* Used for widget tree construction. */ ++ MOZ_GTK_COMBOBOX_ENTRY, ++ /* Paints a GtkComboBox entry widget. */ ++ MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA, ++ /* Paints a GtkComboBox entry button widget. */ ++ MOZ_GTK_COMBOBOX_ENTRY_BUTTON, ++ /* Paints a GtkComboBox entry arrow widget. */ ++ MOZ_GTK_COMBOBOX_ENTRY_ARROW, + /* Used for scrolled window shell. */ + MOZ_GTK_SCROLLED_WINDOW, + +@@ -300,12 +336,14 @@ gint moz_gtk_get_widget_border(WidgetNod + * top/bottom: [OUT] the tab's top/bottom border + * direction: the text direction for the widget + * flags: tab-dependant flags; see the GtkTabFlags definition. ++ * widget: tab widget + * + * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise + */ + gint + moz_gtk_get_tab_border(gint* left, gint* top, gint* right, gint* bottom, +- GtkTextDirection direction, GtkTabFlags flags); ++ GtkTextDirection direction, GtkTabFlags flags, ++ WidgetNodeType widget); + + /** + * Get the desired size of a GtkCheckButton +@@ -481,19 +519,8 @@ GtkWidget* moz_gtk_get_scrollbar_widget( + /** + * Get the YTHICKNESS of a tab (notebook extension). + */ +-gint moz_gtk_get_tab_thickness(void); +- +-/** +- * Get a boolean which indicates whether or not to use images in menus. +- * If TRUE, use images in menus. +- */ +-gboolean moz_gtk_images_in_menus(void); +- +-/** +- * Get a boolean which indicates whether or not to use images in buttons. +- * If TRUE, use images in buttons. +- */ +-gboolean moz_gtk_images_in_buttons(void); ++gint ++moz_gtk_get_tab_thickness(WidgetNodeType aNodeType); + + /** + * Get a boolean which indicates whether the theme draws scrollbar buttons. +@@ -501,6 +528,13 @@ gboolean moz_gtk_images_in_buttons(void) + */ + gboolean moz_gtk_has_scrollbar_buttons(void); + ++/** ++ * Get minimum widget size as sum of margin, padding, border and min-width, ++ * min-height. ++ */ ++void moz_gtk_get_widget_min_size(WidgetNodeType aGtkWidgetType, int* width, ++ int* height); ++ + #if (MOZ_WIDGET_GTK == 2) + #ifdef __cplusplus + } +diff -up firefox-50.0/widget/gtk/mozgtk/mozgtk.c.widget-rebase firefox-50.0/widget/gtk/mozgtk/mozgtk.c +--- firefox-50.0/widget/gtk/mozgtk/mozgtk.c.widget-rebase 2016-10-31 21:15:38.000000000 +0100 ++++ firefox-50.0/widget/gtk/mozgtk/mozgtk.c 2016-11-24 11:57:52.034064779 +0100 +@@ -9,6 +9,7 @@ STUB(gdk_atom_name) + STUB(gdk_beep) + STUB(gdk_cairo_create) + STUB(gdk_color_free) ++STUB(gdk_color_parse) + STUB(gdk_cursor_new_for_display) + STUB(gdk_cursor_new_from_name) + STUB(gdk_cursor_new_from_pixbuf) +@@ -244,7 +245,6 @@ STUB(gtk_icon_theme_get_icon_sizes) + STUB(gtk_icon_theme_lookup_by_gicon) + STUB(gtk_icon_theme_lookup_icon) + STUB(gtk_image_get_type) +-STUB(gtk_image_menu_item_new) + STUB(gtk_image_new) + STUB(gtk_image_new_from_stock) + STUB(gtk_image_set_from_pixbuf) +@@ -530,6 +530,7 @@ STUB(gtk_get_minor_version) + STUB(gtk_menu_button_new) + STUB(gtk_offscreen_window_new) + STUB(gtk_paned_new) ++STUB(gtk_radio_menu_item_new) + STUB(gtk_render_activity) + STUB(gtk_render_arrow) + STUB(gtk_render_background) +@@ -561,6 +562,7 @@ STUB(gtk_style_context_get_state) + STUB(gtk_style_context_get_style) + STUB(gtk_style_context_has_class) + STUB(gtk_style_context_invalidate) ++STUB(gtk_style_context_list_classes) + STUB(gtk_style_context_new) + STUB(gtk_style_context_remove_class) + STUB(gtk_style_context_remove_region) +@@ -575,10 +577,10 @@ STUB(gtk_tree_view_column_get_button) + STUB(gtk_widget_get_preferred_size) + STUB(gtk_widget_get_state_flags) + STUB(gtk_widget_get_style_context) +-STUB(gtk_widget_path_append_for_widget) + STUB(gtk_widget_path_append_type) + STUB(gtk_widget_path_copy) + STUB(gtk_widget_path_free) ++STUB(gtk_widget_path_iter_add_class) + STUB(gtk_widget_path_new) + STUB(gtk_widget_path_unref) + STUB(gtk_widget_set_visual) +diff -up firefox-50.0/widget/gtk/nsLookAndFeel.cpp.widget-rebase firefox-50.0/widget/gtk/nsLookAndFeel.cpp +--- firefox-50.0/widget/gtk/nsLookAndFeel.cpp.widget-rebase 2016-11-24 12:00:04.563704500 +0100 ++++ firefox-50.0/widget/gtk/nsLookAndFeel.cpp 2016-11-24 12:00:47.316588277 +0100 +@@ -786,10 +786,10 @@ nsLookAndFeel::GetIntImpl(IntID aID, int + aResult = NS_STYLE_TEXT_DECORATION_STYLE_WAVY; + break; + case eIntID_ImagesInMenus: +- aResult = moz_gtk_images_in_menus(); ++ aResult = 0; + break; + case eIntID_ImagesInButtons: +- aResult = moz_gtk_images_in_buttons(); ++ aResult = 0; + break; + case eIntID_MenuBarDrag: + aResult = sMenuSupportsDrag; +diff -up firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp.widget-rebase firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp +--- firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp.widget-rebase 2016-10-31 21:15:38.000000000 +0100 ++++ firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp 2016-11-24 11:57:52.034064779 +0100 +@@ -149,19 +149,15 @@ static void SetWidgetStateSafe(uint8_t * + aSafeVector[key >> 3] |= (1 << (key & 7)); + } + +-static GtkTextDirection GetTextDirection(nsIFrame* aFrame) ++/* static */ GtkTextDirection ++nsNativeThemeGTK::GetTextDirection(nsIFrame* aFrame) + { +- if (!aFrame) +- return GTK_TEXT_DIR_NONE; +- +- switch (aFrame->StyleVisibility()->mDirection) { +- case NS_STYLE_DIRECTION_RTL: +- return GTK_TEXT_DIR_RTL; +- case NS_STYLE_DIRECTION_LTR: +- return GTK_TEXT_DIR_LTR; +- } +- +- return GTK_TEXT_DIR_NONE; ++ // IsFrameRTL() treats vertical-rl modes as right-to-left (in addition to ++ // horizontal text with direction=RTL), rather than just considering the ++ // text direction. GtkTextDirection does not have distinct values for ++ // vertical writing modes, but considering the block flow direction is ++ // important for resizers and scrollbar elements, at least. ++ return IsFrameRTL(aFrame) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR; + } + + // Returns positive for negative margins (otherwise 0). +@@ -193,205 +189,199 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + gint* aWidgetFlags) + { + if (aState) { +- if (!aFrame) { +- // reset the entire struct to zero +- memset(aState, 0, sizeof(GtkWidgetState)); +- } else { ++ // 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)) { + +- // 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)) { +- +- nsIAtom* atom = nullptr; +- if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { +- if (aWidgetType == NS_THEME_CHECKBOX_LABEL || +- aWidgetType == NS_THEME_RADIO_LABEL) { +- // Adjust stateFrame so GetContentState finds the correct state. +- stateFrame = aFrame = aFrame->GetParent()->GetParent(); +- } else { +- // GetContentState knows to look one frame up for radio/checkbox +- // widgets, so don't adjust stateFrame here. +- aFrame = aFrame->GetParent(); ++ nsIAtom* atom = nullptr; ++ if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { ++ if (aWidgetType == NS_THEME_CHECKBOX_LABEL || ++ aWidgetType == NS_THEME_RADIO_LABEL) { ++ // Adjust stateFrame so GetContentState finds the correct state. ++ stateFrame = aFrame = aFrame->GetParent()->GetParent(); ++ } else { ++ // GetContentState knows to look one frame up for radio/checkbox ++ // widgets, so don't adjust stateFrame here. ++ aFrame = aFrame->GetParent(); ++ } ++ if (aWidgetFlags) { ++ if (!atom) { ++ atom = (aWidgetType == NS_THEME_CHECKBOX || ++ aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked ++ : nsGkAtoms::selected; + } +- if (aWidgetFlags) { +- if (!atom) { +- atom = (aWidgetType == NS_THEME_CHECKBOX || +- aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked +- : nsGkAtoms::selected; +- } +- *aWidgetFlags = CheckBooleanAttr(aFrame, atom); ++ *aWidgetFlags = CheckBooleanAttr(aFrame, atom); ++ } ++ } else { ++ if (aWidgetFlags) { ++ nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent())); ++ *aWidgetFlags = 0; ++ if (inputElt) { ++ bool isHTMLChecked; ++ inputElt->GetChecked(&isHTMLChecked); ++ if (isHTMLChecked) ++ *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; + } +- } else { +- if (aWidgetFlags) { +- nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent())); +- *aWidgetFlags = 0; +- if (inputElt) { +- bool isHTMLChecked; +- inputElt->GetChecked(&isHTMLChecked); +- if (isHTMLChecked) +- *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; +- } + +- if (GetIndeterminate(aFrame)) +- *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT; +- } ++ if (GetIndeterminate(aFrame)) ++ *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) { +- // The state of an arrow comes from its parent. +- stateFrame = aFrame = aFrame->GetParent(); +- } +- +- EventStates eventState = GetContentState(stateFrame, aWidgetType); +- +- aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame); +- aState->active = eventState.HasState(NS_EVENT_STATE_ACTIVE); +- aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS); +- aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER); +- aState->isDefault = IsDefaultButton(aFrame); +- aState->canDefault = FALSE; // XXX fix me +- aState->depressed = FALSE; +- +- if (aWidgetType == NS_THEME_FOCUS_OUTLINE) { +- aState->disabled = FALSE; +- aState->active = FALSE; +- aState->inHover = FALSE; +- aState->isDefault = FALSE; +- aState->canDefault = FALSE; +- +- 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) { +- aState->active &= aState->inHover; + } +- +- if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { +- // 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) { +- aState->focused = IsFocused(aFrame); +- } else if (aWidgetType == NS_THEME_RADIO || +- aWidgetType == NS_THEME_CHECKBOX) { +- // In XUL, checkboxes and radios shouldn't have focus rings, their labels do +- aState->focused = FALSE; ++ } 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) { ++ // The state of an arrow comes from its parent. ++ stateFrame = aFrame = aFrame->GetParent(); ++ } ++ ++ EventStates eventState = GetContentState(stateFrame, aWidgetType); ++ ++ aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame); ++ aState->active = eventState.HasState(NS_EVENT_STATE_ACTIVE); ++ aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS); ++ aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER); ++ aState->isDefault = IsDefaultButton(aFrame); ++ aState->canDefault = FALSE; // XXX fix me ++ aState->depressed = FALSE; ++ ++ if (aWidgetType == NS_THEME_FOCUS_OUTLINE) { ++ aState->disabled = FALSE; ++ aState->active = FALSE; ++ aState->inHover = FALSE; ++ aState->isDefault = FALSE; ++ aState->canDefault = FALSE; ++ ++ 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) { ++ aState->active &= aState->inHover; ++ } ++ ++ if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { ++ // 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) { ++ aState->focused = IsFocused(aFrame); ++ } else if (aWidgetType == NS_THEME_RADIO || ++ aWidgetType == NS_THEME_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) { ++ // 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(); ++ ++ aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0); ++ aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100); ++ ++ if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) { ++ aState->active = TRUE; ++ // Set hover state to emulate Gtk style of active scrollbar thumb ++ aState->inHover = TRUE; + } ++ } + +- if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL || +- aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) { +- // 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(); +- +- aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0); +- aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100); +- +- if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) { +- aState->active = TRUE; +- // Set hover state to emulate Gtk style of active scrollbar thumb +- aState->inHover = TRUE; +- } ++ if (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || ++ aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || ++ aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || ++ aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) { ++ // 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); ++ int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); ++ if (ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType)) { ++ aState->disabled = true; + } + +- if (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || +- aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) { +- // 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); +- int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); +- if (ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType)) { +- aState->disabled = true; +- } ++ // In order to simulate native GTK scrollbar click behavior, ++ // we set the active attribute on the element to true if it's ++ // pressed with any mouse button. ++ // This allows us to show that it's active without setting :active ++ else if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) ++ aState->active = true; ++ ++ if (aWidgetFlags) { ++ *aWidgetFlags = GetScrollbarButtonType(aFrame); ++ if (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP < 2) ++ *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL; ++ } ++ } + +- // In order to simulate native GTK scrollbar click behavior, +- // we set the active attribute on the element to true if it's +- // pressed with any mouse button. +- // This allows us to show that it's active without setting :active +- else if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) +- aState->active = true; +- +- if (aWidgetFlags) { +- *aWidgetFlags = GetScrollbarButtonType(aFrame); +- if (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP < 2) +- *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL; +- } ++ // menu item state is determined by the attribute "_moz-menuactive", ++ // and not by the mouse hovering (accessibility). as a special case, ++ // 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) { ++ bool isTopLevel = false; ++ nsMenuFrame *menuFrame = do_QueryFrame(aFrame); ++ if (menuFrame) { ++ isTopLevel = menuFrame->IsOnMenuBar(); + } + +- // menu item state is determined by the attribute "_moz-menuactive", +- // and not by the mouse hovering (accessibility). as a special case, +- // 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) { +- bool isTopLevel = false; +- nsMenuFrame *menuFrame = do_QueryFrame(aFrame); +- if (menuFrame) { +- isTopLevel = menuFrame->IsOnMenuBar(); +- } ++ if (isTopLevel) { ++ aState->inHover = menuFrame->IsOpen(); ++ } else { ++ aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); ++ } + +- if (isTopLevel) { +- aState->inHover = menuFrame->IsOpen(); +- } else { +- aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); +- } ++ aState->active = FALSE; + +- aState->active = FALSE; +- +- if (aWidgetType == NS_THEME_CHECKMENUITEM || +- aWidgetType == NS_THEME_RADIOMENUITEM) { +- *aWidgetFlags = 0; +- if (aFrame && aFrame->GetContent()) { +- *aWidgetFlags = aFrame->GetContent()-> +- AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, +- nsGkAtoms::_true, eIgnoreCase); +- } ++ if (aWidgetType == NS_THEME_CHECKMENUITEM || ++ aWidgetType == NS_THEME_RADIOMENUITEM) { ++ *aWidgetFlags = 0; ++ if (aFrame && aFrame->GetContent()) { ++ *aWidgetFlags = aFrame->GetContent()-> ++ AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, ++ nsGkAtoms::_true, eIgnoreCase); + } + } ++ } + +- // 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) { +- bool menuOpen = IsOpenButton(aFrame); +- aState->depressed = IsCheckedButton(aFrame) || menuOpen; +- // we must not highlight buttons with open drop down menus on hover. +- aState->inHover = aState->inHover && !menuOpen; +- } +- +- // 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) { +- *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused); +- } ++ // 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) { ++ bool menuOpen = IsOpenButton(aFrame); ++ aState->depressed = IsCheckedButton(aFrame) || menuOpen; ++ // we must not highlight buttons with open drop down menus on hover. ++ aState->inHover = aState->inHover && !menuOpen; ++ } ++ ++ // 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) { ++ *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused); + } + } + } +@@ -435,6 +425,20 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + else + *aWidgetFlags = 0; + break; ++ case NS_THEME_SCROLLBARTRACK_HORIZONTAL: ++ if (gtk_check_version(3,20,0) == nullptr) { ++ aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL; ++ } else { ++ return false; ++ } ++ break; ++ case NS_THEME_SCROLLBARTRACK_VERTICAL: ++ if (gtk_check_version(3,20,0) == nullptr) { ++ aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL; ++ } else { ++ return false; ++ } ++ break; + case NS_THEME_SCROLLBARTHUMB_VERTICAL: + aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL; + break; +@@ -644,17 +648,17 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + break; + case NS_THEME_TAB: + { ++ if (IsBottomTab(aFrame)) { ++ aGtkWidgetType = MOZ_GTK_TAB_BOTTOM; ++ } else { ++ aGtkWidgetType = MOZ_GTK_TAB_TOP; ++ } ++ + if (aWidgetFlags) { + /* First bits will be used to store max(0,-bmargin) where bmargin + * is the bottom margin of the tab in pixels (resp. top margin, + * for bottom tabs). */ +- if (IsBottomTab(aFrame)) { +- *aWidgetFlags = MOZ_GTK_TAB_BOTTOM; +- } else { +- *aWidgetFlags = 0; +- } +- +- *aWidgetFlags |= GetTabMarginPixels(aFrame); ++ *aWidgetFlags = GetTabMarginPixels(aFrame); + + if (IsSelectedTab(aFrame)) + *aWidgetFlags |= MOZ_GTK_TAB_SELECTED; +@@ -662,8 +666,6 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u + if (IsFirstTab(aFrame)) + *aWidgetFlags |= MOZ_GTK_TAB_FIRST; + } +- +- aGtkWidgetType = MOZ_GTK_TAB; + } + break; + case NS_THEME_SPLITTER: +@@ -862,12 +864,19 @@ DrawThemeWithCairo(gfxContext* aContext, + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(transform, mat); + ++ nsIntSize clipSize((aDrawSize.width + aScaleFactor - 1) / aScaleFactor, ++ (aDrawSize.height + aScaleFactor - 1) / aScaleFactor); ++ + #ifndef MOZ_TREE_CAIRO + // Directly use the Cairo draw target to render the widget if using system Cairo everywhere. + BorrowedCairoContext borrowCairo(aDrawTarget); + if (borrowCairo.mCairo) { + cairo_set_matrix(borrowCairo.mCairo, &mat); + ++ cairo_new_path(borrowCairo.mCairo); ++ cairo_rectangle(borrowCairo.mCairo, 0, 0, clipSize.width, clipSize.height); ++ cairo_clip(borrowCairo.mCairo); ++ + moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect, &aState, aFlags, aDirection); + + borrowCairo.Finish(); +@@ -908,6 +917,10 @@ DrawThemeWithCairo(gfxContext* aContext, + + cairo_set_matrix(cr, &mat); + ++ cairo_new_path(cr); ++ cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height); ++ cairo_clip(cr); ++ + moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection); + + cairo_destroy(cr); +@@ -942,6 +955,10 @@ DrawThemeWithCairo(gfxContext* aContext, + + cairo_set_matrix(cr, &mat); + ++ cairo_new_path(cr); ++ cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height); ++ cairo_clip(cr); ++ + moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection); + + cairo_destroy(cr); +@@ -1062,7 +1079,8 @@ nsNativeThemeGTK::GetExtraSizeForWidget( + if (!IsSelectedTab(aFrame)) + return false; + +- gint gap_height = moz_gtk_get_tab_thickness(); ++ gint gap_height = moz_gtk_get_tab_thickness(IsBottomTab(aFrame) ? ++ MOZ_GTK_TAB_BOTTOM : MOZ_GTK_TAB_TOP); + if (!gap_height) + return false; + +@@ -1097,14 +1115,7 @@ nsNativeThemeGTK::DrawWidgetBackground(n + { + GtkWidgetState state; + WidgetNodeType gtkWidgetType; +- // For resizer drawing, we want IsFrameRTL, which treats vertical-rl modes +- // as right-to-left (in addition to horizontal text with direction=RTL), +- // rather than just considering the text direction. +- // This will make resizers on vertically-oriented elements render properly. +- GtkTextDirection direction = +- aWidgetType == NS_THEME_RESIZER +- ? (IsFrameRTL(aFrame) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR) +- : GetTextDirection(aFrame); ++ GtkTextDirection direction = GetTextDirection(aFrame); + gint flags; + if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state, + &flags)) +@@ -1240,6 +1251,21 @@ nsNativeThemeGTK::DrawWidgetBackground(n + return NS_OK; + } + ++WidgetNodeType ++nsNativeThemeGTK::NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame) ++{ ++ WidgetNodeType gtkWidgetType; ++ gint unusedFlags; ++ ++ if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, ++ &unusedFlags)) ++ { ++ MOZ_ASSERT_UNREACHABLE("Unknown native widget to gtk widget mapping"); ++ return MOZ_GTK_WINDOW; ++ } ++ return gtkWidgetType; ++} ++ + NS_IMETHODIMP + nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame, + uint8_t aWidgetType, nsIntMargin* aResult) +@@ -1247,24 +1273,6 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi + GtkTextDirection direction = GetTextDirection(aFrame); + aResult->top = aResult->left = aResult->right = aResult->bottom = 0; + switch (aWidgetType) { +- case NS_THEME_SCROLLBAR_VERTICAL: +- case NS_THEME_SCROLLBARTRACK_HORIZONTAL: +- { +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); +- /* Top and bottom border for whole vertical scrollbar, top and bottom +- * border for horizontal track - to correctly position thumb element */ +- aResult->top = aResult->bottom = metrics.trough_border; +- } +- break; +- case NS_THEME_SCROLLBAR_HORIZONTAL: +- case NS_THEME_SCROLLBARTRACK_VERTICAL: +- { +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); +- aResult->left = aResult->right = metrics.trough_border; +- } +- break; + case NS_THEME_TOOLBOX: + // gtk has no toolbox equivalent. So, although we map toolbox to + // gtk's 'toolbar' for purposes of painting the widget background, +@@ -1289,7 +1297,7 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi + + moz_gtk_get_tab_border(&aResult->left, &aResult->top, + &aResult->right, &aResult->bottom, direction, +- (GtkTabFlags)flags); ++ (GtkTabFlags)flags, gtkWidgetType); + } + break; + case NS_THEME_MENUITEM: +@@ -1304,8 +1312,9 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi + default: + { + WidgetNodeType gtkWidgetType; ++ gint unusedFlags; + if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, +- nullptr)) { ++ &unusedFlags)) { + moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top, + &aResult->right, &aResult->bottom, direction, + IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML)); +@@ -1418,22 +1427,33 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + case NS_THEME_SCROLLBARBUTTON_UP: + case NS_THEME_SCROLLBARBUTTON_DOWN: + { +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); ++ if (gtk_check_version(3,20,0) == nullptr) { ++ moz_gtk_get_widget_min_size(MOZ_GTK_SCROLLBAR_BUTTON, ++ &(aResult->width), &(aResult->height)); ++ } else { ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); ++ ++ aResult->width = metrics.slider_width; ++ aResult->height = metrics.stepper_size; ++ } + +- aResult->width = metrics.slider_width; +- aResult->height = metrics.stepper_size; + *aIsOverridable = false; + } + break; + case NS_THEME_SCROLLBARBUTTON_LEFT: + case NS_THEME_SCROLLBARBUTTON_RIGHT: + { +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); ++ if (gtk_check_version(3,20,0) == nullptr) { ++ moz_gtk_get_widget_min_size(MOZ_GTK_SCROLLBAR_BUTTON, ++ &(aResult->width), &(aResult->height)); ++ } else { ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); + +- aResult->width = metrics.stepper_size; +- aResult->height = metrics.slider_width; ++ aResult->width = metrics.stepper_size; ++ aResult->height = metrics.slider_width; ++ } + *aIsOverridable = false; + } + break; +@@ -1460,39 +1480,65 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n + * the thumb isn't a direct child of the scrollbar, unlike the buttons + * or track. So add a minimum size to the track as well to prevent a + * 0-width scrollbar. */ +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); ++ if (gtk_check_version(3,20,0) == nullptr) { ++ // Thumb min dimensions to start with ++ WidgetNodeType thumbType = aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ? ++ MOZ_GTK_SCROLLBAR_THUMB_VERTICAL : MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL; ++ moz_gtk_get_widget_min_size(thumbType, &(aResult->width), &(aResult->height)); ++ ++ // Add scrollbar's borders ++ nsIntMargin border; ++ nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(), ++ aFrame, aWidgetType, &border); ++ aResult->width += border.left + border.right; ++ aResult->height += border.top + border.bottom; ++ ++ // Add track's borders ++ uint8_t trackType = aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ? ++ NS_THEME_SCROLLBARTRACK_VERTICAL : NS_THEME_SCROLLBARTRACK_HORIZONTAL; ++ nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(), ++ aFrame, trackType, &border); ++ aResult->width += border.left + border.right; ++ aResult->height += border.top + border.bottom; ++ } else { ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); + +- // Require room for the slider in the track if we don't have buttons. +- bool hasScrollbarButtons = moz_gtk_has_scrollbar_buttons(); ++ // Require room for the slider in the track if we don't have buttons. ++ bool hasScrollbarButtons = moz_gtk_has_scrollbar_buttons(); + +- if (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL) { +- aResult->width = metrics.slider_width + 2 * metrics.trough_border; +- if (!hasScrollbarButtons) +- aResult->height = metrics.min_slider_size + 2 * metrics.trough_border; +- } else { +- aResult->height = metrics.slider_width + 2 * metrics.trough_border; +- if (!hasScrollbarButtons) +- aResult->width = metrics.min_slider_size + 2 * metrics.trough_border; ++ if (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL) { ++ aResult->width = metrics.slider_width + 2 * metrics.trough_border; ++ if (!hasScrollbarButtons) ++ aResult->height = metrics.min_slider_size + 2 * metrics.trough_border; ++ } else { ++ aResult->height = metrics.slider_width + 2 * metrics.trough_border; ++ if (!hasScrollbarButtons) ++ aResult->width = metrics.min_slider_size + 2 * metrics.trough_border; ++ } ++ *aIsOverridable = false; + } + +- *aIsOverridable = false; + } + break; + case NS_THEME_SCROLLBARTHUMB_VERTICAL: + case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: + { +- MozGtkScrollbarMetrics metrics; +- moz_gtk_get_scrollbar_metrics(&metrics); +- +- if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL) { +- aResult->width = metrics.slider_width; +- aResult->height = metrics.min_slider_size; ++ if (gtk_check_version(3,20,0) == nullptr) { ++ moz_gtk_get_widget_min_size(NativeThemeToGtkTheme(aWidgetType, aFrame), ++ &(aResult->width), &(aResult->height)); + } else { +- aResult->height = metrics.slider_width; +- aResult->width = metrics.min_slider_size; +- } ++ MozGtkScrollbarMetrics metrics; ++ moz_gtk_get_scrollbar_metrics(&metrics); + ++ if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL) { ++ aResult->width = metrics.slider_width; ++ aResult->height = metrics.min_slider_size; ++ } else { ++ aResult->height = metrics.slider_width; ++ aResult->width = metrics.min_slider_size; ++ } ++ } + *aIsOverridable = false; + } + break; +diff -up firefox-50.0/widget/gtk/nsNativeThemeGTK.h.widget-rebase firefox-50.0/widget/gtk/nsNativeThemeGTK.h +--- firefox-50.0/widget/gtk/nsNativeThemeGTK.h.widget-rebase 2016-07-25 22:22:07.000000000 +0200 ++++ firefox-50.0/widget/gtk/nsNativeThemeGTK.h 2016-11-24 11:57:52.035064777 +0100 +@@ -74,6 +74,7 @@ protected: + virtual ~nsNativeThemeGTK(); + + private: ++ GtkTextDirection GetTextDirection(nsIFrame* aFrame); + gint GetTabMarginPixels(nsIFrame* aFrame); + bool GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame, + WidgetNodeType& aGtkWidgetType, +@@ -82,6 +83,7 @@ private: + nsIntMargin* aExtra); + + void RefreshWidgetWindow(nsIFrame* aFrame); ++ WidgetNodeType NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame); + + uint8_t mDisabledWidgetTypes[32]; + uint8_t mSafeWidgetStates[1024]; // 256 widgets * 32 bits per widget +diff -up firefox-50.0/widget/gtk/WidgetStyleCache.cpp.widget-rebase firefox-50.0/widget/gtk/WidgetStyleCache.cpp +--- firefox-50.0/widget/gtk/WidgetStyleCache.cpp.widget-rebase 2016-10-31 21:15:38.000000000 +0100 ++++ firefox-50.0/widget/gtk/WidgetStyleCache.cpp 2016-11-24 11:57:52.035064777 +0100 +@@ -25,6 +25,8 @@ static bool sStyleContextNeedsRestore; + static GtkStyleContext* sCurrentStyleContext; + #endif + static GtkStyleContext* ++GetWidgetRootStyle(WidgetNodeType aNodeType); ++static GtkStyleContext* + GetCssNodeStyleInternal(WidgetNodeType aNodeType); + + static GtkWidget* +@@ -93,14 +95,6 @@ CreateMenuPopupWidget() + } + + static GtkWidget* +-CreateMenuItemWidget(WidgetNodeType aShellType) +-{ +- GtkWidget* widget = gtk_menu_item_new(); +- gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(aShellType)), widget); +- return widget; +-} +- +-static GtkWidget* + CreateProgressWidget() + { + GtkWidget* widget = gtk_progress_bar_new(); +@@ -211,6 +205,235 @@ CreateEntryWidget() + } + + static GtkWidget* ++CreateComboBoxWidget() ++{ ++ GtkWidget* widget = gtk_combo_box_new(); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++typedef struct ++{ ++ GType type; ++ GtkWidget** widget; ++} GtkInnerWidgetInfo; ++ ++static void ++GetInnerWidget(GtkWidget* widget, gpointer client_data) ++{ ++ auto info = static_cast<GtkInnerWidgetInfo*>(client_data); ++ ++ if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) { ++ *info->widget = widget; ++ } ++ ++ gtk_widget_realize(widget); ++} ++ ++static GtkWidget* ++CreateComboBoxButtonWidget() ++{ ++ GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX); ++ GtkWidget* comboBoxButton = nullptr; ++ ++ /* Get its inner Button */ ++ GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON, ++ &comboBoxButton }; ++ gtk_container_forall(GTK_CONTAINER(comboBox), ++ GetInnerWidget, &info); ++ ++ if (!comboBoxButton) { ++ /* Shouldn't be reached with current internal gtk implementation; we ++ * use a generic toggle button as last resort fallback to avoid ++ * crashing. */ ++ comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON); ++ } else { ++ /* We need to have pointers to the inner widgets (button, separator, arrow) ++ * of the ComboBox to get the correct rendering from theme engines which ++ * special cases their look. Since the inner layout can change, we ask GTK ++ * to NULL our pointers when they are about to become invalid because the ++ * corresponding widgets don't exist anymore. It's the role of ++ * g_object_add_weak_pointer(). ++ * Note that if we don't find the inner widgets (which shouldn't happen), we ++ * fallback to use generic "non-inner" widgets, and they don't need that kind ++ * of weak pointer since they are explicit children of gProtoLayout and as ++ * such GTK holds a strong reference to them. */ ++ g_object_add_weak_pointer(G_OBJECT(comboBoxButton), ++ reinterpret_cast<gpointer *>(sWidgetStorage) + ++ MOZ_GTK_COMBOBOX_BUTTON); ++ } ++ ++ return comboBoxButton; ++} ++ ++static GtkWidget* ++CreateComboBoxArrowWidget() ++{ ++ GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON); ++ GtkWidget* comboBoxArrow = nullptr; ++ ++ /* Get the widgets inside the Button */ ++ GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(comboBoxButton)); ++ if (GTK_IS_BOX(buttonChild)) { ++ /* appears-as-list = FALSE, cell-view = TRUE; the button ++ * contains an hbox. This hbox is there because the ComboBox ++ * needs to place a cell renderer, a separator, and an arrow in ++ * the button when appears-as-list is FALSE. */ ++ GtkInnerWidgetInfo info = { GTK_TYPE_ARROW, ++ &comboBoxArrow }; ++ gtk_container_forall(GTK_CONTAINER(buttonChild), ++ GetInnerWidget, &info); ++ } else if (GTK_IS_ARROW(buttonChild)) { ++ /* appears-as-list = TRUE, or cell-view = FALSE; ++ * the button only contains an arrow */ ++ comboBoxArrow = buttonChild; ++ gtk_widget_realize(comboBoxArrow); ++ } ++ ++ if (!comboBoxArrow) { ++ /* Shouldn't be reached with current internal gtk implementation; ++ * we gButtonArrowWidget as last resort fallback to avoid ++ * crashing. */ ++ comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW); ++ } else { ++ g_object_add_weak_pointer(G_OBJECT(comboBoxArrow), ++ reinterpret_cast<gpointer *>(sWidgetStorage) + ++ MOZ_GTK_COMBOBOX_ARROW); ++ } ++ ++ return comboBoxArrow; ++} ++ ++static GtkWidget* ++CreateComboBoxSeparatorWidget() ++{ ++ // Ensure to search for separator only once as it can fail ++ // TODO - it won't initialize after ResetWidgetCache() call ++ static bool isMissingSeparator = false; ++ if (isMissingSeparator) ++ return nullptr; ++ ++ /* Get the widgets inside the Button */ ++ GtkWidget* comboBoxSeparator = nullptr; ++ GtkWidget* buttonChild = ++ gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_BUTTON))); ++ if (GTK_IS_BOX(buttonChild)) { ++ /* appears-as-list = FALSE, cell-view = TRUE; the button ++ * contains an hbox. This hbox is there because the ComboBox ++ * needs to place a cell renderer, a separator, and an arrow in ++ * the button when appears-as-list is FALSE. */ ++ GtkInnerWidgetInfo info = { GTK_TYPE_SEPARATOR, ++ &comboBoxSeparator }; ++ gtk_container_forall(GTK_CONTAINER(buttonChild), ++ GetInnerWidget, &info); ++ } ++ ++ if (comboBoxSeparator) { ++ g_object_add_weak_pointer(G_OBJECT(comboBoxSeparator), ++ reinterpret_cast<gpointer *>(sWidgetStorage) + ++ MOZ_GTK_COMBOBOX_SEPARATOR); ++ } else { ++ /* comboBoxSeparator may be NULL ++ * when "appears-as-list" = TRUE or "cell-view" = FALSE; ++ * if there is no separator, then we just won't paint it. */ ++ isMissingSeparator = true; ++ } ++ ++ return comboBoxSeparator; ++} ++ ++static GtkWidget* ++CreateComboBoxEntryWidget() ++{ ++ GtkWidget* widget = gtk_combo_box_new_with_entry(); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++static GtkWidget* ++CreateComboBoxEntryTextareaWidget() ++{ ++ GtkWidget* comboBoxTextarea = nullptr; ++ ++ /* Get its inner Entry and Button */ ++ GtkInnerWidgetInfo info = { GTK_TYPE_ENTRY, ++ &comboBoxTextarea }; ++ gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)), ++ GetInnerWidget, &info); ++ ++ if (!comboBoxTextarea) { ++ comboBoxTextarea = GetWidget(MOZ_GTK_ENTRY); ++ } else { ++ g_object_add_weak_pointer(G_OBJECT(comboBoxTextarea), ++ reinterpret_cast<gpointer *>(sWidgetStorage) + ++ MOZ_GTK_COMBOBOX_ENTRY); ++ } ++ ++ return comboBoxTextarea; ++} ++ ++static GtkWidget* ++CreateComboBoxEntryButtonWidget() ++{ ++ GtkWidget* comboBoxButton = nullptr; ++ ++ /* Get its inner Entry and Button */ ++ GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON, ++ &comboBoxButton }; ++ gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)), ++ GetInnerWidget, &info); ++ ++ if (!comboBoxButton) { ++ comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON); ++ } else { ++ g_object_add_weak_pointer(G_OBJECT(comboBoxButton), ++ reinterpret_cast<gpointer *>(sWidgetStorage) + ++ MOZ_GTK_COMBOBOX_ENTRY_BUTTON); ++ } ++ ++ return comboBoxButton; ++} ++ ++static GtkWidget* ++CreateComboBoxEntryArrowWidget() ++{ ++ GtkWidget* comboBoxArrow = nullptr; ++ ++ /* Get the Arrow inside the Button */ ++ GtkWidget* buttonChild = ++ gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON))); ++ ++ if (GTK_IS_BOX(buttonChild)) { ++ /* appears-as-list = FALSE, cell-view = TRUE; the button ++ * contains an hbox. This hbox is there because the ComboBox ++ * needs to place a cell renderer, a separator, and an arrow in ++ * the button when appears-as-list is FALSE. */ ++ GtkInnerWidgetInfo info = { GTK_TYPE_ARROW, ++ &comboBoxArrow }; ++ gtk_container_forall(GTK_CONTAINER(buttonChild), ++ GetInnerWidget, &info); ++ } else if (GTK_IS_ARROW(buttonChild)) { ++ /* appears-as-list = TRUE, or cell-view = FALSE; ++ * the button only contains an arrow */ ++ comboBoxArrow = buttonChild; ++ gtk_widget_realize(comboBoxArrow); ++ } ++ ++ if (!comboBoxArrow) { ++ /* Shouldn't be reached with current internal gtk implementation; ++ * we gButtonArrowWidget as last resort fallback to avoid ++ * crashing. */ ++ comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW); ++ } else { ++ g_object_add_weak_pointer(G_OBJECT(comboBoxArrow), ++ reinterpret_cast<gpointer *>(sWidgetStorage) + ++ MOZ_GTK_COMBOBOX_ENTRY_ARROW); ++ } ++ ++ return comboBoxArrow; ++} ++ ++static GtkWidget* + CreateScrolledWindowWidget() + { + GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr); +@@ -237,6 +460,95 @@ CreateMenuSeparatorWidget() + return widget; + } + ++static GtkWidget* ++CreateTreeViewWidget() ++{ ++ GtkWidget* widget = gtk_tree_view_new(); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++static GtkWidget* ++CreateTreeHeaderCellWidget() ++{ ++ /* ++ * Some GTK engines paint the first and last cell ++ * of a TreeView header with a highlight. ++ * Since we do not know where our widget will be relative ++ * to the other buttons in the TreeView header, we must ++ * paint it as a button that is between two others, ++ * thus ensuring it is neither the first or last button ++ * in the header. ++ * GTK doesn't give us a way to do this explicitly, ++ * so we must paint with a button that is between two ++ * others. ++ */ ++ GtkTreeViewColumn* firstTreeViewColumn; ++ GtkTreeViewColumn* middleTreeViewColumn; ++ GtkTreeViewColumn* lastTreeViewColumn; ++ ++ GtkWidget *treeView = GetWidget(MOZ_GTK_TREEVIEW); ++ ++ /* Create and append our three columns */ ++ firstTreeViewColumn = gtk_tree_view_column_new(); ++ gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); ++ gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), ++ firstTreeViewColumn); ++ ++ middleTreeViewColumn = gtk_tree_view_column_new(); ++ gtk_tree_view_column_set_title(middleTreeViewColumn, "M"); ++ gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), ++ middleTreeViewColumn); ++ ++ lastTreeViewColumn = gtk_tree_view_column_new(); ++ gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); ++ gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), ++ lastTreeViewColumn); ++ ++ /* Use the middle column's header for our button */ ++ return gtk_tree_view_column_get_button(middleTreeViewColumn); ++} ++ ++static GtkWidget* ++CreateTreeHeaderSortArrowWidget() ++{ ++ /* TODO, but it can't be NULL */ ++ GtkWidget* widget = gtk_button_new(); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++static GtkWidget* ++CreateHPanedWidget() ++{ ++ GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++static GtkWidget* ++CreateVPanedWidget() ++{ ++ GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_VERTICAL); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++static GtkWidget* ++CreateScaleWidget(GtkOrientation aOrientation) ++{ ++ GtkWidget* widget = gtk_scale_new(aOrientation, nullptr); ++ AddToWindowContainer(widget); ++ return widget; ++} ++ ++static GtkWidget* ++CreateNotebookWidget() ++{ ++ GtkWidget* widget = gtk_notebook_new(); ++ AddToWindowContainer(widget); ++ return widget; ++} + + static GtkWidget* + CreateWidget(WidgetNodeType aWidgetType) +@@ -262,10 +574,6 @@ CreateWidget(WidgetNodeType aWidgetType) + return CreateMenuBarWidget(); + case MOZ_GTK_MENUPOPUP: + return CreateMenuPopupWidget(); +- case MOZ_GTK_MENUBARITEM: +- return CreateMenuItemWidget(MOZ_GTK_MENUBAR); +- case MOZ_GTK_MENUITEM: +- return CreateMenuItemWidget(MOZ_GTK_MENUPOPUP); + case MOZ_GTK_MENUSEPARATOR: + return CreateMenuSeparatorWidget(); + case MOZ_GTK_EXPANDER: +@@ -294,6 +602,38 @@ CreateWidget(WidgetNodeType aWidgetType) + return CreateScrolledWindowWidget(); + case MOZ_GTK_TEXT_VIEW: + return CreateTextViewWidget(); ++ case MOZ_GTK_TREEVIEW: ++ return CreateTreeViewWidget(); ++ case MOZ_GTK_TREE_HEADER_CELL: ++ return CreateTreeHeaderCellWidget(); ++ case MOZ_GTK_TREE_HEADER_SORTARROW: ++ return CreateTreeHeaderSortArrowWidget(); ++ case MOZ_GTK_SPLITTER_HORIZONTAL: ++ return CreateHPanedWidget(); ++ case MOZ_GTK_SPLITTER_VERTICAL: ++ return CreateVPanedWidget(); ++ case MOZ_GTK_SCALE_HORIZONTAL: ++ return CreateScaleWidget(GTK_ORIENTATION_HORIZONTAL); ++ case MOZ_GTK_SCALE_VERTICAL: ++ return CreateScaleWidget(GTK_ORIENTATION_VERTICAL); ++ case MOZ_GTK_NOTEBOOK: ++ return CreateNotebookWidget(); ++ case MOZ_GTK_COMBOBOX: ++ return CreateComboBoxWidget(); ++ case MOZ_GTK_COMBOBOX_BUTTON: ++ return CreateComboBoxButtonWidget(); ++ case MOZ_GTK_COMBOBOX_ARROW: ++ return CreateComboBoxArrowWidget(); ++ case MOZ_GTK_COMBOBOX_SEPARATOR: ++ return CreateComboBoxSeparatorWidget(); ++ case MOZ_GTK_COMBOBOX_ENTRY: ++ return CreateComboBoxEntryWidget(); ++ case MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA: ++ return CreateComboBoxEntryTextareaWidget(); ++ case MOZ_GTK_COMBOBOX_ENTRY_BUTTON: ++ return CreateComboBoxEntryButtonWidget(); ++ case MOZ_GTK_COMBOBOX_ENTRY_ARROW: ++ return CreateComboBoxEntryArrowWidget(); + default: + /* Not implemented */ + return nullptr; +@@ -314,27 +654,53 @@ GetWidget(WidgetNodeType aWidgetType) + GtkStyleContext* + CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle) + { +- GtkWidgetPath* path = aParentStyle ? +- gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle)) : +- gtk_widget_path_new(); +- +- // Work around https://bugzilla.gnome.org/show_bug.cgi?id=767312 +- // which exists in GTK+ 3.20. +- gtk_widget_get_style_context(aWidget); ++ static auto sGtkWidgetClassGetCSSName = ++ reinterpret_cast<const char* (*)(GtkWidgetClass*)> ++ (dlsym(RTLD_DEFAULT, "gtk_widget_class_get_css_name")); ++ ++ GtkWidgetClass *widgetClass = GTK_WIDGET_GET_CLASS(aWidget); ++ const gchar* name = sGtkWidgetClassGetCSSName ? ++ sGtkWidgetClassGetCSSName(widgetClass) : nullptr; ++ ++ GtkStyleContext *context = ++ CreateCSSNode(name, aParentStyle, G_TYPE_FROM_CLASS(widgetClass)); ++ ++ // Classes are stored on the style context instead of the path so that any ++ // future gtk_style_context_save() will inherit classes on the head CSS ++ // node, in the same way as happens when called on a style context owned by ++ // a widget. ++ // ++ // Classes can be stored on a GtkCssNodeDeclaration and/or the path. ++ // gtk_style_context_save() reuses the GtkCssNodeDeclaration, and appends a ++ // new object to the path, without copying the classes from the old path ++ // head. The new head picks up classes from the GtkCssNodeDeclaration, but ++ // not the path. GtkWidgets store their classes on the ++ // GtkCssNodeDeclaration, so make sure to add classes there. ++ // ++ // Picking up classes from the style context also means that ++ // https://bugzilla.gnome.org/show_bug.cgi?id=767312, which can stop ++ // gtk_widget_path_append_for_widget() from finding classes in GTK 3.20, ++ // is not a problem. ++ GtkStyleContext* widgetStyle = gtk_widget_get_style_context(aWidget); ++ GList* classes = gtk_style_context_list_classes(widgetStyle); ++ for (GList* link = classes; link; link = link->next) { ++ gtk_style_context_add_class(context, static_cast<gchar*>(link->data)); ++ } ++ g_list_free(classes); + +- gtk_widget_path_append_for_widget(path, aWidget); + // Release any floating reference on aWidget. + g_object_ref_sink(aWidget); + g_object_unref(aWidget); + +- GtkStyleContext *context = gtk_style_context_new(); +- gtk_style_context_set_path(context, path); +- gtk_style_context_set_parent(context, aParentStyle); +- gtk_widget_path_unref(path); +- + return context; + } + ++static GtkStyleContext* ++CreateStyleForWidget(GtkWidget* aWidget, WidgetNodeType aParentType) ++{ ++ return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType)); ++} ++ + GtkStyleContext* + CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle, GType aType) + { +@@ -342,13 +708,26 @@ CreateCSSNode(const char* aName, GtkStyl + reinterpret_cast<void (*)(GtkWidgetPath *, gint, const char *)> + (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name")); + +- GtkWidgetPath* path = aParentStyle ? +- gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle)) : +- gtk_widget_path_new(); ++ GtkWidgetPath* path; ++ if (aParentStyle) { ++ path = gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle)); ++ // Copy classes from the parent style context to its corresponding node in ++ // the path, because GTK will only match against ancestor classes if they ++ // are on the path. ++ GList* classes = gtk_style_context_list_classes(aParentStyle); ++ for (GList* link = classes; link; link = link->next) { ++ gtk_widget_path_iter_add_class(path, -1, static_cast<gchar*>(link->data)); ++ } ++ g_list_free(classes); ++ } else { ++ path = gtk_widget_path_new(); ++ } + + gtk_widget_path_append_type(path, aType); + +- (*sGtkWidgetPathIterSetObjectName)(path, -1, aName); ++ if (sGtkWidgetPathIterSetObjectName) { ++ (*sGtkWidgetPathIterSetObjectName)(path, -1, aName); ++ } + + GtkStyleContext *context = gtk_style_context_new(); + gtk_style_context_set_path(context, path); +@@ -358,6 +737,40 @@ CreateCSSNode(const char* aName, GtkStyl + return context; + } + ++// Return a style context matching that of the root CSS node of a widget. ++// This is used by all GTK versions. ++static GtkStyleContext* ++GetWidgetRootStyle(WidgetNodeType aNodeType) ++{ ++ GtkStyleContext* style = sStyleStorage[aNodeType]; ++ if (style) ++ return style; ++ ++ switch (aNodeType) { ++ case MOZ_GTK_MENUBARITEM: ++ style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUBAR); ++ break; ++ case MOZ_GTK_MENUITEM: ++ style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUPOPUP); ++ break; ++ case MOZ_GTK_CHECKMENUITEM: ++ style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP); ++ break; ++ case MOZ_GTK_RADIOMENUITEM: ++ style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr), ++ MOZ_GTK_MENUPOPUP); ++ break; ++ default: ++ GtkWidget* widget = GetWidget(aNodeType); ++ MOZ_ASSERT(widget); ++ return gtk_widget_get_style_context(widget); ++ } ++ ++ MOZ_ASSERT(style); ++ sStyleStorage[aNodeType] = style; ++ return style; ++} ++ + static GtkStyleContext* + CreateChildCSSNode(const char* aName, WidgetNodeType aParentNodeType) + { +@@ -367,7 +780,7 @@ CreateChildCSSNode(const char* aName, Wi + static GtkStyleContext* + GetWidgetStyleWithClass(WidgetNodeType aWidgetType, const gchar* aStyleClass) + { +- GtkStyleContext* style = gtk_widget_get_style_context(GetWidget(aWidgetType)); ++ GtkStyleContext* style = GetWidgetRootStyle(aWidgetType); + gtk_style_context_save(style); + MOZ_ASSERT(!sStyleContextNeedsRestore); + sStyleContextNeedsRestore = true; +@@ -408,6 +821,10 @@ GetCssNodeStyleInternal(WidgetNodeType a + style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER, + MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL); + break; ++ case MOZ_GTK_SCROLLBAR_BUTTON: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_BUTTON, ++ MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL); ++ break; + case MOZ_GTK_RADIOBUTTON: + style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO, + MOZ_GTK_RADIOBUTTON_CONTAINER); +@@ -416,6 +833,14 @@ GetCssNodeStyleInternal(WidgetNodeType a + style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK, + MOZ_GTK_CHECKBUTTON_CONTAINER); + break; ++ case MOZ_GTK_RADIOMENUITEM_INDICATOR: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO, ++ MOZ_GTK_RADIOMENUITEM); ++ break; ++ case MOZ_GTK_CHECKMENUITEM_INDICATOR: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK, ++ MOZ_GTK_CHECKMENUITEM); ++ break; + case MOZ_GTK_PROGRESS_TROUGH: + /* Progress bar background (trough) */ + style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH, +@@ -451,11 +876,77 @@ GetCssNodeStyleInternal(WidgetNodeType a + return GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, + GTK_STYLE_CLASS_VIEW); + case MOZ_GTK_FRAME_BORDER: +- return CreateChildCSSNode("border", MOZ_GTK_FRAME); +- default: +- // TODO - create style from style path +- GtkWidget* widget = GetWidget(aNodeType); ++ style = CreateChildCSSNode("border", MOZ_GTK_FRAME); ++ break; ++ case MOZ_GTK_TREEVIEW_VIEW: ++ // TODO - create from CSS node ++ return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, ++ GTK_STYLE_CLASS_VIEW); ++ case MOZ_GTK_TREEVIEW_EXPANDER: ++ // TODO - create from CSS node ++ return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, ++ GTK_STYLE_CLASS_EXPANDER); ++ case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL: ++ style = CreateChildCSSNode("separator", ++ MOZ_GTK_SPLITTER_HORIZONTAL); ++ break; ++ case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL: ++ style = CreateChildCSSNode("separator", ++ MOZ_GTK_SPLITTER_VERTICAL); ++ break; ++ case MOZ_GTK_SCALE_CONTENTS_HORIZONTAL: ++ style = CreateChildCSSNode("contents", ++ MOZ_GTK_SCALE_HORIZONTAL); ++ break; ++ case MOZ_GTK_SCALE_CONTENTS_VERTICAL: ++ style = CreateChildCSSNode("contents", ++ MOZ_GTK_SCALE_VERTICAL); ++ break; ++ case MOZ_GTK_SCALE_TROUGH_HORIZONTAL: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH, ++ MOZ_GTK_SCALE_CONTENTS_HORIZONTAL); ++ break; ++ case MOZ_GTK_SCALE_TROUGH_VERTICAL: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH, ++ MOZ_GTK_SCALE_CONTENTS_VERTICAL); ++ break; ++ case MOZ_GTK_SCALE_THUMB_HORIZONTAL: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER, ++ MOZ_GTK_SCALE_TROUGH_HORIZONTAL); ++ break; ++ case MOZ_GTK_SCALE_THUMB_VERTICAL: ++ style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER, ++ MOZ_GTK_SCALE_TROUGH_VERTICAL); ++ break; ++ case MOZ_GTK_TAB_TOP: ++ { ++ // TODO - create from CSS node ++ style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, ++ GTK_STYLE_CLASS_TOP); ++ gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, ++ static_cast<GtkRegionFlags>(0)); ++ return style; ++ } ++ case MOZ_GTK_TAB_BOTTOM: ++ { ++ // TODO - create from CSS node ++ style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, ++ GTK_STYLE_CLASS_BOTTOM); ++ gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, ++ static_cast<GtkRegionFlags>(0)); ++ return style; ++ } ++ case MOZ_GTK_NOTEBOOK: ++ case MOZ_GTK_NOTEBOOK_HEADER: ++ case MOZ_GTK_TABPANELS: ++ case MOZ_GTK_TAB_SCROLLARROW: ++ { ++ // TODO - create from CSS node ++ GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK); + return gtk_widget_get_style_context(widget); ++ } ++ default: ++ return GetWidgetRootStyle(aNodeType); + } + + MOZ_ASSERT(style, "missing style context for node type"); +@@ -486,6 +977,12 @@ GetWidgetStyleInternal(WidgetNodeType aN + case MOZ_GTK_CHECKBUTTON: + return GetWidgetStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER, + GTK_STYLE_CLASS_CHECK); ++ case MOZ_GTK_RADIOMENUITEM_INDICATOR: ++ return GetWidgetStyleWithClass(MOZ_GTK_RADIOMENUITEM, ++ GTK_STYLE_CLASS_RADIO); ++ case MOZ_GTK_CHECKMENUITEM_INDICATOR: ++ return GetWidgetStyleWithClass(MOZ_GTK_CHECKMENUITEM, ++ GTK_STYLE_CLASS_CHECK); + case MOZ_GTK_PROGRESS_TROUGH: + return GetWidgetStyleWithClass(MOZ_GTK_PROGRESSBAR, + GTK_STYLE_CLASS_TROUGH); +@@ -519,11 +1016,57 @@ GetWidgetStyleInternal(WidgetNodeType aN + return GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, + GTK_STYLE_CLASS_VIEW); + case MOZ_GTK_FRAME_BORDER: +- return GetWidgetStyleInternal(MOZ_GTK_FRAME); +- default: +- GtkWidget* widget = GetWidget(aNodeType); +- MOZ_ASSERT(widget); ++ return GetWidgetRootStyle(MOZ_GTK_FRAME); ++ case MOZ_GTK_TREEVIEW_VIEW: ++ return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, ++ GTK_STYLE_CLASS_VIEW); ++ case MOZ_GTK_TREEVIEW_EXPANDER: ++ return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, ++ GTK_STYLE_CLASS_EXPANDER); ++ case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL: ++ return GetWidgetStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL, ++ GTK_STYLE_CLASS_PANE_SEPARATOR); ++ case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL: ++ return GetWidgetStyleWithClass(MOZ_GTK_SPLITTER_VERTICAL, ++ GTK_STYLE_CLASS_PANE_SEPARATOR); ++ case MOZ_GTK_SCALE_TROUGH_HORIZONTAL: ++ return GetWidgetStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL, ++ GTK_STYLE_CLASS_TROUGH); ++ case MOZ_GTK_SCALE_TROUGH_VERTICAL: ++ return GetWidgetStyleWithClass(MOZ_GTK_SCALE_VERTICAL, ++ GTK_STYLE_CLASS_TROUGH); ++ case MOZ_GTK_SCALE_THUMB_HORIZONTAL: ++ return GetWidgetStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL, ++ GTK_STYLE_CLASS_SLIDER); ++ case MOZ_GTK_SCALE_THUMB_VERTICAL: ++ return GetWidgetStyleWithClass(MOZ_GTK_SCALE_VERTICAL, ++ GTK_STYLE_CLASS_SLIDER); ++ case MOZ_GTK_TAB_TOP: ++ { ++ GtkStyleContext* style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, ++ GTK_STYLE_CLASS_TOP); ++ gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, ++ static_cast<GtkRegionFlags>(0)); ++ return style; ++ } ++ case MOZ_GTK_TAB_BOTTOM: ++ { ++ GtkStyleContext* style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, ++ GTK_STYLE_CLASS_BOTTOM); ++ gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, ++ static_cast<GtkRegionFlags>(0)); ++ return style; ++ } ++ case MOZ_GTK_NOTEBOOK: ++ case MOZ_GTK_NOTEBOOK_HEADER: ++ case MOZ_GTK_TABPANELS: ++ case MOZ_GTK_TAB_SCROLLARROW: ++ { ++ GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK); + return gtk_widget_get_style_context(widget); ++ } ++ default: ++ return GetWidgetRootStyle(aNodeType); + } + } + +diff -up firefox-50.0/widget/gtk/WidgetStyleCache.h.widget-rebase firefox-50.0/widget/gtk/WidgetStyleCache.h +--- firefox-50.0/widget/gtk/WidgetStyleCache.h.widget-rebase 2016-10-31 21:15:38.000000000 +0100 ++++ firefox-50.0/widget/gtk/WidgetStyleCache.h 2016-11-24 11:57:52.035064777 +0100 +@@ -28,7 +28,6 @@ GetWidget(WidgetNodeType aNodeType); + GtkStyleContext* + CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle); + +-// CreateCSSNode is implemented for gtk >= 3.20 only. + GtkStyleContext* + CreateCSSNode(const char* aName, + GtkStyleContext* aParentStyle, |