--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -961,16 +961,42 @@ moz_gtk_draw_styled_frame(GtkStyleContex
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);
}
}
+class MOZ_RAII AutoAddGtkStyleProvider
+{
+public:
+ AutoAddGtkStyleProvider(GtkStyleContext* aContext,
+ GtkStyleProvider* aProvider)
+ : mContext(aContext)
+ , mProvider(aProvider)
+ {
+ const auto PRIORITY = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION;
+ if (mProvider) {
+ gtk_style_context_add_provider(mContext, mProvider, PRIORITY);
+ }
+ }
+
+ ~AutoAddGtkStyleProvider()
+ {
+ if (mProvider) {
+ gtk_style_context_remove_provider(mContext, mProvider);
+ }
+ }
+
+private:
+ GtkStyleContext* mContext;
+ GtkStyleProvider* mProvider;
+};
+
static gint
moz_gtk_scrollbar_trough_paint(WidgetNodeType widget,
cairo_t *cr, const GdkRectangle* aRect,
GtkWidgetState* state,
GtkTextDirection direction)
{
GdkRectangle rect = *aRect;
GtkStyleContext* style;
@@ -1022,32 +1048,34 @@ moz_gtk_scrollbar_paint(WidgetNodeType w
return MOZ_GTK_SUCCESS;
}
static gint
moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget,
cairo_t *cr, const GdkRectangle* aRect,
GtkWidgetState* state,
- GtkTextDirection direction)
+ GtkTextDirection direction,
+ GtkStyleProvider* extra_style)
{
GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
GtkStyleContext* style = GetStyleContext(widget, direction, state_flags);
GtkOrientation orientation = (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
GdkRectangle rect = *aRect;
const ScrollbarGTKMetrics* metrics =
(state->depressed || state->active || state->inHover) ?
GetActiveScrollbarMetrics(orientation) :
GetScrollbarMetrics(orientation);
Inset(&rect, metrics->margin.thumb);
+ AutoAddGtkStyleProvider addStyle(style, extra_style);
gtk_render_slider(style, cr, rect.x, rect.y, rect.width, rect.height,
orientation);
return MOZ_GTK_SUCCESS;
}
static gint
moz_gtk_inner_spin_paint(cairo_t *cr, GdkRectangle* rect,
@@ -3178,17 +3206,18 @@ GetCSDDecorationSize(GtkWindow *aGtkWind
return true;
}
/* cairo_t *cr argument has to be a system-cairo. */
gint
moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
GdkRectangle* rect,
GtkWidgetState* state, gint flags,
- GtkTextDirection direction)
+ GtkTextDirection direction,
+ GtkStyleProvider* extra_style)
{
/* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
*/
cairo_new_path(cr);
switch (widget) {
case MOZ_GTK_BUTTON:
case MOZ_GTK_TOOLBAR_BUTTON:
@@ -3225,16 +3254,17 @@ moz_gtk_widget_paint(WidgetNodeType widg
(GtkScrollbarButtonFlags) flags,
direction);
break;
case MOZ_GTK_SCROLLBAR_HORIZONTAL:
case MOZ_GTK_SCROLLBAR_VERTICAL:
if (flags & MOZ_GTK_TRACK_OPAQUE) {
GtkStyleContext* style =
GetStyleContext(MOZ_GTK_WINDOW, direction);
+ AutoAddGtkStyleProvider addStyle(style, extra_style);
gtk_render_background(style, cr,
rect->x, rect->y, rect->width, rect->height);
}
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;
@@ -3246,18 +3276,18 @@ moz_gtk_widget_paint(WidgetNodeType widg
case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
if (gtk_check_version(3,20,0) == nullptr) {
return moz_gtk_scrollbar_trough_paint(widget, cr, rect,
state, direction);
}
break;
case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
- return moz_gtk_scrollbar_thumb_paint(widget, cr, rect,
- state, direction);
+ return moz_gtk_scrollbar_thumb_paint(widget, cr, rect, state,
+ direction, extra_style);
break;
case MOZ_GTK_SCALE_HORIZONTAL:
case MOZ_GTK_SCALE_VERTICAL:
return moz_gtk_scale_paint(cr, rect, state,
(GtkOrientation) flags, direction);
break;
case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
case MOZ_GTK_SCALE_THUMB_VERTICAL:
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -373,22 +373,24 @@ gint moz_gtk_shutdown();
* Paint a widget in the current theme.
* widget: a constant giving the widget to paint
* drawable: the drawable to paint to;
* it's colormap must be moz_gtk_widget_get_colormap().
* rect: the bounding rectangle for the widget
* state: the state of the widget. ignored for some widgets.
* flags: widget-dependant flags; see the WidgetNodeType definition.
* direction: the text direction, to draw the widget correctly LTR and RTL.
+ * extra_style: extra style the widget should use.
*/
gint
moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
GdkRectangle* rect,
GtkWidgetState* state, gint flags,
- GtkTextDirection direction);
+ GtkTextDirection direction,
+ GtkStyleProvider* extra_style);
/*** Widget metrics ***/
/**
* Get the border size of a widget
* left/right: [OUT] the widget's left/right border
* top/bottom: [OUT] the widget's top/bottom border
* direction: the text direction for the widget. Callers depend on this
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -536,16 +536,18 @@ STUB(gdk_screen_get_monitor_workarea)
STUB(gdk_window_get_type)
STUB(gdk_x11_window_get_xid)
STUB(gdk_x11_display_get_type)
STUB(gdk_wayland_display_get_type)
STUB(gtk_box_new)
STUB(gtk_cairo_should_draw_window)
STUB(gtk_cairo_transform_to_window)
STUB(gtk_combo_box_text_append)
+STUB(gtk_css_provider_load_from_data)
+STUB(gtk_css_provider_new)
STUB(gtk_drag_set_icon_surface)
STUB(gtk_get_major_version)
STUB(gtk_get_micro_version)
STUB(gtk_get_minor_version)
STUB(gtk_icon_info_load_symbolic_for_context)
STUB(gtk_menu_button_new)
STUB(gtk_offscreen_window_new)
STUB(gtk_paned_new)
@@ -562,16 +564,17 @@ STUB(gtk_render_frame_gap)
STUB(gtk_render_handle)
STUB(gtk_render_icon)
STUB(gtk_render_line)
STUB(gtk_render_option)
STUB(gtk_render_slider)
STUB(gtk_scale_new)
STUB(gtk_scrollbar_new)
STUB(gtk_style_context_add_class)
+STUB(gtk_style_context_add_provider)
STUB(gtk_style_context_add_region)
STUB(gtk_style_context_get)
STUB(gtk_style_context_get_background_color)
STUB(gtk_style_context_get_border)
STUB(gtk_style_context_get_border_color)
STUB(gtk_style_context_get_color)
STUB(gtk_style_context_get_direction)
STUB(gtk_style_context_get_margin)
@@ -580,24 +583,26 @@ STUB(gtk_style_context_get_path)
STUB(gtk_style_context_get_property)
STUB(gtk_style_context_get_state)
STUB(gtk_style_context_get_style)
STUB(gtk_style_context_has_class)
STUB(gtk_style_context_invalidate)
STUB(gtk_style_context_list_classes)
STUB(gtk_style_context_new)
STUB(gtk_style_context_remove_class)
+STUB(gtk_style_context_remove_provider)
STUB(gtk_style_context_remove_region)
STUB(gtk_style_context_restore)
STUB(gtk_style_context_save)
STUB(gtk_style_context_set_direction)
STUB(gtk_style_context_set_path)
STUB(gtk_style_context_set_parent)
STUB(gtk_style_context_set_state)
STUB(gtk_style_properties_lookup_property)
+STUB(gtk_style_provider_get_type)
STUB(gtk_tree_view_column_get_button)
STUB(gtk_widget_get_preferred_size)
STUB(gtk_widget_get_preferred_width)
STUB(gtk_widget_get_preferred_height)
STUB(gtk_widget_get_state_flags)
STUB(gtk_widget_get_style_context)
STUB(gtk_widget_path_append_type)
STUB(gtk_widget_path_copy)
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -855,17 +855,18 @@ private:
Point mCurrentPoint;
};
static void
DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
GtkWidgetState aState, WidgetNodeType aGTKWidgetType,
gint aFlags, GtkTextDirection aDirection, gint aScaleFactor,
bool aSnapped, const Point& aDrawOrigin, const nsIntSize& aDrawSize,
- GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency)
+ GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency,
+ GtkStyleProvider* aExtraStyle)
{
Point drawOffset;
Matrix transform;
if (!aSnapped) {
// If we are not snapped, we depend on the DT for translation.
drawOffset = aDrawOrigin;
transform = aDrawTarget->GetTransform().PreTranslate(aDrawOrigin);
} else {
@@ -888,17 +889,18 @@ DrawThemeWithCairo(gfxContext* aContext,
BorrowedCairoContext borrowCairo(aDrawTarget);
if (borrowCairo.mCairo) {
cairo_set_matrix(borrowCairo.mCairo, &mat);
cairo_new_path(borrowCairo.mCairo);
cairo_rectangle(borrowCairo.mCairo, 0, 0, clipSize.width, clipSize.height);
cairo_clip(borrowCairo.mCairo);
- moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect, &aState, aFlags, aDirection);
+ moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect,
+ &aState, aFlags, aDirection, aExtraStyle);
borrowCairo.Finish();
return;
}
#endif
// A direct Cairo draw target is not available, so we need to create a temporary one.
#if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
@@ -932,17 +934,18 @@ DrawThemeWithCairo(gfxContext* aContext,
aContext->ExportClip(*clipper);
cairo_set_matrix(cr, &mat);
cairo_new_path(cr);
cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height);
cairo_clip(cr);
- moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
+ moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect,
+ &aState, aFlags, aDirection, aExtraStyle);
cairo_destroy(cr);
}
cairo_surface_destroy(surf);
}
borrow.Finish();
return;
}
@@ -970,17 +973,18 @@ DrawThemeWithCairo(gfxContext* aContext,
aContext->ExportClip(*clipper);
cairo_set_matrix(cr, &mat);
cairo_new_path(cr);
cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height);
cairo_clip(cr);
- moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
+ moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect,
+ &aState, aFlags, aDirection, aExtraStyle);
cairo_destroy(cr);
}
cairo_surface_destroy(surf);
}
aDrawTarget->ReleaseBits(data);
} else {
// If the widget has any transparency, make sure to choose an alpha format.
@@ -997,17 +1001,18 @@ DrawThemeWithCairo(gfxContext* aContext,
cairo_t* cr = nullptr;
if (!NS_WARN_IF(!surf)) {
cr = cairo_create(surf);
if (!NS_WARN_IF(!cr)) {
if (aScaleFactor != 1) {
cairo_scale(cr, aScaleFactor, aScaleFactor);
}
- moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
+ moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect,
+ &aState, aFlags, aDirection, aExtraStyle);
}
}
// Unmap the surface before using it as a source
dataSurface->Unmap();
if (cr) {
// The widget either needs to be masked or has transparency, so use the slower drawing path.
@@ -1090,16 +1095,91 @@ nsNativeThemeGTK::GetExtraSizeForWidget(
gint scale = GetMonitorScaleFactor(aFrame);
aExtra->top *= scale;
aExtra->right *= scale;
aExtra->bottom *= scale;
aExtra->left *= scale;
return true;
}
+struct GtkStyleProviderHolder
+{
+ // A pointer to ComputedStyle as an invalidation key. It's made opaque
+ // so that we don't accidentally access it, since it may be dangling.
+ // XXX It is possible that if there are multiple style flushes happen
+ // between two paint flush, and the ComputedStyle pointer is reused
+ // while the underlying style has changed. But that should be rare
+ // enough that we just ignore it for now. To completely avoid that,
+ // for scrollbar colors, we would need to put two used colors as well
+ // as whether they are auto here, which would add some boilerplate.
+ void* mStyle;
+ GtkStyleProvider* mProvider;
+
+ ~GtkStyleProviderHolder() {
+ g_object_unref(mProvider);
+ }
+};
+
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(ScrollbarStyleProviderProperty,
+ GtkStyleProviderHolder)
+
+GtkStyleProvider*
+nsNativeThemeGTK::GetExtraStyleProvider(nsIFrame* aFrame, uint8_t aWidgetType)
+{
+ if (!IsWidgetScrollbarPart(aWidgetType)) {
+ return nullptr;
+ }
+ ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
+ const nsStyleUserInterface* ui = style->StyleUserInterface();
+ if (!ui->HasCustomScrollbars()) {
+ return nullptr;
+ }
+ GtkStyleProviderHolder* holder =
+ aFrame->GetProperty(ScrollbarStyleProviderProperty());
+ if (!holder || holder->mStyle != style) {
+ if (holder) {
+ aFrame->RemoveProperty(ScrollbarStyleProviderProperty());
+ }
+ nsAutoCString data;
+ if (!ui->mScrollbarTrackColor.IsAuto()) {
+ nscolor color = ui->mScrollbarTrackColor.CalcColor(style);
+ // By default, CSS node scrollbar, contents, and trough in GTK+
+ // are transparent, and we don't want to change that. However,
+ // when MOZ_GTK_TRACK_OPAQUE is set, we additionally paint the
+ // track area with background of MOZ_GTK_WINDOW, so `window` is
+ // used below. See moz_gtk_widget_paint.
+ // This is fine as this style is only used by scrollbar currently.
+ // It may need to be changed if we reuse this mechanism in the
+ // future for something else.
+ data.AppendPrintf(
+ "window { background: rgba(%u,%u,%u,%.2f); }",
+ NS_GET_R(color), NS_GET_G(color), NS_GET_B(color),
+ (1.f / 255) * NS_GET_A(color));
+ }
+ if (!ui->mScrollbarFaceColor.IsAuto()) {
+ nscolor color = ui->mScrollbarFaceColor.CalcColor(style);
+ data.AppendPrintf(
+ "scrollbar slider { background: rgba(%u,%u,%u,%.2f); }",
+ NS_GET_R(color), NS_GET_G(color), NS_GET_B(color),
+ (1.f / 255) * NS_GET_A(color));
+ }
+ GError* error = nullptr;
+ GtkCssProvider* provider = gtk_css_provider_new();
+ gtk_css_provider_load_from_data(provider, data.get(),
+ data.Length(), &error);
+ if (error) {
+ NS_WARNING("Failed to parse constructed CSS?");
+ g_error_free(error);
+ }
+ holder = new GtkStyleProviderHolder { style, GTK_STYLE_PROVIDER(provider) };
+ aFrame->SetProperty(ScrollbarStyleProviderProperty(), holder);
+ }
+ return holder->mProvider;
+}
+
NS_IMETHODIMP
nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext,
nsIFrame* aFrame,
uint8_t aWidgetType,
const nsRect& aRect,
const nsRect& aDirtyRect)
{
GtkWidgetState state;
@@ -1107,16 +1187,17 @@ nsNativeThemeGTK::DrawWidgetBackground(g
GtkTextDirection direction = GetTextDirection(aFrame);
gint flags;
if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
&flags))
return NS_OK;
gfxContext* ctx = aContext;
nsPresContext *presContext = aFrame->PresContext();
+ GtkStyleProvider* extraStyle = GetExtraStyleProvider(aFrame, aWidgetType);
gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
gint scaleFactor = GetMonitorScaleFactor(aFrame);
// Align to device pixels where sensible
// to provide crisper and faster drawing.
// Don't snap if it's a non-unit scale factor. We're going to have to take
@@ -1173,17 +1254,17 @@ nsNativeThemeGTK::DrawWidgetBackground(g
state.scale = scaleFactor;
// translate everything so (0,0) is the top left of the drawingRect
gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
state, gtkWidgetType, flags, direction, scaleFactor,
snapped, ToPoint(origin), drawingRect.Size(),
- gdk_rect, transparency);
+ gdk_rect, transparency, extraStyle);
if (!safeState) {
// gdk_flush() call from expose event crashes Gtk+ on Wayland
// (Gnome BZ #773307)
if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
gdk_flush();
}
gLastGdkError = gdk_error_trap_pop ();
--- a/widget/gtk/nsNativeThemeGTK.h
+++ b/widget/gtk/nsNativeThemeGTK.h
@@ -88,16 +88,19 @@ private:
WidgetNodeType& aGtkWidgetType,
GtkWidgetState* aState, gint* aWidgetFlags);
bool GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
nsIntMargin* aExtra);
void RefreshWidgetWindow(nsIFrame* aFrame);
WidgetNodeType NativeThemeToGtkTheme(uint8_t aWidgetType, nsIFrame* aFrame);
+ GtkStyleProvider* GetExtraStyleProvider(nsIFrame* aFrame,
+ uint8_t aWidgetType);
+
uint8_t mDisabledWidgetTypes[(ThemeWidgetType_COUNT + 7) / 8];
uint8_t mSafeWidgetStates[ThemeWidgetType_COUNT * 4]; // 32 bits per widget
static const char* sDisabledEngines[];
// Because moz_gtk_get_widget_border can be slow, we cache its results
// by widget type. Each bit in mBorderCacheValid says whether the
// corresponding entry in mBorderCache is valid.
void GetCachedWidgetBorder(nsIFrame* aFrame, uint8_t aWidgetType,