Bug 279703, backing out the popup changes due to performance regressions. sigh.
authorenndeakin@sympatico.ca
Fri, 29 Jun 2007 15:15:59 -0700
changeset 2946 317abe6fc05ca31883fe0b36177bf7c62aa44388
parent 2945 8ba20dc675f47cd262bebf3da259185b98a6c44f
child 2947 2ac122696d2d3d20ccce2c0ab1a13acc7bfd9bbb
push idunknown
push userunknown
push dateunknown
bugs279703
milestone1.9a6pre
Bug 279703, backing out the popup changes due to performance regressions. sigh.
accessible/src/base/nsRootAccessible.cpp
browser/base/content/browser.xul
content/base/src/nsDocument.cpp
content/xul/content/public/Makefile.in
content/xul/content/public/nsIXULPopupListener.h
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULPopupListener.cpp
content/xul/content/src/nsXULPopupListener.h
content/xul/document/src/nsXULDocument.cpp
dom/public/idl/xul/nsIDOMXULDocument.idl
dom/src/base/nsGlobalWindow.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsDocumentViewer.cpp
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/build/nsLayoutModule.cpp
layout/build/nsLayoutStatics.cpp
layout/generic/nsContainerFrame.cpp
layout/xul/base/public/Makefile.in
layout/xul/base/public/nsIMenuFrame.h
layout/xul/base/public/nsIPopupBoxObject.idl
layout/xul/base/public/nsIPopupSetFrame.h
layout/xul/base/public/nsXULPopupManager.h
layout/xul/base/src/Makefile.in
layout/xul/base/src/nsMenuBarFrame.h
layout/xul/base/src/nsMenuBarListener.cpp
layout/xul/base/src/nsMenuBarListener.h
layout/xul/base/src/nsMenuBoxObject.cpp
layout/xul/base/src/nsMenuDismissalListener.cpp
layout/xul/base/src/nsMenuDismissalListener.h
layout/xul/base/src/nsMenuFrame.cpp
layout/xul/base/src/nsMenuFrame.h
layout/xul/base/src/nsMenuListener.cpp
layout/xul/base/src/nsMenuListener.h
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.h
layout/xul/base/src/nsPopupBoxObject.cpp
layout/xul/base/src/nsPopupSetFrame.cpp
layout/xul/base/src/nsPopupSetFrame.h
layout/xul/base/src/nsRootBoxFrame.cpp
layout/xul/base/src/nsXULPopupManager.cpp
toolkit/components/autocomplete/public/nsIAutoCompleteController.idl
toolkit/components/autocomplete/public/nsIAutoCompletePopup.idl
toolkit/components/autocomplete/src/nsAutoCompleteController.cpp
toolkit/components/satchel/src/nsFormFillController.cpp
toolkit/content/widgets/autocomplete.xml
toolkit/content/widgets/popup.xml
toolkit/content/xul.css
toolkit/themes/gnomestripe/global/popup.css
toolkit/themes/pinstripe/global/autocomplete.css
toolkit/themes/pinstripe/global/popup.css
toolkit/themes/winstripe/global/autocomplete.css
toolkit/themes/winstripe/global/popup.css
widget/src/gtk2/nsNativeThemeGTK.cpp
widget/src/windows/nsNativeThemeWin.cpp
widget/src/xpwidgets/nsWidgetAtomList.h
xpfe/appshell/src/nsWebShellWindow.cpp
xpfe/components/autocomplete/resources/content/autocomplete.xml
xpfe/global/resources/content/bindings/popup.xml
xpfe/global/resources/content/xul.css
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -57,17 +57,16 @@
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULPopupElement.h"
 #include "nsIDocument.h"
 #include "nsIEventListenerManager.h"
 #include "nsIFocusController.h"
 #include "nsIFrame.h"
-#include "nsIMenuFrame.h"
 #include "nsIHTMLDocument.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMenuParent.h"
 #include "nsIScrollableView.h"
 #include "nsISelectionPrivate.h"
 #include "nsIServiceManager.h"
 #include "nsIViewManager.h"
 #include "nsPIDOMWindow.h"
@@ -797,33 +796,32 @@ nsresult nsRootAccessible::HandleEventWi
   else if (eventType.EqualsLiteral("DOMMenuInactive")) {
     if (Role(accessible) == nsIAccessibleRole::ROLE_MENUPOPUP) {
       privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
                                 accessible, nsnull);
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
     if (!treeItemAccessible) {
-      nsCOMPtr<nsPIAccessNode> menuAccessNode = do_QueryInterface(accessible);
-      NS_ENSURE_TRUE(menuAccessNode, NS_ERROR_FAILURE);
-      nsIFrame* menuFrame = menuAccessNode->GetFrame();
-      NS_ENSURE_TRUE(menuFrame, NS_ERROR_FAILURE);
-      nsIMenuFrame* imenuFrame;
-      CallQueryInterface(menuFrame, &imenuFrame);
-      NS_ENSURE_TRUE(imenuFrame, NS_ERROR_FAILURE);
-      if (imenuFrame->IsOnMenuBar()) {
-        if (!imenuFrame->IsOnActiveMenuBar()) {
+      nsCOMPtr<nsIAccessible> containerAccessible;
+      accessible->GetParent(getter_AddRefs(containerAccessible));
+      NS_ENSURE_TRUE(containerAccessible, NS_OK);
+      if (Role(containerAccessible) == nsIAccessibleRole::ROLE_MENUBAR) {
+        nsCOMPtr<nsPIAccessNode> menuBarAccessNode(do_QueryInterface(containerAccessible));
+        NS_ENSURE_TRUE(menuBarAccessNode, NS_ERROR_FAILURE);
+        nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(menuBarAccessNode->GetFrame());
+        NS_ENSURE_TRUE(menuParent, NS_ERROR_FAILURE);
+        PRBool isActive;
+        menuParent->GetIsActive(isActive);
+        if (!isActive) {
           // It is a top level menuitem. Only fire a focus event when the menu bar
           // is active.
           return NS_OK;
         }
       } else {
-        nsCOMPtr<nsIAccessible> containerAccessible;
-        accessible->GetParent(getter_AddRefs(containerAccessible));
-        NS_ENSURE_TRUE(containerAccessible, NS_ERROR_FAILURE);
         // It is not top level menuitem
         // Only fire focus event if it is not inside collapsed popup
         if (State(containerAccessible) & nsIAccessibleStates::STATE_COLLAPSED)
           return NS_OK;
       }
     }
     FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE);
   }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -102,17 +102,17 @@
            oncommand="gotoHistoryIndex(event);"
            onclick="checkForMiddleClick(this, event);"/>
     <tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);">
       <hbox>
         <label flex="1" class="htmltooltip-label"/>
       </hbox>
     </tooltip>
 
-    <panel type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete"/>
+    <popup type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete"/>
 
     <popup id="toolbar-context-menu"
            onpopupshowing="onViewToolbarsPopupShowing(event);">
       <menuseparator/>
       <menuitem command="cmd_CustomizeToolbars"
                 label="&viewCustomizeToolbar.label;"
                 accesskey="&viewCustomizeToolbar.accesskey;"/>
     </popup>
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3677,17 +3677,16 @@ nsDocument::GetBoxObjectFor(nsIDOMElemen
     if (tag == nsGkAtoms::browser ||
         tag == nsGkAtoms::editor ||
         tag == nsGkAtoms::iframe)
       contractID += "-container";
     else if (tag == nsGkAtoms::menu)
       contractID += "-menu";
     else if (tag == nsGkAtoms::popup ||
              tag == nsGkAtoms::menupopup ||
-             tag == nsGkAtoms::panel ||
              tag == nsGkAtoms::tooltip)
       contractID += "-popup";
     else if (tag == nsGkAtoms::tree)
       contractID += "-tree";
     else if (tag == nsGkAtoms::listbox)
       contractID += "-listbox";
     else if (tag == nsGkAtoms::scrollbox)
       contractID += "-scrollbox";
new file mode 100644
--- /dev/null
+++ b/content/xul/content/public/Makefile.in
@@ -0,0 +1,54 @@
+#
+# ***** 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 of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# 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 *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= xul
+
+ifdef MOZ_XUL
+EXPORTS		= \
+                nsIXULPopupListener.h \
+		$(NULL)
+endif
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/content/xul/content/public/nsIXULPopupListener.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** 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 of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 nsIXULPopupListener_h__
+#define nsIXULPopupListener_h__
+
+// {2C453161-0942-11d3-BF87-00105A1B0627}
+#define NS_IXULPOPUPLISTENER_IID \
+{ 0x2c453161, 0x942, 0x11d3, { 0xbf, 0x87, 0x0, 0x10, 0x5a, 0x1b, 0x6, 0x27 } }
+
+class nsIDOMElement;
+
+typedef enum {
+    eXULPopupType_popup,
+    eXULPopupType_context,
+    eXULPopupType_tooltip,
+    eXULPopupType_blur
+} XULPopupType;
+
+class nsIXULPopupListener: public nsISupports {
+public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULPOPUPLISTENER_IID)
+
+    NS_IMETHOD Init(nsIDOMElement* anElement, const XULPopupType& aPopupType) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPopupListener, NS_IXULPOPUPLISTENER_IID)
+
+nsresult
+NS_NewXULPopupListener(nsIXULPopupListener** result);
+
+#endif // nsIXULPopupListener_h__
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -102,28 +102,28 @@
 #include "nsIScriptGlobalObjectOwner.h"
 #include "nsIServiceManager.h"
 #include "nsICSSStyleRule.h"
 #include "nsIStyleSheet.h"
 #include "nsIURL.h"
 #include "nsIViewManager.h"
 #include "nsIWidget.h"
 #include "nsIXULDocument.h"
+#include "nsIXULPopupListener.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsIXBLService.h"
 #include "nsLayoutCID.h"
 #include "nsContentCID.h"
 #include "nsRDFCID.h"
 #include "nsStyleConsts.h"
 #include "nsXPIDLString.h"
 #include "nsXULControllers.h"
 #include "nsIBoxObject.h"
 #include "nsPIBoxObject.h"
 #include "nsXULDocument.h"
-#include "nsXULPopupListener.h"
 #include "nsRuleWalker.h"
 #include "nsIDOMViewCSS.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsCSSDeclaration.h"
 #include "nsIListBoxObject.h"
 #include "nsContentUtils.h"
 #include "nsContentList.h"
 #include "nsMutationEvent.h"
@@ -2042,67 +2042,73 @@ nsXULElement::IsNodeOfType(PRUint32 aFla
 {
     return !(aFlags & ~(eCONTENT | eELEMENT | eXUL));
 }
 
 static void
 PopupListenerPropertyDtor(void* aObject, nsIAtom* aPropertyName,
                           void* aPropertyValue, void* aData)
 {
-  nsIDOMEventListener* listener =
-    NS_STATIC_CAST(nsIDOMEventListener*, aPropertyValue);
+  nsIXULPopupListener* listener =
+    NS_STATIC_CAST(nsIXULPopupListener*, aPropertyValue);
   if (!listener) {
     return;
   }
+  nsCOMPtr<nsIDOMEventListener> eventListener = do_QueryInterface(listener);
   nsCOMPtr<nsIDOMEventTarget> target =
     do_QueryInterface(NS_STATIC_CAST(nsINode*, aObject));
   if (target) {
-    target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), listener,
+    target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), eventListener,
                                 PR_FALSE);
-    target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), listener,
+    target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), eventListener,
                                 PR_FALSE);
   }
   NS_RELEASE(listener);
 }
 
 nsresult
 nsXULElement::AddPopupListener(nsIAtom* aName)
 {
-    // Add a popup listener to the element
-    PRBool isContext = (aName == nsGkAtoms::context ||
-                        aName == nsGkAtoms::contextmenu);
-    nsIAtom* listenerAtom = isContext ?
-                            nsGkAtoms::contextmenulistener :
-                            nsGkAtoms::popuplistener;
-
-    nsCOMPtr<nsIDOMEventListener> popupListener =
-        NS_STATIC_CAST(nsIDOMEventListener*, GetProperty(listenerAtom));
+    XULPopupType popupType;
+    nsCOMPtr<nsIAtom> listenerAtom;
+    if (aName == nsGkAtoms::context || aName == nsGkAtoms::contextmenu) {
+        popupType = eXULPopupType_context;
+        listenerAtom = nsGkAtoms::contextmenulistener;
+    } else {
+        popupType = eXULPopupType_popup;
+        listenerAtom = nsGkAtoms::popuplistener;
+    }
+
+    nsCOMPtr<nsIXULPopupListener> popupListener =
+        NS_STATIC_CAST(nsIXULPopupListener*, GetProperty(listenerAtom));
     if (popupListener) {
         // Popup listener is already installed.
         return NS_OK;
     }
-
-    nsresult rv = NS_NewXULPopupListener(this, isContext,
-                                         getter_AddRefs(popupListener));
-    if (NS_FAILED(rv))
-        return rv;
+    // Add a popup listener to the element
+    nsresult rv;
+
+    popupListener = do_CreateInstance(kXULPopupListenerCID, &rv);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to create an instance of the popup listener object.");
+    if (NS_FAILED(rv)) return rv;
+
+    // Add a weak reference to the node.
+    popupListener->Init(this, popupType);
 
     // Add the popup as a listener on this element.
+    nsCOMPtr<nsIDOMEventListener> eventListener = do_QueryInterface(popupListener);
     nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this)));
     NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
     rv = SetProperty(listenerAtom, popupListener, PopupListenerPropertyDtor,
                      PR_TRUE);
     NS_ENSURE_SUCCESS(rv, rv);
-    // Want the property to have a reference to the listener.
-    nsIDOMEventListener* listener = nsnull;
-    popupListener.swap(listener);
-    if (isContext)
-      target->AddEventListener(NS_LITERAL_STRING("contextmenu"), listener, PR_FALSE);
-    else
-      target->AddEventListener(NS_LITERAL_STRING("mousedown"), listener, PR_FALSE);
+    nsIXULPopupListener* listener = popupListener;
+    NS_ADDREF(listener);
+    target->AddEventListener(NS_LITERAL_STRING("mousedown"), eventListener, PR_FALSE);
+    target->AddEventListener(NS_LITERAL_STRING("contextmenu"), eventListener, PR_FALSE);
     return NS_OK;
 }
 
 PRInt32
 nsXULElement::IntrinsicState() const
 {
     PRInt32 state = nsGenericElement::IntrinsicState();
 
--- a/content/xul/content/src/nsXULPopupListener.cpp
+++ b/content/xul/content/src/nsXULPopupListener.cpp
@@ -39,101 +39,180 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
   This file provides the implementation for xul popup listener which
   tracks xul popups and context menus
  */
 
-#include "nsXULPopupListener.h"
 #include "nsCOMPtr.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentXBL.h"
+#include "nsIXULPopupListener.h"
+#include "nsIDOMMouseListener.h"
+#include "nsIDOMContextMenuListener.h"
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
-#include "nsXULPopupManager.h"
 
 #include "nsIScriptContext.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDocument.h"
+#include "nsIContent.h"
+#include "nsIDOMMouseEvent.h"
 #include "nsIDOMNSUIEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNSEvent.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
-#include "nsLayoutUtils.h"
-#include "nsFrameManager.h"
-#include "nsHTMLReflowState.h"
+
+#include "nsIBoxObject.h"
+#include "nsIPopupBoxObject.h"
 
 // for event firing in context menus
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIEventStateManager.h"
 #include "nsIFocusController.h"
 #include "nsPIDOMWindow.h"
-#include "nsIViewManager.h"
 #include "nsDOMError.h"
+
+#include "nsIFrame.h"
 #include "nsIMenuFrame.h"
 
 // on win32 and os/2, context menus come up on mouse up. On other platforms,
 // they appear on mouse down. Certain bits of code care about this difference.
 #if !defined(XP_WIN) && !defined(XP_OS2)
 #define NS_CONTEXT_MENU_IS_MOUSEUP 1
 #endif
 
-nsXULPopupListener::nsXULPopupListener(nsIDOMElement *aElement, PRBool aIsContext)
-  : mElement(aElement), mPopupContent(nsnull), mIsContext(aIsContext)
+
+////////////////////////////////////////////////////////////////////////
+// PopupListenerImpl
+//
+//   This is the popup listener implementation for popup menus and context menus.
+//
+class XULPopupListenerImpl : public nsIXULPopupListener,
+                             public nsIDOMMouseListener,
+                             public nsIDOMContextMenuListener
+{
+public:
+    XULPopupListenerImpl(void);
+    virtual ~XULPopupListenerImpl(void);
+
+public:
+    // nsISupports
+    NS_DECL_ISUPPORTS
+
+    // nsIXULPopupListener
+    NS_IMETHOD Init(nsIDOMElement* aElement, const XULPopupType& popupType);
+
+    // nsIDOMMouseListener
+    NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent);
+    NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+    NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+    NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+    NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+    NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+
+    // nsIDOMContextMenuListener
+    NS_IMETHOD ContextMenu(nsIDOMEvent* aContextMenuEvent);
+
+    // nsIDOMEventListener
+    NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return NS_OK; }
+
+protected:
+
+    virtual nsresult LaunchPopup(nsIDOMEvent* anEvent);
+    virtual nsresult LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) ;
+
+private:
+
+    nsresult PreLaunchPopup(nsIDOMEvent* aMouseEvent);
+    nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode);
+
+    // |mElement| is the node to which this listener is attached.
+    nsIDOMElement* mElement;               // Weak ref. The element will go away first.
+
+    // The popup that is getting shown on top of mElement.
+    nsCOMPtr<nsIPopupBoxObject> mPopup;
+
+    // The type of the popup
+    XULPopupType popupType;
+    
+};
+
+////////////////////////////////////////////////////////////////////////
+      
+XULPopupListenerImpl::XULPopupListenerImpl(void)
+  : mElement(nsnull)
 {
 }
 
-nsXULPopupListener::~nsXULPopupListener(void)
+XULPopupListenerImpl::~XULPopupListenerImpl(void)
 {
-  ClosePopup();
+  if (mPopup) {
+    mPopup->HidePopup();
+  }
+  
+#ifdef DEBUG_REFS
+    --gInstanceCount;
+    fprintf(stdout, "%d - RDF: XULPopupListenerImpl\n", gInstanceCount);
+#endif
 }
 
-NS_IMPL_ADDREF(nsXULPopupListener)
-NS_IMPL_RELEASE(nsXULPopupListener)
+NS_IMPL_ADDREF(XULPopupListenerImpl)
+NS_IMPL_RELEASE(XULPopupListenerImpl)
 
 
-NS_INTERFACE_MAP_BEGIN(nsXULPopupListener)
+NS_INTERFACE_MAP_BEGIN(XULPopupListenerImpl)
+  NS_INTERFACE_MAP_ENTRY(nsIXULPopupListener)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
   NS_INTERFACE_MAP_ENTRY(nsIDOMContextMenuListener)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMMouseListener)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULPopupListener)
 NS_INTERFACE_MAP_END
 
+NS_IMETHODIMP
+XULPopupListenerImpl::Init(nsIDOMElement* aElement, const XULPopupType& popup)
+{
+  mElement = aElement; // Weak reference. Don't addref it.
+  popupType = popup;
+  return NS_OK;
+}
+
 ////////////////////////////////////////////////////////////////
 // nsIDOMMouseListener
 
 nsresult
-nsXULPopupListener::MouseDown(nsIDOMEvent* aMouseEvent)
+XULPopupListenerImpl::MouseDown(nsIDOMEvent* aMouseEvent)
 {
-  if(!mIsContext)
+  if(popupType != eXULPopupType_context)
     return PreLaunchPopup(aMouseEvent);
   else
     return NS_OK;
 }
 
 nsresult
-nsXULPopupListener::ContextMenu(nsIDOMEvent* aMouseEvent)
+XULPopupListenerImpl::ContextMenu(nsIDOMEvent* aMouseEvent)
 {
-  if(mIsContext)
+  if(popupType == eXULPopupType_context)
     return PreLaunchPopup(aMouseEvent);
   else 
     return NS_OK;
 }
 
 nsresult
-nsXULPopupListener::PreLaunchPopup(nsIDOMEvent* aMouseEvent)
+XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent)
 {
   PRUint16 button;
 
   nsCOMPtr<nsIDOMMouseEvent> mouseEvent;
   mouseEvent = do_QueryInterface(aMouseEvent);
   if (!mouseEvent) {
     //non-ui event passed in.  bad things.
     return NS_OK;
@@ -146,17 +225,17 @@ nsXULPopupListener::PreLaunchPopup(nsIDO
     return NS_OK;
   }
 
   // Get the node that was clicked on.
   nsCOMPtr<nsIDOMEventTarget> target;
   mouseEvent->GetTarget(getter_AddRefs(target));
   nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target);
 
-  if (!targetNode && mIsContext) {
+  if (!targetNode && popupType == eXULPopupType_context) {
     // Not a DOM node, see if it's the DOM window (bug 380818).
     nsCOMPtr<nsIDOMWindow> domWin = do_QueryInterface(target);
     if (!domWin) {
       return NS_ERROR_DOM_WRONG_TYPE_ERR;
     }
     // Try to use the root node as target node.
     nsCOMPtr<nsIDOMDocument> domDoc;
     domWin->GetDocument(getter_AddRefs(domDoc));
@@ -166,17 +245,17 @@ nsXULPopupListener::PreLaunchPopup(nsIDO
       targetNode = do_QueryInterface(doc->GetRootContent());
     if (!targetNode) {
       return NS_ERROR_FAILURE;
     }
   }
 
   PRBool preventDefault;
   nsUIEvent->GetPreventDefault(&preventDefault);
-  if (preventDefault && targetNode && mIsContext) {
+  if (preventDefault && targetNode && popupType == eXULPopupType_context) {
     // Someone called preventDefault on a context menu.
     // Let's make sure they are allowed to do so.
     PRBool eventEnabled =
       nsContentUtils::GetBoolPref("dom.event.contextmenu.enabled", PR_TRUE);
     if (!eventEnabled) {
       // The user wants his contextmenus.  Let's make sure that this is a website
       // and not chrome since there could be places in chrome which don't want
       // contextmenus.
@@ -194,65 +273,75 @@ nsXULPopupListener::PreLaunchPopup(nsIDO
     }
   }
 
   if (preventDefault) {
     // someone called preventDefault. bail.
     return NS_OK;
   }
 
-  // prevent popups on menu and menuitems as they handle their own popups
-  // This was added for bug 96920.
+  // This is a gross hack to deal with a recursive popup situation happening in AIM code. 
+  // See http://bugzilla.mozilla.org/show_bug.cgi?id=96920.
   // If a menu item child was clicked on that leads to a popup needing
   // to show, we know (guaranteed) that we're dealing with a menu or
   // submenu of an already-showing popup.  We don't need to do anything at all.
-  nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
-  if (!mIsContext) {
+  if (popupType == eXULPopupType_popup) {
+    nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
     nsIAtom *tag = targetContent ? targetContent->Tag() : nsnull;
     if (tag == nsGkAtoms::menu || tag == nsGkAtoms::menuitem)
       return NS_OK;
   }
 
   // Get the document with the popup.
   nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
 
   // Turn the document into a XUL document so we can use SetPopupNode.
   nsCOMPtr<nsIDOMXULDocument> xulDocument = do_QueryInterface(content->GetDocument());
-  if (!xulDocument)
+  if (!xulDocument) {
+    NS_ERROR("Popup attached to an element that isn't in XUL!");
     return NS_ERROR_FAILURE;
+  }
 
   // Store clicked-on node in xul document for context menus and menu popups.
-  xulDocument->SetPopupNode(targetNode);
+  // CLEAR THE POPUP EVENT BEFORE THIS FUNCTION EXITS
+  xulDocument->SetPopupNode( targetNode );
+  xulDocument->SetTrustedPopupEvent( aMouseEvent );
 
   nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aMouseEvent));
 
-  if (mIsContext) {
+  switch (popupType) {
+    case eXULPopupType_popup:
+      // Check for left mouse button down
+      mouseEvent->GetButton(&button);
+      if (button == 0) {
+        // Time to launch a popup menu.
+        LaunchPopup(aMouseEvent);
+        aMouseEvent->StopPropagation();
+        aMouseEvent->PreventDefault();
+      }
+      break;
+    case eXULPopupType_context:
+
+      // Time to launch a context menu
 #ifndef NS_CONTEXT_MENU_IS_MOUSEUP
-    // If the context menu launches on mousedown,
-    // we have to fire focus on the content we clicked on
-    FireFocusOnTargetContent(targetNode);
+      // If the context menu launches on mousedown,
+      // we have to fire focus on the content we clicked on
+      FireFocusOnTargetContent(targetNode);
 #endif
+      LaunchPopup(aMouseEvent);
+      aMouseEvent->StopPropagation();
+      aMouseEvent->PreventDefault();
+      break;
   }
-  else {
-    // Only open popups when the left mouse button is down.
-    mouseEvent->GetButton(&button);
-    if (button != 0)
-      return NS_OK;
-  }
-
-  // Open the popup and cancel the default handling of the event.
-  LaunchPopup(aMouseEvent, targetContent);
-  aMouseEvent->StopPropagation();
-  aMouseEvent->PreventDefault();
-
+  xulDocument->SetTrustedPopupEvent(nsnull);
   return NS_OK;
 }
 
 nsresult
-nsXULPopupListener::FireFocusOnTargetContent(nsIDOMNode* aTargetNode)
+XULPopupListenerImpl::FireFocusOnTargetContent(nsIDOMNode* aTargetNode)
 {
   nsresult rv;
   nsCOMPtr<nsIDOMDocument> domDoc;
   rv = aTargetNode->GetOwnerDocument(getter_AddRefs(domDoc));
   if(NS_SUCCEEDED(rv) && domDoc)
   {
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
 
@@ -314,35 +403,36 @@ nsXULPopupListener::FireFocusOnTargetCon
     } else if (!suppressBlur)
       esm->SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
 
     esm->SetContentState(focusableContent, NS_EVENT_STATE_ACTIVE);
   }
   return rv;
 }
 
-// ClosePopup
 //
-// Do everything needed to shut down the popup.
+// LaunchPopup
 //
-// NOTE: This routine is safe to call even if the popup is already closed.
-//
-void
-nsXULPopupListener::ClosePopup()
+nsresult
+XULPopupListenerImpl::LaunchPopup ( nsIDOMEvent* anEvent )
 {
-  if (mPopupContent) {
-    // this is called when the listener is going away, so make sure that the
-    // popup is hidden. Use asynchronous hiding just to be safe so we don't
-    // fire events during destruction.  
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (pm)
-      pm->HidePopup(mPopupContent, PR_FALSE, PR_TRUE, PR_TRUE);
-    mPopupContent = nsnull;  // release the popup
+  // Retrieve our x and y position.
+  nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(anEvent) );
+  if (!mouseEvent) {
+    //non-ui event passed in.  bad things.
+    return NS_OK;
   }
-} // ClosePopup
+
+  PRInt32 xPos, yPos;
+  mouseEvent->GetClientX(&xPos); 
+  mouseEvent->GetClientY(&yPos); 
+
+  return LaunchPopup(xPos, yPos);
+}
+
 
 static void
 GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 
 {
   *aResult = nsnull;
   PRInt32 childCount = aContent->GetChildCount();
   for (PRInt32 i = 0; i < childCount; i++) {
     nsIContent *child = aContent->GetChildAt(i);
@@ -351,39 +441,89 @@ GetImmediateChild(nsIContent* aContent, 
       NS_ADDREF(*aResult);
       return;
     }
   }
 
   return;
 }
 
+static void ConvertPosition(nsIDOMElement* aPopupElt, nsString& aAnchor, nsString& aAlign, PRInt32& aY)
+{
+  nsAutoString position;
+  aPopupElt->GetAttribute(NS_LITERAL_STRING("position"), position);
+  if (position.IsEmpty())
+    return;
+
+  if (position.EqualsLiteral("before_start")) {
+    aAnchor.AssignLiteral("topleft");
+    aAlign.AssignLiteral("bottomleft");
+  }
+  else if (position.EqualsLiteral("before_end")) {
+    aAnchor.AssignLiteral("topright");
+    aAlign.AssignLiteral("bottomright");
+  }
+  else if (position.EqualsLiteral("after_start")) {
+    aAnchor.AssignLiteral("bottomleft");
+    aAlign.AssignLiteral("topleft");
+  }
+  else if (position.EqualsLiteral("after_end")) {
+    aAnchor.AssignLiteral("bottomright");
+    aAlign.AssignLiteral("topright");
+  }
+  else if (position.EqualsLiteral("start_before")) {
+    aAnchor.AssignLiteral("topleft");
+    aAlign.AssignLiteral("topright");
+  }
+  else if (position.EqualsLiteral("start_after")) {
+    aAnchor.AssignLiteral("bottomleft");
+    aAlign.AssignLiteral("bottomright");
+  }
+  else if (position.EqualsLiteral("end_before")) {
+    aAnchor.AssignLiteral("topright");
+    aAlign.AssignLiteral("topleft");
+  }
+  else if (position.EqualsLiteral("end_after")) {
+    aAnchor.AssignLiteral("bottomright");
+    aAlign.AssignLiteral("bottomleft");
+  }
+  else if (position.EqualsLiteral("overlap")) {
+    aAnchor.AssignLiteral("topleft");
+    aAlign.AssignLiteral("topleft");
+  }
+  else if (position.EqualsLiteral("after_pointer"))
+    aY += 21;
+}
+
 //
 // LaunchPopup
 //
 // Given the element on which the event was triggered and the mouse locations in
 // Client and widget coordinates, popup a new window showing the appropriate 
 // content.
 //
-// aTargetContent is the target of the mouse event aEvent that triggered the
-// popup. mElement is the element that the popup menu is attached to. The
-// former may be equal to mElement or it may be a descendant.
-//
-// This looks for an attribute on |mElement| of the appropriate popup type 
+// This looks for an attribute on |aElement| of the appropriate popup type 
 // (popup, context) and uses that attribute's value as an ID for
 // the popup content in the document.
 //
 nsresult
-nsXULPopupListener::LaunchPopup(nsIDOMEvent* aEvent, nsIContent* aTargetContent)
+XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY)
 {
   nsresult rv = NS_OK;
 
   nsAutoString type(NS_LITERAL_STRING("popup"));
-  if (mIsContext)
+  if ( popupType == eXULPopupType_context ) {
     type.AssignLiteral("context");
+    
+    // position the menu two pixels down and to the right from the current
+    // mouse position. This makes it easier to dismiss the menu by just
+    // clicking.
+    aClientX += 2;
+    aClientY += 2;
+  }
 
   nsAutoString identifier;
   mElement->GetAttribute(type, identifier);
 
   if (identifier.IsEmpty()) {
     if (type.EqualsLiteral("popup"))
       mElement->GetAttribute(NS_LITERAL_STRING("menu"), identifier);
     else if (type.EqualsLiteral("context"))
@@ -399,110 +539,106 @@ nsXULPopupListener::LaunchPopup(nsIDOMEv
   // Turn the document into a DOM document so we can use getElementById
   nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(document);
   if (!domDocument) {
     NS_ERROR("Popup attached to an element that isn't in XUL!");
     return NS_ERROR_FAILURE;
   }
 
   // Handle the _child case for popups and context menus
-  nsCOMPtr<nsIDOMElement> popupElement;
+  nsCOMPtr<nsIDOMElement> popupContent;
 
   if (identifier.EqualsLiteral("_child")) {
     nsCOMPtr<nsIContent> popup;
 
     GetImmediateChild(content, nsGkAtoms::menupopup, getter_AddRefs(popup));
     if (popup)
-      popupElement = do_QueryInterface(popup);
+      popupContent = do_QueryInterface(popup);
     else {
       nsCOMPtr<nsIDOMDocumentXBL> nsDoc(do_QueryInterface(domDocument));
       nsCOMPtr<nsIDOMNodeList> list;
       nsDoc->GetAnonymousNodes(mElement, getter_AddRefs(list));
       if (list) {
         PRUint32 ctr,listLength;
         nsCOMPtr<nsIDOMNode> node;
         list->GetLength(&listLength);
         for (ctr = 0; ctr < listLength; ctr++) {
           list->Item(ctr, getter_AddRefs(node));
           nsCOMPtr<nsIContent> childContent(do_QueryInterface(node));
 
           if (childContent->NodeInfo()->Equals(nsGkAtoms::menupopup,
                                                kNameSpaceID_XUL)) {
-            popupElement = do_QueryInterface(childContent);
+            popupContent = do_QueryInterface(childContent);
             break;
           }
         }
       }
     }
   }
   else if (NS_FAILED(rv = domDocument->GetElementById(identifier,
-                                              getter_AddRefs(popupElement)))) {
+                                              getter_AddRefs(popupContent)))) {
     // Use getElementById to obtain the popup content and gracefully fail if 
     // we didn't find any popup content in the document. 
     NS_ERROR("GetElementById had some kind of spasm.");
     return rv;
   }
-
-  // return if no popup was found or the popup is the element itself.
-  if ( !popupElement || popupElement == mElement)
+  if ( !popupContent )
     return NS_OK;
 
   // Submenus can't be used as context menus or popups, bug 288763.
   // Similar code also in nsXULTooltipListener::GetTooltipFor.
-  nsCOMPtr<nsIContent> popup = do_QueryInterface(popupElement);
+  nsCOMPtr<nsIContent> popup = do_QueryInterface(popupContent);
   nsIContent* parent = popup->GetParent();
   if (parent) {
     nsIDocument* doc = parent->GetCurrentDoc();
     nsIPresShell* presShell = doc ? doc->GetPrimaryShell() : nsnull;
     nsIFrame* frame = presShell ? presShell->GetPrimaryFrameFor(parent) : nsnull;
-    if (frame && frame->GetType() == nsGkAtoms::menuFrame)
-      return NS_OK;
+    if (frame) {
+      nsIMenuFrame* menu = nsnull;
+      CallQueryInterface(frame, &menu);
+      NS_ENSURE_FALSE(menu, NS_OK);
+    }
   }
 
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (!pm)
-    return NS_OK;
+  // We have some popup content. Obtain our window.
+  nsPIDOMWindow *domWindow = document->GetWindow();
 
-  // XXXndeakin this is temporary. It is needed to grab the mouse location details
-  //            used by the spellchecking popup. See bug 383930.
-  pm->SetMouseLocation(aEvent);
+  if (domWindow) {
+    // Find out if we're anchored.
+    nsAutoString anchorAlignment;
+    popupContent->GetAttribute(NS_LITERAL_STRING("popupanchor"), anchorAlignment);
+
+    nsAutoString popupAlignment;
+    popupContent->GetAttribute(NS_LITERAL_STRING("popupalign"), popupAlignment);
+
+    PRInt32 xPos = aClientX, yPos = aClientY;
 
-  // if the popup has an anchoring attribute, anchor it to the element,
-  // otherwise just open it at the screen position where the mouse was clicked.
-  mPopupContent = popup;
-  if (mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::position) ||
-      mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popupanchor) ||
-      mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popupalign)) {
-    pm->ShowPopup(mPopupContent, content, EmptyString(), 0, 0,
-                  mIsContext, PR_TRUE, PR_FALSE);
-  }
-  else {
-    PRInt32 xPos = 0, yPos = 0;
-    nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
-    mouseEvent->GetScreenX(&xPos);
-    mouseEvent->GetScreenY(&yPos);
+    ConvertPosition(popupContent, anchorAlignment, popupAlignment, yPos);
+    if (!anchorAlignment.IsEmpty() && !popupAlignment.IsEmpty())
+      xPos = yPos = -1;
 
-    if (mIsContext) {
-      // position the menu two pixels down and to the right from the current
-      // mouse position. This makes it easier to dismiss the menu by just clicking
-      xPos += 2;
-      yPos += 2;
+    nsCOMPtr<nsIBoxObject> popupBox;
+    nsCOMPtr<nsIDOMXULElement> xulPopupElt(do_QueryInterface(popupContent));
+    xulPopupElt->GetBoxObject(getter_AddRefs(popupBox));
+    nsCOMPtr<nsIPopupBoxObject> popupBoxObject(do_QueryInterface(popupBox));
+    if (popupBoxObject) {
+      mPopup = popupBoxObject;
+      popupBoxObject->ShowPopup(mElement, popupContent, xPos, yPos, 
+                                type.get(), anchorAlignment.get(), 
+                                popupAlignment.get());
     }
-
-    pm->ShowPopupAtScreen(mPopupContent, xPos, yPos, mIsContext);
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////
 nsresult
-NS_NewXULPopupListener(nsIDOMElement* aElement, PRBool aIsContext,
-                       nsIDOMEventListener** aListener)
+NS_NewXULPopupListener(nsIXULPopupListener** pop)
 {
-    nsXULPopupListener* pl = new nsXULPopupListener(aElement, aIsContext);
-    if (!pl)
+    XULPopupListenerImpl* popup = new XULPopupListenerImpl();
+    if (!popup)
       return NS_ERROR_OUT_OF_MEMORY;
-
-    *aListener = NS_STATIC_CAST(nsIDOMMouseListener *, pl);
-    NS_ADDREF(*aListener);
+    
+    NS_ADDREF(popup);
+    *pop = popup;
     return NS_OK;
 }
deleted file mode 100644
--- a/content/xul/content/src/nsXULPopupListener.h
+++ /dev/null
@@ -1,123 +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):
- *   Original Author: David W. Hyatt (hyatt@netscape.com)
- *   Dean Tessman <dean_tessman@hotmail.com>
- *   Pierre Phaneuf <pp@ludusdesign.com>
- *   Robert O'Callahan <roc+moz@cs.cmu.edu>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * 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 ***** */
-
-/**
- * This is the popup listener implementation for popup menus and context menus.
- */
-
-#ifndef nsXULPopupListener_h___
-#define nsXULPopupListener_h___
-
-#include "nsCOMPtr.h"
-
-#include "nsIContent.h"
-#include "nsIDOMElement.h"
-#include "nsIDOMMouseEvent.h"
-#include "nsIFrame.h"
-#include "nsIDOMMouseListener.h"
-#include "nsIDOMContextMenuListener.h"
-
-class nsXULPopupListener : public nsIDOMMouseListener,
-                           public nsIDOMContextMenuListener
-{
-public:
-    // aElement is the element that the popup is attached to. If aIsContext is
-    // false, the popup opens on left click on aElement or a descendant. If
-    // aIsContext is true, the popup is a context menu which opens on a
-    // context menu event.
-    nsXULPopupListener(nsIDOMElement *aElement, PRBool aIsContext);
-    virtual ~nsXULPopupListener(void);
-
-public:
-    // nsISupports
-    NS_DECL_ISUPPORTS
-
-    // nsIDOMMouseListener
-    NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent);
-    NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; }
-    NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
-    NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
-    NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; }
-    NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; }
-
-    // nsIDOMContextMenuListener
-    NS_IMETHOD ContextMenu(nsIDOMEvent* aContextMenuEvent);
-
-    // nsIDOMEventListener
-    NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return NS_OK; }
-
-protected:
-
-    // open the popup. aEvent is the event that triggered the popup such as
-    // a mouse click and aTargetContent is the target of this event.
-    virtual nsresult LaunchPopup(nsIDOMEvent* aEvent, nsIContent* aTargetContent);
-
-    // close the popup when the listener goes away
-    virtual void ClosePopup();
-
-private:
-
-    // PreLaunchPopup is called before LaunchPopup to ensure that the event is
-    // suitable and to initialize the XUL document's popupNode to the event
-    // target.
-    nsresult PreLaunchPopup(nsIDOMEvent* aMouseEvent);
-
-    // When a context menu is opened, focus the target of the contextmenu event.
-    nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode);
-
-    // |mElement| is the node to which this listener is attached.
-    nsIDOMElement* mElement;               // Weak ref. The element will go away first.
-
-    // The popup that is getting shown on top of mElement.
-    nsCOMPtr<nsIContent> mPopupContent; 
-
-    // true if a context popup
-    PRBool mIsContext;
-};
-
-// Construct a new nsXULPopupListener and return in aListener. See the
-// nsXULPopupListener constructor for details about the aElement and
-// aIsContext arguments.
-nsresult
-NS_NewXULPopupListener(nsIDOMElement* aElement, PRBool aIsContext,
-                       nsIDOMEventListener** aListener);
-
-#endif // nsXULPopupListener_h___
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -123,17 +123,16 @@
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsEventDispatcher.h"
 #include "nsContentErrors.h"
 #include "nsIObserverService.h"
 #include "nsNodeUtils.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
-#include "nsXULPopupManager.h"
 
 //----------------------------------------------------------------------
 //
 // CIDs
 //
 
 static NS_DEFINE_CID(kLocalStoreCID,             NS_LOCALSTORE_CID);
 static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
@@ -1452,59 +1451,94 @@ nsXULDocument::SetPopupNode(nsIDOMNode* 
     GetFocusController(getter_AddRefs(focusController));
     NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
     // set popup node
     rv = focusController->SetPopupNode(aNode);
 
     return rv;
 }
 
-// Returns the rangeOffset element from the XUL Popup Manager. This is for
-// chrome callers only.
+NS_IMETHODIMP
+nsXULDocument::GetTrustedPopupEvent(nsIDOMEvent** aEvent)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIFocusController> focusController;
+    GetFocusController(getter_AddRefs(focusController));
+    NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
+
+    rv = focusController->GetPopupEvent(aEvent);
+
+    return rv;
+}
+
+NS_IMETHODIMP
+nsXULDocument::SetTrustedPopupEvent(nsIDOMEvent* aEvent)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIFocusController> focusController;
+    GetFocusController(getter_AddRefs(focusController));
+    NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
+
+    rv = focusController->SetPopupEvent(aEvent);
+
+    return rv;
+}
+
+// Returns the rangeOffset element from the popupEvent. This is for chrome
+// callers only.
 NS_IMETHODIMP
 nsXULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent)
 {
     NS_ENSURE_ARG_POINTER(aRangeParent);
     *aRangeParent = nsnull;
 
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (!pm)
-        return NS_ERROR_FAILURE;
-
-    PRInt32 offset;
-    pm->GetMouseLocation(aRangeParent, &offset);
-
-    if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) {
+    nsCOMPtr<nsIDOMEvent> event;
+    nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event));
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (! event)
+        return NS_ERROR_UNEXPECTED; // no event active
+
+    nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(event, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = uiEvent->GetRangeParent(aRangeParent); // addrefs
+
+    if (NS_SUCCEEDED(rv) && *aRangeParent &&
+            !nsContentUtils::CanCallerAccess(*aRangeParent)) {
         NS_RELEASE(*aRangeParent);
         return NS_ERROR_DOM_SECURITY_ERR;
     }
-
-    return NS_OK;
+    return rv;
 }
 
-// Returns the rangeOffset element from the XUL Popup Manager. We check the
-// rangeParent to determine if the caller has rights to access to the data.
+// Returns the rangeOffset element from the popupEvent. We check the rangeParent
+// to determine if the caller has rights to access to the data.
 NS_IMETHODIMP
 nsXULDocument::GetPopupRangeOffset(PRInt32* aRangeOffset)
 {
     NS_ENSURE_ARG_POINTER(aRangeOffset);
 
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (!pm)
-        return NS_ERROR_FAILURE;
-
-    PRInt32 offset;
+    nsCOMPtr<nsIDOMEvent> event;
+    nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event));
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (! event)
+        return NS_ERROR_UNEXPECTED; // no event active
+
+    nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(event, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsCOMPtr<nsIDOMNode> parent;
-    pm->GetMouseLocation(getter_AddRefs(parent), &offset);
+    rv = uiEvent->GetRangeParent(getter_AddRefs(parent));
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (parent && !nsContentUtils::CanCallerAccess(parent))
         return NS_ERROR_DOM_SECURITY_ERR;
 
-    *aRangeOffset = offset;
-    return NS_OK;
+    return uiEvent->GetRangeOffset(aRangeOffset);
 }
 
 NS_IMETHODIMP
 nsXULDocument::GetTooltipNode(nsIDOMNode** aNode)
 {
     if (mTooltipNode && !nsContentUtils::CanCallerAccess(mTooltipNode)) {
         return NS_ERROR_DOM_SECURITY_ERR;
     }
--- a/dom/public/idl/xul/nsIDOMXULDocument.idl
+++ b/dom/public/idl/xul/nsIDOMXULDocument.idl
@@ -105,9 +105,14 @@ interface nsIDOMXULDocument : nsISupport
    * callers that can indirectly be called from content.
    */
   [noscript] nsIDOMNode     trustedGetPopupNode();
 
   /**
    * Like trustedGetPopupNode, but gets the tooltip node instead.
    */
   [noscript] nsIDOMNode     trustedGetTooltipNode();
+
+  /**
+   * Like trustedGetPopupNode, but gets the 
+   */
+  [noscript] attribute nsIDOMEvent trustedPopupEvent;
 };
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -153,17 +153,16 @@
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsCSSProps.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsEventDispatcher.h"
 #include "nsIObserverService.h"
 #include "nsNetUtil.h"
-#include "nsXULPopupManager.h"
 
 #include "plbase64.h"
 
 #ifdef NS_PRINTING
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
 #endif
@@ -3031,20 +3030,20 @@ nsGlobalWindow::SetScreenY(PRInt32 aScre
   return NS_OK;
 }
 
 nsresult
 nsGlobalWindow::CheckSecurityWidthAndHeight(PRInt32* aWidth, PRInt32* aHeight)
 {
   if (!nsContentUtils::IsCallerTrustedForWrite()) {
     // if attempting to resize the window, hide any open popups
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
-    if (pm && doc)
-      pm->HidePopupsInDocument(doc);
+    nsCOMPtr<nsIPresShell> presShell;
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+    if (presShell)
+      presShell->HidePopups();
   }
 
   // This one is easy. Just ensure the variable is greater than 100;
   if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
     // Check security state for use in determing window dimensions
 
     if (!nsContentUtils::IsCallerTrustedForWrite()) {
       //sec check failed
@@ -3064,20 +3063,20 @@ nsresult
 nsGlobalWindow::CheckSecurityLeftAndTop(PRInt32* aLeft, PRInt32* aTop)
 {
   // This one is harder. We have to get the screen size and window dimensions.
 
   // Check security state for use in determing window dimensions
 
   if (!nsContentUtils::IsCallerTrustedForWrite()) {
     // if attempting to move the window, hide any open popups
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
-    if (pm && doc)
-      pm->HidePopupsInDocument(doc);
+    nsCOMPtr<nsIPresShell> presShell;
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+    if (presShell)
+      presShell->HidePopups();
 
     PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
     PRInt32 winLeft, winTop, winWidth, winHeight;
 
     nsGlobalWindow* rootWindow =
       NS_STATIC_CAST(nsGlobalWindow*, GetPrivateRoot());
     if (rootWindow) {
       rootWindow->FlushPendingNotifications(Flush_Layout);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -114,16 +114,17 @@
 #include "nsAutoPtr.h"
 #include "nsBoxFrame.h"
 #include "nsIBoxLayout.h"
 #include "nsImageFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsContentErrors.h"
 
 #include "nsIDOMWindowInternal.h"
+#include "nsIMenuFrame.h"
 
 #include "nsBox.h"
 
 #ifdef MOZ_XUL
 #include "nsIRootBox.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIXULDocument.h"
@@ -1700,18 +1701,21 @@ GetChildListNameFor(nsIFrame*       aChi
       listName = nsGkAtoms::absoluteList;
     } else if (NS_STYLE_POSITION_FIXED == disp->mPosition) {
       listName = nsGkAtoms::fixedList;
 #ifdef MOZ_XUL
     } else if (NS_STYLE_DISPLAY_POPUP == disp->mDisplay) {
       // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set
 #ifdef DEBUG
       nsIFrame* parent = aChildFrame->GetParent();
-      NS_ASSERTION(parent && parent->GetType() == nsGkAtoms::popupSetFrame,
-                   "Unexpected parent");
+      if (parent) {
+        nsIPopupSetFrame* popupSet;
+        CallQueryInterface(parent, &popupSet);
+        NS_ASSERTION(popupSet, "Unexpected parent");
+      }
 #endif // DEBUG
 
       // XXX FIXME: Bug 350740
       // Return here, because the postcondition for this function actually
       // fails for this case, since the popups are not in a "real" frame list
       // in the popup set.
       return nsGkAtoms::popupList;      
 #endif // MOZ_XUL
@@ -5949,29 +5953,31 @@ nsCSSFrameConstructor::ConstructXULFrame
       }
       else if (display->mDisplay == NS_STYLE_DISPLAY_POPUP) {
         // If a popup is inside a menu, then the menu understands the complex
         // rules/behavior governing the cascade of multiple menu popups and can handle
         // having the real popup frame placed under it as a child.  
         // If, however, the parent is *not* a menu frame, then we need to create
         // a placeholder frame for the popup, and then we add the popup frame to the
         // root popup set (that manages all such "detached" popups).
-        if (aParentFrame->GetType() != nsGkAtoms::menuFrame) {
+        nsIMenuFrame* menuFrame;
+        CallQueryInterface(aParentFrame, &menuFrame);
+        if (!menuFrame) {
           if (!aState.mPopupItems.containingBlock) {
             // Just don't create a frame for this popup; we can't do
             // anything with it, since there is no root popup set.
             *aHaltProcessing = PR_TRUE;
             NS_ASSERTION(!aState.mRootBox, "Popup containing block is missing");
             return NS_OK;
           }
 
 #ifdef NS_DEBUG
-          NS_ASSERTION(aState.mPopupItems.containingBlock->GetType() ==
-                       nsGkAtoms::popupSetFrame,
-                       "Popup containing block isn't a nsIPopupSetFrame");
+          nsIPopupSetFrame* popupSet;
+          CallQueryInterface(aState.mPopupItems.containingBlock, &popupSet);
+          NS_ASSERTION(popupSet, "Popup containing block isn't a nsIPopupSetFrame");
 #endif
           isPopup = PR_TRUE;
         }
 
         // This is its own frame that derives from box.
         newFrame = NS_NewMenuPopupFrame(mPresShell, aStyleContext);
 
         if (aTag == nsGkAtoms::tooltip) {
@@ -6136,26 +6142,16 @@ nsCSSFrameConstructor::ConstructXULFrame
     // the placeholder frame
     if (!primaryFrameSet)
         aState.mFrameManager->SetPrimaryFrameFor(aContent, topFrame);
   }
 
   return rv;
 }
 
-nsresult
-nsCSSFrameConstructor::AddLazyChildren(nsIContent* aContent,
-                                       nsLazyFrameConstructionCallback* aCallback,
-                                       void* aArg)
-{
-  nsCOMPtr<nsIRunnable> event =
-    new LazyGenerateChildrenEvent(aContent, mPresShell, aCallback, aArg);
-  return NS_DispatchToCurrentThread(event);
-}
-
 already_AddRefed<nsStyleContext>
 nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
                                                 nsIContent*              aContent,
                                                 nsStyleContext*          aContentStyle,
                                                 nsIFrame*                aParentFrame,
                                                 nsIFrame*                aContentParentFrame,
                                                 nsIAtom*                 aScrolledPseudo,
                                                 PRBool                   aIsRoot,
@@ -10135,16 +10131,39 @@ nsCSSFrameConstructor::AttributeChanged(
 
   // See if we can optimize away the style re-resolution -- must be called after
   // the frame's AttributeChanged() in case it does something that affects the style
   nsFrameManager *frameManager = shell->FrameManager();
   nsReStyleHint rshint = frameManager->HasAttributeDependentStyle(aContent,
                                                                   aAttribute,
                                                                   aModType);
 
+  // Menus and such can't deal with asynchronous changes of display
+  // when the menugenerated or menuactive attribute changes, so make
+  // sure to process that immediately
+  if (aNameSpaceID == kNameSpaceID_None &&
+      ((aAttribute == nsGkAtoms::menugenerated &&
+        aModType != nsIDOMMutationEvent::REMOVAL) ||
+       aAttribute == nsGkAtoms::menuactive)) {
+    PRInt32 namespaceID;
+    nsIAtom* tag =
+      mDocument->BindingManager()->ResolveTag(aContent, &namespaceID);
+
+    if (namespaceID == kNameSpaceID_XUL &&
+        (tag == nsGkAtoms::menupopup || tag == nsGkAtoms::popup ||
+         tag == nsGkAtoms::tooltip || tag == nsGkAtoms::menu)) {
+      nsIViewManager* viewManager = mPresShell->GetViewManager();
+      viewManager->BeginUpdateViewBatch();
+      ProcessOneRestyle(aContent, rshint, hint);
+      viewManager->EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
+
+      return result;
+    }
+  }
+
   PostRestyleEvent(aContent, rshint, hint);
 
   return result;
 }
 
 void
 nsCSSFrameConstructor::EndUpdate()
 {
@@ -13004,47 +13023,8 @@ NS_IMETHODIMP nsCSSFrameConstructor::Res
   mConstructor->mRestyleEvent.Forget();
 
   mConstructor->ProcessPendingRestyles();
   viewManager->EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCSSFrameConstructor::LazyGenerateChildrenEvent::Run()
-{
-  mPresShell->GetDocument()->FlushPendingNotifications(Flush_Layout);
-
-  // this is hard-coded to handle only menu popup frames
-  nsIFrame* frame = mPresShell->GetPrimaryFrameFor(mContent);
-  if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
-    // it is possible that the frame is different than the one that requested
-    // the lazy generation, but as long as it's a popup frame that hasn't
-    // generated its children yet, that's OK.
-    nsMenuPopupFrame* menuPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, frame);
-    if (menuPopupFrame->HasGeneratedChildren())
-      return NS_OK;
-
-    // indicate that the children have been generated
-    menuPopupFrame->SetGeneratedChildren();
-
-    nsFrameItems childItems;
-    nsFrameConstructorState state(mPresShell, nsnull, nsnull, nsnull);
-    nsCSSFrameConstructor* fc = mPresShell->FrameConstructor();
-    nsresult rv = fc->ProcessChildren(state, mContent, frame, PR_FALSE,
-                                      childItems, PR_FALSE);
-    if (NS_FAILED(rv))
-      return rv;
-
-    fc->CreateAnonymousFrames(mContent->Tag(), state, mContent, frame,
-                              PR_FALSE, childItems);
-    frame->SetInitialChildList(nsnull, childItems.childList);
-
-    if (mCallback)
-      mCallback(mContent, frame, mArg);
-
-    // call XBL constructors after the frames are created
-    mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
-  }
-
-  return NS_OK;
-}
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -67,19 +67,16 @@ class nsStyleChangeList;
 class nsIFrame;
 
 struct nsFindFrameHint
 {
   nsIFrame *mPrimaryFrameForPrevSibling;  // weak ref to the primary frame for the content for which we need a frame
   nsFindFrameHint() : mPrimaryFrameForPrevSibling(nsnull) { }
 };
 
-typedef void (PR_CALLBACK nsLazyFrameConstructionCallback)
-             (nsIContent* aContent, nsIFrame* aFrame, void* aArg);
-
 class nsFrameConstructorState;
 class nsFrameConstructorSaveState;
   
 class nsCSSFrameConstructor
 {
 public:
   nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell);
   ~nsCSSFrameConstructor(void) { }
@@ -120,27 +117,16 @@ public:
 
   nsresult CharacterDataChanged(nsIContent*     aContent,
                                 PRBool          aAppend);
 
   nsresult ContentStatesChanged(nsIContent*     aContent1,
                                 nsIContent*     aContent2,
                                 PRInt32         aStateMask);
 
-  // Process the children of aContent and indicate that frames should be
-  // created for them. This is used for lazily built content such as that
-  // inside popups so that it is only created when the popup is opened.
-  // This method constructs the frames asynchronously.
-  // aCallback will be called with three arguments, the first is the value
-  // of aContent, the second is aContent's primary frame, and the third is
-  // the value of aArg.
-  nsresult AddLazyChildren(nsIContent* aContent,
-                           nsLazyFrameConstructionCallback* aCallback,
-                           void* aArg);
-
   // Should be called when a frame is going to be destroyed and
   // WillDestroyFrameTree hasn't been called yet.
   void NotifyDestroyingFrame(nsIFrame* aFrame);
 
   nsresult AttributeChanged(nsIContent* aContent,
                             PRInt32     aNameSpaceID,
                             nsIAtom*    aAttribute,
                             PRInt32     aModType);
@@ -1012,37 +998,16 @@ public:
     void Revoke() { mConstructor = nsnull; }
   private:
     nsCSSFrameConstructor *mConstructor;
   };
 
   friend class nsFrameConstructorState;
 
 private:
-
-  class LazyGenerateChildrenEvent;
-  friend class LazyGenerateChildrenEvent;
-
-  class LazyGenerateChildrenEvent : public nsRunnable {
-  public:
-    NS_DECL_NSIRUNNABLE
-    LazyGenerateChildrenEvent(nsIContent *aContent,
-                              nsIPresShell *aPresShell,
-                              nsLazyFrameConstructionCallback* aCallback,
-                              void* aArg)
-      : mContent(aContent), mPresShell(aPresShell), mCallback(aCallback), mArg(aArg)
-    {}
-
-  private:
-    nsCOMPtr<nsIContent> mContent;
-    nsCOMPtr<nsIPresShell> mPresShell;
-    nsLazyFrameConstructionCallback* mCallback;
-    void* mArg;
-  };
-
   nsIDocument*        mDocument;  // Weak ref
   nsIPresShell*       mPresShell; // Weak ref
 
   nsIFrame*           mInitialContainingBlock;
   nsIFrame*           mFixedContainingBlock;
   nsIFrame*           mDocElementContainingBlock;
   nsIFrame*           mGfxScrollFrame;
   nsIFrame*           mPageSequenceFrame;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -106,17 +106,16 @@
 #include "nsIDOMHTMLAreaElement.h"
 #include "nsIDOMHTMLLinkElement.h"
 #include "nsIImageLoadingContent.h"
 #include "nsCopySupport.h"
 #include "nsIDOMHTMLFrameSetElement.h"
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif
-#include "nsXULPopupManager.h"
 #include "nsPrintfCString.h"
 
 #include "nsIClipboardHelper.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsJSEnvironment.h"
 #include "nsIFocusController.h"
 #include "nsIMenuParent.h"
@@ -1149,19 +1148,20 @@ DocumentViewerImpl::PageHide(PRBool aIsU
     // here.
     nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE);
 
     nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
   }
 
   // look for open menupopups and close them after the unload event, in case
   // the unload event listeners open any new popups
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mDocument)
-    pm->HidePopupsInDocument(mDocument);
+  if (mPresShell) {
+    nsCOMPtr<nsIPresShell> kungFuDeathGrip = mPresShell;
+    mPresShell->HidePopups();
+  }
 
   return NS_OK;
 }
 
 static void
 AttachContainerRecurse(nsIDocShell* aShell)
 {
   nsCOMPtr<nsIContentViewer> viewer;
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -97,20 +97,20 @@ template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
 class gfxContext;
 
 typedef short SelectionType;
 typedef PRUint32 nsFrameState;
 
-// D93B931B-D5EF-4D3C-AB99-444176963464
+// 9562bb2b-990c-4875-aafd-bd46fc9a4fc1
 #define NS_IPRESSHELL_IID \
-{ 0xd93b931b, 0xd5ef, 0x4d3c, \
-  { 0xab, 0x99, 0x44, 0x41, 0x76, 0x96, 0x34, 0x64 } }
+{ 0x9562bb2b, 0x990c, 0x4875, \
+  { 0xaa, 0xfd, 0xbd, 0x46, 0xfc, 0x9a, 0x4f, 0xc1 } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -720,16 +720,18 @@ public:
    * such that the mouse is over the same point in the scaled image as in
    * the original. When scaling does not occur, the mouse point isn't used
    * as the position can be determined from the displayed frames.
    */
   virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
                                                         nsPoint& aPoint,
                                                         nsRect* aScreenRect) = 0;
 
+  virtual void HidePopups() = 0;
+
   void AddWeakFrame(nsWeakFrame* aWeakFrame);
   void RemoveWeakFrame(nsWeakFrame* aWeakFrame);
 
 #ifdef NS_DEBUG
   nsIFrame* GetDrawEventTargetFrame() { return mDrawEventTargetFrame; }
 #endif
 
 protected:
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -184,17 +184,17 @@
 #include "nsIAccessible.h"
 #include "nsIAccessibleEvent.h"
 #endif
 
 // For style data reconstruction
 #include "nsStyleChangeList.h"
 #include "nsCSSFrameConstructor.h"
 #ifdef MOZ_XUL
-#include "nsMenuFrame.h"
+#include "nsIMenuFrame.h"
 #include "nsITreeBoxObject.h"
 #endif
 #include "nsIMenuParent.h"
 #include "nsPlaceholderFrame.h"
 
 // Content viewer interfaces
 #include "nsIContentViewer.h"
 #include "imgIEncoder.h"
@@ -882,16 +882,18 @@ public:
                                                    nsIRegion* aRegion,
                                                    nsPoint& aPoint,
                                                    nsRect* aScreenRect);
 
   virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
                                                         nsPoint& aPoint,
                                                         nsRect* aScreenRect);
 
+  virtual void HidePopups();
+
   //nsIViewObserver interface
 
   NS_IMETHOD Paint(nsIView *aView,
                    nsIRenderingContext* aRenderingContext,
                    const nsRegion& aDirtyRegion);
   NS_IMETHOD ComputeRepaintRegionForCopy(nsIView*      aRootView,
                                          nsIView*      aMovingView,
                                          nsPoint       aDelta,
@@ -5832,16 +5834,28 @@ PresShell::Thaw()
   }
 
   if (mDocument)
     mDocument->EnumerateSubDocuments(ThawSubDocument, nsnull);
 
   UnsuppressPainting();
 }
 
+void
+PresShell::HidePopups()
+{
+  nsIViewManager *vm = GetViewManager();
+  if (vm) {
+    nsIView *rootView = nsnull;
+    vm->GetRootView(rootView);
+    if (rootView)
+      HideViewIfPopup(rootView);
+  }
+}
+
 //--------------------------------------------------------
 // Start of protected and private methods on the PresShell
 //--------------------------------------------------------
 
 //-------------- Begin Reflow Event Definition ------------------------
 
 NS_IMETHODIMP
 PresShell::ReflowEvent::Run() {    
@@ -6166,18 +6180,21 @@ ReResolveMenusAndTrees(nsIFrame *aFrame,
   nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(aFrame));
   if (treeBox)
     treeBox->ClearStyleAndImageCaches();
 
   // We deliberately don't re-resolve style on a menu's popup
   // sub-content, since doing so slows menus to a crawl.  That means we
   // have to special-case them on a skin switch, and ensure that the
   // popup frames just get destroyed completely.
-  if (aFrame && aFrame->GetType() == nsGkAtoms::menuFrame)
-    (NS_STATIC_CAST(nsMenuFrame *, aFrame))->CloseMenu(PR_TRUE);
+  nsCOMPtr<nsIMenuFrame> menuFrame(do_QueryInterface(aFrame));
+  if (menuFrame) {
+    menuFrame->UngenerateMenu();  
+    menuFrame->OpenMenu(PR_FALSE);
+  }
   return PR_TRUE;
 }
 
 PR_STATIC_CALLBACK(PRBool)
 ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
 {
   nsStyleChangeList *list = NS_STATIC_CAST(nsStyleChangeList*, aClosure);
   if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
@@ -6305,16 +6322,37 @@ PresShell::EnumeratePlugins(nsIDOMDocume
     nodes->Item(i, getter_AddRefs(node));
 
     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
     if (content)
       aCallback(this, content);
   }
 }
 
+void
+PresShell::HideViewIfPopup(nsIView* aView)
+{
+  nsIFrame* frame = NS_STATIC_CAST(nsIFrame*, aView->GetClientData());
+  if (frame) {
+    nsIMenuParent* parent;
+    CallQueryInterface(frame, &parent);
+    if (parent) {
+      parent->HideChain();
+      // really make sure the view is hidden
+      mViewManager->SetViewVisibility(aView, nsViewVisibility_kHide);
+    }
+  }
+
+  nsIView* child = aView->GetFirstChild();
+  while (child) {
+    HideViewIfPopup(child);
+    child = child->GetNextSibling();
+  }
+}
+
 //------------------------------------------------------
 // End of protected and private methods on the PresShell
 //------------------------------------------------------
 
 // Start of DEBUG only code
 
 #ifdef NS_DEBUG
 #include "nsViewsCID.h"
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -231,16 +231,17 @@ class nsIDocumentLoaderFactory;
  { /* 0DE2FBFA-6B7F-11D7-BBBA-0003938A9D96 */        \
   0x0DE2FBFA, 0x6B7F, 0x11D7, {0xBB, 0xBA, 0x00, 0x03, 0x93, 0x8A, 0x9D, 0x96} }
 
 static NS_DEFINE_CID(kWindowCommandTableCID, NS_WINDOWCOMMANDTABLE_CID);
 
 #ifdef MOZ_XUL
 #include "nsIBoxObject.h"
 #include "nsIXULDocument.h"
+#include "nsIXULPopupListener.h"
 #include "nsIXULPrototypeCache.h"
 #include "nsIXULSortService.h"
 
 #ifndef MOZ_NO_INSPECTOR_APIS
 #include "inDOMView.h"
 #include "inDeepTreeWalker.h"
 #include "inFlasher.h"
 #include "inCSSValueSearch.h"
@@ -503,16 +504,17 @@ MAKE_CTOR(CreateSanitizingHTMLSerializer
 MAKE_CTOR(CreateXBLService,               nsIXBLService,               NS_NewXBLService)
 MAKE_CTOR(CreateContentPolicy,            nsIContentPolicy,            NS_NewContentPolicy)
 MAKE_CTOR(CreateComputedDOMStyle,         nsIComputedDOMStyle,         NS_NewComputedDOMStyle)
 #ifdef MOZ_XUL
 MAKE_CTOR(CreateXULSortService,           nsIXULSortService,           NS_NewXULSortService)
 // NS_NewXULContentBuilder
 // NS_NewXULTreeBuilder
 MAKE_CTOR(CreateXULDocument,              nsIXULDocument,              NS_NewXULDocument)
+MAKE_CTOR(CreateXULPopupListener,         nsIXULPopupListener,         NS_NewXULPopupListener)
 // NS_NewXULControllers
 // NS_NewXULPrototypeCache
 #endif
 #ifdef MOZ_XTF
 MAKE_CTOR(CreateXTFService,               nsIXTFService,               NS_NewXTFService)
 MAKE_CTOR(CreateXMLContentBuilder,        nsIXMLContentBuilder,        NS_NewXMLContentBuilder)
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsContentHTTPStartup)
@@ -1187,16 +1189,21 @@ static const nsModuleComponentInfo gComp
     "@mozilla.org/xul/xul-tree-builder;1",
     NS_NewXULTreeBuilder },
 
   { "XUL Document",
     NS_XULDOCUMENT_CID,
     "@mozilla.org/xul/xul-document;1",
     CreateXULDocument },
 
+  { "XUL PopupListener",
+    NS_XULPOPUPLISTENER_CID,
+    "@mozilla.org/xul/xul-popup-listener;1",
+    CreateXULPopupListener },
+
   { "XUL Prototype Cache",
     NS_XULPROTOTYPECACHE_CID,
     "@mozilla.org/xul/xul-prototype-cache;1",
     NS_NewXULPrototypeCache },
 
 #endif
 
 #ifdef MOZ_XTF
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -75,17 +75,16 @@
 #include "nsTextControlFrame.h"
 #include "nsTextTransformer.h"
 #include "nsXBLWindowKeyHandler.h"
 #include "txMozillaXSLTProcessor.h"
 #include "nsDOMStorage.h"
 #include "nsCellMap.h"
 #include "nsTextFrameTextRunCache.h"
 #include "nsCCUncollectableMarker.h"
-#include "nsXULPopupManager.h"
 
 #ifdef MOZ_XUL
 #include "nsXULContentUtils.h"
 #include "nsXULElement.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 #endif
 
@@ -219,29 +218,22 @@ nsLayoutStatics::Initialize()
   }
 
   rv = nsCCUncollectableMarker::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsCCUncollectableMarker");
     return rv;
   }
 
-  rv = nsXULPopupManager::Init();
-  if (NS_FAILED(rv)) {
-    NS_ERROR("Could not initialize nsXULPopupManager");
-    return rv;
-  }
-
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
-  nsXULPopupManager::Shutdown();
   nsDOMStorageManager::Shutdown();
   txMozillaXSLTProcessor::Shutdown();
   nsDOMAttribute::Shutdown();
   nsDOMEventRTTearoff::Shutdown();
   nsEventListenerManager::Shutdown();
   nsContentList::Shutdown();
   nsComputedDOMStyle::Shutdown();
   CSSLoaderImpl::Shutdown();
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -712,22 +712,17 @@ nsContainerFrame::PositionChildViews(nsI
       } else {
         PositionChildViews(childFrame);
       }
 
       // Get the next sibling child frame
       childFrame = childFrame->GetNextSibling();
     }
 
-    // also process the additional child lists, but skip the popup list as the
-    // view for popups is managed by the parent. Currently only nsMenuFrame
-    // has a popupList and during layout will call nsMenuPopupFrame::AdjustView.
-    do {
-      childListName = aFrame->GetAdditionalChildListName(childListIndex++);
-    } while (childListName == nsGkAtoms::popupList);
+    childListName = aFrame->GetAdditionalChildListName(childListIndex++);
   } while (childListName);
 }
 
 /**
  * The second half of frame reflow. Does the following:
  * - sets the frame's bounds
  * - sizes and positions (if requested) the frame's view. If the frame's final
  *   position differs from the current position and the frame itself does not
--- a/layout/xul/base/public/Makefile.in
+++ b/layout/xul/base/public/Makefile.in
@@ -44,18 +44,18 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE		= layout
 XPIDL_MODULE	= layout_xul
 GRE_MODULE	= 1
 
 EXPORTS		= \
 		nsPIBoxObject.h \
 		nsIMenuFrame.h \
+		nsIPopupSetFrame.h \
 		nsIScrollbarMediator.h \
-		nsXULPopupManager.h \
 		$(NULL)
 
 XPIDLSRCS=      nsIBoxObject.idl                  \
                 nsIScrollBoxObject.idl              \
                 nsIScrollBoxObject.idl \
                 nsIPopupBoxObject.idl \
                 nsIMenuBoxObject.idl \
                 nsIBrowserBoxObject.idl \
--- a/layout/xul/base/public/nsIMenuFrame.h
+++ b/layout/xul/base/public/nsIMenuFrame.h
@@ -33,33 +33,61 @@
  * 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 nsIMenuFrame_h___
 #define nsIMenuFrame_h___
 
-#include "nsISupports.h"
+// {2281EFC8-A8BA-4a73-8CF7-DB4EECA5EAEC}
+#define NS_IMENUFRAME_IID \
+{ 0x2281efc8, 0xa8ba, 0x4a73, { 0x8c, 0xf7, 0xdb, 0x4e, 0xec, 0xa5, 0xea, 0xec } }
 
-// this interface exists solely because native themes need to call into it.
-// Only menu frames should implement it
+class nsIMenuParent;
+class nsIDOMElement;
+class nsIDOMKeyEvent;
 
-// {212521C8-1509-4F41-ADDB-6A0B9356770F}
-#define NS_IMENUFRAME_IID \
-{ 0x212521C8, 0x1509, 0x4F41, { 0xAD, 0xDB, 0x6A, 0x0B, 0x93, 0x56, 0x77, 0x0F } }
+enum nsMenuType {
+  eMenuType_Normal = 0,
+  eMenuType_Checkbox = 1,
+  eMenuType_Radio = 2
+};
 
 class nsIMenuFrame : public nsISupports {
 
 public:
-
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUFRAME_IID)
 
-  virtual PRBool IsOpen() = 0;
-  virtual PRBool IsMenu() = 0;
-  virtual PRBool IsOnMenuBar() = 0;
-  virtual PRBool IsOnActiveMenuBar() = 0;
+  NS_IMETHOD ActivateMenu(PRBool aActivate) = 0;
+  NS_IMETHOD SelectMenu(PRBool aFlag) = 0;
+  NS_IMETHOD OpenMenu(PRBool aFlag) = 0;
+
+  NS_IMETHOD MenuIsOpen(PRBool& aResult) = 0;
+  NS_IMETHOD MenuIsContainer(PRBool& aResult) = 0;
+  NS_IMETHOD MenuIsChecked(PRBool& aResult) = 0;
+  NS_IMETHOD MenuIsDisabled(PRBool& aResult) = 0;
+
+  NS_IMETHOD SelectFirstItem() = 0;
+
+  NS_IMETHOD Escape(PRBool& aHandledFlag) = 0;
+  NS_IMETHOD Enter() = 0;
+  NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) = 0;
+  NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) = 0;
+
+  virtual nsIMenuParent *GetMenuParent() = 0;
+  virtual nsIFrame *GetMenuChild() = 0;
+ 
+  NS_IMETHOD GetRadioGroupName(nsString &aName) = 0;
+  NS_IMETHOD GetMenuType(nsMenuType &aType) = 0;
+
+  NS_IMETHOD MarkAsGenerated() = 0;
+
+  NS_IMETHOD UngenerateMenu() = 0;
+
+  NS_IMETHOD GetActiveChild(nsIDOMElement** aResult)=0;
+  NS_IMETHOD SetActiveChild(nsIDOMElement* aChild)=0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuFrame, NS_IMENUFRAME_IID)
 
 #endif
 
--- a/layout/xul/base/public/nsIPopupBoxObject.idl
+++ b/layout/xul/base/public/nsIPopupBoxObject.idl
@@ -36,47 +36,34 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIBoxObject.idl"
 
 interface nsIDOMElement;
 
-[scriptable, uuid(8714441F-0E24-4EB5-BE58-905F2854B4EB)]
+
+[scriptable, uuid(116ffbea-336d-4ff1-a978-7335f54d11da)]
 interface nsIPopupBoxObject : nsISupports
 {
-  /**
-   *  This method is deprecated. Use openPopup or openPopupAtScreen instead.
-   */
   void showPopup(in nsIDOMElement srcContent, in nsIDOMElement popupContent,
                  in long xpos, in long ypos,
                  in wstring popupType, in wstring anchorAlignment, 
                  in wstring popupAlignment);
+  void hidePopup();
 
-  /**
-   *  Hide the popup if it is open.
-   */
-  void hidePopup();
 
   /** 
    * Allow the popup to automatically position itself.
    */
   attribute boolean autoPosition;
 
   /**
-   * If keyboard navigation is enabled, the keyboard may be used to navigate
-   * the menuitems on the popup. Enabling keyboard navigation is the default
-   * behaviour and will install capturing key event listeners on the popup
-   * that do not propagate key events to the contents. If you wish to place
-   * elements in a popup which accept key events, such as textboxes, keyboard
-   * navigation should be disabled.
-   *
-   * Setting ignorekeys="true" on the popup element also disables keyboard
-   * navigation, and is recommended over calling this method.
+   * Allow the popup to eat all key events
    */
   void enableKeyboardNavigator(in boolean enableKeyboardNavigator);
 
   /** 
    * Enable automatic popup dismissal. This only has effect when called
    * on an open popup.
    */
   void enableRollup(in boolean enableRollup);
@@ -93,71 +80,19 @@ interface nsIPopupBoxObject : nsISupport
   void setConsumeRollupEvent(in PRUint32 consume);
 
   /** 
    * Size the popup to the given dimensions
    */
   void sizeTo(in long width, in long height);
 
   /**
-   * Move the popup to a point on screen in CSS pixels.
+   * Move the popup to a point on screen
    */
   void moveTo(in long left, in long top);
 
-  /**
-   * Open the popup relative to a specified node at a specific location.
-   *
-   * The popup may be either anchored to another node or opened freely.
-   * To anchor a popup to a node, supply an anchor node and set the position
-   * to a string indicating the manner in which the popup should be anchored.
-   * Possible values for position are:
-   *    before_start, before_end, after_start, after_end,
-   *    start_before, start_after, end_before, end_after,
-   *    overlap, after_pointer
-   *
-   * The anchor node does not need to be in the same document as the popup.
-   *
-   * If the attributesOverride argument is true, the popupanchor, popupalign
-   * and position attributes on the popup node override the position value
-   * argument. If attributesOverride is false, the attributes are only used
-   * if position is empty.
-   *
-   * For an anchored popup, the x and y arguments may be used to offset the 
-   * popup from its anchored position by some number, measured in CSS pixels.
-   *
-   * Unanchored popups may be created by supplying null as the anchor node.
-   * An unanchored popup appears at the position specified by x and y,
-   * relative to the viewport of the document containing the popup node. In
-   * this case, position and attributesOverride are ignored.
-   *
-   * @param anchorElement the node to anchor the popup to, may be null
-   * @param position manner is which to anchor the popup to node
-   * @param x horizontal offset
-   * @param y vertical offset
-   * @param isContextMenu true for context menus, false for other popups
-   * @param attributesOverride true if popup node attributes override position
-   */
-  void openPopup(in nsIDOMElement anchorElement,
-                 in AString position,
-                 in long x, in long y,
-                 in boolean isContextMenu,
-                 in boolean attributesOverride);
-
-  /**
-   * Open the popup at a specific screen position specified by x and y. This
-   * position may be adjusted if it would cause the popup to be off of the
-   * screen. The x and y coordinates are measured in CSS pixels. The monitor
-   * selected is determined within the platform specific widget code, but
-   * in general, the coordinates are relative to the screen the window
-   * containing the popup is on.
-   *
-   * @param isContextMenu true for context menus, false for other popups
-   * @param x horizontal screen position
-   * @param y vertical screen position
-   */
-  void openPopupAtScreen(in long x, in long y, in boolean isContextMenu);
 };
 
 %{C++
 nsresult
 NS_NewPopupBoxObject(nsIBoxObject** aResult);
 
 %}
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/public/nsIPopupSetFrame.h
@@ -0,0 +1,68 @@
+/* -*- 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 Communicator client 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 of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 nsIPopupSetFrame_h___
+#define nsIPopupSetFrame_h___
+
+// 043ecc8e-469f-40e1-9569-0529ac0c3039
+#define NS_IPOPUPSETFRAME_IID \
+{ 0x043ecc8e, 0x469f, 0x40e1, \
+ { 0x95, 0x69, 0x05, 0x29, 0xac, 0x0c, 0x30, 0x39 } }
+
+class nsIFrame;
+class nsIContent;
+class nsIDOMElement;
+
+#include "nsString.h"
+
+class nsIPopupSetFrame : public nsISupports {
+
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPOPUPSETFRAME_IID)
+
+  NS_IMETHOD ShowPopup(nsIContent* aElementContent, nsIContent* aPopupContent, 
+                       PRInt32 aXPos, PRInt32 aYPos, 
+                       const nsString& aPopupType, const nsString& anAnchorAlignment,
+                       const nsString& aPopupAlignment) = 0;
+  NS_IMETHOD HidePopup(nsIFrame* aPopup) = 0;
+  NS_IMETHOD DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIPopupSetFrame, NS_IPOPUPSETFRAME_IID)
+
+#endif
+
deleted file mode 100644
--- a/layout/xul/base/public/nsXULPopupManager.h
+++ /dev/null
@@ -1,636 +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 Neil Deakin
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * 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 ***** */
-
-/**
- * The XUL Popup Manager keeps track of all open popups.
- */
-
-#ifndef nsXULPopupManager_h__
-#define nsXULPopupManager_h__
-
-#include "nsIContent.h"
-#include "nsIWidget.h"
-#include "nsIRollupListener.h"
-#include "nsIMenuRollup.h"
-#include "nsIDOMKeyListener.h"
-#include "nsCOMPtr.h"
-#include "nsITimer.h"
-#include "nsThreadUtils.h"
-
-/**
- * There are two types that are used:
- *   - dismissable popups such as menus, which should close up when there is a
- *     click outside the popup. In this situation, the entire chain of menus
- *     above should also be closed.
- *   - panels, which stay open until a request is made to close them. This
- *     type is used by tooltips.
- *   XXXndeakin note that panels don't work too well currently due to widget
- *              changes needed to handle activation events properly.
- *
- * When a new popup is opened, it is appended to the popup chain, stored in a
- * linked list in mCurrentMenu for dismissable menus or mPanels for panels.
- * Popups are stored in this list linked from newest to oldest. When a click
- * occurs outside one of the open dismissable popups, the chain is closed by
- * calling Rollup.
- */
-
-class nsIPresShell;
-class nsMenuFrame;
-class nsMenuPopupFrame;
-class nsMenuBarFrame;
-class nsIMenuParent;
-class nsIDOMKeyEvent;
-
-/**
- * nsNavigationDirection: an enum expressing navigation through the menus in
- * terms which are independent of the directionality of the chrome. The
- * terminology, derived from XSL-FO and CSS3 (e.g. 
- * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start,
- * End), with the addition of First and Last (mapped to Home and End
- * respectively).
- *
- * In languages such as English where the inline progression is left-to-right
- * and the block progression is top-to-bottom (lr-tb), these terms will map out
- * as in the following diagram
- *
- *  --- inline progression --->
- *
- *           First              |
- *           ...                |
- *           Before             |
- *         +--------+         block
- *   Start |        | End  progression
- *         +--------+           |
- *           After              |
- *           ...                |
- *           Last               V
- * 
- */
-
-enum nsNavigationDirection {
-  eNavigationDirection_Last,
-  eNavigationDirection_First,
-  eNavigationDirection_Start,
-  eNavigationDirection_Before,
-  eNavigationDirection_End,
-  eNavigationDirection_After
-};
-
-#define NS_DIRECTION_IS_INLINE(dir) (dir == eNavigationDirection_Start ||     \
-                                     dir == eNavigationDirection_End)
-#define NS_DIRECTION_IS_BLOCK(dir) (dir == eNavigationDirection_Before || \
-                                    dir == eNavigationDirection_After)
-#define NS_DIRECTION_IS_BLOCK_TO_EDGE(dir) (dir == eNavigationDirection_First ||    \
-                                            dir == eNavigationDirection_Last)
-
-/**
- * DirectionFromKeyCode_lr_tb: an array that maps keycodes to values of
- * nsNavigationDirection for left-to-right and top-to-bottom flow orientation
- */
-static nsNavigationDirection DirectionFromKeyCode_lr_tb [6] = {
-  eNavigationDirection_Last,   // NS_VK_END
-  eNavigationDirection_First,  // NS_VK_HOME
-  eNavigationDirection_Start,  // NS_VK_LEFT
-  eNavigationDirection_Before, // NS_VK_UP
-  eNavigationDirection_End,    // NS_VK_RIGHT
-  eNavigationDirection_After   // NS_VK_DOWN
-};
-
-/**
- * DirectionFromKeyCode_rl_tb: an array that maps keycodes to values of
- * nsNavigationDirection for right-to-left and top-to-bottom flow orientation
- */
-static nsNavigationDirection DirectionFromKeyCode_rl_tb [6] = {
-  eNavigationDirection_Last,   // NS_VK_END
-  eNavigationDirection_First,  // NS_VK_HOME
-  eNavigationDirection_End,    // NS_VK_LEFT
-  eNavigationDirection_Before, // NS_VK_UP
-  eNavigationDirection_Start,  // NS_VK_RIGHT
-  eNavigationDirection_After   // NS_VK_DOWN
-};
-
-#define NS_DIRECTION_FROM_KEY_CODE(frame, direction, keycode)    \
-  NS_ASSERTION(NS_VK_HOME == NS_VK_END + 1, "Broken ordering");  \
-  NS_ASSERTION(NS_VK_LEFT == NS_VK_END + 2, "Broken ordering");  \
-  NS_ASSERTION(NS_VK_UP == NS_VK_END + 3, "Broken ordering");    \
-  NS_ASSERTION(NS_VK_RIGHT == NS_VK_END + 4, "Broken ordering"); \
-  NS_ASSERTION(NS_VK_DOWN == NS_VK_END + 5, "Broken ordering");  \
-  NS_ASSERTION(keycode >= NS_VK_END && keycode <= NS_VK_DOWN,    \
-               "Illegal key code");                              \
-  const nsStyleVisibility* vis = frame->GetStyleVisibility();    \
-  if (vis->mDirection == NS_STYLE_DIRECTION_RTL)                 \
-    direction = DirectionFromKeyCode_rl_tb[keycode - NS_VK_END]; \
-  else                                                           \
-    direction = DirectionFromKeyCode_lr_tb[keycode - NS_VK_END];
-
-// nsMenuChainItem holds info about an open popup. Items are stored in a
-// doubly linked list. Note that the linked list is stored beginning from
-// the lowest child in a chain of menus, as this is the active submenu.
-class nsMenuChainItem
-{
-private:
-  nsMenuPopupFrame* mFrame; // the popup frame
-  PRPackedBool mIsMenu; // true if the popup is a menu, false for a panel
-  PRPackedBool mIsContext; // true for context menus
-  PRPackedBool mOnMenuBar; // true if the menu is on a menu bar
-  PRPackedBool mIgnoreKeys; // true if keyboard listeners should not be used
-
-  nsMenuChainItem* mParent;
-  nsMenuChainItem* mChild;
-
-public:
-  nsMenuChainItem(nsMenuPopupFrame* aFrame, PRBool aIsContext, PRBool aIsMenu)
-    : mFrame(aFrame),
-      mIsMenu(aIsMenu),
-      mIsContext(aIsContext),
-      mOnMenuBar(PR_FALSE),
-      mIgnoreKeys(!aIsMenu), // always ignore keys on non-menus
-      mParent(nsnull),
-      mChild(nsnull)
-  {
-    NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor");
-    MOZ_COUNT_CTOR(nsMenuChainItem);
-  }
-
-  ~nsMenuChainItem()
-  {
-    MOZ_COUNT_DTOR(nsMenuChainItem);
-  }
-
-  nsIContent* Content();
-  nsMenuPopupFrame* Frame() { return mFrame; }
-  PRBool IsMenu() { return mIsMenu; }
-  PRBool IsContextMenu() { return mIsContext; }
-  PRBool IgnoreKeys() { return mIgnoreKeys; }
-  PRBool IsOnMenuBar() { return mOnMenuBar; }
-  void SetIgnoreKeys(PRBool aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; }
-  void SetOnMenuBar(PRBool aOnMenuBar) { mOnMenuBar = aOnMenuBar; }
-  nsMenuChainItem* GetParent() { return mParent; }
-  nsMenuChainItem* GetChild() { return mChild; }
-
-  // set the parent of this item to aParent, also changing the parent
-  // to have this as a child.
-  void SetParent(nsMenuChainItem* aParent);
-
-  // removes an item from the chain. The root pointer must be supplied in case
-  // the item is the first item in the chain in which case the pointer will be
-  // set to the next item, or null if there isn't another item. After detaching,
-  // this item will not have a parent or a child.
-  void Detach(nsMenuChainItem** aRoot);
-};
-
-// this class is used for dispatching popupshowing events asynchronously.
-class nsXULPopupShowingEvent : public nsRunnable
-{
-public:
-  nsXULPopupShowingEvent(nsIContent *aPopup,
-                         nsIContent *aMenu,
-                         PRBool aIsContextMenu,
-                         PRBool aSelectFirstItem)
-    : mPopup(aPopup),
-      mMenu(aMenu),
-      mIsContextMenu(aIsContextMenu),
-      mSelectFirstItem(aSelectFirstItem)
-  {
-    NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor");
-    NS_ASSERTION(aMenu, "null menu supplied to nsXULPopupShowingEvent constructor");
-  }
-
-  NS_IMETHOD Run();
-
-private:
-  nsCOMPtr<nsIContent> mPopup;
-  nsCOMPtr<nsIContent> mMenu;
-  PRBool mIsContextMenu;
-  PRBool mSelectFirstItem;
-};
-
-// this class is used for dispatching popuphiding events asynchronously.
-class nsXULPopupHidingEvent : public nsRunnable
-{
-public:
-  nsXULPopupHidingEvent(nsIContent *aPopup,
-                        nsIContent* aNextPopup,
-                        nsIContent* aLastPopup,
-                        PRBool aIsMenu,
-                        PRBool aDeselectMenu)
-    : mPopup(aPopup),
-      mNextPopup(aNextPopup),
-      mLastPopup(aLastPopup),
-      mIsMenu(aIsMenu),
-      mDeselectMenu(aDeselectMenu)
-  {
-    NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupHidingEvent constructor");
-    // aNextPopup and aLastPopup may be null
-  }
-
-  NS_IMETHOD Run();
-
-private:
-  nsCOMPtr<nsIContent> mPopup;
-  nsCOMPtr<nsIContent> mNextPopup;
-  nsCOMPtr<nsIContent> mLastPopup;
-  PRBool mIsMenu;
-  PRBool mDeselectMenu;
-};
-
-// this class is used for dispatching menu command events asynchronously.
-class nsXULMenuCommandEvent : public nsRunnable
-{
-public:
-  nsXULMenuCommandEvent(nsIContent *aMenu,
-                        PRBool aIsTrusted,
-                        PRBool aShift,
-                        PRBool aControl,
-                        PRBool aAlt,
-                        PRBool aMeta)
-    : mMenu(aMenu),
-      mIsTrusted(aIsTrusted),
-      mShift(aShift),
-      mControl(aControl),
-      mAlt(aAlt),
-      mMeta(aMeta)
-  {
-    NS_ASSERTION(aMenu, "null menu supplied to nsXULMenuCommandEvent constructor");
-  }
-
-  NS_IMETHOD Run();
-
-private:
-  nsCOMPtr<nsIContent> mMenu;
-  PRBool mIsTrusted;
-  PRBool mShift;
-  PRBool mControl;
-  PRBool mAlt;
-  PRBool mMeta;
-};
-
-class nsXULPopupManager : public nsIDOMKeyListener,
-                          public nsIMenuRollup,
-                          public nsIRollupListener,
-                          public nsITimerCallback
-{
-
-public:
-  friend class nsXULPopupShowingEvent;
-  friend class nsXULPopupHidingEvent;
-  friend class nsXULMenuCommandEvent;
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIROLLUPLISTENER
-  NS_DECL_NSIMENUROLLUP
-  NS_DECL_NSITIMERCALLBACK
-
-  static nsXULPopupManager* sInstance;
-
-  // initialize and shutdown methods called by nsLayoutStatics
-  static nsresult Init();
-  static void Shutdown();
-
-  // returns a weak reference to the popup manager instance, could return null
-  // if a popup manager could not be allocated
-  static nsXULPopupManager* GetInstance();
-
-  // given a menu frame, find the prevous or next menu frame. If aPopup is
-  // true then navigate a menupopup, from one item on the menu to the previous
-  // or next one. This is used for cursor navigation between items in a popup
-  // menu. If aIsPopup is false, the navigation is on a menubar, so navigate
-  // between menus on the menubar. This is used for left/right cursor navigation.
-  //
-  // Items that not valid, such as non-menu or menuitem elements are skipped,
-  // and the next or previous item after that is checked.
-  //
-  // If aStart is null, the first valid item is retrieved for GetNextMenuItem
-  // or the last valid item for GetPreviousMenuItem is used.
-  //
-  // aParent - the parent menubar or menupopup
-  // aStart - the menu/menuitem to start navigation from. GetPreviousMenuItem
-  //          returns the item before it, while GetNextMenuItem returns the
-  //          next item.
-  // aIsPopup - true for menupopups, false for menubars
-  static nsMenuFrame* GetPreviousMenuItem(nsIFrame* aParent,
-                                          nsMenuFrame* aStart,
-                                          PRBool aIsPopup);
-  static nsMenuFrame* GetNextMenuItem(nsIFrame* aParent,
-                                      nsMenuFrame* aStart,
-                                      PRBool aIsPopup);
-
-  // returns true if the menu item aContent is a valid menuitem which may
-  // be navigated to. aIsPopup should be true for items on a popup, or false
-  // for items on a menubar.
-  static PRBool IsValidMenuItem(nsPresContext* aPresContext,
-                                nsIContent* aContent,
-                                PRBool aOnPopup);
-
-  // inform the popup manager that a menu bar has been activated or deactivated,
-  // either because one of its menus has opened or closed, or that the menubar
-  // has been focused such that its menus may be navigated with the keyboard.
-  // aActivate should be true when the menubar should be focused, and false
-  // when the active menu bar should be defocused. In the latter case, if
-  // aMenuBar isn't currently active, yet another menu bar is, that menu bar
-  // will remain active.
-  void SetActiveMenuBar(nsMenuBarFrame* aMenuBar, PRBool aActivate);
-
-  // retrieve the node and offset of the last mouse event used to open a
-  // context menu. This information is determined from the rangeParent and
-  // the rangeOffset of the event supplied from the last call to SetMouseLocation.
-  // This is used by the implementation of nsIDOMXULDocument::GetPopupRangeParent
-  // and nsIDOMXULDocument::GetPopupRangeOffset.
-  void GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset);
-  // set the mouse event that was used to activate the next popup to be opened.
-  void SetMouseLocation(nsIDOMEvent* aEvent);
-
-  /**
-   * Open a <menu> given its content node. If aSelectFirstItem is
-   * set to true, the first item on the menu will automatically be
-   * selected. If aAsynchronous is true, the event will be dispatched
-   * asynchronously. This should be true when called from frame code.
-   */
-  void ShowMenu(nsIContent *aMenu, PRBool aSelectFirstItem, PRBool aAsynchronous);
-
-  /**
-   * Open a popup, either anchored or unanchored. If aSelectFirstItem is
-   * true, then the first item in the menu is selected. The arguments are
-   * similar to those for nsIPopupBoxObject::OpenPopup.
-   *
-   * This fires the popupshowing event synchronously.
-   */
-  void ShowPopup(nsIContent* aPopup,
-                 nsIContent* aAnchorContent,
-                 const nsAString& aPosition,
-                 PRInt32 aXPos, PRInt32 aYPos,
-                 PRBool aIsContextMenu,
-                 PRBool aAttributesOverride,
-                 PRBool aSelectFirstItem);
-
-  /**
-   * Open a popup at a specific screen position specified by aXPos and aYPos,
-   * measured in CSS pixels.
-   *
-   * This fires the popupshowing event synchronously.
-   */
-  void ShowPopupAtScreen(nsIContent* aPopup,
-                         PRInt32 aXPos, PRInt32 aYPos,
-                         PRBool aIsContextMenu);
-
-  /**
-   * This method is provided only for compatibility with an older popup API.
-   * New code should not call this function and should call ShowPopup instead.
-   *
-   * This fires the popupshowing event synchronously.
-   */
-  void ShowPopupWithAnchorAlign(nsIContent* aPopup,
-                                nsIContent* aAnchorContent,
-                                nsAString& aAnchor,
-                                nsAString& aAlign,
-                                PRInt32 aXPos, PRInt32 aYPos,
-                                PRBool aIsContextMenu);
-
-  /*
-   * Hide a popup aPopup. If the popup is in a <menu>, then also inform the
-   * menu that the popup is being hidden.
-   *
-   * aHideChain - true if the entire chain of menus should be closed. If false,
-   *              only this popup is closed.
-   * aDeselectMenu - true if the parent <menu> of the popup should be deselected.
-   *                 This will be false when the menu is closed by pressing the
-   *                 Escape key.
-   * aAsynchronous - true if the first popuphiding event should be sent
-   *                 asynchrously. This should be true if HidePopup is called
-   *                 from a frame.
-   */
-  void HidePopup(nsIContent* aPopup,
-                 PRBool aHideChain,
-                 PRBool aDeselectMenu,
-                 PRBool aAsynchronous);
-
-  /**
-   * Hide a popup after a short delay. This is used when rolling over menu items.
-   * This timer is stored in mCloseTimer. The timer may be cancelled and the popup
-   * closed by calling KillMenuTimer.
-   */
-  void HidePopupAfterDelay(nsMenuPopupFrame* aPopup);
-
-  /**
-   * Hide all of the popups from a given document. This should be called when the
-   * document is hidden.
-   */
-  void HidePopupsInDocument(nsIDocument* aDocument);
-
-  /**
-   * Execute a menu command from the triggering event aEvent.
-   *
-   * aMenu - a menuitem to execute
-   * aEvent - the mouse event which triggered the menu to be executed,
-   *          may be null
-   */
-  void ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent);
-
-  /**
-   * Return true if the popup for the supplied menu parent is open.
-   */
-  PRBool IsPopupOpenForMenuParent(nsIMenuParent* aMenuParent);
-
-  /**
-   * Return false if a popup may not be opened. This will return false if the
-   * popup is already open, if the popup is in a content shell that is not
-   * focused, or if it is a submenu of another menu that isn't open.
-   */
-  PRBool MayShowPopup(nsMenuPopupFrame* aFrame);
-
-  /**
-   * Called when a popup frame is destroyed. In this case, just remove the
-   * item and later popups from the list. No point going through HidePopup as
-   * the frames have gone away.
-   */
-  void PopupDestroyed(nsMenuPopupFrame* aFrame);
-
-  /**
-   * Returns true if there is a context menu open. If aPopup is specified,
-   * then the context menu must be later in the chain than aPopup. If aPopup
-   * is null, returns true if any context menu at all is open.
-   */
-  PRBool HasContextMenu(nsMenuPopupFrame* aPopup);
-
-  /**
-   * Update the commands for the menus within the menu popup for a given
-   * content node. aPopup should be a XUL menupopup element. This method
-   * changes attributes on the children of aPopup, and deals only with the
-   * content of the popup, not the frames.
-   */
-  void UpdateMenuItems(nsIContent* aPopup);
-
-  /**
-   * Stop the timer which hides a popup after a delay, started by a previous
-   * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden
-   * is closed asynchronously.
-   */
-  void KillMenuTimer();
-
-  /**
-   * Handles navigation for menu accelkeys. Returns true if the key has
-   * been handled.
-   */
-  PRBool HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent);
-
-  /**
-   * Handles cursor navigation within a menu. Returns true if the key has
-   * been handled.
-   */
-  PRBool HandleKeyboardNavigation(PRUint32 aKeyCode);
-
-  NS_IMETHODIMP HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
-
-  NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent);
-  NS_IMETHOD KeyDown(nsIDOMEvent* aKeyEvent);
-  NS_IMETHOD KeyPress(nsIDOMEvent* aKeyEvent);
-
-protected:
-  nsXULPopupManager();
-  ~nsXULPopupManager();
-
-  // get the frame for a content node aContent if the frame's type
-  // matches aFrameType. Otherwise, return null.
-  nsIFrame* GetFrameOfTypeForContent(nsIContent* aContent, nsIAtom* aFrameType);
-
-  // get the nsMenuFrame, if any, for the given content node
-  nsMenuFrame* GetMenuFrameForContent(nsIContent* aContent);
-
-  // get the nsMenuPopupFrame, if any, for the given content node
-  nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent);
-
-  // callbacks for ShowPopup and HidePopup as events may be done asynchronously
-  void ShowPopupCallback(nsIContent* aPopup,
-                         nsMenuPopupFrame* aPopupFrame,
-                         PRBool aIsContextMenu,
-                         PRBool aSelectFirstItem);
-  void HidePopupCallback(nsIContent* aPopup,
-                         nsMenuPopupFrame* aPopupFrame,
-                         nsIContent* aNextPopup,
-                         nsIContent* aLastPopup,
-                         PRBool aIsMenu,
-                         PRBool aDeselectMenu);
-
-  /**
-   * Fire a popupshowing event on the popup aPopup and then open the popup.
-   *
-   * aPopup - the popup node to open
-   * aMenu - should be set to the parent menu if this is a popup associated
-   *         with a menu. Otherwise, should be null.
-   * aPresContext - the prescontext 
-   * aIsContextMenu - true for context menus
-   * aSelectFirstItem - true to select the first item in the menu
-   */
-  void FirePopupShowingEvent(nsIContent* aPopup,
-                             nsIContent* aMenu,
-                             nsPresContext* aPresContext,
-                             PRBool aIsContextMenu,
-                             PRBool aSelectFirstItem);
-
-  /**
-   * Fire a popuphiding event and then hide the popup. This will be called
-   * recursively if aNextPopup and aLastPopup are set in order to hide a chain
-   * of open menus. If these are not set, only one popup is closed. However,
-   * if aIsMenu is true, yet the next popup is not a menu, then this ends the
-   * closing of popups. This allows a menulist inside a non-menu to close up
-   * the menu but not close up the panel it is contained within.
-   *
-   * aPopup - the popup to hide
-   * aNextPopup - the next popup to hide
-   * aLastPopup - the last popup in the chain to hide
-   * aPresContext - nsPresContext for the popup's frame
-   * aIsMenu - true if aPopup is a menu. 
-   * aDeselectMenu - true to unhighlight the menu when hiding it
-   */
-  void FirePopupHidingEvent(nsIContent* aPopup,
-                            nsIContent* aNextPopup,
-                            nsIContent* aLastPopup,
-                            nsPresContext *aPresContext,
-                            PRBool aIsMenu,
-                            PRBool aDeselectMenu);
-
-  // handle keyboard navigation within a menu popup. Returns true if the
-  // key was handled and that other default handling should not occur.
-  PRBool HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
-                                         nsNavigationDirection aDir);
-
-  /**
-   * Set mouse capturing for the current popup. This traps mouse clicks that
-   * occur outside the popup so that it can be closed up. aOldPopup should be
-   * set to the popup that was previously the current popup.
-   */
-  void SetCaptureState(nsIContent *aOldPopup);
-
-  /**
-   * Key event listeners are attached to the document containing the current
-   * menu for menu and shortcut navigation. Only one listener is needed at a
-   * time, stored in mKeyListener, so switch it only if the document changes.
-   * Having menus in different documents is very rare, so the listeners will
-   * usually only be attached when the first menu opens and removed when all
-   * menus have closed.
-   *
-   * This is also used when only a menubar is active without any open menus,
-   * so that keyboard navigation between menus on the menubar may be done.
-   */
-  void UpdateKeyboardListeners();
-
-  // the document the key event listener is attached to
-  nsCOMPtr<nsIDOMEventTarget> mKeyListener;
-
-  // widget that is currently listening to rollup events
-  nsCOMPtr<nsIWidget> mWidget;
-
-  // range parent and offset set in SetMouseLocation
-  nsCOMPtr<nsIDOMNode> mRangeParent;
-  PRInt32 mRangeOffset;
-
-  // set to the currently active menu bar, if any
-  nsMenuBarFrame* mActiveMenuBar;
-
-  // linked list of dismissable menus.
-  nsMenuChainItem* mCurrentMenu;
-
-  // linked list of panels
-  nsMenuChainItem* mPanels;
-
-  // timer used for HidePopupAfterDelay
-  nsCOMPtr<nsITimer> mCloseTimer;
-
-  // a popup that is waiting on the timer
-  nsMenuPopupFrame* mTimerMenu;
-};
-
-#endif
--- a/layout/xul/base/src/Makefile.in
+++ b/layout/xul/base/src/Makefile.in
@@ -114,23 +114,24 @@ CPPSRCS		+= \
 		nsFrameNavigator.cpp \
 		nsSplitterFrame.cpp \
 		nsDeckFrame.cpp \
 		nsProgressMeterFrame.cpp \
 		nsMenuPopupFrame.cpp \
 		nsMenuFrame.cpp \
 		nsMenuBarFrame.cpp \
 		nsMenuBarListener.cpp \
+		nsMenuListener.cpp \
+		nsMenuDismissalListener.cpp \
 		nsPopupSetFrame.cpp \
 		nsTitleBarFrame.cpp \
 		nsResizerFrame.cpp \
 		nsListBoxBodyFrame.cpp \
 		nsListItemFrame.cpp \
 		nsListBoxLayout.cpp \
-		nsXULPopupManager.cpp \
 		$(NULL)
 
 endif
 
 include $(topsrcdir)/config/config.mk
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
--- a/layout/xul/base/src/nsMenuBarFrame.h
+++ b/layout/xul/base/src/nsMenuBarFrame.h
@@ -44,73 +44,96 @@
 
 #ifndef nsMenuBarFrame_h__
 #define nsMenuBarFrame_h__
 
 #include "prtypes.h"
 #include "nsIAtom.h"
 #include "nsCOMPtr.h"
 #include "nsBoxFrame.h"
-#include "nsMenuFrame.h"
 #include "nsMenuBarListener.h"
+#include "nsMenuListener.h"
 #include "nsIMenuParent.h"
 #include "nsIWidget.h"
 
 class nsIContent;
+class nsIMenuFrame;
 
 nsIFrame* NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 class nsMenuBarFrame : public nsBoxFrame, public nsIMenuParent
 {
 public:
   nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext);
+  virtual ~nsMenuBarFrame();
+
+  NS_DECL_ISUPPORTS
 
   // nsIMenuParentInterface
-  virtual nsMenuFrame* GetCurrentMenuItem();
-  NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem);
-  virtual void CurrentMenuIsBeingDestroyed();
-  NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, PRBool aSelectFirstItem);
+  virtual nsIMenuFrame* GetCurrentMenuItem();
+  NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem);
+  virtual nsIMenuFrame* GetNextMenuItem(nsIMenuFrame* aStart);
+  virtual nsIMenuFrame* GetPreviousMenuItem(nsIMenuFrame* aStart);
+  NS_IMETHOD SetActive(PRBool aActiveFlag); 
+  NS_IMETHOD GetIsActive(PRBool& isActive) { isActive = IsActive(); return NS_OK; }
+  NS_IMETHOD IsMenuBar(PRBool& isMenuBar) { isMenuBar = PR_TRUE; return NS_OK; }
+  NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks) \
+    {aConsumeOutsideClicks = PR_FALSE; return NS_OK;}
+  NS_IMETHOD ClearRecentlyRolledUp();
+  NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp);
 
-  NS_IMETHOD SetActive(PRBool aActiveFlag); 
+  NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) { return NS_OK; }
+  NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) { aIsContextMenu = PR_FALSE; return NS_OK; }
+
+  NS_IMETHOD GetParentPopup(nsIMenuParent** aResult) { *aResult = nsnull;
+                                                       return NS_OK;}
 
-  virtual PRBool IsMenuBar() { return PR_TRUE; }
-  virtual PRBool IsContextMenu() { return PR_FALSE; }
-  virtual PRBool IsActive() { return mIsActive; }
-  virtual PRBool IsMenu() { return PR_FALSE; }
-  virtual PRBool IsOpen() { return PR_TRUE; } // menubars are considered always open
+  NS_IMETHOD IsActive() { return mIsActive; }
+
+  NS_IMETHOD IsOpen();
+  NS_IMETHOD KillPendingTimers();
+  NS_IMETHOD CancelPendingTimers() { return NS_OK; }
+
+  // Closes up the chain of open cascaded menus.
+  NS_IMETHOD DismissChain();
 
-  PRBool IsMenuOpen() { return mCurrentMenu && mCurrentMenu->IsOpen(); }
+  // Hides the chain of cascaded menus without closing them up.
+  NS_IMETHOD HideChain();
 
-  void InstallKeyboardNavigator();
-  void RemoveKeyboardNavigator();
+  NS_IMETHOD InstallKeyboardNavigator();
+  NS_IMETHOD RemoveKeyboardNavigator();
+
+  NS_IMETHOD GetWidget(nsIWidget **aWidget);
+  // The dismissal listener gets created and attached to the window.
+  NS_IMETHOD AttachedDismissalListener() { return NS_OK; }
 
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
   virtual void Destroy();
 
-  virtual nsIAtom* GetType() const { return nsGkAtoms::menuBarFrame; }
-
 // Non-interface helpers
 
-  // Called when a menu on the menu bar is clicked on. Returns a menu if one
-  // needs to be closed.
-  nsMenuFrame* ToggleMenuActiveState();
-
-  // indicate that a menu on the menubar was closed. Returns true if the caller
-  // may deselect the menuitem.
-  virtual PRBool MenuClosed();
-
-  // Called when Enter is pressed while the menubar is focused. If the current
-  // menu is open, let the child handle the key.
-  nsMenuFrame* Enter();
+  // Called when a menu on the menu bar is clicked on.
+  void ToggleMenuActiveState();
+  
+  // Used to move up, down, left, and right in menus.
+  NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag);
+  NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag);
+  // Called when the ESC key is held down to close levels of menus.
+  NS_IMETHOD Escape(PRBool& aHandledFlag);
+  // Called to execute a menu item.
+  NS_IMETHOD Enter();
 
   // Used to handle ALT+key combos
-  nsMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent);
+  nsIMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent);
+
+  PRBool IsValidItem(nsIContent* aContent);
+  PRBool IsDisabled(nsIContent* aContent);
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
     // Override bogus IsFrameOfType in nsBoxFrame.
     if (aFlags & (nsIFrame::eReplacedContainsBlock | nsIFrame::eReplaced))
       return PR_FALSE;
     return nsBoxFrame::IsFrameOfType(aFlags);
   }
@@ -119,21 +142,24 @@ public:
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
       return MakeFrameName(NS_LITERAL_STRING("MenuBar"), aResult);
   }
 #endif
 
 protected:
   nsMenuBarListener* mMenuBarListener; // The listener that tells us about key and mouse events.
+  nsMenuListener* mKeyboardNavigator;
 
   PRBool mIsActive; // Whether or not the menu bar is active (a menu item is highlighted or shown).
-  // The current menu that is active (highlighted), which may not be open. This will
-  // be null if no menu is active.
-  nsMenuFrame* mCurrentMenu;
+  nsIMenuFrame* mCurrentMenu; // The current menu that is active.
+
+  // Can contain a menu that was rolled up via nsIMenuDismissalListener::Rollup()
+  // if nothing has happened since the last click. Otherwise, contains nsnull.
+  nsIMenuFrame* mRecentRollupMenu; 
 
   nsIDOMEventTarget* mTarget;
 
 private:
   PRBool mCaretWasVisible;
 
 }; // class nsMenuBarFrame
 
--- a/layout/xul/base/src/nsMenuBarListener.cpp
+++ b/layout/xul/base/src/nsMenuBarListener.cpp
@@ -35,17 +35,16 @@
  * 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 "nsMenuBarListener.h"
 #include "nsMenuBarFrame.h"
-#include "nsMenuPopupFrame.h"
 #include "nsIDOMKeyListener.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMNSUIEvent.h"
 #include "nsIDOMNSEvent.h"
 #include "nsGUIEvent.h"
 
 // Drag & Drop, Clipboard
@@ -127,28 +126,16 @@ void nsMenuBarListener::InitAccessKey()
     mAccessKeyMask = MODIFIER_ALT;
   else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_META)
     mAccessKeyMask = MODIFIER_META;
 
   mAccessKeyFocuses =
     nsContentUtils::GetBoolPref("ui.key.menuAccessKeyFocuses");
 }
 
-void
-nsMenuBarListener::ToggleMenuActiveState()
-{
-  nsMenuFrame* closemenu = mMenuBarFrame->ToggleMenuActiveState();
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && closemenu) {
-    nsMenuPopupFrame* popupFrame = closemenu->GetPopup();
-    if (popupFrame)
-      pm->HidePopup(popupFrame->GetContent(), PR_FALSE, PR_FALSE, PR_TRUE);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////
 nsresult
 nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent)
 {  
   InitAccessKey();
 
   //handlers shouldn't be triggered by non-trusted events.
   nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
@@ -169,35 +156,37 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aK
     nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
     PRUint32 theChar;
     keyEvent->GetKeyCode(&theChar);
 
     if (mAccessKeyDown && (PRInt32)theChar == mAccessKey)
     {
       // The access key was down and is now up, and no other
       // keys were pressed in between.
-      ToggleMenuActiveState();
+      mMenuBarFrame->ToggleMenuActiveState();
     }
     mAccessKeyDown = PR_FALSE; 
 
     PRBool active = mMenuBarFrame->IsActive();
     if (active) {
       aKeyEvent->StopPropagation();
       aKeyEvent->PreventDefault();
-      return NS_OK; // I am consuming event
+      return NS_ERROR_BASE; // I am consuming event
     }
   }
   
   return NS_OK; // means I am NOT consuming event
 }
 
 ////////////////////////////////////////////////////////////////////////
 nsresult
 nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent)
 {
+  mMenuBarFrame->ClearRecentlyRolledUp();
+
   // if event has already been handled, bail
   nsCOMPtr<nsIDOMNSUIEvent> uiEvent ( do_QueryInterface(aKeyEvent) );
   if ( uiEvent ) {
     PRBool eventHandled = PR_FALSE;
     uiEvent->GetPreventDefault ( &eventHandled );
     if ( eventHandled )
       return NS_OK;       // don't consume event
   }
@@ -235,42 +224,42 @@ nsMenuBarListener::KeyPress(nsIDOMEvent*
 
       // If charCode == 0, then it is not a printable character.
       // Don't attempt to handle accesskey for non-printable characters.
       if (IsAccessKeyPressed(keyEvent) && charCode)
       {
         // Do shortcut navigation.
         // A letter was pressed. We want to see if a shortcut gets matched. If
         // so, we'll know the menu got activated.
-        nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent);
-        if (result) {
-          mMenuBarFrame->SetActive(PR_TRUE);
-          result->OpenMenu(PR_TRUE);
+        PRBool active = PR_FALSE;
+        mMenuBarFrame->ShortcutNavigation(keyEvent, active);
+
+        if (active) {
           aKeyEvent->StopPropagation();
           aKeyEvent->PreventDefault();
-          retVal = NS_OK;       // I am consuming event
+
+          retVal = NS_ERROR_BASE;       // I am consuming event
         }
       }    
 #if !defined(XP_MAC) && !defined(XP_MACOSX)
       // Also need to handle F10 specially on Non-Mac platform.
       else if (keyCode == NS_VK_F10) {
         if ((GetModifiers(keyEvent) & ~MODIFIER_CONTROL) == 0) {
           // The F10 key just went down by itself or with ctrl pressed.
           // In Windows, both of these activate the menu bar.
-          ToggleMenuActiveState();
+          mMenuBarFrame->ToggleMenuActiveState();
 
           aKeyEvent->StopPropagation();
           aKeyEvent->PreventDefault();
-          return NS_OK; // consume the event
+          return NS_ERROR_BASE; // consume the event
         }
       }
 #endif   // !XP_MAC && !XP_MACOSX
     } 
   }
-
   return retVal;
 }
 
 PRBool
 nsMenuBarListener::IsAccessKeyPressed(nsIDOMKeyEvent* aKeyEvent)
 {
   InitAccessKey();
   // No other modifiers are allowed to be down except for Shift.
@@ -355,38 +344,46 @@ nsMenuBarListener::Focus(nsIDOMEvent* aE
 {
   return NS_OK; // means I am NOT consuming event
 }
 
 ////////////////////////////////////////////////////////////////////////
 nsresult
 nsMenuBarListener::Blur(nsIDOMEvent* aEvent)
 {
-  if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive()) {
-    ToggleMenuActiveState();
+  if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) {
+    mMenuBarFrame->ToggleMenuActiveState();
+    PRBool handled;
+    mMenuBarFrame->Escape(handled);
     mAccessKeyDown = PR_FALSE;
   }
   return NS_OK; // means I am NOT consuming event
 }
   
 ////////////////////////////////////////////////////////////////////////
 nsresult 
 nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent)
 {
-  if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive())
-    ToggleMenuActiveState();
+  if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) {
+    mMenuBarFrame->ToggleMenuActiveState();
+    PRBool handled;
+    mMenuBarFrame->Escape(handled);
+  }
+
   mAccessKeyDown = PR_FALSE;
 
   return NS_OK; // means I am NOT consuming event
 }
 
 ////////////////////////////////////////////////////////////////////////
 nsresult 
 nsMenuBarListener::MouseUp(nsIDOMEvent* aMouseEvent)
 {
+  mMenuBarFrame->ClearRecentlyRolledUp();
+
   return NS_OK; // means I am NOT consuming event
 }
 
 nsresult 
 nsMenuBarListener::MouseClick(nsIDOMEvent* aMouseEvent)
 {
   return NS_OK; // means I am NOT consuming event
 }
--- a/layout/xul/base/src/nsMenuBarListener.h
+++ b/layout/xul/base/src/nsMenuBarListener.h
@@ -82,20 +82,16 @@ public:
 
   static PRBool IsAccessKeyPressed(nsIDOMKeyEvent* event);
 
 protected:
   static void InitAccessKey();
 
   static PRUint32 GetModifiers(nsIDOMKeyEvent* event);
 
-  // This should only be called by the nsMenuBarListener during event dispatch,
-  // thus ensuring that this doesn't get destroyed during the process.
-  void ToggleMenuActiveState();
-
   nsMenuBarFrame* mMenuBarFrame; // The menu bar object.
   PRBool mAccessKeyDown;         // Whether or not the ALT key is currently down.
   static PRBool mAccessKeyFocuses; // Does the access key by itself focus the menubar?
   static PRInt32 mAccessKey;     // See nsIDOMKeyEvent.h for sample values
   static PRUint32 mAccessKeyMask;// Modifier mask for the access key
 };
 
 
--- a/layout/xul/base/src/nsMenuBoxObject.cpp
+++ b/layout/xul/base/src/nsMenuBoxObject.cpp
@@ -34,22 +34,21 @@
  * 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 "nsISupportsUtils.h"
 #include "nsIMenuBoxObject.h"
 #include "nsBoxObject.h"
 #include "nsIPresShell.h"
+#include "nsIMenuFrame.h"
 #include "nsIFrame.h"
 #include "nsGUIEvent.h"
 #include "nsIDOMNSUIEvent.h"
 #include "nsMenuBarListener.h"
-#include "nsMenuFrame.h"
-#include "nsMenuPopupFrame.h"
 #include "nsPopupSetFrame.h"
 
 class nsMenuBoxObject : public nsIMenuBoxObject, public nsBoxObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMENUBOXOBJECT
 
@@ -85,97 +84,98 @@ nsMenuBoxObject::nsMenuBoxObject()
 nsMenuBoxObject::~nsMenuBoxObject()
 {
   /* destructor code */
 }
 
 /* void openMenu (in boolean openFlag); */
 NS_IMETHODIMP nsMenuBoxObject::OpenMenu(PRBool aOpenFlag)
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm) {
-    nsIFrame* frame = GetFrame(PR_FALSE);
-    if (frame) {
-      if (aOpenFlag) {
-        nsCOMPtr<nsIContent> content = mContent;
-        pm->ShowMenu(content, PR_FALSE, PR_FALSE);
-      }
-      else {
-        if (frame->GetType() == nsGkAtoms::menuFrame) {
-          nsMenuPopupFrame* popupFrame = (NS_STATIC_CAST(nsMenuFrame *, frame))->GetPopup();
-          if (popupFrame)
-            pm->HidePopup(popupFrame->GetContent(), PR_FALSE, PR_TRUE, PR_FALSE);
-        }
-      }
-    }
-  }
+  nsIFrame* frame = GetFrame(PR_FALSE);
+  if (!frame)
+    return NS_OK;
+
+  if (!nsPopupSetFrame::MayOpenPopup(frame))
+    return NS_OK;
 
-  return NS_OK;
+  nsIMenuFrame* menuFrame;
+  CallQueryInterface(frame, &menuFrame);
+  if (!menuFrame)
+    return NS_OK;
+
+  return menuFrame->OpenMenu(aOpenFlag);
 }
 
 NS_IMETHODIMP nsMenuBoxObject::GetActiveChild(nsIDOMElement** aResult)
 {
   *aResult = nsnull;
   nsIFrame* frame = GetFrame(PR_FALSE);
-  if (frame && frame->GetType() == nsGkAtoms::menuFrame)
-    return NS_STATIC_CAST(nsMenuFrame *, frame)->GetActiveChild(aResult);
+  if (!frame)
+    return NS_OK;
+
+  nsIMenuFrame* menuFrame;
+  CallQueryInterface(frame, &menuFrame);
+  if (menuFrame)
+    menuFrame->GetActiveChild(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMenuBoxObject::SetActiveChild(nsIDOMElement* aResult)
 {
   nsIFrame* frame = GetFrame(PR_FALSE);
-  if (frame && frame->GetType() == nsGkAtoms::menuFrame)
-    return NS_STATIC_CAST(nsMenuFrame *, frame)->SetActiveChild(aResult);
+  if (!frame)
+    return NS_OK;
+
+  nsIMenuFrame* menuFrame;
+  CallQueryInterface(frame, &menuFrame);
+  if (menuFrame) {
+    menuFrame->MarkAsGenerated();
+    menuFrame->SetActiveChild(aResult);
+  }
   return NS_OK;
 }
 
 /* boolean handleKeyPress (in nsIDOMKeyEvent keyEvent); */
 NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool* aHandledFlag)
 {
   *aHandledFlag = PR_FALSE;
   NS_ENSURE_ARG(aKeyEvent);
 
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (!pm)
-    return NS_OK;
-
   // if event has already been handled, bail
   nsCOMPtr<nsIDOMNSUIEvent> uiEvent(do_QueryInterface(aKeyEvent));
   if (!uiEvent)
     return NS_OK;
 
   PRBool eventHandled = PR_FALSE;
   uiEvent->GetPreventDefault(&eventHandled);
   if (eventHandled)
     return NS_OK;
 
   if (nsMenuBarListener::IsAccessKeyPressed(aKeyEvent))
     return NS_OK;
 
   nsIFrame* frame = GetFrame(PR_FALSE);
-  if (!frame || frame->GetType() != nsGkAtoms::menuFrame)
+  if (!frame)
     return NS_OK;
 
-  nsMenuPopupFrame* popupFrame = NS_STATIC_CAST(nsMenuFrame *, frame)->GetPopup();
-  if (!popupFrame)
+  nsIMenuFrame* menuFrame;
+  CallQueryInterface(frame, &menuFrame);
+  if (!menuFrame)
     return NS_OK;
 
   PRUint32 keyCode;
   aKeyEvent->GetKeyCode(&keyCode);
   switch (keyCode) {
     case NS_VK_UP:
     case NS_VK_DOWN:
     case NS_VK_HOME:
     case NS_VK_END:
-      *aHandledFlag = pm->HandleKeyboardNavigation(keyCode);
-      return NS_OK;
+      return menuFrame->KeyboardNavigation(keyCode, *aHandledFlag);
     default:
-      *aHandledFlag = pm->HandleShortcutNavigation(aKeyEvent);
-      return NS_OK;
+      return menuFrame->ShortcutNavigation(aKeyEvent, *aHandledFlag);
   }
 }
 
 // Creation Routine ///////////////////////////////////////////////////////////////////////
 
 nsresult
 NS_NewMenuBoxObject(nsIBoxObject** aResult)
 {
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/nsMenuDismissalListener.cpp
@@ -0,0 +1,227 @@
+/* -*- 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 Communicator client 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):
+ *   Dean Tessman <dean_tessman@hotmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 "nsMenuDismissalListener.h"
+#include "nsIMenuParent.h"
+#include "nsMenuFrame.h"
+#include "nsIPopupBoxObject.h"
+#include "nsContentUtils.h"
+
+nsMenuDismissalListener* nsMenuDismissalListener::sInstance = nsnull;
+
+/*
+ * nsMenuDismissalListener implementation
+ */
+
+NS_IMPL_ADDREF(nsMenuDismissalListener)
+NS_IMPL_RELEASE(nsMenuDismissalListener)
+NS_INTERFACE_MAP_BEGIN(nsMenuDismissalListener)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+  NS_INTERFACE_MAP_ENTRY(nsIMenuRollup)
+  NS_INTERFACE_MAP_ENTRY(nsIRollupListener)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMouseListener)
+NS_INTERFACE_MAP_END
+
+
+////////////////////////////////////////////////////////////////////////
+
+nsMenuDismissalListener::nsMenuDismissalListener() :
+  mEnabled(PR_TRUE)
+{
+  mMenuParent = nsnull;
+}
+
+nsMenuDismissalListener::~nsMenuDismissalListener() 
+{
+}
+
+nsMenuDismissalListener*
+nsMenuDismissalListener::GetInstance()
+{
+  if (!sInstance) {
+    sInstance = new nsMenuDismissalListener();
+    NS_IF_ADDREF(sInstance);
+  }
+  return sInstance;
+}
+
+/* static */ void
+nsMenuDismissalListener::Shutdown()
+{
+  if (sInstance) {
+    // XXX temporary code for bug 381426 until bug 279703
+    nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_FALSE);
+
+    sInstance->Unregister();
+    NS_RELEASE(sInstance);
+  }
+}
+
+
+nsIMenuParent*
+nsMenuDismissalListener::GetCurrentMenuParent()
+{
+  return mMenuParent;
+}
+
+void
+nsMenuDismissalListener::SetCurrentMenuParent(nsIMenuParent* aMenuParent)
+{
+  if (aMenuParent == mMenuParent)
+    return;
+
+  mMenuParent = aMenuParent;
+
+  if (!aMenuParent) {
+    Shutdown();
+    return;
+  }
+
+  Unregister();
+  Register();
+}
+
+NS_IMETHODIMP
+nsMenuDismissalListener::Rollup()
+{
+  if (mEnabled) {
+    if (mMenuParent) {
+      AddRef();
+      mMenuParent->HideChain();
+      mMenuParent->DismissChain();
+      Release();
+    }
+    else
+      Shutdown();
+  }
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+NS_IMETHODIMP nsMenuDismissalListener::ShouldRollupOnMouseWheelEvent(PRBool *aShouldRollup) 
+{ 
+  *aShouldRollup = PR_FALSE; 
+  return NS_OK;
+}
+
+
+// uggggh.
+
+// a menu should not roll up if activated by a mouse activate message (eg. X-mouse)
+NS_IMETHODIMP nsMenuDismissalListener::ShouldRollupOnMouseActivate(PRBool *aShouldRollup) 
+{ 
+  *aShouldRollup = PR_FALSE; 
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuDismissalListener::GetSubmenuWidgetChain(nsISupportsArray **_retval)
+{
+  NS_NewISupportsArray ( _retval );
+  nsIMenuParent *curr = mMenuParent;
+  while ( curr ) {
+    nsCOMPtr<nsIWidget> widget;
+    curr->GetWidget ( getter_AddRefs(widget) );
+    nsCOMPtr<nsISupports> genericWidget ( do_QueryInterface(widget) );
+    (**_retval).AppendElement ( genericWidget );
+    
+    // move up the chain
+    nsIFrame* currAsFrame = nsnull;
+    if ( NS_SUCCEEDED(CallQueryInterface(curr, &currAsFrame)) ) {
+      nsIMenuFrame *menuFrame = nsnull;
+      nsIFrame *parentFrame = currAsFrame->GetParent();
+      if (parentFrame) {
+        CallQueryInterface(parentFrame, &menuFrame);
+      }
+      if ( menuFrame ) {
+        curr = menuFrame->GetMenuParent ();       // Advance to next parent
+      }
+      else {
+        // we are a menuParent but not a menuFrame. This is probably the case
+        // of the menu bar. Nothing to do here, really.
+        return NS_OK;
+      }
+    }
+    else {
+      // We've run into a menu parent that isn't a frame at all. Not good.
+      NS_WARNING ( "nsIMenuParent that is not a nsIFrame" );
+      return NS_ERROR_FAILURE;
+    }
+  } // foreach parent menu
+  
+  return NS_OK; 
+}
+
+
+void
+nsMenuDismissalListener::Register()
+{
+  if (mWidget)
+    return;
+
+  nsCOMPtr<nsIWidget> widget;
+  mMenuParent->GetWidget(getter_AddRefs(widget));
+  if (!widget) {
+    Shutdown();
+    return;
+  }
+
+  PRBool consumeOutsideClicks = PR_FALSE;
+  mMenuParent->ConsumeOutsideClicks(consumeOutsideClicks);
+  widget->CaptureRollupEvents(this, PR_TRUE, consumeOutsideClicks);
+  mWidget = widget;
+
+  mMenuParent->AttachedDismissalListener();
+}
+
+void
+nsMenuDismissalListener::Unregister()
+{
+  if (mWidget) {
+    mWidget->CaptureRollupEvents(this, PR_FALSE, PR_FALSE);
+    mWidget = nsnull;
+  }
+}
+
+void
+nsMenuDismissalListener::EnableListener(PRBool aEnabled)
+{
+  mEnabled = aEnabled;
+}
+
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/nsMenuDismissalListener.h
@@ -0,0 +1,103 @@
+/* -*- 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 Communicator client 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):
+ *   Original Author: David W. Hyatt (hyatt@netscape.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 nsMenuDismissalListener_h__
+#define nsMenuDismissalListener_h__
+
+#include "nsIWidget.h"
+#include "nsIDOMMouseListener.h"
+#include "nsIRollupListener.h"
+#include "nsIMenuRollup.h"
+#include "nsIDOMEventTarget.h"
+#include "nsCOMPtr.h"
+
+class nsIMenuParent;
+
+/**
+ * The object responsible for rolling up the open menu popups in cases when
+ * it's not done by menu code (for example, when clicking outside a popup
+ * on Windows).
+ *
+ * It is a singleton, which exists as long as there is a menu popup open.
+ */
+class nsMenuDismissalListener : public nsIDOMMouseListener,
+                                public nsIMenuRollup,
+                                public nsIRollupListener
+{
+
+public:
+  friend class nsMenuPopupFrame;
+
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
+  NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIROLLUPLISTENER
+  NS_DECL_NSIMENUROLLUP
+
+  void EnableListener(PRBool aEnabled);
+  void SetCurrentMenuParent(nsIMenuParent* aMenuParent);
+  nsIMenuParent* GetCurrentMenuParent();
+
+  static nsMenuDismissalListener* GetInstance();
+  static nsMenuDismissalListener* sInstance;
+  static void Shutdown();
+
+protected:
+  nsMenuDismissalListener();
+  ~nsMenuDismissalListener();
+
+  /**
+   * Registers itself as a rollup event listener for current mMenuParent's
+   * widget. mMenuParent must be non-null.
+   */
+  void Register();
+  
+  void Unregister();
+
+  nsIMenuParent* mMenuParent;
+  nsCOMPtr<nsIWidget> mWidget;
+  PRBool mEnabled;
+};
+
+
+#endif
--- a/layout/xul/base/src/nsMenuFrame.cpp
+++ b/layout/xul/base/src/nsMenuFrame.cpp
@@ -66,29 +66,28 @@
 #include "nsWidgetsCID.h"
 #include "nsBoxLayoutState.h"
 #include "nsIScrollableFrame.h"
 #include "nsIViewManager.h"
 #include "nsBindingManager.h"
 #include "nsIServiceManager.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsIDOMKeyEvent.h"
-#include "nsEventDispatcher.h"
-#include "nsIPrivateDOMEvent.h"
 #include "nsIScrollableView.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsIStringBundle.h"
 #include "nsGUIEvent.h"
+#include "nsIEventStateManager.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsIReflowCallback.h"
 
-#define NS_MENU_POPUP_LIST_INDEX 0
+#define NS_MENU_POPUP_LIST_INDEX   0
 
 #if defined(XP_WIN) || defined(XP_OS2)
 #define NSCONTEXTMENUISMOUSEUP 1
 #endif
 
 static PRInt32 gEatMouseMove = PR_FALSE;
 
 static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
@@ -137,63 +136,56 @@ NS_INTERFACE_MAP_BEGIN(nsMenuFrame)
 NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
 
 //
 // nsMenuFrame cntr
 //
 nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext):
   nsBoxFrame(aShell, aContext),
     mIsMenu(PR_FALSE),
+    mMenuOpen(PR_FALSE),
+    mCreateHandlerSucceeded(PR_FALSE),
     mChecked(PR_FALSE),
     mType(eMenuType_Normal),
     mMenuParent(nsnull),
-    mPopupFrame(nsnull),
     mLastPref(-1,-1)
 {
 
 } // cntr
 
 NS_IMETHODIMP
 nsMenuFrame::SetParent(const nsIFrame* aParent)
 {
   nsBoxFrame::SetParent(aParent);
-  InitMenuParent(NS_CONST_CAST(nsIFrame *, aParent));
-  return NS_OK;
-}
+  const nsIFrame* currFrame = aParent;
+  while (!mMenuParent && currFrame) {
+    // Set our menu parent.
+    CallQueryInterface(NS_CONST_CAST(nsIFrame*, currFrame), &mMenuParent);
 
-void
-nsMenuFrame::InitMenuParent(nsIFrame* aParent)
-{
-  while (aParent) {
-    nsIAtom* type = aParent->GetType();
-    if (type == nsGkAtoms::menuPopupFrame) {
-      mMenuParent = NS_STATIC_CAST(nsMenuPopupFrame *, aParent);
-      break;
-    }
-    else if (type == nsGkAtoms::menuBarFrame) {
-      mMenuParent = NS_STATIC_CAST(nsMenuBarFrame *, aParent);
-      break;
-    }
-    aParent = aParent->GetParent();
+    currFrame = currFrame->GetParent();
   }
+
+  return NS_OK;
 }
 
 class nsASyncMenuInitialization : public nsIReflowCallback
 {
 public:
   nsASyncMenuInitialization(nsIFrame* aFrame)
     : mWeakFrame(aFrame)
   {
   }
 
   virtual PRBool ReflowFinished() {
     PRBool shouldFlush = PR_FALSE;
     if (mWeakFrame.IsAlive()) {
-      if (mWeakFrame.GetFrame()->GetType() == nsGkAtoms::menuFrame) {
-        nsMenuFrame* menu = NS_STATIC_CAST(nsMenuFrame*, mWeakFrame.GetFrame());
+      nsIMenuFrame* imenu = nsnull;
+      CallQueryInterface(mWeakFrame.GetFrame(), &imenu);
+      if (imenu) {
+        nsMenuFrame* menu = NS_STATIC_CAST(nsMenuFrame*, imenu);
         menu->UpdateMenuType(menu->PresContext());
         shouldFlush = PR_TRUE;
       }
     }
     delete this;
     return shouldFlush;
   }
 
@@ -207,17 +199,23 @@ nsMenuFrame::Init(nsIContent*      aCont
 {
   nsresult  rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   // Set up a mediator which can be used for callbacks on this frame.
   mTimerMediator = new nsMenuTimerMediator(this);
   if (NS_UNLIKELY(!mTimerMediator))
     return NS_ERROR_OUT_OF_MEMORY;
 
-  InitMenuParent(aParent);
+  nsIFrame* currFrame = aParent;
+  while (!mMenuParent && currFrame) {
+    // Set our menu parent.
+    CallQueryInterface(currFrame, &mMenuParent);
+
+    currFrame = currFrame->GetParent();
+  }
 
   //load the display strings for the keyboard accelerators, but only once
   if (gRefCnt++ == 0) {
     
     nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
     nsCOMPtr<nsIStringBundle> bundle;
     if (NS_SUCCEEDED(rv) && bundleService) {
       rv = bundleService->CreateBundle( "chrome://global-platform/locale/platformKeys.properties",
@@ -273,75 +271,114 @@ nsMenuFrame::~nsMenuFrame()
 }
 
 // The following methods are all overridden to ensure that the menupopup frame
 // is placed in the appropriate list.
 nsIFrame*
 nsMenuFrame::GetFirstChild(nsIAtom* aListName) const
 {
   if (nsGkAtoms::popupList == aListName) {
-    return mPopupFrame;
+    return mPopupFrames.FirstChild();
   }
   return nsBoxFrame::GetFirstChild(aListName);
 }
 
 NS_IMETHODIMP
 nsMenuFrame::SetInitialChildList(nsIAtom*        aListName,
                                  nsIFrame*       aChildList)
 {
-  // Check for a menupopup and move it to mPopupFrame
-  nsFrameList frames(aChildList);
-  nsIFrame* frame = frames.FirstChild();
-  while (frame) {
-    if (frame->GetType() == nsGkAtoms::menuPopupFrame) {
-      // Remove this frame from the list and set it as mPopupFrame
-      frames.RemoveFrame(frame);
-      mPopupFrame = (nsMenuPopupFrame *)frame;
-      aChildList = frames.FirstChild();
-      break;
+  nsresult rv = NS_OK;
+  if (nsGkAtoms::popupList == aListName) {
+    mPopupFrames.SetFrames(aChildList);
+  } else {
+
+    nsFrameList frames(aChildList);
+
+    // We may have a menupopup in here. Get it out, and move it into
+    // the popup frame list.
+    nsIFrame* frame = frames.FirstChild();
+    while (frame) {
+      nsIMenuParent *menuPar;
+      CallQueryInterface(frame, &menuPar);
+      if (menuPar) {
+        PRBool isMenuBar;
+        menuPar->IsMenuBar(isMenuBar);
+        if (!isMenuBar) {
+          // Remove this frame from the list and place it in the other list.
+          frames.RemoveFrame(frame);
+          mPopupFrames.AppendFrame(this, frame);
+          nsIFrame* first = frames.FirstChild();
+          rv = nsBoxFrame::SetInitialChildList(aListName, first);
+          return rv;
+        }
+      }
+      frame = frame->GetNextSibling();
     }
-    frame = frame->GetNextSibling();
+
+    // Didn't find it.
+    rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
   }
-
-  // Didn't find it.
-  return nsBoxFrame::SetInitialChildList(aListName, aChildList);
+  return rv;
 }
 
 nsIAtom*
 nsMenuFrame::GetAdditionalChildListName(PRInt32 aIndex) const
 {
   if (NS_MENU_POPUP_LIST_INDEX == aIndex) {
     return nsGkAtoms::popupList;
   }
+
   return nsnull;
 }
 
+nsresult
+nsMenuFrame::DestroyPopupFrames(nsPresContext* aPresContext)
+{
+  // Remove our frame mappings
+  nsCSSFrameConstructor* frameConstructor =
+    aPresContext->PresShell()->FrameConstructor();
+  nsIFrame* curFrame = mPopupFrames.FirstChild();
+  while (curFrame) {
+    frameConstructor->RemoveMappingsForFrameSubtree(curFrame);
+    curFrame = curFrame->GetNextSibling();
+  }
+
+   // Cleanup frames in popup child list
+  mPopupFrames.DestroyFrames();
+  return NS_OK;
+}
+
 void
 nsMenuFrame::Destroy()
 {
   // Kill our timer if one is active. This is not strictly necessary as
   // the pointer to this frame will be cleared from the mediator, but
   // this is done for added safety.
   if (mOpenTimer) {
     mOpenTimer->Cancel();
   }
 
   // Null out the pointer to this frame in the mediator wrapper so that it 
   // doesn't try to interact with a deallocated frame.
   mTimerMediator->ClearFrame();
 
+  nsWeakFrame weakFrame(this);
   // are we our menu parent's current menu item?
-  if (mMenuParent && mMenuParent->GetCurrentMenuItem() == this) {
-    // yes; tell it that we're going away
-    mMenuParent->CurrentMenuIsBeingDestroyed();
+  if (mMenuParent) {
+    nsIMenuFrame *curItem = mMenuParent->GetCurrentMenuItem();
+    if (curItem == this) {
+      // yes; tell it that we're going away
+      mMenuParent->SetCurrentMenuItem(nsnull);
+      ENSURE_TRUE(weakFrame.IsAlive());
+    }
   }
 
-  if (mPopupFrame)
-    mPopupFrame->Destroy();
-
+  UngenerateMenu();
+  ENSURE_TRUE(weakFrame.IsAlive());
+  DestroyPopupFrames(PresContext());
   nsBoxFrame::Destroy();
 }
 
 NS_IMETHODIMP
 nsMenuFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                          const nsRect&           aDirtyRect,
                                          const nsDisplayListSet& aLists)
 {
@@ -350,299 +387,625 @@ nsMenuFrame::BuildDisplayListForChildren
     
   nsDisplayListCollection set;
   nsresult rv = nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, set);
   NS_ENSURE_SUCCESS(rv, rv);
   
   return WrapListsInRedirector(aBuilder, set, aLists);
 }
 
-NS_IMETHODIMP
+NS_IMETHODIMP 
 nsMenuFrame::HandleEvent(nsPresContext* aPresContext, 
-                         nsGUIEvent*     aEvent,
-                         nsEventStatus*  aEventStatus)
+                             nsGUIEvent*     aEvent,
+                             nsEventStatus*  aEventStatus)
 {
   NS_ENSURE_ARG_POINTER(aEventStatus);
   nsWeakFrame weakFrame(this);
   if (*aEventStatus == nsEventStatus_eIgnore)
     *aEventStatus = nsEventStatus_eConsumeDoDefault;
-
-  PRBool onmenu = IsOnMenu();
-
+  
   if (aEvent->message == NS_KEY_PRESS && !IsDisabled()) {
     nsKeyEvent* keyEvent = (nsKeyEvent*)aEvent;
     PRUint32 keyCode = keyEvent->keyCode;
 #ifdef XP_MACOSX
     // On mac, open menulist on either up/down arrow or space (w/o Cmd pressed)
     if (!IsOpen() && ((keyEvent->charCode == NS_VK_SPACE && !keyEvent->isMeta) ||
         (keyCode == NS_VK_UP || keyCode == NS_VK_DOWN)))
-      OpenMenu(PR_FALSE);
+      OpenMenu(PR_TRUE);
 #else
     // On other platforms, toggle menulist on unmodified F4 or Alt arrow
     if ((keyCode == NS_VK_F4 && !keyEvent->isAlt) ||
         ((keyCode == NS_VK_UP || keyCode == NS_VK_DOWN) && keyEvent->isAlt))
-      ToggleMenuState();
+      OpenMenu(!IsOpen());
 #endif
   }
   else if (aEvent->eventStructType == NS_MOUSE_EVENT &&
            aEvent->message == NS_MOUSE_BUTTON_DOWN &&
            NS_STATIC_CAST(nsMouseEvent*, aEvent)->button == nsMouseEvent::eLeftButton &&
            !IsDisabled() && IsMenu()) {
+    PRBool isMenuBar = PR_FALSE;
+    if (mMenuParent)
+      mMenuParent->IsMenuBar(isMenuBar);
+
     // The menu item was selected. Bring up the menu.
     // We have children.
-    if (!mMenuParent || mMenuParent->IsMenuBar()) {
+    if ( isMenuBar || !mMenuParent ) {
       ToggleMenuState();
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+
+      if (!IsOpen() && mMenuParent) {
+        // We closed up. The menu bar should always be
+        // deactivated when this happens.
+        mMenuParent->SetActive(PR_FALSE);
+      }
     }
-    else {
-      if (!IsOpen())
-        OpenMenu(PR_FALSE);
-    }
+    else
+      if ( !IsOpen() ) {
+        // one of our siblings is probably open and even possibly waiting
+        // for its close timer to fire. Tell our parent to close it down. Not
+        // doing this before its timer fires will cause the rollup state to
+        // get very confused.
+        if ( mMenuParent )
+          mMenuParent->KillPendingTimers();
+
+        // safe to open up
+        OpenMenu(PR_TRUE);
+      }
   }
   else if (
 #ifndef NSCONTEXTMENUISMOUSEUP
            (aEvent->eventStructType == NS_MOUSE_EVENT &&
             aEvent->message == NS_MOUSE_BUTTON_UP &&
             NS_STATIC_CAST(nsMouseEvent*, aEvent)->button ==
               nsMouseEvent::eRightButton) &&
 #else
             aEvent->message == NS_CONTEXTMENU &&
 #endif
-            onmenu && !IsMenu() && !IsDisabled()) {
+            mMenuParent && !IsMenu() && !IsDisabled()) {
     // if this menu is a context menu it accepts right-clicks...fire away!
     // Make sure we cancel default processing of the context menu event so
     // that it doesn't bubble and get seen again by the popuplistener and show
     // another context menu.
     //
     // Furthermore (there's always more, isn't there?), on some platforms (win32
     // being one of them) we get the context menu event on a mouse up while
     // on others we get it on a mouse down. For the ones where we get it on a
     // mouse down, we must continue listening for the right button up event to
     // dismiss the menu.
-    if (mMenuParent->IsContextMenu()) {
+    PRBool isContextMenu = PR_FALSE;
+    mMenuParent->GetIsContextMenu(isContextMenu);
+    if ( isContextMenu ) {
       *aEventStatus = nsEventStatus_eConsumeNoDefault;
       Execute(aEvent);
     }
   }
   else if (aEvent->eventStructType == NS_MOUSE_EVENT &&
            aEvent->message == NS_MOUSE_BUTTON_UP &&
            NS_STATIC_CAST(nsMouseEvent*, aEvent)->button == nsMouseEvent::eLeftButton &&
-           !IsMenu() && !IsDisabled()) {
+           !IsMenu() && mMenuParent && !IsDisabled()) {
     // Execute the execute event handler.
     Execute(aEvent);
   }
   else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
     // Kill our timer if one is active.
     if (mOpenTimer) {
       mOpenTimer->Cancel();
       mOpenTimer = nsnull;
     }
 
     // Deactivate the menu.
+    PRBool isActive = PR_FALSE;
+    PRBool isMenuBar = PR_FALSE;
     if (mMenuParent) {
-      PRBool onmenubar = mMenuParent->IsMenuBar();
-      if (!(onmenubar && mMenuParent->IsActive())) {
-        if (IsMenu() && !onmenubar && IsOpen()) {
+      mMenuParent->IsMenuBar(isMenuBar);
+      PRBool cancel = PR_TRUE;
+      if (isMenuBar) {
+        mMenuParent->GetIsActive(isActive);
+        if (isActive) cancel = PR_FALSE;
+      }
+      
+      if (cancel) {
+        if (IsMenu() && !isMenuBar && mMenuOpen) {
           // Submenus don't get closed up immediately.
         }
-        else
-          mMenuParent->ChangeMenuItem(nsnull, PR_FALSE);
+        else mMenuParent->SetCurrentMenuItem(nsnull);
       }
     }
   }
-  else if (aEvent->message == NS_MOUSE_MOVE &&
-           (onmenu || (mMenuParent && mMenuParent->IsMenuBar()))) {
+  else if (aEvent->message == NS_MOUSE_MOVE && mMenuParent) {
     if (gEatMouseMove) {
       gEatMouseMove = PR_FALSE;
       return NS_OK;
     }
 
+    // we checked for mMenuParent right above
+
+    PRBool isMenuBar = PR_FALSE;
+    mMenuParent->IsMenuBar(isMenuBar);
+
     // Let the menu parent know we're the new item.
-    mMenuParent->ChangeMenuItem(this, PR_FALSE);
+    mMenuParent->SetCurrentMenuItem(this);
     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
     NS_ENSURE_TRUE(mMenuParent, NS_OK);
-
+    
     // we need to check if we really became the current menu
     // item or not
-    nsMenuFrame *realCurrentItem = mMenuParent->GetCurrentMenuItem();
+    nsIMenuFrame *realCurrentItem = mMenuParent->GetCurrentMenuItem();
     if (realCurrentItem != this) {
       // we didn't (presumably because a context menu was active)
       return NS_OK;
     }
 
-    // Hovering over a menu in a popup should open it without a need for a click.
-    // A timer is used so that it doesn't open if the user moves the mouse quickly
-    // past the menu. This conditional check ensures that only menus have this
-    // behaviour
-    if (!IsDisabled() && IsMenu() && !IsOpen() && !mOpenTimer && !mMenuParent->IsMenuBar()) {
+    // If we're a menu (and not a menu item),
+    // kick off the timer.
+    if (!IsDisabled() && !isMenuBar && IsMenu() && !mMenuOpen && !mOpenTimer) {
+
       PRInt32 menuDelay = 300;   // ms
 
       nsCOMPtr<nsILookAndFeel> lookAndFeel(do_GetService(kLookAndFeelCID));
       if (lookAndFeel)
         lookAndFeel->GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay);
 
       // We're a menu, we're built, we're closed, and no timer has been kicked off.
       mOpenTimer = do_CreateInstance("@mozilla.org/timer;1");
       mOpenTimer->InitWithCallback(mTimerMediator, menuDelay, nsITimer::TYPE_ONE_SHOT);
+
     }
   }
   
   return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 nsMenuFrame::ToggleMenuState()
 {
-  if (IsOpen())
-    CloseMenu(PR_FALSE);
-  else
+  nsWeakFrame weakFrame(this);
+  if (mMenuOpen) {
     OpenMenu(PR_FALSE);
-}
-
-void
-nsMenuFrame::PopupOpened()
-{
-  nsWeakFrame weakFrame(this);
-  mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
-                    NS_LITERAL_STRING("true"), PR_TRUE);
-  if (!weakFrame.IsAlive())
-    return;
+    NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+  }
+  else {
+    PRBool justRolledUp = PR_FALSE;
+    if (mMenuParent) {
+      mMenuParent->RecentlyRolledUp(this, &justRolledUp);
+    }
+    if (justRolledUp) {
+      // Don't let a click reopen a menu that was just rolled up
+      // from the same click. Otherwise, the user can't click on
+      // a menubar item to toggle its submenu closed.
+      OpenMenu(PR_FALSE);
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+      SelectMenu(PR_TRUE);
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+      NS_ENSURE_TRUE(mMenuParent, NS_OK);
+      mMenuParent->SetActive(PR_FALSE);
+    }
+    else {
+      if (mMenuParent) {
+        mMenuParent->SetActive(PR_TRUE);
+        NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+      }
+      OpenMenu(PR_TRUE);
+    }
+  }
+  NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
 
   if (mMenuParent) {
-    mMenuParent->SetActive(PR_TRUE);
     // Make sure the current menu which is being toggled on
     // the menubar is highlighted
     mMenuParent->SetCurrentMenuItem(this);
-  }
-}
-
-void
-nsMenuFrame::PopupClosed(PRBool aDeselectMenu)
-{
-  nsWeakFrame weakFrame(this);
-  mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, PR_TRUE);
-  if (!weakFrame.IsAlive())
-    return;
-
-  // if the popup is for a menu on a menubar, inform menubar to deactivate
-  if (mMenuParent && mMenuParent->MenuClosed()) {
-    if (aDeselectMenu)
-      SelectMenu(PR_FALSE);
-  }
-}
-
-// this class is used for dispatching menu activation events asynchronously.
-class nsMenuActivateEvent : public nsRunnable
-{
-public:
-  nsMenuActivateEvent(nsIContent *aMenu,
-                      nsPresContext* aPresContext,
-                      PRBool aIsActivate)
-    : mMenu(aMenu), mPresContext(aPresContext), mIsActivate(aIsActivate)
-  {
+    NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    NS_ENSURE_TRUE(mMenuParent, NS_OK);
+    // We've successfully prevented the same click from both
+    // dismissing and reopening this menu. 
+    // Clear the recent rollup state so we don't prevent
+    // this menu from being opened by the next click.
+    mMenuParent->ClearRecentlyRolledUp();
   }
 
-  NS_IMETHOD Run()
-  {
-    nsAutoString domEventToFire;
-
-    if (mIsActivate) {
-      // Highlight the menu.
-      mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive,
-                     NS_LITERAL_STRING("true"), PR_TRUE);
-      // The menuactivated event is used by accessibility to track the user's
-      // movements through menus
-      domEventToFire.AssignLiteral("DOMMenuItemActive");
-    }
-    else {
-      // Unhighlight the menu.
-      mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, PR_TRUE);
-      domEventToFire.AssignLiteral("DOMMenuItemInactive");
-    }
-
-    nsCOMPtr<nsIDOMEvent> event;
-    if (NS_SUCCEEDED(nsEventDispatcher::CreateEvent(mPresContext, nsnull,
-                                                    NS_LITERAL_STRING("Events"),
-                                                    getter_AddRefs(event)))) {
-      event->InitEvent(domEventToFire, PR_TRUE, PR_TRUE);
-
-      nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
-      privateEvent->SetTrusted(PR_TRUE);
-
-      nsEventDispatcher::DispatchDOMEvent(mMenu, nsnull, event,
-                                          mPresContext, nsnull);
-    }
-
-    return NS_OK;
-  }
-
-private:
-  nsCOMPtr<nsIContent> mMenu;
-  nsCOMPtr<nsPresContext> mPresContext;
-  PRBool mIsActivate;
-};
+  return NS_OK;
+}
 
 NS_IMETHODIMP
 nsMenuFrame::SelectMenu(PRBool aActivateFlag)
 {
-  if (mContent) {
-    nsCOMPtr<nsIRunnable> event =
-      new nsMenuActivateEvent(mContent, PresContext(), aActivateFlag);
-    NS_DispatchToCurrentThread(event);
+  if (!mContent) {
+    return NS_OK;
+  }
+
+  nsAutoString domEventToFire;
+
+  nsWeakFrame weakFrame(this);
+  if (aActivateFlag) {
+    if (mMenuParent) {
+      nsIMenuParent* ancestor = nsnull;
+      nsresult rv = mMenuParent->GetParentPopup(&ancestor);
+      while (NS_SUCCEEDED(rv) && ancestor) {
+        ancestor->CancelPendingTimers();
+        rv = ancestor->GetParentPopup(&ancestor);
+      }
+    }
+    // Highlight the menu.
+    mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, NS_LITERAL_STRING("true"), PR_TRUE);
+    // The menuactivated event is used by accessibility to track the user's movements through menus
+    domEventToFire.AssignLiteral("DOMMenuItemActive");
+  }
+  else {
+    // Unhighlight the menu.
+    mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, PR_TRUE);
+    domEventToFire.AssignLiteral("DOMMenuItemInactive");
+  }
+
+  if (weakFrame.IsAlive()) {
+    FireDOMEventSynch(domEventToFire);
+  }
+  return NS_OK;
+}
+
+PRBool nsMenuFrame::IsGenerated()
+{
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  // Generate the menu if it hasn't been generated already.  This
+  // takes it from display: none to display: block and gives us
+  // a menu forevermore.
+  if (child &&
+      !nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None,
+                                       nsGkAtoms::menugenerated)) {
+    return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
+NS_IMETHODIMP
+nsMenuFrame::MarkAsGenerated()
+{
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  // Generate the menu if it hasn't been generated already.  This
+  // takes it from display: none to display: block and gives us
+  // a menu forevermore.
+  if (child &&
+      !nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None,
+                                       nsGkAtoms::menugenerated)) {
+    child->SetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated,
+                   NS_LITERAL_STRING("true"), PR_TRUE);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsMenuFrame::UngenerateMenu()
+{
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  if (child &&
+      nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None,
+                                      nsGkAtoms::menugenerated)) {
+    child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, PR_TRUE);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuFrame::ActivateMenu(PRBool aActivateFlag)
+{
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame;
+  
+  if (!menuPopup) 
+    return NS_OK;
+
+  if (aActivateFlag) {
+      nsRect rect = menuPopup->GetRect();
+      nsIView* view = menuPopup->GetView();
+      nsIViewManager* viewManager = view->GetViewManager();
+      rect.x = rect.y = 0;
+      viewManager->ResizeView(view, rect);
+
+      // make sure the scrolled window is at 0,0
+      if (mLastPref.height <= rect.height) {
+        nsIBox* child = menuPopup->GetChildBox();
+
+        nsCOMPtr<nsIScrollableFrame> scrollframe(do_QueryInterface(child));
+        if (scrollframe) {
+          scrollframe->ScrollTo(nsPoint(0,0));
+        }
+      }
+
+      viewManager->UpdateView(view, rect, NS_VMREFRESH_IMMEDIATE);
+      viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
+      PresContext()->RootPresContext()->NotifyAddedActivePopupToTop(menuPopup);
+  } else {
+    if (mMenuOpen) {
+      nsWeakFrame weakFrame(this);
+      nsWeakFrame weakPopup(menuPopup);
+      FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuInactive"), menuPopup->GetContent());
+      NS_ENSURE_TRUE(weakFrame.IsAlive() && weakPopup.IsAlive(), NS_OK);
+    }
+    nsIView* view = menuPopup->GetView();
+    NS_ASSERTION(view, "View is gone, looks like someone forgot to rollup the popup!");
+    if (view) {
+      nsIViewManager* viewManager = view->GetViewManager();
+      if (viewManager) { // the view manager can be null during widget teardown
+        viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
+        viewManager->ResizeView(view, nsRect(0, 0, 0, 0));
+      }
+    }
+    // set here so hide chain can close the menu as well.
+    mMenuOpen = PR_FALSE;
+    PresContext()->RootPresContext()->NotifyRemovedActivePopup(menuPopup);
+  }
+  
+  return NS_OK;
+}  
+
+NS_IMETHODIMP
 nsMenuFrame::AttributeChanged(PRInt32 aNameSpaceID,
                               nsIAtom* aAttribute,
                               PRInt32 aModType)
 {
   nsAutoString value;
 
   if (aAttribute == nsGkAtoms::checked) {
     if (mType != eMenuType_Normal)
         UpdateMenuSpecialState(PresContext());
   } else if (aAttribute == nsGkAtoms::acceltext) {
     // someone reset the accelText attribute, so clear the bit that says *we* set it
     AddStateBits(NS_STATE_ACCELTEXT_IS_DERIVED);
     BuildAcceleratorText();
   } else if (aAttribute == nsGkAtoms::key) {
     BuildAcceleratorText();
-  } else if (aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::name)
+  } else if ( aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::name )
     UpdateMenuType(PresContext());
 
   return NS_OK;
 }
 
-void
-nsMenuFrame::OpenMenu(PRBool aSelectFirstItem)
+NS_IMETHODIMP
+nsMenuFrame::OpenMenu(PRBool aActivateFlag)
 {
   if (!mContent)
+    return NS_OK;
+
+  nsWeakFrame weakFrame(this);
+  if (aActivateFlag) {
+    // Now that the menu is opened, we should have a menupopup child built.
+    // Mark it as generated, which ensures a frame gets built.
+    MarkAsGenerated();
+    NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+
+    mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE);
+    NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuItemActive"));
+  }
+  else {
+    mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, PR_TRUE);
+  }
+
+  NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+  OpenMenuInternal(aActivateFlag);
+
+  return NS_OK;
+}
+
+void 
+nsMenuFrame::OpenMenuInternal(PRBool aActivateFlag) 
+{
+  gEatMouseMove = PR_TRUE;
+
+  if (!mIsMenu)
     return;
 
-  gEatMouseMove = PR_TRUE;
+  nsPresContext* presContext = PresContext();
+  nsWeakFrame weakFrame(this);
+
+  if (aActivateFlag) {
+    // Execute the oncreate handler
+    if (!OnCreate() || !weakFrame.IsAlive())
+      return;
+
+    mCreateHandlerSucceeded = PR_TRUE;
+  
+    // Set the focus back to our view's widget.
+    if (nsMenuDismissalListener::sInstance)
+      nsMenuDismissalListener::sInstance->EnableListener(PR_FALSE);
+    
+    // XXX Only have this here because of RDF-generated content.
+    MarkAsGenerated();
+    ENSURE_TRUE(weakFrame.IsAlive());
+
+    nsIFrame* frame = mPopupFrames.FirstChild();
+    nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame;
+    
+    PRBool wasOpen = mMenuOpen;
+    mMenuOpen = PR_TRUE;
+
+    if (menuPopup) {
+      nsWeakFrame weakMenuPopup(frame);
+      // inherit whether or not we're a context menu from the parent
+      if ( mMenuParent ) {
+        PRBool parentIsContextMenu = PR_FALSE;
+        mMenuParent->GetIsContextMenu(parentIsContextMenu);
+        menuPopup->SetIsContextMenu(parentIsContextMenu);
+        ENSURE_TRUE(weakFrame.IsAlive());
+      }
+
+      // Install a keyboard navigation listener if we're the root of the menu chain.
+      PRBool onMenuBar = PR_TRUE;
+      if (mMenuParent)
+        mMenuParent->IsMenuBar(onMenuBar);
+
+      if (mMenuParent && onMenuBar)
+        mMenuParent->InstallKeyboardNavigator();
+      else if (!mMenuParent) {
+        ENSURE_TRUE(weakMenuPopup.IsAlive());
+        menuPopup->InstallKeyboardNavigator();
+      }
+      
+      // Tell the menu bar we're active.
+      if (mMenuParent) {
+        mMenuParent->SetActive(PR_TRUE);
+        ENSURE_TRUE(weakFrame.IsAlive());
+      }
+
+      nsIContent* menuPopupContent = menuPopup->GetContent();
+
+      // Sync up the view.
+      nsAutoString popupAnchor, popupAlign;
+      
+      menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, popupAnchor);
+      menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, popupAlign);
+
+      ConvertPosition(menuPopupContent, popupAnchor, popupAlign);
+
+      if (onMenuBar) {
+        if (popupAnchor.IsEmpty())
+          popupAnchor.AssignLiteral("bottomleft");
+        if (popupAlign.IsEmpty())
+          popupAlign.AssignLiteral("topleft");
+      }
+      else {
+        if (popupAnchor.IsEmpty())
+          popupAnchor.AssignLiteral("topright");
+        if (popupAlign.IsEmpty())
+          popupAlign.AssignLiteral("topleft");
+      }
+
+      // If the menu popup was not open, do a reflow.  This is either the
+      // initial reflow for a brand-new popup, or a subsequent reflow for
+      // a menu that was deactivated and needs to be brought back to its
+      // active dimensions.
+      if (!wasOpen)
+      {
+         presContext->PresShell()->
+           FrameNeedsReflow(menuPopup, nsIPresShell::eStyleChange,
+                            NS_FRAME_IS_DIRTY);
+         presContext->PresShell()->FlushPendingNotifications(Flush_OnlyReflow);
+      }
+
+      nsRect curRect(menuPopup->GetRect());
+      nsBoxLayoutState state(presContext);
+      menuPopup->SetBounds(state, nsRect(0,0,mLastPref.width, mLastPref.height));
 
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm) {
-    pm->KillMenuTimer();
-    // This opens the menu asynchronously
-    pm->ShowMenu(mContent, aSelectFirstItem, PR_TRUE);
+      nsIView* view = menuPopup->GetView();
+      nsIViewManager* vm = view->GetViewManager();
+      if (vm) {
+        vm->SetViewVisibility(view, nsViewVisibility_kHide);
+      }
+      menuPopup->SyncViewWithFrame(presContext, popupAnchor, popupAlign, this, -1, -1);
+      nscoord newHeight = menuPopup->GetRect().height;
+
+      // if the height is different then reflow. It might need scrollbars force a reflow
+      if (curRect.height != newHeight || mLastPref.height != newHeight)
+      {
+         presContext->PresShell()->
+           FrameNeedsReflow(menuPopup, nsIPresShell::eStyleChange,
+                            NS_FRAME_IS_DIRTY);
+         presContext->PresShell()->FlushPendingNotifications(Flush_OnlyReflow);
+      }
+
+      ActivateMenu(PR_TRUE);
+      ENSURE_TRUE(weakFrame.IsAlive());
+
+      nsIMenuParent *childPopup = nsnull;
+      CallQueryInterface(frame, &childPopup);
+
+      nsMenuDismissalListener* listener = nsMenuDismissalListener::GetInstance();
+      if (listener)
+        listener->SetCurrentMenuParent(childPopup);
+
+      OnCreated();
+      ENSURE_TRUE(weakFrame.IsAlive());
+    }
+
+    // Set the focus back to our view's widget.
+    if (nsMenuDismissalListener::sInstance)
+      nsMenuDismissalListener::sInstance->EnableListener(PR_TRUE);
+
   }
+  else {
+
+    // Close the menu. 
+    // Execute the ondestroy handler, but only if we're actually open
+    if ( !mCreateHandlerSucceeded || !OnDestroy() || !weakFrame.IsAlive())
+      return;
+
+    // Set the focus back to our view's widget.
+    if (nsMenuDismissalListener::sInstance) {
+      nsMenuDismissalListener::sInstance->EnableListener(PR_FALSE);
+      nsMenuDismissalListener::sInstance->SetCurrentMenuParent(mMenuParent);
+    }
+
+    nsIFrame* frame = mPopupFrames.FirstChild();
+    nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame;
+  
+    // Make sure we clear out our own items.
+    if (menuPopup) {
+      menuPopup->SetCurrentMenuItem(nsnull);
+      ENSURE_TRUE(weakFrame.IsAlive());
+      menuPopup->KillCloseTimer();
+
+      PRBool onMenuBar = PR_TRUE;
+      if (mMenuParent)
+        mMenuParent->IsMenuBar(onMenuBar);
+
+      if (mMenuParent && onMenuBar)
+        mMenuParent->RemoveKeyboardNavigator();
+      else if (!mMenuParent)
+        menuPopup->RemoveKeyboardNavigator();
+
+      // XXX, bug 137033, In Windows, if mouse is outside the window when the menupopup closes, no
+      // mouse_enter/mouse_exit event will be fired to clear current hover state, we should clear it manually.
+      // This code may not the best solution, but we can leave it here until we find the better approach.
+
+      nsIEventStateManager *esm = presContext->EventStateManager();
+
+      PRInt32 state;
+      esm->GetContentState(menuPopup->GetContent(), state);
+
+      if (state & NS_EVENT_STATE_HOVER)
+        esm->SetContentState(nsnull, NS_EVENT_STATE_HOVER);
+    }
+
+    ActivateMenu(PR_FALSE);
+    ENSURE_TRUE(weakFrame.IsAlive());
+    // XXX hack: ensure that mMenuOpen is set to false, in case where
+    // there is actually no popup. because ActivateMenu() will return 
+    // early without setting it. It could be that mMenuOpen is true
+    // in that case, because OpenMenuInternal(true) gets called if
+    // the attribute open="true", whether there is a popup or not.
+    // We should not allow mMenuOpen unless there is a popup in the first place,
+    // in which case this line would not be necessary.
+    mMenuOpen = PR_FALSE;
+
+    OnDestroyed();
+    ENSURE_TRUE(weakFrame.IsAlive());
+
+    if (nsMenuDismissalListener::sInstance)
+      nsMenuDismissalListener::sInstance->EnableListener(PR_TRUE);
+
+    mCreateHandlerSucceeded = PR_FALSE;
+  }
+
 }
 
 void
-nsMenuFrame::CloseMenu(PRBool aDeselectMenu)
+nsMenuFrame::GetMenuChildrenElement(nsIContent** aResult)
 {
-  gEatMouseMove = PR_TRUE;
-
-  // Close the menu asynchronously
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mPopupFrame)
-    pm->HidePopup(mPopupFrame->GetContent(), PR_FALSE, aDeselectMenu, PR_TRUE);
+  *aResult = nsContentUtils::FindFirstChildWithResolvedTag(mContent,
+                                                           kNameSpaceID_XUL,
+                                                           nsGkAtoms::menupopup);
+  NS_IF_ADDREF(*aResult);
 }
 
 PRBool
 nsMenuFrame::IsSizedToPopup(nsIContent* aContent, PRBool aRequireAlways)
 {
   PRBool sizeToPopup;
   if (aContent->Tag() == nsGkAtoms::select)
     sizeToPopup = PR_TRUE;
@@ -670,84 +1033,93 @@ nsMenuFrame::GetMinSize(nsBoxLayoutState
 
 NS_IMETHODIMP
 nsMenuFrame::DoLayout(nsBoxLayoutState& aState)
 {
   // lay us out
   nsresult rv = nsBoxFrame::DoLayout(aState);
 
   // layout the popup. First we need to get it.
-  if (mPopupFrame) {
+  nsIFrame* popupChild = mPopupFrames.FirstChild();
+
+  if (popupChild) {
     PRBool sizeToPopup = IsSizedToPopup(mContent, PR_FALSE);
+    
+    NS_ASSERTION(popupChild->IsBoxFrame(), "popupChild is not box!!");
+
     // then get its preferred size
-    nsSize prefSize = mPopupFrame->GetPrefSize(aState);
-    nsSize minSize = mPopupFrame->GetMinSize(aState); 
-    nsSize maxSize = mPopupFrame->GetMaxSize(aState);
+    nsSize prefSize = popupChild->GetPrefSize(aState);
+    nsSize minSize = popupChild->GetMinSize(aState); 
+    nsSize maxSize = popupChild->GetMaxSize(aState);
 
     BoundsCheck(minSize, prefSize, maxSize);
 
     if (sizeToPopup)
         prefSize.width = mRect.width;
 
     // if the pref size changed then set bounds to be the pref size
-    PRBool sizeChanged = (mLastPref != prefSize);
-    if (sizeChanged) {
-      mPopupFrame->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
+    // and sync the view. And set new pref size.
+    if (mLastPref != prefSize) {
+      popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
+      RePositionPopup(aState);
       mLastPref = prefSize;
     }
 
-    // if the menu has just been opened, or its size changed, position
-    // the popup. The flag that the popup checks in the HasOpenChanged
-    // method will get cleared in AdjustView which is called below.
-    if (IsOpen() && (sizeChanged || mPopupFrame->HasOpenChanged()))
-      mPopupFrame->SetPopupPosition(this);
+    // is the new size too small? Make sure we handle scrollbars correctly
+    nsIBox* child = popupChild->GetChildBox();
 
-    // is the new size too small? Make sure we handle scrollbars correctly
-    nsIBox* child = mPopupFrame->GetChildBox();
-
-    nsRect bounds(mPopupFrame->GetRect());
+    nsRect bounds(popupChild->GetRect());
 
     nsCOMPtr<nsIScrollableFrame> scrollframe(do_QueryInterface(child));
     if (scrollframe &&
         scrollframe->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
       if (bounds.height < prefSize.height) {
         // layout the child
-        mPopupFrame->Layout(aState);
+        popupChild->Layout(aState);
 
         nsMargin scrollbars = scrollframe->GetActualScrollbarSizes();
         if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right)
         {
           bounds.width += scrollbars.left + scrollbars.right;
-          mPopupFrame->SetBounds(aState, bounds);
+          //printf("Width=%d\n",width);
+          popupChild->SetBounds(aState, bounds);
         }
       }
     }
-
+    
     // layout the child
-    mPopupFrame->Layout(aState);
-    mPopupFrame->AdjustView();
+    popupChild->Layout(aState);
+
+    // Only size the popups view if open.
+    if (mMenuOpen) {
+      nsIView* view = popupChild->GetView();
+      nsRect r(0, 0, bounds.width, bounds.height);
+      view->GetViewManager()->ResizeView(view, r);
+    }
+
   }
 
+  SyncLayout(aState);
+
   return rv;
 }
 
 #ifdef DEBUG_LAYOUT
 NS_IMETHODIMP
 nsMenuFrame::SetDebug(nsBoxLayoutState& aState, PRBool aDebug)
 {
   // see if our state matches the given debug state
   PRBool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
   PRBool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
 
   // if it doesn't then tell each child below us the new debug state
   if (debugChanged)
   {
       nsBoxFrame::SetDebug(aState, aDebug);
-      if (mPopupFrame)
-        SetDebug(aState, mPopupFrame, aDebug);
+      SetDebug(aState, mPopupFrames.FirstChild(), aDebug);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsMenuFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug)
 {
@@ -760,83 +1132,238 @@ nsMenuFrame::SetDebug(nsBoxLayoutState& 
 
         aList = aList->GetNextSibling();
       }
 
       return NS_OK;
 }
 #endif
 
+void
+nsMenuFrame::ConvertPosition(nsIContent* aPopupElt, nsString& aAnchor, nsString& aAlign)
+{
+  static nsIContent::AttrValuesArray strings[] =
+    {&nsGkAtoms::_empty, &nsGkAtoms::before_start, &nsGkAtoms::before_end,
+     &nsGkAtoms::after_start, &nsGkAtoms::after_end, &nsGkAtoms::start_before,
+     &nsGkAtoms::start_after, &nsGkAtoms::end_before, &nsGkAtoms::end_after,
+     &nsGkAtoms::overlap, nsnull};
+
+  switch (aPopupElt->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::position,
+                                     strings, eCaseMatters)) {
+    case nsIContent::ATTR_MISSING:
+    case 0:
+      return;
+    case 1:
+      aAnchor.AssignLiteral("topleft");
+      aAlign.AssignLiteral("bottomleft");
+      break;
+    case 2:
+      aAnchor.AssignLiteral("topright");
+      aAlign.AssignLiteral("bottomright");
+      break;
+    case 3:
+      aAnchor.AssignLiteral("bottomleft");
+      aAlign.AssignLiteral("topleft");
+      break;
+    case 4:
+      aAnchor.AssignLiteral("bottomright");
+      aAlign.AssignLiteral("topright");
+      break;
+    case 5:
+      aAnchor.AssignLiteral("topleft");
+      aAlign.AssignLiteral("topright");
+      break;
+    case 6:
+      aAnchor.AssignLiteral("bottomleft");
+      aAlign.AssignLiteral("bottomright");
+      break;
+    case 7:
+      aAnchor.AssignLiteral("topright");
+      aAlign.AssignLiteral("topleft");
+      break;
+    case 8:
+      aAnchor.AssignLiteral("bottomright");
+      aAlign.AssignLiteral("bottomleft");
+      break;
+    case 9:
+      aAnchor.AssignLiteral("topleft");
+      aAlign.AssignLiteral("topleft");
+      break;
+  }
+}
+
+void
+nsMenuFrame::RePositionPopup(nsBoxLayoutState& aState)
+{  
+  nsPresContext* presContext = aState.PresContext();
+
+  // Sync up the view.
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame;
+  if (mMenuOpen && menuPopup) {
+    nsIContent* menuPopupContent = menuPopup->GetContent();
+    nsAutoString popupAnchor, popupAlign;
+      
+    menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, popupAnchor);
+    menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, popupAlign);
+
+    ConvertPosition(menuPopupContent, popupAnchor, popupAlign);
+
+    PRBool onMenuBar = PR_TRUE;
+    if (mMenuParent)
+      mMenuParent->IsMenuBar(onMenuBar);
+
+    if (onMenuBar) {
+      if (popupAnchor.IsEmpty())
+          popupAnchor.AssignLiteral("bottomleft");
+      if (popupAlign.IsEmpty())
+          popupAlign.AssignLiteral("topleft");
+    }
+    else {
+      if (popupAnchor.IsEmpty())
+        popupAnchor.AssignLiteral("topright");
+      if (popupAlign.IsEmpty())
+        popupAlign.AssignLiteral("topleft");
+    }
+
+    menuPopup->SyncViewWithFrame(presContext, popupAnchor, popupAlign, this, -1, -1);
+  }
+}
+
+NS_IMETHODIMP
+nsMenuFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag)
+{
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  if (frame) {
+    nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame;
+    popup->ShortcutNavigation(aKeyEvent, aHandledFlag);
+  } 
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag)
+{
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  if (frame) {
+    nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame;
+    popup->KeyboardNavigation(aKeyCode, aHandledFlag);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuFrame::Escape(PRBool& aHandledFlag)
+{
+  if (mMenuParent) {
+    mMenuParent->ClearRecentlyRolledUp();
+  }
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  if (frame) {
+    nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame;
+    popup->Escape(aHandledFlag);
+  }
+
+  return NS_OK;
+}
+
+
 //
 // Enter
 //
 // Called when the user hits the <Enter>/<Return> keys or presses the
 // shortcut key. If this is a leaf item, the item's action will be executed.
+// If it is a submenu parent, open the submenu and select the first time.
 // In either case, do nothing if the item is disabled.
 //
-nsMenuFrame*
+NS_IMETHODIMP
 nsMenuFrame::Enter()
 {
   if (IsDisabled()) {
 #ifdef XP_WIN
     // behavior on Windows - close the popup chain
-    if (mMenuParent) {
-      nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-      if (pm)
-        pm->Rollup();
-    }
+    if (mMenuParent)
+      mMenuParent->DismissChain();
 #endif   // #ifdef XP_WIN
     // this menu item was disabled - exit
-    return nsnull;
+    return NS_OK;
   }
-
-  if (!IsOpen()) {
+    
+  if (!mMenuOpen) {
     // The enter key press applies to us.
     if (!IsMenu() && mMenuParent)
       Execute(0);          // Execute our event handler
-    else
-      return this;
+    else {
+      OpenMenu(PR_TRUE);
+      SelectFirstItem();
+    }
+
+    return NS_OK;
+  }
+
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  if (frame) {
+    nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame;
+    popup->Enter();
   }
 
-  return nsnull;
+  return NS_OK;
 }
 
-PRBool
-nsMenuFrame::IsOpen()
+NS_IMETHODIMP
+nsMenuFrame::SelectFirstItem()
 {
-  return mPopupFrame && mPopupFrame->IsOpen();
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  if (frame) {
+    nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame;
+    popup->SetCurrentMenuItem(popup->GetNextMenuItem(nsnull));
+  }
+
+  return NS_OK;
 }
 
 PRBool
 nsMenuFrame::IsMenu()
 {
   return mIsMenu;
 }
 
 nsresult
 nsMenuFrame::Notify(nsITimer* aTimer)
 {
   // Our timer has fired.
   if (aTimer == mOpenTimer.get()) {
-    mOpenTimer = nsnull;
-
-    if (!IsOpen() && mMenuParent) {
+    if (!mMenuOpen && mMenuParent) {
       // make sure we didn't open a context menu in the meantime
       // (i.e. the user right-clicked while hovering over a submenu).
-      nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-      if (pm) {
-        if ((!pm->HasContextMenu(nsnull) || mMenuParent->IsContextMenu()) &&
-            mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
+      // However, also make sure that we're not the context menu itself,
+      // to allow context submenus to open.
+      nsIMenuParent *ctxMenu = nsMenuFrame::GetContextMenu();
+      PRBool parentIsContextMenu = PR_FALSE;
+
+      if (ctxMenu)
+        mMenuParent->GetIsContextMenu(parentIsContextMenu);
+
+      if (ctxMenu == nsnull || parentIsContextMenu) {
+        if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
                                   nsGkAtoms::_true, eCaseMatters)) {
-          OpenMenu(PR_FALSE);
+          // We're still the active menu. Make sure all submenus/timers are closed
+          // before opening this one
+          mMenuParent->KillPendingTimers();
+          OpenMenu(PR_TRUE);
         }
       }
     }
+    mOpenTimer->Cancel();
+    mOpenTimer = nsnull;
   }
-
+  
+  mOpenTimer = nsnull;
   return NS_OK;
 }
 
 PRBool 
 nsMenuFrame::IsDisabled()
 {
   return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
                                nsGkAtoms::_true, eCaseMatters);
@@ -865,21 +1392,21 @@ nsMenuFrame::UpdateMenuType(nsPresContex
       mType = eMenuType_Normal;
       break;
   }
   UpdateMenuSpecialState(aPresContext);
 }
 
 /* update checked-ness for type="checkbox" and type="radio" */
 void
-nsMenuFrame::UpdateMenuSpecialState(nsPresContext* aPresContext)
-{
+nsMenuFrame::UpdateMenuSpecialState(nsPresContext* aPresContext) {
   PRBool newChecked =
     mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
                           nsGkAtoms::_true, eCaseMatters); 
+
   if (newChecked == mChecked) {
     /* checked state didn't change */
 
     if (mType != eMenuType_Radio)
       return; // only Radio possibly cares about other kinds of change
 
     if (!mChecked || mGroupName.IsEmpty())
       return;                   // no interesting change
@@ -906,37 +1433,53 @@ nsMenuFrame::UpdateMenuSpecialState(nsPr
    *
    * The only other reasonable behaviour would be to check for another selected
    * item in that group.  If found, unselect ourselves, otherwise we're the
    * selected item.  That, however, would be a lot more work, and I don't think
    * it's better at all.
    */
 
   /* walk siblings, looking for the other checked item with the same name */
+  nsIMenuFrame *sibMenu;
+  nsMenuType sibType;
+  nsAutoString sibGroup;
+  PRBool sibChecked;
+  
   // get the first sibling in this menu popup. This frame may be it, and if we're
   // being called at creation time, this frame isn't yet in the parent's child list.
   // All I'm saying is that this may fail, but it's most likely alright.
   nsIFrame* sib = GetParent()->GetFirstChild(nsnull);
+  if ( !sib )
+    return;
 
-  while (sib) {
-    if (sib != this && sib->GetType() == nsGkAtoms::menuFrame) {
-      nsMenuFrame* menu = NS_STATIC_CAST(nsMenuFrame*, sib);
-      if (menu->GetMenuType() == eMenuType_Radio &&
-          menu->IsChecked() &&
-          (menu->GetRadioGroupName() == mGroupName)) {      
-        /* uncheck the old item */
-        sib->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
-                                     PR_TRUE);
-        /* XXX in DEBUG, check to make sure that there aren't two checked items */
-        return;
-      }
+  // XXX - egcs 1.1.2 & gcc 2.95.x -Oy builds, where y > 1, 
+  // are known to break if we declare nsCOMPtrs inside this loop.  
+  // Moving the declaration out of the loop works around this problem.
+  // http://bugzilla.mozilla.org/show_bug.cgi?id=80988
+
+  do {
+    if (NS_FAILED(sib->QueryInterface(NS_GET_IID(nsIMenuFrame),
+                                      (void **)&sibMenu)))
+        continue;
+        
+    if (sibMenu != (nsIMenuFrame *)this &&        // correct way to check?
+        (sibMenu->GetMenuType(sibType), sibType == eMenuType_Radio) &&
+        (sibMenu->MenuIsChecked(sibChecked), sibChecked) &&
+        (sibMenu->GetRadioGroupName(sibGroup), sibGroup == mGroupName)) {
+      
+      /* uncheck the old item */
+      sib->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
+                                   PR_TRUE);
+
+      /* XXX in DEBUG, check to make sure that there aren't two checked items */
+      return;
     }
 
-    sib = sib->GetNextSibling();
-  } 
+  } while ((sib = sib->GetNextSibling()) != nsnull);
+
 }
 
 void 
 nsMenuFrame::BuildAcceleratorText()
 {
   nsAutoString accelText;
 
   if ((GetStateBits() & NS_STATE_ACCELTEXT_IS_DERIVED) == 0) {
@@ -1096,35 +1639,247 @@ nsMenuFrame::Execute(nsGUIEvent *aEvent)
         mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
                             PR_TRUE);
         ENSURE_TRUE(weakFrame.IsAlive());
       }
       else {
         mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, NS_LITERAL_STRING("true"),
                           PR_TRUE);
         ENSURE_TRUE(weakFrame.IsAlive());
+      }        
+      /* the AttributeChanged code will update all the internal state */
+    }
+  }
+
+  // Temporarily disable rollup events on this menu.  This is
+  // to suppress this menu getting removed in the case where
+  // the oncommand handler opens a dialog, etc.
+  if ( nsMenuDismissalListener::sInstance ) {
+    nsMenuDismissalListener::sInstance->EnableListener(PR_FALSE);
+  }
+
+  // Get our own content node and hold on to it to keep it from going away.
+  nsCOMPtr<nsIContent> content = mContent;
+
+  // Deselect ourselves.
+  SelectMenu(PR_FALSE);
+  ENSURE_TRUE(weakFrame.IsAlive());
+
+  // Now hide all of the open menus.
+  if (mMenuParent) {
+    mMenuParent->HideChain();
+
+    // Since menu was not dismissed via click outside menu
+    // we don't want to keep track of this rollup.
+    // Otherwise, we keep track so that the same click 
+    // won't both dismiss and then reopen a menu.
+    mMenuParent->ClearRecentlyRolledUp();
+  }
+
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+  // Create a trusted event if the triggering event was trusted, or if
+  // we're called from chrome code (since at least one of our caller
+  // passes in a null event).
+  nsXULCommandEvent event(aEvent ? NS_IS_TRUSTED_EVENT(aEvent) :
+                          nsContentUtils::IsCallerChrome(),
+                          NS_XUL_COMMAND, nsnull);
+  if (aEvent && (aEvent->eventStructType == NS_MOUSE_EVENT ||
+                 aEvent->eventStructType == NS_KEY_EVENT ||
+                 aEvent->eventStructType == NS_ACCESSIBLE_EVENT)) {
+
+    event.isShift = NS_STATIC_CAST(nsInputEvent *, aEvent)->isShift;
+    event.isControl = NS_STATIC_CAST(nsInputEvent *, aEvent)->isControl;
+    event.isAlt = NS_STATIC_CAST(nsInputEvent *, aEvent)->isAlt;
+    event.isMeta = NS_STATIC_CAST(nsInputEvent *, aEvent)->isMeta;
+  }
+
+  // The order of the nsIViewManager and nsIPresShell COM pointers is
+  // important below.  We want the pres shell to get released before the
+  // associated view manager on exit from this function.
+  // See bug 54233.
+  nsPresContext* presContext = PresContext();
+  nsCOMPtr<nsIViewManager> kungFuDeathGrip = presContext->GetViewManager();
+  nsCOMPtr<nsIPresShell> shell = presContext->GetPresShell();
+  if (shell) {
+    shell->HandleDOMEventWithTarget(mContent, &event, &status);
+    ENSURE_TRUE(weakFrame.IsAlive());
+  }
+
+  if (mMenuParent) {
+    mMenuParent->DismissChain();
+  }
+
+  // Re-enable rollup events on this menu.
+  if ( nsMenuDismissalListener::sInstance ) {
+    nsMenuDismissalListener::sInstance->EnableListener(PR_TRUE);
+  }
+}
+
+PRBool
+nsMenuFrame::OnCreate()
+{
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull,
+                     nsMouseEvent::eReal);
+
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  nsresult rv = NS_OK;
+
+  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
+  if (shell) {
+    if (child) {
+      rv = shell->HandleDOMEventWithTarget(child, &event, &status);
+    }
+    else {
+      rv = shell->HandleDOMEventWithTarget(mContent, &event, &status);
+    }
+  }
+
+  if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
+    return PR_FALSE;
+
+  // The menu is going to show, and the create handler has executed.
+  // We should now walk all of our menu item children, checking to see if any
+  // of them has a command attribute.  If so, then several attributes must
+  // potentially be updated.
+  if (child) {
+    nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(child->GetDocument()));
+
+    PRUint32 count = child->GetChildCount();
+    for (PRUint32 i = 0; i < count; i++) {
+      nsCOMPtr<nsIContent> grandChild = child->GetChildAt(i);
+
+      if (grandChild->Tag() == nsGkAtoms::menuitem) {
+        // See if we have a command attribute.
+        nsAutoString command;
+        grandChild->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
+        if (!command.IsEmpty()) {
+          // We do! Look it up in our document
+          nsCOMPtr<nsIDOMElement> commandElt;
+          domDoc->GetElementById(command, getter_AddRefs(commandElt));
+          nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt));
+
+          if ( commandContent ) {
+            nsAutoString commandAttr;
+            // The menu's disabled state needs to be updated to match the command.
+            if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandAttr))
+              grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandAttr, PR_TRUE);
+            else
+              grandChild->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, PR_TRUE);
+
+            // The menu's label, accesskey and checked states need to be updated
+            // to match the command. Note that unlike the disabled state if the
+            // command has *no* value, we assume the menu is supplying its own.
+            if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandAttr))
+              grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandAttr, PR_TRUE);
+
+            if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandAttr))
+              grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandAttr, PR_TRUE);
+
+            if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, commandAttr))
+              grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::label, commandAttr, PR_TRUE);
+          }
+        }
       }
     }
   }
 
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && mMenuParent)
-    pm->ExecuteMenu(mContent, aEvent);
+  return PR_TRUE;
+}
+
+PRBool
+nsMenuFrame::OnCreated()
+{
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull,
+                     nsMouseEvent::eReal);
+
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
+  if (shell) {
+    if (child) {
+      rv = shell->HandleDOMEventWithTarget(child, &event, &status);
+    }
+    else {
+      rv = shell->HandleDOMEventWithTarget(mContent, &event, &status);
+    }
+  }
+
+  if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
+    return PR_FALSE;
+  return PR_TRUE;
+}
+
+PRBool
+nsMenuFrame::OnDestroy()
+{
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull,
+                     nsMouseEvent::eReal);
+
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
+  if (shell) {
+    if (child) {
+      rv = shell->HandleDOMEventWithTarget(child, &event, &status);
+    }
+    else {
+      rv = shell->HandleDOMEventWithTarget(mContent, &event, &status);
+    }
+  }
+
+  if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
+    return PR_FALSE;
+  return PR_TRUE;
+}
+
+PRBool
+nsMenuFrame::OnDestroyed()
+{
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull,
+                     nsMouseEvent::eReal);
+
+  nsCOMPtr<nsIContent> child;
+  GetMenuChildrenElement(getter_AddRefs(child));
+  
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
+  if (shell) {
+    if (child) {
+      rv = shell->HandleDOMEventWithTarget(child, &event, &status);
+    }
+    else {
+      rv = shell->HandleDOMEventWithTarget(mContent, &event, &status);
+    }
+  }
+
+  if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
+    return PR_FALSE;
+  return PR_TRUE;
 }
 
 NS_IMETHODIMP
 nsMenuFrame::RemoveFrame(nsIAtom*        aListName,
                          nsIFrame*       aOldFrame)
 {
-  nsresult rv =  NS_OK;
+  nsresult  rv;
 
-  if (mPopupFrame == aOldFrame) {
+  if (mPopupFrames.ContainsFrame(aOldFrame)) {
     // Go ahead and remove this frame.
-    mPopupFrame->Destroy();
-    mPopupFrame = nsnull;
+    mPopupFrames.DestroyFrame(aOldFrame);
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN);
     rv = NS_OK;
   } else {
     rv = nsBoxFrame::RemoveFrame(aListName, aOldFrame);
   }
 
@@ -1133,21 +1888,23 @@ nsMenuFrame::RemoveFrame(nsIAtom*       
 
 NS_IMETHODIMP
 nsMenuFrame::InsertFrames(nsIAtom*        aListName,
                           nsIFrame*       aPrevFrame,
                           nsIFrame*       aFrameList)
 {
   nsresult          rv;
 
-  if (!mPopupFrame && aFrameList->GetType() == nsGkAtoms::menuPopupFrame) {
-    mPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, aFrameList);
+  nsIMenuParent *menuPar;
+  if (aFrameList && NS_SUCCEEDED(CallQueryInterface(aFrameList, &menuPar))) {
+    NS_ASSERTION(aFrameList->IsBoxFrame(),"Popup is not a box!!!");
+    mPopupFrames.InsertFrames(nsnull, nsnull, aFrameList);
 
 #ifdef DEBUG_LAYOUT
-    nsBoxLayoutState state(PresContext());
+    nsBoxLayoutState state(GetPresContext());
     SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG);
 #endif
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN);
     rv = NS_OK;
   } else {
     rv = nsBoxFrame::InsertFrames(aListName, aPrevFrame, aFrameList);  
@@ -1160,44 +1917,99 @@ NS_IMETHODIMP
 nsMenuFrame::AppendFrames(nsIAtom*        aListName,
                           nsIFrame*       aFrameList)
 {
   if (!aFrameList)
     return NS_OK;
 
   nsresult          rv;
 
-  if (!mPopupFrame && aFrameList->GetType() == nsGkAtoms::menuPopupFrame) {
-    mPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, aFrameList);
+  nsIMenuParent *menuPar;
+  if (aFrameList && NS_SUCCEEDED(CallQueryInterface(aFrameList, &menuPar))) {
+    NS_ASSERTION(aFrameList->IsBoxFrame(),"Popup is not a box!!!");
 
+    mPopupFrames.AppendFrames(nsnull, aFrameList);
 #ifdef DEBUG_LAYOUT
-    nsBoxLayoutState state(PresContext());
+    nsBoxLayoutState state(GetPresContext());
     SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG);
 #endif
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN);
     rv = NS_OK;
   } else {
     rv = nsBoxFrame::AppendFrames(aListName, aFrameList); 
   }
 
   return rv;
 }
 
+class nsASyncMenuGeneration : public nsIReflowCallback
+{
+public:
+  nsASyncMenuGeneration(nsIFrame* aFrame)
+    : mWeakFrame(aFrame)
+  {
+    nsIContent* content = aFrame ? aFrame->GetContent() : nsnull;
+    mDocument = content ? content->GetCurrentDoc() : nsnull;
+    if (mDocument) {
+      mDocument->BlockOnload();
+    }
+  }
+
+  virtual PRBool ReflowFinished() {
+    PRBool shouldFlush = PR_FALSE;
+    nsIFrame* frame = mWeakFrame.GetFrame();
+    if (frame) {
+      nsBoxLayoutState state(frame->PresContext());
+      if (!frame->IsCollapsed(state)) {
+        nsIMenuFrame* imenu = nsnull;
+        CallQueryInterface(frame, &imenu);
+        if (imenu) {
+          imenu->MarkAsGenerated();
+          shouldFlush = PR_TRUE;
+        }
+      }
+    }
+    if (mDocument) {
+      mDocument->UnblockOnload(PR_FALSE);
+    }
+    delete this;
+    return shouldFlush;
+  }
+
+  nsWeakFrame           mWeakFrame;
+  nsCOMPtr<nsIDocument> mDocument;
+};
+
 PRBool
 nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize)
 {
   if (!IsCollapsed(aState)) {
     nsSize tmpSize(-1, 0);
     nsIBox::AddCSSPrefSize(aState, this, tmpSize);
     if (tmpSize.width == -1 && GetFlex(aState) == 0) {
-      if (!mPopupFrame)
+      nsIFrame* frame = mPopupFrames.FirstChild();
+      if (!frame) {
+        nsCOMPtr<nsIContent> child;
+        GetMenuChildrenElement(getter_AddRefs(child));
+        if (child &&
+            !nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None,
+                                             nsGkAtoms::menugenerated)) {
+          nsIReflowCallback* cb = new nsASyncMenuGeneration(this);
+          if (cb) {
+            PresContext()->PresShell()->PostReflowCallback(cb);
+          }
+        }
         return PR_FALSE;
-      tmpSize = mPopupFrame->GetPrefSize(aState);
+      }
+
+      NS_ASSERTION(frame->IsBoxFrame(), "popupChild is not box!!");
+
+      tmpSize = frame->GetPrefSize(aState);
       aSize.width = tmpSize.width;
       return PR_TRUE;
     }
   }
 
   return PR_FALSE;
 }
 
@@ -1219,60 +2031,129 @@ nsMenuFrame::GetPrefSize(nsBoxLayoutStat
   }
 
   return size;
 }
 
 NS_IMETHODIMP
 nsMenuFrame::GetActiveChild(nsIDOMElement** aResult)
 {
-  if (!mPopupFrame)
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame;
+  if (!frame)
     return NS_ERROR_FAILURE;
 
-  nsMenuFrame* menuFrame = mPopupFrame->GetCurrentMenuItem();
+  nsIMenuFrame* menuFrame = menuPopup->GetCurrentMenuItem();
+  
   if (!menuFrame) {
     *aResult = nsnull;
   }
   else {
-    nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(menuFrame->GetContent()));
+    nsIFrame* f;
+    menuFrame->QueryInterface(NS_GET_IID(nsIFrame), (void**)&f);
+    nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(f->GetContent()));
     *aResult = elt;
     NS_IF_ADDREF(*aResult);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMenuFrame::SetActiveChild(nsIDOMElement* aChild)
 {
-  if (!mPopupFrame)
+  nsIFrame* frame = mPopupFrames.FirstChild();
+  nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame;
+  if (!frame)
     return NS_ERROR_FAILURE;
 
   if (!aChild) {
     // Remove the current selection
-    mPopupFrame->ChangeMenuItem(nsnull, PR_FALSE);
+    menuPopup->SetCurrentMenuItem(nsnull);
     return NS_OK;
   }
 
   nsCOMPtr<nsIContent> child(do_QueryInterface(aChild));
-
+  
   nsIFrame* kid = PresContext()->PresShell()->GetPrimaryFrameFor(child);
-  if (kid && kid->GetType() == nsGkAtoms::menuFrame)
-    mPopupFrame->ChangeMenuItem(NS_STATIC_CAST(nsMenuFrame *, kid), PR_FALSE);
+  if (!kid)
+    return NS_ERROR_FAILURE;
+  nsIMenuFrame *menuFrame;
+  nsresult rv = CallQueryInterface(kid, &menuFrame);
+  if (NS_FAILED(rv))
+    return rv;
+  menuPopup->SetCurrentMenuItem(menuFrame);
   return NS_OK;
 }
 
 nsIScrollableView* nsMenuFrame::GetScrollableView()
 {
-  if (!mPopupFrame)
+  if (!mPopupFrames.FirstChild())
     return nsnull;
 
-  nsIFrame* childFrame = mPopupFrame->GetFirstChild(nsnull);
-  if (childFrame)
-    return mPopupFrame->GetScrollableView(childFrame);
+  nsMenuPopupFrame* popup = (nsMenuPopupFrame*) mPopupFrames.FirstChild();
+  nsIFrame* childFrame = popup->GetFirstChild(nsnull);
+  if (childFrame) {
+    return popup->GetScrollableView(childFrame);
+  }
+  return nsnull;
+}
+
+/* Need to figure out what this does.
+NS_IMETHODIMP
+nsMenuFrame::GetBoxInfo(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsBoxInfo& aSize)
+{
+  nsresult rv = nsBoxFrame::GetBoxInfo(aPresContext, aReflowState, aSize);
+  nsCOMPtr<nsIDOMXULMenuListElement> menulist(do_QueryInterface(mContent));
+  if (menulist) {
+    nsCalculatedBoxInfo boxInfo(this);
+    boxInfo.prefSize.width = NS_UNCONSTRAINEDSIZE;
+    boxInfo.prefSize.height = NS_UNCONSTRAINEDSIZE;
+    boxInfo.flex = 0;
+    GetRedefinedMinPrefMax(aPresContext, this, boxInfo);
+    if (boxInfo.prefSize.width == NS_UNCONSTRAINEDSIZE &&
+        boxInfo.prefSize.height == NS_UNCONSTRAINEDSIZE &&
+        boxInfo.flex == 0) {
+      nsIFrame* frame = mPopupFrames.FirstChild();
+      if (!frame) {
+        MarkAsGenerated();
+        frame = mPopupFrames.FirstChild();
+      }
+      
+      nsCalculatedBoxInfo childInfo(frame);
+      frame->GetBoxInfo(aPresContext, aReflowState, childInfo);
+      GetRedefinedMinPrefMax(aPresContext, this, childInfo);
+      aSize.prefSize.width = childInfo.prefSize.width;
+    }
+
+    // This retrieval guarantess that the selectedItem will
+    // be set before we lay out.
+    nsCOMPtr<nsIDOMElement> element;
+    menulist->GetSelectedItem(getter_AddRefs(element));
+  }
+  return rv;
+}
+*/
+
+nsIMenuParent*
+nsMenuFrame::GetContextMenu()
+{
+  if (!nsMenuDismissalListener::sInstance)
+    return nsnull;
+
+  nsIMenuParent *menuParent =
+    nsMenuDismissalListener::sInstance->GetCurrentMenuParent();
+  if (!menuParent)
+    return nsnull;
+
+  PRBool isContextMenu;
+  menuParent->GetIsContextMenu(isContextMenu);
+  if (isContextMenu)
+    return menuParent;
+
   return nsnull;
 }
 
 // nsMenuTimerMediator implementation.
 NS_IMPL_ISUPPORTS1(nsMenuTimerMediator, nsITimerCallback)
 
 /**
  * Constructs a wrapper around an nsMenuFrame.
--- a/layout/xul/base/src/nsMenuFrame.h
+++ b/layout/xul/base/src/nsMenuFrame.h
@@ -44,44 +44,33 @@
 #define nsMenuFrame_h__
 
 #include "prtypes.h"
 #include "nsIAtom.h"
 #include "nsCOMPtr.h"
 
 #include "nsBoxFrame.h"
 #include "nsFrameList.h"
-#include "nsGkAtoms.h"
 #include "nsIMenuParent.h"
 #include "nsIMenuFrame.h"
-#include "nsXULPopupManager.h"
+#include "nsMenuDismissalListener.h"
 #include "nsITimer.h"
 #include "nsISupportsArray.h"
 #include "nsIDOMText.h"
 #include "nsIContent.h"
 #include "nsIScrollableViewProvider.h"
 
 nsIFrame* NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags);
 
 class nsMenuBarFrame;
+class nsMenuPopupFrame;
 class nsIScrollableView;
 
 #define NS_STATE_ACCELTEXT_IS_DERIVED  NS_STATE_BOX_CHILD_RESERVED
 
-// the type of menuitem
-enum nsMenuType {
-  // a normal menuitem where a command is carried out when activated
-  eMenuType_Normal = 0,
-  // a menuitem with a checkmark that toggles when activated
-  eMenuType_Checkbox = 1,
-  // a radio menuitem where only one of it and its siblings with the same
-  // name attribute can be checked at a time
-  eMenuType_Radio = 2
-};
-
 class nsMenuFrame;
 
 /**
  * nsMenuTimerMediator is a wrapper around an nsMenuFrame which can be safely
  * passed to timers. The class is reference counted unlike the underlying
  * nsMenuFrame, so that it will exist as long as the timer holds a reference
  * to it. The callback is delegated to the contained nsMenuFrame as long as
  * the contained nsMenuFrame has not been destroyed.
@@ -99,17 +88,17 @@ public:
 
 private:
 
   // Pointer to the wrapped frame.
   nsMenuFrame* mFrame;
 };
 
 /**
- * @note *** Methods marked with '@see comment above ***' may cause the frame to be
+ * @note *** Methods marked with '@see comment ***' may cause the frame to be
  *           deleted during the method call. Be careful whenever using those
  *           methods.
  */
 
 class nsMenuFrame : public nsBoxFrame, 
                     public nsIMenuFrame,
                     public nsIScrollableViewProvider
 {
@@ -128,161 +117,164 @@ public:
                   nsIFrame*        aPrevInFlow);
 
 #ifdef DEBUG_LAYOUT
   NS_IMETHOD SetDebug(nsBoxLayoutState& aState, PRBool aDebug);
 #endif
 
   NS_IMETHOD IsActive(PRBool& aResult) { aResult = PR_TRUE; return NS_OK; }
 
-  // The following methods are all overridden so that the menupopup
-  // can be stored in a separate list, so that it doesn't impact reflow of the
-  // actual menu item at all.
+  // The following four methods are all overridden so that the menu children
+  // can be stored in a separate list (so that they don't impact reflow of the
+  // actual menu item at all).
   virtual nsIFrame* GetFirstChild(nsIAtom* aListName) const;
   NS_IMETHOD SetInitialChildList(nsIAtom*        aListName,
                                  nsIFrame*       aChildList);
   virtual nsIAtom* GetAdditionalChildListName(PRInt32 aIndex) const;
-  virtual void Destroy(); // @see comment above ***
+  virtual void Destroy(); // @see comment ***
 
   // Overridden to prevent events from going to children of the menu.
   NS_IMETHOD BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                          const nsRect&           aDirtyRect,
                                          const nsDisplayListSet& aLists);
                                          
   NS_IMETHOD HandleEvent(nsPresContext* aPresContext, 
                          nsGUIEvent*     aEvent,
-                         nsEventStatus*  aEventStatus); // @see comment above ***
+                         nsEventStatus*  aEventStatus); // @see comment ***
 
   NS_IMETHOD  AppendFrames(nsIAtom*        aListName,
                            nsIFrame*       aFrameList);
 
   NS_IMETHOD  InsertFrames(nsIAtom*        aListName,
                            nsIFrame*       aPrevFrame,
                            nsIFrame*       aFrameList);
 
   NS_IMETHOD  RemoveFrame(nsIAtom*        aListName,
                           nsIFrame*       aOldFrame);
 
-  virtual nsIAtom* GetType() const { return nsGkAtoms::menuFrame; }
+  // nsIMenuFrame Interface
 
-  NS_IMETHOD SelectMenu(PRBool aActivateFlag); // @see comment above ***
+  NS_IMETHOD ActivateMenu(PRBool aActivateFlag); // @see comment ***
+  NS_IMETHOD SelectMenu(PRBool aActivateFlag); // @see comment ***
+  NS_IMETHOD OpenMenu(PRBool aActivateFlag); // @see comment ***
 
-  /**
-   * NOTE: OpenMenu will open the menu synchronously. Don't call this if a frame
-   *       is manipulated afterwards without checking to make sure it is still alive.
-   *       All current calls to OpenMenu do not adjust the frame.
-   */
-  void OpenMenu(PRBool aSelectFirstItem);
-  // CloseMenu closes the menu asynchronously
-  void CloseMenu(PRBool aDeselectMenu);
+  NS_IMETHOD MenuIsOpen(PRBool& aResult) { aResult = IsOpen(); return NS_OK; }
+  NS_IMETHOD MenuIsContainer(PRBool& aResult) { aResult = IsMenu(); return NS_OK; }
+  NS_IMETHOD MenuIsChecked(PRBool& aResult) { aResult = mChecked; return NS_OK; }
+  NS_IMETHOD MenuIsDisabled(PRBool& aResult) { aResult = IsDisabled(); return NS_OK; }
+  
+  NS_IMETHOD GetActiveChild(nsIDOMElement** aResult);
+  NS_IMETHOD SetActiveChild(nsIDOMElement* aChild); // @see comment ***
 
-  PRBool IsChecked() { return mChecked; }
+  NS_IMETHOD UngenerateMenu(); // @see comment ***
 
-  NS_IMETHOD GetActiveChild(nsIDOMElement** aResult);
-  NS_IMETHOD SetActiveChild(nsIDOMElement* aChild); // @see comment above ***
+  NS_IMETHOD SelectFirstItem(); // @see comment ***
 
-  // called when the Enter key is pressed while the menuitem is the current
-  // one in its parent popup. This will carry out the command attached to
-  // the menuitem.
-  nsMenuFrame* Enter();
+  NS_IMETHOD Escape(PRBool& aHandledFlag); // @see comment ***
+  NS_IMETHOD Enter(); // @see comment ***
+  NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag); // @see comment ***
+  NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag); // @see comment ***
 
   NS_IMETHOD SetParent(const nsIFrame* aParent);
 
   virtual nsIMenuParent *GetMenuParent() { return mMenuParent; }
-  const nsAString& GetRadioGroupName() { return mGroupName; }
-  nsMenuType GetMenuType() { return mType; }
-  nsMenuPopupFrame* GetPopup() { return mPopupFrame; }
+  virtual nsIFrame *GetMenuChild() { return mPopupFrames.FirstChild(); }
+  NS_IMETHOD GetRadioGroupName(nsString &aName) { aName = mGroupName; return NS_OK; }
+  NS_IMETHOD GetMenuType(nsMenuType &aType) { aType = mType; return NS_OK; }
+  NS_IMETHOD MarkAsGenerated();
 
   // nsIScrollableViewProvider methods
 
   virtual nsIScrollableView* GetScrollableView();
 
   // nsMenuFrame methods 
 
   nsresult DestroyPopupFrames(nsPresContext* aPresContext);
 
-  virtual PRBool IsOnMenuBar() { return mMenuParent && mMenuParent->IsMenuBar(); }
-  virtual PRBool IsOnActiveMenuBar() { return IsOnMenuBar() && mMenuParent->IsActive(); }
-  virtual PRBool IsOpen();
-  virtual PRBool IsMenu();
+  PRBool IsOpen() { return mMenuOpen; }
+  PRBool IsMenu();
   PRBool IsDisabled();
   PRBool IsGenerated();
-  void ToggleMenuState();
+  NS_IMETHOD ToggleMenuState(); // @see comment ***
 
-  // indiciate that the menu's popup has just been opened, so that the menu
-  // can update its open state. This method modifies the open attribute on
-  // the menu, so the frames could be gone after this call
-  void PopupOpened();
-  // indiciate that the menu's popup has just been closed, so that the menu
-  // can update its open state. The menu should be unhighlighted if
-  // aDeselectedMenu is true.
-  void PopupClosed(PRBool aDeselectMenu);
-
-  // returns true if this is a menu on another menu popup. A menu is a submenu
-  // if it has a parent popup or menupopup.
-  PRBool IsOnMenu() { return mMenuParent && mMenuParent->IsMenu(); }
   void SetIsMenu(PRBool aIsMenu) { mIsMenu = aIsMenu; }
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
       return MakeFrameName(NS_LITERAL_STRING("Menu"), aResult);
   }
 #endif
 
   static PRBool IsSizedToPopup(nsIContent* aContent, PRBool aRequireAlways);
 
+  static nsIMenuParent *GetContextMenu();
+
 protected:
   friend class nsMenuTimerMediator;
-  friend class nsASyncMenuInitialization;
+  
+  virtual void RePositionPopup(nsBoxLayoutState& aState);
+
+  void
+  ConvertPosition(nsIContent* aPopupElt, nsString& aAnchor, nsString& aAlign);
 
-  // set mMenuParent to the nearest enclosing menu bar or menupopup frame of
-  // aParent (or aParent itself). This is called when initializing the frame,
-  // so aParent should be the expected parent of this frame.
-  void InitMenuParent(nsIFrame* aParent);
+  friend class nsASyncMenuInitialization;
+  void UpdateMenuType(nsPresContext* aPresContext); // @see comment ***
+  void UpdateMenuSpecialState(nsPresContext* aPresContext); // @see comment ***
 
-  void UpdateMenuType(nsPresContext* aPresContext); // @see comment above ***
-  void UpdateMenuSpecialState(nsPresContext* aPresContext); // @see comment above ***
+  void OpenMenuInternal(PRBool aActivateFlag); // @see comment ***
+  void GetMenuChildrenElement(nsIContent** aResult);
 
   // Examines the key node and builds the accelerator.
   void BuildAcceleratorText();
 
   // Called to execute our command handler.
-  void Execute(nsGUIEvent *aEvent); // @see comment above ***
+  void Execute(nsGUIEvent *aEvent); // @see comment ***
+
+  // Called as a hook just before the menu gets opened.
+  PRBool OnCreate(); // @see comment ***
+
+  // Called as a hook just after the menu gets opened.
+  PRBool OnCreated(); // @see comment ***
+
+  // Called as a hook just before the menu goes away.
+  PRBool OnDestroy(); // @see comment ***
+
+  // Called as a hook just after the menu goes away.
+  PRBool OnDestroyed(); // @see comment ***
 
   NS_IMETHOD AttributeChanged(PRInt32 aNameSpaceID,
                               nsIAtom* aAttribute,
-                              PRInt32 aModType); // @see comment above ***
+                              PRInt32 aModType); // @see comment ***
   virtual ~nsMenuFrame();
 
   PRBool SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize);
 
 protected:
 #ifdef DEBUG_LAYOUT
   nsresult SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug);
 #endif
   NS_HIDDEN_(nsresult) Notify(nsITimer* aTimer);
 
+  nsFrameList mPopupFrames;
   PRPackedBool mIsMenu; // Whether or not we can even have children or not.
+  PRPackedBool mMenuOpen;
+  PRPackedBool mCreateHandlerSucceeded;  // Did the create handler succeed?
   PRPackedBool mChecked;              // are we checked?
   nsMenuType mType;
 
   nsIMenuParent* mMenuParent; // Our parent menu.
 
-  // the popup for this menu, owned
-  nsMenuPopupFrame* mPopupFrame;
-
-  nsSize mLastPref;
-
   // Reference to the mediator which wraps this frame.
   nsRefPtr<nsMenuTimerMediator> mTimerMediator;
 
   nsCOMPtr<nsITimer> mOpenTimer;
 
   nsString mGroupName;
+  nsSize mLastPref;
   
   //we load some display strings from platformKeys.properties only once
   static nsrefcnt gRefCnt; 
   static nsString *gShiftText;
   static nsString *gControlText;
   static nsString *gMetaText;
   static nsString *gAltText;
   static nsString *gModifierSeparator;
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/nsMenuListener.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 Communicator client 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):
+ *   Original Author: David W. Hyatt (hyatt@netscape.com)
+ *   Dean Tessman <dean_tessman@hotmail.com>
+ *   Mark Hammond <markh@ActiveState.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 "nsMenuListener.h"
+#include "nsMenuBarFrame.h"
+#include "nsIDOMKeyListener.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMNSUIEvent.h"
+#include "nsGUIEvent.h"
+
+// Drag & Drop, Clipboard
+#include "nsIServiceManager.h"
+#include "nsWidgetsCID.h"
+#include "nsCOMPtr.h"
+#include "nsIDOMKeyEvent.h"
+#include "nsIDOMNSEvent.h"
+#include "nsPresContext.h"
+#include "nsIContent.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMElement.h"
+
+#include "nsIEventStateManager.h"
+
+#include "nsISupportsArray.h"
+
+/*
+ * nsMenuListener implementation
+ */
+
+NS_IMPL_ADDREF(nsMenuListener)
+NS_IMPL_RELEASE(nsMenuListener)
+NS_IMPL_QUERY_INTERFACE3(nsMenuListener, nsIDOMKeyListener, nsIDOMFocusListener, nsIDOMMouseListener)
+
+
+////////////////////////////////////////////////////////////////////////
+
+nsMenuListener::nsMenuListener(nsIMenuParent* aMenuParent) 
+{
+  mMenuParent = aMenuParent;
+}
+
+////////////////////////////////////////////////////////////////////////
+nsMenuListener::~nsMenuListener() 
+{
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+nsresult
+nsMenuListener::KeyUp(nsIDOMEvent* aKeyEvent)
+{
+  aKeyEvent->StopPropagation();
+  aKeyEvent->PreventDefault();
+
+  return NS_ERROR_BASE; // I am consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult
+nsMenuListener::KeyDown(nsIDOMEvent* aKeyEvent)
+{
+  PRInt32 menuAccessKey = -1;
+  
+  // If the key just pressed is the access key (usually Alt),
+  // dismiss and unfocus the menu.
+
+  nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
+  if (menuAccessKey) {
+    PRUint32 theChar;
+    nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
+    keyEvent->GetKeyCode(&theChar);
+
+    if (theChar == (PRUint32)menuAccessKey) {
+      // No other modifiers can be down.
+      // Especially CTRL.  CTRL+ALT == AltGR, and
+      // we'll fuck up on non-US enhanced 102-key
+      // keyboards if we don't check this.
+      PRBool ctrl = PR_FALSE;
+      if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_CONTROL)
+        keyEvent->GetCtrlKey(&ctrl);
+      PRBool alt=PR_FALSE;
+      if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_ALT)
+        keyEvent->GetAltKey(&alt);
+      PRBool shift=PR_FALSE;
+      if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_SHIFT)
+        keyEvent->GetShiftKey(&shift);
+      PRBool meta=PR_FALSE;
+      if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_META)
+        keyEvent->GetMetaKey(&meta);
+      if (!(ctrl || alt || shift || meta)) {
+        // The access key just went down and no other
+        // modifiers are already down.
+        mMenuParent->DismissChain();
+      }
+    }
+  }
+
+  // Since a menu was open, eat the event to keep other event
+  // listeners from becoming confused.
+  aKeyEvent->StopPropagation();
+  aKeyEvent->PreventDefault();
+  return NS_ERROR_BASE; // I am consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult
+nsMenuListener::KeyPress(nsIDOMEvent* aKeyEvent)
+{
+  // Don't check prevent default flag -- menus always get first shot at key events.
+  // When a menu is open, the prevent default flag on a keypress is always set, so
+  // that no one else uses the key event.
+
+  //handlers shouldn't be triggered by non-trusted events.
+  nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
+  PRBool trustedEvent = PR_FALSE;
+
+  if (domNSEvent) {
+    domNSEvent->GetIsTrusted(&trustedEvent);
+  }
+
+  if (!trustedEvent)
+    return NS_OK;
+
+  nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
+  PRUint32 theChar;
+	keyEvent->GetKeyCode(&theChar);
+  PRBool handled = PR_FALSE;
+
+  if (theChar == NS_VK_LEFT ||
+      theChar == NS_VK_RIGHT ||
+      theChar == NS_VK_UP ||
+      theChar == NS_VK_DOWN ||
+      theChar == NS_VK_HOME ||
+      theChar == NS_VK_END) {
+    // The navigation keys were pressed. User is moving around within
+    // the menus.
+	  mMenuParent->KeyboardNavigation(theChar, handled);
+  }
+  else if (theChar == NS_VK_ESCAPE) {
+    // Close one level.
+    // Prevents us from getting destroyed by Escape(), we need to return to ourselves
+    NS_ADDREF_THIS();
+	  mMenuParent->Escape(handled);
+    NS_RELEASE_THIS();
+    if (!handled)
+      mMenuParent->DismissChain();
+  }
+  else if (theChar == NS_VK_TAB)
+    mMenuParent->DismissChain();
+  else if (theChar == NS_VK_ENTER ||
+           theChar == NS_VK_RETURN) {
+    // Open one level.
+    mMenuParent->Enter();
+  }
+#if !defined(XP_MAC) && !defined(XP_MACOSX)
+  else if (theChar == NS_VK_F10) {
+    // doesn't matter what modifier keys are down in Non-Mac platform
+    // if the menu bar is active and F10 is pressed - deactivate it
+    mMenuParent->DismissChain();
+  }
+#endif // !XP_MAC && !XP_MACOSX
+  else {
+    PRInt32 menuAccessKey = -1;
+    nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
+    if (menuAccessKey) {
+      // Do shortcut navigation.
+      // A letter was pressed. We want to see if a shortcut gets matched. If
+      // so, we'll know the menu got activated.
+      mMenuParent->ShortcutNavigation(keyEvent, handled);
+    }
+  }
+
+  aKeyEvent->StopPropagation();
+  aKeyEvent->PreventDefault();
+  return NS_ERROR_BASE; // I am consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+
+nsresult
+nsMenuListener::Focus(nsIDOMEvent* aEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult
+nsMenuListener::Blur(nsIDOMEvent* aEvent)
+{
+  
+  return NS_OK; // means I am NOT consuming event
+}
+  
+////////////////////////////////////////////////////////////////////////
+nsresult 
+nsMenuListener::MouseDown(nsIDOMEvent* aMouseEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult 
+nsMenuListener::MouseUp(nsIDOMEvent* aMouseEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+nsresult 
+nsMenuListener::MouseClick(nsIDOMEvent* aMouseEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult 
+nsMenuListener::MouseDblClick(nsIDOMEvent* aMouseEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult 
+nsMenuListener::MouseOver(nsIDOMEvent* aMouseEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult 
+nsMenuListener::MouseOut(nsIDOMEvent* aMouseEvent)
+{
+  return NS_OK; // means I am NOT consuming event
+}
+
+////////////////////////////////////////////////////////////////////////
+nsresult
+nsMenuListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+  return NS_OK;
+}
+
+
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/nsMenuListener.h
@@ -0,0 +1,81 @@
+/* -*- 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 Communicator client 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 of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * 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 nsMenuListener_h__
+#define nsMenuListener_h__
+
+#include "nsIDOMMouseMotionListener.h"
+#include "nsIDOMMouseListener.h"
+#include "nsIDOMKeyListener.h"
+#include "nsIDOMFocusListener.h"
+#include "nsIDOMEventTarget.h"
+
+class nsIMenuParent;
+class nsPresContext;
+
+/** editor Implementation of the DragListener interface
+ */
+class nsMenuListener : public nsIDOMKeyListener, public nsIDOMFocusListener, public nsIDOMMouseListener
+{
+public:
+  nsMenuListener(nsIMenuParent* aMenuParent);
+  
+  virtual ~nsMenuListener();
+   
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+  
+  NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent);
+  NS_IMETHOD KeyDown(nsIDOMEvent* aKeyEvent);
+  NS_IMETHOD KeyPress(nsIDOMEvent* aKeyEvent);
+  
+  NS_IMETHOD Focus(nsIDOMEvent* aEvent);
+  NS_IMETHOD Blur(nsIDOMEvent* aEvent);
+  
+  NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent);
+  
+  NS_DECL_ISUPPORTS
+
+protected:
+  nsIMenuParent* mMenuParent;    // The outermost object capturing events (either a menu bar or menupopup).
+};
+
+
+#endif
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -39,107 +39,135 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 
 #include "nsMenuPopupFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
+#include "nsContentUtils.h"
 #include "prtypes.h"
 #include "nsIAtom.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsCSSRendering.h"
 #include "nsINameSpaceManager.h"
 #include "nsIViewManager.h"
 #include "nsWidgetsCID.h"
 #include "nsMenuFrame.h"
-#include "nsMenuBarFrame.h"
-#include "nsPopupSetFrame.h"
-#include "nsEventDispatcher.h"
+#include "nsIPopupSetFrame.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMScreen.h"
 #include "nsIPresShell.h"
 #include "nsFrameManager.h"
 #include "nsIDocument.h"
 #include "nsIDeviceContext.h"
 #include "nsRect.h"
+#include "nsIDOMXULDocument.h"
 #include "nsILookAndFeel.h"
 #include "nsIComponentManager.h"
 #include "nsBoxLayoutState.h"
 #include "nsIScrollableView.h"
 #include "nsIScrollableFrame.h"
 #include "nsGUIEvent.h"
 #include "nsIRootBox.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
-#include "nsLayoutUtils.h"
 #include "nsCSSFrameConstructor.h"
-#include "nsIEventStateManager.h"
 #include "nsIBoxLayout.h"
 #include "nsIPopupBoxObject.h"
 #include "nsIReflowCallback.h"
 #ifdef XP_WIN
 #include "nsISound.h"
 #endif
 
 const PRInt32 kMaxZ = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32
 
-static nsPopupSetFrame*
+
+static nsIPopupSetFrame*
 GetPopupSetFrame(nsPresContext* aPresContext)
 {
   nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresContext->PresShell());
   if (!rootBox)
     return nsnull;
 
-  return rootBox->GetPopupSetFrame();
+  nsIFrame* popupSetFrame = rootBox->GetPopupSetFrame();
+  if (!popupSetFrame)
+    return nsnull;
+
+  nsIPopupSetFrame* popupSet = nsnull;
+  CallQueryInterface(popupSetFrame, &popupSet);
+  return popupSet;
 }
 
+
 // NS_NewMenuPopupFrame
 //
 // Wrapper for creating a new menu popup container
 //
 nsIFrame*
 NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsMenuPopupFrame (aPresShell, aContext);
 }
 
+NS_IMETHODIMP_(nsrefcnt) 
+nsMenuPopupFrame::AddRef(void)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(nsrefcnt) 
+nsMenuPopupFrame::Release(void)
+{
+    return NS_OK;
+}
+
+
+//
+// QueryInterface
+//
+NS_INTERFACE_MAP_BEGIN(nsMenuPopupFrame)
+  NS_INTERFACE_MAP_ENTRY(nsIMenuParent)
+NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
+
+
 //
 // nsMenuPopupFrame ctor
 //
 nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext)
   :nsBoxFrame(aShell, aContext),
   mCurrentMenu(nsnull),
-  mPopupAlignment(POPUPALIGNMENT_NONE),
-  mPopupAnchor(POPUPALIGNMENT_NONE),
-  mPopupType(ePopupTypePanel),
-  mIsOpen(PR_FALSE),
-  mIsOpenChanged(PR_FALSE),
-  mIsOpenPending(PR_FALSE),
-  mIsContextMenu(PR_FALSE),
-  mGeneratedChildren(PR_FALSE),
+  mTimerMenu(nsnull),
+  mCloseTimer(nsnull),
   mMenuCanOverlapOSBar(PR_FALSE),
   mShouldAutoPosition(PR_TRUE),
+  mShouldRollup(PR_TRUE),
   mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
   mInContentShell(PR_TRUE)
 {
+  SetIsContextMenu(PR_FALSE);   // we're not a context menu by default
 } // ctor
 
 
 NS_IMETHODIMP
 nsMenuPopupFrame::Init(nsIContent*      aContent,
                        nsIFrame*        aParent,
                        nsIFrame*        aPrevInFlow)
 {
   nsresult rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Set up a mediator which can be used for callbacks on this frame.
+  mTimerMediator = new nsMenuPopupTimerMediator(this);
+  if (NS_UNLIKELY(!mTimerMediator))
+    return NS_ERROR_OUT_OF_MEMORY;
+
   nsPresContext* presContext = PresContext();
 
   // lookup if we're allowed to overlap the OS bar (menubar/taskbar) from the
   // look&feel object
   PRBool tempBool;
   presContext->LookAndFeel()->
     GetMetric(nsILookAndFeel::eMetric_MenusCanOverlapOSBar, tempBool);
   mMenuCanOverlapOSBar = tempBool;
@@ -157,47 +185,36 @@ nsMenuPopupFrame::Init(nsIContent*      
   viewManager->RemoveChild(ourView);
 
   // Reinsert ourselves as the root view with a maximum z-index.
   nsIView* rootView;
   viewManager->GetRootView(rootView);
   viewManager->SetViewZIndex(ourView, PR_FALSE, kMaxZ);
   viewManager->InsertChild(rootView, ourView, nsnull, PR_TRUE);
 
-  // XXX Hack. The popup's view should float above all other views,
+  // XXX Hack. The menu's view should float above all other views,
   // so we use the nsIView::SetFloating() to tell the view manager
   // about that constraint.
   viewManager->SetViewFloating(ourView, PR_TRUE);
 
-  mPopupType = ePopupTypePanel;
-  nsIDocument* doc = aContent->GetOwnerDoc();
-  if (doc) {
-    PRInt32 namespaceID;
-    nsCOMPtr<nsIAtom> tag = doc->BindingManager()->ResolveTag(aContent, &namespaceID);
-    if (namespaceID == kNameSpaceID_XUL) {
-      if (tag == nsGkAtoms::menupopup || tag == nsGkAtoms::popup)
-        mPopupType = ePopupTypeMenu;
-      else if (tag == nsGkAtoms::tooltip)
-        mPopupType = ePopupTypeTooltip;
-    }
-  }
-
   nsCOMPtr<nsISupports> cont = PresContext()->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
   PRInt32 type = -1;
   if (dsti && NS_SUCCEEDED(dsti->GetItemType(&type)) &&
       type == nsIDocShellTreeItem::typeChrome)
     mInContentShell = PR_FALSE;
 
   // XXX make sure we are hidden (shouldn't this be done automatically?)
   viewManager->SetViewVisibility(ourView, nsViewVisibility_kHide);
   if (!ourView->HasWidget()) {
     CreateWidgetForView(ourView);
   }
 
+  MoveToAttributePosition();
+
   return rv;
 }
 
 nsresult
 nsMenuPopupFrame::CreateWidgetForView(nsIView* aView)
 {
   // Create a widget for ourselves.
   nsWidgetInitData widgetData;
@@ -220,355 +237,16 @@ nsMenuPopupFrame::CreateWidgetForView(ns
 #else
   static NS_DEFINE_IID(kCChildCID,  NS_CHILD_CID);
   aView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE);
 #endif
   aView->GetWidget()->SetWindowTranslucency(viewHasTransparentContent);
   return NS_OK;
 }
 
-// this class is used for dispatching popupshowing events asynchronously.
-class nsXULPopupShownEvent : public nsRunnable
-{
-public:
-  nsXULPopupShownEvent(nsIContent *aPopup, nsPresContext* aPresContext)
-    : mPopup(aPopup), mPresContext(aPresContext)
-  {
-  }
-
-  NS_IMETHOD Run()
-  {
-    nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull, nsMouseEvent::eReal);
-    return nsEventDispatcher::Dispatch(mPopup, mPresContext, &event);                 
-  }
-
-private:
-  nsCOMPtr<nsIContent> mPopup;
-  nsRefPtr<nsPresContext> mPresContext;
-};
-
-NS_IMETHODIMP
-nsMenuPopupFrame::SetInitialChildList(nsIAtom* aListName,
-                                      nsIFrame* aChildList)
-{
-  // unless the list is empty, indicate that children have been generated.
-  if (aChildList)
-    mGeneratedChildren = PR_TRUE;
-  return nsBoxFrame::SetInitialChildList(aListName, aChildList);
-}
-
-void
-nsMenuPopupFrame::AdjustView()
-{
-  if (mIsOpen) {
-    // if the popup has just opened, make sure the scrolled window is at 0,0
-    if (mIsOpenChanged) {
-      nsIBox* child = GetChildBox();
-      nsCOMPtr<nsIScrollableFrame> scrollframe(do_QueryInterface(child));
-      if (scrollframe)
-        scrollframe->ScrollTo(nsPoint(0,0));
-    }
-
-    nsIView* view = GetView();
-    nsIViewManager* viewManager = view->GetViewManager();
-    nsRect rect = GetRect();
-    rect.x = rect.y = 0;
-    viewManager->ResizeView(view, rect);
-    viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
-
-    nsPresContext* pc = PresContext();
-    nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0);
-
-    // fire popupshown event when the state has changed
-    if (mIsOpenChanged) {
-      mIsOpenChanged = PR_FALSE;
-      nsCOMPtr<nsIRunnable> event = new nsXULPopupShownEvent(GetContent(), pc);
-      NS_DispatchToCurrentThread(event);
-    }
-  }
-}
-
-void
-nsMenuPopupFrame::InitPositionFromAnchorAlign(const nsAString& aAnchor,
-                                              const nsAString& aAlign)
-{
-  if (aAnchor.EqualsLiteral("topleft"))
-    mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
-  else if (aAnchor.EqualsLiteral("topright"))
-    mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
-  else if (aAnchor.EqualsLiteral("bottomleft"))
-    mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
-  else if (aAnchor.EqualsLiteral("bottomright"))
-    mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
-  else
-    mPopupAnchor = POPUPALIGNMENT_NONE;
-
-  if (aAlign.EqualsLiteral("topleft"))
-    mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
-  else if (aAlign.EqualsLiteral("topright"))
-    mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
-  else if (aAlign.EqualsLiteral("bottomleft"))
-    mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
-  else if (aAlign.EqualsLiteral("bottomright"))
-    mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
-  else
-    mPopupAlignment = POPUPALIGNMENT_NONE;
-}
-
-void
-nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
-                                  const nsAString& aPosition,
-                                  PRInt32 aXPos, PRInt32 aYPos,
-                                  PRBool aAttributesOverride)
-{
-  mIsOpenPending = PR_TRUE;
-  mAnchorContent = aAnchorContent;
-  mXPos = aXPos;
-  mYPos = aYPos;
-
-  // if aAttributesOverride is true, then the popupanchor, popupalign and
-  // position attributes on the <popup> override those values passed in.
-  // If false, those attributes are only used if the values passed in are empty
-  if (aAnchorContent) {
-    nsAutoString anchor, align, position;
-    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, anchor);
-    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, align);
-    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::position, position);
-
-    if (aAttributesOverride) {
-      // if the attributes are set, clear the offset position. Otherwise,
-      // the offset is used to adjust the position from the anchor point
-      if (anchor.IsEmpty() && align.IsEmpty() && position.IsEmpty())
-        position.Assign(aPosition);
-      else
-        mXPos = mYPos = 0;
-    }
-    else if (!aPosition.IsEmpty()) {
-      position.Assign(aPosition);
-    }
-
-    if (position.EqualsLiteral("before_start")) {
-      mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
-      mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
-    }
-    else if (position.EqualsLiteral("before_end")) {
-      mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
-      mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
-    }
-    else if (position.EqualsLiteral("after_start")) {
-      mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
-      mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
-    }
-    else if (position.EqualsLiteral("after_end")) {
-      mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
-      mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
-    }
-    else if (position.EqualsLiteral("start_before")) {
-      mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
-      mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
-    }
-    else if (position.EqualsLiteral("start_after")) {
-      mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
-      mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
-    }
-    else if (position.EqualsLiteral("end_before")) {
-      mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
-      mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
-    }
-    else if (position.EqualsLiteral("end_after")) {
-      mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
-      mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
-    }
-    else if (position.EqualsLiteral("overlap")) {
-      mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
-      mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
-    }
-    else if (position.EqualsLiteral("after_pointer")) {
-      mPopupAnchor = POPUPALIGNMENT_NONE;
-      mPopupAlignment = POPUPALIGNMENT_NONE;
-      // XXXndeakin this is supposed to anchor vertically after, but with the
-      // horizontal position as the mouse pointer.
-      mYPos += 21;
-    }
-    else {
-      InitPositionFromAnchorAlign(anchor, align);
-    }
-  }
-
-  mScreenXPos = -1;
-  mScreenYPos = -1;
-
-  if (aAttributesOverride) {
-    // Use |left| and |top| dimension attributes to position the popup if
-    // present, as they may have been persisted. 
-    nsAutoString left, top;
-    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left);
-    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top);
-
-    PRInt32 err;
-    if (!left.IsEmpty()) {
-      PRInt32 x = left.ToInteger(&err);
-      if (NS_SUCCEEDED(err))
-        mScreenXPos = x;
-    }
-    if (!top.IsEmpty()) {
-      PRInt32 y = top.ToInteger(&err);
-      if (NS_SUCCEEDED(err))
-        mScreenYPos = y;
-    }
-  }
-}
-
-void
-nsMenuPopupFrame::InitializePopupAtScreen(PRInt32 aXPos, PRInt32 aYPos)
-{
-  mIsOpenPending = PR_TRUE;
-  mAnchorContent = nsnull;
-  mScreenXPos = aXPos;
-  mScreenYPos = aYPos;
-  mPopupAnchor = POPUPALIGNMENT_NONE;
-  mPopupAlignment = POPUPALIGNMENT_NONE;
-}
-
-void
-nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
-                                                 nsAString& aAnchor,
-                                                 nsAString& aAlign,
-                                                 PRInt32 aXPos, PRInt32 aYPos)
-{
-  mIsOpenPending = PR_TRUE;
-  mXPos = aXPos;
-  mYPos = aYPos;
-
-  // this popup opening function is provided for backwards compatibility
-  // only. It accepts either coordinates or an anchor and alignment value
-  // but doesn't use both together.
-  if (aXPos == -1 && aYPos == -1) {
-    mAnchorContent = aAnchorContent;
-    mScreenXPos = -1;
-    mScreenYPos = -1;
-    InitPositionFromAnchorAlign(aAnchor, aAlign);
-  }
-  else {
-    mAnchorContent = nsnull;
-    mPopupAnchor = POPUPALIGNMENT_NONE;
-    mPopupAlignment = POPUPALIGNMENT_NONE;
-    mScreenXPos = aXPos;
-    mScreenYPos = aYPos;
-  }
-}
-
-void PR_CALLBACK
-LazyGeneratePopupDone(nsIContent* aPopup, nsIFrame* aFrame, void* aArg)
-{
-  // be safe and check the frame type
-  if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
-    nsWeakFrame weakFrame(aFrame);
-    nsMenuPopupFrame* popupFrame = NS_STATIC_CAST(nsMenuPopupFrame*, aFrame);
-
-    popupFrame->SetGeneratedChildren();
-
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (pm && popupFrame->IsMenu()) {
-      nsCOMPtr<nsIContent> popup = aPopup;
-      PRBool selectFirstItem = (PRBool)aArg;
-      if (selectFirstItem) {
-        nsMenuFrame* next = pm->GetNextMenuItem(popupFrame, nsnull, PR_TRUE);
-        popupFrame->SetCurrentMenuItem(next);
-      }
-
-      pm->UpdateMenuItems(popup);
-    }
-
-    if (weakFrame.IsAlive()) {
-      popupFrame->PresContext()->PresShell()->
-        FrameNeedsReflow(popupFrame, nsIPresShell::eTreeChange,
-                         NS_FRAME_HAS_DIRTY_CHILDREN);
-    }
-  }
-}
-
-
-PRBool
-nsMenuPopupFrame::ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem)
-{
-  mIsContextMenu = aIsContextMenu;
-
-  PRBool hasChildren = PR_FALSE;
-
-  if (!mIsOpen) {
-    mIsOpen = PR_TRUE;
-    mIsOpenChanged = PR_TRUE;
-
-    nsIFrame* parent = GetParent();
-    if (parent && parent->GetType() == nsGkAtoms::menuFrame) {
-      nsWeakFrame weakFrame(this);
-      (NS_STATIC_CAST(nsMenuFrame*, parent))->PopupOpened();
-      if (!weakFrame.IsAlive())
-        return PR_FALSE;
-      PresContext()->RootPresContext()->NotifyAddedActivePopupToTop(this);
-    }
-
-    // the frames for the child menus have not been created yet, so tell the
-    // frame constructor to build them
-    if (mFrames.IsEmpty() && !mGeneratedChildren) {
-      PresContext()->PresShell()->FrameConstructor()->
-        AddLazyChildren(mContent, LazyGeneratePopupDone, (void *)aSelectFirstItem);
-    }
-    else {
-      hasChildren = PR_TRUE;
-      PresContext()->PresShell()->
-        FrameNeedsReflow(this, nsIPresShell::eTreeChange,
-                         NS_FRAME_HAS_DIRTY_CHILDREN);
-    }
-  }
-
-  mShouldAutoPosition = PR_TRUE;
-  return hasChildren;
-}
-
-void
-nsMenuPopupFrame::HidePopup(PRBool aDeselectMenu)
-{
-  if (mIsOpen) {
-    if (IsMenu())
-      SetCurrentMenuItem(nsnull);
-
-    mIncrementalString.Truncate();
-
-    mIsOpen = PR_FALSE;
-    mIsOpenChanged = PR_FALSE;
-    mCurrentMenu = nsnull; // make sure no current menu is set
- 
-    nsIView* view = GetView();
-    nsIViewManager* viewManager = view->GetViewManager();
-    viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
-    viewManager->ResizeView(view, nsRect(0, 0, 0, 0));
-
-    FireDOMEvent(NS_LITERAL_STRING("DOMMenuInactive"), mContent);
-  }
-
-  // XXX, bug 137033, In Windows, if mouse is outside the window when the menupopup closes, no
-  // mouse_enter/mouse_exit event will be fired to clear current hover state, we should clear it manually.
-  // This code may not the best solution, but we can leave it here until we find the better approach.
-  nsIEventStateManager *esm = PresContext()->EventStateManager();
-
-  PRInt32 state;
-  esm->GetContentState(mContent, state);
-
-  if (state & NS_EVENT_STATE_HOVER)
-    esm->SetContentState(nsnull, NS_EVENT_STATE_HOVER);
-
-  nsIFrame* parent = GetParent();
-  if (parent && parent->GetType() == nsGkAtoms::menuFrame) {
-    (NS_STATIC_CAST(nsMenuFrame*, parent))->PopupClosed(aDeselectMenu);
-    PresContext()->RootPresContext()->NotifyRemovedActivePopup(this);
-  }
-}
-
 void
 nsMenuPopupFrame::InvalidateInternal(const nsRect& aDamageRect,
                                      nscoord aX, nscoord aY, nsIFrame* aForChild,
                                      PRBool aImmediate)
 {
   InvalidateRoot(aDamageRect, aX, aY, aImmediate);
 }
 
@@ -601,20 +279,23 @@ nsMenuPopupFrame::GetViewOffset(nsIView*
 
 ///////////////////////////////////////////////////////////////////////////////
 // GetRootViewForPopup
 //   Retrieves the view for the popup widget that contains the given frame. 
 //   If the given frame is not contained by a popup widget, return the
 //   root view.  This is the root view of the pres context's
 //   viewmanager if aStopAtViewManagerRoot is true; otherwise it's the
 //   root view of the root viewmanager.
-nsIView*
+void
 nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame,
-                                      PRBool    aStopAtViewManagerRoot)
+                                      PRBool    aStopAtViewManagerRoot,
+                                      nsIView** aResult)
 {
+  *aResult = nsnull;
+
   nsIView* view = aStartFrame->GetClosestView();
   NS_ASSERTION(view, "frame must have a closest view!");
   if (view) {
     nsIView* rootView = nsnull;
     if (aStopAtViewManagerRoot) {
       view->GetViewManager()->GetRootView(rootView);
     }
     
@@ -622,106 +303,220 @@ nsMenuPopupFrame::GetRootViewForPopup(ns
       // Walk up the view hierarchy looking for a view whose widget has a 
       // window type of eWindowType_popup - in other words a popup window
       // widget. If we find one, this is the view we want. 
       nsIWidget* widget = view->GetWidget();
       if (widget) {
         nsWindowType wtype;
         widget->GetWindowType(wtype);
         if (wtype == eWindowType_popup) {
-          return view;
+          *aResult = view;
+          return;
         }
       }
 
       if (aStopAtViewManagerRoot && view == rootView) {
-        return view;
+        *aResult = view;
+        return;
       }
 
       nsIView* temp = view->GetParent();
       if (!temp) {
         // Otherwise, we've walked all the way up to the root view and not
         // found a view for a popup window widget. Just return the root view.
-        return view;
+        *aResult = view;
       }
       view = temp;
     }
   }
+}
 
-  return nsnull;
-}
+
+//
+// AdjustClientXYForNestedDocuments
+// 
+// almost certainly, the document where the mouse was clicked is not
+// the document that contains the popup, especially if we're viewing a page
+// with frames. Thus we need to make adjustments to the client coordinates to
+// take this into account and get them back into the relative coordinates of
+// this document.
+//
+void
+nsMenuPopupFrame::AdjustClientXYForNestedDocuments ( nsIDOMXULDocument* inPopupDoc, nsIPresShell* inPopupShell, 
+                                                         PRInt32 inClientX, PRInt32 inClientY, 
+                                                         PRInt32* outAdjX, PRInt32* outAdjY )
+{
+  if ( !inPopupDoc || !outAdjX || !outAdjY )
+    return;
+
+  // Find the widget associated with the popup's document
+  nsIWidget* popupDocumentWidget = nsnull;
+  nsIViewManager* viewManager = inPopupShell->GetViewManager();
+  if ( viewManager ) {  
+    nsIView* rootView;
+    viewManager->GetRootView(rootView);
+    if ( rootView )
+      popupDocumentWidget = rootView->GetNearestWidget(nsnull);
+  }
+  NS_ASSERTION(popupDocumentWidget, "ACK, BAD WIDGET");
+  
+  // Find the widget associated with the target's document.
+  // For tooltips, we check the document's tooltipNode (which is set by
+  // nsXULTooltipListener).  For regular popups, use popupNode (set by
+  // nsXULPopupListener).
+
+  nsCOMPtr<nsIDOMNode> targetNode;
+  if (mContent->Tag() == nsGkAtoms::tooltip)
+    inPopupDoc->TrustedGetTooltipNode(getter_AddRefs(targetNode));
+  else
+    inPopupDoc->TrustedGetPopupNode(getter_AddRefs(targetNode));
+
+  //NS_ASSERTION(targetNode, "no popup/tooltip node on document!");
+  nsCOMPtr<nsIContent> targetAsContent ( do_QueryInterface(targetNode) );
+  nsIWidget* targetDocumentWidget = nsnull;
+  if ( targetAsContent ) {
+    nsCOMPtr<nsIDocument> targetDocument = targetAsContent->GetDocument();
+    if (targetDocument) {
+      nsIPresShell *shell = targetDocument->GetPrimaryShell();
+      if ( shell ) {
+        // We might be inside a popup widget. If so, we need to use that widget and
+        // not the root view's widget.
+        nsIFrame* targetFrame = shell->GetPrimaryFrameFor(targetAsContent);
+        nsIView* parentView = nsnull;
+        if (targetFrame) {
+          GetRootViewForPopup(targetFrame, PR_TRUE, &parentView);
+          if (parentView) {
+            targetDocumentWidget = parentView->GetNearestWidget(nsnull);
+          }
+        }
+        if (!targetDocumentWidget) {
+          // We aren't inside a popup. This means we should use the root view's
+          // widget.
+          nsIViewManager* viewManagerTarget = shell->GetViewManager();
+          if ( viewManagerTarget ) {
+            nsIView* rootViewTarget;
+            viewManagerTarget->GetRootView(rootViewTarget);
+            if ( rootViewTarget ) {
+              targetDocumentWidget = rootViewTarget->GetNearestWidget(nsnull);
+            }
+          }
+        }
+      }
+    }
+  }
+  //NS_ASSERTION(targetDocumentWidget, "ACK, BAD TARGET");
+
+  // the offset we need is the difference between the upper left corner of the two widgets. Use
+  // screen coordinates to find the global offset between them.
+  nsRect popupDocTopLeft;
+  if ( popupDocumentWidget ) {
+    nsRect topLeftClient ( 0, 0, 10, 10 );
+    popupDocumentWidget->WidgetToScreen ( topLeftClient, popupDocTopLeft );
+  }
+  nsRect targetDocTopLeft;
+  if ( targetDocumentWidget ) {
+    nsRect topLeftClient ( 0, 0, 10, 10 );
+    targetDocumentWidget->WidgetToScreen ( topLeftClient, targetDocTopLeft );
+  }
+  nsPoint pixelOffset ( targetDocTopLeft.x - popupDocTopLeft.x, targetDocTopLeft.y - popupDocTopLeft.y );
+
+  nsPresContext* context = PresContext();
+  *outAdjX = nsPresContext::CSSPixelsToAppUnits(inClientX) +
+             context->DevPixelsToAppUnits(pixelOffset.x);
+  *outAdjY = nsPresContext::CSSPixelsToAppUnits(inClientY) +
+             context->DevPixelsToAppUnits(pixelOffset.y);
+  
+} // AdjustClientXYForNestedDocuments
+
 
 //
 // AdjustPositionForAnchorAlign
 // 
-// Uses the anchor and alignment to move the popup around and anchor it to its
-// parent. |outFlushWithTopBottom| will be TRUE if the popup is flush with
-// either the top or bottom edge of its parent, and FALSE if it is flush with
-// the left or right edge of the parent.
+// Uses the |popupanchor| and |popupalign| attributes on the popup to move the popup around and
+// anchor it to its parent. |outFlushWithTopBottom| will be TRUE if the popup is flush with either
+// the top or bottom edge of its parent, and FALSE if it is flush with the left or right edge of
+// the parent.
 // 
 void
-nsMenuPopupFrame::AdjustPositionForAnchorAlign(PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect,
-                                               PRBool* outFlushWithTopBottom)
+nsMenuPopupFrame::AdjustPositionForAnchorAlign ( PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect,
+                                                    const nsString& aPopupAnchor, const nsString& aPopupAlign,
+                                                    PRBool* outFlushWithTopBottom )
 {
-  PRInt8 popupAnchor(mPopupAnchor);
-  PRInt8 popupAlign(mPopupAlignment);
+  nsAutoString popupAnchor(aPopupAnchor);
+  nsAutoString popupAlign(aPopupAlign);
 
   if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
-    popupAnchor = -popupAnchor;
-    popupAlign = -popupAlign;
+    if (popupAnchor.EqualsLiteral("topright"))
+      popupAnchor.AssignLiteral("topleft");
+    else if (popupAnchor.EqualsLiteral("topleft"))
+      popupAnchor.AssignLiteral("topright");
+    else if (popupAnchor.EqualsLiteral("bottomleft"))
+      popupAnchor.AssignLiteral("bottomright");
+    else if (popupAnchor.EqualsLiteral("bottomright"))
+      popupAnchor.AssignLiteral("bottomleft");
+
+    if (popupAlign.EqualsLiteral("topright"))
+      popupAlign.AssignLiteral("topleft");
+    else if (popupAlign.EqualsLiteral("topleft"))
+      popupAlign.AssignLiteral("topright");
+    else if (popupAlign.EqualsLiteral("bottomleft"))
+      popupAlign.AssignLiteral("bottomright");
+    else if (popupAnchor.EqualsLiteral("bottomright"))
+      popupAlign.AssignLiteral("bottomleft");
   }
 
   // Adjust position for margins at the aligned corner
   nsMargin margin;
   GetStyleMargin()->GetMargin(margin);
-  if (popupAlign == POPUPALIGNMENT_TOPLEFT) {
+  if (popupAlign.EqualsLiteral("topleft")) {
     *ioXPos += margin.left;
     *ioYPos += margin.top;
-  } else if (popupAlign == POPUPALIGNMENT_TOPRIGHT) {
+  } else if (popupAlign.EqualsLiteral("topright")) {
     *ioXPos += margin.right;
     *ioYPos += margin.top;
-  } else if (popupAlign == POPUPALIGNMENT_BOTTOMLEFT) {
+  } else if (popupAlign.EqualsLiteral("bottomleft")) {
     *ioXPos += margin.left;
     *ioYPos += margin.bottom;
-  } else if (popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
+  } else if (popupAlign.EqualsLiteral("bottomright")) {
     *ioXPos += margin.right;
     *ioYPos += margin.bottom;
   }
   
-  if (popupAnchor == POPUPALIGNMENT_TOPRIGHT && popupAlign == POPUPALIGNMENT_TOPLEFT) {
+  if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("topleft")) {
     *ioXPos += inParentRect.width;
   }
-  else if (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT) {
+  else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topleft")) {
     *outFlushWithTopBottom = PR_TRUE;
   }
-  else if (popupAnchor == POPUPALIGNMENT_TOPRIGHT && popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
+  else if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("bottomright")) {
     *ioXPos -= (mRect.width - inParentRect.width);
     *ioYPos -= mRect.height;
     *outFlushWithTopBottom = PR_TRUE;
   }
-  else if (popupAnchor == POPUPALIGNMENT_BOTTOMRIGHT && popupAlign == POPUPALIGNMENT_BOTTOMLEFT) {
+  else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("bottomleft")) {
     *ioXPos += inParentRect.width;
     *ioYPos -= (mRect.height - inParentRect.height);
   }
-  else if (popupAnchor == POPUPALIGNMENT_BOTTOMRIGHT && popupAlign == POPUPALIGNMENT_TOPRIGHT) {
+  else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("topright")) {
     *ioXPos -= (mRect.width - inParentRect.width);
     *ioYPos += inParentRect.height;
     *outFlushWithTopBottom = PR_TRUE;
   }
-  else if (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPRIGHT) {
+  else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topright")) {
     *ioXPos -= mRect.width;
   }
-  else if (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_BOTTOMLEFT) {
+  else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("bottomleft")) {
     *ioYPos -= mRect.height;
     *outFlushWithTopBottom = PR_TRUE;
   }
-  else if (popupAnchor == POPUPALIGNMENT_BOTTOMLEFT && popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
+  else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("bottomright")) {
     *ioXPos -= mRect.width;
     *ioYPos -= (mRect.height - inParentRect.height);
   }
-  else if (popupAnchor == POPUPALIGNMENT_BOTTOMLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT) {
+  else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("topleft")) {
     *ioYPos += inParentRect.height;
     *outFlushWithTopBottom = PR_TRUE;
   }
   else
     NS_WARNING ( "Hmmm, looks like you've hit a anchor/align case we weren't setup for." );
 
 } // AdjustPositionForAnchorAlign
 
@@ -817,159 +612,132 @@ nsMenuPopupFrame::MovePopupToOtherSideOf
       PRInt32 shiftDistX = inScreenParentFrameRect.width + mRect.width;
       *ioXPos += shiftDistX;
       *ioScreenViewLocX += shiftDistX;
     }               
   }
 
 } // MovePopupToOtherSideOfParent
 
-// XXXndeakin this function will be reworked in bug 384062 such that positioning
-// of the popup is done only when the popup is first opened, so that the popup doesn't
-// move around when it is changed in some way.
-nsresult
-nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
+class nsASyncMenuActivation : public nsIReflowCallback
 {
+public:
+  nsASyncMenuActivation(nsIContent* aContent)
+    : mContent(aContent)
+  {
+  }
+
+  virtual PRBool ReflowFinished() {
+    PRBool shouldFlush = PR_FALSE;
+    if (mContent &&
+        !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
+                               nsGkAtoms::_true, eCaseMatters) &&
+        mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menutobedisplayed,
+                              nsGkAtoms::_true, eCaseMatters)) {
+      mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive,
+                        NS_LITERAL_STRING("true"), PR_TRUE);
+      shouldFlush = PR_TRUE;
+    }
+
+    delete this;
+    return shouldFlush;
+  }
+
+  nsCOMPtr<nsIContent> mContent;
+};
+
+nsresult 
+nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext,
+                                    const nsString& aPopupAnchor,
+                                    const nsString& aPopupAlign,
+                                    nsIFrame* aFrame, 
+                                    PRInt32 aXPos, PRInt32 aYPos)
+{
+  NS_ENSURE_ARG(aPresContext);
+  NS_ENSURE_ARG(aFrame);
+
   if (!mShouldAutoPosition && !mInContentShell) 
     return NS_OK;
 
-  PRBool sizedToPopup = PR_FALSE;
-
-  nsPresContext* presContext = PresContext();
-
-  // if the frame is not specified, use the anchor node passed to ShowPopup. If
-  // that wasn't specified either, use the root frame. Note that mAnchorContent
-  // might be a different document so its presshell must be used.
-  if (!aAnchorFrame) {
-    if (mAnchorContent) {
-      nsCOMPtr<nsIDocument> document = mAnchorContent->GetDocument();
-      nsIPresShell *shell = document->GetPrimaryShell();
-      if (!shell)
-        return NS_ERROR_FAILURE;
-      
-      aAnchorFrame = shell->GetPrimaryFrameFor(mAnchorContent);
-    }
-    else {
-      aAnchorFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
-    }
-
-    if (!aAnchorFrame)
-      return NS_OK;
-  }
-  else {
-    // the popup should be the same size as the anchor menu, for example, a menulist.
-    sizedToPopup = nsMenuFrame::IsSizedToPopup(aAnchorFrame->GetContent(), PR_FALSE);
-  }
-
   // |containingView|
   //   The view that contains the frame that is invoking this popup. This is 
   //   the canvas view inside the scrollport view. It can have negative bounds
   //   if the canvas is scrolled so that part is off screen.
   nsIView* containingView = nsnull;
   nsPoint offset;
   nsMargin margin;
-  containingView = aAnchorFrame->GetClosestView(&offset);
+  containingView = aFrame->GetClosestView(&offset);
   if (!containingView)
     return NS_OK;
 
+  // |view|
+  //   The root view for the popup window widget associated with this frame,
+  //   or, the view associated with this frame. 
+  nsIView* view = GetView();
+
   // |parentPos|
   //   The distance between the containingView and the root view. This provides
   //   a hint as to where to position the menu relative to the window. 
   nsPoint parentPos;
   GetViewOffset(containingView, parentPos);
 
   // |parentRect|
   //   The dimensions of the frame invoking the popup. 
-  nsRect parentRect = aAnchorFrame->GetRect();
+  nsRect parentRect = aFrame->GetRect();
 
   // get the document and the global script object
-  nsIPresShell *presShell = presContext->PresShell();
+  nsIPresShell *presShell = aPresContext->PresShell();
   nsIDocument *document = presShell->GetDocument();
 
+  PRBool sizedToPopup = (mContent->Tag() != nsGkAtoms::tooltip) &&
+    (nsMenuFrame::IsSizedToPopup(aFrame->GetContent(), PR_FALSE));
+
   // If we stick to our parent's width, set it here before we move the
   // window around, because moving is done with respect to the width...
   if (sizedToPopup) {
     mRect.width = parentRect.width;
   }
 
-  // Use containingView instead of parentView, to account for the scrollarrows
-  // that a parent menu might have.
-  nsPoint parentViewWidgetOffset;
-  nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset);
-  nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect;
-  parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect );
-
   // |xpos| and |ypos| hold the x and y positions of where the popup will be moved to,
   // in _twips_, in the coordinate system of the _parent view_.
-  PRBool readjustAboveBelow = PR_FALSE;
   PRInt32 xpos = 0, ypos = 0;
-  PRInt32 screenViewLocX, screenViewLocY;
-
-  if (mScreenXPos == -1 && mScreenYPos == -1) {
-    // if we are anchored to our parent, there are certain things we don't want to do
-    // when repositioning the view to fit on the screen, such as end up positioned over
-    // the parent. When doing this reposition, we want to move the popup to the side with
-    // the most room. The combination of anchor and alignment dictate if we readjust 
-    // above/below or to the left/right.
 
-    if (mAnchorContent) {
-      xpos = parentPos.x + offset.x;
-      ypos = parentPos.y + offset.y;
-
-      // move the popup according to the anchor and alignment. This will also tell us
-      // which axis the popup is flush against in case we have to move it around later.
-      AdjustPositionForAnchorAlign(&xpos, &ypos, parentRect, &readjustAboveBelow);
+  // if we are anchored to our parent, there are certain things we don't want to do
+  // when repositioning the view to fit on the screen, such as end up positioned over
+  // the parent. When doing this reposition, we want to move the popup to the side with
+  // the most room. The combination of anchor and alignment dictate if we readjst 
+  // above/below or to the left/right.
+  PRBool anchoredToParent = PR_FALSE;
+  PRBool readjustAboveBelow = PR_FALSE;
 
-      // the x and y position may be used to offset the popup after it has been anchored
-      xpos += presContext->DevPixelsToAppUnits(mXPos);
-      ypos += presContext->DevPixelsToAppUnits(mYPos);
-    }
-    else {
-      GetStyleMargin()->GetMargin(margin);
-      xpos = presContext->DevPixelsToAppUnits(mXPos) + margin.left;
-      ypos = presContext->DevPixelsToAppUnits(mYPos) + margin.top;
-    }
+  if ( aXPos != -1 || aYPos != -1 ) {
+  
+    // for this case, we've been handed a specific x/y location (in client coordinates) for
+    // the popup. However, we may be deeply nested in a frameset, etc and so the client coordinates
+    // need some adjusting. 
+    nsCOMPtr<nsIDOMXULDocument> xulDoc ( do_QueryInterface(document) );
+    AdjustClientXYForNestedDocuments ( xulDoc, presShell, aXPos, aYPos, &xpos, &ypos );
 
-    // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In
-    // order to determine the screen coordinates of where our view will end up, we
-    // need to find the x/y position of the parent view in screen coords. That is done
-    // by getting the widget associated with the parent view and determining the offset 
-    // based on converting (0,0) in its coordinate space to screen coords. We then
-    // offset that point by (|xpos|,|ypos|) to get the true screen coordinates of
-    // the view. *whew*
+    // Add in the top and left margins
+    GetStyleMargin()->GetMargin(margin);
 
-    // |parentView|
-    //   The root view for the window that contains the frame, for frames inside 
-    //   menupopups this is the first view inside the popup window widget, for 
-    //   frames inside a toplevel window, this is the root view of the toplevel
-    //   window.
-    nsIView* parentView = GetRootViewForPopup(aAnchorFrame, PR_FALSE);
-    if (!parentView)
-      return NS_OK;
+    xpos += margin.left;
+    ypos += margin.top;
+  } 
+  else {
+    anchoredToParent = PR_TRUE;
 
-    screenViewLocX = presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) +
-      (xpos - parentPos.x) + parentViewWidgetOffset.x;
-    screenViewLocY = presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) +
-      (ypos - parentPos.y) + parentViewWidgetOffset.y;
+    xpos = parentPos.x + offset.x;
+    ypos = parentPos.y + offset.y;
+    
+    // move the popup according to the anchor/alignment attributes. This will also tell us
+    // which axis the popup is flush against in case we have to move it around later.
+    AdjustPositionForAnchorAlign ( &xpos, &ypos, parentRect, aPopupAnchor, aPopupAlign, &readjustAboveBelow );    
   }
-  else {
-    // positioned on screen
-    GetStyleMargin()->GetMargin(margin);
-    screenViewLocX = nsPresContext::CSSPixelsToAppUnits(mScreenXPos) + margin.left;
-    screenViewLocY = nsPresContext::CSSPixelsToAppUnits(mScreenYPos) + margin.top;
-
-    xpos = screenViewLocX - presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) -
-           parentViewWidgetOffset.x - parentPos.x;
-    ypos = screenViewLocY - presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) -
-           parentViewWidgetOffset.y - parentPos.y;
-
-    // once the popup is positioned on screen, it doesn't need to be positioned again
-    mShouldAutoPosition = PR_FALSE;
-  }
-
+  
   // Compute info about the screen dimensions. Because of multiple monitor systems,
   // the left or top sides of the screen may be in negative space (main monitor is on the
   // right, etc). We need to be sure to do the right thing.
   nsPIDOMWindow *window = document->GetWindow();
   if (!window)
     return NS_OK;
 
   nsIDeviceContext* devContext = PresContext()->DeviceContext();
@@ -983,28 +751,59 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
 
   // keep 3px margin to the right and bottom of the screen for WinXP dropshadow
   rect.width  -= nsPresContext::CSSPixelsToAppUnits(3);
   rect.height -= nsPresContext::CSSPixelsToAppUnits(3);
 
   // for content shells, clip to the client area rather than the screen area
   if (mInContentShell) {
     nsRect rootScreenRect = presShell->GetRootFrame()->GetScreenRect();
-    rootScreenRect.ScaleRoundIn(presContext->AppUnitsPerDevPixel());
+    rootScreenRect.ScaleRoundIn(aPresContext->AppUnitsPerDevPixel());
     rect.IntersectRect(rect, rootScreenRect);
   }
 
   PRInt32 screenLeftTwips   = rect.x;
   PRInt32 screenTopTwips    = rect.y;
   PRInt32 screenWidthTwips  = rect.width;
   PRInt32 screenHeightTwips = rect.height;
   PRInt32 screenRightTwips  = rect.XMost();
   PRInt32 screenBottomTwips = rect.YMost();
+  
+  // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In
+  // order to determine the screen coordinates of where our view will end up, we
+  // need to find the x/y position of the parent view in screen coords. That is done
+  // by getting the widget associated with the parent view and determining the offset 
+  // based on converting (0,0) in its coordinate space to screen coords. We then
+  // offset that point by (|xpos|,|ypos|) to get the true screen coordinates of
+  // the view. *whew*
 
-  if (mPopupAnchor != POPUPALIGNMENT_NONE) {
+  // |parentView|
+  //   The root view for the window that contains the frame, for frames inside 
+  //   menupopups this is the first view inside the popup window widget, for 
+  //   frames inside a toplevel window, this is the root view of the toplevel
+  //   window.
+  nsIView* parentView = nsnull;
+  GetRootViewForPopup(aFrame, PR_FALSE, &parentView);
+  if (!parentView)
+    return NS_OK;
+
+  // Use containingView instead of parentView, to account for the scrollarrows
+  // that a parent menu might have.
+
+  nsPoint parentViewWidgetOffset;
+  nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset);
+  nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect;
+  parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect );
+  PRInt32 screenViewLocX = aPresContext->DevPixelsToAppUnits(screenParentWidgetRect.x) +
+    (xpos - parentPos.x) + parentViewWidgetOffset.x;
+  PRInt32 screenViewLocY = aPresContext->DevPixelsToAppUnits(screenParentWidgetRect.y) +
+    (ypos - parentPos.y) + parentViewWidgetOffset.y;
+
+  if ( anchoredToParent ) {
+    
     //
     // Popup is anchored to the parent, guarantee that it does not cover the parent. We
     // shouldn't do anything funky if it will already fit on the screen as is.
     //
 
     ///////////////////////////////////////////////////////////////////////////////
     //
     //                +------------------------+          
@@ -1021,21 +820,21 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
     //                +------------------------+|  ( = mRect )
     //                |           \/           ||
     //                +------------------------+
 
 
 
     // compute screen coordinates of parent frame so we can play with it. Make sure we put it
     // into twips as everything else is as well.
-    nsRect screenParentFrameRect (presContext->AppUnitsToDevPixels(offset.x), presContext->AppUnitsToDevPixels(offset.y),
+    nsRect screenParentFrameRect (aPresContext->AppUnitsToDevPixels(offset.x), aPresContext->AppUnitsToDevPixels(offset.y),
                                     parentRect.width, parentRect.height );
     parentViewWidget->WidgetToScreen ( screenParentFrameRect, screenParentFrameRect );
-    screenParentFrameRect.x = presContext->DevPixelsToAppUnits(screenParentFrameRect.x);
-    screenParentFrameRect.y = presContext->DevPixelsToAppUnits(screenParentFrameRect.y);
+    screenParentFrameRect.x = aPresContext->DevPixelsToAppUnits(screenParentFrameRect.x);
+    screenParentFrameRect.y = aPresContext->DevPixelsToAppUnits(screenParentFrameRect.y);
 
     // Don't let it spill off the screen to the top
     if (screenViewLocY < screenTopTwips) {
       PRInt32 moveDist = screenTopTwips - screenViewLocY;
       screenViewLocY = screenTopTwips;
       ypos += moveDist;
     }
     
@@ -1139,20 +938,20 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
     // XXXbz this is really silly.  We should be able to anchor popups to a
     // point or rect, not a frame, and we should be doing so with context
     // menus.  Furthermore, we should not be adding in pixels manually to
     // adjust position (in XULPopupListenerImpl::LaunchPopup comes to mind,
     // though ConvertPosition in the same file has some 21-px bogosity in the
     // y-direction too).
 
     // shrink to fit onto the screen, vertically and horizontally
-    if(mRect.width > screenWidthTwips)
-       mRect.width = screenWidthTwips;
+    if(mRect.width > screenWidthTwips) 
+        mRect.width = screenWidthTwips;    
     if(mRect.height > screenHeightTwips)
-       mRect.height = screenHeightTwips;
+        mRect.height = screenHeightTwips;   
 
     // First, adjust the X position.  For the X position, we slide the popup
     // left or right as needed to get it on screen.
     if ( screenViewLocX < screenLeftTwips ) {
       PRInt32 moveDistX = screenLeftTwips - screenViewLocX;
       xpos += moveDistX;
       screenViewLocX += moveDistX;
     }
@@ -1187,65 +986,209 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
           // We wouldn't fit.  Shorten before flipping.
           mRect.height = screenViewLocY - screenTopTwips;
         }
         ypos -= (mRect.height + margin.top + margin.bottom);
       }
     }
   }  
 
-  presContext->GetViewManager()->MoveViewTo(GetView(), xpos, ypos); 
+  aPresContext->GetViewManager()->MoveViewTo(view, xpos, ypos); 
 
   // Now that we've positioned the view, sync up the frame's origin.
   nsPoint frameOrigin = GetPosition();
   nsPoint offsetToView;
   GetOriginToViewOffset(offsetToView, nsnull);
   frameOrigin -= offsetToView;
   nsBoxFrame::SetPosition(frameOrigin);
 
   if (sizedToPopup) {
-    nsBoxLayoutState state(PresContext());
-    SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height));
+      nsBoxLayoutState state(PresContext());
+      SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height));
+  }
+    
+  if (!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
+                             nsGkAtoms::_true, eCaseMatters) &&
+      mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menutobedisplayed,
+                            nsGkAtoms::_true, eCaseMatters)) {
+    nsIReflowCallback* cb = new nsASyncMenuActivation(mContent);
+    NS_ENSURE_TRUE(cb, NS_ERROR_OUT_OF_MEMORY);
+    PresContext()->PresShell()->PostReflowCallback(cb);
   }
 
   return NS_OK;
 }
 
-/* virtual */ nsMenuFrame*
+static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
+                              nsIFrame** aResult)
+{
+  nsIContent* child = nsnull;
+  if (aChild)
+    child = aChild->GetContent();
+  aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
+}
+
+/* virtual */ nsIMenuFrame*
+nsMenuPopupFrame::GetNextMenuItem(nsIMenuFrame* aStart)
+{
+  nsIFrame* immediateParent = nsnull;
+  GetInsertionPoint(PresContext()->PresShell(), this, nsnull,
+                    &immediateParent);
+  if (!immediateParent)
+    immediateParent = this;
+
+  nsIFrame* currFrame = nsnull;
+  nsIFrame* startFrame = nsnull;
+  if (aStart) {
+    aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); 
+    if (currFrame) {
+      startFrame = currFrame;
+      currFrame = currFrame->GetNextSibling();
+    }
+  }
+  else 
+    currFrame = immediateParent->GetFirstChild(nsnull);
+  
+  while (currFrame) {
+    // See if it's a menu item.
+    if (IsValidItem(currFrame->GetContent())) {
+      nsIMenuFrame *menuFrame;
+      if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
+        menuFrame = nsnull;
+      return menuFrame;
+    }
+    currFrame = currFrame->GetNextSibling();
+  }
+
+  currFrame = immediateParent->GetFirstChild(nsnull);
+
+  // Still don't have anything. Try cycling from the beginning.
+  while (currFrame && currFrame != startFrame) {
+    // See if it's a menu item.
+    if (IsValidItem(currFrame->GetContent())) {
+      nsIMenuFrame *menuFrame;
+      if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
+        menuFrame = nsnull;
+      return menuFrame;
+    }
+
+    currFrame = currFrame->GetNextSibling();
+  }
+
+  // No luck. Just return our start value.
+  return aStart;
+}
+
+/* virtual */ nsIMenuFrame*
+nsMenuPopupFrame::GetPreviousMenuItem(nsIMenuFrame* aStart)
+{
+  nsIFrame* immediateParent = nsnull;
+  GetInsertionPoint(PresContext()->PresShell(), this, nsnull,
+                    &immediateParent);
+  if (!immediateParent)
+    immediateParent = this;
+
+  nsFrameList frames(immediateParent->GetFirstChild(nsnull));
+                              
+  nsIFrame* currFrame = nsnull;
+  nsIFrame* startFrame = nsnull;
+  if (aStart) {
+    aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame);
+    if (currFrame) {
+      startFrame = currFrame;
+      currFrame = frames.GetPrevSiblingFor(currFrame);
+    }
+  }
+  else currFrame = frames.LastChild();
+
+  while (currFrame) {
+    // See if it's a menu item.
+    if (IsValidItem(currFrame->GetContent())) {
+      nsIMenuFrame *menuFrame;
+      if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
+        menuFrame = nsnull;
+      return menuFrame;
+    }
+    currFrame = frames.GetPrevSiblingFor(currFrame);
+  }
+
+  currFrame = frames.LastChild();
+
+  // Still don't have anything. Try cycling from the end.
+  while (currFrame && currFrame != startFrame) {
+    // See if it's a menu item.
+    if (IsValidItem(currFrame->GetContent())) {
+      nsIMenuFrame *menuFrame;
+      if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
+        menuFrame = nsnull;
+      return menuFrame;
+    }
+
+    currFrame = frames.GetPrevSiblingFor(currFrame);
+  }
+
+  // No luck. Just return our start value.
+  return aStart;
+}
+
+/* virtual */ nsIMenuFrame*
 nsMenuPopupFrame::GetCurrentMenuItem()
 {
   return mCurrentMenu;
 }
 
-PRBool nsMenuPopupFrame::ConsumeOutsideClicks()
+NS_IMETHODIMP nsMenuPopupFrame::ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks)
 {
+  /*
+   * When this popup is open, should clicks outside of it be consumed?
+   * Return PR_TRUE if the popup hould rollup on an outside click, 
+   * but consume that click so it can't be used for anything else.
+   * Return PR_FALSE to allow clicks outside the popup to activate content 
+   * even when the popup is open.
+   * ---------------------------------------------------------------------
+   * 
+   * Should clicks outside of a popup be eaten?
+   *
+   *       Menus     Autocomplete     Comboboxes
+   * Mac     Eat           No              Eat
+   * Win     No            No              Eat     
+   * Unix    Eat           No              Eat
+   *
+   */
+
   // If the popup has explicitly set a consume mode, honor that.
-  if (mConsumeRollupEvent != nsIPopupBoxObject::ROLLUP_DEFAULT)
-    return (mConsumeRollupEvent == nsIPopupBoxObject::ROLLUP_CONSUME);
+  if (mConsumeRollupEvent != nsIPopupBoxObject::ROLLUP_DEFAULT) {
+    aConsumeOutsideClicks = mConsumeRollupEvent == nsIPopupBoxObject::ROLLUP_CONSUME;
+    return NS_OK;
+  }
+
+  aConsumeOutsideClicks = PR_TRUE;
 
   nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
+
   if (parentContent) {
-    nsINodeInfo *ni = parentContent->NodeInfo();
-    if (ni->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL))
-      return PR_TRUE;  // Consume outside clicks for combo boxes on all platforms
+    nsIAtom *parentTag = parentContent->Tag();
+    if (parentTag == nsGkAtoms::menulist)
+      return NS_OK;  // Consume outside clicks for combo boxes on all platforms
+    if (parentTag == nsGkAtoms::menu || parentTag == nsGkAtoms::popupset) {
 #if defined(XP_WIN) || defined(XP_OS2)
-    // Don't consume outside clicks for menus in Windows
-    if (ni->Equals(nsGkAtoms::menu, kNameSpaceID_XUL) ||
-       (ni->Equals(nsGkAtoms::popupset, kNameSpaceID_XUL)))
-      return PR_FALSE;
+      // Don't consume outside clicks for menus in Windows
+      aConsumeOutsideClicks = PR_FALSE;
 #endif
-    if (ni->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL)) {
+      return NS_OK;
+    }
+    if (parentTag == nsGkAtoms::textbox) {
       // Don't consume outside clicks for autocomplete widget
       if (parentContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                                      nsGkAtoms::autocomplete, eCaseMatters))
-        return PR_FALSE;
+        aConsumeOutsideClicks = PR_FALSE;
     }
   }
 
-  return PR_TRUE;
+  return NS_OK;
 }
 
 static nsIScrollableView* GetScrollableViewForFrame(nsIFrame* aFrame)
 {
   nsIScrollableFrame* sf;
   nsresult rv = CallQueryInterface(aFrame, &sf);
   if (NS_FAILED(rv))
     return nsnull;
@@ -1280,140 +1223,181 @@ nsIScrollableView* nsMenuPopupFrame::Get
     if ( scrollableView )
       return scrollableView;
     currFrame = currFrame->GetNextSibling();
   } while ( currFrame );
 
   return nsnull;
 }
 
-void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsMenuFrame* aMenuItem)
+void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsIMenuFrame* aMenuItem)
 {
-  if (aMenuItem) {
-    nsIFrame* childFrame = GetFirstChild(nsnull);
+  nsIFrame* frame=nsnull;
+  aMenuItem->QueryInterface(NS_GET_IID(nsIFrame), (void**)&frame);
+  if ( frame ) {
+    nsIFrame* childFrame=nsnull;
+    childFrame = GetFirstChild(nsnull);
     nsIScrollableView *scrollableView;
-    scrollableView = GetScrollableView(childFrame);
-    if (scrollableView) {
+    scrollableView=GetScrollableView(childFrame);
+    if ( scrollableView ) {
       nscoord scrollX, scrollY;
 
       nsRect viewRect = scrollableView->View()->GetBounds();
-      nsRect itemRect = aMenuItem->GetRect();
+      nsRect itemRect = frame->GetRect();
       scrollableView->GetScrollPosition(scrollX, scrollY);
   
       // scroll down
       if ( itemRect.y + itemRect.height > scrollY + viewRect.height )
         scrollableView->ScrollTo(scrollX, itemRect.y + itemRect.height - viewRect.height, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
       
       // scroll up
       else if ( itemRect.y < scrollY )
         scrollableView->ScrollTo(scrollX, itemRect.y, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
     }
   }
 }
 
-NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
+NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
 {
-  if (mCurrentMenu == aMenuItem)
+  // When a context menu is open, the current menu is locked, and no change
+  // to the menu is allowed.
+  nsIMenuParent *contextMenu = GetContextMenu();
+  if (contextMenu)
     return NS_OK;
 
-  if (mCurrentMenu) {
-    mCurrentMenu->SelectMenu(PR_FALSE);
-  }
-
-  if (aMenuItem) {
-    EnsureMenuItemIsVisible(aMenuItem);
-    aMenuItem->SelectMenu(PR_TRUE);
-  }
-
-  mCurrentMenu = aMenuItem;
-
-  return NS_OK;
-}
-
-void
-nsMenuPopupFrame::CurrentMenuIsBeingDestroyed()
-{
-  mCurrentMenu = nsnull;
-}
-
-NS_IMETHODIMP
-nsMenuPopupFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
-                                 PRBool aSelectFirstItem)
-{
   if (mCurrentMenu == aMenuItem)
     return NS_OK;
-
-  // When a context menu is open, the current menu is locked, and no change
-  // to the menu is allowed.
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (!mIsContextMenu && pm && pm->HasContextMenu(this))
-    return NS_OK;
-
+  
   // Unset the current child.
   if (mCurrentMenu) {
+    PRBool isOpen = PR_FALSE;
+    mCurrentMenu->MenuIsOpen(isOpen);
     mCurrentMenu->SelectMenu(PR_FALSE);
-    nsMenuPopupFrame* popup = mCurrentMenu->GetPopup();
-    if (popup) {
-      if (mCurrentMenu->IsOpen()) {
-        if (pm)
-          pm->HidePopupAfterDelay(popup);
-      }
+    // XXX bug 294183 sometimes mCurrentMenu gets cleared
+    if (mCurrentMenu && isOpen) {
+      // Don't close up immediately.
+      // Kick off a close timer.
+      KillCloseTimer(); // Ensure we don't have another stray waiting closure.
+      PRInt32 menuDelay = 300;   // ms
+
+      PresContext()->LookAndFeel()->
+        GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay);
+
+      // Kick off the timer.
+      mCloseTimer = do_CreateInstance("@mozilla.org/timer;1");
+      mCloseTimer->InitWithCallback(mTimerMediator, menuDelay, nsITimer::TYPE_ONE_SHOT);
+      mTimerMenu = mCurrentMenu;
     }
   }
 
   // Set the new child.
   if (aMenuItem) {
     EnsureMenuItemIsVisible(aMenuItem);
     aMenuItem->SelectMenu(PR_TRUE);
   }
 
   mCurrentMenu = aMenuItem;
 
   return NS_OK;
 }
 
-nsMenuFrame*
+
+NS_IMETHODIMP
+nsMenuPopupFrame::Escape(PRBool& aHandledFlag)
+{
+  mIncrementalString.Truncate();
+
+  // See if we have a context menu open.
+  nsIMenuParent* contextMenu = GetContextMenu();
+  if (contextMenu) {
+    // Get the context menu parent.
+    nsIFrame* childFrame;
+    CallQueryInterface(contextMenu, &childFrame);
+    nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(PresContext());
+    if (popupSetFrame)
+      // Destroy the popup.
+      popupSetFrame->DestroyPopup(childFrame, PR_FALSE);
+    aHandledFlag = PR_TRUE;
+    return NS_OK;
+  }
+
+  if (!mCurrentMenu)
+    return NS_OK;
+
+  // See if our menu is open.
+  PRBool isOpen = PR_FALSE;
+  mCurrentMenu->MenuIsOpen(isOpen);
+  if (isOpen) {
+    // Let the child menu handle this.
+    mCurrentMenu->Escape(aHandledFlag);
+    if (!aHandledFlag) {
+      // We should close up.
+      mCurrentMenu->OpenMenu(PR_FALSE);
+      // SelectMenu() so DOMMenuItemActive is fired for accessibility
+      mCurrentMenu->SelectMenu(PR_TRUE);
+      aHandledFlag = PR_TRUE;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsMenuPopupFrame::Enter()
 {
   mIncrementalString.Truncate();
 
+  // See if we have a context menu open.
+  nsIMenuParent *contextMenu = GetContextMenu();
+  if (contextMenu)
+    return contextMenu->Enter();
+
   // Give it to the child.
   if (mCurrentMenu)
-    return mCurrentMenu->Enter();
+    mCurrentMenu->Enter();
 
-  return nsnull;
+  return NS_OK;
 }
 
-nsMenuFrame*
+nsIMenuParent*
+nsMenuPopupFrame::GetContextMenu()
+{
+  if (mIsContextMenu)
+    return nsnull;
+
+  return nsMenuFrame::GetContextMenu();
+}
+
+nsIMenuFrame*
 nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction)
 {
   PRUint32 charCode, keyCode;
   aKeyEvent->GetCharCode(&charCode);
   aKeyEvent->GetKeyCode(&keyCode);
 
   doAction = PR_FALSE;
 
   // Enumerate over our list of frames.
   nsIFrame* immediateParent = nsnull;
-  PresContext()->PresShell()->
-    FrameConstructor()->GetInsertionPoint(this, nsnull, &immediateParent);
+  GetInsertionPoint(PresContext()->PresShell(), this, nsnull,
+                    &immediateParent);
   if (!immediateParent)
     immediateParent = this;
 
   PRUint32 matchCount = 0, matchShortcutCount = 0;
   PRBool foundActive = PR_FALSE;
   PRBool isShortcut;
-  nsMenuFrame* frameBefore = nsnull;
-  nsMenuFrame* frameAfter = nsnull;
-  nsMenuFrame* frameShortcut = nsnull;
+  nsIMenuFrame* frameBefore = nsnull;
+  nsIMenuFrame* frameAfter = nsnull;
+  nsIMenuFrame* frameShortcut = nsnull;
 
   nsIContent* parentContent = mContent->GetParent();
 
-  PRBool isMenu = parentContent &&
-                  !parentContent->NodeInfo()->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL);
+  PRBool isMenu =
+    parentContent && parentContent->Tag() != nsGkAtoms::menulist;
 
   static DOMTimeStamp lastKeyTime = 0;
   DOMTimeStamp keyTime;
   aKeyEvent->GetTimeStamp(&keyTime);
 
   if (charCode == 0) {
     if (keyCode == NS_VK_BACK) {
       if (!isMenu && !mIncrementalString.IsEmpty()) {
@@ -1456,76 +1440,75 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
   nsIFrame* currFrame;
   // NOTE: If you crashed here due to a bogus |immediateParent| it is 
   //       possible that the menu whose shortcut is being looked up has 
   //       been destroyed already.  One strategy would be to 
   //       setTimeout(<func>,0) as detailed in:
   //       <http://bugzilla.mozilla.org/show_bug.cgi?id=126675#c32>
   currFrame = immediateParent->GetFirstChild(nsnull);
 
-  PRInt32 menuAccessKey = -1;
-  nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
-
   // We start searching from first child. This process is divided into two parts
   //   -- before current and after current -- by the current item
   while (currFrame) {
     nsIContent* current = currFrame->GetContent();
     
     // See if it's a menu item.
-    if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, PR_TRUE)) {
+    if (IsValidItem(current)) {
       nsAutoString textKey;
-      if (menuAccessKey >= 0) {
-        // Get the shortcut attribute.
-        current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, textKey);
-      }
+      // Get the shortcut attribute.
+      current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, textKey);
       if (textKey.IsEmpty()) { // No shortcut, try first letter
         isShortcut = PR_FALSE;
         current->GetAttr(kNameSpaceID_None, nsGkAtoms::label, textKey);
         if (textKey.IsEmpty()) // No label, try another attribute (value)
           current->GetAttr(kNameSpaceID_None, nsGkAtoms::value, textKey);
       }
       else
         isShortcut = PR_TRUE;
 
       if (StringBeginsWith(textKey, incrementalString,
                            nsCaseInsensitiveStringComparator())) {
         // mIncrementalString is a prefix of textKey
-        if (currFrame->GetType() == nsGkAtoms::menuFrame) {
+        nsIMenuFrame* menuFrame;
+        if (NS_SUCCEEDED(CallQueryInterface(currFrame, &menuFrame))) {
           // There is one match
           matchCount++;
           if (isShortcut) {
             // There is one shortcut-key match
             matchShortcutCount++;
             // Record the matched item. If there is only one matched shortcut item, do it
-            frameShortcut = NS_STATIC_CAST(nsMenuFrame *, currFrame);
+            frameShortcut = menuFrame;
           }
           if (!foundActive) {
             // It's a first candidate item located before/on the current item
             if (!frameBefore)
-              frameBefore = NS_STATIC_CAST(nsMenuFrame *, currFrame);
+              frameBefore = menuFrame;
           }
           else {
             // It's a first candidate item located after the current item
             if (!frameAfter)
-              frameAfter = NS_STATIC_CAST(nsMenuFrame *, currFrame);
+              frameAfter = menuFrame;
           }
         }
         else
           return nsnull;
       }
 
       // Get the active status
       if (current->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
                                nsGkAtoms::_true, eCaseMatters)) {
         foundActive = PR_TRUE;
         if (stringLength > 1) {
           // If there is more than one char typed, the current item has highest priority,
           //   otherwise the item next to current has highest priority
-          if (currFrame == frameBefore)
+          nsIMenuFrame* menuFrame;
+          if (NS_SUCCEEDED(CallQueryInterface(currFrame, &menuFrame)) &&
+              menuFrame == frameBefore) {
             return frameBefore;
+          }
         }
       }
     }
     currFrame = currFrame->GetNextSibling();
   }
 
   doAction = (isMenu && (matchCount == 1 || matchShortcutCount == 1));
 
@@ -1548,94 +1531,492 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
     if (soundInterface)
       soundInterface->Beep();
   }
 #endif  // #ifdef XP_WIN
 
   return nsnull;
 }
 
+NS_IMETHODIMP 
+nsMenuPopupFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag)
+{
+  // See if we have a context menu open.
+  nsIMenuParent *contextMenu = GetContextMenu();
+  if (contextMenu)
+    return contextMenu->ShortcutNavigation(aKeyEvent, aHandledFlag);
+
+  if (mCurrentMenu) {
+    PRBool isOpen = PR_FALSE;
+    mCurrentMenu->MenuIsOpen(isOpen);
+    if (isOpen) {
+      // No way this applies to us. Give it to our child.
+      mCurrentMenu->ShortcutNavigation(aKeyEvent, aHandledFlag);
+      return NS_OK;
+    }
+  }
+
+  // This applies to us. Let's see if one of the shortcuts applies
+  PRBool action;
+  nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent, action);
+  if (result) {
+    // We got one!
+    nsIFrame* frame = nsnull;
+    CallQueryInterface(result, &frame);
+    nsWeakFrame weakResult(frame);
+    aHandledFlag = PR_TRUE;
+    SetCurrentMenuItem(result);
+    if (action && weakResult.IsAlive()) {
+      result->Enter();
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag)
+{
+  // See if we have a context menu open.
+  nsIMenuParent *contextMenu = GetContextMenu();
+  if (contextMenu)
+    return contextMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
+
+  nsNavigationDirection theDirection;
+  NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode);
+
+  mIncrementalString.Truncate();
+
+  // This method only gets called if we're open.
+  if (!mCurrentMenu && NS_DIRECTION_IS_INLINE(theDirection)) {
+    // We've been opened, but we haven't had anything selected.
+    // We can handle End, but our parent handles Start.
+    if (theDirection == eNavigationDirection_End) {
+      nsIMenuFrame* nextItem = GetNextMenuItem(nsnull);
+      if (nextItem) {
+        aHandledFlag = PR_TRUE;
+        SetCurrentMenuItem(nextItem);
+      }
+    }
+    return NS_OK;
+  }
+
+  PRBool isContainer = PR_FALSE;
+  PRBool isOpen = PR_FALSE;
+  PRBool isDisabled = PR_FALSE;
+  nsWeakFrame weakFrame(this);
+  if (mCurrentMenu) {
+    mCurrentMenu->MenuIsContainer(isContainer);
+    mCurrentMenu->MenuIsOpen(isOpen);
+    mCurrentMenu->MenuIsDisabled(isDisabled);
+
+    if (isOpen) {
+      // Give our child a shot.
+      mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    }
+    else if (theDirection == eNavigationDirection_End &&
+             isContainer && !isDisabled) {
+      // The menu is not yet open. Open it and select the first item.
+      aHandledFlag = PR_TRUE;
+      nsIFrame* frame = nsnull;
+      CallQueryInterface(mCurrentMenu, &frame);
+      nsWeakFrame weakCurrentFrame(frame);
+      mCurrentMenu->OpenMenu(PR_TRUE);
+      NS_ENSURE_TRUE(weakCurrentFrame.IsAlive(), NS_OK);
+      mCurrentMenu->SelectFirstItem();
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    }
+  }
+
+  if (aHandledFlag)
+    return NS_OK; // The child menu took it for us.
+
+  // For block progression, we can move in either direction
+  if (NS_DIRECTION_IS_BLOCK(theDirection) ||
+      NS_DIRECTION_IS_BLOCK_TO_EDGE(theDirection)) {
+
+    nsIMenuFrame* nextItem;
+    
+    if (theDirection == eNavigationDirection_Before)
+      nextItem = GetPreviousMenuItem(mCurrentMenu);
+    else if (theDirection == eNavigationDirection_After)
+      nextItem = GetNextMenuItem(mCurrentMenu);
+    else if (theDirection == eNavigationDirection_First)
+      nextItem = GetNextMenuItem(nsnull);
+    else
+      nextItem = GetPreviousMenuItem(nsnull);
+
+    if (nextItem) {
+      aHandledFlag = PR_TRUE;
+      SetCurrentMenuItem(nextItem);
+    }
+  }
+  else if (mCurrentMenu && isContainer && isOpen) {
+    if (theDirection == eNavigationDirection_Start) {
+      // Close it up.
+      mCurrentMenu->OpenMenu(PR_FALSE);
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+      // SelectMenu() so DOMMenuItemActive is fired for accessibility
+      mCurrentMenu->SelectMenu(PR_TRUE);
+      aHandledFlag = PR_TRUE;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::GetParentPopup(nsIMenuParent** aMenuParent)
+{
+  *aMenuParent = nsnull;
+  nsIFrame* parent = GetParent();
+  while (parent) {
+    nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(parent);
+    if (menuParent) {
+      *aMenuParent = menuParent.get();
+      NS_ADDREF(*aMenuParent);
+      return NS_OK;
+    }
+    parent = parent->GetParent();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::HideChain()
+{
+  if (!mShouldRollup)
+    return NS_OK;
+
+  // Stop capturing rollups
+  // (must do this during Hide, which happens before the menu item is executed,
+  // since this reinstates normal event handling.)
+  nsMenuDismissalListener::Shutdown();
+  
+  nsIFrame* frame = GetParent();
+  if (frame) {
+    nsWeakFrame weakMenu(frame);
+    nsIMenuFrame* menuFrame;
+    if (NS_FAILED(CallQueryInterface(frame, &menuFrame))) {
+      nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(PresContext());
+      if (popupSetFrame)
+        // Hide the popup.
+        popupSetFrame->HidePopup(this);
+      return NS_OK;
+    }
+   
+    menuFrame->ActivateMenu(PR_FALSE);
+    NS_ENSURE_TRUE(weakMenu.IsAlive(), NS_OK);
+    menuFrame->SelectMenu(PR_FALSE);
+    NS_ENSURE_TRUE(weakMenu.IsAlive(), NS_OK);
+
+    // Get the parent.
+    nsIMenuParent *menuParent = menuFrame->GetMenuParent();
+    if (menuParent)
+      menuParent->HideChain();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::DismissChain()
+{
+  if (!mShouldRollup)
+    return NS_OK;
+
+  // Stop capturing rollups
+  nsMenuDismissalListener::Shutdown();
+  
+  // Get our menu parent.
+  nsIFrame* frame = GetParent();
+  if (frame) {
+    nsIMenuFrame *menuFrame = nsnull;
+    CallQueryInterface(frame, &menuFrame);
+    if (!menuFrame) {
+      nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(PresContext());
+      if (popupSetFrame) {
+        // make sure the menu is not highlighted
+        if (mCurrentMenu) {
+          PRBool wasOpen;
+          mCurrentMenu->MenuIsOpen(wasOpen);
+          if (wasOpen)
+            mCurrentMenu->OpenMenu(PR_FALSE);
+          mCurrentMenu->SelectMenu(PR_FALSE);
+        }
+        // Destroy the popup.
+        popupSetFrame->DestroyPopup(this, PR_TRUE);
+      }
+      return NS_OK;
+    }
+  
+    menuFrame->OpenMenu(PR_FALSE);
+
+    // Get the parent.
+    nsIMenuParent* menuParent = menuFrame->GetMenuParent();
+    if (menuParent)
+      menuParent->DismissChain();
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsMenuPopupFrame::GetWidget(nsIWidget **aWidget)
 {
   // Get parent view
+  nsIView * view = nsnull;
   // XXX should this be passing PR_FALSE or PR_TRUE for aStopAtViewManagerRoot?
-  nsIView * view = GetRootViewForPopup(this, PR_FALSE);
+  nsMenuPopupFrame::GetRootViewForPopup(this, PR_FALSE, &view);
   if (!view)
     return NS_OK;
 
   *aWidget = view->GetWidget();
   NS_IF_ADDREF(*aWidget);
   return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 nsMenuPopupFrame::AttachedDismissalListener()
 {
   mConsumeRollupEvent = nsIPopupBoxObject::ROLLUP_DEFAULT;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::InstallKeyboardNavigator()
+{
+  if (mKeyboardNavigator)
+    return NS_OK;
+
+  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mContent->GetDocument());
+  
+  mTarget = target;
+  mKeyboardNavigator = new nsMenuListener(this);
+  NS_IF_ADDREF(mKeyboardNavigator);
+
+  target->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); 
+  target->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);  
+  target->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);   
+
+  nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_TRUE);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::RemoveKeyboardNavigator()
+{
+  if (!mKeyboardNavigator)
+    return NS_OK;
+
+  mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
+  mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
+  mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
+
+  NS_IF_RELEASE(mKeyboardNavigator);
+
+  nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_FALSE);
+
+  return NS_OK;
 }
 
 // helpers /////////////////////////////////////////////////////////////
 
+PRBool 
+nsMenuPopupFrame::IsValidItem(nsIContent* aContent)
+{
+  nsIAtom *tag = aContent->Tag();
+  
+  PRBool skipNavigatingDisabledMenuItem;
+  PresContext()->LookAndFeel()->
+    GetMetric(nsILookAndFeel::eMetric_SkipNavigatingDisabledMenuItem,
+              skipNavigatingDisabledMenuItem);
+
+  PRBool result = (tag == nsGkAtoms::menu ||
+                   tag == nsGkAtoms::menuitem ||
+                   tag == nsGkAtoms::option);
+  if (skipNavigatingDisabledMenuItem)
+    result = result && !IsDisabled(aContent);
+
+  return result;
+}
+
+PRBool 
+nsMenuPopupFrame::IsDisabled(nsIContent* aContent)
+{
+  return aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
+                               nsGkAtoms::_true, eCaseMatters);
+}
+
 NS_IMETHODIMP 
 nsMenuPopupFrame::AttributeChanged(PRInt32 aNameSpaceID,
                                    nsIAtom* aAttribute,
                                    PRInt32 aModType)
 
 {
   nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                              aModType);
   
   if (aAttribute == nsGkAtoms::left || aAttribute == nsGkAtoms::top)
     MoveToAttributePosition();
-
-  // accessibility needs this to ensure the frames get constructed when the
-  // menugenerated attribute is set, see bug 279703 comment 42 for discussion
-  if (aAttribute == nsGkAtoms::menugenerated &&
-      mFrames.IsEmpty() && !mGeneratedChildren) {
-    PresContext()->PresShell()->FrameConstructor()->
-      AddLazyChildren(mContent, LazyGeneratePopupDone, nsnull);
-  }
   
   return rv;
 }
 
-void
+void 
 nsMenuPopupFrame::MoveToAttributePosition()
 {
   // Move the widget around when the user sets the |left| and |top| attributes. 
   // Note that this is not the best way to move the widget, as it results in lots
   // of FE notifications and is likely to be slow as molasses. Use |moveTo| on
   // nsIPopupBoxObject if possible. 
   nsAutoString left, top;
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left);
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top);
-  PRInt32 err1, err2;
-  mScreenXPos = left.ToInteger(&err1);
-  mScreenYPos = top.ToInteger(&err2);
+  PRInt32 err1, err2, xPos, yPos;
+  xPos = left.ToInteger(&err1);
+  yPos = top.ToInteger(&err2);
 
-  if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2))
-    MoveToInternal(mScreenXPos, mScreenYPos);
+  if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2)) {
+    MoveToInternal(xPos, yPos);
+  }
+}
+
+
+NS_IMETHODIMP 
+nsMenuPopupFrame::HandleEvent(nsPresContext* aPresContext, 
+                              nsGUIEvent*     aEvent,
+                              nsEventStatus*  aEventStatus)
+{
+  return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 }
 
 void
 nsMenuPopupFrame::Destroy()
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->PopupDestroyed(this);
+  // Null out the pointer to this frame in the mediator wrapper so that it 
+  // doesn't try to interact with a deallocated frame.
+  mTimerMediator->ClearFrame();
+
+  if (mCloseTimer)
+    mCloseTimer->Cancel();
 
   nsPresContext* rootPresContext = PresContext()->RootPresContext();
   if (rootPresContext->ContainsActivePopup(this)) {
     rootPresContext->NotifyRemovedActivePopup(this);
   }
 
+  RemoveKeyboardNavigator();
   nsBoxFrame::Destroy();
 }
 
+// REVIEW: The override here was doing nothing at all since nsBoxFrame is our
+// parent class
+//
+// Notify
+//
+// The item selection timer has fired, we might have to readjust the 
+// selected item. There are two cases here that we are trying to deal with:
+//   (1) diagonal movement from a parent menu to a submenu passing briefly over
+//       other items, and
+//   (2) moving out from a submenu to a parent or grandparent menu.
+// In both cases, |mTimerMenu| is the menu item that might have an open submenu and
+// |mCurrentMenu| is the item the mouse is currently over, which could be none of them.
+//
+// case (1):
+//  As the mouse moves from the parent item of a submenu (we'll call 'A') diagonally into the
+//  submenu, it probably passes through one or more sibilings (B). As the mouse passes
+//  through B, it becomes the current menu item and the timer is set and mTimerMenu is 
+//  set to A. Before the timer fires, the mouse leaves the menu containing A and B and
+//  enters the submenus. Now when the timer fires, |mCurrentMenu| is null (!= |mTimerMenu|)
+//  so we have to see if anything in A's children is selected (recall that even disabled
+//  items are selected, the style just doesn't show it). If that is the case, we need to
+//  set the selected item back to A.
+//
+// case (2);
+//  Item A has an open submenu, and in it there is an item (B) which also has an open
+//  submenu (so there are 3 menus displayed right now). The mouse then leaves B's child
+//  submenu and selects an item that is a sibling of A, call it C. When the mouse enters C,
+//  the timer is set and |mTimerMenu| is A and |mCurrentMenu| is C. As the timer fires,
+//  the mouse is still within C. The correct behavior is to set the current item to C
+//  and close up the chain parented at A.
+//
+//  This brings up the question of is the logic of case (1) enough? The answer is no,
+//  and is discussed in bugzilla bug 29400. Case (1) asks if A's submenu has a selected
+//  child, and if it does, set the selected item to A. Because B has a submenu open, it
+//  is selected and as a result, A is set to be the selected item even though the mouse
+//  rests in C -- very wrong. 
+//
+//  The solution is to use the same idea, but instead of only checking one level, 
+//  drill all the way down to the deepest open submenu and check if it has something 
+//  selected. Since the mouse is in a grandparent, it won't, and we know that we can
+//  safely close up A and all its children.
+//
+// The code below melds the two cases together.
+//
+nsresult
+nsMenuPopupFrame::Notify(nsITimer* aTimer)
+{
+  // Our timer has fired. 
+  if (aTimer == mCloseTimer.get()) {
+    PRBool menuOpen = PR_FALSE;
+    mTimerMenu->MenuIsOpen(menuOpen);
+    if (menuOpen)
+      mTimerMenu->OpenMenu(PR_FALSE);
+
+    if (mCloseTimer)
+      mCloseTimer->Cancel();
+  }
+  
+  mCloseTimer = nsnull;
+  mTimerMenu = nsnull;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuPopupFrame::KillCloseTimer()
+{
+  if (mCloseTimer && mTimerMenu) {
+    PRBool menuOpen = PR_FALSE;
+    mTimerMenu->MenuIsOpen(menuOpen);
+    if (menuOpen) {
+      mTimerMenu->OpenMenu(PR_FALSE);
+    }
+    mCloseTimer->Cancel();
+    mCloseTimer = nsnull;
+    mTimerMenu = nsnull;
+  }
+  return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+nsMenuPopupFrame::KillPendingTimers ( )
+{
+  return KillCloseTimer();
+
+} // KillPendingTimers
+
+NS_IMETHODIMP
+nsMenuPopupFrame::CancelPendingTimers()
+{
+  if (mCloseTimer && mTimerMenu) {
+    if (mTimerMenu != mCurrentMenu) {
+      SetCurrentMenuItem(mTimerMenu);
+    }
+    mCloseTimer->Cancel();
+    mCloseTimer = nsnull;
+    mTimerMenu = nsnull;
+  }
+  return NS_OK;
+}
+
 void
 nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop)
 {
   // Set the 'left' and 'top' attributes
   nsAutoString left, top;
   left.AppendInt(aLeft);
   top.AppendInt(aTop);
 
@@ -1656,24 +2037,20 @@ void
 nsMenuPopupFrame::MoveToInternal(PRInt32 aLeft, PRInt32 aTop)
 {
   // just don't support moving popups for content shells
   if (mInContentShell)
     return;
 
   nsIView* view = GetView();
   NS_ASSERTION(view->GetParent(), "Must have parent!");
-
+  
   // Retrieve screen position of parent view
   nsIntPoint screenPos = view->GetParent()->GetScreenPosition();
 
-  nsPresContext* context = PresContext();
-  aLeft = context->AppUnitsToDevPixels(nsPresContext::CSSPixelsToAppUnits(aLeft));
-  aTop = context->AppUnitsToDevPixels(nsPresContext::CSSPixelsToAppUnits(aTop));
-
   // Move the widget
   // XXXbz don't we want screenPos to be the parent _widget_'s position, then?
   view->GetWidget()->Move(aLeft - screenPos.x, aTop - screenPos.y);
 }
 
 void 
 nsMenuPopupFrame::GetAutoPosition(PRBool* aShouldAutoPosition)
 {
@@ -1682,12 +2059,64 @@ nsMenuPopupFrame::GetAutoPosition(PRBool
 
 void
 nsMenuPopupFrame::SetAutoPosition(PRBool aShouldAutoPosition)
 {
   mShouldAutoPosition = aShouldAutoPosition;
 }
 
 void
+nsMenuPopupFrame::EnableRollup(PRBool aShouldRollup)
+{
+  if (!nsMenuDismissalListener::sInstance ||
+       nsMenuDismissalListener::sInstance->GetCurrentMenuParent() != this)
+    return;
+
+  if (aShouldRollup)
+    nsMenuDismissalListener::sInstance->Register();
+  else
+    nsMenuDismissalListener::sInstance->Unregister();
+}
+
+void
 nsMenuPopupFrame::SetConsumeRollupEvent(PRUint32 aConsumeMode)
 {
   mConsumeRollupEvent = aConsumeMode;
 }
+
+// nsMenuPopupTimerMediator implementation.
+NS_IMPL_ISUPPORTS1(nsMenuPopupTimerMediator, nsITimerCallback)
+
+/**
+ * Constructs a wrapper around an nsMenuFrame.
+ * @param aFrame nsMenuFrame to create a wrapper around.
+ */
+nsMenuPopupTimerMediator::nsMenuPopupTimerMediator(nsMenuPopupFrame *aFrame) :
+  mFrame(aFrame)
+{
+  NS_ASSERTION(mFrame, "Must have frame");
+}
+
+nsMenuPopupTimerMediator::~nsMenuPopupTimerMediator()
+{
+}
+
+/**
+ * Delegates the notification to the contained frame if it has not been destroyed.
+ * @param aTimer Timer which initiated the callback.
+ * @return NS_ERROR_FAILURE if the frame has been destroyed.
+ */
+NS_IMETHODIMP nsMenuPopupTimerMediator::Notify(nsITimer* aTimer)
+{
+  if (!mFrame)
+    return NS_ERROR_FAILURE;
+
+  return mFrame->Notify(aTimer);
+}
+
+/**
+ * Clear the pointer to the contained nsMenuFrame. This should be called
+ * when the contained nsMenuFrame is destroyed.
+ */
+void nsMenuPopupTimerMediator::ClearFrame()
+{
+  mFrame = nsnull;
+}
--- a/layout/xul/base/src/nsMenuPopupFrame.h
+++ b/layout/xul/base/src/nsMenuPopupFrame.h
@@ -42,269 +42,225 @@
 // nsMenuPopupFrame
 //
 
 #ifndef nsMenuPopupFrame_h__
 #define nsMenuPopupFrame_h__
 
 #include "prtypes.h"
 #include "nsIAtom.h"
-#include "nsGkAtoms.h"
 #include "nsCOMPtr.h"
-#include "nsMenuFrame.h"
 #include "nsIDOMEventTarget.h"
+#include "nsMenuListener.h"
 
 #include "nsBoxFrame.h"
 #include "nsIMenuParent.h"
 #include "nsIWidget.h"
 
 #include "nsITimer.h"
 
-enum nsPopupType {
-  ePopupTypePanel,
-  ePopupTypeMenu,
-  ePopupTypeTooltip
-};
-
-// values are selected so that the direction can be flipped just by
-// changing the sign
-#define POPUPALIGNMENT_NONE 0
-#define POPUPALIGNMENT_TOPLEFT 1
-#define POPUPALIGNMENT_TOPRIGHT -1
-#define POPUPALIGNMENT_BOTTOMLEFT 2
-#define POPUPALIGNMENT_BOTTOMRIGHT -2
-
 #define INC_TYP_INTERVAL  1000  // 1s. If the interval between two keypresses is shorter than this, 
                                 //   treat as a continue typing
 // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose:
 //  nsMenuPopupFrame.h, nsListControlFrame.cpp, listbox.xml, tree.xml
 //  need to find a good place to put them together.
 //  if someone changes one, please also change the other.
 
 nsIFrame* NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 class nsIViewManager;
 class nsIView;
 class nsIMenuParent;
+class nsIMenuFrame;
+class nsIDOMXULDocument;
+
 class nsMenuPopupFrame;
 
+/**
+ * nsMenuPopupTimerMediator is a wrapper around an nsMenuPopupFrame which can be safely
+ * passed to timers. The class is reference counted unlike the underlying
+ * nsMenuPopupFrame, so that it will exist as long as the timer holds a reference
+ * to it. The callback is delegated to the contained nsMenuPopupFrame as long as
+ * the contained nsMenuPopupFrame has not been destroyed.
+ */
+class nsMenuPopupTimerMediator : public nsITimerCallback
+{
+public:
+  nsMenuPopupTimerMediator(nsMenuPopupFrame* aFrame);
+  ~nsMenuPopupTimerMediator();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITIMERCALLBACK
+
+  void ClearFrame();
+
+private:
+
+  // Pointer to the wrapped frame.
+  nsMenuPopupFrame* mFrame;
+};
+
 class nsMenuPopupFrame : public nsBoxFrame, public nsIMenuParent
 {
 public:
   nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext);
 
-  // nsIMenuParentInterface
-  virtual nsMenuFrame* GetCurrentMenuItem();
-  NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem);
-  virtual void CurrentMenuIsBeingDestroyed();
-  NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, PRBool aSelectFirstItem);
+  NS_DECL_ISUPPORTS
 
-  // as popups are opened asynchronously, the popup pending state is used to
-  // prevent multiple requests from attempting to open the same popup twice
-  PRBool IsOpenPending() { return mIsOpenPending; }
-  void ClearOpenPending() { mIsOpenPending = PR_FALSE; }
-
-  NS_IMETHOD SetActive(PRBool aActiveFlag) { return NS_OK; } // We don't care.
-  virtual PRBool IsActive() { return PR_FALSE; }
-  virtual PRBool IsMenuBar() { return PR_FALSE; }
 
-  /*
-   * When this popup is open, should clicks outside of it be consumed?
-   * Return PR_TRUE if the popup should rollup on an outside click, 
-   * but consume that click so it can't be used for anything else.
-   * Return PR_FALSE to allow clicks outside the popup to activate content 
-   * even when the popup is open.
-   * ---------------------------------------------------------------------
-   * 
-   * Should clicks outside of a popup be eaten?
-   *
-   *       Menus     Autocomplete     Comboboxes
-   * Mac     Eat           No              Eat
-   * Win     No            No              Eat     
-   * Unix    Eat           No              Eat
-   *
-   */
-  PRBool ConsumeOutsideClicks();
+  // nsIMenuParentInterface
+  virtual nsIMenuFrame* GetCurrentMenuItem();
+  NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem);
+  virtual nsIMenuFrame* GetNextMenuItem(nsIMenuFrame* aStart);
+  virtual nsIMenuFrame* GetPreviousMenuItem(nsIMenuFrame* aStart);
+  NS_IMETHOD SetActive(PRBool aActiveFlag) { return NS_OK; } // We don't care.
+  NS_IMETHOD GetIsActive(PRBool& isActive) { isActive = PR_FALSE; return NS_OK; }
+  NS_IMETHOD IsMenuBar(PRBool& isMenuBar) { isMenuBar = PR_FALSE; return NS_OK; }
+  NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks);
+  NS_IMETHOD ClearRecentlyRolledUp() {return NS_OK;}
+  NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp) {*aJustRolledUp = PR_FALSE; return NS_OK;}
+  NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) { mIsContextMenu = aIsContextMenu; return NS_OK; }
+  NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) { aIsContextMenu = mIsContextMenu; return NS_OK; }
+  
+  NS_IMETHOD GetParentPopup(nsIMenuParent** aResult);
 
-  virtual PRBool IsContextMenu() { return mIsContextMenu; }
+  // Closes up the chain of open cascaded menus.
+  NS_IMETHOD DismissChain();
+
+  // Hides the chain of cascaded menus without closing them up.
+  NS_IMETHOD HideChain();
 
-  virtual PRBool MenuClosed() { return PR_TRUE; }
+  NS_IMETHOD KillPendingTimers();
+  NS_IMETHOD CancelPendingTimers();
+
+  NS_IMETHOD InstallKeyboardNavigator();
+  NS_IMETHOD RemoveKeyboardNavigator();
 
   NS_IMETHOD GetWidget(nsIWidget **aWidget);
 
   // The dismissal listener gets created and attached to the window.
-  void AttachedDismissalListener();
+  NS_IMETHOD AttachedDismissalListener();
 
   // Overridden methods
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
   NS_IMETHOD AttributeChanged(PRInt32 aNameSpaceID,
                               nsIAtom* aAttribute,
                               PRInt32 aModType);
 
+  NS_IMETHOD HandleEvent(nsPresContext* aPresContext, 
+                         nsGUIEvent*     aEvent,
+                         nsEventStatus*  aEventStatus);
+
   virtual void Destroy();
 
   virtual void InvalidateInternal(const nsRect& aDamageRect,
                                   nscoord aX, nscoord aY, nsIFrame* aForChild,
                                   PRBool aImmediate);
 
   virtual nsresult CreateWidgetForView(nsIView* aView);
 
-  NS_IMETHOD SetInitialChildList(nsIAtom*        aListName,
-                                 nsIFrame*       aChildList);
-
-  virtual PRBool IsLeaf() const
-  {
-    if (!mGeneratedChildren && mPopupType == ePopupTypeMenu) {
-      // menu popups generate their child frames lazily only when opened, so
-      // behave like a leaf frame. However, generate child frames normally if
-      // the parent menu has a sizetopopup attribute. In this case the size of
-      // the parent menu is dependant on the size of the popup, so the frames
-      // need to exist in order to calculate this size.
-      nsIContent* parentContent = mContent->GetParent();
-      if (parentContent &&
-          !parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup))
-        return PR_TRUE;
-    }
+  void GetViewOffset(nsIView* aView, nsPoint& aPoint);
+  static void GetRootViewForPopup(nsIFrame* aStartFrame,
+                                  PRBool aStopAtViewManagerRoot,
+                                  nsIView** aResult);
 
-    return PR_FALSE;
-  }
-
-  // AdjustView should be called by the parent frame after the popup has been
-  // laid out, so that the view can be shown.
-  void AdjustView();
-
-  void GetViewOffset(nsIView* aView, nsPoint& aPoint);
-  nsIView* GetRootViewForPopup(nsIFrame* aStartFrame,
-                               PRBool aStopAtViewManagerRoot);
-
-  // set the position of the popup either relative to the anchor aAnchorFrame
-  // (or the frame for mAnchorContent if aAnchorFrame is null) or at a specific
-  // point if a screen position (mScreenXPos and mScreenYPos) are set. The popup
-  // will be adjusted so that it is on screen.
-  nsresult SetPopupPosition(nsIFrame* aAnchorFrame);
-
-  PRBool HasGeneratedChildren() { return mGeneratedChildren; }
-  void SetGeneratedChildren() { mGeneratedChildren = PR_TRUE; }
+  nsresult SyncViewWithFrame(nsPresContext* aPresContext, const nsString& aPopupAnchor,
+                             const nsString& aPopupAlign,
+                             nsIFrame* aFrame, PRInt32 aXPos, PRInt32 aYPos);
 
-  // called when the Enter key is pressed while the popup is open. This will
-  // just pass the call down to the current menu, if any. Also, calling Enter
-  // will reset the current incremental search string, calculated in
-  // FindMenuWithShortcut
-  nsMenuFrame* Enter();
-
-  PRInt32 PopupType() const { return mPopupType; }
-  PRBool IsMenu() { return mPopupType == ePopupTypeMenu; }
-  PRBool IsOpen() { return mIsOpen; }
-  PRBool HasOpenChanged() { return mIsOpenChanged; }
-
-  // the Initialize methods are used to set the anchor position for
-  // each way of opening a popup.
-  void InitializePopup(nsIContent* aAnchorContent,
-                       const nsAString& aPosition,
-                       PRInt32 aXPos, PRInt32 aYPos,
-                       PRBool aAttributesOverride);
-
-  void InitializePopupAtScreen(PRInt32 aXPos, PRInt32 aYPos);
+  NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag);
+  NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag);
+  
+  NS_IMETHOD Escape(PRBool& aHandledFlag);
+  NS_IMETHOD Enter();
 
-  void InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
-                                      nsAString& aAnchor,
-                                      nsAString& aAlign,
-                                      PRInt32 aXPos, PRInt32 aYPos);
-
-  // indicate that the popup should be opened
-  PRBool ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem);
-  // indicate that the popup should be hidden
-  void HidePopup(PRBool aDeselectMenu);
+  nsIMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction);
 
-  // locate and return the menu frame that should be activated for the
-  // supplied key event. If doAction is set to true by this method,
-  // then the menu's action should be carried out, as if the user had pressed
-  // the Enter key. If doAction is false, the menu should just be highlighted.
-  // This method also handles incremental searching in menus so the user can
-  // type the first few letters of an item/s name to select it.
-  nsMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction);
+  PRBool IsValidItem(nsIContent* aContent);
+  PRBool IsDisabled(nsIContent* aContent);
 
-  void ClearIncrementalString() { mIncrementalString.Truncate(); }
+  nsIMenuParent* GetContextMenu();
+
+  NS_IMETHOD KillCloseTimer();
 
   virtual nsIAtom* GetType() const { return nsGkAtoms::menuPopupFrame; }
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
       return MakeFrameName(NS_LITERAL_STRING("MenuPopup"), aResult);
   }
 #endif
 
-  void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame);
+  void EnsureMenuItemIsVisible(nsIMenuFrame* aMenuFrame);
 
   // This sets 'left' and 'top' attributes.
   // May kill the frame.
   void MoveTo(PRInt32 aLeft, PRInt32 aTop);
 
   void GetAutoPosition(PRBool* aShouldAutoPosition);
   void SetAutoPosition(PRBool aShouldAutoPosition);
+  void EnableRollup(PRBool aShouldRollup);
   void SetConsumeRollupEvent(PRUint32 aConsumeMode);
 
   nsIScrollableView* GetScrollableView(nsIFrame* aStart);
   
 protected:
-  // Move without updating attributes.                                          
-  void MoveToInternal(PRInt32 aLeft, PRInt32 aTop);                             
+  friend class nsMenuPopupTimerMediator;
+  NS_HIDDEN_(nsresult) Notify(nsITimer* aTimer);
 
-  // redefine to tell the box system not to move the views.
+  // Move without updating attributes.
+  void MoveToInternal(PRInt32 aLeft, PRInt32 aTop);
+
+  // redefine to tell the box system not to move the
+  // views.
   virtual void GetLayoutFlags(PRUint32& aFlags);
 
-  void InitPositionFromAnchorAlign(const nsAString& aAnchor,
-                                   const nsAString& aAlign);
+  // given x,y in client coordinates, compensate for nested documents like framesets.
+  void AdjustClientXYForNestedDocuments ( nsIDOMXULDocument* inPopupDoc, nsIPresShell* inPopupShell, 
+                                            PRInt32 inClientX, PRInt32 inClientY, 
+                                            PRInt32* outAdjX, PRInt32* outAdjY ) ;
 
   void AdjustPositionForAnchorAlign ( PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect,
-                                      PRBool* outFlushWithTopBottom ) ;
+                                        const nsString& aPopupAnchor, const nsString& aPopupAlign,
+                                        PRBool* outFlushWithTopBottom ) ;
 
   PRBool IsMoreRoomOnOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32 inScreenViewLocX, PRInt32 inScreenViewLocY,
                                            const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips,
                                            PRInt32 inScreenBottomTwips, PRInt32 inScreenRightTwips ) ;
 
   void MovePopupToOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32* ioXPos, PRInt32* ioYPos, 
                                            PRInt32* ioScreenViewLocX, PRInt32* ioScreenViewLocY,
                                            const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips,
                                            PRInt32 inScreenBottomTwips, PRInt32 inScreenRightTwips ) ;
 
   // Move the popup to the position specified in its |left| and |top| attributes.
   void MoveToAttributePosition();
 
-  // the content that the popup is anchored to, if any, which may be in a
-  // different document than the popup.
-  nsCOMPtr<nsIContent> mAnchorContent;
+
+  nsIMenuFrame* mCurrentMenu; // The current menu that is active.
 
-  nsMenuFrame* mCurrentMenu; // The current menu that is active.
+  nsMenuListener* mKeyboardNavigator; // The listener that tells us about key events.
+  nsIDOMEventTarget* mTarget;
 
-  // popup alignment relative to the anchor node
-  PRInt8 mPopupAlignment;
-  PRInt8 mPopupAnchor;
+  nsIMenuFrame* mTimerMenu; // A menu awaiting closure.
+  nsCOMPtr<nsITimer> mCloseTimer; // Close timer.
 
-  // the position of the popup. The screen coordinates, if set to values other
-  // than -1, override mXPos and mYPos.
-  PRInt32 mXPos;
-  PRInt32 mYPos;
-  PRInt32 mScreenXPos;
-  PRInt32 mScreenYPos;
-
-  nsPopupType mPopupType; // type of popup
+  // Reference to the mediator which wraps this frame.
+  nsRefPtr<nsMenuPopupTimerMediator> mTimerMediator;
 
-  PRPackedBool mIsOpen;  // true if the popup is open
-  PRPackedBool mIsOpenChanged; // true if the open state changed since the last layout
-  PRPackedBool mIsOpenPending; // true if an open is pending
-  PRPackedBool mIsContextMenu; // true for context menus
-  PRPackedBool mGeneratedChildren; // true if the contents have been created
+  PRPackedBool mIsContextMenu;  // is this a context menu?
+  
+  PRPackedBool mMenuCanOverlapOSBar;    // can we appear over the taskbar/menubar?
 
-  PRPackedBool mMenuCanOverlapOSBar;    // can we appear over the taskbar/menubar?
-  PRPackedBool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup?
+  PRPackedBool mShouldAutoPosition; // Should SyncViewWithFrame be allowed to auto position popup?
+  PRPackedBool mShouldRollup; // Should this menupopup be allowed to dismiss automatically?
   PRPackedBool mConsumeRollupEvent; // Should the rollup event be consumed?
   PRPackedBool mInContentShell; // True if the popup is in a content shell
 
   nsString     mIncrementalString;  // for incremental typing navigation
 
 }; // class nsMenuPopupFrame
 
 #endif
--- a/layout/xul/base/src/nsPopupBoxObject.cpp
+++ b/layout/xul/base/src/nsPopupBoxObject.cpp
@@ -33,16 +33,17 @@
  * 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 "nsIPopupBoxObject.h"
+#include "nsIPopupSetFrame.h"
 #include "nsIRootBox.h"
 #include "nsBoxObject.h"
 #include "nsIPresShell.h"
 #include "nsFrameManager.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
@@ -58,96 +59,97 @@ class nsPopupBoxObject : public nsBoxObj
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPOPUPBOXOBJECT
 
   nsPopupBoxObject() {}
 protected:
   virtual ~nsPopupBoxObject() {}
 
-  nsPopupSetFrame* GetPopupSetFrame();
+  nsIPopupSetFrame* GetPopupSetFrame();
   nsMenuPopupFrame* GetMenuPopupFrame()
-  {
-    nsIFrame* frame = GetFrame(PR_FALSE);
-    if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame)
-      return NS_STATIC_CAST(nsMenuPopupFrame*, frame);
-    return nsnull;
-  }
+  { return NS_STATIC_CAST(nsMenuPopupFrame*, GetFrame(PR_FALSE)); }
 };
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsPopupBoxObject, nsBoxObject, nsIPopupBoxObject)
 
-nsPopupSetFrame*
+nsIPopupSetFrame*
 nsPopupBoxObject::GetPopupSetFrame()
 {
   nsIRootBox* rootBox = nsIRootBox::GetRootBox(GetPresShell(PR_FALSE));
   if (!rootBox)
     return nsnull;
 
-  return rootBox->GetPopupSetFrame();
+  nsIFrame* popupSetFrame = rootBox->GetPopupSetFrame();
+  if (!popupSetFrame)
+    return nsnull;
+
+  nsIPopupSetFrame *popupSet = nsnull;
+  CallQueryInterface(popupSetFrame, &popupSet);
+  return popupSet;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::HidePopup()
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->HidePopup(mContent, PR_FALSE, PR_TRUE, PR_FALSE);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPopupBoxObject::ShowPopup(nsIDOMElement* aAnchorElement,
-                            nsIDOMElement* aPopupElement,
-                            PRInt32 aXPos, PRInt32 aYPos,
-                            const PRUnichar *aPopupType,
-                            const PRUnichar *aAnchorAlignment,
-                            const PRUnichar *aPopupAlignment)
-{
-  NS_ENSURE_TRUE(aPopupElement, NS_ERROR_INVALID_ARG);
-  // srcContent can be null.
-
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm) {
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-    nsAutoString popupType(aPopupType);
-    nsAutoString anchor(aAnchorAlignment);
-    nsAutoString align(aPopupAlignment);
-    pm->ShowPopupWithAnchorAlign(mContent, anchorContent, anchor, align,
-                                 aXPos, aYPos, popupType.EqualsLiteral("context"));
+  nsIPopupSetFrame *popupSet = GetPopupSetFrame();
+  nsIFrame *ourFrame = GetFrame(PR_FALSE);
+  if (ourFrame && popupSet) {
+    nsWeakFrame weakFrame(ourFrame);
+    popupSet->HidePopup(ourFrame);
+    if (weakFrame.IsAlive()) {
+      popupSet->DestroyPopup(ourFrame, PR_TRUE);
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPopupBoxObject::OpenPopup(nsIDOMElement* aAnchorElement,
-                            const nsAString& aPosition,
-                            PRInt32 aXPos, PRInt32 aYPos,
-                            PRBool aIsContextMenu,
-                            PRBool aAttributesOverride)
+nsPopupBoxObject::ShowPopup(nsIDOMElement* aSrcContent, 
+                            nsIDOMElement* aPopupContent, 
+                            PRInt32 aXPos, PRInt32 aYPos, 
+                            const PRUnichar *aPopupType,
+                            const PRUnichar *anAnchorAlignment, 
+                            const PRUnichar *aPopupAlignment)
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm) {
-    nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-    pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos,
-                  aIsContextMenu, aAttributesOverride, PR_FALSE);
+  nsIPopupSetFrame *popupSet = GetPopupSetFrame();
+  if (!popupSet) {
+    return NS_OK;
   }
 
-  return NS_OK;
-}
+  nsCOMPtr<nsIContent> srcContent(do_QueryInterface(aSrcContent));
+  nsCOMPtr<nsIContent> popupContent(do_QueryInterface(aPopupContent));
+  NS_ENSURE_TRUE(popupContent, NS_ERROR_INVALID_ARG);
+  // srcContent can be null.
+
+  nsAutoString popupType(aPopupType);
+  nsAutoString anchorAlign(anAnchorAlignment);
+  nsAutoString popupAlign(aPopupAlignment);
 
-NS_IMETHODIMP
-nsPopupBoxObject::OpenPopupAtScreen(PRInt32 aXPos, PRInt32 aYPos, PRBool aIsContextMenu)
-{
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu);
-  return NS_OK;
+  // Use |left| and |top| dimension attributes to position the popup if
+  // present, as they may have been persisted. 
+  nsAutoString left, top;
+  popupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left);
+  popupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top);
+  
+  PRInt32 err;
+  if (!left.IsEmpty()) {
+    aXPos = left.ToInteger(&err);
+    if (NS_FAILED(err))
+      return err;
+  }
+  if (!top.IsEmpty()) {
+    aYPos = top.ToInteger(&err);
+    if (NS_FAILED(err))
+      return err;
+  }
+
+  return popupSet->ShowPopup(srcContent, popupContent, aXPos, aYPos, 
+                             popupType, anchorAlign, popupAlign);
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::MoveTo(PRInt32 aLeft, PRInt32 aTop)
 {
   nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
   if (menuPopupFrame) {
     menuPopupFrame->MoveTo(aLeft, aTop);
@@ -190,17 +192,21 @@ nsPopupBoxObject::SetAutoPosition(PRBool
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::EnableRollup(PRBool aShouldRollup)
 {
-  // this does nothing nows
+  nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
+  if (menuPopupFrame) {
+    menuPopupFrame->EnableRollup(aShouldRollup);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::SetConsumeRollupEvent(PRUint32 aConsume)
 {
   nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
   if (menuPopupFrame) {
@@ -208,29 +214,32 @@ nsPopupBoxObject::SetConsumeRollupEvent(
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::EnableKeyboardNavigator(PRBool aEnableKeyboardNavigator)
 {
-  // Use ignorekeys="true" on the popup instead of using this function.
-  if (aEnableKeyboardNavigator)
-    mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys,
-                      NS_LITERAL_STRING("true"), PR_TRUE);
-  else
-    mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, PR_TRUE);
+  nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
+  if (menuPopupFrame) {
+    if (aEnableKeyboardNavigator) {
+      menuPopupFrame->InstallKeyboardNavigator();
+    } else {
+      menuPopupFrame->RemoveKeyboardNavigator();
+    }
+  }
 
   return NS_OK;
 }
 
 // Creation Routine ///////////////////////////////////////////////////////////////////////
 
 nsresult
 NS_NewPopupBoxObject(nsIBoxObject** aResult)
 {
   *aResult = new nsPopupBoxObject;
   if (!*aResult)
     return NS_ERROR_OUT_OF_MEMORY;
   NS_ADDREF(*aResult);
   return NS_OK;
 }
+
--- a/layout/xul/base/src/nsPopupSetFrame.cpp
+++ b/layout/xul/base/src/nsPopupSetFrame.cpp
@@ -61,37 +61,87 @@
 #include "nsIDOMElement.h"
 #include "nsISupportsArray.h"
 #include "nsIDOMText.h"
 #include "nsBoxLayoutState.h"
 #include "nsIScrollableFrame.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsGUIEvent.h"
 #include "nsIRootBox.h"
+#include "nsIFocusController.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShell.h"
+#include "nsPIDOMWindow.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIBaseWindow.h"
+#include "nsIViewManager.h"
 
 #define NS_MENU_POPUP_LIST_INDEX   0
 
 nsPopupFrameList::nsPopupFrameList(nsIContent* aPopupContent, nsPopupFrameList* aNext)
 :mNextPopup(aNext), 
  mPopupFrame(nsnull),
- mPopupContent(aPopupContent)
+ mPopupContent(aPopupContent),
+ mElementContent(nsnull), 
+ mCreateHandlerSucceeded(PR_FALSE),
+ mIsOpen(PR_FALSE),
+ mLastPref(-1,-1)
 {
 }
 
+nsPopupFrameList* nsPopupFrameList::GetEntry(nsIContent* aPopupContent) {
+  if (aPopupContent == mPopupContent)
+    return this;
+
+  if (mNextPopup)
+    return mNextPopup->GetEntry(aPopupContent);
+
+  return nsnull;
+}
+
+nsPopupFrameList* nsPopupFrameList::GetEntryByFrame(nsIFrame* aPopupFrame) {
+  if (aPopupFrame == mPopupFrame)
+    return this;
+
+  if (mNextPopup)
+    return mNextPopup->GetEntryByFrame(aPopupFrame);
+
+  return nsnull;
+}
+
 //
 // NS_NewPopupSetFrame
 //
 // Wrapper for creating a new menu popup container
 //
 nsIFrame*
 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsPopupSetFrame (aPresShell, aContext);
 }
 
+NS_IMETHODIMP_(nsrefcnt) 
+nsPopupSetFrame::AddRef(void)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(nsrefcnt) 
+nsPopupSetFrame::Release(void)
+{
+    return NS_OK;
+}
+
+//
+// QueryInterface
+//
+NS_INTERFACE_MAP_BEGIN(nsPopupSetFrame)
+  NS_INTERFACE_MAP_ENTRY(nsIPopupSetFrame)
+NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
+
 NS_IMETHODIMP
 nsPopupSetFrame::Init(nsIContent*      aContent,
                       nsIFrame*        aParent,
                       nsIFrame*        aPrevInFlow)
 {
   nsresult  rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   nsIRootBox *rootBox;
@@ -143,24 +193,41 @@ nsPopupSetFrame::SetInitialChildList(nsI
     return AddPopupFrameList(aChildList);
   }
   return nsBoxFrame::SetInitialChildList(aListName, aChildList);
 }
 
 void
 nsPopupSetFrame::Destroy()
 {
-  // remove each popup from the list as we go.
-  while (mPopupList) {
-    if (mPopupList->mPopupFrame)
-      mPopupList->mPopupFrame->Destroy();
+  // Remove our frame list.
+  if (mPopupList) {
+    // Try to hide any active popups
+    if (nsMenuDismissalListener::sInstance) {
+      nsIMenuParent *menuParent =
+        nsMenuDismissalListener::sInstance->GetCurrentMenuParent();
+      nsIFrame* frame;
+      CallQueryInterface(menuParent, &frame);
+      // Rollup popups, but only if they're ours
+      if (frame && mPopupList->GetEntryByFrame(frame)) {
+        nsMenuDismissalListener::sInstance->Rollup();
+      }
+    }
 
-    nsPopupFrameList* temp = mPopupList;
-    mPopupList = mPopupList->mNextPopup;
-    delete temp;
+    // Actually remove each popup from the list as we go. This
+    // keeps things consistent so reentering won't crash us
+    while (mPopupList) {
+      if (mPopupList->mPopupFrame) {
+        mPopupList->mPopupFrame->Destroy();
+      }
+
+      nsPopupFrameList* temp = mPopupList;
+      mPopupList = mPopupList->mNextPopup;
+      delete temp;
+    }
   }
 
   nsIRootBox *rootBox;
   nsresult res = CallQueryInterface(mParent->GetParent(), &rootBox);
   NS_ASSERTION(NS_SUCCEEDED(res), "grandparent should be root box");
   if (NS_SUCCEEDED(res)) {
     rootBox->SetPopupSetFrame(nsnull);
   }
@@ -172,27 +239,34 @@ NS_IMETHODIMP
 nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState)
 {
   // lay us out
   nsresult rv = nsBoxFrame::DoLayout(aState);
 
   // lay out all of our currently open popups.
   nsPopupFrameList* currEntry = mPopupList;
   while (currEntry) {
-    nsMenuPopupFrame* popupChild = currEntry->mPopupFrame;
-    if (popupChild && popupChild->IsOpen()) {
+    nsIFrame* popupChild = currEntry->mPopupFrame;
+    if (popupChild) {
+      NS_ASSERTION(popupChild->IsBoxFrame(), "popupChild is not box!!");
+
       // then get its preferred size
       nsSize prefSize = popupChild->GetPrefSize(aState);
       nsSize minSize = popupChild->GetMinSize(aState);
       nsSize maxSize = popupChild->GetMaxSize(aState);
 
       BoundsCheck(minSize, prefSize, maxSize);
 
-      popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
-      popupChild->SetPopupPosition(nsnull);
+      // if the pref size changed then set bounds to be the pref size
+      // and sync the view. Also set new pref size.
+     // if (currEntry->mLastPref != prefSize) {
+        popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
+        RepositionPopup(currEntry, aState);
+        currEntry->mLastPref = prefSize;
+     // }
 
       // is the new size too small? Make sure we handle scrollbars correctly
       nsIBox* child = popupChild->GetChildBox();
 
       nsRect bounds(popupChild->GetRect());
 
       nsCOMPtr<nsIScrollableFrame> scrollframe = do_QueryInterface(child);
       if (scrollframe &&
@@ -201,32 +275,501 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutSta
         if (bounds.height < prefSize.height) {
           // layout the child
           popupChild->Layout(aState);
 
           nsMargin scrollbars = scrollframe->GetActualScrollbarSizes();
           if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right)
           {
             bounds.width += scrollbars.left + scrollbars.right;
+            //printf("Width=%d\n",width);
             popupChild->SetBounds(aState, bounds);
           }
         }
       }
-
+    
       // layout the child
       popupChild->Layout(aState);
-      popupChild->AdjustView();
+
+      // only size popup if open
+      if (currEntry->mCreateHandlerSucceeded) {
+        nsIView* view = popupChild->GetView();
+        nsIViewManager* viewManager = view->GetViewManager();
+        nsRect r(0, 0, bounds.width, bounds.height);
+        viewManager->ResizeView(view, r);
+        viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
+      }
     }
 
     currEntry = currEntry->mNextPopup;
   }
 
+  SyncLayout(aState);
+
   return rv;
 }
 
+
+#ifdef DEBUG_LAYOUT
+NS_IMETHODIMP
+nsPopupSetFrame::SetDebug(nsBoxLayoutState& aState, PRBool aDebug)
+{
+  // see if our state matches the given debug state
+  PRBool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
+  PRBool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
+
+  // if it doesn't then tell each child below us the new debug state
+  if (debugChanged)
+  {
+    // XXXdwh fix later.  nobody uses this anymore anyway.
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsPopupSetFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug)
+{
+      if (!aList)
+          return NS_OK;
+
+      while (aList) {
+        if (aList->IsBoxFrame())
+          aList->SetDebug(aState, aDebug);
+
+        aList = aList->GetNextSibling();
+      }
+
+      return NS_OK;
+}
+#endif
+
+
+void
+nsPopupSetFrame::RepositionPopup(nsPopupFrameList* aEntry, nsBoxLayoutState& aState)
+{
+  // Sync up the view.
+  if (aEntry && aEntry->mElementContent) {
+    nsPresContext* presContext = aState.PresContext();
+    nsIFrame* frameToSyncTo = presContext->PresShell()->
+      GetPrimaryFrameFor(aEntry->mElementContent);
+    ((nsMenuPopupFrame*)(aEntry->mPopupFrame))->SyncViewWithFrame(presContext, 
+          aEntry->mPopupAnchor, aEntry->mPopupAlign, frameToSyncTo, aEntry->mXPos, aEntry->mYPos);
+  }
+}
+
+NS_IMETHODIMP
+nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupContent, 
+                           PRInt32 aXPos, PRInt32 aYPos, 
+                           const nsString& aPopupType, const nsString& anAnchorAlignment,
+                           const nsString& aPopupAlignment)
+{
+  if (!MayOpenPopup(this))
+    return NS_OK;
+
+  nsWeakFrame weakFrame(this);
+  // First fire the popupshowing event.
+  if (!OnCreate(aXPos, aYPos, aPopupContent) || !weakFrame.IsAlive())
+    return NS_OK;
+        
+  // See if we already have an entry in our list.  We must create a new one on a miss.
+  nsPopupFrameList* entry = nsnull;
+  if (mPopupList)
+    entry = mPopupList->GetEntry(aPopupContent);
+  if (!entry) {
+    entry = new nsPopupFrameList(aPopupContent, mPopupList);
+    if (!entry)
+      return NS_ERROR_OUT_OF_MEMORY;
+    mPopupList = entry;
+  }
+
+  // Cache the element content we're supposed to sync to
+  entry->mPopupType = aPopupType;
+  entry->mElementContent = aElementContent;
+  entry->mPopupAlign = aPopupAlignment;
+  entry->mPopupAnchor = anAnchorAlignment;
+  entry->mXPos = aXPos;
+  entry->mYPos = aYPos;
+
+  // If a frame exists already, go ahead and use it.
+  entry->mPopupFrame = PresContext()->PresShell()
+    ->GetPrimaryFrameFor(aPopupContent);
+
+#ifdef DEBUG_PINK
+  printf("X Pos: %d\n", mXPos);
+  printf("Y Pos: %d\n", mYPos);
+#endif
+
+  // Generate the popup.
+  entry->mCreateHandlerSucceeded = PR_TRUE;
+  entry->mIsOpen = PR_TRUE;
+  // This may destroy or change entry->mPopupFrame or remove the entry from
+  // mPopupList. |this| may also get deleted.
+  MarkAsGenerated(aPopupContent);
+
+  if (!weakFrame.IsAlive()) {
+    return NS_OK;
+  }
+
+  nsPopupFrameList* newEntry =
+    mPopupList ? mPopupList->GetEntry(aPopupContent) : nsnull;
+  if (!newEntry || newEntry != entry) {
+    NS_WARNING("The popup entry for aPopupContent has changed!");
+    return NS_OK;
+  }
+
+  // determine if this menu is a context menu and flag it
+  nsIMenuParent* childPopup = nsnull;
+  if (entry->mPopupFrame)
+    CallQueryInterface(entry->mPopupFrame, &childPopup);
+  if ( childPopup && aPopupType.EqualsLiteral("context") )
+    childPopup->SetIsContextMenu(PR_TRUE);
+
+  // Now open the popup.
+  OpenPopup(entry, PR_TRUE);
+  
+  if (!weakFrame.IsAlive()) {
+    return NS_OK;
+  }
+
+  // Now fire the popupshown event.
+  OnCreated(aXPos, aYPos, aPopupContent);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPopupSetFrame::HidePopup(nsIFrame* aPopup)
+{
+  if (!mPopupList)
+    return NS_OK; // No active popups
+
+  nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup);
+  if (!entry)
+    return NS_OK;
+
+  if (entry->mCreateHandlerSucceeded)
+    ActivatePopup(entry, PR_FALSE);
+
+  if (entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) {
+    // If we are a context menu, and if we are attached to a
+    // menupopup, then hiding us should also hide the parent menu
+    // popup.
+    if (entry->mElementContent->Tag() == nsGkAtoms::menupopup) {
+      nsIFrame* popupFrame = PresContext()->PresShell()
+        ->GetPrimaryFrameFor(entry->mElementContent);
+      if (popupFrame) {
+        nsIMenuParent *menuParent;
+        if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) {
+          menuParent->HideChain();
+        }
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPopupSetFrame::DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain)
+{
+  if (!mPopupList)
+    return NS_OK; // No active popups
+
+  nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup);
+
+  if (entry && entry->mCreateHandlerSucceeded) {    // ensure the popup was created before we try to destroy it
+    nsWeakFrame weakFrame(this);
+    OpenPopup(entry, PR_FALSE);
+    nsCOMPtr<nsIContent> popupContent = entry->mPopupContent;
+    if (weakFrame.IsAlive()) {
+      if (aDestroyEntireChain && entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) {
+        // If we are a context menu, and if we are attached to a
+        // menupopup, then destroying us should also dismiss the parent
+        // menu popup.
+        if (entry->mElementContent->Tag() == nsGkAtoms::menupopup) {
+          nsIFrame* popupFrame = PresContext()->PresShell()
+            ->GetPrimaryFrameFor(entry->mElementContent);
+          if (popupFrame) {
+            nsIMenuParent *menuParent;
+            if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) {
+              menuParent->DismissChain();
+            }
+          }
+        }
+      }
+  
+      // clear things out for next time
+      entry->mPopupType.Truncate();
+      entry->mCreateHandlerSucceeded = PR_FALSE;
+      entry->mElementContent = nsnull;
+      entry->mXPos = entry->mYPos = 0;
+      entry->mLastPref.width = -1;
+      entry->mLastPref.height = -1;
+    }
+    // ungenerate the popup.
+    popupContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, PR_TRUE);
+  }
+
+  return NS_OK;
+}
+
+void
+nsPopupSetFrame::MarkAsGenerated(nsIContent* aPopupContent)
+{
+  // Set our attribute, but only if we aren't already generated.
+  // Retrieve the menugenerated attribute.
+  if (!aPopupContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menugenerated,
+                                  nsGkAtoms::_true, eCaseMatters)) {
+    // Generate this element.
+    aPopupContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, NS_LITERAL_STRING("true"),
+                           PR_TRUE);
+  }
+}
+
+void
+nsPopupSetFrame::OpenPopup(nsPopupFrameList* aEntry, PRBool aActivateFlag)
+{
+  nsWeakFrame weakFrame(this);