Bug 389996, menubar menus flicker when switching between them, r+sr=bz, a=dbaron
authorenndeakin@sympatico.ca
Tue, 04 Sep 2007 10:42:38 -0700
changeset 5647 60b5828c26010a7cf0408080914c17749a469bd1
parent 5646 f919d5d4b7c4484f726e7555987a3f0f5b4a0dcb
child 5648 412cc3929707170f23248ea3e777a343ce90d63d
child 5649 9e80cfa6867e8b5953ac32617a673ee064d359de
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs389996
milestone1.9a8pre
Bug 389996, menubar menus flicker when switching between them, r+sr=bz, a=dbaron
layout/xul/base/src/nsMenuBarFrame.cpp
toolkit/content/tests/widgets/window_menubar.xul
--- a/layout/xul/base/src/nsMenuBarFrame.cpp
+++ b/layout/xul/base/src/nsMenuBarFrame.cpp
@@ -336,55 +336,91 @@ nsMenuBarFrame::SetCurrentMenuItem(nsMen
 
 void
 nsMenuBarFrame::CurrentMenuIsBeingDestroyed()
 {
   mCurrentMenu->SelectMenu(PR_FALSE);
   mCurrentMenu = nsnull;
 }
 
+class nsMenuBarSwitchMenu : public nsRunnable
+{
+public:
+  nsMenuBarSwitchMenu(nsIContent *aOldMenu,
+                      nsIContent *aNewMenu,
+                      PRBool aSelectFirstItem)
+    : mOldMenu(aOldMenu), mNewMenu(aNewMenu), mSelectFirstItem(aSelectFirstItem)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+    if (!pm)
+      return NS_ERROR_UNEXPECTED;
+
+    if (mOldMenu)
+      pm->HidePopup(mOldMenu, PR_FALSE, PR_FALSE, PR_FALSE);
+    if (mNewMenu)
+      pm->ShowMenu(mNewMenu, mSelectFirstItem, PR_FALSE);
+
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIContent> mOldMenu;
+  nsCOMPtr<nsIContent> mNewMenu;
+  PRBool mSelectFirstItem;
+};
+
 NS_IMETHODIMP
 nsMenuBarFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
                                PRBool aSelectFirstItem)
 {
   if (mCurrentMenu == aMenuItem)
     return NS_OK;
 
   // check if there's an open context menu, we ignore this
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm && pm->HasContextMenu(nsnull))
     return NS_OK;
 
+  nsIContent* aOldMenu = nsnull, *aNewMenu = nsnull;
+  
   // 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);
+        aOldMenu = popupFrame->GetContent();
     }
   }
 
   // set to null first in case the IsAlive check below returns false
   mCurrentMenu = nsnull;
 
   // 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);
+      aNewMenu = content;
   }
 
-  return NS_OK;
+  // use an event so that hiding and showing can be done synchronously, which
+  // avoids flickering
+  nsCOMPtr<nsIRunnable> event =
+    new nsMenuBarSwitchMenu(aOldMenu, aNewMenu, aSelectFirstItem);
+  return NS_DispatchToCurrentThread(event);
 }
 
 nsMenuFrame*
 nsMenuBarFrame::Enter()
 {
   if (!mCurrentMenu)
     return nsnull;
 
--- a/toolkit/content/tests/widgets/window_menubar.xul
+++ b/toolkit/content/tests/widgets/window_menubar.xul
@@ -142,18 +142,18 @@ var popupTests = [
 
 {
   // cursor right should skip the disabled menu and move to the edit menu
   testname: "cursor right skip disabled",
   events: function() {
     var elist = [
       // the file menu gets deactivated, the file menu gets hidden, then
       // the edit menu is activated
-      "DOMMenuItemInactive filemenu", "popuphiding filepopup",
-      "popuphidden filepopup", "DOMMenuItemActive editmenu",
+      "DOMMenuItemInactive filemenu", "DOMMenuItemActive editmenu",
+      "popuphiding filepopup", "popuphidden filepopup",
       // the popupshowing event gets fired when showing the edit menu.
       // The item from the file menu doesn't get deactivated until the
       // next item needs to be selected
       "popupshowing editpopup", "DOMMenuItemInactive item1",
       // not sure why the menu inactivated event is firing so late
       "DOMMenuInactive filepopup", "DOMMenuBarInactive menubar",
       // the new menu gets disabled and reenabled again, I suspect a
       // check isn't done to see if the old value equals the new value