Decomtaminate native menu system. Add new native menu API. Lots of cleanup for the native menu system code. Memory usage and speed improvements. b=433952 r=bent r=kreeger sr=roc
authorJosh Aas <joshmoz@gmail.com>
Fri, 27 Jun 2008 00:14:45 -0400
changeset 15558 739d8ae68afb50ea3dbf16a550c73dac7c7d208b
parent 15557 039ec7b9e494d26854f6107eda1c7a6e8cceb110
child 15559 9c7170bebded4c9ef50bcbc31f9f8a809f25e2ef
push idunknown
push userunknown
push dateunknown
reviewersbent, kreeger, roc
bugs433952
milestone1.9.1a1pre
Decomtaminate native menu system. Add new native menu API. Lots of cleanup for the native menu system code. Memory usage and speed improvements. b=433952 r=bent r=kreeger sr=roc
dom/public/idl/base/nsIDOMWindowUtils.idl
dom/src/base/nsDOMWindowUtils.cpp
toolkit/content/tests/chrome/Makefile.in
widget/public/Makefile.in
widget/public/nsIMenuBar.h
widget/public/nsINativeMenuService.h
widget/public/nsIWidget.h
widget/public/nsWidgetsCID.h
widget/src/beos/nsWindow.h
widget/src/cocoa/Makefile.in
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsCocoaUtils.mm
widget/src/cocoa/nsCocoaWindow.h
widget/src/cocoa/nsCocoaWindow.mm
widget/src/cocoa/nsIMenu.h
widget/src/cocoa/nsIMenuItem.h
widget/src/cocoa/nsMenuBarX.h
widget/src/cocoa/nsMenuBarX.mm
widget/src/cocoa/nsMenuBaseX.h
widget/src/cocoa/nsMenuItemIconX.h
widget/src/cocoa/nsMenuItemIconX.mm
widget/src/cocoa/nsMenuItemX.h
widget/src/cocoa/nsMenuItemX.mm
widget/src/cocoa/nsMenuUtilsX.h
widget/src/cocoa/nsMenuUtilsX.mm
widget/src/cocoa/nsMenuX.h
widget/src/cocoa/nsMenuX.mm
widget/src/cocoa/nsPIWidgetCocoa.idl
widget/src/cocoa/nsWidgetFactory.mm
widget/src/gtk2/nsWindow.cpp
widget/src/gtk2/nsWindow.h
widget/src/os2/nsWindow.h
widget/src/photon/nsWindow.cpp
widget/src/windows/nsWindow.h
widget/src/xpwidgets/nsBaseWidget.h
widget/tests/Makefile.in
widget/tests/native_menus_window.xul
widget/tests/test_native_menus.xul
xpfe/appshell/src/Makefile.in
xpfe/appshell/src/nsWebShellWindow.cpp
--- a/dom/public/idl/base/nsIDOMWindowUtils.idl
+++ b/dom/public/idl/base/nsIDOMWindowUtils.idl
@@ -42,17 +42,17 @@
  * to the current nsIDOMWindow.  Some of the methods may require
  * elevated privileges; the method implementations should contain the
  * necessary security checks.  Access this interface by calling
  * getInterface on a DOMWindow.
  */
 
 interface nsIDOMElement;
 
-[scriptable, uuid(993da427-2ac3-4766-8485-21a236d258e4)]
+[scriptable, uuid(ef136142-9925-45f4-a3e4-6f0d275c6aa8)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -156,16 +156,25 @@ interface nsIDOMWindowUtils : nsISupport
    */
   void sendNativeKeyEvent(in long aNativeKeyboardLayout,
                           in long aNativeKeyCode,
                           in long aModifierFlags,
                           in AString aCharacters,
                           in AString aUnmodifiedCharacters);
 
   /**
+   * See nsIWidget::ActivateNativeMenuItemAt
+   *
+   * Cannot be accessed from unprivileged context (not content-accessible)
+   * Will throw a DOM security error if called without UniversalXPConnect
+   * privileges.
+   */
+  void activateNativeMenuItemAt(in AString indexString);
+
+  /**
    * Focus the element aElement. The element should be in the same document
    * that the window is displaying. Pass null to blur the element, if any,
    * that currently has focus, and focus the document.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without UniversalXPConnect
    * privileges.
    *
--- a/dom/src/base/nsDOMWindowUtils.cpp
+++ b/dom/src/base/nsDOMWindowUtils.cpp
@@ -303,16 +303,32 @@ nsDOMWindowUtils::SendNativeKeyEvent(PRI
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
   return widget->SynthesizeNativeKeyEvent(aNativeKeyboardLayout, aNativeKeyCode,
                                           aModifiers, aCharacters, aUnmodifiedCharacters);
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString)
+{
+  PRBool hasCap = PR_FALSE;
+  if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
+      || !hasCap)
+    return NS_ERROR_DOM_SECURITY_ERR;
+
+  // get the widget to send the event to
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  return widget->ActivateNativeMenuItemAt(indexString);
+}
+
 nsIWidget*
 nsDOMWindowUtils::GetWidget()
 {
   if (mWindow) {
     nsIDocShell *docShell = mWindow->GetDocShell();
     if (docShell) {
       nsCOMPtr<nsIPresShell> presShell;
       docShell->GetPresShell(getter_AddRefs(presShell));
--- a/toolkit/content/tests/chrome/Makefile.in
+++ b/toolkit/content/tests/chrome/Makefile.in
@@ -61,17 +61,17 @@ include $(topsrcdir)/config/rules.mk
 		window_largemenu.xul \
 		test_popup_anchor.xul \
 		window_popup_anchor.xul \
 		frame_popup_anchor.xul \
 		test_preferences.xul \
 		window_preferences.xul \
 		$(NULL)
 
-ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += test_panel_focus.xul \
                window_panel_focus.xul
 else
 _TEST_FILES += test_autocomplete.xul
 endif
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
--- a/widget/public/Makefile.in
+++ b/widget/public/Makefile.in
@@ -59,17 +59,17 @@ EXPORTS		= \
 		nsILookAndFeel.h \
 		nsIPluginWidget.h \
 		nsINativeKeyBindings.h \
 		nsIDeviceContextSpec.h \
 		nsIMenuRollup.h \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-EXPORTS		+= nsIMenuBar.h
+EXPORTS		+= nsINativeMenuService.h
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 EXPORTS		+= nsIDragSessionOS2.h
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),beos)
 EXPORTS		+= nsIDragSessionBeOS.h
deleted file mode 100644
--- a/widget/public/nsIMenuBar.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either 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
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nsIMenuBar_h__
-#define nsIMenuBar_h__
-
-#include "nsISupports.h"
-#include "nsString.h"
-#include "nsIMenu.h"
-
-class nsIWidget;
-
-// F81C6D64-B260-44ED-9289-2E410A130E35
-#define NS_IMENUBAR_IID      \
-{ 0xF81C6D64, 0xB260, 0x44ED, \
-  { 0x92, 0x89, 0x2E, 0x41, 0x0A, 0x13, 0x0E, 0x35 } }
-
-/**
- * MenuBar widget
- */
-class nsIMenuBar : public nsISupports {
-
-  public:
-    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUBAR_IID)
-
-   /**
-    * Creates the MenuBar
-    *
-    */
-    NS_IMETHOD Create(nsIWidget * aParent) = 0;
-
-   /**
-    * Get the MenuBar's Parent.  This addrefs.
-    *
-    */
-    NS_IMETHOD GetParent(nsIWidget *&aParent) = 0;
-
-   /**
-    * Set the MenuBar's Parent
-    *
-    */
-    NS_IMETHOD SetParent(nsIWidget *aParent) = 0;
-
-   /**
-    * Adds the Menu 
-    *
-    */
-    NS_IMETHOD AddMenu(nsIMenu * aMenu) = 0;
-    
-   /**
-    * Returns the number of menus
-    *
-    */
-    NS_IMETHOD GetMenuCount(PRUint32 &aCount) = 0;
-
-   /**
-    * Returns a Menu Item at a specified Index
-    *
-    */
-    NS_IMETHOD GetMenuAt(const PRUint32 aCount, nsIMenu *& aMenu) = 0;
-
-   /**
-    * Inserts a Menu at a specified Index
-    *
-    */
-    NS_IMETHOD InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu) = 0;
-
-   /**
-    * Removes an Menu from a specified Index
-    *
-    */
-    NS_IMETHOD RemoveMenu(const PRUint32 aCount) = 0;
-
-   /**
-    * Removes all the Menus
-    *
-    */
-    NS_IMETHOD RemoveAll() = 0;
-
-   /**
-    * Gets Native MenuHandle
-    *
-    */
-    NS_IMETHOD  GetNativeData(void*& aData) = 0;
-
-   /**
-    * Sets Native MenuHandle. Temporary hack for mac until 
-    * nsMenuBar does it's own construction
-    */
-    NS_IMETHOD  SetNativeData(void* aData) = 0;
-    
-   /**
-    * Draw the menubar
-    *
-    */
-    NS_IMETHOD  Paint() = 0;
-
-   /**
-    * Construct the menubar
-    *
-    */
-    NS_IMETHOD MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * aMenuNode) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuBar, NS_IMENUBAR_IID)
-
-#endif
new file mode 100644
--- /dev/null
+++ b/widget/public/nsINativeMenuService.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Josh Aas <josh@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsINativeMenuService_h_
+#define nsINativeMenuService_h_
+
+#include "nsISupports.h"
+
+class nsIWidget;
+class nsIContent;
+
+// {90DF88F9-F084-4EF3-829A-49496E636DED}
+#define NS_INATIVEMENUSERVICE_IID \
+{ 0x90DF88F9, 0xF084, 0x4EF3, \
+{ 0x82, 0x9A, 0x49, 0x49, 0x6E, 0x63, 0x6D, 0xED} }
+
+class nsINativeMenuService : public nsISupports {
+public:
+  // Given a top-level window widget and a menu bar DOM node, sets up native
+  // menus. Once created, native menus are controlled via the DOM, including
+  // destruction.
+  NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode)=0;
+};
+
+#endif // nsINativeMenuService_h_
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -50,17 +50,16 @@
 class   nsIAppShell;
 class   nsIToolkit;
 class   nsIFontMetrics;
 class   nsIRenderingContext;
 class   nsIDeviceContext;
 class   nsIRegion;
 struct  nsRect;
 struct  nsFont;
-class   nsIMenuBar;
 class   nsIEventListener;
 class   nsIRollupListener;
 class   nsGUIEvent;
 struct  nsColorMap;
 class   imgIContainer;
 class   gfxASurface;
 class   nsIMouseListener;
 class   nsIContent;
@@ -90,20 +89,20 @@ typedef nsEventStatus (*PR_CALLBACK EVEN
 #define NS_NATIVE_PLUGIN_PORT 8
 #define NS_NATIVE_SCREEN      9
 #define NS_NATIVE_SHELLWIDGET 10      // Get the shell GtkWidget
 #ifdef XP_MACOSX
 #define NS_NATIVE_PLUGIN_PORT_QD    100
 #define NS_NATIVE_PLUGIN_PORT_CG    101
 #endif
 
-// 517a0eef-cd1c-48b3-96f0-e341a50f120d
+// 00e25b3d-c872-4985-a15e-8e650b7b8ff6
 #define NS_IWIDGET_IID \
-{ 0x517a0eef, 0xcd1c, 0x48b3, \
-  { 0x96, 0xf0, 0xe3, 0x41, 0xa5, 0x0f, 0x12, 0x0d } }
+{ 0x00e25b3d, 0xc872, 0x4985, \
+  { 0xa1, 0x5e, 0x8e, 0x65, 0x0b, 0x7b, 0x8f, 0xf6 } }
 
 // Hide the native window systems real window type so as to avoid
 // including native window system types and APIs. This is necessary
 // to ensure cross-platform code.
 typedef void* nsNativeWidget;
 
 /**
  * Border styles
@@ -874,17 +873,17 @@ class nsIWidget : public nsISupports {
 
     /**
      * Set the widget's MenuBar.
      * Must be called after Create.
      *
      * @param aMenuBar the menubar
      */
 
-    NS_IMETHOD SetMenuBar(nsIMenuBar * aMenuBar) = 0;
+    NS_IMETHOD SetMenuBar(void* aMenuBar) = 0;
 
     /**
      * Set the widget's MenuBar's visibility
      *
      * @param aShow PR_TRUE to show, PR_FALSE to hide
      */
 
     NS_IMETHOD ShowMenuBar(PRBool aShow) = 0;
@@ -1100,16 +1099,30 @@ class nsIWidget : public nsISupports {
      * layout is not supported and the event was not fired
      */
     virtual nsresult SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
                                               PRInt32 aNativeKeyCode,
                                               PRUint32 aModifierFlags,
                                               const nsAString& aCharacters,
                                               const nsAString& aUnmodifiedCharacters) = 0;
 
+    /**
+     * Activates a native menu item at the position specified by the index
+     * string. The index string is a string of positive integers separated
+     * by the "|" (pipe) character. The last integer in the string represents
+     * the item index in a submenu located using the integers prior to it.
+     *
+     * Example: 1|0|4
+     * In this string, the first integer represents the top-level submenu
+     * in the native menu bar. Since the integer is 1, it is the second submeu
+     * in the native menu bar. Within that, the first item (index 0) is a
+     * submenu, and we want to activate the 5th item within that submenu.
+     */
+    virtual nsresult ActivateNativeMenuItemAt(const nsAString& aString) = 0;
+
 protected:
     // keep the list of children.  We also keep track of our siblings.
     // The ownership model is as follows: parent holds a strong ref to
     // the first element of the list, and each element holds a strong
     // ref to the next element in the list.  The prevsibling and
     // lastchild pointers are weak, which is fine as long as they are
     // maintained properly.
     nsCOMPtr<nsIWidget> mFirstChild;
--- a/widget/public/nsWidgetsCID.h
+++ b/widget/public/nsWidgetsCID.h
@@ -74,30 +74,20 @@
 #define NS_LOOKANDFEEL_CID \
 { 0xa61e6398, 0x2057, 0x40fd,  \
     { 0x9c, 0x81, 0x87, 0x3b, 0x90, 0x8d, 0x24, 0xe7 } }
 
 //-----------------------------------------------------------
 // Menus 
 //-----------------------------------------------------------
 
-// {BC658C81-4BEB-11d2-8DBB-00609703C14E}
-#define NS_MENUBAR_CID      \
-{ 0xbc658c81, 0x4beb, 0x11d2, \
-  { 0x8d, 0xbb, 0x0, 0x60, 0x97, 0x3, 0xc1, 0x4e } }
-
-// {35A3DEC1-4992-11d2-8DBA-00609703C14E}
-#define NS_MENU_CID      \
-{ 0x35a3dec1, 0x4992, 0x11d2, \
-  { 0x8d, 0xba, 0x0, 0x60, 0x97, 0x3, 0xc1, 0x4e } }
-
-// {7F045771-4BEB-11d2-8DBB-00609703C14E}
-#define NS_MENUITEM_CID      \
-{ 0x7f045771, 0x4beb, 0x11d2, \
-  { 0x8d, 0xbb, 0x0, 0x60, 0x97, 0x3, 0xc1, 0x4e } }
+// {0B3FE5AA-BC72-4303-85AE-76365DF1251D}
+#define NS_NATIVEMENUSERVICE_CID \
+{ 0x0B3FE5AA, 0xBC72, 0x4303, \
+  { 0x85, 0xAE, 0x76, 0x36, 0x5D, 0xF1, 0x25, 0x1D} }
 
 // {F6CD4F21-53AF-11d2-8DC4-00609703C14E}
 #define NS_POPUPMENU_CID      \
 { 0xf6cd4f21, 0x53af, 0x11d2, \
   { 0x8d, 0xc4, 0x0, 0x60, 0x97, 0x3, 0xc1, 0x4e } }
 
 //-----------------------------------------------------------
 //Drag & Drop & Clipboard
--- a/widget/src/beos/nsWindow.h
+++ b/widget/src/beos/nsWindow.h
@@ -43,18 +43,16 @@
 
 #include "nsBaseWidget.h"
 #include "nsdefs.h"
 #include "nsSwitchToUIThread.h"
 #include "nsToolkit.h"
 
 #include "nsIWidget.h"
 
-#include "nsIMenuBar.h"
-
 #include "nsIMouseListener.h"
 #include "nsIEventListener.h"
 #include "nsString.h"
 #include "nsRegion.h"
 
 #include <Window.h>
 #include <View.h>
 #include <Region.h>
@@ -159,17 +157,17 @@ public:
 	NS_IMETHOD              Invalidate(const nsRect & aRect, PRBool aIsSynchronous);
 	NS_IMETHOD              InvalidateRegion(const nsIRegion *aRegion,
 	                                         PRBool aIsSynchronous);
 	NS_IMETHOD              Update();
 	virtual void*           GetNativeData(PRUint32 aDataType);
 	NS_IMETHOD              SetColorMap(nsColorMap *aColorMap);
 	NS_IMETHOD              Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect);
 	NS_IMETHOD              SetTitle(const nsAString& aTitle);
-	NS_IMETHOD              SetMenuBar(nsIMenuBar * aMenuBar) { return NS_ERROR_FAILURE; }
+	NS_IMETHOD              SetMenuBar(void * aMenuBar) { return NS_ERROR_FAILURE; }
 	NS_IMETHOD              ShowMenuBar(PRBool aShow) { return NS_ERROR_FAILURE; }
 	NS_IMETHOD              WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect);
 	NS_IMETHOD              ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect);
 	NS_IMETHOD              BeginResizingChildren(void);
 	NS_IMETHOD              EndResizingChildren(void);
 	NS_IMETHOD              GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight);
 	NS_IMETHOD              SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight);
 	NS_IMETHOD              DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);
--- a/widget/src/cocoa/Makefile.in
+++ b/widget/src/cocoa/Makefile.in
@@ -61,17 +61,17 @@ REQUIRES = xpcom \
 		  layout \
 		  view \
 		  necko \
 		  locale \
 		  pref \
 		  intl \
 		  exthandler \
 		  appshell \
-                  lcms \
+		  lcms \
 		  thebes \
 		  js \
 		  xpconnect \
 		  imglib2 \
 		  $(NULL)
 
 ifdef MOZ_ENABLE_GLITZ
 REQUIRES += glitz glitzagl
@@ -79,27 +79,26 @@ endif
 
 ifdef ACCESSIBILITY
 REQUIRES += accessibility
 endif
 
 EXPORTS = \
 		mozView.h \
 		nsChangeObserver.h \
-		nsIMenu.h \
-		nsIMenuItem.h \
 		$(NULL)
 
 CMMSRCS = \
 		nsBidiKeyboard.mm \
 		nsClipboard.mm \
 		nsMenuX.mm \
 		nsMenuBarX.mm \
 		nsMenuItemX.mm \
 		nsMenuItemIconX.mm \
+		nsMenuUtilsX.mm \
 		nsFilePicker.mm \
 		nsDragService.mm \
 		nsToolkit.mm \
 		nsAppShell.mm \
 		nsCocoaUtils.mm \
 		nsCocoaWindow.mm \
 		nsChildView.mm \
 		nsWindowMap.mm \
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -56,17 +56,16 @@
 #include "nsIWidget.h"
 #include "nsIKBStateControl.h"
 #include "nsIAppShell.h"
 
 #include "nsIMouseListener.h"
 #include "nsIEventListener.h"
 #include "nsString.h"
 #include "nsIDragService.h"
-#include "nsIMenuBar.h"
 
 #include "nsplugindefs.h"
 
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 
 class gfxASurface;
 class nsChildView;
@@ -286,30 +285,32 @@ public:
 
   NS_IMETHOD              Update();
 
   virtual void      ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY);
   void              LocalToWindowCoordinate(nsPoint& aPoint)            { ConvertToDeviceCoordinates(aPoint.x, aPoint.y); }
   void              LocalToWindowCoordinate(nscoord& aX, nscoord& aY)   { ConvertToDeviceCoordinates(aX, aY); }
   void              LocalToWindowCoordinate(nsRect& aRect)              { ConvertToDeviceCoordinates(aRect.x, aRect.y); }
 
-  NS_IMETHOD        SetMenuBar(nsIMenuBar * aMenuBar);
+  NS_IMETHOD        SetMenuBar(void* aMenuBar);
   NS_IMETHOD        ShowMenuBar(PRBool aShow);
 
   NS_IMETHOD        GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight);
   NS_IMETHOD        SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight);
   
   NS_IMETHOD        SetCursor(nsCursor aCursor);
   NS_IMETHOD        SetCursor(imgIContainer* aCursor, PRUint32 aHotspotX, PRUint32 aHotspotY);
   
   NS_IMETHOD        CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent);
   NS_IMETHOD        SetTitle(const nsAString& title);
 
   NS_IMETHOD        GetAttention(PRInt32 aCycleCount);
 
+  NS_IMETHOD ActivateNativeMenuItemAt(const nsAString& indexString);
+
   // nsIPluginWidget
   NS_IMETHOD        GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible);
   NS_IMETHOD        StartDrawPlugin();
   NS_IMETHOD        EndDrawPlugin();
   NS_IMETHOD        SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner);
   
   NS_IMETHOD        GetHasTransparentBackground(PRBool& aTransparent);
   NS_IMETHOD        SetHasTransparentBackground(PRBool aTransparent);
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -955,17 +955,17 @@ NS_IMETHODIMP nsChildView::SetFocus(PRBo
 
 // Set the colormap of the window
 NS_IMETHODIMP nsChildView::SetColorMap(nsColorMap *aColorMap)
 {
   return NS_OK;
 }
 
 
-NS_IMETHODIMP nsChildView::SetMenuBar(nsIMenuBar * aMenuBar)
+NS_IMETHODIMP nsChildView::SetMenuBar(void* aMenuBar)
 {
   return NS_ERROR_FAILURE; // subviews don't have menu bars
 }
 
 
 NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
 {
   return NS_ERROR_FAILURE; // subviews don't have menu bars
@@ -1357,16 +1357,61 @@ nsresult nsChildView::SynthesizeNativeKe
     gOverrideKeyboardLayout = currentLayout;
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+// Used for testing native menu system structure and event handling.
+NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
+{  
+  NSString* title = [NSString stringWithCharacters:indexString.BeginReading() length:indexString.Length()];
+  NSArray* indexes = [title componentsSeparatedByString:@"|"];
+  unsigned int indexCount = [indexes count];
+  if (indexCount == 0)
+    return NS_OK;
+  
+  NSMenu* currentSubmenu = [NSApp mainMenu];
+  for (unsigned int i = 0; i < (indexCount - 1); i++) {
+    NSMenu* newSubmenu = nil;
+    int targetIndex;
+    // We remove the application menu from consideration for the top-level menu
+    if (i == 0)
+      targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
+    else
+      targetIndex = [[indexes objectAtIndex:i] intValue];
+    int itemCount = [currentSubmenu numberOfItems];
+    if (targetIndex < itemCount) {
+      NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
+      if ([menuItem hasSubmenu])
+        newSubmenu = [menuItem submenu];
+    }
+    
+    if (newSubmenu)
+      currentSubmenu = newSubmenu;
+    else
+      return NS_ERROR_FAILURE;
+  }
+
+  int itemCount = [currentSubmenu numberOfItems];
+  int targetIndex = [[indexes objectAtIndex:(indexCount - 1)] intValue];
+  if (targetIndex < itemCount) {
+    // NSLog(@"Performing action for native menu item titled: %@\n",
+    //       [[currentSubmenu itemAtIndex:targetIndex] title]);
+    [currentSubmenu performActionForItemAtIndex:targetIndex];
+  }
+  else {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
 #pragma mark -
 
 
 #ifdef INVALIDATE_DEBUGGING
 
 static Boolean KeyDown(const UInt8 theKey)
 {
   KeyMap map;
--- a/widget/src/cocoa/nsCocoaUtils.mm
+++ b/widget/src/cocoa/nsCocoaUtils.mm
@@ -42,16 +42,17 @@
 #include "nsMenuBarX.h"
 #include "nsCocoaWindow.h"
 #include "nsCOMPtr.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIAppShellService.h"
 #include "nsIXULWindow.h"
 #include "nsIBaseWindow.h"
 #include "nsIServiceManager.h"
+#include "nsMenuUtilsX.h"
 
 float nsCocoaUtils::MenuBarScreenHeight()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   NSArray* allScreens = [NSScreen screens];
   if ([allScreens count])
     return [[allScreens objectAtIndex:0] frame].size.height;
@@ -186,17 +187,17 @@ nsIWidget* nsCocoaUtils::GetHiddenWindow
 
 
 void nsCocoaUtils::PrepareForNativeAppModalDialog()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // Don't do anything if this is embedding. We'll assume that if there is no hidden
   // window we shouldn't do anything, and that should cover the embedding case.
-  nsIMenuBar* hiddenWindowMenuBar = MenuHelpersX::GetHiddenWindowMenuBar();
+  nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
   if (!hiddenWindowMenuBar)
     return;
 
   // First put up the hidden window menu bar so that app menu event handling is correct.
   hiddenWindowMenuBar->Paint();
 
   NSMenu* mainMenu = [NSApp mainMenu];
   NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
@@ -207,33 +208,33 @@ void nsCocoaUtils::PrepareForNativeAppMo
   // Swap in our app menu. Note that the event target is whatever window is up when
   // the app modal dialog goes up.
   NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
   [mainMenu removeItemAtIndex:0];
   [newMenuBar insertItem:firstMenuItem atIndex:0];
   [firstMenuItem release];
   
   // Add standard edit menu
-  [newMenuBar addItem:MenuHelpersX::GetStandardEditMenuItem()];
+  [newMenuBar addItem:nsMenuUtilsX::GetStandardEditMenuItem()];
   
   // Show the new menu bar
   [NSApp setMainMenu:newMenuBar];
   [newMenuBar release];
   
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
 void nsCocoaUtils::CleanUpAfterNativeAppModalDialog()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // Don't do anything if this is embedding. We'll assume that if there is no hidden
   // window we shouldn't do anything, and that should cover the embedding case.
-  nsIMenuBar* hiddenWindowMenuBar = MenuHelpersX::GetHiddenWindowMenuBar();
+  nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
   if (!hiddenWindowMenuBar)
     return;
 
   NSWindow* mainWindow = [NSApp mainWindow];
   if (!mainWindow)
     hiddenWindowMenuBar->Paint();
   else
     [WindowDelegate paintMenubarForWindow:mainWindow];
--- a/widget/src/cocoa/nsCocoaWindow.h
+++ b/widget/src/cocoa/nsCocoaWindow.h
@@ -41,19 +41,21 @@
 #define nsCocoaWindow_h_
 
 #undef DARWIN
 
 #import <Cocoa/Cocoa.h>
 
 #include "nsBaseWidget.h"
 #include "nsPIWidgetCocoa.h"
+#include "nsAutoPtr.h"
 
 class nsCocoaWindow;
 class nsChildView;
+class nsMenuBarX;
 
 typedef struct _nsCocoaWindowList {
   _nsCocoaWindowList() : prev(NULL), window(NULL) {}
   struct _nsCocoaWindowList *prev;
   nsCocoaWindow *window; // Weak
 } nsCocoaWindowList;
 
 
@@ -205,18 +207,18 @@ public:
     virtual nsIWidget*      GetSheetWindowParent(void);
     NS_IMETHOD              AddMouseListener(nsIMouseListener * aListener);
     NS_IMETHOD              AddEventListener(nsIEventListener * aListener);
     NS_IMETHOD              Enable(PRBool aState);
     NS_IMETHOD              IsEnabled(PRBool *aState);
     NS_IMETHOD              SetModal(PRBool aState);
     NS_IMETHOD              IsVisible(PRBool & aState);
     NS_IMETHOD              SetFocus(PRBool aState=PR_FALSE);
-    NS_IMETHOD              SetMenuBar(nsIMenuBar * aMenuBar);
-    virtual nsIMenuBar*     GetMenuBar();
+    NS_IMETHOD              SetMenuBar(void* aMenuBar);
+    virtual nsMenuBarX*     GetMenuBar();
     NS_IMETHOD              ShowMenuBar(PRBool aShow);
     NS_IMETHOD WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect);
     NS_IMETHOD ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect);
     
     virtual void* GetNativeData(PRUint32 aDataType) ;
 
     NS_IMETHOD              ConstrainPosition(PRBool aAllowSlop,
                                               PRInt32 *aX, PRInt32 *aY);
@@ -270,17 +272,17 @@ public:
     NS_IMETHOD BeginSecureKeyboardInput();
     NS_IMETHOD EndSecureKeyboardInput();
 
 protected:
   
   nsIWidget*           mParent;         // if we're a popup, this is our parent [WEAK]
   NSWindow*            mWindow;         // our cocoa window [STRONG]
   WindowDelegate*      mDelegate;       // our delegate for processing window msgs [STRONG]
-  nsCOMPtr<nsIMenuBar> mMenuBar;
+  nsRefPtr<nsMenuBarX> mMenuBar;
   NSWindow*            mSheetWindowParent; // if this is a sheet, this is the NSWindow it's attached to
   nsChildView*         mPopupContentView; // if this is a popup, this is its content widget
 
   PRPackedBool         mIsResizing;     // we originated the resize, prevent infinite recursion
   PRPackedBool         mWindowMadeHere; // true if we created the window, false for embedding
   PRPackedBool         mSheetNeedsShow; // if this is a sheet, are we waiting to be shown?
                                         // this is used for sibling sheet contention only
   PRPackedBool         mModal;
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -59,16 +59,17 @@
 #include "nsThreadUtils.h"
 #include "nsIFocusController.h"
 #include "nsIWindowWatcher.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsMenuBarX.h"
+#include "nsMenuUtilsX.h"
 
 #include "gfxPlatform.h"
 #include "lcms.h"
 
 PRInt32 gXULModalLevel = 0;
 // In principle there should be only one app-modal window at any given time.
 // But sometimes, despite our best efforts, another window appears above the
 // current app-modal window.  So we need to keep a linked list of app-modal
@@ -1112,23 +1113,16 @@ NS_IMETHODIMP nsCocoaWindow::GetChildShe
   }
 
   *_retval = nsnull;
 
   return NS_OK;
 }
 
 
-NS_IMETHODIMP nsCocoaWindow::GetMenuBar(nsIMenuBar** menuBar)
-{
-  *menuBar = mMenuBar;
-  return NS_OK;
-}
-
-
 NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent)
 {
   *parent = mParent;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP nsCocoaWindow::GetIsSheet(PRBool* isSheet)
@@ -1195,25 +1189,25 @@ nsCocoaWindow::ReportSizeEvent(NSRect *r
 
   nsEventStatus status = nsEventStatus_eIgnore;
   DispatchEvent(&sizeEvent, status);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
-NS_IMETHODIMP nsCocoaWindow::SetMenuBar(nsIMenuBar *aMenuBar)
+NS_IMETHODIMP nsCocoaWindow::SetMenuBar(void *aMenuBar)
 {
   if (mMenuBar)
     mMenuBar->SetParent(nsnull);
-  mMenuBar = aMenuBar;
+  mMenuBar = static_cast<nsMenuBarX*>(aMenuBar);
   
   // We paint the hidden window menu bar if no other menu bar has been painted
   // yet so that some reasonable menu bar is displayed when the app starts up.
-  if (!gSomeMenuBarPainted && mMenuBar && (MenuHelpersX::GetHiddenWindowMenuBar() == mMenuBar))
+  if (!gSomeMenuBarPainted && mMenuBar && (nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar))
     mMenuBar->Paint();
   
   return NS_OK;
 }
 
 
 NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
 {
@@ -1259,17 +1253,17 @@ NS_IMETHODIMP nsCocoaWindow::ScreenToWid
   aNewRect.height = aOldRect.height;
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-nsIMenuBar* nsCocoaWindow::GetMenuBar()
+nsMenuBarX* nsCocoaWindow::GetMenuBar()
 {
   return mMenuBar;
 }
 
 
 NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener * aListener, 
                                                  PRBool aDoCapture, 
                                                  PRBool aConsumeRollupEvent)
@@ -1405,17 +1399,17 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
   // object as a delegate
   id windowDelegate = [aWindow delegate];
   if ([windowDelegate class] != [self class])
     return;
 
   nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget];
   NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!");
   
-  nsIMenuBar* geckoMenuBar = geckoWidget->GetMenuBar();
+  nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar();
   if (geckoMenuBar) {
     geckoMenuBar->Paint();
   }
   else {
     // sometimes we don't have a native application menu early in launching
     if (!sApplicationMenu)
       return;
 
@@ -1493,17 +1487,17 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
 - (void)windowDidResignMain:(NSNotification *)aNotification
 {
   RollUpPopups();
 
   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
   // app modally. If one of those is up then we want it to retain its menu bar.
   if ([NSApp _isRunningAppModal])
     return;
-  nsCOMPtr<nsIMenuBar> hiddenWindowMenuBar = MenuHelpersX::GetHiddenWindowMenuBar();
+  nsRefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
   if (hiddenWindowMenuBar) {
     // printf("painting hidden window menu bar due to window losing main status\n");
     hiddenWindowMenuBar->Paint();
   }
 }
 
 
 - (void)windowDidBecomeKey:(NSNotification *)aNotification
deleted file mode 100644
--- a/widget/src/cocoa/nsIMenu.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either 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
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nsIMenu_h__
-#define nsIMenu_h__
-
-#include "nsISupports.h"
-#include "nsStringFwd.h"
-#include "nsEvent.h"
-
-class nsIMenuBar;
-class nsIMenu;
-class nsIMenuItem;
-class nsIContent;
-class nsIWidget;
-class nsMenuBarX;
-
-
-// 9225136B-3F56-4CA3-92E0-623D5FB8356B
-#define NS_IMENU_IID \
-{ 0x9225136B, 0x3F56, 0x4CA3, \
-  { 0x92, 0xE0, 0x62, 0x3D, 0x5F, 0xB8, 0x35, 0x6B } }
-
-/**
- * Menu widget
- */
-class nsIMenu : public nsISupports {
-
-  public:
-    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENU_IID)
-
-  /**
-    * Creates the Menu
-    *
-    */
-    NS_IMETHOD Create(nsISupports * aParent, const nsAString &aLabel, const nsAString &aAccessKey, 
-                      nsMenuBarX* aMenuBar, nsIContent* aNode) = 0;
-
-  /**
-   * Get the Menu's Parent.  This addrefs.
-   *
-   */
-    NS_IMETHOD GetParent(nsISupports *&aParent) = 0;
-
-  /**
-   * Get the Menu label
-   *
-   */
-    NS_IMETHOD GetLabel(nsString &aText) = 0;
-
-  /**
-   * Set the Menu label
-   *
-   */
-    NS_IMETHOD SetLabel(const nsAString &aText) = 0;
-
-  /**
-    * Get the Menu Access Key
-    *
-    */
-  NS_IMETHOD GetAccessKey(nsString &aText) = 0;
-   
-  /**
-    * Set the Menu Access Key
-    *
-    */
-  NS_IMETHOD SetAccessKey(const nsAString &aText) = 0;
-
-  /**
-    * Set the Menu enabled state
-    *
-    */
-  NS_IMETHOD SetEnabled(PRBool aIsEnabled) = 0;
-
-  /**
-    * Get the Menu enabled state
-    *
-    */
-  NS_IMETHOD GetEnabled(PRBool* aIsEnabled) = 0;
-  
-  /**
-    * Adds a Menu Item. Do not use outside of widget menu implementations.
-    * Add and modify menu items via DOM content.
-    *
-    */
-    NS_IMETHOD AddItem(nsISupports* aItem) = 0;
-
-   /**
-    * Returns the number of visible menu items
-    * This includes separators. It does not include hidden items.
-    *
-    */
-    NS_IMETHOD GetVisibleItemCount(PRUint32 &aCount) = 0;
-
-   /**
-    * Returns a Menu or Menu Item at a specified Index.
-    * This includes separators. It does not include hidden items.
-    *
-    */
-    NS_IMETHOD GetVisibleItemAt(const PRUint32 aPos, nsISupports *& aMenuItem) = 0;
-
-   /**
-    * Returns the number of menu items
-    * This includes separators. It -does- include hidden items.
-    *
-    */
-    NS_IMETHOD GetItemCount(PRUint32 &aCount) = 0;
-
-   /**
-    * Returns a Menu or Menu Item at a specified Index.
-    * This includes separators. It -does- include hidden items.
-    *
-    */
-    NS_IMETHOD GetItemAt(const PRUint32 aPos, nsISupports *& aMenuItem) = 0;
-
-   /**
-    * Inserts a Menu Item at a specified Index
-    *
-    */
-    NS_IMETHOD InsertItemAt(const PRUint32 aPos, nsISupports * aMenuItem) = 0;
-
-   /**
-    * Removes an Menu Item from a specified Index
-    *
-    */
-    NS_IMETHOD RemoveItem(const PRUint32 aPos) = 0;
-
-   /**
-    * Removes all the Menu Items
-    *
-    */
-    NS_IMETHOD RemoveAll() = 0;
-
-   /**
-    * Gets Native MenuHandle
-    *
-    */
-    NS_IMETHOD  GetNativeData(void** aData) = 0;
-
-   /**
-    * Sets Native MenuHandle
-    *
-    */
-    NS_IMETHOD  SetNativeData(void* aData) = 0;
-
-   /**
-    * Get menu content
-    *
-    */
-    NS_IMETHOD GetMenuContent(nsIContent ** aMenuContent) = 0;
-    
-   /**
-    * Enable/disable native widget for a particular nsIMenuItem
-    *
-    */
-    NS_IMETHOD ChangeNativeEnabledStatusForMenuItem(nsIMenuItem* aMenuItem,
-                                                    PRBool aEnabled) = 0;
-
-   /**
-    * Retrieve the native menu and the index of the item within that menu.
-    *
-    */
-    NS_IMETHOD GetMenuRefAndItemIndexForMenuItem(nsISupports* aMenuItem,
-                                                 void**       aMenuRef,
-                                                 PRUint16*    aMenuItemIndex) = 0;
-
-   /**
-    * Sets an appropriate icon for the menu.
-    *
-    */
-    NS_IMETHOD SetupIcon() = 0;
-    
-   /**
-    * Menu has been selected
-    *
-    */
-    virtual nsEventStatus MenuSelected(const nsMenuEvent & aMenuEvent) = 0;
-    
-   /**
-    * Menu has been deselected
-    *
-    */
-    virtual void MenuDeselected(const nsMenuEvent & aMenuEvent) = 0;
-
-   /**
-    * Construct menu
-    *
-    */
-    virtual void MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * aMenuNode) = 0;
-
-   /**
-    * Destruct menu
-    *
-    */
-    virtual void MenuDestruct(const nsMenuEvent & aMenuEvent) = 0;
-    
-   /**
-    * Set rebuild
-    *
-    */
-    virtual void SetRebuild(PRBool aMenuEvent) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenu, NS_IMENU_IID)
-
-#endif
deleted file mode 100644
--- a/widget/src/cocoa/nsIMenuItem.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either 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
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nsIMenuItem_h__
-#define nsIMenuItem_h__
-
-#include "prtypes.h"
-#include "nsISupports.h"
-#include "nsString.h"
-
-#include "nsIDOMElement.h"
-
-// 33FA04E3-EAFE-4DD1-AFB3-B3BC8C712716
-#define NS_IMENUITEM_IID \
-{ 0x33FA04E3, 0xEAFE, 0x4DD1, \
-  { 0xAF, 0xB3, 0xB3, 0xBC, 0x8C, 0x71, 0x27, 0x16 } }
-
-class nsIMenu;
-class nsIWidget;
-class nsIContent;
-class nsMenuBarX;
-
-enum {
-  knsMenuItemNoModifier      = 0,
-  knsMenuItemShiftModifier   = (1 << 0),
-  knsMenuItemAltModifier     = (1 << 1),
-  knsMenuItemControlModifier = (1 << 2),
-  knsMenuItemCommandModifier = (1 << 3)
-};
-
-/**
- * MenuItem widget
- */
-class nsIMenuItem : public nsISupports {
-
-  public:
-    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUITEM_IID)
-
-    enum EMenuItemType { eRegular = 0, eCheckbox, eRadio, eSeparator} ;
-
-   /**
-    * Creates the MenuItem
-    *
-    */
-    NS_IMETHOD Create(nsIMenu* aParent, const nsString & aLabel, EMenuItemType aItemType,
-                      nsMenuBarX* aMenuBar, nsIContent* aNode) = 0;
-
-   /**
-    * Get the MenuItem label
-    *
-    */
-    NS_IMETHOD GetLabel(nsString &aText) = 0;
-
-    /**
-    * Get the Menu shortcut char
-    *
-    */
-    NS_IMETHOD GetShortcutChar(nsString &aText) = 0;
-
-   /**
-    * Gets whether the item is enabled or disabled
-    *
-    */
-    NS_IMETHOD GetEnabled(PRBool *aIsEnabled) = 0;
-
-   /**
-    * Sets whether the item is checked or not
-    *
-    */
-    NS_IMETHOD SetChecked(PRBool aIsEnabled) = 0;
-
-   /**
-    * Gets whether the item is checked or not
-    *
-    */
-    NS_IMETHOD GetChecked(PRBool *aIsEnabled) = 0;
-
-   /**
-    * Gets whether the item is a checkbox or radio
-    *
-    */
-    NS_IMETHOD GetMenuItemType(EMenuItemType *aType) = 0;
-    
-   /**
-    * Gets Native Menu Handle
-    *
-    */
-    NS_IMETHOD GetNativeData(void*& aData) = 0;
-
-   /**
-    * Indicates whether it is a separator
-    *
-    */
-    NS_IMETHOD IsSeparator(PRBool & aIsSep) = 0;
-
-   /**
-    * Executes the "cached" JavaScript Command 
-    * @return NS_OK if the command was executed properly, otherwise an error code
-    */
-    NS_IMETHOD DoCommand() = 0;
-    
-    /**
-     * Sends a DOM event to the menu item's content node 
-     * @return NS_OK if the event was sent properly, otherwise an error code
-     */
-    NS_IMETHOD DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled) = 0;
-
-   /**
-    * Sets an appropriate icon for the menu item.
-    */
-    NS_IMETHOD SetupIcon() = 0;
-
-    /**
-     * Get GetMenuItemContent
-     *
-     */
-    NS_IMETHOD GetMenuItemContent(nsIContent ** aMenuItemContent) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuItem, NS_IMENUITEM_IID)
-
-#endif
--- a/widget/src/cocoa/nsMenuBarX.h
+++ b/widget/src/cocoa/nsMenuBarX.h
@@ -34,129 +34,109 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMenuBarX_h_
 #define nsMenuBarX_h_
 
-#include "nsIMenuBar.h"
-#include "nsObjCExceptions.h"
+#import <Cocoa/Cocoa.h>
+
+#include "nsMenuBaseX.h"
 #include "nsIMutationObserver.h"
-#include "nsCOMArray.h"
 #include "nsHashtable.h"
-#include "nsWeakReference.h"
-#include "nsIContent.h"
+#include "nsINativeMenuService.h"
+#include "nsAutoPtr.h"
 
-#import  <Carbon/Carbon.h>
-#import  <Cocoa/Cocoa.h>
-
-class nsIWidget;
-class nsIDocument;
-class nsIDOMNode;
+class nsMenuX;
+class nsMenuItemX;
 class nsChangeObserver;
-
-extern "C" MenuRef _NSGetCarbonMenu(NSMenu* aMenu);
+class nsIWidget;
+class nsIContent;
+class nsIDocument;
 
-PRBool NodeIsHiddenOrCollapsed(nsIContent* inContent);
 
-namespace MenuHelpersX
+// The native menu service for creating native menu bars.
+class nsNativeMenuServiceX : public nsINativeMenuService
 {
-  nsEventStatus DispatchCommandTo(nsIContent* aTargetContent);
-  NSString* CreateTruncatedCocoaLabel(const nsString& itemLabel);
-  PRUint8 GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute);
-  unsigned int MacModifiersForGeckoModifiers(PRUint8 geckoModifiers);
-  nsIMenuBar* GetHiddenWindowMenuBar();
-  NSMenuItem* GetStandardEditMenuItem();
-}
+public:
+  NS_DECL_ISUPPORTS
+  NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode);
+};
 
 
 // Objective-C class used to allow us to have keyboard commands
 // look like they are doing something but actually do nothing.
 // We allow mouse actions to work normally.
 @interface GeckoNSMenu : NSMenu
 {
 }
-- (BOOL)performKeyEquivalent:(NSEvent *)theEvent;
-- (void)actOnKeyEquivalent:(NSEvent *)theEvent;
+- (BOOL)performKeyEquivalent:(NSEvent*)theEvent;
+- (void)actOnKeyEquivalent:(NSEvent*)theEvent;
 - (void)performMenuUserInterfaceEffectsForEvent:(NSEvent*)theEvent;
 @end
 
 
 // Objective-C class used as action target for menu items
 @interface NativeMenuItemTarget : NSObject
 {
 }
 -(IBAction)menuItemHit:(id)sender;
 @end
 
 
-//
-// Native menu bar wrapper
-//
-
-class nsMenuBarX : public nsIMenuBar,
-                   public nsIMutationObserver,
-                   public nsSupportsWeakReference
+// Once instantiated, this object lives until its DOM node or its parent window is destroyed.
+// Do not hold references to this, they can become invalid any time the DOM node can be destroyed.
+class nsMenuBarX : public nsMenuObjectX,
+                   public nsIMutationObserver
 {
 public:
-    nsMenuBarX();
-    virtual ~nsMenuBarX();
+  nsMenuBarX();
+  virtual ~nsMenuBarX();
 
-    // |NSMenuItem|s target Objective-C objects
-    static NativeMenuItemTarget* sNativeEventTarget;
-    
-    static nsMenuBarX* sLastGeckoMenuBarPainted;
-    
-    NS_DECL_ISUPPORTS
-
-    // nsIMutationObserver
-    NS_DECL_NSIMUTATIONOBSERVER
+  static NativeMenuItemTarget* sNativeEventTarget;
+  static nsMenuBarX*           sLastGeckoMenuBarPainted;
 
-    // nsIMenuBar Methods
-    NS_IMETHOD Create(nsIWidget * aParent);
-    NS_IMETHOD GetParent(nsIWidget *&aParent);
-    NS_IMETHOD SetParent(nsIWidget * aParent);
-    NS_IMETHOD AddMenu(nsIMenu * aMenu);
-    NS_IMETHOD GetMenuCount(PRUint32 &aCount);
-    NS_IMETHOD GetMenuAt(const PRUint32 aCount, nsIMenu *& aMenu);
-    NS_IMETHOD InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu);
-    NS_IMETHOD RemoveMenu(const PRUint32 aCount);
-    NS_IMETHOD RemoveAll();
-    NS_IMETHOD GetNativeData(void*& aData);
-    NS_IMETHOD Paint();
-    NS_IMETHOD SetNativeData(void* aData);
-    NS_IMETHOD MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * aMenuNode);
+  // The following content nodes have been removed from the menu system.
+  // We save them here for use in command handling.
+  nsCOMPtr<nsIContent> mAboutItemContent;
+  nsCOMPtr<nsIContent> mPrefItemContent;
+  nsCOMPtr<nsIContent> mQuitItemContent;
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMUTATIONOBSERVER
+
+  // nsMenuObjectX
+  void*             NativeData()     {return (void*)mRootMenu;}
+  nsMenuObjectTypeX MenuObjectType() {return eMenuBarObjectType;}
 
-    PRUint32 RegisterForCommand(nsIMenuItem* aItem);
-    void UnregisterCommand(PRUint32 aCommandID);
-    nsIMenuItem* GetMenuItemForCommandID(PRUint32 inCommandID);
+  // nsMenuBarX
+  nsresult          Create(nsIWidget* aParent, nsIContent* aContent);
+  void              SetParent(nsIWidget* aParent);
+  void              RegisterForContentChanges(nsIContent* aContent, nsChangeObserver* aMenuObject);
+  void              UnregisterForContentChanges(nsIContent* aContent);
+  PRUint32          RegisterForCommand(nsMenuItemX* aItem);
+  void              UnregisterCommand(PRUint32 aCommandID);
+  PRUint32          GetMenuCount();
+  nsMenuX*          GetMenuAt(PRUint32 aIndex);
+  nsMenuItemX*      GetMenuItemForCommandID(PRUint32 inCommandID);
+  nsresult          Paint();
 
-    void RegisterForContentChanges(nsIContent* aContent, nsChangeObserver* aMenuObject);
-    void UnregisterForContentChanges(nsIContent* aContent);
-    nsChangeObserver* LookupContentChangeObserver(nsIContent* aContent);
-
-    nsCOMPtr<nsIContent>    mAboutItemContent;    // holds the content node for the about item that has
-                                                  //   been removed from the menubar
-    nsCOMPtr<nsIContent>    mPrefItemContent;     // as above, but for prefs
-    nsCOMPtr<nsIContent>    mQuitItemContent;     // as above, but for quit
 protected:
-    // Make our menubar conform to Aqua UI guidelines
-    void AquifyMenuBar();
-    void HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode);
-
-    // build the Application menu shared by all menu bars.
-    NSMenuItem* CreateNativeAppMenuItem(nsIMenu* inMenu, const nsAString& nodeID, SEL action,
-                                                    int tag, NativeMenuItemTarget* target);
-    nsresult CreateApplicationMenu(nsIMenu* inMenu);
+  nsresult          AddMenu(nsMenuX* aMenu);
+  void              RemoveMenu(PRUint32 aIndex);
+  nsChangeObserver* LookupContentChangeObserver(nsIContent* aContent);
+  void              HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode);
+  void              AquifyMenuBar();
+  NSMenuItem*       CreateNativeAppMenuItem(nsMenuX* inMenu, const nsAString& nodeID, SEL action,
+                                            int tag, NativeMenuItemTarget* target);
+  nsresult          CreateApplicationMenu(nsMenuX* inMenu);
 
-    nsCOMArray<nsIMenu>     mMenusArray;          // holds refs
-    nsCOMPtr<nsIContent>    mMenuBarContent;      // menubar content node, strong ref
-    nsIWidget*              mParent;              // weak ref
-    PRBool                  mIsMenuBarAdded;
-    PRUint32                mCurrentCommandID;    // unique command id (per menu-bar) to give to next item that asks
-    nsIDocument*            mDocument;            // pointer to document
-    GeckoNSMenu*            mRootMenu;            // root menu, representing entire menu bar
-    nsHashtable             mObserverTable;       // stores observers for content change notification
+  nsTArray< nsAutoPtr<nsMenuX> > mMenuArray;
+  nsIWidget*         mParent;              // [weak]
+  PRUint32           mCurrentCommandID;    // unique command id (per menu-bar) to give to next item that asks
+  nsIDocument*       mDocument;            // pointer to document
+  GeckoNSMenu*       mRootMenu;            // root menu, representing entire menu bar
+  nsHashtable        mObserverTable;       // stores observers for content change notification
 };
 
 #endif // nsMenuBarX_h_
--- a/widget/src/cocoa/nsMenuBarX.mm
+++ b/widget/src/cocoa/nsMenuBarX.mm
@@ -31,47 +31,38 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsCOMPtr.h"
-#include "nsIServiceManager.h"
-#include "nsIComponentManager.h"
-#include "nsINameSpaceManager.h"
-#include "nsIMenu.h"
-#include "nsIMenuItem.h"
-#include "nsIContent.h"
-
 #include "nsMenuBarX.h"
 #include "nsMenuX.h"
-#include "nsChildView.h"
+#include "nsMenuItemX.h"
+#include "nsMenuUtilsX.h"
 #include "nsCocoaUtils.h"
 #include "nsCocoaWindow.h"
 
-#include "nsISupports.h"
-#include "nsIWidget.h"
+#include "nsCOMPtr.h"
 #include "nsString.h"
-#include "nsIStringBundle.h"
-#include "nsIDocument.h"
-#include "nsIMutationObserver.h"
-
-#include "nsIDOMDocument.h"
 #include "nsWidgetAtoms.h"
-
 #include "nsGUIEvent.h"
+#include "nsObjCExceptions.h"
+#include "nsHashtable.h"
+#include "nsThreadUtils.h"
 
-// CIDs
-#include "nsWidgetsCID.h"
-static NS_DEFINE_CID(kMenuCID, NS_MENU_CID);
+#include "nsIContent.h"
+#include "nsIWidget.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMElement.h"
 
-NS_IMPL_ISUPPORTS3(nsMenuBarX, nsIMenuBar, nsIMutationObserver, nsISupportsWeakReference)
+NS_IMPL_ISUPPORTS1(nsMenuBarX, nsIMutationObserver)
 
 NativeMenuItemTarget* nsMenuBarX::sNativeEventTarget = nil;
 nsMenuBarX* nsMenuBarX::sLastGeckoMenuBarPainted = nsnull;
 NSMenu* sApplicationMenu = nil;
 BOOL gSomeMenuBarPainted = NO;
 
 // We keep references to the first quit and pref item content nodes we find, which
 // will be from the hidden window. We use these when the document for the current
@@ -87,28 +78,32 @@ static nsIContent* sQuitItemContent  = n
 enum {
   eCommand_ID_About = 1,
   eCommand_ID_Prefs = 2,
   eCommand_ID_Quit  = 3,
   eCommand_ID_Last  = 4
 };
 
 
-PRBool NodeIsHiddenOrCollapsed(nsIContent* inContent)
+NS_IMPL_ISUPPORTS1(nsNativeMenuServiceX, nsINativeMenuService)
+
+NS_IMETHODIMP nsNativeMenuServiceX::CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode)
 {
-  return (inContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::hidden,
-                                 nsWidgetAtoms::_true, eCaseMatters) ||
-          inContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::collapsed,
-                                 nsWidgetAtoms::_true, eCaseMatters));
+  NS_ASSERTION(NS_IsMainThread(), "Attempting to create native menu bar on wrong thread!");
+
+  nsRefPtr<nsMenuBarX> mb = new nsMenuBarX();
+  if (!mb)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  return mb->Create(aParent, aMenuBarNode);
 }
 
 
 nsMenuBarX::nsMenuBarX()
 : mParent(nsnull),
-  mIsMenuBarAdded(PR_FALSE),
   mCurrentCommandID(eCommand_ID_Last),
   mDocument(nsnull)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   mRootMenu = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
@@ -117,42 +112,196 @@ nsMenuBarX::nsMenuBarX()
 
 nsMenuBarX::~nsMenuBarX()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (nsMenuBarX::sLastGeckoMenuBarPainted == this)
     nsMenuBarX::sLastGeckoMenuBarPainted = nsnull;
 
-  mMenusArray.Clear(); // release all menus
-
   // the quit/pref items of a random window might have been used if there was no
   // hidden window, thus we need to invalidate the weak references.
   if (sAboutItemContent == mAboutItemContent)
     sAboutItemContent = nsnull;
   if (sQuitItemContent == mQuitItemContent)
     sQuitItemContent = nsnull;
   if (sPrefItemContent == mPrefItemContent)
     sPrefItemContent = nsnull;
 
   // make sure we unregister ourselves as a document observer
   if (mDocument)
     mDocument->RemoveMutationObserver(this);
-  
+
+  // We have to manually clear the array here because clearing causes menu items
+  // to call back into the menu bar to unregister themselves. We don't want to
+  // depend on member variable ordering to ensure that the array gets cleared
+  // before the registration hash table is destroyed.
+  mMenuArray.Clear();
+
   [mRootMenu release];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
-// Do what's necessary to conform to the Aqua guidelines for menus.
-void
-nsMenuBarX::AquifyMenuBar()
+nsresult nsMenuBarX::Create(nsIWidget* aParent, nsIContent* aContent)
+{
+  if (!aParent || !aContent)
+    return NS_ERROR_INVALID_ARG;
+
+  mParent = aParent;
+  mContent = aContent;
+
+  AquifyMenuBar();
+
+  nsIDocument* doc = aContent->GetOwnerDoc();
+  if (!doc)
+    return NS_ERROR_FAILURE;
+  doc->AddMutationObserver(this);
+  mDocument = doc;
+
+  PRUint32 count = mContent->GetChildCount();
+  for (PRUint32 i = 0; i < count; i++) { 
+    nsIContent *menuContent = mContent->GetChildAt(i);
+    if (menuContent) {
+      if (menuContent->Tag() == nsWidgetAtoms::menu &&
+          menuContent->IsNodeOfType(nsINode::eXUL)) {
+        nsAutoString menuName;
+        menuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
+        nsMenuX* newMenu = new nsMenuX();
+        if (newMenu) {
+          nsresult rv = newMenu->Create(this, menuName, this, menuContent);
+          if (NS_SUCCEEDED(rv))
+            AddMenu(newMenu);
+          else
+            delete newMenu;
+        }
+      }
+    }
+  }
+
+  // Give this to the parent window. The parent takes ownership.
+  return mParent->SetMenuBar(this);
+}
+
+
+PRUint32 nsMenuBarX::GetMenuCount()
+{
+  return mMenuArray.Length();
+}
+
+
+nsresult nsMenuBarX::AddMenu(nsMenuX* aMenu)
 {
-  nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mMenuBarContent->GetDocument()));
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  // If we haven't created a global Application menu yet, do it.
+  if (!sApplicationMenu) {
+    nsresult rv = NS_OK; // avoid warning about rv being unused
+    rv = CreateApplicationMenu(aMenu);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create Application menu");
+
+    // Hook the new Application menu up to the menu bar.
+    NSMenu* mainMenu = [NSApp mainMenu];
+    NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
+    [[mainMenu itemAtIndex:0] setSubmenu:sApplicationMenu];
+  }
+
+  // keep track of all added menus
+  mMenuArray.AppendElement(aMenu); // owner
+
+  NSMenu* nativeMenu = (NSMenu*)aMenu->NativeData();
+  nsIContent* menuContent = aMenu->Content();
+  if (menuContent->GetChildCount() > 0 &&
+      !nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
+    NSMenuItem* newMenuItem = [[[NSMenuItem alloc] initWithTitle:[nativeMenu title] action:NULL keyEquivalent:@""] autorelease];
+    [mRootMenu addItem:newMenuItem];
+    [newMenuItem setSubmenu:nativeMenu];
+  }
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+
+void nsMenuBarX::RemoveMenu(PRUint32 aIndex)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NS_ASSERTION(aIndex < mMenuArray.Length(), "Attempting submenu removal with bad index!");
+
+  // Our native menu and our internal menu object array might be out of sync.
+  // This happens, for example, when a submenu is hidden. Because of this we
+  // should not assume that a native submenu is hooked up.
+  [mRootMenu removeItem:(mMenuArray[aIndex]->NativeMenuItem())];
+  mMenuArray.RemoveElementAt(aIndex);
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+
+nsMenuX* nsMenuBarX::GetMenuAt(PRUint32 aIndex)
+{
+  if (mMenuArray.Length() <= aIndex) {
+    NS_ERROR("Requesting menu at invalid index!");
+    return NULL;
+  }
+  return mMenuArray[aIndex];
+}
+
+
+nsresult nsMenuBarX::Paint()
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+  
+  NSMenu* mainMenu = [NSApp mainMenu];
+  NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
+
+  // Swap out first item into incoming menu bar. We have to keep the same menu item for the
+  // Application menu and its submenu is global so we keep passing it along.
+  NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
+  [mainMenu removeItemAtIndex:0];
+  [mRootMenu insertItem:firstMenuItem atIndex:0];
+  [firstMenuItem release];
+
+  // Set menu bar and event target.
+  [NSApp setMainMenu:mRootMenu];
+  nsMenuBarX::sLastGeckoMenuBarPainted = this;
+
+  gSomeMenuBarPainted = YES;
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+
+// Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
+// the caller can hang onto it if they so choose. It is acceptable to pass nsull
+// for |outHiddenNode| if the caller doesn't care about the hidden node.
+void nsMenuBarX::HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode)
+{
+  nsCOMPtr<nsIDOMElement> menuItem;
+  inDoc->GetElementById(inID, getter_AddRefs(menuItem));  
+  nsCOMPtr<nsIContent> menuContent(do_QueryInterface(menuItem));
+  if (menuContent) {
+    menuContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, NS_LITERAL_STRING("true"), PR_FALSE);
+    if (outHiddenNode) {
+      *outHiddenNode = menuContent.get();
+      NS_IF_ADDREF(*outHiddenNode);
+    }
+  }
+}
+
+
+// Do what is necessary to conform to the Aqua guidelines for menus.
+void nsMenuBarX::AquifyMenuBar()
+{
+  nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mContent->GetDocument()));
   if (domDoc) {
     // remove the "About..." item and its separator
     HideItem(domDoc, NS_LITERAL_STRING("aboutSeparator"), nsnull);
     HideItem(domDoc, NS_LITERAL_STRING("aboutName"), getter_AddRefs(mAboutItemContent));
     if (!sAboutItemContent)
       sAboutItemContent = mAboutItemContent;
 
     // remove quit item and its separator
@@ -172,155 +321,23 @@ nsMenuBarX::AquifyMenuBar()
     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_services"), nsnull);
     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_app"), nsnull);
     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_others"), nsnull);
     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_show_all"), nsnull);
   }
 }
 
 
-// Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
-// the caller can hang onto it if they so choose. It is acceptable to pass nsull
-// for |outHiddenNode| if the caller doesn't care about the hidden node.
-void
-nsMenuBarX::HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode)
-{
-  nsCOMPtr<nsIDOMElement> menuItem;
-  inDoc->GetElementById(inID, getter_AddRefs(menuItem));  
-  nsCOMPtr<nsIContent> menuContent(do_QueryInterface(menuItem));
-  if (menuContent) {
-    menuContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, NS_LITERAL_STRING("true"), PR_FALSE);
-    if (outHiddenNode) {
-      *outHiddenNode = menuContent.get();
-      NS_IF_ADDREF(*outHiddenNode);
-    }
-  }
-}
-
-
-NS_IMETHODIMP
-nsMenuBarX::MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, void * aMenubarNode)
-{
-  nsIDOMNode* domNode  = static_cast<nsIDOMNode*>(aMenubarNode);
-  mMenuBarContent = do_QueryInterface(domNode); // strong ref
-  NS_ASSERTION(mMenuBarContent, "No content specified for this menubar");
-  if (!mMenuBarContent)
-    return NS_ERROR_FAILURE;
-
-  SetParent(aParentWindow);
-
-  AquifyMenuBar();
-
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  domNode->GetOwnerDocument(getter_AddRefs(domDoc));
-  nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
-  if (!doc)
-    return NS_ERROR_FAILURE;
-  doc->AddMutationObserver(this);
-  mDocument = doc;
-
-  PRUint32 count = mMenuBarContent->GetChildCount();
-  for (PRUint32 i = 0; i < count; i++) { 
-    nsIContent *menu = mMenuBarContent->GetChildAt(i);
-    if (menu) {
-      if (menu->Tag() == nsWidgetAtoms::menu &&
-          menu->IsNodeOfType(nsINode::eXUL)) {
-        nsAutoString menuName;
-        nsAutoString menuAccessKey(NS_LITERAL_STRING(" "));
-        menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
-        menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::accesskey, menuAccessKey);
-
-        // Create nsMenu, the menubar will own it
-        nsCOMPtr<nsIMenu> pnsMenu(do_CreateInstance(kMenuCID));
-        if (pnsMenu) {
-          pnsMenu->Create(static_cast<nsIMenuBar*>(this), menuName, menuAccessKey, this, menu);
-          AddMenu(pnsMenu);
-        }
-      } 
-    }
-  }
-  
-  // Give the aParentWindow this nsMenuBarX to hold onto.
-  // The parent takes ownership.
-  aParentWindow->SetMenuBar(this);
-  
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::Create(nsIWidget *aParent)
-{
-  SetParent(aParent);
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::GetParent(nsIWidget *&aParent)
-{
-  NS_IF_ADDREF(aParent = mParent);
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::SetParent(nsIWidget *aParent)
-{
-  mParent = aParent; // weak ref  
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::AddMenu(nsIMenu * aMenu)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
-  // If we haven't created a global Application menu yet, do it.
-  if (!sApplicationMenu) {
-    nsresult rv = NS_OK; // avoid warning about rv being unused
-    rv = CreateApplicationMenu(aMenu);
-    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create Application menu");
-
-    // Hook the new Application menu up to the menu bar.
-    NSMenu* mainMenu = [NSApp mainMenu];
-    NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
-    [[mainMenu itemAtIndex:0] setSubmenu:sApplicationMenu];
-  }
-
-  // keep track of all added menus
-  mMenusArray.AppendObject(aMenu); // owner
-  
-  NSMenu* nativeMenu = NULL;
-  aMenu->GetNativeData((void**)&nativeMenu);
-  
-  nsCOMPtr<nsIContent> menu;
-  aMenu->GetMenuContent(getter_AddRefs(menu));
-  if (menu->GetChildCount() > 0 &&
-      !NodeIsHiddenOrCollapsed(menu)) {
-    NSMenuItem* newMenuItem = [[[NSMenuItem alloc] initWithTitle:[nativeMenu title] action:NULL keyEquivalent:@""] autorelease];
-    [mRootMenu addItem:newMenuItem];
-    [newMenuItem setSubmenu:nativeMenu];
-  }
-  
-  return NS_OK;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-
 // for creating menu items destined for the Application menu
-NSMenuItem* nsMenuBarX::CreateNativeAppMenuItem(nsIMenu* inMenu, const nsAString& nodeID, SEL action,
+NSMenuItem* nsMenuBarX::CreateNativeAppMenuItem(nsMenuX* inMenu, const nsAString& nodeID, SEL action,
                                                 int tag, NativeMenuItemTarget* target)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-  nsCOMPtr<nsIContent> menu;
-  inMenu->GetMenuContent(getter_AddRefs(menu));
-  if (!menu)
-    return nil;
-
-  nsCOMPtr<nsIDocument> doc = menu->GetDocument();
+  nsCOMPtr<nsIDocument> doc = inMenu->Content()->GetDocument();
   if (!doc)
     return nil;
 
   nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(doc));
   if (!domdoc)
     return nil;
 
   // Get information from the gecko menu item
@@ -351,18 +368,18 @@ NSMenuItem* nsMenuBarX::CreateNativeAppM
       nsAutoString keyChar(NS_LITERAL_STRING(" "));
       keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, keyChar);
       if (!keyChar.EqualsLiteral(" ")) {
         keyEquiv = [[NSString stringWithCharacters:keyChar.get() length:keyChar.Length()] lowercaseString];
       }
       // now grab the key equivalent modifiers
       nsAutoString modifiersStr;
       keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::modifiers, modifiersStr);
-      PRUint8 geckoModifiers = MenuHelpersX::GeckoModifiersForNodeAttribute(modifiersStr);
-      macKeyModifiers = MenuHelpersX::MacModifiersForGeckoModifiers(geckoModifiers);
+      PRUint8 geckoModifiers = nsMenuUtilsX::GeckoModifiersForNodeAttribute(modifiersStr);
+      macKeyModifiers = nsMenuUtilsX::MacModifiersForGeckoModifiers(geckoModifiers);
     }
   }
   // get the label into NSString-form
   NSString* labelString = [NSString stringWithCharacters:label.get() length:label.Length()];
   
   if (!labelString)
     labelString = @"";
   if (!keyEquiv)
@@ -377,18 +394,17 @@ NSMenuItem* nsMenuBarX::CreateNativeAppM
   
   return newMenuItem;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 
 // build the Application menu shared by all menu bars
-nsresult
-nsMenuBarX::CreateApplicationMenu(nsIMenu* inMenu)
+nsresult nsMenuBarX::CreateApplicationMenu(nsMenuX* inMenu)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   // At this point, the application menu is the application menu from
   // the nib in cocoa widgets. We do not have a way to create an application
   // menu manually, so we grab the one from the nib and use that.
   sApplicationMenu = [[[[NSApp mainMenu] itemAtIndex:0] submenu] retain];
   
@@ -531,160 +547,79 @@ nsMenuBarX::CreateApplicationMenu(nsIMen
   }
   
   return (sApplicationMenu) ? NS_OK : NS_ERROR_FAILURE;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-NS_IMETHODIMP nsMenuBarX::GetMenuCount(PRUint32 &aCount)
-{
-  aCount = mMenusArray.Count();
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::GetMenuAt(const PRUint32 aCount, nsIMenu *& aMenu)
-{ 
-  aMenu = NULL;
-  nsCOMPtr<nsIMenu> menu = mMenusArray.ObjectAt(aCount);
-  if (!menu)
-    return NS_OK;
-  
-  return CallQueryInterface(menu, &aMenu); // addref
-}
-
-
-NS_IMETHODIMP nsMenuBarX::InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu)
+void nsMenuBarX::SetParent(nsIWidget* aParent)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::RemoveMenu(const PRUint32 aCount)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
-  mMenusArray.RemoveObjectAt(aCount);
-  [mRootMenu removeItemAtIndex:aCount];
-  return NS_OK;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::RemoveAll()
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
+  mParent = aParent;
 }
 
-
-NS_IMETHODIMP nsMenuBarX::GetNativeData(void *& aData)
-{
-  aData = (void*)mRootMenu;
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::SetNativeData(void* aData)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-NS_IMETHODIMP nsMenuBarX::Paint()
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
-  NSMenu* mainMenu = [NSApp mainMenu];
-  NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
-
-  // Swap out first item into incoming menu bar. We have to keep the same menu item for the
-  // Application menu and its submenu is global so we keep passing it along.
-  NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
-  [mainMenu removeItemAtIndex:0];
-  [mRootMenu insertItem:firstMenuItem atIndex:0];
-  [firstMenuItem release];
-
-  // Set menu bar and event target.
-  [NSApp setMainMenu:mRootMenu];
-  nsMenuBarX::sLastGeckoMenuBarPainted = this;
-
-  gSomeMenuBarPainted = YES;
-
-  return NS_OK;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-
 //
 // nsIMutationObserver
 //
 
-void
-nsMenuBarX::CharacterDataWillChange(nsIDocument * aDocument,
-                                    nsIContent * aContent,
-                                    CharacterDataChangeInfo * aInfo)
-{
-}
-
-void
-nsMenuBarX::CharacterDataChanged(nsIDocument * aDocument,
-                                 nsIContent * aContent,
-                                 CharacterDataChangeInfo * aInfo)
+void nsMenuBarX::CharacterDataWillChange(nsIDocument* aDocument,
+                                         nsIContent* aContent,
+                                         CharacterDataChangeInfo* aInfo)
 {
 }
 
 
-void
-nsMenuBarX::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
-                            PRInt32 aNewIndexInContainer)
+void nsMenuBarX::CharacterDataChanged(nsIDocument* aDocument,
+                                      nsIContent* aContent,
+                                      CharacterDataChangeInfo* aInfo)
 {
-  if (aContainer != mMenuBarContent) {
+}
+
+
+void nsMenuBarX::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
+                                 PRInt32 aNewIndexInContainer)
+{
+  if (aContainer != mContent) {
     nsChangeObserver* obs = LookupContentChangeObserver(aContainer);
     if (obs)
       obs->ObserveContentInserted(aDocument, aContainer, aNewIndexInContainer);
     else {
       nsCOMPtr<nsIContent> parent = aContainer->GetParent();
       if (parent) {
         obs = LookupContentChangeObserver(parent);
         if (obs)
           obs->ObserveContentInserted(aDocument, aContainer, aNewIndexInContainer);
       }
     }
   }
 }
 
 
-void
-nsMenuBarX::NodeWillBeDestroyed(const nsINode * aNode)
+void nsMenuBarX::NodeWillBeDestroyed(const nsINode * aNode)
 {
   mDocument = nsnull;
 }
 
 
-void
-nsMenuBarX::AttributeChanged(nsIDocument * aDocument, nsIContent * aContent,
-                             PRInt32 aNameSpaceID, nsIAtom * aAttribute,
-                             PRInt32 aModType, PRUint32 aStateMask)
+void nsMenuBarX::AttributeChanged(nsIDocument * aDocument, nsIContent * aContent,
+                                  PRInt32 aNameSpaceID, nsIAtom * aAttribute,
+                                  PRInt32 aModType, PRUint32 aStateMask)
 {
   // lookup and dispatch to registered thang
   nsChangeObserver* obs = LookupContentChangeObserver(aContent);
   if (obs)
     obs->ObserveAttributeChanged(aDocument, aContent, aAttribute);
 }
 
 
-void
-nsMenuBarX::ContentRemoved(nsIDocument * aDocument, nsIContent * aContainer,
-                           nsIContent * aChild, PRInt32 aIndexInContainer)
+void nsMenuBarX::ContentRemoved(nsIDocument * aDocument, nsIContent * aContainer,
+                                nsIContent * aChild, PRInt32 aIndexInContainer)
 {
-  if (aContainer == mMenuBarContent) {
+  if (aContainer == mContent) {
     UnregisterForContentChanges(aChild);
     RemoveMenu(aIndexInContainer);
   }
   else {
     nsChangeObserver* obs = LookupContentChangeObserver(aContainer);
     if (obs) {
       obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer);
     }
@@ -695,74 +630,68 @@ nsMenuBarX::ContentRemoved(nsIDocument *
         if (obs)
           obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer);
       }
     }
   }
 }
 
 
-void
-nsMenuBarX::ContentInserted(nsIDocument * aDocument, nsIContent * aContainer,
-                             nsIContent * aChild, PRInt32 aIndexInContainer)
+void nsMenuBarX::ContentInserted(nsIDocument * aDocument, nsIContent * aContainer,
+                                 nsIContent * aChild, PRInt32 aIndexInContainer)
 {
-  if (aContainer != mMenuBarContent) {
+  if (aContainer != mContent) {
     nsChangeObserver* obs = LookupContentChangeObserver(aContainer);
     if (obs)
       obs->ObserveContentInserted(aDocument, aChild, aIndexInContainer);
     else {
       nsCOMPtr<nsIContent> parent = aContainer->GetParent();
       if (parent) {
         obs = LookupContentChangeObserver(parent);
         if (obs)
           obs->ObserveContentInserted(aDocument, aChild, aIndexInContainer);
       }
     }
   }
 }
 
 
-void
-nsMenuBarX::ParentChainChanged(nsIContent *aContent)
+void nsMenuBarX::ParentChainChanged(nsIContent *aContent)
 {
 }
 
 
 // For change management, we don't use a |nsSupportsHashtable| because we know that the
 // lifetime of all these items is bounded by the lifetime of the menubar. No need to add
 // any more strong refs to the picture because the containment hierarchy already uses
 // strong refs.
-void
-nsMenuBarX::RegisterForContentChanges(nsIContent *aContent, nsChangeObserver *aMenuObject)
+void nsMenuBarX::RegisterForContentChanges(nsIContent *aContent, nsChangeObserver *aMenuObject)
 {
   nsVoidKey key(aContent);
   mObserverTable.Put(&key, aMenuObject);
 }
 
 
-void
-nsMenuBarX::UnregisterForContentChanges(nsIContent *aContent)
+void nsMenuBarX::UnregisterForContentChanges(nsIContent *aContent)
 {
   nsVoidKey key(aContent);
   mObserverTable.Remove(&key);
 }
 
 
-nsChangeObserver*
-nsMenuBarX::LookupContentChangeObserver(nsIContent* aContent)
+nsChangeObserver* nsMenuBarX::LookupContentChangeObserver(nsIContent* aContent)
 {
   nsVoidKey key(aContent);
   return reinterpret_cast<nsChangeObserver*>(mObserverTable.Get(&key));
 }
 
 
 // Given a menu item, creates a unique 4-character command ID and
 // maps it to the item. Returns the id for use by the client.
-PRUint32
-nsMenuBarX::RegisterForCommand(nsIMenuItem* inMenuItem)
+PRUint32 nsMenuBarX::RegisterForCommand(nsMenuItemX* inMenuItem)
 {
   // no real need to check for uniqueness. We always start afresh with each
   // window at 1. Even if we did get close to the reserved Apple command id's,
   // those don't start until at least '    ', which is integer 538976288. If
   // we have that many menu items in one window, I think we have other problems.
 
   // make id unique
   ++mCurrentCommandID;
@@ -772,174 +701,27 @@ nsMenuBarX::RegisterForCommand(nsIMenuIt
   mObserverTable.Put(&key, inMenuItem);
 
   return mCurrentCommandID;
 }
 
 
 // Removes the mapping between the given 4-character command ID
 // and its associated menu item.
-void
-nsMenuBarX::UnregisterCommand(PRUint32 inCommandID)
+void nsMenuBarX::UnregisterCommand(PRUint32 inCommandID)
 {
   nsPRUint32Key key(inCommandID);
   mObserverTable.Remove(&key);
 }
 
 
-nsIMenuItem*
-nsMenuBarX::GetMenuItemForCommandID(PRUint32 inCommandID)
+nsMenuItemX* nsMenuBarX::GetMenuItemForCommandID(PRUint32 inCommandID)
 {
   nsPRUint32Key key(inCommandID);
-  return reinterpret_cast<nsIMenuItem*>(mObserverTable.Get(&key));
-}
-
-
-nsEventStatus
-MenuHelpersX::DispatchCommandTo(nsIContent* aTargetContent)
-{
-  NS_PRECONDITION(aTargetContent, "null ptr");
-
-  nsEventStatus status = nsEventStatus_eConsumeNoDefault;
-  nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
-  
-  // FIXME: Should probably figure out how to init this with the actual
-  // pressed keys, but this is a big old edge case anyway. -dwh
-  
-  aTargetContent->DispatchDOMEvent(&event, nsnull, nsnull, &status);
-  return status;
-}
-
-
-NSString* MenuHelpersX::CreateTruncatedCocoaLabel(const nsString& itemLabel)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  // ::TruncateThemeText() doesn't take the number of characters to truncate to, it takes a pixel with
-  // to fit the string in. Ugh. I talked it over with sfraser and we couldn't come up with an 
-  // easy way to compute what this should be given the system font, etc, so we're just going
-  // to hard code it to something reasonable and bigger fonts will just have to deal.
-  const short kMaxItemPixelWidth = 300;
-  NSMutableString *label = [[NSMutableString stringWithCharacters:itemLabel.get() length:itemLabel.Length()] retain];
-  ::TruncateThemeText((CFMutableStringRef)label, kThemeMenuItemFont, kThemeStateActive, kMaxItemPixelWidth, truncMiddle, NULL);
-  return label; // caller releases
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
-
-PRUint8 MenuHelpersX::GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute)
-{
-  PRUint8 modifiers = knsMenuItemNoModifier;
-  char* str = ToNewCString(modifiersAttribute);
-  char* newStr;
-  char* token = nsCRT::strtok(str, ", \t", &newStr);
-  while (token != NULL) {
-    if (PL_strcmp(token, "shift") == 0)
-      modifiers |= knsMenuItemShiftModifier;
-    else if (PL_strcmp(token, "alt") == 0) 
-      modifiers |= knsMenuItemAltModifier;
-    else if (PL_strcmp(token, "control") == 0) 
-      modifiers |= knsMenuItemControlModifier;
-    else if ((PL_strcmp(token, "accel") == 0) ||
-             (PL_strcmp(token, "meta") == 0)) {
-      modifiers |= knsMenuItemCommandModifier;
-    }
-    token = nsCRT::strtok(newStr, ", \t", &newStr);
-  }
-  nsMemory::Free(str);
-
-  return modifiers;
-}
-
-
-unsigned int MenuHelpersX::MacModifiersForGeckoModifiers(PRUint8 geckoModifiers)
-{
-  unsigned int macModifiers = 0;
-  
-  if (geckoModifiers & knsMenuItemShiftModifier)
-    macModifiers |= NSShiftKeyMask;
-  if (geckoModifiers & knsMenuItemAltModifier)
-    macModifiers |= NSAlternateKeyMask;
-  if (geckoModifiers & knsMenuItemControlModifier)
-    macModifiers |= NSControlKeyMask;
-  if (geckoModifiers & knsMenuItemCommandModifier)
-    macModifiers |= NSCommandKeyMask;
-  
-  return macModifiers;
-}
-
-
-nsIMenuBar* MenuHelpersX::GetHiddenWindowMenuBar()
-{
-  nsIWidget* hiddenWindowWidgetNoCOMPtr = nsCocoaUtils::GetHiddenWindowWidget();
-  if (hiddenWindowWidgetNoCOMPtr)
-    return static_cast<nsCocoaWindow*>(hiddenWindowWidgetNoCOMPtr)->GetMenuBar();
-  else
-    return nsnull;
-}
-
-
-// It would be nice if we could localize these edit menu names.
-static NSMenuItem* standardEditMenuItem = nil;
-NSMenuItem* MenuHelpersX::GetStandardEditMenuItem()
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  if (standardEditMenuItem)
-    return standardEditMenuItem;
-
-  NSMenuItem* standardEditMenuItem = [[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""];
-  NSMenu* standardEditMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
-  [standardEditMenuItem setSubmenu:standardEditMenu];
-  [standardEditMenu release];
-
-  // Add Undo
-  NSMenuItem* undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"];
-  [standardEditMenu addItem:undoItem];
-  [undoItem release];
-
-  // Add Redo
-  NSMenuItem* redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"];
-  [standardEditMenu addItem:redoItem];
-  [redoItem release];
-
-  // Add separator
-  [standardEditMenu addItem:[NSMenuItem separatorItem]];
-  
-  // Add Cut
-  NSMenuItem* cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
-  [standardEditMenu addItem:cutItem];
-  [cutItem release];
-
-  // Add Copy
-  NSMenuItem* copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
-  [standardEditMenu addItem:copyItem];
-  [copyItem release];
-
-  // Add Paste
-  NSMenuItem* pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
-  [standardEditMenu addItem:pasteItem];
-  [pasteItem release];
-
-  // Add Delete
-  NSMenuItem* deleteItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""];
-  [standardEditMenu addItem:deleteItem];
-  [deleteItem release];
-
-  // Add Select All
-  NSMenuItem* selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
-  [standardEditMenu addItem:selectAllItem];
-  [selectAllItem release];
-
-  standardEditMenuItem = standardEditMenuItem;
-
-  return standardEditMenuItem;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+  return reinterpret_cast<nsMenuItemX*>(mObserverTable.Get(&key));
 }
 
 
 //
 // Objective-C class used to allow us to have keyboard commands
 // look like they are doing something but actually do nothing.
 // We allow mouse actions to work normally.
 //
@@ -1017,33 +799,33 @@ static BOOL gActOnSpecialCommands = YES;
   // perform native menu effects only. This avoids sending events twice,
   // which can lead to major problems.
   if (gActOnSpecialCommands) {
     // Do special processing if this is for an app-global command.
     if (tag == eCommand_ID_About) {
       nsIContent* mostSpecificContent = sAboutItemContent;
       if (menuBar && menuBar->mAboutItemContent)
         mostSpecificContent = menuBar->mAboutItemContent;
-      MenuHelpersX::DispatchCommandTo(mostSpecificContent);
+      nsMenuUtilsX::DispatchCommandTo(mostSpecificContent);
     }
     else if (tag == eCommand_ID_Prefs) {
       nsIContent* mostSpecificContent = sPrefItemContent;
       if (menuBar && menuBar->mPrefItemContent)
         mostSpecificContent = menuBar->mPrefItemContent;
-      MenuHelpersX::DispatchCommandTo(mostSpecificContent);
+      nsMenuUtilsX::DispatchCommandTo(mostSpecificContent);
     }
     else if (tag == eCommand_ID_Quit) {
       nsIContent* mostSpecificContent = sQuitItemContent;
       if (menuBar && menuBar->mQuitItemContent)
         mostSpecificContent = menuBar->mQuitItemContent;
       // If we have some content for quit we execute it. Otherwise we send a native app terminate
       // message. If you want to stop a quit from happening, provide quit content and return
       // the event as unhandled.
       if (mostSpecificContent) {
-        MenuHelpersX::DispatchCommandTo(mostSpecificContent);
+        nsMenuUtilsX::DispatchCommandTo(mostSpecificContent);
       }
       else {
         [NSApp terminate:nil];
         return;
       }
     }
     // Quit now if the "active" menu bar has changed (as the result of
     // processing an app-global command above).  This resolves bmo bug
@@ -1056,20 +838,19 @@ static BOOL gActOnSpecialCommands = YES;
   // this isn't for the hidden window menu. We assume that if there
   // is no main window then the hidden window menu bar is up, even
   // if that isn't true for some reason we better play it safe if
   // there is no main window.
   if (gPerformKeyEquivOnStack && !gActOnKeyEquiv && [NSApp mainWindow])
     return;
 
   // given the commandID, look it up in our hashtable and dispatch to
-  // that content node. Recall that we store weak pointers to the content
-  // nodes in the hash table.
+  // that menu item.
   if (menuBar) {
-    nsIMenuItem* content = menuBar->GetMenuItemForCommandID(static_cast<PRUint32>(tag));
-    if (content)
-      content->DoCommand();    
+    nsMenuItemX* menuItem = menuBar->GetMenuItemForCommandID(static_cast<PRUint32>(tag));
+    if (menuItem)
+      menuItem->DoCommand();
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 @end
new file mode 100644
--- /dev/null
+++ b/widget/src/cocoa/nsMenuBaseX.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsMenuBaseX_h_
+#define nsMenuBaseX_h_
+
+#include "nsCOMPtr.h"
+#include "nsIContent.h"
+
+enum nsMenuObjectTypeX {
+  eMenuBarObjectType,
+  eSubmenuObjectType,
+  eMenuItemObjectType
+};
+
+// All menu objects subclass this.
+// Menu bars are owned by their top-level nsIWidgets.
+// All other objects are memory-managed based on the DOM.
+// Content removal deletes them immediately and nothing else should.
+// Do not attempt to hold strong references to them or delete them.
+class nsMenuObjectX
+{
+public:
+  virtual nsMenuObjectTypeX MenuObjectType()=0;
+  virtual void*             NativeData()=0;
+  nsIContent*               Content() { return mContent; }
+
+protected:
+  nsCOMPtr<nsIContent> mContent;
+};
+
+#endif // nsMenuBaseX_h_
--- a/widget/src/cocoa/nsMenuItemIconX.h
+++ b/widget/src/cocoa/nsMenuItemIconX.h
@@ -46,29 +46,28 @@
 
 
 #include "nsCOMPtr.h"
 #include "imgIDecoderObserver.h"
 
 class nsIURI;
 class nsIContent;
 class imgIRequest;
-class nsIMenu;
+class nsMenuObjectX;
 
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 
 
 class nsMenuItemIconX : public imgIDecoderObserver
 {
 public:
-  nsMenuItemIconX(nsISupports* aMenuItem,
-                 nsIMenu*     aMenu,
-                 nsIContent*  aContent,
-                 NSMenuItem* aNativeMenuItem);
+  nsMenuItemIconX(nsMenuObjectX* aMenuItem,
+                  nsIContent*    aContent,
+                  NSMenuItem*    aNativeMenuItem);
 private:
   ~nsMenuItemIconX();
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGICONTAINEROBSERVER
   NS_DECL_IMGIDECODEROBSERVER
 
@@ -81,18 +80,15 @@ public:
 
   // LoadIcon will set a placeholder image and start a load request for the
   // icon.  The request may not complete until after LoadIcon returns.
   nsresult LoadIcon(nsIURI* aIconURI);
 
 protected:
   nsCOMPtr<nsIContent>  mContent;
   nsCOMPtr<imgIRequest> mIconRequest;
-  nsISupports*          mMenuItem;
-  nsIMenu*              mMenu;
-  MenuRef               mMenuRef;
-  PRUint16              mMenuItemIndex;
+  nsMenuObjectX*        mMenuObject;
   PRPackedBool          mLoadedIcon;
   PRPackedBool          mSetIcon;
   NSMenuItem*           mNativeMenuItem;
 };
 
 #endif // nsMenuItemIconX_h_
--- a/widget/src/cocoa/nsMenuItemIconX.mm
+++ b/widget/src/cocoa/nsMenuItemIconX.mm
@@ -40,18 +40,16 @@
  * Retrieves and displays icons in native menu items on Mac OS X.
  */
 
 
 #include "nsMenuItemIconX.h"
 
 #include "nsObjCExceptions.h"
 #include "prmem.h"
-#include "nsIMenu.h"
-#include "nsIMenuItem.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsINameSpaceManager.h"
 #include "nsWidgetAtoms.h"
 #include "nsIDOMDocumentView.h"
 #include "nsIDOMViewCSS.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMCSSStyleDeclaration.h"
@@ -59,16 +57,17 @@
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsThreadUtils.h"
 #include "nsToolkit.h"
 #include "nsNetUtil.h"
 #include "imgILoader.h"
 #include "imgIRequest.h"
 #include "gfxIImageFrame.h"
 #include "nsIImage.h"
+#include "nsMenuItemX.h"
 
 static const PRUint32 kIconWidth = 16;
 static const PRUint32 kIconHeight = 16;
 static const PRUint32 kIconBitsPerComponent = 8;
 static const PRUint32 kIconComponents = 4;
 static const PRUint32 kIconBitsPerPixel = kIconBitsPerComponent *
                                           kIconComponents;
 static const PRUint32 kIconBytesPerRow = kIconWidth * kIconBitsPerPixel / 8;
@@ -78,25 +77,21 @@ static const PRUint32 kIconBytes = kIcon
 static void
 PRAllocCGFree(void* aInfo, const void* aData, size_t aSize) {
   free((void*)aData);
 }
 
 
 NS_IMPL_ISUPPORTS2(nsMenuItemIconX, imgIContainerObserver, imgIDecoderObserver)
 
-nsMenuItemIconX::nsMenuItemIconX(nsISupports* aMenuItem,
-                               nsIMenu*     aMenu,
-                               nsIContent*  aContent,
-                               NSMenuItem* aNativeMenuItem)
+nsMenuItemIconX::nsMenuItemIconX(nsMenuObjectX* aMenuItem,
+                                 nsIContent*    aContent,
+                                 NSMenuItem*    aNativeMenuItem)
 : mContent(aContent)
-, mMenuItem(aMenuItem)
-, mMenu(aMenu)
-, mMenuRef(NULL)
-, mMenuItemIndex(0)
+, mMenuObject(aMenuItem)
 , mLoadedIcon(PR_FALSE)
 , mSetIcon(PR_FALSE)
 , mNativeMenuItem(aNativeMenuItem)
 {
   //  printf("Creating icon for menu item %d, menu %d, native item is %d\n", aMenuItem, aMenu, aNativeMenuItem);
 }
 
 
@@ -107,36 +102,24 @@ nsMenuItemIconX::~nsMenuItemIconX()
 }
 
 
 nsresult
 nsMenuItemIconX::SetupIcon()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  nsresult rv;
-  if (!mMenuRef || !mMenuItemIndex) {
-    // These values are initialized here instead of in the constructor
-    // because they depend on the parent menu, mMenu, having inserted
-    // this object into its array of children.  That can only happen after
-    // the object is constructed.
-    rv = mMenu->GetMenuRefAndItemIndexForMenuItem(mMenuItem,
-                                                  (void**)&mMenuRef,
-                                                  &mMenuItemIndex);
-    if (NS_FAILED(rv)) return rv;
-  }
-
   // Still don't have one, then something is wrong, get out of here.
   if (!mNativeMenuItem) {
     NS_ERROR("No native menu item\n");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> iconURI;
-  rv = GetIconURI(getter_AddRefs(iconURI));
+  nsresult rv = GetIconURI(getter_AddRefs(iconURI));
   if (NS_FAILED(rv)) {
     // There is no icon for this menu item. An icon might have been set
     // earlier.  Clear it.
     [mNativeMenuItem setImage:nil];
 
     return NS_OK;
   }
 
@@ -150,25 +133,24 @@ nsresult
 nsMenuItemIconX::GetIconURI(nsIURI** aIconURI)
 {
   // Mac native menu items support having both a checkmark and an icon
   // simultaneously, but this is unheard of in the cross-platform toolkit,
   // seemingly because the win32 theme is unable to cope with both at once.
   // The downside is that it's possible to get a menu item marked with a
   // native checkmark and a checkmark for an icon.  Head off that possibility
   // by pretending that no icon exists if this is a checkable menu item.
-  nsCOMPtr<nsIMenuItem> menuItem = do_QueryInterface(mMenuItem);
-  if (menuItem) {
-    nsIMenuItem::EMenuItemType menuItemType;
-    menuItem->GetMenuItemType(&menuItemType);
-    if (menuItemType != nsIMenuItem::eRegular)
+  if (mMenuObject->MenuObjectType() == eMenuItemObjectType) {
+    nsMenuItemX* menuItem = static_cast<nsMenuItemX*>(mMenuObject);
+    if (menuItem->GetMenuItemType() != eRegularMenuItemType)
       return NS_ERROR_FAILURE;
   }
 
-  if (!mContent) return NS_ERROR_FAILURE;
+  if (!mContent)
+    return NS_ERROR_FAILURE;
 
   // First, look at the content node's "image" attribute.
   nsAutoString imageURIString;
   PRBool hasImageAttr = mContent->GetAttr(kNameSpaceID_None,
                                           nsWidgetAtoms::image,
                                           imageURIString);
 
   nsresult rv;
--- a/widget/src/cocoa/nsMenuItemX.h
+++ b/widget/src/cocoa/nsMenuItemX.h
@@ -34,74 +34,73 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMenuItemX_h_
 #define nsMenuItemX_h_
 
-#include "nsIMenuItem.h"
-#include "nsString.h"
+#include "nsMenuBaseX.h"
 #include "nsChangeObserver.h"
 #include "nsAutoPtr.h"
 
 #import <Cocoa/Cocoa.h>
 
-class nsIMenu;
+class nsString;
 class nsMenuItemIconX;
+class nsMenuX;
+class nsMenuBarX;
 
-/**
- * Native menu item wrapper
- */
+enum {
+  knsMenuItemNoModifier      = 0,
+  knsMenuItemShiftModifier   = (1 << 0),
+  knsMenuItemAltModifier     = (1 << 1),
+  knsMenuItemControlModifier = (1 << 2),
+  knsMenuItemCommandModifier = (1 << 3)
+};
 
-class nsMenuItemX : public nsIMenuItem,
+enum EMenuItemType {
+  eRegularMenuItemType = 0,
+  eCheckboxMenuItemType,
+  eRadioMenuItemType,
+  eSeparatorMenuItemType
+};
+
+
+// Once instantiated, this object lives until its DOM node or its parent window is destroyed.
+// Do not hold references to this, they can become invalid any time the DOM node can be destroyed.
+class nsMenuItemX : public nsMenuObjectX,
                     public nsChangeObserver
 {
 public:
   nsMenuItemX();
   virtual ~nsMenuItemX();
 
-  // nsISupports
-  NS_DECL_ISUPPORTS
   NS_DECL_CHANGEOBSERVER
 
-  // nsIMenuItem Methods
-  NS_IMETHOD Create(nsIMenu* aParent, const nsString & aLabel, EMenuItemType aItemType,
-                    nsMenuBarX* aMenuBar, nsIContent* aNode);
-  NS_IMETHOD GetLabel(nsString &aText);
-  NS_IMETHOD GetShortcutChar(nsString &aText);
-  NS_IMETHOD GetEnabled(PRBool *aIsEnabled);
-  NS_IMETHOD SetChecked(PRBool aIsEnabled);
-  NS_IMETHOD GetChecked(PRBool *aIsEnabled);
-  NS_IMETHOD GetMenuItemType(EMenuItemType *aIsCheckbox);
-  NS_IMETHOD GetNativeData(void*& aData);
-  NS_IMETHOD IsSeparator(PRBool & aIsSep);
+  // nsMenuObjectX
+  void*             NativeData()     {return (void*)mNativeMenuItem;}
+  nsMenuObjectTypeX MenuObjectType() {return eMenuItemObjectType;}
 
-  NS_IMETHOD DoCommand();
-  NS_IMETHOD DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled);
-  NS_IMETHOD SetupIcon();
-  NS_IMETHOD GetMenuItemContent(nsIContent ** aMenuItemContent);
+  // nsMenuItemX
+  nsresult      Create(nsMenuX* aParent, const nsString& aLabel, EMenuItemType aItemType,
+                       nsMenuBarX* aMenuBar, nsIContent* aNode);
+  nsresult      SetChecked(PRBool aIsChecked);
+  EMenuItemType GetMenuItemType();
+  void          DoCommand();
+  nsresult      DispatchDOMEvent(const nsString &eventName, PRBool* preventDefaultCalled);
+  void          SetupIcon();
 
 protected:
-
   void UncheckRadioSiblings(nsIContent* inCheckedElement);
   void SetKeyEquiv(PRUint8 aModifiers, const nsString &aText);
 
-  NSMenuItem*               mNativeMenuItem;       // strong ref, we own
-  
-  nsString                  mLabel;
-  nsString                  mKeyEquivalent;
-
-  nsIMenu*                  mMenuParent;          // weak, parent owns us
-  nsMenuBarX*               mMenuBar;             // weak
-  
-  nsCOMPtr<nsIContent>      mContent;
+  EMenuItemType             mType;
+  NSMenuItem*               mNativeMenuItem;      // [strong]
+  nsMenuX*                  mMenuParent;          // [weak]
+  nsMenuBarX*               mMenuBar;             // [weak]
   nsCOMPtr<nsIContent>      mCommandContent;
   nsRefPtr<nsMenuItemIconX> mIcon;
-  
-  PRUint8           mModifiers;
-  PRPackedBool      mEnabled;
-  PRPackedBool      mIsChecked;
-  EMenuItemType     mType; // regular, checkbox, radio, or separator
+  PRBool                    mIsChecked;
 };
 
 #endif // nsMenuItemX_h_
--- a/widget/src/cocoa/nsMenuItemX.mm
+++ b/widget/src/cocoa/nsMenuItemX.mm
@@ -31,49 +31,45 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsCOMPtr.h"
-#include "nsIContent.h"
+#include "nsMenuItemX.h"
+#include "nsMenuBarX.h"
+#include "nsMenuX.h"
+#include "nsMenuItemIconX.h"
+#include "nsMenuUtilsX.h"
+
 #include "nsObjCExceptions.h"
-#include "nsMenuBarX.h"  // for MenuHelpers namespace
-#include "nsMenuItemX.h"
-#include "nsIMenu.h"
-#include "nsIMenuBar.h"
+
+#include "nsCOMPtr.h"
+#include "nsWidgetAtoms.h"
+#include "nsGUIEvent.h"
+
+#include "nsIContent.h"
 #include "nsIWidget.h"
-#include "nsINameSpaceManager.h"
-#include "nsWidgetAtoms.h"
-#include "nsIServiceManager.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMDocumentEvent.h"
-
-#include "nsMenuItemIconX.h"
-#include "nsGUIEvent.h"
-
-
-NS_IMPL_ISUPPORTS1(nsMenuItemX, nsIMenuItem)
+#include "nsIDOMElement.h"
 
 
 nsMenuItemX::nsMenuItemX()
 {
-  mNativeMenuItem     = nil;
-  mMenuParent         = nsnull;
-  mMenuBar            = nsnull;
-  mKeyEquivalent.AssignLiteral(" ");
-  mEnabled            = PR_TRUE;
-  mIsChecked          = PR_FALSE;
-  mType               = eRegular;
+  mType           = eRegularMenuItemType;
+  mNativeMenuItem = nil;
+  mMenuParent     = nsnull;
+  mMenuBar        = nsnull;
+  mIsChecked      = PR_FALSE;
 }
 
 
 nsMenuItemX::~nsMenuItemX()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [mNativeMenuItem autorelease];
@@ -81,29 +77,28 @@ nsMenuItemX::~nsMenuItemX()
     mMenuBar->UnregisterForContentChanges(mContent);
   if (mCommandContent)
     mMenuBar->UnregisterForContentChanges(mCommandContent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
-NS_METHOD nsMenuItemX::Create(nsIMenu* aParent, const nsString & aLabel, EMenuItemType aItemType,
-                              nsMenuBarX* aMenuBar, nsIContent* aNode)
+nsresult nsMenuItemX::Create(nsMenuX* aParent, const nsString& aLabel, EMenuItemType aItemType,
+                             nsMenuBarX* aMenuBar, nsIContent* aNode)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  mContent = aNode;      // addref
-  mMenuParent = aParent; // weak
+  mType = aItemType;
+  mMenuParent = aParent;
+  mContent = aNode;
 
-  mType = aItemType;
+  mMenuBar = aMenuBar;
+  NS_ASSERTION(mMenuBar, "No menu bar given, must have one!");
 
-  // register for AttributeChanged messages
-  mMenuBar = aMenuBar;
-  NS_ASSERTION(mMenuBar, "No menu bar given, must have one");
   mMenuBar->RegisterForContentChanges(mContent, this);
 
   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mContent->GetCurrentDoc()));
 
   // if we have a command associated with this menu item, register for changes
   // to the command DOM node
   if (domDoc) {
     nsAutoString ourCommand;
@@ -115,89 +110,72 @@ NS_METHOD nsMenuItemX::Create(nsIMenu* a
 
       if (commandElement) {
         mCommandContent = do_QueryInterface(commandElement);
         // register to observe the command DOM element
         mMenuBar->RegisterForContentChanges(mCommandContent, this);
       }
     }
   }
-  
-  // set up mEnabled based on command content if it exists, otherwise do it based
+
+  // decide enabled state based on command content if it exists, otherwise do it based
   // on our own content
+  PRBool isEnabled;
   if (mCommandContent)
-    mEnabled = !mCommandContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled, nsWidgetAtoms::_true, eCaseMatters);
+    isEnabled = !mCommandContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled, nsWidgetAtoms::_true, eCaseMatters);
   else
-    mEnabled = !mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled, nsWidgetAtoms::_true, eCaseMatters);
-  
-  mLabel = aLabel;
-  
+    isEnabled = !mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled, nsWidgetAtoms::_true, eCaseMatters);
+
   // set up the native menu item
-  if (mType == nsIMenuItem::eSeparator) {
+  if (mType == eSeparatorMenuItemType) {
     mNativeMenuItem = [[NSMenuItem separatorItem] retain];
   }
   else {
-    NSString *newCocoaLabelString = MenuHelpersX::CreateTruncatedCocoaLabel(mLabel);
+    NSString *newCocoaLabelString = nsMenuUtilsX::CreateTruncatedCocoaLabel(aLabel);
     mNativeMenuItem = [[NSMenuItem alloc] initWithTitle:newCocoaLabelString action:nil keyEquivalent:@""];
     [newCocoaLabelString release];
-    
-    [mNativeMenuItem setEnabled:(BOOL)mEnabled];
+
+    [mNativeMenuItem setEnabled:(BOOL)isEnabled];
 
     SetChecked(mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::checked,
                                      nsWidgetAtoms::_true, eCaseMatters));
 
     // Set key shortcut and modifiers
     if (domDoc) {
       nsAutoString keyValue;
       mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, keyValue);
-
       if (!keyValue.IsEmpty()) {
         nsCOMPtr<nsIDOMElement> keyElement;
         domDoc->GetElementById(keyValue, getter_AddRefs(keyElement));
-
         if (keyElement) {
           nsCOMPtr<nsIContent> keyContent(do_QueryInterface(keyElement));
           nsAutoString keyChar(NS_LITERAL_STRING(" "));
           keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, keyChar);
 
           nsAutoString modifiersStr;
           keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::modifiers, modifiersStr);
-          PRUint8 modifiers = MenuHelpersX::GeckoModifiersForNodeAttribute(modifiersStr);
+          PRUint8 modifiers = nsMenuUtilsX::GeckoModifiersForNodeAttribute(modifiersStr);
 
           SetKeyEquiv(modifiers, keyChar);
         }
       }
     }
   }
 
-  mIcon = new nsMenuItemIconX(static_cast<nsIMenuItem*>(this), mMenuParent, mContent, mNativeMenuItem);
-  
+  mIcon = new nsMenuItemIconX(this, mContent, mNativeMenuItem);
+  if (!mIcon)
+    return NS_ERROR_OUT_OF_MEMORY;
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-NS_METHOD
-nsMenuItemX::GetLabel(nsString &aText)
-{
-  aText = mLabel;
-  return NS_OK;
-}
-
-
-NS_METHOD 
-nsMenuItemX::GetEnabled(PRBool *aIsEnabled)
-{
-  *aIsEnabled = mEnabled;
-  return NS_OK;
-}
-
-
-NS_METHOD nsMenuItemX::SetChecked(PRBool aIsChecked)
+nsresult nsMenuItemX::SetChecked(PRBool aIsChecked)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   mIsChecked = aIsChecked;
   
   // update the content model. This will also handle unchecking our siblings
   // if we are a radiomenu
   mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::checked, 
@@ -210,120 +188,89 @@ NS_METHOD nsMenuItemX::SetChecked(PRBool
     [mNativeMenuItem setState:NSOffState];
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-NS_METHOD nsMenuItemX::GetChecked(PRBool *aIsEnabled)
-{
-  *aIsEnabled = mIsChecked;
-  return NS_OK;
-}
-
-
-NS_METHOD nsMenuItemX::GetMenuItemType(EMenuItemType *aType)
+EMenuItemType nsMenuItemX::GetMenuItemType()
 {
-  *aType = mType;
-  return NS_OK;
-}
-
-
-NS_METHOD nsMenuItemX::GetNativeData(void *& aData)
-{
-  aData = mNativeMenuItem;
-  return NS_OK;
-}
-
-
-NS_METHOD nsMenuItemX::IsSeparator(PRBool & aIsSep)
-{
-  aIsSep = (mType == nsIMenuItem::eSeparator);
-  return NS_OK;
+  return mType;
 }
 
 
 // Executes the "cached" javaScript command.
 // Returns NS_OK if the command was executed properly, otherwise an error code.
-NS_IMETHODIMP nsMenuItemX::DoCommand()
+void nsMenuItemX::DoCommand()
 {
   // flip "checked" state if we're a checkbox menu, or an un-checked radio menu
-  if (mType == nsIMenuItem::eCheckbox ||
-      (mType == nsIMenuItem::eRadio && !mIsChecked)) {
+  if (mType == eCheckboxMenuItemType ||
+      (mType == eRadioMenuItemType && !mIsChecked)) {
     if (!mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::autocheck,
                                nsWidgetAtoms::_false, eCaseMatters))
     SetChecked(!mIsChecked);
     /* the AttributeChanged code will update all the internal state */
   }
 
   nsEventStatus status = nsEventStatus_eIgnore;
   nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
 
   mContent->DispatchDOMEvent(&event, nsnull, nsnull, &status);
-  return NS_OK;
 }
     
 
-NS_IMETHODIMP nsMenuItemX::DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled)
+nsresult nsMenuItemX::DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled)
 {
   if (!mContent)
     return NS_ERROR_FAILURE;
-  
+
   // get owner document for content
   nsCOMPtr<nsIDocument> parentDoc = mContent->GetOwnerDoc();
   if (!parentDoc) {
     NS_WARNING("Failed to get owner nsIDocument for menu item content");
     return NS_ERROR_FAILURE;
   }
-  
+
   // get interface for creating DOM events from content owner document
   nsCOMPtr<nsIDOMDocumentEvent> DOMEventFactory = do_QueryInterface(parentDoc);
   if (!DOMEventFactory) {
     NS_WARNING("Failed to QI parent nsIDocument to nsIDOMDocumentEvent");
     return NS_ERROR_FAILURE;
   }
-  
+
   // create DOM event
   nsCOMPtr<nsIDOMEvent> event;
   nsresult rv = DOMEventFactory->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to create nsIDOMEvent");
     return rv;
   }
   event->InitEvent(eventName, PR_TRUE, PR_TRUE);
-  
+
   // mark DOM event as trusted
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
   privateEvent->SetTrusted(PR_TRUE);
-  
+
   // send DOM event
   nsCOMPtr<nsIDOMEventTarget> eventTarget = do_QueryInterface(mContent);
   rv = eventTarget->DispatchEvent(event, preventDefaultCalled);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to send DOM event via nsIDOMEventTarget");
     return rv;
   }
-  
+
   return NS_OK;  
 }
 
 
-NS_METHOD nsMenuItemX::GetShortcutChar(nsString &aText)
-{
-  aText = mKeyEquivalent;
-  return NS_OK;
-}
-
-
 // Walk the sibling list looking for nodes with the same name and
 // uncheck them all.
-void
-nsMenuItemX::UncheckRadioSiblings(nsIContent* inCheckedContent)
+void nsMenuItemX::UncheckRadioSiblings(nsIContent* inCheckedContent)
 {
   nsAutoString myGroupName;
   inCheckedContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::name, myGroupName);
   if (!myGroupName.Length()) // no groupname, nothing to do
     return;
   
   nsCOMPtr<nsIContent> parent = inCheckedContent->GetParent();
   if (!parent)
@@ -344,23 +291,21 @@ nsMenuItemX::UncheckRadioSiblings(nsICon
   }
 }
 
 
 void nsMenuItemX::SetKeyEquiv(PRUint8 aModifiers, const nsString &aText)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  mModifiers = aModifiers;
-  unsigned int macModifiers = MenuHelpersX::MacModifiersForGeckoModifiers(mModifiers);
+  unsigned int macModifiers = nsMenuUtilsX::MacModifiersForGeckoModifiers(aModifiers);
   [mNativeMenuItem setKeyEquivalentModifierMask:macModifiers];
 
-  mKeyEquivalent = aText;
-  NSString *keyEquivalent = [[NSString stringWithCharacters:(unichar*)mKeyEquivalent.get()
-                                                     length:mKeyEquivalent.Length()] lowercaseString];
+  NSString *keyEquivalent = [[NSString stringWithCharacters:(unichar*)aText.get()
+                                                     length:aText.Length()] lowercaseString];
   if ([keyEquivalent isEqualToString:@" "])
     [mNativeMenuItem setKeyEquivalent:@""];
   else
     [mNativeMenuItem setKeyEquivalent:keyEquivalent];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
@@ -377,17 +322,17 @@ nsMenuItemX::ObserveAttributeChanged(nsI
 
   if (!aContent)
     return;
   
   if (aContent == mContent) { // our own content node changed
     if (aAttribute == nsWidgetAtoms::checked) {
       // if we're a radio menu, uncheck our sibling radio items. No need to
       // do any of this if we're just a normal check menu.
-      if (mType == nsIMenuItem::eRadio) {
+      if (mType == eRadioMenuItemType) {
         if (mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::checked,
                                   nsWidgetAtoms::_true, eCaseMatters))
           UncheckRadioSiblings(mContent);
       }
       mMenuParent->SetRebuild(PR_TRUE);
     }
     else if (aAttribute == nsWidgetAtoms::hidden ||
              aAttribute == nsWidgetAtoms::collapsed ||
@@ -427,44 +372,30 @@ nsMenuItemX::ObserveAttributeChanged(nsI
         [mNativeMenuItem setEnabled:YES];
     }
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
-void
-nsMenuItemX::ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
+void nsMenuItemX::ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
 {
   if (aChild == mCommandContent) {
     mMenuBar->UnregisterForContentChanges(mCommandContent);
     mCommandContent = nsnull;
   }
 
   mMenuParent->SetRebuild(PR_TRUE);
 }
 
 
-void
-nsMenuItemX::ObserveContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
+void nsMenuItemX::ObserveContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
 {
   mMenuParent->SetRebuild(PR_TRUE);
 }
 
 
-NS_IMETHODIMP
-nsMenuItemX::SetupIcon()
+void nsMenuItemX::SetupIcon()
 {
-  if (!mIcon)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  return mIcon->SetupIcon();
+  if (mIcon)
+    mIcon->SetupIcon();
 }
-
-
-NS_IMETHODIMP
-nsMenuItemX::GetMenuItemContent(nsIContent ** aMenuItemContent)
-{
-  NS_ENSURE_ARG_POINTER(aMenuItemContent);
-  NS_IF_ADDREF(*aMenuItemContent = mContent);
-  return NS_OK;
-}
new file mode 100644
--- /dev/null
+++ b/widget/src/cocoa/nsMenuUtilsX.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsMenuUtilsX_h_
+#define nsMenuUtilsX_h_
+
+#include "nscore.h"
+#include "nsGUIEvent.h"
+
+#import <Cocoa/Cocoa.h>
+
+class nsIContent;
+class nsString;
+class nsMenuBarX;
+
+// Namespace containing utility functions used in our native menu implementation.
+namespace nsMenuUtilsX
+{
+  nsEventStatus DispatchCommandTo(nsIContent* aTargetContent);
+  NSString*     CreateTruncatedCocoaLabel(const nsString& itemLabel); // returned object is not retained
+  PRUint8       GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute);
+  unsigned int  MacModifiersForGeckoModifiers(PRUint8 geckoModifiers);
+  nsMenuBarX*   GetHiddenWindowMenuBar(); // returned object is not retained
+  NSMenuItem*   GetStandardEditMenuItem(); // returned object is not retained
+  PRBool        NodeIsHiddenOrCollapsed(nsIContent* inContent);
+}
+
+#endif // nsMenuUtilsX_h_
new file mode 100644
--- /dev/null
+++ b/widget/src/cocoa/nsMenuUtilsX.mm
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Josh Aas <josh@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsMenuUtilsX.h"
+#include "nsMenuBarX.h"
+#include "nsMenuItemX.h"
+#include "nsObjCExceptions.h"
+#include "nsCocoaUtils.h"
+#include "nsCocoaWindow.h"
+#include "nsWidgetAtoms.h"
+
+#import <Carbon/Carbon.h>
+
+nsEventStatus nsMenuUtilsX::DispatchCommandTo(nsIContent* aTargetContent)
+{
+  NS_PRECONDITION(aTargetContent, "null ptr");
+
+  nsEventStatus status = nsEventStatus_eConsumeNoDefault;
+  nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
+
+  // FIXME: Should probably figure out how to init this with the actual
+  // pressed keys, but this is a big old edge case anyway. -dwh
+
+  aTargetContent->DispatchDOMEvent(&event, nsnull, nsnull, &status);
+  return status;
+}
+
+
+NSString* nsMenuUtilsX::CreateTruncatedCocoaLabel(const nsString& itemLabel)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+  // ::TruncateThemeText() doesn't take the number of characters to truncate to, it takes a pixel with
+  // to fit the string in. Ugh. I talked it over with sfraser and we couldn't come up with an 
+  // easy way to compute what this should be given the system font, etc, so we're just going
+  // to hard code it to something reasonable and bigger fonts will just have to deal.
+  const short kMaxItemPixelWidth = 300;
+  NSMutableString *label = [[NSMutableString stringWithCharacters:itemLabel.get() length:itemLabel.Length()] retain];
+  ::TruncateThemeText((CFMutableStringRef)label, kThemeMenuItemFont, kThemeStateActive, kMaxItemPixelWidth, truncMiddle, NULL);
+  return label; // caller releases
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+
+PRUint8 nsMenuUtilsX::GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute)
+{
+  PRUint8 modifiers = knsMenuItemNoModifier;
+  char* str = ToNewCString(modifiersAttribute);
+  char* newStr;
+  char* token = strtok_r(str, ", \t", &newStr);
+  while (token != NULL) {
+    if (strcmp(token, "shift") == 0)
+      modifiers |= knsMenuItemShiftModifier;
+    else if (strcmp(token, "alt") == 0) 
+      modifiers |= knsMenuItemAltModifier;
+    else if (strcmp(token, "control") == 0) 
+      modifiers |= knsMenuItemControlModifier;
+    else if ((strcmp(token, "accel") == 0) ||
+             (strcmp(token, "meta") == 0)) {
+      modifiers |= knsMenuItemCommandModifier;
+    }
+    token = strtok_r(newStr, ", \t", &newStr);
+  }
+  free(str);
+
+  return modifiers;
+}
+
+
+unsigned int nsMenuUtilsX::MacModifiersForGeckoModifiers(PRUint8 geckoModifiers)
+{
+  unsigned int macModifiers = 0;
+  
+  if (geckoModifiers & knsMenuItemShiftModifier)
+    macModifiers |= NSShiftKeyMask;
+  if (geckoModifiers & knsMenuItemAltModifier)
+    macModifiers |= NSAlternateKeyMask;
+  if (geckoModifiers & knsMenuItemControlModifier)
+    macModifiers |= NSControlKeyMask;
+  if (geckoModifiers & knsMenuItemCommandModifier)
+    macModifiers |= NSCommandKeyMask;
+
+  return macModifiers;
+}
+
+
+nsMenuBarX* nsMenuUtilsX::GetHiddenWindowMenuBar()
+{
+  nsIWidget* hiddenWindowWidgetNoCOMPtr = nsCocoaUtils::GetHiddenWindowWidget();
+  if (hiddenWindowWidgetNoCOMPtr)
+    return static_cast<nsCocoaWindow*>(hiddenWindowWidgetNoCOMPtr)->GetMenuBar();
+  else
+    return nsnull;
+}
+
+
+// It would be nice if we could localize these edit menu names.
+static NSMenuItem* standardEditMenuItem = nil;
+NSMenuItem* nsMenuUtilsX::GetStandardEditMenuItem()
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+  if (standardEditMenuItem)
+    return standardEditMenuItem;
+
+  standardEditMenuItem = [[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""];
+  NSMenu* standardEditMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
+  [standardEditMenuItem setSubmenu:standardEditMenu];
+  [standardEditMenu release];
+
+  // Add Undo
+  NSMenuItem* undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"];
+  [standardEditMenu addItem:undoItem];
+  [undoItem release];
+
+  // Add Redo
+  NSMenuItem* redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"];
+  [standardEditMenu addItem:redoItem];
+  [redoItem release];
+
+  // Add separator
+  [standardEditMenu addItem:[NSMenuItem separatorItem]];
+
+  // Add Cut
+  NSMenuItem* cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
+  [standardEditMenu addItem:cutItem];
+  [cutItem release];
+
+  // Add Copy
+  NSMenuItem* copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
+  [standardEditMenu addItem:copyItem];
+  [copyItem release];
+
+  // Add Paste
+  NSMenuItem* pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
+  [standardEditMenu addItem:pasteItem];
+  [pasteItem release];
+
+  // Add Delete
+  NSMenuItem* deleteItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""];
+  [standardEditMenu addItem:deleteItem];
+  [deleteItem release];
+
+  // Add Select All
+  NSMenuItem* selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
+  [standardEditMenu addItem:selectAllItem];
+  [selectAllItem release];
+
+  return standardEditMenuItem;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+
+PRBool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent)
+{
+  return (inContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::hidden,
+                                 nsWidgetAtoms::_true, eCaseMatters) ||
+          inContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::collapsed,
+                                 nsWidgetAtoms::_true, eCaseMatters));
+}
--- a/widget/src/cocoa/nsMenuX.h
+++ b/widget/src/cocoa/nsMenuX.h
@@ -34,131 +34,103 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMenuX_h_
 #define nsMenuX_h_
 
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
-#include "nsIMenu.h"
-#include "nsChangeObserver.h"
-#include "nsMenuBarX.h"
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
 
-#import <Carbon/Carbon.h>
-#import <Cocoa/Cocoa.h>
+#include "nsMenuBaseX.h"
+#include "nsMenuBarX.h"
+#include "nsCOMPtr.h"
+#include "nsChangeObserver.h"
+#include "nsAutoPtr.h"
 
-
-class nsIMenuBar;
 class nsMenuX;
 class nsMenuItemIconX;
+class nsMenuItemX;
+class nsIWidget;
 
 
 // MenuDelegate is used to receive Cocoa notifications for
 // setting up carbon events
 @interface MenuDelegate : NSObject
 {
   nsMenuX* mGeckoMenu; // weak ref
   EventHandlerRef mEventHandler;
   BOOL mHaveInstalledCarbonEvents;
 }
 - (id)initWithGeckoMenu:(nsMenuX*)geckoMenu;
 @end
 
 
-class nsMenuX : public nsIMenu,
+// Once instantiated, this object lives until its DOM node or its parent window is destroyed.
+// Do not hold references to this, they can become invalid any time the DOM node can be destroyed.
+class nsMenuX : public nsMenuObjectX,
                 public nsChangeObserver
 {
 public:
-    nsMenuX();
-    virtual ~nsMenuX();
+  nsMenuX();
+  virtual ~nsMenuX();
 
-    // If > 0, the OS is indexing all the app's menus (triggered by opening
-    // Help menu on Leopard and higher).  There are some things that are
-    // unsafe to do while this is happening.
-    static PRInt32 sIndexingMenuLevel;
+  // If > 0, the OS is indexing all the app's menus (triggered by opening
+  // Help menu on Leopard and higher).  There are some things that are
+  // unsafe to do while this is happening.
+  static PRInt32 sIndexingMenuLevel;
 
-    NS_DECL_ISUPPORTS
-    NS_DECL_CHANGEOBSERVER
-
-    id GetNativeMenuItem();
+  NS_DECL_CHANGEOBSERVER
 
-    // nsIMenu Methods
-    NS_IMETHOD Create(nsISupports * aParent, const nsAString &aLabel, const nsAString &aAccessKey, 
-                      nsMenuBarX* aMenuBar, nsIContent* aNode);
-    NS_IMETHOD GetParent(nsISupports *&aParent);
-    NS_IMETHOD GetLabel(nsString &aText);
-    NS_IMETHOD SetLabel(const nsAString &aText);
-    NS_IMETHOD GetAccessKey(nsString &aText);
-    NS_IMETHOD SetAccessKey(const nsAString &aText);
-    NS_IMETHOD AddItem(nsISupports* aText);
-    NS_IMETHOD GetItemCount(PRUint32 &aCount);
-    NS_IMETHOD GetItemAt(const PRUint32 aPos, nsISupports *& aMenuItem);
-    NS_IMETHOD GetVisibleItemCount(PRUint32 &aCount);
-    NS_IMETHOD GetVisibleItemAt(const PRUint32 aPos, nsISupports *& aMenuItem);
-    NS_IMETHOD InsertItemAt(const PRUint32 aPos, nsISupports * aMenuItem);
-    NS_IMETHOD RemoveItem(const PRUint32 aPos);
-    NS_IMETHOD RemoveAll();
-    NS_IMETHOD GetNativeData(void** aData);
-    NS_IMETHOD SetNativeData(void* aData);
-    NS_IMETHOD GetMenuContent(nsIContent ** aMenuNode);
-    NS_IMETHOD SetEnabled(PRBool aIsEnabled);
-    NS_IMETHOD GetEnabled(PRBool* aIsEnabled);
+  // nsMenuObjectX
+  void*             NativeData()     {return (void*)mMacMenu;}
+  nsMenuObjectTypeX MenuObjectType() {return eSubmenuObjectType;}
 
-    NS_IMETHOD ChangeNativeEnabledStatusForMenuItem(nsIMenuItem* aMenuItem, PRBool aEnabled);
-    NS_IMETHOD GetMenuRefAndItemIndexForMenuItem(nsISupports* aMenuItem,
-                                                 void**       aMenuRef,
-                                                 PRUint16*    aMenuItemIndex);
-    NS_IMETHOD SetupIcon();
-    nsEventStatus MenuSelected(const nsMenuEvent & aMenuEvent); 
-    void MenuDeselected(const nsMenuEvent & aMenuEvent); 
-    void MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * aMenuNode);
-    void MenuDestruct(const nsMenuEvent & aMenuEvent);
-    void SetRebuild(PRBool aMenuEvent);
+  // nsMenuX
+  nsresult       Create(nsMenuObjectX* aParent, const nsAString &aLabel,
+                        nsMenuBarX* aMenuBar, nsIContent* aNode);
+  PRUint32       GetItemCount();
+  nsMenuObjectX* GetItemAt(PRUint32 aPos);
+  nsresult       GetVisibleItemCount(PRUint32 &aCount);
+  nsMenuObjectX* GetVisibleItemAt(PRUint32 aPos);
+  nsEventStatus  MenuOpened(const nsMenuEvent& aMenuEvent);
+  void           MenuClosed(const nsMenuEvent& aMenuEvent);
+  void           SetRebuild(PRBool aMenuEvent);
+  NSMenuItem*    NativeMenuItem();
 
 protected:
-    // Determines how many menus are visible among the siblings that are before me.
-    // It doesn't matter if I am visible.
-    nsresult CountVisibleBefore(PRUint32* outVisibleBefore);
-
-    // fetch the content node associated with the menupopup item
-    void GetMenuPopupContent(nsIContent** aResult);
-    
-    // fire handlers for oncreate/ondestroy
-    PRBool OnDestroy();
-    PRBool OnCreate();
-    PRBool OnDestroyed();
-    PRBool OnCreated();
-
-    nsresult AddMenuItem(nsIMenuItem * aMenuItem);
-    nsresult AddMenu(nsIMenu * aMenu);
-
-    void LoadMenuItem(nsIContent* inMenuItemContent);  
-    void LoadSubMenu(nsIContent* inMenuContent);
-
-    GeckoNSMenu* CreateMenuWithGeckoString(nsString& menuTitle);
+  void           MenuConstruct(nsIWidget* aParentWindow, void* aMenuNode);
+  nsresult       RemoveAll();
+  nsresult       SetEnabled(PRBool aIsEnabled);
+  nsresult       GetEnabled(PRBool* aIsEnabled);
+  nsresult       SetupIcon();
+  nsresult       CountVisibleBefore(PRUint32* outVisibleBefore);
+  void           GetMenuPopupContent(nsIContent** aResult);
+  PRBool         OnOpen();
+  PRBool         OnOpened();
+  PRBool         OnClose();
+  PRBool         OnClosed();
+  nsresult       AddMenuItem(nsMenuItemX* aMenuItem);
+  nsresult       AddMenu(nsMenuX* aMenu);
+  void           LoadMenuItem(nsIContent* inMenuItemContent);  
+  void           LoadSubMenu(nsIContent* inMenuContent);
+  GeckoNSMenu*   CreateMenuWithGeckoString(nsString& menuTitle);
 
-protected:
-    nsString                    mLabel;
-    nsCOMArray<nsISupports>     mMenuItemsArray;
-    PRUint32                    mVisibleItemsCount;     // caching number of visible items in mMenuItemsArray
-
-    nsISupports*                mParent;                // weak, my parent owns me
-    nsMenuBarX*                 mMenuBar;               // weak ref, it will outlive us
-    nsCOMPtr<nsIContent>        mMenuContent;           // the |menu| tag, strong ref
-    nsRefPtr<nsMenuItemIconX>   mIcon;
-
-    // Mac specific
-    PRInt16                     mMacMenuID;
-    GeckoNSMenu*                mMacMenu;               // strong ref, we own it
-    MenuDelegate*               mMenuDelegate;          // strong ref, we keep this around to get events for us
-    NSMenuItem*                 mNativeMenuItem;        // strong ref, we own
-    PRPackedBool                mIsEnabled;
-    PRPackedBool                mDestroyHandlerCalled;
-    PRPackedBool                mNeedsRebuild;
-    PRPackedBool                mConstructed;
-    PRPackedBool                mVisible;               // are we visible to the user?
-    PRPackedBool                mXBLAttached;
+  nsTArray< nsAutoPtr<nsMenuObjectX> > mMenuObjectsArray;
+  nsString                  mLabel;
+  PRUint32                  mVisibleItemsCount; // cache
+  nsMenuObjectX*            mParent; // [weak]
+  nsMenuBarX*               mMenuBar; // [weak]
+  nsRefPtr<nsMenuItemIconX> mIcon;
+  GeckoNSMenu*              mMacMenu; // [strong]
+  MenuDelegate*             mMenuDelegate; // [strong]
+  NSMenuItem*               mNativeMenuItem; // [strong]
+  PRPackedBool              mIsEnabled;
+  PRPackedBool              mDestroyHandlerCalled;
+  PRPackedBool              mNeedsRebuild;
+  PRPackedBool              mConstructed;
+  PRPackedBool              mVisible;
+  PRPackedBool              mXBLAttached;
 };
 
 #endif // nsMenuX_h_
--- a/widget/src/cocoa/nsMenuX.mm
+++ b/widget/src/cocoa/nsMenuX.mm
@@ -32,73 +32,63 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsMenuX.h"
+#include "nsMenuItemX.h"
+#include "nsMenuUtilsX.h"
+#include "nsMenuItemIconX.h"
 
+#include "nsObjCExceptions.h"
+
+#include "nsToolkit.h"
+#include "nsCocoaUtils.h"
 #include "nsCOMPtr.h"
-#include "nsObjCExceptions.h"
+#include "prinrval.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsUnicharUtils.h"
+#include "plstr.h"
+#include "nsWidgetAtoms.h"
+#include "nsGUIEvent.h"
+#include "nsCRT.h"
+
 #include "nsIDocument.h"
 #include "nsIContent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocumentObserver.h"
 #include "nsIComponentManager.h"
-#include "prinrval.h"
 #include "nsIRollupListener.h"
-
-#include "nsMenuItemIconX.h"
-#include "nsIMenu.h"
-#include "nsIMenuBar.h"
-#include "nsIMenuItem.h"
-#include "nsToolkit.h"
-#include "nsCocoaUtils.h"
-
-#include "nsString.h"
-#include "nsReadableUtils.h"
-#include "nsUnicharUtils.h"
-#include "plstr.h"
-
-#include "nsINameSpaceManager.h"
-#include "nsWidgetAtoms.h"
+#include "nsIDOMElement.h"
 #include "nsIXBLService.h"
 #include "nsIServiceManager.h"
 
-#include "nsGUIEvent.h"
-
-#include "nsCRT.h"
-
 #include "jsapi.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 
 // externs defined in nsChildView.mm
 extern nsIRollupListener * gRollupListener;
 extern nsIWidget         * gRollupWidget;
 
-static PRBool gConstructingMenu = PR_FALSE;
-
-static PRBool gMenuMethodsSwizzled = PR_FALSE;
+extern "C" MenuRef _NSGetCarbonMenu(NSMenu* aMenu);
 
-// CIDs
-#include "nsWidgetsCID.h"
-static NS_DEFINE_CID(kMenuCID,     NS_MENU_CID);
-static NS_DEFINE_CID(kMenuItemCID, NS_MENUITEM_CID);
-
-NS_IMPL_ISUPPORTS1(nsMenuX, nsIMenu)
+static PRBool gConstructingMenu = PR_FALSE;
+static PRBool gMenuMethodsSwizzled = PR_FALSE;
 
 PRInt32 nsMenuX::sIndexingMenuLevel = 0;
 
 
 nsMenuX::nsMenuX()
-: mVisibleItemsCount(0), mParent(nsnull), mMenuBar(nsnull), mMacMenuID(0), 
+: mVisibleItemsCount(0), mParent(nsnull), mMenuBar(nsnull),
   mMacMenu(nil), mNativeMenuItem(nil), mIsEnabled(PR_TRUE),
   mDestroyHandlerCalled(PR_FALSE), mNeedsRebuild(PR_TRUE),
   mConstructed(PR_FALSE), mVisible(PR_TRUE), mXBLAttached(PR_FALSE)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (nsToolkit::OnLeopardOrLater() && !gMenuMethodsSwizzled) {
     nsToolkit::SwizzleMethods([NSMenu class], @selector(_addItem:toTable:),
@@ -127,408 +117,285 @@ nsMenuX::~nsMenuX()
   RemoveAll();
 
   [mMacMenu setDelegate:nil];
   [mMacMenu release];
   [mMenuDelegate release];
   [mNativeMenuItem release];
 
   // alert the change notifier we don't care no more
-  if (mMenuContent)
-    mMenuBar->UnregisterForContentChanges(mMenuContent);
+  if (mContent)
+    mMenuBar->UnregisterForContentChanges(mContent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
-NS_IMETHODIMP
-nsMenuX::Create(nsISupports * aParent, const nsAString &aLabel, const nsAString &aAccessKey, 
-                nsMenuBarX* aMenuBar, nsIContent* aNode)
+nsresult nsMenuX::Create(nsMenuObjectX* aParent, const nsAString& aLabel,
+                         nsMenuBarX* aMenuBar, nsIContent* aNode)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  mMenuContent = aNode;
-  NS_ASSERTION(mMenuContent, "Menu not given a dom node at creation time");
+  mContent = aNode;
+  mLabel = aLabel;
+  mMacMenu = CreateMenuWithGeckoString(mLabel);
 
   // register this menu to be notified when changes are made to our content object
   mMenuBar = aMenuBar; // weak ref
   NS_ASSERTION(mMenuBar, "No menu bar given, must have one");
-  mMenuBar->RegisterForContentChanges(mMenuContent, this);
+  mMenuBar->RegisterForContentChanges(mContent, this);
 
   mParent = aParent;
   // our parent could be either a menu bar (if we're toplevel) or a menu (if we're a submenu)
-  nsCOMPtr<nsIMenuBar> menubar = do_QueryInterface(aParent);
-  nsCOMPtr<nsIMenu> menu = do_QueryInterface(aParent);
-  NS_ASSERTION(menubar || menu, "Menu parent not a menu bar or menu!");
+  nsMenuObjectTypeX parentType = mParent->MenuObjectType();
+  NS_ASSERTION((parentType == eMenuBarObjectType || parentType == eSubmenuObjectType),
+               "Menu parent not a menu bar or menu!");
 
-  // SetLabel will create the native menu if it has not been created yet
-  SetLabel(aLabel);
-
-  if (NodeIsHiddenOrCollapsed(mMenuContent))
+  if (nsMenuUtilsX::NodeIsHiddenOrCollapsed(mContent))
+    mVisible = PR_FALSE;
+  if (mContent->GetChildCount() == 0)
     mVisible = PR_FALSE;
 
-  if (menubar && mMenuContent->GetChildCount() == 0)
-    mVisible = PR_FALSE;
+  SetEnabled(!mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled,
+                                    nsWidgetAtoms::_true, eCaseMatters));
 
-  SetEnabled(!mMenuContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled,
-                                        nsWidgetAtoms::_true, eCaseMatters));
-
-  NSString *newCocoaLabelString = MenuHelpersX::CreateTruncatedCocoaLabel(mLabel);
+  NSString *newCocoaLabelString = nsMenuUtilsX::CreateTruncatedCocoaLabel(mLabel);
   mNativeMenuItem = [[NSMenuItem alloc] initWithTitle:newCocoaLabelString action:nil keyEquivalent:@""];
   [newCocoaLabelString release];
 
   [mNativeMenuItem setEnabled:(BOOL)mIsEnabled];
 
   // We call MenuConstruct here because keyboard commands are dependent upon
   // native menu items being created. If we only call MenuConstruct when a menu
   // is actually selected, then we can't access keyboard commands until the
   // menu gets selected, which is bad.
-  nsMenuEvent fake(PR_TRUE, 0, nsnull);
-  MenuConstruct(fake, nsnull, nsnull);
-  
-  if (menu)
-    mIcon = new nsMenuItemIconX(static_cast<nsIMenu*>(this), menu, mMenuContent, mNativeMenuItem);
+  MenuConstruct(nsnull, nsnull);
+
+  mIcon = new nsMenuItemIconX(this, mContent, mNativeMenuItem);
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-NS_IMETHODIMP nsMenuX::GetParent(nsISupports*& aParent)
-{
-  aParent = mParent;
-  NS_IF_ADDREF(aParent);
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuX::GetLabel(nsString &aText)
-{
-  aText = mLabel;
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuX::SetLabel(const nsAString &aText)
-{
-  mLabel = aText;
-  // create an actual NSMenu if this is the first time
-  if (mMacMenu == nil)
-    mMacMenu = CreateMenuWithGeckoString(mLabel);
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuX::GetAccessKey(nsString &aText)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-NS_IMETHODIMP nsMenuX::SetAccessKey(const nsAString &aText)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-// This should only be used internally by our menu implementation. In all other
-// cases menus and their items should be added and modified via the DOM.
-NS_IMETHODIMP nsMenuX::AddItem(nsISupports* aItem)
-{
-  nsresult rv = NS_ERROR_FAILURE;
-
-  if (!aItem)
-    return NS_ERROR_INVALID_ARG;
-
-  // Figure out what we're adding
-  nsCOMPtr<nsIMenuItem> menuItem(do_QueryInterface(aItem, &rv));
-  if (NS_SUCCEEDED(rv)) {
-    rv = AddMenuItem(menuItem);
-  }
-  else {
-    nsCOMPtr<nsIMenu> menu(do_QueryInterface(aItem, &rv));
-    if (NS_SUCCEEDED(rv))
-      rv = AddMenu(menu);
-  }
-
-  return rv;
-}
-
-
-nsresult nsMenuX::AddMenuItem(nsIMenuItem * aMenuItem)
+nsresult nsMenuX::AddMenuItem(nsMenuItemX* aMenuItem)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if (!aMenuItem)
     return NS_ERROR_INVALID_ARG;
 
-  nsCOMPtr<nsIContent> menuItemContent;
-  aMenuItem->GetMenuItemContent(getter_AddRefs(menuItemContent));
-  mMenuItemsArray.AppendObject(aMenuItem); // owning ref
-  if (menuItemContent && NodeIsHiddenOrCollapsed(menuItemContent))
+  mMenuObjectsArray.AppendElement(aMenuItem);
+  if (nsMenuUtilsX::NodeIsHiddenOrCollapsed(aMenuItem->Content()))
     return NS_OK;
   ++mVisibleItemsCount;
- 
+
+  NSMenuItem* newNativeMenuItem = (NSMenuItem*)aMenuItem->NativeData();
+
   // add the menu item to this menu
-  NSMenuItem* newNativeMenuItem;
-  aMenuItem->GetNativeData((void*&)newNativeMenuItem);
-  if (!newNativeMenuItem)
-    return NS_ERROR_FAILURE;
   [mMacMenu addItem:newNativeMenuItem];
 
   // set up target/action
   [newNativeMenuItem setTarget:nsMenuBarX::sNativeEventTarget];
   [newNativeMenuItem setAction:@selector(menuItemHit:)];
-  
+
   // set its command. we get the unique command id from the menubar
   [newNativeMenuItem setTag:mMenuBar->RegisterForCommand(aMenuItem)];
-  
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-nsresult nsMenuX::AddMenu(nsIMenu * aMenu)
+nsresult nsMenuX::AddMenu(nsMenuX* aMenu)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   // Add a submenu
   if (!aMenu)
     return NS_ERROR_NULL_POINTER;
 
-  nsCOMPtr<nsIContent> menuContent;
-  aMenu->GetMenuContent(getter_AddRefs(menuContent));
-  mMenuItemsArray.AppendObject(aMenu); // owning ref
-  if (menuContent && NodeIsHiddenOrCollapsed(menuContent))
+  mMenuObjectsArray.AppendElement(aMenu);
+  if (nsMenuUtilsX::NodeIsHiddenOrCollapsed(aMenu->Content()))
     return NS_OK;
   ++mVisibleItemsCount;
 
   // We have to add a menu item and then associate the menu with it
-  NSMenuItem* newNativeMenuItem = (static_cast<nsMenuX*>(aMenu))->GetNativeMenuItem();
+  NSMenuItem* newNativeMenuItem = (static_cast<nsMenuX*>(aMenu))->NativeMenuItem();
   if (!newNativeMenuItem)
     return NS_ERROR_FAILURE;
   [mMacMenu addItem:newNativeMenuItem];
-  
-  NSMenu* childMenu;
-  if (aMenu->GetNativeData((void**)&childMenu) == NS_OK)
-    [newNativeMenuItem setSubmenu:childMenu];
+
+  [newNativeMenuItem setSubmenu:(NSMenu*)aMenu->NativeData()];
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
 // Includes all items, including hidden/collapsed ones
-NS_IMETHODIMP nsMenuX::GetItemCount(PRUint32 &aCount)
+PRUint32 nsMenuX::GetItemCount()
 {
-  aCount = mMenuItemsArray.Count();
-  return NS_OK;
+  return mMenuObjectsArray.Length();
 }
 
 
 // Includes all items, including hidden/collapsed ones
-NS_IMETHODIMP nsMenuX::GetItemAt(const PRUint32 aPos, nsISupports *& aMenuItem)
+nsMenuObjectX* nsMenuX::GetItemAt(PRUint32 aPos)
 {
-  if (aPos >= (PRUint32)mMenuItemsArray.Count())
-    return NS_ERROR_INVALID_ARG;
+  if (aPos >= (PRUint32)mMenuObjectsArray.Length())
+    return NULL;
 
-  aMenuItem = mMenuItemsArray.ObjectAt(aPos);
-  NS_IF_ADDREF(aMenuItem);
-  return NS_OK;
+  return mMenuObjectsArray[aPos];
 }
 
 
-// Checks both nsIMenus and nsIMenuItems. Not suitable for menus that are children
-// of nsIMenuBar, which has slightly different rules for visiblity.
-static PRBool MenuNodeIsVisible(nsISupports *item)
+// Checks submenus and menu items. Not suitable for submenus that are children
+// of a menu bar, which has slightly different rules for visiblity.
+static PRBool MenuNodeIsVisible(nsMenuObjectX* item)
 {
-  // Find the content for this item in the menu, be it a MenuItem or a Menu
-  nsCOMPtr<nsIContent> itemContent;
-  nsCOMPtr<nsIMenuItem> menuItem = do_QueryInterface(item);
-  if (menuItem) {
-    menuItem->GetMenuItemContent(getter_AddRefs(itemContent));
-  }
-  else {
-    nsCOMPtr<nsIMenu> menu = do_QueryInterface(item);
-    if (menu)
-      menu->GetMenuContent(getter_AddRefs(itemContent));
-  }
-  
-  // Check the visibility of the item's content
-  return (itemContent && !NodeIsHiddenOrCollapsed(itemContent));
+  return (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(item->Content()));
 }
 
 
 // Only includes visible items
-NS_IMETHODIMP nsMenuX::GetVisibleItemCount(PRUint32 &aCount)
+nsresult nsMenuX::GetVisibleItemCount(PRUint32 &aCount)
 {
   aCount = mVisibleItemsCount;
   return NS_OK;
 }
 
 
 // Only includes visible items. Note that this is provides O(N) access
 // If you need to iterate or search, consider using GetItemAt and doing your own filtering
-NS_IMETHODIMP nsMenuX::GetVisibleItemAt(const PRUint32 aPos, nsISupports *& aMenuItem)
+nsMenuObjectX* nsMenuX::GetVisibleItemAt(PRUint32 aPos)
 {
-  PRUint32 count = mMenuItemsArray.Count();
+  
+  PRUint32 count = mMenuObjectsArray.Length();
   if (aPos >= mVisibleItemsCount || aPos >= count)
-    return NS_ERROR_INVALID_ARG;
+    return NULL;
 
   // If there are no invisible items, can provide direct access
-  if (mVisibleItemsCount == count) {
-    nsCOMPtr<nsISupports> item = mMenuItemsArray.ObjectAt(aPos);
-    aMenuItem = item;
-    NS_IF_ADDREF(aMenuItem);
-    return NS_OK;
-  }
+  if (mVisibleItemsCount == count)
+    return mMenuObjectsArray[aPos];
 
   // Otherwise, traverse the array until we find the the item we're looking for.
-  nsCOMPtr<nsISupports> item;
+  nsMenuObjectX* item;
   PRUint32 visibleNodeIndex = 0;
   for (PRUint32 i = 0; i < count; i++) {
-    item = mMenuItemsArray.ObjectAt(i);
+    item = mMenuObjectsArray[i];
     if (MenuNodeIsVisible(item)) {
       if (aPos == visibleNodeIndex) {
         // we found the visible node we're looking for, return it
-        aMenuItem = item;
-        NS_IF_ADDREF(aMenuItem);
-        return NS_OK;
+        return item;
       }
       visibleNodeIndex++;
     }
   }
 
-  return NS_ERROR_FAILURE;
+  return NULL;
 }
 
 
-NS_IMETHODIMP nsMenuX::InsertItemAt(const PRUint32 aPos, nsISupports * aMenuItem)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-NS_IMETHODIMP nsMenuX::RemoveItem(const PRUint32 aPos)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-NS_IMETHODIMP nsMenuX::RemoveAll()
+nsresult nsMenuX::RemoveAll()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if (mMacMenu) {
     // clear command id's
     int itemCount = [mMacMenu numberOfItems];
     for (int i = 0; i < itemCount; i++)
       mMenuBar->UnregisterCommand((PRUint32)[[mMacMenu itemAtIndex:i] tag]);
     // get rid of Cocoa menu items
     for (int i = [mMacMenu numberOfItems] - 1; i >= 0; i--)
       [mMacMenu removeItemAtIndex:i];
   }
-  // get rid of Gecko menu items
-  mMenuItemsArray.Clear();
+
+  mMenuObjectsArray.Clear();
   mVisibleItemsCount = 0;
-  
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
-NS_IMETHODIMP nsMenuX::GetNativeData(void ** aData)
-{
-  *aData = mMacMenu;
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP nsMenuX::SetNativeData(void * aData)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
-  [mMacMenu release];
-  mMacMenu = [(NSMenu*)aData retain];
-  return NS_OK;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-
-nsEventStatus nsMenuX::MenuSelected(const nsMenuEvent & aMenuEvent)
+nsEventStatus nsMenuX::MenuOpened(const nsMenuEvent & aMenuEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
-  // printf("JOSH: MenuSelected called for %s \n", NS_LossyConvertUTF16toASCII(mLabel).get());
-
   // Determine if this is the correct menu to handle the event
   MenuRef selectedMenuHandle = (MenuRef)aMenuEvent.mCommand;
 
   // at this point, the carbon event handler was installed so there
   // must be a carbon MenuRef to be had
   if (_NSGetCarbonMenu(mMacMenu) == selectedMenuHandle) {
     // Open the node.
-    mMenuContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE);
+    mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE);
 
-    // Fire our oncreate handler. If we're told to stop, don't build the menu at all
-    PRBool keepProcessing = OnCreate();
+    // Fire a handler. If we're told to stop, don't build the menu at all
+    PRBool keepProcessing = OnOpen();
 
     if (!mNeedsRebuild || !keepProcessing)
       return nsEventStatus_eConsumeNoDefault;
 
     if (!mConstructed || mNeedsRebuild) {
       if (mNeedsRebuild)
         RemoveAll();
 
-      MenuConstruct(aMenuEvent, nsnull, nsnull);
+      MenuConstruct(nsnull, nsnull);
       mConstructed = true;
     }
 
-    OnCreated();  // Now that it's built, fire the popupShown event.
+    OnOpened();
 
     return nsEventStatus_eConsumeNoDefault;  
   }
   else {
     // Make sure none of our submenus are the ones that should be handling this
-    for (PRUint32 i = mMenuItemsArray.Count() - 1; i >= 0; i--) {
-      nsISupports* menuSupports = mMenuItemsArray.ObjectAt(i);
-      nsCOMPtr<nsIMenu> menu = do_QueryInterface(menuSupports);
-      if (menu) {
-        nsEventStatus status = menu->MenuSelected(aMenuEvent);
+    PRUint32 count = mMenuObjectsArray.Length();
+    for (PRUint32 i = 0; i < count; i++) {
+      nsMenuObjectX* menuObject = mMenuObjectsArray[i];
+      if (menuObject->MenuObjectType() == eSubmenuObjectType) {
+        nsEventStatus status = static_cast<nsMenuX*>(menuObject)->MenuOpened(aMenuEvent);
         if (status != nsEventStatus_eIgnore)
           return status;
       }  
     }
   }
 
   return nsEventStatus_eIgnore;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
 }
 
 
-void nsMenuX::MenuDeselected(const nsMenuEvent & aMenuEvent)
+void nsMenuX::MenuClosed(const nsMenuEvent & aMenuEvent)
 {
-  // Destroy the menu
   if (mConstructed) {
-    MenuDestruct(aMenuEvent);
+    // Don't close if a handler tells us to stop.
+    if (!OnClose())
+      return;
+
+    if (mNeedsRebuild)
+      mConstructed = false;
+
+    mContent->UnsetAttr(kNameSpaceID_None, nsWidgetAtoms::open, PR_TRUE);
+
+    OnClosed();
+
     mConstructed = false;
   }
 }
 
 
-void nsMenuX::MenuConstruct(
-    const nsMenuEvent & aMenuEvent,
-    nsIWidget         * aParentWindow, 
-    void              * aMenuNode)
+void nsMenuX::MenuConstruct(nsIWidget* aParentWindow, void* aMenuNode)
 {
   mConstructed = false;
   gConstructingMenu = PR_TRUE;
   
   // reset destroy handler flag so that we'll know to fire it next time this menu goes away.
   mDestroyHandlerCalled = PR_FALSE;
   
   //printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUTF16toASCII(mLabel).get(), mMacMenu);
@@ -577,175 +444,156 @@ void nsMenuX::MenuConstruct(
         LoadMenuItem(child);
       else if (tag == nsWidgetAtoms::menu)
         LoadSubMenu(child);
     }
   } // for each menu item
 
   gConstructingMenu = PR_FALSE;
   mNeedsRebuild = PR_FALSE;
-  // printf("Done building, mMenuItemsArray.Count() = %d \n", mMenuItemsArray.Count());
-}
-
-
-void nsMenuX::MenuDestruct(const nsMenuEvent & aMenuEvent)
-{
-  // printf("nsMenuX::MenuDestruct() called for %s \n", NS_LossyConvertUTF16toASCII(mLabel).get());
-
-  // Fire our ondestroy handler. If we're told to stop, don't destroy the menu.
-  if (!OnDestroy())
-    return;
-
-  if (mNeedsRebuild)
-    mConstructed = false;
-  // Close the node.
-  mMenuContent->UnsetAttr(kNameSpaceID_None, nsWidgetAtoms::open, PR_TRUE);
-  OnDestroyed();
+  // printf("Done building, mMenuObjectsArray.Count() = %d \n", mMenuObjectsArray.Count());
 }
 
 
 void nsMenuX::SetRebuild(PRBool aNeedsRebuild)
 {
   if (!gConstructingMenu)
     mNeedsRebuild = aNeedsRebuild;
 }
 
 
-NS_IMETHODIMP nsMenuX::SetEnabled(PRBool aIsEnabled)
+nsresult nsMenuX::SetEnabled(PRBool aIsEnabled)
 {
   if (aIsEnabled != mIsEnabled) {
     // we always want to rebuild when this changes
     SetRebuild(PR_TRUE); 
     mIsEnabled = aIsEnabled;
   }
   return NS_OK;
 }
 
 
-NS_IMETHODIMP nsMenuX::GetEnabled(PRBool* aIsEnabled)
+nsresult nsMenuX::GetEnabled(PRBool* aIsEnabled)
 {
   NS_ENSURE_ARG_POINTER(aIsEnabled);
   *aIsEnabled = mIsEnabled;
   return NS_OK;
 }
 
 
-NS_IMETHODIMP nsMenuX::GetMenuContent(nsIContent ** aMenuContent)
-{
-  NS_ENSURE_ARG_POINTER(aMenuContent);
-  NS_IF_ADDREF(*aMenuContent = mMenuContent);
-  return NS_OK;
-}
-
-
 GeckoNSMenu* nsMenuX::CreateMenuWithGeckoString(nsString& menuTitle)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   NSString* title = [NSString stringWithCharacters:(UniChar*)menuTitle.get() length:menuTitle.Length()];
   GeckoNSMenu* myMenu = [[GeckoNSMenu alloc] initWithTitle:title];
   [myMenu setDelegate:mMenuDelegate];
-  
+
   // We don't want this menu to auto-enable menu items because then Cocoa
   // overrides our decisions and things get incorrectly enabled/disabled.
   [myMenu setAutoenablesItems:NO];
-  
+
   // we used to install Carbon event handlers here, but since NSMenu* doesn't
   // create its underlying MenuRef until just before display, we delay until
   // that happens. Now we install the event handlers when Cocoa notifies
   // us that a menu is about to display - see the Cocoa MenuDelegate class.
-  
+
   return myMenu;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 
 void nsMenuX::LoadMenuItem(nsIContent* inMenuItemContent)
 {
   if (!inMenuItemContent)
     return;
 
-  // create nsMenuItem
-  nsCOMPtr<nsIMenuItem> pnsMenuItem = do_CreateInstance(kMenuItemCID);
-  if (!pnsMenuItem)
-    return;
-
   nsAutoString menuitemName;
   inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuitemName);
 
   // printf("menuitem %s \n", NS_LossyConvertUTF16toASCII(menuitemName).get());
 
-  nsIMenuItem::EMenuItemType itemType = nsIMenuItem::eRegular;
+  EMenuItemType itemType = eRegularMenuItemType;
   if (inMenuItemContent->Tag() == nsWidgetAtoms::menuseparator) {
-    itemType = nsIMenuItem::eSeparator;
+    itemType = eSeparatorMenuItemType;
   }
   else {
     static nsIContent::AttrValuesArray strings[] =
   {&nsWidgetAtoms::checkbox, &nsWidgetAtoms::radio, nsnull};
     switch (inMenuItemContent->FindAttrValueIn(kNameSpaceID_None, nsWidgetAtoms::type,
                                                strings, eCaseMatters)) {
-      case 0: itemType = nsIMenuItem::eCheckbox; break;
-      case 1: itemType = nsIMenuItem::eRadio; break;
+      case 0: itemType = eCheckboxMenuItemType; break;
+      case 1: itemType = eRadioMenuItemType; break;
     }
   }
 
   // Create the item.
-  pnsMenuItem->Create(this, menuitemName, itemType, mMenuBar, inMenuItemContent);
+  nsMenuItemX* menuItem = new nsMenuItemX();
+  if (!menuItem)
+    return;
 
-  AddMenuItem(pnsMenuItem);
+  nsresult rv = menuItem->Create(this, menuitemName, itemType, mMenuBar, inMenuItemContent);
+  if (NS_FAILED(rv)) {
+    delete menuItem;
+    return;
+  }
+
+  AddMenuItem(menuItem);
 
   // This needs to happen after the nsIMenuItem object is inserted into
   // our item array in AddMenuItem()
-  pnsMenuItem->SetupIcon();
+  menuItem->SetupIcon();
 }
 
 
 void nsMenuX::LoadSubMenu(nsIContent* inMenuContent)
 {
   nsAutoString menuName; 
   inMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
   //printf("Creating Menu [%s] \n", NS_LossyConvertUTF16toASCII(menuName).get());
 
-  // Create nsMenu
-  nsCOMPtr<nsIMenu> pnsMenu(do_CreateInstance(kMenuCID));
-  if (!pnsMenu)
+  nsAutoPtr<nsMenuX> menu(new nsMenuX());
+  if (!menu)
     return;
 
-  pnsMenu->Create(reinterpret_cast<nsISupports*>(this), menuName, EmptyString(), mMenuBar, inMenuContent);
+  nsresult rv = menu->Create(this, menuName, mMenuBar, inMenuContent);
+  if (NS_FAILED(rv))
+    return;
 
-  AddMenu(pnsMenu);
+  AddMenu(menu);
 
   // This needs to happen after the nsIMenu object is inserted into
   // our item array in AddMenu()
-  pnsMenu->SetupIcon();
+  menu->SetupIcon();
+
+  menu.forget();
 }
 
 
-// Fire our oncreate handler. Returns TRUE if we should keep processing the event,
-// FALSE if the handler wants to stop the creation of the menu
-PRBool nsMenuX::OnCreate()
+// This menu is about to open. Returns TRUE if we should keep processing the event,
+// FALSE if the handler wants to stop the opening of the menu.
+PRBool nsMenuX::OnOpen()
 {
   nsEventStatus status = nsEventStatus_eIgnore;
   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull,
                      nsMouseEvent::eReal);
   
   nsCOMPtr<nsIContent> popupContent;
   GetMenuPopupContent(getter_AddRefs(popupContent));
   
   nsresult rv = NS_OK;
-  nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent;
+  nsIContent* dispatchTo = popupContent ? popupContent : mContent;
   rv = dispatchTo->DispatchDOMEvent(&event, nsnull, nsnull, &status);
   if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault)
     return PR_FALSE;
 
-  // the menu is going to show and the oncreate handler has executed. We
-  // now need to walk our menu items, checking to see if any of them have
-  // a command attribute. If so, several apptributes must potentially
-  // be updated.
+  // If the open is going to succeed we need to walk our menu items, checking to
+  // see if any of them have a command attribute. If so, several apptributes
+  // must potentially be updated.
   if (popupContent) {
     nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(popupContent->GetDocument()));
 
     PRUint32 count = popupContent->GetChildCount();
     for (PRUint32 i = 0; i < count; i++) {
       nsIContent *grandChild = popupContent->GetChildAt(i);
       if (grandChild->Tag() == nsWidgetAtoms::menuitem) {
         // See if we have a command attribute.
@@ -792,72 +640,72 @@ PRBool nsMenuX::OnCreate()
       }
     }
   }
   
   return PR_TRUE;
 }
 
 
