Bug 1282753 - implement combo boxes by WidgetCache, r=?karlt draft
authorMartin Stransky <stransky@redhat.com>
Wed, 19 Oct 2016 15:13:25 +1300
changeset 426707 4a9205597101ca283a05e92a15c0a1d188dae7cd
parent 423199 7be6b348c431d69f96f0765af3a0c0a0fe56d4bf
child 534257 b6c2dfdb40ffe930a731f74fd230d098ed919819
push id32790
push userktomlinson@mozilla.com
push dateWed, 19 Oct 2016 02:50:12 +0000
bugs1282753
milestone52.0a1
Bug 1282753 - implement combo boxes by WidgetCache, r=?karlt MozReview-Commit-ID: FrMbxT2D5p0
widget/gtk/WidgetStyleCache.cpp
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -206,16 +206,245 @@ static GtkWidget*
 CreateEntryWidget()
 {
   GtkWidget* widget = gtk_entry_new();
   AddToWindowContainer(widget);
   return widget;
 }
 
 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);
   AddToWindowContainer(widget);
   return widget;
 }
 
 static GtkWidget*
@@ -427,16 +656,32 @@ CreateWidget(WidgetNodeType aWidgetType)
     case MOZ_GTK_RADIOMENUITEM_CONTAINER:
       return CreateRadioMenuItemWidget();
     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;
   }
 }
 
 GtkWidget*
 GetWidget(WidgetNodeType aWidgetType)
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -13,26 +13,16 @@
 #include <string.h>
 #include "gtkdrawing.h"
 #include "mozilla/Assertions.h"
 #include "prinrval.h"
 #include "WidgetStyleCache.h"
 
 #include <math.h>
 
-static GtkWidget* gProtoLayout;
-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 style_prop_t style_prop_func;
 static gboolean have_arrow_scaling;
 static gboolean checkbox_check_state;
 static gboolean notebook_has_tab_gap;
 static gboolean is_initialized;
 
 #define ARROW_UP      0
 #define ARROW_DOWN    G_PI
@@ -74,222 +64,16 @@ GetStateFlagsFromGtkTabFlags(GtkTabFlags
 
 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;
-}
-
-/* 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;
-}
-
 gint
 moz_gtk_init()
 {
     if (is_initialized)
         return MOZ_GTK_SUCCESS;
 
     is_initialized = TRUE;
     have_arrow_scaling = (gtk_major_version > 2 ||
@@ -331,26 +115,34 @@ moz_gtk_radio_get_metrics(gint* indicato
 {
     gtk_widget_style_get(GetWidget(MOZ_GTK_RADIOBUTTON_CONTAINER),
                          "indicator_size", indicator_size,
                          "indicator_spacing", indicator_spacing,
                           NULL);
     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;
 }
 
 gint
 moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding)
 {
     gtk_widget_style_get(GetWidget(MOZ_GTK_MENUITEM),
@@ -974,70 +766,36 @@ moz_gtk_vpaned_paint(cairo_t *cr, GdkRec
     ReleaseStyleContext(style);
     return MOZ_GTK_SUCCESS;
 }
 
 // See gtk_entry_draw() for reference.
 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;
 }
 
 static gint
 moz_gtk_text_view_paint(cairo_t *cr, GdkRectangle* rect,
                         GtkWidgetState* state,
                         GtkTextDirection direction)
 {
@@ -1192,44 +950,47 @@ moz_gtk_combo_box_paint(cairo_t *cr, Gdk
                         GtkTextDirection direction)
 {
     GdkRectangle arrow_rect, real_arrow_rect;
     gint separator_width;
     gboolean wide_separators;
     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,
                                 NULL);
 
     if (wide_separators) {
         if (direction == GTK_TEXT_DIR_LTR)
             arrow_rect.x -= separator_width;
@@ -1300,41 +1061,39 @@ moz_gtk_combo_box_entry_button_paint(cai
                                      gboolean input_focus,
                                      GtkTextDirection direction)
 {
     gint x_displacement, y_displacement;
     GdkRectangle arrow_rect, real_arrow_rect;
     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);
         arrow_rect.x += x_displacement;
         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;
 }
 
 static gint
 moz_gtk_container_paint(cairo_t *cr, GdkRectangle* rect,
                         GtkWidgetState* state, 
                         WidgetNodeType  widget_type,
                         GtkTextDirection direction)
@@ -1344,17 +1103,16 @@ moz_gtk_container_paint(cairo_t *cr, Gdk
                                                state_flags);
     /* this is for drawing a prelight box */
     if (state_flags & GTK_STATE_FLAG_PRELIGHT) {
         gtk_render_background(style, cr,
                               rect->x, rect->y, rect->width, rect->height);
     }
 
     ReleaseStyleContext(style);
