diff -up firefox-50.0/widget/gtk/gtk3drawing.cpp.widget-rebase firefox-50.0/widget/gtk/gtk3drawing.cpp --- firefox-50.0/widget/gtk/gtk3drawing.cpp.widget-rebase 2016-10-31 21:15:38.000000000 +0100 +++ firefox-50.0/widget/gtk/gtk3drawing.cpp 2016-11-24 11:57:52.033064782 +0100 @@ -18,27 +18,6 @@ #include -static GtkWidget* gProtoLayout; -static GtkWidget* gHScaleWidget; -static GtkWidget* gVScaleWidget; -static GtkWidget* gComboBoxWidget; -static GtkWidget* gComboBoxButtonWidget; -static GtkWidget* gComboBoxArrowWidget; -static GtkWidget* gComboBoxSeparatorWidget; -static GtkWidget* gComboBoxEntryWidget; -static GtkWidget* gComboBoxEntryTextareaWidget; -static GtkWidget* gComboBoxEntryButtonWidget; -static GtkWidget* gComboBoxEntryArrowWidget; -static GtkWidget* gTabWidget; -static GtkWidget* gImageMenuItemWidget; -static GtkWidget* gCheckMenuItemWidget; -static GtkWidget* gTreeViewWidget; -static GtkTreeViewColumn* gMiddleTreeViewColumn; -static GtkWidget* gTreeHeaderCellWidget; -static GtkWidget* gTreeHeaderSortArrowWidget; -static GtkWidget* gHPanedWidget; -static GtkWidget* gVPanedWidget; - static style_prop_t style_prop_func; static gboolean have_arrow_scaling; static gboolean checkbox_check_state; @@ -54,6 +33,13 @@ static gboolean is_initialized; #define GTK_STATE_FLAG_CHECKED (1 << 11) #endif +static gint +moz_gtk_get_tab_thickness(GtkStyleContext *style); + +static gint +moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect, + GtkWidgetState* state, GtkTextDirection direction); + static GtkStateFlags GetStateFlagsFromGtkWidgetState(GtkWidgetState* state) { @@ -73,338 +59,17 @@ GetStateFlagsFromGtkWidgetState(GtkWidge return stateFlags; } -gint -moz_gtk_enable_style_props(style_prop_t styleGetProp) -{ - style_prop_func = styleGetProp; - return MOZ_GTK_SUCCESS; -} - -static gint -setup_widget_prototype(GtkWidget* widget) -{ - if (!gProtoLayout) { - gProtoLayout = GetWidget(MOZ_GTK_WINDOW_CONTAINER); - } - gtk_container_add(GTK_CONTAINER(gProtoLayout), widget); - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_hpaned_widget() -{ - if (!gHPanedWidget) { - gHPanedWidget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - setup_widget_prototype(gHPanedWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_vpaned_widget() -{ - if (!gVPanedWidget) { - gVPanedWidget = gtk_paned_new(GTK_ORIENTATION_VERTICAL); - setup_widget_prototype(gVPanedWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_scale_widget() -{ - if (!gHScaleWidget) { - gHScaleWidget = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, NULL); - setup_widget_prototype(gHScaleWidget); - } - if (!gVScaleWidget) { - gVScaleWidget = gtk_scale_new(GTK_ORIENTATION_VERTICAL, NULL); - setup_widget_prototype(gVScaleWidget); - } - return MOZ_GTK_SUCCESS; -} - -/* We need to have pointers to the inner widgets (button, separator, arrow) - * of the ComboBox to get the correct rendering from theme engines which - * special cases their look. Since the inner layout can change, we ask GTK - * to NULL our pointers when they are about to become invalid because the - * corresponding widgets don't exist anymore. It's the role of - * g_object_add_weak_pointer(). - * Note that if we don't find the inner widgets (which shouldn't happen), we - * fallback to use generic "non-inner" widgets, and they don't need that kind - * of weak pointer since they are explicit children of gProtoLayout and as - * such GTK holds a strong reference to them. */ -static void -moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) -{ - if (GTK_IS_TOGGLE_BUTTON(widget)) { - gComboBoxButtonWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxButtonWidget); - gtk_widget_realize(widget); - } -} - -static void -moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, - gpointer client_data) -{ - if (GTK_IS_SEPARATOR(widget)) { - gComboBoxSeparatorWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxSeparatorWidget); - } else if (GTK_IS_ARROW(widget)) { - gComboBoxArrowWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxArrowWidget); - } else - return; - gtk_widget_realize(widget); -} - -static gint -ensure_combo_box_widgets() -{ - GtkWidget* buttonChild; - - if (gComboBoxButtonWidget && gComboBoxArrowWidget) - return MOZ_GTK_SUCCESS; - - /* Create a ComboBox if needed */ - if (!gComboBoxWidget) { - gComboBoxWidget = gtk_combo_box_new(); - setup_widget_prototype(gComboBoxWidget); - } - - /* Get its inner Button */ - gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), - moz_gtk_get_combo_box_inner_button, - NULL); - - if (gComboBoxButtonWidget) { - /* Get the widgets inside the Button */ - buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget)); - if (GTK_IS_BOX(buttonChild)) { - /* appears-as-list = FALSE, cell-view = TRUE; the button - * contains an hbox. This hbox is there because the ComboBox - * needs to place a cell renderer, a separator, and an arrow in - * the button when appears-as-list is FALSE. */ - gtk_container_forall(GTK_CONTAINER(buttonChild), - moz_gtk_get_combo_box_button_inner_widgets, - NULL); - } else if(GTK_IS_ARROW(buttonChild)) { - /* appears-as-list = TRUE, or cell-view = FALSE; - * the button only contains an arrow */ - gComboBoxArrowWidget = buttonChild; - g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *) - &gComboBoxArrowWidget); - gtk_widget_realize(gComboBoxArrowWidget); - } - } else { - /* Shouldn't be reached with current internal gtk implementation; we - * use a generic toggle button as last resort fallback to avoid - * crashing. */ - gComboBoxButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON); - } - - if (!gComboBoxArrowWidget) { - /* Shouldn't be reached with current internal gtk implementation; - * we gButtonArrowWidget as last resort fallback to avoid - * crashing. */ - gComboBoxArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW); - } - - /* We don't test the validity of gComboBoxSeparatorWidget since there - * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it - * is invalid we just won't paint it. */ - - return MOZ_GTK_SUCCESS; -} - -/* We need to have pointers to the inner widgets (entry, button, arrow) of - * the ComboBoxEntry to get the correct rendering from theme engines which - * special cases their look. Since the inner layout can change, we ask GTK - * to NULL our pointers when they are about to become invalid because the - * corresponding widgets don't exist anymore. It's the role of - * g_object_add_weak_pointer(). - * Note that if we don't find the inner widgets (which shouldn't happen), we - * fallback to use generic "non-inner" widgets, and they don't need that kind - * of weak pointer since they are explicit children of gProtoLayout and as - * such GTK holds a strong reference to them. */ -static void -moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, - gpointer client_data) -{ - if (GTK_IS_TOGGLE_BUTTON(widget)) { - gComboBoxEntryButtonWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxEntryButtonWidget); - } else if (GTK_IS_ENTRY(widget)) { - gComboBoxEntryTextareaWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxEntryTextareaWidget); - } else - return; - gtk_widget_realize(widget); -} - -static void -moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) -{ - if (GTK_IS_ARROW(widget)) { - gComboBoxEntryArrowWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxEntryArrowWidget); - gtk_widget_realize(widget); - } -} - -static gint -ensure_combo_box_entry_widgets() -{ - GtkWidget* buttonChild; - - if (gComboBoxEntryTextareaWidget && - gComboBoxEntryButtonWidget && - gComboBoxEntryArrowWidget) - return MOZ_GTK_SUCCESS; - - /* Create a ComboBoxEntry if needed */ - if (!gComboBoxEntryWidget) { - gComboBoxEntryWidget = gtk_combo_box_new_with_entry(); - setup_widget_prototype(gComboBoxEntryWidget); - } - - /* Get its inner Entry and Button */ - gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), - moz_gtk_get_combo_box_entry_inner_widgets, - NULL); - - if (!gComboBoxEntryTextareaWidget) { - gComboBoxEntryTextareaWidget = GetWidget(MOZ_GTK_ENTRY); - } - - if (gComboBoxEntryButtonWidget) { - /* Get the Arrow inside the Button */ - buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget)); - if (GTK_IS_BOX(buttonChild)) { - /* appears-as-list = FALSE, cell-view = TRUE; the button - * contains an hbox. This hbox is there because the ComboBox - * needs to place a cell renderer, a separator, and an arrow in - * the button when appears-as-list is FALSE. */ - gtk_container_forall(GTK_CONTAINER(buttonChild), - moz_gtk_get_combo_box_entry_arrow, - NULL); - } else if(GTK_IS_ARROW(buttonChild)) { - /* appears-as-list = TRUE, or cell-view = FALSE; - * the button only contains an arrow */ - gComboBoxEntryArrowWidget = buttonChild; - g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *) - &gComboBoxEntryArrowWidget); - gtk_widget_realize(gComboBoxEntryArrowWidget); - } - } else { - /* Shouldn't be reached with current internal gtk implementation; - * we use a generic toggle button as last resort fallback to avoid - * crashing. */ - gComboBoxEntryButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON); - } - - if (!gComboBoxEntryArrowWidget) { - /* Shouldn't be reached with current internal gtk implementation; - * we gButtonArrowWidget as last resort fallback to avoid - * crashing. */ - gComboBoxEntryArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW); - } - - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_tab_widget() -{ - if (!gTabWidget) { - gTabWidget = gtk_notebook_new(); - setup_widget_prototype(gTabWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_image_menu_item_widget() -{ - if (!gImageMenuItemWidget) { - gImageMenuItemWidget = gtk_image_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)), - gImageMenuItemWidget); - gtk_widget_realize(gImageMenuItemWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_check_menu_item_widget() -{ - if (!gCheckMenuItemWidget) { - gCheckMenuItemWidget = gtk_check_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)), - gCheckMenuItemWidget); - gtk_widget_realize(gCheckMenuItemWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_tree_view_widget() +static GtkStateFlags +GetStateFlagsFromGtkTabFlags(GtkTabFlags flags) { - if (!gTreeViewWidget) { - gTreeViewWidget = gtk_tree_view_new(); - setup_widget_prototype(gTreeViewWidget); - } - return MOZ_GTK_SUCCESS; + return ((flags & MOZ_GTK_TAB_SELECTED) == 0) ? + GTK_STATE_FLAG_NORMAL : GTK_STATE_FLAG_ACTIVE; } -static gint -ensure_tree_header_cell_widget() +gint +moz_gtk_enable_style_props(style_prop_t styleGetProp) { - if(!gTreeHeaderCellWidget) { - /* - * 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* lastTreeViewColumn; - - ensure_tree_view_widget(); - - /* 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(gTreeViewWidget), firstTreeViewColumn); - - gMiddleTreeViewColumn = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M"); - gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), - gMiddleTreeViewColumn); - - lastTreeViewColumn = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); - gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn); - - /* Use the middle column's header for our button */ - gTreeHeaderCellWidget = gtk_tree_view_column_get_button(gMiddleTreeViewColumn); - /* TODO, but it can't be NULL */ - gTreeHeaderSortArrowWidget = gtk_button_new(); - } + style_prop_func = styleGetProp; return MOZ_GTK_SUCCESS; } @@ -423,13 +88,17 @@ moz_gtk_init() else checkbox_check_state = GTK_STATE_FLAG_ACTIVE; - if(!gtk_check_version(3, 12, 0)) { - ensure_tab_widget(); - gtk_style_context_get_style(gtk_widget_get_style_context(gTabWidget), + if (gtk_check_version(3, 12, 0) == nullptr && + gtk_check_version(3, 20, 0) != nullptr) + { + // Deprecated for Gtk >= 3.20+ + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_TAB_TOP); + gtk_style_context_get_style(style, "has-tab-gap", ¬ebook_has_tab_gap, NULL); + ReleaseStyleContext(style); } else { - notebook_has_tab_gap = TRUE; + notebook_has_tab_gap = true; } return MOZ_GTK_SUCCESS; @@ -455,16 +124,24 @@ moz_gtk_radio_get_metrics(gint* indicato return MOZ_GTK_SUCCESS; } -gint -moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width) +static gint +moz_gtk_get_focus_outline_size(GtkStyleContext* style, + gint* focus_h_width, gint* focus_v_width) { GtkBorder border; GtkBorder padding; - GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_ENTRY); gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); *focus_h_width = border.left + padding.left; *focus_v_width = border.top + padding.top; + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width) +{ + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_ENTRY); + moz_gtk_get_focus_outline_size(style, focus_h_width, focus_v_width); ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -472,22 +149,22 @@ moz_gtk_get_focus_outline_size(gint* foc gint moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding) { - gtk_widget_style_get(GetWidget(MOZ_GTK_MENUITEM), - "horizontal-padding", horizontal_padding, - nullptr); - + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_MENUITEM); + gtk_style_context_get_style(style, + "horizontal-padding", horizontal_padding, + nullptr); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } gint moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding) { - ensure_check_menu_item_widget(); - - gtk_style_context_get_style(gtk_widget_get_style_context(gCheckMenuItemWidget), + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_CHECKMENUITEM); + gtk_style_context_get_style(style, "horizontal-padding", horizontal_padding, - NULL); - + nullptr); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -543,15 +220,14 @@ moz_gtk_button_get_default_border(gint* gint moz_gtk_splitter_get_metrics(gint orientation, gint* size) { + GtkStyleContext *style; if (orientation == GTK_ORIENTATION_HORIZONTAL) { - ensure_hpaned_widget(); - gtk_style_context_get_style(gtk_widget_get_style_context(gHPanedWidget), - "handle_size", size, NULL); + style = ClaimStyleContext(MOZ_GTK_SPLITTER_HORIZONTAL); } else { - ensure_vpaned_widget(); - gtk_style_context_get_style(gtk_widget_get_style_context(gVPanedWidget), - "handle_size", size, NULL); + style = ClaimStyleContext(MOZ_GTK_SPLITTER_VERTICAL); } + gtk_style_context_get_style(style, "handle_size", size, NULL); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -756,8 +432,66 @@ calculate_arrow_rect(GtkWidget* arrow, G return MOZ_GTK_SUCCESS; } +void +moz_gtk_get_widget_min_size(WidgetNodeType aGtkWidgetType, int* width, + int* height) +{ + GtkStyleContext* style = ClaimStyleContext(aGtkWidgetType); + GtkStateFlags state_flags = gtk_style_context_get_state(style); + gtk_style_context_get(style, state_flags, + "min-height", height, + "min-width", width, + nullptr); + + GtkBorder border, padding, margin; + gtk_style_context_get_border(style, state_flags, &border); + gtk_style_context_get_padding(style, state_flags, &padding); + gtk_style_context_get_margin(style, state_flags, &margin); + ReleaseStyleContext(style); + + *width += border.left + border.right + margin.left + margin.right + + padding.left + padding.right; + *height += border.top + border.bottom + margin.top + margin.bottom + + padding.top + padding.bottom; +} + +static void +Inset(GdkRectangle* rect, GtkBorder& aBorder) +{ + MOZ_ASSERT(rect); + rect->x += aBorder.left; + rect->y += aBorder.top; + rect->width -= aBorder.left + aBorder.right; + rect->height -= aBorder.top + aBorder.bottom; +} + +// Inset a rectangle by the margins specified in a style context. +static void +InsetByMargin(GdkRectangle* rect, GtkStyleContext* style) +{ + MOZ_ASSERT(rect); + GtkBorder margin; + + gtk_style_context_get_margin(style, gtk_style_context_get_state(style), + &margin); + Inset(rect, margin); +} + +// Inset a rectangle by the border and padding specified in a style context. +static void +InsetByBorderPadding(GdkRectangle* rect, GtkStyleContext* style) +{ + GtkStateFlags state = gtk_style_context_get_state(style); + GtkBorder padding, border; + + gtk_style_context_get_padding(style, state, &padding); + Inset(rect, padding); + gtk_style_context_get_border(style, state, &border); + Inset(rect, border); +} + static gint -moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, +moz_gtk_scrollbar_button_paint(cairo_t *cr, const GdkRectangle* aRect, GtkWidgetState* state, GtkScrollbarButtonFlags flags, GtkTextDirection direction) @@ -795,32 +529,38 @@ moz_gtk_scrollbar_button_paint(cairo_t * gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOP); } - /* Scrollbar button has to be inset by trough_border because its DOM element - * is filling width of vertical scrollbar's track (or height in case - * of horizontal scrollbars). */ - - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - if (flags & MOZ_GTK_STEPPER_VERTICAL) { - rect->x += metrics.trough_border; - rect->width = metrics.slider_width; + GdkRectangle rect = *aRect; + if (gtk_check_version(3,20,0) == nullptr) { + // The "trough-border" is not used since GTK 3.20. The stepper margin + // box occupies the full width of the "contents" gadget content box. + InsetByMargin(&rect, style); } else { - rect->y += metrics.trough_border; - rect->height = metrics.slider_width; + // Scrollbar button has to be inset by trough_border because its DOM + // element is filling width of vertical scrollbar's track (or height + // in case of horizontal scrollbars). + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + if (flags & MOZ_GTK_STEPPER_VERTICAL) { + rect.x += metrics.trough_border; + rect.width = metrics.slider_width; + } else { + rect.y += metrics.trough_border; + rect.height = metrics.slider_width; + } } - gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); + gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); - arrow_rect.width = rect->width / 2; - arrow_rect.height = rect->height / 2; + arrow_rect.width = rect.width / 2; + arrow_rect.height = rect.height / 2; gfloat arrow_scaling; gtk_style_context_get_style(style, "arrow-scaling", &arrow_scaling, NULL); - gdouble arrow_size = MIN(rect->width, rect->height) * arrow_scaling; - arrow_rect.x = rect->x + (rect->width - arrow_size) / 2; - arrow_rect.y = rect->y + (rect->height - arrow_size) / 2; + gdouble arrow_size = MIN(rect.width, rect.height) * arrow_scaling; + arrow_rect.x = rect.x + (rect.width - arrow_size) / 2; + arrow_rect.y = rect.y + (rect.height - arrow_size) / 2; if (state_flags & GTK_STATE_FLAG_ACTIVE) { gtk_style_context_get_style(style, @@ -862,19 +602,23 @@ moz_gtk_update_scrollbar_style(GtkStyleC static void moz_gtk_draw_styled_frame(GtkStyleContext* style, cairo_t *cr, - GdkRectangle* rect, bool drawFocus) + const GdkRectangle* aRect, bool drawFocus) { - gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + GdkRectangle rect = *aRect; + if (gtk_check_version(3, 6, 0) == nullptr) { + InsetByMargin(&rect, style); + } + gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); + gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); if (drawFocus) { gtk_render_focus(style, cr, - rect->x, rect->y, rect->width, rect->height); + rect.x, rect.y, rect.width, rect.height); } } static gint moz_gtk_scrollbar_trough_paint(WidgetNodeType widget, - cairo_t *cr, GdkRectangle* rect, + cairo_t *cr, const GdkRectangle* rect, GtkWidgetState* state, GtkScrollbarTrackFlags flags, GtkTextDirection direction) @@ -886,26 +630,28 @@ moz_gtk_scrollbar_trough_paint(WidgetNod ReleaseStyleContext(style); } - bool isHorizontal = (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL); - GtkStyleContext* style; + GtkStyleContext* style = ClaimStyleContext(widget, direction); + moz_gtk_draw_styled_frame(style, cr, rect, state->focused); + ReleaseStyleContext(style); - // Draw all child CSS Nodes for Gtk >= 3.20 - if (gtk_check_version(3, 20, 0) == nullptr) { - style = ClaimStyleContext(widget, direction); - moz_gtk_update_scrollbar_style(style, widget, direction); - moz_gtk_draw_styled_frame(style, cr, rect, state->focused); - ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; +} - style = ClaimStyleContext(isHorizontal ? - MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL : - MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL, - direction); - moz_gtk_draw_styled_frame(style, cr, rect, state->focused); - ReleaseStyleContext(style); - } - style = ClaimStyleContext(isHorizontal ? - MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL : - MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL, +static gint +moz_gtk_scrollbar_paint(WidgetNodeType widget, + cairo_t *cr, const GdkRectangle* rect, + GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyleContext* style = ClaimStyleContext(widget, direction); + moz_gtk_update_scrollbar_style(style, widget, direction); + + moz_gtk_draw_styled_frame(style, cr, rect, state->focused); + + ReleaseStyleContext(style); + style = ClaimStyleContext((widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) ? + MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL : + MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL, direction); moz_gtk_draw_styled_frame(style, cr, rect, state->focused); ReleaseStyleContext(style); @@ -915,21 +661,21 @@ moz_gtk_scrollbar_trough_paint(WidgetNod static gint moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget, - cairo_t *cr, GdkRectangle* rect, + cairo_t *cr, const GdkRectangle* aRect, GtkWidgetState* state, GtkTextDirection direction) { GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); - GtkBorder margin; + GdkRectangle rect = *aRect; GtkStyleContext* style = ClaimStyleContext(widget, direction, state_flags); - gtk_style_context_get_margin (style, state_flags, &margin); + InsetByMargin(&rect, style); gtk_render_slider(style, cr, - rect->x + margin.left, - rect->y + margin.top, - rect->width - margin.left - margin.right, - rect->height - margin.top - margin.bottom, + rect.x, + rect.y, + rect.width, + rect.height, (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); @@ -987,18 +733,15 @@ moz_gtk_scale_paint(cairo_t *cr, GdkRect GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); gint x, y, width, height, min_width, min_height; GtkStyleContext* style; - GtkWidget* widget; GtkBorder margin; - ensure_scale_widget(); - widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); - gtk_widget_set_direction(widget, direction); moz_gtk_get_scale_metrics(flags, &min_width, &min_height); - style = gtk_widget_get_style_context(widget); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); - gtk_style_context_get_margin(style, state_flags, &margin); + WidgetNodeType widget = (flags == GTK_ORIENTATION_HORIZONTAL) ? + MOZ_GTK_SCALE_TROUGH_HORIZONTAL : + MOZ_GTK_SCALE_TROUGH_VERTICAL; + style = ClaimStyleContext(widget, direction, state_flags); + gtk_style_context_get_margin(style, state_flags, &margin); // Clamp the dimension perpendicular to the direction that the slider crosses // to the minimum size. @@ -1020,7 +763,8 @@ moz_gtk_scale_paint(cairo_t *cr, GdkRect if (state->focused) gtk_render_focus(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_style_context_restore(style); + + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1031,17 +775,8 @@ moz_gtk_scale_thumb_paint(cairo_t *cr, G { GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); GtkStyleContext* style; - GtkWidget* widget; gint thumb_width, thumb_height, x, y; - ensure_scale_widget(); - widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); - gtk_widget_set_direction(widget, direction); - - style = gtk_widget_get_style_context(widget); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER); - gtk_style_context_set_state(style, state_flags); /* determine the thumb size, and position the thumb in the center in the opposite axis */ if (flags == GTK_ORIENTATION_HORIZONTAL) { @@ -1055,8 +790,13 @@ moz_gtk_scale_thumb_paint(cairo_t *cr, G y = rect->y; } + WidgetNodeType widget = (flags == GTK_ORIENTATION_HORIZONTAL) ? + MOZ_GTK_SCALE_THUMB_HORIZONTAL : + MOZ_GTK_SCALE_THUMB_VERTICAL; + style = ClaimStyleContext(widget, direction, state_flags); gtk_render_slider(style, cr, x, y, thumb_width, thumb_height, flags); - gtk_style_context_restore(style); + ReleaseStyleContext(style); + return MOZ_GTK_SUCCESS; } @@ -1078,17 +818,13 @@ static gint moz_gtk_hpaned_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state) { - GtkStyleContext* style; - - ensure_hpaned_widget(); - style = gtk_widget_get_style_context(gHPanedWidget); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); - gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + GtkStyleContext* style = + ClaimStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL, + GTK_TEXT_DIR_LTR, + GetStateFlagsFromGtkWidgetState(state)); gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_style_context_restore(style); - + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1096,17 +832,13 @@ static gint moz_gtk_vpaned_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state) { - GtkStyleContext* style; - - ensure_vpaned_widget(); - style = gtk_widget_get_style_context(gVPanedWidget); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); - gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + GtkStyleContext* style = + ClaimStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL, + GTK_TEXT_DIR_LTR, + GetStateFlagsFromGtkWidgetState(state)); gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_style_context_restore(style); - + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1114,60 +846,26 @@ moz_gtk_vpaned_paint(cairo_t *cr, GdkRec static gint moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state, - GtkWidget* widget, GtkTextDirection direction) + GtkStyleContext* style) { gint x = rect->x, y = rect->y, width = rect->width, height = rect->height; - GtkStyleContext* style; int draw_focus_outline_only = state->depressed; // NS_THEME_FOCUS_OUTLINE - gtk_widget_set_direction(widget, direction); - - style = gtk_widget_get_style_context(widget); - if (draw_focus_outline_only) { // Inflate the given 'rect' with the focus outline size. gint h, v; - moz_gtk_get_focus_outline_size(&h, &v); + moz_gtk_get_focus_outline_size(style, &h, &v); rect->x -= h; rect->width += 2 * h; rect->y -= v; rect->height += 2 * v; width = rect->width; height = rect->height; - } - - /* gtkentry.c uses two windows, one for the entire widget and one for the - * text area inside it. The background of both windows is set to the "base" - * color of the new state in gtk_entry_state_changed, but only the inner - * textarea window uses gtk_paint_flat_box when exposed */ - - /* This gets us a lovely greyish disabledish look */ - gtk_widget_set_sensitive(widget, !state->disabled); - - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_ENTRY); - - /* Now paint the shadow and focus border. - * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad - * smaller when focused if the focus is not interior, then the focus. */ - - if (state->focused && !state->disabled) { - /* This will get us the lit borders that focused textboxes enjoy on - * some themes. */ - gtk_style_context_set_state(style, GTK_STATE_FLAG_FOCUSED); - } - - if (state->disabled) { - gtk_style_context_set_state(style, GTK_STATE_FLAG_INSENSITIVE); - } - - if (!draw_focus_outline_only) { + } else { gtk_render_background(style, cr, x, y, width, height); } gtk_render_frame(style, cr, x, y, width, height); - gtk_style_context_restore(style); - return MOZ_GTK_SUCCESS; } @@ -1214,9 +912,6 @@ moz_gtk_treeview_paint(cairo_t *cr, GdkR GtkStateFlags state_flags; GtkBorder border; - ensure_tree_view_widget(); - gtk_widget_set_direction(gTreeViewWidget, direction); - /* only handle disabled and normal states, otherwise the whole background * area will be painted differently with other states */ state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; @@ -1225,19 +920,19 @@ moz_gtk_treeview_paint(cairo_t *cr, GdkR gtk_style_context_get_border(style, state_flags, &border); xthickness = border.left; ythickness = border.top; + ReleaseStyleContext(style); - style_tree = gtk_widget_get_style_context(gTreeViewWidget); - gtk_style_context_save(style_tree); - gtk_style_context_add_class(style_tree, GTK_STYLE_CLASS_VIEW); - + style_tree = ClaimStyleContext(MOZ_GTK_TREEVIEW_VIEW, direction); gtk_render_background(style_tree, cr, rect->x + xthickness, rect->y + ythickness, rect->width - 2 * xthickness, rect->height - 2 * ythickness); + ReleaseStyleContext(style_tree); + + style = ClaimStyleContext(MOZ_GTK_SCROLLED_WINDOW, direction); gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); ReleaseStyleContext(style); - gtk_style_context_restore(style_tree); return MOZ_GTK_SUCCESS; } @@ -1247,7 +942,7 @@ moz_gtk_tree_header_cell_paint(cairo_t * gboolean isSorted, GtkTextDirection direction) { moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, - gTreeHeaderCellWidget, direction); + GetWidget(MOZ_GTK_TREE_HEADER_CELL), direction); return MOZ_GTK_SUCCESS; } @@ -1260,18 +955,13 @@ moz_gtk_tree_header_sort_arrow_paint(cai gdouble arrow_angle; GtkStyleContext* style; - ensure_tree_header_cell_widget(); - gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction); - /* hard code these values */ arrow_rect.width = 11; arrow_rect.height = 11; arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; - style = gtk_widget_get_style_context(gTreeHeaderSortArrowWidget); - gtk_style_context_save(style); - gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); - + style = ClaimStyleContext(MOZ_GTK_TREE_HEADER_SORTARROW, direction, + GetStateFlagsFromGtkWidgetState(state)); switch (arrow_type) { case GTK_ARROW_LEFT: arrow_angle = ARROW_LEFT; @@ -1290,7 +980,7 @@ moz_gtk_tree_header_sort_arrow_paint(cai gtk_render_arrow(style, cr, arrow_angle, arrow_rect.x, arrow_rect.y, arrow_rect.width); - gtk_style_context_restore(style); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1302,37 +992,28 @@ moz_gtk_treeview_expander_paint(cairo_t GtkExpanderStyle expander_state, GtkTextDirection direction) { - GtkStyleContext *style; - GtkStateFlags state_flags; - - ensure_tree_view_widget(); - gtk_widget_set_direction(gTreeViewWidget, direction); - - style = gtk_widget_get_style_context(gTreeViewWidget); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_EXPANDER); - /* Because the frame we get is of the entire treeview, we can't get the precise * event state of one expander, thus rendering hover and active feedback useless. */ - state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; + GtkStateFlags state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : + GTK_STATE_FLAG_NORMAL; /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering * in gtk_render_expander() */ if (expander_state == GTK_EXPANDER_EXPANDED) - state_flags = static_cast(state_flags|GTK_STATE_FLAG_ACTIVE); + state_flags = static_cast(state_flags|checkbox_check_state); else - state_flags = static_cast(state_flags&~(GTK_STATE_FLAG_ACTIVE)); - - gtk_style_context_set_state(style, state_flags); + state_flags = static_cast(state_flags&~(checkbox_check_state)); + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_TREEVIEW_EXPANDER, + direction, state_flags); gtk_render_expander(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_style_context_restore(style); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1349,34 +1030,37 @@ moz_gtk_combo_box_paint(cairo_t *cr, Gdk GtkStyleContext* style; GtkRequisition arrow_req; - ensure_combo_box_widgets(); + GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON); + GtkWidget* comboBoxArrow = GetWidget(MOZ_GTK_COMBOBOX_ARROW); /* Also sets the direction on gComboBoxButtonWidget, which is then * inherited by the separator and arrow */ moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, - gComboBoxButtonWidget, direction); + comboBoxButton, direction); - calculate_button_inner_rect(gComboBoxButtonWidget, - rect, &arrow_rect, direction); + calculate_button_inner_rect(comboBoxButton, rect, &arrow_rect, direction); /* Now arrow_rect contains the inner rect ; we want to correct the width * to what the arrow needs (see gtk_combo_box_size_allocate) */ - gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req); + gtk_widget_get_preferred_size(comboBoxArrow, NULL, &arrow_req); + if (direction == GTK_TEXT_DIR_LTR) arrow_rect.x += arrow_rect.width - arrow_req.width; arrow_rect.width = arrow_req.width; - calculate_arrow_rect(gComboBoxArrowWidget, + calculate_arrow_rect(comboBoxArrow, &arrow_rect, &real_arrow_rect, direction); - style = gtk_widget_get_style_context(gComboBoxArrowWidget); + style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ARROW); gtk_render_arrow(style, cr, ARROW_DOWN, real_arrow_rect.x, real_arrow_rect.y, real_arrow_rect.width); + ReleaseStyleContext(style); /* If there is no separator in the theme, there's nothing left to do. */ - if (!gComboBoxSeparatorWidget) + GtkWidget* widget = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR); + if (!widget) return MOZ_GTK_SUCCESS; - style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); + style = gtk_widget_get_style_context(widget); gtk_style_context_get_style(style, "wide-separators", &wide_separators, "separator-width", &separator_width, @@ -1460,15 +1144,14 @@ moz_gtk_combo_box_entry_button_paint(cai GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); GtkStyleContext* style; - ensure_combo_box_entry_widgets(); - + GtkWidget* comboBoxEntry = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON); moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, - gComboBoxEntryButtonWidget, direction); + comboBoxEntry, direction); + calculate_button_inner_rect(comboBoxEntry, rect, &arrow_rect, direction); - calculate_button_inner_rect(gComboBoxEntryButtonWidget, - rect, &arrow_rect, direction); if (state_flags & GTK_STATE_FLAG_ACTIVE) { - gtk_style_context_get_style(gtk_widget_get_style_context(gComboBoxEntryButtonWidget), + style = gtk_widget_get_style_context(comboBoxEntry); + gtk_style_context_get_style(style, "child-displacement-x", &x_displacement, "child-displacement-y", &y_displacement, NULL); @@ -1476,15 +1159,14 @@ moz_gtk_combo_box_entry_button_paint(cai arrow_rect.y += y_displacement; } - calculate_arrow_rect(gComboBoxEntryArrowWidget, + calculate_arrow_rect(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_ARROW), &arrow_rect, &real_arrow_rect, direction); - style = gtk_widget_get_style_context(gComboBoxEntryArrowWidget); - + style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_ARROW); gtk_render_arrow(style, cr, ARROW_DOWN, real_arrow_rect.x, real_arrow_rect.y, real_arrow_rect.width); - + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1504,7 +1186,6 @@ moz_gtk_container_paint(cairo_t *cr, Gdk } ReleaseStyleContext(style); - return MOZ_GTK_SUCCESS; } @@ -1590,12 +1271,53 @@ moz_gtk_toolbar_separator_paint(cairo_t } static gint -moz_gtk_tooltip_paint(cairo_t *cr, GdkRectangle* rect, +moz_gtk_tooltip_paint(cairo_t *cr, const GdkRectangle* aRect, GtkTextDirection direction) { + // Tooltip widget is made in GTK3 as following tree: + // Tooltip window + // Horizontal Box + // Icon (not supported by Firefox) + // Label + // Each element can be fully styled by CSS of GTK theme. + // We have to draw all elements with appropriate offset and right dimensions. + + // Tooltip drawing GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TOOLTIP, direction); - gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + GdkRectangle rect = *aRect; + gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); + gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); + + // Horizontal Box drawing + // + // The box element has hard-coded 6px margin-* GtkWidget properties, which + // are added between the window dimensions and the CSS margin box of the + // horizontal box. The frame of the tooltip window is drawn in the + // 6px margin. + // For drawing Horizontal Box we have to inset drawing area by that 6px + // plus its CSS margin. + GtkStyleContext* boxStyle = + CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), style); + + rect.x += 6; + rect.y += 6; + rect.width -= 12; + rect.height -= 12; + + InsetByMargin(&rect, boxStyle); + gtk_render_background(boxStyle, cr, rect.x, rect.y, rect.width, rect.height); + gtk_render_frame(boxStyle, cr, rect.x, rect.y, rect.width, rect.height); + + // Label drawing + InsetByBorderPadding(&rect, boxStyle); + + GtkStyleContext* labelStyle = + CreateStyleForWidget(gtk_label_new(nullptr), boxStyle); + moz_gtk_draw_styled_frame(labelStyle, cr, &rect, false); + g_object_unref(labelStyle); + + g_object_unref(boxStyle); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1721,46 +1443,35 @@ moz_gtk_progress_chunk_paint(cairo_t *cr return MOZ_GTK_SUCCESS; } -gint -moz_gtk_get_tab_thickness(void) +static gint +moz_gtk_get_tab_thickness(GtkStyleContext *style) { - GtkBorder border; - GtkStyleContext * style; - - ensure_tab_widget(); if (!notebook_has_tab_gap) return 0; /* tabs do not overdraw the tabpanel border with "no gap" style */ - style = gtk_widget_get_style_context(gTabWidget); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); + GtkBorder border; gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); - if (border.top < 2) return 2; /* some themes don't set ythickness correctly */ return border.top; } -static void -moz_gtk_tab_prepare_style_context(GtkStyleContext *style, - GtkTabFlags flags) -{ - gtk_style_context_set_state(style, ((flags & MOZ_GTK_TAB_SELECTED) == 0) ? - GTK_STATE_FLAG_NORMAL : - GTK_STATE_FLAG_ACTIVE); - gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, - (flags & MOZ_GTK_TAB_FIRST) ? - GTK_REGION_FIRST : static_cast(0)); - gtk_style_context_add_class(style, (flags & MOZ_GTK_TAB_BOTTOM) ? - GTK_STYLE_CLASS_BOTTOM : - GTK_STYLE_CLASS_TOP); +gint +moz_gtk_get_tab_thickness(WidgetNodeType aNodeType) +{ + GtkStyleContext *style = ClaimStyleContext(aNodeType); + int thickness = moz_gtk_get_tab_thickness(style); + ReleaseStyleContext(style); + return thickness; } /* actual small tabs */ static gint moz_gtk_tab_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state, - GtkTabFlags flags, GtkTextDirection direction) + GtkTabFlags flags, GtkTextDirection direction, + WidgetNodeType widget) { /* When the tab isn't selected, we just draw a notebook extension. * When it is selected, we overwrite the adjacent border of the tabpanel @@ -1772,14 +1483,10 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan GdkRectangle focusRect; GdkRectangle backRect; int initial_gap = 0; + bool isBottomTab = (widget == MOZ_GTK_TAB_BOTTOM); - ensure_tab_widget(); - gtk_widget_set_direction(gTabWidget, direction); - - style = gtk_widget_get_style_context(gTabWidget); - gtk_style_context_save(style); - moz_gtk_tab_prepare_style_context(style, flags); - + style = ClaimStyleContext(widget, direction, + GetStateFlagsFromGtkTabFlags(flags)); tabRect = *rect; if (flags & MOZ_GTK_TAB_FIRST) { @@ -1798,8 +1505,7 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan /* Only draw the tab */ gtk_render_extension(style, cr, tabRect.x, tabRect.y, tabRect.width, tabRect.height, - (flags & MOZ_GTK_TAB_BOTTOM) ? - GTK_POS_TOP : GTK_POS_BOTTOM ); + isBottomTab ? GTK_POS_TOP : GTK_POS_BOTTOM ); } else { /* Draw the tab and the gap * We want the gap to be positioned exactly on the tabpanel top @@ -1840,7 +1546,7 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan gint gap_loffset, gap_roffset, gap_voffset, gap_height; /* Get height needed by the gap */ - gap_height = moz_gtk_get_tab_thickness(); + gap_height = moz_gtk_get_tab_thickness(style); /* Extract gap_voffset from the first bits of flags */ gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK; @@ -1856,7 +1562,7 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan gap_loffset = initial_gap; } - if (flags & MOZ_GTK_TAB_BOTTOM) { + if (isBottomTab) { /* Draw the tab on bottom */ focusRect.y += gap_voffset; focusRect.height -= gap_voffset; @@ -1920,15 +1626,9 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan gtk_render_frame(style, cr, tabRect.x, tabRect.y, tabRect.width, tabRect.height); } - gtk_style_context_restore(style); - if (state->focused) { /* Paint the focus ring */ GtkBorder padding; - - gtk_style_context_save(style); - moz_gtk_tab_prepare_style_context(style, flags); - gtk_style_context_get_padding(style, GetStateFlagsFromGtkWidgetState(state), &padding); focusRect.x += padding.left; @@ -1938,10 +1638,8 @@ moz_gtk_tab_paint(cairo_t *cr, GdkRectan gtk_render_focus(style, cr, focusRect.x, focusRect.y, focusRect.width, focusRect.height); - - gtk_style_context_restore(style); } - + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -1951,14 +1649,7 @@ static gint moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect, GtkTextDirection direction) { - GtkStyleContext* style; - - ensure_tab_widget(); - gtk_widget_set_direction(gTabWidget, direction); - - style = gtk_widget_get_style_context(gTabWidget); - gtk_style_context_save(style); - + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TABPANELS, direction); gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); /* @@ -1982,7 +1673,7 @@ moz_gtk_tabpanels_paint(cairo_t *cr, Gdk rect->width, rect->height, GTK_POS_TOP, rect->width - 1, rect->width); cairo_restore(cr); - + /* right side */ cairo_save(cr); cairo_rectangle(cr, rect->x + rect->width / 2, rect->y, @@ -1995,7 +1686,7 @@ moz_gtk_tabpanels_paint(cairo_t *cr, Gdk GTK_POS_TOP, 0, 1); cairo_restore(cr); - gtk_style_context_restore(style); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2005,17 +1696,12 @@ moz_gtk_tab_scroll_arrow_paint(cairo_t * GtkArrowType arrow_type, GtkTextDirection direction) { - GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); GtkStyleContext* style; gdouble arrow_angle; gint arrow_size = MIN(rect->width, rect->height); gint x = rect->x + (rect->width - arrow_size) / 2; gint y = rect->y + (rect->height - arrow_size) / 2; - ensure_tab_widget(); - - style = gtk_widget_get_style_context(gTabWidget); - gtk_style_context_save(style); if (direction == GTK_TEXT_DIR_RTL) { arrow_type = (arrow_type == GTK_ARROW_LEFT) ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT; @@ -2035,12 +1721,12 @@ moz_gtk_tab_scroll_arrow_paint(cairo_t * break; } if (arrow_type != GTK_ARROW_NONE) { - gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); /* TODO TEST */ - gtk_style_context_set_state(style, state_flags); + style = ClaimStyleContext(MOZ_GTK_TAB_SCROLLARROW, direction, + GetStateFlagsFromGtkWidgetState(state)); gtk_render_arrow(style, cr, arrow_angle, x, y, arrow_size); + ReleaseStyleContext(style); } - gtk_style_context_restore(style); return MOZ_GTK_SUCCESS; } @@ -2092,22 +1778,25 @@ static gint moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect, GtkTextDirection direction) { + GtkWidgetState defaultState = { 0 }; + moz_gtk_menu_item_paint(MOZ_GTK_MENUSEPARATOR, cr, rect, + &defaultState, direction); + + if (gtk_get_minor_version() >= 20) + return MOZ_GTK_SUCCESS; + GtkStyleContext* style; gboolean wide_separators; gint separator_height; - guint border_width; gint x, y, w; GtkBorder padding; - border_width = - gtk_container_get_border_width(GTK_CONTAINER( - GetWidget(MOZ_GTK_MENUSEPARATOR))); style = ClaimStyleContext(MOZ_GTK_MENUSEPARATOR, direction); gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); - x = rect->x + border_width; - y = rect->y + border_width; - w = rect->width - border_width * 2; + x = rect->x; + y = rect->y; + w = rect->width; gtk_style_context_save(style); gtk_style_context_add_class(style, GTK_STYLE_CLASS_SEPARATOR); @@ -2144,37 +1833,32 @@ moz_gtk_menu_item_paint(WidgetNodeType w { gint x, y, w, h; - if (state->inHover && !state->disabled) { - guint border_width = - gtk_container_get_border_width(GTK_CONTAINER(GetWidget(widget))); - GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); - GtkStyleContext* style = - ClaimStyleContext(widget, direction, state_flags); - - bool pre_3_6 = gtk_check_version(3, 6, 0) != nullptr; - if (pre_3_6) { - // GTK+ 3.4 saves the style context and adds the menubar class to - // menubar children, but does each of these only when drawing, not - // during layout. - gtk_style_context_save(style); - if (widget == MOZ_GTK_MENUBARITEM) { - gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); - } + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style = ClaimStyleContext(widget, direction, state_flags); + + bool pre_3_6 = gtk_check_version(3, 6, 0) != nullptr; + if (pre_3_6) { + // GTK+ 3.4 saves the style context and adds the menubar class to + // menubar children, but does each of these only when drawing, not + // during layout. + gtk_style_context_save(style); + if (widget == MOZ_GTK_MENUBARITEM) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); } + } - x = rect->x + border_width; - y = rect->y + border_width; - w = rect->width - border_width * 2; - h = rect->height - border_width * 2; + x = rect->x; + y = rect->y; + w = rect->width; + h = rect->height; - gtk_render_background(style, cr, x, y, w, h); - gtk_render_frame(style, cr, x, y, w, h); + gtk_render_background(style, cr, x, y, w, h); + gtk_render_frame(style, cr, x, y, w, h); - if (pre_3_6) { - gtk_style_context_restore(style); - } - ReleaseStyleContext(style); + if (pre_3_6) { + gtk_style_context_restore(style); } + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2194,63 +1878,68 @@ moz_gtk_menu_arrow_paint(cairo_t *cr, Gd return MOZ_GTK_SUCCESS; } -// See gtk_real_check_menu_item_draw_indicator() for reference. +// For reference, see gtk_check_menu_item_size_allocate() in GTK versions after +// 3.20 and gtk_real_check_menu_item_draw_indicator() in earlier versions. static gint -moz_gtk_check_menu_item_paint(cairo_t *cr, GdkRectangle* rect, +moz_gtk_check_menu_item_paint(WidgetNodeType widgetType, + cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state, - gboolean checked, gboolean isradio, - GtkTextDirection direction) + gboolean checked, GtkTextDirection direction) { GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); GtkStyleContext* style; - GtkBorder padding; - gint offset; gint indicator_size, horizontal_padding; gint x, y; moz_gtk_menu_item_paint(MOZ_GTK_MENUITEM, cr, rect, state, direction); - ensure_check_menu_item_widget(); - gtk_widget_set_direction(gCheckMenuItemWidget, direction); - - style = gtk_widget_get_style_context(gCheckMenuItemWidget); - gtk_style_context_save(style); + if (checked) { + state_flags = static_cast(state_flags|checkbox_check_state); + } + bool pre_3_20 = gtk_get_minor_version() < 20; + gint offset; + style = ClaimStyleContext(widgetType, direction); gtk_style_context_get_style(style, "indicator-size", &indicator_size, "horizontal-padding", &horizontal_padding, NULL); - - if (isradio) { - gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); + if (pre_3_20) { + GtkBorder padding; + gtk_style_context_get_padding(style, state_flags, &padding); + offset = horizontal_padding + padding.left + 2; } else { - gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); - } - - if (checked) { - state_flags = static_cast(state_flags|checkbox_check_state); + GdkRectangle r = { 0 }; + InsetByMargin(&r, style); + InsetByBorderPadding(&r, style); + offset = r.x; } - - gtk_style_context_set_state(style, state_flags); - gtk_style_context_get_padding(style, state_flags, &padding); + ReleaseStyleContext(style); - offset = gtk_container_get_border_width(GTK_CONTAINER(gCheckMenuItemWidget)) + - padding.left + 2; + bool isRadio = (widgetType == MOZ_GTK_RADIOMENUITEM); + WidgetNodeType indicatorType = isRadio ? MOZ_GTK_RADIOMENUITEM_INDICATOR + : MOZ_GTK_CHECKMENUITEM_INDICATOR; + style = ClaimStyleContext(indicatorType, direction, state_flags); if (direction == GTK_TEXT_DIR_RTL) { - x = rect->width - indicator_size - offset - horizontal_padding; + x = rect->width - indicator_size - offset; } else { - x = rect->x + offset + horizontal_padding; + x = rect->x + offset; } y = rect->y + (rect->height - indicator_size) / 2; - if (isradio) { + if (!pre_3_20) { + gtk_render_background(style, cr, x, y, indicator_size, indicator_size); + gtk_render_frame(style, cr, x, y, indicator_size, indicator_size); + } + + if (isRadio) { gtk_render_option(style, cr, x, y, indicator_size, indicator_size); } else { gtk_render_check(style, cr, x, y, indicator_size, indicator_size); } - gtk_style_context_restore(style); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2271,6 +1960,20 @@ moz_gtk_info_bar_paint(cairo_t *cr, GdkR } static void +moz_gtk_add_style_margin(GtkStyleContext* style, + gint* left, gint* top, gint* right, gint* bottom) +{ + GtkBorder margin; + + gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); + + *left += margin.left; + *right += margin.right; + *top += margin.top; + *bottom += margin.bottom; +} + +static void moz_gtk_add_style_border(GtkStyleContext* style, gint* left, gint* top, gint* right, gint* bottom) { @@ -2298,6 +2001,15 @@ moz_gtk_add_style_padding(GtkStyleContex *bottom += padding.bottom; } +static void moz_gtk_add_margin_border_padding(GtkStyleContext *style, + gint* left, gint* top, + gint* right, gint* bottom) +{ + moz_gtk_add_style_margin(style, left, top, right, bottom); + moz_gtk_add_style_border(style, left, top, right, bottom); + moz_gtk_add_style_padding(style, left, top, right, bottom); +} + gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, gint* right, gint* bottom, GtkTextDirection direction, @@ -2362,26 +2074,24 @@ moz_gtk_get_widget_border(WidgetNodeType * assigned. * That is why the following code is the same as for MOZ_GTK_BUTTON. * */ - ensure_tree_header_cell_widget(); - *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gTreeHeaderCellWidget)); - - style = gtk_widget_get_style_context(gTreeHeaderCellWidget); + *left = *top = *right = *bottom = + gtk_container_get_border_width(GTK_CONTAINER( + GetWidget(MOZ_GTK_TREE_HEADER_CELL))); + style = ClaimStyleContext(MOZ_GTK_TREE_HEADER_CELL); moz_gtk_add_style_border(style, left, top, right, bottom); moz_gtk_add_style_padding(style, left, top, right, bottom); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } case MOZ_GTK_TREE_HEADER_SORTARROW: - ensure_tree_header_cell_widget(); - w = gTreeHeaderSortArrowWidget; + w = GetWidget(MOZ_GTK_TREE_HEADER_SORTARROW); break; case MOZ_GTK_DROPDOWN_ENTRY: - ensure_combo_box_entry_widgets(); - w = gComboBoxEntryTextareaWidget; + w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA); break; case MOZ_GTK_DROPDOWN_ARROW: - ensure_combo_box_entry_widgets(); - w = gComboBoxEntryButtonWidget; + w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON); break; case MOZ_GTK_DROPDOWN: { @@ -2393,32 +2103,33 @@ moz_gtk_get_widget_border(WidgetNodeType GtkRequisition arrow_req; GtkBorder border; - ensure_combo_box_widgets(); - - *left = *top = *right = *bottom = - gtk_container_get_border_width(GTK_CONTAINER(gComboBoxButtonWidget)); - - style = gtk_widget_get_style_context(gComboBoxButtonWidget); - + *left = *top = *right = *bottom = + gtk_container_get_border_width(GTK_CONTAINER( + GetWidget(MOZ_GTK_COMBOBOX_BUTTON))); + style = ClaimStyleContext(MOZ_GTK_COMBOBOX_BUTTON); moz_gtk_add_style_padding(style, left, top, right, bottom); moz_gtk_add_style_border(style, left, top, right, bottom); + ReleaseStyleContext(style); /* If there is no separator, don't try to count its width. */ separator_width = 0; - if (gComboBoxSeparatorWidget) { - style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); + GtkWidget* comboBoxSeparator = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR); + if (comboBoxSeparator) { + style = gtk_widget_get_style_context(comboBoxSeparator); gtk_style_context_get_style(style, "wide-separators", &wide_separators, "separator-width", &separator_width, NULL); if (!wide_separators) { - gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, &border); + gtk_style_context_get_border(style, GTK_STATE_FLAG_NORMAL, + &border); separator_width = border.left; } } - gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req); + gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ARROW), + NULL, &arrow_req); if (direction == GTK_TEXT_DIR_RTL) *left += separator_width + arrow_req.width; @@ -2428,8 +2139,7 @@ moz_gtk_get_widget_border(WidgetNodeType return MOZ_GTK_SUCCESS; } case MOZ_GTK_TABPANELS: - ensure_tab_widget(); - w = gTabWidget; + w = GetWidget(MOZ_GTK_TABPANELS); break; case MOZ_GTK_PROGRESSBAR: w = GetWidget(MOZ_GTK_PROGRESSBAR); @@ -2440,12 +2150,8 @@ moz_gtk_get_widget_border(WidgetNodeType w = GetWidget(MOZ_GTK_SPINBUTTON); break; case MOZ_GTK_SCALE_HORIZONTAL: - ensure_scale_widget(); - w = gHScaleWidget; - break; case MOZ_GTK_SCALE_VERTICAL: - ensure_scale_widget(); - w = gVScaleWidget; + w = GetWidget(widget); break; case MOZ_GTK_FRAME: w = GetWidget(MOZ_GTK_FRAME); @@ -2471,17 +2177,18 @@ moz_gtk_get_widget_border(WidgetNodeType case MOZ_GTK_CHECKMENUITEM: case MOZ_GTK_RADIOMENUITEM: { - if (widget == MOZ_GTK_MENUBARITEM || widget == MOZ_GTK_MENUITEM) { - // Bug 1274143 for MOZ_GTK_MENUBARITEM - w = GetWidget(MOZ_GTK_MENUITEM); + // Bug 1274143 for MOZ_GTK_MENUBARITEM + WidgetNodeType type = + widget == MOZ_GTK_MENUBARITEM ? MOZ_GTK_MENUITEM : widget; + style = ClaimStyleContext(type); + + if (gtk_get_minor_version() < 20) { + moz_gtk_add_style_padding(style, left, top, right, bottom); } else { - ensure_check_menu_item_widget(); - w = gCheckMenuItemWidget; + moz_gtk_add_margin_border_padding(style, + left, top, right, bottom); } - - *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w)); - moz_gtk_add_style_padding(gtk_widget_get_style_context(w), - left, top, right, bottom); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } case MOZ_GTK_INFO_BAR: @@ -2490,11 +2197,86 @@ moz_gtk_get_widget_border(WidgetNodeType case MOZ_GTK_TOOLTIP: { style = ClaimStyleContext(MOZ_GTK_TOOLTIP); - moz_gtk_add_style_border(style, left, top, right, bottom); - moz_gtk_add_style_padding(style, left, top, right, bottom); + // In GTK 3 there are 6 pixels of additional margin around the box. + // See details there: + // https://github.com/GNOME/gtk/blob/5ea69a136bd7e4970b3a800390e20314665aaed2/gtk/ui/gtktooltipwindow.ui#L11 + *left = *right = *top = *bottom = 6; + + // We also need to add margin/padding/borders from Tooltip content. + // Tooltip contains horizontal box, where icon and label is put. + // We ignore icon as long as we don't have support for it. + GtkStyleContext* boxStyle = + CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), + style); + moz_gtk_add_margin_border_padding(boxStyle, + left, top, right, bottom); + + GtkStyleContext* labelStyle = + CreateStyleForWidget(gtk_label_new(nullptr), boxStyle); + moz_gtk_add_margin_border_padding(labelStyle, + left, top, right, bottom); + + g_object_unref(labelStyle); + g_object_unref(boxStyle); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } + case MOZ_GTK_SCROLLBAR_VERTICAL: + case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL: + { + if (gtk_check_version(3,20,0) == nullptr) { + style = ClaimStyleContext(widget); + moz_gtk_add_margin_border_padding(style, left, top, right, bottom); + ReleaseStyleContext(style); + if (widget == MOZ_GTK_SCROLLBAR_VERTICAL) { + style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL); + moz_gtk_add_margin_border_padding(style, left, top, right, bottom); + ReleaseStyleContext(style); + } + } else { + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + /* Top and bottom border for whole vertical scrollbar, top and bottom + * border for horizontal track - to correctly position thumb element */ + *top = *bottom = metrics.trough_border; + } + return MOZ_GTK_SUCCESS; + } + break; + + case MOZ_GTK_SCROLLBAR_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL: + { + if (gtk_check_version(3,20,0) == nullptr) { + style = ClaimStyleContext(widget); + moz_gtk_add_margin_border_padding(style, left, top, right, bottom); + ReleaseStyleContext(style); + if (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) { + style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL); + moz_gtk_add_margin_border_padding(style, left, top, right, bottom); + ReleaseStyleContext(style); + } + } else { + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + *left = *right = metrics.trough_border; + } + return MOZ_GTK_SUCCESS; + } + break; + + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + { + if (gtk_check_version(3,20,0) == nullptr) { + style = ClaimStyleContext(widget); + moz_gtk_add_margin_border_padding(style, left, top, right, bottom); + ReleaseStyleContext(style); + } + return MOZ_GTK_SUCCESS; + } + break; /* These widgets have no borders, since they are not containers. */ case MOZ_GTK_CHECKBUTTON_LABEL: case MOZ_GTK_RADIOBUTTON_LABEL: @@ -2503,10 +2285,6 @@ moz_gtk_get_widget_border(WidgetNodeType case MOZ_GTK_CHECKBUTTON: case MOZ_GTK_RADIOBUTTON: case MOZ_GTK_SCROLLBAR_BUTTON: - case MOZ_GTK_SCROLLBAR_HORIZONTAL: - case MOZ_GTK_SCROLLBAR_VERTICAL: - case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: - case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: case MOZ_GTK_SCALE_THUMB_HORIZONTAL: case MOZ_GTK_SCALE_THUMB_VERTICAL: case MOZ_GTK_GRIPPER: @@ -2540,35 +2318,48 @@ moz_gtk_get_widget_border(WidgetNodeType gint moz_gtk_get_tab_border(gint* left, gint* top, gint* right, gint* bottom, - GtkTextDirection direction, GtkTabFlags flags) + GtkTextDirection direction, GtkTabFlags flags, + WidgetNodeType widget) { - GtkStyleContext* style; - int tab_curvature; - - ensure_tab_widget(); - - style = gtk_widget_get_style_context(gTabWidget); - gtk_style_context_save(style); - moz_gtk_tab_prepare_style_context(style, flags); + GtkStyleContext* style = ClaimStyleContext(widget, direction, + GetStateFlagsFromGtkTabFlags(flags)); *left = *top = *right = *bottom = 0; moz_gtk_add_style_padding(style, left, top, right, bottom); - gtk_style_context_get_style(style, "tab-curvature", &tab_curvature, NULL); - *left += tab_curvature; - *right += tab_curvature; + // Gtk >= 3.20 does not use those styles + if (gtk_check_version(3, 20, 0) != nullptr) { + int tab_curvature; - if (flags & MOZ_GTK_TAB_FIRST) { - int initial_gap; - gtk_style_context_get_style(style, "initial-gap", &initial_gap, NULL); - if (direction == GTK_TEXT_DIR_RTL) - *right += initial_gap; - else - *left += initial_gap; - } + gtk_style_context_get_style(style, "tab-curvature", &tab_curvature, NULL); + *left += tab_curvature; + *right += tab_curvature; + + if (flags & MOZ_GTK_TAB_FIRST) { + int initial_gap = 0; + gtk_style_context_get_style(style, "initial-gap", &initial_gap, NULL); + if (direction == GTK_TEXT_DIR_RTL) + *right += initial_gap; + else + *left += initial_gap; + } + } else { + GtkBorder margin; - gtk_style_context_restore(style); + gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); + *left += margin.left; + *right += margin.right; + if (flags & MOZ_GTK_TAB_FIRST) { + ReleaseStyleContext(style); + style = ClaimStyleContext(MOZ_GTK_NOTEBOOK_HEADER, direction); + gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin); + *left += margin.left; + *right += margin.right; + } + } + + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2581,9 +2372,9 @@ moz_gtk_get_combo_box_entry_button_size( * as well as the minimum arrow size and its padding * */ GtkRequisition requisition; - ensure_combo_box_entry_widgets(); - gtk_widget_get_preferred_size(gComboBoxEntryButtonWidget, NULL, &requisition); + gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON), + NULL, &requisition); *width = requisition.width; *height = requisition.height; @@ -2595,10 +2386,11 @@ moz_gtk_get_tab_scroll_arrow_size(gint* { gint arrow_size; - ensure_tab_widget(); - gtk_style_context_get_style(gtk_widget_get_style_context(gTabWidget), + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_TABPANELS); + gtk_style_context_get_style(style, "scroll-arrow-hlength", &arrow_size, NULL); + ReleaseStyleContext(style); *height = *width = arrow_size; @@ -2611,8 +2403,7 @@ moz_gtk_get_arrow_size(WidgetNodeType wi GtkWidget* widget; switch (widgetType) { case MOZ_GTK_DROPDOWN: - ensure_combo_box_widgets(); - widget = gComboBoxArrowWidget; + widget = GetWidget(MOZ_GTK_COMBOBOX_ARROW); break; default: widget = GetWidget(MOZ_GTK_BUTTON_ARROW); @@ -2659,11 +2450,9 @@ moz_gtk_get_expander_size(gint* size) gint moz_gtk_get_treeview_expander_size(gint* size) { - ensure_tree_view_widget(); - gtk_style_context_get_style(gtk_widget_get_style_context(gTreeViewWidget), - "expander-size", size, - NULL); - + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TREEVIEW); + gtk_style_context_get_style(style, "expander-size", size, NULL); + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2674,14 +2463,7 @@ moz_gtk_get_menu_separator_height(gint * gboolean wide_separators; gint separator_height; GtkBorder padding; - GtkStyleContext* style; - guint border_width; - - border_width = - gtk_container_get_border_width(GTK_CONTAINER( - GetWidget(MOZ_GTK_MENUSEPARATOR))); - - style = ClaimStyleContext(MOZ_GTK_MENUSEPARATOR); + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_MENUSEPARATOR); gtk_style_context_get_padding(style, GTK_STATE_FLAG_NORMAL, &padding); gtk_style_context_save(style); @@ -2695,7 +2477,7 @@ moz_gtk_get_menu_separator_height(gint * gtk_style_context_restore(style); ReleaseStyleContext(style); - *size = padding.top + padding.bottom + border_width*2; + *size = padding.top + padding.bottom; *size += (wide_separators) ? separator_height : 1; return MOZ_GTK_SUCCESS; @@ -2726,34 +2508,60 @@ void moz_gtk_get_scale_metrics(GtkOrientation orient, gint* scale_width, gint* scale_height) { - gint thumb_length, thumb_height, trough_border; - GtkWidget* widget = orient == GTK_ORIENTATION_HORIZONTAL ? - gHScaleWidget : gVScaleWidget; - moz_gtk_get_scalethumb_metrics(orient, &thumb_length, &thumb_height); - gtk_style_context_get_style(gtk_widget_get_style_context(widget), - "trough-border", &trough_border, NULL); - - if (orient == GTK_ORIENTATION_HORIZONTAL) { - *scale_width = thumb_length + trough_border * 2; - *scale_height = thumb_height + trough_border * 2; + WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ? + MOZ_GTK_SCALE_HORIZONTAL : + MOZ_GTK_SCALE_VERTICAL; + + if (gtk_check_version(3, 20, 0) != nullptr) { + gint thumb_length, thumb_height, trough_border; + moz_gtk_get_scalethumb_metrics(orient, &thumb_length, &thumb_height); + + GtkStyleContext* style = ClaimStyleContext(widget); + gtk_style_context_get_style(style, "trough-border", &trough_border, NULL); + + if (orient == GTK_ORIENTATION_HORIZONTAL) { + *scale_width = thumb_length + trough_border * 2; + *scale_height = thumb_height + trough_border * 2; + } else { + *scale_width = thumb_height + trough_border * 2; + *scale_height = thumb_length + trough_border * 2; + } + ReleaseStyleContext(style); } else { - *scale_width = thumb_height + trough_border * 2; - *scale_height = thumb_length + trough_border * 2; + GtkStyleContext* style = ClaimStyleContext(widget); + gtk_style_context_get(style, gtk_style_context_get_state(style), + "min-width", scale_width, + "min-height", scale_height, + nullptr); + ReleaseStyleContext(style); } } gint moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height) { - GtkWidget* widget; - ensure_scale_widget(); - widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); - - gtk_style_context_get_style(gtk_widget_get_style_context(widget), - "slider_length", thumb_length, - "slider_width", thumb_height, - NULL); + if (gtk_check_version(3, 20, 0) != nullptr) { + WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ? + MOZ_GTK_SCALE_HORIZONTAL: + MOZ_GTK_SCALE_VERTICAL; + GtkStyleContext* style = ClaimStyleContext(widget); + gtk_style_context_get_style(style, + "slider_length", thumb_length, + "slider_width", thumb_height, + NULL); + ReleaseStyleContext(style); + } else { + WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ? + MOZ_GTK_SCALE_THUMB_HORIZONTAL: + MOZ_GTK_SCALE_THUMB_VERTICAL; + GtkStyleContext* style = ClaimStyleContext(widget); + gtk_style_context_get(style, gtk_style_context_get_state(style), + "min-width", thumb_length, + "min-height", thumb_height, + nullptr); + ReleaseStyleContext(style); + } return MOZ_GTK_SUCCESS; } @@ -2761,6 +2569,9 @@ moz_gtk_get_scalethumb_metrics(GtkOrient gint moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) { + // For Gtk >= 3.20 scrollbar metrics are ignored + MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr); + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_VERTICAL); gtk_style_context_get_style(style, "slider_width", &metrics->slider_width, @@ -2771,41 +2582,9 @@ moz_gtk_get_scrollbar_metrics(MozGtkScro nullptr); ReleaseStyleContext(style); - if(!gtk_check_version(3, 20, 0)) { - style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_THUMB_VERTICAL); - gtk_style_context_get(style, gtk_style_context_get_state(style), - "min-height", &metrics->min_slider_size, nullptr); - ReleaseStyleContext(style); - } - return MOZ_GTK_SUCCESS; } -gboolean -moz_gtk_images_in_menus() -{ - gboolean result; - GtkSettings* settings; - - ensure_image_menu_item_widget(); - settings = gtk_widget_get_settings(gImageMenuItemWidget); - - g_object_get(settings, "gtk-menu-images", &result, NULL); - return result; -} - -gboolean -moz_gtk_images_in_buttons() -{ - gboolean result; - GtkSettings* settings; - - settings = gtk_widget_get_settings(GetWidget(MOZ_GTK_BUTTON)); - - g_object_get(settings, "gtk-button-images", &result, NULL); - return result; -} - /* cairo_t *cr argument has to be a system-cairo. */ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr, @@ -2846,10 +2625,25 @@ moz_gtk_widget_paint(WidgetNodeType widg break; case MOZ_GTK_SCROLLBAR_HORIZONTAL: case MOZ_GTK_SCROLLBAR_VERTICAL: - return moz_gtk_scrollbar_trough_paint(widget, cr, rect, - state, - (GtkScrollbarTrackFlags) flags, - direction); + if (gtk_check_version(3,20,0) == nullptr) { + return moz_gtk_scrollbar_paint(widget, cr, rect, state, direction); + } else { + WidgetNodeType trough_widget = (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) ? + MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL : MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL; + return moz_gtk_scrollbar_trough_paint(trough_widget, cr, rect, + state, + (GtkScrollbarTrackFlags) flags, + direction); + } + break; + case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL: + if (gtk_check_version(3,20,0) == nullptr) { + return moz_gtk_scrollbar_trough_paint(widget, cr, rect, + state, + (GtkScrollbarTrackFlags) flags, + direction); + } break; case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: @@ -2876,9 +2670,13 @@ moz_gtk_widget_paint(WidgetNodeType widg state, direction); break; case MOZ_GTK_SPINBUTTON_ENTRY: - // TODO - use MOZ_GTK_SPINBUTTON_ENTRY style directly - return moz_gtk_entry_paint(cr, rect, state, - GetWidget(MOZ_GTK_SPINBUTTON), direction); + { + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SPINBUTTON_ENTRY, + direction, GetStateFlagsFromGtkWidgetState(state)); + gint ret = moz_gtk_entry_paint(cr, rect, state, style); + ReleaseStyleContext(style); + return ret; + } break; case MOZ_GTK_GRIPPER: return moz_gtk_gripper_paint(cr, rect, state, @@ -2903,9 +2701,13 @@ moz_gtk_widget_paint(WidgetNodeType widg (GtkExpanderStyle) flags, direction); break; case MOZ_GTK_ENTRY: - return moz_gtk_entry_paint(cr, rect, state, GetWidget(MOZ_GTK_ENTRY), - direction); - break; + { + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_ENTRY, + direction, GetStateFlagsFromGtkWidgetState(state)); + gint ret = moz_gtk_entry_paint(cr, rect, state, style); + ReleaseStyleContext(style); + return ret; + } case MOZ_GTK_TEXT_VIEW: return moz_gtk_text_view_paint(cr, rect, state, direction); break; @@ -2917,9 +2719,13 @@ moz_gtk_widget_paint(WidgetNodeType widg state, flags, direction); break; case MOZ_GTK_DROPDOWN_ENTRY: - ensure_combo_box_entry_widgets(); - return moz_gtk_entry_paint(cr, rect, state, - gComboBoxEntryTextareaWidget, direction); + { + GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA, + direction, GetStateFlagsFromGtkWidgetState(state)); + gint ret = moz_gtk_entry_paint(cr, rect, state, style); + ReleaseStyleContext(style); + return ret; + } break; case MOZ_GTK_CHECKBUTTON_CONTAINER: case MOZ_GTK_RADIOBUTTON_CONTAINER: @@ -2957,9 +2763,10 @@ moz_gtk_widget_paint(WidgetNodeType widg return moz_gtk_progress_chunk_paint(cr, rect, direction, widget); break; - case MOZ_GTK_TAB: + case MOZ_GTK_TAB_TOP: + case MOZ_GTK_TAB_BOTTOM: return moz_gtk_tab_paint(cr, rect, state, - (GtkTabFlags) flags, direction); + (GtkTabFlags) flags, direction, widget); break; case MOZ_GTK_TABPANELS: return moz_gtk_tabpanels_paint(cr, rect, direction); @@ -2992,10 +2799,8 @@ moz_gtk_widget_paint(WidgetNodeType widg break; case MOZ_GTK_CHECKMENUITEM: case MOZ_GTK_RADIOMENUITEM: - return moz_gtk_check_menu_item_paint(cr, rect, state, - (gboolean) flags, - (widget == MOZ_GTK_RADIOMENUITEM), - direction); + return moz_gtk_check_menu_item_paint(widget, cr, rect, state, + (gboolean) flags, direction); break; case MOZ_GTK_SPLITTER_HORIZONTAL: return moz_gtk_vpaned_paint(cr, rect, state); @@ -3043,31 +2848,6 @@ moz_gtk_shutdown() /* This will destroy all of our widgets */ ResetWidgetCache(); - /* TODO - replace it with appropriate widget */ - if (gTreeHeaderSortArrowWidget) - gtk_widget_destroy(gTreeHeaderSortArrowWidget); - - gProtoLayout = NULL; - gHScaleWidget = NULL; - gVScaleWidget = NULL; - gComboBoxWidget = NULL; - gComboBoxButtonWidget = NULL; - gComboBoxSeparatorWidget = NULL; - gComboBoxArrowWidget = NULL; - gComboBoxEntryWidget = NULL; - gComboBoxEntryButtonWidget = NULL; - gComboBoxEntryArrowWidget = NULL; - gComboBoxEntryTextareaWidget = NULL; - gTabWidget = NULL; - gImageMenuItemWidget = NULL; - gCheckMenuItemWidget = NULL; - gTreeViewWidget = NULL; - gMiddleTreeViewColumn = NULL; - gTreeHeaderCellWidget = NULL; - gTreeHeaderSortArrowWidget = NULL; - gHPanedWidget = NULL; - gVPanedWidget = NULL; - is_initialized = FALSE; return MOZ_GTK_SUCCESS; diff -up firefox-50.0/widget/gtk/gtkdrawing.h.widget-rebase firefox-50.0/widget/gtk/gtkdrawing.h --- firefox-50.0/widget/gtk/gtkdrawing.h.widget-rebase 2016-10-31 21:15:38.000000000 +0100 +++ firefox-50.0/widget/gtk/gtkdrawing.h 2016-11-24 11:57:52.034064779 +0100 @@ -61,8 +61,6 @@ typedef enum { typedef enum { /* first eight bits are used to pass a margin */ MOZ_GTK_TAB_MARGIN_MASK = 0xFF, - /* bottom tabs */ - MOZ_GTK_TAB_BOTTOM = 1 << 8, /* the first tab in the group */ MOZ_GTK_TAB_FIRST = 1 << 9, /* the selected tab */ @@ -128,6 +126,11 @@ typedef enum { /* Paints a GtkScale. */ MOZ_GTK_SCALE_HORIZONTAL, MOZ_GTK_SCALE_VERTICAL, + /* Paints a GtkScale trough. */ + MOZ_GTK_SCALE_CONTENTS_HORIZONTAL, + MOZ_GTK_SCALE_CONTENTS_VERTICAL, + MOZ_GTK_SCALE_TROUGH_HORIZONTAL, + MOZ_GTK_SCALE_TROUGH_VERTICAL, /* Paints a GtkScale thumb. */ MOZ_GTK_SCALE_THUMB_HORIZONTAL, MOZ_GTK_SCALE_THUMB_VERTICAL, @@ -173,14 +176,22 @@ typedef enum { MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE, /* Paints a progress chunk of a vertical indeterminated GtkProgressBar. */ MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE, + /* Used as root style of whole GtkNotebook widget */ + MOZ_GTK_NOTEBOOK, + /* Used as root style of active GtkNotebook area which contains tabs and arrows. */ + MOZ_GTK_NOTEBOOK_HEADER, /* Paints a tab of a GtkNotebook. flags is a GtkTabFlags, defined above. */ - MOZ_GTK_TAB, + MOZ_GTK_TAB_TOP, + /* Paints a tab of a GtkNotebook. flags is a GtkTabFlags, defined above. */ + MOZ_GTK_TAB_BOTTOM, /* Paints the background and border of a GtkNotebook. */ MOZ_GTK_TABPANELS, /* Paints a GtkArrow for a GtkNotebook. flags is a GtkArrowType. */ MOZ_GTK_TAB_SCROLLARROW, - /* Paints the background and border of a GtkTreeView */ + /* Paints the expander and border of a GtkTreeView */ MOZ_GTK_TREEVIEW, + /* Paints the border of a GtkTreeView */ + MOZ_GTK_TREEVIEW_VIEW, /* Paints treeheader cells */ MOZ_GTK_TREE_HEADER_CELL, /* Paints sort arrows in treeheader cells */ @@ -199,19 +210,44 @@ typedef enum { MOZ_GTK_MENUBARITEM, /* Paints items of popup menus. */ MOZ_GTK_MENUITEM, + /* Paints a menuitem with check indicator, or the gets the style context for + a menuitem that contains a checkbox. */ MOZ_GTK_CHECKMENUITEM, + /* Gets the style context for a checkbox in a check menuitem. */ + MOZ_GTK_CHECKMENUITEM_INDICATOR, MOZ_GTK_RADIOMENUITEM, + MOZ_GTK_RADIOMENUITEM_INDICATOR, MOZ_GTK_MENUSEPARATOR, - /* Paints a GtkVPaned separator */ + /* GtkVPaned base class */ MOZ_GTK_SPLITTER_HORIZONTAL, - /* Paints a GtkHPaned separator */ + /* GtkHPaned base class */ MOZ_GTK_SPLITTER_VERTICAL, + /* Paints a GtkVPaned separator */ + MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL, + /* Paints a GtkHPaned separator */ + MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL, /* Paints the background of a window, dialog or page. */ MOZ_GTK_WINDOW, /* Window container for all widgets */ MOZ_GTK_WINDOW_CONTAINER, /* Paints a GtkInfoBar, for notifications. */ MOZ_GTK_INFO_BAR, + /* Used for widget tree construction. */ + MOZ_GTK_COMBOBOX, + /* Paints a GtkComboBox button widget. */ + MOZ_GTK_COMBOBOX_BUTTON, + /* Paints a GtkComboBox arrow widget. */ + MOZ_GTK_COMBOBOX_ARROW, + /* Paints a GtkComboBox separator widget. */ + MOZ_GTK_COMBOBOX_SEPARATOR, + /* Used for widget tree construction. */ + MOZ_GTK_COMBOBOX_ENTRY, + /* Paints a GtkComboBox entry widget. */ + MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA, + /* Paints a GtkComboBox entry button widget. */ + MOZ_GTK_COMBOBOX_ENTRY_BUTTON, + /* Paints a GtkComboBox entry arrow widget. */ + MOZ_GTK_COMBOBOX_ENTRY_ARROW, /* Used for scrolled window shell. */ MOZ_GTK_SCROLLED_WINDOW, @@ -300,12 +336,14 @@ gint moz_gtk_get_widget_border(WidgetNod * top/bottom: [OUT] the tab's top/bottom border * direction: the text direction for the widget * flags: tab-dependant flags; see the GtkTabFlags definition. + * widget: tab widget * * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise */ gint moz_gtk_get_tab_border(gint* left, gint* top, gint* right, gint* bottom, - GtkTextDirection direction, GtkTabFlags flags); + GtkTextDirection direction, GtkTabFlags flags, + WidgetNodeType widget); /** * Get the desired size of a GtkCheckButton @@ -481,19 +519,8 @@ GtkWidget* moz_gtk_get_scrollbar_widget( /** * Get the YTHICKNESS of a tab (notebook extension). */ -gint moz_gtk_get_tab_thickness(void); - -/** - * Get a boolean which indicates whether or not to use images in menus. - * If TRUE, use images in menus. - */ -gboolean moz_gtk_images_in_menus(void); - -/** - * Get a boolean which indicates whether or not to use images in buttons. - * If TRUE, use images in buttons. - */ -gboolean moz_gtk_images_in_buttons(void); +gint +moz_gtk_get_tab_thickness(WidgetNodeType aNodeType); /** * Get a boolean which indicates whether the theme draws scrollbar buttons. @@ -501,6 +528,13 @@ gboolean moz_gtk_images_in_buttons(void) */ gboolean moz_gtk_has_scrollbar_buttons(void); +/** + * Get minimum widget size as sum of margin, padding, border and min-width, + * min-height. + */ +void moz_gtk_get_widget_min_size(WidgetNodeType aGtkWidgetType, int* width, + int* height); + #if (MOZ_WIDGET_GTK == 2) #ifdef __cplusplus } diff -up firefox-50.0/widget/gtk/mozgtk/mozgtk.c.widget-rebase firefox-50.0/widget/gtk/mozgtk/mozgtk.c --- firefox-50.0/widget/gtk/mozgtk/mozgtk.c.widget-rebase 2016-10-31 21:15:38.000000000 +0100 +++ firefox-50.0/widget/gtk/mozgtk/mozgtk.c 2016-11-24 11:57:52.034064779 +0100 @@ -9,6 +9,7 @@ STUB(gdk_atom_name) STUB(gdk_beep) STUB(gdk_cairo_create) STUB(gdk_color_free) +STUB(gdk_color_parse) STUB(gdk_cursor_new_for_display) STUB(gdk_cursor_new_from_name) STUB(gdk_cursor_new_from_pixbuf) @@ -244,7 +245,6 @@ STUB(gtk_icon_theme_get_icon_sizes) STUB(gtk_icon_theme_lookup_by_gicon) STUB(gtk_icon_theme_lookup_icon) STUB(gtk_image_get_type) -STUB(gtk_image_menu_item_new) STUB(gtk_image_new) STUB(gtk_image_new_from_stock) STUB(gtk_image_set_from_pixbuf) @@ -530,6 +530,7 @@ STUB(gtk_get_minor_version) STUB(gtk_menu_button_new) STUB(gtk_offscreen_window_new) STUB(gtk_paned_new) +STUB(gtk_radio_menu_item_new) STUB(gtk_render_activity) STUB(gtk_render_arrow) STUB(gtk_render_background) @@ -561,6 +562,7 @@ STUB(gtk_style_context_get_state) STUB(gtk_style_context_get_style) STUB(gtk_style_context_has_class) STUB(gtk_style_context_invalidate) +STUB(gtk_style_context_list_classes) STUB(gtk_style_context_new) STUB(gtk_style_context_remove_class) STUB(gtk_style_context_remove_region) @@ -575,10 +577,10 @@ STUB(gtk_tree_view_column_get_button) STUB(gtk_widget_get_preferred_size) STUB(gtk_widget_get_state_flags) STUB(gtk_widget_get_style_context) -STUB(gtk_widget_path_append_for_widget) STUB(gtk_widget_path_append_type) STUB(gtk_widget_path_copy) STUB(gtk_widget_path_free) +STUB(gtk_widget_path_iter_add_class) STUB(gtk_widget_path_new) STUB(gtk_widget_path_unref) STUB(gtk_widget_set_visual) diff -up firefox-50.0/widget/gtk/nsLookAndFeel.cpp.widget-rebase firefox-50.0/widget/gtk/nsLookAndFeel.cpp --- firefox-50.0/widget/gtk/nsLookAndFeel.cpp.widget-rebase 2016-11-24 12:00:04.563704500 +0100 +++ firefox-50.0/widget/gtk/nsLookAndFeel.cpp 2016-11-24 12:00:47.316588277 +0100 @@ -786,10 +786,10 @@ nsLookAndFeel::GetIntImpl(IntID aID, int aResult = NS_STYLE_TEXT_DECORATION_STYLE_WAVY; break; case eIntID_ImagesInMenus: - aResult = moz_gtk_images_in_menus(); + aResult = 0; break; case eIntID_ImagesInButtons: - aResult = moz_gtk_images_in_buttons(); + aResult = 0; break; case eIntID_MenuBarDrag: aResult = sMenuSupportsDrag; diff -up firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp.widget-rebase firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp --- firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp.widget-rebase 2016-10-31 21:15:38.000000000 +0100 +++ firefox-50.0/widget/gtk/nsNativeThemeGTK.cpp 2016-11-24 11:57:52.034064779 +0100 @@ -149,19 +149,15 @@ static void SetWidgetStateSafe(uint8_t * aSafeVector[key >> 3] |= (1 << (key & 7)); } -static GtkTextDirection GetTextDirection(nsIFrame* aFrame) +/* static */ GtkTextDirection +nsNativeThemeGTK::GetTextDirection(nsIFrame* aFrame) { - if (!aFrame) - return GTK_TEXT_DIR_NONE; - - switch (aFrame->StyleVisibility()->mDirection) { - case NS_STYLE_DIRECTION_RTL: - return GTK_TEXT_DIR_RTL; - case NS_STYLE_DIRECTION_LTR: - return GTK_TEXT_DIR_LTR; - } - - return GTK_TEXT_DIR_NONE; + // IsFrameRTL() treats vertical-rl modes as right-to-left (in addition to + // horizontal text with direction=RTL), rather than just considering the + // text direction. GtkTextDirection does not have distinct values for + // vertical writing modes, but considering the block flow direction is + // important for resizers and scrollbar elements, at least. + return IsFrameRTL(aFrame) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR; } // Returns positive for negative margins (otherwise 0). @@ -193,205 +189,199 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u gint* aWidgetFlags) { if (aState) { - if (!aFrame) { - // reset the entire struct to zero - memset(aState, 0, sizeof(GtkWidgetState)); - } else { + // For XUL checkboxes and radio buttons, the state of the parent + // determines our state. + nsIFrame *stateFrame = aFrame; + if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX || + aWidgetType == NS_THEME_RADIO)) || + aWidgetType == NS_THEME_CHECKBOX_LABEL || + aWidgetType == NS_THEME_RADIO_LABEL)) { - // For XUL checkboxes and radio buttons, the state of the parent - // determines our state. - nsIFrame *stateFrame = aFrame; - if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX || - aWidgetType == NS_THEME_RADIO)) || - aWidgetType == NS_THEME_CHECKBOX_LABEL || - aWidgetType == NS_THEME_RADIO_LABEL)) { - - nsIAtom* atom = nullptr; - if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { - if (aWidgetType == NS_THEME_CHECKBOX_LABEL || - aWidgetType == NS_THEME_RADIO_LABEL) { - // Adjust stateFrame so GetContentState finds the correct state. - stateFrame = aFrame = aFrame->GetParent()->GetParent(); - } else { - // GetContentState knows to look one frame up for radio/checkbox - // widgets, so don't adjust stateFrame here. - aFrame = aFrame->GetParent(); + nsIAtom* atom = nullptr; + if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { + if (aWidgetType == NS_THEME_CHECKBOX_LABEL || + aWidgetType == NS_THEME_RADIO_LABEL) { + // Adjust stateFrame so GetContentState finds the correct state. + stateFrame = aFrame = aFrame->GetParent()->GetParent(); + } else { + // GetContentState knows to look one frame up for radio/checkbox + // widgets, so don't adjust stateFrame here. + aFrame = aFrame->GetParent(); + } + if (aWidgetFlags) { + if (!atom) { + atom = (aWidgetType == NS_THEME_CHECKBOX || + aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked + : nsGkAtoms::selected; } - if (aWidgetFlags) { - if (!atom) { - atom = (aWidgetType == NS_THEME_CHECKBOX || - aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked - : nsGkAtoms::selected; - } - *aWidgetFlags = CheckBooleanAttr(aFrame, atom); + *aWidgetFlags = CheckBooleanAttr(aFrame, atom); + } + } else { + if (aWidgetFlags) { + nsCOMPtr inputElt(do_QueryInterface(aFrame->GetContent())); + *aWidgetFlags = 0; + if (inputElt) { + bool isHTMLChecked; + inputElt->GetChecked(&isHTMLChecked); + if (isHTMLChecked) + *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; } - } else { - if (aWidgetFlags) { - nsCOMPtr inputElt(do_QueryInterface(aFrame->GetContent())); - *aWidgetFlags = 0; - if (inputElt) { - bool isHTMLChecked; - inputElt->GetChecked(&isHTMLChecked); - if (isHTMLChecked) - *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; - } - if (GetIndeterminate(aFrame)) - *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT; - } + if (GetIndeterminate(aFrame)) + *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT; } - } else if (aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || - aWidgetType == NS_THEME_TREEHEADERSORTARROW || - aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS || - aWidgetType == NS_THEME_BUTTON_ARROW_NEXT || - aWidgetType == NS_THEME_BUTTON_ARROW_UP || - aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) { - // The state of an arrow comes from its parent. - stateFrame = aFrame = aFrame->GetParent(); - } - - EventStates eventState = GetContentState(stateFrame, aWidgetType); - - aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame); - aState->active = eventState.HasState(NS_EVENT_STATE_ACTIVE); - aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS); - aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER); - aState->isDefault = IsDefaultButton(aFrame); - aState->canDefault = FALSE; // XXX fix me - aState->depressed = FALSE; - - if (aWidgetType == NS_THEME_FOCUS_OUTLINE) { - aState->disabled = FALSE; - aState->active = FALSE; - aState->inHover = FALSE; - aState->isDefault = FALSE; - aState->canDefault = FALSE; - - aState->focused = TRUE; - aState->depressed = TRUE; // see moz_gtk_entry_paint() - } else if (aWidgetType == NS_THEME_BUTTON || - aWidgetType == NS_THEME_TOOLBARBUTTON || - aWidgetType == NS_THEME_DUALBUTTON || - aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || - aWidgetType == NS_THEME_MENULIST || - aWidgetType == NS_THEME_MENULIST_BUTTON) { - aState->active &= aState->inHover; } - - if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { - // For these widget types, some element (either a child or parent) - // actually has element focus, so we check the focused attribute - // to see whether to draw in the focused state. - if (aWidgetType == NS_THEME_NUMBER_INPUT || - aWidgetType == NS_THEME_TEXTFIELD || - aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || - aWidgetType == NS_THEME_MENULIST_TEXTFIELD || - aWidgetType == NS_THEME_SPINNER_TEXTFIELD || - aWidgetType == NS_THEME_RADIO_CONTAINER || - aWidgetType == NS_THEME_RADIO_LABEL) { - aState->focused = IsFocused(aFrame); - } else if (aWidgetType == NS_THEME_RADIO || - aWidgetType == NS_THEME_CHECKBOX) { - // In XUL, checkboxes and radios shouldn't have focus rings, their labels do - aState->focused = FALSE; + } else if (aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || + aWidgetType == NS_THEME_TREEHEADERSORTARROW || + aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS || + aWidgetType == NS_THEME_BUTTON_ARROW_NEXT || + aWidgetType == NS_THEME_BUTTON_ARROW_UP || + aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) { + // The state of an arrow comes from its parent. + stateFrame = aFrame = aFrame->GetParent(); + } + + EventStates eventState = GetContentState(stateFrame, aWidgetType); + + aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame); + aState->active = eventState.HasState(NS_EVENT_STATE_ACTIVE); + aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS); + aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER); + aState->isDefault = IsDefaultButton(aFrame); + aState->canDefault = FALSE; // XXX fix me + aState->depressed = FALSE; + + if (aWidgetType == NS_THEME_FOCUS_OUTLINE) { + aState->disabled = FALSE; + aState->active = FALSE; + aState->inHover = FALSE; + aState->isDefault = FALSE; + aState->canDefault = FALSE; + + aState->focused = TRUE; + aState->depressed = TRUE; // see moz_gtk_entry_paint() + } else if (aWidgetType == NS_THEME_BUTTON || + aWidgetType == NS_THEME_TOOLBARBUTTON || + aWidgetType == NS_THEME_DUALBUTTON || + aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || + aWidgetType == NS_THEME_MENULIST || + aWidgetType == NS_THEME_MENULIST_BUTTON) { + aState->active &= aState->inHover; + } + + if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { + // For these widget types, some element (either a child or parent) + // actually has element focus, so we check the focused attribute + // to see whether to draw in the focused state. + if (aWidgetType == NS_THEME_NUMBER_INPUT || + aWidgetType == NS_THEME_TEXTFIELD || + aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || + aWidgetType == NS_THEME_MENULIST_TEXTFIELD || + aWidgetType == NS_THEME_SPINNER_TEXTFIELD || + aWidgetType == NS_THEME_RADIO_CONTAINER || + aWidgetType == NS_THEME_RADIO_LABEL) { + aState->focused = IsFocused(aFrame); + } else if (aWidgetType == NS_THEME_RADIO || + aWidgetType == NS_THEME_CHECKBOX) { + // In XUL, checkboxes and radios shouldn't have focus rings, their labels do + aState->focused = FALSE; + } + + if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL || + aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) { + // for scrollbars we need to go up two to go from the thumb to + // the slider to the actual scrollbar object + nsIFrame *tmpFrame = aFrame->GetParent()->GetParent(); + + aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0); + aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100); + + if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) { + aState->active = TRUE; + // Set hover state to emulate Gtk style of active scrollbar thumb + aState->inHover = TRUE; } + } - if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL || - aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) { - // for scrollbars we need to go up two to go from the thumb to - // the slider to the actual scrollbar object - nsIFrame *tmpFrame = aFrame->GetParent()->GetParent(); - - aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0); - aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100); - - if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) { - aState->active = TRUE; - // Set hover state to emulate Gtk style of active scrollbar thumb - aState->inHover = TRUE; - } + if (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || + aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || + aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || + aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) { + // set the state to disabled when the scrollbar is scrolled to + // the beginning or the end, depending on the button type. + int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); + int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); + if (ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType)) { + aState->disabled = true; } - if (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP || - aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN || - aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || - aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) { - // set the state to disabled when the scrollbar is scrolled to - // the beginning or the end, depending on the button type. - int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); - int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); - if (ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType)) { - aState->disabled = true; - } + // In order to simulate native GTK scrollbar click behavior, + // we set the active attribute on the element to true if it's + // pressed with any mouse button. + // This allows us to show that it's active without setting :active + else if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) + aState->active = true; + + if (aWidgetFlags) { + *aWidgetFlags = GetScrollbarButtonType(aFrame); + if (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP < 2) + *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL; + } + } - // In order to simulate native GTK scrollbar click behavior, - // we set the active attribute on the element to true if it's - // pressed with any mouse button. - // This allows us to show that it's active without setting :active - else if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) - aState->active = true; - - if (aWidgetFlags) { - *aWidgetFlags = GetScrollbarButtonType(aFrame); - if (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP < 2) - *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL; - } + // menu item state is determined by the attribute "_moz-menuactive", + // and not by the mouse hovering (accessibility). as a special case, + // menus which are children of a menu bar are only marked as prelight + // if they are open, not on normal hover. + + if (aWidgetType == NS_THEME_MENUITEM || + aWidgetType == NS_THEME_CHECKMENUITEM || + aWidgetType == NS_THEME_RADIOMENUITEM || + aWidgetType == NS_THEME_MENUSEPARATOR || + aWidgetType == NS_THEME_MENUARROW) { + bool isTopLevel = false; + nsMenuFrame *menuFrame = do_QueryFrame(aFrame); + if (menuFrame) { + isTopLevel = menuFrame->IsOnMenuBar(); } - // menu item state is determined by the attribute "_moz-menuactive", - // and not by the mouse hovering (accessibility). as a special case, - // menus which are children of a menu bar are only marked as prelight - // if they are open, not on normal hover. - - if (aWidgetType == NS_THEME_MENUITEM || - aWidgetType == NS_THEME_CHECKMENUITEM || - aWidgetType == NS_THEME_RADIOMENUITEM || - aWidgetType == NS_THEME_MENUSEPARATOR || - aWidgetType == NS_THEME_MENUARROW) { - bool isTopLevel = false; - nsMenuFrame *menuFrame = do_QueryFrame(aFrame); - if (menuFrame) { - isTopLevel = menuFrame->IsOnMenuBar(); - } + if (isTopLevel) { + aState->inHover = menuFrame->IsOpen(); + } else { + aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); + } - if (isTopLevel) { - aState->inHover = menuFrame->IsOpen(); - } else { - aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); - } + aState->active = FALSE; - aState->active = FALSE; - - if (aWidgetType == NS_THEME_CHECKMENUITEM || - aWidgetType == NS_THEME_RADIOMENUITEM) { - *aWidgetFlags = 0; - if (aFrame && aFrame->GetContent()) { - *aWidgetFlags = aFrame->GetContent()-> - AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, - nsGkAtoms::_true, eIgnoreCase); - } + if (aWidgetType == NS_THEME_CHECKMENUITEM || + aWidgetType == NS_THEME_RADIOMENUITEM) { + *aWidgetFlags = 0; + if (aFrame && aFrame->GetContent()) { + *aWidgetFlags = aFrame->GetContent()-> + AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, + nsGkAtoms::_true, eIgnoreCase); } } + } - // A button with drop down menu open or an activated toggle button - // should always appear depressed. - if (aWidgetType == NS_THEME_BUTTON || - aWidgetType == NS_THEME_TOOLBARBUTTON || - aWidgetType == NS_THEME_DUALBUTTON || - aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || - aWidgetType == NS_THEME_MENULIST || - aWidgetType == NS_THEME_MENULIST_BUTTON) { - bool menuOpen = IsOpenButton(aFrame); - aState->depressed = IsCheckedButton(aFrame) || menuOpen; - // we must not highlight buttons with open drop down menus on hover. - aState->inHover = aState->inHover && !menuOpen; - } - - // When the input field of the drop down button has focus, some themes - // should draw focus for the drop down button as well. - if (aWidgetType == NS_THEME_MENULIST_BUTTON && aWidgetFlags) { - *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused); - } + // A button with drop down menu open or an activated toggle button + // should always appear depressed. + if (aWidgetType == NS_THEME_BUTTON || + aWidgetType == NS_THEME_TOOLBARBUTTON || + aWidgetType == NS_THEME_DUALBUTTON || + aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN || + aWidgetType == NS_THEME_MENULIST || + aWidgetType == NS_THEME_MENULIST_BUTTON) { + bool menuOpen = IsOpenButton(aFrame); + aState->depressed = IsCheckedButton(aFrame) || menuOpen; + // we must not highlight buttons with open drop down menus on hover. + aState->inHover = aState->inHover && !menuOpen; + } + + // When the input field of the drop down button has focus, some themes + // should draw focus for the drop down button as well. + if (aWidgetType == NS_THEME_MENULIST_BUTTON && aWidgetFlags) { + *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused); } } } @@ -435,6 +425,20 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u else *aWidgetFlags = 0; break; + case NS_THEME_SCROLLBARTRACK_HORIZONTAL: + if (gtk_check_version(3,20,0) == nullptr) { + aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL; + } else { + return false; + } + break; + case NS_THEME_SCROLLBARTRACK_VERTICAL: + if (gtk_check_version(3,20,0) == nullptr) { + aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL; + } else { + return false; + } + break; case NS_THEME_SCROLLBARTHUMB_VERTICAL: aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL; break; @@ -644,17 +648,17 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u break; case NS_THEME_TAB: { + if (IsBottomTab(aFrame)) { + aGtkWidgetType = MOZ_GTK_TAB_BOTTOM; + } else { + aGtkWidgetType = MOZ_GTK_TAB_TOP; + } + if (aWidgetFlags) { /* First bits will be used to store max(0,-bmargin) where bmargin * is the bottom margin of the tab in pixels (resp. top margin, * for bottom tabs). */ - if (IsBottomTab(aFrame)) { - *aWidgetFlags = MOZ_GTK_TAB_BOTTOM; - } else { - *aWidgetFlags = 0; - } - - *aWidgetFlags |= GetTabMarginPixels(aFrame); + *aWidgetFlags = GetTabMarginPixels(aFrame); if (IsSelectedTab(aFrame)) *aWidgetFlags |= MOZ_GTK_TAB_SELECTED; @@ -662,8 +666,6 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u if (IsFirstTab(aFrame)) *aWidgetFlags |= MOZ_GTK_TAB_FIRST; } - - aGtkWidgetType = MOZ_GTK_TAB; } break; case NS_THEME_SPLITTER: @@ -862,12 +864,19 @@ DrawThemeWithCairo(gfxContext* aContext, cairo_matrix_t mat; GfxMatrixToCairoMatrix(transform, mat); + nsIntSize clipSize((aDrawSize.width + aScaleFactor - 1) / aScaleFactor, + (aDrawSize.height + aScaleFactor - 1) / aScaleFactor); + #ifndef MOZ_TREE_CAIRO // Directly use the Cairo draw target to render the widget if using system Cairo everywhere. BorrowedCairoContext borrowCairo(aDrawTarget); if (borrowCairo.mCairo) { cairo_set_matrix(borrowCairo.mCairo, &mat); + cairo_new_path(borrowCairo.mCairo); + cairo_rectangle(borrowCairo.mCairo, 0, 0, clipSize.width, clipSize.height); + cairo_clip(borrowCairo.mCairo); + moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect, &aState, aFlags, aDirection); borrowCairo.Finish(); @@ -908,6 +917,10 @@ DrawThemeWithCairo(gfxContext* aContext, cairo_set_matrix(cr, &mat); + cairo_new_path(cr); + cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height); + cairo_clip(cr); + moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection); cairo_destroy(cr); @@ -942,6 +955,10 @@ DrawThemeWithCairo(gfxContext* aContext, cairo_set_matrix(cr, &mat); + cairo_new_path(cr); + cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height); + cairo_clip(cr); + moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection); cairo_destroy(cr); @@ -1062,7 +1079,8 @@ nsNativeThemeGTK::GetExtraSizeForWidget( if (!IsSelectedTab(aFrame)) return false; - gint gap_height = moz_gtk_get_tab_thickness(); + gint gap_height = moz_gtk_get_tab_thickness(IsBottomTab(aFrame) ? + MOZ_GTK_TAB_BOTTOM : MOZ_GTK_TAB_TOP); if (!gap_height) return false; @@ -1097,14 +1115,7 @@ nsNativeThemeGTK::DrawWidgetBackground(n { GtkWidgetState state; WidgetNodeType gtkWidgetType; - // For resizer drawing, we want IsFrameRTL, which treats vertical-rl modes - // as right-to-left (in addition to horizontal text with direction=RTL), - // rather than just considering the text direction. - // This will make resizers on vertically-oriented elements render properly. - GtkTextDirection direction = - aWidgetType == NS_THEME_RESIZER - ? (IsFrameRTL(aFrame) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR) - : GetTextDirection(aFrame); + GtkTextDirection direction = GetTextDirection(aFrame); gint flags; if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state, &flags)) @@ -1240,6 +1251,21 @@ nsNativeThemeGTK::DrawWidgetBackground(n return NS_OK; } +WidgetNodeType +nsNativeThemeGTK::NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame) +{ + WidgetNodeType gtkWidgetType; + gint unusedFlags; + + if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, + &unusedFlags)) + { + MOZ_ASSERT_UNREACHABLE("Unknown native widget to gtk widget mapping"); + return MOZ_GTK_WINDOW; + } + return gtkWidgetType; +} + NS_IMETHODIMP nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame, uint8_t aWidgetType, nsIntMargin* aResult) @@ -1247,24 +1273,6 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi GtkTextDirection direction = GetTextDirection(aFrame); aResult->top = aResult->left = aResult->right = aResult->bottom = 0; switch (aWidgetType) { - case NS_THEME_SCROLLBAR_VERTICAL: - case NS_THEME_SCROLLBARTRACK_HORIZONTAL: - { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - /* Top and bottom border for whole vertical scrollbar, top and bottom - * border for horizontal track - to correctly position thumb element */ - aResult->top = aResult->bottom = metrics.trough_border; - } - break; - case NS_THEME_SCROLLBAR_HORIZONTAL: - case NS_THEME_SCROLLBARTRACK_VERTICAL: - { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - aResult->left = aResult->right = metrics.trough_border; - } - break; case NS_THEME_TOOLBOX: // gtk has no toolbox equivalent. So, although we map toolbox to // gtk's 'toolbar' for purposes of painting the widget background, @@ -1289,7 +1297,7 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi moz_gtk_get_tab_border(&aResult->left, &aResult->top, &aResult->right, &aResult->bottom, direction, - (GtkTabFlags)flags); + (GtkTabFlags)flags, gtkWidgetType); } break; case NS_THEME_MENUITEM: @@ -1304,8 +1312,9 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi default: { WidgetNodeType gtkWidgetType; + gint unusedFlags; if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, - nullptr)) { + &unusedFlags)) { moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top, &aResult->right, &aResult->bottom, direction, IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML)); @@ -1418,22 +1427,33 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n case NS_THEME_SCROLLBARBUTTON_UP: case NS_THEME_SCROLLBARBUTTON_DOWN: { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); + if (gtk_check_version(3,20,0) == nullptr) { + moz_gtk_get_widget_min_size(MOZ_GTK_SCROLLBAR_BUTTON, + &(aResult->width), &(aResult->height)); + } else { + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + + aResult->width = metrics.slider_width; + aResult->height = metrics.stepper_size; + } - aResult->width = metrics.slider_width; - aResult->height = metrics.stepper_size; *aIsOverridable = false; } break; case NS_THEME_SCROLLBARBUTTON_LEFT: case NS_THEME_SCROLLBARBUTTON_RIGHT: { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); + if (gtk_check_version(3,20,0) == nullptr) { + moz_gtk_get_widget_min_size(MOZ_GTK_SCROLLBAR_BUTTON, + &(aResult->width), &(aResult->height)); + } else { + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); - aResult->width = metrics.stepper_size; - aResult->height = metrics.slider_width; + aResult->width = metrics.stepper_size; + aResult->height = metrics.slider_width; + } *aIsOverridable = false; } break; @@ -1460,39 +1480,65 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n * the thumb isn't a direct child of the scrollbar, unlike the buttons * or track. So add a minimum size to the track as well to prevent a * 0-width scrollbar. */ - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); + if (gtk_check_version(3,20,0) == nullptr) { + // Thumb min dimensions to start with + WidgetNodeType thumbType = aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ? + MOZ_GTK_SCROLLBAR_THUMB_VERTICAL : MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL; + moz_gtk_get_widget_min_size(thumbType, &(aResult->width), &(aResult->height)); + + // Add scrollbar's borders + nsIntMargin border; + nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(), + aFrame, aWidgetType, &border); + aResult->width += border.left + border.right; + aResult->height += border.top + border.bottom; + + // Add track's borders + uint8_t trackType = aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ? + NS_THEME_SCROLLBARTRACK_VERTICAL : NS_THEME_SCROLLBARTRACK_HORIZONTAL; + nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(), + aFrame, trackType, &border); + aResult->width += border.left + border.right; + aResult->height += border.top + border.bottom; + } else { + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); - // Require room for the slider in the track if we don't have buttons. - bool hasScrollbarButtons = moz_gtk_has_scrollbar_buttons(); + // Require room for the slider in the track if we don't have buttons. + bool hasScrollbarButtons = moz_gtk_has_scrollbar_buttons(); - if (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL) { - aResult->width = metrics.slider_width + 2 * metrics.trough_border; - if (!hasScrollbarButtons) - aResult->height = metrics.min_slider_size + 2 * metrics.trough_border; - } else { - aResult->height = metrics.slider_width + 2 * metrics.trough_border; - if (!hasScrollbarButtons) - aResult->width = metrics.min_slider_size + 2 * metrics.trough_border; + if (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL) { + aResult->width = metrics.slider_width + 2 * metrics.trough_border; + if (!hasScrollbarButtons) + aResult->height = metrics.min_slider_size + 2 * metrics.trough_border; + } else { + aResult->height = metrics.slider_width + 2 * metrics.trough_border; + if (!hasScrollbarButtons) + aResult->width = metrics.min_slider_size + 2 * metrics.trough_border; + } + *aIsOverridable = false; } - *aIsOverridable = false; } break; case NS_THEME_SCROLLBARTHUMB_VERTICAL: case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - - if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL) { - aResult->width = metrics.slider_width; - aResult->height = metrics.min_slider_size; + if (gtk_check_version(3,20,0) == nullptr) { + moz_gtk_get_widget_min_size(NativeThemeToGtkTheme(aWidgetType, aFrame), + &(aResult->width), &(aResult->height)); } else { - aResult->height = metrics.slider_width; - aResult->width = metrics.min_slider_size; - } + MozGtkScrollbarMetrics metrics; + moz_gtk_get_scrollbar_metrics(&metrics); + if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL) { + aResult->width = metrics.slider_width; + aResult->height = metrics.min_slider_size; + } else { + aResult->height = metrics.slider_width; + aResult->width = metrics.min_slider_size; + } + } *aIsOverridable = false; } break; diff -up firefox-50.0/widget/gtk/nsNativeThemeGTK.h.widget-rebase firefox-50.0/widget/gtk/nsNativeThemeGTK.h --- firefox-50.0/widget/gtk/nsNativeThemeGTK.h.widget-rebase 2016-07-25 22:22:07.000000000 +0200 +++ firefox-50.0/widget/gtk/nsNativeThemeGTK.h 2016-11-24 11:57:52.035064777 +0100 @@ -74,6 +74,7 @@ protected: virtual ~nsNativeThemeGTK(); private: + GtkTextDirection GetTextDirection(nsIFrame* aFrame); gint GetTabMarginPixels(nsIFrame* aFrame); bool GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame, WidgetNodeType& aGtkWidgetType, @@ -82,6 +83,7 @@ private: nsIntMargin* aExtra); void RefreshWidgetWindow(nsIFrame* aFrame); + WidgetNodeType NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame); uint8_t mDisabledWidgetTypes[32]; uint8_t mSafeWidgetStates[1024]; // 256 widgets * 32 bits per widget diff -up firefox-50.0/widget/gtk/WidgetStyleCache.cpp.widget-rebase firefox-50.0/widget/gtk/WidgetStyleCache.cpp --- firefox-50.0/widget/gtk/WidgetStyleCache.cpp.widget-rebase 2016-10-31 21:15:38.000000000 +0100 +++ firefox-50.0/widget/gtk/WidgetStyleCache.cpp 2016-11-24 11:57:52.035064777 +0100 @@ -25,6 +25,8 @@ static bool sStyleContextNeedsRestore; static GtkStyleContext* sCurrentStyleContext; #endif static GtkStyleContext* +GetWidgetRootStyle(WidgetNodeType aNodeType); +static GtkStyleContext* GetCssNodeStyleInternal(WidgetNodeType aNodeType); static GtkWidget* @@ -93,14 +95,6 @@ CreateMenuPopupWidget() } static GtkWidget* -CreateMenuItemWidget(WidgetNodeType aShellType) -{ - GtkWidget* widget = gtk_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(aShellType)), widget); - return widget; -} - -static GtkWidget* CreateProgressWidget() { GtkWidget* widget = gtk_progress_bar_new(); @@ -211,6 +205,235 @@ CreateEntryWidget() } 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(client_data); + + if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) { + *info->widget = widget; + } + + gtk_widget_realize(widget); +} + +static GtkWidget* +CreateComboBoxButtonWidget() +{ + GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX); + GtkWidget* comboBoxButton = nullptr; + + /* Get its inner Button */ + GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON, + &comboBoxButton }; + gtk_container_forall(GTK_CONTAINER(comboBox), + GetInnerWidget, &info); + + if (!comboBoxButton) { + /* Shouldn't be reached with current internal gtk implementation; we + * use a generic toggle button as last resort fallback to avoid + * crashing. */ + comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON); + } else { + /* We need to have pointers to the inner widgets (button, separator, arrow) + * of the ComboBox to get the correct rendering from theme engines which + * special cases their look. Since the inner layout can change, we ask GTK + * to NULL our pointers when they are about to become invalid because the + * corresponding widgets don't exist anymore. It's the role of + * g_object_add_weak_pointer(). + * Note that if we don't find the inner widgets (which shouldn't happen), we + * fallback to use generic "non-inner" widgets, and they don't need that kind + * of weak pointer since they are explicit children of gProtoLayout and as + * such GTK holds a strong reference to them. */ + g_object_add_weak_pointer(G_OBJECT(comboBoxButton), + reinterpret_cast(sWidgetStorage) + + MOZ_GTK_COMBOBOX_BUTTON); + } + + return comboBoxButton; +} + +static GtkWidget* +CreateComboBoxArrowWidget() +{ + GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON); + GtkWidget* comboBoxArrow = nullptr; + + /* Get the widgets inside the Button */ + GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(comboBoxButton)); + if (GTK_IS_BOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + GtkInnerWidgetInfo info = { GTK_TYPE_ARROW, + &comboBoxArrow }; + gtk_container_forall(GTK_CONTAINER(buttonChild), + GetInnerWidget, &info); + } else if (GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + comboBoxArrow = buttonChild; + gtk_widget_realize(comboBoxArrow); + } + + if (!comboBoxArrow) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW); + } else { + g_object_add_weak_pointer(G_OBJECT(comboBoxArrow), + reinterpret_cast(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(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(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(sWidgetStorage) + + MOZ_GTK_COMBOBOX_ENTRY_BUTTON); + } + + return comboBoxButton; +} + +static GtkWidget* +CreateComboBoxEntryArrowWidget() +{ + GtkWidget* comboBoxArrow = nullptr; + + /* Get the Arrow inside the Button */ + GtkWidget* buttonChild = + gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON))); + + if (GTK_IS_BOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + GtkInnerWidgetInfo info = { GTK_TYPE_ARROW, + &comboBoxArrow }; + gtk_container_forall(GTK_CONTAINER(buttonChild), + GetInnerWidget, &info); + } else if (GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + comboBoxArrow = buttonChild; + gtk_widget_realize(comboBoxArrow); + } + + if (!comboBoxArrow) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW); + } else { + g_object_add_weak_pointer(G_OBJECT(comboBoxArrow), + reinterpret_cast(sWidgetStorage) + + MOZ_GTK_COMBOBOX_ENTRY_ARROW); + } + + return comboBoxArrow; +} + +static GtkWidget* CreateScrolledWindowWidget() { GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr); @@ -237,6 +460,95 @@ CreateMenuSeparatorWidget() 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* CreateWidget(WidgetNodeType aWidgetType) @@ -262,10 +574,6 @@ CreateWidget(WidgetNodeType aWidgetType) return CreateMenuBarWidget(); case MOZ_GTK_MENUPOPUP: return CreateMenuPopupWidget(); - case MOZ_GTK_MENUBARITEM: - return CreateMenuItemWidget(MOZ_GTK_MENUBAR); - case MOZ_GTK_MENUITEM: - return CreateMenuItemWidget(MOZ_GTK_MENUPOPUP); case MOZ_GTK_MENUSEPARATOR: return CreateMenuSeparatorWidget(); case MOZ_GTK_EXPANDER: @@ -294,6 +602,38 @@ CreateWidget(WidgetNodeType aWidgetType) return CreateScrolledWindowWidget(); case MOZ_GTK_TEXT_VIEW: return CreateTextViewWidget(); + 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(); default: /* Not implemented */ return nullptr; @@ -314,27 +654,53 @@ GetWidget(WidgetNodeType aWidgetType) GtkStyleContext* CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle) { - GtkWidgetPath* path = aParentStyle ? - gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle)) : - gtk_widget_path_new(); - - // Work around https://bugzilla.gnome.org/show_bug.cgi?id=767312 - // which exists in GTK+ 3.20. - gtk_widget_get_style_context(aWidget); + static auto sGtkWidgetClassGetCSSName = + reinterpret_cast + (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); + GList* classes = gtk_style_context_list_classes(widgetStyle); + for (GList* link = classes; link; link = link->next) { + gtk_style_context_add_class(context, static_cast(link->data)); + } + g_list_free(classes); - gtk_widget_path_append_for_widget(path, aWidget); // Release any floating reference on aWidget. g_object_ref_sink(aWidget); g_object_unref(aWidget); - GtkStyleContext *context = gtk_style_context_new(); - gtk_style_context_set_path(context, path); - gtk_style_context_set_parent(context, aParentStyle); - gtk_widget_path_unref(path); - return context; } +static GtkStyleContext* +CreateStyleForWidget(GtkWidget* aWidget, WidgetNodeType aParentType) +{ + return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType)); +} + GtkStyleContext* CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle, GType aType) { @@ -342,13 +708,26 @@ CreateCSSNode(const char* aName, GtkStyl reinterpret_cast (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name")); - GtkWidgetPath* path = aParentStyle ? - gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle)) : - gtk_widget_path_new(); + 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(link->data)); + } + g_list_free(classes); + } else { + path = gtk_widget_path_new(); + } gtk_widget_path_append_type(path, aType); - (*sGtkWidgetPathIterSetObjectName)(path, -1, aName); + if (sGtkWidgetPathIterSetObjectName) { + (*sGtkWidgetPathIterSetObjectName)(path, -1, aName); + } GtkStyleContext *context = gtk_style_context_new(); gtk_style_context_set_path(context, path); @@ -358,6 +737,40 @@ CreateCSSNode(const char* aName, GtkStyl 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; + 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) { @@ -367,7 +780,7 @@ CreateChildCSSNode(const char* aName, Wi static GtkStyleContext* GetWidgetStyleWithClass(WidgetNodeType aWidgetType, const gchar* aStyleClass) { - GtkStyleContext* style = gtk_widget_get_style_context(GetWidget(aWidgetType)); + GtkStyleContext* style = GetWidgetRootStyle(aWidgetType); gtk_style_context_save(style); MOZ_ASSERT(!sStyleContextNeedsRestore); sStyleContextNeedsRestore = true; @@ -408,6 +821,10 @@ GetCssNodeStyleInternal(WidgetNodeType a 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); @@ -416,6 +833,14 @@ GetCssNodeStyleInternal(WidgetNodeType a 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, @@ -451,11 +876,77 @@ GetCssNodeStyleInternal(WidgetNodeType a return GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW); case MOZ_GTK_FRAME_BORDER: - return CreateChildCSSNode("border", MOZ_GTK_FRAME); - default: - // TODO - create style from style path - GtkWidget* widget = GetWidget(aNodeType); + style = CreateChildCSSNode("border", MOZ_GTK_FRAME); + break; + case MOZ_GTK_TREEVIEW_VIEW: + // TODO - create from CSS node + return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, + GTK_STYLE_CLASS_VIEW); + case MOZ_GTK_TREEVIEW_EXPANDER: + // TODO - create from CSS node + return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, + GTK_STYLE_CLASS_EXPANDER); + 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 = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, + GTK_STYLE_CLASS_TOP); + gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, + static_cast(0)); + return style; + } + case MOZ_GTK_TAB_BOTTOM: + { + // TODO - create from CSS node + style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, + GTK_STYLE_CLASS_BOTTOM); + gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, + static_cast(0)); + return style; + } + 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); + } + default: + return GetWidgetRootStyle(aNodeType); } MOZ_ASSERT(style, "missing style context for node type"); @@ -486,6 +977,12 @@ GetWidgetStyleInternal(WidgetNodeType aN case MOZ_GTK_CHECKBUTTON: return GetWidgetStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER, GTK_STYLE_CLASS_CHECK); + case MOZ_GTK_RADIOMENUITEM_INDICATOR: + return GetWidgetStyleWithClass(MOZ_GTK_RADIOMENUITEM, + GTK_STYLE_CLASS_RADIO); + case MOZ_GTK_CHECKMENUITEM_INDICATOR: + return GetWidgetStyleWithClass(MOZ_GTK_CHECKMENUITEM, + GTK_STYLE_CLASS_CHECK); case MOZ_GTK_PROGRESS_TROUGH: return GetWidgetStyleWithClass(MOZ_GTK_PROGRESSBAR, GTK_STYLE_CLASS_TROUGH); @@ -519,11 +1016,57 @@ GetWidgetStyleInternal(WidgetNodeType aN return GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW); case MOZ_GTK_FRAME_BORDER: - return GetWidgetStyleInternal(MOZ_GTK_FRAME); - default: - GtkWidget* widget = GetWidget(aNodeType); - MOZ_ASSERT(widget); + return GetWidgetRootStyle(MOZ_GTK_FRAME); + case MOZ_GTK_TREEVIEW_VIEW: + return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, + GTK_STYLE_CLASS_VIEW); + case MOZ_GTK_TREEVIEW_EXPANDER: + return GetWidgetStyleWithClass(MOZ_GTK_TREEVIEW, + GTK_STYLE_CLASS_EXPANDER); + case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL: + return GetWidgetStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL, + GTK_STYLE_CLASS_PANE_SEPARATOR); + case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL: + return GetWidgetStyleWithClass(MOZ_GTK_SPLITTER_VERTICAL, + GTK_STYLE_CLASS_PANE_SEPARATOR); + case MOZ_GTK_SCALE_TROUGH_HORIZONTAL: + return GetWidgetStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL, + GTK_STYLE_CLASS_TROUGH); + case MOZ_GTK_SCALE_TROUGH_VERTICAL: + return GetWidgetStyleWithClass(MOZ_GTK_SCALE_VERTICAL, + GTK_STYLE_CLASS_TROUGH); + case MOZ_GTK_SCALE_THUMB_HORIZONTAL: + return GetWidgetStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL, + GTK_STYLE_CLASS_SLIDER); + case MOZ_GTK_SCALE_THUMB_VERTICAL: + return GetWidgetStyleWithClass(MOZ_GTK_SCALE_VERTICAL, + GTK_STYLE_CLASS_SLIDER); + case MOZ_GTK_TAB_TOP: + { + GtkStyleContext* style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, + GTK_STYLE_CLASS_TOP); + gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, + static_cast(0)); + return style; + } + case MOZ_GTK_TAB_BOTTOM: + { + GtkStyleContext* style = GetWidgetStyleWithClass(MOZ_GTK_NOTEBOOK, + GTK_STYLE_CLASS_BOTTOM); + gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, + static_cast(0)); + return style; + } + 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); } } diff -up firefox-50.0/widget/gtk/WidgetStyleCache.h.widget-rebase firefox-50.0/widget/gtk/WidgetStyleCache.h --- firefox-50.0/widget/gtk/WidgetStyleCache.h.widget-rebase 2016-10-31 21:15:38.000000000 +0100 +++ firefox-50.0/widget/gtk/WidgetStyleCache.h 2016-11-24 11:57:52.035064777 +0100 @@ -28,7 +28,6 @@ GetWidget(WidgetNodeType aNodeType); GtkStyleContext* CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle); -// CreateCSSNode is implemented for gtk >= 3.20 only. GtkStyleContext* CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle,