summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Stransky <stransky@redhat.com>2016-11-24 12:17:42 +0100
committerMartin Stransky <stransky@redhat.com>2016-11-24 12:17:42 +0100
commit29e45f715a97cb5235b260ef893fb2a079ccf7eb (patch)
treef0110acc23edb043e375a4c0d8b178ece93f15af
parentUpdate to Firefox 50.0 (diff)
downloadlibrewolf-fedora-ff-29e45f715a97cb5235b260ef893fb2a079ccf7eb.tar.gz
librewolf-fedora-ff-29e45f715a97cb5235b260ef893fb2a079ccf7eb.tar.bz2
librewolf-fedora-ff-29e45f715a97cb5235b260ef893fb2a079ccf7eb.zip
Rebase Gtk3 widget code to latest trunk to fix various rendering problems (rhbz#1397290)
-rw-r--r--firefox.spec12
-rw-r--r--widget-rebase.patch3962
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", &notebook_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,
bgstack15