1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsNativeThemeGTK.h"
7 #include "nsPresContext.h"
8 #include "nsStyleConsts.h"
9 #include "gtkdrawing.h"
10 #include "ScreenHelperGTK.h"
11 #include "WidgetUtilsGtk.h"
13 #include "gfx2DGlue.h"
14 #include "nsIObserverService.h"
16 #include "nsIContent.h"
17 #include "nsViewManager.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsGfxCIID.h"
20 #include "nsTransform2D.h"
21 #include "nsXULPopupManager.h"
22 #include "tree/nsTreeBodyFrame.h"
24 #include "nsGkAtoms.h"
25 #include "nsAttrValueInlines.h"
27 #include "mozilla/dom/HTMLInputElement.h"
28 #include "mozilla/ClearOnShutdown.h"
29 #include "mozilla/Services.h"
31 #include <gdk/gdkprivate.h>
34 #include "gfxContext.h"
35 #include "mozilla/dom/XULButtonElement.h"
36 #include "mozilla/gfx/BorrowedContext.h"
37 #include "mozilla/gfx/HelpersCairo.h"
38 #include "mozilla/gfx/PathHelpers.h"
39 #include "mozilla/Preferences.h"
40 #include "mozilla/PresShell.h"
41 #include "mozilla/layers/StackingContextHelper.h"
42 #include "mozilla/StaticPrefs_layout.h"
43 #include "mozilla/StaticPrefs_widget.h"
45 #include "nsLayoutUtils.h"
49 # ifdef CAIRO_HAS_XLIB_SURFACE
50 # include "cairo-xlib.h"
57 using namespace mozilla
;
58 using namespace mozilla::gfx
;
59 using namespace mozilla::widget
;
61 static int gLastGdkError
;
63 // Return widget scale factor of the monitor where the window is located by the
64 // most part. We intentionally honor the text scale factor here in order to
65 // have consistent scaling with other UI elements.
66 static inline CSSToLayoutDeviceScale
GetWidgetScaleFactor(nsIFrame
* aFrame
) {
67 return aFrame
->PresContext()->CSSToDevPixelScale();
70 nsNativeThemeGTK::nsNativeThemeGTK() : Theme(ScrollbarStyle()) {
71 if (moz_gtk_init() != MOZ_GTK_SUCCESS
) {
72 memset(mDisabledWidgetTypes
, 0xff, sizeof(mDisabledWidgetTypes
));
79 nsNativeThemeGTK::~nsNativeThemeGTK() { moz_gtk_shutdown(); }
81 void nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame
* aFrame
) {
83 MOZ_ASSERT(aFrame
->PresShell());
85 nsViewManager
* vm
= aFrame
->PresShell()->GetViewManager();
89 vm
->InvalidateAllViews();
92 static bool IsFrameContentNodeInNamespace(nsIFrame
* aFrame
,
93 uint32_t aNamespace
) {
94 nsIContent
* content
= aFrame
? aFrame
->GetContent() : nullptr;
95 if (!content
) return false;
96 return content
->IsInNamespace(aNamespace
);
99 static bool IsWidgetTypeDisabled(const uint8_t* aDisabledVector
,
100 StyleAppearance aAppearance
) {
101 auto type
= static_cast<size_t>(aAppearance
);
102 MOZ_ASSERT(type
< static_cast<size_t>(StyleAppearance::Count
));
103 return (aDisabledVector
[type
>> 3] & (1 << (type
& 7))) != 0;
106 static void SetWidgetTypeDisabled(uint8_t* aDisabledVector
,
107 StyleAppearance aAppearance
) {
108 auto type
= static_cast<size_t>(aAppearance
);
109 MOZ_ASSERT(type
< static_cast<size_t>(mozilla::StyleAppearance::Count
));
110 aDisabledVector
[type
>> 3] |= (1 << (type
& 7));
113 static inline uint16_t GetWidgetStateKey(StyleAppearance aAppearance
,
114 GtkWidgetState
* aWidgetState
) {
115 return (aWidgetState
->active
| aWidgetState
->focused
<< 1 |
116 aWidgetState
->inHover
<< 2 | aWidgetState
->disabled
<< 3 |
117 aWidgetState
->isDefault
<< 4 |
118 static_cast<uint16_t>(aAppearance
) << 5);
121 static bool IsWidgetStateSafe(uint8_t* aSafeVector
, StyleAppearance aAppearance
,
122 GtkWidgetState
* aWidgetState
) {
123 MOZ_ASSERT(static_cast<size_t>(aAppearance
) <
124 static_cast<size_t>(mozilla::StyleAppearance::Count
));
125 uint16_t key
= GetWidgetStateKey(aAppearance
, aWidgetState
);
126 return (aSafeVector
[key
>> 3] & (1 << (key
& 7))) != 0;
129 static void SetWidgetStateSafe(uint8_t* aSafeVector
,
130 StyleAppearance aAppearance
,
131 GtkWidgetState
* aWidgetState
) {
132 MOZ_ASSERT(static_cast<size_t>(aAppearance
) <
133 static_cast<size_t>(mozilla::StyleAppearance::Count
));
134 uint16_t key
= GetWidgetStateKey(aAppearance
, aWidgetState
);
135 aSafeVector
[key
>> 3] |= (1 << (key
& 7));
139 GtkTextDirection
nsNativeThemeGTK::GetTextDirection(nsIFrame
* aFrame
) {
140 // IsFrameRTL() treats vertical-rl modes as right-to-left (in addition to
141 // horizontal text with direction=RTL), rather than just considering the
142 // text direction. GtkTextDirection does not have distinct values for
143 // vertical writing modes, but considering the block flow direction is
144 // important for resizers and scrollbar elements, at least.
145 return IsFrameRTL(aFrame
) ? GTK_TEXT_DIR_RTL
: GTK_TEXT_DIR_LTR
;
148 // Returns positive for negative margins (otherwise 0).
149 gint
nsNativeThemeGTK::GetTabMarginPixels(nsIFrame
* aFrame
) {
150 nscoord margin
= IsBottomTab(aFrame
) ? aFrame
->GetUsedMargin().top
151 : aFrame
->GetUsedMargin().bottom
;
153 return std::min
<gint
>(
154 MOZ_GTK_TAB_MARGIN_MASK
,
155 std::max(0, aFrame
->PresContext()->AppUnitsToDevPixels(-margin
)));
158 bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance
,
160 WidgetNodeType
& aGtkWidgetType
,
161 GtkWidgetState
* aState
,
162 gint
* aWidgetFlags
) {
167 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
169 memset(aState
, 0, sizeof(GtkWidgetState
));
171 // For XUL checkboxes and radio buttons, the state of the parent
172 // determines our state.
174 if (elementState
.HasState(ElementState::CHECKED
)) {
175 *aWidgetFlags
|= MOZ_GTK_WIDGET_CHECKED
;
177 if (elementState
.HasState(ElementState::INDETERMINATE
)) {
178 *aWidgetFlags
|= MOZ_GTK_WIDGET_INCONSISTENT
;
183 elementState
.HasState(ElementState::DISABLED
) || IsReadOnly(aFrame
);
184 aState
->active
= elementState
.HasState(ElementState::ACTIVE
);
185 aState
->focused
= elementState
.HasState(ElementState::FOCUS
);
186 aState
->inHover
= elementState
.HasState(ElementState::HOVER
);
187 aState
->isDefault
= IsDefaultButton(aFrame
);
188 aState
->canDefault
= FALSE
; // XXX fix me
190 if (aAppearance
== StyleAppearance::Button
||
191 aAppearance
== StyleAppearance::Toolbarbutton
||
192 aAppearance
== StyleAppearance::Dualbutton
||
193 aAppearance
== StyleAppearance::ToolbarbuttonDropdown
||
194 aAppearance
== StyleAppearance::Menulist
||
195 aAppearance
== StyleAppearance::MenulistButton
) {
196 aState
->active
&= aState
->inHover
;
197 } else if (aAppearance
== StyleAppearance::Treetwisty
||
198 aAppearance
== StyleAppearance::Treetwistyopen
) {
199 if (nsTreeBodyFrame
* treeBodyFrame
= do_QueryFrame(aFrame
)) {
200 const mozilla::AtomArray
& atoms
=
201 treeBodyFrame
->GetPropertyArrayForCurrentDrawingItem();
202 aState
->selected
= atoms
.Contains(nsGkAtoms::selected
);
203 aState
->inHover
= atoms
.Contains(nsGkAtoms::hover
);
207 if (IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XUL
)) {
208 // For these widget types, some element (either a child or parent)
209 // actually has element focus, so we check the focused attribute
210 // to see whether to draw in the focused state.
211 aState
->focused
= elementState
.HasState(ElementState::FOCUSRING
);
212 if (aAppearance
== StyleAppearance::Radio
||
213 aAppearance
== StyleAppearance::Checkbox
) {
214 // In XUL, checkboxes and radios shouldn't have focus rings, their
216 aState
->focused
= FALSE
;
219 // A button with drop down menu open or an activated toggle button
220 // should always appear depressed.
221 if (aAppearance
== StyleAppearance::Button
||
222 aAppearance
== StyleAppearance::Toolbarbutton
||
223 aAppearance
== StyleAppearance::Dualbutton
||
224 aAppearance
== StyleAppearance::ToolbarbuttonDropdown
||
225 aAppearance
== StyleAppearance::Menulist
||
226 aAppearance
== StyleAppearance::MenulistButton
) {
227 bool menuOpen
= IsOpenButton(aFrame
);
228 aState
->depressed
= IsCheckedButton(aFrame
) || menuOpen
;
229 // we must not highlight buttons with open drop down menus on hover.
230 aState
->inHover
= aState
->inHover
&& !menuOpen
;
234 if (aAppearance
== StyleAppearance::MozWindowTitlebar
||
235 aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
||
236 aAppearance
== StyleAppearance::MozWindowButtonClose
||
237 aAppearance
== StyleAppearance::MozWindowButtonMinimize
||
238 aAppearance
== StyleAppearance::MozWindowButtonMaximize
||
239 aAppearance
== StyleAppearance::MozWindowButtonRestore
) {
240 aState
->backdrop
= !nsWindow::GetTopLevelWindowActiveState(aFrame
);
244 switch (aAppearance
) {
245 case StyleAppearance::Button
:
246 if (aWidgetFlags
) *aWidgetFlags
= GTK_RELIEF_NORMAL
;
247 aGtkWidgetType
= MOZ_GTK_BUTTON
;
249 case StyleAppearance::Toolbarbutton
:
250 case StyleAppearance::Dualbutton
:
251 if (aWidgetFlags
) *aWidgetFlags
= GTK_RELIEF_NONE
;
252 aGtkWidgetType
= MOZ_GTK_TOOLBAR_BUTTON
;
254 case StyleAppearance::Checkbox
:
255 aGtkWidgetType
= MOZ_GTK_CHECKBUTTON
;
257 case StyleAppearance::Radio
:
258 aGtkWidgetType
= MOZ_GTK_RADIOBUTTON
;
260 case StyleAppearance::Spinner
:
261 aGtkWidgetType
= MOZ_GTK_SPINBUTTON
;
263 case StyleAppearance::SpinnerUpbutton
:
264 aGtkWidgetType
= MOZ_GTK_SPINBUTTON_UP
;
266 case StyleAppearance::SpinnerDownbutton
:
267 aGtkWidgetType
= MOZ_GTK_SPINBUTTON_DOWN
;
269 case StyleAppearance::SpinnerTextfield
:
270 aGtkWidgetType
= MOZ_GTK_SPINBUTTON_ENTRY
;
272 case StyleAppearance::Range
: {
273 if (IsRangeHorizontal(aFrame
)) {
274 if (aWidgetFlags
) *aWidgetFlags
= GTK_ORIENTATION_HORIZONTAL
;
275 aGtkWidgetType
= MOZ_GTK_SCALE_HORIZONTAL
;
277 if (aWidgetFlags
) *aWidgetFlags
= GTK_ORIENTATION_VERTICAL
;
278 aGtkWidgetType
= MOZ_GTK_SCALE_VERTICAL
;
282 case StyleAppearance::RangeThumb
: {
283 if (IsRangeHorizontal(aFrame
)) {
284 if (aWidgetFlags
) *aWidgetFlags
= GTK_ORIENTATION_HORIZONTAL
;
285 aGtkWidgetType
= MOZ_GTK_SCALE_THUMB_HORIZONTAL
;
287 if (aWidgetFlags
) *aWidgetFlags
= GTK_ORIENTATION_VERTICAL
;
288 aGtkWidgetType
= MOZ_GTK_SCALE_THUMB_VERTICAL
;
292 case StyleAppearance::NumberInput
:
293 case StyleAppearance::Textfield
:
294 aGtkWidgetType
= MOZ_GTK_ENTRY
;
296 case StyleAppearance::Textarea
:
297 aGtkWidgetType
= MOZ_GTK_TEXT_VIEW
;
299 case StyleAppearance::Listbox
:
300 case StyleAppearance::Treeview
:
301 aGtkWidgetType
= MOZ_GTK_TREEVIEW
;
303 case StyleAppearance::Treetwisty
:
304 aGtkWidgetType
= MOZ_GTK_TREEVIEW_EXPANDER
;
305 if (aWidgetFlags
) *aWidgetFlags
= GTK_EXPANDER_COLLAPSED
;
307 case StyleAppearance::Treetwistyopen
:
308 aGtkWidgetType
= MOZ_GTK_TREEVIEW_EXPANDER
;
309 if (aWidgetFlags
) *aWidgetFlags
= GTK_EXPANDER_EXPANDED
;
311 case StyleAppearance::MenulistButton
:
312 case StyleAppearance::Menulist
:
313 aGtkWidgetType
= MOZ_GTK_DROPDOWN
;
316 IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XHTML
);
318 case StyleAppearance::ToolbarbuttonDropdown
:
319 case StyleAppearance::ButtonArrowDown
:
320 case StyleAppearance::ButtonArrowUp
:
321 case StyleAppearance::ButtonArrowNext
:
322 case StyleAppearance::ButtonArrowPrevious
:
323 aGtkWidgetType
= MOZ_GTK_TOOLBARBUTTON_ARROW
;
325 *aWidgetFlags
= GTK_ARROW_DOWN
;
327 if (aAppearance
== StyleAppearance::ButtonArrowUp
)
328 *aWidgetFlags
= GTK_ARROW_UP
;
329 else if (aAppearance
== StyleAppearance::ButtonArrowNext
)
330 *aWidgetFlags
= GTK_ARROW_RIGHT
;
331 else if (aAppearance
== StyleAppearance::ButtonArrowPrevious
)
332 *aWidgetFlags
= GTK_ARROW_LEFT
;
335 case StyleAppearance::Tooltip
:
336 aGtkWidgetType
= MOZ_GTK_TOOLTIP
;
338 case StyleAppearance::ProgressBar
:
339 aGtkWidgetType
= MOZ_GTK_PROGRESSBAR
;
341 case StyleAppearance::Progresschunk
: {
342 nsIFrame
* stateFrame
= aFrame
->GetParent();
343 ElementState elementState
= GetContentState(stateFrame
, aAppearance
);
345 aGtkWidgetType
= elementState
.HasState(ElementState::INDETERMINATE
)
346 ? IsVerticalProgress(stateFrame
)
347 ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
348 : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
349 : MOZ_GTK_PROGRESS_CHUNK
;
351 case StyleAppearance::TabScrollArrowBack
:
352 case StyleAppearance::TabScrollArrowForward
:
354 *aWidgetFlags
= aAppearance
== StyleAppearance::TabScrollArrowBack
357 aGtkWidgetType
= MOZ_GTK_TAB_SCROLLARROW
;
359 case StyleAppearance::Tabpanels
:
360 aGtkWidgetType
= MOZ_GTK_TABPANELS
;
362 case StyleAppearance::Tab
: {
363 if (IsBottomTab(aFrame
)) {
364 aGtkWidgetType
= MOZ_GTK_TAB_BOTTOM
;
366 aGtkWidgetType
= MOZ_GTK_TAB_TOP
;
370 /* First bits will be used to store max(0,-bmargin) where bmargin
371 * is the bottom margin of the tab in pixels (resp. top margin,
372 * for bottom tabs). */
373 *aWidgetFlags
= GetTabMarginPixels(aFrame
);
375 if (IsSelectedTab(aFrame
)) *aWidgetFlags
|= MOZ_GTK_TAB_SELECTED
;
377 if (IsFirstTab(aFrame
)) *aWidgetFlags
|= MOZ_GTK_TAB_FIRST
;
380 case StyleAppearance::Splitter
:
381 if (IsHorizontal(aFrame
))
382 aGtkWidgetType
= MOZ_GTK_SPLITTER_VERTICAL
;
384 aGtkWidgetType
= MOZ_GTK_SPLITTER_HORIZONTAL
;
386 case StyleAppearance::MozWindowTitlebar
:
387 aGtkWidgetType
= MOZ_GTK_HEADER_BAR
;
389 case StyleAppearance::MozWindowDecorations
:
390 aGtkWidgetType
= MOZ_GTK_WINDOW_DECORATION
;
392 case StyleAppearance::MozWindowTitlebarMaximized
:
393 aGtkWidgetType
= MOZ_GTK_HEADER_BAR_MAXIMIZED
;
395 case StyleAppearance::MozWindowButtonBox
:
396 aGtkWidgetType
= MOZ_GTK_HEADER_BAR_BUTTON_BOX
;
398 case StyleAppearance::MozWindowButtonClose
:
399 aGtkWidgetType
= MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
;
401 case StyleAppearance::MozWindowButtonMinimize
:
402 aGtkWidgetType
= MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE
;
404 case StyleAppearance::MozWindowButtonMaximize
:
405 aGtkWidgetType
= MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
;
407 case StyleAppearance::MozWindowButtonRestore
:
408 aGtkWidgetType
= MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE
;
417 class SystemCairoClipper
: public ClipExporter
{
419 explicit SystemCairoClipper(cairo_t
* aContext
, gint aScaleFactor
= 1)
420 : mContext(aContext
), mScaleFactor(aScaleFactor
) {}
422 void BeginClip(const Matrix
& aTransform
) override
{
424 GfxMatrixToCairoMatrix(aTransform
, mat
);
425 // We also need to remove the scale factor effect from the matrix
426 mat
.y0
= mat
.y0
/ mScaleFactor
;
427 mat
.x0
= mat
.x0
/ mScaleFactor
;
428 cairo_set_matrix(mContext
, &mat
);
430 cairo_new_path(mContext
);
433 void MoveTo(const Point
& aPoint
) override
{
434 cairo_move_to(mContext
, aPoint
.x
/ mScaleFactor
, aPoint
.y
/ mScaleFactor
);
435 mBeginPoint
= aPoint
;
436 mCurrentPoint
= aPoint
;
439 void LineTo(const Point
& aPoint
) override
{
440 cairo_line_to(mContext
, aPoint
.x
/ mScaleFactor
, aPoint
.y
/ mScaleFactor
);
441 mCurrentPoint
= aPoint
;
444 void BezierTo(const Point
& aCP1
, const Point
& aCP2
,
445 const Point
& aCP3
) override
{
446 cairo_curve_to(mContext
, aCP1
.x
/ mScaleFactor
, aCP1
.y
/ mScaleFactor
,
447 aCP2
.x
/ mScaleFactor
, aCP2
.y
/ mScaleFactor
,
448 aCP3
.x
/ mScaleFactor
, aCP3
.y
/ mScaleFactor
);
449 mCurrentPoint
= aCP3
;
452 void QuadraticBezierTo(const Point
& aCP1
, const Point
& aCP2
) override
{
453 Point CP0
= CurrentPoint();
454 Point CP1
= (CP0
+ aCP1
* 2.0) / 3.0;
455 Point CP2
= (aCP2
+ aCP1
* 2.0) / 3.0;
457 cairo_curve_to(mContext
, CP1
.x
/ mScaleFactor
, CP1
.y
/ mScaleFactor
,
458 CP2
.x
/ mScaleFactor
, CP2
.y
/ mScaleFactor
,
459 CP3
.x
/ mScaleFactor
, CP3
.y
/ mScaleFactor
);
460 mCurrentPoint
= aCP2
;
463 void Arc(const Point
& aOrigin
, float aRadius
, float aStartAngle
,
464 float aEndAngle
, bool aAntiClockwise
) override
{
465 ArcToBezier(this, aOrigin
, Size(aRadius
, aRadius
), aStartAngle
, aEndAngle
,
469 void Close() override
{
470 cairo_close_path(mContext
);
471 mCurrentPoint
= mBeginPoint
;
474 void EndClip() override
{ cairo_clip(mContext
); }
481 static void DrawThemeWithCairo(gfxContext
* aContext
, DrawTarget
* aDrawTarget
,
482 GtkWidgetState aState
,
483 WidgetNodeType aGTKWidgetType
, gint aFlags
,
484 GtkTextDirection aDirection
, double aScaleFactor
,
485 bool aSnapped
, const Point
& aDrawOrigin
,
486 const nsIntSize
& aDrawSize
,
487 GdkRectangle
& aGDKRect
,
488 nsITheme::Transparency aTransparency
) {
489 static auto sCairoSurfaceSetDeviceScalePtr
=
490 (void (*)(cairo_surface_t
*, double, double))dlsym(
491 RTLD_DEFAULT
, "cairo_surface_set_device_scale");
492 const bool useHiDPIWidgets
=
493 aScaleFactor
!= 1.0 && sCairoSurfaceSetDeviceScalePtr
;
495 Point drawOffsetScaled
;
496 Point drawOffsetOriginal
;
499 // If we are not snapped, we depend on the DT for translation.
500 drawOffsetOriginal
= aDrawOrigin
;
501 drawOffsetScaled
= useHiDPIWidgets
? drawOffsetOriginal
/ aScaleFactor
502 : drawOffsetOriginal
;
503 transform
= aDrawTarget
->GetTransform().PreTranslate(drawOffsetScaled
);
505 // Otherwise, we only need to take the device offset into account.
506 drawOffsetOriginal
= aDrawOrigin
- aContext
->GetDeviceOffset();
507 drawOffsetScaled
= useHiDPIWidgets
? drawOffsetOriginal
/ aScaleFactor
508 : drawOffsetOriginal
;
509 transform
= Matrix::Translation(drawOffsetScaled
);
512 if (!useHiDPIWidgets
&& aScaleFactor
!= 1) {
513 transform
.PreScale(aScaleFactor
, aScaleFactor
);
517 GfxMatrixToCairoMatrix(transform
, mat
);
519 Size
clipSize((aDrawSize
.width
+ aScaleFactor
- 1) / aScaleFactor
,
520 (aDrawSize
.height
+ aScaleFactor
- 1) / aScaleFactor
);
522 // A direct Cairo draw target is not available, so we need to create a
524 #if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
525 if (GdkIsX11Display()) {
526 // If using a Cairo xlib surface, then try to reuse it.
527 BorrowedXlibDrawable
borrow(aDrawTarget
);
528 if (Drawable drawable
= borrow
.GetDrawable()) {
529 nsIntSize size
= borrow
.GetSize();
530 cairo_surface_t
* surf
= cairo_xlib_surface_create(
531 borrow
.GetDisplay(), drawable
, borrow
.GetVisual(), size
.width
,
533 if (!NS_WARN_IF(!surf
)) {
534 Point offset
= borrow
.GetOffset();
535 if (offset
!= Point()) {
536 cairo_surface_set_device_offset(surf
, offset
.x
, offset
.y
);
538 cairo_t
* cr
= cairo_create(surf
);
539 if (!NS_WARN_IF(!cr
)) {
540 RefPtr
<SystemCairoClipper
> clipper
= new SystemCairoClipper(cr
);
541 aContext
->ExportClip(*clipper
);
543 cairo_set_matrix(cr
, &mat
);
546 cairo_rectangle(cr
, 0, 0, clipSize
.width
, clipSize
.height
);
549 moz_gtk_widget_paint(aGTKWidgetType
, cr
, &aGDKRect
, &aState
, aFlags
,
554 cairo_surface_destroy(surf
);
562 // Check if the widget requires complex masking that must be composited.
563 // Try to directly write to the draw target's pixels if possible.
567 SurfaceFormat format
;
569 if (aDrawTarget
->LockBits(&data
, &size
, &stride
, &format
, &origin
)) {
570 // Create a Cairo image surface context the device rectangle.
571 cairo_surface_t
* surf
= cairo_image_surface_create_for_data(
572 data
, GfxFormatToCairoFormat(format
), size
.width
, size
.height
, stride
);
573 if (!NS_WARN_IF(!surf
)) {
574 if (useHiDPIWidgets
) {
575 sCairoSurfaceSetDeviceScalePtr(surf
, aScaleFactor
, aScaleFactor
);
577 if (origin
!= IntPoint()) {
578 cairo_surface_set_device_offset(surf
, -origin
.x
, -origin
.y
);
580 cairo_t
* cr
= cairo_create(surf
);
581 if (!NS_WARN_IF(!cr
)) {
582 RefPtr
<SystemCairoClipper
> clipper
=
583 new SystemCairoClipper(cr
, useHiDPIWidgets
? aScaleFactor
: 1);
584 aContext
->ExportClip(*clipper
);
586 cairo_set_matrix(cr
, &mat
);
589 cairo_rectangle(cr
, 0, 0, clipSize
.width
, clipSize
.height
);
592 moz_gtk_widget_paint(aGTKWidgetType
, cr
, &aGDKRect
, &aState
, aFlags
,
597 cairo_surface_destroy(surf
);
599 aDrawTarget
->ReleaseBits(data
);
601 // If the widget has any transparency, make sure to choose an alpha format.
602 format
= aTransparency
!= nsITheme::eOpaque
? SurfaceFormat::B8G8R8A8
603 : aDrawTarget
->GetFormat();
604 // Create a temporary data surface to render the widget into.
605 RefPtr
<DataSourceSurface
> dataSurface
= Factory::CreateDataSourceSurface(
606 aDrawSize
, format
, aTransparency
!= nsITheme::eOpaque
);
607 DataSourceSurface::MappedSurface map
;
610 dataSurface
->Map(DataSourceSurface::MapType::WRITE
, &map
)))) {
611 // Create a Cairo image surface wrapping the data surface.
612 cairo_surface_t
* surf
= cairo_image_surface_create_for_data(
613 map
.mData
, GfxFormatToCairoFormat(format
), aDrawSize
.width
,
614 aDrawSize
.height
, map
.mStride
);
615 cairo_t
* cr
= nullptr;
616 if (!NS_WARN_IF(!surf
)) {
617 cr
= cairo_create(surf
);
618 if (!NS_WARN_IF(!cr
)) {
619 if (aScaleFactor
!= 1) {
620 if (useHiDPIWidgets
) {
621 sCairoSurfaceSetDeviceScalePtr(surf
, aScaleFactor
, aScaleFactor
);
623 cairo_scale(cr
, aScaleFactor
, aScaleFactor
);
627 moz_gtk_widget_paint(aGTKWidgetType
, cr
, &aGDKRect
, &aState
, aFlags
,
632 // Unmap the surface before using it as a source
633 dataSurface
->Unmap();
636 // The widget either needs to be masked or has transparency, so use the
637 // slower drawing path.
638 aDrawTarget
->DrawSurface(
640 Rect(aSnapped
? drawOffsetOriginal
-
641 aDrawTarget
->GetTransform().GetTranslation()
642 : drawOffsetOriginal
,
644 Rect(0, 0, aDrawSize
.width
, aDrawSize
.height
));
649 cairo_surface_destroy(surf
);
655 CSSIntMargin
nsNativeThemeGTK::GetExtraSizeForWidget(
656 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
658 // Allow an extra one pixel above and below the thumb for certain
659 // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
660 // We modify the frame's overflow area. See bug 297508.
661 switch (aAppearance
) {
662 case StyleAppearance::Button
: {
663 if (IsDefaultButton(aFrame
)) {
664 // Some themes draw a default indicator outside the widget,
665 // include that in overflow
666 moz_gtk_button_get_default_overflow(&extra
.top
.value
, &extra
.left
.value
,
679 bool nsNativeThemeGTK::IsWidgetVisible(StyleAppearance aAppearance
) {
680 switch (aAppearance
) {
681 case StyleAppearance::MozWindowButtonBox
:
690 nsNativeThemeGTK::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
691 StyleAppearance aAppearance
,
693 const nsRect
& aDirtyRect
,
694 DrawOverflow aDrawOverflow
) {
695 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
696 return Theme::DrawWidgetBackground(aContext
, aFrame
, aAppearance
, aRect
,
697 aDirtyRect
, aDrawOverflow
);
700 GtkWidgetState state
;
701 WidgetNodeType gtkWidgetType
;
702 GtkTextDirection direction
= GetTextDirection(aFrame
);
705 if (!IsWidgetVisible(aAppearance
) ||
706 !GetGtkWidgetAndState(aAppearance
, aFrame
, gtkWidgetType
, &state
,
711 gfxContext
* ctx
= aContext
;
712 nsPresContext
* presContext
= aFrame
->PresContext();
714 gfxRect rect
= presContext
->AppUnitsToGfxUnits(aRect
);
715 gfxRect dirtyRect
= presContext
->AppUnitsToGfxUnits(aDirtyRect
);
717 // Align to device pixels where sensible
718 // to provide crisper and faster drawing.
719 // Don't snap if it's a non-unit scale factor. We're going to have to take
720 // slow paths then in any case.
721 // We prioritize the size when snapping in order to avoid distorting widgets
722 // that should be square, which can occur if edges are snapped independently.
723 bool snapped
= ctx
->UserToDevicePixelSnapped(
724 rect
, gfxContext::SnapOption::PrioritizeSize
);
726 // Leave rect in device coords but make dirtyRect consistent.
727 dirtyRect
= ctx
->UserToDevice(dirtyRect
);
730 // Translate the dirty rect so that it is wrt the widget top-left.
731 dirtyRect
.MoveBy(-rect
.TopLeft());
732 // Round out the dirty rect to gdk pixels to ensure that gtk draws
733 // enough pixels for interpolation to device pixels.
734 dirtyRect
.RoundOut();
736 // GTK themes can only draw an integer number of pixels
737 // (even when not snapped).
738 LayoutDeviceIntRect
widgetRect(0, 0, NS_lround(rect
.Width()),
739 NS_lround(rect
.Height()));
741 // This is the rectangle that will actually be drawn, in gdk pixels
742 LayoutDeviceIntRect
drawingRect(
743 int32_t(dirtyRect
.X()), int32_t(dirtyRect
.Y()),
744 int32_t(dirtyRect
.Width()), int32_t(dirtyRect
.Height()));
745 if (widgetRect
.IsEmpty() ||
746 !drawingRect
.IntersectRect(widgetRect
, drawingRect
)) {
750 NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes
, aAppearance
),
751 "Trying to render an unsafe widget!");
753 bool safeState
= IsWidgetStateSafe(mSafeWidgetStates
, aAppearance
, &state
);
756 gdk_error_trap_push();
759 Transparency transparency
= GetWidgetTransparency(aFrame
, aAppearance
);
761 // gdk rectangles are wrt the drawing rect.
762 auto scaleFactor
= GetWidgetScaleFactor(aFrame
);
763 LayoutDeviceIntRect
gdkDevRect(-drawingRect
.TopLeft(), widgetRect
.Size());
765 auto gdkCssRect
= CSSIntRect::RoundIn(gdkDevRect
/ scaleFactor
);
766 GdkRectangle gdk_rect
= {gdkCssRect
.x
, gdkCssRect
.y
, gdkCssRect
.width
,
769 // Save actual widget scale to GtkWidgetState as we don't provide
770 // the frame to gtk3drawing routines.
771 state
.image_scale
= std::ceil(scaleFactor
.scale
);
773 // translate everything so (0,0) is the top left of the drawingRect
774 gfxPoint origin
= rect
.TopLeft() + drawingRect
.TopLeft().ToUnknownPoint();
776 DrawThemeWithCairo(ctx
, aContext
->GetDrawTarget(), state
, gtkWidgetType
,
777 flags
, direction
, scaleFactor
.scale
, snapped
,
778 ToPoint(origin
), drawingRect
.Size().ToUnknownSize(),
779 gdk_rect
, transparency
);
782 // gdk_flush() call from expose event crashes Gtk+ on Wayland
783 // (Gnome BZ #773307)
784 if (GdkIsX11Display()) {
787 gLastGdkError
= gdk_error_trap_pop();
792 "GTK theme failed for widget type %d, error was %d, state was "
793 "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
794 static_cast<int>(aAppearance
), gLastGdkError
, state
.active
,
795 state
.focused
, state
.inHover
, state
.disabled
);
797 NS_WARNING("GTK theme failed; disabling unsafe widget");
798 SetWidgetTypeDisabled(mDisabledWidgetTypes
, aAppearance
);
799 // force refresh of the window, because the widget was not
800 // successfully drawn it must be redrawn using the default look
801 RefreshWidgetWindow(aFrame
);
803 SetWidgetStateSafe(mSafeWidgetStates
, aAppearance
, &state
);
807 // Indeterminate progress bar are animated.
808 if (gtkWidgetType
== MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
||
809 gtkWidgetType
== MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
) {
810 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 30)) {
811 NS_WARNING("unable to animate widget!");
818 bool nsNativeThemeGTK::CreateWebRenderCommandsForWidget(
819 mozilla::wr::DisplayListBuilder
& aBuilder
,
820 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
821 const mozilla::layers::StackingContextHelper
& aSc
,
822 mozilla::layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
823 StyleAppearance aAppearance
, const nsRect
& aRect
) {
824 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
825 return Theme::CreateWebRenderCommandsForWidget(
826 aBuilder
, aResources
, aSc
, aManager
, aFrame
, aAppearance
, aRect
);
831 WidgetNodeType
nsNativeThemeGTK::NativeThemeToGtkTheme(
832 StyleAppearance aAppearance
, nsIFrame
* aFrame
) {
833 WidgetNodeType gtkWidgetType
;
836 if (!GetGtkWidgetAndState(aAppearance
, aFrame
, gtkWidgetType
, nullptr,
838 MOZ_ASSERT_UNREACHABLE("Unknown native widget to gtk widget mapping");
839 return MOZ_GTK_WINDOW
;
841 return gtkWidgetType
;
844 static void FixupForVerticalWritingMode(WritingMode aWritingMode
,
845 CSSIntMargin
* aResult
) {
846 if (aWritingMode
.IsVertical()) {
847 bool rtl
= aWritingMode
.IsBidiRTL();
848 LogicalMargin
logical(aWritingMode
, aResult
->top
,
849 rtl
? aResult
->left
: aResult
->right
, aResult
->bottom
,
850 rtl
? aResult
->right
: aResult
->left
);
851 nsMargin physical
= logical
.GetPhysicalMargin(aWritingMode
);
852 aResult
->top
= physical
.top
;
853 aResult
->right
= physical
.right
;
854 aResult
->bottom
= physical
.bottom
;
855 aResult
->left
= physical
.left
;
859 CSSIntMargin
nsNativeThemeGTK::GetCachedWidgetBorder(
860 nsIFrame
* aFrame
, StyleAppearance aAppearance
,
861 GtkTextDirection aDirection
) {
864 WidgetNodeType gtkWidgetType
;
866 if (GetGtkWidgetAndState(aAppearance
, aFrame
, gtkWidgetType
, nullptr,
868 MOZ_ASSERT(0 <= gtkWidgetType
&& gtkWidgetType
< MOZ_GTK_WIDGET_NODE_COUNT
);
869 uint8_t cacheIndex
= gtkWidgetType
/ 8;
870 uint8_t cacheBit
= 1u << (gtkWidgetType
% 8);
872 if (mBorderCacheValid
[cacheIndex
] & cacheBit
) {
873 result
= mBorderCache
[gtkWidgetType
];
875 moz_gtk_get_widget_border(gtkWidgetType
, &result
.left
.value
,
876 &result
.top
.value
, &result
.right
.value
,
877 &result
.bottom
.value
, aDirection
);
878 if (gtkWidgetType
!= MOZ_GTK_DROPDOWN
) { // depends on aDirection
879 mBorderCacheValid
[cacheIndex
] |= cacheBit
;
880 mBorderCache
[gtkWidgetType
] = result
;
884 FixupForVerticalWritingMode(aFrame
->GetWritingMode(), &result
);
888 LayoutDeviceIntMargin
nsNativeThemeGTK::GetWidgetBorder(
889 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
890 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
891 return Theme::GetWidgetBorder(aContext
, aFrame
, aAppearance
);
895 GtkTextDirection direction
= GetTextDirection(aFrame
);
896 switch (aAppearance
) {
897 case StyleAppearance::Toolbox
:
898 // gtk has no toolbox equivalent. So, although we map toolbox to
899 // gtk's 'toolbar' for purposes of painting the widget background,
900 // we don't use the toolbar border for toolbox.
902 case StyleAppearance::Dualbutton
:
903 // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw
904 // around the entire button + dropdown, and also an inner border if you're
905 // over the button part. But, we want the inner button to be right up
906 // against the edge of the outer button so that the borders overlap.
907 // To make this happen, we draw a button border for the outer button,
908 // but don't reserve any space for it.
910 case StyleAppearance::Tab
: {
911 WidgetNodeType gtkWidgetType
;
914 if (!GetGtkWidgetAndState(aAppearance
, aFrame
, gtkWidgetType
, nullptr,
918 moz_gtk_get_tab_border(&result
.left
.value
, &result
.top
.value
,
919 &result
.right
.value
, &result
.bottom
.value
,
920 direction
, (GtkTabFlags
)flags
, gtkWidgetType
);
923 result
= GetCachedWidgetBorder(aFrame
, aAppearance
, direction
);
927 return (CSSMargin(result
) * GetWidgetScaleFactor(aFrame
)).Rounded();
930 bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext
* aContext
,
932 StyleAppearance aAppearance
,
933 LayoutDeviceIntMargin
* aResult
) {
934 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
935 return Theme::GetWidgetPadding(aContext
, aFrame
, aAppearance
, aResult
);
937 switch (aAppearance
) {
938 case StyleAppearance::Toolbarbutton
:
939 case StyleAppearance::Tooltip
:
940 case StyleAppearance::MozWindowButtonBox
:
941 case StyleAppearance::MozWindowButtonClose
:
942 case StyleAppearance::MozWindowButtonMinimize
:
943 case StyleAppearance::MozWindowButtonMaximize
:
944 case StyleAppearance::MozWindowButtonRestore
:
945 case StyleAppearance::Dualbutton
:
946 case StyleAppearance::TabScrollArrowBack
:
947 case StyleAppearance::TabScrollArrowForward
:
948 case StyleAppearance::ToolbarbuttonDropdown
:
949 case StyleAppearance::ButtonArrowUp
:
950 case StyleAppearance::ButtonArrowDown
:
951 case StyleAppearance::ButtonArrowNext
:
952 case StyleAppearance::ButtonArrowPrevious
:
953 case StyleAppearance::RangeThumb
:
954 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
955 // and have a meaningful baseline, so they can't have
956 // author-specified padding.
957 case StyleAppearance::Checkbox
:
958 case StyleAppearance::Radio
:
959 aResult
->SizeTo(0, 0, 0, 0);
968 bool nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext
* aContext
,
970 StyleAppearance aAppearance
,
971 nsRect
* aOverflowRect
) {
972 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
973 return Theme::GetWidgetOverflow(aContext
, aFrame
, aAppearance
,
976 auto overflow
= GetExtraSizeForWidget(aFrame
, aAppearance
);
977 if (overflow
== CSSIntMargin()) {
980 aOverflowRect
->Inflate(CSSIntMargin::ToAppUnits(overflow
));
984 auto nsNativeThemeGTK::IsWidgetNonNative(nsIFrame
* aFrame
,
985 StyleAppearance aAppearance
)
987 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
988 return NonNative::Always
;
991 // If the current GTK theme color scheme matches our color-scheme, then we
992 // can draw a native widget.
993 if (LookAndFeel::ColorSchemeForFrame(aFrame
) ==
994 PreferenceSheet::ColorSchemeForChrome()) {
995 return NonNative::No
;
998 // As an special-case, for tooltips, we check if the tooltip color is the
999 // same between the light and dark themes. If so we can get away with drawing
1000 // the native widget, see bug 1817396.
1001 if (aAppearance
== StyleAppearance::Tooltip
) {
1003 LookAndFeel::Color(StyleSystemColor::Infotext
, ColorScheme::Dark
,
1004 LookAndFeel::UseStandins::No
);
1006 LookAndFeel::Color(StyleSystemColor::Infotext
, ColorScheme::Light
,
1007 LookAndFeel::UseStandins::No
);
1008 if (darkColor
== lightColor
) {
1009 return NonNative::No
;
1013 // If the non-native theme doesn't support the widget then oh well...
1014 if (!Theme::ThemeSupportsWidget(aFrame
->PresContext(), aFrame
, aAppearance
)) {
1015 return NonNative::No
;
1018 return NonNative::BecauseColorMismatch
;
1021 bool nsNativeThemeGTK::IsWidgetAlwaysNonNative(nsIFrame
* aFrame
,
1022 StyleAppearance aAppearance
) {
1023 return Theme::IsWidgetAlwaysNonNative(aFrame
, aAppearance
) ||
1024 aAppearance
== StyleAppearance::MozMenulistArrowButton
;
1027 LayoutDeviceIntSize
nsNativeThemeGTK::GetMinimumWidgetSize(
1028 nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1029 StyleAppearance aAppearance
) {
1030 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
1031 return Theme::GetMinimumWidgetSize(aPresContext
, aFrame
, aAppearance
);
1035 switch (aAppearance
) {
1036 case StyleAppearance::Splitter
: {
1037 if (IsHorizontal(aFrame
)) {
1038 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL
, &result
.width
);
1040 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL
, &result
.height
);
1043 case StyleAppearance::RangeThumb
: {
1044 if (IsRangeHorizontal(aFrame
)) {
1045 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL
,
1046 &result
.width
, &result
.height
);
1048 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL
, &result
.width
,
1052 case StyleAppearance::TabScrollArrowBack
:
1053 case StyleAppearance::TabScrollArrowForward
: {
1054 moz_gtk_get_tab_scroll_arrow_size(&result
.width
, &result
.height
);
1056 case StyleAppearance::Checkbox
:
1057 case StyleAppearance::Radio
: {
1058 const ToggleGTKMetrics
* metrics
= GetToggleMetrics(
1059 aAppearance
== StyleAppearance::Radio
? MOZ_GTK_RADIOBUTTON
1060 : MOZ_GTK_CHECKBUTTON
);
1061 result
.width
= metrics
->minSizeWithBorder
.width
;
1062 result
.height
= metrics
->minSizeWithBorder
.height
;
1064 case StyleAppearance::ToolbarbuttonDropdown
:
1065 case StyleAppearance::ButtonArrowUp
:
1066 case StyleAppearance::ButtonArrowDown
:
1067 case StyleAppearance::ButtonArrowNext
:
1068 case StyleAppearance::ButtonArrowPrevious
: {
1069 moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW
, &result
.width
,
1072 case StyleAppearance::MozWindowButtonClose
: {
1073 const ToolbarButtonGTKMetrics
* metrics
=
1074 GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
);
1075 result
.width
= metrics
->minSizeWithBorderMargin
.width
;
1076 result
.height
= metrics
->minSizeWithBorderMargin
.height
;
1079 case StyleAppearance::MozWindowButtonMinimize
: {
1080 const ToolbarButtonGTKMetrics
* metrics
=
1081 GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE
);
1082 result
.width
= metrics
->minSizeWithBorderMargin
.width
;
1083 result
.height
= metrics
->minSizeWithBorderMargin
.height
;
1086 case StyleAppearance::MozWindowButtonMaximize
:
1087 case StyleAppearance::MozWindowButtonRestore
: {
1088 const ToolbarButtonGTKMetrics
* metrics
=
1089 GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
);
1090 result
.width
= metrics
->minSizeWithBorderMargin
.width
;
1091 result
.height
= metrics
->minSizeWithBorderMargin
.height
;
1094 case StyleAppearance::Button
:
1095 case StyleAppearance::Menulist
:
1096 case StyleAppearance::MenulistButton
: {
1097 if (aAppearance
== StyleAppearance::Menulist
||
1098 aAppearance
== StyleAppearance::MenulistButton
) {
1099 // Include the arrow size.
1100 moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN
, &result
.width
, &result
.height
);
1102 // else the minimum size is missing consideration of container
1103 // descendants; the value returned here will not be helpful, but the
1104 // box model may consider border and padding with child minimum sizes.
1106 CSSIntMargin border
=
1107 GetCachedWidgetBorder(aFrame
, aAppearance
, GetTextDirection(aFrame
));
1108 result
.width
+= border
.LeftRight();
1109 result
.height
+= border
.TopBottom();
1111 case StyleAppearance::NumberInput
:
1112 case StyleAppearance::Textfield
: {
1113 gint contentHeight
= 0;
1114 gint borderPaddingHeight
= 0;
1115 moz_gtk_get_entry_min_height(&contentHeight
, &borderPaddingHeight
);
1117 // Scale the min content height proportionately with the font-size if it's
1118 // smaller than the default one. This prevents <input type=text
1119 // style="font-size: .5em"> from keeping a ridiculously large size, for
1121 const gfxFloat fieldFontSizeInCSSPixels
= [] {
1122 gfxFontStyle fieldFontStyle
;
1123 nsAutoString unusedFontName
;
1124 DebugOnly
<bool> result
= LookAndFeel::GetFont(
1125 LookAndFeel::FontID::MozField
, unusedFontName
, fieldFontStyle
);
1126 MOZ_ASSERT(result
, "GTK look and feel supports the field font");
1127 // NOTE: GetFont returns font sizes in CSS pixels, and we want just
1129 return fieldFontStyle
.size
;
1132 const gfxFloat fontSize
= aFrame
->StyleFont()->mFont
.size
.ToCSSPixels();
1133 if (fieldFontSizeInCSSPixels
> fontSize
) {
1135 std::round(contentHeight
* fontSize
/ fieldFontSizeInCSSPixels
);
1138 gint height
= contentHeight
+ borderPaddingHeight
;
1139 if (aFrame
->GetWritingMode().IsVertical()) {
1140 result
.width
= height
;
1142 result
.height
= height
;
1145 case StyleAppearance::Spinner
:
1146 // hard code these sizes
1150 case StyleAppearance::SpinnerUpbutton
:
1151 case StyleAppearance::SpinnerDownbutton
:
1152 // hard code these sizes
1156 case StyleAppearance::Treetwisty
:
1157 case StyleAppearance::Treetwistyopen
: {
1159 moz_gtk_get_treeview_expander_size(&expander_size
);
1160 result
.width
= result
.height
= expander_size
;
1166 return LayoutDeviceIntSize::Round(CSSSize(result
) *
1167 GetWidgetScaleFactor(aFrame
));
1171 nsNativeThemeGTK::WidgetStateChanged(nsIFrame
* aFrame
,
1172 StyleAppearance aAppearance
,
1173 nsAtom
* aAttribute
, bool* aShouldRepaint
,
1174 const nsAttrValue
* aOldValue
) {
1175 *aShouldRepaint
= false;
1177 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1178 return Theme::WidgetStateChanged(aFrame
, aAppearance
, aAttribute
,
1179 aShouldRepaint
, aOldValue
);
1182 // Some widget types just never change state.
1183 if (aAppearance
== StyleAppearance::Toolbox
||
1184 aAppearance
== StyleAppearance::Toolbar
||
1185 aAppearance
== StyleAppearance::Progresschunk
||
1186 aAppearance
== StyleAppearance::ProgressBar
||
1187 aAppearance
== StyleAppearance::Tooltip
||
1188 aAppearance
== StyleAppearance::MozWindowDecorations
) {
1192 if (aAppearance
== StyleAppearance::MozWindowTitlebar
||
1193 aAppearance
== StyleAppearance::MozWindowTitlebarMaximized
||
1194 aAppearance
== StyleAppearance::MozWindowButtonClose
||
1195 aAppearance
== StyleAppearance::MozWindowButtonMinimize
||
1196 aAppearance
== StyleAppearance::MozWindowButtonMaximize
||
1197 aAppearance
== StyleAppearance::MozWindowButtonRestore
) {
1198 *aShouldRepaint
= true;
1202 // XXXdwh Not sure what can really be done here. Can at least guess for
1203 // specific widgets that they're highly unlikely to have certain states.
1204 // For example, a toolbar doesn't care about any states.
1206 // Hover/focus/active changed. Always repaint.
1207 *aShouldRepaint
= true;
1211 // Check the attribute to see if it's relevant.
1212 // disabled, checked, dlgtype, default, etc.
1213 *aShouldRepaint
= false;
1214 if (aAttribute
== nsGkAtoms::disabled
|| aAttribute
== nsGkAtoms::checked
||
1215 aAttribute
== nsGkAtoms::selected
||
1216 aAttribute
== nsGkAtoms::visuallyselected
||
1217 aAttribute
== nsGkAtoms::focused
|| aAttribute
== nsGkAtoms::readonly
||
1218 aAttribute
== nsGkAtoms::_default
||
1219 aAttribute
== nsGkAtoms::menuactive
|| aAttribute
== nsGkAtoms::open
) {
1220 *aShouldRepaint
= true;
1227 nsNativeThemeGTK::ThemeChanged() {
1228 memset(mDisabledWidgetTypes
, 0, sizeof(mDisabledWidgetTypes
));
1229 memset(mSafeWidgetStates
, 0, sizeof(mSafeWidgetStates
));
1230 memset(mBorderCacheValid
, 0, sizeof(mBorderCacheValid
));
1234 NS_IMETHODIMP_(bool)
1235 nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext
* aPresContext
,
1237 StyleAppearance aAppearance
) {
1238 if (IsWidgetTypeDisabled(mDisabledWidgetTypes
, aAppearance
)) {
1242 if (IsWidgetAlwaysNonNative(aFrame
, aAppearance
)) {
1243 return Theme::ThemeSupportsWidget(aPresContext
, aFrame
, aAppearance
);
1246 switch (aAppearance
) {
1247 // Combobox dropdowns don't support native theming in vertical mode.
1248 case StyleAppearance::Menulist
:
1249 case StyleAppearance::MenulistButton
:
1250 if (aFrame
&& aFrame
->GetWritingMode().IsVertical()) {
1255 case StyleAppearance::Button
:
1256 case StyleAppearance::Radio
:
1257 case StyleAppearance::Checkbox
:
1258 case StyleAppearance::Toolbox
: // N/A
1259 case StyleAppearance::Toolbarbutton
:
1260 case StyleAppearance::Dualbutton
: // so we can override the border with 0
1261 case StyleAppearance::ToolbarbuttonDropdown
:
1262 case StyleAppearance::ButtonArrowUp
:
1263 case StyleAppearance::ButtonArrowDown
:
1264 case StyleAppearance::ButtonArrowNext
:
1265 case StyleAppearance::ButtonArrowPrevious
:
1266 case StyleAppearance::Listbox
:
1267 case StyleAppearance::Treeview
:
1268 // case StyleAppearance::Treeitem:
1269 case StyleAppearance::Treetwisty
:
1270 // case StyleAppearance::Treeline:
1271 // case StyleAppearance::Treeheader:
1272 case StyleAppearance::Treetwistyopen
:
1273 case StyleAppearance::ProgressBar
:
1274 case StyleAppearance::Progresschunk
:
1275 case StyleAppearance::Tab
:
1276 // case StyleAppearance::Tabpanel:
1277 case StyleAppearance::Tabpanels
:
1278 case StyleAppearance::TabScrollArrowBack
:
1279 case StyleAppearance::TabScrollArrowForward
:
1280 case StyleAppearance::Tooltip
:
1281 case StyleAppearance::Spinner
:
1282 case StyleAppearance::SpinnerUpbutton
:
1283 case StyleAppearance::SpinnerDownbutton
:
1284 case StyleAppearance::SpinnerTextfield
:
1285 case StyleAppearance::NumberInput
:
1286 case StyleAppearance::Textfield
:
1287 case StyleAppearance::Textarea
:
1288 case StyleAppearance::Range
:
1289 case StyleAppearance::RangeThumb
:
1290 case StyleAppearance::Splitter
:
1291 case StyleAppearance::MozWindowButtonBox
:
1292 case StyleAppearance::MozWindowButtonClose
:
1293 case StyleAppearance::MozWindowButtonMinimize
:
1294 case StyleAppearance::MozWindowButtonMaximize
:
1295 case StyleAppearance::MozWindowButtonRestore
:
1296 case StyleAppearance::MozWindowTitlebar
:
1297 case StyleAppearance::MozWindowTitlebarMaximized
:
1298 case StyleAppearance::MozWindowDecorations
:
1299 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1307 NS_IMETHODIMP_(bool)
1308 nsNativeThemeGTK::WidgetIsContainer(StyleAppearance aAppearance
) {
1309 // XXXdwh At some point flesh all of this out.
1310 if (aAppearance
== StyleAppearance::Radio
||
1311 aAppearance
== StyleAppearance::RangeThumb
||
1312 aAppearance
== StyleAppearance::Checkbox
||
1313 aAppearance
== StyleAppearance::TabScrollArrowBack
||
1314 aAppearance
== StyleAppearance::TabScrollArrowForward
||
1315 aAppearance
== StyleAppearance::ButtonArrowUp
||
1316 aAppearance
== StyleAppearance::ButtonArrowDown
||
1317 aAppearance
== StyleAppearance::ButtonArrowNext
||
1318 aAppearance
== StyleAppearance::ButtonArrowPrevious
)
1323 bool nsNativeThemeGTK::ThemeDrawsFocusForWidget(nsIFrame
* aFrame
,
1324 StyleAppearance aAppearance
) {
1325 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1326 return Theme::ThemeDrawsFocusForWidget(aFrame
, aAppearance
);
1328 switch (aAppearance
) {
1329 case StyleAppearance::Checkbox
:
1330 case StyleAppearance::Radio
:
1331 // These are drawn only for non-XUL elements, but in XUL the label has
1334 case StyleAppearance::Button
:
1335 case StyleAppearance::Menulist
:
1336 case StyleAppearance::MenulistButton
:
1337 case StyleAppearance::Textarea
:
1338 case StyleAppearance::Textfield
:
1339 case StyleAppearance::NumberInput
:
1346 bool nsNativeThemeGTK::ThemeNeedsComboboxDropmarker() { return false; }
1348 nsITheme::Transparency
nsNativeThemeGTK::GetWidgetTransparency(
1349 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1350 if (IsWidgetNonNative(aFrame
, aAppearance
) != NonNative::No
) {
1351 return Theme::GetWidgetTransparency(aFrame
, aAppearance
);
1354 switch (aAppearance
) {
1355 // Tooltips use gtk_paint_flat_box() on Gtk2
1356 // but are shaped on Gtk3
1357 case StyleAppearance::Tooltip
:
1358 return eTransparent
;
1360 return eUnknownTransparency
;
1364 already_AddRefed
<Theme
> do_CreateNativeThemeDoNotUseDirectly() {
1365 if (gfxPlatform::IsHeadless()) {
1366 return do_AddRef(new Theme(Theme::ScrollbarStyle()));
1368 return do_AddRef(new nsNativeThemeGTK());