Fix several focus bugs regressed by patch for bug 413882. b=404433 r=josh sr=roc

git-svn-id: svn://10.0.0.236/trunk@246567 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
smichaud%pobox.com
2008-02-26 22:08:27 +00:00
parent ec3604e31f
commit a240c30fef

View File

@@ -56,6 +56,12 @@
#include "nsToolkit.h"
#include "nsPrintfCString.h"
#include "nsThreadUtils.h"
#include "nsIFocusController.h"
#include "nsIWindowWatcher.h"
#include "nsIServiceManager.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsIDOMElement.h"
PRInt32 gXULModalLevel = 0;
@@ -1935,6 +1941,37 @@ void patternDraw(void* aInfo, CGContextRef aContext)
@end
already_AddRefed<nsIDOMElement> GetFocusedElement()
{
nsresult rv;
nsIDOMElement *focusedElement = nsnull;
nsCOMPtr<nsIWindowWatcher> wwatch =
(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
if (!NS_SUCCEEDED(rv) || !wwatch)
return nsnull;
nsCOMPtr<nsIDOMWindow> domWindow;
wwatch->GetActiveWindow(getter_AddRefs(domWindow));
nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(domWindow));
if (!piWin)
return nsnull;
nsIFocusController *fc = piWin->GetRootFocusController();
if (!fc)
return nsnull;
rv = fc->GetFocusedElement(&focusedElement);
if (!NS_SUCCEEDED(rv)) {
NS_IF_RELEASE(focusedElement);
return nsnull;
}
return focusedElement;
}
@interface NSWindow (MethodSwizzling)
- (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent;
@end
@@ -1945,28 +1982,53 @@ void patternDraw(void* aInfo, CGContextRef aContext)
// 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.
// But if the mouseDown event doesn't change the focus where it really counts
// (if it doesn't change the focused nsIDOMElement/nsIContent), put the
// Cocoa keyboard focus back where it originally was. This resolves bmo bug
// 413882 without regressing bmo bugs 404433, 403232 and 357535/418031.
// For non-embedders (e.g. Firefox, Thunderbird 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
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NSResponder *oldFirstResponder = nil;
nsCOMPtr<nsIDOMElement> oldFocusedElement;
NSResponder *oldFirstResponder;
NSEventType type = [anEvent type];
if (type == NSLeftMouseDown)
oldFirstResponder = [[self firstResponder] retain];
if (type == NSLeftMouseDown) {
oldFirstResponder = [self firstResponder];
if ([oldFirstResponder isKindOfClass:[ChildView class]]) {
oldFirstResponder = [oldFirstResponder retain];
oldFocusedElement = GetFocusedElement();
}
else {
oldFirstResponder = nil;
}
}
[self nsCocoaWindow_NSWindow_sendEvent:anEvent];
if (type == NSLeftMouseDown) {
nsCOMPtr<nsIDOMElement> newFocusedElement;
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];
if ([newFirstResponder isKindOfClass:[ChildView class]]) {
newFirstResponder = [newFirstResponder retain];
newFocusedElement = GetFocusedElement();
}
else {
newFirstResponder = nil;
}
if (oldFirstResponder != newFirstResponder) {
if (oldFocusedElement != newFocusedElement) {
[(ChildView *)oldFirstResponder sendFocusEvent:NS_LOSTFOCUS];
[(ChildView *)newFirstResponder sendFocusEvent:NS_GOTFOCUS];
}
else if (newFocusedElement && [(ChildView *)oldFirstResponder widget]) {
[self makeFirstResponder:oldFirstResponder];
}
}
[newFirstResponder release];
[oldFirstResponder release];
}