Bug 337771 - Native -moz-appearance work for menus and toolbars on Windows XP. Patch by Simon Bünzli <zeniko@gmail.com>. r=emaijala, sr=bzbarsky.
authorkherron@fmailbox.com
Sat, 14 Jul 2007 08:11:37 -0700
changeset 3477 f6254cdb0f63d9f65ffdef8aa7e258d7ca2ba91c
parent 3476 0f5b3289a6dda1b7a4d2a7ddebe80f382bd8e991
child 3478 731d2f5f4fc329a471e5eb657aad5cedb6799d69
push idunknown
push userunknown
push dateunknown
reviewersemaijala, bzbarsky
bugs337771
milestone1.9a7pre
Bug 337771 - Native -moz-appearance work for menus and toolbars on Windows XP. Patch by Simon Bünzli <zeniko@gmail.com>. r=emaijala, sr=bzbarsky.
gfx/public/nsThemeConstants.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
toolkit/themes/winstripe/global/menu.css
toolkit/themes/winstripe/global/popup.css
toolkit/themes/winstripe/global/toolbar.css
widget/src/windows/nsNativeThemeWin.cpp
--- a/gfx/public/nsThemeConstants.h
+++ b/gfx/public/nsThemeConstants.h
@@ -205,8 +205,14 @@
 // Menu Bar background
 #define NS_THEME_MENUBAR                                   210
 // Menu Popup background
 #define NS_THEME_MENUPOPUP                                 211
 // <menu> and <menuitem> appearances
 #define NS_THEME_MENUITEM                                  212
 #define NS_THEME_CHECKMENUITEM                             213
 #define NS_THEME_RADIOMENUITEM                             214
+
+// menu checkbox/radio appearances
+#define NS_THEME_MENUCHECKBOX                              215
+#define NS_THEME_MENURADIO                                 216
+#define NS_THEME_MENUSEPARATOR                             217
+#define NS_THEME_MENUARROW                                 218
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -519,16 +519,20 @@ CSS_KEY(scrollbargripper-vertical, scrol
 CSS_KEY(textfield, textfield)
 CSS_KEY(textfield-multiline, textfield_multiline)
 CSS_KEY(caret, caret)
 CSS_KEY(menubar, menubar)
 CSS_KEY(menupopup, menupopup)
 CSS_KEY(menuitem, menuitem)
 CSS_KEY(checkmenuitem, checkmenuitem)
 CSS_KEY(radiomenuitem, radiomenuitem)
+CSS_KEY(menucheckbox, menucheckbox)
+CSS_KEY(menuradio, menuradio)
+CSS_KEY(menuseparator, menuseparator)
+CSS_KEY(menuarrow, menuarrow)
 CSS_KEY(menulist, menulist)
 CSS_KEY(menulist-button, menulistbutton)
 CSS_KEY(menulist-text, menulisttext)
 CSS_KEY(menulist-textfield, menulisttextfield)
 CSS_KEY(scale-horizontal, scale_horizontal)
 CSS_KEY(scale-vertical, scale_vertical)
 CSS_KEY(scalethumb-horizontal, scalethumb_horizontal)
 CSS_KEY(scalethumb-vertical, scalethumb_vertical)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -250,16 +250,20 @@ const PRInt32 nsCSSProps::kAppearanceKTa
   eCSSKeyword_buttonfocus,            NS_THEME_BUTTON_FOCUS,
   eCSSKeyword_window,                 NS_THEME_WINDOW,
   eCSSKeyword_dialog,                 NS_THEME_DIALOG,
   eCSSKeyword_menubar,                NS_THEME_MENUBAR,
   eCSSKeyword_menupopup,              NS_THEME_MENUPOPUP,
   eCSSKeyword_menuitem,               NS_THEME_MENUITEM,
   eCSSKeyword_checkmenuitem,          NS_THEME_CHECKMENUITEM,
   eCSSKeyword_radiomenuitem,          NS_THEME_RADIOMENUITEM,
+  eCSSKeyword_menucheckbox,           NS_THEME_MENUCHECKBOX,
+  eCSSKeyword_menuradio,              NS_THEME_MENURADIO,
+  eCSSKeyword_menuseparator,          NS_THEME_MENUSEPARATOR,
+  eCSSKeyword_menuarrow,              NS_THEME_MENUARROW,
   eCSSKeyword_UNKNOWN,-1
 };
 
 // Keyword id tables for variant/enum parsing
 const PRInt32 nsCSSProps::kAzimuthKTable[] = {
   eCSSKeyword_left_side,    NS_STYLE_AZIMUTH_LEFT_SIDE,
   eCSSKeyword_far_left,     NS_STYLE_AZIMUTH_FAR_LEFT,
   eCSSKeyword_left,         NS_STYLE_AZIMUTH_LEFT,
--- a/toolkit/themes/winstripe/global/menu.css
+++ b/toolkit/themes/winstripe/global/menu.css
@@ -41,18 +41,18 @@
   ======================================================================= */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* ::::: menu/menuitem ::::: */
 
 menu,
 menuitem {
+  -moz-appearance: menuitem;
   -moz-box-align: center;
-  border: 1px solid transparent;
   color: MenuText;
   font: menu;
   list-style-image: none;
   -moz-image-region: auto;
 }
 
 menuitem[default="true"] {
   font-weight: bold;
@@ -71,17 +71,16 @@ menuitem.spell-suggestion {
 /* ..... internal content .... */
 
 .menu-accel,
 .menu-iconic-accel,
 .menu-text,
 .menu-iconic-text {
   margin: 0px !important;
   padding: 0px;
-  padding-bottom: 2px;
   color: inherit;
 }
 
 .menu-text {
   -moz-margin-start: 1.28em !important;
 }
 
 .menu-text,
@@ -102,66 +101,43 @@ menuitem.spell-suggestion {
   color: inherit;
   -moz-margin-start: 0.74em !important;
   -moz-margin-end: 1.35em !important;
 }
 
 .menu-iconic-left,
 .menu-right {
   min-width:  1.28em;
-  min-height: 1.28em;
+  min-height: 1.21em;
+}
+
+menuitem.menuitem-iconic > .menu-iconic-left {
+  /* there's only space for 15px - make room for one more */
+  -moz-margin-start: -1px;
 }
 
 /* ..... menu arrow box ..... */
 
 .menu-right {
-  background-position: center center;
-  background-repeat: no-repeat;
-  background-image: url("chrome://global/skin/menu/Menu-arrow.png");
+  -moz-appearance: menuarrow;
+  -moz-margin-end: -2px;
   list-style-image: none;
 }
 
-.menu-right[_moz-menuactive="true"] {
-  background-image: url("chrome://global/skin/menu/Menu-arrow-hover.png");
-}
-
-.menu-right[disabled="true"] {
-  background-image: url("chrome://global/skin/menu/Menu-arrow-disabled.png") !important;
-}
-
-.menu-right[chromedir="rtl"] {
-  background-image: url("chrome://global/skin/menu/Menu-arrow-rtl.png");
-}
-
-.menu-right[chromedir="rtl"][_moz-menuactive="true"] {
-  background-image: url("chrome://global/skin/menu/Menu-arrow-hover-rtl.png");
-}
-
-.menu-right[chromedir="rtl"][disabled="true"] {
-  background-image: url("chrome://global/skin/menu/Menu-arrow-disabled-rtl.png") !important;
-}
-
 /* ::::: menu/menuitems in menubar ::::: */
 
 menubar > menu {
-  border: 1px solid transparent;
-  padding: 1px;
-}
-menubar > menu[_moz-menuactive="true"] {
-  border-top-color   : ThreeDHighlight;
-  border-left-color  : ThreeDHighlight;
-  border-right-color : ThreeDShadow;
-  border-bottom-color: ThreeDShadow;
+  border: 2px solid transparent;
 }
 menubar > menu[_moz-menuactive="true"][open="true"] {
-  border-top-color   : ThreeDShadow;
-  border-left-color  : ThreeDShadow;
-  border-right-color : ThreeDHighlight;
-  border-bottom-color: ThreeDHighlight;
-  padding: 2px 0px 0px 2px;
+  border-width: 3px 1px 1px 3px;
+}
+menubar > menu[_moz-menuactive="true"],
+menubar > menu[_moz-menuactive="true"][open="true"] {
+  color: -moz-menubarhovertext;
 }
 
 /* ..... internal content .... */
 
 .menubar-left {
   color: inherit;
 }
 
@@ -178,18 +154,18 @@ menupopup > menuitem,
 popup > menuitem {
   max-width: 42em;
 }
 
 menupopup > menu[_moz-menuactive="true"],
 menupopup > menuitem[_moz-menuactive="true"],
 popup > menu[_moz-menuactive="true"],
 popup > menuitem[_moz-menuactive="true"] {
-  background-color: Highlight;
-  color: HighlightText;
+  background-color: -moz-menuhover;
+  color: -moz-menuhovertext;
 }
 
 /* ::::: menu/menuitems in menulist popups ::::: */
 
 .menulist-menupopup > menuitem,
 menulist > menupopup > menuitem,
 .menulist-menupopup > menu,
 menulist > menupopup > menu {
@@ -213,48 +189,32 @@ menulist > menupopup > menu > .menu-icon
 menulist > menupopup > menuitem[_moz-menuactive="true"] {
   border: 1px dotted #F5DB95;
 }
 
 menulist > menupopup > menuitem > .menu-iconic-text {
   margin: 0 !important;
 }
 
-/* ::::: checkbox menuitem ::::: */
+/* ::::: checkbox and radio menuitems ::::: */
 
+menuitem[type="checkbox"],
 menuitem[checked="true"] {
-  list-style-image: url("chrome://global/skin/menu/menu-check.gif");
-  -moz-image-region: auto;
+  -moz-appearance: checkmenuitem;
 }
-
-menuitem[checked="true"][disabled="true"] {
-  list-style-image: url("chrome://global/skin/menu/menu-check-disabled.gif");
-  -moz-image-region: auto;
+menuitem[type="checkbox"] > .menu-iconic-left,
+menuitem[checked="true"] > .menu-iconic-left {
+  -moz-appearance: menucheckbox;
 }
 
-menuitem[checked="true"][_moz-menuactive="true"] {
-  list-style-image: url("chrome://global/skin/menu/menu-check-hover.gif");
-  -moz-image-region: auto;
+menuitem[type="radio"] {
+  -moz-appearance: radiomenuitem;
 }
-
-/* ::::: radio menuitem ::::: */
-
-menuitem[checked="true"][type="radio"] {
-  list-style-image: url("chrome://global/skin/menu/menu-radio.gif");
-  -moz-image-region: auto;
-}
-
-menuitem[checked="true"][type="radio"][disabled="true"] {
-  list-style-image: url("chrome://global/skin/menu/menu-radio-disabled.gif");
-  -moz-image-region: auto;
-}
-
-menuitem[checked="true"][type="radio"][_moz-menuactive="true"] {
-  list-style-image: url("chrome://global/skin/menu/menu-radio-hover.gif");
-  -moz-image-region: auto;
+menuitem[type="radio"] > .menu-iconic-left {
+  -moz-appearance: menuradio;
 }
 
 /* ::::: menuseparator ::::: */
 
 menuseparator {
   margin: 3px 1px 4px 1px;
   border-top: 1px solid ThreeDShadow;
   border-bottom: 1px solid ThreeDHighlight;
--- a/toolkit/themes/winstripe/global/popup.css
+++ b/toolkit/themes/winstripe/global/popup.css
@@ -42,22 +42,23 @@
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* ::::: menupopup ::::: */
 
 menupopup,
 popup,
 panel {
-  border: 2px solid transparent;
-  -moz-border-top-colors   : ThreeDLightShadow ThreeDHighlight;
-  -moz-border-left-colors  : ThreeDLightShadow ThreeDHighlight;
-  -moz-border-right-colors : ThreeDDarkShadow  ThreeDShadow;
-  -moz-border-bottom-colors: ThreeDDarkShadow  ThreeDShadow;
-  padding: 1px;
+  -moz-appearance: menupopup;
+  border: 3px solid transparent;
+  -moz-border-top-colors   : ThreeDLightShadow ThreeDHighlight ThreeDFace;
+  -moz-border-left-colors  : ThreeDLightShadow ThreeDHighlight ThreeDFace;
+  -moz-border-right-colors : ThreeDDarkShadow  ThreeDShadow    ThreeDFace;
+  -moz-border-bottom-colors: ThreeDDarkShadow  ThreeDShadow    ThreeDFace;
+  padding: 0px;
   min-width: 1px;
   background: Menu;
 }
 
 menupopup > menu > menupopup,
 popup > menu > menupopup {
  /* align submenus */
   -moz-margin-start: -3px;
--- a/toolkit/themes/winstripe/global/toolbar.css
+++ b/toolkit/themes/winstripe/global/toolbar.css
@@ -48,16 +48,20 @@ toolbox {
   -moz-appearance: toolbox;
   background-color: -moz-Dialog;
   border-top: 2px solid;
   -moz-border-top-colors: ThreeDShadow ThreeDHighlight;
 }
 
 /* ::::: toolbar & menubar ::::: */
 
+toolbar, menubar {
+  -moz-appearance: toolbar;
+}
+
 toolbar {
   min-width: 1px;
   min-height: 19px;
   border-top: 1px solid ThreeDHighlight;
   border-bottom: 1px solid ThreeDShadow;
 }
 
 toolbar:first-child, menubar {
--- a/widget/src/windows/nsNativeThemeWin.cpp
+++ b/widget/src/windows/nsNativeThemeWin.cpp
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * David Hyatt (hyatt@netscape.com).
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Tim Hill (tim@prismelite.com)
  *   James Ross (silver@warwickcompsoc.co.uk)
+ *   Simon Bünzli (zeniko@gmail.com)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -132,16 +133,19 @@
 #define TABP_PANEL           10
 
 // Tooltip constants
 #define TTP_STANDARD         1
 
 // Dropdown constants
 #define CBP_DROPMARKER       1
 
+// Rebar constants
+#define RP_BAND              3
+
 // Constants only found in new (98+, 2K+, XP+, etc.) Windows.
 #ifdef DFCS_HOT
 #undef DFCS_HOT
 #endif
 #define DFCS_HOT             0x00001000
 
 #ifdef COLOR_MENUHILIGHT
 #undef COLOR_MENUHILIGHT
@@ -150,29 +154,31 @@
 
 #ifdef SPI_GETFLATMENU
 #undef SPI_GETFLATMENU
 #endif
 #define SPI_GETFLATMENU      0x1022
 
 // Our extra constants for passing a little bit more info to the renderer.
 #define DFCS_RTL             0x00010000
-#define DFCS_CONTAINER       0x00020000
+
+// Toolbar separator dimension which can't be gotten from Windows
+#define TB_SEPARATOR_HEIGHT  2
 
 NS_IMPL_ISUPPORTS1(nsNativeThemeWin, nsITheme)
 
 typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
 typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
 typedef HRESULT (WINAPI*DrawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId, 
                                           int iStateId, const RECT *pRect,
                                           const RECT* pClipRect);
 typedef HRESULT (WINAPI*DrawThemeEdgePtr)(HANDLE hTheme, HDC hdc, int iPartId, 
-                                          int iStateId, const RECT *pRect,
+                                          int iStateId, const RECT *pDestRect,
                                           uint uEdge, uint uFlags,
-                                          const RECT* pClipRect);
+                                          const RECT* pContentRect);
 typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
                                           int iStateId, const RECT* pRect,
                                           RECT* pContentRect);
 typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
                                        int iStateId, RECT* prc, int ts,
                                        SIZE* psz);
 typedef HRESULT (WINAPI*GetThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont);
 typedef HRESULT (WINAPI*GetThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId,
@@ -693,16 +699,17 @@ nsNativeThemeWin::GetThemePartAndState(n
       // Use -1 to indicate we don't wish to have the theme background drawn
       // for this item. We will pass any nessessary information via aState,
       // and will render the item using separate code.
       aPart = -1;
       aState = 0;
       if (aFrame) {
         nsIContent* content = aFrame->GetContent();
         nsIContent* parent = content->GetParent();
+        // XXXzeniko hiding the first toolbar will result in an unwanted margin
         if (parent && parent->GetChildAt(0) == content) {
           aState = 1;
         }
       }
       return NS_OK;
     }
     case NS_THEME_STATUSBAR_PANEL:
     case NS_THEME_STATUSBAR_RESIZER_PANEL:
@@ -887,23 +894,16 @@ RENDER_AGAIN:
     // effectively shifting the edge out of view (such that it won't be painted).
     if (aWidgetType == NS_THEME_TAB_LEFT_EDGE)
       // The right edge should not be drawn.  Extend our rect by the edge size.
       widgetRect.right += edgeSize;
     else
       // The left edge should not be drawn.  Move the widget rect's left coord back.
       widgetRect.left -= edgeSize;
   }
-  else if (aWidgetType == NS_THEME_TOOLBOX) {
-    // The toolbox's toolbar elements will show a 1px border top and bottom.
-    // We want the toolbox's background to end right up against the bottom
-    // border of the last toolbar, so we simply make it leave a 1px gap at the
-    // bottom. This gap will get the bottom border of the last toolbar in it.
-    widgetRect.bottom -= 1;
-  }
 
   // widgetRect is the bounding box for a widget, yet the scale track is only
   // a small portion of this size, so the edges of the scale need to be
   // adjusted to the real size of the track.
   if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
       aWidgetType == NS_THEME_SCALE_VERTICAL) {
     RECT contentRect;
     getThemeContentRect(theme, hdc, part, state, &widgetRect, &contentRect);
@@ -948,24 +948,25 @@ RENDER_AGAIN:
         ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, NULL);
         PRInt32 oldColor;
         oldColor = ::SetTextColor(hdc, 0);
         // draw focus rectangle
         ::DrawFocusRect(hdc, &widgetRect);
         ::SetTextColor(hdc, oldColor);
       }
   }
-  else if (aWidgetType == NS_THEME_TOOLBAR) {
-    // state == 1 iff this toolbar is the first inside the toolbox, which
-    // means we should omit the top border for correct rendering.
-    if (state == 1) {
-      drawThemeEdge(theme, hdc, 0, 0, &widgetRect, BDR_RAISEDINNER, BF_BOTTOM, &clipRect);
-    } else {
-      drawThemeEdge(theme, hdc, 0, 0, &widgetRect, BDR_RAISEDINNER, BF_TOP | BF_BOTTOM, &clipRect);
-    }
+  else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
+    // Draw toolbar separator lines above all toolbars except the first one.
+    // The lines are part of the Rebar theme, which is loaded for NS_THEME_TOOLBOX.
+    theme = GetTheme(NS_THEME_TOOLBOX);
+    if (!theme)
+      return NS_ERROR_FAILURE;
+
+    widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
+    drawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, NULL);
   }
 
   nativeDrawing.EndNativeDrawing();
 
   if (nativeDrawing.ShouldRenderAgain())
     goto RENDER_AGAIN;
 
   nativeDrawing.PaintToContext();
@@ -988,39 +989,31 @@ nsNativeThemeWin::GetWidgetBorder(nsIDev
   if (!WidgetIsContainer(aWidgetType) ||
       aWidgetType == NS_THEME_TOOLBOX || 
       aWidgetType == NS_THEME_STATUSBAR || 
       aWidgetType == NS_THEME_RESIZER || aWidgetType == NS_THEME_TAB_PANEL ||
       aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL ||
       aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL)
     return NS_OK; // Don't worry about it.
 
-  if (aWidgetType == NS_THEME_TOOLBAR) {
-    // A normal toolbar has a 1px border above and below it, with 2px of
-    // space either size. If it is the first toolbar, no top border is needed.
-    aResult->top = aResult->bottom = 1;
-    aResult->left = 2;
-    if (aFrame) {
-      nsIContent* content = aFrame->GetContent();
-      nsIContent* parent = content->GetParent();
-      if (parent && parent->GetChildAt(0) == content) {
-        aResult->top = 0;
-      }
-    }
-    return NS_OK;
-  }
-
   if (!getThemeContentRect)
     return NS_ERROR_FAILURE;
 
   PRInt32 part, state;
   nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
   if (NS_FAILED(rv))
     return rv;
 
+  if (aWidgetType == NS_THEME_TOOLBAR) {
+    // make space for the separator line above all toolbars but the first
+    if (state == 0)
+      aResult->top = TB_SEPARATOR_HEIGHT;
+    return NS_OK;
+  }
+
   // Get our info.
   RECT outerRect; // Create a fake outer rect.
   outerRect.top = outerRect.left = 100;
   outerRect.right = outerRect.bottom = 200;
   RECT contentRect(outerRect);
   HRESULT res = getThemeContentRect(theme, NULL, part, state, &outerRect, &contentRect);
   
   if (FAILED(res))
@@ -1303,16 +1296,21 @@ nsNativeThemeWin::ThemeNeedsComboboxDrop
 /* Windows 9x/NT/2000/Classic XP Theme Support */
 
 PRBool 
 nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
                                       nsIFrame* aFrame,
                                       PRUint8 aWidgetType)
 {
   switch (aWidgetType) {
+    case NS_THEME_MENUBAR:
+    case NS_THEME_MENUPOPUP:
+      // Classic non-flat menus are handled almost entirely through CSS.
+      if (!mFlatMenus)
+        return PR_FALSE;
     case NS_THEME_BUTTON:
     case NS_THEME_TEXTFIELD:
     case NS_THEME_TEXTFIELD_MULTILINE:
     case NS_THEME_CHECKBOX:
     case NS_THEME_CHECKBOX_SMALL:
     case NS_THEME_RADIO:
     case NS_THEME_RADIO_SMALL:
     case NS_THEME_SCROLLBAR_BUTTON_UP:
@@ -1343,21 +1341,22 @@ nsNativeThemeWin::ClassicThemeSupportsWi
     case NS_THEME_PROGRESSBAR_VERTICAL:
     case NS_THEME_PROGRESSBAR_CHUNK:
     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
     case NS_THEME_TAB:
     case NS_THEME_TAB_LEFT_EDGE:
     case NS_THEME_TAB_RIGHT_EDGE:
     case NS_THEME_TAB_PANEL:
     case NS_THEME_TAB_PANELS:
-    case NS_THEME_MENUBAR:
-    case NS_THEME_MENUPOPUP:
     case NS_THEME_MENUITEM:
     case NS_THEME_CHECKMENUITEM:
     case NS_THEME_RADIOMENUITEM:
+    case NS_THEME_MENUCHECKBOX:
+    case NS_THEME_MENURADIO:
+    case NS_THEME_MENUARROW:
       return PR_TRUE;
   }
   return PR_FALSE;
 }
 
 nsresult
 nsNativeThemeWin::ClassicGetWidgetBorder(nsIDeviceContext* aContext, 
                                   nsIFrame* aFrame,
@@ -1405,40 +1404,43 @@ nsNativeThemeWin::ClassicGetWidgetBorder
     case NS_THEME_PROGRESSBAR:
     case NS_THEME_PROGRESSBAR_VERTICAL:
       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
       break;
     case NS_THEME_MENUBAR:
       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 0;
       break;
     case NS_THEME_MENUPOPUP:
-      (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
+      (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 3;
       break;
     case NS_THEME_MENUITEM:
     case NS_THEME_CHECKMENUITEM:
     case NS_THEME_RADIOMENUITEM: {
-      PRBool isTopLevel = PR_FALSE;
-      nsIMenuFrame *menuFrame = nsnull;
-      CallQueryInterface(aFrame, &menuFrame);
+      PRInt32 part, state;
+      PRBool focused;
+      nsresult rv;
 
-      if (menuFrame) {
-        // If this is a real menu item, we should check if it is part of the
-        // main menu bar or not, as this affects rendering.
-        isTopLevel = menuFrame->IsOnMenuBar();
-      }
+      rv = ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused);
+      if (NS_FAILED(rv))
+        return rv;
 
-      // These values are obtained from visual inspection of equivelant
-      // native components.
-      if (isTopLevel) {
-        (*aResult).top = (*aResult).bottom = 1;
-        (*aResult).left = 3;
-        (*aResult).right = 4;
-      } else {
-        (*aResult).top = 0;
-        (*aResult).bottom = (*aResult).left = (*aResult).right = 1;
+      if (part == 1) { // top level menu
+        if (mFlatMenus || !(state & DFCS_PUSHED)) {
+          (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
+        }
+        else {
+          // make top-level menus look sunken when pushed in the Classic look
+          (*aResult).top = (*aResult).left = 3;
+          (*aResult).bottom = (*aResult).right = 1;
+        }
+      }
+      else {
+        (*aResult).top = 1;
+        (*aResult).bottom = 3;
+        (*aResult).left = (*aResult).right = 2;
       }
       break;
     }
     default:
       (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
       break;
   }
   return NS_OK;
@@ -1453,16 +1455,22 @@ nsNativeThemeWin::ClassicGetMinimumWidge
   *aIsOverridable = PR_TRUE;
   switch (aWidgetType) {
     case NS_THEME_RADIO:
     case NS_THEME_RADIO_SMALL:
     case NS_THEME_CHECKBOX:
     case NS_THEME_CHECKBOX_SMALL:
       (*aResult).width = (*aResult).height = 13;
       break;
+    case NS_THEME_MENUCHECKBOX:
+    case NS_THEME_MENURADIO:
+    case NS_THEME_MENUARROW:
+      (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
+      (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
+      break;
     case NS_THEME_SCROLLBAR_BUTTON_UP:
     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
       (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
       (*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
       *aIsOverridable = PR_FALSE;
       break;
     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
@@ -1649,35 +1657,39 @@ nsresult nsNativeThemeWin::ClassicGetThe
 
       if (IsDisabled(aFrame))
         aState |= DFCS_INACTIVE;
 
       if (isTopLevel) {
         aPart = 1;
         if (isOpen)
           aState |= DFCS_PUSHED;
-      } else {
-        if (isContainer)
-          aState |= DFCS_CONTAINER;
-        if (aFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
-          aState |= DFCS_RTL;
       }
 
       if (CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive))
         aState |= DFCS_HOT;
 
-      // Only menu items of the appropriate type may have tick or bullet marks.
-      if (aWidgetType == NS_THEME_CHECKMENUITEM ||
-          aWidgetType == NS_THEME_RADIOMENUITEM) {
+      return NS_OK;
+    }
+    case NS_THEME_MENUCHECKBOX:
+    case NS_THEME_MENURADIO:
+    case NS_THEME_MENUARROW:
+      aState = 0;
+      if (IsDisabled(aFrame))
+        aState |= DFCS_INACTIVE;
+      if (CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive))
+        aState |= DFCS_HOT;
+      if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO) {
         if (IsCheckedButton(aFrame))
           aState |= DFCS_CHECKED;
+      } else {
+        if (aFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
+          aState |= DFCS_RTL;
       }
-
       return NS_OK;
-    }
     case NS_THEME_LISTBOX:
     case NS_THEME_TREEVIEW:
     case NS_THEME_TEXTFIELD:
     case NS_THEME_TEXTFIELD_MULTILINE:
     case NS_THEME_DROPDOWN:
     case NS_THEME_DROPDOWN_TEXTFIELD:
     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:     
@@ -1908,16 +1920,20 @@ static void DrawMenuImage(HDC hdc, const
         // XXXjgr This will go pear-shaped if the image is bigger than the
         // provided rect. What should we do?
         RECT imgRect = { 0, 0, checkW, checkH };
         POINT imgPos = {
               rc.left + (rc.right  - rc.left - checkW) / 2,
               rc.top  + (rc.bottom - rc.top  - checkH) / 2
             };
 
+        // XXXzeniko Windows renders these 1px lower than you'd expect
+        if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
+          imgPos.y++;
+
         ::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
         COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
         COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
         ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCAND);
         ::SetTextColor(hdc, ::GetSysColor(aColor));
         ::SetBkColor(hdc, 0x00000000);
         ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCPAINT);
         ::SetTextColor(hdc, oldTextCol);
@@ -2183,26 +2199,23 @@ RENDER_AGAIN:
     case NS_THEME_TAB_PANELS:
       ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_SOFT | BF_MIDDLE |
           BF_LEFT | BF_RIGHT | BF_BOTTOM);
 
       break;
     case NS_THEME_MENUBAR:
       break;
     case NS_THEME_MENUPOPUP:
-      if (mFlatMenus) {
-        ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
-        ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
-      } else {
-        ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
-      }
+      NS_ASSERTION(mFlatMenus, "Classic menus are styled entirely through CSS");
+      ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
+      ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
       break;
     case NS_THEME_MENUITEM:
     case NS_THEME_CHECKMENUITEM:
-    case NS_THEME_RADIOMENUITEM: {
+    case NS_THEME_RADIOMENUITEM:
       // part == 0 for normal items
       // part == 1 for top-level menu items
       if (mFlatMenus) {
         // Not disabled and hot/pushed.
         if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
           ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENUHILIGHT+1));
           ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
         }
@@ -2216,56 +2229,40 @@ RENDER_AGAIN:
             }
           }
         } else {
           if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
             ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
           }
         }
       }
-      if (((state & DFCS_CHECKED) != 0) || ((state & DFCS_CONTAINER) != 0)) {
-        RECT menuRectStart, menuRectEnd;
-        PRUint32 color = COLOR_MENUTEXT;
-
-        if ((state & DFCS_INACTIVE) != 0)
-          color = COLOR_GRAYTEXT;
-        else if ((state & DFCS_HOT) != 0)
-          color = COLOR_HIGHLIGHTTEXT;
-
-        ::CopyRect(&menuRectStart, &widgetRect);
-        ::InflateRect(&menuRectStart, -1, -1);
-        ::CopyRect(&menuRectEnd, &menuRectStart);
-
-        // WARNING: This value of 15 must match the value in menu.css for the min-width of .menu-iconic-left
-        if ((state & DFCS_RTL) == 0) {
-          menuRectStart.right = menuRectStart.left  + 15;  // Left box
-          menuRectEnd.left    = menuRectEnd.right   - 15;  // Right box
-        } else {
-          menuRectStart.left  = menuRectStart.right - 15;  // Right box
-          menuRectEnd.right   = menuRectEnd.left    + 15;  // left box
-        }
-
+      break;
 #ifndef WINCE
-        if ((state & DFCS_CHECKED) != 0) {
-          if (aWidgetType == NS_THEME_CHECKMENUITEM) {
-            DrawMenuImage(hdc, menuRectStart, DFCS_MENUCHECK, color);
-          } else if (aWidgetType == NS_THEME_RADIOMENUITEM) {
-            DrawMenuImage(hdc, menuRectStart, DFCS_MENUBULLET, color);
-          }
-        }
-        if ((state & DFCS_CONTAINER) != 0) {
-          if ((state & DFCS_RTL) == 0)
-            DrawMenuImage(hdc, menuRectEnd, DFCS_MENUARROW, color);
-          else
-            DrawMenuImage(hdc, menuRectEnd, DFCS_MENUARROWRIGHT, color);
-        }
-#endif
-      }
+    case NS_THEME_MENUCHECKBOX:
+    case NS_THEME_MENURADIO:
+      if (!(state & DFCS_CHECKED))
+        break; // nothin' to do
+    case NS_THEME_MENUARROW: {
+      PRUint32 color = COLOR_MENUTEXT;
+      if ((state & DFCS_INACTIVE))
+        color = COLOR_GRAYTEXT;
+      else if ((state & DFCS_HOT))
+        color = COLOR_HIGHLIGHTTEXT;
+      
+      if (aWidgetType == NS_THEME_MENUCHECKBOX)
+        DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
+      else if (aWidgetType == NS_THEME_MENURADIO)
+        DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
+      else if (aWidgetType == NS_THEME_MENUARROW)
+        DrawMenuImage(hdc, widgetRect, 
+                      (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
+                      color);
       break;
     }
+#endif
     default:
       rv = NS_ERROR_FAILURE;
       break;
   }
 
   nativeDrawing.EndNativeDrawing();
 
   if (NS_FAILED(rv))
@@ -2336,16 +2333,19 @@ nsNativeThemeWin::GetWidgetNativeDrawing
     case NS_THEME_DROPDOWN_BUTTON:
     // these are definitely no; they're all graphics that don't get scaled up
     case NS_THEME_CHECKBOX:
     case NS_THEME_CHECKBOX_SMALL:
     case NS_THEME_RADIO:
     case NS_THEME_RADIO_SMALL:
     case NS_THEME_CHECKMENUITEM:
     case NS_THEME_RADIOMENUITEM:
+    case NS_THEME_MENUCHECKBOX:
+    case NS_THEME_MENURADIO:
+    case NS_THEME_MENUARROW:
       return
         gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
         gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
         gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
   }
 
   return
     gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |