Fix multiple focus bugs. b=403232 r=joshmoz sr=roc

This commit is contained in:
smichaud@pobox.com 2007-12-18 16:35:14 -08:00
parent 05a63a0e24
commit 6d86e37dc1
2 changed files with 89 additions and 4 deletions

View File

@ -91,6 +91,9 @@ union nsPluginPort;
// Whether we're a plugin view.
BOOL mIsPluginView;
// Whether we're in nsChildView::SetFocus()
int mInSetFocusLevel;
NSEvent* mCurKeyEvent; // only valid during a keyDown
PRBool mKeyDownHandled;
@ -127,6 +130,9 @@ union nsPluginPort;
// Stop NSView hierarchy being changed during [ChildView drawRect:]
- (void)delayedTearDown;
-(void)setInSetFocus:(BOOL)aInSetFocus;
-(BOOL)inSetFocus;
@end

View File

@ -718,11 +718,64 @@ NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
}
class nsAutoSetInSetFocus {
public:
nsAutoSetInSetFocus(NSView *aView)
{
if ([aView isKindOfClass:[ChildView class]]) {
mChildView = (ChildView *) [aView retain];
[mChildView setInSetFocus:PR_TRUE];
} else {
mChildView = nil;
}
}
~nsAutoSetInSetFocus()
{
if (mChildView) {
[mChildView setInSetFocus:PR_FALSE];
[mChildView release];
}
}
private:
ChildView *mChildView; // [STRONG]
};
NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
{
NSWindow* window = [mView window];
if (window)
[window makeFirstResponder:mView];
if (window) {
// For reasons that aren't yet clear, focus changes within a window (as
// opposed to those between windows or between apps) should only trigger
// NS_LOSTFOCUS and NS_GOTFOCUS messages (to Gecko) in the context of a
// call to nsChildView::SetFocus() (or nsCocoaWindow::SetFocus(), which
// in any case re-routes to nsChildView::SetFocus()). If we send these
// messages on every intra-window focus change (on every call to
// [ChildView becomeFirstResponder:] or [ChildView resignFirstResponder:]),
// the result will be strange focus bugs (like bmo bugs 399471, 403232,
// 404433 and 408266).
nsAutoSetInSetFocus setInSetFocus(mView);
NSResponder* firstResponder = [window firstResponder];
if ([mView isEqual:firstResponder]) {
// Sometimes SetFocus() is called on an nsChildView object that's
// already focused. If we simply call [NSWindow makeFirstResponder:],
// neither [ChildView becomeFirstResponder:] nor [ChildView
// resignFirstResponder:] will get called, and no NS_LOSTFOCUS or
// NS_GOTFOCUS messages will be sent. But in this case we sometimes
// get text-input cursors blinking in more than one text field. So we
// need to guarantee that the code in nsEventStateManager::
// PreHandleEvent() which handles NS_LOSTFOCUS messages (and calls
// SetContentCaretVisible(... PR_FALSE)) gets invoked on every call to
// nsChildView::SetFocus().
if ([mView isKindOfClass:[ChildView class]]) {
[(ChildView *)mView sendFocusEvent:NS_LOSTFOCUS];
[(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
}
} else {
[window makeFirstResponder:mView];
}
}
return NS_OK;
}
@ -1763,6 +1816,7 @@ NSEvent* gLastDragEvent = nil;
mWindow = nil;
mGeckoChild = inChild;
mIsPluginView = NO;
mInSetFocusLevel = 0;
mCurKeyEvent = nil;
mKeyDownHandled = PR_FALSE;
@ -2074,6 +2128,24 @@ NSEvent* gLastDragEvent = nil;
}
-(void)setInSetFocus:(BOOL)aInSetFocus
{
if (aInSetFocus) {
++mInSetFocusLevel;
} else if (mInSetFocusLevel > 0) {
--mInSetFocusLevel;
} else {
NS_WARNING("ChildView setInFocus: inSetFocus already false");
}
}
-(BOOL)inSetFocus
{
return mInSetFocusLevel > 0;
}
- (void)sendFocusEvent:(PRUint32)eventType
{
if (!mGeckoChild)
@ -4086,7 +4158,11 @@ static BOOL keyUpAlreadySentKeyDown = NO;
if (!mGeckoChild)
return NO;
[self sendFocusEvent:NS_GOTFOCUS];
// Focus events should only be sent to Gecko here in the context of a call
// to nsChildView::SetFocus(). For more info see nsChildView::SetFocus()
// above.
if ([self inSetFocus])
[self sendFocusEvent:NS_GOTFOCUS];
return [super becomeFirstResponder];
}
@ -4099,7 +4175,10 @@ static BOOL keyUpAlreadySentKeyDown = NO;
{
nsTSMManager::CommitIME();
if (mGeckoChild)
// Focus events should only be sent to Gecko here in the context of a call
// to nsChildView::SetFocus(). For more info see nsChildView::SetFocus()
// above.
if (mGeckoChild && [self inSetFocus])
[self sendFocusEvent:NS_LOSTFOCUS];
return [super resignFirstResponder];