widget/gtk/nsNativeThemeGTK.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsNativeThemeGTK.h"
     7 #include "nsThemeConstants.h"
     8 #include "gtkdrawing.h"
    10 #include "nsIObserverService.h"
    11 #include "nsIServiceManager.h"
    12 #include "nsIFrame.h"
    13 #include "nsIPresShell.h"
    14 #include "nsIContent.h"
    15 #include "nsViewManager.h"
    16 #include "nsNameSpaceManager.h"
    17 #include "nsGfxCIID.h"
    18 #include "nsTransform2D.h"
    19 #include "nsMenuFrame.h"
    20 #include "prlink.h"
    21 #include "nsIDOMHTMLInputElement.h"
    22 #include "nsRenderingContext.h"
    23 #include "nsGkAtoms.h"
    25 #include "mozilla/EventStates.h"
    26 #include "mozilla/Services.h"
    28 #include <gdk/gdkprivate.h>
    29 #include <gtk/gtk.h>
    31 #include "gfxContext.h"
    32 #include "gfxPlatformGtk.h"
    33 #include "gfxGdkNativeRenderer.h"
    34 #include <algorithm>
    36 using namespace mozilla;
    38 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
    39                                                              nsIObserver)
    41 static int gLastGdkError;
    43 nsNativeThemeGTK::nsNativeThemeGTK()
    44 {
    45   if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
    46     memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
    47     return;
    48   }
    50   // We have to call moz_gtk_shutdown before the event loop stops running.
    51   nsCOMPtr<nsIObserverService> obsServ =
    52     mozilla::services::GetObserverService();
    53   obsServ->AddObserver(this, "xpcom-shutdown", false);
    55   memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
    56   memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates));
    57 }
    59 nsNativeThemeGTK::~nsNativeThemeGTK() {
    60 }
    62 NS_IMETHODIMP
    63 nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
    64                           const char16_t *aData)
    65 {
    66   if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
    67     moz_gtk_shutdown();
    68   } else {
    69     NS_NOTREACHED("unexpected topic");
    70     return NS_ERROR_UNEXPECTED;
    71   }
    73   return NS_OK;
    74 }
    76 void
    77 nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
    78 {
    79   nsIPresShell *shell = GetPresShell(aFrame);
    80   if (!shell)
    81     return;
    83   nsViewManager* vm = shell->GetViewManager();
    84   if (!vm)
    85     return;
    87   vm->InvalidateAllViews();
    88 }
    90 static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace)
    91 {
    92   nsIContent *content = aFrame ? aFrame->GetContent() : nullptr;
    93   if (!content)
    94     return false;
    95   return content->IsInNamespace(aNamespace);
    96 }
    98 static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
    99   return (aDisabledVector[aWidgetType >> 3] & (1 << (aWidgetType & 7))) != 0;
   100 }
   102 static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) {
   103   aDisabledVector[aWidgetType >> 3] |= (1 << (aWidgetType & 7));
   104 }
   106 static inline uint16_t
   107 GetWidgetStateKey(uint8_t aWidgetType, GtkWidgetState *aWidgetState)
   108 {
   109   return (aWidgetState->active |
   110           aWidgetState->focused << 1 |
   111           aWidgetState->inHover << 2 |
   112           aWidgetState->disabled << 3 |
   113           aWidgetState->isDefault << 4 |
   114           aWidgetType << 5);
   115 }
   117 static bool IsWidgetStateSafe(uint8_t* aSafeVector,
   118                                 uint8_t aWidgetType,
   119                                 GtkWidgetState *aWidgetState)
   120 {
   121   uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
   122   return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0;
   123 }
   125 static void SetWidgetStateSafe(uint8_t *aSafeVector,
   126                                uint8_t aWidgetType,
   127                                GtkWidgetState *aWidgetState)
   128 {
   129   uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState);
   130   aSafeVector[key >> 3] |= (1 << (key & 7));
   131 }
   133 static GtkTextDirection GetTextDirection(nsIFrame* aFrame)
   134 {
   135   if (!aFrame)
   136     return GTK_TEXT_DIR_NONE;
   138   switch (aFrame->StyleVisibility()->mDirection) {
   139     case NS_STYLE_DIRECTION_RTL:
   140       return GTK_TEXT_DIR_RTL;
   141     case NS_STYLE_DIRECTION_LTR:
   142       return GTK_TEXT_DIR_LTR;
   143   }
   145   return GTK_TEXT_DIR_NONE;
   146 }
   148 // Returns positive for negative margins (otherwise 0).
   149 gint
   150 nsNativeThemeGTK::GetTabMarginPixels(nsIFrame* aFrame)
   151 {
   152   nscoord margin =
   153     IsBottomTab(aFrame) ? aFrame->GetUsedMargin().top
   154     : aFrame->GetUsedMargin().bottom;
   156   return std::min<gint>(MOZ_GTK_TAB_MARGIN_MASK,
   157                 std::max(0,
   158                        aFrame->PresContext()->AppUnitsToDevPixels(-margin)));
   159 }
   161 bool
   162 nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame,
   163                                        GtkThemeWidgetType& aGtkWidgetType,
   164                                        GtkWidgetState* aState,
   165                                        gint* aWidgetFlags)
   166 {
   167   if (aState) {
   168     if (!aFrame) {
   169       // reset the entire struct to zero
   170       memset(aState, 0, sizeof(GtkWidgetState));
   171     } else {
   173       // For XUL checkboxes and radio buttons, the state of the parent
   174       // determines our state.
   175       nsIFrame *stateFrame = aFrame;
   176       if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX ||
   177                                        aWidgetType == NS_THEME_RADIO)) ||
   178                      aWidgetType == NS_THEME_CHECKBOX_LABEL ||
   179                      aWidgetType == NS_THEME_RADIO_LABEL)) {
   181         nsIAtom* atom = nullptr;
   182         if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
   183           if (aWidgetType == NS_THEME_CHECKBOX_LABEL ||
   184               aWidgetType == NS_THEME_RADIO_LABEL) {
   185             // Adjust stateFrame so GetContentState finds the correct state.
   186             stateFrame = aFrame = aFrame->GetParent()->GetParent();
   187           } else {
   188             // GetContentState knows to look one frame up for radio/checkbox
   189             // widgets, so don't adjust stateFrame here.
   190             aFrame = aFrame->GetParent();
   191           }
   192           if (aWidgetFlags) {
   193             if (!atom) {
   194               atom = (aWidgetType == NS_THEME_CHECKBOX ||
   195                       aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked
   196                                                               : nsGkAtoms::selected;
   197             }
   198             *aWidgetFlags = CheckBooleanAttr(aFrame, atom);
   199           }
   200         } else {
   201           if (aWidgetFlags) {
   202             nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent()));
   203             *aWidgetFlags = 0;
   204             if (inputElt) {
   205               bool isHTMLChecked;
   206               inputElt->GetChecked(&isHTMLChecked);
   207               if (isHTMLChecked)
   208                 *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
   209             }
   211             if (GetIndeterminate(aFrame))
   212               *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
   213           }
   214         }
   215       } else if (aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN ||
   216                  aWidgetType == NS_THEME_TREEVIEW_HEADER_SORTARROW ||
   217                  aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS ||
   218                  aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
   219                  aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
   220                  aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) {
   221         // The state of an arrow comes from its parent.
   222         stateFrame = aFrame = aFrame->GetParent();
   223       }
   225       EventStates eventState = GetContentState(stateFrame, aWidgetType);
   227       aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame);
   228       aState->active  = eventState.HasState(NS_EVENT_STATE_ACTIVE);
   229       aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS);
   230       aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER);
   231       aState->isDefault = IsDefaultButton(aFrame);
   232       aState->canDefault = FALSE; // XXX fix me
   233       aState->depressed = FALSE;
   235       if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
   236         // For these widget types, some element (either a child or parent)
   237         // actually has element focus, so we check the focused attribute
   238         // to see whether to draw in the focused state.
   239         if (aWidgetType == NS_THEME_NUMBER_INPUT ||
   240             aWidgetType == NS_THEME_TEXTFIELD ||
   241             aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
   242             aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
   243             aWidgetType == NS_THEME_SPINNER_TEXTFIELD ||
   244             aWidgetType == NS_THEME_RADIO_CONTAINER ||
   245             aWidgetType == NS_THEME_RADIO_LABEL) {
   246           aState->focused = IsFocused(aFrame);
   247         } else if (aWidgetType == NS_THEME_RADIO ||
   248                    aWidgetType == NS_THEME_CHECKBOX) {
   249           // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
   250           aState->focused = FALSE;
   251         }
   253         if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL ||
   254             aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) {
   255           // for scrollbars we need to go up two to go from the thumb to
   256           // the slider to the actual scrollbar object
   257           nsIFrame *tmpFrame = aFrame->GetParent()->GetParent();
   259           aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0);
   260           aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100);
   261         }
   263         if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
   264             aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
   265             aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT ||
   266             aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) {
   267           // set the state to disabled when the scrollbar is scrolled to
   268           // the beginning or the end, depending on the button type.
   269           int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
   270           int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
   271           if ((curpos == 0 && (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
   272                 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT)) ||
   273               (curpos == maxpos &&
   274                (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
   275                 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT)))
   276             aState->disabled = true;
   278           // In order to simulate native GTK scrollbar click behavior,
   279           // we set the active attribute on the element to true if it's
   280           // pressed with any mouse button.
   281           // This allows us to show that it's active without setting :active
   282           else if (CheckBooleanAttr(aFrame, nsGkAtoms::active))
   283             aState->active = true;
   285           if (aWidgetFlags) {
   286             *aWidgetFlags = GetScrollbarButtonType(aFrame);
   287             if (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP < 2)
   288               *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL;
   289           }
   290         }
   292         // menu item state is determined by the attribute "_moz-menuactive",
   293         // and not by the mouse hovering (accessibility).  as a special case,
   294         // menus which are children of a menu bar are only marked as prelight
   295         // if they are open, not on normal hover.
   297         if (aWidgetType == NS_THEME_MENUITEM ||
   298             aWidgetType == NS_THEME_CHECKMENUITEM ||
   299             aWidgetType == NS_THEME_RADIOMENUITEM ||
   300             aWidgetType == NS_THEME_MENUSEPARATOR ||
   301             aWidgetType == NS_THEME_MENUARROW) {
   302           bool isTopLevel = false;
   303           nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
   304           if (menuFrame) {
   305             isTopLevel = menuFrame->IsOnMenuBar();
   306           }
   308           if (isTopLevel) {
   309             aState->inHover = menuFrame->IsOpen();
   310             *aWidgetFlags |= MOZ_TOPLEVEL_MENU_ITEM;
   311           } else {
   312             aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
   313             *aWidgetFlags &= ~MOZ_TOPLEVEL_MENU_ITEM;
   314           }
   316           aState->active = FALSE;
   318           if (aWidgetType == NS_THEME_CHECKMENUITEM ||
   319               aWidgetType == NS_THEME_RADIOMENUITEM) {
   320             *aWidgetFlags = 0;
   321             if (aFrame && aFrame->GetContent()) {
   322               *aWidgetFlags = aFrame->GetContent()->
   323                 AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
   324                             nsGkAtoms::_true, eIgnoreCase);
   325             }
   326           }
   327         }
   329         // A button with drop down menu open or an activated toggle button
   330         // should always appear depressed.
   331         if (aWidgetType == NS_THEME_BUTTON ||
   332             aWidgetType == NS_THEME_TOOLBAR_BUTTON ||
   333             aWidgetType == NS_THEME_TOOLBAR_DUAL_BUTTON ||
   334             aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN ||
   335             aWidgetType == NS_THEME_DROPDOWN ||
   336             aWidgetType == NS_THEME_DROPDOWN_BUTTON) {
   337           bool menuOpen = IsOpenButton(aFrame);
   338           aState->depressed = IsCheckedButton(aFrame) || menuOpen;
   339           // we must not highlight buttons with open drop down menus on hover.
   340           aState->inHover = aState->inHover && !menuOpen;
   341         }
   343         // When the input field of the drop down button has focus, some themes
   344         // should draw focus for the drop down button as well.
   345         if (aWidgetType == NS_THEME_DROPDOWN_BUTTON && aWidgetFlags) {
   346           *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
   347         }
   348       }
   349     }
   350   }
   352   switch (aWidgetType) {
   353   case NS_THEME_BUTTON:
   354   case NS_THEME_TOOLBAR_BUTTON:
   355   case NS_THEME_TOOLBAR_DUAL_BUTTON:
   356     if (aWidgetFlags)
   357       *aWidgetFlags = (aWidgetType == NS_THEME_BUTTON) ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE;
   358     aGtkWidgetType = MOZ_GTK_BUTTON;
   359     break;
   360   case NS_THEME_CHECKBOX:
   361   case NS_THEME_RADIO:
   362     aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
   363     break;
   364   case NS_THEME_SCROLLBAR_BUTTON_UP:
   365   case NS_THEME_SCROLLBAR_BUTTON_DOWN:
   366   case NS_THEME_SCROLLBAR_BUTTON_LEFT:
   367   case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
   368     aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON;
   369     break;
   370   case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
   371     aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_VERTICAL;
   372     break;
   373   case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
   374     aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL;
   375     break;
   376   case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
   377     aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
   378     break;
   379   case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
   380     aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
   381     break;
   382   case NS_THEME_SPINNER:
   383     aGtkWidgetType = MOZ_GTK_SPINBUTTON;
   384     break;
   385   case NS_THEME_SPINNER_UP_BUTTON:
   386     aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP;
   387     break;
   388   case NS_THEME_SPINNER_DOWN_BUTTON:
   389     aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN;
   390     break;
   391   case NS_THEME_SPINNER_TEXTFIELD:
   392     aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY;
   393     break;
   394   case NS_THEME_RANGE:
   395     {
   396       if (IsRangeHorizontal(aFrame)) {
   397         if (aWidgetFlags)
   398           *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   399         aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
   400       } else {
   401         if (aWidgetFlags)
   402           *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   403         aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
   404       }
   405       break;
   406     }
   407   case NS_THEME_RANGE_THUMB:
   408     {
   409       if (IsRangeHorizontal(aFrame)) {
   410         if (aWidgetFlags)
   411           *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   412         aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
   413       } else {
   414         if (aWidgetFlags)
   415           *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   416         aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
   417       }
   418       break;
   419     }
   420   case NS_THEME_SCALE_HORIZONTAL:
   421     if (aWidgetFlags)
   422       *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   423     aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL;
   424     break;
   425   case NS_THEME_SCALE_THUMB_HORIZONTAL:
   426     if (aWidgetFlags)
   427       *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
   428     aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
   429     break;
   430   case NS_THEME_SCALE_VERTICAL:
   431     if (aWidgetFlags)
   432       *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   433     aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL;
   434     break;
   435   case NS_THEME_TOOLBAR_SEPARATOR:
   436     aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR;
   437     break;
   438   case NS_THEME_SCALE_THUMB_VERTICAL:
   439     if (aWidgetFlags)
   440       *aWidgetFlags = GTK_ORIENTATION_VERTICAL;
   441     aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL;
   442     break;
   443   case NS_THEME_TOOLBAR_GRIPPER:
   444     aGtkWidgetType = MOZ_GTK_GRIPPER;
   445     break;
   446   case NS_THEME_RESIZER:
   447     aGtkWidgetType = MOZ_GTK_RESIZER;
   448     break;
   449   case NS_THEME_NUMBER_INPUT:
   450   case NS_THEME_TEXTFIELD:
   451   case NS_THEME_TEXTFIELD_MULTILINE:
   452     aGtkWidgetType = MOZ_GTK_ENTRY;
   453     break;
   454   case NS_THEME_LISTBOX:
   455   case NS_THEME_TREEVIEW:
   456     aGtkWidgetType = MOZ_GTK_TREEVIEW;
   457     break;
   458   case NS_THEME_TREEVIEW_HEADER_CELL:
   459     if (aWidgetFlags) {
   460       // In this case, the flag denotes whether the header is the sorted one or not
   461       if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural)
   462         *aWidgetFlags = false;
   463       else
   464         *aWidgetFlags = true;
   465     }
   466     aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL;
   467     break;
   468   case NS_THEME_TREEVIEW_HEADER_SORTARROW:
   469     if (aWidgetFlags) {
   470       switch (GetTreeSortDirection(aFrame)) {
   471         case eTreeSortDirection_Ascending:
   472           *aWidgetFlags = GTK_ARROW_DOWN;
   473           break;
   474         case eTreeSortDirection_Descending:
   475           *aWidgetFlags = GTK_ARROW_UP;
   476           break;
   477         case eTreeSortDirection_Natural:
   478         default:
   479           /* This prevents the treecolums from getting smaller
   480            * and wider when switching sort direction off and on
   481            * */
   482           *aWidgetFlags = GTK_ARROW_NONE;
   483           break;
   484       }
   485     }
   486     aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW;
   487     break;
   488   case NS_THEME_TREEVIEW_TWISTY:
   489     aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
   490     if (aWidgetFlags)
   491       *aWidgetFlags = GTK_EXPANDER_COLLAPSED;
   492     break;
   493   case NS_THEME_TREEVIEW_TWISTY_OPEN:
   494     aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER;
   495     if (aWidgetFlags)
   496       *aWidgetFlags = GTK_EXPANDER_EXPANDED;
   497     break;
   498   case NS_THEME_DROPDOWN:
   499     aGtkWidgetType = MOZ_GTK_DROPDOWN;
   500     if (aWidgetFlags)
   501         *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
   502     break;
   503   case NS_THEME_DROPDOWN_TEXT:
   504     return false; // nothing to do, but prevents the bg from being drawn
   505   case NS_THEME_DROPDOWN_TEXTFIELD:
   506     aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
   507     break;
   508   case NS_THEME_DROPDOWN_BUTTON:
   509     aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
   510     break;
   511   case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
   512   case NS_THEME_BUTTON_ARROW_DOWN:
   513   case NS_THEME_BUTTON_ARROW_UP:
   514   case NS_THEME_BUTTON_ARROW_NEXT:
   515   case NS_THEME_BUTTON_ARROW_PREVIOUS:
   516     aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
   517     if (aWidgetFlags) {
   518       *aWidgetFlags = GTK_ARROW_DOWN;
   520       if (aWidgetType == NS_THEME_BUTTON_ARROW_UP)
   521         *aWidgetFlags = GTK_ARROW_UP;
   522       else if (aWidgetType == NS_THEME_BUTTON_ARROW_NEXT)
   523         *aWidgetFlags = GTK_ARROW_RIGHT;
   524       else if (aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
   525         *aWidgetFlags = GTK_ARROW_LEFT;
   526     }
   527     break;
   528   case NS_THEME_CHECKBOX_CONTAINER:
   529     aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER;
   530     break;
   531   case NS_THEME_RADIO_CONTAINER:
   532     aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER;
   533     break;
   534   case NS_THEME_CHECKBOX_LABEL:
   535     aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL;
   536     break;
   537   case NS_THEME_RADIO_LABEL:
   538     aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL;
   539     break;
   540   case NS_THEME_TOOLBAR:
   541     aGtkWidgetType = MOZ_GTK_TOOLBAR;
   542     break;
   543   case NS_THEME_TOOLTIP:
   544     aGtkWidgetType = MOZ_GTK_TOOLTIP;
   545     break;
   546   case NS_THEME_STATUSBAR_PANEL:
   547   case NS_THEME_STATUSBAR_RESIZER_PANEL:
   548     aGtkWidgetType = MOZ_GTK_FRAME;
   549     break;
   550   case NS_THEME_PROGRESSBAR:
   551   case NS_THEME_PROGRESSBAR_VERTICAL:
   552     aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
   553     break;
   554   case NS_THEME_PROGRESSBAR_CHUNK:
   555   case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
   556     {
   557       nsIFrame* stateFrame = aFrame->GetParent();
   558       EventStates eventStates = GetContentState(stateFrame, aWidgetType);
   560       aGtkWidgetType = IsIndeterminateProgress(stateFrame, eventStates)
   561                          ? (stateFrame->StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL)
   562                            ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
   563                            : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
   564                          : MOZ_GTK_PROGRESS_CHUNK;
   565     }
   566     break;
   567   case NS_THEME_TAB_SCROLLARROW_BACK:
   568   case NS_THEME_TAB_SCROLLARROW_FORWARD:
   569     if (aWidgetFlags)
   570       *aWidgetFlags = aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ?
   571                         GTK_ARROW_LEFT : GTK_ARROW_RIGHT;
   572     aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW;
   573     break;
   574   case NS_THEME_TAB_PANELS:
   575     aGtkWidgetType = MOZ_GTK_TABPANELS;
   576     break;
   577   case NS_THEME_TAB:
   578     {
   579       if (aWidgetFlags) {
   580         /* First bits will be used to store max(0,-bmargin) where bmargin
   581          * is the bottom margin of the tab in pixels  (resp. top margin,
   582          * for bottom tabs). */
   583         if (IsBottomTab(aFrame)) {
   584             *aWidgetFlags = MOZ_GTK_TAB_BOTTOM;
   585         } else {
   586             *aWidgetFlags = 0;
   587         }
   589         *aWidgetFlags |= GetTabMarginPixels(aFrame);
   591         if (IsSelectedTab(aFrame))
   592           *aWidgetFlags |= MOZ_GTK_TAB_SELECTED;
   594         if (IsFirstTab(aFrame))
   595           *aWidgetFlags |= MOZ_GTK_TAB_FIRST;
   596       }
   598       aGtkWidgetType = MOZ_GTK_TAB;
   599     }
   600     break;
   601   case NS_THEME_SPLITTER:
   602     if (IsHorizontal(aFrame))
   603       aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
   604     else 
   605       aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
   606     break;
   607   case NS_THEME_MENUBAR:
   608     aGtkWidgetType = MOZ_GTK_MENUBAR;
   609     break;
   610   case NS_THEME_MENUPOPUP:
   611     aGtkWidgetType = MOZ_GTK_MENUPOPUP;
   612     break;
   613   case NS_THEME_MENUITEM:
   614     aGtkWidgetType = MOZ_GTK_MENUITEM;
   615     break;
   616   case NS_THEME_MENUSEPARATOR:
   617     aGtkWidgetType = MOZ_GTK_MENUSEPARATOR;
   618     break;
   619   case NS_THEME_MENUARROW:
   620     aGtkWidgetType = MOZ_GTK_MENUARROW;
   621     break;
   622   case NS_THEME_CHECKMENUITEM:
   623     aGtkWidgetType = MOZ_GTK_CHECKMENUITEM;
   624     break;
   625   case NS_THEME_RADIOMENUITEM:
   626     aGtkWidgetType = MOZ_GTK_RADIOMENUITEM;
   627     break;
   628   case NS_THEME_WINDOW:
   629   case NS_THEME_DIALOG:
   630     aGtkWidgetType = MOZ_GTK_WINDOW;
   631     break;
   632   default:
   633     return false;
   634   }
   636   return true;
   637 }
   639 #if (MOZ_WIDGET_GTK == 2)
   640 class ThemeRenderer : public gfxGdkNativeRenderer {
   641 public:
   642   ThemeRenderer(GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
   643                 gint aFlags, GtkTextDirection aDirection,
   644                 const GdkRectangle& aGDKRect, const GdkRectangle& aGDKClip)
   645     : mState(aState), mGTKWidgetType(aGTKWidgetType), mFlags(aFlags),
   646       mDirection(aDirection), mGDKRect(aGDKRect), mGDKClip(aGDKClip) {}
   647   nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX, gint offsetY,
   648                        GdkRectangle * clipRects, uint32_t numClipRects);
   649 private:
   650   GtkWidgetState mState;
   651   GtkThemeWidgetType mGTKWidgetType;
   652   gint mFlags;
   653   GtkTextDirection mDirection;
   654   const GdkRectangle& mGDKRect;
   655   const GdkRectangle& mGDKClip;
   656 };
   658 nsresult
   659 ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX, 
   660         gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects)
   661 {
   662   GdkRectangle gdk_rect = mGDKRect;
   663   gdk_rect.x += offsetX;
   664   gdk_rect.y += offsetY;
   666   GdkRectangle gdk_clip = mGDKClip;
   667   gdk_clip.x += offsetX;
   668   gdk_clip.y += offsetY;
   670   GdkRectangle surfaceRect;
   671   surfaceRect.x = 0;
   672   surfaceRect.y = 0;
   673   gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height);
   674   gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
   676   NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
   677   moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,
   678                        &mState, mFlags, mDirection);
   680   return NS_OK;
   681 }
   682 #endif
   684 bool
   685 nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
   686                                         nsIntMargin* aExtra)
   687 {
   688   *aExtra = nsIntMargin(0,0,0,0);
   689   // Allow an extra one pixel above and below the thumb for certain
   690   // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
   691   // We modify the frame's overflow area.  See bug 297508.
   692   switch (aWidgetType) {
   693   case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
   694     aExtra->top = aExtra->bottom = 1;
   695     return true;
   696   case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
   697     aExtra->left = aExtra->right = 1;
   698     return true;
   700   // Include the indicator spacing (the padding around the control).
   701   case NS_THEME_CHECKBOX:
   702   case NS_THEME_RADIO:
   703     {
   704       gint indicator_size, indicator_spacing;
   706       if (aWidgetType == NS_THEME_CHECKBOX) {
   707         moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
   708       } else {
   709         moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
   710       }
   712       aExtra->top = indicator_spacing;
   713       aExtra->right = indicator_spacing;
   714       aExtra->bottom = indicator_spacing;
   715       aExtra->left = indicator_spacing;
   716       return true;
   717     }
   718   case NS_THEME_BUTTON :
   719     {
   720       if (IsDefaultButton(aFrame)) {
   721         // Some themes draw a default indicator outside the widget,
   722         // include that in overflow
   723         gint top, left, bottom, right;
   724         moz_gtk_button_get_default_overflow(&top, &left, &bottom, &right);
   725         aExtra->top = top;
   726         aExtra->right = right;
   727         aExtra->bottom = bottom;
   728         aExtra->left = left;
   729         return true;
   730       }
   731     }
   732   case NS_THEME_TAB :
   733     {
   734       if (!IsSelectedTab(aFrame))
   735         return false;
   737       gint gap_height = moz_gtk_get_tab_thickness();
   739       int32_t extra = gap_height - GetTabMarginPixels(aFrame);
   740       if (extra <= 0)
   741         return false;
   743       if (IsBottomTab(aFrame)) {
   744         aExtra->top = extra;
   745       } else {
   746         aExtra->bottom = extra;
   747       }
   748     }
   749   default:
   750     return false;
   751   }
   752 }
   754 NS_IMETHODIMP
   755 nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
   756                                        nsIFrame* aFrame,
   757                                        uint8_t aWidgetType,
   758                                        const nsRect& aRect,
   759                                        const nsRect& aDirtyRect)
   760 {
   761   GtkWidgetState state;
   762   GtkThemeWidgetType gtkWidgetType;
   763   GtkTextDirection direction = GetTextDirection(aFrame);
   764   gint flags;
   765   if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
   766                             &flags))
   767     return NS_OK;
   769   gfxContext* ctx = aContext->ThebesContext();
   770   nsPresContext *presContext = aFrame->PresContext();
   772   gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
   773   gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
   775   // Align to device pixels where sensible
   776   // to provide crisper and faster drawing.
   777   // Don't snap if it's a non-unit scale factor. We're going to have to take
   778   // slow paths then in any case.
   779   bool snapXY = ctx->UserToDevicePixelSnapped(rect);
   780   if (snapXY) {
   781     // Leave rect in device coords but make dirtyRect consistent.
   782     dirtyRect = ctx->UserToDevice(dirtyRect);
   783   }
   785   // Translate the dirty rect so that it is wrt the widget top-left.
   786   dirtyRect.MoveBy(-rect.TopLeft());
   787   // Round out the dirty rect to gdk pixels to ensure that gtk draws
   788   // enough pixels for interpolation to device pixels.
   789   dirtyRect.RoundOut();
   791   // GTK themes can only draw an integer number of pixels
   792   // (even when not snapped).
   793   nsIntRect widgetRect(0, 0, NS_lround(rect.Width()), NS_lround(rect.Height()));
   794   nsIntRect overflowRect(widgetRect);
   795   nsIntMargin extraSize;
   796   if (GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) {
   797     overflowRect.Inflate(extraSize);
   798   }
   800   // This is the rectangle that will actually be drawn, in gdk pixels
   801   nsIntRect drawingRect(int32_t(dirtyRect.X()),
   802                         int32_t(dirtyRect.Y()),
   803                         int32_t(dirtyRect.Width()),
   804                         int32_t(dirtyRect.Height()));
   805   if (widgetRect.IsEmpty()
   806       || !drawingRect.IntersectRect(overflowRect, drawingRect))
   807     return NS_OK;
   809   // gdk rectangles are wrt the drawing rect.
   811   GdkRectangle gdk_rect = {-drawingRect.x, -drawingRect.y,
   812                            widgetRect.width, widgetRect.height};
   814   // translate everything so (0,0) is the top left of the drawingRect
   815   gfxContextAutoSaveRestore autoSR(ctx);
   816   if (snapXY) {
   817     // Rects are in device coords.
   818     ctx->IdentityMatrix(); 
   819   }
   820   ctx->Translate(rect.TopLeft() + gfxPoint(drawingRect.x, drawingRect.y));
   822   NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
   823                "Trying to render an unsafe widget!");
   825   bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
   826   if (!safeState) {
   827     gLastGdkError = 0;
   828     gdk_error_trap_push ();
   829   }
   831 #if (MOZ_WIDGET_GTK == 2)
   832   // The gdk_clip is just advisory here, meaning "you don't
   833   // need to draw outside this rect if you don't feel like it!"
   834   GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height};
   836   ThemeRenderer renderer(state, gtkWidgetType, flags, direction,
   837                          gdk_rect, gdk_clip);
   839   // Some themes (e.g. Clearlooks) just don't clip properly to any
   840   // clip rect we provide, so we cannot advertise support for clipping within
   841   // the widget bounds.
   842   uint32_t rendererFlags = 0;
   843   if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) {
   844     rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE;
   845   }
   847   // GtkStyles (used by the widget drawing backend) are created for a
   848   // particular colormap/visual.
   849   GdkColormap* colormap = moz_gtk_widget_get_colormap();
   851   renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
   852 #else 
   853   moz_gtk_widget_paint(gtkWidgetType, ctx->GetCairo(), &gdk_rect, 
   854                        &state, flags, direction);
   855 #endif
   857   if (!safeState) {
   858     gdk_flush();
   859     gLastGdkError = gdk_error_trap_pop ();
   861     if (gLastGdkError) {
   862 #ifdef DEBUG
   863       printf("GTK theme failed for widget type %d, error was %d, state was "
   864              "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
   865              aWidgetType, gLastGdkError, state.active, state.focused,
   866              state.inHover, state.disabled);
   867 #endif
   868       NS_WARNING("GTK theme failed; disabling unsafe widget");
   869       SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType);
   870       // force refresh of the window, because the widget was not
   871       // successfully drawn it must be redrawn using the default look
   872       RefreshWidgetWindow(aFrame);
   873     } else {
   874       SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
   875     }
   876   }
   878   // Indeterminate progress bar are animated.
   879   if (gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
   880       gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
   881     if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
   882       NS_WARNING("unable to animate widget!");
   883     }
   884   }
   886   return NS_OK;
   887 }
   889 NS_IMETHODIMP
   890 nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
   891                                   uint8_t aWidgetType, nsIntMargin* aResult)
   892 {
   893   GtkTextDirection direction = GetTextDirection(aFrame);
   894   aResult->top = aResult->left = aResult->right = aResult->bottom = 0;
   895   switch (aWidgetType) {
   896   case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
   897   case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
   898     {
   899       MozGtkScrollbarMetrics metrics;
   900       moz_gtk_get_scrollbar_metrics(&metrics);
   901       aResult->top = aResult->left = aResult->right = aResult->bottom = metrics.trough_border;
   902     }
   903     break;
   904   case NS_THEME_TOOLBOX:
   905     // gtk has no toolbox equivalent.  So, although we map toolbox to
   906     // gtk's 'toolbar' for purposes of painting the widget background,
   907     // we don't use the toolbar border for toolbox.
   908     break;
   909   case NS_THEME_TOOLBAR_DUAL_BUTTON:
   910     // TOOLBAR_DUAL_BUTTON is an interesting case.  We want a border to draw
   911     // around the entire button + dropdown, and also an inner border if you're
   912     // over the button part.  But, we want the inner button to be right up
   913     // against the edge of the outer button so that the borders overlap.
   914     // To make this happen, we draw a button border for the outer button,
   915     // but don't reserve any space for it.
   916     break;
   917   case NS_THEME_TAB:
   918     // Top tabs have no bottom border, bottom tabs have no top border
   919     moz_gtk_get_widget_border(MOZ_GTK_TAB, &aResult->left, &aResult->top,
   920                               &aResult->right, &aResult->bottom, direction,
   921                               FALSE);
   922     if (IsBottomTab(aFrame))
   923         aResult->top = 0;
   924     else
   925         aResult->bottom = 0;
   926     break;
   927   case NS_THEME_MENUITEM:
   928   case NS_THEME_CHECKMENUITEM:
   929   case NS_THEME_RADIOMENUITEM:
   930     // For regular menuitems, we will be using GetWidgetPadding instead of
   931     // GetWidgetBorder to pad up the widget's internals; other menuitems
   932     // will need to fall through and use the default case as before.
   933     if (IsRegularMenuItem(aFrame))
   934       break;
   935   default:
   936     {
   937       GtkThemeWidgetType gtkWidgetType;
   938       if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
   939                                nullptr)) {
   940         moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
   941                                   &aResult->right, &aResult->bottom, direction,
   942                                   IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
   943       }
   944     }
   945   }
   946   return NS_OK;
   947 }
   949 bool
   950 nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
   951                                    nsIFrame* aFrame, uint8_t aWidgetType,
   952                                    nsIntMargin* aResult)
   953 {
   954   switch (aWidgetType) {
   955     case NS_THEME_BUTTON_FOCUS:
   956     case NS_THEME_TOOLBAR_BUTTON:
   957     case NS_THEME_TOOLBAR_DUAL_BUTTON:
   958     case NS_THEME_TAB_SCROLLARROW_BACK:
   959     case NS_THEME_TAB_SCROLLARROW_FORWARD:
   960     case NS_THEME_DROPDOWN_BUTTON:
   961     case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
   962     case NS_THEME_BUTTON_ARROW_UP:
   963     case NS_THEME_BUTTON_ARROW_DOWN:
   964     case NS_THEME_BUTTON_ARROW_NEXT:
   965     case NS_THEME_BUTTON_ARROW_PREVIOUS:
   966     case NS_THEME_RANGE_THUMB:
   967     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
   968     // and have a meaningful baseline, so they can't have
   969     // author-specified padding.
   970     case NS_THEME_CHECKBOX:
   971     case NS_THEME_RADIO:
   972       aResult->SizeTo(0, 0, 0, 0);
   973       return true;
   974     case NS_THEME_MENUITEM:
   975     case NS_THEME_CHECKMENUITEM:
   976     case NS_THEME_RADIOMENUITEM:
   977       {
   978         // Menubar and menulist have their padding specified in CSS.
   979         if (!IsRegularMenuItem(aFrame))
   980           return false;
   982         aResult->SizeTo(0, 0, 0, 0);
   983         GtkThemeWidgetType gtkWidgetType;
   984         if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr,
   985                                  nullptr)) {
   986           moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top,
   987                                     &aResult->right, &aResult->bottom, GetTextDirection(aFrame),
   988                                     IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML));
   989         }
   991         gint horizontal_padding;
   993         if (aWidgetType == NS_THEME_MENUITEM)
   994           moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
   995         else
   996           moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
   998         aResult->left += horizontal_padding;
   999         aResult->right += horizontal_padding;
  1001         return true;
  1005   return false;
  1008 bool
  1009 nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
  1010                                     nsIFrame* aFrame, uint8_t aWidgetType,
  1011                                     nsRect* aOverflowRect)
  1013   nsIntMargin extraSize;
  1014   if (!GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize))
  1015     return false;
  1017   int32_t p2a = aContext->AppUnitsPerDevPixel();
  1018   nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a),
  1019              NSIntPixelsToAppUnits(extraSize.right, p2a),
  1020              NSIntPixelsToAppUnits(extraSize.bottom, p2a),
  1021              NSIntPixelsToAppUnits(extraSize.left, p2a));
  1023   aOverflowRect->Inflate(m);
  1024   return true;
  1027 NS_IMETHODIMP
  1028 nsNativeThemeGTK::GetMinimumWidgetSize(nsRenderingContext* aContext,
  1029                                        nsIFrame* aFrame, uint8_t aWidgetType,
  1030                                        nsIntSize* aResult, bool* aIsOverridable)
  1032   aResult->width = aResult->height = 0;
  1033   *aIsOverridable = true;
  1035   switch (aWidgetType) {
  1036     case NS_THEME_SCROLLBAR_BUTTON_UP:
  1037     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  1039         MozGtkScrollbarMetrics metrics;
  1040         moz_gtk_get_scrollbar_metrics(&metrics);
  1042         aResult->width = metrics.slider_width;
  1043         aResult->height = metrics.stepper_size;
  1044         *aIsOverridable = false;
  1046       break;
  1047     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  1048     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  1050         MozGtkScrollbarMetrics metrics;
  1051         moz_gtk_get_scrollbar_metrics(&metrics);
  1053         aResult->width = metrics.stepper_size;
  1054         aResult->height = metrics.slider_width;
  1055         *aIsOverridable = false;
  1057       break;
  1058     case NS_THEME_SPLITTER:
  1060       gint metrics;
  1061       if (IsHorizontal(aFrame)) {
  1062         moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
  1063         aResult->width = metrics;
  1064         aResult->height = 0;
  1065       } else {
  1066         moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics);
  1067         aResult->width = 0;
  1068         aResult->height = metrics;
  1070       *aIsOverridable = false;
  1072     break;
  1073     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1074     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  1076       /* While we enforce a minimum size for the thumb, this is ignored
  1077        * for the some scrollbars if buttons are hidden (bug 513006) because
  1078        * the thumb isn't a direct child of the scrollbar, unlike the buttons
  1079        * or track. So add a minimum size to the track as well to prevent a
  1080        * 0-width scrollbar. */
  1081       MozGtkScrollbarMetrics metrics;
  1082       moz_gtk_get_scrollbar_metrics(&metrics);
  1084       if (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL)
  1085         aResult->width = metrics.slider_width + 2 * metrics.trough_border;
  1086       else
  1087         aResult->height = metrics.slider_width + 2 * metrics.trough_border;
  1089       *aIsOverridable = false;
  1091     break;
  1092     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  1093     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  1095         MozGtkScrollbarMetrics metrics;
  1096         moz_gtk_get_scrollbar_metrics(&metrics);
  1098         nsRect rect = aFrame->GetParent()->GetRect();
  1099         int32_t p2a = aFrame->PresContext()->DeviceContext()->
  1100                         AppUnitsPerDevPixel();
  1101         nsMargin margin;
  1103         /* Get the available space, if that is smaller then the minimum size,
  1104          * adjust the mininum size to fit into it.
  1105          * Setting aIsOverridable to true has no effect for thumbs. */
  1106         aFrame->GetMargin(margin);
  1107         rect.Deflate(margin);
  1108         aFrame->GetParent()->GetBorderAndPadding(margin);
  1109         rect.Deflate(margin);
  1111         if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL) {
  1112           aResult->width = metrics.slider_width;
  1113           aResult->height = std::min(NSAppUnitsToIntPixels(rect.height, p2a),
  1114                                    metrics.min_slider_size);
  1115         } else {
  1116           aResult->height = metrics.slider_width;
  1117           aResult->width = std::min(NSAppUnitsToIntPixels(rect.width, p2a),
  1118                                   metrics.min_slider_size);
  1121         *aIsOverridable = false;
  1123       break;
  1124     case NS_THEME_RANGE_THUMB:
  1126         gint thumb_length, thumb_height;
  1128         if (IsRangeHorizontal(aFrame)) {
  1129           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
  1130         } else {
  1131           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_length);
  1133         aResult->width = thumb_length;
  1134         aResult->height = thumb_height;
  1136         *aIsOverridable = false;
  1138       break;
  1139     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  1140     case NS_THEME_SCALE_THUMB_VERTICAL:
  1142         gint thumb_length, thumb_height;
  1144         if (aWidgetType == NS_THEME_SCALE_THUMB_VERTICAL) {
  1145           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height);
  1146           aResult->width = thumb_height;
  1147           aResult->height = thumb_length;
  1148         } else {
  1149           moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height);
  1150           aResult->width = thumb_length;
  1151           aResult->height = thumb_height;
  1154         *aIsOverridable = false;
  1156       break;
  1157     case NS_THEME_TAB_SCROLLARROW_BACK:
  1158     case NS_THEME_TAB_SCROLLARROW_FORWARD:
  1160         moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
  1161         *aIsOverridable = false;
  1163       break;
  1164   case NS_THEME_DROPDOWN_BUTTON:
  1166       moz_gtk_get_combo_box_entry_button_size(&aResult->width,
  1167                                               &aResult->height);
  1168       *aIsOverridable = false;
  1170     break;
  1171   case NS_THEME_MENUSEPARATOR:
  1173       gint separator_height;
  1175       moz_gtk_get_menu_separator_height(&separator_height);
  1176       aResult->height = separator_height;
  1178       *aIsOverridable = false;
  1180     break;
  1181   case NS_THEME_CHECKBOX:
  1182   case NS_THEME_RADIO:
  1184       gint indicator_size, indicator_spacing;
  1186       if (aWidgetType == NS_THEME_CHECKBOX) {
  1187         moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing);
  1188       } else {
  1189         moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing);
  1192       // Include space for the indicator and the padding around it.
  1193       aResult->width = indicator_size;
  1194       aResult->height = indicator_size;
  1196     break;
  1197   case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
  1198   case NS_THEME_BUTTON_ARROW_UP:
  1199   case NS_THEME_BUTTON_ARROW_DOWN:
  1200   case NS_THEME_BUTTON_ARROW_NEXT:
  1201   case NS_THEME_BUTTON_ARROW_PREVIOUS:
  1203         moz_gtk_get_arrow_size(&aResult->width, &aResult->height);
  1204         *aIsOverridable = false;
  1206     break;
  1207   case NS_THEME_CHECKBOX_CONTAINER:
  1208   case NS_THEME_RADIO_CONTAINER:
  1209   case NS_THEME_CHECKBOX_LABEL:
  1210   case NS_THEME_RADIO_LABEL:
  1211   case NS_THEME_BUTTON:
  1212   case NS_THEME_DROPDOWN:
  1213   case NS_THEME_TOOLBAR_BUTTON:
  1214   case NS_THEME_TREEVIEW_HEADER_CELL:
  1216       // Just include our border, and let the box code augment the size.
  1217       nsIntMargin border;
  1218       nsNativeThemeGTK::GetWidgetBorder(aContext->DeviceContext(),
  1219                                         aFrame, aWidgetType, &border);
  1220       aResult->width = border.left + border.right;
  1221       aResult->height = border.top + border.bottom;
  1223     break;
  1224   case NS_THEME_TOOLBAR_SEPARATOR:
  1226       gint separator_width;
  1228       moz_gtk_get_toolbar_separator_width(&separator_width);
  1230       aResult->width = separator_width;
  1232     break;
  1233   case NS_THEME_SPINNER:
  1234     // hard code these sizes
  1235     aResult->width = 14;
  1236     aResult->height = 26;
  1237     break;
  1238   case NS_THEME_TREEVIEW_HEADER_SORTARROW:
  1239   case NS_THEME_SPINNER_UP_BUTTON:
  1240   case NS_THEME_SPINNER_DOWN_BUTTON:
  1241     // hard code these sizes
  1242     aResult->width = 14;
  1243     aResult->height = 13;
  1244     break;
  1245   case NS_THEME_RESIZER:
  1246     // same as Windows to make our lives easier
  1247     aResult->width = aResult->height = 15;
  1248     *aIsOverridable = false;
  1249     break;
  1250   case NS_THEME_TREEVIEW_TWISTY:
  1251   case NS_THEME_TREEVIEW_TWISTY_OPEN:
  1253       gint expander_size;
  1255       moz_gtk_get_treeview_expander_size(&expander_size);
  1256       aResult->width = aResult->height = expander_size;
  1257       *aIsOverridable = false;
  1259     break;
  1261   return NS_OK;
  1264 NS_IMETHODIMP
  1265 nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
  1266                                      nsIAtom* aAttribute, bool* aShouldRepaint)
  1268   // Some widget types just never change state.
  1269   if (aWidgetType == NS_THEME_TOOLBOX ||
  1270       aWidgetType == NS_THEME_TOOLBAR ||
  1271       aWidgetType == NS_THEME_STATUSBAR ||
  1272       aWidgetType == NS_THEME_STATUSBAR_PANEL ||
  1273       aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL ||
  1274       aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
  1275       aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
  1276       aWidgetType == NS_THEME_PROGRESSBAR ||
  1277       aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
  1278       aWidgetType == NS_THEME_MENUBAR ||
  1279       aWidgetType == NS_THEME_MENUPOPUP ||
  1280       aWidgetType == NS_THEME_TOOLTIP ||
  1281       aWidgetType == NS_THEME_MENUSEPARATOR ||
  1282       aWidgetType == NS_THEME_WINDOW ||
  1283       aWidgetType == NS_THEME_DIALOG) {
  1284     *aShouldRepaint = false;
  1285     return NS_OK;
  1288   if ((aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP ||
  1289        aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN ||
  1290        aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT ||
  1291        aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) &&
  1292       (aAttribute == nsGkAtoms::curpos ||
  1293        aAttribute == nsGkAtoms::maxpos)) {
  1294     *aShouldRepaint = true;
  1295     return NS_OK;
  1298   // XXXdwh Not sure what can really be done here.  Can at least guess for
  1299   // specific widgets that they're highly unlikely to have certain states.
  1300   // For example, a toolbar doesn't care about any states.
  1301   if (!aAttribute) {
  1302     // Hover/focus/active changed.  Always repaint.
  1303     *aShouldRepaint = true;
  1305   else {
  1306     // Check the attribute to see if it's relevant.  
  1307     // disabled, checked, dlgtype, default, etc.
  1308     *aShouldRepaint = false;
  1309     if (aAttribute == nsGkAtoms::disabled ||
  1310         aAttribute == nsGkAtoms::checked ||
  1311         aAttribute == nsGkAtoms::selected ||
  1312         aAttribute == nsGkAtoms::focused ||
  1313         aAttribute == nsGkAtoms::readonly ||
  1314         aAttribute == nsGkAtoms::_default ||
  1315         aAttribute == nsGkAtoms::menuactive ||
  1316         aAttribute == nsGkAtoms::open ||
  1317         aAttribute == nsGkAtoms::parentfocused)
  1318       *aShouldRepaint = true;
  1321   return NS_OK;
  1324 NS_IMETHODIMP
  1325 nsNativeThemeGTK::ThemeChanged()
  1327   memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
  1328   return NS_OK;
  1331 NS_IMETHODIMP_(bool)
  1332 nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
  1333                                       nsIFrame* aFrame,
  1334                                       uint8_t aWidgetType)
  1336   if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
  1337     return false;
  1339   switch (aWidgetType) {
  1340   case NS_THEME_BUTTON:
  1341   case NS_THEME_BUTTON_FOCUS:
  1342   case NS_THEME_RADIO:
  1343   case NS_THEME_CHECKBOX:
  1344   case NS_THEME_TOOLBOX: // N/A
  1345   case NS_THEME_TOOLBAR:
  1346   case NS_THEME_TOOLBAR_BUTTON:
  1347   case NS_THEME_TOOLBAR_DUAL_BUTTON: // so we can override the border with 0
  1348   case NS_THEME_TOOLBAR_BUTTON_DROPDOWN:
  1349   case NS_THEME_BUTTON_ARROW_UP:
  1350   case NS_THEME_BUTTON_ARROW_DOWN:
  1351   case NS_THEME_BUTTON_ARROW_NEXT:
  1352   case NS_THEME_BUTTON_ARROW_PREVIOUS:
  1353   case NS_THEME_TOOLBAR_SEPARATOR:
  1354   case NS_THEME_TOOLBAR_GRIPPER:
  1355   case NS_THEME_STATUSBAR:
  1356   case NS_THEME_STATUSBAR_PANEL:
  1357   case NS_THEME_STATUSBAR_RESIZER_PANEL:
  1358   case NS_THEME_RESIZER:
  1359   case NS_THEME_LISTBOX:
  1360     // case NS_THEME_LISTBOX_LISTITEM:
  1361   case NS_THEME_TREEVIEW:
  1362     // case NS_THEME_TREEVIEW_TREEITEM:
  1363   case NS_THEME_TREEVIEW_TWISTY:
  1364     // case NS_THEME_TREEVIEW_LINE:
  1365     // case NS_THEME_TREEVIEW_HEADER:
  1366   case NS_THEME_TREEVIEW_HEADER_CELL:
  1367   case NS_THEME_TREEVIEW_HEADER_SORTARROW:
  1368   case NS_THEME_TREEVIEW_TWISTY_OPEN:
  1369     case NS_THEME_PROGRESSBAR:
  1370     case NS_THEME_PROGRESSBAR_CHUNK:
  1371     case NS_THEME_PROGRESSBAR_VERTICAL:
  1372     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  1373     case NS_THEME_TAB:
  1374     // case NS_THEME_TAB_PANEL:
  1375     case NS_THEME_TAB_PANELS:
  1376     case NS_THEME_TAB_SCROLLARROW_BACK:
  1377     case NS_THEME_TAB_SCROLLARROW_FORWARD:
  1378   case NS_THEME_TOOLTIP:
  1379   case NS_THEME_SPINNER:
  1380   case NS_THEME_SPINNER_UP_BUTTON:
  1381   case NS_THEME_SPINNER_DOWN_BUTTON:
  1382   case NS_THEME_SPINNER_TEXTFIELD:
  1383     // case NS_THEME_SCROLLBAR:  (n/a for gtk)
  1384     // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk)
  1385   case NS_THEME_SCROLLBAR_BUTTON_UP:
  1386   case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  1387   case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  1388   case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  1389   case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1390   case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  1391   case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  1392   case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  1393   case NS_THEME_NUMBER_INPUT:
  1394   case NS_THEME_TEXTFIELD:
  1395   case NS_THEME_TEXTFIELD_MULTILINE:
  1396   case NS_THEME_DROPDOWN_TEXTFIELD:
  1397   case NS_THEME_RANGE:
  1398   case NS_THEME_RANGE_THUMB:
  1399   case NS_THEME_SCALE_HORIZONTAL:
  1400   case NS_THEME_SCALE_THUMB_HORIZONTAL:
  1401   case NS_THEME_SCALE_VERTICAL:
  1402   case NS_THEME_SCALE_THUMB_VERTICAL:
  1403     // case NS_THEME_SCALE_THUMB_START:
  1404     // case NS_THEME_SCALE_THUMB_END:
  1405     // case NS_THEME_SCALE_TICK:
  1406   case NS_THEME_CHECKBOX_CONTAINER:
  1407   case NS_THEME_RADIO_CONTAINER:
  1408   case NS_THEME_CHECKBOX_LABEL:
  1409   case NS_THEME_RADIO_LABEL:
  1410   case NS_THEME_MENUBAR:
  1411   case NS_THEME_MENUPOPUP:
  1412   case NS_THEME_MENUITEM:
  1413   case NS_THEME_MENUARROW:
  1414   case NS_THEME_MENUSEPARATOR:
  1415   case NS_THEME_CHECKMENUITEM:
  1416   case NS_THEME_RADIOMENUITEM:
  1417   case NS_THEME_SPLITTER:
  1418   case NS_THEME_WINDOW:
  1419   case NS_THEME_DIALOG:
  1420   case NS_THEME_DROPDOWN:
  1421   case NS_THEME_DROPDOWN_TEXT:
  1422     return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
  1424   case NS_THEME_DROPDOWN_BUTTON:
  1425     // "Native" dropdown buttons cause padding and margin problems, but only
  1426     // in HTML so allow them in XUL.
  1427     return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
  1428            !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
  1432   return false;
  1435 NS_IMETHODIMP_(bool)
  1436 nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType)
  1438   // XXXdwh At some point flesh all of this out.
  1439   if (aWidgetType == NS_THEME_DROPDOWN_BUTTON ||
  1440       aWidgetType == NS_THEME_RADIO ||
  1441       aWidgetType == NS_THEME_RANGE_THUMB ||
  1442       aWidgetType == NS_THEME_CHECKBOX ||
  1443       aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ||
  1444       aWidgetType == NS_THEME_TAB_SCROLLARROW_FORWARD ||
  1445       aWidgetType == NS_THEME_BUTTON_ARROW_UP ||
  1446       aWidgetType == NS_THEME_BUTTON_ARROW_DOWN ||
  1447       aWidgetType == NS_THEME_BUTTON_ARROW_NEXT ||
  1448       aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS)
  1449     return false;
  1450   return true;
  1453 bool
  1454 nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
  1456    if (aWidgetType == NS_THEME_DROPDOWN ||
  1457       aWidgetType == NS_THEME_BUTTON || 
  1458       aWidgetType == NS_THEME_TREEVIEW_HEADER_CELL)
  1459     return true;
  1461   return false;
  1464 bool
  1465 nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
  1467   return false;
  1470 nsITheme::Transparency
  1471 nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
  1473   switch (aWidgetType) {
  1474   // These widgets always draw a default background.
  1475 #if (MOZ_WIDGET_GTK == 2)
  1476   case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  1477   case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1478   case NS_THEME_TOOLBAR:
  1479   case NS_THEME_MENUBAR:
  1480 #endif
  1481   case NS_THEME_MENUPOPUP:
  1482   case NS_THEME_WINDOW:
  1483   case NS_THEME_DIALOG:
  1484   // Tooltips use gtk_paint_flat_box().
  1485   case NS_THEME_TOOLTIP:
  1486     return eOpaque;
  1489   return eUnknownTransparency;

mercurial