Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/nsNativeThemeGTK.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
5
6
#include "nsNativeThemeGTK.h"
7
#include "HeadlessThemeGTK.h"
8
#include "nsStyleConsts.h"
9
#include "gtkdrawing.h"
10
#include "ScreenHelperGTK.h"
11
12
#include "gfx2DGlue.h"
13
#include "nsIObserverService.h"
14
#include "nsIServiceManager.h"
15
#include "nsIFrame.h"
16
#include "nsIPresShell.h"
17
#include "nsIContent.h"
18
#include "nsViewManager.h"
19
#include "nsNameSpaceManager.h"
20
#include "nsGfxCIID.h"
21
#include "nsTransform2D.h"
22
#include "nsMenuFrame.h"
23
#include "tree/nsTreeBodyFrame.h"
24
#include "prlink.h"
25
#include "nsGkAtoms.h"
26
#include "nsAttrValueInlines.h"
27
28
#include "mozilla/dom/HTMLInputElement.h"
29
#include "mozilla/ClearOnShutdown.h"
30
#include "mozilla/EventStates.h"
31
#include "mozilla/Services.h"
32
33
#include <gdk/gdkprivate.h>
34
#include <gtk/gtk.h>
35
#include <gtk/gtkx.h>
36
37
#include "gfxContext.h"
38
#include "gfxPlatformGtk.h"
39
#include "gfxGdkNativeRenderer.h"
40
#include "mozilla/gfx/BorrowedContext.h"
41
#include "mozilla/gfx/HelpersCairo.h"
42
#include "mozilla/gfx/PathHelpers.h"
43
#include "mozilla/Preferences.h"
44
#include "mozilla/layers/StackingContextHelper.h"
45
#include "mozilla/StaticPrefs.h"
46
#include "nsWindow.h"
47
48
#ifdef MOZ_X11
49
#  ifdef CAIRO_HAS_XLIB_SURFACE
50
#    include "cairo-xlib.h"
51
#  endif
52
#  ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
53
#    include "cairo-xlib-xrender.h"
54
#  endif
55
#endif
56
57
#include <algorithm>
58
#include <dlfcn.h>
59
60
using namespace mozilla;
61
using namespace mozilla::gfx;
62
using namespace mozilla::widget;
63
using mozilla::dom::HTMLInputElement;
64
65
NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
66
                                                             nsIObserver)
67
68
static int gLastGdkError;
69
70
// from nsWindow.cpp
71
extern bool gDisableNativeTheme;
72
73
// Return scale factor of the monitor where the window is located
74
// by the most part or layout.css.devPixelsPerPx pref if set to > 0.
75
static inline gint
76
GetMonitorScaleFactor(nsIFrame* aFrame)
77
0
{
78
0
  // When the layout.css.devPixelsPerPx is set the scale can be < 1,
79
0
  // the real monitor scale cannot go under 1.
80
0
  double scale = nsIWidget::DefaultScaleOverride();
81
0
  if (scale <= 0) {
82
0
    nsIWidget* rootWidget = aFrame->PresContext()->GetRootWidget();
83
0
    if (rootWidget) {
84
0
        // We need to use GetDefaultScale() despite it returns monitor scale
85
0
        // factor multiplied by font scale factor because it is the only scale
86
0
        // updated in nsPuppetWidget.
87
0
        // Since we don't want to apply font scale factor for UI elements
88
0
        // (because GTK does not do so) we need to remove that from returned value.
89
0
        // The computed monitor scale factor needs to be rounded before casting to
90
0
        // integer to avoid rounding errors which would lead to returning 0.
91
0
        int monitorScale = int(round(rootWidget->GetDefaultScale().scale
92
0
              / gfxPlatformGtk::GetFontScaleFactor()));
93
0
        // Monitor scale can be negative if it has not been initialized in the
94
0
        // puppet widget yet. We also make sure that we return positive value.
95
0
        if (monitorScale < 1) {
96
0
          return 1;
97
0
        }
98
0
        return monitorScale;
99
0
    }
100
0
  }
101
0
  // Use monitor scaling factor where devPixelsPerPx is set
102
0
  return ScreenHelperGTK::GetGTKMonitorScaleFactor();
103
0
}
104
105
nsNativeThemeGTK::nsNativeThemeGTK()
106
0
{
107
0
  if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
108
0
    memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
109
0
    return;
110
0
  }
111
0
112
0
  // We have to call moz_gtk_shutdown before the event loop stops running.
113
0
  nsCOMPtr<nsIObserverService> obsServ =
114
0
    mozilla::services::GetObserverService();
115
0
  obsServ->AddObserver(this, "xpcom-shutdown", false);
116
0
117
0
  ThemeChanged();
118
0
}
119
120
0
nsNativeThemeGTK::~nsNativeThemeGTK() {
121
0
}
122
123
NS_IMETHODIMP
124
nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
125
                          const char16_t *aData)
126
0
{
127
0
  if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
128
0
    moz_gtk_shutdown();
129
0
  } else {
130
0
    MOZ_ASSERT_UNREACHABLE("unexpected topic");
131
0
    return NS_ERROR_UNEXPECTED;
132
0
  }
133
0
134
0
  return NS_OK;
135
0
}
136
137
void
138
nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
139
0
{
140
0
  nsIPresShell *shell = GetPresShell(aFrame);
141
0
  if (!shell)
142
0
    return;
143
0
144
0
  nsViewManager* vm = shell->GetViewManager();
145
0
  if (!vm)
146
0
    return;
147
0
148
0
  vm->InvalidateAllViews();
149
0
}
150
151
152
static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace)
153
0
{
154
0
  nsIContent *content = aFrame ? aFrame->GetContent() : nullptr;
155
0
  if (!content)
156
0
    return false;
157
0
  return content->IsInNamespace(aNamespace);
158
0
}
159
160
0
static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, StyleAppearance aWidgetType) {
161
0
  auto type = static_cast<size_t>(aWidgetType);
162
0
  MOZ_ASSERT(type < static_cast<size_t>(mozilla::StyleAppearance::Count));
163
0
  return (aDisabledVector[type >> 3] & (1 << (type & 7))) != 0;
164
0
}
165
166
0
static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, StyleAppearance aWidgetType) {
167
0
  auto type = static_cast<size_t>(aWidgetType);
168
0
  MOZ_ASSERT(type < static_cast<size_t>(mozilla::StyleAppearance::Count));
169
0
  aDisabledVector[type >> 3] |= (1 << (type & 7));
170
0
}
171
172
static inline uint16_t
173
GetWidgetStateKey(StyleAppearance aWidgetType, GtkWidgetState *aWidgetState)
174
0
{
175
0
  return (aWidgetState->active |
176
0
          aWidgetState->focused << 1 |
177
0
          aWidgetState->inHover << 2 |
178
0
          aWidgetState->disabled << 3 |
179
0
          aWidgetState->isDefault << 4 |
180
0
          static_cast<uint16_t>(aWidgetType) << 5);
181
0
}
182
183
static bool IsWidgetStateSafe(uint8_t* aSafeVector,
184
                              StyleAppearance aWidgetType,
185
                              GtkWidgetState *aWidgetState)
186
0
{
187
0
  MOZ_ASSERT(static_cast<size_t>(aWidgetType) < static_cast<size_t>(mozilla::StyleAppearance::Count));
188
0
  uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
189
0
  return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0;
190
0
}
191
192
static void SetWidgetStateSafe(uint8_t *aSafeVector,
193
                               StyleAppearance aWidgetType,
194
                               GtkWidgetState *aWidgetState)
195
0
{
196
0
  MOZ_ASSERT(static_cast<size_t>(aWidgetType) < static_cast<size_t>(mozilla::StyleAppearance::Count));
197
0
  uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
198
0
  aSafeVector[key >> 3] |= (1 << (key & 7));
199
0
}
200
201
/* static */ GtkTextDirection
202
nsNativeThemeGTK::GetTextDirection(nsIFrame* aFrame)
203
0
{
204
0
  // IsFrameRTL() treats vertical-rl modes as right-to-left (in addition to
205
0
  // horizontal text with direction=RTL), rather than just considering the
206
0
  // text direction.  GtkTextDirection does not have distinct values for
207
0
  // vertical writing modes, but considering the block flow direction is
208
0
  // important for resizers and scrollbar elements, at least.
209
0
  return IsFrameRTL(aFrame) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
210
0
}
211
212
// Returns positive for negative margins (otherwise 0).
213
gint
214
nsNativeThemeGTK::GetTabMarginPixels(nsIFrame* aFrame)
215
0
{
216
0
  nscoord margin =
217
0
    IsBottomTab(aFrame) ? aFrame->GetUsedMargin().top
218
0
    : aFrame->GetUsedMargin().bottom;
219
0
220
0
  return std::min<gint>(MOZ_GTK_TAB_MARGIN_MASK,
221
0
                std::max(0,
222
0
                       aFrame->PresContext()->AppUnitsToDevPixels(-margin)));
223
0
}
224
225
static bool ShouldScrollbarButtonBeDisabled(int32_t aCurpos, int32_t aMaxpos,
226
                                            StyleAppearance aWidgetType)
227
0
{
228
0
  return ((aCurpos == 0 && (aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
229
0
                            aWidgetType == StyleAppearance::ScrollbarbuttonLeft))
230
0
      || (aCurpos == aMaxpos && (aWidgetType == StyleAppearance::ScrollbarbuttonDown ||
231
0
                                 aWidgetType == StyleAppearance::ScrollbarbuttonRight)));
232
0
}
233
234
bool
235
nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aWidgetType, nsIFrame* aFrame,
236
                                       WidgetNodeType& aGtkWidgetType,
237
                                       GtkWidgetState* aState,
238
                                       gint* aWidgetFlags)
239
0
{
240
0
  if (aWidgetType == StyleAppearance::MenulistButton &&
241
0
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
242
0
    aWidgetType = StyleAppearance::Menulist;
243
0
  }
244
0
245
0
  if (aState) {
246
0
    memset(aState, 0, sizeof(GtkWidgetState));
247
0
248
0
    // For XUL checkboxes and radio buttons, the state of the parent
249
0
    // determines our state.
250
0
    nsIFrame *stateFrame = aFrame;
251
0
    if (aFrame && ((aWidgetFlags && (aWidgetType == StyleAppearance::Checkbox ||
252
0
                                     aWidgetType == StyleAppearance::Radio)) ||
253
0
                   aWidgetType == StyleAppearance::CheckboxLabel ||
254
0
                   aWidgetType == StyleAppearance::RadioLabel)) {
255
0
256
0
      nsAtom* atom = nullptr;
257
0
      if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
258
0
        if (aWidgetType == StyleAppearance::CheckboxLabel ||
259
0
            aWidgetType == StyleAppearance::RadioLabel) {
260
0
          // Adjust stateFrame so GetContentState finds the correct state.
261
0
          stateFrame = aFrame = aFrame->GetParent()->GetParent();
262
0
        } else {
263
0
          // GetContentState knows to look one frame up for radio/checkbox
264
0
          // widgets, so don't adjust stateFrame here.
265
0
          aFrame = aFrame->GetParent();
266
0
        }
267
0
        if (aWidgetFlags) {
268
0
          if (!atom) {
269
0
            atom = (aWidgetType == StyleAppearance::Checkbox ||
270
0
                    aWidgetType == StyleAppearance::CheckboxLabel) ? nsGkAtoms::checked
271
0
                                                            : nsGkAtoms::selected;
272
0
          }
273
0
          *aWidgetFlags = CheckBooleanAttr(aFrame, atom);
274
0
        }
275
0
      } else {
276
0
        if (aWidgetFlags) {
277
0
          *aWidgetFlags = 0;
278
0
          HTMLInputElement* inputElt = HTMLInputElement::FromNode(aFrame->GetContent());
279
0
          if (inputElt && inputElt->Checked())
280
0
            *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
281
0
282
0
          if (GetIndeterminate(aFrame))
283
0
            *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
284
0
        }
285
0
      }
286
0
    } else if (aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
287
0
               aWidgetType == StyleAppearance::Treeheadersortarrow ||
288
0
               aWidgetType == StyleAppearance::ButtonArrowPrevious ||
289
0
               aWidgetType == StyleAppearance::ButtonArrowNext ||
290
0
               aWidgetType == StyleAppearance::ButtonArrowUp ||
291
0
               aWidgetType == StyleAppearance::ButtonArrowDown) {
292
0
      // The state of an arrow comes from its parent.
293
0
      stateFrame = aFrame = aFrame->GetParent();
294
0
    }
295
0
296
0
    EventStates eventState = GetContentState(stateFrame, aWidgetType);
297
0
298
0
    aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame);
299
0
    aState->active  = eventState.HasState(NS_EVENT_STATE_ACTIVE);
300
0
    aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS);
301
0
    aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER);
302
0
    aState->isDefault = IsDefaultButton(aFrame);
303
0
    aState->canDefault = FALSE; // XXX fix me
304
0
305
0
    if (aWidgetType == StyleAppearance::FocusOutline) {
306
0
      aState->disabled = FALSE;
307
0
      aState->active  = FALSE;
308
0
      aState->inHover = FALSE;
309
0
      aState->isDefault = FALSE;
310
0
      aState->canDefault = FALSE;
311
0
312
0
      aState->focused = TRUE;
313
0
      aState->depressed = TRUE; // see moz_gtk_entry_paint()
314
0
    } else if (aWidgetType == StyleAppearance::Button ||
315
0
               aWidgetType == StyleAppearance::Toolbarbutton ||
316
0
               aWidgetType == StyleAppearance::Dualbutton ||
317
0
               aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
318
0
               aWidgetType == StyleAppearance::Menulist ||
319
0
               aWidgetType == StyleAppearance::MenulistButton ||
320
0
               aWidgetType == StyleAppearance::MozMenulistButton) {
321
0
      aState->active &= aState->inHover;
322
0
    } else if (aWidgetType == StyleAppearance::Treetwisty ||
323
0
               aWidgetType == StyleAppearance::Treetwistyopen) {
324
0
      nsTreeBodyFrame *treeBodyFrame = do_QueryFrame(aFrame);
325
0
      if (treeBodyFrame) {
326
0
        const mozilla::AtomArray& atoms =
327
0
          treeBodyFrame->GetPropertyArrayForCurrentDrawingItem();
328
0
        aState->selected = atoms.Contains(nsGkAtoms::selected);
329
0
        aState->inHover = atoms.Contains(nsGkAtoms::hover);
330
0
      }
331
0
    }
332
0
333
0
    if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
334
0
      // For these widget types, some element (either a child or parent)
335
0
      // actually has element focus, so we check the focused attribute
336
0
      // to see whether to draw in the focused state.
337
0
      if (aWidgetType == StyleAppearance::NumberInput ||
338
0
          aWidgetType == StyleAppearance::Textfield ||
339
0
          aWidgetType == StyleAppearance::TextfieldMultiline ||
340
0
          aWidgetType == StyleAppearance::MenulistTextfield ||
341
0
          aWidgetType == StyleAppearance::SpinnerTextfield ||
342
0
          aWidgetType == StyleAppearance::RadioContainer ||
343
0
          aWidgetType == StyleAppearance::RadioLabel) {
344
0
        aState->focused = IsFocused(aFrame);
345
0
      } else if (aWidgetType == StyleAppearance::Radio ||
346
0
                 aWidgetType == StyleAppearance::Checkbox) {
347
0
        // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
348
0
        aState->focused = FALSE;
349
0
      }
350
0
351
0
      if (aWidgetType == StyleAppearance::ScrollbarthumbVertical ||
352
0
          aWidgetType == StyleAppearance::ScrollbarthumbHorizontal) {
353
0
        // for scrollbars we need to go up two to go from the thumb to
354
0
        // the slider to the actual scrollbar object
355
0
        nsIFrame *tmpFrame = aFrame->GetParent()->GetParent();
356
0
357
0
        aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0);
358
0
        aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100);
359
0
360
0
        if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) {
361
0
          aState->active = TRUE;
362
0
          // Set hover state to emulate Gtk style of active scrollbar thumb
363
0
          aState->inHover = TRUE;
364
0
        }
365
0
      }
366
0
367
0
      if (aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
368
0
          aWidgetType == StyleAppearance::ScrollbarbuttonDown ||
369
0
          aWidgetType == StyleAppearance::ScrollbarbuttonLeft ||
370
0
          aWidgetType == StyleAppearance::ScrollbarbuttonRight) {
371
0
        // set the state to disabled when the scrollbar is scrolled to
372
0
        // the beginning or the end, depending on the button type.
373
0
        int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
374
0
        int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
375
0
        if (ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType)) {
376
0
          aState->disabled = true;
377
0
        }
378
0
379
0
        // In order to simulate native GTK scrollbar click behavior,
380
0
        // we set the active attribute on the element to true if it's
381
0
        // pressed with any mouse button.
382
0
        // This allows us to show that it's active without setting :active
383
0
        else if (CheckBooleanAttr(aFrame, nsGkAtoms::active))
384
0
          aState->active = true;
385
0
386
0
        if (aWidgetFlags) {
387
0
          *aWidgetFlags = GetScrollbarButtonType(aFrame);
388
0
          if (static_cast<uint8_t>(aWidgetType) -
389
0
                static_cast<uint8_t>(StyleAppearance::ScrollbarbuttonUp) < 2)
390
0
            *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL;
391
0
        }
392
0
      }
393
0
394
0
      // menu item state is determined by the attribute "_moz-menuactive",
395
0
      // and not by the mouse hovering (accessibility).  as a special case,
396
0
      // menus which are children of a menu bar are only marked as prelight
397
0
      // if they are open, not on normal hover.
398
0
399
0
      if (aWidgetType == StyleAppearance::Menuitem ||
400
0
          aWidgetType == StyleAppearance::Checkmenuitem ||
401
0
          aWidgetType == StyleAppearance::Radiomenuitem ||
402
0
          aWidgetType == StyleAppearance::Menuseparator ||
403
0
          aWidgetType == StyleAppearance::Menuarrow) {
404
0
        bool isTopLevel = false;
405
0
        nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
406
0
        if (menuFrame) {
407
0
          isTopLevel = menuFrame->IsOnMenuBar();
408
0
        }
409
0
410
0
        if (isTopLevel) {
411
0
          aState->inHover = menuFrame->IsOpen();
412
0
        } else {
413
0
          aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
414
0
        }
415
0
416
0
        aState->active = FALSE;
417
0
418
0
        if (aWidgetType == StyleAppearance::Checkmenuitem ||
419
0
            aWidgetType == StyleAppearance::Radiomenuitem) {
420
0
          *aWidgetFlags = 0;
421
0
          if (aFrame && aFrame->GetContent() &&
422
0
              aFrame->GetContent()->IsElement()) {
423
0
            *aWidgetFlags = aFrame->GetContent()->AsElement()->
424
0
              AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
425
0
                          nsGkAtoms::_true, eIgnoreCase);
426
0
          }
427
0
        }
428
0
      }
429
0
430
0
      // A button with drop down menu open or an activated toggle button
431
0
      // should always appear depressed.
432
0
      if (aWidgetType == StyleAppearance::Button ||
433
0
          aWidgetType == StyleAppearance::Toolbarbutton ||
434
0
          aWidgetType == StyleAppearance::Dualbutton ||
435
0
          aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
436
0
          aWidgetType == StyleAppearance::Menulist ||
437
0
          aWidgetType == StyleAppearance::MenulistButton ||
438
0
          aWidgetType == StyleAppearance::MozMenulistButton) {
439
0
        bool menuOpen = IsOpenButton(aFrame);
440
0
        aState->depressed = IsCheckedButton(aFrame) || menuOpen;
441
0
        // we must not highlight buttons with open drop down menus on hover.
442
0
        aState->inHover = aState->inHover && !menuOpen;
443
0
      }
444
0
445
0
      // When the input field of the drop down button has focus, some themes
446
0
      // should draw focus for the drop down button as well.
447
0
      if ((aWidgetType == StyleAppearance::MenulistButton ||
448
0
           aWidgetType == StyleAppearance::MozMenulistButton) &&
449
0
          aWidgetFlags) {
450
0
        *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
451
0
      }
452
0
    }
453
0
454
0
    if (aWidgetType == StyleAppearance::MozWindowTitlebar ||
455
0
        aWidgetType == StyleAppearance::MozWindowTitlebarMaximized ||
456
0
        aWidgetType == StyleAppearance::MozWindowButtonClose ||
457
0
        aWidgetType == StyleAppearance::MozWindowButtonMinimize ||
458
0
        aWidgetType == StyleAppearance::MozWindowButtonMaximize ||
459
0
        aWidgetType == StyleAppearance::MozWindowButtonRestore) {
460
0
      aState->backdrop = !nsWindow::GetTopLevelWindowActiveState(aFrame);
461
0
    }
462
0
  }
463
0
464
0
  switch (aWidgetType) {
465
0
  case StyleAppearance::Button:
466
0
    if (aWidgetFlags)
467
0
      *aWidgetFlags = GTK_RELIEF_NORMAL;
468
0
    aGtkWidgetType = MOZ_GTK_BUTTON;
469
0
    break;
470
0
  case StyleAppearance::Toolbarbutton:
471
0
  case StyleAppearance::Dualbutton:
472
0
    if (aWidgetFlags)
473
0
      *aWidgetFlags = GTK_RELIEF_NONE;
474
0
    aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON;
475
0
    break;
476
0
  case StyleAppearance::FocusOutline:
477
0
    aGtkWidgetType = MOZ_GTK_ENTRY;
478
0
    break;
479
0
  case StyleAppearance::Checkbox:
480
0
  case StyleAppearance::Radio:
481
0
    aGtkWidgetType = (aWidgetType == StyleAppearance::Radio) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
482
0
    break;
483
0
  case StyleAppearance::ScrollbarbuttonUp:
484
0
  case StyleAppearance::ScrollbarbuttonDown:
485
0
  case StyleAppearance::ScrollbarbuttonLeft:
486
0
  case StyleAppearance::ScrollbarbuttonRight:
487
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON;
488
0
    break;
489
0
  case StyleAppearance::ScrollbarVertical:
490
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_VERTICAL;
491
0
    if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque)
492
0
        *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE;
493
0
    else
494
0
        *aWidgetFlags = 0;
495
0
    break;
496
0
  case StyleAppearance::ScrollbarHorizontal:
497
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_HORIZONTAL;
498
0
    if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque)
499
0
        *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE;
500
0
    else
501
0
        *aWidgetFlags = 0;
502
0
    break;
503
0
  case StyleAppearance::ScrollbartrackHorizontal:
504
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL;
505
0
    break;
506
0
  case StyleAppearance::ScrollbartrackVertical:
507
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
508
0
    break;
509
0
  case StyleAppearance::ScrollbarthumbVertical:
510
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
511
0
    break;
512
0
  case StyleAppearance::ScrollbarthumbHorizontal:
513
0
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
514
0
    break;
515
0
  case StyleAppearance::InnerSpinButton:
516
0
    aGtkWidgetType = MOZ_GTK_INNER_SPIN_BUTTON;
517
0
    break;
518
0
  case StyleAppearance::Spinner:
519
0
    aGtkWidgetType = MOZ_GTK_SPINBUTTON;
520
0
    break;
521
0
  case StyleAppearance::SpinnerUpbutton:
522
0
    aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP;
523
0
    break;
524
0
  case StyleAppearance::SpinnerDownbutton:
525
0
    aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN;
526
0
    break;
527
0
  case StyleAppearance::SpinnerTextfield:
528
0
    aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY;
529
0
    break;
530
0
  case StyleAppearance::Range:
531
0
    {
532
0
      if (IsRangeHorizontal(aFrame)) {
533
0
        if (aWidgetFlags)
534
0
          *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
535
0
        aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
536
0
      } else {
537
0
        if (aWidgetFlags)
538
0
          *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
539
0
        aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
540
0
      }
541
0
      break;
542
0
    }
543
0
  case StyleAppearance::RangeThumb:
544
0
    {
545
0
      if (IsRangeHorizontal(aFrame)) {
546
0
        if (aWidgetFlags)
547
0
          *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
548
0
        aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
549
0
      } else {
550
0
        if (aWidgetFlags)
551
0
          *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
552
0
        aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
553
0
      }
554
0
      break;
555
0
    }
556
0
  case StyleAppearance::ScaleHorizontal:
557
0
    if (aWidgetFlags)
558
0
      *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
559
0
    aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
560
0
    break;
561
0
  case StyleAppearance::ScalethumbHorizontal:
562
0
    if (aWidgetFlags)
563
0
      *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
564
0
    aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
565
0
    break;
566
0
  case StyleAppearance::ScaleVertical:
567
0
    if (aWidgetFlags)
568
0
      *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
569
0
    aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
570
0
    break;
571
0
  case StyleAppearance::Separator:
572
0
    aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR;
573
0
    break;
574
0
  case StyleAppearance::ScalethumbVertical:
575
0
    if (aWidgetFlags)
576
0
      *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
577
0
    aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
578
0
    break;
579
0
  case StyleAppearance::Toolbargripper:
580
0
    aGtkWidgetType = MOZ_GTK_GRIPPER;
581
0
    break;
582
0
  case StyleAppearance::Resizer:
583
0
    aGtkWidgetType = MOZ_GTK_RESIZER;
584
0
    break;
585
0
  case StyleAppearance::NumberInput:
586
0
  case StyleAppearance::Textfield:
587
0
    aGtkWidgetType = MOZ_GTK_ENTRY;
588
0
    break;
589
0
  case StyleAppearance::TextfieldMultiline:
590
0
#ifdef MOZ_WIDGET_GTK
591
0
    aGtkWidgetType = MOZ_GTK_TEXT_VIEW;
592
#else
593
    aGtkWidgetType = MOZ_GTK_ENTRY;
594
#endif
595
    break;
596
0
  case StyleAppearance::Listbox:
597
0
  case StyleAppearance::Treeview:
598
0
    aGtkWidgetType = MOZ_GTK_TREEVIEW;
599
0
    break;
600
0
  case StyleAppearance::Treeheadercell:
601
0
    if (aWidgetFlags) {
602
0
      // In this case, the flag denotes whether the header is the sorted one or not
603
0
      if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural)
604
0
        *aWidgetFlags = false;
605
0
      else
606
0
        *aWidgetFlags = true;
607
0
    }
608
0
    aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL;
609
0
    break;
610
0
  case StyleAppearance::Treeheadersortarrow:
611
0
    if (aWidgetFlags) {
612
0
      switch (GetTreeSortDirection(aFrame)) {
613
0
        case eTreeSortDirection_Ascending:
614
0
          *aWidgetFlags = GTK_ARROW_DOWN;
615
0
          break;
616
0
        case eTreeSortDirection_Descending:
617
0
          *aWidgetFlags = GTK_ARROW_UP;
618
0
          break;
619
0
        case eTreeSortDirection_Natural:
620
0
        default:
621
0
          /* This prevents the treecolums from getting smaller
622
0
           * and wider when switching sort direction off and on
623
0
           * */
624
0
          *aWidgetFlags = GTK_ARROW_NONE;
625
0
          break;
626
0
      }
627
0
    }
628
0
    aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW;
629
0
    break;
630
0
  case StyleAppearance::Treetwisty:
631
0
    aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
632
0
    if (aWidgetFlags)
633
0
      *aWidgetFlags = GTK_EXPANDER_COLLAPSED;
634
0
    break;
635
0
  case StyleAppearance::Treetwistyopen:
636
0
    aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
637
0
    if (aWidgetFlags)
638
0
      *aWidgetFlags = GTK_EXPANDER_EXPANDED;
639
0
    break;
640
0
  case StyleAppearance::Menulist:
641
0
    aGtkWidgetType = MOZ_GTK_DROPDOWN;
642
0
    if (aWidgetFlags)
643
0
        *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
644
0
    break;
645
0
  case StyleAppearance::MenulistText:
646
0
    return false; // nothing to do, but prevents the bg from being drawn
647
0
  case StyleAppearance::MenulistTextfield:
648
0
    aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
649
0
    break;
650
0
  case StyleAppearance::MenulistButton:
651
0
  case StyleAppearance::MozMenulistButton:
652
0
    aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
653
0
    break;
654
0
  case StyleAppearance::ToolbarbuttonDropdown:
655
0
  case StyleAppearance::ButtonArrowDown:
656
0
  case StyleAppearance::ButtonArrowUp:
657
0
  case StyleAppearance::ButtonArrowNext:
658
0
  case StyleAppearance::ButtonArrowPrevious:
659
0
    aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
660
0
    if (aWidgetFlags) {
661
0
      *aWidgetFlags = GTK_ARROW_DOWN;
662
0
663
0
      if (aWidgetType == StyleAppearance::ButtonArrowUp)
664
0
        *aWidgetFlags = GTK_ARROW_UP;
665
0
      else if (aWidgetType == StyleAppearance::ButtonArrowNext)
666
0
        *aWidgetFlags = GTK_ARROW_RIGHT;
667
0
      else if (aWidgetType == StyleAppearance::ButtonArrowPrevious)
668
0
        *aWidgetFlags = GTK_ARROW_LEFT;
669
0
    }
670
0
    break;
671
0
  case StyleAppearance::CheckboxContainer:
672
0
    aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER;
673
0
    break;
674
0
  case StyleAppearance::RadioContainer:
675
0
    aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER;
676
0
    break;
677
0
  case StyleAppearance::CheckboxLabel:
678
0
    aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL;
679
0
    break;
680
0
  case StyleAppearance::RadioLabel:
681
0
    aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL;
682
0
    break;
683
0
  case StyleAppearance::Toolbar:
684
0
    aGtkWidgetType = MOZ_GTK_TOOLBAR;
685
0
    break;
686
0
  case StyleAppearance::Tooltip:
687
0
    aGtkWidgetType = MOZ_GTK_TOOLTIP;
688
0
    break;
689
0
  case StyleAppearance::Statusbarpanel:
690
0
  case StyleAppearance::Resizerpanel:
691
0
    aGtkWidgetType = MOZ_GTK_FRAME;
692
0
    break;
693
0
  case StyleAppearance::Progressbar:
694
0
  case StyleAppearance::ProgressbarVertical:
695
0
    aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
696
0
    break;
697
0
  case StyleAppearance::Progresschunk:
698
0
  case StyleAppearance::ProgresschunkVertical:
699
0
    {
700
0
      nsIFrame* stateFrame = aFrame->GetParent();
701
0
      EventStates eventStates = GetContentState(stateFrame, aWidgetType);
702
0
703
0
      aGtkWidgetType = IsIndeterminateProgress(stateFrame, eventStates)
704
0
                         ? IsVerticalProgress(stateFrame)
705
0
                           ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
706
0
                           : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
707
0
                         : MOZ_GTK_PROGRESS_CHUNK;
708
0
    }
709
0
    break;
710
0
  case StyleAppearance::TabScrollArrowBack:
711
0
  case StyleAppearance::TabScrollArrowForward:
712
0
    if (aWidgetFlags)
713
0
      *aWidgetFlags = aWidgetType == StyleAppearance::TabScrollArrowBack ?
714
0
                        GTK_ARROW_LEFT : GTK_ARROW_RIGHT;
715
0
    aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW;
716
0
    break;
717
0
  case StyleAppearance::Tabpanels:
718
0
    aGtkWidgetType = MOZ_GTK_TABPANELS;
719
0
    break;
720
0
  case StyleAppearance::Tab:
721
0
    {
722
0
      if (IsBottomTab(aFrame)) {
723
0
        aGtkWidgetType = MOZ_GTK_TAB_BOTTOM;
724
0
      } else {
725
0
        aGtkWidgetType = MOZ_GTK_TAB_TOP;
726
0
      }
727
0
728
0
      if (aWidgetFlags) {
729
0
        /* First bits will be used to store max(0,-bmargin) where bmargin
730
0
         * is the bottom margin of the tab in pixels  (resp. top margin,
731
0
         * for bottom tabs). */
732
0
        *aWidgetFlags = GetTabMarginPixels(aFrame);
733
0
734
0
        if (IsSelectedTab(aFrame))
735
0
          *aWidgetFlags |= MOZ_GTK_TAB_SELECTED;
736
0
737
0
        if (IsFirstTab(aFrame))
738
0
          *aWidgetFlags |= MOZ_GTK_TAB_FIRST;
739
0
      }
740
0
    }
741
0
    break;
742
0
  case StyleAppearance::Splitter:
743
0
    if (IsHorizontal(aFrame))
744
0
      aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
745
0
    else
746
0
      aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
747
0
    break;
748
0
  case StyleAppearance::Menubar:
749
0
    aGtkWidgetType = MOZ_GTK_MENUBAR;
750
0
    break;
751
0
  case StyleAppearance::Menupopup:
752
0
    aGtkWidgetType = MOZ_GTK_MENUPOPUP;
753
0
    break;
754
0
  case StyleAppearance::Menuitem:
755
0
    {
756
0
      nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
757
0
      if (menuFrame && menuFrame->IsOnMenuBar()) {
758
0
        aGtkWidgetType = MOZ_GTK_MENUBARITEM;
759
0
        break;
760
0
      }
761
0
    }
762
0
    aGtkWidgetType = MOZ_GTK_MENUITEM;
763
0
    break;
764
0
  case StyleAppearance::Menuseparator:
765
0
    aGtkWidgetType = MOZ_GTK_MENUSEPARATOR;
766
0
    break;
767
0
  case StyleAppearance::Menuarrow:
768
0
    aGtkWidgetType = MOZ_GTK_MENUARROW;
769
0
    break;
770
0
  case StyleAppearance::Checkmenuitem:
771
0
    aGtkWidgetType = MOZ_GTK_CHECKMENUITEM;
772
0
    break;
773
0
  case StyleAppearance::Radiomenuitem:
774
0
    aGtkWidgetType = MOZ_GTK_RADIOMENUITEM;
775
0
    break;
776
0
  case StyleAppearance::Window:
777
0
  case StyleAppearance::Dialog:
778
0
    aGtkWidgetType = MOZ_GTK_WINDOW;
779
0
    break;
780
0
  case StyleAppearance::MozGtkInfoBar:
781
0
    aGtkWidgetType = MOZ_GTK_INFO_BAR;
782
0
    break;
783
0
  case StyleAppearance::MozWindowTitlebar:
784
0
    aGtkWidgetType = MOZ_GTK_HEADER_BAR;
785
0
    break;
786
0
  case StyleAppearance::MozWindowTitlebarMaximized:
787
0
    aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED;
788
0
    break;
789
0
  case StyleAppearance::MozWindowButtonClose:
790
0
    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE;
791
0
    break;
792
0
  case StyleAppearance::MozWindowButtonMinimize:
793
0
    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
794
0
    break;
795
0
  case StyleAppearance::MozWindowButtonMaximize:
796
0
    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
797
0
    break;
798
0
  case StyleAppearance::MozWindowButtonRestore:
799
0
    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE;
800
0
    break;
801
0
  default:
802
0
    return false;
803
0
  }
804
0
805
0
  return true;
806
0
}
807
808
class SystemCairoClipper : public ClipExporter {
809
public:
810
  explicit SystemCairoClipper(cairo_t* aContext) : mContext(aContext)
811
0
  {
812
0
  }
813
814
  void
815
  BeginClip(const Matrix& aTransform) override
816
0
  {
817
0
    cairo_matrix_t mat;
818
0
    GfxMatrixToCairoMatrix(aTransform, mat);
819
0
    cairo_set_matrix(mContext, &mat);
820
0
821
0
    cairo_new_path(mContext);
822
0
  }
823
824
  void
825
  MoveTo(const Point &aPoint) override
826
0
  {
827
0
    cairo_move_to(mContext, aPoint.x, aPoint.y);
828
0
    mCurrentPoint = aPoint;
829
0
  }
830
831
  void
832
  LineTo(const Point &aPoint) override
833
0
  {
834
0
    cairo_line_to(mContext, aPoint.x, aPoint.y);
835
0
    mCurrentPoint = aPoint;
836
0
  }
837
838
  void
839
  BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3) override
840
0
  {
841
0
    cairo_curve_to(mContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
842
0
    mCurrentPoint = aCP3;
843
0
  }
844
845
  void
846
  QuadraticBezierTo(const Point &aCP1, const Point &aCP2) override
847
0
  {
848
0
    Point CP0 = CurrentPoint();
849
0
    Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
850
0
    Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
851
0
    Point CP3 = aCP2;
852
0
    cairo_curve_to(mContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
853
0
    mCurrentPoint = aCP2;
854
0
  }
855
856
  void
857
  Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle,
858
      bool aAntiClockwise) override
859
0
  {
860
0
    ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
861
0
                aAntiClockwise);
862
0
  }
863
864
  void
865
  Close() override
866
0
  {
867
0
    cairo_close_path(mContext);
868
0
  }
869
870
  void
871
  EndClip() override
872
0
  {
873
0
    cairo_clip(mContext);
874
0
  }
875
876
  Point
877
  CurrentPoint() const override
878
0
  {
879
0
    return mCurrentPoint;
880
0
  }
881
882
private:
883
  cairo_t* mContext;
884
  Point mCurrentPoint;
885
};
886
887
static void
888
DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
889
                   GtkWidgetState aState, WidgetNodeType aGTKWidgetType,
890
                   gint aFlags, GtkTextDirection aDirection, gint aScaleFactor,
891
                   bool aSnapped, const Point& aDrawOrigin, const nsIntSize& aDrawSize,
892
                   GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency)
893
0
{
894
0
  Point drawOffset;
895
0
  Matrix transform;
896
0
  if (!aSnapped) {
897
0
    // If we are not snapped, we depend on the DT for translation.
898
0
    drawOffset = aDrawOrigin;
899
0
    transform = aDrawTarget->GetTransform().PreTranslate(aDrawOrigin);
900
0
  } else {
901
0
    // Otherwise, we only need to take the device offset into account.
902
0
    drawOffset = aDrawOrigin - aContext->GetDeviceOffset();
903
0
    transform = Matrix::Translation(drawOffset);
904
0
  }
905
0
906
0
  if (aScaleFactor != 1)
907
0
    transform.PreScale(aScaleFactor, aScaleFactor);
908
0
909
0
  cairo_matrix_t mat;
910
0
  GfxMatrixToCairoMatrix(transform, mat);
911
0
912
0
  nsIntSize clipSize((aDrawSize.width + aScaleFactor - 1) / aScaleFactor,
913
0
                     (aDrawSize.height + aScaleFactor - 1) / aScaleFactor);
914
0
915
#ifndef MOZ_TREE_CAIRO
916
  // Directly use the Cairo draw target to render the widget if using system Cairo everywhere.
917
  BorrowedCairoContext borrowCairo(aDrawTarget);
918
  if (borrowCairo.mCairo) {
919
    cairo_set_matrix(borrowCairo.mCairo, &mat);
920
921
    cairo_new_path(borrowCairo.mCairo);
922
    cairo_rectangle(borrowCairo.mCairo, 0, 0, clipSize.width, clipSize.height);
923
    cairo_clip(borrowCairo.mCairo);
924
925
    moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect, &aState, aFlags, aDirection);
926
927
    borrowCairo.Finish();
928
    return;
929
  }
930
#endif
931
932
0
  // A direct Cairo draw target is not available, so we need to create a temporary one.
933
0
#if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
934
0
  // If using a Cairo xlib surface, then try to reuse it.
935
0
  BorrowedXlibDrawable borrow(aDrawTarget);
936
0
  if (borrow.GetDrawable()) {
937
0
    nsIntSize size = borrow.GetSize();
938
0
    cairo_surface_t* surf = nullptr;
939
0
    // Check if the surface is using XRender.
940
0
#ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
941
0
    if (borrow.GetXRenderFormat()) {
942
0
      surf = cairo_xlib_surface_create_with_xrender_format(
943
0
          borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetScreen(),
944
0
          borrow.GetXRenderFormat(), size.width, size.height);
945
0
    } else {
946
#else
947
      if (! borrow.GetXRenderFormat()) {
948
#endif
949
        surf = cairo_xlib_surface_create(
950
0
            borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetVisual(),
951
0
            size.width, size.height);
952
0
      }
953
0
      if (!NS_WARN_IF(!surf)) {
954
0
        Point offset = borrow.GetOffset();
955
0
        if (offset != Point()) {
956
0
          cairo_surface_set_device_offset(surf, offset.x, offset.y);
957
0
        }
958
0
        cairo_t* cr = cairo_create(surf);
959
0
        if (!NS_WARN_IF(!cr)) {
960
0
          RefPtr<SystemCairoClipper> clipper = new SystemCairoClipper(cr);
961
0
          aContext->ExportClip(*clipper);
962
0
963
0
          cairo_set_matrix(cr, &mat);
964
0
965
0
          cairo_new_path(cr);
966
0
          cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height);
967
0
          cairo_clip(cr);
968
0
969
0
          moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
970
0
971
0
          cairo_destroy(cr);
972
0
        }
973
0
        cairo_surface_destroy(surf);
974
0
      }
975
0
      borrow.Finish();
976
0
      return;
977
0
    }
978
0
#endif
979
0
980
0
  // Check if the widget requires complex masking that must be composited.
981
0
  // Try to directly write to the draw target's pixels if possible.
982
0
  uint8_t* data;
983
0
  nsIntSize size;
984
0
  int32_t stride;
985
0
  SurfaceFormat format;
986
0
  IntPoint origin;
987
0
  if (aDrawTarget->LockBits(&data, &size, &stride, &format, &origin)) {
988
0
    // Create a Cairo image surface context the device rectangle.
989
0
    cairo_surface_t* surf =
990
0
      cairo_image_surface_create_for_data(
991
0
        data, GfxFormatToCairoFormat(format), size.width, size.height, stride);
992
0
    if (!NS_WARN_IF(!surf)) {
993
0
      if (origin != IntPoint()) {
994
0
        cairo_surface_set_device_offset(surf, -origin.x, -origin.y);
995
0
      }
996
0
      cairo_t* cr = cairo_create(surf);
997
0
      if (!NS_WARN_IF(!cr)) {
998
0
        RefPtr<SystemCairoClipper> clipper = new SystemCairoClipper(cr);
999
0
        aContext->ExportClip(*clipper);
1000
0
1001
0
        cairo_set_matrix(cr, &mat);
1002
0
1003
0
        cairo_new_path(cr);
1004
0
        cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height);
1005
0
        cairo_clip(cr);
1006
0
1007
0
        moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
1008
0
1009
0
        cairo_destroy(cr);
1010
0
      }
1011
0
      cairo_surface_destroy(surf);
1012
0
    }
1013
0
    aDrawTarget->ReleaseBits(data);
1014
0
  } else {
1015
0
    // If the widget has any transparency, make sure to choose an alpha format.
1016
0
    format = aTransparency != nsITheme::eOpaque ? SurfaceFormat::B8G8R8A8 : aDrawTarget->GetFormat();
1017
0
    // Create a temporary data surface to render the widget into.
1018
0
    RefPtr<DataSourceSurface> dataSurface =
1019
0
      Factory::CreateDataSourceSurface(aDrawSize, format, aTransparency != nsITheme::eOpaque);
1020
0
    DataSourceSurface::MappedSurface map;
1021
0
    if (!NS_WARN_IF(!(dataSurface && dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)))) {
1022
0
      // Create a Cairo image surface wrapping the data surface.
1023
0
      cairo_surface_t* surf =
1024
0
        cairo_image_surface_create_for_data(map.mData, GfxFormatToCairoFormat(format),
1025
0
                                            aDrawSize.width, aDrawSize.height, map.mStride);
1026
0
      cairo_t* cr = nullptr;
1027
0
      if (!NS_WARN_IF(!surf)) {
1028
0
        cr = cairo_create(surf);
1029
0
        if (!NS_WARN_IF(!cr)) {
1030
0
          if (aScaleFactor != 1) {
1031
0
            cairo_scale(cr, aScaleFactor, aScaleFactor);
1032
0
          }
1033
0
1034
0
          moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
1035
0
        }
1036
0
      }
1037
0
1038
0
      // Unmap the surface before using it as a source
1039
0
      dataSurface->Unmap();
1040
0
1041
0
      if (cr) {
1042
0
        // The widget either needs to be masked or has transparency, so use the slower drawing path.
1043
0
        aDrawTarget->DrawSurface(dataSurface,
1044
0
                                 Rect(aSnapped ? drawOffset - aDrawTarget->GetTransform().GetTranslation() : drawOffset,
1045
0
                                      Size(aDrawSize)),
1046
0
                                 Rect(0, 0, aDrawSize.width, aDrawSize.height));
1047
0
        cairo_destroy(cr);
1048
0
      }
1049
0
1050
0
      if (surf) {
1051
0
        cairo_surface_destroy(surf);
1052
0
      }
1053
0
    }
1054
0
  }
1055
0
}
1056
1057
bool
1058
nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame,
1059
                                        StyleAppearance aWidgetType,
1060
                                        nsIntMargin* aExtra)
1061
0
{
1062
0
  *aExtra = nsIntMargin(0,0,0,0);
1063
0
  // Allow an extra one pixel above and below the thumb for certain
1064
0
  // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
1065
0
  // We modify the frame's overflow area.  See bug 297508.
1066
0
  switch (aWidgetType) {
1067
0
  case StyleAppearance::ScrollbarthumbVertical:
1068
0
    aExtra->top = aExtra->bottom = 1;
1069
0
    break;
1070
0
  case StyleAppearance::ScrollbarthumbHorizontal:
1071
0
    aExtra->left = aExtra->right = 1;
1072
0
    break;
1073
0
1074
0
  case StyleAppearance::Button :
1075
0
    {
1076
0
      if (IsDefaultButton(aFrame)) {
1077
0
        // Some themes draw a default indicator outside the widget,
1078
0
        // include that in overflow
1079
0
        gint top, left, bottom, right;
1080
0
        moz_gtk_button_get_default_overflow(&top, &left, &bottom, &right);
1081
0
        aExtra->top = top;
1082
0
        aExtra->right = right;
1083
0
        aExtra->bottom = bottom;
1084
0
        aExtra->left = left;
1085
0
        break;
1086
0
      }
1087
0
      return false;
1088
0
    }
1089
0
  case StyleAppearance::FocusOutline:
1090
0
    {
1091
0
      moz_gtk_get_focus_outline_size(&aExtra->left, &aExtra->top);
1092
0
      aExtra->right = aExtra->left;
1093
0
      aExtra->bottom = aExtra->top;
1094
0
      break;
1095
0
    }
1096
0
  case StyleAppearance::Tab :
1097
0
    {
1098
0
      if (!IsSelectedTab(aFrame))
1099
0
        return false;
1100
0
1101
0
      gint gap_height = moz_gtk_get_tab_thickness(IsBottomTab(aFrame) ?
1102
0
                            MOZ_GTK_TAB_BOTTOM : MOZ_GTK_TAB_TOP);
1103
0
      if (!gap_height)
1104
0
        return false;
1105
0
1106
0
      int32_t extra = gap_height - GetTabMarginPixels(aFrame);
1107
0
      if (extra <= 0)
1108
0
        return false;
1109
0
1110
0
      if (IsBottomTab(aFrame)) {
1111
0
        aExtra->top = extra;
1112
0
      } else {
1113
0
        aExtra->bottom = extra;
1114
0
      }
1115
0
      return false;
1116
0
    }
1117
0
  default:
1118
0
    return false;
1119
0
  }
1120
0
  gint scale = GetMonitorScaleFactor(aFrame);
1121
0
  aExtra->top *= scale;
1122
0
  aExtra->right *= scale;
1123
0
  aExtra->bottom *= scale;
1124
0
  aExtra->left *= scale;
1125
0
  return true;
1126
0
}
1127
1128
NS_IMETHODIMP
1129
nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext,
1130
                                       nsIFrame* aFrame,
1131
                                       StyleAppearance aWidgetType,
1132
                                       const nsRect& aRect,
1133
                                       const nsRect& aDirtyRect)
1134
0
{
1135
0
  GtkWidgetState state;
1136
0
  WidgetNodeType gtkWidgetType;
1137
0
  GtkTextDirection direction = GetTextDirection(aFrame);
1138
0
  gint flags;
1139
0
  if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
1140
0
                            &flags))
1141
0
    return NS_OK;
1142
0
1143
0
  gfxContext* ctx = aContext;
1144
0
  nsPresContext *presContext = aFrame->PresContext();
1145
0
1146
0
  gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
1147
0
  gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
1148
0
  gint scaleFactor = GetMonitorScaleFactor(aFrame);
1149
0
1150
0
  // Align to device pixels where sensible
1151
0
  // to provide crisper and faster drawing.
1152
0
  // Don't snap if it's a non-unit scale factor. We're going to have to take
1153
0
  // slow paths then in any case.
1154
0
  bool snapped = ctx->UserToDevicePixelSnapped(rect);
1155
0
  if (snapped) {
1156
0
    // Leave rect in device coords but make dirtyRect consistent.
1157
0
    dirtyRect = ctx->UserToDevice(dirtyRect);
1158
0
  }
1159
0
1160
0
  // Translate the dirty rect so that it is wrt the widget top-left.
1161
0
  dirtyRect.MoveBy(-rect.TopLeft());
1162
0
  // Round out the dirty rect to gdk pixels to ensure that gtk draws
1163
0
  // enough pixels for interpolation to device pixels.
1164
0
  dirtyRect.RoundOut();
1165
0
1166
0
  // GTK themes can only draw an integer number of pixels
1167
0
  // (even when not snapped).
1168
0
  nsIntRect widgetRect(0, 0, NS_lround(rect.Width()), NS_lround(rect.Height()));
1169
0
  nsIntRect overflowRect(widgetRect);
1170
0
  nsIntMargin extraSize;
1171
0
  if (GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) {
1172
0
    overflowRect.Inflate(extraSize);
1173
0
  }
1174
0
1175
0
  // This is the rectangle that will actually be drawn, in gdk pixels
1176
0
  nsIntRect drawingRect(int32_t(dirtyRect.X()),
1177
0
                        int32_t(dirtyRect.Y()),
1178
0
                        int32_t(dirtyRect.Width()),
1179
0
                        int32_t(dirtyRect.Height()));
1180
0
  if (widgetRect.IsEmpty()
1181
0
      || !drawingRect.IntersectRect(overflowRect, drawingRect))
1182
0
    return NS_OK;
1183
0
1184
0
  NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
1185
0
               "Trying to render an unsafe widget!");
1186
0
1187
0
  bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
1188
0
  if (!safeState) {
1189
0
    gLastGdkError = 0;
1190
0
    gdk_error_trap_push ();
1191
0
  }
1192
0
1193
0
  Transparency transparency = GetWidgetTransparency(aFrame, aWidgetType);
1194
0
1195
0
  // gdk rectangles are wrt the drawing rect.
1196
0
  GdkRectangle gdk_rect = {-drawingRect.x/scaleFactor,
1197
0
                           -drawingRect.y/scaleFactor,
1198
0
                           widgetRect.width/scaleFactor,
1199
0
                           widgetRect.height/scaleFactor};
1200
0
1201
0
  // Save actual widget scale to GtkWidgetState as we don't provide
1202
0
  // nsFrame to gtk3drawing routines.
1203
0
  state.scale = scaleFactor;
1204
0
1205
0
  // translate everything so (0,0) is the top left of the drawingRect
1206
0
  gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
1207
0
1208
0
  DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
1209
0
                     state, gtkWidgetType, flags, direction, scaleFactor,
1210
0
                     snapped, ToPoint(origin), drawingRect.Size(),
1211
0
                     gdk_rect, transparency);
1212
0
1213
0
  if (!safeState) {
1214
0
    // gdk_flush() call from expose event crashes Gtk+ on Wayland
1215
0
    // (Gnome BZ #773307)
1216
0
    if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
1217
0
      gdk_flush();
1218
0
    }
1219
0
    gLastGdkError = gdk_error_trap_pop ();
1220
0
1221
0
    if (gLastGdkError) {
1222
#ifdef DEBUG
1223
      printf("GTK theme failed for widget type %d, error was %d, state was "
1224
             "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
1225
             static_cast<int>(aWidgetType), gLastGdkError, state.active,
1226
             state.focused, state.inHover, state.disabled);
1227
#endif
1228
0
      NS_WARNING("GTK theme failed; disabling unsafe widget");
1229
0
      SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType);
1230
0
      // force refresh of the window, because the widget was not
1231
0
      // successfully drawn it must be redrawn using the default look
1232
0
      RefreshWidgetWindow(aFrame);
1233
0
    } else {
1234
0
      SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
1235
0
    }
1236
0
  }
1237
0
1238
0
  // Indeterminate progress bar are animated.
1239
0
  if (gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
1240
0
      gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
1241
0
    if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
1242
0
      NS_WARNING("unable to animate widget!");
1243
0
    }
1244
0
  }
1245
0
1246
0
  return NS_OK;
1247
0
}
1248
1249
bool
1250
nsNativeThemeGTK::CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder& aBuilder,
1251
                                                   mozilla::wr::IpcResourceUpdateQueue& aResources,
1252
                                                   const mozilla::layers::StackingContextHelper& aSc,
1253
                                                   mozilla::layers::WebRenderLayerManager* aManager,
1254
                                                   nsIFrame* aFrame,
1255
                                                   StyleAppearance aWidgetType,
1256
                                                   const nsRect& aRect)
1257
0
{
1258
0
  nsPresContext* presContext = aFrame->PresContext();
1259
0
  wr::LayoutRect bounds = wr::ToRoundedLayoutRect(
1260
0
    LayoutDeviceRect::FromAppUnits(aRect, presContext->AppUnitsPerDevPixel()));
1261
0
1262
0
  switch (aWidgetType) {
1263
0
  case StyleAppearance::Window:
1264
0
  case StyleAppearance::Dialog:
1265
0
    aBuilder.PushRect(bounds, bounds, true,
1266
0
                      wr::ToColorF(Color::FromABGR(
1267
0
                        LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground,
1268
0
                                              NS_RGBA(0, 0, 0, 0)))));
1269
0
    return true;
1270
0
1271
0
  default:
1272
0
    return false;
1273
0
  }
1274
0
}
1275
1276
WidgetNodeType
1277
nsNativeThemeGTK::NativeThemeToGtkTheme(StyleAppearance aWidgetType, nsIFrame* aFrame)
1278
0
{
1279
0
  WidgetNodeType gtkWidgetType;
1280
0
  gint unusedFlags;
1281
0
1282
0
  if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
1283
0
                            &unusedFlags))
1284
0
  {
1285
0
    MOZ_ASSERT_UNREACHABLE("Unknown native widget to gtk widget mapping");
1286
0
    return MOZ_GTK_WINDOW;
1287
0
  }
1288
0
  return gtkWidgetType;
1289
0
}
1290
1291
void
1292
nsNativeThemeGTK::GetCachedWidgetBorder(nsIFrame* aFrame,
1293
                                        StyleAppearance aWidgetType,
1294
                                        GtkTextDirection aDirection,
1295
                                        LayoutDeviceIntMargin* aResult)
1296
0
{
1297
0
  aResult->SizeTo(0, 0, 0, 0);
1298
0
1299
0
  WidgetNodeType gtkWidgetType;
1300
0
  gint unusedFlags;
1301
0
  if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
1302
0
                           &unusedFlags)) {
1303
0
    MOZ_ASSERT(0 <= gtkWidgetType && gtkWidgetType < MOZ_GTK_WIDGET_NODE_COUNT);
1304
0
    uint8_t cacheIndex = gtkWidgetType / 8;
1305
0
    uint8_t cacheBit = 1u << (gtkWidgetType % 8);
1306
0
1307
0
    if (mBorderCacheValid[cacheIndex] & cacheBit) {
1308
0
      *aResult = mBorderCache[gtkWidgetType];
1309
0
    } else {
1310
0
      moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
1311
0
                                &aResult->right, &aResult->bottom, aDirection);
1312
0
      if (gtkWidgetType != MOZ_GTK_DROPDOWN) { // depends on aDirection
1313
0
        mBorderCacheValid[cacheIndex] |= cacheBit;
1314
0
        mBorderCache[gtkWidgetType] = *aResult;
1315
0
      }
1316
0
    }
1317
0
  }
1318
0
}
1319
1320
LayoutDeviceIntMargin
1321
nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext,
1322
                                  nsIFrame* aFrame,
1323
                                  StyleAppearance aWidgetType)
1324
0
{
1325
0
  LayoutDeviceIntMargin result;
1326
0
  GtkTextDirection direction = GetTextDirection(aFrame);
1327
0
  switch (aWidgetType) {
1328
0
  case StyleAppearance::ScrollbarHorizontal:
1329
0
  case StyleAppearance::ScrollbarVertical:
1330
0
    {
1331
0
      GtkOrientation orientation =
1332
0
        aWidgetType == StyleAppearance::ScrollbarHorizontal ?
1333
0
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1334
0
      const ScrollbarGTKMetrics* metrics =
1335
0
        GetActiveScrollbarMetrics(orientation);
1336
0
1337
0
      const GtkBorder& border = metrics->border.scrollbar;
1338
0
      result.top = border.top;
1339
0
      result.right = border.right;
1340
0
      result.bottom = border.bottom;
1341
0
      result.left = border.left;
1342
0
    }
1343
0
    break;
1344
0
  case StyleAppearance::ScrollbartrackHorizontal:
1345
0
  case StyleAppearance::ScrollbartrackVertical:
1346
0
    {
1347
0
      GtkOrientation orientation =
1348
0
        aWidgetType == StyleAppearance::ScrollbartrackHorizontal ?
1349
0
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1350
0
      const ScrollbarGTKMetrics* metrics =
1351
0
        GetActiveScrollbarMetrics(orientation);
1352
0
1353
0
      const GtkBorder& border = metrics->border.track;
1354
0
      result.top = border.top;
1355
0
      result.right = border.right;
1356
0
      result.bottom = border.bottom;
1357
0
      result.left = border.left;
1358
0
    }
1359
0
    break;
1360
0
  case StyleAppearance::Toolbox:
1361
0
    // gtk has no toolbox equivalent.  So, although we map toolbox to
1362
0
    // gtk's 'toolbar' for purposes of painting the widget background,
1363
0
    // we don't use the toolbar border for toolbox.
1364
0
    break;
1365
0
  case StyleAppearance::Dualbutton:
1366
0
    // TOOLBAR_DUAL_BUTTON is an interesting case.  We want a border to draw
1367
0
    // around the entire button + dropdown, and also an inner border if you're
1368
0
    // over the button part.  But, we want the inner button to be right up
1369
0
    // against the edge of the outer button so that the borders overlap.
1370
0
    // To make this happen, we draw a button border for the outer button,
1371
0
    // but don't reserve any space for it.
1372
0
    break;
1373
0
  case StyleAppearance::Tab:
1374
0
    {
1375
0
      WidgetNodeType gtkWidgetType;
1376
0
      gint flags;
1377
0
1378
0
      if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
1379
0
                                &flags)) {
1380
0
        return result;
1381
0
      }
1382
0
      moz_gtk_get_tab_border(&result.left, &result.top,
1383
0
                             &result.right, &result.bottom, direction,
1384
0
                             (GtkTabFlags)flags, gtkWidgetType);
1385
0
    }
1386
0
    break;
1387
0
  case StyleAppearance::Menuitem:
1388
0
  case StyleAppearance::Checkmenuitem:
1389
0
  case StyleAppearance::Radiomenuitem:
1390
0
    // For regular menuitems, we will be using GetWidgetPadding instead of
1391
0
    // GetWidgetBorder to pad up the widget's internals; other menuitems
1392
0
    // will need to fall through and use the default case as before.
1393
0
    if (IsRegularMenuItem(aFrame))
1394
0
      break;
1395
0
    MOZ_FALLTHROUGH;
1396
0
  default:
1397
0
    {
1398
0
      GetCachedWidgetBorder(aFrame, aWidgetType, direction, &result);
1399
0
    }
1400
0
  }
1401
0
1402
0
  gint scale = GetMonitorScaleFactor(aFrame);
1403
0
  result.top *= scale;
1404
0
  result.right *= scale;
1405
0
  result.bottom *= scale;
1406
0
  result.left *= scale;
1407
0
  return result;
1408
0
}
1409
1410
bool
1411
nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
1412
                                   nsIFrame* aFrame,
1413
                                   StyleAppearance aWidgetType,
1414
                                   LayoutDeviceIntMargin* aResult)
1415
0
{
1416
0
  if (aWidgetType == StyleAppearance::MenulistButton &&
1417
0
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
1418
0
    aWidgetType = StyleAppearance::Menulist;
1419
0
  }
1420
0
1421
0
  switch (aWidgetType) {
1422
0
    case StyleAppearance::ButtonFocus:
1423
0
    case StyleAppearance::Toolbarbutton:
1424
0
    case StyleAppearance::MozWindowButtonClose:
1425
0
    case StyleAppearance::MozWindowButtonMinimize:
1426
0
    case StyleAppearance::MozWindowButtonMaximize:
1427
0
    case StyleAppearance::MozWindowButtonRestore:
1428
0
    case StyleAppearance::Dualbutton:
1429
0
    case StyleAppearance::TabScrollArrowBack:
1430
0
    case StyleAppearance::TabScrollArrowForward:
1431
0
    case StyleAppearance::MenulistButton:
1432
0
    case StyleAppearance::MozMenulistButton:
1433
0
    case StyleAppearance::ToolbarbuttonDropdown:
1434
0
    case StyleAppearance::ButtonArrowUp:
1435
0
    case StyleAppearance::ButtonArrowDown:
1436
0
    case StyleAppearance::ButtonArrowNext:
1437
0
    case StyleAppearance::ButtonArrowPrevious:
1438
0
    case StyleAppearance::RangeThumb:
1439
0
    // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1440
0
    // and have a meaningful baseline, so they can't have
1441
0
    // author-specified padding.
1442
0
    case StyleAppearance::Checkbox:
1443
0
    case StyleAppearance::Radio:
1444
0
      aResult->SizeTo(0, 0, 0, 0);
1445
0
      return true;
1446
0
    case StyleAppearance::Menuitem:
1447
0
    case StyleAppearance::Checkmenuitem:
1448
0
    case StyleAppearance::Radiomenuitem:
1449
0
      {
1450
0
        // Menubar and menulist have their padding specified in CSS.
1451
0
        if (!IsRegularMenuItem(aFrame))
1452
0
          return false;
1453
0
1454
0
        GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame),
1455
0
                              aResult);
1456
0
1457
0
        gint horizontal_padding;
1458
0
        if (aWidgetType == StyleAppearance::Menuitem)
1459
0
          moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
1460
0
        else
1461
0
          moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
1462
0
1463
0
        aResult->left += horizontal_padding;
1464
0
        aResult->right += horizontal_padding;
1465
0
1466
0
        gint scale = GetMonitorScaleFactor(aFrame);
1467
0
        aResult->top *= scale;
1468
0
        aResult->right *= scale;
1469
0
        aResult->bottom *= scale;
1470
0
        aResult->left *= scale;
1471
0
1472
0
        return true;
1473
0
      }
1474
0
    default:
1475
0
      break;
1476
0
  }
1477
0
1478
0
  return false;
1479
0
}
1480
1481
bool
1482
nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
1483
                                    nsIFrame* aFrame,
1484
                                    StyleAppearance aWidgetType,
1485
                                    nsRect* aOverflowRect)
1486
0
{
1487
0
  nsIntMargin extraSize;
1488
0
  if (!GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize))
1489
0
    return false;
1490
0
1491
0
  int32_t p2a = aContext->AppUnitsPerDevPixel();
1492
0
  nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a),
1493
0
             NSIntPixelsToAppUnits(extraSize.right, p2a),
1494
0
             NSIntPixelsToAppUnits(extraSize.bottom, p2a),
1495
0
             NSIntPixelsToAppUnits(extraSize.left, p2a));
1496
0
1497
0
  aOverflowRect->Inflate(m);
1498
0
  return true;
1499
0
}
1500
1501
NS_IMETHODIMP
1502
nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
1503
                                       nsIFrame* aFrame,
1504
                                       StyleAppearance aWidgetType,
1505
                                       LayoutDeviceIntSize* aResult,
1506
                                       bool* aIsOverridable)
1507
0
{
1508
0
  aResult->width = aResult->height = 0;
1509
0
  *aIsOverridable = true;
1510
0
1511
0
  if (aWidgetType == StyleAppearance::MenulistButton &&
1512
0
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
1513
0
    aWidgetType = StyleAppearance::Menulist;
1514
0
  }
1515
0
1516
0
  switch (aWidgetType) {
1517
0
    case StyleAppearance::ScrollbarbuttonUp:
1518
0
    case StyleAppearance::ScrollbarbuttonDown:
1519
0
      {
1520
0
        const ScrollbarGTKMetrics* metrics =
1521
0
          GetActiveScrollbarMetrics(GTK_ORIENTATION_VERTICAL);
1522
0
1523
0
        aResult->width = metrics->size.button.width;
1524
0
        aResult->height = metrics->size.button.height;
1525
0
        *aIsOverridable = false;
1526
0
      }
1527
0
      break;
1528
0
    case StyleAppearance::ScrollbarbuttonLeft:
1529
0
    case StyleAppearance::ScrollbarbuttonRight:
1530
0
      {
1531
0
        const ScrollbarGTKMetrics* metrics =
1532
0
          GetActiveScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL);
1533
0
1534
0
        aResult->width = metrics->size.button.width;
1535
0
        aResult->height = metrics->size.button.height;
1536
0
        *aIsOverridable = false;
1537
0
      }
1538
0
      break;
1539
0
    case StyleAppearance::Splitter:
1540
0
    {
1541
0
      gint metrics;
1542
0
      if (IsHorizontal(aFrame)) {
1543
0
        moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
1544
0
        aResult->width = metrics;
1545
0
        aResult->height = 0;
1546
0
      } else {
1547
0
        moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics);
1548
0
        aResult->width = 0;
1549
0
        aResult->height = metrics;
1550
0
      }
1551
0
      *aIsOverridable = false;
1552
0
    }
1553
0
    break;
1554
0
    case StyleAppearance::ScrollbarHorizontal:
1555
0
    case StyleAppearance::ScrollbarVertical:
1556
0
    {
1557
0
      /* While we enforce a minimum size for the thumb, this is ignored
1558
0
       * for the some scrollbars if buttons are hidden (bug 513006) because
1559
0
       * the thumb isn't a direct child of the scrollbar, unlike the buttons
1560
0
       * or track. So add a minimum size to the track as well to prevent a
1561
0
       * 0-width scrollbar. */
1562
0
      GtkOrientation orientation =
1563
0
        aWidgetType == StyleAppearance::ScrollbarHorizontal ?
1564
0
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1565
0
      const ScrollbarGTKMetrics* metrics =
1566
0
        GetActiveScrollbarMetrics(orientation);
1567
0
1568
0
      aResult->width = metrics->size.scrollbar.width;
1569
0
      aResult->height = metrics->size.scrollbar.height;
1570
0
    }
1571
0
    break;
1572
0
    case StyleAppearance::ScrollbarthumbVertical:
1573
0
    case StyleAppearance::ScrollbarthumbHorizontal:
1574
0
      {
1575
0
        GtkOrientation orientation =
1576
0
          aWidgetType == StyleAppearance::ScrollbarthumbHorizontal ?
1577
0
          GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1578
0
        const ScrollbarGTKMetrics* metrics =
1579
0
          GetActiveScrollbarMetrics(orientation);
1580
0
1581
0
        aResult->width = metrics->size.thumb.width;
1582
0
        aResult->height = metrics->size.thumb.height;
1583
0
        *aIsOverridable = false;
1584
0
      }
1585
0
      break;
1586
0
    case StyleAppearance::RangeThumb:
1587
0
      {
1588
0
        gint thumb_length, thumb_height;
1589
0
1590
0
        if (IsRangeHorizontal(aFrame)) {
1591
0
          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
1592
0
        } else {
1593
0
          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_length);
1594
0
        }
1595
0
        aResult->width = thumb_length;
1596
0
        aResult->height = thumb_height;
1597
0
1598
0
        *aIsOverridable = false;
1599
0
      }
1600
0
      break;
1601
0
    case StyleAppearance::Range:
1602
0
      {
1603
0
        gint scale_width, scale_height;
1604
0
1605
0
        moz_gtk_get_scale_metrics(IsRangeHorizontal(aFrame) ?
1606
0
            GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL,
1607
0
            &scale_width, &scale_height);
1608
0
        aResult->width = scale_width;
1609
0
        aResult->height = scale_height;
1610
0
1611
0
        *aIsOverridable = true;
1612
0
      }
1613
0
      break;
1614
0
    case StyleAppearance::ScalethumbHorizontal:
1615
0
    case StyleAppearance::ScalethumbVertical:
1616
0
      {
1617
0
        gint thumb_length, thumb_height;
1618
0
1619
0
        if (aWidgetType == StyleAppearance::ScalethumbVertical) {
1620
0
          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height);
1621
0
          aResult->width = thumb_height;
1622
0
          aResult->height = thumb_length;
1623
0
        } else {
1624
0
          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
1625
0
          aResult->width = thumb_length;
1626
0
          aResult->height = thumb_height;
1627
0
        }
1628
0
1629
0
        *aIsOverridable = false;
1630
0
      }
1631
0
      break;
1632
0
    case StyleAppearance::TabScrollArrowBack:
1633
0
    case StyleAppearance::TabScrollArrowForward:
1634
0
      {
1635
0
        moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
1636
0
        *aIsOverridable = false;
1637
0
      }
1638
0
      break;
1639
0
  case StyleAppearance::MenulistButton:
1640
0
  case StyleAppearance::MozMenulistButton:
1641
0
    {
1642
0
      moz_gtk_get_combo_box_entry_button_size(&aResult->width,
1643
0
                                              &aResult->height);
1644
0
      *aIsOverridable = false;
1645
0
    }
1646
0
    break;
1647
0
  case StyleAppearance::Menuseparator:
1648
0
    {
1649
0
      gint separator_height;
1650
0
1651
0
      moz_gtk_get_menu_separator_height(&separator_height);
1652
0
      aResult->height = separator_height;
1653
0
1654
0
      *aIsOverridable = false;
1655
0
    }
1656
0
    break;
1657
0
  case StyleAppearance::Checkbox:
1658
0
  case StyleAppearance::Radio:
1659
0
    {
1660
0
      const ToggleGTKMetrics* metrics = GetToggleMetrics(aWidgetType == StyleAppearance::Radio);
1661
0
      aResult->width = metrics->minSizeWithBorder.width;
1662
0
      aResult->height = metrics->minSizeWithBorder.height;
1663
0
    }
1664
0
    break;
1665
0
  case StyleAppearance::ToolbarbuttonDropdown:
1666
0
  case StyleAppearance::ButtonArrowUp:
1667
0
  case StyleAppearance::ButtonArrowDown:
1668
0
  case StyleAppearance::ButtonArrowNext:
1669
0
  case StyleAppearance::ButtonArrowPrevious:
1670
0
    {
1671
0
      moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW,
1672
0
                             &aResult->width, &aResult->height);
1673
0
      *aIsOverridable = false;
1674
0
    }
1675
0
    break;
1676
0
  case StyleAppearance::MozWindowButtonClose:
1677
0
    {
1678
0
      const ToolbarButtonGTKMetrics* metrics =
1679
0
          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
1680
0
      aResult->width = metrics->minSizeWithBorderMargin.width;
1681
0
      aResult->height = metrics->minSizeWithBorderMargin.height;
1682
0
      break;
1683
0
    }
1684
0
  case StyleAppearance::MozWindowButtonMinimize:
1685
0
    {
1686
0
      const ToolbarButtonGTKMetrics* metrics =
1687
0
          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE);
1688
0
      aResult->width = metrics->minSizeWithBorderMargin.width;
1689
0
      aResult->height = metrics->minSizeWithBorderMargin.height;
1690
0
      break;
1691
0
    }
1692
0
  case StyleAppearance::MozWindowButtonMaximize:
1693
0
  case StyleAppearance::MozWindowButtonRestore:
1694
0
    {
1695
0
      const ToolbarButtonGTKMetrics* metrics =
1696
0
          GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE);
1697
0
      aResult->width = metrics->minSizeWithBorderMargin.width;
1698
0
      aResult->height = metrics->minSizeWithBorderMargin.height;
1699
0
      break;
1700
0
    }
1701
0
  case StyleAppearance::CheckboxContainer:
1702
0
  case StyleAppearance::RadioContainer:
1703
0
  case StyleAppearance::CheckboxLabel:
1704
0
  case StyleAppearance::RadioLabel:
1705
0
  case StyleAppearance::Button:
1706
0
  case StyleAppearance::Menulist:
1707
0
  case StyleAppearance::Toolbarbutton:
1708
0
  case StyleAppearance::Treeheadercell:
1709
0
    {
1710
0
      if (aWidgetType == StyleAppearance::Menulist) {
1711
0
        // Include the arrow size.
1712
0
        moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN,
1713
0
                               &aResult->width, &aResult->height);
1714
0
      }
1715
0
      // else the minimum size is missing consideration of container
1716
0
      // descendants; the value returned here will not be helpful, but the
1717
0
      // box model may consider border and padding with child minimum sizes.
1718
0
1719
0
      LayoutDeviceIntMargin border;
1720
0
      GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame), &border);
1721
0
      aResult->width += border.left + border.right;
1722
0
      aResult->height += border.top + border.bottom;
1723
0
    }
1724
0
    break;
1725
0
#ifdef MOZ_WIDGET_GTK
1726
0
  case StyleAppearance::NumberInput:
1727
0
  case StyleAppearance::Textfield:
1728
0
    {
1729
0
      moz_gtk_get_entry_min_height(&aResult->height);
1730
0
    }
1731
0
    break;
1732
0
#endif
1733
0
  case StyleAppearance::Separator:
1734
0
    {
1735
0
      gint separator_width;
1736
0
1737
0
      moz_gtk_get_toolbar_separator_width(&separator_width);
1738
0
1739
0
      aResult->width = separator_width;
1740
0
    }
1741
0
    break;
1742
0
  case StyleAppearance::InnerSpinButton:
1743
0
  case StyleAppearance::Spinner:
1744
0
    // hard code these sizes
1745
0
    aResult->width = 14;
1746
0
    aResult->height = 26;
1747
0
    break;
1748
0
  case StyleAppearance::Treeheadersortarrow:
1749
0
  case StyleAppearance::SpinnerUpbutton:
1750
0
  case StyleAppearance::SpinnerDownbutton:
1751
0
    // hard code these sizes
1752
0
    aResult->width = 14;
1753
0
    aResult->height = 13;
1754
0
    break;
1755
0
  case StyleAppearance::Resizer:
1756
0
    // same as Windows to make our lives easier
1757
0
    aResult->width = aResult->height = 15;
1758
0
    *aIsOverridable = false;
1759
0
    break;
1760
0
  case StyleAppearance::Treetwisty:
1761
0
  case StyleAppearance::Treetwistyopen:
1762
0
    {
1763
0
      gint expander_size;
1764
0
1765
0
      moz_gtk_get_treeview_expander_size(&expander_size);
1766
0
      aResult->width = aResult->height = expander_size;
1767
0
      *aIsOverridable = false;
1768
0
    }
1769
0
    break;
1770
0
  default:
1771
0
    break;
1772
0
  }
1773
0
1774
0
  *aResult = *aResult * GetMonitorScaleFactor(aFrame);
1775
0
1776
0
  return NS_OK;
1777
0
}
1778
1779
NS_IMETHODIMP
1780
nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame,
1781
                                     StyleAppearance aWidgetType,
1782
                                     nsAtom* aAttribute, bool* aShouldRepaint,
1783
                                     const nsAttrValue* aOldValue)
1784
0
{
1785
0
  // Some widget types just never change state.
1786
0
  if (aWidgetType == StyleAppearance::Toolbox ||
1787
0
      aWidgetType == StyleAppearance::Toolbar ||
1788
0
      aWidgetType == StyleAppearance::Statusbar ||
1789
0
      aWidgetType == StyleAppearance::Statusbarpanel ||
1790
0
      aWidgetType == StyleAppearance::Resizerpanel ||
1791
0
      aWidgetType == StyleAppearance::Progresschunk ||
1792
0
      aWidgetType == StyleAppearance::ProgresschunkVertical ||
1793
0
      aWidgetType == StyleAppearance::Progressbar ||
1794
0
      aWidgetType == StyleAppearance::ProgressbarVertical ||
1795
0
      aWidgetType == StyleAppearance::Menubar ||
1796
0
      aWidgetType == StyleAppearance::Menupopup ||
1797
0
      aWidgetType == StyleAppearance::Tooltip ||
1798
0
      aWidgetType == StyleAppearance::Menuseparator ||
1799
0
      aWidgetType == StyleAppearance::Window ||
1800
0
      aWidgetType == StyleAppearance::Dialog) {
1801
0
    *aShouldRepaint = false;
1802
0
    return NS_OK;
1803
0
  }
1804
0
1805
0
  if (aWidgetType == StyleAppearance::MozWindowTitlebar ||
1806
0
      aWidgetType == StyleAppearance::MozWindowTitlebarMaximized ||
1807
0
      aWidgetType == StyleAppearance::MozWindowButtonClose ||
1808
0
      aWidgetType == StyleAppearance::MozWindowButtonMinimize ||
1809
0
      aWidgetType == StyleAppearance::MozWindowButtonMaximize ||
1810
0
      aWidgetType == StyleAppearance::MozWindowButtonRestore) {
1811
0
    *aShouldRepaint = true;
1812
0
    return NS_OK;
1813
0
  }
1814
0
1815
0
  if ((aWidgetType == StyleAppearance::ScrollbarthumbVertical ||
1816
0
       aWidgetType == StyleAppearance::ScrollbarthumbHorizontal) &&
1817
0
       aAttribute == nsGkAtoms::active) {
1818
0
    *aShouldRepaint = true;
1819
0
    return NS_OK;
1820
0
  }
1821
0
1822
0
  if ((aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
1823
0
       aWidgetType == StyleAppearance::ScrollbarbuttonDown ||
1824
0
       aWidgetType == StyleAppearance::ScrollbarbuttonLeft ||
1825
0
       aWidgetType == StyleAppearance::ScrollbarbuttonRight) &&
1826
0
      (aAttribute == nsGkAtoms::curpos ||
1827
0
       aAttribute == nsGkAtoms::maxpos)) {
1828
0
    // If 'curpos' has changed and we are passed its old value, we can
1829
0
    // determine whether the button's enablement actually needs to change.
1830
0
    if (aAttribute == nsGkAtoms::curpos && aOldValue) {
1831
0
      int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
1832
0
      int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 0);
1833
0
      nsAutoString str;
1834
0
      aOldValue->ToString(str);
1835
0
      nsresult err;
1836
0
      int32_t oldCurpos = str.ToInteger(&err);
1837
0
      if (str.IsEmpty() || NS_FAILED(err)) {
1838
0
        *aShouldRepaint = true;
1839
0
      } else {
1840
0
        bool disabledBefore = ShouldScrollbarButtonBeDisabled(oldCurpos, maxpos, aWidgetType);
1841
0
        bool disabledNow = ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType);
1842
0
        *aShouldRepaint = (disabledBefore != disabledNow);
1843
0
      }
1844
0
    } else {
1845
0
      *aShouldRepaint = true;
1846
0
    }
1847
0
    return NS_OK;
1848
0
  }
1849
0
1850
0
  // XXXdwh Not sure what can really be done here.  Can at least guess for
1851
0
  // specific widgets that they're highly unlikely to have certain states.
1852
0
  // For example, a toolbar doesn't care about any states.
1853
0
  if (!aAttribute) {
1854
0
    // Hover/focus/active changed.  Always repaint.
1855
0
    *aShouldRepaint = true;
1856
0
  }
1857
0
  else {
1858
0
    // Check the attribute to see if it's relevant.
1859
0
    // disabled, checked, dlgtype, default, etc.
1860
0
    *aShouldRepaint = false;
1861
0
    if (aAttribute == nsGkAtoms::disabled ||
1862
0
        aAttribute == nsGkAtoms::checked ||
1863
0
        aAttribute == nsGkAtoms::selected ||
1864
0
        aAttribute == nsGkAtoms::visuallyselected ||
1865
0
        aAttribute == nsGkAtoms::focused ||
1866
0
        aAttribute == nsGkAtoms::readonly ||
1867
0
        aAttribute == nsGkAtoms::_default ||
1868
0
        aAttribute == nsGkAtoms::menuactive ||
1869
0
        aAttribute == nsGkAtoms::open ||
1870
0
        aAttribute == nsGkAtoms::parentfocused)
1871
0
      *aShouldRepaint = true;
1872
0
  }
1873
0
1874
0
  return NS_OK;
1875
0
}
1876
1877
NS_IMETHODIMP
1878
nsNativeThemeGTK::ThemeChanged()
1879
0
{
1880
0
  memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
1881
0
  memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates));
1882
0
  memset(mBorderCacheValid, 0, sizeof(mBorderCacheValid));
1883
0
  return NS_OK;
1884
0
}
1885
1886
NS_IMETHODIMP_(bool)
1887
nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
1888
                                      nsIFrame* aFrame,
1889
                                      StyleAppearance aWidgetType)
1890
0
{
1891
0
  if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
1892
0
    return false;
1893
0
1894
0
  if (IsWidgetScrollbarPart(aWidgetType)) {
1895
0
    ComputedStyle* cs = nsLayoutUtils::StyleForScrollbar(aFrame);
1896
0
    if (cs->StyleUI()->HasCustomScrollbars() ||
1897
0
        // We cannot handle thin scrollbar on GTK+ widget directly as well.
1898
0
        cs->StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin) {
1899
0
      return false;
1900
0
    }
1901
0
  }
1902
0
1903
0
  if (aWidgetType == StyleAppearance::MenulistButton &&
1904
0
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
1905
0
    aWidgetType = StyleAppearance::Menulist;
1906
0
  }
1907
0
1908
0
  switch (aWidgetType) {
1909
0
  // Combobox dropdowns don't support native theming in vertical mode.
1910
0
  case StyleAppearance::Menulist:
1911
0
  case StyleAppearance::MenulistText:
1912
0
  case StyleAppearance::MenulistTextfield:
1913
0
    if (aFrame && aFrame->GetWritingMode().IsVertical()) {
1914
0
      return false;
1915
0
    }
1916
0
    MOZ_FALLTHROUGH;
1917
0
1918
0
  case StyleAppearance::Button:
1919
0
  case StyleAppearance::ButtonFocus:
1920
0
  case StyleAppearance::Radio:
1921
0
  case StyleAppearance::Checkbox:
1922
0
  case StyleAppearance::Toolbox: // N/A
1923
0
  case StyleAppearance::Toolbar:
1924
0
  case StyleAppearance::Toolbarbutton:
1925
0
  case StyleAppearance::Dualbutton: // so we can override the border with 0
1926
0
  case StyleAppearance::ToolbarbuttonDropdown:
1927
0
  case StyleAppearance::ButtonArrowUp:
1928
0
  case StyleAppearance::ButtonArrowDown:
1929
0
  case StyleAppearance::ButtonArrowNext:
1930
0
  case StyleAppearance::ButtonArrowPrevious:
1931
0
  case StyleAppearance::Separator:
1932
0
  case StyleAppearance::Toolbargripper:
1933
0
  case StyleAppearance::Statusbar:
1934
0
  case StyleAppearance::Statusbarpanel:
1935
0
  case StyleAppearance::Resizerpanel:
1936
0
  case StyleAppearance::Resizer:
1937
0
  case StyleAppearance::Listbox:
1938
0
    // case StyleAppearance::Listitem:
1939
0
  case StyleAppearance::Treeview:
1940
0
    // case StyleAppearance::Treeitem:
1941
0
  case StyleAppearance::Treetwisty:
1942
0
    // case StyleAppearance::Treeline:
1943
0
    // case StyleAppearance::Treeheader:
1944
0
  case StyleAppearance::Treeheadercell:
1945
0
  case StyleAppearance::Treeheadersortarrow:
1946
0
  case StyleAppearance::Treetwistyopen:
1947
0
    case StyleAppearance::Progressbar:
1948
0
    case StyleAppearance::Progresschunk:
1949
0
    case StyleAppearance::ProgressbarVertical:
1950
0
    case StyleAppearance::ProgresschunkVertical:
1951
0
    case StyleAppearance::Tab:
1952
0
    // case StyleAppearance::Tabpanel:
1953
0
    case StyleAppearance::Tabpanels:
1954
0
    case StyleAppearance::TabScrollArrowBack:
1955
0
    case StyleAppearance::TabScrollArrowForward:
1956
0
  case StyleAppearance::Tooltip:
1957
0
  case StyleAppearance::InnerSpinButton:
1958
0
  case StyleAppearance::Spinner:
1959
0
  case StyleAppearance::SpinnerUpbutton:
1960
0
  case StyleAppearance::SpinnerDownbutton:
1961
0
  case StyleAppearance::SpinnerTextfield:
1962
0
    // case StyleAppearance::Scrollbar:  (n/a for gtk)
1963
0
    // case StyleAppearance::ScrollbarSmall: (n/a for gtk)
1964
0
  case StyleAppearance::ScrollbarbuttonUp:
1965
0
  case StyleAppearance::ScrollbarbuttonDown:
1966
0
  case StyleAppearance::ScrollbarbuttonLeft:
1967
0
  case StyleAppearance::ScrollbarbuttonRight:
1968
0
  case StyleAppearance::ScrollbarHorizontal:
1969
0
  case StyleAppearance::ScrollbarVertical:
1970
0
  case StyleAppearance::ScrollbartrackHorizontal:
1971
0
  case StyleAppearance::ScrollbartrackVertical:
1972
0
  case StyleAppearance::ScrollbarthumbHorizontal:
1973
0
  case StyleAppearance::ScrollbarthumbVertical:
1974
0
  case StyleAppearance::NumberInput:
1975
0
  case StyleAppearance::Textfield:
1976
0
  case StyleAppearance::TextfieldMultiline:
1977
0
  case StyleAppearance::Range:
1978
0
  case StyleAppearance::RangeThumb:
1979
0
  case StyleAppearance::ScaleHorizontal:
1980
0
  case StyleAppearance::ScalethumbHorizontal:
1981
0
  case StyleAppearance::ScaleVertical:
1982
0
  case StyleAppearance::ScalethumbVertical:
1983
0
    // case StyleAppearance::Scalethumbstart:
1984
0
    // case StyleAppearance::Scalethumbend:
1985
0
    // case StyleAppearance::Scalethumbtick:
1986
0
  case StyleAppearance::CheckboxContainer:
1987
0
  case StyleAppearance::RadioContainer:
1988
0
  case StyleAppearance::CheckboxLabel:
1989
0
  case StyleAppearance::RadioLabel:
1990
0
  case StyleAppearance::Menubar:
1991
0
  case StyleAppearance::Menupopup:
1992
0
  case StyleAppearance::Menuitem:
1993
0
  case StyleAppearance::Menuarrow:
1994
0
  case StyleAppearance::Menuseparator:
1995
0
  case StyleAppearance::Checkmenuitem:
1996
0
  case StyleAppearance::Radiomenuitem:
1997
0
  case StyleAppearance::Splitter:
1998
0
  case StyleAppearance::Window:
1999
0
  case StyleAppearance::Dialog:
2000
0
#ifdef MOZ_WIDGET_GTK
2001
0
  case StyleAppearance::MozGtkInfoBar:
2002
0
#endif
2003
0
    return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
2004
0
2005
0
  case StyleAppearance::MozWindowButtonClose:
2006
0
  case StyleAppearance::MozWindowButtonMinimize:
2007
0
  case StyleAppearance::MozWindowButtonMaximize:
2008
0
  case StyleAppearance::MozWindowButtonRestore:
2009
0
  case StyleAppearance::MozWindowTitlebar:
2010
0
  case StyleAppearance::MozWindowTitlebarMaximized:
2011
0
    // GtkHeaderBar is available on GTK 3.10+, which is used for styling
2012
0
    // title bars and title buttons.
2013
0
    return gtk_check_version(3, 10, 0) == nullptr &&
2014
0
           !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
2015
0
2016
0
  case StyleAppearance::MenulistButton:
2017
0
  case StyleAppearance::MozMenulistButton:
2018
0
    if (aFrame && aFrame->GetWritingMode().IsVertical()) {
2019
0
      return false;
2020
0
    }
2021
0
    // "Native" dropdown buttons cause padding and margin problems, but only
2022
0
    // in HTML so allow them in XUL.
2023
0
    return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
2024
0
           !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
2025
0
2026
0
  case StyleAppearance::FocusOutline:
2027
0
    return true;
2028
0
  default:
2029
0
    break;
2030
0
  }
2031
0
2032
0
  return false;
2033
0
}
2034
2035
NS_IMETHODIMP_(bool)
2036
nsNativeThemeGTK::WidgetIsContainer(StyleAppearance aWidgetType)
2037
0
{
2038
0
  if (aWidgetType == StyleAppearance::MenulistButton &&
2039
0
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
2040
0
    aWidgetType = StyleAppearance::Menulist;
2041
0
  }
2042
0
2043
0
  // XXXdwh At some point flesh all of this out.
2044
0
  if (aWidgetType == StyleAppearance::MenulistButton ||
2045
0
      aWidgetType == StyleAppearance::MozMenulistButton ||
2046
0
      aWidgetType == StyleAppearance::Radio ||
2047
0
      aWidgetType == StyleAppearance::RangeThumb ||
2048
0
      aWidgetType == StyleAppearance::Checkbox ||
2049
0
      aWidgetType == StyleAppearance::TabScrollArrowBack ||
2050
0
      aWidgetType == StyleAppearance::TabScrollArrowForward ||
2051
0
      aWidgetType == StyleAppearance::ButtonArrowUp ||
2052
0
      aWidgetType == StyleAppearance::ButtonArrowDown ||
2053
0
      aWidgetType == StyleAppearance::ButtonArrowNext ||
2054
0
      aWidgetType == StyleAppearance::ButtonArrowPrevious)
2055
0
    return false;
2056
0
  return true;
2057
0
}
2058
2059
bool
2060
nsNativeThemeGTK::ThemeDrawsFocusForWidget(StyleAppearance aWidgetType)
2061
0
{
2062
0
  if (aWidgetType == StyleAppearance::MenulistButton &&
2063
0
      StaticPrefs::layout_css_webkit_appearance_enabled()) {
2064
0
    aWidgetType = StyleAppearance::Menulist;
2065
0
  }
2066
0
2067
0
  if (aWidgetType == StyleAppearance::Menulist ||
2068
0
      aWidgetType == StyleAppearance::Button ||
2069
0
      aWidgetType == StyleAppearance::Treeheadercell)
2070
0
    return true;
2071
0
2072
0
  return false;
2073
0
}
2074
2075
bool
2076
nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
2077
0
{
2078
0
  return false;
2079
0
}
2080
2081
nsITheme::Transparency
2082
nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame,
2083
                                        StyleAppearance aWidgetType)
2084
0
{
2085
0
  switch (aWidgetType) {
2086
0
  // These widgets always draw a default background.
2087
0
  case StyleAppearance::Menupopup:
2088
0
  case StyleAppearance::Window:
2089
0
  case StyleAppearance::Dialog:
2090
0
    return eOpaque;
2091
0
  case StyleAppearance::ScrollbarVertical:
2092
0
  case StyleAppearance::ScrollbarHorizontal:
2093
0
#ifdef MOZ_WIDGET_GTK
2094
0
    // Make scrollbar tracks opaque on the window's scroll frame to prevent
2095
0
    // leaf layers from overlapping. See bug 1179780.
2096
0
    if (!(CheckBooleanAttr(aFrame, nsGkAtoms::root_) &&
2097
0
          aFrame->PresContext()->IsRootContentDocument() &&
2098
0
          IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)))
2099
0
      return eTransparent;
2100
0
#endif
2101
0
    return eOpaque;
2102
0
  // Tooltips use gtk_paint_flat_box() on Gtk2
2103
0
  // but are shaped on Gtk3
2104
0
  case StyleAppearance::Tooltip:
2105
0
    return eTransparent;
2106
0
  default:
2107
0
    return eUnknownTransparency;
2108
0
  }
2109
0
2110
0
}
2111
2112
bool
2113
nsNativeThemeGTK::WidgetAppearanceDependsOnWindowFocus(StyleAppearance aWidgetType)
2114
{
2115
  switch (aWidgetType) {
2116
    case StyleAppearance::MozWindowTitlebar:
2117
    case StyleAppearance::MozWindowTitlebarMaximized:
2118
    case StyleAppearance::MozWindowButtonClose:
2119
    case StyleAppearance::MozWindowButtonMinimize:
2120
    case StyleAppearance::MozWindowButtonMaximize:
2121
    case StyleAppearance::MozWindowButtonRestore:
2122
      return true;
2123
    default:
2124
      return false;
2125
  }
2126
}
2127
2128
already_AddRefed<nsITheme>
2129
do_GetNativeTheme()
2130
0
{
2131
0
  if (gDisableNativeTheme) {
2132
0
    return nullptr;
2133
0
  }
2134
0
2135
0
  static nsCOMPtr<nsITheme> inst;
2136
0
2137
0
  if (!inst) {
2138
0
    if (gfxPlatform::IsHeadless()) {
2139
0
      inst = new HeadlessThemeGTK();
2140
0
    } else {
2141
0
      inst = new nsNativeThemeGTK();
2142
0
    }
2143
0
    ClearOnShutdown(&inst);
2144
0
  }
2145
0
2146
0
  return do_AddRef(inst);
2147
0
}