Bug 404766, right click on a bookmark menuitem closes it instead of opening another context menu, r+sr=roc

This commit is contained in:
Neil Deakin 2009-06-12 14:23:16 -04:00
parent b8523ccc63
commit 1ff1ebff1c
16 changed files with 147 additions and 53 deletions

View File

@ -1285,7 +1285,8 @@ nsComboboxControlFrame::GetAdditionalChildListName(PRInt32 aIndex) const
//nsIRollupListener
//----------------------------------------------------------------------
NS_IMETHODIMP
nsComboboxControlFrame::Rollup(nsIContent** aLastRolledUp)
nsComboboxControlFrame::Rollup(PRUint32 aCount,
nsIContent** aLastRolledUp)
{
if (aLastRolledUp)
*aLastRolledUp = nsnull;

View File

@ -195,7 +195,7 @@ public:
* Hide the dropdown menu and stop capturing mouse events.
* @note This method might destroy |this|.
*/
NS_IMETHOD Rollup(nsIContent** aLastRolledUp);
NS_IMETHOD Rollup(PRUint32 aCount, nsIContent** aLastRolledUp);
/**
* A combobox should roll up if a mousewheel event happens outside of
* the popup area.

View File

@ -313,7 +313,7 @@ public:
NS_DECL_NSIROLLUPLISTENER
NS_DECL_NSITIMERCALLBACK
virtual void GetSubmenuWidgetChain(nsTArray<nsIWidget*> *_retval);
virtual PRUint32 GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain);
virtual void AdjustPopupsOnWindowChange(void);
static nsXULPopupManager* sInstance;
@ -451,11 +451,14 @@ public:
* aAsynchronous - true if the first popuphiding event should be sent
* asynchrously. This should be true if HidePopup is called
* from a frame.
* aLastPopup - optional popup to close last when hiding a chain of menus.
* If null, then all popups will be closed.
*/
void HidePopup(nsIContent* aPopup,
PRBool aHideChain,
PRBool aDeselectMenu,
PRBool aAsynchronous);
PRBool aAsynchronous,
nsIContent* aLastPopup = nsnull);
/**
* Hide a popup after a short delay. This is used when rolling over menu items.

View File

@ -170,7 +170,7 @@ nsXULPopupManager::GetInstance()
}
NS_IMETHODIMP
nsXULPopupManager::Rollup(nsIContent** aLastRolledUp)
nsXULPopupManager::Rollup(PRUint32 aCount, nsIContent** aLastRolledUp)
{
if (aLastRolledUp)
*aLastRolledUp = nsnull;
@ -192,7 +192,21 @@ nsXULPopupManager::Rollup(nsIContent** aLastRolledUp)
if (first)
NS_ADDREF(*aLastRolledUp = first->Content());
}
HidePopup(item->Content(), PR_TRUE, PR_TRUE, PR_FALSE);
// if a number of popups to close has been specified, determine the last
// popup to close
nsIContent* lastPopup = nsnull;
if (aCount != PR_UINT32_MAX) {
nsMenuChainItem* last = item;
while (--aCount && last->GetParent()) {
last = last->GetParent();
}
if (last) {
lastPopup = last->Content();
}
}
HidePopup(item->Content(), PR_TRUE, PR_TRUE, PR_FALSE, lastPopup);
}
return NS_OK;
}
@ -214,28 +228,36 @@ NS_IMETHODIMP nsXULPopupManager::ShouldRollupOnMouseActivate(PRBool *aShouldRoll
return NS_OK;
}
void
nsXULPopupManager::GetSubmenuWidgetChain(nsTArray<nsIWidget*> *_retval)
PRUint32
nsXULPopupManager::GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain)
{
// this method is used by the widget code to determine the list of popups
// that are open. If a mouse click occurs outside one of these popups, the
// panels will roll up. If the click is inside a popup, they will not roll up
NS_ASSERTION(_retval, "null parameter");
PRUint32 count = 0, sameTypeCount = 0;
NS_ASSERTION(aWidgetChain, "null parameter");
nsMenuChainItem* item = GetTopVisibleMenu();
while (item) {
nsCOMPtr<nsIWidget> widget;
item->Frame()->GetWidget(getter_AddRefs(widget));
NS_ASSERTION(widget, "open popup has no widget");
_retval->AppendElement(widget.get());
aWidgetChain->AppendElement(widget.get());
// In the case when a menulist inside a panel is open, clicking in the
// panel should still roll up the menu, so if a different type is found,
// stop scanning.
nsMenuChainItem* parent = item->GetParent();
if (!parent || item->Frame()->PopupType() != parent->Frame()->PopupType() ||
item->IsContextMenu() != parent->IsContextMenu())
break;
if (!sameTypeCount) {
count++;
if (!parent || item->Frame()->PopupType() != parent->Frame()->PopupType() ||
item->IsContextMenu() != parent->IsContextMenu()) {
sameTypeCount = count;
}
}
item = parent;
}
return sameTypeCount;
}
void
@ -605,7 +627,8 @@ void
nsXULPopupManager::HidePopup(nsIContent* aPopup,
PRBool aHideChain,
PRBool aDeselectMenu,
PRBool aAsynchronous)
PRBool aAsynchronous,
nsIContent* aLastPopup)
{
// if the popup is on the nohide panels list, remove it but don't close any
// other panels
@ -676,7 +699,7 @@ nsXULPopupManager::HidePopup(nsIContent* aPopup,
if (parent && (aHideChain || topMenu != foundMenu))
nextPopup = parent->Content();
lastPopup = aHideChain ? nsnull : aPopup;
lastPopup = aLastPopup ? aLastPopup : (aHideChain ? nsnull : aPopup);
}
else if (foundPanel) {
popupToHide = aPopup;
@ -777,6 +800,7 @@ nsXULPopupManager::HidePopupCallback(nsIContent* aPopup,
// closes the menu and not the panel as well.
if (foundMenu &&
(aLastPopup || aPopupType == foundMenu->PopupType())) {
nsCOMPtr<nsIContent> popupToHide = item->Content();
nsMenuChainItem* parent = item->GetParent();
@ -1885,7 +1909,7 @@ nsXULPopupManager::KeyDown(nsIDOMEvent* aKeyEvent)
// The access key just went down and no other
// modifiers are already down.
if (mPopups)
Rollup(nsnull);
Rollup(nsnull, nsnull);
else if (mActiveMenuBar)
mActiveMenuBar->MenuClosed();
}
@ -1960,7 +1984,7 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent)
) {
// close popups or deactivate menubar when Tab or F10 are pressed
if (item)
Rollup(nsnull);
Rollup(nsnull, nsnull);
else if (mActiveMenuBar)
mActiveMenuBar->MenuClosed();
}

View File

@ -43,20 +43,29 @@
#include "nsTArray.h"
class nsIWidget;
class nsIContent;
#define NS_IMENUROLLUP_IID \
{0x2b65d177, 0xc3e4, 0x4564, \
{ 0x8d, 0xed, 0x86, 0xd2, 0xfa, 0x2f, 0x65, 0x9a }}
{0x61c9d01f, 0x8a4c, 0x4bb0, \
{ 0xa9, 0x90, 0xeb, 0xf6, 0x65, 0x4c, 0xda, 0x61 }}
class nsIMenuRollup : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUROLLUP_IID)
/* void GetSubmenuWidgetChain (nsTArray<nsIWidget*>*); */
virtual void GetSubmenuWidgetChain(nsTArray<nsIWidget*> *_retval) = 0;
/*
* Retrieve the widgets for open menus are store them in the array
* aWidgetChain. The number of menus of the same type should be returned,
* for example, if a context menu is open, return only the number of menus
* that are part of the context menu chain. This allows closing up only
* those menus in different situations.
*/
virtual PRUint32 GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain) = 0;
/* void AdjustPopupsOnWindowChange (); */
/**
* Adjust the position of open panels when a window is moved or resized.
*/
virtual void AdjustPopupsOnWindowChange(void) = 0;
};

View File

@ -43,15 +43,19 @@
interface nsIContent;
[uuid(ee6efe03-77dc-4aac-a6a8-905731a1796e)]
[uuid(0CA103E5-80D4-4B81-A310-BE0708F8EAA9)]
interface nsIRollupListener : nsISupports
{
/**
* Notifies the object to rollup, optionally returning the node that
* was just rolled up.
*
* aCount is the number of popups in a chain to close. If this is
* PR_UINT32_MAX, then all popups are closed.
*
* @result NS_Ok if no errors
*/
nsIContent Rollup();
nsIContent Rollup(in unsigned long aCount);
/**
* Asks the RollupListener if it should rollup on mousevents

View File

@ -920,7 +920,7 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
NSString *sender = [aNotification object];
if (!sender || ![sender isEqualToString:@"org.mozilla.gecko.PopupWindow"]) {
if (gRollupListener && gRollupWidget)
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
}
NS_OBJC_END_TRY_ABORT_BLOCK;

View File

@ -3147,23 +3147,34 @@ static const PRInt32 sShadowInvalidationInterval = 100;
// if we're dealing with menus, we probably have submenus and
// we don't want to rollup if the click is in a parent menu of
// the current submenu
PRUint32 popupsToRollup = PR_UINT32_MAX;
nsCOMPtr<nsIMenuRollup> menuRollup;
menuRollup = (do_QueryInterface(gRollupListener));
if (menuRollup) {
nsAutoTArray<nsIWidget*, 5> widgetChain;
menuRollup->GetSubmenuWidgetChain(&widgetChain);
PRUint32 sameTypeCount = menuRollup->GetSubmenuWidgetChain(&widgetChain);
for (PRUint32 i = 0; i < widgetChain.Length(); i++) {
nsIWidget* widget = widgetChain[i];
NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
shouldRollup = PR_FALSE;
// don't roll up if the mouse event occured within a menu of the
// same type. If the mouse event occured in a menu higher than
// that, roll up, but pass the number of popups to Rollup so
// that only those of the same type close up.
if (i < sameTypeCount) {
shouldRollup = PR_FALSE;
}
else {
popupsToRollup = sameTypeCount;
}
break;
}
}
}
if (shouldRollup) {
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(popupsToRollup, nsnull);
consumeEvent = (BOOL)gConsumeRollupEvent;
}
}

View File

@ -108,7 +108,7 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
static void RollUpPopups()
{
if (gRollupListener && gRollupWidget)
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
}

View File

@ -907,7 +907,7 @@ static pascal OSStatus MyMenuEventHandler(EventHandlerCallRef myHandler, EventRe
}
else if (kind == kEventMenuOpening || kind == kEventMenuClosed) {
if (kind == kEventMenuOpening && gRollupListener && gRollupWidget) {
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
return userCanceledErr;
}
MenuRef menuRef;

View File

@ -248,7 +248,7 @@ static CGEventRef EventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEv
// so would break the corresponding context menu).
if (NSPointInRect(screenLocation, [ctxMenuWindow frame]))
return event;
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
return event;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NULL);

View File

@ -736,7 +736,7 @@ nsWindow::Destroy(void)
nsCOMPtr<nsIWidget> rollupWidget = do_QueryReferent(gRollupWindow);
if (static_cast<nsIWidget *>(this) == rollupWidget.get()) {
if (gRollupListener)
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
gRollupWindow = nsnull;
gRollupListener = nsnull;
}
@ -4998,26 +4998,39 @@ check_for_rollup(GdkWindow *aWindow, gdouble aMouseX, gdouble aMouseY,
// if we're dealing with menus, we probably have submenus and
// we don't want to rollup if the clickis in a parent menu of
// the current submenu
PRUint32 popupsToRollup = PR_UINT32_MAX;
nsCOMPtr<nsIMenuRollup> menuRollup;
menuRollup = (do_QueryInterface(gRollupListener));
if (menuRollup) {
nsAutoTArray<nsIWidget*, 5> widgetChain;
menuRollup->GetSubmenuWidgetChain(&widgetChain);
PRUint32 sameTypeCount = menuRollup->GetSubmenuWidgetChain(&widgetChain);
for (PRUint32 i=0; i<widgetChain.Length(); ++i) {
nsIWidget* widget = widgetChain[i];
nsIWidget* widget = widgetChain[i];
GdkWindow* currWindow =
(GdkWindow*) widget->GetNativeData(NS_NATIVE_WINDOW);
if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
rollup = PR_FALSE;
break;
// don't roll up if the mouse event occured within a
// menu of the same type. If the mouse event occured
// in a menu higher than that, roll up, but pass the
// number of popups to Rollup so that only those of the
// same type close up.
if (i < sameTypeCount) {
rollup = PR_FALSE;
}
else {
popupsToRollup = sameTypeCount;
}
break;
}
} // foreach parent menu widget
} // if rollup listener knows about menus
// if we've determined that we should still rollup, do it.
if (rollup) {
gRollupListener->Rollup(nsnull);
retVal = PR_TRUE;
gRollupListener->Rollup(popupsToRollup, nsnull);
if (popupsToRollup == PR_UINT32_MAX) {
retVal = PR_TRUE;
}
}
}
} else {

View File

@ -603,15 +603,21 @@ nsWindow :: DealWithPopups ( ULONG inMsg, MRESULT* outResult )
// If we're dealing with menus, we probably have submenus and we don't
// want to rollup if the click is in a parent menu of the current submenu.
PRUint32 popupsToRollup = PR_UINT32_MAX;
if (rollup) {
nsCOMPtr<nsIMenuRollup> menuRollup ( do_QueryInterface(gRollupListener) );
if ( menuRollup ) {
nsAutoTArray<nsIWidget*, 5> widgetChain;
menuRollup->GetSubmenuWidgetChain ( &widgetChain );
PRUint32 sameTypeCount = menuRollup->GetSubmenuWidgetChain(&widgetChain);
for ( PRUint32 i = 0; i < widgetChain.Length(); ++i ) {
nsIWidget* widget = widgetChain[i];
if ( nsWindow::EventIsInsideWindow((nsWindow*)widget) ) {
rollup = PR_FALSE;
if (i < sameTypeCount) {
rollup = PR_FALSE;
}
else {
popupsToRollup = sameTypeCount;
}
break;
}
} // foreach parent menu widget
@ -621,7 +627,7 @@ nsWindow :: DealWithPopups ( ULONG inMsg, MRESULT* outResult )
// if we've determined that we should still rollup everything, do it.
if ( rollup ) {
// only need to deal with the last rollup for left mouse down events.
gRollupListener->Rollup(inMsg == WM_BUTTON1DOWN ? &mLastRollup : nsnull);
gRollupListener->Rollup(popupsToRollup, inMsg == WM_BUTTON1DOWN ? &mLastRollup : nsnull);
// return TRUE tells Windows that the event is consumed,
// false allows the event to be dispatched

View File

@ -651,13 +651,13 @@ int nsWindow::WindowWMHandler( PtWidget_t *widget, void *data, PtCallbackInfo_t
case Ph_WM_CONSWITCH:
gConsoleRectValid = PR_FALSE; /* force a call tp PhWindowQueryVisible() next time, since we might have moved this window into a different console */
/* rollup the menus */
if( gRollupWidget && gRollupListener ) gRollupListener->Rollup(nsnull);
if( gRollupWidget && gRollupListener ) gRollupListener->Rollup(nsnull, nsnull);
break;
case Ph_WM_FOCUS:
if( we->event_state == Ph_WM_EVSTATE_FOCUSLOST ) {
/* rollup the menus */
if( gRollupWidget && gRollupListener ) gRollupListener->Rollup(nsnull);
if( gRollupWidget && gRollupListener ) gRollupListener->Rollup(nsnull, nsnull);
if( sFocusWidget ) sFocusWidget->DispatchStandardEvent(NS_DEACTIVATE);
}
@ -902,7 +902,7 @@ NS_METHOD nsWindow::Move( PRInt32 aX, PRInt32 aY ) {
int nsWindow::MenuRegionCallback( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo ) {
if( gRollupWidget && gRollupListener ) {
/* rollup the menu */
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
}
return Pt_CONTINUE;
}

View File

@ -289,7 +289,7 @@ nsWindow::Destroy(void)
nsCOMPtr<nsIWidget> rollupWidget = do_QueryReferent(gRollupWindow);
if (static_cast<nsIWidget *>(this) == rollupWidget.get()) {
if (gRollupListener)
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
gRollupWindow = nsnull;
gRollupListener = nsnull;
}
@ -935,25 +935,31 @@ check_for_rollup(double aMouseX, double aMouseY,
// if we're dealing with menus, we probably have submenus and
// we don't want to rollup if the clickis in a parent menu of
// the current submenu
PRUint32 popupsToRollup = PR_UINT32_MAX;
nsCOMPtr<nsIMenuRollup> menuRollup;
menuRollup = (do_QueryInterface(gRollupListener));
if (menuRollup) {
nsAutoTArray<nsIWidget*, 5> widgetChain;
menuRollup->GetSubmenuWidgetChain(&widgetChain);
PRUint32 sameTypeCount = menuRollup->GetSubmenuWidgetChain(&widgetChain);
for (PRUint32 i=0; i<widgetChain.Length(); ++i) {
nsIWidget* widget = widgetChain[i];
QWidget* currWindow =
(QWidget*) widget->GetNativeData(NS_NATIVE_WINDOW);
if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
rollup = PR_FALSE;
break;
if (i < sameTypeCount) {
rollup = PR_FALSE;
}
else {
popupsToRollup = sameTypeCount;
}
break;
}
} // foreach parent menu widget
} // if rollup listener knows about menus
// if we've determined that we should still rollup, do it.
if (rollup) {
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(popupsToRollup, nsnull);
retVal = PR_TRUE;
}
}

View File

@ -1451,7 +1451,7 @@ NS_METHOD nsWindow::Destroy()
// the rollup widget, rollup and turn off capture.
if ( this == gRollupWidget ) {
if ( gRollupListener )
gRollupListener->Rollup(nsnull);
gRollupListener->Rollup(nsnull, nsnull);
CaptureRollupEvents(nsnull, PR_FALSE, PR_TRUE);
}
@ -7321,15 +7321,25 @@ nsWindow :: DealWithPopups ( HWND inWnd, UINT inMsg, WPARAM inWParam, LPARAM inL
// If we're dealing with menus, we probably have submenus and we don't
// want to rollup if the click is in a parent menu of the current submenu.
PRUint32 popupsToRollup = PR_UINT32_MAX;
if (rollup) {
nsCOMPtr<nsIMenuRollup> menuRollup ( do_QueryInterface(gRollupListener) );
if ( menuRollup ) {
nsAutoTArray<nsIWidget*, 5> widgetChain;
menuRollup->GetSubmenuWidgetChain ( &widgetChain );
PRUint32 sameTypeCount = menuRollup->GetSubmenuWidgetChain(&widgetChain);
for ( PRUint32 i = 0; i < widgetChain.Length(); ++i ) {
nsIWidget* widget = widgetChain[i];
if ( nsWindow::EventIsInsideWindow(inMsg, (nsWindow*)widget) ) {
rollup = PR_FALSE;
// don't roll up if the mouse event occured within a menu of the
// same type. If the mouse event occured in a menu higher than
// that, roll up, but pass the number of popups to Rollup so
// that only those of the same type close up.
if (i < sameTypeCount) {
rollup = PR_FALSE;
}
else {
popupsToRollup = sameTypeCount;
}
break;
}
} // foreach parent menu widget
@ -7337,7 +7347,7 @@ nsWindow :: DealWithPopups ( HWND inWnd, UINT inMsg, WPARAM inWParam, LPARAM inL
}
#ifndef WINCE
if (inMsg == WM_MOUSEACTIVATE) {
if (inMsg == WM_MOUSEACTIVATE && popupsToRollup == PR_UINT32_MAX) {
// Prevent the click inside the popup from causing a change in window
// activation. Since the popup is shown non-activated, we need to eat
// any requests to activate the window while it is displayed. Windows
@ -7370,7 +7380,7 @@ nsWindow :: DealWithPopups ( HWND inWnd, UINT inMsg, WPARAM inWParam, LPARAM inL
// nsIRollupListener::Rollup.
PRBool consumeRollupEvent = gRollupConsumeRollupEvent;
// only need to deal with the last rollup for left mouse down events.
gRollupListener->Rollup(inMsg == WM_LBUTTONDOWN ? &mLastRollup : nsnull);
gRollupListener->Rollup(popupsToRollup, inMsg == WM_LBUTTONDOWN ? &mLastRollup : nsnull);
// Tell hook to stop processing messages
gProcessHook = PR_FALSE;
@ -7385,6 +7395,13 @@ nsWindow :: DealWithPopups ( HWND inWnd, UINT inMsg, WPARAM inWParam, LPARAM inL
*outResult = TRUE;
return TRUE;
}
// if we are only rolling up some popups, don't activate and don't let
// the event go through. This prevents clicks menus higher in the
// chain from opening when a context menu is open
if (popupsToRollup != PR_UINT32_MAX && inMsg == WM_MOUSEACTIVATE) {
*outResult = MA_NOACTIVATEANDEAT;
return TRUE;
}
}
} // if event that might trigger a popup to rollup
} // if rollup listeners registered