-PRBool nsMenuX::OnCreated()
+PRBool nsMenuX::OnOpened()
 {
   nsEventStatus status = nsEventStatus_eIgnore;
   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull, nsMouseEvent::eReal);
   
   nsCOMPtr<nsIContent> popupContent;
   GetMenuPopupContent(getter_AddRefs(popupContent));
 
   nsresult rv = NS_OK;
-  nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent;
+  nsIContent* dispatchTo = popupContent ? popupContent : mContent;
   rv = dispatchTo->DispatchDOMEvent(&event, nsnull, nsnull, &status);
   if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault)
     return PR_FALSE;  
   
   return PR_TRUE;
 }
 
 
-// Fire our ondestroy handler. Returns TRUE if we should keep processing the event,
-// FALSE if the handler wants to stop the destruction of the menu
-PRBool nsMenuX::OnDestroy()
+// Returns TRUE if we should keep processing the event, FALSE if the handler
+// wants to stop the closing of the menu.
+PRBool nsMenuX::OnClose()
 {
   if (mDestroyHandlerCalled)
     return PR_TRUE;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull,
                      nsMouseEvent::eReal);
 
   nsCOMPtr<nsIContent> popupContent;
   GetMenuPopupContent(getter_AddRefs(popupContent));
 
   nsresult rv = NS_OK;
-  nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent;
+  nsIContent* dispatchTo = popupContent ? popupContent : mContent;
   rv = dispatchTo->DispatchDOMEvent(&event, nsnull, nsnull, &status);
   
   mDestroyHandlerCalled = PR_TRUE;
   
   if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault)
     return PR_FALSE;
   
   return PR_TRUE;
 }
 
 
-PRBool nsMenuX::OnDestroyed()
+PRBool nsMenuX::OnClosed()
 {
   nsEventStatus status = nsEventStatus_eIgnore;
   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull,
                      nsMouseEvent::eReal);
 
   nsCOMPtr<nsIContent> popupContent;
   GetMenuPopupContent(getter_AddRefs(popupContent));
 
   nsresult rv = NS_OK;
-  nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent;
+  nsIContent* dispatchTo = popupContent ? popupContent : mContent;
   rv = dispatchTo->DispatchDOMEvent(&event, nsnull, nsnull, &status);
   
   mDestroyHandlerCalled = PR_TRUE;
   
   if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault)
     return PR_FALSE;
   
   return PR_TRUE;
@@ -873,21 +721,21 @@ void nsMenuX::GetMenuPopupContent(nsICon
     return;
   *aResult = nsnull;
   
   nsresult rv;
   nsCOMPtr<nsIXBLService> xblService = do_GetService("@mozilla.org/xbl;1", &rv);
   if (!xblService)
     return;
   
-  PRUint32 count = mMenuContent->GetChildCount();
+  PRUint32 count = mContent->GetChildCount();
 
   for (PRUint32 i = 0; i < count; i++) {
     PRInt32 dummy;
-    nsIContent *child = mMenuContent->GetChildAt(i);
+    nsIContent *child = mContent->GetChildAt(i);
     nsCOMPtr<nsIAtom> tag;
     xblService->ResolveTag(child, &dummy, getter_AddRefs(tag));
     if (tag == nsWidgetAtoms::menupopup) {
       *aResult = child;
       NS_ADDREF(*aResult);
       return;
     }
   }
@@ -897,239 +745,170 @@ void nsMenuX::GetMenuPopupContent(nsICon
 
 // Determines how many menus are visible among the siblings that are before me.
 // It doesn't matter if I am visible. Note that this will always count the
 // Application menu, since we always put it in there.
 nsresult nsMenuX::CountVisibleBefore(PRUint32* outVisibleBefore)
 {
   NS_ASSERTION(outVisibleBefore, "bad index param in nsMenuX::CountVisibleBefore");
   
-  nsCOMPtr<nsIMenuBar> menubarParent = do_QueryInterface(mParent);
-  if (menubarParent) {
-    PRUint32 numMenus = 0;
-    menubarParent->GetMenuCount(numMenus);
-    
+  nsMenuObjectTypeX parentType = mParent->MenuObjectType();
+  if (parentType == eMenuBarObjectType) {
+    nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(mParent);
+    PRUint32 numMenus = menubarParent->GetMenuCount();
+
     // Find this menu among the children of my parent menubar
     *outVisibleBefore = 1; // start at 1, the Application menu will always be there
     for (PRUint32 i = 0; i < numMenus; i++) {
-      nsCOMPtr<nsIMenu> currMenu;
-      menubarParent->GetMenuAt(i, *getter_AddRefs(currMenu));
-      if (currMenu == static_cast<nsIMenu*>(this)) {
+      nsMenuX* currMenu = menubarParent->GetMenuAt(i);
+      if (currMenu == this) {
         // we found ourselves, break out
         return NS_OK;
       }
-  
+
       if (currMenu) {
-        nsCOMPtr<nsIContent> menuContent;
-        currMenu->GetMenuContent(getter_AddRefs(menuContent));
-        if (menuContent &&
-            menuContent->GetChildCount() > 0 &&
-            !NodeIsHiddenOrCollapsed(menuContent)) {
+        nsIContent* menuContent = currMenu->Content();
+        if (menuContent->GetChildCount() > 0 &&
+            !nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
           ++(*outVisibleBefore);
         }
       }
     }
-  } // if menubarParent
-  else {
-    nsCOMPtr<nsIMenu> menuParent = do_QueryInterface(mParent);
-    if (!menuParent)
-      return NS_ERROR_FAILURE;
-
-    PRUint32 numItems;
-    menuParent->GetItemCount(numItems);
-
+  }
+  else if (parentType == eSubmenuObjectType) {
     // Find this menu among the children of my parent menu
+    nsMenuX* menuParent = static_cast<nsMenuX*>(mParent);
+    PRUint32 numItems = menuParent->GetItemCount();
     for (PRUint32 i = 0; i < numItems; i++) {
       // Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2)
-      nsCOMPtr<nsISupports> currItem;
-      menuParent->GetItemAt(i, *getter_AddRefs(currItem));
-      nsCOMPtr<nsIMenu> currMenu = do_QueryInterface(currItem);
-      if (currMenu == static_cast<nsIMenu*>(this)) {
-        // we found ourselves, break out
-        return NS_OK;
-      }
-
+      nsMenuObjectX* currItem = menuParent->GetItemAt(i);
+      if (currItem == this)
+        return NS_OK; // we found ourselves, break out
       // If the node is visible increment the outparam.
       if (MenuNodeIsVisible(currItem))
         ++(*outVisibleBefore);
-      
     }
   }
   return NS_ERROR_FAILURE;
 }
 
 
-NS_IMETHODIMP
-nsMenuX::ChangeNativeEnabledStatusForMenuItem(nsIMenuItem* aMenuItem,
-                                              PRBool aEnabled)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-
-NS_IMETHODIMP
-nsMenuX::GetMenuRefAndItemIndexForMenuItem(nsISupports* aMenuItem,
-                                           void**       aMenuRef,
-                                           PRUint16*    aMenuItemIndex)
-{
-  if (!mMacMenu)
-    return NS_ERROR_FAILURE;
-  
-  // look for the menu item given, and skip invisible elements
-  PRUint32 menuItemCount;
-  GetItemCount(menuItemCount);
-  PRUint32 visibleNodeIndex = 0;
-  for (PRUint32 i = 0; i < menuItemCount; i++) {
-    nsCOMPtr<nsISupports> currItem;
-    GetItemAt(i, *getter_AddRefs(currItem));
-    // Only check visible nodes
-    if (MenuNodeIsVisible(currItem)) {
-      if (currItem == aMenuItem) {
-        *aMenuRef = _NSGetCarbonMenu(mMacMenu);
-        // add 1 because carbon menu items are 1-indexed.
-        *aMenuItemIndex = visibleNodeIndex + 1;
-        return NS_OK;
-      }
-      visibleNodeIndex++;
-    }
-  }
-  
-  return NS_ERROR_FAILURE;
-}
-
-
-id
-nsMenuX::GetNativeMenuItem()
+NSMenuItem* nsMenuX::NativeMenuItem()
 {
   return mNativeMenuItem;
 }
 
 
 //
 // nsChangeObserver
 //
 
 
-void
-nsMenuX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aContent, nsIAtom *aAttribute)
+void nsMenuX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aContent,
+                                      nsIAtom *aAttribute)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // ignore the |open| attribute, which is by far the most common
   if (gConstructingMenu || (aAttribute == nsWidgetAtoms::open))
     return;
 
-  nsCOMPtr<nsIMenuBar> menubarParent = do_QueryInterface(mParent);
+  nsMenuObjectTypeX parentType = mParent->MenuObjectType();
 
   if (aAttribute == nsWidgetAtoms::disabled) {
     SetRebuild(PR_TRUE);
-    SetEnabled(!mMenuContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled,
-                                          nsWidgetAtoms::_true, eCaseMatters));
+    SetEnabled(!mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled,
+                                      nsWidgetAtoms::_true, eCaseMatters));
   }
   else if (aAttribute == nsWidgetAtoms::label) {
     SetRebuild(PR_TRUE);
     
-    mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, mLabel);
+    mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, mLabel);
 
     // invalidate my parent. If we're a submenu parent, we have to rebuild
     // the parent menu in order for the changes to be picked up. If we're
     // a regular menu, just change the title and redraw the menubar.
-    if (menubarParent) {
+    if (parentType == eMenuBarObjectType) {
       // reuse the existing menu, to avoid rebuilding the root menu bar.
       NS_ASSERTION(mMacMenu, "nsMenuX::AttributeChanged: invalid menu handle.");
-      NSString *newCocoaLabelString = MenuHelpersX::CreateTruncatedCocoaLabel(mLabel);
+      NSString *newCocoaLabelString = nsMenuUtilsX::CreateTruncatedCocoaLabel(mLabel);
       [mMacMenu setTitle:newCocoaLabelString];
       [newCocoaLabelString release];
     }
     else {
-      nsCOMPtr<nsIMenu> parentMenu(do_QueryInterface(mParent));
-      parentMenu->SetRebuild(PR_TRUE);
+      static_cast<nsMenuX*>(mParent)->SetRebuild(PR_TRUE);
     }    
   }
   else if (aAttribute == nsWidgetAtoms::hidden || aAttribute == nsWidgetAtoms::collapsed) {
     SetRebuild(PR_TRUE);
 
-    PRBool contentIsHiddenOrCollapsed = NodeIsHiddenOrCollapsed(mMenuContent);
+    PRBool contentIsHiddenOrCollapsed = nsMenuUtilsX::NodeIsHiddenOrCollapsed(mContent);
 
     // don't do anything if the state is correct already
     if (contentIsHiddenOrCollapsed != mVisible)
       return;
 
-    nsCOMPtr<nsIMenu> menuParent = do_QueryInterface(mParent);
     if (contentIsHiddenOrCollapsed) {
-      void *clientData = nsnull;
-      if (menubarParent)
-        menubarParent->GetNativeData(clientData);
-      else if (menuParent)
-        menuParent->GetNativeData(&clientData);
-      if (clientData) {
-        NSMenu* parentMenu = reinterpret_cast<NSMenu*>(clientData);
+      if (parentType == eMenuBarObjectType || parentType == eSubmenuObjectType) {
+        NSMenu* parentMenu = (NSMenu*)mParent->NativeData();
         // An exception will get thrown if we try to remove an item that isn't
         // in the menu.
         if ([parentMenu indexOfItem:mNativeMenuItem] != -1)
           [parentMenu removeItem:mNativeMenuItem];
         mVisible = PR_FALSE;
       }
     }
     else {
       PRUint32 insertAfter = 0;
       if (NS_SUCCEEDED(CountVisibleBefore(&insertAfter))) {
-        void *clientData = nsnull;
-        if (menubarParent)
-          menubarParent->GetNativeData(clientData);
-        else if (menuParent)
-          menuParent->GetNativeData(&clientData);
-        if (clientData) {
-          NSMenu* parentMenu = reinterpret_cast<NSMenu*>(clientData);
+        if (parentType == eMenuBarObjectType || parentType == eSubmenuObjectType) {
+          NSMenu* parentMenu = (NSMenu*)mParent->NativeData();
           [parentMenu insertItem:mNativeMenuItem atIndex:insertAfter];
           [mNativeMenuItem setSubmenu:mMacMenu];
           mVisible = PR_TRUE;
         }
       }
     }
   }
   else if (aAttribute == nsWidgetAtoms::image) {
     SetupIcon();
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
-void
-nsMenuX::ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild,
-                          PRInt32 aIndexInContainer)
-{  
+void nsMenuX::ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild,
+                                    PRInt32 aIndexInContainer)
+{
   if (gConstructingMenu)
     return;
 
   SetRebuild(PR_TRUE);
-
-  RemoveItem(aIndexInContainer);
   mMenuBar->UnregisterForContentChanges(aChild);
 }
 
 
-void
-nsMenuX::ObserveContentInserted(nsIDocument *aDocument, nsIContent *aChild,
-                           PRInt32 aIndexInContainer)
-{  
+void nsMenuX::ObserveContentInserted(nsIDocument *aDocument, nsIContent *aChild,
+                                     PRInt32 aIndexInContainer)
+{
   if (gConstructingMenu)
     return;
 
   SetRebuild(PR_TRUE);
 }
 
 
-NS_IMETHODIMP
-nsMenuX::SetupIcon()
+nsresult nsMenuX::SetupIcon()
 {
   // In addition to out-of-memory, menus that are children of the menu bar
   // will not have mIcon set.
-  if (!mIcon) return NS_ERROR_OUT_OF_MEMORY;
+  if (!mIcon)
+    return NS_ERROR_OUT_OF_MEMORY;
+
   return mIcon->SetupIcon();
 }
 
 
 //
 // Carbon event support
 //
 
@@ -1143,65 +922,55 @@ static pascal OSStatus MyMenuEventHandle
   // menus, but it also resolves many other problems -- including crashes and
   // long delays while opening the Help menu.  Once we know better which
   // operations are safe during (re)indexing, we can start allowing some
   // operations here while it's happening.  This change resolves bmo bugs
   // 426499 and 414699.
   if (nsMenuX::sIndexingMenuLevel > 0)
     return noErr;
 
+  nsMenuX* targetMenu = static_cast<nsMenuX*>(userData);
   UInt32 kind = ::GetEventKind(event);
   if (kind == kEventMenuTargetItem) {
     // get the position of the menu item we want
     PRUint16 aPos;
     ::GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex, NULL, sizeof(MenuItemIndex), NULL, &aPos);
     aPos--; // subtract 1 from aPos because Carbon menu positions start at 1 not 0
     
     // don't request a menu item that doesn't exist or we crash
     // this might happen just due to some random quirks in the event system
     PRUint32 itemCount;
-    nsIMenu* targetMenu = reinterpret_cast<nsIMenu*>(userData);
     targetMenu->GetVisibleItemCount(itemCount);
     if (aPos >= itemCount)
       return eventNotHandledErr;
-    
-    nsCOMPtr<nsISupports> aTargetMenuItem;
-    targetMenu->GetVisibleItemAt((PRUint32)aPos, *getter_AddRefs(aTargetMenuItem));
-    
-    // Send DOM event
-    // If the QI fails, we're over a submenu and we shouldn't send the event
-    nsCOMPtr<nsIMenuItem> bTargetMenuItem(do_QueryInterface(aTargetMenuItem));
-    if (bTargetMenuItem) {
+
+    // Send DOM event if we're over a menu item
+    nsMenuObjectX* target = targetMenu->GetVisibleItemAt((PRUint32)aPos);
+    if (target->MenuObjectType() == eMenuItemObjectType) {
+      nsMenuItemX* targetMenuItem = static_cast<nsMenuItemX*>(target);
       PRBool handlerCalledPreventDefault; // but we don't actually care
-      bTargetMenuItem->DispatchDOMEvent(NS_LITERAL_STRING("DOMMenuItemActive"), &handlerCalledPreventDefault);
+      targetMenuItem->DispatchDOMEvent(NS_LITERAL_STRING("DOMMenuItemActive"), &handlerCalledPreventDefault);
       return noErr;
     }
   }
   else if (kind == kEventMenuOpening || kind == kEventMenuClosed) {
     if (kind == kEventMenuOpening && gRollupListener && gRollupWidget) {
       gRollupListener->Rollup(nsnull);
       return userCanceledErr;
     }
-    
-    nsISupports* supports = reinterpret_cast<nsISupports*>(userData);
-    nsCOMPtr<nsIMenu> menu(do_QueryInterface(supports));
-    if (menu) {
-      MenuRef menuRef;
-      ::GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menuRef), NULL, &menuRef);
-      nsMenuEvent menuEvent(PR_TRUE, NS_MENU_SELECTED, nsnull);
-      menuEvent.time = PR_IntervalNow();
-      menuEvent.mCommand = (PRUint32)menuRef;
-      if (kind == kEventMenuOpening) {
-        menu->MenuSelected(menuEvent);
-      }
-      else {
-        menu->MenuDeselected(menuEvent);
-      }
-      return noErr;
-    }
+    MenuRef menuRef;
+    ::GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menuRef), NULL, &menuRef);
+    nsMenuEvent menuEvent(PR_TRUE, NS_MENU_SELECTED, nsnull);
+    menuEvent.time = PR_IntervalNow();
+    menuEvent.mCommand = (PRUint32)menuRef;
+    if (kind == kEventMenuOpening)
+      targetMenu->MenuOpened(menuEvent);
+    else
+      targetMenu->MenuClosed(menuEvent);
+    return noErr;
   }
   return eventNotHandledErr;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(noErr);
 }
 
 
 static OSStatus InstallMyMenuEventHandler(MenuRef menuRef, void* userData, EventHandlerRef* outHandler)
--- a/widget/src/cocoa/nsPIWidgetCocoa.idl
+++ b/widget/src/cocoa/nsPIWidgetCocoa.idl
@@ -34,36 +34,33 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
-interface nsIMenuBar;
+interface nsMenuBarX;
 interface nsCocoaWindow;
 interface nsIWidget;
 
 [ptr] native NSWindowPtr(NSWindow);
 
 //
 // nsPIWidgetCocoa
 //
 // A private interface (unfrozen, private to the widget implementation) that
 // gives us access to some extra features on a widget/window.
 //
 [uuid(F08E9D06-6705-4749-BE81-CEF931246E06)]
 interface nsPIWidgetCocoa : nsISupports
 {
   void SendSetZLevelEvent();
 
-  // Obtain the menubar for a window
-  nsIMenuBar GetMenuBar();
-
   // Find the displayed child sheet (if aShown) or a child sheet that
   // wants to be displayed (if !aShown)
   nsCocoaWindow GetChildSheet(in boolean aShown);
   
   // Get the parent widget (if any) StandardCreate() was called with.
   nsIWidget GetRealParent();
   
   // If the object implementing this interface is a sheet, this will return the
--- a/widget/src/cocoa/nsWidgetFactory.mm
+++ b/widget/src/cocoa/nsWidgetFactory.mm
@@ -44,20 +44,16 @@
 
 #include "nsToolkit.h"
 #include "nsChildView.h"
 #include "nsCocoaWindow.h"
 #include "nsAppShell.h"
 #include "nsAppShellSingleton.h"
 #include "nsFilePicker.h"
 
-#include "nsMenuBarX.h"
-#include "nsMenuX.h"
-#include "nsMenuItemX.h"
-
 #include "nsClipboard.h"
 #include "nsClipboardHelper.h"
 #include "nsTransferable.h"
 #include "nsHTMLFormatConverter.h"
 #include "nsDragService.h"
 
 #include "nsLookAndFeel.h"
 
@@ -69,31 +65,31 @@
 #include "nsPrintOptionsX.h"
 #include "nsPrintSessionX.h"
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCocoaWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildView)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsToolkit)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsLookAndFeel)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsMenuBarX)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsMenuX)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsMenuItemX)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerCocoa)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecX)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsX, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSessionX, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceX)
 
+#include "nsMenuBarX.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeMenuServiceX)
+
 #include "nsBidiKeyboard.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
 
 #include "nsNativeThemeCocoa.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeThemeCocoa)
 
 static const nsModuleComponentInfo gComponents[] =
 {
@@ -120,28 +116,16 @@ static const nsModuleComponentInfo gComp
   { "Toolkit",
     NS_TOOLKIT_CID,
     "@mozilla.org/widget/toolkit/mac;1",
     nsToolkitConstructor },
   { "Look And Feel",
     NS_LOOKANDFEEL_CID,
     "@mozilla.org/widget/lookandfeel;1",
     nsLookAndFeelConstructor },
-  { "Menubar",
-    NS_MENUBAR_CID,
-    "@mozilla.org/widget/menubar/mac;1",
-    nsMenuBarXConstructor },
-  { "Menu",
-    NS_MENU_CID,
-    "@mozilla.org/widget/menu/mac;1",
-    nsMenuXConstructor },
-  { "MenuItem",
-    NS_MENUITEM_CID,
-    "@mozilla.org/widget/menuitem/mac;1",
-    nsMenuItemXConstructor },
   { "Sound",
     NS_SOUND_CID,
     "@mozilla.org/sound;1",
     nsSoundConstructor },
   { "Transferable",
     NS_TRANSFERABLE_CID,
     "@mozilla.org/widget/transferable;1",
     nsTransferableConstructor },
@@ -184,12 +168,16 @@ static const nsModuleComponentInfo gComp
   { "Print Session",
     NS_PRINTSESSION_CID,
     "@mozilla.org/gfx/printsession;1",
     nsPrintSessionXConstructor },
   { "User Idle Service",
     NS_IDLE_SERVICE_CID,
     "@mozilla.org/widget/idleservice;1",
     nsIdleServiceXConstructor },
+  { "Native Menu Service",
+    NS_NATIVEMENUSERVICE_CID,
+    "@mozilla.org/widget/nativemenuservice;1",
+    nsNativeMenuServiceXConstructor },
 };
 
 NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(nsWidgetMacModule, gComponents,
                                    nsAppShellInit, nsAppShellShutdown)
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -1368,17 +1368,17 @@ nsWindow::SetIcon(const nsAString& aIcon
     // leave the default icon intact if no matching icons were found
     if (iconList.Count() == 0)
         return NS_OK;
 
     return SetWindowIconList(iconList);
 }
 
 NS_IMETHODIMP
-nsWindow::SetMenuBar(nsIMenuBar * aMenuBar)
+nsWindow::SetMenuBar(void * aMenuBar)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsWindow::ShowMenuBar(PRBool aShow)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/widget/src/gtk2/nsWindow.h
+++ b/widget/src/gtk2/nsWindow.h
@@ -136,17 +136,17 @@ public:
     NS_IMETHOD         ScrollRect(nsRect  &aSrcRect,
                                   PRInt32  aDx,
                                   PRInt32  aDy);
     virtual void*      GetNativeData(PRUint32 aDataType);
     NS_IMETHOD         SetBorderStyle(nsBorderStyle aBorderStyle);
     NS_IMETHOD         SetTitle(const nsAString& aTitle);
     NS_IMETHOD         SetIcon(const nsAString& aIconSpec);
     NS_IMETHOD         SetWindowClass(const nsAString& xulWinType);
-    NS_IMETHOD         SetMenuBar(nsIMenuBar * aMenuBar);
+    NS_IMETHOD         SetMenuBar(void * aMenuBar);
     NS_IMETHOD         ShowMenuBar(PRBool aShow);
     NS_IMETHOD         WidgetToScreen(const nsRect& aOldRect,
                                       nsRect& aNewRect);
     NS_IMETHOD         ScreenToWidget(const nsRect& aOldRect,
                                       nsRect& aNewRect);
     NS_IMETHOD         BeginResizingChildren(void);
     NS_IMETHOD         EndResizingChildren(void);
     NS_IMETHOD         EnableDragDrop(PRBool aEnable);
--- a/widget/src/os2/nsWindow.h
+++ b/widget/src/os2/nsWindow.h
@@ -56,17 +56,16 @@
 
 #include "nsWidgetDefs.h"
 #include "nsBaseWidget.h"
 #include "nsToolkit.h"
 #include "nsSwitchToUIThread.h"
 #include "gfxOS2Surface.h"
 #include "gfxContext.h"
 
-class nsIMenuBar;
 class imgIContainer;
 
 //#define DEBUG_FOCUS
 
 #ifdef DEBUG_FOCUS
   #define DEBUGFOCUS(what) printf("[%x] "#what" (%d)\n", (int)this, mWindowIdentifier)
 #else
   #define DEBUGFOCUS(what)
@@ -173,17 +172,17 @@ class nsWindow : public nsBaseWidget,
    // Widget appearance
    NS_IMETHOD              SetColorMap( nsColorMap *aColorMap);
    NS_IMETHOD              SetCursor( nsCursor aCursor);
    NS_IMETHOD              SetCursor(imgIContainer* aCursor,
                                      PRUint32 aHotspotX, PRUint32 aHotspotY);
    NS_IMETHOD              HideWindowChrome(PRBool aShouldHide);
    NS_IMETHOD              SetTitle( const nsAString& aTitle); 
    NS_IMETHOD              SetIcon(const nsAString& aIconSpec); 
-   NS_IMETHOD              SetMenuBar(nsIMenuBar * aMenuBar) { return NS_ERROR_FAILURE; } 
+   NS_IMETHOD              SetMenuBar(void * aMenuBar) { return NS_ERROR_FAILURE; } 
    NS_IMETHOD              ShowMenuBar(PRBool aShow)         { return NS_ERROR_FAILURE; } 
    NS_IMETHOD              Invalidate( PRBool aIsSynchronous);
    NS_IMETHOD              Invalidate( const nsRect & aRect, PRBool aIsSynchronous);
    NS_IMETHOD              InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous);
    NS_IMETHOD              Update();
    NS_IMETHOD              Scroll( PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect);
    NS_IMETHOD              ScrollWidgets(PRInt32 aDx, PRInt32 aDy);
    NS_IMETHOD              ScrollRect(nsRect &aRect, PRInt32 aDx, PRInt32 aDy);
@@ -276,17 +275,16 @@ protected:
    HWND      GetParentHWND() const;
    HWND      GetHWND() const   { return mWnd; }
    PFNWP     GetPrevWP() const { return mPrevWndProc; }
 
    // nglayout data members
    PRInt32        mPreferredHeight;
    PRInt32        mPreferredWidth;
    nsToolkit     *mOS2Toolkit;
-   nsIMenuBar    *mMenuBar;
    PRInt32        mWindowState;
    nsRefPtr<gfxOS2Surface> mThebesSurface;
 
    // Implementation ------------------------------
    void DoCreate( HWND hwndP, nsWindow *wndP, const nsRect &rect,
                   EVENT_CALLBACK aHandleEventFunction,
                   nsIDeviceContext *aContext, nsIAppShell *aAppShell,
                   nsIToolkit *aToolkit, nsWidgetInitData *aInitData);
--- a/widget/src/photon/nsWindow.cpp
+++ b/widget/src/photon/nsWindow.cpp
@@ -48,17 +48,16 @@
 #include "nsIFontMetrics.h"
 #include "nsFont.h"
 #include "nsGUIEvent.h"
 #include "nsIRenderingContext.h"
 #include "nsIRegion.h"
 #include "nsRect.h"
 #include "nsTransform2D.h"
 #include "nsGfxCIID.h"
-#include "nsIMenuBar.h"
 #include "nsToolkit.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 
 #include "nsClipboard.h"
 #include "nsIRollupListener.h"
 
 #include "nsIServiceManager.h"
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -56,17 +56,16 @@
 #include "nsString.h"
 
 #include "nsVoidArray.h"
 #include "nsTArray.h"
 
 class nsNativeDragTarget;
 class nsIRollupListener;
 
-class nsIMenuBar;
 class nsIFile;
 
 class imgIContainer;
 
 struct nsAlternativeCharCode;
 struct nsFakeCharMessage;
 
 #ifdef ACCESSIBILITY
@@ -185,17 +184,17 @@ public:
   virtual void            FreeNativeData(void * data, PRUint32 aDataType);//~~~
   NS_IMETHOD              SetColorMap(nsColorMap *aColorMap);
   //XXX-Scroll is obsolete it is going away soon
   NS_IMETHOD              Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect);
   NS_IMETHOD              ScrollWidgets(PRInt32 aDx, PRInt32 aDy);
   NS_IMETHOD              ScrollRect(nsRect &aRect, PRInt32 aDx, PRInt32 aDy);
   NS_IMETHOD              SetTitle(const nsAString& aTitle);
   NS_IMETHOD              SetIcon(const nsAString& aIconSpec);
-  NS_IMETHOD              SetMenuBar(nsIMenuBar * aMenuBar) { return NS_ERROR_FAILURE; }
+  NS_IMETHOD              SetMenuBar(void * aMenuBar) { return NS_ERROR_FAILURE; }
   NS_IMETHOD              ShowMenuBar(PRBool aShow)         { return NS_ERROR_FAILURE; }
   NS_IMETHOD              WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect);
   NS_IMETHOD              ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect);
   NS_IMETHOD              BeginResizingChildren(void);
   NS_IMETHOD              EndResizingChildren(void);
   NS_IMETHOD              GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight);
   NS_IMETHOD              SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight);
   NS_IMETHOD              DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);
--- a/widget/src/xpwidgets/nsBaseWidget.h
+++ b/widget/src/xpwidgets/nsBaseWidget.h
@@ -131,16 +131,17 @@ public:
   NS_IMETHOD              GetLastInputEventTime(PRUint32& aTime);
   NS_IMETHOD              SetIcon(const nsAString &anIconSpec);
   NS_IMETHOD              BeginSecureKeyboardInput();
   NS_IMETHOD              EndSecureKeyboardInput();
   NS_IMETHOD              SetWindowTitlebarColor(nscolor aColor, PRBool aActive);
   virtual void            ConvertToDeviceCoordinates(nscoord  &aX,nscoord &aY) {}
   virtual void            FreeNativeData(void * data, PRUint32 aDataType) {}
   NS_IMETHOD              BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
+  NS_IMETHOD              ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
 
 protected:
 
   virtual void            ResolveIconName(const nsAString &aIconName,
                                           const nsAString &aIconSuffix,
                                           nsILocalFile **aResult);
   virtual void            OnDestroy();
   virtual void            BaseCreate(nsIWidget *aParent,
--- a/widget/tests/Makefile.in
+++ b/widget/tests/Makefile.in
@@ -43,10 +43,15 @@ relativesrcdir  = widget/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =	test_bug343416.xul \
 		test_keycodes.xul \
 		$(NULL)
 
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+_TEST_FILES += native_menus_window.xul \
+               test_native_menus.xul
+endif
+
 libs:: $(_TEST_FILES)
 	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/widget/tests/native_menus_window.xul
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Findbar Test code
+   -
+   - The Initial Developer of the Original Code is
+   - Mozilla Corporation.
+   - Portions created by the Initial Developer are Copyright (C) 2006
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Josh Aas <josh@mozilla.com>
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either 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
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the GPL or the LGPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="NativeMenuWindow"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        width="300"
+        height="300"
+        onload="onLoad();"
+        title="Native Menu Test">
+
+  <command id="cmd_FooItem1" oncommand="executedCommandID = 'cmd_FooItem1';"/>
+  <command id="cmd_FooItem2" oncommand="executedCommandID = 'cmd_FooItem2';"/>
+  <command id="cmd_BarItem1" oncommand="executedCommandID = 'cmd_BarItem1';"/>
+  <command id="cmd_BarItem2" oncommand="executedCommandID = 'cmd_BarItem2';"/>
+
+  <menubar id="nativemenubar">
+    <menu id="foo" label="Foo">
+      <menupopup>
+        <menuitem label="FooItem1" command="cmd_FooItem1"/>
+        <menuitem label="FooItem2" command="cmd_FooItem2"/>
+        <menuseparator/>
+        <menu label="Bar">
+          <menupopup>
+            <menuitem label="BarItem1" command="cmd_BarItem1"/>
+            <menuitem label="BarItem2" command="cmd_BarItem2"/>
+          </menupopup>
+        </menu>
+      </menupopup>
+    </menu>
+  </menubar>
+
+  <script type="application/javascript"><![CDATA[
+
+    function ok(condition, message) {
+      window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+    }
+
+    function onTestsFinished() {
+      window.close();
+      window.opener.wrappedJSObject.SimpleTest.finish();
+    }
+
+    var executedCommandID = "";
+
+    function testItem(location, targetID) {
+      netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+      var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                                          getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+      var correctCommandHandler = false;
+      try {
+        utils.activateNativeMenuItemAt(location);
+        correctCommandHandler = executedCommandID == targetID;
+      }
+      catch (e) {
+        dump(e + "\n");
+      }
+      finally {
+        ok(correctCommandHandler, 'Command handler not run correctly');
+        executedCommandID = "";
+      }
+    }
+
+    function onLoad() {
+      var _delayedOnLoad = function() {
+        testItem("0|0", "cmd_FooItem1");
+        testItem("0|1", "cmd_FooItem2");
+        testItem("0|3|0", "cmd_BarItem1");
+        testItem("0|3|1", "cmd_BarItem2");
+        onTestsFinished()
+      }
+
+      setTimeout(_delayedOnLoad, 1000);
+    }    
+
+  ]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/widget/tests/test_native_menus.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window title="Native menu system tests"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Native menu system tests</title>
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body  xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+window.open("native_menus_window.xul", "NativeMenuWindow", 
+            "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
--- a/xpfe/appshell/src/Makefile.in
+++ b/xpfe/appshell/src/Makefile.in
@@ -99,8 +99,9 @@ EXTRA_DSO_LDOPTS = \
 		$(EXTRA_DSO_LIBS) \
 		$(MOZ_UNICHARUTIL_LIBS) \
 		$(MOZ_COMPONENT_LIBS) \
 		$(MOZ_JS_LIBS) \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
+DEFINES += -D_IMPL_NS_COM
--- a/xpfe/appshell/src/nsWebShellWindow.cpp
+++ b/xpfe/appshell/src/nsWebShellWindow.cpp
@@ -104,32 +104,29 @@
 
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 
 #include "nsIMarkupDocumentViewer.h"
 #include "nsIFocusEventSuppressor.h"
 
-#if defined(XP_MACOSX)
-#include "nsIMenuBar.h"
+#ifdef XP_MACOSX
+#include "nsINativeMenuService.h"
 #define USE_NATIVE_MENUS
 #endif
 
 static nsWebShellWindow* gCurrentlyFocusedWindow = nsnull;
 static nsWebShellWindow* gFocusedWindowBeforeSuppression = nsnull;
 static PRBool gFocusSuppressed = PR_FALSE;
 static PRUint32 gWebShellWindowCount = 0;
 
 /* Define Class IDs */
 static NS_DEFINE_CID(kWindowCID,           NS_WINDOW_CID);
 
-#include "nsWidgetsCID.h"
-static NS_DEFINE_CID(kMenuBarCID,          NS_MENUBAR_CID);
-
 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
 
 nsWebShellWindow::nsWebShellWindow() : nsXULWindow()
 {
   mSPTimerLock = PR_NewLock();
   if (++gWebShellWindowCount == 1) {
     nsCOMPtr<nsIFocusEventSuppressorService> suppressor =
       do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID);
@@ -547,33 +544,27 @@ nsWebShellWindow::HandleEvent(nsGUIEvent
 static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow)
 {
   // Find the menubar tag (if there is more than one, we ignore all but
   // the first).
   nsCOMPtr<nsIDOMNodeList> menubarElements;
   aDOMDoc->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
                                   NS_LITERAL_STRING("menubar"),
                                   getter_AddRefs(menubarElements));
-  
+
   nsCOMPtr<nsIDOMNode> menubarNode;
   if (menubarElements)
     menubarElements->Item(0, getter_AddRefs(menubarNode));
-
   if (!menubarNode)
     return;
 
-  nsCOMPtr<nsIMenuBar> pnsMenuBar = do_CreateInstance(kMenuBarCID);
-  if (!pnsMenuBar)
-    return;
-
-  pnsMenuBar->Create(aParentWindow);
-
-  // fake event
-  nsMenuEvent fake(PR_TRUE, 0, nsnull);
-  pnsMenuBar->MenuConstruct(fake, aParentWindow, menubarNode);
+  nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
+  nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
+  if (nms && menubarContent)
+    nms->CreateNativeMenuBar(aParentWindow, menubarContent);
 }
 #endif
 
 void
 nsWebShellWindow::SetPersistenceTimer(PRUint32 aDirtyFlags)
 {
   if (!mSPTimerLock)
     return;