widget/windows/nsNativeThemeWin.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: 40; 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 "nsNativeThemeWin.h"
     7 #include "mozilla/EventStates.h"
     8 #include "mozilla/WindowsVersion.h"
     9 #include "nsRenderingContext.h"
    10 #include "nsRect.h"
    11 #include "nsSize.h"
    12 #include "nsTransform2D.h"
    13 #include "nsThemeConstants.h"
    14 #include "nsIPresShell.h"
    15 #include "nsPresContext.h"
    16 #include "nsIContent.h"
    17 #include "nsIFrame.h"
    18 #include "nsNameSpaceManager.h"
    19 #include "nsIDOMHTMLInputElement.h"
    20 #include "nsLookAndFeel.h"
    21 #include "nsMenuFrame.h"
    22 #include "nsGkAtoms.h"
    23 #include <malloc.h>
    24 #include "nsWindow.h"
    25 #include "nsIComboboxControlFrame.h"
    26 #include "prinrval.h"
    27 #include "WinUtils.h"
    29 #include "gfxPlatform.h"
    30 #include "gfxContext.h"
    31 #include "gfxWindowsPlatform.h"
    32 #include "gfxWindowsSurface.h"
    33 #include "gfxWindowsNativeDrawing.h"
    35 #include "nsUXThemeData.h"
    36 #include "nsUXThemeConstants.h"
    37 #include <algorithm>
    39 using mozilla::IsVistaOrLater;
    40 using namespace mozilla;
    41 using namespace mozilla::widget;
    43 #ifdef PR_LOGGING
    44 extern PRLogModuleInfo* gWindowsLog;
    45 #endif
    47 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeWin, nsNativeTheme, nsITheme)
    49 nsNativeThemeWin::nsNativeThemeWin() :
    50   mProgressDeterminateTimeStamp(TimeStamp::Now()),
    51   mProgressIndeterminateTimeStamp(TimeStamp::Now())
    52 {
    53   // If there is a relevant change in forms.css for windows platform,
    54   // static widget style variables (e.g. sButtonBorderSize) should be 
    55   // reinitialized here.
    56 }
    58 nsNativeThemeWin::~nsNativeThemeWin()
    59 {
    60   nsUXThemeData::Invalidate();
    61 }
    63 static int32_t
    64 GetTopLevelWindowActiveState(nsIFrame *aFrame)
    65 {
    66   // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
    67   // until it finds a real window.
    68   nsIWidget* widget = aFrame->GetNearestWidget();
    69   nsWindowBase * window = static_cast<nsWindowBase*>(widget);
    70   if (!window)
    71     return mozilla::widget::themeconst::FS_INACTIVE;
    72   if (widget && !window->IsTopLevelWidget() &&
    73       !(window = window->GetParentWindowBase(false)))
    74     return mozilla::widget::themeconst::FS_INACTIVE;
    76   if (window->GetWindowHandle() == ::GetActiveWindow())
    77     return mozilla::widget::themeconst::FS_ACTIVE;
    78   return mozilla::widget::themeconst::FS_INACTIVE;
    79 }
    81 static int32_t
    82 GetWindowFrameButtonState(nsIFrame* aFrame, EventStates eventState)
    83 {
    84   if (GetTopLevelWindowActiveState(aFrame) ==
    85       mozilla::widget::themeconst::FS_INACTIVE) {
    86     if (eventState.HasState(NS_EVENT_STATE_HOVER))
    87       return mozilla::widget::themeconst::BS_HOT;
    88     return mozilla::widget::themeconst::BS_INACTIVE;
    89   }
    91   if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
    92     if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
    93       return mozilla::widget::themeconst::BS_PUSHED;
    94     return mozilla::widget::themeconst::BS_HOT;
    95   }
    96   return mozilla::widget::themeconst::BS_NORMAL;
    97 }
    99 static int32_t
   100 GetClassicWindowFrameButtonState(EventStates eventState)
   101 {
   102   if (eventState.HasState(NS_EVENT_STATE_ACTIVE) &&
   103       eventState.HasState(NS_EVENT_STATE_HOVER))
   104     return DFCS_BUTTONPUSH|DFCS_PUSHED;
   105   return DFCS_BUTTONPUSH;
   106 }
   108 static bool
   109 IsTopLevelMenu(nsIFrame *aFrame)
   110 {
   111   bool isTopLevel(false);
   112   nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
   113   if (menuFrame) {
   114     isTopLevel = menuFrame->IsOnMenuBar();
   115   }
   116   return isTopLevel;
   117 }
   119 static MARGINS
   120 GetCheckboxMargins(HANDLE theme, HDC hdc)
   121 {
   122     MARGINS checkboxContent = {0};
   123     GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL,
   124                     TMT_CONTENTMARGINS, nullptr, &checkboxContent);
   125     return checkboxContent;
   126 }
   128 static SIZE
   129 GetCheckboxBGSize(HANDLE theme, HDC hdc)
   130 {
   131     SIZE checkboxSize;
   132     GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL,
   133                      nullptr, TS_TRUE, &checkboxSize);
   135     MARGINS checkboxMargins = GetCheckboxMargins(theme, hdc);
   137     int leftMargin = checkboxMargins.cxLeftWidth;
   138     int rightMargin = checkboxMargins.cxRightWidth;
   139     int topMargin = checkboxMargins.cyTopHeight;
   140     int bottomMargin = checkboxMargins.cyBottomHeight;
   142     int width = leftMargin + checkboxSize.cx + rightMargin;
   143     int height = topMargin + checkboxSize.cy + bottomMargin;
   144     SIZE ret;
   145     ret.cx = width;
   146     ret.cy = height;
   147     return ret;
   148 }
   150 static SIZE
   151 GetCheckboxBGBounds(HANDLE theme, HDC hdc)
   152 {
   153     MARGINS checkboxBGSizing = {0};
   154     MARGINS checkboxBGContent = {0};
   155     GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
   156                     TMT_SIZINGMARGINS, nullptr, &checkboxBGSizing);
   157     GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
   158                     TMT_CONTENTMARGINS, nullptr, &checkboxBGContent);
   160 #define posdx(d) ((d) > 0 ? d : 0)
   162     int dx = posdx(checkboxBGContent.cxRightWidth -
   163                    checkboxBGSizing.cxRightWidth) +
   164              posdx(checkboxBGContent.cxLeftWidth -
   165                    checkboxBGSizing.cxLeftWidth);
   166     int dy = posdx(checkboxBGContent.cyTopHeight -
   167                    checkboxBGSizing.cyTopHeight) +
   168              posdx(checkboxBGContent.cyBottomHeight -
   169                    checkboxBGSizing.cyBottomHeight);
   171 #undef posdx
   173     SIZE ret(GetCheckboxBGSize(theme, hdc));
   174     ret.cx += dx;
   175     ret.cy += dy;
   176     return ret;
   177 }
   179 static SIZE
   180 GetGutterSize(HANDLE theme, HDC hdc)
   181 {
   182     SIZE gutterSize;
   183     GetThemePartSize(theme, hdc, MENU_POPUPGUTTER, 0, nullptr, TS_TRUE, &gutterSize);
   185     SIZE checkboxBGSize(GetCheckboxBGBounds(theme, hdc));
   187     SIZE itemSize;
   188     GetThemePartSize(theme, hdc, MENU_POPUPITEM, MPI_NORMAL, nullptr, TS_TRUE, &itemSize);
   190     // Figure out how big the menuitem's icon will be (if present) at current DPI
   191     double scaleFactor = nsIWidget::DefaultScaleOverride();
   192     if (scaleFactor <= 0.0) {
   193       scaleFactor = gfxWindowsPlatform::GetPlatform()->GetDPIScale();
   194     }
   195     int iconDevicePixels = NSToIntRound(16 * scaleFactor);
   196     SIZE iconSize = {
   197       iconDevicePixels, iconDevicePixels
   198     };
   199     // Not really sure what margins should be used here, but this seems to work in practice...
   200     MARGINS margins = {0};
   201     GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
   202                     TMT_CONTENTMARGINS, nullptr, &margins);
   203     iconSize.cx += margins.cxLeftWidth + margins.cxRightWidth;
   204     iconSize.cy += margins.cyTopHeight + margins.cyBottomHeight;
   206     int width = std::max(itemSize.cx, std::max(iconSize.cx, checkboxBGSize.cx) + gutterSize.cx);
   207     int height = std::max(itemSize.cy, std::max(iconSize.cy, checkboxBGSize.cy));
   209     SIZE ret;
   210     ret.cx = width;
   211     ret.cy = height;
   212     return ret;
   213 }
   215 /* DrawThemeBGRTLAware - render a theme part based on rtl state.
   216  * Some widgets are not direction-neutral and need to be drawn reversed for
   217  * RTL.  Windows provides a way to do this with SetLayout, but this reverses
   218  * the entire drawing area of a given device context, which means that its
   219  * use will also affect the positioning of the widget.  There are two ways
   220  * to work around this:
   221  *
   222  * Option 1: Alter the position of the rect that we send so that we cancel
   223  *           out the positioning effects of SetLayout
   224  * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto
   225  *           that, and then transfer the results back to our DC
   226  *
   227  * This function tries to implement option 1, under the assumption that the
   228  * correct way to reverse the effects of SetLayout is to translate the rect
   229  * such that the offset from the DC bitmap's left edge to the old rect's
   230  * left edge is equal to the offset from the DC bitmap's right edge to the
   231  * new rect's right edge.  In other words,
   232  * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right)
   233  */
   234 static HRESULT
   235 DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState,
   236                     const RECT *aWidgetRect, const RECT *aClipRect,
   237                     bool aIsRtl)
   238 {
   239   NS_ASSERTION(aTheme, "Bad theme handle.");
   240   NS_ASSERTION(aHdc, "Bad hdc.");
   241   NS_ASSERTION(aWidgetRect, "Bad rect.");
   242   NS_ASSERTION(aClipRect, "Bad clip rect.");
   244   if (!aIsRtl) {
   245     return DrawThemeBackground(aTheme, aHdc, aPart, aState,
   246                                aWidgetRect, aClipRect);
   247   }
   249   HGDIOBJ hObj = GetCurrentObject(aHdc, OBJ_BITMAP);
   250   BITMAP bitmap;
   251   POINT vpOrg;
   253   if (hObj && GetObject(hObj, sizeof(bitmap), &bitmap) &&
   254       GetViewportOrgEx(aHdc, &vpOrg)) {
   255     RECT newWRect(*aWidgetRect);
   256     newWRect.left = bitmap.bmWidth - (aWidgetRect->right + 2*vpOrg.x);
   257     newWRect.right = bitmap.bmWidth - (aWidgetRect->left + 2*vpOrg.x);
   259     RECT newCRect;
   260     RECT *newCRectPtr = nullptr;
   262     if (aClipRect) {
   263       newCRect.top = aClipRect->top;
   264       newCRect.bottom = aClipRect->bottom;
   265       newCRect.left = bitmap.bmWidth - (aClipRect->right + 2*vpOrg.x);
   266       newCRect.right = bitmap.bmWidth - (aClipRect->left + 2*vpOrg.x);
   267       newCRectPtr = &newCRect;
   268     }
   270     SetLayout(aHdc, LAYOUT_RTL);
   271     HRESULT hr = DrawThemeBackground(aTheme, aHdc, aPart, aState, &newWRect,
   272                                      newCRectPtr);
   273     SetLayout(aHdc, 0);
   274     if (SUCCEEDED(hr)) {
   275       return hr;
   276     }
   277   }
   278   return DrawThemeBackground(aTheme, aHdc, aPart, aState,
   279                              aWidgetRect, aClipRect);
   280 }
   282 /*
   283  *  Caption button padding data - 'hot' button padding.
   284  *  These areas are considered hot, in that they activate
   285  *  a button when hovered or clicked. The button graphic
   286  *  is drawn inside the padding border. Unrecognized themes
   287  *  are treated as their recognized counterparts for now.
   288  *                       left      top    right   bottom
   289  *  classic min             1        2        0        1
   290  *  classic max             0        2        1        1
   291  *  classic close           1        2        2        1
   292  *
   293  *  aero basic min          1        2        0        2
   294  *  aero basic max          0        2        1        2
   295  *  aero basic close        1        2        1        2
   296  *
   297  *  xp theme min            0        2        0        2
   298  *  xp theme max            0        2        1        2
   299  *  xp theme close          1        2        2        2
   300  *
   301  *  'cold' button padding - generic button padding, should
   302  *  be handled in css.
   303  *                       left      top    right   bottom
   304  *  classic min             0        0        0        0
   305  *  classic max             0        0        0        0
   306  *  classic close           0        0        0        0
   307  *
   308  *  aero basic min          0        0        1        0
   309  *  aero basic max          1        0        0        0
   310  *  aero basic close        0        0        0        0
   311  *
   312  *  xp theme min            0        0        1        0
   313  *  xp theme max            1        0        0        0
   314  *  xp theme close          0        0        0        0
   315  */
   317 enum CaptionDesktopTheme {
   318   CAPTION_CLASSIC = 0,
   319   CAPTION_BASIC,
   320   CAPTION_XPTHEME,
   321 };
   323 enum CaptionButton {
   324   CAPTIONBUTTON_MINIMIZE = 0,
   325   CAPTIONBUTTON_RESTORE,
   326   CAPTIONBUTTON_CLOSE,
   327 };
   329 struct CaptionButtonPadding {
   330   RECT hotPadding[3];
   331 };
   333 // RECT: left, top, right, bottom
   334 static CaptionButtonPadding buttonData[3] = {
   335   { 
   336     { { 1, 2, 0, 1 }, { 0, 2, 1, 1 }, { 1, 2, 2, 1 } }
   337   },
   338   { 
   339     { { 1, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
   340   },
   341   { 
   342     { { 0, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
   343   }
   344 };
   346 // Adds "hot" caption button padding to minimum widget size.
   347 static void
   348 AddPaddingRect(nsIntSize* aSize, CaptionButton button) {
   349   if (!aSize)
   350     return;
   351   RECT offset;
   352   if (!IsAppThemed())
   353     offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
   354   else if (!IsVistaOrLater())
   355     offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
   356   else
   357     offset = buttonData[CAPTION_BASIC].hotPadding[button];
   358   aSize->width += offset.left + offset.right;
   359   aSize->height += offset.top + offset.bottom;
   360 }
   362 // If we've added padding to the minimum widget size, offset
   363 // the area we draw into to compensate.
   364 static void
   365 OffsetBackgroundRect(RECT& rect, CaptionButton button) {
   366   RECT offset;
   367   if (!IsAppThemed())
   368     offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
   369   else if (!IsVistaOrLater())
   370     offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
   371   else
   372     offset = buttonData[CAPTION_BASIC].hotPadding[button];
   373   rect.left += offset.left;
   374   rect.top += offset.top;
   375   rect.right -= offset.right;
   376   rect.bottom -= offset.bottom;
   377 }
   379 /*
   380  * Notes on progress track and meter part constants:
   381  * xp and up:
   382  * PP_BAR(_VERT)            - base progress track
   383  * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
   384  *                            the underlying surface supports alpha. otherwise
   385  *                            theme lib's DrawThemeBackground falls back on
   386  *                            opaque PP_BAR. we currently don't use this.
   387  * PP_CHUNK(_VERT)          - xp progress meter. this does not draw an xp style
   388  *                            progress w/chunks, it draws fill using the chunk
   389  *                            graphic.
   390  * vista and up:
   391  * PP_FILL(_VERT)           - progress meter. these have four states/colors.
   392  * PP_PULSEOVERLAY(_VERT)   - white reflection - an overlay, not sure what this
   393  *                            is used for.
   394  * PP_MOVEOVERLAY(_VERT)    - green pulse - the pulse effect overlay on
   395  *                            determined progress bars. we also use this for
   396  *                            indeterminate chunk.
   397  *
   398  * Notes on state constants:
   399  * PBBS_NORMAL               - green progress
   400  * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
   401  * PBFS_PAUSED               - yellow paused progress
   402  *
   403  * There is no common controls style indeterminate part on vista and up.
   404  */
   406 /*
   407  * Progress bar related constants. These values are found by experimenting and
   408  * comparing against native widgets used by the system. They are very unlikely
   409  * exact but try to not be too wrong.
   410  */
   411 // The amount of time we animate progress meters parts across the frame.
   412 static const double kProgressDeterminateTimeSpan = 3.0;
   413 static const double kProgressIndeterminateTimeSpan = 5.0;
   414 // The width of the overlay used to animate the horizontal progress bar (Vista and later).
   415 static const int32_t kProgressHorizontalVistaOverlaySize = 120;
   416 // The width of the overlay used for the horizontal indeterminate progress bars on XP.
   417 static const int32_t kProgressHorizontalXPOverlaySize = 55;
   418 // The height of the overlay used to animate the vertical progress bar (Vista and later).
   419 static const int32_t kProgressVerticalOverlaySize = 45;
   420 // The height of the overlay used for the vertical indeterminate progress bar (Vista and later).
   421 static const int32_t kProgressVerticalIndeterminateOverlaySize = 60;
   422 // The width of the overlay used to animate the indeterminate progress bar (Windows Classic).
   423 static const int32_t kProgressClassicOverlaySize = 40;
   425 /*
   426  * GetProgressOverlayStyle - returns the proper overlay part for themed
   427  * progress bars based on os and orientation.
   428  */
   429 static int32_t
   430 GetProgressOverlayStyle(bool aIsVertical)
   431 { 
   432   if (aIsVertical) {
   433     if (IsVistaOrLater()) {
   434       return PP_MOVEOVERLAYVERT;
   435     }
   436     return PP_CHUNKVERT;
   437   } else {
   438     if (IsVistaOrLater()) {
   439       return PP_MOVEOVERLAY;
   440     }
   441     return PP_CHUNK;
   442   }
   443 }
   445 /*
   446  * GetProgressOverlaySize - returns the minimum width or height for themed
   447  * progress bar overlays. This includes the width of indeterminate chunks
   448  * and vista pulse overlays.
   449  */
   450 static int32_t
   451 GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate)
   452 {
   453   if (IsVistaOrLater()) {
   454     if (aIsVertical) {
   455       return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
   456                               : kProgressVerticalOverlaySize;
   457     }
   458     return kProgressHorizontalVistaOverlaySize;
   459   }
   460   return kProgressHorizontalXPOverlaySize;
   461 }
   463 /*
   464  * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
   465  * on a comparison of the current value and maximum.
   466  */
   467 static bool
   468 IsProgressMeterFilled(nsIFrame* aFrame)
   469 {
   470   NS_ENSURE_TRUE(aFrame, false);
   471   nsIFrame* parentFrame = aFrame->GetParent();
   472   NS_ENSURE_TRUE(parentFrame, false);
   473   return nsNativeTheme::GetProgressValue(parentFrame) ==
   474          nsNativeTheme::GetProgressMaxValue(parentFrame);
   475 }
   477 /*
   478  * CalculateProgressOverlayRect - returns the padded overlay animation rect
   479  * used in rendering progress bars. Resulting rects are used in rendering
   480  * vista+ pulse overlays and indeterminate progress meters. Graphics should
   481  * be rendered at the origin.
   482  */
   483 RECT
   484 nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame,
   485                                                RECT* aWidgetRect,
   486                                                bool aIsVertical,
   487                                                bool aIsIndeterminate,
   488                                                bool aIsClassic)
   489 {
   490   NS_ASSERTION(aFrame, "bad frame pointer");
   491   NS_ASSERTION(aWidgetRect, "bad rect pointer");
   493   int32_t frameSize = aIsVertical ? aWidgetRect->bottom - aWidgetRect->top
   494                                   : aWidgetRect->right - aWidgetRect->left;
   496   // Recycle a set of progress pulse timers - these timers control the position
   497   // of all progress overlays and indeterminate chunks that get rendered.
   498   double span = aIsIndeterminate ? kProgressIndeterminateTimeSpan
   499                                  : kProgressDeterminateTimeSpan;
   500   TimeDuration period;
   501   if (!aIsIndeterminate) {
   502     if (TimeStamp::Now() > (mProgressDeterminateTimeStamp +
   503                             TimeDuration::FromSeconds(span))) {
   504       mProgressDeterminateTimeStamp = TimeStamp::Now();
   505     }
   506     period = TimeStamp::Now() - mProgressDeterminateTimeStamp;
   507   } else {
   508     if (TimeStamp::Now() > (mProgressIndeterminateTimeStamp +
   509                             TimeDuration::FromSeconds(span))) {
   510       mProgressIndeterminateTimeStamp = TimeStamp::Now();
   511     }
   512     period = TimeStamp::Now() - mProgressIndeterminateTimeStamp;
   513   }
   515   double percent = period / TimeDuration::FromSeconds(span);
   517   if (!aIsVertical && IsFrameRTL(aFrame))
   518     percent = 1 - percent;
   520   RECT overlayRect = *aWidgetRect;
   521   int32_t overlaySize;
   522   if (!aIsClassic) {
   523     overlaySize = GetProgressOverlaySize(aIsVertical, aIsIndeterminate);
   524   } else {
   525     overlaySize = kProgressClassicOverlaySize;
   526   } 
   528   // Calculate a bounds that is larger than the meters frame such that the
   529   // overlay starts and ends completely off the edge of the frame:
   530   // [overlay][frame][overlay]
   531   // This also yields a nice delay on rotation. Use overlaySize as the minimum
   532   // size for [overlay] based on the graphics dims. If [frame] is larger, use
   533   // the frame size instead.
   534   int trackWidth = frameSize > overlaySize ? frameSize : overlaySize;
   535   if (!aIsVertical) {
   536     int xPos = aWidgetRect->left - trackWidth;
   537     xPos += (int)ceil(((double)(trackWidth*2) * percent));
   538     overlayRect.left = xPos;
   539     overlayRect.right = xPos + overlaySize;
   540   } else {
   541     int yPos = aWidgetRect->bottom + trackWidth;
   542     yPos -= (int)ceil(((double)(trackWidth*2) * percent));
   543     overlayRect.bottom = yPos;
   544     overlayRect.top = yPos - overlaySize;
   545   }
   546   return overlayRect;
   547 }
   549 /*
   550  * DrawChunkProgressMeter - renders an xp style chunked progress meter. Called
   551  * by DrawProgressMeter.
   552  *
   553  * @param aTheme       progress theme handle
   554  * @param aHdc         hdc returned by gfxWindowsNativeDrawing
   555  * @param aPart        the PP_X progress part
   556  * @param aState       the theme state
   557  * @param aFrame       the elements frame
   558  * @param aWidgetRect  bounding rect for the widget
   559  * @param aClipRect    dirty rect that needs drawing.
   560  * @param aAppUnits    app units per device pixel
   561  * @param aIsIndeterm  is an indeterminate progress?
   562  * @param aIsVertical  render a vertical progress?
   563  * @param aIsRtl       direction is rtl
   564  */
   565 static void
   566 DrawChunkProgressMeter(HTHEME aTheme, HDC aHdc, int aPart,
   567                        int aState, nsIFrame* aFrame, RECT* aWidgetRect,
   568                        RECT* aClipRect, gfxFloat aAppUnits, bool aIsIndeterm,
   569                        bool aIsVertical, bool aIsRtl)
   570 {
   571   NS_ASSERTION(aTheme, "Bad theme.");
   572   NS_ASSERTION(aHdc, "Bad hdc.");
   573   NS_ASSERTION(aWidgetRect, "Bad rect.");
   574   NS_ASSERTION(aClipRect, "Bad clip rect.");
   575   NS_ASSERTION(aFrame, "Bad frame.");
   577   // For horizontal meters, the theme lib paints the right graphic but doesn't
   578   // paint the chunks, so we do that manually. For vertical meters, the theme
   579   // library draws everything correctly.
   580   if (aIsVertical) {
   581     DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
   582     return;
   583   }
   585   // query for the proper chunk metrics
   586   int chunkSize, spaceSize;
   587   if (FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
   588                             TMT_PROGRESSCHUNKSIZE, &chunkSize)) ||
   589       FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
   590                             TMT_PROGRESSSPACESIZE, &spaceSize))) {
   591     DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
   592     return;
   593   }
   595   // render chunks
   596   if (!aIsRtl || aIsIndeterm) {
   597     for (int chunk = aWidgetRect->left; chunk <= aWidgetRect->right;
   598          chunk += (chunkSize+spaceSize)) {
   599       if (!aIsIndeterm && ((chunk + chunkSize) > aWidgetRect->right)) {
   600         // aWidgetRect->right represents the end of the meter. Partial blocks
   601         // don't get rendered with one exception, so exit here if we don't have
   602         // a full chunk to draw.
   603         // The above is true *except* when the meter is at 100% fill, in which
   604         // case Windows renders any remaining partial block. Query the parent
   605         // frame to find out if we're at 100%.
   606         if (!IsProgressMeterFilled(aFrame)) {
   607           break;
   608         }
   609       }
   610       RECT bounds =
   611         { chunk, aWidgetRect->top, chunk + chunkSize, aWidgetRect->bottom };
   612       DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
   613     }
   614   } else {
   615     // rtl needs to grow in the opposite direction to look right.
   616     for (int chunk = aWidgetRect->right; chunk >= aWidgetRect->left;
   617          chunk -= (chunkSize+spaceSize)) {
   618       if ((chunk - chunkSize) < aWidgetRect->left) {
   619         if (!IsProgressMeterFilled(aFrame)) {
   620           break;
   621         }
   622       }
   623       RECT bounds =
   624         { chunk - chunkSize, aWidgetRect->top, chunk, aWidgetRect->bottom };
   625       DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
   626     }
   627   }
   628 }
   630 /*
   631  * DrawProgressMeter - render an appropriate progress meter based on progress
   632  * meter style, orientation, and os. Note, this does not render the underlying
   633  * progress track.
   634  *
   635  * @param aFrame       the widget frame
   636  * @param aWidgetType  type of widget
   637  * @param aTheme       progress theme handle
   638  * @param aHdc         hdc returned by gfxWindowsNativeDrawing
   639  * @param aPart        the PP_X progress part
   640  * @param aState       the theme state
   641  * @param aWidgetRect  bounding rect for the widget
   642  * @param aClipRect    dirty rect that needs drawing.
   643  * @param aAppUnits    app units per device pixel
   644  */
   645 void
   646 nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType,
   647                                           HANDLE aTheme, HDC aHdc,
   648                                           int aPart, int aState,
   649                                           RECT* aWidgetRect, RECT* aClipRect,
   650                                           gfxFloat aAppUnits)
   651 {
   652   if (!aFrame || !aTheme || !aHdc)
   653     return;
   655   NS_ASSERTION(aWidgetRect, "bad rect pointer");
   656   NS_ASSERTION(aClipRect, "bad clip rect pointer");
   658   RECT adjWidgetRect, adjClipRect;
   659   adjWidgetRect = *aWidgetRect;
   660   adjClipRect = *aClipRect;
   661   if (!IsVistaOrLater()) {
   662     // Adjust clipping out by one pixel. XP progress meters are inset,
   663     // Vista+ are not.
   664     InflateRect(&adjWidgetRect, 1, 1);
   665     InflateRect(&adjClipRect, 1, 1);
   666   }
   668   nsIFrame* parentFrame = aFrame->GetParent();
   669   if (!parentFrame) {
   670     // We have no parent to work with, just bail.
   671     NS_WARNING("No parent frame for progress rendering. Can't paint.");
   672     return;
   673   }
   675   EventStates eventStates = GetContentState(parentFrame, aWidgetType);
   676   bool vertical = IsVerticalProgress(parentFrame) ||
   677                   aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL;
   678   bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates);
   679   bool animate = indeterminate;
   681   if (IsVistaOrLater()) {
   682     // Vista and up progress meter is fill style, rendered here. We render
   683     // the pulse overlay in the follow up section below.
   684     DrawThemeBackground(aTheme, aHdc, aPart, aState,
   685                         &adjWidgetRect, &adjClipRect);
   686     if (!IsProgressMeterFilled(aFrame)) {
   687       animate = true;
   688     }
   689   } else if (!indeterminate) {
   690     // XP progress meters are 'chunk' style.
   691     DrawChunkProgressMeter(aTheme, aHdc, aPart, aState, aFrame,
   692                            &adjWidgetRect, &adjClipRect, aAppUnits,
   693                            indeterminate, vertical, IsFrameRTL(aFrame));
   694   }    
   696   if (animate) {
   697     // Indeterminate rendering
   698     int32_t overlayPart = GetProgressOverlayStyle(vertical);
   699     RECT overlayRect =
   700       CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical,
   701                                    indeterminate, false);
   702     if (IsVistaOrLater()) {
   703       DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
   704                           &adjClipRect);
   705     } else {
   706       DrawChunkProgressMeter(aTheme, aHdc, overlayPart, aState, aFrame,
   707                              &overlayRect, &adjClipRect, aAppUnits,
   708                              indeterminate, vertical, IsFrameRTL(aFrame));
   709     }
   711     if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
   712       NS_WARNING("unable to animate progress widget!");
   713     }
   714   }
   715 }
   717 HANDLE
   718 nsNativeThemeWin::GetTheme(uint8_t aWidgetType)
   719 { 
   720   if (!IsVistaOrLater()) {
   721     // On XP or earlier, render dropdowns as textfields;
   722     // doing it the right way works fine with the MS themes,
   723     // but breaks on a lot of custom themes (presumably because MS
   724     // apps do the textfield border business as well).
   725     if (aWidgetType == NS_THEME_DROPDOWN)
   726       aWidgetType = NS_THEME_TEXTFIELD;
   727   }
   729   switch (aWidgetType) {
   730     case NS_THEME_BUTTON:
   731     case NS_THEME_RADIO:
   732     case NS_THEME_CHECKBOX:
   733     case NS_THEME_GROUPBOX:
   734       return nsUXThemeData::GetTheme(eUXButton);
   735     case NS_THEME_NUMBER_INPUT:
   736     case NS_THEME_TEXTFIELD:
   737     case NS_THEME_TEXTFIELD_MULTILINE:
   738       return nsUXThemeData::GetTheme(eUXEdit);
   739     case NS_THEME_TOOLTIP:
   740       // XP/2K3 should force a classic treatment of tooltips
   741       return !IsVistaOrLater() ?
   742         nullptr : nsUXThemeData::GetTheme(eUXTooltip);
   743     case NS_THEME_TOOLBOX:
   744       return nsUXThemeData::GetTheme(eUXRebar);
   745     case NS_THEME_WIN_MEDIA_TOOLBOX:
   746       return nsUXThemeData::GetTheme(eUXMediaRebar);
   747     case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
   748       return nsUXThemeData::GetTheme(eUXCommunicationsRebar);
   749     case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
   750       return nsUXThemeData::GetTheme(eUXBrowserTabBarRebar);
   751     case NS_THEME_TOOLBAR:
   752     case NS_THEME_TOOLBAR_BUTTON:
   753     case NS_THEME_TOOLBAR_SEPARATOR:
   754       return nsUXThemeData::GetTheme(eUXToolbar);
   755     case NS_THEME_PROGRESSBAR:
   756     case NS_THEME_PROGRESSBAR_VERTICAL:
   757     case NS_THEME_PROGRESSBAR_CHUNK:
   758     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
   759       return nsUXThemeData::GetTheme(eUXProgress);
   760     case NS_THEME_TAB:
   761     case NS_THEME_TAB_PANEL:
   762     case NS_THEME_TAB_PANELS:
   763       return nsUXThemeData::GetTheme(eUXTab);
   764     case NS_THEME_SCROLLBAR:
   765     case NS_THEME_SCROLLBAR_SMALL:
   766     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
   767     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
   768     case NS_THEME_SCROLLBAR_BUTTON_UP:
   769     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
   770     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
   771     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
   772     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
   773     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
   774       return nsUXThemeData::GetTheme(eUXScrollbar);
   775     case NS_THEME_RANGE:
   776     case NS_THEME_RANGE_THUMB:
   777     case NS_THEME_SCALE_HORIZONTAL:
   778     case NS_THEME_SCALE_VERTICAL:
   779     case NS_THEME_SCALE_THUMB_HORIZONTAL:
   780     case NS_THEME_SCALE_THUMB_VERTICAL:
   781       return nsUXThemeData::GetTheme(eUXTrackbar);
   782     case NS_THEME_SPINNER_UP_BUTTON:
   783     case NS_THEME_SPINNER_DOWN_BUTTON:
   784       return nsUXThemeData::GetTheme(eUXSpin);
   785     case NS_THEME_STATUSBAR:
   786     case NS_THEME_STATUSBAR_PANEL:
   787     case NS_THEME_STATUSBAR_RESIZER_PANEL:
   788     case NS_THEME_RESIZER:
   789       return nsUXThemeData::GetTheme(eUXStatus);
   790     case NS_THEME_DROPDOWN:
   791     case NS_THEME_DROPDOWN_BUTTON:
   792       return nsUXThemeData::GetTheme(eUXCombobox);
   793     case NS_THEME_TREEVIEW_HEADER_CELL:
   794     case NS_THEME_TREEVIEW_HEADER_SORTARROW:
   795       return nsUXThemeData::GetTheme(eUXHeader);
   796     case NS_THEME_LISTBOX:
   797     case NS_THEME_LISTBOX_LISTITEM:
   798     case NS_THEME_TREEVIEW:
   799     case NS_THEME_TREEVIEW_TWISTY_OPEN:
   800     case NS_THEME_TREEVIEW_TREEITEM:
   801       return nsUXThemeData::GetTheme(eUXListview);
   802     case NS_THEME_MENUBAR:
   803     case NS_THEME_MENUPOPUP:
   804     case NS_THEME_MENUITEM:
   805     case NS_THEME_CHECKMENUITEM:
   806     case NS_THEME_RADIOMENUITEM:
   807     case NS_THEME_MENUCHECKBOX:
   808     case NS_THEME_MENURADIO:
   809     case NS_THEME_MENUSEPARATOR:
   810     case NS_THEME_MENUARROW:
   811     case NS_THEME_MENUIMAGE:
   812     case NS_THEME_MENUITEMTEXT:
   813       return nsUXThemeData::GetTheme(eUXMenu);
   814     case NS_THEME_WINDOW_TITLEBAR:
   815     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
   816     case NS_THEME_WINDOW_FRAME_LEFT:
   817     case NS_THEME_WINDOW_FRAME_RIGHT:
   818     case NS_THEME_WINDOW_FRAME_BOTTOM:
   819     case NS_THEME_WINDOW_BUTTON_CLOSE:
   820     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
   821     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
   822     case NS_THEME_WINDOW_BUTTON_RESTORE:
   823     case NS_THEME_WINDOW_BUTTON_BOX:
   824     case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
   825     case NS_THEME_WIN_GLASS:
   826     case NS_THEME_WIN_BORDERLESS_GLASS:
   827       return nsUXThemeData::GetTheme(eUXWindowFrame);
   828   }
   829   return nullptr;
   830 }
   832 int32_t
   833 nsNativeThemeWin::StandardGetState(nsIFrame* aFrame, uint8_t aWidgetType,
   834                                    bool wantFocused)
   835 {
   836   EventStates eventState = GetContentState(aFrame, aWidgetType);
   837   if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
   838     return TS_ACTIVE;
   839   if (eventState.HasState(NS_EVENT_STATE_HOVER))
   840     return TS_HOVER;
   841   if (wantFocused && eventState.HasState(NS_EVENT_STATE_FOCUS))
   842     return TS_FOCUSED;
   844   return TS_NORMAL;
   845 }
   847 bool
   848 nsNativeThemeWin::IsMenuActive(nsIFrame *aFrame, uint8_t aWidgetType)
   849 {
   850   nsIContent* content = aFrame->GetContent();
   851   if (content->IsXUL() &&
   852       content->NodeInfo()->Equals(nsGkAtoms::richlistitem))
   853     return CheckBooleanAttr(aFrame, nsGkAtoms::selected);
   855   return CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
   856 }
   858 /**
   859  * aPart is filled in with the UXTheme part code. On return, values > 0
   860  * are the actual UXTheme part code; -1 means the widget will be drawn by
   861  * us; 0 means that we should use part code 0, which isn't a real part code
   862  * but elicits some kind of default behaviour from UXTheme when drawing
   863  * (but isThemeBackgroundPartiallyTransparent may not work).
   864  */
   865 nsresult 
   866 nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, 
   867                                        int32_t& aPart, int32_t& aState)
   868 {
   869   if (!IsVistaOrLater()) {
   870     // See GetTheme
   871     if (aWidgetType == NS_THEME_DROPDOWN)
   872       aWidgetType = NS_THEME_TEXTFIELD;
   873   }
   875   switch (aWidgetType) {
   876     case NS_THEME_BUTTON: {
   877       aPart = BP_BUTTON;
   878       if (!aFrame) {
   879         aState = TS_NORMAL;
   880         return NS_OK;
   881       }
   883       EventStates eventState = GetContentState(aFrame, aWidgetType);
   884       if (IsDisabled(aFrame, eventState)) {
   885         aState = TS_DISABLED;
   886         return NS_OK;
   887       } else if (IsOpenButton(aFrame) ||
   888                  IsCheckedButton(aFrame)) {
   889         aState = TS_ACTIVE;
   890         return NS_OK;
   891       }
   893       aState = StandardGetState(aFrame, aWidgetType, true);
   895       // Check for default dialog buttons.  These buttons should always look
   896       // focused.
   897       if (aState == TS_NORMAL && IsDefaultButton(aFrame))
   898         aState = TS_FOCUSED;
   899       return NS_OK;
   900     }
   901     case NS_THEME_CHECKBOX:
   902     case NS_THEME_RADIO: {
   903       bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
   904       aPart = isCheckbox ? BP_CHECKBOX : BP_RADIO;
   906       enum InputState {
   907         UNCHECKED = 0, CHECKED, INDETERMINATE
   908       };
   909       InputState inputState = UNCHECKED;
   910       bool isXULCheckboxRadio = false;
   912       if (!aFrame) {
   913         aState = TS_NORMAL;
   914       } else {
   915         if (GetCheckedOrSelected(aFrame, !isCheckbox)) {
   916           inputState = CHECKED;
   917         } if (isCheckbox && GetIndeterminate(aFrame)) {
   918           inputState = INDETERMINATE;
   919         }
   921         EventStates eventState =
   922           GetContentState(isXULCheckboxRadio ? aFrame->GetParent() : aFrame,
   923                           aWidgetType);
   924         if (IsDisabled(aFrame, eventState)) {
   925           aState = TS_DISABLED;
   926         } else {
   927           aState = StandardGetState(aFrame, aWidgetType, false);
   928         }
   929       }
   931       // 4 unchecked states, 4 checked states, 4 indeterminate states.
   932       aState += inputState * 4;
   933       return NS_OK;
   934     }
   935     case NS_THEME_GROUPBOX: {
   936       aPart = BP_GROUPBOX;
   937       aState = TS_NORMAL;
   938       // Since we don't support groupbox disabled and GBS_DISABLED looks the
   939       // same as GBS_NORMAL don't bother supporting GBS_DISABLED.
   940       return NS_OK;
   941     }
   942     case NS_THEME_NUMBER_INPUT:
   943     case NS_THEME_TEXTFIELD:
   944     case NS_THEME_TEXTFIELD_MULTILINE: {
   945       EventStates eventState = GetContentState(aFrame, aWidgetType);
   947       if (IsVistaOrLater()) {
   948         /* Note: the NOSCROLL type has a rounded corner in each
   949          * corner.  The more specific HSCROLL, VSCROLL, HVSCROLL types
   950          * have side and/or top/bottom edges rendered as straight
   951          * horizontal lines with sharp corners to accommodate a
   952          * scrollbar.  However, the scrollbar gets rendered on top of
   953          * this for us, so we don't care, and can just use NOSCROLL
   954          * here.
   955          */
   956         aPart = TFP_EDITBORDER_NOSCROLL;
   958         if (!aFrame) {
   959           aState = TFS_EDITBORDER_NORMAL;
   960         } else if (IsDisabled(aFrame, eventState)) {
   961           aState = TFS_EDITBORDER_DISABLED;
   962         } else if (IsReadOnly(aFrame)) {
   963           /* no special read-only state */
   964           aState = TFS_EDITBORDER_NORMAL;
   965         } else {
   966           nsIContent* content = aFrame->GetContent();
   968           /* XUL textboxes don't get focused themselves, because they have child
   969            * html:input.. but we can check the XUL focused attributes on them
   970            */
   971           if (content && content->IsXUL() && IsFocused(aFrame))
   972             aState = TFS_EDITBORDER_FOCUSED;
   973           else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS))
   974             aState = TFS_EDITBORDER_FOCUSED;
   975           else if (eventState.HasState(NS_EVENT_STATE_HOVER))
   976             aState = TFS_EDITBORDER_HOVER;
   977           else
   978             aState = TFS_EDITBORDER_NORMAL;
   979         }
   980       } else {
   981         aPart = TFP_TEXTFIELD;
   983         if (!aFrame)
   984           aState = TS_NORMAL;
   985         else if (IsDisabled(aFrame, eventState))
   986           aState = TS_DISABLED;
   987         else if (IsReadOnly(aFrame))
   988           aState = TFS_READONLY;
   989         else
   990           aState = StandardGetState(aFrame, aWidgetType, true);
   991       }
   993       return NS_OK;
   994     }
   995     case NS_THEME_TOOLTIP: {
   996       aPart = TTP_STANDARD;
   997       aState = TS_NORMAL;
   998       return NS_OK;
   999     }
  1000     case NS_THEME_PROGRESSBAR:
  1001     case NS_THEME_PROGRESSBAR_VERTICAL: {
  1002       // Note IsVerticalProgress only tests for orient css attrribute,
  1003       // NS_THEME_PROGRESSBAR_VERTICAL is dedicated to -moz-appearance:
  1004       // progressbar-vertical.
  1005       bool vertical = IsVerticalProgress(aFrame) ||
  1006                       aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL;
  1007       aPart = vertical ? PP_BARVERT : PP_BAR;
  1008       aState = PBBS_NORMAL;
  1009       return NS_OK;
  1011     case NS_THEME_PROGRESSBAR_CHUNK:
  1012     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: {
  1013       nsIFrame* parentFrame = aFrame->GetParent();
  1014       EventStates eventStates = GetContentState(parentFrame, aWidgetType);
  1015       if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
  1016           IsVerticalProgress(parentFrame)) {
  1017         aPart = IsVistaOrLater() ?
  1018           PP_FILLVERT : PP_CHUNKVERT;
  1019       } else {
  1020         aPart = IsVistaOrLater() ?
  1021           PP_FILL : PP_CHUNK;
  1024       aState = PBBVS_NORMAL;
  1025       return NS_OK;
  1027     case NS_THEME_TOOLBAR_BUTTON: {
  1028       aPart = BP_BUTTON;
  1029       if (!aFrame) {
  1030         aState = TS_NORMAL;
  1031         return NS_OK;
  1034       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1035       if (IsDisabled(aFrame, eventState)) {
  1036         aState = TS_DISABLED;
  1037         return NS_OK;
  1039       if (IsOpenButton(aFrame)) {
  1040         aState = TS_ACTIVE;
  1041         return NS_OK;
  1044       if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
  1045         aState = TS_ACTIVE;
  1046       else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
  1047         if (IsCheckedButton(aFrame))
  1048           aState = TB_HOVER_CHECKED;
  1049         else
  1050           aState = TS_HOVER;
  1052       else {
  1053         if (IsCheckedButton(aFrame))
  1054           aState = TB_CHECKED;
  1055         else
  1056           aState = TS_NORMAL;
  1059       return NS_OK;
  1061     case NS_THEME_TOOLBAR_SEPARATOR: {
  1062       aPart = TP_SEPARATOR;
  1063       aState = TS_NORMAL;
  1064       return NS_OK;
  1066     case NS_THEME_SCROLLBAR_BUTTON_UP:
  1067     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  1068     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  1069     case NS_THEME_SCROLLBAR_BUTTON_RIGHT: {
  1070       aPart = SP_BUTTON;
  1071       aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP)*4;
  1072       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1073       if (!aFrame)
  1074         aState += TS_NORMAL;
  1075       else if (IsDisabled(aFrame, eventState))
  1076         aState += TS_DISABLED;
  1077       else {
  1078         nsIFrame *parent = aFrame->GetParent();
  1079         EventStates parentState =
  1080           GetContentState(parent, parent->StyleDisplay()->mAppearance);
  1081         if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
  1082           aState += TS_ACTIVE;
  1083         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
  1084           aState += TS_HOVER;
  1085         else if (IsVistaOrLater() &&
  1086                  parentState.HasState(NS_EVENT_STATE_HOVER))
  1087           aState = (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE;
  1088         else
  1089           aState += TS_NORMAL;
  1091       return NS_OK;
  1093     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  1094     case NS_THEME_SCROLLBAR_TRACK_VERTICAL: {
  1095       aPart = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL) ?
  1096               SP_TRACKSTARTHOR : SP_TRACKSTARTVERT;
  1097       aState = TS_NORMAL;
  1098       return NS_OK;
  1100     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  1101     case NS_THEME_SCROLLBAR_THUMB_VERTICAL: {
  1102       aPart = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) ?
  1103               SP_THUMBHOR : SP_THUMBVERT;
  1104       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1105       if (!aFrame)
  1106         aState = TS_NORMAL;
  1107       else if (IsDisabled(aFrame, eventState))
  1108         aState = TS_DISABLED;
  1109       else {
  1110         if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a requirement for
  1111                                                         // the thumb, since the drag is not canceled
  1112                                                         // when you move outside the thumb.
  1113           aState = TS_ACTIVE;
  1114         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
  1115           aState = TS_HOVER;
  1116         else 
  1117           aState = TS_NORMAL;
  1119       return NS_OK;
  1121     case NS_THEME_RANGE:
  1122     case NS_THEME_SCALE_HORIZONTAL:
  1123     case NS_THEME_SCALE_VERTICAL: {
  1124       if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
  1125           (aWidgetType == NS_THEME_RANGE &&
  1126            IsRangeHorizontal(aFrame))) {
  1127         aPart = TKP_TRACK;
  1128         aState = TRS_NORMAL;
  1129       } else {
  1130         aPart = TKP_TRACKVERT;
  1131         aState = TRVS_NORMAL;
  1133       return NS_OK;
  1135     case NS_THEME_RANGE_THUMB:
  1136     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  1137     case NS_THEME_SCALE_THUMB_VERTICAL: {
  1138       if (aWidgetType == NS_THEME_RANGE_THUMB) {
  1139         if (IsRangeHorizontal(aFrame)) {
  1140           aPart = TKP_THUMBBOTTOM;
  1141         } else {
  1142           aPart = IsFrameRTL(aFrame) ? TKP_THUMBLEFT : TKP_THUMBRIGHT;
  1144       } else {
  1145         aPart = (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL) ?
  1146                 TKP_THUMB : TKP_THUMBVERT;
  1148       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1149       if (!aFrame)
  1150         aState = TS_NORMAL;
  1151       else if (IsDisabled(aFrame, eventState)) {
  1152         aState = TKP_DISABLED;
  1154       else {
  1155         if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a requirement for
  1156                                                         // the thumb, since the drag is not canceled
  1157                                                         // when you move outside the thumb.
  1158           aState = TS_ACTIVE;
  1159         else if (eventState.HasState(NS_EVENT_STATE_FOCUS))
  1160           aState = TKP_FOCUSED;
  1161         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
  1162           aState = TS_HOVER;
  1163         else
  1164           aState = TS_NORMAL;
  1166       return NS_OK;
  1168     case NS_THEME_SPINNER_UP_BUTTON:
  1169     case NS_THEME_SPINNER_DOWN_BUTTON: {
  1170       aPart = (aWidgetType == NS_THEME_SPINNER_UP_BUTTON) ?
  1171               SPNP_UP : SPNP_DOWN;
  1172       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1173       if (!aFrame)
  1174         aState = TS_NORMAL;
  1175       else if (IsDisabled(aFrame, eventState))
  1176         aState = TS_DISABLED;
  1177       else
  1178         aState = StandardGetState(aFrame, aWidgetType, false);
  1179       return NS_OK;    
  1181     case NS_THEME_TOOLBOX:
  1182     case NS_THEME_WIN_MEDIA_TOOLBOX:
  1183     case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
  1184     case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
  1185     case NS_THEME_STATUSBAR:
  1186     case NS_THEME_SCROLLBAR:
  1187     case NS_THEME_SCROLLBAR_SMALL: {
  1188       aState = 0;
  1189       if (IsVistaOrLater()) {
  1190         // On vista, they have a part
  1191         aPart = RP_BACKGROUND;
  1192       } else {
  1193         // Otherwise, they don't.  (But I bet
  1194         // RP_BACKGROUND would work here, too);
  1195         aPart = 0;
  1197       return NS_OK;
  1199     case NS_THEME_TOOLBAR: {
  1200       // Use -1 to indicate we don't wish to have the theme background drawn
  1201       // for this item. We will pass any nessessary information via aState,
  1202       // and will render the item using separate code.
  1203       aPart = -1;
  1204       aState = 0;
  1205       if (aFrame) {
  1206         nsIContent* content = aFrame->GetContent();
  1207         nsIContent* parent = content->GetParent();
  1208         // XXXzeniko hiding the first toolbar will result in an unwanted margin
  1209         if (parent && parent->GetFirstChild() == content) {
  1210           aState = 1;
  1213       return NS_OK;
  1215     case NS_THEME_STATUSBAR_PANEL:
  1216     case NS_THEME_STATUSBAR_RESIZER_PANEL:
  1217     case NS_THEME_RESIZER: {
  1218       aPart = (aWidgetType - NS_THEME_STATUSBAR_PANEL) + 1;
  1219       aState = TS_NORMAL;
  1220       return NS_OK;
  1222     case NS_THEME_TREEVIEW:
  1223     case NS_THEME_LISTBOX: {
  1224       aPart = TREEVIEW_BODY;
  1225       aState = TS_NORMAL;
  1226       return NS_OK;
  1228     case NS_THEME_TAB_PANELS: {
  1229       aPart = TABP_PANELS;
  1230       aState = TS_NORMAL;
  1231       return NS_OK;
  1233     case NS_THEME_TAB_PANEL: {
  1234       aPart = TABP_PANEL;
  1235       aState = TS_NORMAL;
  1236       return NS_OK;
  1238     case NS_THEME_TAB: {
  1239       aPart = TABP_TAB;
  1240       if (!aFrame) {
  1241         aState = TS_NORMAL;
  1242         return NS_OK;
  1245       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1246       if (IsDisabled(aFrame, eventState)) {
  1247         aState = TS_DISABLED;
  1248         return NS_OK;
  1251       if (IsSelectedTab(aFrame)) {
  1252         aPart = TABP_TAB_SELECTED;
  1253         aState = TS_ACTIVE; // The selected tab is always "pressed".
  1255       else
  1256         aState = StandardGetState(aFrame, aWidgetType, true);
  1258       return NS_OK;
  1260     case NS_THEME_TREEVIEW_HEADER_SORTARROW: {
  1261       // XXX Probably will never work due to a bug in the Luna theme.
  1262       aPart = 4;
  1263       aState = 1;
  1264       return NS_OK;
  1266     case NS_THEME_TREEVIEW_HEADER_CELL: {
  1267       aPart = 1;
  1268       if (!aFrame) {
  1269         aState = TS_NORMAL;
  1270         return NS_OK;
  1273       aState = StandardGetState(aFrame, aWidgetType, true);
  1275       return NS_OK;
  1277     case NS_THEME_DROPDOWN: {
  1278       nsIContent* content = aFrame->GetContent();
  1279       bool isHTML = content && content->IsHTML();
  1280       bool useDropBorder = isHTML || IsMenuListEditable(aFrame);
  1281       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1283       /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
  1284        * content or for editable menulists; this gives us the thin outline,
  1285        * instead of the gradient-filled background */
  1286       if (useDropBorder)
  1287         aPart = CBP_DROPBORDER;
  1288       else
  1289         aPart = CBP_DROPFRAME;
  1291       if (IsDisabled(aFrame, eventState)) {
  1292         aState = TS_DISABLED;
  1293       } else if (IsReadOnly(aFrame)) {
  1294         aState = TS_NORMAL;
  1295       } else if (IsOpenButton(aFrame)) {
  1296         aState = TS_ACTIVE;
  1297       } else {
  1298         if (useDropBorder && (eventState.HasState(NS_EVENT_STATE_FOCUS) || IsFocused(aFrame)))
  1299           aState = TS_ACTIVE;
  1300         else if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
  1301           aState = TS_ACTIVE;
  1302         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
  1303           aState = TS_HOVER;
  1304         else
  1305           aState = TS_NORMAL;
  1308       return NS_OK;
  1310     case NS_THEME_DROPDOWN_BUTTON: {
  1311       bool isHTML = IsHTMLContent(aFrame);
  1312       nsIFrame* parentFrame = aFrame->GetParent();
  1313       bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
  1314       bool isOpen = false;
  1316       // HTML select and XUL menulist dropdown buttons get state from the parent.
  1317       if (isHTML || isMenulist)
  1318         aFrame = parentFrame;
  1320       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1321       aPart = IsVistaOrLater() ?
  1322         CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
  1324       // For HTML controls with author styling, we should fall
  1325       // back to the old dropmarker style to avoid clashes with
  1326       // author-specified backgrounds and borders (bug #441034)
  1327       if (isHTML && IsWidgetStyled(aFrame->PresContext(), aFrame, NS_THEME_DROPDOWN))
  1328         aPart = CBP_DROPMARKER;
  1330       if (IsDisabled(aFrame, eventState)) {
  1331         aState = TS_DISABLED;
  1332         return NS_OK;
  1335       if (isHTML) {
  1336         nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
  1337         isOpen = (ccf && ccf->IsDroppedDown());
  1339       else
  1340         isOpen = IsOpenButton(aFrame);
  1342       if (IsVistaOrLater()) {
  1343         if (isHTML || IsMenuListEditable(aFrame)) {
  1344           if (isOpen) {
  1345             /* Hover is propagated, but we need to know whether we're
  1346              * hovering just the combobox frame, not the dropdown frame.
  1347              * But, we can't get that information, since hover is on the
  1348              * content node, and they share the same content node.  So,
  1349              * instead, we cheat -- if the dropdown is open, we always
  1350              * show the hover state.  This looks fine in practice.
  1351              */
  1352             aState = TS_HOVER;
  1353             return NS_OK;
  1355         } else {
  1356           /* On Vista, the dropdown indicator on a menulist button in  
  1357            * chrome is not given a hover effect. When the frame isn't
  1358            * isn't HTML content, we cheat and force the dropdown state
  1359            * to be normal. (Bug 430434)
  1360            */
  1361           aState = TS_NORMAL;
  1362           return NS_OK;
  1366       aState = TS_NORMAL;
  1368       // Dropdown button active state doesn't need :hover.
  1369       if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) {
  1370         if (isOpen && (isHTML || isMenulist)) {
  1371           // XXX Button should look active until the mouse is released, but
  1372           //     without making it look active when the popup is clicked.
  1373           return NS_OK;
  1375         aState = TS_ACTIVE;
  1377       else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
  1378         // No hover effect for XUL menulists and autocomplete dropdown buttons
  1379         // while the dropdown menu is open.
  1380         if (isOpen) {
  1381           // XXX HTML select dropdown buttons should have the hover effect when
  1382           //     hovering the combobox frame, but not the popup frame.
  1383           return NS_OK;
  1385         aState = TS_HOVER;
  1387       return NS_OK;
  1389     case NS_THEME_MENUPOPUP: {
  1390       aPart = MENU_POPUPBACKGROUND;
  1391       aState = MB_ACTIVE;
  1392       return NS_OK;
  1394     case NS_THEME_MENUITEM:
  1395     case NS_THEME_CHECKMENUITEM: 
  1396     case NS_THEME_RADIOMENUITEM: {
  1397       bool isTopLevel = false;
  1398       bool isOpen = false;
  1399       bool isHover = false;
  1400       nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
  1401       EventStates eventState = GetContentState(aFrame, aWidgetType);
  1403       isTopLevel = IsTopLevelMenu(aFrame);
  1405       if (menuFrame)
  1406         isOpen = menuFrame->IsOpen();
  1408       isHover = IsMenuActive(aFrame, aWidgetType);
  1410       if (isTopLevel) {
  1411         aPart = MENU_BARITEM;
  1413         if (isOpen)
  1414           aState = MBI_PUSHED;
  1415         else if (isHover)
  1416           aState = MBI_HOT;
  1417         else
  1418           aState = MBI_NORMAL;
  1420         // the disabled states are offset by 3
  1421         if (IsDisabled(aFrame, eventState))
  1422           aState += 3;
  1423       } else {
  1424         aPart = MENU_POPUPITEM;
  1426         if (isHover)
  1427           aState = MPI_HOT;
  1428         else
  1429           aState = MPI_NORMAL;
  1431         // the disabled states are offset by 2
  1432         if (IsDisabled(aFrame, eventState))
  1433           aState += 2;
  1436       return NS_OK;
  1438     case NS_THEME_MENUSEPARATOR:
  1439       aPart = MENU_POPUPSEPARATOR;
  1440       aState = 0;
  1441       return NS_OK;
  1442     case NS_THEME_MENUARROW:
  1444         aPart = MENU_POPUPSUBMENU;
  1445         EventStates eventState = GetContentState(aFrame, aWidgetType);
  1446         aState = IsDisabled(aFrame, eventState) ? MSM_DISABLED : MSM_NORMAL;
  1447         return NS_OK;
  1449     case NS_THEME_MENUCHECKBOX:
  1450     case NS_THEME_MENURADIO:
  1452         bool isChecked;
  1453         EventStates eventState = GetContentState(aFrame, aWidgetType);
  1455         // NOTE: we can probably use NS_EVENT_STATE_CHECKED
  1456         isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
  1458         aPart = MENU_POPUPCHECK;
  1459         aState = MC_CHECKMARKNORMAL;
  1461         // Radio states are offset by 2
  1462         if (aWidgetType == NS_THEME_MENURADIO)
  1463           aState += 2;
  1465         // the disabled states are offset by 1
  1466         if (IsDisabled(aFrame, eventState))
  1467           aState += 1;
  1469         return NS_OK;
  1471     case NS_THEME_MENUITEMTEXT:
  1472     case NS_THEME_MENUIMAGE:
  1473       aPart = -1;
  1474       aState = 0;
  1475       return NS_OK;
  1477     case NS_THEME_WINDOW_TITLEBAR:
  1478       aPart = mozilla::widget::themeconst::WP_CAPTION;
  1479       aState = GetTopLevelWindowActiveState(aFrame);
  1480       return NS_OK;
  1481     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  1482       aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
  1483       aState = GetTopLevelWindowActiveState(aFrame);
  1484       return NS_OK;
  1485     case NS_THEME_WINDOW_FRAME_LEFT:
  1486       aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
  1487       aState = GetTopLevelWindowActiveState(aFrame);
  1488       return NS_OK;
  1489     case NS_THEME_WINDOW_FRAME_RIGHT:
  1490       aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
  1491       aState = GetTopLevelWindowActiveState(aFrame);
  1492       return NS_OK;
  1493     case NS_THEME_WINDOW_FRAME_BOTTOM:
  1494       aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
  1495       aState = GetTopLevelWindowActiveState(aFrame);
  1496       return NS_OK;
  1497     case NS_THEME_WINDOW_BUTTON_CLOSE:
  1498       aPart = mozilla::widget::themeconst::WP_CLOSEBUTTON;
  1499       aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
  1500       return NS_OK;
  1501     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  1502       aPart = mozilla::widget::themeconst::WP_MINBUTTON;
  1503       aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
  1504       return NS_OK;
  1505     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  1506       aPart = mozilla::widget::themeconst::WP_MAXBUTTON;
  1507       aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
  1508       return NS_OK;
  1509     case NS_THEME_WINDOW_BUTTON_RESTORE:
  1510       aPart = mozilla::widget::themeconst::WP_RESTOREBUTTON;
  1511       aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
  1512       return NS_OK;
  1513     case NS_THEME_WINDOW_BUTTON_BOX:
  1514     case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
  1515     case NS_THEME_WIN_GLASS:
  1516     case NS_THEME_WIN_BORDERLESS_GLASS:
  1517       aPart = -1;
  1518       aState = 0;
  1519       return NS_OK;
  1522   aPart = 0;
  1523   aState = 0;
  1524   return NS_ERROR_FAILURE;
  1527 static bool
  1528 AssumeThemePartAndStateAreTransparent(int32_t aPart, int32_t aState)
  1530   if (aPart == MENU_POPUPITEM && aState == MBI_NORMAL) {
  1531     return true;
  1533   return false;
  1536 NS_IMETHODIMP
  1537 nsNativeThemeWin::DrawWidgetBackground(nsRenderingContext* aContext,
  1538                                        nsIFrame* aFrame,
  1539                                        uint8_t aWidgetType,
  1540                                        const nsRect& aRect,
  1541                                        const nsRect& aDirtyRect)
  1543   HANDLE theme = GetTheme(aWidgetType);
  1544   if (!theme)
  1545     return ClassicDrawWidgetBackground(aContext, aFrame, aWidgetType, aRect, aDirtyRect); 
  1547   // ^^ without the right sdk, assume xp theming and fall through.
  1548   if (nsUXThemeData::CheckForCompositor()) {
  1549     switch (aWidgetType) {
  1550       case NS_THEME_WINDOW_TITLEBAR:
  1551       case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  1552       case NS_THEME_WINDOW_FRAME_LEFT:
  1553       case NS_THEME_WINDOW_FRAME_RIGHT:
  1554       case NS_THEME_WINDOW_FRAME_BOTTOM:
  1555         // Nothing to draw, these areas are glass. Minimum dimensions
  1556         // should be set, so xul content should be layed out correctly.
  1557         return NS_OK;
  1558       break;
  1559       case NS_THEME_WINDOW_BUTTON_CLOSE:
  1560       case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  1561       case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  1562       case NS_THEME_WINDOW_BUTTON_RESTORE:
  1563         // Not conventional bitmaps, can't be retrieved. If we fall
  1564         // through here and call the theme library we'll get aero
  1565         // basic bitmaps. 
  1566         return NS_OK;
  1567       break;
  1568       case NS_THEME_WIN_GLASS:
  1569       case NS_THEME_WIN_BORDERLESS_GLASS:
  1570         // Nothing to draw, this is the glass background.
  1571         return NS_OK;
  1572       case NS_THEME_WINDOW_BUTTON_BOX:
  1573       case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
  1574         // We handle these through nsIWidget::UpdateThemeGeometries
  1575         return NS_OK;
  1576       break;
  1580   int32_t part, state;
  1581   nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
  1582   if (NS_FAILED(rv))
  1583     return rv;
  1585   if (AssumeThemePartAndStateAreTransparent(part, state)) {
  1586     return NS_OK;
  1589   gfxFloat p2a = gfxFloat(aContext->AppUnitsPerDevPixel());
  1590   RECT widgetRect;
  1591   RECT clipRect;
  1592   gfxRect tr(aRect.x, aRect.y, aRect.width, aRect.height),
  1593           dr(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
  1595   tr.ScaleInverse(p2a);
  1596   dr.ScaleInverse(p2a);
  1598   nsRefPtr<gfxContext> ctx = aContext->ThebesContext();
  1600   gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
  1602 RENDER_AGAIN:
  1604   HDC hdc = nativeDrawing.BeginNativeDrawing();
  1605   if (!hdc)
  1606     return NS_ERROR_FAILURE;
  1608   nativeDrawing.TransformToNativeRect(tr, widgetRect);
  1609   nativeDrawing.TransformToNativeRect(dr, clipRect);
  1611 #if 0
  1613     PR_LOG(gWindowsLog, PR_LOG_ERROR,
  1614            (stderr, "xform: %f %f %f %f [%f %f]\n", m.xx, m.yx, m.xy, m.yy, 
  1615             m.x0, m.y0));
  1616     PR_LOG(gWindowsLog, PR_LOG_ERROR,
  1617            (stderr, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
  1618             tr.x, tr.y, tr.width, tr.height, dr.x, dr.y, dr.width, dr.height,
  1619             offset.x, offset.y));
  1621 #endif
  1623   if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
  1624     // Clip out the left and right corners of the frame, all we want in
  1625     // is the middle section.
  1626     widgetRect.left -= GetSystemMetrics(SM_CXFRAME);
  1627     widgetRect.right += GetSystemMetrics(SM_CXFRAME);
  1628   } else if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
  1629     // The origin of the window is off screen when maximized and windows
  1630     // doesn't compensate for this in rendering the background. Push the
  1631     // top of the bitmap down by SM_CYFRAME so we get the full graphic.
  1632     widgetRect.top += GetSystemMetrics(SM_CYFRAME);
  1633   } else if (aWidgetType == NS_THEME_TAB) {
  1634     // For left edge and right edge tabs, we need to adjust the widget
  1635     // rects and clip rects so that the edges don't get drawn.
  1636     bool isLeft = IsLeftToSelectedTab(aFrame);
  1637     bool isRight = !isLeft && IsRightToSelectedTab(aFrame);
  1639     if (isLeft || isRight) {
  1640       // HACK ALERT: There appears to be no way to really obtain this value, so we're forced
  1641       // to just use the default value for Luna (which also happens to be correct for
  1642       // all the other skins I've tried).
  1643       int32_t edgeSize = 2;
  1645       // Armed with the size of the edge, we now need to either shift to the left or to the
  1646       // right.  The clip rect won't include this extra area, so we know that we're
  1647       // effectively shifting the edge out of view (such that it won't be painted).
  1648       if (isLeft)
  1649         // The right edge should not be drawn.  Extend our rect by the edge size.
  1650         widgetRect.right += edgeSize;
  1651       else
  1652         // The left edge should not be drawn.  Move the widget rect's left coord back.
  1653         widgetRect.left -= edgeSize;
  1656   else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
  1657     OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
  1659   else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
  1660            aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
  1661     OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
  1663   else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
  1664     OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
  1667   // widgetRect is the bounding box for a widget, yet the scale track is only
  1668   // a small portion of this size, so the edges of the scale need to be
  1669   // adjusted to the real size of the track.
  1670   if (aWidgetType == NS_THEME_RANGE ||
  1671       aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
  1672       aWidgetType == NS_THEME_SCALE_VERTICAL) {
  1673     RECT contentRect;
  1674     GetThemeBackgroundContentRect(theme, hdc, part, state, &widgetRect, &contentRect);
  1676     SIZE siz;
  1677     GetThemePartSize(theme, hdc, part, state, &widgetRect, TS_TRUE, &siz);
  1679     // When rounding is necessary, we round the position of the track
  1680     // away from the chevron of the thumb to make it look better.
  1681     if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
  1682         (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
  1683       contentRect.top += (contentRect.bottom - contentRect.top - siz.cy) / 2;
  1684       contentRect.bottom = contentRect.top + siz.cy;
  1686     else {
  1687       if (!IsFrameRTL(aFrame)) {
  1688         contentRect.left += (contentRect.right - contentRect.left - siz.cx) / 2;
  1689         contentRect.right = contentRect.left + siz.cx;
  1690       } else {
  1691         contentRect.right -= (contentRect.right - contentRect.left - siz.cx) / 2;
  1692         contentRect.left = contentRect.right - siz.cx;
  1696     DrawThemeBackground(theme, hdc, part, state, &contentRect, &clipRect);
  1698   else if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO)
  1700       bool isChecked = false;
  1701       isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
  1703       if (isChecked)
  1705         int bgState = MCB_NORMAL;
  1706         EventStates eventState = GetContentState(aFrame, aWidgetType);
  1708         // the disabled states are offset by 1
  1709         if (IsDisabled(aFrame, eventState))
  1710           bgState += 1;
  1712         SIZE checkboxBGSize(GetCheckboxBGSize(theme, hdc));
  1714         RECT checkBGRect = widgetRect;
  1715         if (IsFrameRTL(aFrame)) {
  1716           checkBGRect.left = checkBGRect.right-checkboxBGSize.cx;
  1717         } else {
  1718           checkBGRect.right = checkBGRect.left+checkboxBGSize.cx;
  1721         // Center the checkbox background vertically in the menuitem
  1722         checkBGRect.top += (checkBGRect.bottom - checkBGRect.top)/2 - checkboxBGSize.cy/2;
  1723         checkBGRect.bottom = checkBGRect.top + checkboxBGSize.cy;
  1725         DrawThemeBackground(theme, hdc, MENU_POPUPCHECKBACKGROUND, bgState, &checkBGRect, &clipRect);
  1727         MARGINS checkMargins = GetCheckboxMargins(theme, hdc);
  1728         RECT checkRect = checkBGRect;
  1729         checkRect.left += checkMargins.cxLeftWidth;
  1730         checkRect.right -= checkMargins.cxRightWidth;
  1731         checkRect.top += checkMargins.cyTopHeight;
  1732         checkRect.bottom -= checkMargins.cyBottomHeight;
  1733         DrawThemeBackground(theme, hdc, MENU_POPUPCHECK, state, &checkRect, &clipRect);
  1736   else if (aWidgetType == NS_THEME_MENUPOPUP)
  1738     DrawThemeBackground(theme, hdc, MENU_POPUPBORDERS, /* state */ 0, &widgetRect, &clipRect);
  1739     SIZE borderSize;
  1740     GetThemePartSize(theme, hdc, MENU_POPUPBORDERS, 0, nullptr, TS_TRUE, &borderSize);
  1742     RECT bgRect = widgetRect;
  1743     bgRect.top += borderSize.cy;
  1744     bgRect.bottom -= borderSize.cy;
  1745     bgRect.left += borderSize.cx;
  1746     bgRect.right -= borderSize.cx;
  1748     DrawThemeBackground(theme, hdc, MENU_POPUPBACKGROUND, /* state */ 0, &bgRect, &clipRect);
  1750     SIZE gutterSize(GetGutterSize(theme, hdc));
  1752     RECT gutterRect;
  1753     gutterRect.top = bgRect.top;
  1754     gutterRect.bottom = bgRect.bottom;
  1755     if (IsFrameRTL(aFrame)) {
  1756       gutterRect.right = bgRect.right;
  1757       gutterRect.left = gutterRect.right-gutterSize.cx;
  1758     } else {
  1759       gutterRect.left = bgRect.left;
  1760       gutterRect.right = gutterRect.left+gutterSize.cx;
  1763     DrawThemeBGRTLAware(theme, hdc, MENU_POPUPGUTTER, /* state */ 0,
  1764                         &gutterRect, &clipRect, IsFrameRTL(aFrame));
  1766   else if (aWidgetType == NS_THEME_MENUSEPARATOR)
  1768     SIZE gutterSize(GetGutterSize(theme,hdc));
  1770     RECT sepRect = widgetRect;
  1771     if (IsFrameRTL(aFrame))
  1772       sepRect.right -= gutterSize.cx;
  1773     else
  1774       sepRect.left += gutterSize.cx;
  1776     DrawThemeBackground(theme, hdc, MENU_POPUPSEPARATOR, /* state */ 0, &sepRect, &clipRect);
  1778   else if (aWidgetType == NS_THEME_MENUARROW)
  1780     // We're dpi aware and as such on systems that have dpi > 96 set, the
  1781     // theme library expects us to do proper positioning and scaling of glyphs.
  1782     // For NS_THEME_MENUARROW, layout may hand us a widget rect larger than the
  1783     // glyph rect we request in GetMinimumWidgetSize. To prevent distortion we
  1784     // have to position and scale what we draw.
  1786     SIZE glyphSize;
  1787     GetThemePartSize(theme, hdc, part, state, nullptr, TS_TRUE, &glyphSize);
  1789     int32_t widgetHeight = widgetRect.bottom - widgetRect.top;
  1791     RECT renderRect = widgetRect;
  1793     // We request (glyph width * 2, glyph height) in GetMinimumWidgetSize. In
  1794     // Firefox some menu items provide the full height of the item to us, in
  1795     // others our widget rect is the exact dims of our arrow glyph. Adjust the
  1796     // vertical position by the added space, if any exists.
  1797     renderRect.top += ((widgetHeight - glyphSize.cy) / 2);
  1798     renderRect.bottom = renderRect.top + glyphSize.cy;
  1799     // I'm using the width of the arrow glyph for the arrow-side padding.
  1800     // AFAICT there doesn't appear to be a theme constant we can query
  1801     // for this value. Generally this looks correct, and has the added
  1802     // benefit of being a dpi adjusted value.
  1803     if (!IsFrameRTL(aFrame)) {
  1804       renderRect.right = widgetRect.right - glyphSize.cx;
  1805       renderRect.left = renderRect.right - glyphSize.cx;
  1806     } else {
  1807       renderRect.left = glyphSize.cx;
  1808       renderRect.right = renderRect.left + glyphSize.cx;
  1810     DrawThemeBGRTLAware(theme, hdc, part, state, &renderRect, &clipRect,
  1811                         IsFrameRTL(aFrame));
  1813   // The following widgets need to be RTL-aware
  1814   else if (aWidgetType == NS_THEME_RESIZER ||
  1815            aWidgetType == NS_THEME_DROPDOWN_BUTTON)
  1817     DrawThemeBGRTLAware(theme, hdc, part, state,
  1818                         &widgetRect, &clipRect, IsFrameRTL(aFrame));
  1820   else if (aWidgetType == NS_THEME_PROGRESSBAR ||
  1821            aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL) {
  1822     // DrawThemeBackground renders each corner with a solid white pixel.
  1823     // Restore these pixels to the underlying color. Tracks are rendered
  1824     // using alpha recovery, so this makes the corners transparent.
  1825     COLORREF color;
  1826     color = GetPixel(hdc, widgetRect.left, widgetRect.top);
  1827     DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
  1828     SetPixel(hdc, widgetRect.left, widgetRect.top, color);
  1829     SetPixel(hdc, widgetRect.right-1, widgetRect.top, color);
  1830     SetPixel(hdc, widgetRect.right-1, widgetRect.bottom-1, color);
  1831     SetPixel(hdc, widgetRect.left, widgetRect.bottom-1, color);
  1833   else if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
  1834            aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL) {
  1835     DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state,
  1836                             &widgetRect, &clipRect, p2a);
  1838   // If part is negative, the element wishes us to not render a themed
  1839   // background, instead opting to be drawn specially below.
  1840   else if (part >= 0) {
  1841     DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
  1844   // Draw focus rectangles for XP HTML checkboxes and radio buttons
  1845   // XXX it'd be nice to draw these outside of the frame
  1846   if (((aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_RADIO) &&
  1847         aFrame->GetContent()->IsHTML()) ||
  1848       aWidgetType == NS_THEME_RANGE ||
  1849       aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
  1850       aWidgetType == NS_THEME_SCALE_VERTICAL) {
  1851       EventStates contentState = GetContentState(aFrame, aWidgetType);
  1853       if (contentState.HasState(NS_EVENT_STATE_FOCUS)) {
  1854         POINT vpOrg;
  1855         HPEN hPen = nullptr;
  1857         uint8_t id = SaveDC(hdc);
  1859         ::SelectClipRgn(hdc, nullptr);
  1860         ::GetViewportOrgEx(hdc, &vpOrg);
  1861         ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, nullptr);
  1863         // On vista, choose our own colors and draw an XP style half focus rect
  1864         // for focused checkboxes and a full rect when active.
  1865         if (IsVistaOrLater() &&
  1866             aWidgetType == NS_THEME_CHECKBOX) {
  1867           LOGBRUSH lb;
  1868           lb.lbStyle = BS_SOLID;
  1869           lb.lbColor = RGB(255,255,255);
  1870           lb.lbHatch = 0;
  1872           hPen = ::ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, nullptr);
  1873           ::SelectObject(hdc, hPen);
  1875           // If pressed, draw the upper left corner of the dotted rect.
  1876           if (contentState.HasState(NS_EVENT_STATE_ACTIVE)) {
  1877             ::MoveToEx(hdc, widgetRect.left, widgetRect.bottom-1, nullptr);
  1878             ::LineTo(hdc, widgetRect.left, widgetRect.top);
  1879             ::LineTo(hdc, widgetRect.right-1, widgetRect.top);
  1882           // Draw the lower right corner of the dotted rect.
  1883           ::MoveToEx(hdc, widgetRect.right-1, widgetRect.top, nullptr);
  1884           ::LineTo(hdc, widgetRect.right-1, widgetRect.bottom-1);
  1885           ::LineTo(hdc, widgetRect.left, widgetRect.bottom-1);
  1886         } else {
  1887           ::SetTextColor(hdc, 0);
  1888           ::DrawFocusRect(hdc, &widgetRect);
  1890         ::RestoreDC(hdc, id);
  1891         if (hPen) {
  1892           ::DeleteObject(hPen);
  1896   else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
  1897     // Draw toolbar separator lines above all toolbars except the first one.
  1898     // The lines are part of the Rebar theme, which is loaded for NS_THEME_TOOLBOX.
  1899     theme = GetTheme(NS_THEME_TOOLBOX);
  1900     if (!theme)
  1901       return NS_ERROR_FAILURE;
  1903     widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
  1904     DrawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, nullptr);
  1906   else if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL ||
  1907            aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL)
  1909     // Draw the decorative gripper for the scrollbar thumb button, if it fits
  1911     SIZE gripSize;
  1912     MARGINS thumbMgns;
  1913     int gripPart = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) ?
  1914                    SP_GRIPPERHOR : SP_GRIPPERVERT;
  1916     if (GetThemePartSize(theme, hdc, gripPart, state, nullptr, TS_TRUE, &gripSize) == S_OK &&
  1917         GetThemeMargins(theme, hdc, part, state, TMT_CONTENTMARGINS, nullptr, &thumbMgns) == S_OK &&
  1918         gripSize.cx + thumbMgns.cxLeftWidth + thumbMgns.cxRightWidth <= widgetRect.right - widgetRect.left &&
  1919         gripSize.cy + thumbMgns.cyTopHeight + thumbMgns.cyBottomHeight <= widgetRect.bottom - widgetRect.top)
  1921       DrawThemeBackground(theme, hdc, gripPart, state, &widgetRect, &clipRect);
  1925   nativeDrawing.EndNativeDrawing();
  1927   if (nativeDrawing.ShouldRenderAgain())
  1928     goto RENDER_AGAIN;
  1930   nativeDrawing.PaintToContext();
  1932   return NS_OK;
  1935 NS_IMETHODIMP
  1936 nsNativeThemeWin::GetWidgetBorder(nsDeviceContext* aContext, 
  1937                                   nsIFrame* aFrame,
  1938                                   uint8_t aWidgetType,
  1939                                   nsIntMargin* aResult)
  1941   HANDLE theme = GetTheme(aWidgetType);
  1942   if (!theme)
  1943     return ClassicGetWidgetBorder(aContext, aFrame, aWidgetType, aResult); 
  1945   (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
  1947   if (!WidgetIsContainer(aWidgetType) ||
  1948       aWidgetType == NS_THEME_TOOLBOX || 
  1949       aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
  1950       aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
  1951       aWidgetType == NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX ||
  1952       aWidgetType == NS_THEME_STATUSBAR || 
  1953       aWidgetType == NS_THEME_RESIZER || aWidgetType == NS_THEME_TAB_PANEL ||
  1954       aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL ||
  1955       aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL ||
  1956       aWidgetType == NS_THEME_MENUITEM || aWidgetType == NS_THEME_CHECKMENUITEM ||
  1957       aWidgetType == NS_THEME_RADIOMENUITEM || aWidgetType == NS_THEME_MENUPOPUP ||
  1958       aWidgetType == NS_THEME_MENUIMAGE || aWidgetType == NS_THEME_MENUITEMTEXT ||
  1959       aWidgetType == NS_THEME_TOOLBAR_SEPARATOR ||
  1960       aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
  1961       aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
  1962       aWidgetType == NS_THEME_WIN_GLASS || aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS)
  1963     return NS_OK; // Don't worry about it.
  1965   int32_t part, state;
  1966   nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
  1967   if (NS_FAILED(rv))
  1968     return rv;
  1970   if (aWidgetType == NS_THEME_TOOLBAR) {
  1971     // make space for the separator line above all toolbars but the first
  1972     if (state == 0)
  1973       aResult->top = TB_SEPARATOR_HEIGHT;
  1974     return NS_OK;
  1977   // Get our info.
  1978   RECT outerRect; // Create a fake outer rect.
  1979   outerRect.top = outerRect.left = 100;
  1980   outerRect.right = outerRect.bottom = 200;
  1981   RECT contentRect(outerRect);
  1982   HRESULT res = GetThemeBackgroundContentRect(theme, nullptr, part, state, &outerRect, &contentRect);
  1984   if (FAILED(res))
  1985     return NS_ERROR_FAILURE;
  1987   // Now compute the delta in each direction and place it in our
  1988   // nsIntMargin struct.
  1989   aResult->top = contentRect.top - outerRect.top;
  1990   aResult->bottom = outerRect.bottom - contentRect.bottom;
  1991   aResult->left = contentRect.left - outerRect.left;
  1992   aResult->right = outerRect.right - contentRect.right;
  1994   // Remove the edges for tabs that are before or after the selected tab,
  1995   if (aWidgetType == NS_THEME_TAB) {
  1996     if (IsLeftToSelectedTab(aFrame))
  1997       // Remove the right edge, since we won't be drawing it.
  1998       aResult->right = 0;
  1999     else if (IsRightToSelectedTab(aFrame))
  2000       // Remove the left edge, since we won't be drawing it.
  2001       aResult->left = 0;
  2004   if (aFrame && (aWidgetType == NS_THEME_NUMBER_INPUT ||
  2005                  aWidgetType == NS_THEME_TEXTFIELD ||
  2006                  aWidgetType == NS_THEME_TEXTFIELD_MULTILINE)) {
  2007     nsIContent* content = aFrame->GetContent();
  2008     if (content && content->IsHTML()) {
  2009       // We need to pad textfields by 1 pixel, since the caret will draw
  2010       // flush against the edge by default if we don't.
  2011       aResult->top++;
  2012       aResult->left++;
  2013       aResult->bottom++;
  2014       aResult->right++;
  2018   return NS_OK;
  2021 bool
  2022 nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext, 
  2023                                    nsIFrame* aFrame,
  2024                                    uint8_t aWidgetType,
  2025                                    nsIntMargin* aResult)
  2027   switch (aWidgetType) {
  2028     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
  2029     // and have a meaningful baseline, so they can't have
  2030     // author-specified padding.
  2031     case NS_THEME_CHECKBOX:
  2032     case NS_THEME_RADIO:
  2033       aResult->SizeTo(0, 0, 0, 0);
  2034       return true;
  2037   HANDLE theme = GetTheme(aWidgetType);
  2039   if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX ||
  2040       aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
  2041     aResult->SizeTo(0, 0, 0, 0);
  2043     // aero glass doesn't display custom buttons
  2044     if (nsUXThemeData::CheckForCompositor())
  2045       return true;
  2047     // button padding for standard windows
  2048     if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX) {
  2049       aResult->top = GetSystemMetrics(SM_CXFRAME);
  2051     return true;
  2054   // Content padding
  2055   if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
  2056       aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
  2057     aResult->SizeTo(0, 0, 0, 0);
  2058     // XXX Maximized windows have an offscreen offset equal to
  2059     // the border padding. This should be addressed in nsWindow,
  2060     // but currently can't be, see UpdateNonClientMargins.
  2061     if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED)
  2062       aResult->top = GetSystemMetrics(SM_CXFRAME)
  2063                    + GetSystemMetrics(SM_CXPADDEDBORDER);
  2064     return true;
  2067   if (!theme)
  2068     return ClassicGetWidgetPadding(aContext, aFrame, aWidgetType, aResult);
  2070   if (aWidgetType == NS_THEME_MENUPOPUP)
  2072     SIZE popupSize;
  2073     GetThemePartSize(theme, nullptr, MENU_POPUPBORDERS, /* state */ 0, nullptr, TS_TRUE, &popupSize);
  2074     aResult->top = aResult->bottom = popupSize.cy;
  2075     aResult->left = aResult->right = popupSize.cx;
  2076     return true;
  2079   if (IsVistaOrLater()) {
  2080     if (aWidgetType == NS_THEME_NUMBER_INPUT ||
  2081         aWidgetType == NS_THEME_TEXTFIELD ||
  2082         aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
  2083         aWidgetType == NS_THEME_DROPDOWN)
  2085       /* If we have author-specified padding for these elements, don't do the fixups below */
  2086       if (aFrame->PresContext()->HasAuthorSpecifiedRules(aFrame, NS_AUTHOR_SPECIFIED_PADDING))
  2087         return false;
  2090     /* textfields need extra pixels on all sides, otherwise they
  2091      * wrap their content too tightly.  The actual border is drawn 1px
  2092      * inside the specified rectangle, so Gecko will end up making the
  2093      * contents look too small.  Instead, we add 2px padding for the
  2094      * contents and fix this. (Used to be 1px added, see bug 430212)
  2095      */
  2096     if (aWidgetType == NS_THEME_NUMBER_INPUT ||
  2097         aWidgetType == NS_THEME_TEXTFIELD ||
  2098         aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
  2099       aResult->top = aResult->bottom = 2;
  2100       aResult->left = aResult->right = 2;
  2101       return true;
  2102     } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_DROPDOWN) {
  2103       /* For content menulist controls, we need an extra pixel so
  2104        * that we have room to draw our focus rectangle stuff.
  2105        * Otherwise, the focus rect might overlap the control's
  2106        * border.
  2107        */
  2108       aResult->top = aResult->bottom = 1;
  2109       aResult->left = aResult->right = 1;
  2110       return true;
  2114   int32_t right, left, top, bottom;
  2115   right = left = top = bottom = 0;
  2116   switch (aWidgetType)
  2118     case NS_THEME_MENUIMAGE:
  2119         right = 8;
  2120         left = 3;
  2121         break;
  2122     case NS_THEME_MENUCHECKBOX:
  2123     case NS_THEME_MENURADIO:
  2124         right = 8;
  2125         left = 0;
  2126         break;
  2127     case NS_THEME_MENUITEMTEXT:
  2128         // There seem to be exactly 4 pixels from the edge
  2129         // of the gutter to the text: 2px margin (CSS) + 2px padding (here)
  2131           SIZE size(GetGutterSize(theme, nullptr));
  2132           left = size.cx + 2;
  2134         break;
  2135     case NS_THEME_MENUSEPARATOR:
  2137           SIZE size(GetGutterSize(theme, nullptr));
  2138           left = size.cx + 5;
  2139           top = 10;
  2140           bottom = 7;
  2142         break;
  2143     default:
  2144         return false;
  2147   if (IsFrameRTL(aFrame))
  2149     aResult->right = left;
  2150     aResult->left = right;
  2152   else
  2154     aResult->right = right;
  2155     aResult->left = left;
  2158   return true;
  2161 bool
  2162 nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext, 
  2163                                     nsIFrame* aFrame,
  2164                                     uint8_t aOverflowRect,
  2165                                     nsRect* aResult)
  2167   /* This is disabled for now, because it causes invalidation problems --
  2168    * see bug 420381.  The effect of not updating the overflow area is that
  2169    * for dropdown buttons in content areas, there is a 1px border on 3 sides
  2170    * where, if invalidated, the dropdown control probably won't be repainted.
  2171    * This is fairly minor, as by default there is nothing in that area, and
  2172    * a border only shows up if the widget is being hovered.
  2173    */
  2174 #if 0
  2175   if (IsVistaOrLater()) {
  2176     /* We explicitly draw dropdown buttons in HTML content 1px bigger
  2177      * up, right, and bottom so that they overlap the dropdown's border
  2178      * like they're supposed to.
  2179      */
  2180     if (aWidgetType == NS_THEME_DROPDOWN_BUTTON &&
  2181         IsHTMLContent(aFrame) &&
  2182         !IsWidgetStyled(aFrame->GetParent()->PresContext(),
  2183                         aFrame->GetParent(),
  2184                         NS_THEME_DROPDOWN))
  2186       int32_t p2a = aContext->AppUnitsPerDevPixel();
  2187       /* Note: no overflow on the left */
  2188       nsMargin m(p2a, p2a, p2a, 0);
  2189       aOverflowRect->Inflate (m);
  2190       return true;
  2193 #endif
  2195   return false;
  2198 NS_IMETHODIMP
  2199 nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame,
  2200                                        uint8_t aWidgetType,
  2201                                        nsIntSize* aResult, bool* aIsOverridable)
  2203   (*aResult).width = (*aResult).height = 0;
  2204   *aIsOverridable = true;
  2206   HANDLE theme = GetTheme(aWidgetType);
  2207   if (!theme)
  2208     return ClassicGetMinimumWidgetSize(aContext, aFrame, aWidgetType, aResult, aIsOverridable);
  2210   switch (aWidgetType) {
  2211     case NS_THEME_GROUPBOX:
  2212     case NS_THEME_NUMBER_INPUT:
  2213     case NS_THEME_TEXTFIELD:
  2214     case NS_THEME_TOOLBOX:
  2215     case NS_THEME_WIN_MEDIA_TOOLBOX:
  2216     case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
  2217     case NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX:
  2218     case NS_THEME_TOOLBAR:
  2219     case NS_THEME_STATUSBAR:
  2220     case NS_THEME_PROGRESSBAR_CHUNK:
  2221     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  2222     case NS_THEME_TAB_PANELS:
  2223     case NS_THEME_TAB_PANEL:
  2224     case NS_THEME_LISTBOX:
  2225     case NS_THEME_TREEVIEW:
  2226     case NS_THEME_MENUITEMTEXT:
  2227     case NS_THEME_WIN_GLASS:
  2228     case NS_THEME_WIN_BORDERLESS_GLASS:
  2229       return NS_OK; // Don't worry about it.
  2232   if (aWidgetType == NS_THEME_MENUITEM && IsTopLevelMenu(aFrame))
  2233       return NS_OK; // Don't worry about it for top level menus
  2235   // Call GetSystemMetrics to determine size for WinXP scrollbars
  2236   // (GetThemeSysSize API returns the optimal size for the theme, but 
  2237   //  Windows appears to always use metrics when drawing standard scrollbars)
  2238   THEMESIZE sizeReq = TS_TRUE; // Best-fit size
  2239   switch (aWidgetType) {
  2240     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  2241     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  2242     case NS_THEME_SCROLLBAR_BUTTON_UP:
  2243     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  2244     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  2245     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  2246     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  2247     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  2248     case NS_THEME_DROPDOWN_BUTTON:
  2249       return ClassicGetMinimumWidgetSize(aContext, aFrame, aWidgetType, aResult, aIsOverridable);
  2251     case NS_THEME_MENUITEM:
  2252     case NS_THEME_CHECKMENUITEM:
  2253     case NS_THEME_RADIOMENUITEM:
  2254       if(!IsTopLevelMenu(aFrame))
  2256         SIZE gutterSize(GetGutterSize(theme, nullptr));
  2257         aResult->width = gutterSize.cx;
  2258         aResult->height = gutterSize.cy;
  2259         return NS_OK;
  2261       break;
  2263     case NS_THEME_MENUIMAGE:
  2264     case NS_THEME_MENUCHECKBOX:
  2265     case NS_THEME_MENURADIO:
  2267         SIZE boxSize(GetGutterSize(theme, nullptr));
  2268         aResult->width = boxSize.cx+2;
  2269         aResult->height = boxSize.cy;
  2270         *aIsOverridable = false;
  2273     case NS_THEME_MENUITEMTEXT:
  2274       return NS_OK;
  2276     case NS_THEME_PROGRESSBAR:
  2277     case NS_THEME_PROGRESSBAR_VERTICAL:
  2278       // Best-fit size for progress meters is too large for most 
  2279       // themes. We want these widgets to be able to really shrink
  2280       // down, so use the min-size request value (of 0).
  2281       sizeReq = TS_MIN; 
  2282       break;
  2284     case NS_THEME_RESIZER:
  2285       *aIsOverridable = false;
  2286       break;
  2288     case NS_THEME_RANGE_THUMB:
  2289     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  2290     case NS_THEME_SCALE_THUMB_VERTICAL:
  2292       *aIsOverridable = false;
  2293       // on Vista, GetThemePartAndState returns odd values for
  2294       // scale thumbs, so use a hardcoded size instead.
  2295       if (IsVistaOrLater()) {
  2296         if (aWidgetType == NS_THEME_SCALE_THUMB_HORIZONTAL ||
  2297             (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
  2298           aResult->width = 12;
  2299           aResult->height = 20;
  2301         else {
  2302           aResult->width = 20;
  2303           aResult->height = 12;
  2305         return NS_OK;
  2307       break;
  2310     case NS_THEME_SCROLLBAR:
  2312       if (nsLookAndFeel::GetInt(
  2313             nsLookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
  2314         aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
  2315                         ::GetSystemMetrics(SM_CYVSCROLL));
  2316         return NS_OK;
  2318       break;
  2321     case NS_THEME_TOOLBAR_SEPARATOR:
  2322       // that's 2px left margin, 2px right margin and 2px separator
  2323       // (the margin is drawn as part of the separator, though)
  2324       aResult->width = 6;
  2325       return NS_OK;
  2327     case NS_THEME_BUTTON:
  2328       // We should let HTML buttons shrink to their min size.
  2329       // FIXME bug 403934: We should probably really separate
  2330       // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
  2331       // use the one they want.
  2332       if (aFrame->GetContent()->IsHTML()) {
  2333         sizeReq = TS_MIN;
  2335       break;
  2337     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  2338     case NS_THEME_WINDOW_BUTTON_RESTORE:
  2339       // The only way to get accurate titlebar button info is to query a
  2340       // window w/buttons when it's visible. nsWindow takes care of this and
  2341       // stores that info in nsUXThemeData.
  2342       aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cx;
  2343       aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cy;
  2344       // For XP, subtract 4 from system metrics dimensions.
  2345       if (!IsVistaOrLater()) {
  2346         aResult->width -= 4;
  2347         aResult->height -= 4;
  2349       AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
  2350       *aIsOverridable = false;
  2351       return NS_OK;
  2353     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  2354       aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cx;
  2355       aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cy;
  2356       if (!IsVistaOrLater()) {
  2357         aResult->width -= 4;
  2358         aResult->height -= 4;
  2360       AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
  2361       *aIsOverridable = false;
  2362       return NS_OK;
  2364     case NS_THEME_WINDOW_BUTTON_CLOSE:
  2365       aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cx;
  2366       aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cy;
  2367       if (!IsVistaOrLater()) {
  2368         aResult->width -= 4;
  2369         aResult->height -= 4;
  2371       AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
  2372       *aIsOverridable = false;
  2373       return NS_OK;
  2375     case NS_THEME_WINDOW_TITLEBAR:
  2376     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  2377       aResult->height = GetSystemMetrics(SM_CYCAPTION);
  2378       aResult->height += GetSystemMetrics(SM_CYFRAME);
  2379       aResult->height += GetSystemMetrics(SM_CXPADDEDBORDER);
  2380       *aIsOverridable = false;
  2381       return NS_OK;
  2383     case NS_THEME_WINDOW_BUTTON_BOX:
  2384     case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
  2385       if (nsUXThemeData::CheckForCompositor()) {
  2386         aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx;
  2387         aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy
  2388                           - GetSystemMetrics(SM_CYFRAME)
  2389                           - GetSystemMetrics(SM_CXPADDEDBORDER);
  2390         if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
  2391           aResult->width += 1;
  2392           aResult->height -= 2;
  2394         *aIsOverridable = false;
  2395         return NS_OK;
  2397       break;
  2399     case NS_THEME_WINDOW_FRAME_LEFT:
  2400     case NS_THEME_WINDOW_FRAME_RIGHT:
  2401     case NS_THEME_WINDOW_FRAME_BOTTOM:
  2402       aResult->width = GetSystemMetrics(SM_CXFRAME);
  2403       aResult->height = GetSystemMetrics(SM_CYFRAME);
  2404       *aIsOverridable = false;
  2405       return NS_OK;
  2408   int32_t part, state;
  2409   nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
  2410   if (NS_FAILED(rv))
  2411     return rv;
  2413   HDC hdc = ::GetDC(nullptr);
  2414   if (!hdc)
  2415     return NS_ERROR_FAILURE;
  2417   SIZE sz;
  2418   GetThemePartSize(theme, hdc, part, state, nullptr, sizeReq, &sz);
  2419   aResult->width = sz.cx;
  2420   aResult->height = sz.cy;
  2422   switch(aWidgetType) {
  2423     case NS_THEME_SPINNER_UP_BUTTON:
  2424     case NS_THEME_SPINNER_DOWN_BUTTON:
  2425       aResult->width++;
  2426       aResult->height = aResult->height / 2 + 1;
  2427       break;
  2429     case NS_THEME_MENUSEPARATOR:
  2431       SIZE gutterSize(GetGutterSize(theme, hdc));
  2432       aResult->width += gutterSize.cx;
  2433       break;
  2436     case NS_THEME_MENUARROW:
  2438       // Use the width of the arrow glyph as padding. See the drawing
  2439       // code for details.
  2440       aResult->width *= 2;
  2441       break;
  2445   ::ReleaseDC(nullptr, hdc);
  2446   return NS_OK;
  2449 NS_IMETHODIMP
  2450 nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
  2451                                      nsIAtom* aAttribute, bool* aShouldRepaint)
  2453   // Some widget types just never change state.
  2454   if (aWidgetType == NS_THEME_TOOLBOX ||
  2455       aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
  2456       aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
  2457       aWidgetType == NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX ||
  2458       aWidgetType == NS_THEME_TOOLBAR ||
  2459       aWidgetType == NS_THEME_STATUSBAR || aWidgetType == NS_THEME_STATUSBAR_PANEL ||
  2460       aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL ||
  2461       aWidgetType == NS_THEME_PROGRESSBAR_CHUNK ||
  2462       aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL ||
  2463       aWidgetType == NS_THEME_PROGRESSBAR ||
  2464       aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
  2465       aWidgetType == NS_THEME_TOOLTIP ||
  2466       aWidgetType == NS_THEME_TAB_PANELS ||
  2467       aWidgetType == NS_THEME_TAB_PANEL ||
  2468       aWidgetType == NS_THEME_TOOLBAR_SEPARATOR ||
  2469       aWidgetType == NS_THEME_WIN_GLASS ||
  2470       aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS) {
  2471     *aShouldRepaint = false;
  2472     return NS_OK;
  2475   if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
  2476       aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
  2477       aWidgetType == NS_THEME_WINDOW_FRAME_LEFT ||
  2478       aWidgetType == NS_THEME_WINDOW_FRAME_RIGHT ||
  2479       aWidgetType == NS_THEME_WINDOW_FRAME_BOTTOM ||
  2480       aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE ||
  2481       aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
  2482       aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
  2483       aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
  2484     *aShouldRepaint = true;
  2485     return NS_OK;
  2488   // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover
  2489   if (!IsVistaOrLater() &&
  2490       (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL || 
  2491       aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL)) {
  2492     *aShouldRepaint = false;
  2493     return NS_OK;
  2496   // We need to repaint the dropdown arrow in vista HTML combobox controls when
  2497   // the control is closed to get rid of the hover effect.
  2498   if (IsVistaOrLater() &&
  2499       (aWidgetType == NS_THEME_DROPDOWN || aWidgetType == NS_THEME_DROPDOWN_BUTTON) &&
  2500       IsHTMLContent(aFrame))
  2502     *aShouldRepaint = true;
  2503     return NS_OK;
  2506   // XXXdwh Not sure what can really be done here.  Can at least guess for
  2507   // specific widgets that they're highly unlikely to have certain states.
  2508   // For example, a toolbar doesn't care about any states.
  2509   if (!aAttribute) {
  2510     // Hover/focus/active changed.  Always repaint.
  2511     *aShouldRepaint = true;
  2513   else {
  2514     // Check the attribute to see if it's relevant.  
  2515     // disabled, checked, dlgtype, default, etc.
  2516     *aShouldRepaint = false;
  2517     if (aAttribute == nsGkAtoms::disabled ||
  2518         aAttribute == nsGkAtoms::checked ||
  2519         aAttribute == nsGkAtoms::selected ||
  2520         aAttribute == nsGkAtoms::readonly ||
  2521         aAttribute == nsGkAtoms::open ||
  2522         aAttribute == nsGkAtoms::menuactive ||
  2523         aAttribute == nsGkAtoms::focused)
  2524       *aShouldRepaint = true;
  2527   return NS_OK;
  2530 NS_IMETHODIMP
  2531 nsNativeThemeWin::ThemeChanged()
  2533   nsUXThemeData::Invalidate();
  2534   return NS_OK;
  2537 bool 
  2538 nsNativeThemeWin::ThemeSupportsWidget(nsPresContext* aPresContext,
  2539                                       nsIFrame* aFrame,
  2540                                       uint8_t aWidgetType)
  2542   // XXXdwh We can go even further and call the API to ask if support exists for
  2543   // specific widgets.
  2545   if (aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
  2546     return false;
  2548   HANDLE theme = nullptr;
  2549   if (aWidgetType == NS_THEME_CHECKBOX_CONTAINER)
  2550     theme = GetTheme(NS_THEME_CHECKBOX);
  2551   else if (aWidgetType == NS_THEME_RADIO_CONTAINER)
  2552     theme = GetTheme(NS_THEME_RADIO);
  2553   else
  2554     theme = GetTheme(aWidgetType);
  2556   if ((theme) || (!theme && ClassicThemeSupportsWidget(aPresContext, aFrame, aWidgetType)))
  2557     // turn off theming for some HTML widgets styled by the page
  2558     return (!IsWidgetStyled(aPresContext, aFrame, aWidgetType));
  2560   return false;
  2563 bool 
  2564 nsNativeThemeWin::WidgetIsContainer(uint8_t aWidgetType)
  2566   // XXXdwh At some point flesh all of this out.
  2567   if (aWidgetType == NS_THEME_DROPDOWN_BUTTON || 
  2568       aWidgetType == NS_THEME_RADIO ||
  2569       aWidgetType == NS_THEME_CHECKBOX)
  2570     return false;
  2571   return true;
  2574 bool
  2575 nsNativeThemeWin::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
  2577   return false;
  2580 bool
  2581 nsNativeThemeWin::ThemeNeedsComboboxDropmarker()
  2583   return true;
  2586 bool
  2587 nsNativeThemeWin::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
  2589   switch (aWidgetType) {
  2590     case NS_THEME_WINDOW_TITLEBAR:
  2591     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  2592     case NS_THEME_WINDOW_FRAME_LEFT:
  2593     case NS_THEME_WINDOW_FRAME_RIGHT:
  2594     case NS_THEME_WINDOW_FRAME_BOTTOM:
  2595     case NS_THEME_WINDOW_BUTTON_CLOSE:
  2596     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  2597     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  2598     case NS_THEME_WINDOW_BUTTON_RESTORE:
  2599       return true;
  2600     default:
  2601       return false;
  2605 bool
  2606 nsNativeThemeWin::ShouldHideScrollbars()
  2608   return WinUtils::ShouldHideScrollbars();
  2611 nsITheme::Transparency
  2612 nsNativeThemeWin::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
  2614   switch (aWidgetType) {
  2615   case NS_THEME_SCROLLBAR_SMALL:
  2616   case NS_THEME_SCROLLBAR:
  2617   case NS_THEME_STATUSBAR:
  2618     // Knowing that scrollbars and statusbars are opaque improves
  2619     // performance, because we create layers for them. This better be
  2620     // true across all Windows themes! If it's not true, we should
  2621     // paint an opaque background for them to make it true!
  2622     return eOpaque;
  2623   case NS_THEME_WIN_GLASS:
  2624   case NS_THEME_WIN_BORDERLESS_GLASS:
  2625   case NS_THEME_SCALE_HORIZONTAL:
  2626   case NS_THEME_SCALE_VERTICAL:
  2627   case NS_THEME_PROGRESSBAR:
  2628   case NS_THEME_PROGRESSBAR_VERTICAL:
  2629   case NS_THEME_PROGRESSBAR_CHUNK:
  2630   case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  2631   case NS_THEME_RANGE:
  2632     return eTransparent;
  2635   HANDLE theme = GetTheme(aWidgetType);
  2636   // For the classic theme we don't really have a way of knowing
  2637   if (!theme) {
  2638     // menu backgrounds and tooltips which can't be themed are opaque
  2639     if (aWidgetType == NS_THEME_MENUPOPUP || aWidgetType == NS_THEME_TOOLTIP) {
  2640       return eOpaque;
  2642     return eUnknownTransparency;
  2645   int32_t part, state;
  2646   nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
  2647   // Fail conservatively
  2648   NS_ENSURE_SUCCESS(rv, eUnknownTransparency);
  2650   if (part <= 0) {
  2651     // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
  2652     // not work, so don't call it.
  2653     return eUnknownTransparency;
  2656   if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
  2657     return eTransparent;
  2658   return eOpaque;
  2661 /* Windows 9x/NT/2000/Classic XP Theme Support */
  2663 bool 
  2664 nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
  2665                                       nsIFrame* aFrame,
  2666                                       uint8_t aWidgetType)
  2668   switch (aWidgetType) {
  2669     case NS_THEME_RESIZER:
  2671       // The classic native resizer has an opaque grey background which doesn't
  2672       // match the usually white background of the scrollable container, so
  2673       // only support the native resizer if not in a scrollframe.
  2674       nsIFrame* parentFrame = aFrame->GetParent();
  2675       return (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame);
  2677     case NS_THEME_MENUBAR:
  2678     case NS_THEME_MENUPOPUP:
  2679       // Classic non-flat menus are handled almost entirely through CSS.
  2680       if (!nsUXThemeData::sFlatMenus)
  2681         return false;
  2682     case NS_THEME_BUTTON:
  2683     case NS_THEME_NUMBER_INPUT:
  2684     case NS_THEME_TEXTFIELD:
  2685     case NS_THEME_TEXTFIELD_MULTILINE:
  2686     case NS_THEME_CHECKBOX:
  2687     case NS_THEME_RADIO:
  2688     case NS_THEME_RANGE:
  2689     case NS_THEME_RANGE_THUMB:
  2690     case NS_THEME_GROUPBOX:
  2691     case NS_THEME_SCROLLBAR_BUTTON_UP:
  2692     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  2693     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  2694     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  2695     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  2696     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  2697     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  2698     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  2699     case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
  2700     case NS_THEME_SCALE_HORIZONTAL:
  2701     case NS_THEME_SCALE_VERTICAL:
  2702     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  2703     case NS_THEME_SCALE_THUMB_VERTICAL:
  2704     case NS_THEME_DROPDOWN_BUTTON:
  2705     case NS_THEME_SPINNER_UP_BUTTON:
  2706     case NS_THEME_SPINNER_DOWN_BUTTON:
  2707     case NS_THEME_LISTBOX:
  2708     case NS_THEME_TREEVIEW:
  2709     case NS_THEME_DROPDOWN_TEXTFIELD:
  2710     case NS_THEME_DROPDOWN:
  2711     case NS_THEME_TOOLTIP:
  2712     case NS_THEME_STATUSBAR:
  2713     case NS_THEME_STATUSBAR_PANEL:
  2714     case NS_THEME_STATUSBAR_RESIZER_PANEL:
  2715     case NS_THEME_PROGRESSBAR:
  2716     case NS_THEME_PROGRESSBAR_VERTICAL:
  2717     case NS_THEME_PROGRESSBAR_CHUNK:
  2718     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  2719     case NS_THEME_TAB:
  2720     case NS_THEME_TAB_PANEL:
  2721     case NS_THEME_TAB_PANELS:
  2722     case NS_THEME_MENUITEM:
  2723     case NS_THEME_CHECKMENUITEM:
  2724     case NS_THEME_RADIOMENUITEM:
  2725     case NS_THEME_MENUCHECKBOX:
  2726     case NS_THEME_MENURADIO:
  2727     case NS_THEME_MENUARROW:
  2728     case NS_THEME_MENUSEPARATOR:
  2729     case NS_THEME_MENUITEMTEXT:
  2730     case NS_THEME_WINDOW_TITLEBAR:
  2731     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  2732     case NS_THEME_WINDOW_FRAME_LEFT:
  2733     case NS_THEME_WINDOW_FRAME_RIGHT:
  2734     case NS_THEME_WINDOW_FRAME_BOTTOM:
  2735     case NS_THEME_WINDOW_BUTTON_CLOSE:
  2736     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  2737     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  2738     case NS_THEME_WINDOW_BUTTON_RESTORE:
  2739     case NS_THEME_WINDOW_BUTTON_BOX:
  2740     case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
  2741       return true;
  2743   return false;
  2746 nsresult
  2747 nsNativeThemeWin::ClassicGetWidgetBorder(nsDeviceContext* aContext, 
  2748                                   nsIFrame* aFrame,
  2749                                   uint8_t aWidgetType,
  2750                                   nsIntMargin* aResult)
  2752   switch (aWidgetType) {
  2753     case NS_THEME_GROUPBOX:
  2754     case NS_THEME_BUTTON:
  2755       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2; 
  2756       break;
  2757     case NS_THEME_STATUSBAR:
  2758       (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
  2759       (*aResult).top = 2;
  2760       break;
  2761     case NS_THEME_LISTBOX:
  2762     case NS_THEME_TREEVIEW:
  2763     case NS_THEME_DROPDOWN:
  2764     case NS_THEME_DROPDOWN_TEXTFIELD:
  2765     case NS_THEME_TAB:
  2766     case NS_THEME_NUMBER_INPUT:
  2767     case NS_THEME_TEXTFIELD:
  2768     case NS_THEME_TEXTFIELD_MULTILINE:
  2769       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
  2770       break;
  2771     case NS_THEME_STATUSBAR_PANEL:
  2772     case NS_THEME_STATUSBAR_RESIZER_PANEL: {
  2773       (*aResult).top = 1;      
  2774       (*aResult).left = 1;
  2775       (*aResult).bottom = 1;
  2776       (*aResult).right = aFrame->GetNextSibling() ? 3 : 1;
  2777       break;
  2779     case NS_THEME_TOOLTIP:
  2780       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
  2781       break;
  2782     case NS_THEME_PROGRESSBAR:
  2783     case NS_THEME_PROGRESSBAR_VERTICAL:
  2784       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
  2785       break;
  2786     case NS_THEME_MENUBAR:
  2787       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 0;
  2788       break;
  2789     case NS_THEME_MENUPOPUP:
  2790       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 3;
  2791       break;
  2792     default:
  2793       (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
  2794       break;
  2796   return NS_OK;
  2799 bool
  2800 nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext* aContext,
  2801                                    nsIFrame* aFrame,
  2802                                    uint8_t aWidgetType,
  2803                                    nsIntMargin* aResult)
  2805   switch (aWidgetType) {
  2806     case NS_THEME_MENUITEM:
  2807     case NS_THEME_CHECKMENUITEM:
  2808     case NS_THEME_RADIOMENUITEM: {
  2809       int32_t part, state;
  2810       bool focused;
  2812       if (NS_FAILED(ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused)))
  2813         return false;
  2815       if (part == 1) { // top-level menu
  2816         if (nsUXThemeData::sFlatMenus || !(state & DFCS_PUSHED)) {
  2817           (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
  2819         else {
  2820           // make top-level menus look sunken when pushed in the Classic look
  2821           (*aResult).top = (*aResult).left = 3;
  2822           (*aResult).bottom = (*aResult).right = 1;
  2825       else {
  2826         (*aResult).top = 0;
  2827         (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
  2829       return true;
  2831     case NS_THEME_PROGRESSBAR:
  2832     case NS_THEME_PROGRESSBAR_VERTICAL:
  2833       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
  2834       return true;
  2835     default:
  2836       return false;
  2840 nsresult
  2841 nsNativeThemeWin::ClassicGetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame,
  2842                                        uint8_t aWidgetType,
  2843                                        nsIntSize* aResult, bool* aIsOverridable)
  2845   (*aResult).width = (*aResult).height = 0;
  2846   *aIsOverridable = true;
  2847   switch (aWidgetType) {
  2848     case NS_THEME_RADIO:
  2849     case NS_THEME_CHECKBOX:
  2850       (*aResult).width = (*aResult).height = 13;
  2851       break;
  2852     case NS_THEME_MENUCHECKBOX:
  2853     case NS_THEME_MENURADIO:
  2854     case NS_THEME_MENUARROW:
  2855       (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
  2856       (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
  2857       break;
  2858     case NS_THEME_SPINNER_UP_BUTTON:
  2859     case NS_THEME_SPINNER_DOWN_BUTTON:
  2860       (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
  2861       (*aResult).height = 8; // No good metrics available for this
  2862       *aIsOverridable = false;
  2863       break;
  2864     case NS_THEME_SCROLLBAR_BUTTON_UP:
  2865     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  2866       (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
  2867       (*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
  2868       *aIsOverridable = false;
  2869       break;
  2870     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  2871     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  2872       (*aResult).width = ::GetSystemMetrics(SM_CXHSCROLL);
  2873       (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
  2874       *aIsOverridable = false;
  2875       break;
  2876     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  2877       // XXX HACK We should be able to have a minimum height for the scrollbar
  2878       // track.  However, this causes problems when uncollapsing a scrollbar
  2879       // inside a tree.  See bug 201379 for details.
  2881         //      (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
  2882       break;
  2883     case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
  2885       aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
  2886                       ::GetSystemMetrics(SM_CYVSCROLL));
  2887       break;
  2889     case NS_THEME_RANGE_THUMB: {
  2890       if (IsRangeHorizontal(aFrame)) {
  2891         (*aResult).width = 12;
  2892         (*aResult).height = 20;
  2893       } else {
  2894         (*aResult).width = 20;
  2895         (*aResult).height = 12;
  2897       *aIsOverridable = false;
  2898       break;
  2900     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  2901       (*aResult).width = 12;
  2902       (*aResult).height = 20;
  2903       *aIsOverridable = false;
  2904       break;
  2905     case NS_THEME_SCALE_THUMB_VERTICAL:
  2906       (*aResult).width = 20;
  2907       (*aResult).height = 12;
  2908       *aIsOverridable = false;
  2909       break;
  2910     case NS_THEME_DROPDOWN_BUTTON:
  2911       (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
  2912       break;
  2913     case NS_THEME_DROPDOWN:
  2914     case NS_THEME_BUTTON:
  2915     case NS_THEME_GROUPBOX:
  2916     case NS_THEME_LISTBOX:
  2917     case NS_THEME_TREEVIEW:
  2918     case NS_THEME_NUMBER_INPUT:
  2919     case NS_THEME_TEXTFIELD:
  2920     case NS_THEME_TEXTFIELD_MULTILINE:
  2921     case NS_THEME_DROPDOWN_TEXTFIELD:      
  2922     case NS_THEME_STATUSBAR:
  2923     case NS_THEME_STATUSBAR_PANEL:      
  2924     case NS_THEME_STATUSBAR_RESIZER_PANEL:
  2925     case NS_THEME_PROGRESSBAR_CHUNK:
  2926     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  2927     case NS_THEME_TOOLTIP:
  2928     case NS_THEME_PROGRESSBAR:
  2929     case NS_THEME_PROGRESSBAR_VERTICAL:
  2930     case NS_THEME_TAB:
  2931     case NS_THEME_TAB_PANEL:
  2932     case NS_THEME_TAB_PANELS:
  2933       // no minimum widget size
  2934       break;
  2935     case NS_THEME_RESIZER: {     
  2936       NONCLIENTMETRICS nc;
  2937       nc.cbSize = sizeof(nc);
  2938       if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nc), &nc, 0))
  2939         (*aResult).width = (*aResult).height = abs(nc.lfStatusFont.lfHeight) + 4;
  2940       else
  2941         (*aResult).width = (*aResult).height = 15;
  2942       *aIsOverridable = false;
  2943       break;
  2944     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  2945       (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
  2946       (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB);
  2947       // Without theming, divide the thumb size by two in order to look more
  2948       // native
  2949       if (!GetTheme(aWidgetType))
  2950         (*aResult).height >>= 1;
  2951       *aIsOverridable = false;
  2952       break;
  2953     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  2954       (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB);
  2955       (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
  2956       // Without theming, divide the thumb size by two in order to look more
  2957       // native
  2958       if (!GetTheme(aWidgetType))
  2959         (*aResult).width >>= 1;
  2960       *aIsOverridable = false;
  2961       break;
  2962     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  2963       (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB) << 1;
  2964       break;
  2966     case NS_THEME_MENUSEPARATOR:
  2968       aResult->width = 0;
  2969       aResult->height = 10;
  2970       break;
  2973     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  2974     case NS_THEME_WINDOW_TITLEBAR:
  2975       aResult->height = GetSystemMetrics(SM_CYCAPTION);
  2976       aResult->height += GetSystemMetrics(SM_CYFRAME);
  2977       aResult->width = 0;
  2978     break;
  2979     case NS_THEME_WINDOW_FRAME_LEFT:
  2980     case NS_THEME_WINDOW_FRAME_RIGHT:
  2981       aResult->width = GetSystemMetrics(SM_CXFRAME);
  2982       aResult->height = 0;
  2983     break;
  2985     case NS_THEME_WINDOW_FRAME_BOTTOM:
  2986       aResult->height = GetSystemMetrics(SM_CYFRAME);
  2987       aResult->width = 0;
  2988     break;
  2990     case NS_THEME_WINDOW_BUTTON_CLOSE:
  2991     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  2992     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  2993     case NS_THEME_WINDOW_BUTTON_RESTORE:
  2994       aResult->width = GetSystemMetrics(SM_CXSIZE);
  2995       aResult->height = GetSystemMetrics(SM_CYSIZE);
  2996       // XXX I have no idea why these caption metrics are always off,
  2997       // but they are.
  2998       aResult->width -= 2;
  2999       aResult->height -= 4;
  3000       if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
  3001         AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
  3003       else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
  3004                aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
  3005         AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
  3007       else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
  3008         AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
  3010     break;
  3012     default:
  3013       return NS_ERROR_FAILURE;
  3015   return NS_OK;
  3019 nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
  3020                                  int32_t& aPart, int32_t& aState, bool& aFocused)
  3022   aFocused = false;
  3023   switch (aWidgetType) {
  3024     case NS_THEME_BUTTON: {
  3025       EventStates contentState;
  3027       aPart = DFC_BUTTON;
  3028       aState = DFCS_BUTTONPUSH;
  3029       aFocused = false;
  3031       contentState = GetContentState(aFrame, aWidgetType);
  3032       if (IsDisabled(aFrame, contentState))
  3033         aState |= DFCS_INACTIVE;
  3034       else if (IsOpenButton(aFrame))
  3035         aState |= DFCS_PUSHED;
  3036       else if (IsCheckedButton(aFrame))
  3037         aState |= DFCS_CHECKED;
  3038       else {
  3039         if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
  3040           aState |= DFCS_PUSHED;
  3041           const nsStyleUserInterface *uiData = aFrame->StyleUserInterface();
  3042           // The down state is flat if the button is focusable
  3043           if (uiData->mUserFocus == NS_STYLE_USER_FOCUS_NORMAL) {
  3044             if (!aFrame->GetContent()->IsHTML())
  3045               aState |= DFCS_FLAT;
  3047             aFocused = true;
  3050         if (contentState.HasState(NS_EVENT_STATE_FOCUS) ||
  3051             (aState == DFCS_BUTTONPUSH && IsDefaultButton(aFrame))) {
  3052           aFocused = true;
  3057       return NS_OK;
  3059     case NS_THEME_CHECKBOX:
  3060     case NS_THEME_RADIO: {
  3061       EventStates contentState;
  3062       aFocused = false;
  3064       aPart = DFC_BUTTON;
  3065       aState = 0;
  3066       nsIContent* content = aFrame->GetContent();
  3067       bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
  3068       bool isChecked = GetCheckedOrSelected(aFrame, !isCheckbox);
  3069       bool isIndeterminate = isCheckbox && GetIndeterminate(aFrame);
  3071       if (isCheckbox) {
  3072         // indeterminate state takes precedence over checkedness.
  3073         if (isIndeterminate) {
  3074           aState = DFCS_BUTTON3STATE | DFCS_CHECKED;
  3075         } else {
  3076           aState = DFCS_BUTTONCHECK;
  3078       } else {
  3079         aState = DFCS_BUTTONRADIO;
  3081       if (isChecked) {
  3082         aState |= DFCS_CHECKED;
  3085       contentState = GetContentState(aFrame, aWidgetType);
  3086       if (!content->IsXUL() &&
  3087           contentState.HasState(NS_EVENT_STATE_FOCUS)) {
  3088         aFocused = true;
  3091       if (IsDisabled(aFrame, contentState)) {
  3092         aState |= DFCS_INACTIVE;
  3093       } else if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE |
  3094                                            NS_EVENT_STATE_HOVER)) {
  3095         aState |= DFCS_PUSHED;
  3098       return NS_OK;
  3100     case NS_THEME_MENUITEM:
  3101     case NS_THEME_CHECKMENUITEM:
  3102     case NS_THEME_RADIOMENUITEM: {
  3103       bool isTopLevel = false;
  3104       bool isOpen = false;
  3105       bool isContainer = false;
  3106       nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
  3107       EventStates eventState = GetContentState(aFrame, aWidgetType);
  3109       // We indicate top-level-ness using aPart. 0 is a normal menu item,
  3110       // 1 is a top-level menu item. The state of the item is composed of
  3111       // DFCS_* flags only.
  3112       aPart = 0;
  3113       aState = 0;
  3115       if (menuFrame) {
  3116         // If this is a real menu item, we should check if it is part of the
  3117         // main menu bar or not, and if it is a container, as these affect
  3118         // rendering.
  3119         isTopLevel = menuFrame->IsOnMenuBar();
  3120         isOpen = menuFrame->IsOpen();
  3121         isContainer = menuFrame->IsMenu();
  3124       if (IsDisabled(aFrame, eventState))
  3125         aState |= DFCS_INACTIVE;
  3127       if (isTopLevel) {
  3128         aPart = 1;
  3129         if (isOpen)
  3130           aState |= DFCS_PUSHED;
  3133       if (IsMenuActive(aFrame, aWidgetType))
  3134         aState |= DFCS_HOT;
  3136       return NS_OK;
  3138     case NS_THEME_MENUCHECKBOX:
  3139     case NS_THEME_MENURADIO:
  3140     case NS_THEME_MENUARROW: {
  3141       aState = 0;
  3142       EventStates eventState = GetContentState(aFrame, aWidgetType);
  3144       if (IsDisabled(aFrame, eventState))
  3145         aState |= DFCS_INACTIVE;
  3146       if (IsMenuActive(aFrame, aWidgetType))
  3147         aState |= DFCS_HOT;
  3149       if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO) {
  3150         if (IsCheckedButton(aFrame))
  3151           aState |= DFCS_CHECKED;
  3152       } else if (IsFrameRTL(aFrame)) {
  3153           aState |= DFCS_RTL;
  3155       return NS_OK;
  3157     case NS_THEME_LISTBOX:
  3158     case NS_THEME_TREEVIEW:
  3159     case NS_THEME_NUMBER_INPUT:
  3160     case NS_THEME_TEXTFIELD:
  3161     case NS_THEME_TEXTFIELD_MULTILINE:
  3162     case NS_THEME_DROPDOWN:
  3163     case NS_THEME_DROPDOWN_TEXTFIELD:
  3164     case NS_THEME_RANGE:
  3165     case NS_THEME_RANGE_THUMB:
  3166     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  3167     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:     
  3168     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  3169     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:      
  3170     case NS_THEME_SCALE_HORIZONTAL:
  3171     case NS_THEME_SCALE_VERTICAL:
  3172     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  3173     case NS_THEME_SCALE_THUMB_VERTICAL:
  3174     case NS_THEME_STATUSBAR:
  3175     case NS_THEME_STATUSBAR_PANEL:
  3176     case NS_THEME_STATUSBAR_RESIZER_PANEL:
  3177     case NS_THEME_PROGRESSBAR_CHUNK:
  3178     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  3179     case NS_THEME_TOOLTIP:
  3180     case NS_THEME_PROGRESSBAR:
  3181     case NS_THEME_PROGRESSBAR_VERTICAL:
  3182     case NS_THEME_TAB:
  3183     case NS_THEME_TAB_PANEL:
  3184     case NS_THEME_TAB_PANELS:
  3185     case NS_THEME_MENUBAR:
  3186     case NS_THEME_MENUPOPUP:
  3187     case NS_THEME_GROUPBOX:
  3188       // these don't use DrawFrameControl
  3189       return NS_OK;
  3190     case NS_THEME_DROPDOWN_BUTTON: {
  3192       aPart = DFC_SCROLL;
  3193       aState = DFCS_SCROLLCOMBOBOX;
  3195       nsIFrame* parentFrame = aFrame->GetParent();
  3196       bool isHTML = IsHTMLContent(aFrame);
  3197       bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
  3198       bool isOpen = false;
  3200       // HTML select and XUL menulist dropdown buttons get state from the parent.
  3201       if (isHTML || isMenulist)
  3202         aFrame = parentFrame;
  3204       EventStates eventState = GetContentState(aFrame, aWidgetType);
  3206       if (IsDisabled(aFrame, eventState)) {
  3207         aState |= DFCS_INACTIVE;
  3208         return NS_OK;
  3211       if (isHTML) {
  3212         nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
  3213         isOpen = (ccf && ccf->IsDroppedDown());
  3215       else
  3216         isOpen = IsOpenButton(aFrame);
  3218       // XXX Button should look active until the mouse is released, but
  3219       //     without making it look active when the popup is clicked.
  3220       if (isOpen && (isHTML || isMenulist))
  3221         return NS_OK;
  3223       // Dropdown button active state doesn't need :hover.
  3224       if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
  3225         aState |= DFCS_PUSHED | DFCS_FLAT;
  3227       return NS_OK;
  3229     case NS_THEME_SCROLLBAR_BUTTON_UP:
  3230     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  3231     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  3232     case NS_THEME_SCROLLBAR_BUTTON_RIGHT: {
  3233       EventStates contentState = GetContentState(aFrame, aWidgetType);
  3235       aPart = DFC_SCROLL;
  3236       switch (aWidgetType) {
  3237         case NS_THEME_SCROLLBAR_BUTTON_UP:
  3238           aState = DFCS_SCROLLUP;
  3239           break;
  3240         case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  3241           aState = DFCS_SCROLLDOWN;
  3242           break;
  3243         case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  3244           aState = DFCS_SCROLLLEFT;
  3245           break;
  3246         case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  3247           aState = DFCS_SCROLLRIGHT;
  3248           break;
  3251       if (IsDisabled(aFrame, contentState))
  3252         aState |= DFCS_INACTIVE;
  3253       else {
  3254         if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
  3255           aState |= DFCS_PUSHED | DFCS_FLAT;
  3258       return NS_OK;
  3260     case NS_THEME_SPINNER_UP_BUTTON:
  3261     case NS_THEME_SPINNER_DOWN_BUTTON: {
  3262       EventStates contentState = GetContentState(aFrame, aWidgetType);
  3264       aPart = DFC_SCROLL;
  3265       switch (aWidgetType) {
  3266         case NS_THEME_SPINNER_UP_BUTTON:
  3267           aState = DFCS_SCROLLUP;
  3268           break;
  3269         case NS_THEME_SPINNER_DOWN_BUTTON:
  3270           aState = DFCS_SCROLLDOWN;
  3271           break;
  3274       if (IsDisabled(aFrame, contentState))
  3275         aState |= DFCS_INACTIVE;
  3276       else {
  3277         if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
  3278           aState |= DFCS_PUSHED;
  3281       return NS_OK;    
  3283     case NS_THEME_RESIZER:    
  3284       aPart = DFC_SCROLL;
  3285       aState = (IsFrameRTL(aFrame)) ?
  3286                DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP;
  3287       return NS_OK;
  3288     case NS_THEME_MENUSEPARATOR:
  3289       aPart = 0;
  3290       aState = 0;
  3291       return NS_OK;
  3292     case NS_THEME_WINDOW_TITLEBAR:
  3293       aPart = mozilla::widget::themeconst::WP_CAPTION;
  3294       aState = GetTopLevelWindowActiveState(aFrame);
  3295       return NS_OK;
  3296     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  3297       aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
  3298       aState = GetTopLevelWindowActiveState(aFrame);
  3299       return NS_OK;
  3300     case NS_THEME_WINDOW_FRAME_LEFT:
  3301       aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
  3302       aState = GetTopLevelWindowActiveState(aFrame);
  3303       return NS_OK;
  3304     case NS_THEME_WINDOW_FRAME_RIGHT:
  3305       aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
  3306       aState = GetTopLevelWindowActiveState(aFrame);
  3307       return NS_OK;
  3308     case NS_THEME_WINDOW_FRAME_BOTTOM:
  3309       aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
  3310       aState = GetTopLevelWindowActiveState(aFrame);
  3311       return NS_OK;
  3312     case NS_THEME_WINDOW_BUTTON_CLOSE:
  3313       aPart = DFC_CAPTION;
  3314       aState = DFCS_CAPTIONCLOSE |
  3315                GetClassicWindowFrameButtonState(GetContentState(aFrame,
  3316                                                                 aWidgetType));
  3317       return NS_OK;
  3318     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  3319       aPart = DFC_CAPTION;
  3320       aState = DFCS_CAPTIONMIN |
  3321                GetClassicWindowFrameButtonState(GetContentState(aFrame,
  3322                                                                 aWidgetType));
  3323       return NS_OK;
  3324     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  3325       aPart = DFC_CAPTION;
  3326       aState = DFCS_CAPTIONMAX |
  3327                GetClassicWindowFrameButtonState(GetContentState(aFrame,
  3328                                                                 aWidgetType));
  3329       return NS_OK;
  3330     case NS_THEME_WINDOW_BUTTON_RESTORE:
  3331       aPart = DFC_CAPTION;
  3332       aState = DFCS_CAPTIONRESTORE |
  3333                GetClassicWindowFrameButtonState(GetContentState(aFrame,
  3334                                                                 aWidgetType));
  3335       return NS_OK;
  3337   return NS_ERROR_FAILURE;
  3340 // Draw classic Windows tab
  3341 // (no system API for this, but DrawEdge can draw all the parts of a tab)
  3342 static void DrawTab(HDC hdc, const RECT& R, int32_t aPosition, bool aSelected,
  3343                     bool aDrawLeft, bool aDrawRight)
  3345   int32_t leftFlag, topFlag, rightFlag, lightFlag, shadeFlag;  
  3346   RECT topRect, sideRect, bottomRect, lightRect, shadeRect;
  3347   int32_t selectedOffset, lOffset, rOffset;
  3349   selectedOffset = aSelected ? 1 : 0;
  3350   lOffset = aDrawLeft ? 2 : 0;
  3351   rOffset = aDrawRight ? 2 : 0;
  3353   // Get info for tab orientation/position (Left, Top, Right, Bottom)
  3354   switch (aPosition) {
  3355     case BF_LEFT:
  3356       leftFlag = BF_TOP; topFlag = BF_LEFT;
  3357       rightFlag = BF_BOTTOM;
  3358       lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
  3359       shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
  3361       ::SetRect(&topRect, R.left, R.top+lOffset, R.right, R.bottom-rOffset);
  3362       ::SetRect(&sideRect, R.left+2, R.top, R.right-2+selectedOffset, R.bottom);
  3363       ::SetRect(&bottomRect, R.right-2, R.top, R.right, R.bottom);
  3364       ::SetRect(&lightRect, R.left, R.top, R.left+3, R.top+3);
  3365       ::SetRect(&shadeRect, R.left+1, R.bottom-2, R.left+2, R.bottom-1);
  3366       break;
  3367     case BF_TOP:    
  3368       leftFlag = BF_LEFT; topFlag = BF_TOP;
  3369       rightFlag = BF_RIGHT;
  3370       lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
  3371       shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
  3373       ::SetRect(&topRect, R.left+lOffset, R.top, R.right-rOffset, R.bottom);
  3374       ::SetRect(&sideRect, R.left, R.top+2, R.right, R.bottom-1+selectedOffset);
  3375       ::SetRect(&bottomRect, R.left, R.bottom-1, R.right, R.bottom);
  3376       ::SetRect(&lightRect, R.left, R.top, R.left+3, R.top+3);      
  3377       ::SetRect(&shadeRect, R.right-2, R.top+1, R.right-1, R.top+2);      
  3378       break;
  3379     case BF_RIGHT:    
  3380       leftFlag = BF_TOP; topFlag = BF_RIGHT;
  3381       rightFlag = BF_BOTTOM;
  3382       lightFlag = BF_DIAGONAL_ENDTOPLEFT;
  3383       shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
  3385       ::SetRect(&topRect, R.left, R.top+lOffset, R.right, R.bottom-rOffset);
  3386       ::SetRect(&sideRect, R.left+2-selectedOffset, R.top, R.right-2, R.bottom);
  3387       ::SetRect(&bottomRect, R.left, R.top, R.left+2, R.bottom);
  3388       ::SetRect(&lightRect, R.right-3, R.top, R.right-1, R.top+2);
  3389       ::SetRect(&shadeRect, R.right-2, R.bottom-3, R.right, R.bottom-1);
  3390       break;
  3391     case BF_BOTTOM:    
  3392       leftFlag = BF_LEFT; topFlag = BF_BOTTOM;
  3393       rightFlag = BF_RIGHT;
  3394       lightFlag = BF_DIAGONAL_ENDTOPLEFT;
  3395       shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
  3397       ::SetRect(&topRect, R.left+lOffset, R.top, R.right-rOffset, R.bottom);
  3398       ::SetRect(&sideRect, R.left, R.top+2-selectedOffset, R.right, R.bottom-2);
  3399       ::SetRect(&bottomRect, R.left, R.top, R.right, R.top+2);
  3400       ::SetRect(&lightRect, R.left, R.bottom-3, R.left+2, R.bottom-1);
  3401       ::SetRect(&shadeRect, R.right-2, R.bottom-3, R.right, R.bottom-1);
  3402       break;
  3405   // Background
  3406   ::FillRect(hdc, &R, (HBRUSH) (COLOR_3DFACE+1) );
  3408   // Tab "Top"
  3409   ::DrawEdge(hdc, &topRect, EDGE_RAISED, BF_SOFT | topFlag);
  3411   // Tab "Bottom"
  3412   if (!aSelected)
  3413     ::DrawEdge(hdc, &bottomRect, EDGE_RAISED, BF_SOFT | topFlag);
  3415   // Tab "Sides"
  3416   if (!aDrawLeft)
  3417     leftFlag = 0;
  3418   if (!aDrawRight)
  3419     rightFlag = 0;
  3420   ::DrawEdge(hdc, &sideRect, EDGE_RAISED, BF_SOFT | leftFlag | rightFlag);
  3422   // Tab Diagonal Corners
  3423   if (aDrawLeft)
  3424     ::DrawEdge(hdc, &lightRect, EDGE_RAISED, BF_SOFT | lightFlag);
  3426   if (aDrawRight)
  3427     ::DrawEdge(hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
  3430 static void DrawMenuImage(HDC hdc, const RECT& rc, int32_t aComponent, uint32_t aColor)
  3432   // This procedure creates a memory bitmap to contain the check mark, draws
  3433   // it into the bitmap (it is a mask image), then composes it onto the menu
  3434   // item in appropriate colors.
  3435   HDC hMemoryDC = ::CreateCompatibleDC(hdc);
  3436   if (hMemoryDC) {
  3437     // XXXjgr We should ideally be caching these, but we wont be notified when
  3438     // they change currently, so we can't do so easily. Same for the bitmap.
  3439     int checkW = ::GetSystemMetrics(SM_CXMENUCHECK);
  3440     int checkH = ::GetSystemMetrics(SM_CYMENUCHECK);
  3442     HBITMAP hMonoBitmap = ::CreateBitmap(checkW, checkH, 1, 1, nullptr);
  3443     if (hMonoBitmap) {
  3445       HBITMAP hPrevBitmap = (HBITMAP) ::SelectObject(hMemoryDC, hMonoBitmap);
  3446       if (hPrevBitmap) {
  3448         // XXXjgr This will go pear-shaped if the image is bigger than the
  3449         // provided rect. What should we do?
  3450         RECT imgRect = { 0, 0, checkW, checkH };
  3451         POINT imgPos = {
  3452               rc.left + (rc.right  - rc.left - checkW) / 2,
  3453               rc.top  + (rc.bottom - rc.top  - checkH) / 2
  3454             };
  3456         // XXXzeniko Windows renders these 1px lower than you'd expect
  3457         if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
  3458           imgPos.y++;
  3460         ::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
  3461         COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
  3462         COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
  3463         ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCAND);
  3464         ::SetTextColor(hdc, ::GetSysColor(aColor));
  3465         ::SetBkColor(hdc, 0x00000000);
  3466         ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCPAINT);
  3467         ::SetTextColor(hdc, oldTextCol);
  3468         ::SetBkColor(hdc, oldBackCol);
  3469         ::SelectObject(hMemoryDC, hPrevBitmap);
  3471       ::DeleteObject(hMonoBitmap);
  3473     ::DeleteDC(hMemoryDC);
  3477 void nsNativeThemeWin::DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore, int32_t back,
  3478                                        HBRUSH defaultBack)
  3480   static WORD patBits[8] = {
  3481     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
  3482   };
  3484   HBITMAP patBmp = ::CreateBitmap(8, 8, 1, 1, patBits);
  3485   if (patBmp) {
  3486     HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patBmp);
  3487     if (brush) {        
  3488       COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(fore));
  3489       COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(back));
  3490       POINT vpOrg;
  3492       ::UnrealizeObject(brush);
  3493       ::GetViewportOrgEx(hdc, &vpOrg);
  3494       ::SetBrushOrgEx(hdc, vpOrg.x + rc.left, vpOrg.y + rc.top, nullptr);
  3495       HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
  3496       ::FillRect(hdc, &rc, brush);
  3497       ::SetTextColor(hdc, oldForeColor);
  3498       ::SetBkColor(hdc, oldBackColor);
  3499       ::SelectObject(hdc, oldBrush);
  3500       ::DeleteObject(brush);          
  3502     else
  3503       ::FillRect(hdc, &rc, defaultBack);
  3505     ::DeleteObject(patBmp);
  3509 nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(nsRenderingContext* aContext,
  3510                                   nsIFrame* aFrame,
  3511                                   uint8_t aWidgetType,
  3512                                   const nsRect& aRect,
  3513                                   const nsRect& aDirtyRect)
  3515   int32_t part, state;
  3516   bool focused;
  3517   nsresult rv;
  3518   rv = ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused);
  3519   if (NS_FAILED(rv))
  3520     return rv;
  3522   if (AssumeThemePartAndStateAreTransparent(part, state)) {
  3523     return NS_OK;
  3526   gfxFloat p2a = gfxFloat(aContext->AppUnitsPerDevPixel());
  3527   RECT widgetRect;
  3528   gfxRect tr(aRect.x, aRect.y, aRect.width, aRect.height),
  3529           dr(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
  3531   tr.ScaleInverse(p2a);
  3532   dr.ScaleInverse(p2a);
  3534   nsRefPtr<gfxContext> ctx = aContext->ThebesContext();
  3536   gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
  3538 RENDER_AGAIN:
  3540   HDC hdc = nativeDrawing.BeginNativeDrawing();
  3541   if (!hdc)
  3542     return NS_ERROR_FAILURE;
  3544   nativeDrawing.TransformToNativeRect(tr, widgetRect);
  3546   rv = NS_OK;
  3547   switch (aWidgetType) { 
  3548     // Draw button
  3549     case NS_THEME_BUTTON: {
  3550       if (focused) {
  3551         // draw dark button focus border first
  3552         HBRUSH brush;        
  3553         brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
  3554         if (brush)
  3555           ::FrameRect(hdc, &widgetRect, brush);
  3556         InflateRect(&widgetRect, -1, -1);
  3558       // fall-through...
  3560     // Draw controls supported by DrawFrameControl
  3561     case NS_THEME_CHECKBOX:
  3562     case NS_THEME_RADIO:
  3563     case NS_THEME_SCROLLBAR_BUTTON_UP:
  3564     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  3565     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  3566     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  3567     case NS_THEME_SPINNER_UP_BUTTON:
  3568     case NS_THEME_SPINNER_DOWN_BUTTON:
  3569     case NS_THEME_DROPDOWN_BUTTON:
  3570     case NS_THEME_RESIZER: {
  3571       int32_t oldTA;
  3572       // setup DC to make DrawFrameControl draw correctly
  3573       oldTA = ::SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
  3574       ::DrawFrameControl(hdc, &widgetRect, part, state);
  3575       ::SetTextAlign(hdc, oldTA);
  3577       // Draw focus rectangles for HTML checkboxes and radio buttons
  3578       // XXX it'd be nice to draw these outside of the frame
  3579       if (focused && (aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_RADIO)) {
  3580         // setup DC to make DrawFocusRect draw correctly
  3581         POINT vpOrg;
  3582         ::GetViewportOrgEx(hdc, &vpOrg);
  3583         ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, nullptr);
  3584         int32_t oldColor;
  3585         oldColor = ::SetTextColor(hdc, 0);
  3586         // draw focus rectangle
  3587         ::DrawFocusRect(hdc, &widgetRect);
  3588         ::SetTextColor(hdc, oldColor);
  3590       break;
  3592     // Draw controls with 2px 3D inset border
  3593     case NS_THEME_NUMBER_INPUT:
  3594     case NS_THEME_TEXTFIELD:
  3595     case NS_THEME_TEXTFIELD_MULTILINE:
  3596     case NS_THEME_LISTBOX:
  3597     case NS_THEME_DROPDOWN:
  3598     case NS_THEME_DROPDOWN_TEXTFIELD: {
  3599       // Draw inset edge
  3600       ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  3601       EventStates eventState = GetContentState(aFrame, aWidgetType);
  3603       // Fill in background
  3604       if (IsDisabled(aFrame, eventState) ||
  3605           (aFrame->GetContent()->IsXUL() &&
  3606            IsReadOnly(aFrame)))
  3607         ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
  3608       else
  3609         ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
  3611       break;
  3613     case NS_THEME_TREEVIEW: {
  3614       // Draw inset edge
  3615       ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  3617       // Fill in window color background
  3618       ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
  3620       break;
  3622     // Draw ToolTip background
  3623     case NS_THEME_TOOLTIP:
  3624       ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_WINDOWFRAME));
  3625       InflateRect(&widgetRect, -1, -1);
  3626       ::FillRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_INFOBK));
  3628       break;
  3629     case NS_THEME_GROUPBOX:
  3630       ::DrawEdge(hdc, &widgetRect, EDGE_ETCHED, BF_RECT | BF_ADJUST);
  3631       ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
  3632       break;
  3633     // Draw 3D face background controls
  3634     case NS_THEME_PROGRESSBAR:
  3635     case NS_THEME_PROGRESSBAR_VERTICAL:
  3636       // Draw 3D border
  3637       ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
  3638       InflateRect(&widgetRect, -1, -1);
  3639       // fall through
  3640     case NS_THEME_TAB_PANEL:
  3641     case NS_THEME_STATUSBAR:
  3642     case NS_THEME_STATUSBAR_RESIZER_PANEL: {
  3643       ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
  3645       break;
  3647     // Draw 3D inset statusbar panel
  3648     case NS_THEME_STATUSBAR_PANEL: {
  3649       if (aFrame->GetNextSibling())
  3650         widgetRect.right -= 2; // space between sibling status panels
  3652       ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
  3654       break;
  3656     // Draw scrollbar thumb
  3657     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  3658     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  3659       ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
  3661       break;
  3662     case NS_THEME_RANGE_THUMB:
  3663     case NS_THEME_SCALE_THUMB_VERTICAL:
  3664     case NS_THEME_SCALE_THUMB_HORIZONTAL: {
  3665       EventStates eventState = GetContentState(aFrame, aWidgetType);
  3667       ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
  3668       if (IsDisabled(aFrame, eventState)) {
  3669         DrawCheckedRect(hdc, widgetRect, COLOR_3DFACE, COLOR_3DHILIGHT,
  3670                         (HBRUSH) COLOR_3DHILIGHT);
  3673       break;
  3675     // Draw scrollbar track background
  3676     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  3677     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: {
  3679       // Windows fills in the scrollbar track differently 
  3680       // depending on whether these are equal
  3681       DWORD color3D, colorScrollbar, colorWindow;
  3683       color3D = ::GetSysColor(COLOR_3DFACE);      
  3684       colorWindow = ::GetSysColor(COLOR_WINDOW);
  3685       colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
  3687       if ((color3D != colorScrollbar) && (colorWindow != colorScrollbar))
  3688         // Use solid brush
  3689         ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_SCROLLBAR+1));
  3690       else
  3692         DrawCheckedRect(hdc, widgetRect, COLOR_3DHILIGHT, COLOR_3DFACE,
  3693                         (HBRUSH) COLOR_SCROLLBAR+1);
  3695       // XXX should invert the part of the track being clicked here
  3696       // but the track is never :active
  3698       break;
  3700     // Draw scale track background
  3701     case NS_THEME_RANGE:
  3702     case NS_THEME_SCALE_VERTICAL:
  3703     case NS_THEME_SCALE_HORIZONTAL: { 
  3704       const int32_t trackWidth = 4;
  3705       // When rounding is necessary, we round the position of the track
  3706       // away from the chevron of the thumb to make it look better.
  3707       if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
  3708           (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
  3709         widgetRect.top += (widgetRect.bottom - widgetRect.top - trackWidth) / 2;
  3710         widgetRect.bottom = widgetRect.top + trackWidth;
  3712       else {
  3713         if (!IsFrameRTL(aFrame)) {
  3714           widgetRect.left += (widgetRect.right - widgetRect.left - trackWidth) / 2;
  3715           widgetRect.right = widgetRect.left + trackWidth;
  3716         } else {
  3717           widgetRect.right -= (widgetRect.right - widgetRect.left - trackWidth) / 2;
  3718           widgetRect.left = widgetRect.right - trackWidth;
  3722       ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  3723       ::FillRect(hdc, &widgetRect, (HBRUSH) GetStockObject(GRAY_BRUSH));
  3725       break;
  3727     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  3728       ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
  3729       break;
  3731     case NS_THEME_PROGRESSBAR_CHUNK: {
  3732       nsIFrame* stateFrame = aFrame->GetParent();
  3733       EventStates eventStates = GetContentState(stateFrame, aWidgetType);
  3735       bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates);
  3736       bool vertical = IsVerticalProgress(stateFrame) ||
  3737                       aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL;
  3738       int32_t overlayPart = GetProgressOverlayStyle(vertical);
  3740       nsIContent* content = aFrame->GetContent();
  3741       if (!indeterminate || !content) {
  3742         ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
  3743         break;
  3746       RECT overlayRect =
  3747         CalculateProgressOverlayRect(aFrame, &widgetRect, vertical,
  3748                                      indeterminate, true);
  3750       ::FillRect(hdc, &overlayRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
  3752       if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
  3753         NS_WARNING("unable to animate progress widget!");
  3755       break;
  3758     // Draw Tab
  3759     case NS_THEME_TAB: {
  3760       DrawTab(hdc, widgetRect,
  3761         IsBottomTab(aFrame) ? BF_BOTTOM : BF_TOP, 
  3762         IsSelectedTab(aFrame),
  3763         !IsRightToSelectedTab(aFrame),
  3764         !IsLeftToSelectedTab(aFrame));
  3766       break;
  3768     case NS_THEME_TAB_PANELS:
  3769       ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_SOFT | BF_MIDDLE |
  3770           BF_LEFT | BF_RIGHT | BF_BOTTOM);
  3772       break;
  3773     case NS_THEME_MENUBAR:
  3774       break;
  3775     case NS_THEME_MENUPOPUP:
  3776       NS_ASSERTION(nsUXThemeData::sFlatMenus, "Classic menus are styled entirely through CSS");
  3777       ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
  3778       ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
  3779       break;
  3780     case NS_THEME_MENUITEM:
  3781     case NS_THEME_CHECKMENUITEM:
  3782     case NS_THEME_RADIOMENUITEM:
  3783       // part == 0 for normal items
  3784       // part == 1 for top-level menu items
  3785       if (nsUXThemeData::sFlatMenus) {
  3786         // Not disabled and hot/pushed.
  3787         if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
  3788           ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENUHILIGHT+1));
  3789           ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
  3791       } else {
  3792         if (part == 1) {
  3793           if ((state & DFCS_INACTIVE) == 0) {
  3794             if ((state & DFCS_PUSHED) != 0) {
  3795               ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT);
  3796             } else if ((state & DFCS_HOT) != 0) {
  3797               ::DrawEdge(hdc, &widgetRect, BDR_RAISEDINNER, BF_RECT);
  3800         } else {
  3801           if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
  3802             ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
  3806       break;
  3807     case NS_THEME_MENUCHECKBOX:
  3808     case NS_THEME_MENURADIO:
  3809       if (!(state & DFCS_CHECKED))
  3810         break; // nothin' to do
  3811     case NS_THEME_MENUARROW: {
  3812       uint32_t color = COLOR_MENUTEXT;
  3813       if ((state & DFCS_INACTIVE))
  3814         color = COLOR_GRAYTEXT;
  3815       else if ((state & DFCS_HOT))
  3816         color = COLOR_HIGHLIGHTTEXT;
  3818       if (aWidgetType == NS_THEME_MENUCHECKBOX)
  3819         DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
  3820       else if (aWidgetType == NS_THEME_MENURADIO)
  3821         DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
  3822       else if (aWidgetType == NS_THEME_MENUARROW)
  3823         DrawMenuImage(hdc, widgetRect, 
  3824                       (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
  3825                       color);
  3826       break;
  3828     case NS_THEME_MENUSEPARATOR: {
  3829       // separators are offset by a bit (see menu.css)
  3830       widgetRect.left++;
  3831       widgetRect.right--;
  3833       // This magic number is brought to you by the value in menu.css
  3834       widgetRect.top += 4;
  3835       // Our rectangles are 1 pixel high (see border size in menu.css)
  3836       widgetRect.bottom = widgetRect.top+1;
  3837       ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DSHADOW+1));
  3838       widgetRect.top++;
  3839       widgetRect.bottom++;
  3840       ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DHILIGHT+1));
  3841       break;
  3844     case NS_THEME_WINDOW_TITLEBAR:
  3845     case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
  3847       RECT rect = widgetRect;
  3848       int32_t offset = GetSystemMetrics(SM_CXFRAME);
  3849       rect.bottom -= 1;
  3851       // first fill the area to the color of the window background
  3852       FillRect(hdc, &rect, (HBRUSH)(COLOR_3DFACE+1));
  3854       // inset the caption area so it doesn't overflow.
  3855       rect.top += offset;
  3856       // if enabled, draw a gradient titlebar background, otherwise
  3857       // fill with a solid color.
  3858       BOOL bFlag = TRUE;
  3859       SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &bFlag, 0);
  3860       if (!bFlag) {
  3861         if (state == mozilla::widget::themeconst::FS_ACTIVE)
  3862           FillRect(hdc, &rect, (HBRUSH)(COLOR_ACTIVECAPTION+1));
  3863         else
  3864           FillRect(hdc, &rect, (HBRUSH)(COLOR_INACTIVECAPTION+1));
  3865       } else {
  3866         DWORD startColor, endColor;
  3867         if (state == mozilla::widget::themeconst::FS_ACTIVE) {
  3868           startColor = GetSysColor(COLOR_ACTIVECAPTION);
  3869           endColor = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
  3870         } else {
  3871           startColor = GetSysColor(COLOR_INACTIVECAPTION);
  3872           endColor = GetSysColor(COLOR_GRADIENTINACTIVECAPTION);
  3875         TRIVERTEX vertex[2];
  3876         vertex[0].x     = rect.left;
  3877         vertex[0].y     = rect.top;
  3878         vertex[0].Red   = GetRValue(startColor) << 8;
  3879         vertex[0].Green = GetGValue(startColor) << 8;
  3880         vertex[0].Blue  = GetBValue(startColor) << 8;
  3881         vertex[0].Alpha = 0;
  3883         vertex[1].x     = rect.right;
  3884         vertex[1].y     = rect.bottom; 
  3885         vertex[1].Red   = GetRValue(endColor) << 8;
  3886         vertex[1].Green = GetGValue(endColor) << 8;
  3887         vertex[1].Blue  = GetBValue(endColor) << 8;
  3888         vertex[1].Alpha = 0;
  3890         GRADIENT_RECT gRect;
  3891         gRect.UpperLeft  = 0;
  3892         gRect.LowerRight = 1;
  3893         // available on win2k & up
  3894         GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
  3897       if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
  3898         // frame things up with a top raised border.
  3899         DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_TOP);
  3901       break;
  3904     case NS_THEME_WINDOW_FRAME_LEFT:
  3905       DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_LEFT);
  3906       break;
  3908     case NS_THEME_WINDOW_FRAME_RIGHT:
  3909       DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RIGHT);
  3910       break;
  3912     case NS_THEME_WINDOW_FRAME_BOTTOM:
  3913       DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_BOTTOM);
  3914       break;
  3916     case NS_THEME_WINDOW_BUTTON_CLOSE:
  3917     case NS_THEME_WINDOW_BUTTON_MINIMIZE:
  3918     case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
  3919     case NS_THEME_WINDOW_BUTTON_RESTORE:
  3921       if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
  3922         OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
  3924       else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
  3925                aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
  3926         OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
  3928       else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
  3929         OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
  3931       int32_t oldTA = SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
  3932       DrawFrameControl(hdc, &widgetRect, part, state);
  3933       SetTextAlign(hdc, oldTA);
  3934       break;
  3937     default:
  3938       rv = NS_ERROR_FAILURE;
  3939       break;
  3942   nativeDrawing.EndNativeDrawing();
  3944   if (NS_FAILED(rv))
  3945     return rv;
  3947   if (nativeDrawing.ShouldRenderAgain())
  3948     goto RENDER_AGAIN;
  3950   nativeDrawing.PaintToContext();
  3952   return rv;
  3955 uint32_t
  3956 nsNativeThemeWin::GetWidgetNativeDrawingFlags(uint8_t aWidgetType)
  3958   switch (aWidgetType) {
  3959     case NS_THEME_BUTTON:
  3960     case NS_THEME_NUMBER_INPUT:
  3961     case NS_THEME_TEXTFIELD:
  3962     case NS_THEME_TEXTFIELD_MULTILINE:
  3964     case NS_THEME_DROPDOWN:
  3965     case NS_THEME_DROPDOWN_TEXTFIELD:
  3966       return
  3967         gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
  3968         gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
  3969         gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
  3971     // need to check these others
  3972     case NS_THEME_RANGE:
  3973     case NS_THEME_RANGE_THUMB:
  3974     case NS_THEME_SCROLLBAR_BUTTON_UP:
  3975     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  3976     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  3977     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  3978     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  3979     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  3980     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  3981     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  3982     case NS_THEME_SCALE_HORIZONTAL:
  3983     case NS_THEME_SCALE_VERTICAL:
  3984     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  3985     case NS_THEME_SCALE_THUMB_VERTICAL:
  3986     case NS_THEME_SPINNER_UP_BUTTON:
  3987     case NS_THEME_SPINNER_DOWN_BUTTON:
  3988     case NS_THEME_LISTBOX:
  3989     case NS_THEME_TREEVIEW:
  3990     case NS_THEME_TOOLTIP:
  3991     case NS_THEME_STATUSBAR:
  3992     case NS_THEME_STATUSBAR_PANEL:
  3993     case NS_THEME_STATUSBAR_RESIZER_PANEL:
  3994     case NS_THEME_RESIZER:
  3995     case NS_THEME_PROGRESSBAR:
  3996     case NS_THEME_PROGRESSBAR_VERTICAL:
  3997     case NS_THEME_PROGRESSBAR_CHUNK:
  3998     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  3999     case NS_THEME_TAB:
  4000     case NS_THEME_TAB_PANEL:
  4001     case NS_THEME_TAB_PANELS:
  4002     case NS_THEME_MENUBAR:
  4003     case NS_THEME_MENUPOPUP:
  4004     case NS_THEME_MENUITEM:
  4005       break;
  4007     // the dropdown button /almost/ renders correctly with scaling,
  4008     // except that the graphic in the dropdown button (the downward arrow)
  4009     // doesn't get scaled up.
  4010     case NS_THEME_DROPDOWN_BUTTON:
  4011     // these are definitely no; they're all graphics that don't get scaled up
  4012     case NS_THEME_CHECKBOX:
  4013     case NS_THEME_RADIO:
  4014     case NS_THEME_GROUPBOX:
  4015     case NS_THEME_CHECKMENUITEM:
  4016     case NS_THEME_RADIOMENUITEM:
  4017     case NS_THEME_MENUCHECKBOX:
  4018     case NS_THEME_MENURADIO:
  4019     case NS_THEME_MENUARROW:
  4020       return
  4021         gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
  4022         gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
  4023         gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
  4026   return
  4027     gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
  4028     gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
  4029     gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
  4032 ///////////////////////////////////////////
  4033 // Creation Routine
  4034 ///////////////////////////////////////////
  4036 // from nsWindow.cpp
  4037 extern bool gDisableNativeTheme;
  4039 nsresult NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult)
  4041   if (gDisableNativeTheme)
  4042     return NS_ERROR_NO_INTERFACE;
  4044   if (aOuter)
  4045     return NS_ERROR_NO_AGGREGATION;
  4047   nsNativeThemeWin* theme = new nsNativeThemeWin();
  4048   if (!theme)
  4049     return NS_ERROR_OUT_OF_MEMORY;
  4050   return theme->QueryInterface(aIID, aResult);

mercurial