From c0577cd8de6a98dac605a6f37cf6db9050545619 Mon Sep 17 00:00:00 2001 From: "enndeakin@sympatico.ca" Date: Wed, 15 Aug 2007 07:03:21 -0700 Subject: [PATCH] Bug 390589, capture not being released in some cases when popup is closed, fixes some focus issues, r+sr=bz,a=dbaron --- layout/xul/base/public/nsXULPopupManager.h | 3 ++ layout/xul/base/src/nsXULPopupManager.cpp | 60 ++++++++++++++-------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/layout/xul/base/public/nsXULPopupManager.h b/layout/xul/base/public/nsXULPopupManager.h index 2bad78a382e7..90b396525ad6 100644 --- a/layout/xul/base/public/nsXULPopupManager.h +++ b/layout/xul/base/public/nsXULPopupManager.h @@ -561,6 +561,9 @@ protected: // get the nsMenuPopupFrame, if any, for the given content node nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent); + // return the topmost menu, skipping over invisible popups + nsMenuChainItem* GetTopVisibleMenu(); + // callbacks for ShowPopup and HidePopup as events may be done asynchronously void ShowPopupCallback(nsIContent* aPopup, nsMenuPopupFrame* aPopupFrame, diff --git a/layout/xul/base/src/nsXULPopupManager.cpp b/layout/xul/base/src/nsXULPopupManager.cpp index 9e36d9eef029..1e38ba32a8e2 100644 --- a/layout/xul/base/src/nsXULPopupManager.cpp +++ b/layout/xul/base/src/nsXULPopupManager.cpp @@ -170,7 +170,8 @@ NS_IMETHODIMP nsXULPopupManager::ShouldRollupOnMouseWheelEvent(PRBool *aShouldRo { // should rollup only for autocomplete widgets // XXXndeakin this should really be something the popup has more control over - *aShouldRollup = (mCurrentMenu && !mCurrentMenu->Frame()->IsMenu()); + nsMenuChainItem* item = GetTopVisibleMenu(); + *aShouldRollup = (item && !item->Frame()->IsMenu()); return NS_OK; } @@ -189,7 +190,7 @@ nsXULPopupManager::GetSubmenuWidgetChain(nsISupportsArray **_retval) // panels will roll up. If the click is inside a popup, they will not roll up nsresult rv = NS_NewISupportsArray(_retval); NS_ENSURE_SUCCESS(rv, rv); - nsMenuChainItem* item = mCurrentMenu; + nsMenuChainItem* item = GetTopVisibleMenu(); while (item) { nsCOMPtr widget; item->Frame()->GetWidget(getter_AddRefs(widget)); @@ -238,6 +239,15 @@ nsXULPopupManager::GetPopupFrameForContent(nsIContent* aContent) (GetFrameOfTypeForContent(aContent, nsGkAtoms::menuPopupFrame)); } +nsMenuChainItem* +nsXULPopupManager::GetTopVisibleMenu() +{ + nsMenuChainItem* item = mCurrentMenu; + while (item && item->Frame()->PopupState() == ePopupInvisible) + item = item->GetParent(); + return item; +} + void nsXULPopupManager::GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset) { @@ -676,7 +686,7 @@ nsXULPopupManager::HidePopupAfterDelay(nsMenuPopupFrame* aPopup) void nsXULPopupManager::HidePopupsInDocument(nsIDocument* aDocument) { - nsMenuChainItem* item = mCurrentMenu; + nsMenuChainItem* item = GetTopVisibleMenu(); while (item) { nsMenuChainItem* parent = item->GetParent(); if (item->Content()->GetOwnerDoc() == aDocument) { @@ -697,6 +707,8 @@ nsXULPopupManager::HidePopupsInDocument(nsIDocument* aDocument) } item = parent; } + + SetCaptureState(nsnull); } void @@ -707,7 +719,7 @@ nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent) // opens a modal dialog. The views associated with the popups needed to be // hidden and the accesibility events fired before the command executes, but // the popuphiding/popuphidden events are fired afterwards. - nsMenuChainItem* item = mCurrentMenu; + nsMenuChainItem* item = GetTopVisibleMenu(); while (item) { // if it isn't a , don't close it automatically if (!item->IsMenu()) @@ -718,6 +730,8 @@ nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent) item = next; } + SetCaptureState(nsnull); + // 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). @@ -867,7 +881,7 @@ nsXULPopupManager::IsPopupOpen(nsIContent* aPopup) PRBool nsXULPopupManager::IsPopupOpenForMenuParent(nsIMenuParent* aMenuParent) { - nsMenuChainItem* item = mCurrentMenu; + nsMenuChainItem* item = GetTopVisibleMenu(); while (item) { nsMenuPopupFrame* popup = item->Frame(); if (popup && popup->IsOpen()) { @@ -891,7 +905,8 @@ nsXULPopupManager::GetOpenPopups() nsMenuChainItem* item = mCurrentMenu; while (item) { - popups.AppendElement(static_cast(item->Frame())); + if (item->Frame()->PopupState() != ePopupInvisible) + popups.AppendElement(static_cast(item->Frame())); item = item->GetParent(); } @@ -1037,7 +1052,7 @@ nsXULPopupManager::PopupDestroyed(nsMenuPopupFrame* aPopup) PRBool nsXULPopupManager::HasContextMenu(nsMenuPopupFrame* aPopup) { - nsMenuChainItem* item = mCurrentMenu; + nsMenuChainItem* item = GetTopVisibleMenu(); while (item && item->Frame() != aPopup) { if (item->IsContextMenu()) return PR_TRUE; @@ -1050,7 +1065,8 @@ nsXULPopupManager::HasContextMenu(nsMenuPopupFrame* aPopup) void nsXULPopupManager::SetCaptureState(nsIContent* aOldPopup) { - if (mCurrentMenu && aOldPopup == mCurrentMenu->Content()) + nsMenuChainItem* item = GetTopVisibleMenu(); + if (item && aOldPopup == item->Content()) return; if (mWidget) { @@ -1058,8 +1074,8 @@ nsXULPopupManager::SetCaptureState(nsIContent* aOldPopup) mWidget = nsnull; } - if (mCurrentMenu) { - nsMenuPopupFrame* popup = mCurrentMenu->Frame(); + if (item) { + nsMenuPopupFrame* popup = item->Frame(); nsCOMPtr widget; popup->GetWidget(getter_AddRefs(widget)); if (widget) { @@ -1076,9 +1092,10 @@ void nsXULPopupManager::UpdateKeyboardListeners() { nsCOMPtr newTarget; - if (mCurrentMenu) { - if (!mCurrentMenu->IgnoreKeys()) - newTarget = do_QueryInterface(mCurrentMenu->Content()->GetDocument()); + nsMenuChainItem* item = GetTopVisibleMenu(); + if (item) { + if (!item->IgnoreKeys()) + newTarget = do_QueryInterface(item->Content()->GetDocument()); } else if (mActiveMenuBar) { newTarget = do_QueryInterface(mActiveMenuBar->GetContent()->GetDocument()); @@ -1226,8 +1243,9 @@ PRBool nsXULPopupManager::HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, nsMenuPopupFrame* aFrame) { - if (!aFrame && mCurrentMenu) - aFrame = mCurrentMenu->Frame(); + nsMenuChainItem* item = GetTopVisibleMenu(); + if (!aFrame && item) + aFrame = item->Frame(); if (aFrame) { PRBool action; @@ -1266,7 +1284,7 @@ nsXULPopupManager::HandleKeyboardNavigation(PRUint32 aKeyCode) // navigate up through the open menus, looking for the topmost one // in the same hierarchy nsMenuChainItem* item = nsnull; - nsMenuChainItem* nextitem = mCurrentMenu; + nsMenuChainItem* nextitem = GetTopVisibleMenu(); while (nextitem) { item = nextitem; @@ -1609,8 +1627,9 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent) } else if (theChar == NS_VK_ESCAPE) { // Pressing Escape hides one level of menus only - if (mCurrentMenu) - HidePopup(mCurrentMenu->Content(), PR_FALSE, PR_FALSE, PR_FALSE); + nsMenuChainItem* item = GetTopVisibleMenu(); + if (item) + HidePopup(item->Content(), PR_FALSE, PR_FALSE, PR_FALSE); } else if (theChar == NS_VK_TAB) { Rollup(); @@ -1621,8 +1640,9 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent) // Otherwise, tell the active menubar, if any, to activate the menu. The // Enter method will return a menu if one needs to be opened as a result. nsMenuFrame* menuToOpen = nsnull; - if (mCurrentMenu) - menuToOpen = mCurrentMenu->Frame()->Enter(); + nsMenuChainItem* item = GetTopVisibleMenu(); + if (item) + menuToOpen = item->Frame()->Enter(); else if (mActiveMenuBar) menuToOpen = mActiveMenuBar->Enter(); if (menuToOpen) {