widget/gtk/nsNativeThemeGTK.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/gtk/nsNativeThemeGTK.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1490 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsNativeThemeGTK.h"
    1.10 +#include "nsThemeConstants.h"
    1.11 +#include "gtkdrawing.h"
    1.12 +
    1.13 +#include "nsIObserverService.h"
    1.14 +#include "nsIServiceManager.h"
    1.15 +#include "nsIFrame.h"
    1.16 +#include "nsIPresShell.h"
    1.17 +#include "nsIContent.h"
    1.18 +#include "nsViewManager.h"
    1.19 +#include "nsNameSpaceManager.h"
    1.20 +#include "nsGfxCIID.h"
    1.21 +#include "nsTransform2D.h"
    1.22 +#include "nsMenuFrame.h"
    1.23 +#include "prlink.h"
    1.24 +#include "nsIDOMHTMLInputElement.h"
    1.25 +#include "nsRenderingContext.h"
    1.26 +#include "nsGkAtoms.h"
    1.27 +
    1.28 +#include "mozilla/EventStates.h"
    1.29 +#include "mozilla/Services.h"
    1.30 +
    1.31 +#include <gdk/gdkprivate.h>
    1.32 +#include <gtk/gtk.h>
    1.33 +
    1.34 +#include "gfxContext.h"
    1.35 +#include "gfxPlatformGtk.h"
    1.36 +#include "gfxGdkNativeRenderer.h"
    1.37 +#include <algorithm>
    1.38 +
    1.39 +using namespace mozilla;
    1.40 +
    1.41 +NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
    1.42 +                                                             nsIObserver)
    1.43 +
    1.44 +static int gLastGdkError;
    1.45 +
    1.46 +nsNativeThemeGTK::nsNativeThemeGTK()
    1.47 +{
    1.48 +  if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
    1.49 +    memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
    1.50 +    return;
    1.51 +  }
    1.52 +
    1.53 +  // We have to call moz_gtk_shutdown before the event loop stops running.
    1.54 +  nsCOMPtr<nsIObserverService> obsServ =
    1.55 +    mozilla::services::GetObserverService();
    1.56 +  obsServ->AddObserver(this, "xpcom-shutdown", false);
    1.57 +
    1.58 +  memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
    1.59 +  memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates));
    1.60 +}
    1.61 +
    1.62 +nsNativeThemeGTK::~nsNativeThemeGTK() {
    1.63 +}
    1.64 +
    1.65 +NS_IMETHODIMP
    1.66 +nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
    1.67 +                          const char16_t *aData)
    1.68 +{
    1.69 +  if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
    1.70 +    moz_gtk_shutdown();
    1.71 +  } else {
    1.72 +    NS_NOTREACHED("unexpected topic");
    1.73 +    return NS_ERROR_UNEXPECTED;
    1.74 +  }
    1.75 +
    1.76 +  return NS_OK;
    1.77 +}
    1.78 +
    1.79 +void
    1.80 +nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
    1.81 +{
    1.82 +  nsIPresShell *shell = GetPresShell(aFrame);
    1.83 +  if (!shell)
    1.84 +    return;
    1.85 +
    1.86 +  nsViewManager* vm = shell->GetViewManager();
    1.87 +  if (!vm)
    1.88 +    return;
    1.89 + 
    1.90 +  vm->InvalidateAllViews();
    1.91 +}
    1.92 +
    1.93 +static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace)
    1.94 +{
    1.95 +  nsIContent *content = aFrame ? aFrame->GetContent() : nullptr;
    1.96 +  if (!content)
    1.97 +    return false;
    1.98 +  return content->IsInNamespace(aNamespace);
    1.99 +}
   1.100 +
   1.101 +static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
   1.102 +  return (aDisabledVector[aWidgetType >> 3] & (1 << (aWidgetType & 7))) != 0;
   1.103 +}
   1.104 +
   1.105 +static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
   1.106 +  aDisabledVector[aWidgetType >> 3] |= (1 << (aWidgetType & 7));
   1.107 +}
   1.108 +
   1.109 +static inline uint16_t
   1.110 +GetWidgetStateKey(uint8_t aWidgetType, GtkWidgetState *aWidgetState)
   1.111 +{
   1.112 +  return (aWidgetState->active |
   1.113 +          aWidgetState->focused << 1 |
   1.114 +          aWidgetState->inHover << 2 |
   1.115 +          aWidgetState->disabled << 3 |
   1.116 +          aWidgetState->isDefault << 4 |
   1.117 +          aWidgetType << 5);
   1.118 +}
   1.119 +
   1.120 +static bool IsWidgetStateSafe(uint8_t* aSafeVector,
   1.121 +                                uint8_t aWidgetType,
   1.122 +                                GtkWidgetState *aWidgetState)
   1.123 +{
   1.124 +  uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
   1.125 +  return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0;
   1.126 +}
   1.127 +
   1.128 +static void SetWidgetStateSafe(uint8_t *aSafeVector,
   1.129 +                               uint8_t aWidgetType,
   1.130 +                               GtkWidgetState *aWidgetState)
   1.131 +{
   1.132 +  uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
   1.133 +  aSafeVector[key >> 3] |= (1 << (key & 7));
   1.134 +}
   1.135 +
   1.136 +static GtkTextDirection GetTextDirection(nsIFrame* aFrame)
   1.137 +{
   1.138 +  if (!aFrame)
   1.139 +    return GTK_TEXT_DIR_NONE;
   1.140 +
   1.141 +  switch (aFrame->StyleVisibility()->mDirection) {
   1.142 +    case NS_STYLE_DIRECTION_RTL:
   1.143 +      return GTK_TEXT_DIR_RTL;
   1.144 +    case NS_STYLE_DIRECTION_LTR:
   1.145 +      return GTK_TEXT_DIR_LTR;
   1.146 +  }
   1.147 +
   1.148 +  return GTK_TEXT_DIR_NONE;
   1.149 +}
   1.150 +
   1.151 +// Returns positive for negative margins (otherwise 0).
   1.152 +gint
   1.153 +nsNativeThemeGTK::GetTabMarginPixels(nsIFrame* aFrame)
   1.154 +{
   1.155 +  nscoord margin =
   1.156 +    IsBottomTab(aFrame) ? aFrame->GetUsedMargin().top
   1.157 +    : aFrame->GetUsedMargin().bottom;
   1.158 +
   1.159 +  return std::min<gint>(MOZ_GTK_TAB_MARGIN_MASK,
   1.160 +                std::max(0,
   1.161 +                       aFrame->PresContext()->AppUnitsToDevPixels(-margin)));
   1.162 +}
   1.163 +
   1.164 +bool
   1.165 +nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame,
   1.166 +                                       GtkThemeWidgetType& aGtkWidgetType,
   1.167 +                                       GtkWidgetState* aState,
   1.168 +                                       gint* aWidgetFlags)
   1.169 +{
   1.170 +  if (aState) {
   1.171 +    if (!aFrame) {
   1.172 +      // reset the entire struct to zero
   1.173 +      memset(aState, 0, sizeof(GtkWidgetState));
   1.174 +    } else {
   1.175 +
   1.176 +      // For XUL checkboxes and radio buttons, the state of the parent
   1.177 +      // determines our state.
   1.178 +      nsIFrame *stateFrame = aFrame;
   1.179 +      if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX ||
   1.180 +                                       aWidgetType == NS_THEME_RADIO)) ||
   1.181 +                     aWidgetType == NS_THEME_CHECKBOX_LABEL ||
   1.182 +                     aWidgetType == NS_THEME_RADIO_LABEL)) {
   1.183 +
   1.184 +        nsIAtom* atom = nullptr;
   1.185 +        if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
   1.186 +          if (aWidgetType == NS_THEME_CHECKBOX_LABEL ||
   1.187 +              aWidgetType == NS_THEME_RADIO_LABEL) {
   1.188 +            // Adjust stateFrame so GetContentState finds the correct state.
   1.189 +            stateFrame = aFrame = aFrame->GetParent()->GetParent();
   1.190 +          } else {
   1.191 +            // GetContentState knows to look one frame up for radio/checkbox
   1.192 +            // widgets, so don't adjust stateFrame here.
   1.193 +            aFrame = aFrame->GetParent();
   1.194 +          }
   1.195 +          if (aWidgetFlags) {
   1.196 +            if (!atom) {
   1.197 +              atom = (aWidgetType == NS_THEME_CHECKBOX ||
   1.198 +                      aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked
   1.199 +                                                              : nsGkAtoms::selected;
   1.200 +            }
   1.201 +            *aWidgetFlags = CheckBooleanAttr(aFrame, atom);
   1.202 +          }
   1.203 +        } else {
   1.204 +          if (aWidgetFlags) {
   1.205 +            nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent()));
   1.206 +            *aWidgetFlags = 0;
   1.207 +            if (inputElt) {
   1.208 +              bool isHTMLChecked;
   1.209 +              inputElt->GetChecked(&isHTMLChecked);
   1.210 +              if (isHTMLChecked)
   1.211 +                *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
   1.212 +            }
   1.213 +
   1.214 +            if (GetIndeterminate(aFrame))
   1.215 +              *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
   1.216 +          }
   1.217 +        }
   1.218 +      } else if (aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN ||
   1.219 +                 aWidgetType == NS_THEME_TREEVIEW_HEADER_SORTARROW ||
   1.220 +                 aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS ||
   1.221 +                 aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
   1.222 +                 aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
   1.223 +                 aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) {
   1.224 +        // The state of an arrow comes from its parent.
   1.225 +        stateFrame = aFrame = aFrame->GetParent();
   1.226 +      }
   1.227 +
   1.228 +      EventStates eventState = GetContentState(stateFrame, aWidgetType);
   1.229 +
   1.230 +      aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame);
   1.231 +      aState->active  = eventState.HasState(NS_EVENT_STATE_ACTIVE);
   1.232 +      aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS);
   1.233 +      aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER);
   1.234 +      aState->isDefault = IsDefaultButton(aFrame);
   1.235 +      aState->canDefault = FALSE; // XXX fix me
   1.236 +      aState->depressed = FALSE;
   1.237 +
   1.238 +      if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
   1.239 +        // For these widget types, some element (either a child or parent)
   1.240 +        // actually has element focus, so we check the focused attribute
   1.241 +        // to see whether to draw in the focused state.
   1.242 +        if (aWidgetType == NS_THEME_NUMBER_INPUT ||
   1.243 +            aWidgetType == NS_THEME_TEXTFIELD ||
   1.244 +            aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
   1.245 +            aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
   1.246 +            aWidgetType == NS_THEME_SPINNER_TEXTFIELD ||
   1.247 +            aWidgetType == NS_THEME_RADIO_CONTAINER ||
   1.248 +            aWidgetType == NS_THEME_RADIO_LABEL) {
   1.249 +          aState->focused = IsFocused(aFrame);
   1.250 +        } else if (aWidgetType == NS_THEME_RADIO ||
   1.251 +                   aWidgetType == NS_THEME_CHECKBOX) {
   1.252 +          // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
   1.253 +          aState->focused = FALSE;
   1.254 +        }
   1.255 +
   1.256 +        if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL ||
   1.257 +            aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) {
   1.258 +          // for scrollbars we need to go up two to go from the thumb to
   1.259 +          // the slider to the actual scrollbar object
   1.260 +          nsIFrame *tmpFrame = aFrame->GetParent()->GetParent();
   1.261 +
   1.262 +          aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0);
   1.263 +          aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100);
   1.264 +        }
   1.265 +
   1.266 +        if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
   1.267 +            aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
   1.268 +            aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT ||
   1.269 +            aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) {
   1.270 +          // set the state to disabled when the scrollbar is scrolled to
   1.271 +          // the beginning or the end, depending on the button type.
   1.272 +          int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
   1.273 +          int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
   1.274 +          if ((curpos == 0 && (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
   1.275 +                aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT)) ||
   1.276 +              (curpos == maxpos &&
   1.277 +               (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
   1.278 +                aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT)))
   1.279 +            aState->disabled = true;
   1.280 +
   1.281 +          // In order to simulate native GTK scrollbar click behavior,
   1.282 +          // we set the active attribute on the element to true if it's
   1.283 +          // pressed with any mouse button.
   1.284 +          // This allows us to show that it's active without setting :active
   1.285 +          else if (CheckBooleanAttr(aFrame, nsGkAtoms::active))
   1.286 +            aState->active = true;
   1.287 +
   1.288 +          if (aWidgetFlags) {
   1.289 +            *aWidgetFlags = GetScrollbarButtonType(aFrame);
   1.290 +            if (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP < 2)
   1.291 +              *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL;
   1.292 +          }
   1.293 +        }
   1.294 +
   1.295 +        // menu item state is determined by the attribute "_moz-menuactive",
   1.296 +        // and not by the mouse hovering (accessibility).  as a special case,
   1.297 +        // menus which are children of a menu bar are only marked as prelight
   1.298 +        // if they are open, not on normal hover.
   1.299 +
   1.300 +        if (aWidgetType == NS_THEME_MENUITEM ||
   1.301 +            aWidgetType == NS_THEME_CHECKMENUITEM ||
   1.302 +            aWidgetType == NS_THEME_RADIOMENUITEM ||
   1.303 +            aWidgetType == NS_THEME_MENUSEPARATOR ||
   1.304 +            aWidgetType == NS_THEME_MENUARROW) {
   1.305 +          bool isTopLevel = false;
   1.306 +          nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
   1.307 +          if (menuFrame) {
   1.308 +            isTopLevel = menuFrame->IsOnMenuBar();
   1.309 +          }
   1.310 +
   1.311 +          if (isTopLevel) {
   1.312 +            aState->inHover = menuFrame->IsOpen();
   1.313 +            *aWidgetFlags |= MOZ_TOPLEVEL_MENU_ITEM;
   1.314 +          } else {
   1.315 +            aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
   1.316 +            *aWidgetFlags &= ~MOZ_TOPLEVEL_MENU_ITEM;
   1.317 +          }
   1.318 +
   1.319 +          aState->active = FALSE;
   1.320 +        
   1.321 +          if (aWidgetType == NS_THEME_CHECKMENUITEM ||
   1.322 +              aWidgetType == NS_THEME_RADIOMENUITEM) {
   1.323 +            *aWidgetFlags = 0;
   1.324 +            if (aFrame && aFrame->GetContent()) {
   1.325 +              *aWidgetFlags = aFrame->GetContent()->
   1.326 +                AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
   1.327 +                            nsGkAtoms::_true, eIgnoreCase);
   1.328 +            }
   1.329 +          }
   1.330 +        }
   1.331 +
   1.332 +        // A button with drop down menu open or an activated toggle button
   1.333 +        // should always appear depressed.
   1.334 +        if (aWidgetType == NS_THEME_BUTTON ||
   1.335 +            aWidgetType == NS_THEME_TOOLBAR_BUTTON ||
   1.336 +            aWidgetType == NS_THEME_TOOLBAR_DUAL_BUTTON ||
   1.337 +            aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN ||
   1.338 +            aWidgetType == NS_THEME_DROPDOWN ||
   1.339 +            aWidgetType == NS_THEME_DROPDOWN_BUTTON) {
   1.340 +          bool menuOpen = IsOpenButton(aFrame);
   1.341 +          aState->depressed = IsCheckedButton(aFrame) || menuOpen;
   1.342 +          // we must not highlight buttons with open drop down menus on hover.
   1.343 +          aState->inHover = aState->inHover && !menuOpen;
   1.344 +        }
   1.345 +
   1.346 +        // When the input field of the drop down button has focus, some themes
   1.347 +        // should draw focus for the drop down button as well.
   1.348 +        if (aWidgetType == NS_THEME_DROPDOWN_BUTTON && aWidgetFlags) {
   1.349 +          *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
   1.350 +        }
   1.351 +      }
   1.352 +    }
   1.353 +  }
   1.354 +
   1.355 +  switch (aWidgetType) {
   1.356 +  case NS_THEME_BUTTON:
   1.357 +  case NS_THEME_TOOLBAR_BUTTON:
   1.358 +  case NS_THEME_TOOLBAR_DUAL_BUTTON:
   1.359 +    if (aWidgetFlags)
   1.360 +      *aWidgetFlags = (aWidgetType == NS_THEME_BUTTON) ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE;
   1.361 +    aGtkWidgetType = MOZ_GTK_BUTTON;
   1.362 +    break;
   1.363 +  case NS_THEME_CHECKBOX:
   1.364 +  case NS_THEME_RADIO:
   1.365 +    aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
   1.366 +    break;
   1.367 +  case NS_THEME_SCROLLBAR_BUTTON_UP:
   1.368 +  case NS_THEME_SCROLLBAR_BUTTON_DOWN:
   1.369 +  case NS_THEME_SCROLLBAR_BUTTON_LEFT:
   1.370 +  case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
   1.371 +    aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON;
   1.372 +    break;
   1.373 +  case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
   1.374 +    aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_VERTICAL;
   1.375 +    break;
   1.376 +  case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
   1.377 +    aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL;
   1.378 +    break;
   1.379 +  case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
   1.380 +    aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
   1.381 +    break;
   1.382 +  case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
   1.383 +    aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
   1.384 +    break;
   1.385 +  case NS_THEME_SPINNER:
   1.386 +    aGtkWidgetType = MOZ_GTK_SPINBUTTON;
   1.387 +    break;
   1.388 +  case NS_THEME_SPINNER_UP_BUTTON:
   1.389 +    aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP;
   1.390 +    break;
   1.391 +  case NS_THEME_SPINNER_DOWN_BUTTON:
   1.392 +    aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN;
   1.393 +    break;
   1.394 +  case NS_THEME_SPINNER_TEXTFIELD:
   1.395 +    aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY;
   1.396 +    break;
   1.397 +  case NS_THEME_RANGE:
   1.398 +    {
   1.399 +      if (IsRangeHorizontal(aFrame)) {
   1.400 +        if (aWidgetFlags)
   1.401 +          *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   1.402 +        aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
   1.403 +      } else {
   1.404 +        if (aWidgetFlags)
   1.405 +          *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   1.406 +        aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
   1.407 +      }
   1.408 +      break;
   1.409 +    }
   1.410 +  case NS_THEME_RANGE_THUMB:
   1.411 +    {
   1.412 +      if (IsRangeHorizontal(aFrame)) {
   1.413 +        if (aWidgetFlags)
   1.414 +          *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   1.415 +        aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
   1.416 +      } else {
   1.417 +        if (aWidgetFlags)
   1.418 +          *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   1.419 +        aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
   1.420 +      }
   1.421 +      break;
   1.422 +    }
   1.423 +  case NS_THEME_SCALE_HORIZONTAL:
   1.424 +    if (aWidgetFlags)
   1.425 +      *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   1.426 +    aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
   1.427 +    break;
   1.428 +  case NS_THEME_SCALE_THUMB_HORIZONTAL:
   1.429 +    if (aWidgetFlags)
   1.430 +      *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   1.431 +    aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
   1.432 +    break;
   1.433 +  case NS_THEME_SCALE_VERTICAL:
   1.434 +    if (aWidgetFlags)
   1.435 +      *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   1.436 +    aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
   1.437 +    break;
   1.438 +  case NS_THEME_TOOLBAR_SEPARATOR:
   1.439 +    aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR;
   1.440 +    break;
   1.441 +  case NS_THEME_SCALE_THUMB_VERTICAL:
   1.442 +    if (aWidgetFlags)
   1.443 +      *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   1.444 +    aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
   1.445 +    break;
   1.446 +  case NS_THEME_TOOLBAR_GRIPPER:
   1.447 +    aGtkWidgetType = MOZ_GTK_GRIPPER;
   1.448 +    break;
   1.449 +  case NS_THEME_RESIZER:
   1.450 +    aGtkWidgetType = MOZ_GTK_RESIZER;
   1.451 +    break;
   1.452 +  case NS_THEME_NUMBER_INPUT:
   1.453 +  case NS_THEME_TEXTFIELD:
   1.454 +  case NS_THEME_TEXTFIELD_MULTILINE:
   1.455 +    aGtkWidgetType = MOZ_GTK_ENTRY;
   1.456 +    break;
   1.457 +  case NS_THEME_LISTBOX:
   1.458 +  case NS_THEME_TREEVIEW:
   1.459 +    aGtkWidgetType = MOZ_GTK_TREEVIEW;
   1.460 +    break;
   1.461 +  case NS_THEME_TREEVIEW_HEADER_CELL:
   1.462 +    if (aWidgetFlags) {
   1.463 +      // In this case, the flag denotes whether the header is the sorted one or not
   1.464 +      if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural)
   1.465 +        *aWidgetFlags = false;
   1.466 +      else
   1.467 +        *aWidgetFlags = true;
   1.468 +    }
   1.469 +    aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL;
   1.470 +    break;
   1.471 +  case NS_THEME_TREEVIEW_HEADER_SORTARROW:
   1.472 +    if (aWidgetFlags) {
   1.473 +      switch (GetTreeSortDirection(aFrame)) {
   1.474 +        case eTreeSortDirection_Ascending:
   1.475 +          *aWidgetFlags = GTK_ARROW_DOWN;
   1.476 +          break;
   1.477 +        case eTreeSortDirection_Descending:
   1.478 +          *aWidgetFlags = GTK_ARROW_UP;
   1.479 +          break;
   1.480 +        case eTreeSortDirection_Natural:
   1.481 +        default:
   1.482 +          /* This prevents the treecolums from getting smaller
   1.483 +           * and wider when switching sort direction off and on
   1.484 +           * */
   1.485 +          *aWidgetFlags = GTK_ARROW_NONE;
   1.486 +          break;
   1.487 +      }
   1.488 +    }
   1.489 +    aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW;
   1.490 +    break;
   1.491 +  case NS_THEME_TREEVIEW_TWISTY:
   1.492 +    aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
   1.493 +    if (aWidgetFlags)
   1.494 +      *aWidgetFlags = GTK_EXPANDER_COLLAPSED;
   1.495 +    break;
   1.496 +  case NS_THEME_TREEVIEW_TWISTY_OPEN:
   1.497 +    aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
   1.498 +    if (aWidgetFlags)
   1.499 +      *aWidgetFlags = GTK_EXPANDER_EXPANDED;
   1.500 +    break;
   1.501 +  case NS_THEME_DROPDOWN:
   1.502 +    aGtkWidgetType = MOZ_GTK_DROPDOWN;
   1.503 +    if (aWidgetFlags)
   1.504 +        *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
   1.505 +    break;
   1.506 +  case NS_THEME_DROPDOWN_TEXT:
   1.507 +    return false; // nothing to do, but prevents the bg from being drawn
   1.508 +  case NS_THEME_DROPDOWN_TEXTFIELD:
   1.509 +    aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
   1.510 +    break;
   1.511 +  case NS_THEME_DROPDOWN_BUTTON:
   1.512 +    aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
   1.513 +    break;
   1.514 +  case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
   1.515 +  case NS_THEME_BUTTON_ARROW_DOWN:
   1.516 +  case NS_THEME_BUTTON_ARROW_UP:
   1.517 +  case NS_THEME_BUTTON_ARROW_NEXT:
   1.518 +  case NS_THEME_BUTTON_ARROW_PREVIOUS:
   1.519 +    aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
   1.520 +    if (aWidgetFlags) {
   1.521 +      *aWidgetFlags = GTK_ARROW_DOWN;
   1.522 +
   1.523 +      if (aWidgetType == NS_THEME_BUTTON_ARROW_UP)
   1.524 +        *aWidgetFlags = GTK_ARROW_UP;
   1.525 +      else if (aWidgetType == NS_THEME_BUTTON_ARROW_NEXT)
   1.526 +        *aWidgetFlags = GTK_ARROW_RIGHT;
   1.527 +      else if (aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
   1.528 +        *aWidgetFlags = GTK_ARROW_LEFT;
   1.529 +    }
   1.530 +    break;
   1.531 +  case NS_THEME_CHECKBOX_CONTAINER:
   1.532 +    aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER;
   1.533 +    break;
   1.534 +  case NS_THEME_RADIO_CONTAINER:
   1.535 +    aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER;
   1.536 +    break;
   1.537 +  case NS_THEME_CHECKBOX_LABEL:
   1.538 +    aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL;
   1.539 +    break;
   1.540 +  case NS_THEME_RADIO_LABEL:
   1.541 +    aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL;
   1.542 +    break;
   1.543 +  case NS_THEME_TOOLBAR:
   1.544 +    aGtkWidgetType = MOZ_GTK_TOOLBAR;
   1.545 +    break;
   1.546 +  case NS_THEME_TOOLTIP:
   1.547 +    aGtkWidgetType = MOZ_GTK_TOOLTIP;
   1.548 +    break;
   1.549 +  case NS_THEME_STATUSBAR_PANEL:
   1.550 +  case NS_THEME_STATUSBAR_RESIZER_PANEL:
   1.551 +    aGtkWidgetType = MOZ_GTK_FRAME;
   1.552 +    break;
   1.553 +  case NS_THEME_PROGRESSBAR:
   1.554 +  case NS_THEME_PROGRESSBAR_VERTICAL:
   1.555 +    aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
   1.556 +    break;
   1.557 +  case NS_THEME_PROGRESSBAR_CHUNK:
   1.558 +  case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
   1.559 +    {
   1.560 +      nsIFrame* stateFrame = aFrame->GetParent();
   1.561 +      EventStates eventStates = GetContentState(stateFrame, aWidgetType);
   1.562 +
   1.563 +      aGtkWidgetType = IsIndeterminateProgress(stateFrame, eventStates)
   1.564 +                         ? (stateFrame->StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL)
   1.565 +                           ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
   1.566 +                           : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
   1.567 +                         : MOZ_GTK_PROGRESS_CHUNK;
   1.568 +    }
   1.569 +    break;
   1.570 +  case NS_THEME_TAB_SCROLLARROW_BACK:
   1.571 +  case NS_THEME_TAB_SCROLLARROW_FORWARD:
   1.572 +    if (aWidgetFlags)
   1.573 +      *aWidgetFlags = aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ?
   1.574 +                        GTK_ARROW_LEFT : GTK_ARROW_RIGHT;
   1.575 +    aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW;
   1.576 +    break;
   1.577 +  case NS_THEME_TAB_PANELS:
   1.578 +    aGtkWidgetType = MOZ_GTK_TABPANELS;
   1.579 +    break;
   1.580 +  case NS_THEME_TAB:
   1.581 +    {
   1.582 +      if (aWidgetFlags) {
   1.583 +        /* First bits will be used to store max(0,-bmargin) where bmargin
   1.584 +         * is the bottom margin of the tab in pixels  (resp. top margin,
   1.585 +         * for bottom tabs). */
   1.586 +        if (IsBottomTab(aFrame)) {
   1.587 +            *aWidgetFlags = MOZ_GTK_TAB_BOTTOM;
   1.588 +        } else {
   1.589 +            *aWidgetFlags = 0;
   1.590 +        }
   1.591 +
   1.592 +        *aWidgetFlags |= GetTabMarginPixels(aFrame);
   1.593 +
   1.594 +        if (IsSelectedTab(aFrame))
   1.595 +          *aWidgetFlags |= MOZ_GTK_TAB_SELECTED;
   1.596 +
   1.597 +        if (IsFirstTab(aFrame))
   1.598 +          *aWidgetFlags |= MOZ_GTK_TAB_FIRST;
   1.599 +      }
   1.600 +
   1.601 +      aGtkWidgetType = MOZ_GTK_TAB;
   1.602 +    }
   1.603 +    break;
   1.604 +  case NS_THEME_SPLITTER:
   1.605 +    if (IsHorizontal(aFrame))
   1.606 +      aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
   1.607 +    else 
   1.608 +      aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
   1.609 +    break;
   1.610 +  case NS_THEME_MENUBAR:
   1.611 +    aGtkWidgetType = MOZ_GTK_MENUBAR;
   1.612 +    break;
   1.613 +  case NS_THEME_MENUPOPUP:
   1.614 +    aGtkWidgetType = MOZ_GTK_MENUPOPUP;
   1.615 +    break;
   1.616 +  case NS_THEME_MENUITEM:
   1.617 +    aGtkWidgetType = MOZ_GTK_MENUITEM;
   1.618 +    break;
   1.619 +  case NS_THEME_MENUSEPARATOR:
   1.620 +    aGtkWidgetType = MOZ_GTK_MENUSEPARATOR;
   1.621 +    break;
   1.622 +  case NS_THEME_MENUARROW:
   1.623 +    aGtkWidgetType = MOZ_GTK_MENUARROW;
   1.624 +    break;
   1.625 +  case NS_THEME_CHECKMENUITEM:
   1.626 +    aGtkWidgetType = MOZ_GTK_CHECKMENUITEM;
   1.627 +    break;
   1.628 +  case NS_THEME_RADIOMENUITEM:
   1.629 +    aGtkWidgetType = MOZ_GTK_RADIOMENUITEM;
   1.630 +    break;
   1.631 +  case NS_THEME_WINDOW:
   1.632 +  case NS_THEME_DIALOG:
   1.633 +    aGtkWidgetType = MOZ_GTK_WINDOW;
   1.634 +    break;
   1.635 +  default:
   1.636 +    return false;
   1.637 +  }
   1.638 +
   1.639 +  return true;
   1.640 +}
   1.641 +
   1.642 +#if (MOZ_WIDGET_GTK == 2)
   1.643 +class ThemeRenderer : public gfxGdkNativeRenderer {
   1.644 +public:
   1.645 +  ThemeRenderer(GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
   1.646 +                gint aFlags, GtkTextDirection aDirection,
   1.647 +                const GdkRectangle& aGDKRect, const GdkRectangle& aGDKClip)
   1.648 +    : mState(aState), mGTKWidgetType(aGTKWidgetType), mFlags(aFlags),
   1.649 +      mDirection(aDirection), mGDKRect(aGDKRect), mGDKClip(aGDKClip) {}
   1.650 +  nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX, gint offsetY,
   1.651 +                       GdkRectangle * clipRects, uint32_t numClipRects);
   1.652 +private:
   1.653 +  GtkWidgetState mState;
   1.654 +  GtkThemeWidgetType mGTKWidgetType;
   1.655 +  gint mFlags;
   1.656 +  GtkTextDirection mDirection;
   1.657 +  const GdkRectangle& mGDKRect;
   1.658 +  const GdkRectangle& mGDKClip;
   1.659 +};
   1.660 +
   1.661 +nsresult
   1.662 +ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX, 
   1.663 +        gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects)
   1.664 +{
   1.665 +  GdkRectangle gdk_rect = mGDKRect;
   1.666 +  gdk_rect.x += offsetX;
   1.667 +  gdk_rect.y += offsetY;
   1.668 +
   1.669 +  GdkRectangle gdk_clip = mGDKClip;
   1.670 +  gdk_clip.x += offsetX;
   1.671 +  gdk_clip.y += offsetY;
   1.672 +
   1.673 +  GdkRectangle surfaceRect;
   1.674 +  surfaceRect.x = 0;
   1.675 +  surfaceRect.y = 0;
   1.676 +  gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height);
   1.677 +  gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
   1.678 +  
   1.679 +  NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
   1.680 +  moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,
   1.681 +                       &mState, mFlags, mDirection);
   1.682 +
   1.683 +  return NS_OK;
   1.684 +}
   1.685 +#endif
   1.686 +
   1.687 +bool
   1.688 +nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
   1.689 +                                        nsIntMargin* aExtra)
   1.690 +{
   1.691 +  *aExtra = nsIntMargin(0,0,0,0);
   1.692 +  // Allow an extra one pixel above and below the thumb for certain
   1.693 +  // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
   1.694 +  // We modify the frame's overflow area.  See bug 297508.
   1.695 +  switch (aWidgetType) {
   1.696 +  case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
   1.697 +    aExtra->top = aExtra->bottom = 1;
   1.698 +    return true;
   1.699 +  case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
   1.700 +    aExtra->left = aExtra->right = 1;
   1.701 +    return true;
   1.702 +
   1.703 +  // Include the indicator spacing (the padding around the control).
   1.704 +  case NS_THEME_CHECKBOX:
   1.705 +  case NS_THEME_RADIO:
   1.706 +    {
   1.707 +      gint indicator_size, indicator_spacing;
   1.708 +
   1.709 +      if (aWidgetType == NS_THEME_CHECKBOX) {
   1.710 +        moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
   1.711 +      } else {
   1.712 +        moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
   1.713 +      }
   1.714 +
   1.715 +      aExtra->top = indicator_spacing;
   1.716 +      aExtra->right = indicator_spacing;
   1.717 +      aExtra->bottom = indicator_spacing;
   1.718 +      aExtra->left = indicator_spacing;
   1.719 +      return true;
   1.720 +    }
   1.721 +  case NS_THEME_BUTTON :
   1.722 +    {
   1.723 +      if (IsDefaultButton(aFrame)) {
   1.724 +        // Some themes draw a default indicator outside the widget,
   1.725 +        // include that in overflow
   1.726 +        gint top, left, bottom, right;
   1.727 +        moz_gtk_button_get_default_overflow(&top, &left, &bottom, &right);
   1.728 +        aExtra->top = top;
   1.729 +        aExtra->right = right;
   1.730 +        aExtra->bottom = bottom;
   1.731 +        aExtra->left = left;
   1.732 +        return true;
   1.733 +      }
   1.734 +    }
   1.735 +  case NS_THEME_TAB :
   1.736 +    {
   1.737 +      if (!IsSelectedTab(aFrame))
   1.738 +        return false;
   1.739 +
   1.740 +      gint gap_height = moz_gtk_get_tab_thickness();
   1.741 +
   1.742 +      int32_t extra = gap_height - GetTabMarginPixels(aFrame);
   1.743 +      if (extra <= 0)
   1.744 +        return false;
   1.745 +
   1.746 +      if (IsBottomTab(aFrame)) {
   1.747 +        aExtra->top = extra;
   1.748 +      } else {
   1.749 +        aExtra->bottom = extra;
   1.750 +      }
   1.751 +    }
   1.752 +  default:
   1.753 +    return false;
   1.754 +  }
   1.755 +}
   1.756 +
   1.757 +NS_IMETHODIMP
   1.758 +nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
   1.759 +                                       nsIFrame* aFrame,
   1.760 +                                       uint8_t aWidgetType,
   1.761 +                                       const nsRect& aRect,
   1.762 +                                       const nsRect& aDirtyRect)
   1.763 +{
   1.764 +  GtkWidgetState state;
   1.765 +  GtkThemeWidgetType gtkWidgetType;
   1.766 +  GtkTextDirection direction = GetTextDirection(aFrame);
   1.767 +  gint flags;
   1.768 +  if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
   1.769 +                            &flags))
   1.770 +    return NS_OK;
   1.771 +
   1.772 +  gfxContext* ctx = aContext->ThebesContext();
   1.773 +  nsPresContext *presContext = aFrame->PresContext();
   1.774 +
   1.775 +  gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
   1.776 +  gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
   1.777 +
   1.778 +  // Align to device pixels where sensible
   1.779 +  // to provide crisper and faster drawing.
   1.780 +  // Don't snap if it's a non-unit scale factor. We're going to have to take
   1.781 +  // slow paths then in any case.
   1.782 +  bool snapXY = ctx->UserToDevicePixelSnapped(rect);
   1.783 +  if (snapXY) {
   1.784 +    // Leave rect in device coords but make dirtyRect consistent.
   1.785 +    dirtyRect = ctx->UserToDevice(dirtyRect);
   1.786 +  }
   1.787 +
   1.788 +  // Translate the dirty rect so that it is wrt the widget top-left.
   1.789 +  dirtyRect.MoveBy(-rect.TopLeft());
   1.790 +  // Round out the dirty rect to gdk pixels to ensure that gtk draws
   1.791 +  // enough pixels for interpolation to device pixels.
   1.792 +  dirtyRect.RoundOut();
   1.793 +
   1.794 +  // GTK themes can only draw an integer number of pixels
   1.795 +  // (even when not snapped).
   1.796 +  nsIntRect widgetRect(0, 0, NS_lround(rect.Width()), NS_lround(rect.Height()));
   1.797 +  nsIntRect overflowRect(widgetRect);
   1.798 +  nsIntMargin extraSize;
   1.799 +  if (GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) {
   1.800 +    overflowRect.Inflate(extraSize);
   1.801 +  }
   1.802 +
   1.803 +  // This is the rectangle that will actually be drawn, in gdk pixels
   1.804 +  nsIntRect drawingRect(int32_t(dirtyRect.X()),
   1.805 +                        int32_t(dirtyRect.Y()),
   1.806 +                        int32_t(dirtyRect.Width()),
   1.807 +                        int32_t(dirtyRect.Height()));
   1.808 +  if (widgetRect.IsEmpty()
   1.809 +      || !drawingRect.IntersectRect(overflowRect, drawingRect))
   1.810 +    return NS_OK;
   1.811 +
   1.812 +  // gdk rectangles are wrt the drawing rect.
   1.813 +
   1.814 +  GdkRectangle gdk_rect = {-drawingRect.x, -drawingRect.y,
   1.815 +                           widgetRect.width, widgetRect.height};
   1.816 +
   1.817 +  // translate everything so (0,0) is the top left of the drawingRect
   1.818 +  gfxContextAutoSaveRestore autoSR(ctx);
   1.819 +  if (snapXY) {
   1.820 +    // Rects are in device coords.
   1.821 +    ctx->IdentityMatrix(); 
   1.822 +  }
   1.823 +  ctx->Translate(rect.TopLeft() + gfxPoint(drawingRect.x, drawingRect.y));
   1.824 +
   1.825 +  NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
   1.826 +               "Trying to render an unsafe widget!");
   1.827 +
   1.828 +  bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
   1.829 +  if (!safeState) {
   1.830 +    gLastGdkError = 0;
   1.831 +    gdk_error_trap_push ();
   1.832 +  }
   1.833 +
   1.834 +#if (MOZ_WIDGET_GTK == 2)
   1.835 +  // The gdk_clip is just advisory here, meaning "you don't
   1.836 +  // need to draw outside this rect if you don't feel like it!"
   1.837 +  GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height};
   1.838 +
   1.839 +  ThemeRenderer renderer(state, gtkWidgetType, flags, direction,
   1.840 +                         gdk_rect, gdk_clip);
   1.841 +
   1.842 +  // Some themes (e.g. Clearlooks) just don't clip properly to any
   1.843 +  // clip rect we provide, so we cannot advertise support for clipping within
   1.844 +  // the widget bounds.
   1.845 +  uint32_t rendererFlags = 0;
   1.846 +  if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) {
   1.847 +    rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE;
   1.848 +  }
   1.849 +
   1.850 +  // GtkStyles (used by the widget drawing backend) are created for a
   1.851 +  // particular colormap/visual.
   1.852 +  GdkColormap* colormap = moz_gtk_widget_get_colormap();
   1.853 +
   1.854 +  renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
   1.855 +#else 
   1.856 +  moz_gtk_widget_paint(gtkWidgetType, ctx->GetCairo(), &gdk_rect, 
   1.857 +                       &state, flags, direction);
   1.858 +#endif
   1.859 +
   1.860 +  if (!safeState) {
   1.861 +    gdk_flush();
   1.862 +    gLastGdkError = gdk_error_trap_pop ();
   1.863 +
   1.864 +    if (gLastGdkError) {
   1.865 +#ifdef DEBUG
   1.866 +      printf("GTK theme failed for widget type %d, error was %d, state was "
   1.867 +             "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
   1.868 +             aWidgetType, gLastGdkError, state.active, state.focused,
   1.869 +             state.inHover, state.disabled);
   1.870 +#endif
   1.871 +      NS_WARNING("GTK theme failed; disabling unsafe widget");
   1.872 +      SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType);
   1.873 +      // force refresh of the window, because the widget was not
   1.874 +      // successfully drawn it must be redrawn using the default look
   1.875 +      RefreshWidgetWindow(aFrame);
   1.876 +    } else {
   1.877 +      SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
   1.878 +    }
   1.879 +  }
   1.880 +
   1.881 +  // Indeterminate progress bar are animated.
   1.882 +  if (gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
   1.883 +      gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
   1.884 +    if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
   1.885 +      NS_WARNING("unable to animate widget!");
   1.886 +    }
   1.887 +  }
   1.888 +
   1.889 +  return NS_OK;
   1.890 +}
   1.891 +
   1.892 +NS_IMETHODIMP
   1.893 +nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
   1.894 +                                  uint8_t aWidgetType, nsIntMargin* aResult)
   1.895 +{
   1.896 +  GtkTextDirection direction = GetTextDirection(aFrame);
   1.897 +  aResult->top = aResult->left = aResult->right = aResult->bottom = 0;
   1.898 +  switch (aWidgetType) {
   1.899 +  case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
   1.900 +  case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
   1.901 +    {
   1.902 +      MozGtkScrollbarMetrics metrics;
   1.903 +      moz_gtk_get_scrollbar_metrics(&metrics);
   1.904 +      aResult->top = aResult->left = aResult->right = aResult->bottom = metrics.trough_border;
   1.905 +    }
   1.906 +    break;
   1.907 +  case NS_THEME_TOOLBOX:
   1.908 +    // gtk has no toolbox equivalent.  So, although we map toolbox to
   1.909 +    // gtk's 'toolbar' for purposes of painting the widget background,
   1.910 +    // we don't use the toolbar border for toolbox.
   1.911 +    break;
   1.912 +  case NS_THEME_TOOLBAR_DUAL_BUTTON:
   1.913 +    // TOOLBAR_DUAL_BUTTON is an interesting case.  We want a border to draw
   1.914 +    // around the entire button + dropdown, and also an inner border if you're
   1.915 +    // over the button part.  But, we want the inner button to be right up
   1.916 +    // against the edge of the outer button so that the borders overlap.
   1.917 +    // To make this happen, we draw a button border for the outer button,
   1.918 +    // but don't reserve any space for it.
   1.919 +    break;
   1.920 +  case NS_THEME_TAB:
   1.921 +    // Top tabs have no bottom border, bottom tabs have no top border
   1.922 +    moz_gtk_get_widget_border(MOZ_GTK_TAB, &aResult->left, &aResult->top,
   1.923 +                              &aResult->right, &aResult->bottom, direction,
   1.924 +                              FALSE);
   1.925 +    if (IsBottomTab(aFrame))
   1.926 +        aResult->top = 0;
   1.927 +    else
   1.928 +        aResult->bottom = 0;
   1.929 +    break;
   1.930 +  case NS_THEME_MENUITEM:
   1.931 +  case NS_THEME_CHECKMENUITEM:
   1.932 +  case NS_THEME_RADIOMENUITEM:
   1.933 +    // For regular menuitems, we will be using GetWidgetPadding instead of
   1.934 +    // GetWidgetBorder to pad up the widget's internals; other menuitems
   1.935 +    // will need to fall through and use the default case as before.
   1.936 +    if (IsRegularMenuItem(aFrame))
   1.937 +      break;
   1.938 +  default:
   1.939 +    {
   1.940 +      GtkThemeWidgetType gtkWidgetType;
   1.941 +      if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
   1.942 +                               nullptr)) {
   1.943 +        moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
   1.944 +                                  &aResult->right, &aResult->bottom, direction,
   1.945 +                                  IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
   1.946 +      }
   1.947 +    }
   1.948 +  }
   1.949 +  return NS_OK;
   1.950 +}
   1.951 +
   1.952 +bool
   1.953 +nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
   1.954 +                                   nsIFrame* aFrame, uint8_t aWidgetType,
   1.955 +                                   nsIntMargin* aResult)
   1.956 +{
   1.957 +  switch (aWidgetType) {
   1.958 +    case NS_THEME_BUTTON_FOCUS:
   1.959 +    case NS_THEME_TOOLBAR_BUTTON:
   1.960 +    case NS_THEME_TOOLBAR_DUAL_BUTTON:
   1.961 +    case NS_THEME_TAB_SCROLLARROW_BACK:
   1.962 +    case NS_THEME_TAB_SCROLLARROW_FORWARD:
   1.963 +    case NS_THEME_DROPDOWN_BUTTON:
   1.964 +    case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
   1.965 +    case NS_THEME_BUTTON_ARROW_UP:
   1.966 +    case NS_THEME_BUTTON_ARROW_DOWN:
   1.967 +    case NS_THEME_BUTTON_ARROW_NEXT:
   1.968 +    case NS_THEME_BUTTON_ARROW_PREVIOUS:
   1.969 +    case NS_THEME_RANGE_THUMB:
   1.970 +    // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
   1.971 +    // and have a meaningful baseline, so they can't have
   1.972 +    // author-specified padding.
   1.973 +    case NS_THEME_CHECKBOX:
   1.974 +    case NS_THEME_RADIO:
   1.975 +      aResult->SizeTo(0, 0, 0, 0);
   1.976 +      return true;
   1.977 +    case NS_THEME_MENUITEM:
   1.978 +    case NS_THEME_CHECKMENUITEM:
   1.979 +    case NS_THEME_RADIOMENUITEM:
   1.980 +      {
   1.981 +        // Menubar and menulist have their padding specified in CSS.
   1.982 +        if (!IsRegularMenuItem(aFrame))
   1.983 +          return false;
   1.984 +
   1.985 +        aResult->SizeTo(0, 0, 0, 0);
   1.986 +        GtkThemeWidgetType gtkWidgetType;
   1.987 +        if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
   1.988 +                                 nullptr)) {
   1.989 +          moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
   1.990 +                                    &aResult->right, &aResult->bottom, GetTextDirection(aFrame),
   1.991 +                                    IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
   1.992 +        }
   1.993 +
   1.994 +        gint horizontal_padding;
   1.995 +
   1.996 +        if (aWidgetType == NS_THEME_MENUITEM)
   1.997 +          moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
   1.998 +        else
   1.999 +          moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
  1.1000 +
  1.1001 +        aResult->left += horizontal_padding;
  1.1002 +        aResult->right += horizontal_padding;
  1.1003 +
  1.1004 +        return true;
  1.1005 +      }
  1.1006 +  }
  1.1007 +
  1.1008 +  return false;
  1.1009 +}
  1.1010 +
  1.1011 +bool
  1.1012 +nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
  1.1013 +                                    nsIFrame* aFrame, uint8_t aWidgetType,
  1.1014 +                                    nsRect* aOverflowRect)
  1.1015 +{
  1.1016 +  nsIntMargin extraSize;
  1.1017 +  if (!GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize))
  1.1018 +    return false;
  1.1019 +
  1.1020 +  int32_t p2a = aContext->AppUnitsPerDevPixel();
  1.1021 +  nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a),
  1.1022 +             NSIntPixelsToAppUnits(extraSize.right, p2a),
  1.1023 +             NSIntPixelsToAppUnits(extraSize.bottom, p2a),
  1.1024 +             NSIntPixelsToAppUnits(extraSize.left, p2a));
  1.1025 +
  1.1026 +  aOverflowRect->Inflate(m);
  1.1027 +  return true;
  1.1028 +}
  1.1029 +
  1.1030 +NS_IMETHODIMP
  1.1031 +nsNativeThemeGTK::GetMinimumWidgetSize(nsRenderingContext* aContext,
  1.1032 +                                       nsIFrame* aFrame, uint8_t aWidgetType,
  1.1033 +                                       nsIntSize* aResult, bool* aIsOverridable)
  1.1034 +{
  1.1035 +  aResult->width = aResult->height = 0;
  1.1036 +  *aIsOverridable = true;
  1.1037 +
  1.1038 +  switch (aWidgetType) {
  1.1039 +    case NS_THEME_SCROLLBAR_BUTTON_UP:
  1.1040 +    case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  1.1041 +      {
  1.1042 +        MozGtkScrollbarMetrics metrics;
  1.1043 +        moz_gtk_get_scrollbar_metrics(&metrics);
  1.1044 +
  1.1045 +        aResult->width = metrics.slider_width;
  1.1046 +        aResult->height = metrics.stepper_size;
  1.1047 +        *aIsOverridable = false;
  1.1048 +      }
  1.1049 +      break;
  1.1050 +    case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  1.1051 +    case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  1.1052 +      {
  1.1053 +        MozGtkScrollbarMetrics metrics;
  1.1054 +        moz_gtk_get_scrollbar_metrics(&metrics);
  1.1055 +
  1.1056 +        aResult->width = metrics.stepper_size;
  1.1057 +        aResult->height = metrics.slider_width;
  1.1058 +        *aIsOverridable = false;
  1.1059 +      }
  1.1060 +      break;
  1.1061 +    case NS_THEME_SPLITTER:
  1.1062 +    {
  1.1063 +      gint metrics;
  1.1064 +      if (IsHorizontal(aFrame)) {
  1.1065 +        moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
  1.1066 +        aResult->width = metrics;
  1.1067 +        aResult->height = 0;
  1.1068 +      } else {
  1.1069 +        moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics);
  1.1070 +        aResult->width = 0;
  1.1071 +        aResult->height = metrics;
  1.1072 +      }
  1.1073 +      *aIsOverridable = false;
  1.1074 +    }
  1.1075 +    break;
  1.1076 +    case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1.1077 +    case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  1.1078 +    {
  1.1079 +      /* While we enforce a minimum size for the thumb, this is ignored
  1.1080 +       * for the some scrollbars if buttons are hidden (bug 513006) because
  1.1081 +       * the thumb isn't a direct child of the scrollbar, unlike the buttons
  1.1082 +       * or track. So add a minimum size to the track as well to prevent a
  1.1083 +       * 0-width scrollbar. */
  1.1084 +      MozGtkScrollbarMetrics metrics;
  1.1085 +      moz_gtk_get_scrollbar_metrics(&metrics);
  1.1086 +
  1.1087 +      if (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL)
  1.1088 +        aResult->width = metrics.slider_width + 2 * metrics.trough_border;
  1.1089 +      else
  1.1090 +        aResult->height = metrics.slider_width + 2 * metrics.trough_border;
  1.1091 +
  1.1092 +      *aIsOverridable = false;
  1.1093 +    }
  1.1094 +    break;
  1.1095 +    case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  1.1096 +    case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  1.1097 +      {
  1.1098 +        MozGtkScrollbarMetrics metrics;
  1.1099 +        moz_gtk_get_scrollbar_metrics(&metrics);
  1.1100 +
  1.1101 +        nsRect rect = aFrame->GetParent()->GetRect();
  1.1102 +        int32_t p2a = aFrame->PresContext()->DeviceContext()->
  1.1103 +                        AppUnitsPerDevPixel();
  1.1104 +        nsMargin margin;
  1.1105 +
  1.1106 +        /* Get the available space, if that is smaller then the minimum size,
  1.1107 +         * adjust the mininum size to fit into it.
  1.1108 +         * Setting aIsOverridable to true has no effect for thumbs. */
  1.1109 +        aFrame->GetMargin(margin);
  1.1110 +        rect.Deflate(margin);
  1.1111 +        aFrame->GetParent()->GetBorderAndPadding(margin);
  1.1112 +        rect.Deflate(margin);
  1.1113 +
  1.1114 +        if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL) {
  1.1115 +          aResult->width = metrics.slider_width;
  1.1116 +          aResult->height = std::min(NSAppUnitsToIntPixels(rect.height, p2a),
  1.1117 +                                   metrics.min_slider_size);
  1.1118 +        } else {
  1.1119 +          aResult->height = metrics.slider_width;
  1.1120 +          aResult->width = std::min(NSAppUnitsToIntPixels(rect.width, p2a),
  1.1121 +                                  metrics.min_slider_size);
  1.1122 +        }
  1.1123 +
  1.1124 +        *aIsOverridable = false;
  1.1125 +      }
  1.1126 +      break;
  1.1127 +    case NS_THEME_RANGE_THUMB:
  1.1128 +      {
  1.1129 +        gint thumb_length, thumb_height;
  1.1130 +
  1.1131 +        if (IsRangeHorizontal(aFrame)) {
  1.1132 +          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
  1.1133 +        } else {
  1.1134 +          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_length);
  1.1135 +        }
  1.1136 +        aResult->width = thumb_length;
  1.1137 +        aResult->height = thumb_height;
  1.1138 +
  1.1139 +        *aIsOverridable = false;
  1.1140 +      }
  1.1141 +      break;
  1.1142 +    case NS_THEME_SCALE_THUMB_HORIZONTAL:
  1.1143 +    case NS_THEME_SCALE_THUMB_VERTICAL:
  1.1144 +      {
  1.1145 +        gint thumb_length, thumb_height;
  1.1146 +
  1.1147 +        if (aWidgetType == NS_THEME_SCALE_THUMB_VERTICAL) {
  1.1148 +          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height);
  1.1149 +          aResult->width = thumb_height;
  1.1150 +          aResult->height = thumb_length;
  1.1151 +        } else {
  1.1152 +          moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
  1.1153 +          aResult->width = thumb_length;
  1.1154 +          aResult->height = thumb_height;
  1.1155 +        }
  1.1156 +
  1.1157 +        *aIsOverridable = false;
  1.1158 +      }
  1.1159 +      break;
  1.1160 +    case NS_THEME_TAB_SCROLLARROW_BACK:
  1.1161 +    case NS_THEME_TAB_SCROLLARROW_FORWARD:
  1.1162 +      {
  1.1163 +        moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
  1.1164 +        *aIsOverridable = false;
  1.1165 +      }
  1.1166 +      break;
  1.1167 +  case NS_THEME_DROPDOWN_BUTTON:
  1.1168 +    {
  1.1169 +      moz_gtk_get_combo_box_entry_button_size(&aResult->width,
  1.1170 +                                              &aResult->height);
  1.1171 +      *aIsOverridable = false;
  1.1172 +    }
  1.1173 +    break;
  1.1174 +  case NS_THEME_MENUSEPARATOR:
  1.1175 +    {
  1.1176 +      gint separator_height;
  1.1177 +
  1.1178 +      moz_gtk_get_menu_separator_height(&separator_height);
  1.1179 +      aResult->height = separator_height;
  1.1180 +    
  1.1181 +      *aIsOverridable = false;
  1.1182 +    }
  1.1183 +    break;
  1.1184 +  case NS_THEME_CHECKBOX:
  1.1185 +  case NS_THEME_RADIO:
  1.1186 +    {
  1.1187 +      gint indicator_size, indicator_spacing;
  1.1188 +
  1.1189 +      if (aWidgetType == NS_THEME_CHECKBOX) {
  1.1190 +        moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
  1.1191 +      } else {
  1.1192 +        moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
  1.1193 +      }
  1.1194 +
  1.1195 +      // Include space for the indicator and the padding around it.
  1.1196 +      aResult->width = indicator_size;
  1.1197 +      aResult->height = indicator_size;
  1.1198 +    }
  1.1199 +    break;
  1.1200 +  case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
  1.1201 +  case NS_THEME_BUTTON_ARROW_UP:
  1.1202 +  case NS_THEME_BUTTON_ARROW_DOWN:
  1.1203 +  case NS_THEME_BUTTON_ARROW_NEXT:
  1.1204 +  case NS_THEME_BUTTON_ARROW_PREVIOUS:
  1.1205 +    {
  1.1206 +        moz_gtk_get_arrow_size(&aResult->width, &aResult->height);
  1.1207 +        *aIsOverridable = false;
  1.1208 +    }
  1.1209 +    break;
  1.1210 +  case NS_THEME_CHECKBOX_CONTAINER:
  1.1211 +  case NS_THEME_RADIO_CONTAINER:
  1.1212 +  case NS_THEME_CHECKBOX_LABEL:
  1.1213 +  case NS_THEME_RADIO_LABEL:
  1.1214 +  case NS_THEME_BUTTON:
  1.1215 +  case NS_THEME_DROPDOWN:
  1.1216 +  case NS_THEME_TOOLBAR_BUTTON:
  1.1217 +  case NS_THEME_TREEVIEW_HEADER_CELL:
  1.1218 +    {
  1.1219 +      // Just include our border, and let the box code augment the size.
  1.1220 +      nsIntMargin border;
  1.1221 +      nsNativeThemeGTK::GetWidgetBorder(aContext->DeviceContext(),
  1.1222 +                                        aFrame, aWidgetType, &border);
  1.1223 +      aResult->width = border.left + border.right;
  1.1224 +      aResult->height = border.top + border.bottom;
  1.1225 +    }
  1.1226 +    break;
  1.1227 +  case NS_THEME_TOOLBAR_SEPARATOR:
  1.1228 +    {
  1.1229 +      gint separator_width;
  1.1230 +    
  1.1231 +      moz_gtk_get_toolbar_separator_width(&separator_width);
  1.1232 +    
  1.1233 +      aResult->width = separator_width;
  1.1234 +    }
  1.1235 +    break;
  1.1236 +  case NS_THEME_SPINNER:
  1.1237 +    // hard code these sizes
  1.1238 +    aResult->width = 14;
  1.1239 +    aResult->height = 26;
  1.1240 +    break;
  1.1241 +  case NS_THEME_TREEVIEW_HEADER_SORTARROW:
  1.1242 +  case NS_THEME_SPINNER_UP_BUTTON:
  1.1243 +  case NS_THEME_SPINNER_DOWN_BUTTON:
  1.1244 +    // hard code these sizes
  1.1245 +    aResult->width = 14;
  1.1246 +    aResult->height = 13;
  1.1247 +    break;
  1.1248 +  case NS_THEME_RESIZER:
  1.1249 +    // same as Windows to make our lives easier
  1.1250 +    aResult->width = aResult->height = 15;
  1.1251 +    *aIsOverridable = false;
  1.1252 +    break;
  1.1253 +  case NS_THEME_TREEVIEW_TWISTY:
  1.1254 +  case NS_THEME_TREEVIEW_TWISTY_OPEN:
  1.1255 +    {
  1.1256 +      gint expander_size;
  1.1257 +
  1.1258 +      moz_gtk_get_treeview_expander_size(&expander_size);
  1.1259 +      aResult->width = aResult->height = expander_size;
  1.1260 +      *aIsOverridable = false;
  1.1261 +    }
  1.1262 +    break;
  1.1263 +  }
  1.1264 +  return NS_OK;
  1.1265 +}
  1.1266 +
  1.1267 +NS_IMETHODIMP
  1.1268 +nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
  1.1269 +                                     nsIAtom* aAttribute, bool* aShouldRepaint)
  1.1270 +{
  1.1271 +  // Some widget types just never change state.
  1.1272 +  if (aWidgetType == NS_THEME_TOOLBOX ||
  1.1273 +      aWidgetType == NS_THEME_TOOLBAR ||
  1.1274 +      aWidgetType == NS_THEME_STATUSBAR ||
  1.1275 +      aWidgetType == NS_THEME_STATUSBAR_PANEL ||
  1.1276 +      aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL ||
  1.1277 +      aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
  1.1278 +      aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
  1.1279 +      aWidgetType == NS_THEME_PROGRESSBAR ||
  1.1280 +      aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
  1.1281 +      aWidgetType == NS_THEME_MENUBAR ||
  1.1282 +      aWidgetType == NS_THEME_MENUPOPUP ||
  1.1283 +      aWidgetType == NS_THEME_TOOLTIP ||
  1.1284 +      aWidgetType == NS_THEME_MENUSEPARATOR ||
  1.1285 +      aWidgetType == NS_THEME_WINDOW ||
  1.1286 +      aWidgetType == NS_THEME_DIALOG) {
  1.1287 +    *aShouldRepaint = false;
  1.1288 +    return NS_OK;
  1.1289 +  }
  1.1290 +
  1.1291 +  if ((aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
  1.1292 +       aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
  1.1293 +       aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT ||
  1.1294 +       aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) &&
  1.1295 +      (aAttribute == nsGkAtoms::curpos ||
  1.1296 +       aAttribute == nsGkAtoms::maxpos)) {
  1.1297 +    *aShouldRepaint = true;
  1.1298 +    return NS_OK;
  1.1299 +  }
  1.1300 +
  1.1301 +  // XXXdwh Not sure what can really be done here.  Can at least guess for
  1.1302 +  // specific widgets that they're highly unlikely to have certain states.
  1.1303 +  // For example, a toolbar doesn't care about any states.
  1.1304 +  if (!aAttribute) {
  1.1305 +    // Hover/focus/active changed.  Always repaint.
  1.1306 +    *aShouldRepaint = true;
  1.1307 +  }
  1.1308 +  else {
  1.1309 +    // Check the attribute to see if it's relevant.  
  1.1310 +    // disabled, checked, dlgtype, default, etc.
  1.1311 +    *aShouldRepaint = false;
  1.1312 +    if (aAttribute == nsGkAtoms::disabled ||
  1.1313 +        aAttribute == nsGkAtoms::checked ||
  1.1314 +        aAttribute == nsGkAtoms::selected ||
  1.1315 +        aAttribute == nsGkAtoms::focused ||
  1.1316 +        aAttribute == nsGkAtoms::readonly ||
  1.1317 +        aAttribute == nsGkAtoms::_default ||
  1.1318 +        aAttribute == nsGkAtoms::menuactive ||
  1.1319 +        aAttribute == nsGkAtoms::open ||
  1.1320 +        aAttribute == nsGkAtoms::parentfocused)
  1.1321 +      *aShouldRepaint = true;
  1.1322 +  }
  1.1323 +
  1.1324 +  return NS_OK;
  1.1325 +}
  1.1326 +
  1.1327 +NS_IMETHODIMP
  1.1328 +nsNativeThemeGTK::ThemeChanged()
  1.1329 +{
  1.1330 +  memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
  1.1331 +  return NS_OK;
  1.1332 +}
  1.1333 +
  1.1334 +NS_IMETHODIMP_(bool)
  1.1335 +nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
  1.1336 +                                      nsIFrame* aFrame,
  1.1337 +                                      uint8_t aWidgetType)
  1.1338 +{
  1.1339 +  if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
  1.1340 +    return false;
  1.1341 +
  1.1342 +  switch (aWidgetType) {
  1.1343 +  case NS_THEME_BUTTON:
  1.1344 +  case NS_THEME_BUTTON_FOCUS:
  1.1345 +  case NS_THEME_RADIO:
  1.1346 +  case NS_THEME_CHECKBOX:
  1.1347 +  case NS_THEME_TOOLBOX: // N/A
  1.1348 +  case NS_THEME_TOOLBAR:
  1.1349 +  case NS_THEME_TOOLBAR_BUTTON:
  1.1350 +  case NS_THEME_TOOLBAR_DUAL_BUTTON: // so we can override the border with 0
  1.1351 +  case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
  1.1352 +  case NS_THEME_BUTTON_ARROW_UP:
  1.1353 +  case NS_THEME_BUTTON_ARROW_DOWN:
  1.1354 +  case NS_THEME_BUTTON_ARROW_NEXT:
  1.1355 +  case NS_THEME_BUTTON_ARROW_PREVIOUS:
  1.1356 +  case NS_THEME_TOOLBAR_SEPARATOR:
  1.1357 +  case NS_THEME_TOOLBAR_GRIPPER:
  1.1358 +  case NS_THEME_STATUSBAR:
  1.1359 +  case NS_THEME_STATUSBAR_PANEL:
  1.1360 +  case NS_THEME_STATUSBAR_RESIZER_PANEL:
  1.1361 +  case NS_THEME_RESIZER:
  1.1362 +  case NS_THEME_LISTBOX:
  1.1363 +    // case NS_THEME_LISTBOX_LISTITEM:
  1.1364 +  case NS_THEME_TREEVIEW:
  1.1365 +    // case NS_THEME_TREEVIEW_TREEITEM:
  1.1366 +  case NS_THEME_TREEVIEW_TWISTY:
  1.1367 +    // case NS_THEME_TREEVIEW_LINE:
  1.1368 +    // case NS_THEME_TREEVIEW_HEADER:
  1.1369 +  case NS_THEME_TREEVIEW_HEADER_CELL:
  1.1370 +  case NS_THEME_TREEVIEW_HEADER_SORTARROW:
  1.1371 +  case NS_THEME_TREEVIEW_TWISTY_OPEN:
  1.1372 +    case NS_THEME_PROGRESSBAR:
  1.1373 +    case NS_THEME_PROGRESSBAR_CHUNK:
  1.1374 +    case NS_THEME_PROGRESSBAR_VERTICAL:
  1.1375 +    case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  1.1376 +    case NS_THEME_TAB:
  1.1377 +    // case NS_THEME_TAB_PANEL:
  1.1378 +    case NS_THEME_TAB_PANELS:
  1.1379 +    case NS_THEME_TAB_SCROLLARROW_BACK:
  1.1380 +    case NS_THEME_TAB_SCROLLARROW_FORWARD:
  1.1381 +  case NS_THEME_TOOLTIP:
  1.1382 +  case NS_THEME_SPINNER:
  1.1383 +  case NS_THEME_SPINNER_UP_BUTTON:
  1.1384 +  case NS_THEME_SPINNER_DOWN_BUTTON:
  1.1385 +  case NS_THEME_SPINNER_TEXTFIELD:
  1.1386 +    // case NS_THEME_SCROLLBAR:  (n/a for gtk)
  1.1387 +    // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk)
  1.1388 +  case NS_THEME_SCROLLBAR_BUTTON_UP:
  1.1389 +  case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  1.1390 +  case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  1.1391 +  case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  1.1392 +  case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1.1393 +  case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  1.1394 +  case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  1.1395 +  case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  1.1396 +  case NS_THEME_NUMBER_INPUT:
  1.1397 +  case NS_THEME_TEXTFIELD:
  1.1398 +  case NS_THEME_TEXTFIELD_MULTILINE:
  1.1399 +  case NS_THEME_DROPDOWN_TEXTFIELD:
  1.1400 +  case NS_THEME_RANGE:
  1.1401 +  case NS_THEME_RANGE_THUMB:
  1.1402 +  case NS_THEME_SCALE_HORIZONTAL:
  1.1403 +  case NS_THEME_SCALE_THUMB_HORIZONTAL:
  1.1404 +  case NS_THEME_SCALE_VERTICAL:
  1.1405 +  case NS_THEME_SCALE_THUMB_VERTICAL:
  1.1406 +    // case NS_THEME_SCALE_THUMB_START:
  1.1407 +    // case NS_THEME_SCALE_THUMB_END:
  1.1408 +    // case NS_THEME_SCALE_TICK:
  1.1409 +  case NS_THEME_CHECKBOX_CONTAINER:
  1.1410 +  case NS_THEME_RADIO_CONTAINER:
  1.1411 +  case NS_THEME_CHECKBOX_LABEL:
  1.1412 +  case NS_THEME_RADIO_LABEL:
  1.1413 +  case NS_THEME_MENUBAR:
  1.1414 +  case NS_THEME_MENUPOPUP:
  1.1415 +  case NS_THEME_MENUITEM:
  1.1416 +  case NS_THEME_MENUARROW:
  1.1417 +  case NS_THEME_MENUSEPARATOR:
  1.1418 +  case NS_THEME_CHECKMENUITEM:
  1.1419 +  case NS_THEME_RADIOMENUITEM:
  1.1420 +  case NS_THEME_SPLITTER:
  1.1421 +  case NS_THEME_WINDOW:
  1.1422 +  case NS_THEME_DIALOG:
  1.1423 +  case NS_THEME_DROPDOWN:
  1.1424 +  case NS_THEME_DROPDOWN_TEXT:
  1.1425 +    return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
  1.1426 +
  1.1427 +  case NS_THEME_DROPDOWN_BUTTON:
  1.1428 +    // "Native" dropdown buttons cause padding and margin problems, but only
  1.1429 +    // in HTML so allow them in XUL.
  1.1430 +    return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
  1.1431 +           !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
  1.1432 +
  1.1433 +  }
  1.1434 +
  1.1435 +  return false;
  1.1436 +}
  1.1437 +
  1.1438 +NS_IMETHODIMP_(bool)
  1.1439 +nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType)
  1.1440 +{
  1.1441 +  // XXXdwh At some point flesh all of this out.
  1.1442 +  if (aWidgetType == NS_THEME_DROPDOWN_BUTTON ||
  1.1443 +      aWidgetType == NS_THEME_RADIO ||
  1.1444 +      aWidgetType == NS_THEME_RANGE_THUMB ||
  1.1445 +      aWidgetType == NS_THEME_CHECKBOX ||
  1.1446 +      aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ||
  1.1447 +      aWidgetType == NS_THEME_TAB_SCROLLARROW_FORWARD ||
  1.1448 +      aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
  1.1449 +      aWidgetType == NS_THEME_BUTTON_ARROW_DOWN ||
  1.1450 +      aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
  1.1451 +      aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
  1.1452 +    return false;
  1.1453 +  return true;
  1.1454 +}
  1.1455 +
  1.1456 +bool
  1.1457 +nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
  1.1458 +{
  1.1459 +   if (aWidgetType == NS_THEME_DROPDOWN ||
  1.1460 +      aWidgetType == NS_THEME_BUTTON || 
  1.1461 +      aWidgetType == NS_THEME_TREEVIEW_HEADER_CELL)
  1.1462 +    return true;
  1.1463 +  
  1.1464 +  return false;
  1.1465 +}
  1.1466 +
  1.1467 +bool
  1.1468 +nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
  1.1469 +{
  1.1470 +  return false;
  1.1471 +}
  1.1472 +
  1.1473 +nsITheme::Transparency
  1.1474 +nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
  1.1475 +{
  1.1476 +  switch (aWidgetType) {
  1.1477 +  // These widgets always draw a default background.
  1.1478 +#if (MOZ_WIDGET_GTK == 2)
  1.1479 +  case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  1.1480 +  case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1.1481 +  case NS_THEME_TOOLBAR:
  1.1482 +  case NS_THEME_MENUBAR:
  1.1483 +#endif
  1.1484 +  case NS_THEME_MENUPOPUP:
  1.1485 +  case NS_THEME_WINDOW:
  1.1486 +  case NS_THEME_DIALOG:
  1.1487 +  // Tooltips use gtk_paint_flat_box().
  1.1488 +  case NS_THEME_TOOLTIP:
  1.1489 +    return eOpaque;
  1.1490 +  }
  1.1491 +
  1.1492 +  return eUnknownTransparency;
  1.1493 +}

mercurial