From 2386399bb620eb43319f08331d997e587d47141c Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Thu, 3 Jun 2010 15:50:49 +1200 Subject: [PATCH] b=568101 use the value of aRaise in SetFocus to determine whether to set gFocusWindow r=enndeakin --HG-- extra : rebase_source : bb755e470a77360d0d0aef703fa3377df0bec5fe --- widget/public/nsIWidget.h | 10 ++++++- widget/src/gtk2/nsWindow.cpp | 56 +++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/widget/public/nsIWidget.h b/widget/public/nsIWidget.h index 42c31b86ea30..798a72f926f5 100644 --- a/widget/public/nsIWidget.h +++ b/widget/public/nsIWidget.h @@ -459,7 +459,15 @@ class nsIWidget : public nsISupports { NS_IMETHOD IsEnabled(PRBool *aState) = 0; /** - * Give focus to this widget. + * Request activation of this window or give focus to this widget. + * + * @param aRaise If PR_TRUE, this function requests activation of this + * widget's toplevel window. + * If PR_FALSE, the appropriate toplevel window (which in + * the case of popups may not be this widget's toplevel + * window) is already active, and this function indicates + * that keyboard events should be reported through the + * aHandleEventFunction provided to this->Create(). */ NS_IMETHOD SetFocus(PRBool aRaise = PR_FALSE) = 0; diff --git a/widget/src/gtk2/nsWindow.cpp b/widget/src/gtk2/nsWindow.cpp index 571e99a8dcc7..4e487732e9e3 100644 --- a/widget/src/gtk2/nsWindow.cpp +++ b/widget/src/gtk2/nsWindow.cpp @@ -289,8 +289,9 @@ guint32 nsWindow::sLastButtonReleaseTime = 0; static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID); -// the current focus window +// The window from which the focus manager asks us to dispatch key events. static nsWindow *gFocusWindow = NULL; +static PRBool gBlockActivateEvent = PR_FALSE; static PRBool gGlobalsInitialized = PR_FALSE; static PRBool gRaiseWindows = PR_TRUE; static nsWindow *gPluginFocusWindow = NULL; @@ -1336,7 +1337,7 @@ nsWindow::SetFocus(PRBool aRaise) // Make sure that our owning widget has focus. If it doesn't try to // grab it. Note that we don't set our focus flag in this case. - LOGFOCUS((" SetFocus [%p]\n", (void *)this)); + LOGFOCUS((" SetFocus %d [%p]\n", aRaise, (void *)this)); GtkWidget *owningWidget = GetMozContainerWidget(); if (!owningWidget) @@ -1363,20 +1364,42 @@ nsWindow::SetFocus(PRBool aRaise) if (!owningWindow) return NS_ERROR_FAILURE; - if (!GTK_WIDGET_HAS_FOCUS(owningWidget)) { - LOGFOCUS((" grabbing focus for the toplevel [%p]\n", (void *)this)); + if (aRaise) { + // aRaise == PR_TRUE means request toplevel activation. - // Set focus to the window - if (gRaiseWindows && aRaise && toplevelWidget && - !GTK_WIDGET_HAS_FOCUS(toplevelWidget) && - owningWindow->mIsShown && GTK_IS_WINDOW(owningWindow->mShell)) - gtk_window_present(GTK_WINDOW(owningWindow->mShell)); + // This is asynchronous. + // If and when the window manager accepts the request, then the focus + // widget will get a focus-in-event signal. + if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell && + !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) { - gtk_widget_grab_focus(owningWidget); + LOGFOCUS((" requesting toplevel activation [%p]\n", (void *)this)); + NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup + || mParent, + "Presenting an override-redirect window"); + gtk_window_present(GTK_WINDOW(owningWindow->mShell)); + } return NS_OK; } + // aRaise == PR_FALSE means that keyboard events should be dispatched + // from this widget. + + // Ensure owningWidget is the focused GtkWidget within its toplevel window. + // + // For eWindowType_popup, this GtkWidget may not actually be the one that + // receives the key events as it may be the parent window that is active. + if (!gtk_widget_is_focus(owningWidget)) { + // This is synchronous. It takes focus from a plugin or from a widget + // in an embedder. The focus manager already knows that this window + // is active so gBlockActivateEvent avoids another (unnecessary) + // NS_ACTIVATE event. + gBlockActivateEvent = PR_TRUE; + gtk_widget_grab_focus(owningWidget); + gBlockActivateEvent = PR_FALSE; + } + // If this is the widget that already has focus, return. if (gFocusWindow == this) { LOGFOCUS((" already have focus [%p]\n", (void *)this)); @@ -3092,7 +3115,20 @@ nsWindow::OnContainerFocusInEvent(GtkWidget *aWidget, GdkEventFocus *aEvent) if (top_window && (GTK_WIDGET_VISIBLE(top_window))) SetUrgencyHint(top_window, PR_FALSE); + // Return if being called within SetFocus because the focus manager + // already knows that the window is active. + if (gBlockActivateEvent) { + LOGFOCUS(("NS_ACTIVATE event is blocked [%p]\n", (void *)this)); + return; + } + + // This is not usually the correct window for dispatching key events, + // but the focus manager will call SetFocus to set the correct window if + // keyboard input will be accepted. Setting a non-NULL value here + // prevents OnButtonPressEvent() from dispatching NS_ACTIVATE if the + // widget is already active. gFocusWindow = this; + DispatchActivateEvent(); LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this));