LCOV - code coverage report
Current view: top level - widget/gtk - WidgetStyleCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 203 553 36.7 %
Date: 2017-07-14 16:53:18 Functions: 26 53 49.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <dlfcn.h>
       8             : #include <gtk/gtk.h>
       9             : #include "WidgetStyleCache.h"
      10             : #include "gtkdrawing.h"
      11             : 
      12             : #define STATE_FLAG_DIR_LTR (1U << 7)
      13             : #define STATE_FLAG_DIR_RTL (1U << 8)
      14             : #if GTK_CHECK_VERSION(3,8,0)
      15             : static_assert(GTK_STATE_FLAG_DIR_LTR == STATE_FLAG_DIR_LTR &&
      16             :               GTK_STATE_FLAG_DIR_RTL == STATE_FLAG_DIR_RTL,
      17             :               "incorrect direction state flags");
      18             : #endif
      19             : 
      20             : static GtkWidget* sWidgetStorage[MOZ_GTK_WIDGET_NODE_COUNT];
      21             : static GtkStyleContext* sStyleStorage[MOZ_GTK_WIDGET_NODE_COUNT];
      22             : 
      23             : static GtkStyleContext*
      24             : GetWidgetRootStyle(WidgetNodeType aNodeType);
      25             : static GtkStyleContext*
      26             : GetCssNodeStyleInternal(WidgetNodeType aNodeType);
      27             : 
      28             : static GtkWidget*
      29           2 : CreateWindowWidget()
      30             : {
      31           2 :   GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
      32           2 :   gtk_widget_set_name(widget, "MozillaGtkWidget");
      33           2 :   return widget;
      34             : }
      35             : 
      36             : static GtkWidget*
      37           2 : CreateWindowContainerWidget()
      38             : {
      39           2 :   GtkWidget *widget = gtk_fixed_new();
      40           2 :   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW)), widget);
      41           2 :   return widget;
      42             : }
      43             : 
      44             : static void
      45          17 : AddToWindowContainer(GtkWidget* widget)
      46             : {
      47          17 :   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW_CONTAINER)), widget);
      48          17 : }
      49             : 
      50             : static GtkWidget*
      51           4 : CreateScrollbarWidget(WidgetNodeType aWidgetType, GtkOrientation aOrientation)
      52             : {
      53           4 :   GtkWidget* widget = gtk_scrollbar_new(aOrientation, nullptr);
      54           4 :   AddToWindowContainer(widget);
      55           4 :   return widget;
      56             : }
      57             : 
      58             : static GtkWidget*
      59           0 : CreateCheckboxWidget()
      60             : {
      61           0 :   GtkWidget* widget = gtk_check_button_new_with_label("M");
      62           0 :   AddToWindowContainer(widget);
      63           0 :   return widget;
      64             : }
      65             : 
      66             : static GtkWidget*
      67           0 : CreateRadiobuttonWidget()
      68             : {
      69           0 :   GtkWidget* widget = gtk_radio_button_new_with_label(nullptr, "M");
      70           0 :   AddToWindowContainer(widget);
      71           0 :   return widget;
      72             : }
      73             : 
      74             : static GtkWidget*
      75           2 : CreateMenuBarWidget()
      76             : {
      77           2 :   GtkWidget* widget = gtk_menu_bar_new();
      78           2 :   AddToWindowContainer(widget);
      79           2 :   return widget;
      80             : }
      81             : 
      82             : static GtkWidget*
      83           2 : CreateMenuPopupWidget()
      84             : {
      85           2 :   GtkWidget* widget = gtk_menu_new();
      86           2 :   gtk_menu_attach_to_widget(GTK_MENU(widget), GetWidget(MOZ_GTK_WINDOW),
      87           2 :                             nullptr);
      88           2 :   return widget;
      89             : }
      90             : 
      91             : static GtkWidget*
      92           0 : CreateProgressWidget()
      93             : {
      94           0 :   GtkWidget* widget = gtk_progress_bar_new();
      95           0 :   AddToWindowContainer(widget);
      96           0 :   return widget;
      97             : }
      98             : 
      99             : static GtkWidget*
     100           0 : CreateTooltipWidget()
     101             : {
     102           0 :   MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
     103             :              "CreateTooltipWidget should be used for Gtk < 3.20 only.");
     104           0 :   GtkWidget* widget = CreateWindowWidget();
     105           0 :   GtkStyleContext* style = gtk_widget_get_style_context(widget);
     106           0 :   gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
     107           0 :   return widget;
     108             : }
     109             : 
     110             : static GtkWidget*
     111           0 : CreateExpanderWidget()
     112             : {
     113           0 :   GtkWidget* widget = gtk_expander_new("M");
     114           0 :   AddToWindowContainer(widget);
     115           0 :   return widget;
     116             : }
     117             : 
     118             : static GtkWidget*
     119           2 : CreateFrameWidget()
     120             : {
     121           2 :   GtkWidget* widget = gtk_frame_new(nullptr);
     122           2 :   AddToWindowContainer(widget);
     123           2 :   return widget;
     124             : }
     125             : 
     126             : static GtkWidget*
     127           0 : CreateGripperWidget()
     128             : {
     129           0 :   GtkWidget* widget = gtk_handle_box_new();
     130           0 :   AddToWindowContainer(widget);
     131           0 :   return widget;
     132             : }
     133             : 
     134             : static GtkWidget*
     135           0 : CreateToolbarWidget()
     136             : {
     137           0 :   GtkWidget* widget = gtk_toolbar_new();
     138           0 :   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_GRIPPER)), widget);
     139           0 :   return widget;
     140             : }
     141             : 
     142             : static GtkWidget*
     143           0 : CreateToolbarSeparatorWidget()
     144             : {
     145           0 :   GtkWidget* widget = GTK_WIDGET(gtk_separator_tool_item_new());
     146           0 :   AddToWindowContainer(widget);
     147           0 :   return widget;
     148             : }
     149             : 
     150             : static GtkWidget*
     151           0 : CreateInfoBarWidget()
     152             : {
     153           0 :   GtkWidget* widget = gtk_info_bar_new();
     154           0 :   AddToWindowContainer(widget);
     155           0 :   return widget;
     156             : }
     157             : 
     158             : static GtkWidget*
     159           2 : CreateButtonWidget()
     160             : {
     161           2 :   GtkWidget* widget = gtk_button_new_with_label("M");
     162           2 :   AddToWindowContainer(widget);
     163           2 :   return widget;
     164             : }
     165             : 
     166             : static GtkWidget*
     167           1 : CreateToggleButtonWidget()
     168             : {
     169           1 :   GtkWidget* widget = gtk_toggle_button_new();
     170           1 :   AddToWindowContainer(widget);
     171           1 :   return widget;
     172             : }
     173             : 
     174             : static GtkWidget*
     175           1 : CreateButtonArrowWidget()
     176             : {
     177           1 :   GtkWidget* widget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
     178           1 :   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_TOGGLE_BUTTON)), widget);
     179           1 :   gtk_widget_show(widget);
     180           1 :   return widget;
     181             : }
     182             : 
     183             : static GtkWidget*
     184           0 : CreateSpinWidget()
     185             : {
     186           0 :   GtkWidget* widget = gtk_spin_button_new(nullptr, 1, 0);
     187           0 :   AddToWindowContainer(widget);
     188           0 :   return widget;
     189             : }
     190             : 
     191             : static GtkWidget*
     192           0 : CreateEntryWidget()
     193             : {
     194           0 :   GtkWidget* widget = gtk_entry_new();
     195           0 :   AddToWindowContainer(widget);
     196           0 :   return widget;
     197             : }
     198             : 
     199             : static GtkWidget*
     200           0 : CreateComboBoxWidget()
     201             : {
     202           0 :   GtkWidget* widget = gtk_combo_box_new();
     203           0 :   AddToWindowContainer(widget);
     204           0 :   return widget;
     205             : }
     206             : 
     207             : typedef struct
     208             : {
     209             :   GType       type;
     210             :   GtkWidget** widget;
     211             : } GtkInnerWidgetInfo;
     212             : 
     213             : static void
     214           4 : GetInnerWidget(GtkWidget* widget, gpointer client_data)
     215             : {
     216           4 :   auto info = static_cast<GtkInnerWidgetInfo*>(client_data);
     217             : 
     218           4 :   if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) {
     219           2 :     *info->widget = widget;
     220             :   }
     221           4 : }
     222             : 
     223             : static GtkWidget*
     224           0 : CreateComboBoxButtonWidget()
     225             : {
     226           0 :   GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX);
     227           0 :   GtkWidget* comboBoxButton = nullptr;
     228             : 
     229             :   /* Get its inner Button */
     230           0 :   GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON,
     231           0 :                               &comboBoxButton };
     232           0 :   gtk_container_forall(GTK_CONTAINER(comboBox),
     233           0 :                        GetInnerWidget, &info);
     234             : 
     235           0 :   if (!comboBoxButton) {
     236             :     /* Shouldn't be reached with current internal gtk implementation; we
     237             :      * use a generic toggle button as last resort fallback to avoid
     238             :      * crashing. */
     239           0 :     comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
     240             :   } else {
     241             :     /* We need to have pointers to the inner widgets (button, separator, arrow)
     242             :      * of the ComboBox to get the correct rendering from theme engines which
     243             :      * special cases their look. Since the inner layout can change, we ask GTK
     244             :      * to NULL our pointers when they are about to become invalid because the
     245             :      * corresponding widgets don't exist anymore. It's the role of
     246             :      * g_object_add_weak_pointer().
     247             :      * Note that if we don't find the inner widgets (which shouldn't happen), we
     248             :      * fallback to use generic "non-inner" widgets, and they don't need that kind
     249             :      * of weak pointer since they are explicit children of gProtoLayout and as
     250             :      * such GTK holds a strong reference to them. */
     251           0 :     g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
     252             :                               reinterpret_cast<gpointer *>(sWidgetStorage) +
     253           0 :                               MOZ_GTK_COMBOBOX_BUTTON);
     254             :   }
     255             : 
     256           0 :   return comboBoxButton;
     257             : }
     258             : 
     259             : static GtkWidget*
     260           0 : CreateComboBoxArrowWidget()
     261             : {
     262           0 :   GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
     263           0 :   GtkWidget* comboBoxArrow = nullptr;
     264             : 
     265             :   /* Get the widgets inside the Button */
     266           0 :   GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(comboBoxButton));
     267           0 :   if (GTK_IS_BOX(buttonChild)) {
     268             :     /* appears-as-list = FALSE, cell-view = TRUE; the button
     269             :      * contains an hbox. This hbox is there because the ComboBox
     270             :      * needs to place a cell renderer, a separator, and an arrow in
     271             :      * the button when appears-as-list is FALSE. */
     272           0 :     GtkInnerWidgetInfo info = { GTK_TYPE_ARROW,
     273           0 :                                 &comboBoxArrow };
     274           0 :     gtk_container_forall(GTK_CONTAINER(buttonChild),
     275           0 :                          GetInnerWidget, &info);
     276           0 :   } else if (GTK_IS_ARROW(buttonChild)) {
     277             :     /* appears-as-list = TRUE, or cell-view = FALSE;
     278             :      * the button only contains an arrow */
     279           0 :     comboBoxArrow = buttonChild;
     280             :   }
     281             : 
     282           0 :   if (!comboBoxArrow) {
     283             :     /* Shouldn't be reached with current internal gtk implementation;
     284             :      * we gButtonArrowWidget as last resort fallback to avoid
     285             :      * crashing. */
     286           0 :     comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
     287             :   } else {
     288           0 :     g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
     289             :                               reinterpret_cast<gpointer *>(sWidgetStorage) +
     290           0 :                               MOZ_GTK_COMBOBOX_ARROW);
     291             :   }
     292             : 
     293           0 :   return comboBoxArrow;
     294             : }
     295             : 
     296             : static GtkWidget*
     297           0 : CreateComboBoxSeparatorWidget()
     298             : {
     299             :   // Ensure to search for separator only once as it can fail
     300             :   // TODO - it won't initialize after ResetWidgetCache() call
     301             :   static bool isMissingSeparator = false;
     302           0 :   if (isMissingSeparator)
     303           0 :     return nullptr;
     304             : 
     305             :   /* Get the widgets inside the Button */
     306           0 :   GtkWidget* comboBoxSeparator = nullptr;
     307             :   GtkWidget* buttonChild =
     308           0 :     gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
     309           0 :   if (GTK_IS_BOX(buttonChild)) {
     310             :     /* appears-as-list = FALSE, cell-view = TRUE; the button
     311             :      * contains an hbox. This hbox is there because the ComboBox
     312             :      * needs to place a cell renderer, a separator, and an arrow in
     313             :      * the button when appears-as-list is FALSE. */
     314           0 :     GtkInnerWidgetInfo info = { GTK_TYPE_SEPARATOR,
     315           0 :                                 &comboBoxSeparator };
     316           0 :     gtk_container_forall(GTK_CONTAINER(buttonChild),
     317           0 :                          GetInnerWidget, &info);
     318             :   }
     319             : 
     320           0 :   if (comboBoxSeparator) {
     321           0 :     g_object_add_weak_pointer(G_OBJECT(comboBoxSeparator),
     322             :                               reinterpret_cast<gpointer *>(sWidgetStorage) +
     323           0 :                               MOZ_GTK_COMBOBOX_SEPARATOR);
     324             :   } else {
     325             :     /* comboBoxSeparator may be NULL
     326             :      * when "appears-as-list" = TRUE or "cell-view" = FALSE;
     327             :      * if there is no separator, then we just won't paint it. */
     328           0 :     isMissingSeparator = true;
     329             :   }
     330             : 
     331           0 :   return comboBoxSeparator;
     332             : }
     333             : 
     334             : static GtkWidget*
     335           2 : CreateComboBoxEntryWidget()
     336             : {
     337           2 :   GtkWidget* widget = gtk_combo_box_new_with_entry();
     338           2 :   AddToWindowContainer(widget);
     339           2 :   return widget;
     340             : }
     341             : 
     342             : static GtkWidget*
     343           2 : CreateComboBoxEntryTextareaWidget()
     344             : {
     345           2 :   GtkWidget* comboBoxTextarea = nullptr;
     346             : 
     347             :   /* Get its inner Entry and Button */
     348           2 :   GtkInnerWidgetInfo info = { GTK_TYPE_ENTRY,
     349           2 :                               &comboBoxTextarea };
     350           2 :   gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
     351           2 :                        GetInnerWidget, &info);
     352             : 
     353           2 :   if (!comboBoxTextarea) {
     354           0 :     comboBoxTextarea = GetWidget(MOZ_GTK_ENTRY);
     355             :   } else {
     356           2 :     g_object_add_weak_pointer(G_OBJECT(comboBoxTextarea),
     357             :                               reinterpret_cast<gpointer *>(sWidgetStorage) +
     358           2 :                               MOZ_GTK_COMBOBOX_ENTRY);
     359             :   }
     360             : 
     361           2 :   return comboBoxTextarea;
     362             : }
     363             : 
     364             : static GtkWidget*
     365           0 : CreateComboBoxEntryButtonWidget()
     366             : {
     367           0 :   GtkWidget* comboBoxButton = nullptr;
     368             : 
     369             :   /* Get its inner Entry and Button */
     370           0 :   GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON,
     371           0 :                               &comboBoxButton };
     372           0 :   gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
     373           0 :                        GetInnerWidget, &info);
     374             : 
     375           0 :   if (!comboBoxButton) {
     376           0 :     comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
     377             :   } else {
     378           0 :     g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
     379             :                               reinterpret_cast<gpointer *>(sWidgetStorage) +
     380           0 :                               MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
     381             :   }
     382             : 
     383           0 :   return comboBoxButton;
     384             : }
     385             : 
     386             : static GtkWidget*
     387           0 : CreateComboBoxEntryArrowWidget()
     388             : {
     389           0 :   GtkWidget* comboBoxArrow = nullptr;
     390             : 
     391             :   /* Get the Arrow inside the Button */
     392             :   GtkWidget* buttonChild =
     393           0 :     gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON)));
     394             : 
     395           0 :   if (GTK_IS_BOX(buttonChild)) {
     396             :    /* appears-as-list = FALSE, cell-view = TRUE; the button
     397             :      * contains an hbox. This hbox is there because the ComboBox
     398             :      * needs to place a cell renderer, a separator, and an arrow in
     399             :      * the button when appears-as-list is FALSE. */
     400           0 :     GtkInnerWidgetInfo info = { GTK_TYPE_ARROW,
     401           0 :                                 &comboBoxArrow };
     402           0 :     gtk_container_forall(GTK_CONTAINER(buttonChild),
     403           0 :                          GetInnerWidget, &info);
     404           0 :   } else if (GTK_IS_ARROW(buttonChild)) {
     405             :     /* appears-as-list = TRUE, or cell-view = FALSE;
     406             :      * the button only contains an arrow */
     407           0 :     comboBoxArrow = buttonChild;
     408             :   }
     409             : 
     410           0 :   if (!comboBoxArrow) {
     411             :     /* Shouldn't be reached with current internal gtk implementation;
     412             :      * we gButtonArrowWidget as last resort fallback to avoid
     413             :      * crashing. */
     414           0 :     comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
     415             :   } else {
     416           0 :     g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
     417             :                               reinterpret_cast<gpointer *>(sWidgetStorage) +
     418           0 :                               MOZ_GTK_COMBOBOX_ENTRY_ARROW);
     419             :   }
     420             : 
     421           0 :   return comboBoxArrow;
     422             : }
     423             : 
     424             : static GtkWidget*
     425           2 : CreateScrolledWindowWidget()
     426             : {
     427           2 :   GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr);
     428           2 :   AddToWindowContainer(widget);
     429           2 :   return widget;
     430             : }
     431             : 
     432             : static GtkWidget*
     433           0 : CreateMenuSeparatorWidget()
     434             : {
     435           0 :   GtkWidget* widget = gtk_separator_menu_item_new();
     436           0 :   gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)),
     437           0 :                         widget);
     438           0 :   return widget;
     439             : }
     440             : 
     441             : static GtkWidget*
     442           2 : CreateTreeViewWidget()
     443             : {
     444           2 :   GtkWidget* widget = gtk_tree_view_new();
     445           2 :   AddToWindowContainer(widget);
     446           2 :   return widget;
     447             : }
     448             : 
     449             : static GtkWidget*
     450           0 : CreateTreeHeaderCellWidget()
     451             : {
     452             :   /*
     453             :    * Some GTK engines paint the first and last cell
     454             :    * of a TreeView header with a highlight.
     455             :    * Since we do not know where our widget will be relative
     456             :    * to the other buttons in the TreeView header, we must
     457             :    * paint it as a button that is between two others,
     458             :    * thus ensuring it is neither the first or last button
     459             :    * in the header.
     460             :    * GTK doesn't give us a way to do this explicitly,
     461             :    * so we must paint with a button that is between two
     462             :    * others.
     463             :    */
     464             :   GtkTreeViewColumn* firstTreeViewColumn;
     465             :   GtkTreeViewColumn* middleTreeViewColumn;
     466             :   GtkTreeViewColumn* lastTreeViewColumn;
     467             : 
     468           0 :   GtkWidget *treeView = GetWidget(MOZ_GTK_TREEVIEW);
     469             : 
     470             :   /* Create and append our three columns */
     471           0 :   firstTreeViewColumn = gtk_tree_view_column_new();
     472           0 :   gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
     473           0 :   gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
     474           0 :                               firstTreeViewColumn);
     475             : 
     476           0 :   middleTreeViewColumn = gtk_tree_view_column_new();
     477           0 :   gtk_tree_view_column_set_title(middleTreeViewColumn, "M");
     478           0 :   gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
     479           0 :                               middleTreeViewColumn);
     480             : 
     481           0 :   lastTreeViewColumn = gtk_tree_view_column_new();
     482           0 :   gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
     483           0 :   gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
     484           0 :                               lastTreeViewColumn);
     485             : 
     486             :   /* Use the middle column's header for our button */
     487           0 :   return gtk_tree_view_column_get_button(middleTreeViewColumn);
     488             : }
     489             : 
     490             : static GtkWidget*
     491           0 : CreateTreeHeaderSortArrowWidget()
     492             : {
     493             :   /* TODO, but it can't be NULL */
     494           0 :   GtkWidget* widget = gtk_button_new();
     495           0 :   AddToWindowContainer(widget);
     496           0 :   return widget;
     497             : }
     498             : 
     499             : static GtkWidget*
     500           0 : CreateHPanedWidget()
     501             : {
     502           0 :   GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
     503           0 :   AddToWindowContainer(widget);
     504           0 :   return widget;
     505             : }
     506             : 
     507             : static GtkWidget*
     508           0 : CreateVPanedWidget()
     509             : {
     510           0 :   GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
     511           0 :   AddToWindowContainer(widget);
     512           0 :   return widget;
     513             : }
     514             : 
     515             : static GtkWidget*
     516           0 : CreateScaleWidget(GtkOrientation aOrientation)
     517             : {
     518           0 :   GtkWidget* widget = gtk_scale_new(aOrientation, nullptr);
     519           0 :   AddToWindowContainer(widget);
     520           0 :   return widget;
     521             : }
     522             : 
     523             : static GtkWidget*
     524           0 : CreateNotebookWidget()
     525             : {
     526           0 :   GtkWidget* widget = gtk_notebook_new();
     527           0 :   AddToWindowContainer(widget);
     528           0 :   return widget;
     529             : }
     530             : 
     531             : static GtkWidget*
     532          26 : CreateWidget(WidgetNodeType aWidgetType)
     533             : {
     534          26 :   switch (aWidgetType) {
     535             :     case MOZ_GTK_WINDOW:
     536           2 :       return CreateWindowWidget();
     537             :     case MOZ_GTK_WINDOW_CONTAINER:
     538           2 :       return CreateWindowContainerWidget();
     539             :     case MOZ_GTK_CHECKBUTTON_CONTAINER:
     540           0 :       return CreateCheckboxWidget();
     541             :     case MOZ_GTK_PROGRESSBAR:
     542           0 :       return CreateProgressWidget();
     543             :     case MOZ_GTK_RADIOBUTTON_CONTAINER:
     544           0 :       return CreateRadiobuttonWidget();
     545             :     case MOZ_GTK_SCROLLBAR_HORIZONTAL:
     546             :       return CreateScrollbarWidget(aWidgetType,
     547           2 :                                    GTK_ORIENTATION_HORIZONTAL);
     548             :     case MOZ_GTK_SCROLLBAR_VERTICAL:
     549             :       return CreateScrollbarWidget(aWidgetType,
     550           2 :                                    GTK_ORIENTATION_VERTICAL);
     551             :     case MOZ_GTK_MENUBAR:
     552           2 :       return CreateMenuBarWidget();
     553             :     case MOZ_GTK_MENUPOPUP:
     554           2 :       return CreateMenuPopupWidget();
     555             :     case MOZ_GTK_MENUSEPARATOR:
     556           0 :       return CreateMenuSeparatorWidget();
     557             :     case MOZ_GTK_EXPANDER:
     558           0 :       return CreateExpanderWidget();
     559             :     case MOZ_GTK_FRAME:
     560           2 :       return CreateFrameWidget();
     561             :     case MOZ_GTK_GRIPPER:
     562           0 :       return CreateGripperWidget();
     563             :     case MOZ_GTK_TOOLBAR:
     564           0 :       return CreateToolbarWidget();
     565             :     case MOZ_GTK_TOOLBAR_SEPARATOR:
     566           0 :       return CreateToolbarSeparatorWidget();
     567             :     case MOZ_GTK_INFO_BAR:
     568           0 :       return CreateInfoBarWidget();
     569             :     case MOZ_GTK_SPINBUTTON:
     570           0 :       return CreateSpinWidget();
     571             :     case MOZ_GTK_BUTTON:
     572           2 :       return CreateButtonWidget();
     573             :     case MOZ_GTK_TOGGLE_BUTTON:
     574           1 :       return CreateToggleButtonWidget();
     575             :     case MOZ_GTK_BUTTON_ARROW:
     576           1 :       return CreateButtonArrowWidget();
     577             :     case MOZ_GTK_ENTRY:
     578           0 :       return CreateEntryWidget();
     579             :     case MOZ_GTK_SCROLLED_WINDOW: 
     580           2 :       return CreateScrolledWindowWidget();
     581             :     case MOZ_GTK_TREEVIEW:
     582           2 :       return CreateTreeViewWidget();
     583             :     case MOZ_GTK_TREE_HEADER_CELL:
     584           0 :       return CreateTreeHeaderCellWidget();
     585             :     case MOZ_GTK_TREE_HEADER_SORTARROW:
     586           0 :       return CreateTreeHeaderSortArrowWidget();
     587             :     case MOZ_GTK_SPLITTER_HORIZONTAL:
     588           0 :       return CreateHPanedWidget();
     589             :     case MOZ_GTK_SPLITTER_VERTICAL:
     590           0 :       return CreateVPanedWidget();
     591             :     case MOZ_GTK_SCALE_HORIZONTAL:
     592           0 :       return CreateScaleWidget(GTK_ORIENTATION_HORIZONTAL);
     593             :     case MOZ_GTK_SCALE_VERTICAL:
     594           0 :       return CreateScaleWidget(GTK_ORIENTATION_VERTICAL);
     595             :     case MOZ_GTK_NOTEBOOK:
     596           0 :       return CreateNotebookWidget();
     597             :     case MOZ_GTK_COMBOBOX:
     598           0 :       return CreateComboBoxWidget();
     599             :     case MOZ_GTK_COMBOBOX_BUTTON:
     600           0 :       return CreateComboBoxButtonWidget();
     601             :     case MOZ_GTK_COMBOBOX_ARROW:
     602           0 :       return CreateComboBoxArrowWidget();
     603             :     case MOZ_GTK_COMBOBOX_SEPARATOR:
     604           0 :       return CreateComboBoxSeparatorWidget();
     605             :     case MOZ_GTK_COMBOBOX_ENTRY:
     606           2 :       return CreateComboBoxEntryWidget();
     607             :     case MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA:
     608           2 :       return CreateComboBoxEntryTextareaWidget();
     609             :     case MOZ_GTK_COMBOBOX_ENTRY_BUTTON:
     610           0 :       return CreateComboBoxEntryButtonWidget();
     611             :     case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
     612           0 :       return CreateComboBoxEntryArrowWidget();
     613             :     default:
     614             :       /* Not implemented */
     615           0 :       return nullptr;
     616             :   }
     617             : }
     618             : 
     619             : GtkWidget*
     620          74 : GetWidget(WidgetNodeType aWidgetType)
     621             : {
     622          74 :   GtkWidget* widget = sWidgetStorage[aWidgetType];
     623          74 :   if (!widget) {
     624          26 :     widget = CreateWidget(aWidgetType);
     625          26 :     sWidgetStorage[aWidgetType] = widget;
     626             :   }
     627          74 :   return widget;
     628             : }
     629             : 
     630             : static void
     631          14 : AddStyleClassesFromStyle(GtkStyleContext* aDest, GtkStyleContext* aSrc)
     632             : {
     633          14 :   GList* classes = gtk_style_context_list_classes(aSrc);
     634          18 :   for (GList* link = classes; link; link = link->next) {
     635           4 :     gtk_style_context_add_class(aDest, static_cast<gchar*>(link->data));
     636             :   }
     637          14 :   g_list_free(classes);
     638          14 : }
     639             : 
     640             : GtkStyleContext*
     641          14 : CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle)
     642             : {
     643             :   static auto sGtkWidgetClassGetCSSName =
     644           2 :     reinterpret_cast<const char* (*)(GtkWidgetClass*)>
     645          16 :     (dlsym(RTLD_DEFAULT, "gtk_widget_class_get_css_name"));
     646             : 
     647          14 :   GtkWidgetClass *widgetClass = GTK_WIDGET_GET_CLASS(aWidget);
     648          28 :   const gchar* name = sGtkWidgetClassGetCSSName ?
     649          42 :     sGtkWidgetClassGetCSSName(widgetClass) : nullptr;
     650             : 
     651             :   GtkStyleContext *context =
     652          14 :     CreateCSSNode(name, aParentStyle, G_TYPE_FROM_CLASS(widgetClass));
     653             : 
     654             :   // Classes are stored on the style context instead of the path so that any
     655             :   // future gtk_style_context_save() will inherit classes on the head CSS
     656             :   // node, in the same way as happens when called on a style context owned by
     657             :   // a widget.
     658             :   //
     659             :   // Classes can be stored on a GtkCssNodeDeclaration and/or the path.
     660             :   // gtk_style_context_save() reuses the GtkCssNodeDeclaration, and appends a
     661             :   // new object to the path, without copying the classes from the old path
     662             :   // head.  The new head picks up classes from the GtkCssNodeDeclaration, but
     663             :   // not the path.  GtkWidgets store their classes on the
     664             :   // GtkCssNodeDeclaration, so make sure to add classes there.
     665             :   //
     666             :   // Picking up classes from the style context also means that
     667             :   // https://bugzilla.gnome.org/show_bug.cgi?id=767312, which can stop
     668             :   // gtk_widget_path_append_for_widget() from finding classes in GTK 3.20,
     669             :   // is not a problem.
     670          14 :   GtkStyleContext* widgetStyle = gtk_widget_get_style_context(aWidget);
     671          14 :   AddStyleClassesFromStyle(context, widgetStyle);
     672             : 
     673             :   // Release any floating reference on aWidget.
     674          14 :   g_object_ref_sink(aWidget);
     675          14 :   g_object_unref(aWidget);
     676             : 
     677          14 :   return context;
     678             : }
     679             : 
     680             : static GtkStyleContext*
     681          10 : CreateStyleForWidget(GtkWidget* aWidget, WidgetNodeType aParentType)
     682             : {
     683          10 :   return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType));
     684             : }
     685             : 
     686             : GtkStyleContext*
     687          32 : CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle, GType aType)
     688             : {
     689             :   static auto sGtkWidgetPathIterSetObjectName =
     690           2 :     reinterpret_cast<void (*)(GtkWidgetPath *, gint, const char *)>
     691          34 :     (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name"));
     692             : 
     693             :   GtkWidgetPath* path;
     694          32 :   if (aParentStyle) {
     695          30 :     path = gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle));
     696             :     // Copy classes from the parent style context to its corresponding node in
     697             :     // the path, because GTK will only match against ancestor classes if they
     698             :     // are on the path.
     699          30 :     GList* classes = gtk_style_context_list_classes(aParentStyle);
     700          42 :     for (GList* link = classes; link; link = link->next) {
     701          12 :       gtk_widget_path_iter_add_class(path, -1, static_cast<gchar*>(link->data));
     702             :     }
     703          30 :     g_list_free(classes);
     704             :   } else {
     705           2 :     path = gtk_widget_path_new();
     706             :   }
     707             : 
     708          32 :   gtk_widget_path_append_type(path, aType);
     709             : 
     710          32 :   if (sGtkWidgetPathIterSetObjectName) {
     711          32 :     (*sGtkWidgetPathIterSetObjectName)(path, -1, aName);
     712             :   }
     713             : 
     714          32 :   GtkStyleContext *context = gtk_style_context_new();
     715          32 :   gtk_style_context_set_path(context, path);
     716          32 :   gtk_style_context_set_parent(context, aParentStyle);
     717          32 :   gtk_widget_path_unref(path);
     718             : 
     719             :   // In GTK 3.4, gtk_render_* functions use |theming_engine| on the style
     720             :   // context without ensuring any style resolution sets it appropriately
     721             :   // in style_data_lookup(). e.g.
     722             :   // https://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=3.4.4#n3847
     723             :   //
     724             :   // That can result in incorrect drawing on first draw.  To work around this,
     725             :   // force a style look-up to set |theming_engine|.  It is sufficient to do
     726             :   // this only on context creation, instead of after every modification to the
     727             :   // context, because themes typically (Ambiance and oxygen-gtk, at least) set
     728             :   // the "engine" property with the '*' selector.
     729          32 :   if (GTK_MAJOR_VERSION == 3 && gtk_get_minor_version() < 6) {
     730             :     GdkRGBA unused;
     731           0 :     gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, &unused);
     732             :   }
     733             : 
     734          32 :   return context;
     735             : }
     736             : 
     737             : // Return a style context matching that of the root CSS node of a widget.
     738             : // This is used by all GTK versions.
     739             : static GtkStyleContext*
     740          51 : GetWidgetRootStyle(WidgetNodeType aNodeType)
     741             : {
     742          51 :   GtkStyleContext* style = sStyleStorage[aNodeType];
     743          51 :   if (style)
     744           2 :     return style;
     745             : 
     746          49 :   switch (aNodeType) {
     747             :     case MOZ_GTK_MENUBARITEM:
     748           2 :       style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUBAR);
     749           2 :       break;
     750             :     case MOZ_GTK_MENUITEM:
     751           2 :       style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUPOPUP);
     752           2 :       break;
     753             :     case MOZ_GTK_CHECKMENUITEM:
     754           0 :       style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP);
     755           0 :       break;
     756             :     case MOZ_GTK_RADIOMENUITEM:
     757           0 :       style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr),
     758           0 :                                    MOZ_GTK_MENUPOPUP);
     759           0 :       break;
     760             :     case MOZ_GTK_TEXT_VIEW:
     761           2 :       style = CreateStyleForWidget(gtk_text_view_new(),
     762           2 :                                    MOZ_GTK_SCROLLED_WINDOW);
     763           2 :       break;
     764             :     case MOZ_GTK_TOOLTIP:
     765           2 :       if (gtk_check_version(3, 20, 0) != nullptr) {
     766             :           // The tooltip style class is added first in CreateTooltipWidget()
     767             :           // and transfered to style in CreateStyleForWidget().
     768           0 :           GtkWidget* tooltipWindow = CreateTooltipWidget();
     769           0 :           style = CreateStyleForWidget(tooltipWindow, nullptr);
     770           0 :           gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference.
     771             :       } else {
     772             :           // We create this from the path because GtkTooltipWindow is not public.
     773           2 :           style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
     774           2 :           gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
     775             :       }
     776           2 :       break;
     777             :     case MOZ_GTK_TOOLTIP_BOX:
     778           2 :       style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
     779           2 :                                    MOZ_GTK_TOOLTIP);
     780           2 :       break;
     781             :     case MOZ_GTK_TOOLTIP_BOX_LABEL:
     782           2 :       style = CreateStyleForWidget(gtk_label_new(nullptr),
     783           2 :                                    MOZ_GTK_TOOLTIP_BOX);
     784           2 :       break;
     785             :     default:
     786          37 :       GtkWidget* widget = GetWidget(aNodeType);
     787          37 :       MOZ_ASSERT(widget);
     788          37 :       return gtk_widget_get_style_context(widget);
     789             :   }
     790             : 
     791          12 :   MOZ_ASSERT(style);
     792          12 :   sStyleStorage[aNodeType] = style;
     793          12 :   return style;
     794             : }
     795             : 
     796             : static GtkStyleContext*
     797          16 : CreateChildCSSNode(const char* aName, WidgetNodeType aParentNodeType)
     798             : {
     799          16 :   return CreateCSSNode(aName, GetCssNodeStyleInternal(aParentNodeType));
     800             : }
     801             : 
     802             : // Create a style context equivalent to a saved root style context of
     803             : // |aWidgetType| with |aStyleClass| as an additional class.  This is used to
     804             : // produce a context equivalent to what GTK versions < 3.20 use for many
     805             : // internal parts of widgets.
     806             : static GtkStyleContext*
     807           0 : CreateSubStyleWithClass(WidgetNodeType aWidgetType, const gchar* aStyleClass)
     808             : {
     809             :   static auto sGtkWidgetPathIterGetObjectName =
     810           0 :     reinterpret_cast<const char* (*)(const GtkWidgetPath*, gint)>
     811           0 :     (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_object_name"));
     812             : 
     813           0 :   GtkStyleContext* parentStyle = GetWidgetRootStyle(aWidgetType);
     814             : 
     815             :   // Create a new context that behaves like |parentStyle| would after
     816             :   // gtk_style_context_save(parentStyle).
     817             :   //
     818             :   // Avoiding gtk_style_context_save() avoids the need to manage the
     819             :   // restore, and a new context permits caching style resolution.
     820             :   //
     821             :   // gtk_style_context_save(context) changes the node hierarchy of |context|
     822             :   // to add a new GtkCssNodeDeclaration that is a copy of its original node.
     823             :   // The new node is a child of the original node, and so the new heirarchy is
     824             :   // one level deeper.  The new node receives the same classes as the
     825             :   // original, but any changes to the classes on |context| will change only
     826             :   // the new node.  The new node inherits properties from the original node
     827             :   // (which retains the original heirarchy and classes) and matches CSS rules
     828             :   // with the new heirarchy and any changes to the classes.
     829             :   //
     830             :   // The change in hierarchy can produce some surprises in matching theme CSS
     831             :   // rules (e.g. https://bugzilla.gnome.org/show_bug.cgi?id=761870#c2), but it
     832             :   // is important here to produce the same behavior so that rules match the
     833             :   // same widget parts in Gecko as they do in GTK.
     834             :   //
     835             :   // When using public GTK API to construct style contexts, a widget path is
     836             :   // required.  CSS rules are not matched against the style context heirarchy
     837             :   // but according to the heirarchy in the widget path.  The path that matches
     838             :   // the same CSS rules as a saved context is like the path of |parentStyle|
     839             :   // but with an extra copy of the head (last) object appended.  Setting
     840             :   // |parentStyle| as the parent context provides the same inheritance of
     841             :   // properties from the widget root node.
     842           0 :   const GtkWidgetPath* parentPath = gtk_style_context_get_path(parentStyle);
     843           0 :   const gchar* name = sGtkWidgetPathIterGetObjectName ?
     844           0 :     sGtkWidgetPathIterGetObjectName(parentPath, -1) : nullptr;
     845           0 :   GType objectType = gtk_widget_path_get_object_type(parentPath);
     846             : 
     847           0 :   GtkStyleContext* style = CreateCSSNode(name, parentStyle, objectType);
     848             : 
     849             :   // Start with the same classes on the new node as were on |parentStyle|.
     850             :   // GTK puts no regions or junction_sides on widget root nodes, and so there
     851             :   // is no need to copy these.
     852           0 :   AddStyleClassesFromStyle(style, parentStyle);
     853             : 
     854           0 :   gtk_style_context_add_class(style, aStyleClass);
     855           0 :   return style;
     856             : }
     857             : 
     858             : /* GetCssNodeStyleInternal is used by Gtk >= 3.20 */
     859             : static GtkStyleContext*
     860          72 : GetCssNodeStyleInternal(WidgetNodeType aNodeType)
     861             : {
     862          72 :   GtkStyleContext* style = sStyleStorage[aNodeType];
     863          72 :   if (style)
     864          15 :     return style;
     865             : 
     866          57 :   switch (aNodeType) {
     867             :     case MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL:
     868             :       style = CreateChildCSSNode("contents",
     869           2 :                                  MOZ_GTK_SCROLLBAR_HORIZONTAL);
     870           2 :       break;
     871             :     case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
     872             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
     873           2 :                                  MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL);
     874           2 :       break;
     875             :     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
     876             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
     877           2 :                                  MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL);
     878           2 :       break;
     879             :     case MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL:
     880             :       style = CreateChildCSSNode("contents",
     881           2 :                                  MOZ_GTK_SCROLLBAR_VERTICAL);
     882           2 :       break;
     883             :     case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
     884             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
     885           2 :                                  MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
     886           2 :       break;
     887             :     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
     888             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
     889           2 :                                  MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL);
     890           2 :       break;
     891             :     case MOZ_GTK_SCROLLBAR_BUTTON:
     892             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_BUTTON,
     893           0 :                                  MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
     894           0 :       break;
     895             :     case MOZ_GTK_RADIOBUTTON:
     896             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
     897           0 :                                  MOZ_GTK_RADIOBUTTON_CONTAINER);
     898           0 :       break;
     899             :     case MOZ_GTK_CHECKBUTTON:
     900             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
     901           0 :                                  MOZ_GTK_CHECKBUTTON_CONTAINER);
     902           0 :       break;
     903             :     case MOZ_GTK_RADIOMENUITEM_INDICATOR:
     904             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
     905           0 :                                  MOZ_GTK_RADIOMENUITEM);
     906           0 :       break;
     907             :     case MOZ_GTK_CHECKMENUITEM_INDICATOR:
     908             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
     909           0 :                                  MOZ_GTK_CHECKMENUITEM);
     910           0 :       break;
     911             :     case MOZ_GTK_PROGRESS_TROUGH:
     912             :       /* Progress bar background (trough) */
     913             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
     914           0 :                                  MOZ_GTK_PROGRESSBAR);
     915           0 :       break;
     916             :     case MOZ_GTK_PROGRESS_CHUNK:
     917             :       style = CreateChildCSSNode("progress",
     918           0 :                                  MOZ_GTK_PROGRESS_TROUGH);
     919           0 :       break;
     920             :     case MOZ_GTK_GRIPPER:
     921             :       // TODO - create from CSS node
     922             :       style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
     923           0 :                                       GTK_STYLE_CLASS_GRIP);
     924           0 :       break;
     925             :     case MOZ_GTK_INFO_BAR:
     926             :       // TODO - create from CSS node
     927             :       style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
     928           0 :                                       GTK_STYLE_CLASS_INFO);
     929           0 :       break;
     930             :     case MOZ_GTK_SPINBUTTON_ENTRY:
     931             :       // TODO - create from CSS node
     932             :       style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
     933           0 :                                       GTK_STYLE_CLASS_ENTRY);
     934           0 :       break;
     935             :     case MOZ_GTK_SCROLLED_WINDOW:
     936             :       // TODO - create from CSS node
     937             :       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
     938           0 :                                       GTK_STYLE_CLASS_FRAME);
     939           0 :       break;
     940             :     case MOZ_GTK_TEXT_VIEW_TEXT:
     941             :     case MOZ_GTK_RESIZER:
     942           2 :       style = CreateChildCSSNode("text", MOZ_GTK_TEXT_VIEW);
     943           2 :       if (aNodeType == MOZ_GTK_RESIZER) {
     944             :         // The "grip" class provides the correct builtin icon from
     945             :         // gtk_render_handle().  The icon is drawn with shaded variants of
     946             :         // the background color, and so a transparent background would lead to
     947             :         // a transparent resizer.  gtk_render_handle() also uses the
     948             :         // background color to draw a background, and so this style otherwise
     949             :         // matches what is used in GtkTextView to match the background with
     950             :         // textarea elements.
     951             :         GdkRGBA color;
     952             :         gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL,
     953           0 :                                                &color);
     954           0 :         if (color.alpha == 0.0) {
     955           0 :           g_object_unref(style);
     956           0 :           style = CreateStyleForWidget(gtk_text_view_new(),
     957           0 :                                        MOZ_GTK_SCROLLED_WINDOW);
     958             :         }
     959           0 :         gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
     960             :       }
     961           2 :       break;
     962             :     case MOZ_GTK_FRAME_BORDER:
     963           2 :       style = CreateChildCSSNode("border", MOZ_GTK_FRAME);
     964           2 :       break;
     965             :     case MOZ_GTK_TREEVIEW_VIEW:
     966             :       // TODO - create from CSS node
     967             :       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
     968           0 :                                       GTK_STYLE_CLASS_VIEW);
     969           0 :       break;
     970             :     case MOZ_GTK_TREEVIEW_EXPANDER:
     971             :       // TODO - create from CSS node
     972             :       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
     973           0 :                                       GTK_STYLE_CLASS_EXPANDER);
     974           0 :       break;
     975             :     case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
     976             :       style = CreateChildCSSNode("separator",
     977           0 :                                  MOZ_GTK_SPLITTER_HORIZONTAL);
     978           0 :       break;
     979             :     case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
     980             :       style = CreateChildCSSNode("separator",
     981           0 :                                  MOZ_GTK_SPLITTER_VERTICAL);
     982           0 :       break;
     983             :     case MOZ_GTK_SCALE_CONTENTS_HORIZONTAL:
     984             :       style = CreateChildCSSNode("contents",
     985           0 :                                  MOZ_GTK_SCALE_HORIZONTAL);
     986           0 :       break;
     987             :     case MOZ_GTK_SCALE_CONTENTS_VERTICAL:
     988             :       style = CreateChildCSSNode("contents",
     989           0 :                                  MOZ_GTK_SCALE_VERTICAL);
     990           0 :       break; 
     991             :     case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
     992             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
     993           0 :                                  MOZ_GTK_SCALE_CONTENTS_HORIZONTAL);
     994           0 :       break; 
     995             :     case MOZ_GTK_SCALE_TROUGH_VERTICAL:
     996             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
     997           0 :                                  MOZ_GTK_SCALE_CONTENTS_VERTICAL);
     998           0 :       break;
     999             :     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
    1000             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
    1001           0 :                                  MOZ_GTK_SCALE_TROUGH_HORIZONTAL);
    1002           0 :       break;
    1003             :     case MOZ_GTK_SCALE_THUMB_VERTICAL:
    1004             :       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
    1005           0 :                                  MOZ_GTK_SCALE_TROUGH_VERTICAL);
    1006           0 :       break;
    1007             :     case MOZ_GTK_TAB_TOP:
    1008             :     {
    1009             :       // TODO - create from CSS node
    1010             :       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
    1011           0 :                                       GTK_STYLE_CLASS_TOP);
    1012             :       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
    1013           0 :                                    static_cast<GtkRegionFlags>(0));
    1014           0 :       break;
    1015             :     }
    1016             :     case MOZ_GTK_TAB_BOTTOM:
    1017             :     {
    1018             :       // TODO - create from CSS node
    1019             :       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
    1020           0 :                                       GTK_STYLE_CLASS_BOTTOM);
    1021             :       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
    1022           0 :                                    static_cast<GtkRegionFlags>(0));
    1023           0 :       break;
    1024             :     }
    1025             :     case MOZ_GTK_NOTEBOOK:
    1026             :     case MOZ_GTK_NOTEBOOK_HEADER:
    1027             :     case MOZ_GTK_TABPANELS:
    1028             :     case MOZ_GTK_TAB_SCROLLARROW:
    1029             :     { 
    1030             :       // TODO - create from CSS node
    1031           0 :       GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
    1032           0 :       return gtk_widget_get_style_context(widget);
    1033             :     }
    1034             :     default:
    1035          41 :       return GetWidgetRootStyle(aNodeType);
    1036             :   }
    1037             : 
    1038          16 :   MOZ_ASSERT(style, "missing style context for node type");
    1039          16 :   sStyleStorage[aNodeType] = style;
    1040          16 :   return style;
    1041             : }
    1042             : 
    1043             : /* GetWidgetStyleInternal is used by Gtk < 3.20 */
    1044             : static GtkStyleContext*
    1045           0 : GetWidgetStyleInternal(WidgetNodeType aNodeType)
    1046             : {
    1047           0 :   GtkStyleContext* style = sStyleStorage[aNodeType];
    1048           0 :   if (style)
    1049           0 :     return style;
    1050             : 
    1051           0 :   switch (aNodeType) {
    1052             :     case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
    1053             :       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
    1054           0 :                                       GTK_STYLE_CLASS_TROUGH);
    1055           0 :       break;
    1056             :     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
    1057             :       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
    1058           0 :                                       GTK_STYLE_CLASS_SLIDER);
    1059           0 :       break;
    1060             :     case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
    1061             :       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
    1062           0 :                                       GTK_STYLE_CLASS_TROUGH);
    1063           0 :       break;
    1064             :     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
    1065             :       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
    1066           0 :                                       GTK_STYLE_CLASS_SLIDER);
    1067           0 :       break;
    1068             :     case MOZ_GTK_RADIOBUTTON:
    1069             :       style = CreateSubStyleWithClass(MOZ_GTK_RADIOBUTTON_CONTAINER,
    1070           0 :                                       GTK_STYLE_CLASS_RADIO);
    1071           0 :       break;
    1072             :     case MOZ_GTK_CHECKBUTTON:
    1073             :       style = CreateSubStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER,
    1074           0 :                                       GTK_STYLE_CLASS_CHECK);
    1075           0 :       break;
    1076             :     case MOZ_GTK_RADIOMENUITEM_INDICATOR:
    1077             :       style = CreateSubStyleWithClass(MOZ_GTK_RADIOMENUITEM,
    1078           0 :                                       GTK_STYLE_CLASS_RADIO);
    1079           0 :       break;
    1080             :     case MOZ_GTK_CHECKMENUITEM_INDICATOR:
    1081             :       style = CreateSubStyleWithClass(MOZ_GTK_CHECKMENUITEM,
    1082           0 :                                       GTK_STYLE_CLASS_CHECK);
    1083           0 :       break;
    1084             :     case MOZ_GTK_PROGRESS_TROUGH:
    1085             :       style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
    1086           0 :                                       GTK_STYLE_CLASS_TROUGH);
    1087           0 :       break;
    1088             :     case MOZ_GTK_PROGRESS_CHUNK:
    1089             :       style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
    1090           0 :                                       GTK_STYLE_CLASS_PROGRESSBAR);
    1091           0 :       gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH);
    1092           0 :       break;
    1093             :     case MOZ_GTK_GRIPPER:
    1094             :       style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
    1095           0 :                                       GTK_STYLE_CLASS_GRIP);
    1096           0 :       break;
    1097             :     case MOZ_GTK_INFO_BAR:
    1098             :       style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
    1099           0 :                                       GTK_STYLE_CLASS_INFO);
    1100           0 :       break;
    1101             :     case MOZ_GTK_SPINBUTTON_ENTRY:
    1102             :       style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
    1103           0 :                                       GTK_STYLE_CLASS_ENTRY);
    1104           0 :       break;
    1105             :     case MOZ_GTK_SCROLLED_WINDOW:
    1106             :       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
    1107           0 :                                       GTK_STYLE_CLASS_FRAME);
    1108           0 :       break;
    1109             :     case MOZ_GTK_TEXT_VIEW_TEXT:
    1110             :     case MOZ_GTK_RESIZER:
    1111             :       // GTK versions prior to 3.20 do not have the view class on the root
    1112             :       // node, but add this to determine the background for the text window.
    1113           0 :       style = CreateSubStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW);
    1114           0 :       if (aNodeType == MOZ_GTK_RESIZER) {
    1115             :         // The "grip" class provides the correct builtin icon from
    1116             :         // gtk_render_handle().  The icon is drawn with shaded variants of
    1117             :         // the background color, and so a transparent background would lead to
    1118             :         // a transparent resizer.  gtk_render_handle() also uses the
    1119             :         // background color to draw a background, and so this style otherwise
    1120             :         // matches MOZ_GTK_TEXT_VIEW_TEXT to match the background with
    1121             :         // textarea elements.  GtkTextView creates a separate text window and
    1122             :         // so the background should not be transparent.
    1123           0 :         gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
    1124             :       }
    1125           0 :       break;
    1126             :     case MOZ_GTK_FRAME_BORDER:
    1127           0 :       return GetWidgetRootStyle(MOZ_GTK_FRAME);
    1128             :     case MOZ_GTK_TREEVIEW_VIEW:
    1129             :       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
    1130           0 :                                       GTK_STYLE_CLASS_VIEW);
    1131           0 :       break;
    1132             :     case MOZ_GTK_TREEVIEW_EXPANDER:
    1133             :       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
    1134           0 :                                       GTK_STYLE_CLASS_EXPANDER);
    1135           0 :       break;
    1136             :     case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
    1137             :       style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL,
    1138           0 :                                       GTK_STYLE_CLASS_PANE_SEPARATOR);
    1139           0 :       break;
    1140             :     case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
    1141             :       style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_VERTICAL,
    1142           0 :                                       GTK_STYLE_CLASS_PANE_SEPARATOR);
    1143           0 :       break;
    1144             :     case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
    1145             :       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
    1146           0 :                                       GTK_STYLE_CLASS_TROUGH);
    1147           0 :       break;
    1148             :     case MOZ_GTK_SCALE_TROUGH_VERTICAL:
    1149             :       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
    1150           0 :                                       GTK_STYLE_CLASS_TROUGH);
    1151           0 :       break;
    1152             :     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
    1153             :       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
    1154           0 :                                       GTK_STYLE_CLASS_SLIDER);
    1155           0 :       break;
    1156             :     case MOZ_GTK_SCALE_THUMB_VERTICAL:
    1157             :       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
    1158           0 :                                       GTK_STYLE_CLASS_SLIDER);
    1159           0 :       break;
    1160             :     case MOZ_GTK_TAB_TOP:
    1161           0 :       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_TOP);
    1162             :       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
    1163           0 :                                    static_cast<GtkRegionFlags>(0));
    1164           0 :       break;
    1165             :     case MOZ_GTK_TAB_BOTTOM:
    1166           0 :       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_BOTTOM);
    1167             :       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
    1168           0 :                                    static_cast<GtkRegionFlags>(0));
    1169           0 :       break;
    1170             :     case MOZ_GTK_NOTEBOOK:
    1171             :     case MOZ_GTK_NOTEBOOK_HEADER:
    1172             :     case MOZ_GTK_TABPANELS:
    1173             :     case MOZ_GTK_TAB_SCROLLARROW:
    1174             :     { 
    1175           0 :       GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
    1176           0 :       return gtk_widget_get_style_context(widget);
    1177             :     }
    1178             :     default:
    1179           0 :       return GetWidgetRootStyle(aNodeType);
    1180             :   }
    1181             : 
    1182           0 :   MOZ_ASSERT(style);
    1183           0 :   sStyleStorage[aNodeType] = style;
    1184           0 :   return style;
    1185             : }
    1186             : 
    1187             : void
    1188           0 : ResetWidgetCache(void)
    1189             : {
    1190           0 :   for (int i = 0; i < MOZ_GTK_WIDGET_NODE_COUNT; i++) {
    1191           0 :     if (sStyleStorage[i])
    1192           0 :       g_object_unref(sStyleStorage[i]);
    1193             :   }
    1194           0 :   mozilla::PodArrayZero(sStyleStorage);
    1195             : 
    1196             :   /* This will destroy all of our widgets */
    1197           0 :   if (sWidgetStorage[MOZ_GTK_WINDOW])
    1198           0 :     gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
    1199             : 
    1200             :   /* Clear already freed arrays */
    1201           0 :   mozilla::PodArrayZero(sWidgetStorage);
    1202           0 : }
    1203             : 
    1204             : GtkStyleContext*
    1205          56 : ClaimStyleContext(WidgetNodeType aNodeType, GtkTextDirection aDirection,
    1206             :                   GtkStateFlags aStateFlags, StyleFlags aFlags)
    1207             : {
    1208             :   GtkStyleContext* style;
    1209          56 :   if (gtk_check_version(3, 20, 0) != nullptr) {
    1210           0 :     style = GetWidgetStyleInternal(aNodeType);
    1211             :   } else {
    1212          56 :     style = GetCssNodeStyleInternal(aNodeType);
    1213             :   }
    1214          56 :   bool stateChanged = false;
    1215          56 :   bool stateHasDirection = gtk_get_minor_version() >= 8;
    1216          56 :   GtkStateFlags oldState = gtk_style_context_get_state(style);
    1217          56 :   MOZ_ASSERT(!(aStateFlags & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL)));
    1218          56 :   unsigned newState = aStateFlags;
    1219          56 :   if (stateHasDirection) {
    1220          56 :     switch (aDirection) {
    1221             :       case GTK_TEXT_DIR_LTR:
    1222           9 :         newState |= STATE_FLAG_DIR_LTR;
    1223           9 :         break;
    1224             :       case GTK_TEXT_DIR_RTL:
    1225           0 :         newState |= STATE_FLAG_DIR_RTL;
    1226           0 :         break;
    1227             :       default:
    1228           0 :         MOZ_FALLTHROUGH_ASSERT("Bad GtkTextDirection");
    1229             :       case GTK_TEXT_DIR_NONE:
    1230             :         // GtkWidget uses a default direction if neither is explicitly
    1231             :         // specified, but here DIR_NONE is interpreted as meaning the
    1232             :         // direction is not important, so don't change the direction
    1233             :         // unnecessarily.
    1234          47 :         newState |= oldState & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL);
    1235             :     }
    1236           0 :   } else if (aDirection != GTK_TEXT_DIR_NONE) {
    1237           0 :     GtkTextDirection oldDirection = gtk_style_context_get_direction(style);
    1238           0 :     if (aDirection != oldDirection) {
    1239           0 :       gtk_style_context_set_direction(style, aDirection);
    1240           0 :       stateChanged = true;
    1241             :     }
    1242             :   }
    1243          56 :   if (oldState != newState) {
    1244           0 :     gtk_style_context_set_state(style, static_cast<GtkStateFlags>(newState));
    1245           0 :     stateChanged = true;
    1246             :   }
    1247             :   // This invalidate is necessary for unsaved style contexts from GtkWidgets
    1248             :   // in pre-3.18 GTK, because automatic invalidation of such contexts
    1249             :   // was delayed until a resize event runs.
    1250             :   //
    1251             :   // https://bugzilla.mozilla.org/show_bug.cgi?id=1272194#c7
    1252             :   //
    1253             :   // Avoid calling invalidate on contexts that are not owned and constructed
    1254             :   // by widgets to avoid performing build_properties() (in 3.16 stylecontext.c)
    1255             :   // unnecessarily early.
    1256          56 :   if (stateChanged && sWidgetStorage[aNodeType]) {
    1257           0 :     gtk_style_context_invalidate(style);
    1258             :   }
    1259          56 :   return style;
    1260             : }
    1261             : 
    1262             : void
    1263          56 : ReleaseStyleContext(GtkStyleContext* aStyleContext)
    1264             : {
    1265          56 : }

Generated by: LCOV version 1.13