bug 1343802 calculate scrollbar metrics in one place r?jhorak draft
authorKarl Tomlinson <karlt+@karlt.net>
Thu, 23 Mar 2017 18:23:26 +1300
changeset 503431 4320332662fecaa6e6daca3932885f181d12049b
parent 503430 8cad9d001b0e161c6b1be87cb90e3738505e4ddb
child 503432 cf31ccfc8b3b1e6bafaf5bb66304178018bbfdda
push id50576
push userktomlinson@mozilla.com
push dateThu, 23 Mar 2017 06:19:17 +0000
reviewersjhorak
bugs1343802
milestone55.0a1
bug 1343802 calculate scrollbar metrics in one place r?jhorak Changes in behavior are intended to be minimal, but this adds distinct metrics for horizontal and vertical scrollbars even with GTK versions < 3.20. Updates on theme changes will be restored in a subsequent patch. MozReview-Commit-ID: 4vi2nKxCxW7
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
widget/gtk/nsNativeThemeGTK.cpp
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -18,16 +18,18 @@
 
 #include <math.h>
 
 static gboolean have_arrow_scaling;
 static gboolean checkbox_check_state;
 static gboolean notebook_has_tab_gap;
 static gboolean is_initialized;
 
+static ScrollbarGTKMetrics sScrollbarMetrics[2];
+
 #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)
 #endif
@@ -430,17 +432,21 @@ calculate_arrow_rect(GtkWidget* arrow, G
     arrow_rect->y = floor(rect->y + mypad +
                           ((rect->height - extent) * myalign));
 
     arrow_rect->width = arrow_rect->height = extent;
 
     return MOZ_GTK_SUCCESS;
 }
 
-void
+/**
+ * Get minimum widget size as sum of margin, padding, border and
+ * min-width/min-height.
+ */
+static 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,
@@ -453,16 +459,24 @@ moz_gtk_get_widget_min_size(WidgetNodeTy
   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 MozGtkSize
+GetMinMarginBox(WidgetNodeType aNodeType)
+{
+    gint width, height;
+    moz_gtk_get_widget_min_size(aNodeType, &width, &height);
+    return {width, height};
+}
+
 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;
@@ -533,28 +547,32 @@ moz_gtk_scrollbar_button_paint(cairo_t *
     }
 
     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 {
-      // 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;
-      }
+        // 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).
+        GtkOrientation orientation = flags & MOZ_GTK_STEPPER_VERTICAL ?
+            GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL;
+        const auto& metrics = sScrollbarMetrics[orientation];
+        if (!metrics.initialized) {
+            NS_WARNING("Didn't measure before drawing?");
+        }
+        if (flags & MOZ_GTK_STEPPER_VERTICAL) {
+            rect.x += metrics.border.track.left;
+            rect.width = metrics.size.thumb.width;
+        } else {
+            rect.y += metrics.border.track.top;
+            rect.height = metrics.size.thumb.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;
     
@@ -1996,16 +2014,30 @@ static void moz_gtk_add_margin_border_pa
                                               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);
 }
 
+static GtkBorder
+GetMarginBorderPadding(GtkStyleContext* aStyle)
+{
+    gint left = 0, top = 0, right = 0, bottom = 0;
+    moz_gtk_add_margin_border_padding(aStyle, &left, &top, &right, &bottom);
+    // narrowing conversions to gint16:
+    GtkBorder result;
+    result.left = left;
+    result.right = right;
+    result.top = top;
+    result.bottom = bottom;
+    return result;
+}
+
 gint
 moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
                           gint* right, gint* bottom, GtkTextDirection direction,
                           gboolean inhtml)
 {
     GtkWidget* w;
     GtkStyleContext* style;
     *left = *top = *right = *bottom = 0;
@@ -2199,59 +2231,16 @@ moz_gtk_get_widget_border(WidgetNodeType
 
             GtkStyleContext* labelStyle = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL);
             moz_gtk_add_margin_border_padding(labelStyle,
                                               left, top, right, bottom);
             ReleaseStyleContext(labelStyle);
 
             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;
 
     /* 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:
@@ -2550,33 +2539,118 @@ moz_gtk_get_scalethumb_metrics(GtkOrient
       *thumb_height = min_height;
 
       ReleaseStyleContext(style);
   }
 
   return MOZ_GTK_SUCCESS;
 }
 
-gint
-moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
+static MozGtkSize
+SizeFromLengthAndBreadth(GtkOrientation aOrientation,
+                         gint aLength, gint aBreadth)
+{
+    return aOrientation == GTK_ORIENTATION_HORIZONTAL ?
+        MozGtkSize({aLength, aBreadth}) : MozGtkSize({aBreadth, aLength});
+}
+
+const ScrollbarGTKMetrics*
+GetScrollbarMetrics(GtkOrientation aOrientation)
 {
-    // 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,
-                                "trough_border", &metrics->trough_border,
-                                "stepper_size", &metrics->stepper_size,
-                                "stepper_spacing", &metrics->stepper_spacing,
-                                "min-slider-length", &metrics->min_slider_size,
-                                nullptr);
+    auto metrics = &sScrollbarMetrics[aOrientation];
+    if (metrics->initialized)
+        return metrics;
+
+    metrics->initialized = true;
+
+    WidgetNodeType scrollbar = aOrientation == GTK_ORIENTATION_HORIZONTAL ?
+        MOZ_GTK_SCROLLBAR_HORIZONTAL : MOZ_GTK_SCROLLBAR_VERTICAL;
+
+    if (gtk_get_minor_version() < 20) {
+        gint slider_width, trough_border, stepper_size, min_slider_size;
+        gboolean backward, forward, secondary_backward, secondary_forward;
+
+        GtkStyleContext* style = ClaimStyleContext(scrollbar);
+        gtk_style_context_get_style(style,
+                                    "slider-width", &slider_width,
+                                    "trough-border", &trough_border,
+                                    "stepper-size", &stepper_size,
+                                    "min-slider-length", &min_slider_size,
+                                    "has-backward-stepper", &backward,
+                                    "has-forward-stepper", &forward,
+                                    "has-secondary-backward-stepper",
+                                    &secondary_backward,
+                                    "has-secondary-forward-stepper",
+                                    &secondary_forward, nullptr);
+        ReleaseStyleContext(style);
+
+        metrics->size.thumb =
+            SizeFromLengthAndBreadth(aOrientation, min_slider_size, slider_width);
+        metrics->size.button =
+            SizeFromLengthAndBreadth(aOrientation, stepper_size, slider_width);
+        // overall scrollbar
+        gint breadth = slider_width + 2 * trough_border;
+        // Require room for the slider in the track if we don't have buttons.
+        bool hasButtons = backward || forward ||
+            secondary_backward || secondary_forward;
+        gint length = hasButtons ? 0 : min_slider_size + 2 * trough_border;
+        metrics->size.scrollbar =
+            SizeFromLengthAndBreadth(aOrientation, length, breadth);
+
+        // Borders on the major axis are set on the outermost scrollbar
+        // element to correctly position the buttons when
+        // trough-under-steppers is true.
+        // Borders on the minor axis are set on the track element so that it
+        // receives mouse events, as in GTK.
+        // Other borders have been zero-initialized.
+        if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
+            metrics->border.scrollbar.left =
+                metrics->border.scrollbar.right =
+                metrics->border.track.top =
+                metrics->border.track.bottom = trough_border;
+        } else {
+            metrics->border.scrollbar.top =
+                metrics->border.scrollbar.bottom =
+                metrics->border.track.left =
+                metrics->border.track.right = trough_border;
+        }
+
+        return metrics;
+    }
+
+    // GTK version > 3.20
+    WidgetNodeType contents, track, thumb;
+    if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
+        contents = MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL;
+        track = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL;
+        thumb = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
+    } else {
+        contents = MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL;
+        track = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
+        thumb = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
+    }
+    // thumb
+    metrics->size.thumb = GetMinMarginBox(thumb);
+    // track
+    GtkStyleContext* style = ClaimStyleContext(track);
+    metrics->border.track = GetMarginBorderPadding(style);
     ReleaseStyleContext(style);
-
-    return MOZ_GTK_SUCCESS;
+    // button
+    metrics->size.button = GetMinMarginBox(MOZ_GTK_SCROLLBAR_BUTTON);
+    // scrollbar
+    style = ClaimStyleContext(scrollbar);
+    metrics->border.scrollbar = GetMarginBorderPadding(style);
+    ReleaseStyleContext(style);
+    style = ClaimStyleContext(contents);
+    GtkBorder contentsBorder = GetMarginBorderPadding(style);
+    ReleaseStyleContext(style);
+    metrics->size.scrollbar = metrics->size.thumb + metrics->border.track +
+        contentsBorder + metrics->border.scrollbar;
+
+    return metrics;
 }
 
 /* cairo_t *cr argument has to be a system-cairo. */
 gint
 moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
                      GdkRectangle* rect,
                      GtkWidgetState* state, gint flags,
                      GtkTextDirection direction)
@@ -2813,32 +2887,16 @@ moz_gtk_widget_paint(WidgetNodeType widg
     return MOZ_GTK_UNKNOWN_WIDGET;
 }
 
 GtkWidget* moz_gtk_get_scrollbar_widget(void)
 {
     return GetWidget(MOZ_GTK_SCROLLBAR_HORIZONTAL);
 }
 
-gboolean moz_gtk_has_scrollbar_buttons(void)
-{
-    gboolean backward, forward, secondary_backward, secondary_forward;
-    MOZ_ASSERT(is_initialized, "Forgot to call moz_gtk_init()");
-    GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_VERTICAL);
-    gtk_style_context_get_style(style,
-                                "has-backward-stepper", &backward,
-                                "has-forward-stepper", &forward,
-                                "has-secondary-backward-stepper", &secondary_backward,
-                                "has-secondary-forward-stepper", &secondary_forward,
-                                NULL);
-    ReleaseStyleContext(style);
-
-    return backward | forward | secondary_forward | secondary_forward;
-}
-
 gint
 moz_gtk_shutdown()
 {
     /* This will destroy all of our widgets */
     ResetWidgetCache();
 
     is_initialized = FALSE;
 
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -34,23 +34,43 @@ typedef struct {
   guint8 canDefault;
   /* The depressed state is for buttons which remain active for a longer period:
    * activated toggle buttons or buttons showing a popup menu. */
   guint8 depressed;
   gint32 curpos; /* curpos and maxpos are used for scrollbars */
   gint32 maxpos;
 } GtkWidgetState;
 
+/**
+ * A size in the same GTK pixel units as GtkBorder and GdkRectangle.
+ */
+struct MozGtkSize {
+  gint width;
+  gint height;
+
+  MozGtkSize operator+(const GtkBorder& aBorder) const
+  {
+    gint resultWidth = width + aBorder.left + aBorder.right;
+    gint resultHeight = height + aBorder.top + aBorder.bottom;
+    return {resultWidth, resultHeight};
+  }
+};
+
 typedef struct {
-  gint slider_width;
-  gint trough_border;
-  gint stepper_size;
-  gint stepper_spacing;
-  gint min_slider_size;
-} MozGtkScrollbarMetrics;
+  bool initialized;
+  struct {
+    MozGtkSize scrollbar;
+    MozGtkSize thumb;
+    MozGtkSize button;
+  } size;
+  struct {
+    GtkBorder scrollbar;
+    GtkBorder track;
+  } border;
+} ScrollbarGTKMetrics;
 
 typedef enum {
   MOZ_GTK_STEPPER_DOWN        = 1 << 0,
   MOZ_GTK_STEPPER_BOTTOM      = 1 << 1,
   MOZ_GTK_STEPPER_VERTICAL    = 1 << 2
 } GtkScrollbarButtonFlags;
 
 typedef enum {
@@ -415,23 +435,20 @@ moz_gtk_get_scale_metrics(GtkOrientation
  * thumb_height:     [OUT] the height of the thumb
  *
  * returns:    MOZ_GTK_SUCCESS if there was no error, an error code otherwise
  */
 gint
 moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height);
 
 /**
- * Get the desired metrics for a GtkScrollbar
- * metrics:          [IN]  struct which will contain the metrics
- *
- * returns:    MOZ_GTK_SUCCESS if there was no error, an error code otherwise
+ * Get the metrics in GTK pixels for a scrollbar.
  */
-gint
-moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics* metrics);
+const ScrollbarGTKMetrics*
+GetScrollbarMetrics(GtkOrientation aOrientation);
 
 /**
  * Get the desired size of a dropdown arrow button
  * width:   [OUT] the desired width
  * height:  [OUT] the desired height
  *
  * returns:    MOZ_GTK_SUCCESS if there was no error, an error code otherwise
  */
