From 1c5ceca0872c9ef8f4d94a076ca8ab66d7af5bc6 Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Mon, 31 Mar 2014 08:42:33 -0400 Subject: [PATCH] Bug 610545, check for a transition earlier to avoid assertion if a panel is opened while it is closing, r=neil --- layout/xul/nsXULPopupManager.cpp | 91 +++++++++++++++----------------- layout/xul/nsXULPopupManager.h | 4 +- 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp index 70f5cad4728..e3403756714 100644 --- a/layout/xul/nsXULPopupManager.cpp +++ b/layout/xul/nsXULPopupManager.cpp @@ -946,12 +946,15 @@ public: { mContent->RemoveSystemEventListener(NS_LITERAL_STRING("transitionend"), this, false); + nsMenuPopupFrame* popupFrame = do_QueryFrame(mContent->GetPrimaryFrame()); + // Now hide the popup. There could be other properties transitioning, but // we'll assume they all end at the same time and just hide the popup upon // the first one ending. nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm) { - pm->HidePopupFrame(mContent, mDeselectMenu); + if (pm && popupFrame) { + pm->HidePopupCallback(mContent, popupFrame, nullptr, nullptr, + popupFrame->PopupType(), mDeselectMenu); } return NS_OK; @@ -966,8 +969,7 @@ nsXULPopupManager::HidePopupCallback(nsIContent* aPopup, nsIContent* aNextPopup, nsIContent* aLastPopup, nsPopupType aPopupType, - bool aDeselectMenu, - bool aIsRollup) + bool aDeselectMenu) { if (mCloseTimer && mTimerMenu == aPopupFrame) { mCloseTimer->Cancel(); @@ -1003,31 +1005,18 @@ nsXULPopupManager::HidePopupCallback(nsIContent* aPopup, delete item; - // If the popup has an animate attribute and it is not set to false, assume - // that it has a closing transition and wait for it to finish. The transition - // may still occur either way, but the view will be hidden and you won't be - // able to see it. If there is a next popup, indicating that mutliple popups - // are rolling up, don't wait and hide the popup right away since the effect - // would likely be undesirable. This also does a quick check to see if the - // popup has a transition defined, and skips the wait if not. - if (!aNextPopup && aPopup->HasAttr(kNameSpaceID_None, nsGkAtoms::animate) && - aPopupFrame->StyleDisplay()->mTransitionPropertyCount > 0) { - nsAutoString animate; - aPopup->GetAttr(kNameSpaceID_None, nsGkAtoms::animate, animate); + nsWeakFrame weakFrame(aPopupFrame); + aPopupFrame->HidePopup(aDeselectMenu, ePopupClosed); + ENSURE_TRUE(weakFrame.IsAlive()); - // If animate="false" then don't transition at all. If animate="cancel", - // only show the transition if cancelling the popup or rolling up. - // Otherwise, always show the transition. - if (!animate.EqualsLiteral("false") && - (!animate.EqualsLiteral("cancel") || aIsRollup)) { - nsCOMPtr ender = new TransitionEnder(aPopup, aDeselectMenu); - aPopup->AddSystemEventListener(NS_LITERAL_STRING("transitionend"), - ender, false, false); - return; - } - } - - HidePopupFrame(aPopup, aDeselectMenu); + // send the popuphidden event synchronously. This event has no default + // behaviour. + nsEventStatus status = nsEventStatus_eIgnore; + WidgetMouseEvent event(true, NS_XUL_POPUP_HIDDEN, nullptr, + WidgetMouseEvent::eReal); + EventDispatcher::Dispatch(aPopup, aPopupFrame->PresContext(), + &event, nullptr, &status); + ENSURE_TRUE(weakFrame.IsAlive()); // if there are more popups to close, look for the next one if (aNextPopup && aPopup != aLastPopup) { @@ -1069,26 +1058,6 @@ nsXULPopupManager::HidePopupCallback(nsIContent* aPopup, } } -void -nsXULPopupManager::HidePopupFrame(nsIContent* aPopup, bool aDeselectMenu) -{ - nsMenuPopupFrame* popupFrame = do_QueryFrame(aPopup->GetPrimaryFrame()); - if (!popupFrame) - return; - - nsWeakFrame weakFrame(popupFrame); - popupFrame->HidePopup(aDeselectMenu, ePopupClosed); - ENSURE_TRUE(weakFrame.IsAlive()); - - // send the popuphidden event synchronously. This event has no default - // behaviour. - nsEventStatus status = nsEventStatus_eIgnore; - WidgetMouseEvent event(true, NS_XUL_POPUP_HIDDEN, nullptr, - WidgetMouseEvent::eReal); - EventDispatcher::Dispatch(aPopup, popupFrame->PresContext(), - &event, nullptr, &status); -} - void nsXULPopupManager::HidePopup(nsIFrame* aFrame) { @@ -1393,8 +1362,32 @@ nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup, popupFrame->SetPopupState(ePopupOpenAndVisible); } else { + // If the popup has an animate attribute and it is not set to false, assume + // that it has a closing transition and wait for it to finish. The transition + // may still occur either way, but the view will be hidden and you won't be + // able to see it. If there is a next popup, indicating that mutliple popups + // are rolling up, don't wait and hide the popup right away since the effect + // would likely be undesirable. This also does a quick check to see if the + // popup has a transition defined, and skips the wait if not. + if (!aNextPopup && aPopup->HasAttr(kNameSpaceID_None, nsGkAtoms::animate) && + popupFrame->StyleDisplay()->mTransitionPropertyCount > 0) { + nsAutoString animate; + aPopup->GetAttr(kNameSpaceID_None, nsGkAtoms::animate, animate); + + // If animate="false" then don't transition at all. If animate="cancel", + // only show the transition if cancelling the popup or rolling up. + // Otherwise, always show the transition. + if (!animate.EqualsLiteral("false") && + (!animate.EqualsLiteral("cancel") || aIsRollup)) { + nsCOMPtr ender = new TransitionEnder(aPopup, aDeselectMenu); + aPopup->AddSystemEventListener(NS_LITERAL_STRING("transitionend"), + ender, false, false); + return; + } + } + HidePopupCallback(aPopup, popupFrame, aNextPopup, aLastPopup, - aPopupType, aDeselectMenu, aIsRollup); + aPopupType, aDeselectMenu); } } } diff --git a/layout/xul/nsXULPopupManager.h b/layout/xul/nsXULPopupManager.h index 9f3d2e0e117..15b552e5905 100644 --- a/layout/xul/nsXULPopupManager.h +++ b/layout/xul/nsXULPopupManager.h @@ -640,9 +640,7 @@ protected: nsIContent* aNextPopup, nsIContent* aLastPopup, nsPopupType aPopupType, - bool aDeselectMenu, - bool aIsRollup); - void HidePopupFrame(nsIContent* aPopup, bool aDeselectMenu); + bool aDeselectMenu); /** * Fire a popupshowing event on the popup and then open the popup.