forked from mirrors/gecko-dev
		
	 a1bb7c1c32
			
		
	
	
		a1bb7c1c32
		
	
	
	
	
		
			
			MozReview-Commit-ID: ENWBekzq4Oq --HG-- extra : rebase_source : 6c6c63af840629d5454166dba9e074d9c72b0371
		
			
				
	
	
		
			1546 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1546 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include <dlfcn.h>
 | |
| #include <gtk/gtk.h>
 | |
| #include "WidgetStyleCache.h"
 | |
| #include "gtkdrawing.h"
 | |
| 
 | |
| #define STATE_FLAG_DIR_LTR (1U << 7)
 | |
| #define STATE_FLAG_DIR_RTL (1U << 8)
 | |
| #if GTK_CHECK_VERSION(3,8,0)
 | |
| static_assert(GTK_STATE_FLAG_DIR_LTR == STATE_FLAG_DIR_LTR &&
 | |
|               GTK_STATE_FLAG_DIR_RTL == STATE_FLAG_DIR_RTL,
 | |
|               "incorrect direction state flags");
 | |
| #endif
 | |
| 
 | |
| static GtkWidget* sWidgetStorage[MOZ_GTK_WIDGET_NODE_COUNT];
 | |
| static GtkStyleContext* sStyleStorage[MOZ_GTK_WIDGET_NODE_COUNT];
 | |
| 
 | |
| static GtkStyleContext*
 | |
| GetWidgetRootStyle(WidgetNodeType aNodeType);
 | |
| static GtkStyleContext*
 | |
| GetCssNodeStyleInternal(WidgetNodeType aNodeType);
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateWindowWidget()
 | |
| {
 | |
|   GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
 | |
|   gtk_widget_set_name(widget, "MozillaGtkWidget");
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateWindowContainerWidget()
 | |
| {
 | |
|   GtkWidget *widget = gtk_fixed_new();
 | |
|   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW)), widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static void
 | |
| AddToWindowContainer(GtkWidget* widget)
 | |
| {
 | |
|   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW_CONTAINER)), widget);
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateScrollbarWidget(WidgetNodeType aWidgetType, GtkOrientation aOrientation)
 | |
| {
 | |
|   GtkWidget* widget = gtk_scrollbar_new(aOrientation, nullptr);
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateCheckboxWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_check_button_new_with_label("M");
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateRadiobuttonWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_radio_button_new_with_label(nullptr, "M");
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateMenuBarWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_menu_bar_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateMenuPopupWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_menu_new();
 | |
|   gtk_menu_attach_to_widget(GTK_MENU(widget), GetWidget(MOZ_GTK_WINDOW),
 | |
|                             nullptr);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateProgressWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_progress_bar_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateTooltipWidget()
 | |
| {
 | |
|   MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
 | |
|              "CreateTooltipWidget should be used for Gtk < 3.20 only.");
 | |
|   GtkWidget* widget = CreateWindowWidget();
 | |
|   GtkStyleContext* style = gtk_widget_get_style_context(widget);
 | |
|   gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateExpanderWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_expander_new("M");
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateFrameWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_frame_new(nullptr);
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateGripperWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_handle_box_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateToolbarWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_toolbar_new();
 | |
|   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_GRIPPER)), widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateToolbarSeparatorWidget()
 | |
| {
 | |
|   GtkWidget* widget = GTK_WIDGET(gtk_separator_tool_item_new());
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateInfoBarWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_info_bar_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateButtonWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_button_new_with_label("M");
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateToggleButtonWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_toggle_button_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateButtonArrowWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
 | |
|   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_TOGGLE_BUTTON)), widget);
 | |
|   gtk_widget_show(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateSpinWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_spin_button_new(nullptr, 1, 0);
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   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*
 | |
| CreateMenuSeparatorWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_separator_menu_item_new();
 | |
|   gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)),
 | |
|                         widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateTreeViewWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_tree_view_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateTreeHeaderCellWidget()
 | |
| {
 | |
|   /*
 | |
|    * Some GTK engines paint the first and last cell
 | |
|    * of a TreeView header with a highlight.
 | |
|    * Since we do not know where our widget will be relative
 | |
|    * to the other buttons in the TreeView header, we must
 | |
|    * paint it as a button that is between two others,
 | |
|    * thus ensuring it is neither the first or last button
 | |
|    * in the header.
 | |
|    * GTK doesn't give us a way to do this explicitly,
 | |
|    * so we must paint with a button that is between two
 | |
|    * others.
 | |
|    */
 | |
|   GtkTreeViewColumn* firstTreeViewColumn;
 | |
|   GtkTreeViewColumn* middleTreeViewColumn;
 | |
|   GtkTreeViewColumn* lastTreeViewColumn;
 | |
| 
 | |
|   GtkWidget *treeView = GetWidget(MOZ_GTK_TREEVIEW);
 | |
| 
 | |
|   /* Create and append our three columns */
 | |
|   firstTreeViewColumn = gtk_tree_view_column_new();
 | |
|   gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
 | |
|   gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
 | |
|                               firstTreeViewColumn);
 | |
| 
 | |
|   middleTreeViewColumn = gtk_tree_view_column_new();
 | |
|   gtk_tree_view_column_set_title(middleTreeViewColumn, "M");
 | |
|   gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
 | |
|                               middleTreeViewColumn);
 | |
| 
 | |
|   lastTreeViewColumn = gtk_tree_view_column_new();
 | |
|   gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
 | |
|   gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
 | |
|                               lastTreeViewColumn);
 | |
| 
 | |
|   /* Use the middle column's header for our button */
 | |
|   return gtk_tree_view_column_get_button(middleTreeViewColumn);
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateTreeHeaderSortArrowWidget()
 | |
| {
 | |
|   /* TODO, but it can't be NULL */
 | |
|   GtkWidget* widget = gtk_button_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateHPanedWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateVPanedWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateScaleWidget(GtkOrientation aOrientation)
 | |
| {
 | |
|   GtkWidget* widget = gtk_scale_new(aOrientation, nullptr);
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateNotebookWidget()
 | |
| {
 | |
|   GtkWidget* widget = gtk_notebook_new();
 | |
|   AddToWindowContainer(widget);
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateHeaderBar(WidgetNodeType aWidgetType)
 | |
| {
 | |
|   MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
 | |
|              "GtkHeaderBar is only available on GTK 3.10+.");
 | |
| 
 | |
|   static auto sGtkHeaderBarNewPtr = (GtkWidget* (*)())
 | |
|     dlsym(RTLD_DEFAULT, "gtk_header_bar_new");
 | |
| 
 | |
|   GtkWidget* headerbar = sGtkHeaderBarNewPtr();
 | |
|   if (aWidgetType == MOZ_GTK_HEADER_BAR_MAXIMIZED) {
 | |
|     GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP);
 | |
|     gtk_widget_set_name(window, "MozillaMaximizedGtkWidget");
 | |
|     GtkStyleContext* style = gtk_widget_get_style_context(window);
 | |
|     gtk_style_context_add_class(style, "maximized");
 | |
|     GtkWidget *fixed = gtk_fixed_new();
 | |
|     gtk_container_add(GTK_CONTAINER(window), fixed);
 | |
|     gtk_container_add(GTK_CONTAINER(fixed), headerbar);
 | |
|     // Save the window container so we don't leak it.
 | |
|     sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED] = window;
 | |
|   } else {
 | |
|     AddToWindowContainer(headerbar);
 | |
|   }
 | |
| 
 | |
|   // Emulate what create_titlebar() at gtkwindow.c does.
 | |
|   GtkStyleContext* style = gtk_widget_get_style_context(headerbar);
 | |
|   gtk_style_context_add_class(style, "titlebar");
 | |
| 
 | |
|   // TODO: Define default-decoration titlebar style as workaround
 | |
|   // to ensure the titlebar buttons does not overflow outside.
 | |
|   // Recently the titlebar size is calculated as
 | |
|   // tab size + titlebar border/padding (default-decoration has 6px padding
 | |
|   // at default Adwaita theme).
 | |
|   // We need to fix titlebar size calculation to also include
 | |
|   // titlebar button sizes. (Bug 1419442)
 | |
|   gtk_style_context_add_class(style, "default-decoration");
 | |
| 
 | |
|   return headerbar;
 | |
| }
 | |
| 
 | |
| #define ICON_SCALE_VARIANTS 2
 | |
| 
 | |
| static void
 | |
| LoadWidgetIconPixbuf(GtkWidget* aWidgetIcon)
 | |
| {
 | |
|   GtkStyleContext* style = gtk_widget_get_style_context(aWidgetIcon);
 | |
| 
 | |
|   const gchar *iconName;
 | |
|   GtkIconSize gtkIconSize;
 | |
|   gtk_image_get_icon_name(GTK_IMAGE(aWidgetIcon), &iconName, >kIconSize);
 | |
| 
 | |
|   gint iconWidth, iconHeight;
 | |
|   gtk_icon_size_lookup(gtkIconSize, &iconWidth, &iconHeight);
 | |
| 
 | |
|   /* Those are available since Gtk+ 3.10 as well as GtkHeaderBar */
 | |
|   static auto sGtkIconThemeLookupIconForScalePtr =
 | |
|     (GtkIconInfo* (*)(GtkIconTheme *, const gchar *, gint, gint, GtkIconLookupFlags))
 | |
|     dlsym(RTLD_DEFAULT, "gtk_icon_theme_lookup_icon_for_scale");
 | |
|   static auto sGdkCairoSurfaceCreateFromPixbufPtr =
 | |
|     (cairo_surface_t * (*)(const GdkPixbuf *, int, GdkWindow *))
 | |
|     dlsym(RTLD_DEFAULT, "gdk_cairo_surface_create_from_pixbuf");
 | |
| 
 | |
|   for (int scale = 1; scale < ICON_SCALE_VARIANTS+1; scale++) {
 | |
|     GtkIconInfo *gtkIconInfo =
 | |
|       sGtkIconThemeLookupIconForScalePtr(gtk_icon_theme_get_default(),
 | |
|                                          iconName,
 | |
|                                          iconWidth,
 | |
|                                          scale,
 | |
|                                          (GtkIconLookupFlags)0);
 | |
| 
 | |
|     if (!gtkIconInfo) {
 | |
|       // We miss the icon, nothing to do here.
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     gboolean unused;
 | |
|     GdkPixbuf *iconPixbuf =
 | |
|       gtk_icon_info_load_symbolic_for_context(gtkIconInfo, style,
 | |
|                                               &unused, nullptr);
 | |
|     g_object_unref(G_OBJECT(gtkIconInfo));
 | |
| 
 | |
|     cairo_surface_t* iconSurface =
 | |
|       sGdkCairoSurfaceCreateFromPixbufPtr(iconPixbuf, scale, nullptr);
 | |
|     g_object_unref(iconPixbuf);
 | |
| 
 | |
|     nsAutoCString surfaceName;
 | |
|     surfaceName = nsPrintfCString("MozillaIconSurface%d", scale);
 | |
|     g_object_set_data_full(G_OBJECT(aWidgetIcon), surfaceName.get(),
 | |
|                           iconSurface,
 | |
|                           (GDestroyNotify)cairo_surface_destroy);
 | |
|   }
 | |
| }
 | |
| 
 | |
| cairo_surface_t*
 | |
| GetWidgetIconSurface(GtkWidget* aWidgetIcon, int aScale)
 | |
| {
 | |
|   if (aScale > ICON_SCALE_VARIANTS)
 | |
|     aScale = ICON_SCALE_VARIANTS;
 | |
| 
 | |
|   nsAutoCString surfaceName;
 | |
|   surfaceName = nsPrintfCString("MozillaIconSurface%d", aScale);
 | |
|   return (cairo_surface_t*)
 | |
|     g_object_get_data(G_OBJECT(aWidgetIcon), surfaceName.get());
 | |
| }
 | |
| 
 | |
| static void
 | |
| CreateHeaderBarButton(GtkWidget* aParentWidget,
 | |
|                       WidgetNodeType aWidgetType)
 | |
| {
 | |
|   GtkWidget* widget = gtk_button_new();
 | |
| 
 | |
|   // We have to add button to widget hierarchy now to pick
 | |
|   // right icon style at LoadWidgetIconPixbuf().
 | |
|   if (GTK_IS_BOX(aParentWidget)) {
 | |
|       gtk_box_pack_start(GTK_BOX(aParentWidget), widget, FALSE, FALSE, 0);
 | |
|   } else {
 | |
|       gtk_container_add(GTK_CONTAINER(aParentWidget), widget);
 | |
|   }
 | |
| 
 | |
|   // We bypass GetWidget() here because we create all titlebar
 | |
|   // buttons at once when a first one is requested.
 | |
|   NS_ASSERTION(sWidgetStorage[aWidgetType] == nullptr,
 | |
|                "Titlebar button is already created!");
 | |
|   sWidgetStorage[aWidgetType] = widget;
 | |
| 
 | |
|   // We need to show the button widget now as GtkBox does not
 | |
|   // place invisible widgets and we'll miss first-child/last-child
 | |
|   // css selectors at the buttons otherwise.
 | |
|   gtk_widget_show(widget);
 | |
| 
 | |
|   GtkStyleContext* style = gtk_widget_get_style_context(widget);
 | |
|   gtk_style_context_add_class(style, "titlebutton");
 | |
| 
 | |
|   GtkWidget *image = nullptr;
 | |
|   switch (aWidgetType) {
 | |
|      case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
 | |
|        gtk_style_context_add_class(style, "close");
 | |
|        image = gtk_image_new_from_icon_name("window-close-symbolic",
 | |
|                                            GTK_ICON_SIZE_MENU);
 | |
|        break;
 | |
|      case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
 | |
|        gtk_style_context_add_class(style, "minimize");
 | |
|        image = gtk_image_new_from_icon_name("window-minimize-symbolic",
 | |
|                                             GTK_ICON_SIZE_MENU);
 | |
|        break;
 | |
| 
 | |
|      case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
 | |
|        gtk_style_context_add_class(style, "maximize");
 | |
|        image = gtk_image_new_from_icon_name("window-maximize-symbolic",
 | |
|                                             GTK_ICON_SIZE_MENU);
 | |
|        break;
 | |
| 
 | |
|      case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
 | |
|        gtk_style_context_add_class(style, "maximize");
 | |
|        image = gtk_image_new_from_icon_name("window-restore-symbolic",
 | |
|                                             GTK_ICON_SIZE_MENU);
 | |
|        break;
 | |
|      default:
 | |
|        break;
 | |
|    }
 | |
| 
 | |
|    gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
 | |
|    g_object_set(image, "use-fallback", TRUE, NULL);
 | |
|    gtk_container_add(GTK_CONTAINER (widget), image);
 | |
| 
 | |
|    // We bypass GetWidget() here by explicit sWidgetStorage[] update so
 | |
|    // invalidate the style as well as GetWidget() does.
 | |
|    style = gtk_widget_get_style_context(image);
 | |
|    gtk_style_context_invalidate(style);
 | |
| 
 | |
|    LoadWidgetIconPixbuf(image);
 | |
| }
 | |
| 
 | |
| static bool
 | |
| IsToolbarButtonEnabled(WidgetNodeType* aButtonLayout, int aButtonNums,
 | |
|                        WidgetNodeType aWidgetType)
 | |
| {
 | |
|     for (int i = 0; i < aButtonNums; i++) {
 | |
|       if (aButtonLayout[i] == aWidgetType) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static void
 | |
| CreateHeaderBarButtons()
 | |
| {
 | |
|   MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
 | |
|              "GtkHeaderBar is only available on GTK 3.10+.");
 | |
| 
 | |
|   GtkWidget* headerBar = GetWidget(MOZ_GTK_HEADER_BAR);
 | |
| 
 | |
|   gint buttonSpacing = 6;
 | |
|   g_object_get(headerBar, "spacing", &buttonSpacing, nullptr);
 | |
| 
 | |
|   GtkWidget *buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, buttonSpacing);
 | |
|   gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_HEADER_BAR)), buttonBox);
 | |
| 
 | |
|   WidgetNodeType buttonLayout[TOOLBAR_BUTTONS];
 | |
|   int activeButtons =
 | |
|       GetGtkHeaderBarButtonLayout(buttonLayout, TOOLBAR_BUTTONS);
 | |
| 
 | |
|   if (IsToolbarButtonEnabled(buttonLayout, activeButtons,
 | |
|                              MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE)) {
 | |
|     CreateHeaderBarButton(buttonBox, MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE);
 | |
|   }
 | |
|   if (IsToolbarButtonEnabled(buttonLayout, activeButtons,
 | |
|                              MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE)) {
 | |
|     CreateHeaderBarButton(buttonBox, MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE);
 | |
|     // We don't pack "restore" headerbar button to box as it's an icon
 | |
|     // placeholder. Pack it only to header bar to get correct style.
 | |
|     CreateHeaderBarButton(GetWidget(MOZ_GTK_HEADER_BAR),
 | |
|                           MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE);
 | |
|   }
 | |
|   if (IsToolbarButtonEnabled(buttonLayout, activeButtons,
 | |
|                              MOZ_GTK_HEADER_BAR_BUTTON_CLOSE)) {
 | |
|     CreateHeaderBarButton(buttonBox, MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static GtkWidget*
 | |
| CreateWidget(WidgetNodeType aWidgetType)
 | |
| {
 | |
|   switch (aWidgetType) {
 | |
|     case MOZ_GTK_WINDOW:
 | |
|       return CreateWindowWidget();
 | |
|     case MOZ_GTK_WINDOW_CONTAINER:
 | |
|       return CreateWindowContainerWidget();
 | |
|     case MOZ_GTK_CHECKBUTTON_CONTAINER:
 | |
|       return CreateCheckboxWidget();
 | |
|     case MOZ_GTK_PROGRESSBAR:
 | |
|       return CreateProgressWidget();
 | |
|     case MOZ_GTK_RADIOBUTTON_CONTAINER:
 | |
|       return CreateRadiobuttonWidget();
 | |
|     case MOZ_GTK_SCROLLBAR_HORIZONTAL:
 | |
|       return CreateScrollbarWidget(aWidgetType,
 | |
|                                    GTK_ORIENTATION_HORIZONTAL);
 | |
|     case MOZ_GTK_SCROLLBAR_VERTICAL:
 | |
|       return CreateScrollbarWidget(aWidgetType,
 | |
|                                    GTK_ORIENTATION_VERTICAL);
 | |
|     case MOZ_GTK_MENUBAR:
 | |
|       return CreateMenuBarWidget();
 | |
|     case MOZ_GTK_MENUPOPUP:
 | |
|       return CreateMenuPopupWidget();
 | |
|     case MOZ_GTK_MENUSEPARATOR:
 | |
|       return CreateMenuSeparatorWidget();
 | |
|     case MOZ_GTK_EXPANDER:
 | |
|       return CreateExpanderWidget();
 | |
|     case MOZ_GTK_FRAME:
 | |
|       return CreateFrameWidget();
 | |
|     case MOZ_GTK_GRIPPER:
 | |
|       return CreateGripperWidget();
 | |
|     case MOZ_GTK_TOOLBAR:
 | |
|       return CreateToolbarWidget();
 | |
|     case MOZ_GTK_TOOLBAR_SEPARATOR:
 | |
|       return CreateToolbarSeparatorWidget();
 | |
|     case MOZ_GTK_INFO_BAR:
 | |
|       return CreateInfoBarWidget();
 | |
|     case MOZ_GTK_SPINBUTTON:
 | |
|       return CreateSpinWidget();
 | |
|     case MOZ_GTK_BUTTON:
 | |
|       return CreateButtonWidget();
 | |
|     case MOZ_GTK_TOGGLE_BUTTON:
 | |
|       return CreateToggleButtonWidget();
 | |
|     case MOZ_GTK_BUTTON_ARROW:
 | |
|       return CreateButtonArrowWidget();
 | |
|     case MOZ_GTK_ENTRY:
 | |
|       return CreateEntryWidget();
 | |
|     case MOZ_GTK_SCROLLED_WINDOW:
 | |
|       return CreateScrolledWindowWidget();
 | |
|     case MOZ_GTK_TREEVIEW:
 | |
|       return CreateTreeViewWidget();
 | |
|     case MOZ_GTK_TREE_HEADER_CELL:
 | |
|       return CreateTreeHeaderCellWidget();
 | |
|     case MOZ_GTK_TREE_HEADER_SORTARROW:
 | |
|       return CreateTreeHeaderSortArrowWidget();
 | |
|     case MOZ_GTK_SPLITTER_HORIZONTAL:
 | |
|       return CreateHPanedWidget();
 | |
|     case MOZ_GTK_SPLITTER_VERTICAL:
 | |
|       return CreateVPanedWidget();
 | |
|     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();
 | |
|     case MOZ_GTK_HEADER_BAR:
 | |
|     case MOZ_GTK_HEADER_BAR_MAXIMIZED:
 | |
|       return CreateHeaderBar(aWidgetType);
 | |
|     case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
 | |
|     case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
 | |
|     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
 | |
|     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
 | |
|       CreateHeaderBarButtons();
 | |
|       return sWidgetStorage[aWidgetType];
 | |
|     default:
 | |
|       /* Not implemented */
 | |
|       return nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| GtkWidget*
 | |
| GetWidget(WidgetNodeType aWidgetType)
 | |
| {
 | |
|   GtkWidget* widget = sWidgetStorage[aWidgetType];
 | |
|   if (!widget) {
 | |
|     widget = CreateWidget(aWidgetType);
 | |
|     // Some widgets (MOZ_GTK_COMBOBOX_SEPARATOR for instance) may not be
 | |
|     // available or implemented.
 | |
|     if (!widget)
 | |
|       return nullptr;
 | |
|     // In GTK versions prior to 3.18, automatic invalidation of style contexts
 | |
|     // for widgets was delayed until the next resize event.  Gecko however,
 | |
|     // typically uses the style context before the resize event runs and so an
 | |
|     // explicit invalidation may be required.  This is necessary if a style
 | |
|     // property was retrieved before all changes were made to the style
 | |
|     // context.  One such situation is where gtk_button_construct_child()
 | |
|     // retrieves the style property "image-spacing" during construction of the
 | |
|     // GtkButton, before its parent is set to provide inheritance of ancestor
 | |
|     // properties.  More recent GTK versions do not need this, but do not
 | |
|     // re-resolve until required and so invalidation does not trigger
 | |
|     // unnecessary resolution in general.
 | |
|     GtkStyleContext* style = gtk_widget_get_style_context(widget);
 | |
|     gtk_style_context_invalidate(style);
 | |
| 
 | |
|     sWidgetStorage[aWidgetType] = widget;
 | |
|   }
 | |
|   return widget;
 | |
| }
 | |
| 
 | |
| static void
 | |
| AddStyleClassesFromStyle(GtkStyleContext* aDest, GtkStyleContext* aSrc)
 | |
| {
 | |
|   GList* classes = gtk_style_context_list_classes(aSrc);
 | |
|   for (GList* link = classes; link; link = link->next) {
 | |
|     gtk_style_context_add_class(aDest, static_cast<gchar*>(link->data));
 | |
|   }
 | |
|   g_list_free(classes);
 | |
| }
 | |
| 
 | |
| GtkStyleContext*
 | |
| CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle)
 | |
| {
 | |
|   static auto sGtkWidgetClassGetCSSName =
 | |
|     reinterpret_cast<const char* (*)(GtkWidgetClass*)>
 | |
|     (dlsym(RTLD_DEFAULT, "gtk_widget_class_get_css_name"));
 | |
| 
 | |
|   GtkWidgetClass *widgetClass = GTK_WIDGET_GET_CLASS(aWidget);
 | |
|   const gchar* name = sGtkWidgetClassGetCSSName ?
 | |
|     sGtkWidgetClassGetCSSName(widgetClass) : nullptr;
 | |
| 
 | |
|   GtkStyleContext *context =
 | |
|     CreateCSSNode(name, aParentStyle, G_TYPE_FROM_CLASS(widgetClass));
 | |
| 
 | |
|   // Classes are stored on the style context instead of the path so that any
 | |
|   // future gtk_style_context_save() will inherit classes on the head CSS
 | |
|   // node, in the same way as happens when called on a style context owned by
 | |
|   // a widget.
 | |
|   //
 | |
|   // Classes can be stored on a GtkCssNodeDeclaration and/or the path.
 | |
|   // gtk_style_context_save() reuses the GtkCssNodeDeclaration, and appends a
 | |
|   // new object to the path, without copying the classes from the old path
 | |
|   // head.  The new head picks up classes from the GtkCssNodeDeclaration, but
 | |
|   // not the path.  GtkWidgets store their classes on the
 | |
|   // GtkCssNodeDeclaration, so make sure to add classes there.
 | |
|   //
 | |
|   // Picking up classes from the style context also means that
 | |
|   // https://bugzilla.gnome.org/show_bug.cgi?id=767312, which can stop
 | |
|   // gtk_widget_path_append_for_widget() from finding classes in GTK 3.20,
 | |
|   // is not a problem.
 | |
|   GtkStyleContext* widgetStyle = gtk_widget_get_style_context(aWidget);
 | |
|   AddStyleClassesFromStyle(context, widgetStyle);
 | |
| 
 | |
|   // Release any floating reference on aWidget.
 | |
|   g_object_ref_sink(aWidget);
 | |
|   g_object_unref(aWidget);
 | |
| 
 | |
|   return context;
 | |
| }
 | |
| 
 | |
| static GtkStyleContext*
 | |
| CreateStyleForWidget(GtkWidget* aWidget, WidgetNodeType aParentType)
 | |
| {
 | |
|   return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType));
 | |
| }
 | |
| 
 | |
| GtkStyleContext*
 | |
| CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle, GType aType)
 | |
| {
 | |
|   static auto sGtkWidgetPathIterSetObjectName =
 | |
|     reinterpret_cast<void (*)(GtkWidgetPath *, gint, const char *)>
 | |
|     (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name"));
 | |
| 
 | |
|   GtkWidgetPath* path;
 | |
|   if (aParentStyle) {
 | |
|     path = gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle));
 | |
|     // Copy classes from the parent style context to its corresponding node in
 | |
|     // the path, because GTK will only match against ancestor classes if they
 | |
|     // are on the path.
 | |
|     GList* classes = gtk_style_context_list_classes(aParentStyle);
 | |
|     for (GList* link = classes; link; link = link->next) {
 | |
|       gtk_widget_path_iter_add_class(path, -1, static_cast<gchar*>(link->data));
 | |
|     }
 | |
|     g_list_free(classes);
 | |
|   } else {
 | |
|     path = gtk_widget_path_new();
 | |
|   }
 | |
| 
 | |
|   gtk_widget_path_append_type(path, aType);
 | |
| 
 | |
|   if (sGtkWidgetPathIterSetObjectName) {
 | |
|     (*sGtkWidgetPathIterSetObjectName)(path, -1, aName);
 | |
|   }
 | |
| 
 | |
|   GtkStyleContext *context = gtk_style_context_new();
 | |
|   gtk_style_context_set_path(context, path);
 | |
|   gtk_style_context_set_parent(context, aParentStyle);
 | |
|   gtk_widget_path_unref(path);
 | |
| 
 | |
|   // In GTK 3.4, gtk_render_* functions use |theming_engine| on the style
 | |
|   // context without ensuring any style resolution sets it appropriately
 | |
|   // in style_data_lookup(). e.g.
 | |
|   // https://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=3.4.4#n3847
 | |
|   //
 | |
|   // That can result in incorrect drawing on first draw.  To work around this,
 | |
|   // force a style look-up to set |theming_engine|.  It is sufficient to do
 | |
|   // this only on context creation, instead of after every modification to the
 | |
|   // context, because themes typically (Ambiance and oxygen-gtk, at least) set
 | |
|   // the "engine" property with the '*' selector.
 | |
|   if (GTK_MAJOR_VERSION == 3 && gtk_get_minor_version() < 6) {
 | |
|     GdkRGBA unused;
 | |
|     gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, &unused);
 | |
|   }
 | |
| 
 | |
|   return context;
 | |
| }
 | |
| 
 | |
| // Return a style context matching that of the root CSS node of a widget.
 | |
| // This is used by all GTK versions.
 | |
| static GtkStyleContext*
 | |
| GetWidgetRootStyle(WidgetNodeType aNodeType)
 | |
| {
 | |
|   GtkStyleContext* style = sStyleStorage[aNodeType];
 | |
|   if (style)
 | |
|     return style;
 | |
| 
 | |
|   switch (aNodeType) {
 | |
|     case MOZ_GTK_MENUBARITEM:
 | |
|       style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUBAR);
 | |
|       break;
 | |
|     case MOZ_GTK_MENUITEM:
 | |
|       style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUPOPUP);
 | |
|       break;
 | |
|     case MOZ_GTK_CHECKMENUITEM:
 | |
|       style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP);
 | |
|       break;
 | |
|     case MOZ_GTK_RADIOMENUITEM:
 | |
|       style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr),
 | |
|                                    MOZ_GTK_MENUPOPUP);
 | |
|       break;
 | |
|     case MOZ_GTK_TEXT_VIEW:
 | |
|       style = CreateStyleForWidget(gtk_text_view_new(),
 | |
|                                    MOZ_GTK_SCROLLED_WINDOW);
 | |
|       break;
 | |
|     case MOZ_GTK_TOOLTIP:
 | |
|       if (gtk_check_version(3, 20, 0) != nullptr) {
 | |
|           // The tooltip style class is added first in CreateTooltipWidget()
 | |
|           // and transfered to style in CreateStyleForWidget().
 | |
|           GtkWidget* tooltipWindow = CreateTooltipWidget();
 | |
|           style = CreateStyleForWidget(tooltipWindow, nullptr);
 | |
|           gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference.
 | |
|       } else {
 | |
|           // We create this from the path because GtkTooltipWindow is not public.
 | |
|           style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
 | |
|           gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
 | |
|       }
 | |
|       break;
 | |
|     case MOZ_GTK_TOOLTIP_BOX:
 | |
|       style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
 | |
|                                    MOZ_GTK_TOOLTIP);
 | |
|       break;
 | |
|     case MOZ_GTK_TOOLTIP_BOX_LABEL:
 | |
|       style = CreateStyleForWidget(gtk_label_new(nullptr),
 | |
|                                    MOZ_GTK_TOOLTIP_BOX);
 | |
|       break;
 | |
|     default:
 | |
|       GtkWidget* widget = GetWidget(aNodeType);
 | |
|       MOZ_ASSERT(widget);
 | |
|       return gtk_widget_get_style_context(widget);
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(style);
 | |
|   sStyleStorage[aNodeType] = style;
 | |
|   return style;
 | |
| }
 | |
| 
 | |
| static GtkStyleContext*
 | |
| CreateChildCSSNode(const char* aName, WidgetNodeType aParentNodeType)
 | |
| {
 | |
|   return CreateCSSNode(aName, GetCssNodeStyleInternal(aParentNodeType));
 | |
| }
 | |
| 
 | |
| // Create a style context equivalent to a saved root style context of
 | |
| // |aWidgetType| with |aStyleClass| as an additional class.  This is used to
 | |
| // produce a context equivalent to what GTK versions < 3.20 use for many
 | |
| // internal parts of widgets.
 | |
| static GtkStyleContext*
 | |
| CreateSubStyleWithClass(WidgetNodeType aWidgetType, const gchar* aStyleClass)
 | |
| {
 | |
|   static auto sGtkWidgetPathIterGetObjectName =
 | |
|     reinterpret_cast<const char* (*)(const GtkWidgetPath*, gint)>
 | |
|     (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_object_name"));
 | |
| 
 | |
|   GtkStyleContext* parentStyle = GetWidgetRootStyle(aWidgetType);
 | |
| 
 | |
|   // Create a new context that behaves like |parentStyle| would after
 | |
|   // gtk_style_context_save(parentStyle).
 | |
|   //
 | |
|   // Avoiding gtk_style_context_save() avoids the need to manage the
 | |
|   // restore, and a new context permits caching style resolution.
 | |
|   //
 | |
|   // gtk_style_context_save(context) changes the node hierarchy of |context|
 | |
|   // to add a new GtkCssNodeDeclaration that is a copy of its original node.
 | |
|   // The new node is a child of the original node, and so the new heirarchy is
 | |
|   // one level deeper.  The new node receives the same classes as the
 | |
|   // original, but any changes to the classes on |context| will change only
 | |
|   // the new node.  The new node inherits properties from the original node
 | |
|   // (which retains the original heirarchy and classes) and matches CSS rules
 | |
|   // with the new heirarchy and any changes to the classes.
 | |
|   //
 | |
|   // The change in hierarchy can produce some surprises in matching theme CSS
 | |
|   // rules (e.g. https://bugzilla.gnome.org/show_bug.cgi?id=761870#c2), but it
 | |
|   // is important here to produce the same behavior so that rules match the
 | |
|   // same widget parts in Gecko as they do in GTK.
 | |
|   //
 | |
|   // When using public GTK API to construct style contexts, a widget path is
 | |
|   // required.  CSS rules are not matched against the style context heirarchy
 | |
|   // but according to the heirarchy in the widget path.  The path that matches
 | |
|   // the same CSS rules as a saved context is like the path of |parentStyle|
 | |
|   // but with an extra copy of the head (last) object appended.  Setting
 | |
|   // |parentStyle| as the parent context provides the same inheritance of
 | |
|   // properties from the widget root node.
 | |
|   const GtkWidgetPath* parentPath = gtk_style_context_get_path(parentStyle);
 | |
|   const gchar* name = sGtkWidgetPathIterGetObjectName ?
 | |
|     sGtkWidgetPathIterGetObjectName(parentPath, -1) : nullptr;
 | |
|   GType objectType = gtk_widget_path_get_object_type(parentPath);
 | |
| 
 | |
|   GtkStyleContext* style = CreateCSSNode(name, parentStyle, objectType);
 | |
| 
 | |
|   // Start with the same classes on the new node as were on |parentStyle|.
 | |
|   // GTK puts no regions or junction_sides on widget root nodes, and so there
 | |
|   // is no need to copy these.
 | |
|   AddStyleClassesFromStyle(style, parentStyle);
 | |
| 
 | |
|   gtk_style_context_add_class(style, aStyleClass);
 | |
|   return style;
 | |
| }
 | |
| 
 | |
| /* GetCssNodeStyleInternal is used by Gtk >= 3.20 */
 | |
| static GtkStyleContext*
 | |
| GetCssNodeStyleInternal(WidgetNodeType aNodeType)
 | |
| {
 | |
|   GtkStyleContext* style = sStyleStorage[aNodeType];
 | |
|   if (style)
 | |
|     return style;
 | |
| 
 | |
|   switch (aNodeType) {
 | |
|     case MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL:
 | |
|       style = CreateChildCSSNode("contents",
 | |
|                                  MOZ_GTK_SCROLLBAR_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
 | |
|                                  MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
 | |
|                                  MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL:
 | |
|       style = CreateChildCSSNode("contents",
 | |
|                                  MOZ_GTK_SCROLLBAR_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
 | |
|                                  MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
 | |
|                                  MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_BUTTON:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_BUTTON,
 | |
|                                  MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_RADIOBUTTON:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
 | |
|                                  MOZ_GTK_RADIOBUTTON_CONTAINER);
 | |
|       break;
 | |
|     case MOZ_GTK_CHECKBUTTON:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
 | |
|                                  MOZ_GTK_CHECKBUTTON_CONTAINER);
 | |
|       break;
 | |
|     case MOZ_GTK_RADIOMENUITEM_INDICATOR:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
 | |
|                                  MOZ_GTK_RADIOMENUITEM);
 | |
|       break;
 | |
|     case MOZ_GTK_CHECKMENUITEM_INDICATOR:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
 | |
|                                  MOZ_GTK_CHECKMENUITEM);
 | |
|       break;
 | |
|     case MOZ_GTK_PROGRESS_TROUGH:
 | |
|       /* Progress bar background (trough) */
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
 | |
|                                  MOZ_GTK_PROGRESSBAR);
 | |
|       break;
 | |
|     case MOZ_GTK_PROGRESS_CHUNK:
 | |
|       style = CreateChildCSSNode("progress",
 | |
|                                  MOZ_GTK_PROGRESS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_GRIPPER:
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
 | |
|                                       GTK_STYLE_CLASS_GRIP);
 | |
|       break;
 | |
|     case MOZ_GTK_INFO_BAR:
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
 | |
|                                       GTK_STYLE_CLASS_INFO);
 | |
|       break;
 | |
|     case MOZ_GTK_SPINBUTTON_ENTRY:
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
 | |
|                                       GTK_STYLE_CLASS_ENTRY);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLED_WINDOW:
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
 | |
|                                       GTK_STYLE_CLASS_FRAME);
 | |
|       break;
 | |
|     case MOZ_GTK_TEXT_VIEW_TEXT:
 | |
|     case MOZ_GTK_RESIZER:
 | |
|       style = CreateChildCSSNode("text", MOZ_GTK_TEXT_VIEW);
 | |
|       if (aNodeType == MOZ_GTK_RESIZER) {
 | |
|         // The "grip" class provides the correct builtin icon from
 | |
|         // gtk_render_handle().  The icon is drawn with shaded variants of
 | |
|         // the background color, and so a transparent background would lead to
 | |
|         // a transparent resizer.  gtk_render_handle() also uses the
 | |
|         // background color to draw a background, and so this style otherwise
 | |
|         // matches what is used in GtkTextView to match the background with
 | |
|         // textarea elements.
 | |
|         GdkRGBA color;
 | |
|         gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL,
 | |
|                                                &color);
 | |
|         if (color.alpha == 0.0) {
 | |
|           g_object_unref(style);
 | |
|           style = CreateStyleForWidget(gtk_text_view_new(),
 | |
|                                        MOZ_GTK_SCROLLED_WINDOW);
 | |
|         }
 | |
|         gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
 | |
|       }
 | |
|       break;
 | |
|     case MOZ_GTK_FRAME_BORDER:
 | |
|       style = CreateChildCSSNode("border", MOZ_GTK_FRAME);
 | |
|       break;
 | |
|     case MOZ_GTK_TREEVIEW_VIEW:
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
 | |
|                                       GTK_STYLE_CLASS_VIEW);
 | |
|       break;
 | |
|     case MOZ_GTK_TREEVIEW_EXPANDER:
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
 | |
|                                       GTK_STYLE_CLASS_EXPANDER);
 | |
|       break;
 | |
|     case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
 | |
|       style = CreateChildCSSNode("separator",
 | |
|                                  MOZ_GTK_SPLITTER_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
 | |
|       style = CreateChildCSSNode("separator",
 | |
|                                  MOZ_GTK_SPLITTER_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_CONTENTS_HORIZONTAL:
 | |
|       style = CreateChildCSSNode("contents",
 | |
|                                  MOZ_GTK_SCALE_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_CONTENTS_VERTICAL:
 | |
|       style = CreateChildCSSNode("contents",
 | |
|                                  MOZ_GTK_SCALE_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
 | |
|                                  MOZ_GTK_SCALE_CONTENTS_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_TROUGH_VERTICAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
 | |
|                                  MOZ_GTK_SCALE_CONTENTS_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
 | |
|                                  MOZ_GTK_SCALE_TROUGH_HORIZONTAL);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_THUMB_VERTICAL:
 | |
|       style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
 | |
|                                  MOZ_GTK_SCALE_TROUGH_VERTICAL);
 | |
|       break;
 | |
|     case MOZ_GTK_TAB_TOP:
 | |
|     {
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
 | |
|                                       GTK_STYLE_CLASS_TOP);
 | |
|       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
 | |
|                                    static_cast<GtkRegionFlags>(0));
 | |
|       break;
 | |
|     }
 | |
|     case MOZ_GTK_TAB_BOTTOM:
 | |
|     {
 | |
|       // TODO - create from CSS node
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
 | |
|                                       GTK_STYLE_CLASS_BOTTOM);
 | |
|       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
 | |
|                                    static_cast<GtkRegionFlags>(0));
 | |
|       break;
 | |
|     }
 | |
|     case MOZ_GTK_NOTEBOOK:
 | |
|     case MOZ_GTK_NOTEBOOK_HEADER:
 | |
|     case MOZ_GTK_TABPANELS:
 | |
|     case MOZ_GTK_TAB_SCROLLARROW:
 | |
|     {
 | |
|       // TODO - create from CSS node
 | |
|       GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
 | |
|       return gtk_widget_get_style_context(widget);
 | |
|     }
 | |
|     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
 | |
|     {
 | |
|       NS_ASSERTION(false,
 | |
|           "MOZ_GTK_HEADER_BAR_BUTTON_RESTORE is used as an icon only!");
 | |
|       return nullptr;
 | |
|     }
 | |
|     default:
 | |
|       return GetWidgetRootStyle(aNodeType);
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(style, "missing style context for node type");
 | |
|   sStyleStorage[aNodeType] = style;
 | |
|   return style;
 | |
| }
 | |
| 
 | |
| /* GetWidgetStyleInternal is used by Gtk < 3.20 */
 | |
| static GtkStyleContext*
 | |
| GetWidgetStyleInternal(WidgetNodeType aNodeType)
 | |
| {
 | |
|   GtkStyleContext* style = sStyleStorage[aNodeType];
 | |
|   if (style)
 | |
|     return style;
 | |
| 
 | |
|   switch (aNodeType) {
 | |
|     case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
 | |
|                                       GTK_STYLE_CLASS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
 | |
|                                       GTK_STYLE_CLASS_SLIDER);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
 | |
|                                       GTK_STYLE_CLASS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
 | |
|                                       GTK_STYLE_CLASS_SLIDER);
 | |
|       break;
 | |
|     case MOZ_GTK_RADIOBUTTON:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_RADIOBUTTON_CONTAINER,
 | |
|                                       GTK_STYLE_CLASS_RADIO);
 | |
|       break;
 | |
|     case MOZ_GTK_CHECKBUTTON:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER,
 | |
|                                       GTK_STYLE_CLASS_CHECK);
 | |
|       break;
 | |
|     case MOZ_GTK_RADIOMENUITEM_INDICATOR:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_RADIOMENUITEM,
 | |
|                                       GTK_STYLE_CLASS_RADIO);
 | |
|       break;
 | |
|     case MOZ_GTK_CHECKMENUITEM_INDICATOR:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_CHECKMENUITEM,
 | |
|                                       GTK_STYLE_CLASS_CHECK);
 | |
|       break;
 | |
|     case MOZ_GTK_PROGRESS_TROUGH:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
 | |
|                                       GTK_STYLE_CLASS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_PROGRESS_CHUNK:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
 | |
|                                       GTK_STYLE_CLASS_PROGRESSBAR);
 | |
|       gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_GRIPPER:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
 | |
|                                       GTK_STYLE_CLASS_GRIP);
 | |
|       break;
 | |
|     case MOZ_GTK_INFO_BAR:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
 | |
|                                       GTK_STYLE_CLASS_INFO);
 | |
|       break;
 | |
|     case MOZ_GTK_SPINBUTTON_ENTRY:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
 | |
|                                       GTK_STYLE_CLASS_ENTRY);
 | |
|       break;
 | |
|     case MOZ_GTK_SCROLLED_WINDOW:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
 | |
|                                       GTK_STYLE_CLASS_FRAME);
 | |
|       break;
 | |
|     case MOZ_GTK_TEXT_VIEW_TEXT:
 | |
|     case MOZ_GTK_RESIZER:
 | |
|       // GTK versions prior to 3.20 do not have the view class on the root
 | |
|       // node, but add this to determine the background for the text window.
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW);
 | |
|       if (aNodeType == MOZ_GTK_RESIZER) {
 | |
|         // The "grip" class provides the correct builtin icon from
 | |
|         // gtk_render_handle().  The icon is drawn with shaded variants of
 | |
|         // the background color, and so a transparent background would lead to
 | |
|         // a transparent resizer.  gtk_render_handle() also uses the
 | |
|         // background color to draw a background, and so this style otherwise
 | |
|         // matches MOZ_GTK_TEXT_VIEW_TEXT to match the background with
 | |
|         // textarea elements.  GtkTextView creates a separate text window and
 | |
|         // so the background should not be transparent.
 | |
|         gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
 | |
|       }
 | |
|       break;
 | |
|     case MOZ_GTK_FRAME_BORDER:
 | |
|       return GetWidgetRootStyle(MOZ_GTK_FRAME);
 | |
|     case MOZ_GTK_TREEVIEW_VIEW:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
 | |
|                                       GTK_STYLE_CLASS_VIEW);
 | |
|       break;
 | |
|     case MOZ_GTK_TREEVIEW_EXPANDER:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
 | |
|                                       GTK_STYLE_CLASS_EXPANDER);
 | |
|       break;
 | |
|     case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL,
 | |
|                                       GTK_STYLE_CLASS_PANE_SEPARATOR);
 | |
|       break;
 | |
|     case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_VERTICAL,
 | |
|                                       GTK_STYLE_CLASS_PANE_SEPARATOR);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
 | |
|                                       GTK_STYLE_CLASS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_TROUGH_VERTICAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
 | |
|                                       GTK_STYLE_CLASS_TROUGH);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
 | |
|                                       GTK_STYLE_CLASS_SLIDER);
 | |
|       break;
 | |
|     case MOZ_GTK_SCALE_THUMB_VERTICAL:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
 | |
|                                       GTK_STYLE_CLASS_SLIDER);
 | |
|       break;
 | |
|     case MOZ_GTK_TAB_TOP:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_TOP);
 | |
|       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
 | |
|                                    static_cast<GtkRegionFlags>(0));
 | |
|       break;
 | |
|     case MOZ_GTK_TAB_BOTTOM:
 | |
|       style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_BOTTOM);
 | |
|       gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
 | |
|                                    static_cast<GtkRegionFlags>(0));
 | |
|       break;
 | |
|     case MOZ_GTK_NOTEBOOK:
 | |
|     case MOZ_GTK_NOTEBOOK_HEADER:
 | |
|     case MOZ_GTK_TABPANELS:
 | |
|     case MOZ_GTK_TAB_SCROLLARROW:
 | |
|     {
 | |
|       GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
 | |
|       return gtk_widget_get_style_context(widget);
 | |
|     }
 | |
|     default:
 | |
|       return GetWidgetRootStyle(aNodeType);
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(style);
 | |
|   sStyleStorage[aNodeType] = style;
 | |
|   return style;
 | |
| }
 | |
| 
 | |
| void
 | |
| ResetWidgetCache(void)
 | |
| {
 | |
|   for (int i = 0; i < MOZ_GTK_WIDGET_NODE_COUNT; i++) {
 | |
|     if (sStyleStorage[i])
 | |
|       g_object_unref(sStyleStorage[i]);
 | |
|   }
 | |
|   mozilla::PodArrayZero(sStyleStorage);
 | |
| 
 | |
|   /* This will destroy all of our widgets */
 | |
|   if (sWidgetStorage[MOZ_GTK_WINDOW])
 | |
|     gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
 | |
|   if (sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED])
 | |
|     gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED]);
 | |
| 
 | |
|   /* Clear already freed arrays */
 | |
|   mozilla::PodArrayZero(sWidgetStorage);
 | |
| }
 | |
| 
 | |
| GtkStyleContext*
 | |
| GetStyleContext(WidgetNodeType aNodeType, GtkTextDirection aDirection,
 | |
|                 GtkStateFlags aStateFlags, StyleFlags aFlags)
 | |
| {
 | |
|   GtkStyleContext* style;
 | |
|   if (gtk_check_version(3, 20, 0) != nullptr) {
 | |
|     style = GetWidgetStyleInternal(aNodeType);
 | |
|   } else {
 | |
|     style = GetCssNodeStyleInternal(aNodeType);
 | |
|   }
 | |
|   bool stateChanged = false;
 | |
|   bool stateHasDirection = gtk_get_minor_version() >= 8;
 | |
|   GtkStateFlags oldState = gtk_style_context_get_state(style);
 | |
|   MOZ_ASSERT(!(aStateFlags & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL)));
 | |
|   unsigned newState = aStateFlags;
 | |
|   if (stateHasDirection) {
 | |
|     switch (aDirection) {
 | |
|       case GTK_TEXT_DIR_LTR:
 | |
|         newState |= STATE_FLAG_DIR_LTR;
 | |
|         break;
 | |
|       case GTK_TEXT_DIR_RTL:
 | |
|         newState |= STATE_FLAG_DIR_RTL;
 | |
|         break;
 | |
|       default:
 | |
|         MOZ_FALLTHROUGH_ASSERT("Bad GtkTextDirection");
 | |
|       case GTK_TEXT_DIR_NONE:
 | |
|         // GtkWidget uses a default direction if neither is explicitly
 | |
|         // specified, but here DIR_NONE is interpreted as meaning the
 | |
|         // direction is not important, so don't change the direction
 | |
|         // unnecessarily.
 | |
|         newState |= oldState & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL);
 | |
|     }
 | |
|   } else if (aDirection != GTK_TEXT_DIR_NONE) {
 | |
|     GtkTextDirection oldDirection = gtk_style_context_get_direction(style);
 | |
|     if (aDirection != oldDirection) {
 | |
|       gtk_style_context_set_direction(style, aDirection);
 | |
|       stateChanged = true;
 | |
|     }
 | |
|   }
 | |
|   if (oldState != newState) {
 | |
|     gtk_style_context_set_state(style, static_cast<GtkStateFlags>(newState));
 | |
|     stateChanged = true;
 | |
|   }
 | |
|   // This invalidate is necessary for unsaved style contexts from GtkWidgets
 | |
|   // in pre-3.18 GTK, because automatic invalidation of such contexts
 | |
|   // was delayed until a resize event runs.
 | |
|   //
 | |
|   // https://bugzilla.mozilla.org/show_bug.cgi?id=1272194#c7
 | |
|   //
 | |
|   // Avoid calling invalidate on contexts that are not owned and constructed
 | |
|   // by widgets to avoid performing build_properties() (in 3.16 stylecontext.c)
 | |
|   // unnecessarily early.
 | |
|   if (stateChanged && sWidgetStorage[aNodeType]) {
 | |
|     gtk_style_context_invalidate(style);
 | |
|   }
 | |
|   return style;
 | |
| }
 | |
| 
 | |
| GtkStyleContext*
 | |
| CreateStyleContextWithStates(WidgetNodeType aNodeType, GtkTextDirection aDirection,
 | |
|                              GtkStateFlags aStateFlags)
 | |
| {
 | |
|   GtkStyleContext* style = GetStyleContext(aNodeType, aDirection, aStateFlags);
 | |
|   GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(style));
 | |
| 
 | |
|   if (gtk_check_version(3, 14, 0) == nullptr) {
 | |
| 
 | |
|     static auto sGtkWidgetPathIterGetState =
 | |
|       (GtkStateFlags (*)(const GtkWidgetPath*, gint))
 | |
|       dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_state");
 | |
|     static auto sGtkWidgetPathIterSetState =
 | |
|       (void (*)(GtkWidgetPath*, gint, GtkStateFlags))
 | |
|       dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_state");
 | |
| 
 | |
|     int pathLength = gtk_widget_path_length(path);
 | |
|     for(int i = 0; i < pathLength; i++) {
 | |
|       unsigned state = aStateFlags | sGtkWidgetPathIterGetState(path, i);
 | |
|       sGtkWidgetPathIterSetState(path, i, GtkStateFlags(state));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   style = gtk_style_context_new();
 | |
|   gtk_style_context_set_path(style, path);
 | |
|   gtk_widget_path_unref(path);
 | |
| 
 | |
|   return style;
 | |
| }
 |