-
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_toggle_label_paint(cairo_t *cr, GdkRectangle* rect,
                            GtkWidgetState* state, 
                            gboolean isradio, GtkTextDirection direction)
 {
@@ -2179,59 +1937,58 @@ moz_gtk_get_widget_border(WidgetNodeType
             moz_gtk_add_style_padding(style, left, top, right, bottom);
             ReleaseStyleContext(style);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_TREE_HEADER_SORTARROW:
         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:
         {
             /* We need to account for the arrow on the dropdown, so text
              * doesn't come too close to the arrow, or in some cases spill
              * into the arrow. */
             gboolean wide_separators;
             gint separator_width;
             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;
             else
                 *right += separator_width + arrow_req.width;
 
             return MOZ_GTK_SUCCESS;
         }
@@ -2391,19 +2148,19 @@ gint
 moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height)
 {
     /*
      * We get the requisition of the drop down button, which includes
      * all padding, border and focus line widths the button uses,
      * 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;
 
     return MOZ_GTK_SUCCESS;
 }
 
 gint
 moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height)
@@ -2422,18 +2179,17 @@ moz_gtk_get_tab_scroll_arrow_size(gint* 
 }
 
 void
 moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width, gint* height)
 {
     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);
             break;
     }
 
     GtkRequisition requisition;
     gtk_widget_get_preferred_size(widget, NULL, &requisition);
@@ -2686,19 +2442,23 @@ moz_gtk_widget_paint(WidgetNodeType widg
         break;
     case MOZ_GTK_SPINBUTTON_UP:
     case MOZ_GTK_SPINBUTTON_DOWN:
         return moz_gtk_spin_updown_paint(cr, rect,
                                          (widget == MOZ_GTK_SPINBUTTON_DOWN),
                                          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,
                                      direction);
         break;
     case MOZ_GTK_TREEVIEW:
         return moz_gtk_treeview_paint(cr, rect, state,
                                       direction);
@@ -2713,33 +2473,41 @@ moz_gtk_widget_paint(WidgetNodeType widg
                                                     (GtkArrowType) flags,
                                                     direction);
         break;
     case MOZ_GTK_TREEVIEW_EXPANDER:
         return moz_gtk_treeview_expander_paint(cr, rect, state,
                                                (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;
     case MOZ_GTK_DROPDOWN:
         return moz_gtk_combo_box_paint(cr, rect, state, direction);
         break;
     case MOZ_GTK_DROPDOWN_ARROW:
         return moz_gtk_combo_box_entry_button_paint(cr, rect,
                                                     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:
         return moz_gtk_container_paint(cr, rect, state, widget, direction);
         break;
     case MOZ_GTK_CHECKBUTTON_LABEL:
     case MOZ_GTK_RADIOBUTTON_LABEL:
         return moz_gtk_toggle_label_paint(cr, rect, state,
@@ -2854,22 +2622,12 @@ gboolean moz_gtk_has_scrollbar_buttons(v
 }
 
 gint
 moz_gtk_shutdown()
 {
     /* This will destroy all of our widgets */
     ResetWidgetCache();
 
-    gProtoLayout = NULL;
-    gComboBoxWidget = NULL;
-    gComboBoxButtonWidget = NULL;
-    gComboBoxSeparatorWidget = NULL;
-    gComboBoxArrowWidget = NULL;
-    gComboBoxEntryWidget = NULL;
-    gComboBoxEntryButtonWidget = NULL;
-    gComboBoxEntryArrowWidget = NULL;
-    gComboBoxEntryTextareaWidget = NULL;
-
     is_initialized = FALSE;
 
     return MOZ_GTK_SUCCESS;
 }
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -225,16 +225,32 @@ typedef enum {
   /* 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,
 
   MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
 
 /*** General library functions ***/
 /**