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