forked from mirrors/gecko-dev
		
	 9daee9a3a5
			
		
	
	
		9daee9a3a5
		
	
	
	
	
		
			
			When any scrollbar color is specified, or scrollbar-width is thin, we switch to use the fallback rendering. The change to xulscrollbars.css is for ensuring that the scrollbar is displayed for scrollbar-width: thin when there is no scrollbar color specified. It wouldn't affect cases where -moz-appearance takes effect. This also changes the fallback width of the scrollbars. Since the two widths was picked rather randomly, I think it should be fine to change it if a value looks better than the old one, especially on Linux which is the main usecase for this fallback rendering. Differential Revision: https://phabricator.services.mozilla.com/D3952 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			2085 lines
		
	
	
	
		
			72 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2085 lines
		
	
	
	
		
			72 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsNativeThemeGTK.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "gtkdrawing.h"
 | |
| #include "ScreenHelperGTK.h"
 | |
| 
 | |
| #include "gfx2DGlue.h"
 | |
| #include "nsIObserverService.h"
 | |
| #include "nsIServiceManager.h"
 | |
| #include "nsIFrame.h"
 | |
| #include "nsIPresShell.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsViewManager.h"
 | |
| #include "nsNameSpaceManager.h"
 | |
| #include "nsGfxCIID.h"
 | |
| #include "nsTransform2D.h"
 | |
| #include "nsMenuFrame.h"
 | |
| #include "tree/nsTreeBodyFrame.h"
 | |
| #include "prlink.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsAttrValueInlines.h"
 | |
| 
 | |
| #include "mozilla/dom/HTMLInputElement.h"
 | |
| #include "mozilla/EventStates.h"
 | |
| #include "mozilla/Services.h"
 | |
| 
 | |
| #include <gdk/gdkprivate.h>
 | |
| #include <gtk/gtk.h>
 | |
| #include <gtk/gtkx.h>
 | |
| 
 | |
| #include "gfxContext.h"
 | |
| #include "gfxPlatformGtk.h"
 | |
| #include "gfxGdkNativeRenderer.h"
 | |
| #include "mozilla/gfx/BorrowedContext.h"
 | |
| #include "mozilla/gfx/HelpersCairo.h"
 | |
| #include "mozilla/gfx/PathHelpers.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/layers/StackingContextHelper.h"
 | |
| #include "mozilla/StaticPrefs.h"
 | |
| 
 | |
| #ifdef MOZ_X11
 | |
| #  ifdef CAIRO_HAS_XLIB_SURFACE
 | |
| #    include "cairo-xlib.h"
 | |
| #  endif
 | |
| #  ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
 | |
| #    include "cairo-xlib-xrender.h"
 | |
| #  endif
 | |
| #endif
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <dlfcn.h>
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::gfx;
 | |
| using namespace mozilla::widget;
 | |
| using mozilla::dom::HTMLInputElement;
 | |
| 
 | |
| NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
 | |
|                                                              nsIObserver)
 | |
| 
 | |
| static int gLastGdkError;
 | |
| 
 | |
| // Return scale factor of the monitor where the window is located
 | |
| // by the most part or layout.css.devPixelsPerPx pref if set to > 0.
 | |
| static inline gint
 | |
| GetMonitorScaleFactor(nsIFrame* aFrame)
 | |
| {
 | |
|   // When the layout.css.devPixelsPerPx is set the scale can be < 1,
 | |
|   // the real monitor scale cannot go under 1.
 | |
|   double scale = nsIWidget::DefaultScaleOverride();
 | |
|   if (scale <= 0) {
 | |
|     nsIWidget* rootWidget = aFrame->PresContext()->GetRootWidget();
 | |
|     if (rootWidget) {
 | |
|         // We need to use GetDefaultScale() despite it returns monitor scale
 | |
|         // factor multiplied by font scale factor because it is the only scale
 | |
|         // updated in nsPuppetWidget.
 | |
|         // Since we don't want to apply font scale factor for UI elements
 | |
|         // (because GTK does not do so) we need to remove that from returned value.
 | |
|         // The computed monitor scale factor needs to be rounded before casting to
 | |
|         // integer to avoid rounding errors which would lead to returning 0.
 | |
|         int monitorScale = int(round(rootWidget->GetDefaultScale().scale
 | |
|               / gfxPlatformGtk::GetFontScaleFactor()));
 | |
|         // Monitor scale can be negative if it has not been initialized in the
 | |
|         // puppet widget yet. We also make sure that we return positive value.
 | |
|         if (monitorScale < 1) {
 | |
|           return 1;
 | |
|         }
 | |
|         return monitorScale;
 | |
|     }
 | |
|   }
 | |
|   // Use monitor scaling factor where devPixelsPerPx is set
 | |
|   return ScreenHelperGTK::GetGTKMonitorScaleFactor();
 | |
| }
 | |
| 
 | |
| nsNativeThemeGTK::nsNativeThemeGTK()
 | |
| {
 | |
|   if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
 | |
|     memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // We have to call moz_gtk_shutdown before the event loop stops running.
 | |
|   nsCOMPtr<nsIObserverService> obsServ =
 | |
|     mozilla::services::GetObserverService();
 | |
|   obsServ->AddObserver(this, "xpcom-shutdown", false);
 | |
| 
 | |
|   ThemeChanged();
 | |
| }
 | |
| 
 | |
| nsNativeThemeGTK::~nsNativeThemeGTK() {
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
 | |
|                           const char16_t *aData)
 | |
| {
 | |
|   if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
 | |
|     moz_gtk_shutdown();
 | |
|   } else {
 | |
|     MOZ_ASSERT_UNREACHABLE("unexpected topic");
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
 | |
| {
 | |
|   nsIPresShell *shell = GetPresShell(aFrame);
 | |
|   if (!shell)
 | |
|     return;
 | |
| 
 | |
|   nsViewManager* vm = shell->GetViewManager();
 | |
|   if (!vm)
 | |
|     return;
 | |
| 
 | |
|   vm->InvalidateAllViews();
 | |
| }
 | |
| 
 | |
| 
 | |
| static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace)
 | |
| {
 | |
|   nsIContent *content = aFrame ? aFrame->GetContent() : nullptr;
 | |
|   if (!content)
 | |
|     return false;
 | |
|   return content->IsInNamespace(aNamespace);
 | |
| }
 | |
| 
 | |
| static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, StyleAppearance aWidgetType) {
 | |
|   auto type = static_cast<size_t>(aWidgetType);
 | |
|   MOZ_ASSERT(type < static_cast<size_t>(mozilla::StyleAppearance::Count));
 | |
|   return (aDisabledVector[type >> 3] & (1 << (type & 7))) != 0;
 | |
| }
 | |
| 
 | |
| static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, StyleAppearance aWidgetType) {
 | |
|   auto type = static_cast<size_t>(aWidgetType);
 | |
|   MOZ_ASSERT(type < static_cast<size_t>(mozilla::StyleAppearance::Count));
 | |
|   aDisabledVector[type >> 3] |= (1 << (type & 7));
 | |
| }
 | |
| 
 | |
| static inline uint16_t
 | |
| GetWidgetStateKey(StyleAppearance aWidgetType, GtkWidgetState *aWidgetState)
 | |
| {
 | |
|   return (aWidgetState->active |
 | |
|           aWidgetState->focused << 1 |
 | |
|           aWidgetState->inHover << 2 |
 | |
|           aWidgetState->disabled << 3 |
 | |
|           aWidgetState->isDefault << 4 |
 | |
|           static_cast<uint16_t>(aWidgetType) << 5);
 | |
| }
 | |
| 
 | |
| static bool IsWidgetStateSafe(uint8_t* aSafeVector,
 | |
|                               StyleAppearance aWidgetType,
 | |
|                               GtkWidgetState *aWidgetState)
 | |
| {
 | |
|   MOZ_ASSERT(static_cast<size_t>(aWidgetType) < static_cast<size_t>(mozilla::StyleAppearance::Count));
 | |
|   uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
 | |
|   return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0;
 | |
| }
 | |
| 
 | |
| static void SetWidgetStateSafe(uint8_t *aSafeVector,
 | |
|                                StyleAppearance aWidgetType,
 | |
|                                GtkWidgetState *aWidgetState)
 | |
| {
 | |
|   MOZ_ASSERT(static_cast<size_t>(aWidgetType) < static_cast<size_t>(mozilla::StyleAppearance::Count));
 | |
|   uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
 | |
|   aSafeVector[key >> 3] |= (1 << (key & 7));
 | |
| }
 | |
| 
 | |
| /* static */ GtkTextDirection
 | |
| nsNativeThemeGTK::GetTextDirection(nsIFrame* aFrame)
 | |
| {
 | |
|   // IsFrameRTL() treats vertical-rl modes as right-to-left (in addition to
 | |
|   // horizontal text with direction=RTL), rather than just considering the
 | |
|   // text direction.  GtkTextDirection does not have distinct values for
 | |
|   // vertical writing modes, but considering the block flow direction is
 | |
|   // important for resizers and scrollbar elements, at least.
 | |
|   return IsFrameRTL(aFrame) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
 | |
| }
 | |
| 
 | |
| // Returns positive for negative margins (otherwise 0).
 | |
| gint
 | |
| nsNativeThemeGTK::GetTabMarginPixels(nsIFrame* aFrame)
 | |
| {
 | |
|   nscoord margin =
 | |
|     IsBottomTab(aFrame) ? aFrame->GetUsedMargin().top
 | |
|     : aFrame->GetUsedMargin().bottom;
 | |
| 
 | |
|   return std::min<gint>(MOZ_GTK_TAB_MARGIN_MASK,
 | |
|                 std::max(0,
 | |
|                        aFrame->PresContext()->AppUnitsToDevPixels(-margin)));
 | |
| }
 | |
| 
 | |
| static bool ShouldScrollbarButtonBeDisabled(int32_t aCurpos, int32_t aMaxpos,
 | |
|                                             StyleAppearance aWidgetType)
 | |
| {
 | |
|   return ((aCurpos == 0 && (aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
 | |
|                             aWidgetType == StyleAppearance::ScrollbarbuttonLeft))
 | |
|       || (aCurpos == aMaxpos && (aWidgetType == StyleAppearance::ScrollbarbuttonDown ||
 | |
|                                  aWidgetType == StyleAppearance::ScrollbarbuttonRight)));
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aWidgetType, nsIFrame* aFrame,
 | |
|                                        WidgetNodeType& aGtkWidgetType,
 | |
|                                        GtkWidgetState* aState,
 | |
|                                        gint* aWidgetFlags)
 | |
| {
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton &&
 | |
|       StaticPrefs::layout_css_webkit_appearance_enabled()) {
 | |
|     aWidgetType = StyleAppearance::Menulist;
 | |
|   }
 | |
| 
 | |
|   if (aState) {
 | |
|     // For XUL checkboxes and radio buttons, the state of the parent
 | |
|     // determines our state.
 | |
|     nsIFrame *stateFrame = aFrame;
 | |
|     if (aFrame && ((aWidgetFlags && (aWidgetType == StyleAppearance::Checkbox ||
 | |
|                                      aWidgetType == StyleAppearance::Radio)) ||
 | |
|                    aWidgetType == StyleAppearance::CheckboxLabel ||
 | |
|                    aWidgetType == StyleAppearance::RadioLabel)) {
 | |
| 
 | |
|       nsAtom* atom = nullptr;
 | |
|       if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
 | |
|         if (aWidgetType == StyleAppearance::CheckboxLabel ||
 | |
|             aWidgetType == StyleAppearance::RadioLabel) {
 | |
|           // Adjust stateFrame so GetContentState finds the correct state.
 | |
|           stateFrame = aFrame = aFrame->GetParent()->GetParent();
 | |
|         } else {
 | |
|           // GetContentState knows to look one frame up for radio/checkbox
 | |
|           // widgets, so don't adjust stateFrame here.
 | |
|           aFrame = aFrame->GetParent();
 | |
|         }
 | |
|         if (aWidgetFlags) {
 | |
|           if (!atom) {
 | |
|             atom = (aWidgetType == StyleAppearance::Checkbox ||
 | |
|                     aWidgetType == StyleAppearance::CheckboxLabel) ? nsGkAtoms::checked
 | |
|                                                             : nsGkAtoms::selected;
 | |
|           }
 | |
|           *aWidgetFlags = CheckBooleanAttr(aFrame, atom);
 | |
|         }
 | |
|       } else {
 | |
|         if (aWidgetFlags) {
 | |
|           *aWidgetFlags = 0;
 | |
|           HTMLInputElement* inputElt = HTMLInputElement::FromNode(aFrame->GetContent());
 | |
|           if (inputElt && inputElt->Checked())
 | |
|             *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
 | |
| 
 | |
|           if (GetIndeterminate(aFrame))
 | |
|             *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
 | |
|         }
 | |
|       }
 | |
|     } else if (aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
 | |
|                aWidgetType == StyleAppearance::Treeheadersortarrow ||
 | |
|                aWidgetType == StyleAppearance::ButtonArrowPrevious ||
 | |
|                aWidgetType == StyleAppearance::ButtonArrowNext ||
 | |
|                aWidgetType == StyleAppearance::ButtonArrowUp ||
 | |
|                aWidgetType == StyleAppearance::ButtonArrowDown) {
 | |
|       // The state of an arrow comes from its parent.
 | |
|       stateFrame = aFrame = aFrame->GetParent();
 | |
|     }
 | |
| 
 | |
|     EventStates eventState = GetContentState(stateFrame, aWidgetType);
 | |
| 
 | |
|     aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame);
 | |
|     aState->active  = eventState.HasState(NS_EVENT_STATE_ACTIVE);
 | |
|     aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS);
 | |
|     aState->selected = FALSE;
 | |
|     aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER);
 | |
|     aState->isDefault = IsDefaultButton(aFrame);
 | |
|     aState->canDefault = FALSE; // XXX fix me
 | |
|     aState->depressed = FALSE;
 | |
| 
 | |
|     if (aWidgetType == StyleAppearance::FocusOutline) {
 | |
|       aState->disabled = FALSE;
 | |
|       aState->active  = FALSE;
 | |
|       aState->inHover = FALSE;
 | |
|       aState->isDefault = FALSE;
 | |
|       aState->canDefault = FALSE;
 | |
| 
 | |
|       aState->focused = TRUE;
 | |
|       aState->depressed = TRUE; // see moz_gtk_entry_paint()
 | |
|     } else if (aWidgetType == StyleAppearance::Button ||
 | |
|                aWidgetType == StyleAppearance::Toolbarbutton ||
 | |
|                aWidgetType == StyleAppearance::Dualbutton ||
 | |
|                aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
 | |
|                aWidgetType == StyleAppearance::Menulist ||
 | |
|                aWidgetType == StyleAppearance::MenulistButton ||
 | |
|                aWidgetType == StyleAppearance::MozMenulistButton) {
 | |
|       aState->active &= aState->inHover;
 | |
|     } else if (aWidgetType == StyleAppearance::Treetwisty ||
 | |
|                aWidgetType == StyleAppearance::Treetwistyopen) {
 | |
|       nsTreeBodyFrame *treeBodyFrame = do_QueryFrame(aFrame);
 | |
|       if (treeBodyFrame) {
 | |
|         const mozilla::AtomArray& atoms =
 | |
|           treeBodyFrame->GetPropertyArrayForCurrentDrawingItem();
 | |
|         aState->selected = atoms.Contains(nsGkAtoms::selected);
 | |
|         aState->inHover = atoms.Contains(nsGkAtoms::hover);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
 | |
|       // For these widget types, some element (either a child or parent)
 | |
|       // actually has element focus, so we check the focused attribute
 | |
|       // to see whether to draw in the focused state.
 | |
|       if (aWidgetType == StyleAppearance::NumberInput ||
 | |
|           aWidgetType == StyleAppearance::Textfield ||
 | |
|           aWidgetType == StyleAppearance::TextfieldMultiline ||
 | |
|           aWidgetType == StyleAppearance::MenulistTextfield ||
 | |
|           aWidgetType == StyleAppearance::SpinnerTextfield ||
 | |
|           aWidgetType == StyleAppearance::RadioContainer ||
 | |
|           aWidgetType == StyleAppearance::RadioLabel) {
 | |
|         aState->focused = IsFocused(aFrame);
 | |
|       } else if (aWidgetType == StyleAppearance::Radio ||
 | |
|                  aWidgetType == StyleAppearance::Checkbox) {
 | |
|         // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
 | |
|         aState->focused = FALSE;
 | |
|       }
 | |
| 
 | |
|       if (aWidgetType == StyleAppearance::ScrollbarthumbVertical ||
 | |
|           aWidgetType == StyleAppearance::ScrollbarthumbHorizontal) {
 | |
|         // for scrollbars we need to go up two to go from the thumb to
 | |
|         // the slider to the actual scrollbar object
 | |
|         nsIFrame *tmpFrame = aFrame->GetParent()->GetParent();
 | |
| 
 | |
|         aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0);
 | |
|         aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100);
 | |
| 
 | |
|         if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) {
 | |
|           aState->active = TRUE;
 | |
|           // Set hover state to emulate Gtk style of active scrollbar thumb
 | |
|           aState->inHover = TRUE;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
 | |
|           aWidgetType == StyleAppearance::ScrollbarbuttonDown ||
 | |
|           aWidgetType == StyleAppearance::ScrollbarbuttonLeft ||
 | |
|           aWidgetType == StyleAppearance::ScrollbarbuttonRight) {
 | |
|         // set the state to disabled when the scrollbar is scrolled to
 | |
|         // the beginning or the end, depending on the button type.
 | |
|         int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
 | |
|         int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
 | |
|         if (ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType)) {
 | |
|           aState->disabled = true;
 | |
|         }
 | |
| 
 | |
|         // In order to simulate native GTK scrollbar click behavior,
 | |
|         // we set the active attribute on the element to true if it's
 | |
|         // pressed with any mouse button.
 | |
|         // This allows us to show that it's active without setting :active
 | |
|         else if (CheckBooleanAttr(aFrame, nsGkAtoms::active))
 | |
|           aState->active = true;
 | |
| 
 | |
|         if (aWidgetFlags) {
 | |
|           *aWidgetFlags = GetScrollbarButtonType(aFrame);
 | |
|           if (static_cast<uint8_t>(aWidgetType) -
 | |
|                 static_cast<uint8_t>(StyleAppearance::ScrollbarbuttonUp) < 2)
 | |
|             *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // menu item state is determined by the attribute "_moz-menuactive",
 | |
|       // and not by the mouse hovering (accessibility).  as a special case,
 | |
|       // menus which are children of a menu bar are only marked as prelight
 | |
|       // if they are open, not on normal hover.
 | |
| 
 | |
|       if (aWidgetType == StyleAppearance::Menuitem ||
 | |
|           aWidgetType == StyleAppearance::Checkmenuitem ||
 | |
|           aWidgetType == StyleAppearance::Radiomenuitem ||
 | |
|           aWidgetType == StyleAppearance::Menuseparator ||
 | |
|           aWidgetType == StyleAppearance::Menuarrow) {
 | |
|         bool isTopLevel = false;
 | |
|         nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
 | |
|         if (menuFrame) {
 | |
|           isTopLevel = menuFrame->IsOnMenuBar();
 | |
|         }
 | |
| 
 | |
|         if (isTopLevel) {
 | |
|           aState->inHover = menuFrame->IsOpen();
 | |
|         } else {
 | |
|           aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
 | |
|         }
 | |
| 
 | |
|         aState->active = FALSE;
 | |
| 
 | |
|         if (aWidgetType == StyleAppearance::Checkmenuitem ||
 | |
|             aWidgetType == StyleAppearance::Radiomenuitem) {
 | |
|           *aWidgetFlags = 0;
 | |
|           if (aFrame && aFrame->GetContent() &&
 | |
|               aFrame->GetContent()->IsElement()) {
 | |
|             *aWidgetFlags = aFrame->GetContent()->AsElement()->
 | |
|               AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
 | |
|                           nsGkAtoms::_true, eIgnoreCase);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // A button with drop down menu open or an activated toggle button
 | |
|       // should always appear depressed.
 | |
|       if (aWidgetType == StyleAppearance::Button ||
 | |
|           aWidgetType == StyleAppearance::Toolbarbutton ||
 | |
|           aWidgetType == StyleAppearance::Dualbutton ||
 | |
|           aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
 | |
|           aWidgetType == StyleAppearance::Menulist ||
 | |
|           aWidgetType == StyleAppearance::MenulistButton ||
 | |
|           aWidgetType == StyleAppearance::MozMenulistButton) {
 | |
|         bool menuOpen = IsOpenButton(aFrame);
 | |
|         aState->depressed = IsCheckedButton(aFrame) || menuOpen;
 | |
|         // we must not highlight buttons with open drop down menus on hover.
 | |
|         aState->inHover = aState->inHover && !menuOpen;
 | |
|       }
 | |
| 
 | |
|       // When the input field of the drop down button has focus, some themes
 | |
|       // should draw focus for the drop down button as well.
 | |
|       if ((aWidgetType == StyleAppearance::MenulistButton ||
 | |
|            aWidgetType == StyleAppearance::MozMenulistButton) &&
 | |
|           aWidgetFlags) {
 | |
|         *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   switch (aWidgetType) {
 | |
|   case StyleAppearance::Button:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_RELIEF_NORMAL;
 | |
|     aGtkWidgetType = MOZ_GTK_BUTTON;
 | |
|     break;
 | |
|   case StyleAppearance::Toolbarbutton:
 | |
|   case StyleAppearance::Dualbutton:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_RELIEF_NONE;
 | |
|     aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON;
 | |
|     break;
 | |
|   case StyleAppearance::FocusOutline:
 | |
|     aGtkWidgetType = MOZ_GTK_ENTRY;
 | |
|     break;
 | |
|   case StyleAppearance::Checkbox:
 | |
|   case StyleAppearance::Radio:
 | |
|     aGtkWidgetType = (aWidgetType == StyleAppearance::Radio) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbarbuttonUp:
 | |
|   case StyleAppearance::ScrollbarbuttonDown:
 | |
|   case StyleAppearance::ScrollbarbuttonLeft:
 | |
|   case StyleAppearance::ScrollbarbuttonRight:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbarVertical:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_VERTICAL;
 | |
|     if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque)
 | |
|         *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE;
 | |
|     else
 | |
|         *aWidgetFlags = 0;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbarHorizontal:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_HORIZONTAL;
 | |
|     if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque)
 | |
|         *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE;
 | |
|     else
 | |
|         *aWidgetFlags = 0;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbartrackHorizontal:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbartrackVertical:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbarthumbVertical:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbarthumbHorizontal:
 | |
|     aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
 | |
|     break;
 | |
|   case StyleAppearance::InnerSpinButton:
 | |
|     aGtkWidgetType = MOZ_GTK_INNER_SPIN_BUTTON;
 | |
|     break;
 | |
|   case StyleAppearance::Spinner:
 | |
|     aGtkWidgetType = MOZ_GTK_SPINBUTTON;
 | |
|     break;
 | |
|   case StyleAppearance::SpinnerUpbutton:
 | |
|     aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP;
 | |
|     break;
 | |
|   case StyleAppearance::SpinnerDownbutton:
 | |
|     aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN;
 | |
|     break;
 | |
|   case StyleAppearance::SpinnerTextfield:
 | |
|     aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY;
 | |
|     break;
 | |
|   case StyleAppearance::Range:
 | |
|     {
 | |
|       if (IsRangeHorizontal(aFrame)) {
 | |
|         if (aWidgetFlags)
 | |
|           *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
 | |
|         aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
 | |
|       } else {
 | |
|         if (aWidgetFlags)
 | |
|           *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
 | |
|         aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|   case StyleAppearance::RangeThumb:
 | |
|     {
 | |
|       if (IsRangeHorizontal(aFrame)) {
 | |
|         if (aWidgetFlags)
 | |
|           *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
 | |
|         aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
 | |
|       } else {
 | |
|         if (aWidgetFlags)
 | |
|           *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
 | |
|         aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|   case StyleAppearance::ScaleHorizontal:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
 | |
|     aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
 | |
|     break;
 | |
|   case StyleAppearance::ScalethumbHorizontal:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
 | |
|     aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
 | |
|     break;
 | |
|   case StyleAppearance::ScaleVertical:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
 | |
|     aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
 | |
|     break;
 | |
|   case StyleAppearance::Separator:
 | |
|     aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR;
 | |
|     break;
 | |
|   case StyleAppearance::ScalethumbVertical:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
 | |
|     aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
 | |
|     break;
 | |
|   case StyleAppearance::Toolbargripper:
 | |
|     aGtkWidgetType = MOZ_GTK_GRIPPER;
 | |
|     break;
 | |
|   case StyleAppearance::Resizer:
 | |
|     aGtkWidgetType = MOZ_GTK_RESIZER;
 | |
|     break;
 | |
|   case StyleAppearance::NumberInput:
 | |
|   case StyleAppearance::Textfield:
 | |
|     aGtkWidgetType = MOZ_GTK_ENTRY;
 | |
|     break;
 | |
|   case StyleAppearance::TextfieldMultiline:
 | |
| #ifdef MOZ_WIDGET_GTK
 | |
|     aGtkWidgetType = MOZ_GTK_TEXT_VIEW;
 | |
| #else
 | |
|     aGtkWidgetType = MOZ_GTK_ENTRY;
 | |
| #endif
 | |
|     break;
 | |
|   case StyleAppearance::Listbox:
 | |
|   case StyleAppearance::Treeview:
 | |
|     aGtkWidgetType = MOZ_GTK_TREEVIEW;
 | |
|     break;
 | |
|   case StyleAppearance::Treeheadercell:
 | |
|     if (aWidgetFlags) {
 | |
|       // In this case, the flag denotes whether the header is the sorted one or not
 | |
|       if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural)
 | |
|         *aWidgetFlags = false;
 | |
|       else
 | |
|         *aWidgetFlags = true;
 | |
|     }
 | |
|     aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL;
 | |
|     break;
 | |
|   case StyleAppearance::Treeheadersortarrow:
 | |
|     if (aWidgetFlags) {
 | |
|       switch (GetTreeSortDirection(aFrame)) {
 | |
|         case eTreeSortDirection_Ascending:
 | |
|           *aWidgetFlags = GTK_ARROW_DOWN;
 | |
|           break;
 | |
|         case eTreeSortDirection_Descending:
 | |
|           *aWidgetFlags = GTK_ARROW_UP;
 | |
|           break;
 | |
|         case eTreeSortDirection_Natural:
 | |
|         default:
 | |
|           /* This prevents the treecolums from getting smaller
 | |
|            * and wider when switching sort direction off and on
 | |
|            * */
 | |
|           *aWidgetFlags = GTK_ARROW_NONE;
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
|     aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW;
 | |
|     break;
 | |
|   case StyleAppearance::Treetwisty:
 | |
|     aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_EXPANDER_COLLAPSED;
 | |
|     break;
 | |
|   case StyleAppearance::Treetwistyopen:
 | |
|     aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = GTK_EXPANDER_EXPANDED;
 | |
|     break;
 | |
|   case StyleAppearance::Menulist:
 | |
|     aGtkWidgetType = MOZ_GTK_DROPDOWN;
 | |
|     if (aWidgetFlags)
 | |
|         *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
 | |
|     break;
 | |
|   case StyleAppearance::MenulistText:
 | |
|     return false; // nothing to do, but prevents the bg from being drawn
 | |
|   case StyleAppearance::MenulistTextfield:
 | |
|     aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
 | |
|     break;
 | |
|   case StyleAppearance::MenulistButton:
 | |
|   case StyleAppearance::MozMenulistButton:
 | |
|     aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
 | |
|     break;
 | |
|   case StyleAppearance::ToolbarbuttonDropdown:
 | |
|   case StyleAppearance::ButtonArrowDown:
 | |
|   case StyleAppearance::ButtonArrowUp:
 | |
|   case StyleAppearance::ButtonArrowNext:
 | |
|   case StyleAppearance::ButtonArrowPrevious:
 | |
|     aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
 | |
|     if (aWidgetFlags) {
 | |
|       *aWidgetFlags = GTK_ARROW_DOWN;
 | |
| 
 | |
|       if (aWidgetType == StyleAppearance::ButtonArrowUp)
 | |
|         *aWidgetFlags = GTK_ARROW_UP;
 | |
|       else if (aWidgetType == StyleAppearance::ButtonArrowNext)
 | |
|         *aWidgetFlags = GTK_ARROW_RIGHT;
 | |
|       else if (aWidgetType == StyleAppearance::ButtonArrowPrevious)
 | |
|         *aWidgetFlags = GTK_ARROW_LEFT;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::CheckboxContainer:
 | |
|     aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER;
 | |
|     break;
 | |
|   case StyleAppearance::RadioContainer:
 | |
|     aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER;
 | |
|     break;
 | |
|   case StyleAppearance::CheckboxLabel:
 | |
|     aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL;
 | |
|     break;
 | |
|   case StyleAppearance::RadioLabel:
 | |
|     aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL;
 | |
|     break;
 | |
|   case StyleAppearance::Toolbar:
 | |
|     aGtkWidgetType = MOZ_GTK_TOOLBAR;
 | |
|     break;
 | |
|   case StyleAppearance::Tooltip:
 | |
|     aGtkWidgetType = MOZ_GTK_TOOLTIP;
 | |
|     break;
 | |
|   case StyleAppearance::Statusbarpanel:
 | |
|   case StyleAppearance::Resizerpanel:
 | |
|     aGtkWidgetType = MOZ_GTK_FRAME;
 | |
|     break;
 | |
|   case StyleAppearance::Progressbar:
 | |
|   case StyleAppearance::ProgressbarVertical:
 | |
|     aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
 | |
|     break;
 | |
|   case StyleAppearance::Progresschunk:
 | |
|   case StyleAppearance::ProgresschunkVertical:
 | |
|     {
 | |
|       nsIFrame* stateFrame = aFrame->GetParent();
 | |
|       EventStates eventStates = GetContentState(stateFrame, aWidgetType);
 | |
| 
 | |
|       aGtkWidgetType = IsIndeterminateProgress(stateFrame, eventStates)
 | |
|                          ? IsVerticalProgress(stateFrame)
 | |
|                            ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
 | |
|                            : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
 | |
|                          : MOZ_GTK_PROGRESS_CHUNK;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::TabScrollArrowBack:
 | |
|   case StyleAppearance::TabScrollArrowForward:
 | |
|     if (aWidgetFlags)
 | |
|       *aWidgetFlags = aWidgetType == StyleAppearance::TabScrollArrowBack ?
 | |
|                         GTK_ARROW_LEFT : GTK_ARROW_RIGHT;
 | |
|     aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW;
 | |
|     break;
 | |
|   case StyleAppearance::Tabpanels:
 | |
|     aGtkWidgetType = MOZ_GTK_TABPANELS;
 | |
|     break;
 | |
|   case StyleAppearance::Tab:
 | |
|     {
 | |
|       if (IsBottomTab(aFrame)) {
 | |
|         aGtkWidgetType = MOZ_GTK_TAB_BOTTOM;
 | |
|       } else {
 | |
|         aGtkWidgetType = MOZ_GTK_TAB_TOP;
 | |
|       }
 | |
| 
 | |
|       if (aWidgetFlags) {
 | |
|         /* First bits will be used to store max(0,-bmargin) where bmargin
 | |
|          * is the bottom margin of the tab in pixels  (resp. top margin,
 | |
|          * for bottom tabs). */
 | |
|         *aWidgetFlags = GetTabMarginPixels(aFrame);
 | |
| 
 | |
|         if (IsSelectedTab(aFrame))
 | |
|           *aWidgetFlags |= MOZ_GTK_TAB_SELECTED;
 | |
| 
 | |
|         if (IsFirstTab(aFrame))
 | |
|           *aWidgetFlags |= MOZ_GTK_TAB_FIRST;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::Splitter:
 | |
|     if (IsHorizontal(aFrame))
 | |
|       aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
 | |
|     else
 | |
|       aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
 | |
|     break;
 | |
|   case StyleAppearance::Menubar:
 | |
|     aGtkWidgetType = MOZ_GTK_MENUBAR;
 | |
|     break;
 | |
|   case StyleAppearance::Menupopup:
 | |
|     aGtkWidgetType = MOZ_GTK_MENUPOPUP;
 | |
|     break;
 | |
|   case StyleAppearance::Menuitem:
 | |
|     {
 | |
|       nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
 | |
|       if (menuFrame && menuFrame->IsOnMenuBar()) {
 | |
|         aGtkWidgetType = MOZ_GTK_MENUBARITEM;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     aGtkWidgetType = MOZ_GTK_MENUITEM;
 | |
|     break;
 | |
|   case StyleAppearance::Menuseparator:
 | |
|     aGtkWidgetType = MOZ_GTK_MENUSEPARATOR;
 | |
|     break;
 | |
|   case StyleAppearance::Menuarrow:
 | |
|     aGtkWidgetType = MOZ_GTK_MENUARROW;
 | |
|     break;
 | |
|   case StyleAppearance::Checkmenuitem:
 | |
|     aGtkWidgetType = MOZ_GTK_CHECKMENUITEM;
 | |
|     break;
 | |
|   case StyleAppearance::Radiomenuitem:
 | |
|     aGtkWidgetType = MOZ_GTK_RADIOMENUITEM;
 | |
|     break;
 | |
|   case StyleAppearance::Window:
 | |
|   case StyleAppearance::Dialog:
 | |
|     aGtkWidgetType = MOZ_GTK_WINDOW;
 | |
|     break;
 | |
|   case StyleAppearance::MozGtkInfoBar:
 | |
|     aGtkWidgetType = MOZ_GTK_INFO_BAR;
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowTitlebar:
 | |
|     aGtkWidgetType = MOZ_GTK_HEADER_BAR;
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowTitlebarMaximized:
 | |
|     aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED;
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowButtonClose:
 | |
|     aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE;
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowButtonMinimize:
 | |
|     aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowButtonMaximize:
 | |
|     aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowButtonRestore:
 | |
|     aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE;
 | |
|     break;
 | |
|   default:
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| class SystemCairoClipper : public ClipExporter {
 | |
| public:
 | |
|   explicit SystemCairoClipper(cairo_t* aContext) : mContext(aContext)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   BeginClip(const Matrix& aTransform) override
 | |
|   {
 | |
|     cairo_matrix_t mat;
 | |
|     GfxMatrixToCairoMatrix(aTransform, mat);
 | |
|     cairo_set_matrix(mContext, &mat);
 | |
| 
 | |
|     cairo_new_path(mContext);
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   MoveTo(const Point &aPoint) override
 | |
|   {
 | |
|     cairo_move_to(mContext, aPoint.x, aPoint.y);
 | |
|     mCurrentPoint = aPoint;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   LineTo(const Point &aPoint) override
 | |
|   {
 | |
|     cairo_line_to(mContext, aPoint.x, aPoint.y);
 | |
|     mCurrentPoint = aPoint;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3) override
 | |
|   {
 | |
|     cairo_curve_to(mContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
 | |
|     mCurrentPoint = aCP3;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   QuadraticBezierTo(const Point &aCP1, const Point &aCP2) override
 | |
|   {
 | |
|     Point CP0 = CurrentPoint();
 | |
|     Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
 | |
|     Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
 | |
|     Point CP3 = aCP2;
 | |
|     cairo_curve_to(mContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
 | |
|     mCurrentPoint = aCP2;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle,
 | |
|       bool aAntiClockwise) override
 | |
|   {
 | |
|     ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
 | |
|                 aAntiClockwise);
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   Close() override
 | |
|   {
 | |
|     cairo_close_path(mContext);
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   EndClip() override
 | |
|   {
 | |
|     cairo_clip(mContext);
 | |
|   }
 | |
| 
 | |
|   Point
 | |
|   CurrentPoint() const override
 | |
|   {
 | |
|     return mCurrentPoint;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   cairo_t* mContext;
 | |
|   Point mCurrentPoint;
 | |
| };
 | |
| 
 | |
| static void
 | |
| DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
 | |
|                    GtkWidgetState aState, WidgetNodeType aGTKWidgetType,
 | |
|                    gint aFlags, GtkTextDirection aDirection, gint aScaleFactor,
 | |
|                    bool aSnapped, const Point& aDrawOrigin, const nsIntSize& aDrawSize,
 | |
|                    GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency)
 | |
| {
 | |
|   Point drawOffset;
 | |
|   Matrix transform;
 | |
|   if (!aSnapped) {
 | |
|     // If we are not snapped, we depend on the DT for translation.
 | |
|     drawOffset = aDrawOrigin;
 | |
|     transform = aDrawTarget->GetTransform().PreTranslate(aDrawOrigin);
 | |
|   } else {
 | |
|     // Otherwise, we only need to take the device offset into account.
 | |
|     drawOffset = aDrawOrigin - aContext->GetDeviceOffset();
 | |
|     transform = Matrix::Translation(drawOffset);
 | |
|   }
 | |
| 
 | |
|   if (aScaleFactor != 1)
 | |
|     transform.PreScale(aScaleFactor, aScaleFactor);
 | |
| 
 | |
|   cairo_matrix_t mat;
 | |
|   GfxMatrixToCairoMatrix(transform, mat);
 | |
| 
 | |
|   nsIntSize clipSize((aDrawSize.width + aScaleFactor - 1) / aScaleFactor,
 | |
|                      (aDrawSize.height + aScaleFactor - 1) / aScaleFactor);
 | |
| 
 | |
| #ifndef MOZ_TREE_CAIRO
 | |
|   // Directly use the Cairo draw target to render the widget if using system Cairo everywhere.
 | |
|   BorrowedCairoContext borrowCairo(aDrawTarget);
 | |
|   if (borrowCairo.mCairo) {
 | |
|     cairo_set_matrix(borrowCairo.mCairo, &mat);
 | |
| 
 | |
|     cairo_new_path(borrowCairo.mCairo);
 | |
|     cairo_rectangle(borrowCairo.mCairo, 0, 0, clipSize.width, clipSize.height);
 | |
|     cairo_clip(borrowCairo.mCairo);
 | |
| 
 | |
|     moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect, &aState, aFlags, aDirection);
 | |
| 
 | |
|     borrowCairo.Finish();
 | |
|     return;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // A direct Cairo draw target is not available, so we need to create a temporary one.
 | |
| #if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
 | |
|   // If using a Cairo xlib surface, then try to reuse it.
 | |
|   BorrowedXlibDrawable borrow(aDrawTarget);
 | |
|   if (borrow.GetDrawable()) {
 | |
|     nsIntSize size = borrow.GetSize();
 | |
|     cairo_surface_t* surf = nullptr;
 | |
|     // Check if the surface is using XRender.
 | |
| #ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
 | |
|     if (borrow.GetXRenderFormat()) {
 | |
|       surf = cairo_xlib_surface_create_with_xrender_format(
 | |
|           borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetScreen(),
 | |
|           borrow.GetXRenderFormat(), size.width, size.height);
 | |
|     } else {
 | |
| #else
 | |
|       if (! borrow.GetXRenderFormat()) {
 | |
| #endif
 | |
|         surf = cairo_xlib_surface_create(
 | |
|             borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetVisual(),
 | |
|             size.width, size.height);
 | |
|       }
 | |
|       if (!NS_WARN_IF(!surf)) {
 | |
|         Point offset = borrow.GetOffset();
 | |
|         if (offset != Point()) {
 | |
|           cairo_surface_set_device_offset(surf, offset.x, offset.y);
 | |
|         }
 | |
|         cairo_t* cr = cairo_create(surf);
 | |
|         if (!NS_WARN_IF(!cr)) {
 | |
|           RefPtr<SystemCairoClipper> clipper = new SystemCairoClipper(cr);
 | |
|           aContext->ExportClip(*clipper);
 | |
| 
 | |
|           cairo_set_matrix(cr, &mat);
 | |
| 
 | |
|           cairo_new_path(cr);
 | |
|           cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height);
 | |
|           cairo_clip(cr);
 | |
| 
 | |
|           moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
 | |
| 
 | |
|           cairo_destroy(cr);
 | |
|         }
 | |
|         cairo_surface_destroy(surf);
 | |
|       }
 | |
|       borrow.Finish();
 | |
|       return;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   // Check if the widget requires complex masking that must be composited.
 | |
|   // Try to directly write to the draw target's pixels if possible.
 | |
|   uint8_t* data;
 | |
|   nsIntSize size;
 | |
|   int32_t stride;
 | |
|   SurfaceFormat format;
 | |
|   IntPoint origin;
 | |
|   if (aDrawTarget->LockBits(&data, &size, &stride, &format, &origin)) {
 | |
|     // Create a Cairo image surface context the device rectangle.
 | |
|     cairo_surface_t* surf =
 | |
|       cairo_image_surface_create_for_data(
 | |
|         data, GfxFormatToCairoFormat(format), size.width, size.height, stride);
 | |
|     if (!NS_WARN_IF(!surf)) {
 | |
|       if (origin != IntPoint()) {
 | |
|         cairo_surface_set_device_offset(surf, -origin.x, -origin.y);
 | |
|       }
 | |
|       cairo_t* cr = cairo_create(surf);
 | |
|       if (!NS_WARN_IF(!cr)) {
 | |
|         RefPtr<SystemCairoClipper> clipper = new SystemCairoClipper(cr);
 | |
|         aContext->ExportClip(*clipper);
 | |
| 
 | |
|         cairo_set_matrix(cr, &mat);
 | |
| 
 | |
|         cairo_new_path(cr);
 | |
|         cairo_rectangle(cr, 0, 0, clipSize.width, clipSize.height);
 | |
|         cairo_clip(cr);
 | |
| 
 | |
|         moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
 | |
| 
 | |
|         cairo_destroy(cr);
 | |
|       }
 | |
|       cairo_surface_destroy(surf);
 | |
|     }
 | |
|     aDrawTarget->ReleaseBits(data);
 | |
|   } else {
 | |
|     // If the widget has any transparency, make sure to choose an alpha format.
 | |
|     format = aTransparency != nsITheme::eOpaque ? SurfaceFormat::B8G8R8A8 : aDrawTarget->GetFormat();
 | |
|     // Create a temporary data surface to render the widget into.
 | |
|     RefPtr<DataSourceSurface> dataSurface =
 | |
|       Factory::CreateDataSourceSurface(aDrawSize, format, aTransparency != nsITheme::eOpaque);
 | |
|     DataSourceSurface::MappedSurface map;
 | |
|     if (!NS_WARN_IF(!(dataSurface && dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)))) {
 | |
|       // Create a Cairo image surface wrapping the data surface.
 | |
|       cairo_surface_t* surf =
 | |
|         cairo_image_surface_create_for_data(map.mData, GfxFormatToCairoFormat(format),
 | |
|                                             aDrawSize.width, aDrawSize.height, map.mStride);
 | |
|       cairo_t* cr = nullptr;
 | |
|       if (!NS_WARN_IF(!surf)) {
 | |
|         cr = cairo_create(surf);
 | |
|         if (!NS_WARN_IF(!cr)) {
 | |
|           if (aScaleFactor != 1) {
 | |
|             cairo_scale(cr, aScaleFactor, aScaleFactor);
 | |
|           }
 | |
| 
 | |
|           moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Unmap the surface before using it as a source
 | |
|       dataSurface->Unmap();
 | |
| 
 | |
|       if (cr) {
 | |
|         // The widget either needs to be masked or has transparency, so use the slower drawing path.
 | |
|         aDrawTarget->DrawSurface(dataSurface,
 | |
|                                  Rect(aSnapped ? drawOffset - aDrawTarget->GetTransform().GetTranslation() : drawOffset,
 | |
|                                       Size(aDrawSize)),
 | |
|                                  Rect(0, 0, aDrawSize.width, aDrawSize.height));
 | |
|         cairo_destroy(cr);
 | |
|       }
 | |
| 
 | |
|       if (surf) {
 | |
|         cairo_surface_destroy(surf);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame,
 | |
|                                         StyleAppearance aWidgetType,
 | |
|                                         nsIntMargin* aExtra)
 | |
| {
 | |
|   *aExtra = nsIntMargin(0,0,0,0);
 | |
|   // Allow an extra one pixel above and below the thumb for certain
 | |
|   // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
 | |
|   // We modify the frame's overflow area.  See bug 297508.
 | |
|   switch (aWidgetType) {
 | |
|   case StyleAppearance::ScrollbarthumbVertical:
 | |
|     aExtra->top = aExtra->bottom = 1;
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbarthumbHorizontal:
 | |
|     aExtra->left = aExtra->right = 1;
 | |
|     break;
 | |
| 
 | |
|   case StyleAppearance::Button :
 | |
|     {
 | |
|       if (IsDefaultButton(aFrame)) {
 | |
|         // Some themes draw a default indicator outside the widget,
 | |
|         // include that in overflow
 | |
|         gint top, left, bottom, right;
 | |
|         moz_gtk_button_get_default_overflow(&top, &left, &bottom, &right);
 | |
|         aExtra->top = top;
 | |
|         aExtra->right = right;
 | |
|         aExtra->bottom = bottom;
 | |
|         aExtra->left = left;
 | |
|         break;
 | |
|       }
 | |
|       return false;
 | |
|     }
 | |
|   case StyleAppearance::FocusOutline:
 | |
|     {
 | |
|       moz_gtk_get_focus_outline_size(&aExtra->left, &aExtra->top);
 | |
|       aExtra->right = aExtra->left;
 | |
|       aExtra->bottom = aExtra->top;
 | |
|       break;
 | |
|     }
 | |
|   case StyleAppearance::Tab :
 | |
|     {
 | |
|       if (!IsSelectedTab(aFrame))
 | |
|         return false;
 | |
| 
 | |
|       gint gap_height = moz_gtk_get_tab_thickness(IsBottomTab(aFrame) ?
 | |
|                             MOZ_GTK_TAB_BOTTOM : MOZ_GTK_TAB_TOP);
 | |
|       if (!gap_height)
 | |
|         return false;
 | |
| 
 | |
|       int32_t extra = gap_height - GetTabMarginPixels(aFrame);
 | |
|       if (extra <= 0)
 | |
|         return false;
 | |
| 
 | |
|       if (IsBottomTab(aFrame)) {
 | |
|         aExtra->top = extra;
 | |
|       } else {
 | |
|         aExtra->bottom = extra;
 | |
|       }
 | |
|       return false;
 | |
|     }
 | |
|   default:
 | |
|     return false;
 | |
|   }
 | |
|   gint scale = GetMonitorScaleFactor(aFrame);
 | |
|   aExtra->top *= scale;
 | |
|   aExtra->right *= scale;
 | |
|   aExtra->bottom *= scale;
 | |
|   aExtra->left *= scale;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext,
 | |
|                                        nsIFrame* aFrame,
 | |
|                                        StyleAppearance aWidgetType,
 | |
|                                        const nsRect& aRect,
 | |
|                                        const nsRect& aDirtyRect)
 | |
| {
 | |
|   GtkWidgetState state;
 | |
|   WidgetNodeType gtkWidgetType;
 | |
|   GtkTextDirection direction = GetTextDirection(aFrame);
 | |
|   gint flags;
 | |
|   if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
 | |
|                             &flags))
 | |
|     return NS_OK;
 | |
| 
 | |
|   gfxContext* ctx = aContext;
 | |
|   nsPresContext *presContext = aFrame->PresContext();
 | |
| 
 | |
|   gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
 | |
|   gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
 | |
|   gint scaleFactor = GetMonitorScaleFactor(aFrame);
 | |
| 
 | |
|   // Align to device pixels where sensible
 | |
|   // to provide crisper and faster drawing.
 | |
|   // Don't snap if it's a non-unit scale factor. We're going to have to take
 | |
|   // slow paths then in any case.
 | |
|   bool snapped = ctx->UserToDevicePixelSnapped(rect);
 | |
|   if (snapped) {
 | |
|     // Leave rect in device coords but make dirtyRect consistent.
 | |
|     dirtyRect = ctx->UserToDevice(dirtyRect);
 | |
|   }
 | |
| 
 | |
|   // Translate the dirty rect so that it is wrt the widget top-left.
 | |
|   dirtyRect.MoveBy(-rect.TopLeft());
 | |
|   // Round out the dirty rect to gdk pixels to ensure that gtk draws
 | |
|   // enough pixels for interpolation to device pixels.
 | |
|   dirtyRect.RoundOut();
 | |
| 
 | |
|   // GTK themes can only draw an integer number of pixels
 | |
|   // (even when not snapped).
 | |
|   nsIntRect widgetRect(0, 0, NS_lround(rect.Width()), NS_lround(rect.Height()));
 | |
|   nsIntRect overflowRect(widgetRect);
 | |
|   nsIntMargin extraSize;
 | |
|   if (GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) {
 | |
|     overflowRect.Inflate(extraSize);
 | |
|   }
 | |
| 
 | |
|   // This is the rectangle that will actually be drawn, in gdk pixels
 | |
|   nsIntRect drawingRect(int32_t(dirtyRect.X()),
 | |
|                         int32_t(dirtyRect.Y()),
 | |
|                         int32_t(dirtyRect.Width()),
 | |
|                         int32_t(dirtyRect.Height()));
 | |
|   if (widgetRect.IsEmpty()
 | |
|       || !drawingRect.IntersectRect(overflowRect, drawingRect))
 | |
|     return NS_OK;
 | |
| 
 | |
|   NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
 | |
|                "Trying to render an unsafe widget!");
 | |
| 
 | |
|   bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
 | |
|   if (!safeState) {
 | |
|     gLastGdkError = 0;
 | |
|     gdk_error_trap_push ();
 | |
|   }
 | |
| 
 | |
|   Transparency transparency = GetWidgetTransparency(aFrame, aWidgetType);
 | |
| 
 | |
|   // gdk rectangles are wrt the drawing rect.
 | |
|   GdkRectangle gdk_rect = {-drawingRect.x/scaleFactor,
 | |
|                            -drawingRect.y/scaleFactor,
 | |
|                            widgetRect.width/scaleFactor,
 | |
|                            widgetRect.height/scaleFactor};
 | |
| 
 | |
|   // Save actual widget scale to GtkWidgetState as we don't provide
 | |
|   // nsFrame to gtk3drawing routines.
 | |
|   state.scale = scaleFactor;
 | |
| 
 | |
|   // translate everything so (0,0) is the top left of the drawingRect
 | |
|   gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
 | |
| 
 | |
|   DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
 | |
|                      state, gtkWidgetType, flags, direction, scaleFactor,
 | |
|                      snapped, ToPoint(origin), drawingRect.Size(),
 | |
|                      gdk_rect, transparency);
 | |
| 
 | |
|   if (!safeState) {
 | |
|     // gdk_flush() call from expose event crashes Gtk+ on Wayland
 | |
|     // (Gnome BZ #773307)
 | |
|     if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
 | |
|       gdk_flush();
 | |
|     }
 | |
|     gLastGdkError = gdk_error_trap_pop ();
 | |
| 
 | |
|     if (gLastGdkError) {
 | |
| #ifdef DEBUG
 | |
|       printf("GTK theme failed for widget type %d, error was %d, state was "
 | |
|              "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
 | |
|              static_cast<int>(aWidgetType), gLastGdkError, state.active,
 | |
|              state.focused, state.inHover, state.disabled);
 | |
| #endif
 | |
|       NS_WARNING("GTK theme failed; disabling unsafe widget");
 | |
|       SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType);
 | |
|       // force refresh of the window, because the widget was not
 | |
|       // successfully drawn it must be redrawn using the default look
 | |
|       RefreshWidgetWindow(aFrame);
 | |
|     } else {
 | |
|       SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Indeterminate progress bar are animated.
 | |
|   if (gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
 | |
|       gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
 | |
|     if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
 | |
|       NS_WARNING("unable to animate widget!");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder& aBuilder,
 | |
|                                                    mozilla::wr::IpcResourceUpdateQueue& aResources,
 | |
|                                                    const mozilla::layers::StackingContextHelper& aSc,
 | |
|                                                    mozilla::layers::WebRenderLayerManager* aManager,
 | |
|                                                    nsIFrame* aFrame,
 | |
|                                                    StyleAppearance aWidgetType,
 | |
|                                                    const nsRect& aRect)
 | |
| {
 | |
|   nsPresContext* presContext = aFrame->PresContext();
 | |
|   wr::LayoutRect bounds = wr::ToRoundedLayoutRect(
 | |
|     LayoutDeviceRect::FromAppUnits(aRect, presContext->AppUnitsPerDevPixel()));
 | |
| 
 | |
|   switch (aWidgetType) {
 | |
|   case StyleAppearance::Window:
 | |
|   case StyleAppearance::Dialog:
 | |
|     aBuilder.PushRect(bounds, bounds, true,
 | |
|                       wr::ToColorF(Color::FromABGR(
 | |
|                         LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground,
 | |
|                                               NS_RGBA(0, 0, 0, 0)))));
 | |
|     return true;
 | |
| 
 | |
|   default:
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| WidgetNodeType
 | |
| nsNativeThemeGTK::NativeThemeToGtkTheme(StyleAppearance aWidgetType, nsIFrame* aFrame)
 | |
| {
 | |
|   WidgetNodeType gtkWidgetType;
 | |
|   gint unusedFlags;
 | |
| 
 | |
|   if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
 | |
|                             &unusedFlags))
 | |
|   {
 | |
|     MOZ_ASSERT_UNREACHABLE("Unknown native widget to gtk widget mapping");
 | |
|     return MOZ_GTK_WINDOW;
 | |
|   }
 | |
|   return gtkWidgetType;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsNativeThemeGTK::GetCachedWidgetBorder(nsIFrame* aFrame,
 | |
|                                         StyleAppearance aWidgetType,
 | |
|                                         GtkTextDirection aDirection,
 | |
|                                         LayoutDeviceIntMargin* aResult)
 | |
| {
 | |
|   aResult->SizeTo(0, 0, 0, 0);
 | |
| 
 | |
|   WidgetNodeType gtkWidgetType;
 | |
|   gint unusedFlags;
 | |
|   if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
 | |
|                            &unusedFlags)) {
 | |
|     MOZ_ASSERT(0 <= gtkWidgetType && gtkWidgetType < MOZ_GTK_WIDGET_NODE_COUNT);
 | |
|     uint8_t cacheIndex = gtkWidgetType / 8;
 | |
|     uint8_t cacheBit = 1u << (gtkWidgetType % 8);
 | |
| 
 | |
|     if (mBorderCacheValid[cacheIndex] & cacheBit) {
 | |
|       *aResult = mBorderCache[gtkWidgetType];
 | |
|     } else {
 | |
|       moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
 | |
|                                 &aResult->right, &aResult->bottom, aDirection);
 | |
|       if (gtkWidgetType != MOZ_GTK_DROPDOWN) { // depends on aDirection
 | |
|         mBorderCacheValid[cacheIndex] |= cacheBit;
 | |
|         mBorderCache[gtkWidgetType] = *aResult;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntMargin
 | |
| nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext,
 | |
|                                   nsIFrame* aFrame,
 | |
|                                   StyleAppearance aWidgetType)
 | |
| {
 | |
|   LayoutDeviceIntMargin result;
 | |
|   GtkTextDirection direction = GetTextDirection(aFrame);
 | |
|   switch (aWidgetType) {
 | |
|   case StyleAppearance::ScrollbarHorizontal:
 | |
|   case StyleAppearance::ScrollbarVertical:
 | |
|     {
 | |
|       GtkOrientation orientation =
 | |
|         aWidgetType == StyleAppearance::ScrollbarHorizontal ?
 | |
|         GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | |
|       const ScrollbarGTKMetrics* metrics =
 | |
|         GetActiveScrollbarMetrics(orientation);
 | |
| 
 | |
|       const GtkBorder& border = metrics->border.scrollbar;
 | |
|       result.top = border.top;
 | |
|       result.right = border.right;
 | |
|       result.bottom = border.bottom;
 | |
|       result.left = border.left;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::ScrollbartrackHorizontal:
 | |
|   case StyleAppearance::ScrollbartrackVertical:
 | |
|     {
 | |
|       GtkOrientation orientation =
 | |
|         aWidgetType == StyleAppearance::ScrollbartrackHorizontal ?
 | |
|         GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | |
|       const ScrollbarGTKMetrics* metrics =
 | |
|         GetActiveScrollbarMetrics(orientation);
 | |
| 
 | |
|       const GtkBorder& border = metrics->border.track;
 | |
|       result.top = border.top;
 | |
|       result.right = border.right;
 | |
|       result.bottom = border.bottom;
 | |
|       result.left = border.left;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::Toolbox:
 | |
|     // gtk has no toolbox equivalent.  So, although we map toolbox to
 | |
|     // gtk's 'toolbar' for purposes of painting the widget background,
 | |
|     // we don't use the toolbar border for toolbox.
 | |
|     break;
 | |
|   case StyleAppearance::Dualbutton:
 | |
|     // TOOLBAR_DUAL_BUTTON is an interesting case.  We want a border to draw
 | |
|     // around the entire button + dropdown, and also an inner border if you're
 | |
|     // over the button part.  But, we want the inner button to be right up
 | |
|     // against the edge of the outer button so that the borders overlap.
 | |
|     // To make this happen, we draw a button border for the outer button,
 | |
|     // but don't reserve any space for it.
 | |
|     break;
 | |
|   case StyleAppearance::Tab:
 | |
|     {
 | |
|       WidgetNodeType gtkWidgetType;
 | |
|       gint flags;
 | |
| 
 | |
|       if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
 | |
|                                 &flags)) {
 | |
|         return result;
 | |
|       }
 | |
|       moz_gtk_get_tab_border(&result.left, &result.top,
 | |
|                              &result.right, &result.bottom, direction,
 | |
|                              (GtkTabFlags)flags, gtkWidgetType);
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::Menuitem:
 | |
|   case StyleAppearance::Checkmenuitem:
 | |
|   case StyleAppearance::Radiomenuitem:
 | |
|     // For regular menuitems, we will be using GetWidgetPadding instead of
 | |
|     // GetWidgetBorder to pad up the widget's internals; other menuitems
 | |
|     // will need to fall through and use the default case as before.
 | |
|     if (IsRegularMenuItem(aFrame))
 | |
|       break;
 | |
|     MOZ_FALLTHROUGH;
 | |
|   default:
 | |
|     {
 | |
|       GetCachedWidgetBorder(aFrame, aWidgetType, direction, &result);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   gint scale = GetMonitorScaleFactor(aFrame);
 | |
|   result.top *= scale;
 | |
|   result.right *= scale;
 | |
|   result.bottom *= scale;
 | |
|   result.left *= scale;
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
 | |
|                                    nsIFrame* aFrame,
 | |
|                                    StyleAppearance aWidgetType,
 | |
|                                    LayoutDeviceIntMargin* aResult)
 | |
| {
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton &&
 | |
|       StaticPrefs::layout_css_webkit_appearance_enabled()) {
 | |
|     aWidgetType = StyleAppearance::Menulist;
 | |
|   }
 | |
| 
 | |
|   switch (aWidgetType) {
 | |
|     case StyleAppearance::ButtonFocus:
 | |
|     case StyleAppearance::Toolbarbutton:
 | |
|     case StyleAppearance::MozWindowButtonClose:
 | |
|     case StyleAppearance::MozWindowButtonMinimize:
 | |
|     case StyleAppearance::MozWindowButtonMaximize:
 | |
|     case StyleAppearance::MozWindowButtonRestore:
 | |
|     case StyleAppearance::Dualbutton:
 | |
|     case StyleAppearance::TabScrollArrowBack:
 | |
|     case StyleAppearance::TabScrollArrowForward:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::MozMenulistButton:
 | |
|     case StyleAppearance::ToolbarbuttonDropdown:
 | |
|     case StyleAppearance::ButtonArrowUp:
 | |
|     case StyleAppearance::ButtonArrowDown:
 | |
|     case StyleAppearance::ButtonArrowNext:
 | |
|     case StyleAppearance::ButtonArrowPrevious:
 | |
|     case StyleAppearance::RangeThumb:
 | |
|     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
 | |
|     // and have a meaningful baseline, so they can't have
 | |
|     // author-specified padding.
 | |
|     case StyleAppearance::Checkbox:
 | |
|     case StyleAppearance::Radio:
 | |
|       aResult->SizeTo(0, 0, 0, 0);
 | |
|       return true;
 | |
|     case StyleAppearance::Menuitem:
 | |
|     case StyleAppearance::Checkmenuitem:
 | |
|     case StyleAppearance::Radiomenuitem:
 | |
|       {
 | |
|         // Menubar and menulist have their padding specified in CSS.
 | |
|         if (!IsRegularMenuItem(aFrame))
 | |
|           return false;
 | |
| 
 | |
|         GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame),
 | |
|                               aResult);
 | |
| 
 | |
|         gint horizontal_padding;
 | |
|         if (aWidgetType == StyleAppearance::Menuitem)
 | |
|           moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
 | |
|         else
 | |
|           moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
 | |
| 
 | |
|         aResult->left += horizontal_padding;
 | |
|         aResult->right += horizontal_padding;
 | |
| 
 | |
|         gint scale = GetMonitorScaleFactor(aFrame);
 | |
|         aResult->top *= scale;
 | |
|         aResult->right *= scale;
 | |
|         aResult->bottom *= scale;
 | |
|         aResult->left *= scale;
 | |
| 
 | |
|         return true;
 | |
|       }
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
 | |
|                                     nsIFrame* aFrame,
 | |
|                                     StyleAppearance aWidgetType,
 | |
|                                     nsRect* aOverflowRect)
 | |
| {
 | |
|   nsIntMargin extraSize;
 | |
|   if (!GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize))
 | |
|     return false;
 | |
| 
 | |
|   int32_t p2a = aContext->AppUnitsPerDevPixel();
 | |
|   nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a),
 | |
|              NSIntPixelsToAppUnits(extraSize.right, p2a),
 | |
|              NSIntPixelsToAppUnits(extraSize.bottom, p2a),
 | |
|              NSIntPixelsToAppUnits(extraSize.left, p2a));
 | |
| 
 | |
|   aOverflowRect->Inflate(m);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
 | |
|                                        nsIFrame* aFrame,
 | |
|                                        StyleAppearance aWidgetType,
 | |
|                                        LayoutDeviceIntSize* aResult,
 | |
|                                        bool* aIsOverridable)
 | |
| {
 | |
|   aResult->width = aResult->height = 0;
 | |
|   *aIsOverridable = true;
 | |
| 
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton &&
 | |
|       StaticPrefs::layout_css_webkit_appearance_enabled()) {
 | |
|     aWidgetType = StyleAppearance::Menulist;
 | |
|   }
 | |
| 
 | |
|   switch (aWidgetType) {
 | |
|     case StyleAppearance::ScrollbarbuttonUp:
 | |
|     case StyleAppearance::ScrollbarbuttonDown:
 | |
|       {
 | |
|         const ScrollbarGTKMetrics* metrics =
 | |
|           GetActiveScrollbarMetrics(GTK_ORIENTATION_VERTICAL);
 | |
| 
 | |
|         aResult->width = metrics->size.button.width;
 | |
|         aResult->height = metrics->size.button.height;
 | |
|         *aIsOverridable = false;
 | |
|       }
 | |
|       break;
 | |
|     case StyleAppearance::ScrollbarbuttonLeft:
 | |
|     case StyleAppearance::ScrollbarbuttonRight:
 | |
|       {
 | |
|         const ScrollbarGTKMetrics* metrics =
 | |
|           GetActiveScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL);
 | |
| 
 | |
|         aResult->width = metrics->size.button.width;
 | |
|         aResult->height = metrics->size.button.height;
 | |
|         *aIsOverridable = false;
 | |
|       }
 | |
|       break;
 | |
|     case StyleAppearance::Splitter:
 | |
|     {
 | |
|       gint metrics;
 | |
|       if (IsHorizontal(aFrame)) {
 | |
|         moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
 | |
|         aResult->width = metrics;
 | |
|         aResult->height = 0;
 | |
|       } else {
 | |
|         moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics);
 | |
|         aResult->width = 0;
 | |
|         aResult->height = metrics;
 | |
|       }
 | |
|       *aIsOverridable = false;
 | |
|     }
 | |
|     break;
 | |
|     case StyleAppearance::ScrollbarHorizontal:
 | |
|     case StyleAppearance::ScrollbarVertical:
 | |
|     {
 | |
|       /* While we enforce a minimum size for the thumb, this is ignored
 | |
|        * for the some scrollbars if buttons are hidden (bug 513006) because
 | |
|        * the thumb isn't a direct child of the scrollbar, unlike the buttons
 | |
|        * or track. So add a minimum size to the track as well to prevent a
 | |
|        * 0-width scrollbar. */
 | |
|       GtkOrientation orientation =
 | |
|         aWidgetType == StyleAppearance::ScrollbarHorizontal ?
 | |
|         GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | |
|       const ScrollbarGTKMetrics* metrics =
 | |
|         GetActiveScrollbarMetrics(orientation);
 | |
| 
 | |
|       aResult->width = metrics->size.scrollbar.width;
 | |
|       aResult->height = metrics->size.scrollbar.height;
 | |
|     }
 | |
|     break;
 | |
|     case StyleAppearance::ScrollbarthumbVertical:
 | |
|     case StyleAppearance::ScrollbarthumbHorizontal:
 | |
|       {
 | |
|         GtkOrientation orientation =
 | |
|           aWidgetType == StyleAppearance::ScrollbarthumbHorizontal ?
 | |
|           GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | |
|         const ScrollbarGTKMetrics* metrics =
 | |
|           GetActiveScrollbarMetrics(orientation);
 | |
| 
 | |
|         aResult->width = metrics->size.thumb.width;
 | |
|         aResult->height = metrics->size.thumb.height;
 | |
|         *aIsOverridable = false;
 | |
|       }
 | |
|       break;
 | |
|     case StyleAppearance::RangeThumb:
 | |
|       {
 | |
|         gint thumb_length, thumb_height;
 | |
| 
 | |
|         if (IsRangeHorizontal(aFrame)) {
 | |
|           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
 | |
|         } else {
 | |
|           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_length);
 | |
|         }
 | |
|         aResult->width = thumb_length;
 | |
|         aResult->height = thumb_height;
 | |
| 
 | |
|         *aIsOverridable = false;
 | |
|       }
 | |
|       break;
 | |
|     case StyleAppearance::Range:
 | |
|       {
 | |
|         gint scale_width, scale_height;
 | |
| 
 | |
|         moz_gtk_get_scale_metrics(IsRangeHorizontal(aFrame) ?
 | |
|             GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL,
 | |
|             &scale_width, &scale_height);
 | |
|         aResult->width = scale_width;
 | |
|         aResult->height = scale_height;
 | |
| 
 | |
|         *aIsOverridable = true;
 | |
|       }
 | |
|       break;
 | |
|     case StyleAppearance::ScalethumbHorizontal:
 | |
|     case StyleAppearance::ScalethumbVertical:
 | |
|       {
 | |
|         gint thumb_length, thumb_height;
 | |
| 
 | |
|         if (aWidgetType == StyleAppearance::ScalethumbVertical) {
 | |
|           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height);
 | |
|           aResult->width = thumb_height;
 | |
|           aResult->height = thumb_length;
 | |
|         } else {
 | |
|           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
 | |
|           aResult->width = thumb_length;
 | |
|           aResult->height = thumb_height;
 | |
|         }
 | |
| 
 | |
|         *aIsOverridable = false;
 | |
|       }
 | |
|       break;
 | |
|     case StyleAppearance::TabScrollArrowBack:
 | |
|     case StyleAppearance::TabScrollArrowForward:
 | |
|       {
 | |
|         moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
 | |
|         *aIsOverridable = false;
 | |
|       }
 | |
|       break;
 | |
|   case StyleAppearance::MenulistButton:
 | |
|   case StyleAppearance::MozMenulistButton:
 | |
|     {
 | |
|       moz_gtk_get_combo_box_entry_button_size(&aResult->width,
 | |
|                                               &aResult->height);
 | |
|       *aIsOverridable = false;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::Menuseparator:
 | |
|     {
 | |
|       gint separator_height;
 | |
| 
 | |
|       moz_gtk_get_menu_separator_height(&separator_height);
 | |
|       aResult->height = separator_height;
 | |
| 
 | |
|       *aIsOverridable = false;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::Checkbox:
 | |
|   case StyleAppearance::Radio:
 | |
|     {
 | |
|       const ToggleGTKMetrics* metrics = GetToggleMetrics(aWidgetType == StyleAppearance::Radio);
 | |
|       aResult->width = metrics->minSizeWithBorder.width;
 | |
|       aResult->height = metrics->minSizeWithBorder.height;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::ToolbarbuttonDropdown:
 | |
|   case StyleAppearance::ButtonArrowUp:
 | |
|   case StyleAppearance::ButtonArrowDown:
 | |
|   case StyleAppearance::ButtonArrowNext:
 | |
|   case StyleAppearance::ButtonArrowPrevious:
 | |
|     {
 | |
|       moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW,
 | |
|                              &aResult->width, &aResult->height);
 | |
|       *aIsOverridable = false;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::MozWindowButtonClose:
 | |
|     {
 | |
|       const ToolbarButtonGTKMetrics* metrics =
 | |
|           GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
 | |
|       aResult->width = metrics->minSizeWithBorderMargin.width;
 | |
|       aResult->height = metrics->minSizeWithBorderMargin.height;
 | |
|       break;
 | |
|     }
 | |
|   case StyleAppearance::MozWindowButtonMinimize:
 | |
|     {
 | |
|       const ToolbarButtonGTKMetrics* metrics =
 | |
|           GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE);
 | |
|       aResult->width = metrics->minSizeWithBorderMargin.width;
 | |
|       aResult->height = metrics->minSizeWithBorderMargin.height;
 | |
|       break;
 | |
|     }
 | |
|   case StyleAppearance::MozWindowButtonMaximize:
 | |
|   case StyleAppearance::MozWindowButtonRestore:
 | |
|     {
 | |
|       const ToolbarButtonGTKMetrics* metrics =
 | |
|           GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE);
 | |
|       aResult->width = metrics->minSizeWithBorderMargin.width;
 | |
|       aResult->height = metrics->minSizeWithBorderMargin.height;
 | |
|       break;
 | |
|     }
 | |
|   case StyleAppearance::CheckboxContainer:
 | |
|   case StyleAppearance::RadioContainer:
 | |
|   case StyleAppearance::CheckboxLabel:
 | |
|   case StyleAppearance::RadioLabel:
 | |
|   case StyleAppearance::Button:
 | |
|   case StyleAppearance::Menulist:
 | |
|   case StyleAppearance::Toolbarbutton:
 | |
|   case StyleAppearance::Treeheadercell:
 | |
|     {
 | |
|       if (aWidgetType == StyleAppearance::Menulist) {
 | |
|         // Include the arrow size.
 | |
|         moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN,
 | |
|                                &aResult->width, &aResult->height);
 | |
|       }
 | |
|       // else the minimum size is missing consideration of container
 | |
|       // descendants; the value returned here will not be helpful, but the
 | |
|       // box model may consider border and padding with child minimum sizes.
 | |
| 
 | |
|       LayoutDeviceIntMargin border;
 | |
|       GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame), &border);
 | |
|       aResult->width += border.left + border.right;
 | |
|       aResult->height += border.top + border.bottom;
 | |
|     }
 | |
|     break;
 | |
| #ifdef MOZ_WIDGET_GTK
 | |
|   case StyleAppearance::NumberInput:
 | |
|   case StyleAppearance::Textfield:
 | |
|     {
 | |
|       moz_gtk_get_entry_min_height(&aResult->height);
 | |
|     }
 | |
|     break;
 | |
| #endif
 | |
|   case StyleAppearance::Separator:
 | |
|     {
 | |
|       gint separator_width;
 | |
| 
 | |
|       moz_gtk_get_toolbar_separator_width(&separator_width);
 | |
| 
 | |
|       aResult->width = separator_width;
 | |
|     }
 | |
|     break;
 | |
|   case StyleAppearance::InnerSpinButton:
 | |
|   case StyleAppearance::Spinner:
 | |
|     // hard code these sizes
 | |
|     aResult->width = 14;
 | |
|     aResult->height = 26;
 | |
|     break;
 | |
|   case StyleAppearance::Treeheadersortarrow:
 | |
|   case StyleAppearance::SpinnerUpbutton:
 | |
|   case StyleAppearance::SpinnerDownbutton:
 | |
|     // hard code these sizes
 | |
|     aResult->width = 14;
 | |
|     aResult->height = 13;
 | |
|     break;
 | |
|   case StyleAppearance::Resizer:
 | |
|     // same as Windows to make our lives easier
 | |
|     aResult->width = aResult->height = 15;
 | |
|     *aIsOverridable = false;
 | |
|     break;
 | |
|   case StyleAppearance::Treetwisty:
 | |
|   case StyleAppearance::Treetwistyopen:
 | |
|     {
 | |
|       gint expander_size;
 | |
| 
 | |
|       moz_gtk_get_treeview_expander_size(&expander_size);
 | |
|       aResult->width = aResult->height = expander_size;
 | |
|       *aIsOverridable = false;
 | |
|     }
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   *aResult = *aResult * GetMonitorScaleFactor(aFrame);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame,
 | |
|                                      StyleAppearance aWidgetType,
 | |
|                                      nsAtom* aAttribute, bool* aShouldRepaint,
 | |
|                                      const nsAttrValue* aOldValue)
 | |
| {
 | |
|   // Some widget types just never change state.
 | |
|   if (aWidgetType == StyleAppearance::Toolbox ||
 | |
|       aWidgetType == StyleAppearance::Toolbar ||
 | |
|       aWidgetType == StyleAppearance::Statusbar ||
 | |
|       aWidgetType == StyleAppearance::Statusbarpanel ||
 | |
|       aWidgetType == StyleAppearance::Resizerpanel ||
 | |
|       aWidgetType == StyleAppearance::Progresschunk ||
 | |
|       aWidgetType == StyleAppearance::ProgresschunkVertical ||
 | |
|       aWidgetType == StyleAppearance::Progressbar ||
 | |
|       aWidgetType == StyleAppearance::ProgressbarVertical ||
 | |
|       aWidgetType == StyleAppearance::Menubar ||
 | |
|       aWidgetType == StyleAppearance::Menupopup ||
 | |
|       aWidgetType == StyleAppearance::Tooltip ||
 | |
|       aWidgetType == StyleAppearance::Menuseparator ||
 | |
|       aWidgetType == StyleAppearance::Window ||
 | |
|       aWidgetType == StyleAppearance::Dialog) {
 | |
|     *aShouldRepaint = false;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if ((aWidgetType == StyleAppearance::ScrollbarthumbVertical ||
 | |
|        aWidgetType == StyleAppearance::ScrollbarthumbHorizontal) &&
 | |
|        aAttribute == nsGkAtoms::active) {
 | |
|     *aShouldRepaint = true;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if ((aWidgetType == StyleAppearance::ScrollbarbuttonUp ||
 | |
|        aWidgetType == StyleAppearance::ScrollbarbuttonDown ||
 | |
|        aWidgetType == StyleAppearance::ScrollbarbuttonLeft ||
 | |
|        aWidgetType == StyleAppearance::ScrollbarbuttonRight) &&
 | |
|       (aAttribute == nsGkAtoms::curpos ||
 | |
|        aAttribute == nsGkAtoms::maxpos)) {
 | |
|     // If 'curpos' has changed and we are passed its old value, we can
 | |
|     // determine whether the button's enablement actually needs to change.
 | |
|     if (aAttribute == nsGkAtoms::curpos && aOldValue) {
 | |
|       int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
 | |
|       int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 0);
 | |
|       nsAutoString str;
 | |
|       aOldValue->ToString(str);
 | |
|       nsresult err;
 | |
|       int32_t oldCurpos = str.ToInteger(&err);
 | |
|       if (str.IsEmpty() || NS_FAILED(err)) {
 | |
|         *aShouldRepaint = true;
 | |
|       } else {
 | |
|         bool disabledBefore = ShouldScrollbarButtonBeDisabled(oldCurpos, maxpos, aWidgetType);
 | |
|         bool disabledNow = ShouldScrollbarButtonBeDisabled(curpos, maxpos, aWidgetType);
 | |
|         *aShouldRepaint = (disabledBefore != disabledNow);
 | |
|       }
 | |
|     } else {
 | |
|       *aShouldRepaint = true;
 | |
|     }
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // XXXdwh Not sure what can really be done here.  Can at least guess for
 | |
|   // specific widgets that they're highly unlikely to have certain states.
 | |
|   // For example, a toolbar doesn't care about any states.
 | |
|   if (!aAttribute) {
 | |
|     // Hover/focus/active changed.  Always repaint.
 | |
|     *aShouldRepaint = true;
 | |
|   }
 | |
|   else {
 | |
|     // Check the attribute to see if it's relevant.
 | |
|     // disabled, checked, dlgtype, default, etc.
 | |
|     *aShouldRepaint = false;
 | |
|     if (aAttribute == nsGkAtoms::disabled ||
 | |
|         aAttribute == nsGkAtoms::checked ||
 | |
|         aAttribute == nsGkAtoms::selected ||
 | |
|         aAttribute == nsGkAtoms::visuallyselected ||
 | |
|         aAttribute == nsGkAtoms::focused ||
 | |
|         aAttribute == nsGkAtoms::readonly ||
 | |
|         aAttribute == nsGkAtoms::_default ||
 | |
|         aAttribute == nsGkAtoms::menuactive ||
 | |
|         aAttribute == nsGkAtoms::open ||
 | |
|         aAttribute == nsGkAtoms::parentfocused)
 | |
|       *aShouldRepaint = true;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeGTK::ThemeChanged()
 | |
| {
 | |
|   memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
 | |
|   memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates));
 | |
|   memset(mBorderCacheValid, 0, sizeof(mBorderCacheValid));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
 | |
|                                       nsIFrame* aFrame,
 | |
|                                       StyleAppearance aWidgetType)
 | |
| {
 | |
|   if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
 | |
|     return false;
 | |
| 
 | |
|   if (IsWidgetScrollbarPart(aWidgetType)) {
 | |
|     ComputedStyle* cs = nsLayoutUtils::StyleForScrollbar(aFrame);
 | |
|     if (cs->StyleUI()->HasCustomScrollbars() ||
 | |
|         // We cannot handle thin scrollbar on GTK+ widget directly as well.
 | |
|         cs->StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton &&
 | |
|       StaticPrefs::layout_css_webkit_appearance_enabled()) {
 | |
|     aWidgetType = StyleAppearance::Menulist;
 | |
|   }
 | |
| 
 | |
|   switch (aWidgetType) {
 | |
|   // Combobox dropdowns don't support native theming in vertical mode.
 | |
|   case StyleAppearance::Menulist:
 | |
|   case StyleAppearance::MenulistText:
 | |
|   case StyleAppearance::MenulistTextfield:
 | |
|     if (aFrame && aFrame->GetWritingMode().IsVertical()) {
 | |
|       return false;
 | |
|     }
 | |
|     MOZ_FALLTHROUGH;
 | |
| 
 | |
|   case StyleAppearance::Button:
 | |
|   case StyleAppearance::ButtonFocus:
 | |
|   case StyleAppearance::Radio:
 | |
|   case StyleAppearance::Checkbox:
 | |
|   case StyleAppearance::Toolbox: // N/A
 | |
|   case StyleAppearance::Toolbar:
 | |
|   case StyleAppearance::Toolbarbutton:
 | |
|   case StyleAppearance::Dualbutton: // so we can override the border with 0
 | |
|   case StyleAppearance::ToolbarbuttonDropdown:
 | |
|   case StyleAppearance::ButtonArrowUp:
 | |
|   case StyleAppearance::ButtonArrowDown:
 | |
|   case StyleAppearance::ButtonArrowNext:
 | |
|   case StyleAppearance::ButtonArrowPrevious:
 | |
|   case StyleAppearance::Separator:
 | |
|   case StyleAppearance::Toolbargripper:
 | |
|   case StyleAppearance::Statusbar:
 | |
|   case StyleAppearance::Statusbarpanel:
 | |
|   case StyleAppearance::Resizerpanel:
 | |
|   case StyleAppearance::Resizer:
 | |
|   case StyleAppearance::Listbox:
 | |
|     // case StyleAppearance::Listitem:
 | |
|   case StyleAppearance::Treeview:
 | |
|     // case StyleAppearance::Treeitem:
 | |
|   case StyleAppearance::Treetwisty:
 | |
|     // case StyleAppearance::Treeline:
 | |
|     // case StyleAppearance::Treeheader:
 | |
|   case StyleAppearance::Treeheadercell:
 | |
|   case StyleAppearance::Treeheadersortarrow:
 | |
|   case StyleAppearance::Treetwistyopen:
 | |
|     case StyleAppearance::Progressbar:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|     case StyleAppearance::ProgressbarVertical:
 | |
|     case StyleAppearance::ProgresschunkVertical:
 | |
|     case StyleAppearance::Tab:
 | |
|     // case StyleAppearance::Tabpanel:
 | |
|     case StyleAppearance::Tabpanels:
 | |
|     case StyleAppearance::TabScrollArrowBack:
 | |
|     case StyleAppearance::TabScrollArrowForward:
 | |
|   case StyleAppearance::Tooltip:
 | |
|   case StyleAppearance::InnerSpinButton:
 | |
|   case StyleAppearance::Spinner:
 | |
|   case StyleAppearance::SpinnerUpbutton:
 | |
|   case StyleAppearance::SpinnerDownbutton:
 | |
|   case StyleAppearance::SpinnerTextfield:
 | |
|     // case StyleAppearance::Scrollbar:  (n/a for gtk)
 | |
|     // case StyleAppearance::ScrollbarSmall: (n/a for gtk)
 | |
|   case StyleAppearance::ScrollbarbuttonUp:
 | |
|   case StyleAppearance::ScrollbarbuttonDown:
 | |
|   case StyleAppearance::ScrollbarbuttonLeft:
 | |
|   case StyleAppearance::ScrollbarbuttonRight:
 | |
|   case StyleAppearance::ScrollbarHorizontal:
 | |
|   case StyleAppearance::ScrollbarVertical:
 | |
|   case StyleAppearance::ScrollbartrackHorizontal:
 | |
|   case StyleAppearance::ScrollbartrackVertical:
 | |
|   case StyleAppearance::ScrollbarthumbHorizontal:
 | |
|   case StyleAppearance::ScrollbarthumbVertical:
 | |
|   case StyleAppearance::NumberInput:
 | |
|   case StyleAppearance::Textfield:
 | |
|   case StyleAppearance::TextfieldMultiline:
 | |
|   case StyleAppearance::Range:
 | |
|   case StyleAppearance::RangeThumb:
 | |
|   case StyleAppearance::ScaleHorizontal:
 | |
|   case StyleAppearance::ScalethumbHorizontal:
 | |
|   case StyleAppearance::ScaleVertical:
 | |
|   case StyleAppearance::ScalethumbVertical:
 | |
|     // case StyleAppearance::Scalethumbstart:
 | |
|     // case StyleAppearance::Scalethumbend:
 | |
|     // case StyleAppearance::Scalethumbtick:
 | |
|   case StyleAppearance::CheckboxContainer:
 | |
|   case StyleAppearance::RadioContainer:
 | |
|   case StyleAppearance::CheckboxLabel:
 | |
|   case StyleAppearance::RadioLabel:
 | |
|   case StyleAppearance::Menubar:
 | |
|   case StyleAppearance::Menupopup:
 | |
|   case StyleAppearance::Menuitem:
 | |
|   case StyleAppearance::Menuarrow:
 | |
|   case StyleAppearance::Menuseparator:
 | |
|   case StyleAppearance::Checkmenuitem:
 | |
|   case StyleAppearance::Radiomenuitem:
 | |
|   case StyleAppearance::Splitter:
 | |
|   case StyleAppearance::Window:
 | |
|   case StyleAppearance::Dialog:
 | |
| #ifdef MOZ_WIDGET_GTK
 | |
|   case StyleAppearance::MozGtkInfoBar:
 | |
| #endif
 | |
|     return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 | |
| 
 | |
|   case StyleAppearance::MozWindowButtonClose:
 | |
|   case StyleAppearance::MozWindowButtonMinimize:
 | |
|   case StyleAppearance::MozWindowButtonMaximize:
 | |
|   case StyleAppearance::MozWindowButtonRestore:
 | |
|   case StyleAppearance::MozWindowTitlebar:
 | |
|   case StyleAppearance::MozWindowTitlebarMaximized:
 | |
|     // GtkHeaderBar is available on GTK 3.10+, which is used for styling
 | |
|     // title bars and title buttons.
 | |
|     return gtk_check_version(3, 10, 0) == nullptr &&
 | |
|            !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 | |
| 
 | |
|   case StyleAppearance::MenulistButton:
 | |
|   case StyleAppearance::MozMenulistButton:
 | |
|     if (aFrame && aFrame->GetWritingMode().IsVertical()) {
 | |
|       return false;
 | |
|     }
 | |
|     // "Native" dropdown buttons cause padding and margin problems, but only
 | |
|     // in HTML so allow them in XUL.
 | |
|     return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
 | |
|            !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 | |
| 
 | |
|   case StyleAppearance::FocusOutline:
 | |
|     return true;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| nsNativeThemeGTK::WidgetIsContainer(StyleAppearance aWidgetType)
 | |
| {
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton &&
 | |
|       StaticPrefs::layout_css_webkit_appearance_enabled()) {
 | |
|     aWidgetType = StyleAppearance::Menulist;
 | |
|   }
 | |
| 
 | |
|   // XXXdwh At some point flesh all of this out.
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton ||
 | |
|       aWidgetType == StyleAppearance::MozMenulistButton ||
 | |
|       aWidgetType == StyleAppearance::Radio ||
 | |
|       aWidgetType == StyleAppearance::RangeThumb ||
 | |
|       aWidgetType == StyleAppearance::Checkbox ||
 | |
|       aWidgetType == StyleAppearance::TabScrollArrowBack ||
 | |
|       aWidgetType == StyleAppearance::TabScrollArrowForward ||
 | |
|       aWidgetType == StyleAppearance::ButtonArrowUp ||
 | |
|       aWidgetType == StyleAppearance::ButtonArrowDown ||
 | |
|       aWidgetType == StyleAppearance::ButtonArrowNext ||
 | |
|       aWidgetType == StyleAppearance::ButtonArrowPrevious)
 | |
|     return false;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::ThemeDrawsFocusForWidget(StyleAppearance aWidgetType)
 | |
| {
 | |
|   if (aWidgetType == StyleAppearance::MenulistButton &&
 | |
|       StaticPrefs::layout_css_webkit_appearance_enabled()) {
 | |
|     aWidgetType = StyleAppearance::Menulist;
 | |
|   }
 | |
| 
 | |
|   if (aWidgetType == StyleAppearance::Menulist ||
 | |
|       aWidgetType == StyleAppearance::Button ||
 | |
|       aWidgetType == StyleAppearance::Treeheadercell)
 | |
|     return true;
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
 | |
| {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| nsITheme::Transparency
 | |
| nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame,
 | |
|                                         StyleAppearance aWidgetType)
 | |
| {
 | |
|   switch (aWidgetType) {
 | |
|   // These widgets always draw a default background.
 | |
|   case StyleAppearance::Menupopup:
 | |
|   case StyleAppearance::Window:
 | |
|   case StyleAppearance::Dialog:
 | |
|     return eOpaque;
 | |
|   case StyleAppearance::ScrollbarVertical:
 | |
|   case StyleAppearance::ScrollbarHorizontal:
 | |
| #ifdef MOZ_WIDGET_GTK
 | |
|     // Make scrollbar tracks opaque on the window's scroll frame to prevent
 | |
|     // leaf layers from overlapping. See bug 1179780.
 | |
|     if (!(CheckBooleanAttr(aFrame, nsGkAtoms::root_) &&
 | |
|           aFrame->PresContext()->IsRootContentDocument() &&
 | |
|           IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)))
 | |
|       return eTransparent;
 | |
| #endif
 | |
|     return eOpaque;
 | |
|   // Tooltips use gtk_paint_flat_box() on Gtk2
 | |
|   // but are shaped on Gtk3
 | |
|   case StyleAppearance::Tooltip:
 | |
|     return eTransparent;
 | |
|   default:
 | |
|     return eUnknownTransparency;
 | |
|   }
 | |
| 
 | |
| }
 |