Bug 279703, backing out the popup changes due to performance regressions. sigh.
authorenndeakin@sympatico.ca
Fri, 29 Jun 2007 15:39:50 -0700
changeset 2947 2ac122696d2d3d20ccce2c0ab1a13acc7bfd9bbb
parent 2946 317abe6fc05ca31883fe0b36177bf7c62aa44388
child 2948 bd6d109cb8578c9a954be222a8ceeef0b15c188c
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs279703
milestone1.9a6pre
Bug 279703, backing out the popup changes due to performance regressions. sigh.
content/base/src/nsGkAtomList.h
content/xul/content/Makefile.in
layout/xul/base/src/nsIMenuParent.h
layout/xul/base/src/nsIRootBox.h
layout/xul/base/src/nsMenuBarFrame.cpp
layout/xul/base/src/nsXULTooltipListener.cpp
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -483,16 +483,17 @@ GK_ATOM(mediaType, "media-type")
 GK_ATOM(member, "member")
 GK_ATOM(menu, "menu")
 GK_ATOM(menubar, "menubar")
 GK_ATOM(menubutton, "menubutton")
 GK_ATOM(menugenerated, "menugenerated")
 GK_ATOM(menuitem, "menuitem")
 GK_ATOM(menulist, "menulist")
 GK_ATOM(menupopup, "menupopup")
+GK_ATOM(menutobedisplayed, "menutobedisplayed")
 GK_ATOM(message, "message")
 GK_ATOM(meta, "meta")
 GK_ATOM(method, "method")
 GK_ATOM(middle, "middle")
 GK_ATOM(min, "min")
 GK_ATOM(minheight, "minheight")
 GK_ATOM(minpos, "minpos")
 GK_ATOM(minusSign, "minus-sign")
@@ -514,17 +515,16 @@ GK_ATOM(_namespace, "namespace")
 GK_ATOM(namespaceAlias, "namespace-alias")
 GK_ATOM(namespaceUri, "namespace-uri")
 GK_ATOM(NaN, "NaN")
 GK_ATOM(negate, "negate")
 GK_ATOM(never, "never")
 GK_ATOM(_new, "new")
 GK_ATOM(nextBidi, "NextBidi")
 GK_ATOM(no, "no")
-GK_ATOM(noautohide, "noautohide")
 GK_ATOM(nobr, "nobr")
 GK_ATOM(node, "node")
 GK_ATOM(nodeSet, "node-set")
 GK_ATOM(noembed, "noembed")
 GK_ATOM(noframes, "noframes")
 GK_ATOM(nohref, "nohref")
 GK_ATOM(none, "none")
 GK_ATOM(noresize, "noresize")
@@ -629,17 +629,16 @@ GK_ATOM(overlay, "overlay")
 GK_ATOM(overlap, "overlap")
 GK_ATOM(p, "p")
 GK_ATOM(pack, "pack")
 GK_ATOM(page, "page")
 GK_ATOM(pageincrement, "pageincrement")
 GK_ATOM(pagex, "pagex")
 GK_ATOM(pagey, "pagey")
 GK_ATOM(palettename, "palettename")
-GK_ATOM(panel, "panel")
 GK_ATOM(param, "param")
 GK_ATOM(parameter, "parameter")
 GK_ATOM(parent, "parent")
 GK_ATOM(parsetype, "parsetype")
 GK_ATOM(patternSeparator, "pattern-separator")
 GK_ATOM(perMille, "per-mille")
 GK_ATOM(percent, "percent")
 GK_ATOM(persist, "persist")
@@ -1386,25 +1385,22 @@ GK_ATOM(subDocumentFrame, "subDocumentFr
 GK_ATOM(imageBoxFrame, "ImageBoxFrame")
 GK_ATOM(imageFrame, "ImageFrame")
 GK_ATOM(imageControlFrame, "ImageControlFrame")
 GK_ATOM(inlineFrame, "InlineFrame")
 GK_ATOM(legendFrame, "LegendFrame")
 GK_ATOM(letterFrame, "LetterFrame")
 GK_ATOM(lineFrame, "LineFrame")
 GK_ATOM(listControlFrame,"ListControlFrame")
-GK_ATOM(menuBarFrame,"MenuBarFrame")
-GK_ATOM(menuFrame,"MenuFrame")
 GK_ATOM(menuPopupFrame,"MenuPopupFrame")
 GK_ATOM(objectFrame, "ObjectFrame")
 GK_ATOM(pageFrame, "PageFrame")
 GK_ATOM(pageBreakFrame, "PageBreakFrame")
 GK_ATOM(pageContentFrame, "PageContentFrame")
 GK_ATOM(placeholderFrame, "PlaceholderFrame")
-GK_ATOM(popupSetFrame, "PopupSetFrame")
 GK_ATOM(positionedInlineFrame, "PositionedInlineFrame")
 GK_ATOM(canvasFrame, "CanvasFrame")
 GK_ATOM(rootFrame, "RootFrame")
 GK_ATOM(scrollFrame, "ScrollFrame")
 GK_ATOM(scrollbarFrame, "ScrollbarFrame")
 GK_ATOM(sequenceFrame, "SequenceFrame")
 GK_ATOM(sliderFrame, "sliderFrame")
 GK_ATOM(tableCaptionFrame, "TableCaptionFrame")
--- a/content/xul/content/Makefile.in
+++ b/content/xul/content/Makefile.in
@@ -38,12 +38,12 @@
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= xul
-DIRS		= src test
+DIRS		= public src test
 
 include $(topsrcdir)/config/rules.mk
 
--- a/layout/xul/base/src/nsIMenuParent.h
+++ b/layout/xul/base/src/nsIMenuParent.h
@@ -34,58 +34,150 @@
  * 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 nsIMenuParent_h___
 #define nsIMenuParent_h___
 
-class nsMenuFrame;
+
+// {33f700c8-976a-4cdb-8f6c-d9f4cfee8366}
+#define NS_IMENUPARENT_IID \
+{ 0x33f700c8, 0x976a, 0x4cdb, { 0x8f, 0x6c, 0xd9, 0xf4, 0xcf, 0xee, 0x83, 0x66 } }
+
+class nsIMenuFrame;
+class nsIDOMKeyEvent;
 
 /*
- * nsIMenuParent is an interface implemented by nsMenuBarFrame and nsMenuPopupFrame
- * as both serve as parent frames to nsMenuFrame.
+ * nsIMenuParent is implemented on frames and thus should not be
+ * refcounted.  Eventually it should not inherit from nsISupports.
+ */
+
+/**
+ * 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).
  *
- * Don't implement this interface on other classes unless you also fix up references,
- * as this interface is directly cast to and from nsMenuBarFrame and nsMenuPopupFrame.
+ * 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
+ * 
  */
 
-class nsIMenuParent {
+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
+};
+
+#ifdef IBMBIDI
+#define NS_DIRECTION_FROM_KEY_CODE(direction, keycode)           \
+  NS_ASSERTION(keycode >= NS_VK_END && keycode <= NS_VK_DOWN,    \
+               "Illegal key code");                              \
+  const nsStyleVisibility* vis = 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];
+#else
+#define NS_DIRECTION_FROM_KEY_CODE(direction, keycode)           \
+    direction = DirectionFromKeyCode_lr_tb[keycode - NS_VK_END];
+#endif
+
+class nsIMenuParent : public nsISupports {
 
 public:
-  // returns the menu frame of the currently active item within the menu
-  virtual nsMenuFrame *GetCurrentMenuItem() = 0;
-  // sets the currently active menu frame.
-  NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem) = 0;
-  // indicate that the current menu frame is being destroyed, so clear the
-  // current menu item
-  virtual void CurrentMenuIsBeingDestroyed() = 0;
-  // deselects the current item and closes its popup if any, then selects the
-  // new item aMenuItem. For a menubar, if another menu is already open, the
-  // new menu aMenuItem is opened. In this case, if aSelectFirstItem is true,
-  // select the first item in it. For menupoups, the menu is not opened and
-  // the aSelectFirstItem argument is not used.
-  NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, PRBool aSelectFirstItem) = 0;
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUPARENT_IID)
+
+  virtual nsIMenuFrame *GetCurrentMenuItem() = 0;
+  NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem) = 0;
+  virtual nsIMenuFrame *GetNextMenuItem(nsIMenuFrame* aStart) = 0;
+  virtual nsIMenuFrame *GetPreviousMenuItem(nsIMenuFrame* aStart) = 0;
+
+  NS_IMETHOD SetActive(PRBool aActiveFlag) = 0;
+  NS_IMETHOD GetIsActive(PRBool& isActive) = 0;
+  NS_IMETHOD GetWidget(nsIWidget **aWidget) = 0;
+  
+  NS_IMETHOD IsMenuBar(PRBool& isMenuBar) = 0;
+  NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks) = 0;
+  NS_IMETHOD ClearRecentlyRolledUp() = 0;
+  NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp) = 0;
+
+  NS_IMETHOD DismissChain() = 0;
+  NS_IMETHOD HideChain() = 0;
+  NS_IMETHOD KillPendingTimers() = 0;
+  NS_IMETHOD CancelPendingTimers() = 0;
 
-  // returns true if the menupopup is open. For menubars, returns false.
-  virtual PRBool IsOpen() = 0;
-  // returns true if the menubar is currently active. For menupopups, returns false.
-  virtual PRBool IsActive() = 0;
-  // returns true if this is a menubar. If false, it is a popup
-  virtual PRBool IsMenuBar() = 0;
-  // returns true if this is a menu, which has a tag of menupopup or popup.
-  // Otherwise, this returns false
-  virtual PRBool IsMenu() = 0;
-  // returns true if this is a context menu
-  virtual PRBool IsContextMenu() = 0;
+  NS_IMETHOD AttachedDismissalListener() = 0;
+
+  NS_IMETHOD InstallKeyboardNavigator() = 0;
+  NS_IMETHOD RemoveKeyboardNavigator() = 0;
 
-  // indicate that the menubar should become active or inactive
-  NS_IMETHOD SetActive(PRBool aActiveFlag) = 0;
+  // Used to move up, down, left, and right in menus.
+  NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) = 0;
+  NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) = 0;
+  // Called when the ESC key is held down to close levels of menus.
+  NS_IMETHOD Escape(PRBool& aHandledFlag) = 0;
+  // Called to execute a menu item.
+  NS_IMETHOD Enter() = 0;
 
-  // notify that the menu has been closed and any active state should be
-  // cleared. This should return true if the menu should be deselected
-  // by the caller.
-  virtual PRBool MenuClosed() = 0;
+  NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) = 0;
+  NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) = 0;
+
+  NS_IMETHOD GetParentPopup(nsIMenuParent** aResult) = 0;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuParent, NS_IMENUPARENT_IID)
+
 #endif
 
--- a/layout/xul/base/src/nsIRootBox.h
+++ b/layout/xul/base/src/nsIRootBox.h
@@ -36,33 +36,33 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 
 #ifndef nsIRootBox_h___
 #define nsIRootBox_h___
 
 #include "nsISupports.h"
-class nsPopupSetFrame;
+class nsIFrame;
 class nsIContent;
 class nsIPresShell;
 
-// {9777EC2A-9A46-4D01-8CEB-B9CEB2C262A5}
+// {2256d568-3f5a-42ec-b932-3d0f78551a1a}
 #define NS_IROOTBOX_IID \
-{ 0x9777EC2A, 0x9A46, 0x4D01, \
-  { 0x8C, 0xEB, 0xB9, 0xCE, 0xB2, 0xC2, 0x62, 0xA5 } }
+{ 0x2256d568, 0x3f5a, 0x42ec, \
+  { 0xb9, 0x32, 0x3d, 0x0f, 0x78, 0x55, 0x1a, 0x1a } }
 
 
 class nsIRootBox : public nsISupports {
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IROOTBOX_IID)
 
-  virtual nsPopupSetFrame* GetPopupSetFrame() = 0;
-  virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) = 0;
+  virtual nsIFrame* GetPopupSetFrame() = 0;
+  virtual void SetPopupSetFrame(nsIFrame* aPopupSet)=0;
 
   virtual nsIContent* GetDefaultTooltip() = 0;
   virtual void SetDefaultTooltip(nsIContent* aTooltip) = 0;
 
   virtual nsresult AddTooltipSupport(nsIContent* aNode) = 0;
   virtual nsresult RemoveTooltipSupport(nsIContent* aNode) = 0;
 
   static nsIRootBox* GetRootBox(nsIPresShell* aShell);
--- a/layout/xul/base/src/nsMenuBarFrame.cpp
+++ b/layout/xul/base/src/nsMenuBarFrame.cpp
@@ -32,19 +32,21 @@
  * 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 "nsIServiceManager.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 "nsIDocument.h"
 #include "nsIDOMEventTarget.h"
@@ -72,28 +74,61 @@
 // Wrapper for creating a new menu Bar container
 //
 nsIFrame*
 NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsMenuBarFrame (aPresShell, aContext);
 }
 
+NS_IMETHODIMP_(nsrefcnt) 
+nsMenuBarFrame::AddRef(void)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(nsrefcnt) 
+nsMenuBarFrame::Release(void)
+{
+    return NS_OK;
+}
+
+
+//
+// QueryInterface
+//
+NS_INTERFACE_MAP_BEGIN(nsMenuBarFrame)
+  NS_INTERFACE_MAP_ENTRY(nsIMenuParent)
+NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
+
+
 //
 // nsMenuBarFrame cntr
 //
 nsMenuBarFrame::nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext):
   nsBoxFrame(aShell, aContext),
     mMenuBarListener(nsnull),
+    mKeyboardNavigator(nsnull),
     mIsActive(PR_FALSE),
     mTarget(nsnull),
     mCaretWasVisible(PR_FALSE)
 {
 } // cntr
 
+nsMenuBarFrame::~nsMenuBarFrame()
+{
+  /* The menubar can still be active at this point under unusual circumstances.
+     (say, while switching skins (which tears down all frames including
+     this one) after having made a menu selection (say, Edit->Preferences,
+     to get to the skin switching UI)). SetActive(PR_FALSE) releases
+     mKeyboardNavigator, which is by now pointing to a deleted frame.
+  */
+  SetActive(PR_FALSE);
+}
+
 NS_IMETHODIMP
 nsMenuBarFrame::Init(nsIContent*      aContent,
                      nsIFrame*        aParent,
                      nsIFrame*        aPrevInFlow)
 {
   nsresult  rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   // Create the menu bar listener.
@@ -117,31 +152,36 @@ nsMenuBarFrame::Init(nsIContent*      aC
 
   target->AddEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE);   
   target->AddEventListener(NS_LITERAL_STRING("blur"), (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE);   
 
   return rv;
 }
 
 NS_IMETHODIMP
+nsMenuBarFrame::IsOpen()
+{
+  PRBool isOpen = PR_FALSE;
+  if(mCurrentMenu) {
+    mCurrentMenu->MenuIsOpen(isOpen);
+    if (isOpen) {
+      return PR_TRUE;
+    }
+  }
+  return PR_FALSE;
+}
+
+
+NS_IMETHODIMP
 nsMenuBarFrame::SetActive(PRBool aActiveFlag)
 {
   // If the activity is not changed, there is nothing to do.
   if (mIsActive == aActiveFlag)
     return NS_OK;
 
-  if (!aActiveFlag) {
-    // if there is a request to deactivate the menu bar, check to see whether
-    // there is a menu popup open for the menu bar. In this case, don't
-    // deactivate the menu bar.
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (pm && pm->IsPopupOpenForMenuParent(this))
-      return NS_OK;
-  }
-
   mIsActive = aActiveFlag;
   if (mIsActive) {
     InstallKeyboardNavigator();
   }
   else {
     RemoveKeyboardNavigator();
   }
   
@@ -194,68 +234,65 @@ nsMenuBarFrame::SetActive(PRBool aActive
     if (!mIsActive) {
       mCaretWasVisible = PR_FALSE;
     }
   } while (0);
 
   NS_NAMED_LITERAL_STRING(active, "DOMMenuBarActive");
   NS_NAMED_LITERAL_STRING(inactive, "DOMMenuBarInactive");
   
-  FireDOMEvent(mIsActive ? active : inactive, mContent);
+  FireDOMEventSynch(mIsActive ? active : inactive);
 
   return NS_OK;
 }
 
-nsMenuFrame*
+void
 nsMenuBarFrame::ToggleMenuActiveState()
 {
   if (mIsActive) {
     // Deactivate the menu bar
     SetActive(PR_FALSE);
     if (mCurrentMenu) {
-      nsMenuFrame* closeframe = mCurrentMenu;
-      closeframe->SelectMenu(PR_FALSE);
+      // Deactivate the menu.
+      mCurrentMenu->OpenMenu(PR_FALSE);
+      mCurrentMenu->SelectMenu(PR_FALSE);
       mCurrentMenu = nsnull;
-      return closeframe;
     }
   }
   else {
     // if the menu bar is already selected (eg. mouseover), deselect it
     if (mCurrentMenu)
       mCurrentMenu->SelectMenu(PR_FALSE);
     
     // Activate the menu bar
     SetActive(PR_TRUE);
 
     // Set the active menu to be the top left item (e.g., the File menu).
     // We use an attribute called "menuactive" to track the current 
     // active menu.
-    nsMenuFrame* firstFrame = nsXULPopupManager::GetNextMenuItem(this, nsnull, PR_FALSE);
+    nsIMenuFrame* firstFrame = GetNextMenuItem(nsnull);
     if (firstFrame) {
       firstFrame->SelectMenu(PR_TRUE);
       
       // Track this item for keyboard navigation.
       mCurrentMenu = firstFrame;
     }
   }
-
-  return nsnull;
 }
 
-static void
-GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
-                  nsIFrame** aResult)
+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);
 }
 
-nsMenuFrame*
+nsIMenuFrame*
 nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
 {
   PRUint32 charCode;
   aKeyEvent->GetCharCode(&charCode);
 
   // Enumerate over our list of frames.
   nsIFrame* immediateParent = nsnull;
   GetInsertionPoint(PresContext()->PresShell(), this, nsnull, &immediateParent);
@@ -263,180 +300,519 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI
     immediateParent = this;
 
   nsIFrame* currFrame = immediateParent->GetFirstChild(nsnull);
 
   while (currFrame) {
     nsIContent* current = currFrame->GetContent();
     
     // See if it's a menu item.
-    if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, PR_FALSE)) {
+    if (IsValidItem(current)) {
       // Get the shortcut attribute.
       nsAutoString shortcutKey;
       current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey);
       if (!shortcutKey.IsEmpty()) {
         // We've got something.
         PRUnichar letter = PRUnichar(charCode); // throw away the high-zero-fill
         if ( shortcutKey.Equals(Substring(&letter, &letter+1),
                                 nsCaseInsensitiveStringComparator()) )  {
           // We match!
-          return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
-                 NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull;
+          nsIMenuFrame *menuFrame;
+          if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) {
+            menuFrame = nsnull;
+          }
+          return menuFrame;
         }
       }
     }
     currFrame = currFrame->GetNextSibling();
   }
 
   // didn't find a matching menu item
 #ifdef XP_WIN
   // behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar
   if (mIsActive) {
     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
     if (soundInterface)
       soundInterface->Beep();
   }
 
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->Rollup();
-
-  SetCurrentMenuItem(nsnull);
-  SetActive(PR_FALSE);
-
+  DismissChain();
 #endif  // #ifdef XP_WIN
 
   return nsnull;
 }
 
-/* virtual */ nsMenuFrame*
+NS_IMETHODIMP 
+nsMenuBarFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& 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
+  nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent);
+  if (result) {
+    // We got one!
+    nsWeakFrame weakFrame(this);
+    nsIFrame* frame = nsnull;
+    CallQueryInterface(result, &frame);
+    nsWeakFrame weakResult(frame);
+    aHandledFlag = PR_TRUE;
+    SetActive(PR_TRUE);
+    if (weakFrame.IsAlive()) {
+      SetCurrentMenuItem(result);
+    }
+    if (weakResult.IsAlive()) {
+      result->OpenMenu(PR_TRUE);
+      if (weakResult.IsAlive()) {
+        result->SelectFirstItem();
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuBarFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag)
+{
+  nsNavigationDirection theDirection;
+  NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode);
+  if (!mCurrentMenu)
+    return NS_OK;
+
+  nsWeakFrame weakFrame(this);
+  PRBool isContainer = PR_FALSE;
+  PRBool isOpen = PR_FALSE;
+  mCurrentMenu->MenuIsContainer(isContainer);
+  mCurrentMenu->MenuIsOpen(isOpen);
+
+  aHandledFlag = PR_FALSE;
+  
+  if (isOpen) {
+    // Let the child menu try to handle it.
+    mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
+  }
+
+  if (aHandledFlag)
+    return NS_OK;
+
+  if NS_DIRECTION_IS_INLINE(theDirection) {
+    
+    nsIMenuFrame* nextItem = (theDirection == eNavigationDirection_End) ?
+                             GetNextMenuItem(mCurrentMenu) : 
+                             GetPreviousMenuItem(mCurrentMenu);
+
+    nsIFrame* nextFrame = nsnull;
+    if (nextItem) {
+      CallQueryInterface(nextItem, &nextFrame);
+    }
+    nsWeakFrame weakNext(nextFrame);
+    SetCurrentMenuItem(nextItem);
+    if (weakNext.IsAlive()) {
+      PRBool nextIsOpen;
+      nextItem->MenuIsOpen(nextIsOpen);
+      if (nextIsOpen) {
+        // Select the first item.
+        nextItem->SelectFirstItem();
+      }
+    }
+  }
+  else if NS_DIRECTION_IS_BLOCK(theDirection) {
+    NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    nsIFrame* frame = nsnull;
+    CallQueryInterface(mCurrentMenu, &frame);
+    nsWeakFrame weakCurrentMenu(frame);
+    nsIMenuFrame* currentMenu = mCurrentMenu;
+     // Open the menu and select its first item.
+    currentMenu->OpenMenu(PR_TRUE);
+    if (weakCurrentMenu.IsAlive()) {
+      currentMenu->SelectFirstItem();
+    }
+  }
+
+  return NS_OK;
+}
+
+/* virtual */ nsIMenuFrame*
+nsMenuBarFrame::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*
+nsMenuBarFrame::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*
 nsMenuBarFrame::GetCurrentMenuItem()
 {
   return mCurrentMenu;
 }
 
-NS_IMETHODIMP
-nsMenuBarFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
+
+NS_IMETHODIMP nsMenuBarFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
 {
   if (mCurrentMenu == aMenuItem)
     return NS_OK;
 
+  PRBool wasOpen = PR_FALSE;
+  
+  // check if there's an open context menu, we ignore this
+  if (nsMenuFrame::GetContextMenu())
+    return NS_OK;
+
   nsWeakFrame weakFrame(this);
-  if (mCurrentMenu)
-    mCurrentMenu->SelectMenu(PR_FALSE);
+
+  // Unset the current child.
+  if (mCurrentMenu) {
+    nsIFrame* frame = nsnull;
+    CallQueryInterface(mCurrentMenu, &frame);
+    nsWeakFrame weakCurrentMenu(frame);
+    nsIMenuFrame* currentMenu = mCurrentMenu;
+    currentMenu->MenuIsOpen(wasOpen);
+    currentMenu->SelectMenu(PR_FALSE);
+    if (wasOpen && weakCurrentMenu.IsAlive()) {
+      currentMenu->OpenMenu(PR_FALSE);
+    }
+  }
+
+  NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+
 
-  if (aMenuItem)
+  // Set the new child.
+  if (aMenuItem) {
+    nsIFrame* newMenu = nsnull;
+    CallQueryInterface(aMenuItem, &newMenu);
+    nsWeakFrame weakNewMenu(newMenu);
     aMenuItem->SelectMenu(PR_TRUE);
+    NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK);
+    aMenuItem->MarkAsGenerated(); // Have the menu building. Get it ready to be shown.
+    NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK);
+
+    PRBool isDisabled = PR_FALSE;
+    aMenuItem->MenuIsDisabled(isDisabled);
+    if (wasOpen&&!isDisabled)
+      aMenuItem->OpenMenu(PR_TRUE);
+    ClearRecentlyRolledUp();
+  }
 
   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
   mCurrentMenu = aMenuItem;
 
   return NS_OK;
 }
 
-void
-nsMenuBarFrame::CurrentMenuIsBeingDestroyed()
+
+NS_IMETHODIMP 
+nsMenuBarFrame::Escape(PRBool& aHandledFlag)
 {
-  mCurrentMenu->SelectMenu(PR_FALSE);
-  mCurrentMenu = nsnull;
+  if (!mCurrentMenu)
+    return NS_OK;
+
+  nsWeakFrame weakFrame(this);
+  // See if our menu is open.
+  PRBool isOpen = PR_FALSE;
+  mCurrentMenu->MenuIsOpen(isOpen);
+  if (isOpen) {
+    // Let the child menu handle this.
+    aHandledFlag = PR_FALSE;
+    mCurrentMenu->Escape(aHandledFlag);
+    NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    if (!aHandledFlag) {
+      // Close up this menu but keep our current menu item
+      // designation.
+      mCurrentMenu->OpenMenu(PR_FALSE);
+      NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+    }
+    return NS_OK;
+  }
+
+  // Clear our current menu item if we've got one.
+  SetCurrentMenuItem(nsnull);
+  NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
+
+  SetActive(PR_FALSE);
+
+  // Clear out our dismissal listener
+  nsMenuDismissalListener::Shutdown();
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsMenuBarFrame::Enter()
+{
+  if (!mCurrentMenu)
+    return NS_OK;
+
+  ClearRecentlyRolledUp();
+
+  // See if our menu is open.
+  PRBool isOpen = PR_FALSE;
+  mCurrentMenu->MenuIsOpen(isOpen);
+  if (isOpen) {
+    // Let the child menu handle this.
+    mCurrentMenu->Enter();
+    return NS_OK;
+  }
+
+  // It's us. Open the current menu.
+  mCurrentMenu->OpenMenu(PR_TRUE);
+  mCurrentMenu->SelectFirstItem();
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
-nsMenuBarFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
-                               PRBool aSelectFirstItem)
+nsMenuBarFrame::ClearRecentlyRolledUp()
 {
-  if (mCurrentMenu == aMenuItem)
-    return NS_OK;
+  // We're no longer in danger of popping down a menu from the same 
+  // click on the menubar, which was supposed to toggle the menu closed
+  mRecentRollupMenu = nsnull;
 
-  // check if there's an open context menu, we ignore this
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm && pm->HasContextMenu(nsnull))
-    return NS_OK;
+  return NS_OK;
+}
 
-  // Unset the current child.
-  PRBool wasOpen = PR_FALSE;
-  if (mCurrentMenu) {
-    wasOpen = mCurrentMenu->IsOpen();
-    mCurrentMenu->SelectMenu(PR_FALSE);
-    if (wasOpen) {
-      nsMenuPopupFrame* popupFrame = mCurrentMenu->GetPopup();
-      if (popupFrame)
-        pm->HidePopup(popupFrame->GetContent(), PR_FALSE, PR_FALSE, PR_TRUE);
-    }
+NS_IMETHODIMP
+nsMenuBarFrame::RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp)
+{
+  // 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.
+  *aJustRolledUp = (mRecentRollupMenu == aMenuFrame);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMenuBarFrame::HideChain()
+{
+  // XXX hack if a context menu is active, do an Escape, which is
+  // currently bugged and destroys everything.  We need to close
+  // the context menu first, otherwise SetCurrentMenuItem above
+  // would get blocked.
+  if (nsMenuFrame::GetContextMenu()) {
+    PRBool dummy;
+    mCurrentMenu->Escape(dummy);
   }
 
-  // set to null first in case the IsAlive check below returns false
-  mCurrentMenu = nsnull;
+  // Stop capturing rollups
+  // (must do this during Hide, which happens before the menu item is executed,
+  // since this reinstates normal event handling.)
+  nsMenuDismissalListener::Shutdown();
 
-  // Set the new child.
-  if (aMenuItem) {
-    nsCOMPtr<nsIContent> content = aMenuItem->GetContent();
-    nsWeakFrame weakNewMenu(aMenuItem);
-    aMenuItem->SelectMenu(PR_TRUE);
-    NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK);
-    mCurrentMenu = aMenuItem;
-    if (wasOpen && !aMenuItem->IsDisabled())
-      pm->ShowMenu(content, aSelectFirstItem, PR_TRUE);
+  ClearRecentlyRolledUp();
+  if (mCurrentMenu) {
+    mCurrentMenu->ActivateMenu(PR_FALSE);
+    mCurrentMenu->SelectMenu(PR_FALSE);
+    mRecentRollupMenu = mCurrentMenu;
+  }
+
+  if (mIsActive) {
+    ToggleMenuActiveState();
   }
 
   return NS_OK;
 }
 
-nsMenuFrame*
-nsMenuBarFrame::Enter()
+NS_IMETHODIMP
+nsMenuBarFrame::DismissChain()
+{
+  // Stop capturing rollups
+  nsMenuDismissalListener::Shutdown();
+  nsWeakFrame weakFrame(this);
+  SetCurrentMenuItem(nsnull);
+  if (weakFrame.IsAlive()) {
+    SetActive(PR_FALSE);
+  }
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsMenuBarFrame::KillPendingTimers ( )
 {
-  if (!mCurrentMenu)
-    return nsnull;
+  return NS_OK;
+
+} // KillPendingTimers
+
 
-  if (mCurrentMenu->IsOpen())
-    return mCurrentMenu->Enter();
-
-  return mCurrentMenu;
+NS_IMETHODIMP
+nsMenuBarFrame::GetWidget(nsIWidget **aWidget)
+{
+  // (pinkerton/hyatt)
+  // since the menubar is a menuparent but not a menuItem, the win32 rollup code
+  // would erroneously add the entire top-level window to the widget list built up for
+  // determining if a click is in a submenu's menu chain. To get around this, we just 
+  // don't let the menubar have a widget. Things seem to work because the dismissal
+  // listener is registered when a new menu is popped up, which is the only real reason
+  // why we need a widget at all.
+  *aWidget = nsnull;
+  return NS_OK;
 }
 
-PRBool
-nsMenuBarFrame::MenuClosed()
+NS_IMETHODIMP
+nsMenuBarFrame::InstallKeyboardNavigator()
 {
-  SetActive(PR_FALSE);
-  if (!mIsActive && mCurrentMenu) {
-    mCurrentMenu->SelectMenu(PR_FALSE);
-    mCurrentMenu = nsnull;
-    return PR_TRUE;
-  }
-  return PR_FALSE;
+  if (mKeyboardNavigator)
+    return NS_OK;
+
+  mKeyboardNavigator = new nsMenuListener(this);
+  NS_IF_ADDREF(mKeyboardNavigator);
+
+  mTarget->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); 
+  mTarget->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);  
+  mTarget->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);   
+
+  nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_TRUE);
+
+  return NS_OK;
 }
 
-void
-nsMenuBarFrame::InstallKeyboardNavigator()
+NS_IMETHODIMP
+nsMenuBarFrame::RemoveKeyboardNavigator()
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->SetActiveMenuBar(this, PR_TRUE);
+  if (!mKeyboardNavigator || mIsActive)
+    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;
 }
 
-void
-nsMenuBarFrame::RemoveKeyboardNavigator()
+// helpers ///////////////////////////////////////////////////////////
+
+PRBool 
+nsMenuBarFrame::IsValidItem(nsIContent* aContent)
 {
-  if (!mIsActive) {
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (pm)
-      pm->SetActiveMenuBar(this, PR_FALSE);
-  }
+  nsIAtom *tag = aContent->Tag();
+
+  return ((tag == nsGkAtoms::menu ||
+           tag == nsGkAtoms::menuitem) &&
+          !IsDisabled(aContent));
+}
+
+PRBool 
+nsMenuBarFrame::IsDisabled(nsIContent* aContent)
+{
+  return aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
+                               nsGkAtoms::_true, eCaseMatters);
 }
 
 void
 nsMenuBarFrame::Destroy()
 {
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->SetActiveMenuBar(this, PR_FALSE);
-
   mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); 
   mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE);  
   mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE);
 
   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE);
   mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE);
 
   NS_IF_RELEASE(mMenuBarListener);
 
   nsBoxFrame::Destroy();
 }
+
--- a/layout/xul/base/src/nsXULTooltipListener.cpp
+++ b/layout/xul/base/src/nsXULTooltipListener.cpp
@@ -41,29 +41,29 @@
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDocument.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsIFrame.h"
+#include "nsIMenuFrame.h"
 #include "nsIPopupBoxObject.h"
 #include "nsIServiceManager.h"
 #ifdef MOZ_XUL
 #include "nsIDOMNSDocument.h"
 #include "nsITreeView.h"
 #endif
 #include "nsGUIEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsPresContext.h"
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentUtils.h"
-#include "nsXULPopupManager.h"
 #include "nsIRootBox.h"
 
 nsXULTooltipListener* nsXULTooltipListener::mInstance = nsnull;
 
 //////////////////////////////////////////////////////////////////////////
 //// nsISupports
 
 nsXULTooltipListener::nsXULTooltipListener()
@@ -204,18 +204,18 @@ nsXULTooltipListener::MouseMove(nsIDOMEv
     return NS_OK;
 
   // stash the coordinates of the event so that we can still get back to it from within the 
   // timer callback. On win32, we'll get a MouseMove event even when a popup goes away --
   // even when the mouse doesn't change position! To get around this, we make sure the
   // mouse has really moved before proceeding.
   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
   PRInt32 newMouseX, newMouseY;
-  mouseEvent->GetScreenX(&newMouseX);
-  mouseEvent->GetScreenY(&newMouseY);
+  mouseEvent->GetClientX(&newMouseX);
+  mouseEvent->GetClientY(&newMouseY);
   if (mMouseClientX == newMouseX && mMouseClientY == newMouseY)
     return NS_OK;
   mMouseClientX = newMouseX;
   mMouseClientY = newMouseY;
 
   nsCOMPtr<nsIDOMEventTarget> eventTarget;
   aMouseEvent->GetCurrentTarget(getter_AddRefs(eventTarget));
   mSourceNode = do_QueryInterface(eventTarget);
@@ -408,18 +408,22 @@ nsXULTooltipListener::ShowTooltip()
 
       xulDoc->SetTooltipNode(mTargetNode);
       LaunchTooltip();
       mTargetNode = nsnull;
 
       // at this point, |mCurrentTooltip| holds the content node of
       // the tooltip. If there is an attribute on the popup telling us
       // not to create the auto-hide timer, don't.
-      if (mCurrentTooltip->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
-                                       nsGkAtoms::_true, eCaseMatters))
+      nsCOMPtr<nsIDOMElement> tooltipEl(do_QueryInterface(mCurrentTooltip));
+      if (!tooltipEl)
+        return NS_ERROR_FAILURE;
+      nsAutoString noAutoHide;
+      tooltipEl->GetAttribute(NS_LITERAL_STRING("noautohide"), noAutoHide);
+      if (!noAutoHide.EqualsLiteral("true"))
         CreateAutoHideTimer();
 
       // listen for popuphidden on the tooltip node, so that we can
       // be sure DestroyPopup is called even if someone else closes the tooltip
       nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(mCurrentTooltip));
       evtTarget->AddEventListener(NS_LITERAL_STRING("popuphiding"), 
                                   (nsIDOMMouseListener*)this, PR_FALSE);
 
@@ -480,48 +484,76 @@ SetTitletipLabel(nsITreeBoxObject* aTree
 #endif
 
 void
 nsXULTooltipListener::LaunchTooltip()
 {
   if (!mCurrentTooltip)
     return;
 
+  nsCOMPtr<nsIBoxObject> popupBox;
+  nsCOMPtr<nsIDOMXULElement> xulTooltipEl(do_QueryInterface(mCurrentTooltip));
+  if (!xulTooltipEl) {
+    NS_ERROR("tooltip isn't a XUL element!");
+    return;
+  }
+
+  xulTooltipEl->GetBoxObject(getter_AddRefs(popupBox));
+  nsCOMPtr<nsIPopupBoxObject> popupBoxObject(do_QueryInterface(popupBox));
+  if (popupBoxObject) {
+    PRInt32 x = mMouseClientX;
+    PRInt32 y = mMouseClientY;
 #ifdef MOZ_XUL
-  if (mIsSourceTree && mNeedTitletip) {
-    nsCOMPtr<nsITreeBoxObject> obx;
-    GetSourceTreeBoxObject(getter_AddRefs(obx));
+    if (mIsSourceTree && mNeedTitletip) {
+      nsCOMPtr<nsITreeBoxObject> obx;
+      GetSourceTreeBoxObject(getter_AddRefs(obx));
+#ifdef DEBUG_crap
+      GetTreeCellCoords(obx, mSourceNode,
+                        mLastTreeRow, mLastTreeCol, &x, &y);
+#endif
 
-    SetTitletipLabel(obx, mCurrentTooltip, mLastTreeRow, mLastTreeCol);
+      SetTitletipLabel(obx, mCurrentTooltip, mLastTreeRow, mLastTreeCol);
+      if (!mCurrentTooltip) {
+        // Because of mutation events, mCurrentTooltip can be null.
+        return;
+      }
+      mCurrentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip,
+                               NS_LITERAL_STRING("true"), PR_TRUE);
+    } else {
+      mCurrentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip,
+                                 PR_TRUE);
+    }
     if (!mCurrentTooltip) {
       // Because of mutation events, mCurrentTooltip can be null.
       return;
     }
-    mCurrentTooltip->SetAttr(nsnull, nsGkAtoms::titletip, NS_LITERAL_STRING("true"), PR_TRUE);
-  } else {
-    mCurrentTooltip->UnsetAttr(nsnull, nsGkAtoms::titletip, PR_TRUE);
-  }
-  if (!mCurrentTooltip) {
-    // Because of mutation events, mCurrentTooltip can be null.
-    return;
-  }
 #endif
 
-  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-  if (pm)
-    pm->ShowPopupAtScreen(mCurrentTooltip, mMouseClientX, mMouseClientY, PR_FALSE);
+    nsCOMPtr<nsIDOMElement> targetEl(do_QueryInterface(mSourceNode));
+    popupBoxObject->ShowPopup(targetEl, xulTooltipEl, x, y,
+                              NS_LITERAL_STRING("tooltip").get(),
+                              NS_LITERAL_STRING("none").get(),
+                              NS_LITERAL_STRING("topleft").get());
+  }
+
+  return;
 }
 
 nsresult
 nsXULTooltipListener::HideTooltip()
 {
   if (mCurrentTooltip) {
-    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
-    if (pm)
-      pm->HidePopup(mCurrentTooltip, PR_FALSE, PR_FALSE, PR_FALSE);
+    // hide the popup through its box object
+    nsCOMPtr<nsIDOMXULElement> tooltipEl(do_QueryInterface(mCurrentTooltip));
+    nsCOMPtr<nsIBoxObject> boxObject;
+    if (tooltipEl)
+      tooltipEl->GetBoxObject(getter_AddRefs(boxObject));
+    nsCOMPtr<nsIPopupBoxObject> popupObject(do_QueryInterface(boxObject));
+    if (popupObject)
+      popupObject->HidePopup();
   }
 
   DestroyTooltip();
   return NS_OK;
 }
 
 static void
 GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 
@@ -632,19 +664,20 @@ nsXULTooltipListener::GetTooltipFor(nsIC
   }
 
   // Submenus can't be used as tooltips, see bug 288763.
   nsIContent* parent = tooltip->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) {
-      NS_WARNING("Menu cannot be used as a tooltip");
-      return NS_ERROR_FAILURE;
+    if (frame) {
+      nsIMenuFrame* menu = nsnull;
+      CallQueryInterface(frame, &menu);
+      NS_ENSURE_FALSE(menu, NS_ERROR_FAILURE);
     }
   }
 
   tooltip.swap(*aTooltip);
   return rv;
 }
 
 nsresult
@@ -703,17 +736,17 @@ nsXULTooltipListener::KillTooltipTimer()
 
 void
 nsXULTooltipListener::CreateAutoHideTimer()
 {
   if (mAutoHideTimer) {
     mAutoHideTimer->Cancel();
     mAutoHideTimer = nsnull;
   }
-
+  
   mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1");
   if ( mAutoHideTimer )
     mAutoHideTimer->InitWithFuncCallback(sAutoHideCallback, this, kTooltipAutoHideTime, 
                                          nsITimer::TYPE_ONE_SHOT);
 }
 
 void
 nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener)