Fix problems with consecutive right-clicks and ctrl-clicks (context menu problems). b=389542 r=joshmoz sr=pavlov

This commit is contained in:
smichaud@pobox.com 2007-08-20 18:02:09 -07:00
Родитель 99d771c3e9
Коммит 5ad31ca94e
1 изменённых файлов: 83 добавлений и 20 удалений

Просмотреть файл

@ -49,6 +49,9 @@
#include "prmem.h" #include "prmem.h"
#include "nsCRT.h" #include "nsCRT.h"
#include "nsplugindefs.h" #include "nsplugindefs.h"
#include "nsThreadUtils.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIFontMetrics.h" #include "nsIFontMetrics.h"
#include "nsIDeviceContext.h" #include "nsIDeviceContext.h"
@ -147,6 +150,8 @@ nsIWidget * gRollupWidget = nsnull;
- (void)maybeInitContextMenuTracking; - (void)maybeInitContextMenuTracking;
- (nsChildView *)getGeckoChild;
#if USE_CLICK_HOLD_CONTEXTMENU #if USE_CLICK_HOLD_CONTEXTMENU
// called on a timer two seconds after a mouse down to see if we should display // called on a timer two seconds after a mouse down to see if we should display
// a context menu (click-hold) // a context menu (click-hold)
@ -2485,6 +2490,52 @@ NSEvent* globalDragEvent = nil;
} }
- (nsChildView *)getGeckoChild
{
return mGeckoChild;
}
// Browsers that use non-native context menus (e.g. Cocoa widgets Firefox as
// opposed to Camino) need to postpone generating a context menu until after
// the previous one (if one exists) has had a chance to close. The reason why
// is that, as of the fix for bmo bug 279703, non-native context menus are
// hidden asynchronously (after a call to gRollupListener->Rollup()), using an
// nsXULPopupHidingEvent. If we don't asynchronously generate non-native
// context menus, only the first control-click or right-click in a window will
// display a context menu. This change resolves bmo bug 389542.
class nsNonNativeContextMenuEvent : public nsRunnable {
public:
nsNonNativeContextMenuEvent(ChildView *baseView, NSEvent *triggerEvent) {
mBaseView = [baseView retain];
mTriggerEvent = [triggerEvent retain];
}
virtual ~nsNonNativeContextMenuEvent() {
[mBaseView release];
[mTriggerEvent release];
}
NS_IMETHOD Run() {
if (!mBaseView || !mTriggerEvent)
return NS_ERROR_FAILURE;
nsChildView *geckoChild = [mBaseView getGeckoChild];
if (!geckoChild)
return NS_ERROR_FAILURE;
nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
[mBaseView convertCocoaMouseEvent:mTriggerEvent toGeckoEvent:&geckoEvent];
geckoEvent.button = nsMouseEvent::eRightButton;
geckoChild->DispatchMouseEvent(geckoEvent);
[mBaseView maybeInitContextMenuTracking];
return NS_OK;
}
private:
ChildView *mBaseView;
NSEvent *mTriggerEvent;
};
- (void)mouseDown:(NSEvent *)theEvent - (void)mouseDown:(NSEvent *)theEvent
{ {
// Make sure this view is not in the rollup widget. The fastest way to do this // Make sure this view is not in the rollup widget. The fastest way to do this
@ -2544,18 +2595,6 @@ NSEvent* globalDragEvent = nil;
mGeckoChild->DispatchMouseEvent(geckoEvent); mGeckoChild->DispatchMouseEvent(geckoEvent);
// if this is a right button click (either actual right click or ctrl-click) send
// a context menu event
if (geckoEvent.button == nsMouseEvent::eRightButton) {
nsMouseEvent geckoCMEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoCMEvent];
geckoCMEvent.nativeMsg = &macEvent;
geckoCMEvent.isControl = ((modifierFlags & NSControlKeyMask) != 0);
mGeckoChild->DispatchMouseEvent(geckoCMEvent);
// Initialize menu tracking if using custom context menus.
[self maybeInitContextMenuTracking];
}
// XXX maybe call markedTextSelectionChanged:client: here? // XXX maybe call markedTextSelectionChanged:client: here?
} }
@ -3039,17 +3078,41 @@ static nsEventStatus SendMouseEvent(PRBool isTrusted,
if (!mGeckoChild || [self isPluginView]) if (!mGeckoChild || [self isPluginView])
return nil; return nil;
// Roll up a non-native context menu if one is open and we're not it. We do
// this check here (as well as in mouseDown: and rightMouseDown:) because
// sometimes menuForEvent: is called before the associated mouse-down
// method is called.
if (gRollupWidget) {
NSWindow *ourNativeWindow = [self nativeWindow];
NSWindow *rollupNativeWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
if (ourNativeWindow != rollupNativeWindow) {
// roll up any popups
if (gRollupListener != nsnull)
gRollupListener->Rollup();
}
}
[mLastMenuForEventEvent release]; [mLastMenuForEventEvent release];
mLastMenuForEventEvent = [theEvent retain]; mLastMenuForEventEvent = [theEvent retain];
// Fire the context menu event into Gecko. // If we're running in a browser that (like Camino) uses native context
nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal); // menus, fire a context menu event into Gecko here. Otherwise (if we're
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; // using non-native context menus) we need to open the context menu
geckoEvent.button = nsMouseEvent::eRightButton; // asynchronously.
mGeckoChild->DispatchMouseEvent(geckoEvent); nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
// Initialize menu tracking if using custom context menus. PRBool useNativeContextMenus;
[self maybeInitContextMenuTracking]; nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
if (NS_SUCCEEDED(rv) && useNativeContextMenus) {
nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
geckoEvent.button = nsMouseEvent::eRightButton;
mGeckoChild->DispatchMouseEvent(geckoEvent);
} else {
nsIRunnable *contextMenuEvent = new nsNonNativeContextMenuEvent(self, theEvent);
NS_DispatchToCurrentThread(contextMenuEvent);
}
}
// Go up our view chain to fetch the correct menu to return. // Go up our view chain to fetch the correct menu to return.
return [self contextMenu]; return [self contextMenu];