forked from mirrors/gecko-dev
		
	(Path is actually r=froydnj.) Bug 1400459 devirtualized nsIAtom so that it is no longer a subclass of nsISupports. This means that nsAtom is now a better name for it than nsIAtom. MozReview-Commit-ID: 91U22X2NydP --HG-- rename : xpcom/ds/nsIAtom.h => xpcom/ds/nsAtom.h extra : rebase_source : ac3e904a21b8b48e74534fff964f1623ee937c67
		
			
				
	
	
		
			1984 lines
		
	
	
	
		
			65 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1984 lines
		
	
	
	
		
			65 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 "nsThemeConstants.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 "prlink.h"
 | 
						|
#include "nsIDOMHTMLInputElement.h"
 | 
						|
#include "nsGkAtoms.h"
 | 
						|
#include "nsAttrValueInlines.h"
 | 
						|
 | 
						|
#include "mozilla/EventStates.h"
 | 
						|
#include "mozilla/Services.h"
 | 
						|
 | 
						|
#include <gdk/gdkprivate.h>
 | 
						|
#include <gtk/gtk.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"
 | 
						|
 | 
						|
#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;
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
 | 
						|
                                                             nsIObserver)
 | 
						|
 | 
						|
static int gLastGdkError;
 | 
						|
 | 
						|
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 {
 | 
						|
    NS_NOTREACHED("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, uint8_t aWidgetType) {
 | 
						|
  MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT);
 | 
						|
  return (aDisabledVector[aWidgetType >> 3] & (1 << (aWidgetType & 7))) != 0;
 | 
						|
}
 | 
						|
 | 
						|
static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
 | 
						|
  MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT);
 | 
						|
  aDisabledVector[aWidgetType >> 3] |= (1 << (aWidgetType & 7));
 | 
						|
}
 | 
						|
 | 
						|
static inline uint16_t
 | 
						|
GetWidgetStateKey(uint8_t aWidgetType, GtkWidgetState *aWidgetState)
 | 
						|
{
 | 
						|
  return (aWidgetState->active |
 | 
						|
          aWidgetState->focused << 1 |
 | 
						|
          aWidgetState->inHover << 2 |
 | 
						|
          aWidgetState->disabled << 3 |
 | 
						|
          aWidgetState->isDefault << 4 |
 | 
						|
          aWidgetType << 5);
 | 
						|
}
 | 
						|
 | 
						|
static bool IsWidgetStateSafe(uint8_t* aSafeVector,
 | 
						|
                                uint8_t aWidgetType,
 | 
						|
                                GtkWidgetState *aWidgetState)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aWidgetType < ThemeWidgetType_COUNT);
 | 
						|
  uint16_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
 | 
						|
  return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0;
 | 
						|
}
 | 
						|
 | 
						|
static void SetWidgetStateSafe(uint8_t *aSafeVector,
 | 
						|
                               uint8_t aWidgetType,
 | 
						|
                               GtkWidgetState *aWidgetState)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aWidgetType < ThemeWidgetType_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,
 | 
						|
                                            uint8_t aWidgetType)
 | 
						|
{
 | 
						|
  return ((aCurpos == 0 && (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP ||
 | 
						|
                            aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT))
 | 
						|
      || (aCurpos == aMaxpos && (aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN ||
 | 
						|
                                 aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT)));
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame,
 | 
						|
                                       WidgetNodeType& aGtkWidgetType,
 | 
						|
                                       GtkWidgetState* aState,
 | 
						|
                                       gint* aWidgetFlags)
 | 
						|
{
 | 
						|
  if (aState) {
 | 
						|
    // For XUL checkboxes and radio buttons, the state of the parent
 | 
						|
    // determines our state.
 | 
						|
    nsIFrame *stateFrame = aFrame;
 | 
						|
    if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX ||
 | 
						|
                                     aWidgetType == NS_THEME_RADIO)) ||
 | 
						|
                   aWidgetType == NS_THEME_CHECKBOX_LABEL ||
 | 
						|
                   aWidgetType == NS_THEME_RADIO_LABEL)) {
 | 
						|
 | 
						|
      nsAtom* atom = nullptr;
 | 
						|
      if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
 | 
						|
        if (aWidgetType == NS_THEME_CHECKBOX_LABEL ||
 | 
						|
            aWidgetType == NS_THEME_RADIO_LABEL) {
 | 
						|
          // 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 == NS_THEME_CHECKBOX ||
 | 
						|
                    aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked
 | 
						|
                                                            : nsGkAtoms::selected;
 | 
						|
          }
 | 
						|
          *aWidgetFlags = CheckBooleanAttr(aFrame, atom);
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        if (aWidgetFlags) {
 | 
						|
          nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent()));
 | 
						|
          *aWidgetFlags = 0;
 | 
						|
          if (inputElt) {
 | 
						|
            bool isHTMLChecked;
 | 
						|
            inputElt->GetChecked(&isHTMLChecked);
 | 
						|
            if (isHTMLChecked)
 | 
						|
              *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
 | 
						|
          }
 | 
						|
 | 
						|
          if (GetIndeterminate(aFrame))
 | 
						|
            *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } else if (aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN ||
 | 
						|
               aWidgetType == NS_THEME_TREEHEADERSORTARROW ||
 | 
						|
               aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS ||
 | 
						|
               aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
 | 
						|
               aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
 | 
						|
               aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) {
 | 
						|
      // 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->inHover = eventState.HasState(NS_EVENT_STATE_HOVER);
 | 
						|
    aState->isDefault = IsDefaultButton(aFrame);
 | 
						|
    aState->canDefault = FALSE; // XXX fix me
 | 
						|
    aState->depressed = FALSE;
 | 
						|
 | 
						|
    if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
 | 
						|
      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 == NS_THEME_BUTTON ||
 | 
						|
               aWidgetType == NS_THEME_TOOLBARBUTTON ||
 | 
						|
               aWidgetType == NS_THEME_DUALBUTTON ||
 | 
						|
               aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN ||
 | 
						|
               aWidgetType == NS_THEME_MENULIST ||
 | 
						|
               aWidgetType == NS_THEME_MENULIST_BUTTON) {
 | 
						|
      aState->active &= aState->inHover;
 | 
						|
    }
 | 
						|
 | 
						|
    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 == NS_THEME_NUMBER_INPUT ||
 | 
						|
          aWidgetType == NS_THEME_TEXTFIELD ||
 | 
						|
          aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
 | 
						|
          aWidgetType == NS_THEME_MENULIST_TEXTFIELD ||
 | 
						|
          aWidgetType == NS_THEME_SPINNER_TEXTFIELD ||
 | 
						|
          aWidgetType == NS_THEME_RADIO_CONTAINER ||
 | 
						|
          aWidgetType == NS_THEME_RADIO_LABEL) {
 | 
						|
        aState->focused = IsFocused(aFrame);
 | 
						|
      } else if (aWidgetType == NS_THEME_RADIO ||
 | 
						|
                 aWidgetType == NS_THEME_CHECKBOX) {
 | 
						|
        // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
 | 
						|
        aState->focused = FALSE;
 | 
						|
      }
 | 
						|
 | 
						|
      if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL ||
 | 
						|
          aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) {
 | 
						|
        // 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 == NS_THEME_SCROLLBARBUTTON_UP ||
 | 
						|
          aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN ||
 | 
						|
          aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT ||
 | 
						|
          aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) {
 | 
						|
        // 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 (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP < 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 == NS_THEME_MENUITEM ||
 | 
						|
          aWidgetType == NS_THEME_CHECKMENUITEM ||
 | 
						|
          aWidgetType == NS_THEME_RADIOMENUITEM ||
 | 
						|
          aWidgetType == NS_THEME_MENUSEPARATOR ||
 | 
						|
          aWidgetType == NS_THEME_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 == NS_THEME_CHECKMENUITEM ||
 | 
						|
            aWidgetType == NS_THEME_RADIOMENUITEM) {
 | 
						|
          *aWidgetFlags = 0;
 | 
						|
          if (aFrame && aFrame->GetContent()) {
 | 
						|
            *aWidgetFlags = aFrame->GetContent()->
 | 
						|
              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 == NS_THEME_BUTTON ||
 | 
						|
          aWidgetType == NS_THEME_TOOLBARBUTTON ||
 | 
						|
          aWidgetType == NS_THEME_DUALBUTTON ||
 | 
						|
          aWidgetType == NS_THEME_TOOLBARBUTTON_DROPDOWN ||
 | 
						|
          aWidgetType == NS_THEME_MENULIST ||
 | 
						|
          aWidgetType == NS_THEME_MENULIST_BUTTON) {
 | 
						|
        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 == NS_THEME_MENULIST_BUTTON && aWidgetFlags) {
 | 
						|
        *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  switch (aWidgetType) {
 | 
						|
  case NS_THEME_BUTTON:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_RELIEF_NORMAL;
 | 
						|
    aGtkWidgetType = MOZ_GTK_BUTTON;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TOOLBARBUTTON:
 | 
						|
  case NS_THEME_DUALBUTTON:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_RELIEF_NONE;
 | 
						|
    aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON;
 | 
						|
    break;
 | 
						|
  case NS_THEME_FOCUS_OUTLINE:
 | 
						|
    aGtkWidgetType = MOZ_GTK_ENTRY;
 | 
						|
    break;
 | 
						|
  case NS_THEME_CHECKBOX:
 | 
						|
  case NS_THEME_RADIO:
 | 
						|
    aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_UP:
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_DOWN:
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_LEFT:
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_RIGHT:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBAR_VERTICAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_VERTICAL;
 | 
						|
    if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque)
 | 
						|
        *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE;
 | 
						|
    else
 | 
						|
        *aWidgetFlags = 0;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBAR_HORIZONTAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_HORIZONTAL;
 | 
						|
    if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque)
 | 
						|
        *aWidgetFlags = MOZ_GTK_TRACK_OPAQUE;
 | 
						|
    else
 | 
						|
        *aWidgetFlags = 0;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARTRACK_VERTICAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARTHUMB_VERTICAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SPINNER:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SPINBUTTON;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SPINNER_UPBUTTON:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SPINNER_DOWNBUTTON:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SPINNER_TEXTFIELD:
 | 
						|
    aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY;
 | 
						|
    break;
 | 
						|
  case NS_THEME_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 NS_THEME_RANGE_THUMB:
 | 
						|
    {
 | 
						|
      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 NS_THEME_SCALE_HORIZONTAL:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCALETHUMB_HORIZONTAL:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCALE_VERTICAL:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SEPARATOR:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCALETHUMB_VERTICAL:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
 | 
						|
    aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TOOLBARGRIPPER:
 | 
						|
    aGtkWidgetType = MOZ_GTK_GRIPPER;
 | 
						|
    break;
 | 
						|
  case NS_THEME_RESIZER:
 | 
						|
    aGtkWidgetType = MOZ_GTK_RESIZER;
 | 
						|
    break;
 | 
						|
  case NS_THEME_NUMBER_INPUT:
 | 
						|
  case NS_THEME_TEXTFIELD:
 | 
						|
    aGtkWidgetType = MOZ_GTK_ENTRY;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TEXTFIELD_MULTILINE:
 | 
						|
#if (MOZ_WIDGET_GTK == 3)
 | 
						|
    aGtkWidgetType = MOZ_GTK_TEXT_VIEW;
 | 
						|
#else
 | 
						|
    aGtkWidgetType = MOZ_GTK_ENTRY;
 | 
						|
#endif
 | 
						|
    break;
 | 
						|
  case NS_THEME_LISTBOX:
 | 
						|
  case NS_THEME_TREEVIEW:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TREEVIEW;
 | 
						|
    break;
 | 
						|
  case NS_THEME_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 NS_THEME_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 NS_THEME_TREETWISTY:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_EXPANDER_COLLAPSED;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TREETWISTYOPEN:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = GTK_EXPANDER_EXPANDED;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENULIST:
 | 
						|
    aGtkWidgetType = MOZ_GTK_DROPDOWN;
 | 
						|
    if (aWidgetFlags)
 | 
						|
        *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENULIST_TEXT:
 | 
						|
    return false; // nothing to do, but prevents the bg from being drawn
 | 
						|
  case NS_THEME_MENULIST_TEXTFIELD:
 | 
						|
    aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENULIST_BUTTON:
 | 
						|
    aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TOOLBARBUTTON_DROPDOWN:
 | 
						|
  case NS_THEME_BUTTON_ARROW_DOWN:
 | 
						|
  case NS_THEME_BUTTON_ARROW_UP:
 | 
						|
  case NS_THEME_BUTTON_ARROW_NEXT:
 | 
						|
  case NS_THEME_BUTTON_ARROW_PREVIOUS:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
 | 
						|
    if (aWidgetFlags) {
 | 
						|
      *aWidgetFlags = GTK_ARROW_DOWN;
 | 
						|
 | 
						|
      if (aWidgetType == NS_THEME_BUTTON_ARROW_UP)
 | 
						|
        *aWidgetFlags = GTK_ARROW_UP;
 | 
						|
      else if (aWidgetType == NS_THEME_BUTTON_ARROW_NEXT)
 | 
						|
        *aWidgetFlags = GTK_ARROW_RIGHT;
 | 
						|
      else if (aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
 | 
						|
        *aWidgetFlags = GTK_ARROW_LEFT;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_CHECKBOX_CONTAINER:
 | 
						|
    aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER;
 | 
						|
    break;
 | 
						|
  case NS_THEME_RADIO_CONTAINER:
 | 
						|
    aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER;
 | 
						|
    break;
 | 
						|
  case NS_THEME_CHECKBOX_LABEL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_RADIO_LABEL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TOOLBAR:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TOOLBAR;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TOOLTIP:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TOOLTIP;
 | 
						|
    break;
 | 
						|
  case NS_THEME_STATUSBARPANEL:
 | 
						|
  case NS_THEME_RESIZERPANEL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_FRAME;
 | 
						|
    break;
 | 
						|
  case NS_THEME_PROGRESSBAR:
 | 
						|
  case NS_THEME_PROGRESSBAR_VERTICAL:
 | 
						|
    aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
 | 
						|
    break;
 | 
						|
  case NS_THEME_PROGRESSCHUNK:
 | 
						|
  case NS_THEME_PROGRESSCHUNK_VERTICAL:
 | 
						|
    {
 | 
						|
      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 NS_THEME_TAB_SCROLL_ARROW_BACK:
 | 
						|
  case NS_THEME_TAB_SCROLL_ARROW_FORWARD:
 | 
						|
    if (aWidgetFlags)
 | 
						|
      *aWidgetFlags = aWidgetType == NS_THEME_TAB_SCROLL_ARROW_BACK ?
 | 
						|
                        GTK_ARROW_LEFT : GTK_ARROW_RIGHT;
 | 
						|
    aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TABPANELS:
 | 
						|
    aGtkWidgetType = MOZ_GTK_TABPANELS;
 | 
						|
    break;
 | 
						|
  case NS_THEME_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 NS_THEME_SPLITTER:
 | 
						|
    if (IsHorizontal(aFrame))
 | 
						|
      aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
 | 
						|
    else 
 | 
						|
      aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUBAR:
 | 
						|
    aGtkWidgetType = MOZ_GTK_MENUBAR;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUPOPUP:
 | 
						|
    aGtkWidgetType = MOZ_GTK_MENUPOPUP;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUITEM:
 | 
						|
    {
 | 
						|
      nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
 | 
						|
      if (menuFrame && menuFrame->IsOnMenuBar()) {
 | 
						|
        aGtkWidgetType = MOZ_GTK_MENUBARITEM;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    aGtkWidgetType = MOZ_GTK_MENUITEM;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUSEPARATOR:
 | 
						|
    aGtkWidgetType = MOZ_GTK_MENUSEPARATOR;
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUARROW:
 | 
						|
    aGtkWidgetType = MOZ_GTK_MENUARROW;
 | 
						|
    break;
 | 
						|
  case NS_THEME_CHECKMENUITEM:
 | 
						|
    aGtkWidgetType = MOZ_GTK_CHECKMENUITEM;
 | 
						|
    break;
 | 
						|
  case NS_THEME_RADIOMENUITEM:
 | 
						|
    aGtkWidgetType = MOZ_GTK_RADIOMENUITEM;
 | 
						|
    break;
 | 
						|
  case NS_THEME_WINDOW:
 | 
						|
  case NS_THEME_DIALOG:
 | 
						|
    aGtkWidgetType = MOZ_GTK_WINDOW;
 | 
						|
    break;
 | 
						|
  case NS_THEME_GTK_INFO_BAR:
 | 
						|
    aGtkWidgetType = MOZ_GTK_INFO_BAR;
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
#if (MOZ_WIDGET_GTK == 2)
 | 
						|
class ThemeRenderer : public gfxGdkNativeRenderer {
 | 
						|
public:
 | 
						|
  ThemeRenderer(GtkWidgetState aState, WidgetNodeType aGTKWidgetType,
 | 
						|
                gint aFlags, GtkTextDirection aDirection,
 | 
						|
                const GdkRectangle& aGDKRect, const GdkRectangle& aGDKClip)
 | 
						|
    : mState(aState), mGTKWidgetType(aGTKWidgetType), mFlags(aFlags),
 | 
						|
      mDirection(aDirection), mGDKRect(aGDKRect), mGDKClip(aGDKClip) {}
 | 
						|
  nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX, gint offsetY,
 | 
						|
                       GdkRectangle * clipRects, uint32_t numClipRects);
 | 
						|
private:
 | 
						|
  GtkWidgetState mState;
 | 
						|
  WidgetNodeType mGTKWidgetType;
 | 
						|
  gint mFlags;
 | 
						|
  GtkTextDirection mDirection;
 | 
						|
  const GdkRectangle& mGDKRect;
 | 
						|
  const GdkRectangle& mGDKClip;
 | 
						|
};
 | 
						|
 | 
						|
nsresult
 | 
						|
ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX, 
 | 
						|
        gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects)
 | 
						|
{
 | 
						|
  GdkRectangle gdk_rect = mGDKRect;
 | 
						|
  gdk_rect.x += offsetX;
 | 
						|
  gdk_rect.y += offsetY;
 | 
						|
 | 
						|
  GdkRectangle gdk_clip = mGDKClip;
 | 
						|
  gdk_clip.x += offsetX;
 | 
						|
  gdk_clip.y += offsetY;
 | 
						|
 | 
						|
  GdkRectangle surfaceRect;
 | 
						|
  surfaceRect.x = 0;
 | 
						|
  surfaceRect.y = 0;
 | 
						|
  gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height);
 | 
						|
  gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
 | 
						|
  
 | 
						|
  NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
 | 
						|
  moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,
 | 
						|
                       &mState, mFlags, mDirection);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
#else
 | 
						|
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);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
bool
 | 
						|
nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t 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 NS_THEME_SCROLLBARTHUMB_VERTICAL:
 | 
						|
    aExtra->top = aExtra->bottom = 1;
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
 | 
						|
    aExtra->left = aExtra->right = 1;
 | 
						|
    break;
 | 
						|
 | 
						|
  // Include the indicator spacing (the padding around the control).
 | 
						|
  case NS_THEME_CHECKBOX:
 | 
						|
  case NS_THEME_RADIO:
 | 
						|
    {
 | 
						|
      gint indicator_size, indicator_spacing;
 | 
						|
 | 
						|
      if (aWidgetType == NS_THEME_CHECKBOX) {
 | 
						|
        moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
 | 
						|
      } else {
 | 
						|
        moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
 | 
						|
      }
 | 
						|
 | 
						|
      aExtra->top = indicator_spacing;
 | 
						|
      aExtra->right = indicator_spacing;
 | 
						|
      aExtra->bottom = indicator_spacing;
 | 
						|
      aExtra->left = indicator_spacing;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  case NS_THEME_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 NS_THEME_FOCUS_OUTLINE:
 | 
						|
    {
 | 
						|
      moz_gtk_get_focus_outline_size(&aExtra->left, &aExtra->top);
 | 
						|
      aExtra->right = aExtra->left;
 | 
						|
      aExtra->bottom = aExtra->top;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  case NS_THEME_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 = ScreenHelperGTK::GetGTKMonitorScaleFactor();
 | 
						|
  aExtra->top *= scale;
 | 
						|
  aExtra->right *= scale;
 | 
						|
  aExtra->bottom *= scale;
 | 
						|
  aExtra->left *= scale;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext,
 | 
						|
                                       nsIFrame* aFrame,
 | 
						|
                                       uint8_t 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 = ScreenHelperGTK::GetGTKMonitorScaleFactor();
 | 
						|
 | 
						|
  // 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};
 | 
						|
 | 
						|
  // translate everything so (0,0) is the top left of the drawingRect
 | 
						|
  gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
 | 
						|
 | 
						|
#if (MOZ_WIDGET_GTK == 2)
 | 
						|
  gfxContextAutoSaveRestore autoSR(ctx);
 | 
						|
  gfxMatrix matrix;
 | 
						|
  if (!snapped) { // else rects are in device coords
 | 
						|
    matrix = ctx->CurrentMatrix();
 | 
						|
  }
 | 
						|
  matrix.Translate(origin);
 | 
						|
  matrix.Scale(scaleFactor, scaleFactor); // Draw in GDK coords
 | 
						|
  ctx->SetMatrix(matrix);
 | 
						|
 | 
						|
  // The gdk_clip is just advisory here, meaning "you don't
 | 
						|
  // need to draw outside this rect if you don't feel like it!"
 | 
						|
  GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height};
 | 
						|
 | 
						|
  ThemeRenderer renderer(state, gtkWidgetType, flags, direction,
 | 
						|
                         gdk_rect, gdk_clip);
 | 
						|
 | 
						|
  // Some themes (e.g. Clearlooks) just don't clip properly to any
 | 
						|
  // clip rect we provide, so we cannot advertise support for clipping within
 | 
						|
  // the widget bounds.
 | 
						|
  uint32_t rendererFlags = 0;
 | 
						|
  if (transparency == eOpaque) {
 | 
						|
    rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE;
 | 
						|
  }
 | 
						|
 | 
						|
  // GtkStyles (used by the widget drawing backend) are created for a
 | 
						|
  // particular colormap/visual.
 | 
						|
  GdkColormap* colormap = moz_gtk_widget_get_colormap();
 | 
						|
 | 
						|
  renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
 | 
						|
#else 
 | 
						|
  DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
 | 
						|
                     state, gtkWidgetType, flags, direction, scaleFactor,
 | 
						|
                     snapped, ToPoint(origin), drawingRect.Size(),
 | 
						|
                     gdk_rect, transparency);
 | 
						|
#endif
 | 
						|
 | 
						|
  if (!safeState) {
 | 
						|
    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",
 | 
						|
             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;
 | 
						|
}
 | 
						|
 | 
						|
WidgetNodeType
 | 
						|
nsNativeThemeGTK::NativeThemeToGtkTheme(uint8_t 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, uint8_t aWidgetType,
 | 
						|
                                        GtkTextDirection aDirection,
 | 
						|
                                        nsIntMargin* 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 (aWidgetType != MOZ_GTK_DROPDOWN) { // depends on aDirection
 | 
						|
        mBorderCacheValid[cacheIndex] |= cacheBit;
 | 
						|
        mBorderCache[gtkWidgetType] = *aResult;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
 | 
						|
                                  uint8_t aWidgetType, nsIntMargin* aResult)
 | 
						|
{
 | 
						|
  GtkTextDirection direction = GetTextDirection(aFrame);
 | 
						|
  aResult->top = aResult->left = aResult->right = aResult->bottom = 0;
 | 
						|
  switch (aWidgetType) {
 | 
						|
  case NS_THEME_SCROLLBAR_HORIZONTAL:
 | 
						|
  case NS_THEME_SCROLLBAR_VERTICAL:
 | 
						|
    {
 | 
						|
      GtkOrientation orientation =
 | 
						|
        aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
 | 
						|
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | 
						|
      const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
 | 
						|
 | 
						|
      const GtkBorder& border = metrics->border.scrollbar;
 | 
						|
      aResult->top = border.top;
 | 
						|
      aResult->right = border.right;
 | 
						|
      aResult->bottom = border.bottom;
 | 
						|
      aResult->left = border.left;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
 | 
						|
  case NS_THEME_SCROLLBARTRACK_VERTICAL:
 | 
						|
    {
 | 
						|
      GtkOrientation orientation =
 | 
						|
        aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL ?
 | 
						|
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | 
						|
      const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
 | 
						|
 | 
						|
      const GtkBorder& border = metrics->border.track;
 | 
						|
      aResult->top = border.top;
 | 
						|
      aResult->right = border.right;
 | 
						|
      aResult->bottom = border.bottom;
 | 
						|
      aResult->left = border.left;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_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 NS_THEME_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 NS_THEME_TAB:
 | 
						|
    {
 | 
						|
      WidgetNodeType gtkWidgetType;
 | 
						|
      gint flags;
 | 
						|
 | 
						|
      if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
 | 
						|
                                &flags))
 | 
						|
        return NS_OK;
 | 
						|
 | 
						|
      moz_gtk_get_tab_border(&aResult->left, &aResult->top,
 | 
						|
                             &aResult->right, &aResult->bottom, direction,
 | 
						|
                             (GtkTabFlags)flags, gtkWidgetType);
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUITEM:
 | 
						|
  case NS_THEME_CHECKMENUITEM:
 | 
						|
  case NS_THEME_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, aResult);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
 | 
						|
  aResult->top *= scale;
 | 
						|
  aResult->right *= scale;
 | 
						|
  aResult->bottom *= scale;
 | 
						|
  aResult->left *= scale;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
 | 
						|
                                   nsIFrame* aFrame, uint8_t aWidgetType,
 | 
						|
                                   nsIntMargin* aResult)
 | 
						|
{
 | 
						|
  switch (aWidgetType) {
 | 
						|
    case NS_THEME_BUTTON_FOCUS:
 | 
						|
    case NS_THEME_TOOLBARBUTTON:
 | 
						|
    case NS_THEME_DUALBUTTON:
 | 
						|
    case NS_THEME_TAB_SCROLL_ARROW_BACK:
 | 
						|
    case NS_THEME_TAB_SCROLL_ARROW_FORWARD:
 | 
						|
    case NS_THEME_MENULIST_BUTTON:
 | 
						|
    case NS_THEME_TOOLBARBUTTON_DROPDOWN:
 | 
						|
    case NS_THEME_BUTTON_ARROW_UP:
 | 
						|
    case NS_THEME_BUTTON_ARROW_DOWN:
 | 
						|
    case NS_THEME_BUTTON_ARROW_NEXT:
 | 
						|
    case NS_THEME_BUTTON_ARROW_PREVIOUS:
 | 
						|
    case NS_THEME_RANGE_THUMB:
 | 
						|
    // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
 | 
						|
    // and have a meaningful baseline, so they can't have
 | 
						|
    // author-specified padding.
 | 
						|
    case NS_THEME_CHECKBOX:
 | 
						|
    case NS_THEME_RADIO:
 | 
						|
      aResult->SizeTo(0, 0, 0, 0);
 | 
						|
      return true;
 | 
						|
    case NS_THEME_MENUITEM:
 | 
						|
    case NS_THEME_CHECKMENUITEM:
 | 
						|
    case NS_THEME_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 == NS_THEME_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 = ScreenHelperGTK::GetGTKMonitorScaleFactor();
 | 
						|
        aResult->top *= scale;
 | 
						|
        aResult->right *= scale;
 | 
						|
        aResult->bottom *= scale;
 | 
						|
        aResult->left *= scale;
 | 
						|
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
 | 
						|
                                    nsIFrame* aFrame, uint8_t 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, uint8_t aWidgetType,
 | 
						|
                                       LayoutDeviceIntSize* aResult,
 | 
						|
                                       bool* aIsOverridable)
 | 
						|
{
 | 
						|
  aResult->width = aResult->height = 0;
 | 
						|
  *aIsOverridable = true;
 | 
						|
 | 
						|
  switch (aWidgetType) {
 | 
						|
    case NS_THEME_SCROLLBARBUTTON_UP:
 | 
						|
    case NS_THEME_SCROLLBARBUTTON_DOWN:
 | 
						|
      {
 | 
						|
        const ScrollbarGTKMetrics* metrics =
 | 
						|
          GetScrollbarMetrics(GTK_ORIENTATION_VERTICAL);
 | 
						|
 | 
						|
        aResult->width = metrics->size.button.width;
 | 
						|
        aResult->height = metrics->size.button.height;
 | 
						|
        *aIsOverridable = false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case NS_THEME_SCROLLBARBUTTON_LEFT:
 | 
						|
    case NS_THEME_SCROLLBARBUTTON_RIGHT:
 | 
						|
      {
 | 
						|
        const ScrollbarGTKMetrics* metrics =
 | 
						|
          GetScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL);
 | 
						|
 | 
						|
        aResult->width = metrics->size.button.width;
 | 
						|
        aResult->height = metrics->size.button.height;
 | 
						|
        *aIsOverridable = false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case NS_THEME_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 NS_THEME_SCROLLBAR_HORIZONTAL:
 | 
						|
    case NS_THEME_SCROLLBAR_VERTICAL:
 | 
						|
    {
 | 
						|
      /* 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 == NS_THEME_SCROLLBAR_HORIZONTAL ?
 | 
						|
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | 
						|
      const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
 | 
						|
 | 
						|
      aResult->width = metrics->size.scrollbar.width;
 | 
						|
      aResult->height = metrics->size.scrollbar.height;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
    case NS_THEME_SCROLLBARTHUMB_VERTICAL:
 | 
						|
    case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
 | 
						|
      {
 | 
						|
        GtkOrientation orientation =
 | 
						|
          aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ?
 | 
						|
          GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 | 
						|
        const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
 | 
						|
 | 
						|
        aResult->width = metrics->size.thumb.width;
 | 
						|
        aResult->height = metrics->size.thumb.height;
 | 
						|
        *aIsOverridable = false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case NS_THEME_RANGE_THUMB:
 | 
						|
      {
 | 
						|
        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 NS_THEME_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 NS_THEME_SCALETHUMB_HORIZONTAL:
 | 
						|
    case NS_THEME_SCALETHUMB_VERTICAL:
 | 
						|
      {
 | 
						|
        gint thumb_length, thumb_height;
 | 
						|
 | 
						|
        if (aWidgetType == NS_THEME_SCALETHUMB_VERTICAL) {
 | 
						|
          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 NS_THEME_TAB_SCROLL_ARROW_BACK:
 | 
						|
    case NS_THEME_TAB_SCROLL_ARROW_FORWARD:
 | 
						|
      {
 | 
						|
        moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
 | 
						|
        *aIsOverridable = false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
  case NS_THEME_MENULIST_BUTTON:
 | 
						|
    {
 | 
						|
      moz_gtk_get_combo_box_entry_button_size(&aResult->width,
 | 
						|
                                              &aResult->height);
 | 
						|
      *aIsOverridable = false;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_MENUSEPARATOR:
 | 
						|
    {
 | 
						|
      gint separator_height;
 | 
						|
 | 
						|
      moz_gtk_get_menu_separator_height(&separator_height);
 | 
						|
      aResult->height = separator_height;
 | 
						|
    
 | 
						|
      *aIsOverridable = false;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_CHECKBOX:
 | 
						|
  case NS_THEME_RADIO:
 | 
						|
    {
 | 
						|
      gint indicator_size, indicator_spacing;
 | 
						|
 | 
						|
      if (aWidgetType == NS_THEME_CHECKBOX) {
 | 
						|
        moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
 | 
						|
      } else {
 | 
						|
        moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
 | 
						|
      }
 | 
						|
 | 
						|
      // Include space for the indicator and the padding around it.
 | 
						|
      aResult->width = indicator_size;
 | 
						|
      aResult->height = indicator_size;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_TOOLBARBUTTON_DROPDOWN:
 | 
						|
  case NS_THEME_BUTTON_ARROW_UP:
 | 
						|
  case NS_THEME_BUTTON_ARROW_DOWN:
 | 
						|
  case NS_THEME_BUTTON_ARROW_NEXT:
 | 
						|
  case NS_THEME_BUTTON_ARROW_PREVIOUS:
 | 
						|
    {
 | 
						|
      moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW,
 | 
						|
                             &aResult->width, &aResult->height);
 | 
						|
      *aIsOverridable = false;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_CHECKBOX_CONTAINER:
 | 
						|
  case NS_THEME_RADIO_CONTAINER:
 | 
						|
  case NS_THEME_CHECKBOX_LABEL:
 | 
						|
  case NS_THEME_RADIO_LABEL:
 | 
						|
  case NS_THEME_BUTTON:
 | 
						|
  case NS_THEME_MENULIST:
 | 
						|
  case NS_THEME_TOOLBARBUTTON:
 | 
						|
  case NS_THEME_TREEHEADERCELL:
 | 
						|
    {
 | 
						|
      if (aWidgetType == NS_THEME_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.
 | 
						|
 | 
						|
      nsIntMargin border;
 | 
						|
      nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
 | 
						|
                                        aFrame, aWidgetType, &border);
 | 
						|
      aResult->width += border.left + border.right;
 | 
						|
      aResult->height += border.top + border.bottom;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
#if (MOZ_WIDGET_GTK == 3)
 | 
						|
  case NS_THEME_NUMBER_INPUT:
 | 
						|
  case NS_THEME_TEXTFIELD:
 | 
						|
    {
 | 
						|
      moz_gtk_get_entry_min_height(&aResult->height);
 | 
						|
    }
 | 
						|
    break;
 | 
						|
#endif
 | 
						|
  case NS_THEME_SEPARATOR:
 | 
						|
    {
 | 
						|
      gint separator_width;
 | 
						|
    
 | 
						|
      moz_gtk_get_toolbar_separator_width(&separator_width);
 | 
						|
    
 | 
						|
      aResult->width = separator_width;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case NS_THEME_SPINNER:
 | 
						|
    // hard code these sizes
 | 
						|
    aResult->width = 14;
 | 
						|
    aResult->height = 26;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TREEHEADERSORTARROW:
 | 
						|
  case NS_THEME_SPINNER_UPBUTTON:
 | 
						|
  case NS_THEME_SPINNER_DOWNBUTTON:
 | 
						|
    // hard code these sizes
 | 
						|
    aResult->width = 14;
 | 
						|
    aResult->height = 13;
 | 
						|
    break;
 | 
						|
  case NS_THEME_RESIZER:
 | 
						|
    // same as Windows to make our lives easier
 | 
						|
    aResult->width = aResult->height = 15;
 | 
						|
    *aIsOverridable = false;
 | 
						|
    break;
 | 
						|
  case NS_THEME_TREETWISTY:
 | 
						|
  case NS_THEME_TREETWISTYOPEN:
 | 
						|
    {
 | 
						|
      gint expander_size;
 | 
						|
 | 
						|
      moz_gtk_get_treeview_expander_size(&expander_size);
 | 
						|
      aResult->width = aResult->height = expander_size;
 | 
						|
      *aIsOverridable = false;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  *aResult = *aResult * ScreenHelperGTK::GetGTKMonitorScaleFactor();
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
 | 
						|
                                     nsAtom* aAttribute, bool* aShouldRepaint,
 | 
						|
                                     const nsAttrValue* aOldValue)
 | 
						|
{
 | 
						|
  // Some widget types just never change state.
 | 
						|
  if (aWidgetType == NS_THEME_TOOLBOX ||
 | 
						|
      aWidgetType == NS_THEME_TOOLBAR ||
 | 
						|
      aWidgetType == NS_THEME_STATUSBAR ||
 | 
						|
      aWidgetType == NS_THEME_STATUSBARPANEL ||
 | 
						|
      aWidgetType == NS_THEME_RESIZERPANEL ||
 | 
						|
      aWidgetType == NS_THEME_PROGRESSCHUNK ||
 | 
						|
      aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL ||
 | 
						|
      aWidgetType == NS_THEME_PROGRESSBAR ||
 | 
						|
      aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
 | 
						|
      aWidgetType == NS_THEME_MENUBAR ||
 | 
						|
      aWidgetType == NS_THEME_MENUPOPUP ||
 | 
						|
      aWidgetType == NS_THEME_TOOLTIP ||
 | 
						|
      aWidgetType == NS_THEME_MENUSEPARATOR ||
 | 
						|
      aWidgetType == NS_THEME_WINDOW ||
 | 
						|
      aWidgetType == NS_THEME_DIALOG) {
 | 
						|
    *aShouldRepaint = false;
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL ||
 | 
						|
       aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) &&
 | 
						|
       aAttribute == nsGkAtoms::active) {
 | 
						|
    *aShouldRepaint = true;
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((aWidgetType == NS_THEME_SCROLLBARBUTTON_UP ||
 | 
						|
       aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN ||
 | 
						|
       aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT ||
 | 
						|
       aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) &&
 | 
						|
      (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,
 | 
						|
                                      uint8_t aWidgetType)
 | 
						|
{
 | 
						|
  if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
 | 
						|
    return false;
 | 
						|
 | 
						|
  switch (aWidgetType) {
 | 
						|
  // Combobox dropdowns don't support native theming in vertical mode.
 | 
						|
  case NS_THEME_MENULIST:
 | 
						|
  case NS_THEME_MENULIST_TEXT:
 | 
						|
  case NS_THEME_MENULIST_TEXTFIELD:
 | 
						|
    if (aFrame && aFrame->GetWritingMode().IsVertical()) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    MOZ_FALLTHROUGH;
 | 
						|
 | 
						|
  case NS_THEME_BUTTON:
 | 
						|
  case NS_THEME_BUTTON_FOCUS:
 | 
						|
  case NS_THEME_RADIO:
 | 
						|
  case NS_THEME_CHECKBOX:
 | 
						|
  case NS_THEME_TOOLBOX: // N/A
 | 
						|
  case NS_THEME_TOOLBAR:
 | 
						|
  case NS_THEME_TOOLBARBUTTON:
 | 
						|
  case NS_THEME_DUALBUTTON: // so we can override the border with 0
 | 
						|
  case NS_THEME_TOOLBARBUTTON_DROPDOWN:
 | 
						|
  case NS_THEME_BUTTON_ARROW_UP:
 | 
						|
  case NS_THEME_BUTTON_ARROW_DOWN:
 | 
						|
  case NS_THEME_BUTTON_ARROW_NEXT:
 | 
						|
  case NS_THEME_BUTTON_ARROW_PREVIOUS:
 | 
						|
  case NS_THEME_SEPARATOR:
 | 
						|
  case NS_THEME_TOOLBARGRIPPER:
 | 
						|
  case NS_THEME_STATUSBAR:
 | 
						|
  case NS_THEME_STATUSBARPANEL:
 | 
						|
  case NS_THEME_RESIZERPANEL:
 | 
						|
  case NS_THEME_RESIZER:
 | 
						|
  case NS_THEME_LISTBOX:
 | 
						|
    // case NS_THEME_LISTITEM:
 | 
						|
  case NS_THEME_TREEVIEW:
 | 
						|
    // case NS_THEME_TREEITEM:
 | 
						|
  case NS_THEME_TREETWISTY:
 | 
						|
    // case NS_THEME_TREELINE:
 | 
						|
    // case NS_THEME_TREEHEADER:
 | 
						|
  case NS_THEME_TREEHEADERCELL:
 | 
						|
  case NS_THEME_TREEHEADERSORTARROW:
 | 
						|
  case NS_THEME_TREETWISTYOPEN:
 | 
						|
    case NS_THEME_PROGRESSBAR:
 | 
						|
    case NS_THEME_PROGRESSCHUNK:
 | 
						|
    case NS_THEME_PROGRESSBAR_VERTICAL:
 | 
						|
    case NS_THEME_PROGRESSCHUNK_VERTICAL:
 | 
						|
    case NS_THEME_TAB:
 | 
						|
    // case NS_THEME_TABPANEL:
 | 
						|
    case NS_THEME_TABPANELS:
 | 
						|
    case NS_THEME_TAB_SCROLL_ARROW_BACK:
 | 
						|
    case NS_THEME_TAB_SCROLL_ARROW_FORWARD:
 | 
						|
  case NS_THEME_TOOLTIP:
 | 
						|
  case NS_THEME_SPINNER:
 | 
						|
  case NS_THEME_SPINNER_UPBUTTON:
 | 
						|
  case NS_THEME_SPINNER_DOWNBUTTON:
 | 
						|
  case NS_THEME_SPINNER_TEXTFIELD:
 | 
						|
    // case NS_THEME_SCROLLBAR:  (n/a for gtk)
 | 
						|
    // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk)
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_UP:
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_DOWN:
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_LEFT:
 | 
						|
  case NS_THEME_SCROLLBARBUTTON_RIGHT:
 | 
						|
  case NS_THEME_SCROLLBAR_HORIZONTAL:
 | 
						|
  case NS_THEME_SCROLLBAR_VERTICAL:
 | 
						|
  case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
 | 
						|
  case NS_THEME_SCROLLBARTRACK_VERTICAL:
 | 
						|
  case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
 | 
						|
  case NS_THEME_SCROLLBARTHUMB_VERTICAL:
 | 
						|
  case NS_THEME_NUMBER_INPUT:
 | 
						|
  case NS_THEME_TEXTFIELD:
 | 
						|
  case NS_THEME_TEXTFIELD_MULTILINE:
 | 
						|
  case NS_THEME_RANGE:
 | 
						|
  case NS_THEME_RANGE_THUMB:
 | 
						|
  case NS_THEME_SCALE_HORIZONTAL:
 | 
						|
  case NS_THEME_SCALETHUMB_HORIZONTAL:
 | 
						|
  case NS_THEME_SCALE_VERTICAL:
 | 
						|
  case NS_THEME_SCALETHUMB_VERTICAL:
 | 
						|
    // case NS_THEME_SCALETHUMBSTART:
 | 
						|
    // case NS_THEME_SCALETHUMBEND:
 | 
						|
    // case NS_THEME_SCALETHUMBTICK:
 | 
						|
  case NS_THEME_CHECKBOX_CONTAINER:
 | 
						|
  case NS_THEME_RADIO_CONTAINER:
 | 
						|
  case NS_THEME_CHECKBOX_LABEL:
 | 
						|
  case NS_THEME_RADIO_LABEL:
 | 
						|
  case NS_THEME_MENUBAR:
 | 
						|
  case NS_THEME_MENUPOPUP:
 | 
						|
  case NS_THEME_MENUITEM:
 | 
						|
  case NS_THEME_MENUARROW:
 | 
						|
  case NS_THEME_MENUSEPARATOR:
 | 
						|
  case NS_THEME_CHECKMENUITEM:
 | 
						|
  case NS_THEME_RADIOMENUITEM:
 | 
						|
  case NS_THEME_SPLITTER:
 | 
						|
  case NS_THEME_WINDOW:
 | 
						|
  case NS_THEME_DIALOG:
 | 
						|
#if (MOZ_WIDGET_GTK == 3)
 | 
						|
  case NS_THEME_GTK_INFO_BAR:
 | 
						|
#endif
 | 
						|
    return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 | 
						|
 | 
						|
  case NS_THEME_MENULIST_BUTTON:
 | 
						|
    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 NS_THEME_FOCUS_OUTLINE:
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP_(bool)
 | 
						|
nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType)
 | 
						|
{
 | 
						|
  // XXXdwh At some point flesh all of this out.
 | 
						|
  if (aWidgetType == NS_THEME_MENULIST_BUTTON ||
 | 
						|
      aWidgetType == NS_THEME_RADIO ||
 | 
						|
      aWidgetType == NS_THEME_RANGE_THUMB ||
 | 
						|
      aWidgetType == NS_THEME_CHECKBOX ||
 | 
						|
      aWidgetType == NS_THEME_TAB_SCROLL_ARROW_BACK ||
 | 
						|
      aWidgetType == NS_THEME_TAB_SCROLL_ARROW_FORWARD ||
 | 
						|
      aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
 | 
						|
      aWidgetType == NS_THEME_BUTTON_ARROW_DOWN ||
 | 
						|
      aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
 | 
						|
      aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
 | 
						|
    return false;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
 | 
						|
{
 | 
						|
   if (aWidgetType == NS_THEME_MENULIST ||
 | 
						|
      aWidgetType == NS_THEME_BUTTON || 
 | 
						|
      aWidgetType == NS_THEME_TREEHEADERCELL)
 | 
						|
    return true;
 | 
						|
  
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
 | 
						|
{
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
nsITheme::Transparency
 | 
						|
nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
 | 
						|
{
 | 
						|
  switch (aWidgetType) {
 | 
						|
  // These widgets always draw a default background.
 | 
						|
#if (MOZ_WIDGET_GTK == 2)
 | 
						|
  case NS_THEME_TOOLBAR:
 | 
						|
  case NS_THEME_MENUBAR:
 | 
						|
#endif
 | 
						|
  case NS_THEME_MENUPOPUP:
 | 
						|
  case NS_THEME_WINDOW:
 | 
						|
  case NS_THEME_DIALOG:
 | 
						|
    return eOpaque;
 | 
						|
  case NS_THEME_SCROLLBAR_VERTICAL:
 | 
						|
  case NS_THEME_SCROLLBAR_HORIZONTAL:
 | 
						|
#if (MOZ_WIDGET_GTK == 3)
 | 
						|
    // 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 NS_THEME_TOOLTIP:
 | 
						|
#if (MOZ_WIDGET_GTK == 2)
 | 
						|
    return eOpaque;
 | 
						|
#else
 | 
						|
    return eTransparent;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
  return eUnknownTransparency;
 | 
						|
}
 |