Bug 1408335 - Render titlebar icons as themed ones, r?jhorak draft
authorMartin Stransky <stransky@redhat.com>
Thu, 25 Jan 2018 11:13:12 +0100
changeset 748791 dee4d87564186afed85a75410497365489def3af
parent 748520 9a3b6d64a64b328ed0de3d6503b99f20d1c94cfb
push id97234
push userstransky@redhat.com
push dateTue, 30 Jan 2018 11:58:10 +0000
reviewersjhorak
bugs1408335
milestone60.0a1
Bug 1408335 - Render titlebar icons as themed ones, r?jhorak Render titlebar button icons as a part of -moz-window-button-* appearence. It allows us to theme the icons accordingly. We add a GtkImage widget to header bar buttons as Gtk+ does and store icon pixel data there and render it at moz_gtk_header_bar_button_paint() as a part of the buttons. It means that the toolbar buttons are not containers and moz_gtk_get_widget_border() returns zero border for them. Also implement GetToolbarButtonMetrics() per button. MozReview-Commit-ID: gkAu3VmE3q
browser/themes/linux/browser.css
widget/gtk/WidgetStyleCache.cpp
widget/gtk/WidgetStyleCache.h
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
widget/gtk/mozgtk/mozgtk.c
widget/gtk/nsNativeThemeGTK.cpp
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -737,45 +737,41 @@ html|span.ac-emphasize-text-url {
     z-index: 1;
   }
 
   /* Render titlebar command buttons according to system config.
    * Use full scale icons here as the Gtk+ does.
    */
   @media (-moz-gtk-csd-minimize-button) {
     #titlebar-min {
-      list-style-image: url("moz-icon://stock/window-minimize-symbolic");
       -moz-appearance: -moz-window-button-minimize;
     }
   }
   @media (-moz-gtk-csd-minimize-button: 0) {
     #titlebar-min {
       display: none;
     }
   }
 
   @media (-moz-gtk-csd-maximize-button) {
     #titlebar-max {
-      list-style-image: url("moz-icon://stock/window-maximize-symbolic");
       -moz-appearance: -moz-window-button-maximize;
     }
     :root[sizemode="maximized"] #titlebar-max {
-      list-style-image: url("moz-icon://stock/window-restore-symbolic");
       -moz-appearance: -moz-window-button-restore;
     }
   }
   @media (-moz-gtk-csd-maximize-button: 0) {
     #titlebar-max {
       display: none;
     }
   }
 
   @media (-moz-gtk-csd-close-button) {
     #titlebar-close {
-      list-style-image: url("moz-icon://stock/window-close-symbolic");
       -moz-appearance: -moz-window-button-close;
     }
   }
   @media (-moz-gtk-csd-close-button: 0) {
     #titlebar-close {
       display: none;
     }
   }
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -563,43 +563,112 @@ CreateHeaderBar(WidgetNodeType aWidgetTy
   // at default Adwaita theme).
   // We need to fix titlebar size calculation to also include
   // titlebar button sizes. (Bug 1419442)
   gtk_style_context_add_class(style, "default-decoration");
 
   return headerbar;
 }
 
+static void
+LoadWidgetIconPixbuf(GtkWidget* aWidgetIcon)
+{
+  GtkStyleContext* style = gtk_widget_get_style_context(aWidgetIcon);
+
+  const gchar *iconName;
+  GtkIconSize gtkIconSize;
+  gtk_image_get_icon_name(GTK_IMAGE(aWidgetIcon), &iconName, &gtkIconSize);
+
+  gint iconWidth, iconHeight;
+  gtk_icon_size_lookup(gtkIconSize, &iconWidth, &iconHeight);
+
+  /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */
+  static auto sGtkIconThemeLookupIconForScalePtr =
+    (GtkIconInfo* (*)(GtkIconTheme *, const gchar *, gint, gint, GtkIconLookupFlags))
+    dlsym(RTLD_DEFAULT, "gtk_icon_theme_lookup_icon_for_scale");
+
+  GtkIconInfo *gtkIconInfo =
+    sGtkIconThemeLookupIconForScalePtr(gtk_icon_theme_get_default(),
+                                       iconName,
+                                       iconWidth,
+                                       1, /* TODO: scale for HiDPI */
+                                       (GtkIconLookupFlags)0);
+
+  if (!gtkIconInfo) {
+    // We miss the icon, nothing to do here.
+    return;
+  }
+
+  gboolean unused;
+  GdkPixbuf *iconPixbuf =
+    gtk_icon_info_load_symbolic_for_context(gtkIconInfo, style,
+                                            &unused, nullptr);
+  g_object_unref(G_OBJECT(gtkIconInfo));
+
+  g_object_set_data_full(G_OBJECT(aWidgetIcon), "MozillaIconPixbuf", iconPixbuf,
+                        (GDestroyNotify)g_object_unref);
+}
+
+GdkPixbuf*
+GetWidgetIconPixbuf(GtkWidget* aWidgetIcon)
+{
+  return (GdkPixbuf*)g_object_get_data(G_OBJECT(aWidgetIcon),
+                                       "MozillaIconPixbuf");
+}
+
 // TODO - Also return style for buttons located at Maximized toolbar.
 static GtkWidget*
 CreateHeaderBarButton(WidgetNodeType aWidgetType)
 {
   MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
              "GtkHeaderBar is only available on GTK 3.10+.");
 
   GtkWidget* widget = gtk_button_new();
   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_HEADER_BAR)), widget);
 
   GtkStyleContext* style = gtk_widget_get_style_context(widget);
   gtk_style_context_add_class(style, "titlebutton");
 
+  GtkWidget *image = nullptr;
   switch (aWidgetType) {
     case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
       gtk_style_context_add_class(style, "close");
+      image = gtk_image_new_from_icon_name("window-close-symbolic",
+                                          GTK_ICON_SIZE_MENU);
       break;
     case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
       gtk_style_context_add_class(style, "minimize");
+      image = gtk_image_new_from_icon_name("window-minimize-symbolic",
+                                           GTK_ICON_SIZE_MENU);
       break;
     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
       gtk_style_context_add_class(style, "maximize");
+      image = gtk_image_new_from_icon_name("window-maximize-symbolic",
+                                           GTK_ICON_SIZE_MENU);
+      break;
+    case MOZ_GTK_HEADER_BAR_BUTTON_RESTORE:
+      gtk_style_context_add_class(style, "maximize");
+      image = gtk_image_new_from_icon_name("window-restore-symbolic",
+                                           GTK_ICON_SIZE_MENU);
       break;
     default:
       break;
   }
 
+  gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
+  g_object_set(image, "use-fallback", TRUE, NULL);
+  gtk_container_add(GTK_CONTAINER (widget), image);
+
+  // We bypass GetWidget() here by explicit sWidgetStorage[] update so
+  // invalidate the style as well as GetWidget() does.
+  style = gtk_widget_get_style_context(image);
+  gtk_style_context_invalidate(style);
+
+  LoadWidgetIconPixbuf(image);
+
   return widget;
 }
 
 static GtkWidget*
 CreateWidget(WidgetNodeType aWidgetType)
 {
   switch (aWidgetType) {
     case MOZ_GTK_WINDOW:
@@ -681,16 +750,17 @@ CreateWidget(WidgetNodeType aWidgetType)
     case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
       return CreateComboBoxEntryArrowWidget();
     case MOZ_GTK_HEADER_BAR:
     case MOZ_GTK_HEADER_BAR_MAXIMIZED:
       return CreateHeaderBar(aWidgetType);
     case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
     case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_RESTORE:
       return CreateHeaderBarButton(aWidgetType);
     default:
       /* Not implemented */
       return nullptr;
   }
 }
 
 GtkWidget*
--- a/widget/gtk/WidgetStyleCache.h
+++ b/widget/gtk/WidgetStyleCache.h
@@ -16,16 +16,19 @@ typedef unsigned StyleFlags;
 enum : StyleFlags {
   NO_STYLE_FLAGS,
   WHATEVER_MIGHT_BE_NEEDED = 1U << 0,
 };
 
 GtkWidget*
 GetWidget(WidgetNodeType aNodeType);
 
+GdkPixbuf*
+GetWidgetIconPixbuf(GtkWidget* aWidgetIcon);
+
 /*
  * Return a new style context based on aWidget, as a child of aParentStyle.
  * If aWidget still has a floating reference, then it is sunk and released.
  */
 GtkStyleContext*
 CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle);
 
 GtkStyleContext*
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -20,17 +20,17 @@
 #include <math.h>
 
 static gboolean checkbox_check_state;
 static gboolean notebook_has_tab_gap;
 
 static ScrollbarGTKMetrics sScrollbarMetrics[2];
 static ToggleGTKMetrics sCheckboxMetrics;
 static ToggleGTKMetrics sRadioMetrics;
-static ToolbarButtonGTKMetrics sToolbarButtonMetrics;
+static ToolbarGTKMetrics sToolbarMetrics;
 
 #define ARROW_UP      0
 #define ARROW_DOWN    G_PI
 #define ARROW_RIGHT   G_PI_2
 #define ARROW_LEFT    (G_PI+G_PI_2)
 
 #if !GTK_CHECK_VERSION(3,14,0)
 #define GTK_STATE_FLAG_CHECKED (1 << 11)
@@ -175,17 +175,17 @@ moz_gtk_refresh()
     else {
         notebook_has_tab_gap = true;
     }
 
     sScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false;
     sScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false;
     sCheckboxMetrics.initialized = false;
     sRadioMetrics.initialized = false;
-    sToolbarButtonMetrics.initialized = false;
+    sToolbarMetrics.initialized = false;
 }
 
 gint
 moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
 {
     gtk_widget_style_get(GetWidget(MOZ_GTK_CHECKBUTTON_CONTAINER),
                          "indicator_size", indicator_size,
                          "indicator_spacing", indicator_spacing,
@@ -299,61 +299,75 @@ moz_gtk_splitter_get_metrics(gint orient
         style = GetStyleContext(MOZ_GTK_SPLITTER_HORIZONTAL);
     } else {
         style = GetStyleContext(MOZ_GTK_SPLITTER_VERTICAL);
     }
     gtk_style_context_get_style(style, "handle_size", size, NULL);
     return MOZ_GTK_SUCCESS;
 }
 
-const ToolbarButtonGTKMetrics*
-GetToolbarButtonMetrics()
+static void
+InitToolbarButtonMetrics(ToolbarButtonGTKMetrics *aButtonMetrics,
+                         WidgetNodeType aWidgetType)
 {
-    if (!sToolbarButtonMetrics.initialized) {
-        GtkStyleContext* style = GetStyleContext(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
-
-        gint iconWidth, iconHeight;
-        if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &iconWidth, &iconHeight)) {
-            NS_WARNING("Failed to get Gtk+ icon size for titlebar button!");
-
-            // Use some reasonable fallback size
-            iconWidth = 16;
-            iconHeight = 16;
-        }
-
-        gint width = 0, height = 0;
-        if (gtk_check_version(3, 20, 0) == nullptr) {
-            gtk_style_context_get(style,  gtk_style_context_get_state(style),
-                                  "min-width", &width,
-                                  "min-height", &height, NULL);
+    GtkStyleContext* style = GetStyleContext(aWidgetType);
+
+    gint iconWidth, iconHeight;
+    if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &iconWidth, &iconHeight)) {
+        NS_WARNING("Failed to get Gtk+ icon size for titlebar button!");
+
+        // Use some reasonable fallback size
+        iconWidth = 16;
+        iconHeight = 16;
+    }
+
+    gint width = 0, height = 0;
+    if (gtk_check_version(3, 20, 0) == nullptr) {
+        gtk_style_context_get(style,  gtk_style_context_get_state(style),
+                              "min-width", &width,
+                              "min-height", &height, NULL);
+    }
+
+    // Cover cases when min-width/min-height is not set, it's invalid
+    // or we're running on Gtk+ < 3.20.
+    if (width < iconWidth)
+        width = iconWidth;
+    if (height < iconHeight)
+        height = iconHeight;
+
+    gint left = 0, top = 0, right = 0, bottom = 0;
+    moz_gtk_add_margin_border_padding(style, &left, &top, &right, &bottom);
+
+    width += left + right;
+    height += top + bottom;
+    aButtonMetrics->minSizeWithBorderMargin.width = width;
+    aButtonMetrics->minSizeWithBorderMargin.height = height;
+
+    // Get border size from gap between icon and button sizes.
+    // Buton size is calculated as min-width/height + border/padding.
+    aButtonMetrics->iconXPosition = (width - iconWidth) / 2;
+    aButtonMetrics->iconYPosition = (height - iconHeight) / 2;
+}
+
+const ToolbarButtonGTKMetrics*
+GetToolbarButtonMetrics(WidgetNodeType aWidgetType)
+{
+    if (!sToolbarMetrics.initialized) {
+        for (int i = 0; i < TOOLBAR_BUTTONS; i++) {
+            InitToolbarButtonMetrics(sToolbarMetrics.button + i,
+                WidgetNodeType(int(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE) + i));
         }
-
-        // Cover cases when min-width/min-height is not set, it's invalid
-        // or we're running on Gtk+ < 3.20.
-        if (width < iconWidth)
-            width = iconWidth;
-        if (height < iconHeight)
-            height = iconHeight;
-
-        gint left = 0, top = 0, right = 0, bottom = 0;
-        moz_gtk_add_margin_border_padding(style, &left, &top, &right, &bottom);
-
-        width += left + right;
-        height += top + bottom;
-        sToolbarButtonMetrics.minSizeWithBorderMargin.width = width;
-        sToolbarButtonMetrics.minSizeWithBorderMargin.height = height;
-
-        // Get border size from gap between icon and button sizes.
-        // Buton size is calculated as min-width/height + border/padding.
-        sToolbarButtonMetrics.iconXPosition = (width - iconWidth) / 2;
-        sToolbarButtonMetrics.iconYPosition = (height - iconHeight) / 2;
-        sToolbarButtonMetrics.initialized = true;
+        sToolbarMetrics.initialized = true;
     }
 
-    return &sToolbarButtonMetrics;
+    int buttonIndex = (aWidgetType - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
+    NS_ASSERTION(buttonIndex >= 0 &&
+                 buttonIndex <= TOOLBAR_BUTTONS,
+                 "GetToolbarButtonMetrics(): Wrong titlebar button!");
+    return sToolbarMetrics.button + buttonIndex;
 }
 
 static gint
 moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect,
                      GtkTextDirection direction)
 {
     GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW, direction);
 
@@ -417,21 +431,43 @@ moz_gtk_button_paint(cairo_t *cr, GdkRec
     }
     gtk_style_context_restore(style);
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_header_bar_button_paint(cairo_t *cr, GdkRectangle* rect,
                                 GtkWidgetState* state,
-                                GtkReliefStyle relief, GtkWidget* widget,
+                                GtkReliefStyle relief,
+                                WidgetNodeType aWidgetType,
                                 GtkTextDirection direction)
 {
+    GtkWidget *widget = GetWidget(aWidgetType);
     InsetByMargin(rect, gtk_widget_get_style_context(widget));
-    return moz_gtk_button_paint(cr, rect, state, relief, widget, direction);
+    moz_gtk_button_paint(cr, rect, state, relief, widget, direction);
+
+    GtkWidget* iconWidget = gtk_bin_get_child(GTK_BIN(widget));
+    GdkPixbuf* pixbuf = GetWidgetIconPixbuf(iconWidget);
+
+    if (pixbuf) {
+        GtkStyleContext* style = gtk_widget_get_style_context(iconWidget);
+        GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
+
+        gtk_style_context_save(style);
+        gtk_style_context_set_state(style, state_flags);
+
+        const ToolbarButtonGTKMetrics *metrics =
+            GetToolbarButtonMetrics(aWidgetType);
+
+        gtk_render_icon(style, cr, pixbuf,
+                        metrics->iconXPosition, metrics->iconYPosition);
+        gtk_style_context_restore(style);
+    }
+
+    return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect,
                      GtkWidgetState* state,
                      gboolean selected, gboolean inconsistent,
                      gboolean isradio, GtkTextDirection direction)
 {
@@ -2332,25 +2368,16 @@ moz_gtk_get_widget_border(WidgetNodeType
         }
     case MOZ_GTK_HEADER_BAR:
     case MOZ_GTK_HEADER_BAR_MAXIMIZED:
         {
             style = GetStyleContext(widget);
             moz_gtk_add_border_padding(style, left, top, right, bottom);
             return MOZ_GTK_SUCCESS;
         }
-    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
-    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
-    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
-        {
-            style = GetStyleContext(widget);
-            moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
-            return MOZ_GTK_SUCCESS;
-        }
-
     /* These widgets have no borders, since they are not containers. */
     case MOZ_GTK_CHECKBUTTON_LABEL:
     case MOZ_GTK_RADIOBUTTON_LABEL:
     case MOZ_GTK_SPLITTER_HORIZONTAL:
     case MOZ_GTK_SPLITTER_VERTICAL:
     case MOZ_GTK_CHECKBUTTON:
     case MOZ_GTK_RADIOBUTTON:
     case MOZ_GTK_SCROLLBAR_BUTTON:
@@ -2360,16 +2387,20 @@ moz_gtk_get_widget_border(WidgetNodeType
     case MOZ_GTK_SCALE_THUMB_VERTICAL:
     case MOZ_GTK_GRIPPER:
     case MOZ_GTK_PROGRESS_CHUNK:
     case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
     case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
     case MOZ_GTK_TREEVIEW_EXPANDER:
     case MOZ_GTK_TOOLBAR_SEPARATOR:
     case MOZ_GTK_MENUSEPARATOR:
+    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_RESTORE:
     /* These widgets have no borders.*/
     case MOZ_GTK_INNER_SPIN_BUTTON:
     case MOZ_GTK_SPINBUTTON:
     case MOZ_GTK_WINDOW:
     case MOZ_GTK_RESIZER:
     case MOZ_GTK_MENUARROW:
     case MOZ_GTK_TOOLBARBUTTON_ARROW:
     case MOZ_GTK_TOOLBAR:
@@ -2867,19 +2898,20 @@ moz_gtk_widget_paint(WidgetNodeType widg
         return moz_gtk_button_paint(cr, rect, state,
                                     (GtkReliefStyle) flags,
                                     GetWidget(MOZ_GTK_BUTTON),
                                     direction);
         break;
     case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
     case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_RESTORE:
         return moz_gtk_header_bar_button_paint(cr, rect, state,
                                                (GtkReliefStyle) flags,
-                                               GetWidget(widget),
+                                               widget,
                                                direction);
         break;
     case MOZ_GTK_CHECKBUTTON:
     case MOZ_GTK_RADIOBUTTON:
         return moz_gtk_toggle_paint(cr, rect, state,
                                     !!(flags & MOZ_GTK_WIDGET_CHECKED),
                                     !!(flags & MOZ_GTK_WIDGET_INCONSISTENT),
                                     (widget == MOZ_GTK_RADIOBUTTON),
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -80,22 +80,27 @@ typedef struct {
 
 typedef struct {
   bool initialized;
   MozGtkSize minSizeWithBorder;
   GtkBorder borderAndPadding;
 } ToggleGTKMetrics;
 
 typedef struct {
-  bool initialized;
   MozGtkSize minSizeWithBorderMargin;
   gint iconXPosition;
   gint iconYPosition;
 } ToolbarButtonGTKMetrics;
 
+#define TOOLBAR_BUTTONS 4
+typedef struct {
+  bool initialized;
+  ToolbarButtonGTKMetrics button[TOOLBAR_BUTTONS];
+} ToolbarGTKMetrics;
+
 typedef enum {
   MOZ_GTK_STEPPER_DOWN        = 1 << 0,
   MOZ_GTK_STEPPER_BOTTOM      = 1 << 1,
   MOZ_GTK_STEPPER_VERTICAL    = 1 << 2
 } GtkScrollbarButtonFlags;
 
 typedef enum {
   MOZ_GTK_TRACK_OPAQUE        = 1 << 0
@@ -300,20 +305,24 @@ typedef enum {
   /* Paints a GtkComboBox entry arrow widget. */
   MOZ_GTK_COMBOBOX_ENTRY_ARROW,
   /* Used for scrolled window shell. */
   MOZ_GTK_SCROLLED_WINDOW,
   /* Paints a GtkHeaderBar */
   MOZ_GTK_HEADER_BAR,
   /* Paints a GtkHeaderBar in maximized state */
   MOZ_GTK_HEADER_BAR_MAXIMIZED,
-  /* Paints GtkHeaderBar title buttons */
+  /* Paints GtkHeaderBar title buttons.
+   * Keep the order here as MOZ_GTK_HEADER_BAR_BUTTON_* are processed
+   * as an array from MOZ_GTK_HEADER_BAR_BUTTON_CLOSE to the last one.
+   */
   MOZ_GTK_HEADER_BAR_BUTTON_CLOSE,
   MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE,
   MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE,
+  MOZ_GTK_HEADER_BAR_BUTTON_RESTORE,
 
   MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
 
 /*** General library functions ***/
 /**
  * Initializes the drawing library.  You must call this function
  * prior to using any other functionality.
@@ -558,11 +567,12 @@ gint moz_gtk_splitter_get_metrics(gint o
  */
 gint
 moz_gtk_get_tab_thickness(WidgetNodeType aNodeType);
 
 
 /**
  * Get ToolbarButtonGTKMetrics for recent theme.
  */
-const ToolbarButtonGTKMetrics* GetToolbarButtonMetrics();
+const ToolbarButtonGTKMetrics*
+GetToolbarButtonMetrics(WidgetNodeType aWidgetType);
 
 #endif
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -244,18 +244,20 @@ STUB(gtk_icon_size_lookup)
 STUB(gtk_icon_source_free)
 STUB(gtk_icon_source_new)
 STUB(gtk_icon_source_set_icon_name)
 STUB(gtk_icon_theme_add_builtin_icon)
 STUB(gtk_icon_theme_get_default)
 STUB(gtk_icon_theme_get_icon_sizes)
 STUB(gtk_icon_theme_lookup_by_gicon)
 STUB(gtk_icon_theme_lookup_icon)
+STUB(gtk_image_get_icon_name)
 STUB(gtk_image_get_type)
 STUB(gtk_image_new)
+STUB(gtk_image_new_from_icon_name)
 STUB(gtk_image_new_from_stock)
 STUB(gtk_image_set_from_pixbuf)
 STUB(gtk_im_context_filter_keypress)
 STUB(gtk_im_context_focus_in)
 STUB(gtk_im_context_focus_out)
 STUB(gtk_im_context_get_preedit_string)
 STUB(gtk_im_context_reset)
 STUB(gtk_im_context_set_client_window)
@@ -471,16 +473,17 @@ STUB(gtk_widget_set_double_buffered)
 STUB(gtk_widget_set_has_window)
 STUB(gtk_widget_set_mapped)
 STUB(gtk_widget_set_name)
 STUB(gtk_widget_set_parent)
 STUB(gtk_widget_set_parent_window)
 STUB(gtk_widget_set_realized)
 STUB(gtk_widget_set_redraw_on_allocate)
 STUB(gtk_widget_set_sensitive)
+STUB(gtk_widget_set_valign)
 STUB(gtk_widget_set_window)
 STUB(gtk_widget_show)
 STUB(gtk_widget_show_all)
 STUB(gtk_widget_size_allocate)
 STUB(gtk_widget_style_get)
 STUB(gtk_widget_unparent)
 STUB(gtk_widget_unrealize)
 STUB(gtk_window_deiconify)
@@ -533,30 +536,32 @@ STUB(gdk_wayland_display_get_type)
 STUB(gtk_box_new)
 STUB(gtk_cairo_should_draw_window)
 STUB(gtk_cairo_transform_to_window)
 STUB(gtk_combo_box_text_append)
 STUB(gtk_drag_set_icon_surface)
 STUB(gtk_get_major_version)
 STUB(gtk_get_micro_version)
 STUB(gtk_get_minor_version)
+STUB(gtk_icon_info_load_symbolic_for_context)
 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)
 STUB(gtk_render_check)
 STUB(gtk_render_expander)
 STUB(gtk_render_extension)
 STUB(gtk_render_focus)
 STUB(gtk_render_frame)
 STUB(gtk_render_frame_gap)
 STUB(gtk_render_handle)
+STUB(gtk_render_icon)
 STUB(gtk_render_line)
 STUB(gtk_render_option)
 STUB(gtk_render_slider)
 STUB(gtk_scale_new)
 STUB(gtk_scrollbar_new)
 STUB(gtk_style_context_add_class)
 STUB(gtk_style_context_add_region)
 STUB(gtk_style_context_get)
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -720,17 +720,17 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u
     break;
   case NS_THEME_WINDOW_BUTTON_MINIMIZE:
     aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
     break;
   case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
     aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
     break;
   case NS_THEME_WINDOW_BUTTON_RESTORE:
-    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_RESTORE;
     break;
   default:
     return false;
   }
 
   return true;
 }
 
@@ -1571,21 +1571,43 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
   case NS_THEME_BUTTON_ARROW_PREVIOUS:
     {
       moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW,
                              &aResult->width, &aResult->height);
       *aIsOverridable = false;
     }
     break;
   case NS_THEME_WINDOW_BUTTON_CLOSE:
+    {
+      const ToolbarButtonGTKMetrics* metrics =
+          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
+      aResult->width = metrics->minSizeWithBorderMargin.width;
+      aResult->height = metrics->minSizeWithBorderMargin.height;
+      break;
+    }
   case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+    {
+      const ToolbarButtonGTKMetrics* metrics =
+          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE);
+      aResult->width = metrics->minSizeWithBorderMargin.width;
+      aResult->height = metrics->minSizeWithBorderMargin.height;
+      break;
+    }
   case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+    {
+      const ToolbarButtonGTKMetrics* metrics =
+          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE);
+      aResult->width = metrics->minSizeWithBorderMargin.width;
+      aResult->height = metrics->minSizeWithBorderMargin.height;
+      break;
+    }
   case NS_THEME_WINDOW_BUTTON_RESTORE:
     {
-      const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics();
+      const ToolbarButtonGTKMetrics* metrics =
+          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_RESTORE);
       aResult->width = metrics->minSizeWithBorderMargin.width;
       aResult->height = metrics->minSizeWithBorderMargin.height;
       break;
     }
   case NS_THEME_CHECKBOX_CONTAINER:
   case NS_THEME_RADIO_CONTAINER:
   case NS_THEME_CHECKBOX_LABEL:
   case NS_THEME_RADIO_LABEL: