diff -up firefox-51.0/widget/gtk/gtk3drawing.cpp.old firefox-51.0/widget/gtk/gtk3drawing.cpp --- firefox-51.0/widget/gtk/gtk3drawing.cpp.old 2017-01-18 16:24:56.375732107 +0100 +++ firefox-51.0/widget/gtk/gtk3drawing.cpp 2017-01-10 11:28:15.000000000 +0100 @@ -18,16 +18,6 @@ #include -static GtkWidget* gProtoLayout; -static GtkWidget* gComboBoxWidget; -static GtkWidget* gComboBoxButtonWidget; -static GtkWidget* gComboBoxArrowWidget; -static GtkWidget* gComboBoxSeparatorWidget; -static GtkWidget* gComboBoxEntryWidget; -static GtkWidget* gComboBoxEntryTextareaWidget; -static GtkWidget* gComboBoxEntryButtonWidget; -static GtkWidget* gComboBoxEntryArrowWidget; - static style_prop_t style_prop_func; static gboolean have_arrow_scaling; static gboolean checkbox_check_state; @@ -46,6 +36,10 @@ static gboolean is_initialized; 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) { @@ -79,212 +73,6 @@ moz_gtk_enable_style_props(style_prop_t return MOZ_GTK_SUCCESS; } -static gint -setup_widget_prototype(GtkWidget* widget) -{ - if (!gProtoLayout) { - gProtoLayout = GetWidget(MOZ_GTK_WINDOW_CONTAINER); - } - gtk_container_add(GTK_CONTAINER(gProtoLayout), widget); - return MOZ_GTK_SUCCESS; -} - -/* We need to have pointers to the inner widgets (button, separator, arrow) - * of the ComboBox to get the correct rendering from theme engines which - * special cases their look. Since the inner layout can change, we ask GTK - * to NULL our pointers when they are about to become invalid because the - * corresponding widgets don't exist anymore. It's the role of - * g_object_add_weak_pointer(). - * Note that if we don't find the inner widgets (which shouldn't happen), we - * fallback to use generic "non-inner" widgets, and they don't need that kind - * of weak pointer since they are explicit children of gProtoLayout and as - * such GTK holds a strong reference to them. */ -static void -moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) -{ - if (GTK_IS_TOGGLE_BUTTON(widget)) { - gComboBoxButtonWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxButtonWidget); - gtk_widget_realize(widget); - } -} - -static void -moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, - gpointer client_data) -{ - if (GTK_IS_SEPARATOR(widget)) { - gComboBoxSeparatorWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxSeparatorWidget); - } else if (GTK_IS_ARROW(widget)) { - gComboBoxArrowWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxArrowWidget); - } else - return; - gtk_widget_realize(widget); -} - -static gint -ensure_combo_box_widgets() -{ - GtkWidget* buttonChild; - - if (gComboBoxButtonWidget && gComboBoxArrowWidget) - return MOZ_GTK_SUCCESS; - - /* Create a ComboBox if needed */ - if (!gComboBoxWidget) { - gComboBoxWidget = gtk_combo_box_new(); - setup_widget_prototype(gComboBoxWidget); - } - - /* Get its inner Button */ - gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), - moz_gtk_get_combo_box_inner_button, - NULL); - - if (gComboBoxButtonWidget) { - /* Get the widgets inside the Button */ - buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget)); - if (GTK_IS_BOX(buttonChild)) { - /* appears-as-list = FALSE, cell-view = TRUE; the button - * contains an hbox. This hbox is there because the ComboBox - * needs to place a cell renderer, a separator, and an arrow in - * the button when appears-as-list is FALSE. */ - gtk_container_forall(GTK_CONTAINER(buttonChild), - moz_gtk_get_combo_box_button_inner_widgets, - NULL); - } else if(GTK_IS_ARROW(buttonChild)) { - /* appears-as-list = TRUE, or cell-view = FALSE; - * the button only contains an arrow */ - gComboBoxArrowWidget = buttonChild; - g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *) - &gComboBoxArrowWidget); - gtk_widget_realize(gComboBoxArrowWidget); - } - } else { - /* Shouldn't be reached with current internal gtk implementation; we - * use a generic toggle button as last resort fallback to avoid - * crashing. */ - gComboBoxButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON); - } - - if (!gComboBoxArrowWidget) { - /* Shouldn't be reached with current internal gtk implementation; - * we gButtonArrowWidget as last resort fallback to avoid - * crashing. */ - gComboBoxArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW); - } - - /* We don't test the validity of gComboBoxSeparatorWidget since there - * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it - * is invalid we just won't paint it. */ - - return MOZ_GTK_SUCCESS; -} - -/* We need to have pointers to the inner widgets (entry, button, arrow) of - * the ComboBoxEntry to get the correct rendering from theme engines which - * special cases their look. Since the inner layout can change, we ask GTK - * to NULL our pointers when they are about to become invalid because the - * corresponding widgets don't exist anymore. It's the role of - * g_object_add_weak_pointer(). - * Note that if we don't find the inner widgets (which shouldn't happen), we - * fallback to use generic "non-inner" widgets, and they don't need that kind - * of weak pointer since they are explicit children of gProtoLayout and as - * such GTK holds a strong reference to them. */ -static void -moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, - gpointer client_data) -{ - if (GTK_IS_TOGGLE_BUTTON(widget)) { - gComboBoxEntryButtonWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxEntryButtonWidget); - } else if (GTK_IS_ENTRY(widget)) { - gComboBoxEntryTextareaWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxEntryTextareaWidget); - } else - return; - gtk_widget_realize(widget); -} - -static void -moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) -{ - if (GTK_IS_ARROW(widget)) { - gComboBoxEntryArrowWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer *) &gComboBoxEntryArrowWidget); - gtk_widget_realize(widget); - } -} - -static gint -ensure_combo_box_entry_widgets() -{ - GtkWidget* buttonChild; - - if (gComboBoxEntryTextareaWidget && - gComboBoxEntryButtonWidget && - gComboBoxEntryArrowWidget) - return MOZ_GTK_SUCCESS; - - /* Create a ComboBoxEntry if needed */ - if (!gComboBoxEntryWidget) { - gComboBoxEntryWidget = gtk_combo_box_new_with_entry(); - setup_widget_prototype(gComboBoxEntryWidget); - } - - /* Get its inner Entry and Button */ - gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), - moz_gtk_get_combo_box_entry_inner_widgets, - NULL); - - if (!gComboBoxEntryTextareaWidget) { - gComboBoxEntryTextareaWidget = GetWidget(MOZ_GTK_ENTRY); - } - - if (gComboBoxEntryButtonWidget) { - /* Get the Arrow inside the Button */ - buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget)); - if (GTK_IS_BOX(buttonChild)) { - /* appears-as-list = FALSE, cell-view = TRUE; the button - * contains an hbox. This hbox is there because the ComboBox - * needs to place a cell renderer, a separator, and an arrow in - * the button when appears-as-list is FALSE. */ - gtk_container_forall(GTK_CONTAINER(buttonChild), - moz_gtk_get_combo_box_entry_arrow, - NULL); - } else if(GTK_IS_ARROW(buttonChild)) { - /* appears-as-list = TRUE, or cell-view = FALSE; - * the button only contains an arrow */ - gComboBoxEntryArrowWidget = buttonChild; - g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer *) - &gComboBoxEntryArrowWidget); - gtk_widget_realize(gComboBoxEntryArrowWidget); - } - } else { - /* Shouldn't be reached with current internal gtk implementation; - * we use a generic toggle button as last resort fallback to avoid - * crashing. */ - gComboBoxEntryButtonWidget = GetWidget(MOZ_GTK_TOGGLE_BUTTON); - } - - if (!gComboBoxEntryArrowWidget) { - /* Shouldn't be reached with current internal gtk implementation; - * we gButtonArrowWidget as last resort fallback to avoid - * crashing. */ - gComboBoxEntryArrowWidget = GetWidget(MOZ_GTK_BUTTON_ARROW); - } - - return MOZ_GTK_SUCCESS; -} - gint moz_gtk_init() { @@ -336,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; } @@ -364,7 +160,7 @@ moz_gtk_menuitem_get_horizontal_padding( gint moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding) { - GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_CHECKMENUITEM_CONTAINER); + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_CHECKMENUITEM); gtk_style_context_get_style(style, "horizontal-padding", horizontal_padding, nullptr); @@ -636,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) @@ -675,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, @@ -742,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) @@ -766,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); @@ -795,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); @@ -980,65 +846,31 @@ 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; } static gint -moz_gtk_text_view_paint(cairo_t *cr, GdkRectangle* rect, +moz_gtk_text_view_paint(cairo_t *cr, GdkRectangle* aRect, GtkWidgetState* state, GtkTextDirection direction) { @@ -1046,24 +878,29 @@ moz_gtk_text_view_paint(cairo_t *cr, Gdk GtkStyleContext* style_frame = ClaimStyleContext(MOZ_GTK_SCROLLED_WINDOW, direction, state_flags); - gtk_render_frame(style_frame, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style_frame, cr, + aRect->x, aRect->y, aRect->width, aRect->height); + + GdkRectangle rect = *aRect; + InsetByBorderPadding(&rect, style_frame); - GtkBorder border, padding; - gtk_style_context_get_border(style_frame, state_flags, &border); - gtk_style_context_get_padding(style_frame, state_flags, &padding); ReleaseStyleContext(style_frame); GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TEXT_VIEW, direction, state_flags); - - gint xthickness = border.left + padding.left; - gint ythickness = border.top + padding.top; - - gtk_render_background(style, cr, - rect->x + xthickness, rect->y + ythickness, - rect->width - 2 * xthickness, - rect->height - 2 * ythickness); - + gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); + ReleaseStyleContext(style); + // There is a separate "text" window, which usually provides the + // background behind the text. However, this is transparent in Ambiance + // for GTK 3.20, in which case the MOZ_GTK_TEXT_VIEW background is + // visible. + // Workaround for Bug 1328899 - We don't want "selected" background color + // for active view class here which is set by Ambiance theme. + if (state->focused && !state->disabled) { + state_flags = static_cast(GTK_STATE_FLAG_FOCUSED); + } + style = ClaimStyleContext(MOZ_GTK_TEXT_VIEW_TEXT, direction, state_flags); + gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; @@ -1198,34 +1035,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, @@ -1309,15 +1149,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); @@ -1325,15 +1164,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; } @@ -1353,7 +1191,6 @@ moz_gtk_container_paint(cairo_t *cr, Gdk } ReleaseStyleContext(style); - return MOZ_GTK_SUCCESS; } @@ -1439,13 +1276,52 @@ 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); - ReleaseStyleContext(style); + 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); + ReleaseStyleContext(style); + + // 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 = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX, direction); + + 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); + ReleaseStyleContext(boxStyle); + + GtkStyleContext* labelStyle = + ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL, direction); + moz_gtk_draw_styled_frame(labelStyle, cr, &rect, false); + ReleaseStyleContext(labelStyle); + return MOZ_GTK_SUCCESS; } @@ -1454,17 +1330,9 @@ moz_gtk_resizer_paint(cairo_t *cr, GdkRe GtkWidgetState* state, GtkTextDirection direction) { - GtkStyleContext* style; - - // gtk_render_handle() draws a background, so use GtkTextView and its - // GTK_STYLE_CLASS_VIEW to match the background with textarea elements. - // The resizer is drawn with shaded variants of the background color, and - // so a transparent background would lead to a transparent resizer. - style = ClaimStyleContext(MOZ_GTK_TEXT_VIEW, GTK_TEXT_DIR_LTR, - GetStateFlagsFromGtkWidgetState(state)); - // TODO - we need to save/restore style when gtk 3.20 CSS node path - // is used - gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); + GtkStyleContext* style = + ClaimStyleContext(MOZ_GTK_RESIZER, GTK_TEXT_DIR_LTR, + GetStateFlagsFromGtkWidgetState(state)); // Workaround unico not respecting the text direction for resizers. // See bug 1174248. @@ -1511,17 +1379,8 @@ moz_gtk_progress_chunk_paint(cairo_t *cr GtkTextDirection direction, WidgetNodeType widget) { - GtkStyleContext* style; - - if (gtk_check_version(3, 20, 0) != nullptr) { - /* Ask for MOZ_GTK_PROGRESS_TROUGH instead of MOZ_GTK_PROGRESSBAR - * because ClaimStyleContext() saves/restores that style */ - style = ClaimStyleContext(MOZ_GTK_PROGRESS_TROUGH, direction); - gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_PROGRESSBAR); - } else { - style = ClaimStyleContext(MOZ_GTK_PROGRESS_CHUNK, direction); - } + GtkStyleContext* style = + ClaimStyleContext(MOZ_GTK_PROGRESS_CHUNK, direction); if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE || widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) { @@ -1905,6 +1764,13 @@ 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; @@ -1952,36 +1818,39 @@ moz_gtk_menu_item_paint(WidgetNodeType w GtkWidgetState* state, GtkTextDirection direction) { gint x, y, w, h; + guint minorVersion = gtk_get_minor_version(); + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); - if (state->inHover && !state->disabled) { - 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); - } + // GTK versions prior to 3.8 render the background and frame only when not + // a separator and in hover prelight. + if (minorVersion < 8 && (widget == MOZ_GTK_MENUSEPARATOR || + !(state_flags & GTK_STATE_FLAG_PRELIGHT))) + return MOZ_GTK_SUCCESS; + + GtkStyleContext* style = ClaimStyleContext(widget, direction, state_flags); + + if (minorVersion < 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; - y = rect->y; - w = rect->width; - h = rect->height; + 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 (minorVersion < 6) { + gtk_style_context_restore(style); } + ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2001,16 +1870,16 @@ 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 indicator_size, horizontal_padding; gint x, y; @@ -2020,35 +1889,44 @@ moz_gtk_check_menu_item_paint(cairo_t *c state_flags = static_cast(state_flags|checkbox_check_state); } - style = ClaimStyleContext(isradio ? MOZ_GTK_RADIOMENUITEM_CONTAINER : - MOZ_GTK_CHECKMENUITEM_CONTAINER, - direction); + 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 (pre_3_20) { + GtkBorder padding; + gtk_style_context_get_padding(style, state_flags, &padding); + offset = horizontal_padding + padding.left + 2; + } else { + GdkRectangle r = { 0 }; + InsetByMargin(&r, style); + InsetByBorderPadding(&r, style); + offset = r.x; + } ReleaseStyleContext(style); - style = ClaimStyleContext(isradio ? MOZ_GTK_RADIOMENUITEM : - MOZ_GTK_CHECKMENUITEM, - direction, state_flags); - gtk_style_context_get_padding(style, state_flags, &padding); - gint offset = 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 (gtk_check_version(3, 20, 0) == nullptr) { + 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) { + 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); @@ -2074,6 +1952,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) { @@ -2101,6 +1993,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, @@ -2129,9 +2030,6 @@ moz_gtk_get_widget_border(WidgetNodeType if (widget == MOZ_GTK_TOOLBAR_BUTTON) gtk_style_context_restore(style); - // XXX: Subtract 1 pixel from the border to account for the added - // -moz-focus-inner border (Bug 1228281). - *left -= 1; *top -= 1; *right -= 1; *bottom -= 1; moz_gtk_add_style_border(style, left, top, right, bottom); ReleaseStyleContext(style); @@ -2179,12 +2077,10 @@ moz_gtk_get_widget_border(WidgetNodeType 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: { @@ -2196,32 +2092,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; @@ -2271,12 +2168,15 @@ moz_gtk_get_widget_border(WidgetNodeType { // Bug 1274143 for MOZ_GTK_MENUBARITEM WidgetNodeType type = - widget == MOZ_GTK_MENUBARITEM || widget == MOZ_GTK_MENUITEM ? - MOZ_GTK_MENUITEM : MOZ_GTK_CHECKMENUITEM_CONTAINER; + widget == MOZ_GTK_MENUBARITEM ? MOZ_GTK_MENUITEM : widget; style = ClaimStyleContext(type); - moz_gtk_add_style_padding(style, left, top, right, bottom); - + if (gtk_get_minor_version() < 20) { + moz_gtk_add_style_padding(style, left, top, right, bottom); + } else { + moz_gtk_add_margin_border_padding(style, + left, top, right, bottom); + } ReleaseStyleContext(style); return MOZ_GTK_SUCCESS; } @@ -2285,12 +2185,81 @@ moz_gtk_get_widget_border(WidgetNodeType break; 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); - ReleaseStyleContext(style); + // 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 = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX); + moz_gtk_add_margin_border_padding(boxStyle, + left, top, right, bottom); + ReleaseStyleContext(boxStyle); + + GtkStyleContext* labelStyle = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL); + moz_gtk_add_margin_border_padding(labelStyle, + left, top, right, bottom); + ReleaseStyleContext(labelStyle); + 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: @@ -2299,10 +2268,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: @@ -2390,9 +2355,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; @@ -2421,8 +2386,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); @@ -2588,6 +2552,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, @@ -2598,40 +2565,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; - - settings = gtk_widget_get_settings(GetWidget(MOZ_GTK_IMAGEMENUITEM)); - - 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, @@ -2672,10 +2608,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: @@ -2702,9 +2653,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, @@ -2729,9 +2684,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; @@ -2743,9 +2702,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: @@ -2819,10 +2782,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); @@ -2870,16 +2831,6 @@ moz_gtk_shutdown() /* This will destroy all of our widgets */ ResetWidgetCache(); - gProtoLayout = NULL; - gComboBoxWidget = NULL; - gComboBoxButtonWidget = NULL; - gComboBoxSeparatorWidget = NULL; - gComboBoxArrowWidget = NULL; - gComboBoxEntryWidget = NULL; - gComboBoxEntryButtonWidget = NULL; - gComboBoxEntryArrowWidget = NULL; - gComboBoxEntryTextareaWidget = NULL; - is_initialized = FALSE; return MOZ_GTK_SUCCESS; diff -up firefox-51.0/widget/gtk/gtkdrawing.h.old firefox-51.0/widget/gtk/gtkdrawing.h --- firefox-51.0/widget/gtk/gtkdrawing.h.old 2017-01-16 17:16:53.000000000 +0100 +++ firefox-51.0/widget/gtk/gtkdrawing.h 2017-01-06 10:20:43.000000000 +0100 @@ -145,8 +145,11 @@ typedef enum { MOZ_GTK_ENTRY, /* Paints a GtkExpander. */ MOZ_GTK_EXPANDER, - /* Paints a GtkTextView. */ + /* Paints a GtkTextView or gets the style context corresponding to the + root node of a GtkTextView. */ MOZ_GTK_TEXT_VIEW, + /* The "text" window or node of a GtkTextView */ + MOZ_GTK_TEXT_VIEW_TEXT, /* Paints a GtkOptionMenu. */ MOZ_GTK_DROPDOWN, /* Paints a dropdown arrow (a GtkButton containing a down GtkArrow). */ @@ -160,11 +163,15 @@ typedef enum { MOZ_GTK_TOOLBAR_SEPARATOR, /* Paints a GtkToolTip */ MOZ_GTK_TOOLTIP, + /* Paints a GtkBox from GtkToolTip */ + MOZ_GTK_TOOLTIP_BOX, + /* Paints a GtkLabel of GtkToolTip */ + MOZ_GTK_TOOLTIP_BOX_LABEL, /* Paints a GtkFrame (e.g. a status bar panel). */ MOZ_GTK_FRAME, /* Paints the border of a GtkFrame */ MOZ_GTK_FRAME_BORDER, - /* Paints a resize grip for a GtkWindow */ + /* Paints a resize grip for a GtkTextView */ MOZ_GTK_RESIZER, /* Paints a GtkProgressBar. */ MOZ_GTK_PROGRESSBAR, @@ -210,11 +217,13 @@ typedef enum { MOZ_GTK_MENUBARITEM, /* Paints items of popup menus. */ MOZ_GTK_MENUITEM, - MOZ_GTK_IMAGEMENUITEM, - MOZ_GTK_CHECKMENUITEM_CONTAINER, - MOZ_GTK_RADIOMENUITEM_CONTAINER, + /* 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, /* GtkVPaned base class */ MOZ_GTK_SPLITTER_HORIZONTAL, @@ -230,6 +239,22 @@ typedef enum { 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, @@ -505,23 +530,18 @@ gint moz_gtk_get_tab_thickness(WidgetNodeType aNodeType); /** - * 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); - -/** * Get a boolean which indicates whether the theme draws scrollbar buttons. * If TRUE, draw scrollbar buttons. */ 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-51.0/widget/gtk/mozgtk/mozgtk.c.old firefox-51.0/widget/gtk/mozgtk/mozgtk.c --- firefox-51.0/widget/gtk/mozgtk/mozgtk.c.old 2017-01-18 16:26:11.416376760 +0100 +++ firefox-51.0/widget/gtk/mozgtk/mozgtk.c 2016-12-21 13:10:28.000000000 +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) @@ -577,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) @@ -612,6 +612,9 @@ STUB(gdkx_visual_get) STUB(gtk_object_get_type) #endif +#ifndef GTK3_SYMBOLS +// Only define the following workaround when using GTK3, which we detect +// by checking if GTK3 stubs are not provided. #include // Bug 1271100 // We need to trick system Cairo into not using the XShm extension due to @@ -625,4 +628,5 @@ XShmQueryExtension(Display* aDisplay) { return False; } +#endif diff -up firefox-51.0/widget/gtk/nsLookAndFeel.cpp.old firefox-51.0/widget/gtk/nsLookAndFeel.cpp --- firefox-51.0/widget/gtk/nsLookAndFeel.cpp.old 2017-01-18 16:25:23.072605687 +0100 +++ firefox-51.0/widget/gtk/nsLookAndFeel.cpp 2017-01-06 10:20:43.000000000 +0100 @@ -47,9 +47,6 @@ nsLookAndFeel::nsLookAndFeel() : nsXPLookAndFeel(), #if (MOZ_WIDGET_GTK == 2) mStyle(nullptr), -#else - mBackgroundStyle(nullptr), - mButtonStyle(nullptr), #endif mDefaultFontCached(false), mButtonFontCached(false), mFieldFontCached(false), mMenuFontCached(false) @@ -61,13 +58,27 @@ nsLookAndFeel::~nsLookAndFeel() { #if (MOZ_WIDGET_GTK == 2) g_object_unref(mStyle); -#else - g_object_unref(mBackgroundStyle); - g_object_unref(mButtonStyle); #endif } #if MOZ_WIDGET_GTK != 2 +// Modifies color |*aDest| as if a pattern of color |aSource| was painted with +// CAIRO_OPERATOR_OVER to a surface with color |*aDest|. +static void +ApplyColorOver(const GdkRGBA& aSource, GdkRGBA* aDest) { + gdouble sourceCoef = aSource.alpha; + gdouble destCoef = aDest->alpha * (1.0 - sourceCoef); + gdouble resultAlpha = sourceCoef + destCoef; + if (resultAlpha != 0.0) { // don't divide by zero + destCoef /= resultAlpha; + sourceCoef /= resultAlpha; + aDest->red = sourceCoef * aSource.red + destCoef * aDest->red; + aDest->green = sourceCoef * aSource.green + destCoef * aDest->green; + aDest->blue = sourceCoef * aSource.blue + destCoef * aDest->blue; + aDest->alpha = resultAlpha; + } +} + static void GetLightAndDarkness(const GdkRGBA& aColor, double* aLightness, double* aDarkness) @@ -157,30 +168,55 @@ GetUnicoBorderGradientColors(GtkStyleCon return result; } - -static void +// Sets |aLightColor| and |aDarkColor| to colors from |aContext|. Returns +// true if |aContext| uses these colors to render a visible border. +// If returning false, then the colors returned are a fallback from the +// border-color value even though |aContext| does not use these colors to +// render a border. +static bool GetBorderColors(GtkStyleContext* aContext, GdkRGBA* aLightColor, GdkRGBA* aDarkColor) { - if (GetUnicoBorderGradientColors(aContext, aLightColor, aDarkColor)) - return; - + // Determine whether the border on this style context is visible. GtkStateFlags state = gtk_style_context_get_state(aContext); + GtkBorderStyle borderStyle; + gtk_style_context_get(aContext, state, GTK_STYLE_PROPERTY_BORDER_STYLE, + &borderStyle, nullptr); + bool visible = borderStyle != GTK_BORDER_STYLE_NONE && + borderStyle != GTK_BORDER_STYLE_HIDDEN; + if (visible) { + // GTK has an initial value of zero for border-widths, and so themes + // need to explicitly set border-widths to make borders visible. + GtkBorder border; + gtk_style_context_get_border(aContext, GTK_STATE_FLAG_NORMAL, &border); + visible = border.top != 0 || border.right != 0 || + border.bottom != 0 || border.left != 0; + } + + if (visible && + GetUnicoBorderGradientColors(aContext, aLightColor, aDarkColor)) + return true; + + // The initial value for the border-color is the foreground color, and so + // this will usually return a color distinct from the background even if + // there is no visible border detected. gtk_style_context_get_border_color(aContext, state, aDarkColor); // TODO GTK3 - update aLightColor // for GTK_BORDER_STYLE_INSET/OUTSET/GROVE/RIDGE border styles. // https://bugzilla.mozilla.org/show_bug.cgi?id=978172#c25 *aLightColor = *aDarkColor; + return visible; } -static void +static bool GetBorderColors(GtkStyleContext* aContext, nscolor* aLightColor, nscolor* aDarkColor) { GdkRGBA lightColor, darkColor; - GetBorderColors(aContext, &lightColor, &darkColor); + bool ret = GetBorderColors(aContext, &lightColor, &darkColor); *aLightColor = GDK_RGBA_TO_NS_RGBA(lightColor); *aDarkColor = GDK_RGBA_TO_NS_RGBA(darkColor); + return ret; } #endif @@ -352,30 +388,39 @@ nsLookAndFeel::NativeGetColor(ColorID aI break; #else // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors - case eColorID_activeborder: + case eColorID_activeborder: { // active window border - gtk_style_context_get_border_color(mBackgroundStyle, + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_WINDOW); + gtk_style_context_get_border_color(style, GTK_STATE_FLAG_NORMAL, &gdk_color); aColor = GDK_RGBA_TO_NS_RGBA(gdk_color); + ReleaseStyleContext(style); break; - case eColorID_inactiveborder: + } + case eColorID_inactiveborder: { // inactive window border - gtk_style_context_get_border_color(mBackgroundStyle, - GTK_STATE_FLAG_INSENSITIVE, + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_WINDOW); + gtk_style_context_get_border_color(style, + GTK_STATE_FLAG_INSENSITIVE, &gdk_color); aColor = GDK_RGBA_TO_NS_RGBA(gdk_color); + ReleaseStyleContext(style); break; + } case eColorID_graytext: // disabled text in windows, menus, etc. case eColorID_inactivecaptiontext: // text in inactive window caption aColor = sMenuTextInactive; break; - case eColorID_inactivecaption: + case eColorID_inactivecaption: { // inactive window caption - gtk_style_context_get_background_color(mBackgroundStyle, + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_WINDOW); + gtk_style_context_get_background_color(style, GTK_STATE_FLAG_INSENSITIVE, &gdk_color); aColor = GDK_RGBA_TO_NS_RGBA(gdk_color); + ReleaseStyleContext(style); break; + } #endif case eColorID_infobackground: // tooltip background color @@ -496,18 +541,24 @@ nsLookAndFeel::NativeGetColor(ColorID aI case eColorID__moz_fieldtext: aColor = sMozFieldText; break; - case eColorID__moz_buttondefault: - // default button border color - gtk_style_context_get_border_color(mButtonStyle, + case eColorID__moz_buttondefault: { + // default button border color + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_BUTTON); + gtk_style_context_get_border_color(style, GTK_STATE_FLAG_NORMAL, &gdk_color); aColor = GDK_RGBA_TO_NS_RGBA(gdk_color); + ReleaseStyleContext(style); break; - case eColorID__moz_buttonhoverface: - gtk_style_context_get_background_color(mButtonStyle, + } + case eColorID__moz_buttonhoverface: { + GtkStyleContext *style = ClaimStyleContext(MOZ_GTK_BUTTON); + gtk_style_context_get_background_color(style, GTK_STATE_FLAG_PRELIGHT, &gdk_color); aColor = GDK_RGBA_TO_NS_RGBA(gdk_color); + ReleaseStyleContext(style); break; + } case eColorID__moz_buttonhovertext: aColor = sButtonHoverText; break; @@ -784,12 +835,6 @@ nsLookAndFeel::GetIntImpl(IntID aID, int case eIntID_SpellCheckerUnderlineStyle: aResult = NS_STYLE_TEXT_DECORATION_STYLE_WAVY; break; - case eIntID_ImagesInMenus: - aResult = moz_gtk_images_in_menus(); - break; - case eIntID_ImagesInButtons: - aResult = moz_gtk_images_in_buttons(); - break; case eIntID_MenuBarDrag: aResult = sMenuSupportsDrag; break; @@ -1010,16 +1055,6 @@ nsLookAndFeel::GetFontImpl(FontID aID, n return true; } -#if (MOZ_WIDGET_GTK == 3) -static GtkStyleContext* -create_context(GtkWidgetPath *path) -{ - GtkStyleContext *style = gtk_style_context_new(); - gtk_style_context_set_path(style, path); - return(style); -} -#endif - void nsLookAndFeel::Init() { @@ -1110,78 +1145,54 @@ nsLookAndFeel::Init() g_object_set(settings, dark_setting, FALSE, nullptr); } - GtkWidgetPath *path = gtk_widget_path_new(); - gtk_widget_path_append_type(path, GTK_TYPE_WINDOW); - - mBackgroundStyle = create_context(path); - gtk_style_context_add_class(mBackgroundStyle, GTK_STYLE_CLASS_BACKGROUND); - - mButtonStyle = create_context(path); - gtk_style_context_add_class(mButtonStyle, GTK_STYLE_CLASS_BUTTON); - // Scrollbar colors - style = create_context(path); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); + style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL); gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color); sMozScrollbar = GDK_RGBA_TO_NS_RGBA(color); - g_object_unref(style); + ReleaseStyleContext(style); // Window colors - style = create_context(path); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); + style = ClaimStyleContext(MOZ_GTK_WINDOW); gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color); sMozWindowBackground = GDK_RGBA_TO_NS_RGBA(color); gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color); sMozWindowText = GDK_RGBA_TO_NS_RGBA(color); - gtk_style_context_restore(style); - g_object_unref(style); + ReleaseStyleContext(style); // tooltip foreground and background style = ClaimStyleContext(MOZ_GTK_TOOLTIP); gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color); sInfoBackground = GDK_RGBA_TO_NS_RGBA(color); - { - GtkStyleContext* boxStyle = - CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), - style); - GtkStyleContext* labelStyle = - CreateStyleForWidget(gtk_label_new(nullptr), boxStyle); - gtk_style_context_get_color(labelStyle, GTK_STATE_FLAG_NORMAL, &color); - g_object_unref(labelStyle); - g_object_unref(boxStyle); - } - sInfoText = GDK_RGBA_TO_NS_RGBA(color); ReleaseStyleContext(style); - // menu foreground & menu background - GtkWidget *accel_label = gtk_accel_label_new("M"); - GtkWidget *menuitem = gtk_menu_item_new(); - GtkWidget *menu = gtk_menu_new(); - - g_object_ref_sink(menu); - - gtk_container_add(GTK_CONTAINER(menuitem), accel_label); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - style = gtk_widget_get_style_context(accel_label); + style = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL); gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color); - sMenuText = GDK_RGBA_TO_NS_RGBA(color); - gtk_style_context_get_color(style, GTK_STATE_FLAG_INSENSITIVE, &color); - sMenuTextInactive = GDK_RGBA_TO_NS_RGBA(color); + sInfoText = GDK_RGBA_TO_NS_RGBA(color); + ReleaseStyleContext(style); - style = gtk_widget_get_style_context(menu); + style = ClaimStyleContext(MOZ_GTK_MENUITEM); + { + GtkStyleContext* accelStyle = + CreateStyleForWidget(gtk_accel_label_new("M"), style); + gtk_style_context_get_color(accelStyle, GTK_STATE_FLAG_NORMAL, &color); + sMenuText = GDK_RGBA_TO_NS_RGBA(color); + gtk_style_context_get_color(accelStyle, GTK_STATE_FLAG_INSENSITIVE, &color); + sMenuTextInactive = GDK_RGBA_TO_NS_RGBA(color); + g_object_unref(accelStyle); + } + ReleaseStyleContext(style); + + style = ClaimStyleContext(MOZ_GTK_MENUPOPUP); gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color); sMenuBackground = GDK_RGBA_TO_NS_RGBA(color); + ReleaseStyleContext(style); - style = gtk_widget_get_style_context(menuitem); + style = ClaimStyleContext(MOZ_GTK_MENUITEM); gtk_style_context_get_background_color(style, GTK_STATE_FLAG_PRELIGHT, &color); sMenuHover = GDK_RGBA_TO_NS_RGBA(color); gtk_style_context_get_color(style, GTK_STATE_FLAG_PRELIGHT, &color); sMenuHoverText = GDK_RGBA_TO_NS_RGBA(color); - - g_object_unref(menu); + ReleaseStyleContext(style); #endif // button styles @@ -1192,9 +1203,6 @@ nsLookAndFeel::Init() GtkWidget *combobox = gtk_combo_box_new(); GtkWidget *comboboxLabel = gtk_label_new("M"); gtk_container_add(GTK_CONTAINER(combobox), comboboxLabel); -#else - GtkWidget *combobox = gtk_combo_box_new_with_entry(); - GtkWidget *comboboxLabel = gtk_bin_get_child(GTK_BIN(combobox)); #endif GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP); GtkWidget *treeView = gtk_tree_view_new(); @@ -1208,7 +1216,9 @@ nsLookAndFeel::Init() gtk_container_add(GTK_CONTAINER(parent), button); gtk_container_add(GTK_CONTAINER(parent), treeView); gtk_container_add(GTK_CONTAINER(parent), linkButton); +#if (MOZ_WIDGET_GTK == 2) gtk_container_add(GTK_CONTAINER(parent), combobox); +#endif gtk_container_add(GTK_CONTAINER(parent), menuBar); gtk_menu_shell_append(GTK_MENU_SHELL(menuBar), menuBarItem); gtk_container_add(GTK_CONTAINER(window), parent); @@ -1291,11 +1301,19 @@ nsLookAndFeel::Init() } #else // Text colors - style = gtk_widget_get_style_context(textView); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_VIEW); - gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color); - sMozFieldBackground = GDK_RGBA_TO_NS_RGBA(color); + GdkRGBA bgColor; + // If the text window background is translucent, then the background of + // the textview root node is visible. + style = ClaimStyleContext(MOZ_GTK_TEXT_VIEW); + gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, + &bgColor); + ReleaseStyleContext(style); + + style = ClaimStyleContext(MOZ_GTK_TEXT_VIEW_TEXT); + gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, + &color); + ApplyColorOver(color, &bgColor); + sMozFieldBackground = GDK_RGBA_TO_NS_RGBA(bgColor); gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color); sMozFieldText = GDK_RGBA_TO_NS_RGBA(color); @@ -1308,26 +1326,34 @@ nsLookAndFeel::Init() static_cast(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED), &color); sTextSelectedText = GDK_RGBA_TO_NS_RGBA(color); - gtk_style_context_restore(style); + ReleaseStyleContext(style); - // Button text, background, border - style = gtk_widget_get_style_context(label); - gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color); - sButtonText = GDK_RGBA_TO_NS_RGBA(color); - gtk_style_context_get_color(style, GTK_STATE_FLAG_PRELIGHT, &color); - sButtonHoverText = GDK_RGBA_TO_NS_RGBA(color); + // Button text color + style = ClaimStyleContext(MOZ_GTK_BUTTON); + { + GtkStyleContext* labelStyle = + CreateStyleForWidget(gtk_label_new("M"), style); + gtk_style_context_get_color(labelStyle, GTK_STATE_FLAG_NORMAL, &color); + sButtonText = GDK_RGBA_TO_NS_RGBA(color); + gtk_style_context_get_color(labelStyle, GTK_STATE_FLAG_PRELIGHT, &color); + sButtonHoverText = GDK_RGBA_TO_NS_RGBA(color); + g_object_unref(labelStyle); + } + ReleaseStyleContext(style); // Combobox text color - style = gtk_widget_get_style_context(comboboxLabel); + style = ClaimStyleContext(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA); gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color); sComboBoxText = GDK_RGBA_TO_NS_RGBA(color); + ReleaseStyleContext(style); // Menubar text and hover text colors - style = gtk_widget_get_style_context(menuBarItem); + style = ClaimStyleContext(MOZ_GTK_MENUBARITEM); gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color); sMenuBarText = GDK_RGBA_TO_NS_RGBA(color); gtk_style_context_get_color(style, GTK_STATE_FLAG_PRELIGHT, &color); sMenuBarHoverText = GDK_RGBA_TO_NS_RGBA(color); + ReleaseStyleContext(style); // GTK's guide to fancy odd row background colors: // 1) Check if a theme explicitly defines an odd row color @@ -1335,7 +1361,7 @@ nsLookAndFeel::Init() // slightly by a hardcoded value (gtkstyle.c) // 3) If neither are defined, take the base background color and // darken that by a hardcoded value - style = gtk_widget_get_style_context(treeView); + style = ClaimStyleContext(MOZ_GTK_TREEVIEW); // Get odd row background color gtk_style_context_save(style); @@ -1343,12 +1369,21 @@ nsLookAndFeel::Init() gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color); sOddCellBackground = GDK_RGBA_TO_NS_RGBA(color); gtk_style_context_restore(style); + ReleaseStyleContext(style); - gtk_widget_path_free(path); - + // GtkFrame has a "border" subnode on which Adwaita draws the border. + // Some themes do not draw on this node but draw a border on the widget + // root node, so check the root node if no border is found on the border + // node. style = ClaimStyleContext(MOZ_GTK_FRAME_BORDER); - GetBorderColors(style, &sFrameOuterLightBorder, &sFrameInnerDarkBorder); + bool themeUsesColors = + GetBorderColors(style, &sFrameOuterLightBorder, &sFrameInnerDarkBorder); ReleaseStyleContext(style); + if (!themeUsesColors) { + style = ClaimStyleContext(MOZ_GTK_FRAME); + GetBorderColors(style, &sFrameOuterLightBorder, &sFrameInnerDarkBorder); + ReleaseStyleContext(style); + } // GtkInfoBar // TODO - Use WidgetCache for it? @@ -1419,12 +1454,6 @@ nsLookAndFeel::RefreshImpl() #if (MOZ_WIDGET_GTK == 2) g_object_unref(mStyle); mStyle = nullptr; -#else - g_object_unref(mBackgroundStyle); - g_object_unref(mButtonStyle); - - mBackgroundStyle = nullptr; - mButtonStyle = nullptr; #endif Init(); diff -up firefox-51.0/widget/gtk/nsNativeThemeGTK.cpp.old firefox-51.0/widget/gtk/nsNativeThemeGTK.cpp --- firefox-51.0/widget/gtk/nsNativeThemeGTK.cpp.old 2017-01-16 17:16:53.000000000 +0100 +++ firefox-51.0/widget/gtk/nsNativeThemeGTK.cpp 2016-10-31 11:52:13.000000000 +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). @@ -429,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; @@ -1105,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)) @@ -1248,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) @@ -1255,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, @@ -1312,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)); @@ -1426,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; @@ -1468,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-51.0/widget/gtk/nsNativeThemeGTK.h.old firefox-51.0/widget/gtk/nsNativeThemeGTK.h --- firefox-51.0/widget/gtk/nsNativeThemeGTK.h.old 2016-07-25 22:22:07.000000000 +0200 +++ firefox-51.0/widget/gtk/nsNativeThemeGTK.h 2016-10-27 16:22:27.000000000 +0200 @@ -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-51.0/widget/gtk/WidgetStyleCache.cpp.old firefox-51.0/widget/gtk/WidgetStyleCache.cpp --- firefox-51.0/widget/gtk/WidgetStyleCache.cpp.old 2017-01-16 17:16:53.000000000 +0100 +++ firefox-51.0/widget/gtk/WidgetStyleCache.cpp 2017-01-06 10:20:43.000000000 +0100 @@ -33,7 +33,6 @@ static GtkWidget* CreateWindowWidget() { GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_realize(widget); gtk_widget_set_name(widget, "MozillaGtkWidget"); return widget; } @@ -50,7 +49,6 @@ static void AddToWindowContainer(GtkWidget* widget) { gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW_CONTAINER)), widget); - gtk_widget_realize(widget); } static GtkWidget* @@ -142,7 +140,6 @@ CreateToolbarWidget() { GtkWidget* widget = gtk_toolbar_new(); gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_GRIPPER)), widget); - gtk_widget_realize(widget); return widget; } @@ -183,7 +180,6 @@ CreateButtonArrowWidget() { GtkWidget* widget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_TOGGLE_BUTTON)), widget); - gtk_widget_realize(widget); gtk_widget_show(widget); return widget; } @@ -205,19 +201,235 @@ CreateEntryWidget() } static GtkWidget* -CreateScrolledWindowWidget() +CreateComboBoxWidget() { - GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr); + 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; + } +} + +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; + } + + 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* -CreateTextViewWidget() +CreateComboBoxEntryArrowWidget() { - GtkWidget* widget = gtk_text_view_new(); - gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_SCROLLED_WINDOW)), - widget); + GtkWidget* comboBoxArrow = nullptr; + + /* Get the Arrow inside the Button */ + GtkWidget* buttonChild = + gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON))); + + if (GTK_IS_BOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + GtkInnerWidgetInfo info = { GTK_TYPE_ARROW, + &comboBoxArrow }; + gtk_container_forall(GTK_CONTAINER(buttonChild), + GetInnerWidget, &info); + } else if (GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + comboBoxArrow = buttonChild; + } + + if (!comboBoxArrow) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW); + } else { + g_object_add_weak_pointer(G_OBJECT(comboBoxArrow), + reinterpret_cast(sWidgetStorage) + + MOZ_GTK_COMBOBOX_ENTRY_ARROW); + } + + return comboBoxArrow; +} + +static GtkWidget* +CreateScrolledWindowWidget() +{ + GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr); + AddToWindowContainer(widget); return widget; } @@ -227,7 +439,6 @@ CreateMenuSeparatorWidget() GtkWidget* widget = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)), widget); - gtk_widget_realize(widget); return widget; } @@ -306,15 +517,6 @@ CreateVPanedWidget() } static GtkWidget* -CreateImageMenuItemWidget() -{ - GtkWidget* widget = gtk_image_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)), widget); - gtk_widget_realize(widget); - return widget; -} - -static GtkWidget* CreateScaleWidget(GtkOrientation aOrientation) { GtkWidget* widget = gtk_scale_new(aOrientation, nullptr); @@ -380,8 +582,6 @@ CreateWidget(WidgetNodeType aWidgetType) return CreateEntryWidget(); case MOZ_GTK_SCROLLED_WINDOW: return CreateScrolledWindowWidget(); - case MOZ_GTK_TEXT_VIEW: - return CreateTextViewWidget(); case MOZ_GTK_TREEVIEW: return CreateTreeViewWidget(); case MOZ_GTK_TREE_HEADER_CELL: @@ -392,14 +592,28 @@ CreateWidget(WidgetNodeType aWidgetType) return CreateHPanedWidget(); case MOZ_GTK_SPLITTER_VERTICAL: return CreateVPanedWidget(); - case MOZ_GTK_IMAGEMENUITEM: - return CreateImageMenuItemWidget(); 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; @@ -474,9 +688,20 @@ 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); @@ -508,13 +733,38 @@ GetWidgetRootStyle(WidgetNodeType aNodeT case MOZ_GTK_MENUITEM: style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUPOPUP); break; - case MOZ_GTK_CHECKMENUITEM_CONTAINER: + case MOZ_GTK_CHECKMENUITEM: style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP); break; - case MOZ_GTK_RADIOMENUITEM_CONTAINER: + case MOZ_GTK_RADIOMENUITEM: style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr), MOZ_GTK_MENUPOPUP); break; + case MOZ_GTK_TEXT_VIEW: + style = CreateStyleForWidget(gtk_text_view_new(), + MOZ_GTK_SCROLLED_WINDOW); + break; + case MOZ_GTK_TOOLTIP: + if (gtk_check_version(3, 20, 0) != nullptr) { + // The tooltip style class is added first in CreateTooltipWidget() + // and transfered to style in CreateStyleForWidget(). + GtkWidget* tooltipWindow = CreateTooltipWidget(); + style = CreateStyleForWidget(tooltipWindow, nullptr); + gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference. + } else { + // We create this from the path because GtkTooltipWindow is not public. + style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); + } + break; + case MOZ_GTK_TOOLTIP_BOX: + style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), + MOZ_GTK_TOOLTIP); + break; + case MOZ_GTK_TOOLTIP_BOX_LABEL: + style = CreateStyleForWidget(gtk_label_new(nullptr), + MOZ_GTK_TOOLTIP_BOX); + break; default: GtkWidget* widget = GetWidget(aNodeType); MOZ_ASSERT(widget); @@ -576,6 +826,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); @@ -584,13 +838,13 @@ GetCssNodeStyleInternal(WidgetNodeType a style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK, MOZ_GTK_CHECKBUTTON_CONTAINER); break; - case MOZ_GTK_RADIOMENUITEM: + case MOZ_GTK_RADIOMENUITEM_INDICATOR: style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO, - MOZ_GTK_RADIOMENUITEM_CONTAINER); + MOZ_GTK_RADIOMENUITEM); break; - case MOZ_GTK_CHECKMENUITEM: + case MOZ_GTK_CHECKMENUITEM_INDICATOR: style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK, - MOZ_GTK_CHECKMENUITEM_CONTAINER); + MOZ_GTK_CHECKMENUITEM); break; case MOZ_GTK_PROGRESS_TROUGH: /* Progress bar background (trough) */ @@ -601,11 +855,6 @@ GetCssNodeStyleInternal(WidgetNodeType a style = CreateChildCSSNode("progress", MOZ_GTK_PROGRESS_TROUGH); break; - case MOZ_GTK_TOOLTIP: - // We create this from the path because GtkTooltipWindow is not public. - style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); - break; case MOZ_GTK_GRIPPER: // TODO - create from CSS node return GetWidgetStyleWithClass(MOZ_GTK_GRIPPER, @@ -622,10 +871,28 @@ GetCssNodeStyleInternal(WidgetNodeType a // TODO - create from CSS node return GetWidgetStyleWithClass(MOZ_GTK_SCROLLED_WINDOW, GTK_STYLE_CLASS_FRAME); - case MOZ_GTK_TEXT_VIEW: - // TODO - create from CSS node - return GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, - GTK_STYLE_CLASS_VIEW); + case MOZ_GTK_TEXT_VIEW_TEXT: + case MOZ_GTK_RESIZER: + style = CreateChildCSSNode("text", MOZ_GTK_TEXT_VIEW); + if (aNodeType == MOZ_GTK_RESIZER) { + // The "grip" class provides the correct builtin icon from + // gtk_render_handle(). The icon is drawn with shaded variants of + // the background color, and so a transparent background would lead to + // a transparent resizer. gtk_render_handle() also uses the + // background color to draw a background, and so this style otherwise + // matches what is used in GtkTextView to match the background with + // textarea elements. + GdkRGBA color; + gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, + &color); + if (color.alpha == 0.0) { + g_object_unref(style); + style = CreateStyleForWidget(gtk_text_view_new(), + MOZ_GTK_SCROLLED_WINDOW); + } + gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); + } + break; case MOZ_GTK_FRAME_BORDER: style = CreateChildCSSNode("border", MOZ_GTK_FRAME); break; @@ -728,27 +995,20 @@ GetWidgetStyleInternal(WidgetNodeType aN case MOZ_GTK_CHECKBUTTON: return GetWidgetStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER, GTK_STYLE_CLASS_CHECK); - case MOZ_GTK_RADIOMENUITEM: - return GetWidgetStyleWithClass(MOZ_GTK_RADIOMENUITEM_CONTAINER, + case MOZ_GTK_RADIOMENUITEM_INDICATOR: + return GetWidgetStyleWithClass(MOZ_GTK_RADIOMENUITEM, GTK_STYLE_CLASS_RADIO); - case MOZ_GTK_CHECKMENUITEM: - return GetWidgetStyleWithClass(MOZ_GTK_CHECKMENUITEM_CONTAINER, + 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); - case MOZ_GTK_TOOLTIP: { - GtkStyleContext* style = sStyleStorage[aNodeType]; - if (style) - return style; - - // The tooltip style class is added first in CreateTooltipWidget() so - // that gtk_widget_path_append_for_widget() in CreateStyleForWidget() - // will find it. - GtkWidget* tooltipWindow = CreateTooltipWidget(); - style = CreateStyleForWidget(tooltipWindow, nullptr); - gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference. - sStyleStorage[aNodeType] = style; + case MOZ_GTK_PROGRESS_CHUNK: { + GtkStyleContext* style = + GetWidgetStyleWithClass(MOZ_GTK_PROGRESSBAR, + GTK_STYLE_CLASS_PROGRESSBAR); + gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH); return style; } case MOZ_GTK_GRIPPER: @@ -763,9 +1023,25 @@ GetWidgetStyleInternal(WidgetNodeType aN case MOZ_GTK_SCROLLED_WINDOW: return GetWidgetStyleWithClass(MOZ_GTK_SCROLLED_WINDOW, GTK_STYLE_CLASS_FRAME); - case MOZ_GTK_TEXT_VIEW: - return GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, - GTK_STYLE_CLASS_VIEW); + case MOZ_GTK_TEXT_VIEW_TEXT: + case MOZ_GTK_RESIZER: { + // GTK versions prior to 3.20 do not have the view class on the root + // node, but add this to determine the background for the text window. + GtkStyleContext* style = + GetWidgetStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW); + if (aNodeType == MOZ_GTK_RESIZER) { + // The "grip" class provides the correct builtin icon from + // gtk_render_handle(). The icon is drawn with shaded variants of + // the background color, and so a transparent background would lead to + // a transparent resizer. gtk_render_handle() also uses the + // background color to draw a background, and so this style otherwise + // matches MOZ_GTK_TEXT_VIEW_TEXT to match the background with + // textarea elements. GtkTextView creates a separate text window and + // so the background should not be transparent. + gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); + } + return style; + } case MOZ_GTK_FRAME_BORDER: return GetWidgetRootStyle(MOZ_GTK_FRAME); case MOZ_GTK_TREEVIEW_VIEW: