Mouse-down doesn't always change focus appropriately. b=413882 r=joshmoz sr=roc

This commit is contained in:
smichaud@pobox.com 2008-02-13 07:57:12 -08:00
parent e3e67a09b1
commit 12da382dc2
4 changed files with 86 additions and 0 deletions

View File

@ -96,6 +96,8 @@ extern "C" {
CG_EXTERN void CGContextResetClip(CGContextRef);
}
extern PRBool gCocoaWindowMethodsSwizzled; // Defined in nsCocoaWindow.mm
extern nsISupportsArray *gDraggedTransferables;
PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
@ -391,6 +393,13 @@ nsresult nsChildView::StandardCreate(nsIWidget *aParent,
nsWidgetInitData *aInitData,
nsNativeWidget aNativeParent)
{
// See NSWindow (MethodSwizzling) in nsCocoaWindow.mm.
if (!gCocoaWindowMethodsSwizzled) {
nsToolkit::SwizzleMethods([NSWindow class], @selector(sendEvent:),
@selector(nsCocoaWindow_NSWindow_sendEvent:));
gCocoaWindowMethodsSwizzled = PR_TRUE;
}
mBounds = aRect;
BaseCreate(aParent, aRect, aHandleEventFunction,

View File

@ -54,9 +54,12 @@
#include "nsIPrefBranch.h"
#include "nsToolkit.h"
#include "nsPrintfCString.h"
#include "nsThreadUtils.h"
PRInt32 gXULModalLevel = 0;
PRBool gCocoaWindowMethodsSwizzled = PR_FALSE;
// defined in nsMenuBarX.mm
extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
@ -1750,6 +1753,42 @@ void patternDraw(void* aInfo, CGContextRef aContext)
@end
@interface NSWindow (MethodSwizzling)
- (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent;
@end
@implementation NSWindow (MethodSwizzling)
// A mouseDown event can change the focus, and (if this happens) we may need
// to send NS_LOSTFOCUS and NS_GOTFOCUS events to Gecko. Otherwise other
// Gecko events may be sent to the wrong nsChildView, or the "right"
// nsChildView may not be focused. This resolves bmo bug 413882.
// For non-embedders (e.g. Firefox and Seamonkey), it would probably only be
// necessary to add a sendEvent: method to the ToolbarWindow class. But
// embedders (like Camino) generally create their own NSWindows. So in order
// to fix this problem everywhere, it's necessary to "hook" NSWindow's own
// sendEvent: method.
- (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent
{
NSResponder *oldFirstResponder = nil;
NSEventType type = [anEvent type];
if (type == NSLeftMouseDown)
oldFirstResponder = [[self firstResponder] retain];
[self nsCocoaWindow_NSWindow_sendEvent:anEvent];
if (type == NSLeftMouseDown) {
NSResponder *newFirstResponder = [self firstResponder];
if (oldFirstResponder != newFirstResponder) {
if ([oldFirstResponder isKindOfClass:[ChildView class]])
[(ChildView *)oldFirstResponder sendFocusEvent:NS_LOSTFOCUS];
if ([newFirstResponder isKindOfClass:[ChildView class]])
[(ChildView *)newFirstResponder sendFocusEvent:NS_GOTFOCUS];
}
[oldFirstResponder release];
}
}
@end
@implementation PopupWindow
// The OS treats our custom popup windows very strangely -- many mouse events

View File

@ -42,6 +42,8 @@
#include "nsIToolkit.h"
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#import <objc/Object.h>
#import <IOKit/IOKitLib.h>
#define MAC_OS_X_VERSION_10_4_HEX 0x00001040
@ -64,6 +66,8 @@ public:
static void PostSleepWakeNotification(const char* aNotification);
static nsresult SwizzleMethods(Class aClass, SEL orgMethod, SEL posedMethod);
protected:
nsresult RegisterForSleepWakeNotifcations();

View File

@ -382,3 +382,37 @@ PRBool nsToolkit::OnLeopardOrLater()
{
return (OSXVersion() >= MAC_OS_X_VERSION_10_5_HEX) ? PR_TRUE : PR_FALSE;
}
// An alternative to [NSObject poseAsClass:] that isn't deprecated on OS X
// Leopard and is available to 64-bit binaries on Leopard and above. Based on
// ideas and code from http://www.cocoadev.com/index.pl?MethodSwizzling.
// Since the Method type becomes an opaque type as of Objective-C 2.0, we'll
// have to switch to using accessor methods like method_exchangeImplementations()
// when we build 64-bit binaries that use Objective-C 2.0 (on and for Leopard
// and above). But these accessor methods aren't available in Objective-C 1
// (or on Tiger). So we need to access Method's members directly for (Tiger-
// capable) binaries (32-bit or 64-bit) that use Objective-C 1 (as long as we
// keep supporting Tiger).
//
// Be aware that, if aClass doesn't have an orgMethod selector but one of its
// superclasses does, the method substitution will (in effect) take place in
// that superclass (rather than in aClass itself). The substitution has
// effect on the class where it takes place and all of that class's
// subclasses. In order for method swizzling to work properly, posedMethod
// needs to be unique in the class where the substitution takes place and all
// of its subclasses.
nsresult nsToolkit::SwizzleMethods(Class aClass, SEL orgMethod, SEL posedMethod)
{
Method original = class_getInstanceMethod(aClass, orgMethod);
Method posed = class_getInstanceMethod(aClass, posedMethod);
if (!original || !posed)
return NS_ERROR_FAILURE;
IMP aMethodImp = original->method_imp;
original->method_imp = posed->method_imp;
posed->method_imp = aMethodImp;
return NS_OK;
}