bug 1234158 add support for GTK 3.20 scrollbars r?karlt draft
authorMartin Stransky <stransky@redhat.com>
Mon, 09 May 2016 11:08:26 +1200
changeset 364691 d69296f83a60591de19bf0af88953bbbfe73e294
parent 350792 93f1cf1c81aeda5b48e459881d7913cacf5c0f41
child 520366 c94a9b48e672347f0f8ae988f276b06995a76cc2
push id17542
push userktomlinson@mozilla.com
push dateSun, 08 May 2016 23:40:48 +0000
reviewerskarlt
bugs1234158
milestone48.0a1
bug 1234158 add support for GTK 3.20 scrollbars r?karlt MozReview-Commit-ID: 6MURMxcEH6X
widget/gtk/WidgetStyleCache.cpp
widget/gtk/WidgetStyleCache.h
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
widget/gtk/moz.build
widget/gtk/mozgtk/mozgtk.c
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* TODO:
+    - implement GtkTextDirection
+    - implement StyleFlags
+*/
+
+#include <dlfcn.h>
+#include <gtk/gtk.h>
+#include "WidgetStyleCache.h"
+#include "gtkdrawing.h"
+
+static GtkWidget* sWidgetStorage[MOZ_GTK_WIDGET_NODE_COUNT];
+static GtkStyleContext* sStyleStorage[MOZ_GTK_WIDGET_NODE_COUNT];
+
+static bool sStyleContextNeedsRestore;
+#ifdef DEBUG
+static GtkStyleContext* sCurrentStyleContext;
+#endif
+static GtkStyleContext*
+GetStyleInternal(WidgetNodeType aNodeType);
+
+static GtkWidget*
+CreateWindowWidget()
+{
+  GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
+  gtk_widget_realize(widget);
+  gtk_widget_set_name(widget, "MozillaGtkWidget");
+  return widget;
+}
+
+static GtkWidget*
+CreateWindowContainerWidget()
+{
+  GtkWidget *widget = gtk_fixed_new();
+  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW)), widget);
+  return widget;
+}
+
+static void
+AddToWindowContainer(GtkWidget* widget)
+{
+  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW_CONTAINER)), widget);
+  gtk_widget_realize(widget);
+}
+
+static GtkWidget*
+CreateScrollbarWidget(WidgetNodeType aWidgetType, GtkOrientation aOrientation)
+{
+  GtkWidget* widget = gtk_scrollbar_new(aOrientation, nullptr);
+  AddToWindowContainer(widget);
+  return widget;
+}
+
+static GtkWidget*
+CreateWidget(WidgetNodeType aWidgetType)
+{
+  switch (aWidgetType) {
+    case MOZ_GTK_WINDOW:
+      return CreateWindowWidget();
+    case MOZ_GTK_WINDOW_CONTAINER:
+      return CreateWindowContainerWidget();
+    case MOZ_GTK_SCROLLBAR_HORIZONTAL:
+      return CreateScrollbarWidget(aWidgetType,
+                                   GTK_ORIENTATION_HORIZONTAL);
+    case MOZ_GTK_SCROLLBAR_VERTICAL:
+      return CreateScrollbarWidget(aWidgetType,
+                                   GTK_ORIENTATION_VERTICAL);
+    default:
+      /* Not implemented */
+      return nullptr;
+  }
+}
+
+GtkWidget*
+GetWidget(WidgetNodeType aWidgetType)
+{
+  GtkWidget* widget = sWidgetStorage[aWidgetType];
+  if (!widget) {
+    widget = CreateWidget(aWidgetType);
+    sWidgetStorage[aWidgetType] = widget;
+  }
+  return widget;
+}
+
+static GtkStyleContext*
+CreateCSSNode(const char* aName, GtkStyleContext *aParentStyle)
+{
+  static auto sGtkWidgetPathIterSetObjectName =
+    reinterpret_cast<void (*)(GtkWidgetPath *, gint, const char *)>
+    (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name"));
+
+  GtkWidgetPath* path =
+    gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle));
+
+  gtk_widget_path_append_type(path, G_TYPE_NONE);
+
+  (*sGtkWidgetPathIterSetObjectName)(path, -1, aName);
+
+  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*
+GetChildNodeStyle(WidgetNodeType aStyleType,
+                  WidgetNodeType aWidgetType,
+                  const gchar*   aStyleClass,
+                  WidgetNodeType aParentNodeType)
+{
+  GtkStyleContext* style;
+
+  if (gtk_check_version(3, 20, 0) != nullptr) {
+    style = gtk_widget_get_style_context(sWidgetStorage[aWidgetType]);
+
+    gtk_style_context_save(style);
+    MOZ_ASSERT(!sStyleContextNeedsRestore);
+    sStyleContextNeedsRestore = true;
+
+    gtk_style_context_add_class(style, aStyleClass);
+  }
+  else {
+    style = sStyleStorage[aStyleType];
+    if (!style) {
+      style = CreateCSSNode(aStyleClass, GetStyleInternal(aParentNodeType));
+      MOZ_ASSERT(!sStyleContextNeedsRestore);
+      sStyleStorage[aStyleType] = style;
+    }
+  }
+
+  return style;
+}
+
+static GtkStyleContext*
+GetStyleInternal(WidgetNodeType aNodeType)
+{
+  GtkWidget* widget = GetWidget(aNodeType);
+  if (widget) {
+    return gtk_widget_get_style_context(widget);
+  }
+
+  switch (aNodeType) {
+    default:
+      MOZ_FALLTHROUGH_ASSERT("missing style context for node type");
+
+    case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
+      return GetChildNodeStyle(aNodeType,
+                               MOZ_GTK_SCROLLBAR_HORIZONTAL,
+                               GTK_STYLE_CLASS_TROUGH,
+                               MOZ_GTK_SCROLLBAR_HORIZONTAL);
+
+    case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
+      return GetChildNodeStyle(aNodeType,
+                               MOZ_GTK_SCROLLBAR_HORIZONTAL,
+                               GTK_STYLE_CLASS_SLIDER,
+                               MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL);
+
+    case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
+      return GetChildNodeStyle(aNodeType,
+                               MOZ_GTK_SCROLLBAR_VERTICAL,
+                               GTK_STYLE_CLASS_TROUGH,
+                               MOZ_GTK_SCROLLBAR_VERTICAL);
+
+    case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
+      return GetChildNodeStyle(aNodeType,
+                               MOZ_GTK_SCROLLBAR_VERTICAL,
+                               GTK_STYLE_CLASS_SLIDER,
+                               MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL);
+  }
+}
+
+void
+ResetWidgetCache(void)
+{
+  MOZ_ASSERT(!sStyleContextNeedsRestore);
+#ifdef DEBUG
+  MOZ_ASSERT(!sCurrentStyleContext);
+#endif
+
+  for (int i = 0; i < MOZ_GTK_WIDGET_NODE_COUNT; i++) {
+    if (sStyleStorage[i])
+      g_object_unref(sStyleStorage[i]);
+  }
+  PodArrayZero(sStyleStorage);
+
+  /* This will destroy all of our widgets */
+  if (sWidgetStorage[MOZ_GTK_WINDOW])
+    gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
+
+  /* Clear already freed arrays */
+  PodArrayZero(sWidgetStorage);
+}
+
+GtkStyleContext*
+ClaimStyleContext(WidgetNodeType aNodeType, GtkTextDirection aDirection,
+                  StyleFlags aFlags)
+{
+  GtkStyleContext* style = GetStyleInternal(aNodeType);
+#ifdef DEBUG
+  MOZ_ASSERT(!sCurrentStyleContext);
+  sCurrentStyleContext = style;
+#endif
+  return style;
+}
+
+void
+ReleaseStyleContext(GtkStyleContext* aStyleContext)
+{
+  if (sStyleContextNeedsRestore) {
+    gtk_style_context_restore(aStyleContext);
+  }
+  sStyleContextNeedsRestore = false;
+#ifdef DEBUG
+  MOZ_ASSERT(sCurrentStyleContext == aStyleContext);
+  sCurrentStyleContext = nullptr;
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WidgetStyleCache.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef WidgetStyleCache_h
+#define WidgetStyleCache_h
+
+#include <gtk/gtk.h>
+#include "gtkdrawing.h"
+
+
+typedef unsigned StyleFlags;
+enum : StyleFlags {
+  NO_STYLE_FLAGS,
+  WHATEVER_MIGHT_BE_NEEDED = 1U << 0,
+};
+
+GtkWidget*
+GetWidget(WidgetNodeType aNodeType);
+
+// Callers must call ReleaseStyleContext() on the returned context.
+GtkStyleContext*
+ClaimStyleContext(WidgetNodeType aNodeType,
+                  GtkTextDirection aDirection = GTK_TEXT_DIR_LTR,
+                  StyleFlags aFlags = NO_STYLE_FLAGS);
+void
+ReleaseStyleContext(GtkStyleContext* style);
+
+void
+ResetWidgetCache(void);
+
+#endif // WidgetStyleCache_h
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -9,28 +9,27 @@
  */
 
 #include <gtk/gtk.h>
 #include <gdk/gdkprivate.h>
 #include <string.h>
 #include "gtkdrawing.h"
 #include "mozilla/Assertions.h"
 #include "prinrval.h"
+#include "WidgetStyleCache.h"
 
 #include <math.h>
 
 static GtkWidget* gProtoWindow;
 static GtkWidget* gProtoLayout;
 static GtkWidget* gButtonWidget;
 static GtkWidget* gToggleButtonWidget;
 static GtkWidget* gButtonArrowWidget;
 static GtkWidget* gCheckboxWidget;
 static GtkWidget* gRadiobuttonWidget;
-static GtkWidget* gHorizScrollbarWidget;
-static GtkWidget* gVertScrollbarWidget;
 static GtkWidget* gSpinWidget;
 static GtkWidget* gHScaleWidget;
 static GtkWidget* gVScaleWidget;
 static GtkWidget* gEntryWidget;
 static GtkWidget* gComboBoxWidget;
 static GtkWidget* gComboBoxButtonWidget;
 static GtkWidget* gComboBoxArrowWidget;
 static GtkWidget* gComboBoxSeparatorWidget;
@@ -112,34 +111,28 @@ moz_gtk_enable_style_props(style_prop_t 
     style_prop_func = styleGetProp;
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 ensure_window_widget()
 {
     if (!gProtoWindow) {
-        gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
-        gtk_widget_realize(gProtoWindow);
-        moz_gtk_set_widget_name(gProtoWindow);
+        gProtoWindow = GetWidget(MOZ_GTK_WINDOW);
     }
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 setup_widget_prototype(GtkWidget* widget)
 {
-    ensure_window_widget();
     if (!gProtoLayout) {
-        gProtoLayout = gtk_fixed_new();
-        gtk_container_add(GTK_CONTAINER(gProtoWindow), gProtoLayout);
+        gProtoLayout = GetWidget(MOZ_GTK_WINDOW_CONTAINER);
     }
-
     gtk_container_add(GTK_CONTAINER(gProtoLayout), widget);
-    gtk_widget_realize(widget);
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 ensure_button_widget()
 {
     if (!gButtonWidget) {
         gButtonWidget = gtk_button_new_with_label("M");
@@ -208,30 +201,16 @@ ensure_radiobutton_widget()
     if (!gRadiobuttonWidget) {
         gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M");
         setup_widget_prototype(gRadiobuttonWidget);
     }
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
-ensure_scrollbar_widget()
-{
-    if (!gVertScrollbarWidget) {
-        gVertScrollbarWidget = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
-        setup_widget_prototype(gVertScrollbarWidget);
-    }
-    if (!gHorizScrollbarWidget) {
-        gHorizScrollbarWidget = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL);
-        setup_widget_prototype(gHorizScrollbarWidget);
-    }
-    return MOZ_GTK_SUCCESS;
-}
-
-static gint
 ensure_spin_widget()
 {
   if (!gSpinWidget) {
     gSpinWidget = gtk_spin_button_new(NULL, 1, 0);
     setup_widget_prototype(gSpinWidget);
   }
   return MOZ_GTK_SUCCESS;
 }
@@ -1091,25 +1070,21 @@ moz_gtk_scrollbar_button_paint(cairo_t *
                                GtkWidgetState* state,
                                GtkScrollbarButtonFlags flags,
                                GtkTextDirection direction)
 {
     GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
     GdkRectangle arrow_rect;
     gdouble arrow_angle;
     GtkStyleContext* style;
-    GtkWidget *scrollbar;
     gint arrow_displacement_x, arrow_displacement_y;
 
-    ensure_scrollbar_widget();
-
-    if (flags & MOZ_GTK_STEPPER_VERTICAL)
-        scrollbar = gVertScrollbarWidget;
-    else
-        scrollbar = gHorizScrollbarWidget;
+    GtkWidget *scrollbar =
+        GetWidget(flags & MOZ_GTK_STEPPER_VERTICAL ?
+                  MOZ_GTK_SCROLLBAR_VERTICAL : MOZ_GTK_SCROLLBAR_HORIZONTAL);
 
     gtk_widget_set_direction(scrollbar, direction);
 
     if (flags & MOZ_GTK_STEPPER_VERTICAL) {
         arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_DOWN : ARROW_UP;        
     } else {
         arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_RIGHT : ARROW_LEFT;        
     }
@@ -1178,85 +1153,68 @@ moz_gtk_scrollbar_button_paint(cairo_t *
 
 static gint
 moz_gtk_scrollbar_trough_paint(WidgetNodeType widget,
                                cairo_t *cr, GdkRectangle* rect,
                                GtkWidgetState* state,
                                GtkScrollbarTrackFlags flags,
                                GtkTextDirection direction)
 {
-    GtkStyleContext* style;
-    GtkScrollbar *scrollbar;
-
-    ensure_scrollbar_widget();
-
-    if (widget ==  MOZ_GTK_SCROLLBAR_HORIZONTAL)
-        scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
-    else
-        scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
-
-    gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
-    
     if (flags & MOZ_GTK_TRACK_OPAQUE) {
-        style = gtk_widget_get_style_context(GTK_WIDGET(gProtoWindow));
+        GtkStyleContext* style =
+            gtk_widget_get_style_context(GTK_WIDGET(gProtoWindow));
         gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
     }
 
-    style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar));
-    gtk_style_context_save(style);
-    gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH);
+    GtkStyleContext* style =
+        ClaimStyleContext(widget == MOZ_GTK_SCROLLBAR_HORIZONTAL ?
+                          MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL :
+                          MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL,
+                          direction);
+    // TODO - integate with ClaimStyleContext()?
+    gtk_style_context_set_direction(style, 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);
 
     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;
 }
 
 static gint
 moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget,
                               cairo_t *cr, GdkRectangle* rect,
                               GtkWidgetState* state,
                               GtkTextDirection direction)
 {
     GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
-    GtkStyleContext* style;
-    GtkScrollbar *scrollbar;
     GtkBorder margin;
 
-    ensure_scrollbar_widget();
-
-    if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL)
-        scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget);
-    else
-        scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget);
-
-    gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction);
-
-    style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar));
-    gtk_style_context_save(style);
-
-    gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER);
+    GtkStyleContext* style = ClaimStyleContext(widget, direction);
+
+    // TODO - integate those with ClaimStyleContext()?
     gtk_style_context_set_state(style, state_flags);
+    gtk_style_context_set_direction(style, direction);
 
     gtk_style_context_get_margin (style, state_flags, &margin);
 
     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,
                      (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
                      GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
 
-    gtk_style_context_restore(style);
+    ReleaseStyleContext(style);
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_spin_paint(cairo_t *cr, GdkRectangle* rect,
                    GtkTextDirection direction)
 {
@@ -3153,27 +3111,32 @@ moz_gtk_get_scalethumb_metrics(GtkOrient
                               NULL);
 
   return MOZ_GTK_SUCCESS;
 }
 
 gint
 moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
 {
-    ensure_scrollbar_widget();
-
-    gtk_style_context_get_style(gtk_widget_get_style_context(gHorizScrollbarWidget),
+    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,
-                                NULL);
-
-    metrics->min_slider_size = 
-        gtk_range_get_min_slider_size(GTK_RANGE(gHorizScrollbarWidget));
+                                "min-slider-length", &metrics->min_slider_size,
+                                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;
@@ -3406,57 +3369,55 @@ moz_gtk_widget_paint(WidgetNodeType widg
         g_warning("Unknown widget type: %d", widget);
     }
 
     return MOZ_GTK_UNKNOWN_WIDGET;
 }
 
 GtkWidget* moz_gtk_get_scrollbar_widget(void)
 {
-    MOZ_ASSERT(is_initialized, "Forgot to call moz_gtk_init()");
-    ensure_scrollbar_widget();
-    return gHorizScrollbarWidget;
+    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()");
-    ensure_scrollbar_widget();
-    gtk_style_context_get_style(gtk_widget_get_style_context(gHorizScrollbarWidget),
+    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()
 {
     if (gTooltipWidget)
         gtk_widget_destroy(gTooltipWidget);
     /* This will destroy all of our widgets */
-    if (gProtoWindow)
-        gtk_widget_destroy(gProtoWindow);
+
+    ResetWidgetCache();
 
     /* TODO - replace it with appropriate widget */
     if (gTreeHeaderSortArrowWidget)
         gtk_widget_destroy(gTreeHeaderSortArrowWidget);
 
     gProtoWindow = NULL;
     gProtoLayout = NULL;
     gButtonWidget = NULL;
     gToggleButtonWidget = NULL;
     gButtonArrowWidget = NULL;
     gCheckboxWidget = NULL;
     gRadiobuttonWidget = NULL;
-    gHorizScrollbarWidget = NULL;
-    gVertScrollbarWidget = NULL;
     gSpinWidget = NULL;
     gHScaleWidget = NULL;
     gVScaleWidget = NULL;
     gEntryWidget = NULL;
     gComboBoxWidget = NULL;
     gComboBoxButtonWidget = NULL;
     gComboBoxSeparatorWidget = NULL;
     gComboBoxArrowWidget = NULL;
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -97,22 +97,29 @@ typedef enum {
   MOZ_GTK_CHECKBUTTON,
   /* Paints a GtkRadioButton. flags is a boolean, 1=checked, 0=not checked. */
   MOZ_GTK_RADIOBUTTON,
   /**
    * Paints the button of a GtkScrollbar. flags is a GtkArrowType giving
    * the arrow direction.
    */
   MOZ_GTK_SCROLLBAR_BUTTON,
-  /* Paints the trough (track) of a GtkScrollbar. */
+
+  /* Horizontal GtkScrollbar counterparts */
   MOZ_GTK_SCROLLBAR_HORIZONTAL,
-  MOZ_GTK_SCROLLBAR_VERTICAL,
+  /* Paints the trough (track) of a GtkScrollbar. */
+  MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL,
   /* Paints the slider (thumb) of a GtkScrollbar. */
   MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL,
+
+  /* Vertical GtkScrollbar counterparts */
+  MOZ_GTK_SCROLLBAR_VERTICAL,
+  MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL,
   MOZ_GTK_SCROLLBAR_THUMB_VERTICAL,
+
   /* Paints a GtkScale. */
   MOZ_GTK_SCALE_HORIZONTAL,
   MOZ_GTK_SCALE_VERTICAL,
   /* Paints a GtkScale thumb. */
   MOZ_GTK_SCALE_THUMB_HORIZONTAL,
   MOZ_GTK_SCALE_THUMB_VERTICAL,
   /* Paints a GtkSpinButton */
   MOZ_GTK_SPINBUTTON,
@@ -183,18 +190,22 @@ typedef enum {
   MOZ_GTK_RADIOMENUITEM,
   MOZ_GTK_MENUSEPARATOR,
   /* Paints a GtkVPaned separator */
   MOZ_GTK_SPLITTER_HORIZONTAL,
   /* Paints a GtkHPaned separator */
   MOZ_GTK_SPLITTER_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
+  MOZ_GTK_INFO_BAR,
+
+  MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
 
 /*** General library functions ***/
 /**
  * Initializes the drawing library.  You must call this function
  * prior to using any other functionality.
  * returns: MOZ_GTK_SUCCESS if there were no errors
  *          MOZ_GTK_UNSAFE_THEME if the current theme engine is known
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -79,16 +79,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk2
     ]
 else:
     # Ignore GTK3 deprecation warnings while much of the code is still used
     # with GTK2.
     DEFINES['GDK_DISABLE_DEPRECATION_WARNINGS'] = True;
     UNIFIED_SOURCES += [
         'gtk3drawing.cpp',
         'nsApplicationChooser.cpp',
+        'WidgetStyleCache.cpp',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/layout/generic',
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -546,35 +546,40 @@ STUB(gtk_style_context_add_class)
 STUB(gtk_style_context_add_region)
 STUB(gtk_style_context_get)
 STUB(gtk_style_context_get_background_color)
 STUB(gtk_style_context_get_border)
 STUB(gtk_style_context_get_border_color)
 STUB(gtk_style_context_get_color)
 STUB(gtk_style_context_get_margin)
 STUB(gtk_style_context_get_padding)
+STUB(gtk_style_context_get_path)
 STUB(gtk_style_context_get_property)
 STUB(gtk_style_context_get_state)
 STUB(gtk_style_context_get_style)
 STUB(gtk_style_context_has_class)
 STUB(gtk_style_context_new)
 STUB(gtk_style_context_remove_class)
 STUB(gtk_style_context_remove_region)
 STUB(gtk_style_context_restore)
 STUB(gtk_style_context_save)
+STUB(gtk_style_context_set_direction)
 STUB(gtk_style_context_set_path)
+STUB(gtk_style_context_set_parent)
 STUB(gtk_style_context_set_state)
 STUB(gtk_style_properties_lookup_property)
 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_type)
+STUB(gtk_widget_path_copy)
 STUB(gtk_widget_path_free)
 STUB(gtk_widget_path_new)
+STUB(gtk_widget_path_unref)
 STUB(gtk_widget_set_visual)
 STUB(gtk_app_chooser_dialog_new_for_content_type)
 STUB(gtk_app_chooser_get_type)
 STUB(gtk_app_chooser_get_app_info)
 STUB(gtk_app_chooser_dialog_get_type)
 STUB(gtk_app_chooser_dialog_set_heading)
 STUB(gtk_color_chooser_dialog_new)
 STUB(gtk_color_chooser_dialog_get_type)