@@ -512,28 +529,15 @@ gint moz_gtk_splitter_get_metrics(gint o
 GtkWidget* moz_gtk_get_scrollbar_widget(void);
 
 /**
  * Get the YTHICKNESS of a tab (notebook extension).
  */
 gint
 moz_gtk_get_tab_thickness(WidgetNodeType aNodeType);
 
-/**
- * Get a boolean which indicates whether the theme draws scrollbar buttons.
- * If TRUE, draw scrollbar buttons.
- */
-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
 }
 #endif /* __cplusplus */
 #endif
 
 #endif
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -1260,16 +1260,46 @@ nsNativeThemeGTK::NativeThemeToGtkTheme(
 
 NS_IMETHODIMP
 nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
                                   uint8_t aWidgetType, nsIntMargin* aResult)
 {
   GtkTextDirection direction = GetTextDirection(aFrame);
   aResult->top = aResult->left = aResult->right = aResult->bottom = 0;
   switch (aWidgetType) {
+  case NS_THEME_SCROLLBAR_HORIZONTAL:
+  case NS_THEME_SCROLLBAR_VERTICAL:
+    {
+      GtkOrientation orientation =
+        aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
+        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+      const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
+
+      const GtkBorder& border = metrics->border.scrollbar;
+      aResult->top = border.top;
+      aResult->right = border.right;
+      aResult->bottom = border.bottom;
+      aResult->left = border.left;
+    }
+    break;
+  case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
+  case NS_THEME_SCROLLBARTRACK_VERTICAL:
+    {
+      GtkOrientation orientation =
+        aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL ?
+        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+      const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
+
+      const GtkBorder& border = metrics->border.track;
+      aResult->top = border.top;
+      aResult->right = border.right;
+      aResult->bottom = border.bottom;
+      aResult->left = border.left;
+    }
+    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,
     // we don't use the toolbar border for toolbox.
     break;
   case NS_THEME_DUALBUTTON:
     // TOOLBAR_DUAL_BUTTON is an interesting case.  We want a border to draw
     // around the entire button + dropdown, and also an inner border if you're
@@ -1414,43 +1444,32 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
 {
   aResult->width = aResult->height = 0;
   *aIsOverridable = true;
 
   switch (aWidgetType) {
     case NS_THEME_SCROLLBARBUTTON_UP:
     case NS_THEME_SCROLLBARBUTTON_DOWN:
       {
-        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);
+        const ScrollbarGTKMetrics* metrics =
+          GetScrollbarMetrics(GTK_ORIENTATION_VERTICAL);
 
-          aResult->width = metrics.slider_width;
-          aResult->height = metrics.stepper_size;
-        }
-
+        aResult->width = metrics->size.button.width;
+        aResult->height = metrics->size.button.height;
         *aIsOverridable = false;
       }
       break;
     case NS_THEME_SCROLLBARBUTTON_LEFT:
     case NS_THEME_SCROLLBARBUTTON_RIGHT:
       {
-        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);
+        const ScrollbarGTKMetrics* metrics =
+          GetScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL);
 
-          aResult->width = metrics.stepper_size;
-          aResult->height = metrics.slider_width;
-        }
+        aResult->width = metrics->size.button.width;
+        aResult->height = metrics->size.button.height;
         *aIsOverridable = false;
       }
       break;
     case NS_THEME_SPLITTER:
     {
       gint metrics;
       if (IsHorizontal(aFrame)) {
         moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
@@ -1467,75 +1486,35 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
     case NS_THEME_SCROLLBAR_HORIZONTAL:
     case NS_THEME_SCROLLBAR_VERTICAL:
     {
       /* While we enforce a minimum size for the thumb, this is ignored
        * for the some scrollbars if buttons are hidden (bug 513006) because
        * 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. */
-      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;
+      GtkOrientation orientation =
+        aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
+        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+      const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
 
-        // 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();
-
-        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;
-      }
-
+      aResult->width = metrics->size.scrollbar.width;
+      aResult->height = metrics->size.scrollbar.height;
     }
     break;
     case NS_THEME_SCROLLBARTHUMB_VERTICAL:
     case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
       {
-        if (gtk_check_version(3,20,0) == nullptr) {
-          moz_gtk_get_widget_min_size(NativeThemeToGtkTheme(aWidgetType, aFrame),
-                                      &(aResult->width), &(aResult->height));
-        } else {
-          MozGtkScrollbarMetrics metrics;
-          moz_gtk_get_scrollbar_metrics(&metrics);
+        GtkOrientation orientation =
+          aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ?
+          GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+        const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
 
-          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;
-          }
-        }
+        aResult->width = metrics->size.thumb.width;
+        aResult->height = metrics->size.thumb.height;
         *aIsOverridable = false;
       }
       break;
     case NS_THEME_RANGE_THUMB:
       {
         gint thumb_length, thumb_height;
 
         if (IsRangeHorizontal(aFrame)) {