Fix bug #76617. Some embedding apps can't get input focus. r=ccarlen,bryner sr=tor

This commit is contained in:
blizzard%redhat.com 2001-05-03 12:04:56 +00:00
Родитель 9ae6bbd1f3
Коммит 778aa3be46
7 изменённых файлов: 439 добавлений и 66 удалений

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

@ -43,6 +43,9 @@
#include <nsIDOMWindowInternal.h>
#include <nsIChromeEventHandler.h>
// for the focus hacking we need to do
#include <nsIFocusController.h>
// for profiles
#include <nsMPFileLocProvider.h>
@ -448,6 +451,7 @@ EmbedPrivate::FindPrivateForBrowser(nsIWebBrowserChrome *aBrowser)
void
EmbedPrivate::ContentStateChange(void)
{
// we don't attach listeners to chrome
if (mListenersAttached && !mIsChrome)
return;
@ -492,6 +496,69 @@ EmbedPrivate::ContentFinishedLoading(void)
}
}
// handle focus in and focus out events
void
EmbedPrivate::TopLevelFocusIn(void)
{
nsCOMPtr<nsPIDOMWindow> piWin;
GetPIDOMWindow(getter_AddRefs(piWin));
if (!piWin)
return;
nsCOMPtr<nsIFocusController> focusController;
piWin->GetRootFocusController(getter_AddRefs(focusController));
if (focusController)
focusController->SetActive(PR_TRUE);
}
void
EmbedPrivate::TopLevelFocusOut(void)
{
nsCOMPtr<nsPIDOMWindow> piWin;
GetPIDOMWindow(getter_AddRefs(piWin));
if (!piWin)
return;
nsCOMPtr<nsIFocusController> focusController;
piWin->GetRootFocusController(getter_AddRefs(focusController));
if (focusController)
focusController->SetActive(PR_FALSE);
}
void
EmbedPrivate::ChildFocusIn(void)
{
nsCOMPtr<nsPIDOMWindow> piWin;
GetPIDOMWindow(getter_AddRefs(piWin));
if (!piWin)
return;
piWin->Activate();
}
void
EmbedPrivate::ChildFocusOut(void)
{
nsCOMPtr<nsPIDOMWindow> piWin;
GetPIDOMWindow(getter_AddRefs(piWin));
if (!piWin)
return;
piWin->Deactivate();
// but the window is still active until the toplevel gets a focus
// out
nsCOMPtr<nsIFocusController> focusController;
piWin->GetRootFocusController(getter_AddRefs(focusController));
if (focusController)
focusController->SetActive(PR_TRUE);
}
// Get the event listener for the chrome event handler.
void
@ -500,28 +567,15 @@ EmbedPrivate::GetListener(void)
if (mEventReceiver)
return;
// get the web browser
nsCOMPtr<nsIWebBrowser> webBrowser;
mWindow->GetWebBrowser(getter_AddRefs(webBrowser));
nsCOMPtr<nsPIDOMWindow> piWin;
GetPIDOMWindow(getter_AddRefs(piWin));
// get the content DOM window for that web browser
nsCOMPtr<nsIDOMWindow> domWindow;
webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
if (!domWindow) {
NS_WARNING("no dom window in content progress change\n");
if (!piWin)
return;
}
// get the private DOM window
nsCOMPtr<nsPIDOMWindow> domWindowPrivate = do_QueryInterface(domWindow);
// and the root window for that DOM window
nsCOMPtr<nsIDOMWindowInternal> rootWindow;
domWindowPrivate->GetPrivateRoot(getter_AddRefs(rootWindow));
nsCOMPtr<nsIChromeEventHandler> chromeHandler;
nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(rootWindow));
piWin->GetChromeEventHandler(getter_AddRefs(chromeHandler));
mEventReceiver = do_QueryInterface(chromeHandler);
}
@ -587,6 +641,41 @@ EmbedPrivate::DetachListeners(void)
mListenersAttached = PR_FALSE;
}
nsresult
EmbedPrivate::GetPIDOMWindow(nsPIDOMWindow **aPIWin)
{
*aPIWin = nsnull;
// get the web browser
nsCOMPtr<nsIWebBrowser> webBrowser;
mWindow->GetWebBrowser(getter_AddRefs(webBrowser));
// get the content DOM window for that web browser
nsCOMPtr<nsIDOMWindow> domWindow;
webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
if (!domWindow)
return NS_ERROR_FAILURE;
// get the private DOM window
nsCOMPtr<nsPIDOMWindow> domWindowPrivate = do_QueryInterface(domWindow);
// and the root window for that DOM window
nsCOMPtr<nsIDOMWindowInternal> rootWindow;
domWindowPrivate->GetPrivateRoot(getter_AddRefs(rootWindow));
nsCOMPtr<nsIChromeEventHandler> chromeHandler;
nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(rootWindow));
*aPIWin = piWin.get();
if (*aPIWin) {
NS_ADDREF(*aPIWin);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
/* static */
nsresult
EmbedPrivate::StartupProfile(void)

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

@ -43,6 +43,8 @@ class EmbedContentListener;
class EmbedEventListener;
class EmbedStream;
class nsPIDOMWindow;
class EmbedPrivate {
public:
@ -83,6 +85,16 @@ class EmbedPrivate {
// visibility is set.
void ContentFinishedLoading(void);
// these let the widget code know when the toplevel window gets and
// looses focus.
void TopLevelFocusIn (void);
void TopLevelFocusOut(void);
// these are when the widget itself gets focus in and focus out
// events
void ChildFocusIn (void);
void ChildFocusOut(void);
GtkMozEmbed *mOwningWidget;
// all of the objects that we own
@ -135,6 +147,9 @@ class EmbedPrivate {
void GetListener (void);
void AttachListeners(void);
void DetachListeners(void);
// this will get the PIDOMWindow for this widget
nsresult GetPIDOMWindow (nsPIDOMWindow **aPIWin);
static nsresult StartupProfile (void);
static void ShutdownProfile(void);

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

@ -32,6 +32,9 @@
// so we can do our get_nsIWebBrowser later...
#include <nsIWebBrowser.h>
// so we can get callbacks from the mozarea
#include <gtkmozarea.h>
// class and instance initialization
static void
@ -62,6 +65,26 @@ gtk_moz_embed_map(GtkWidget *widget);
static void
gtk_moz_embed_unmap(GtkWidget *widget);
static gint
handle_child_focus_in(GtkWidget *aWidget,
GdkEventFocus *aGdkFocusEvent,
GtkMozEmbed *aEmbed);
static gint
handle_child_focus_out(GtkWidget *aWidget,
GdkEventFocus *aGdkFocusEvent,
GtkMozEmbed *aEmbed);
// signal handlers for tracking the focus and and focus out events on
// the toplevel window.
static void
handle_toplevel_focus_in (GtkMozArea *aArea,
GtkMozEmbed *aEmbed);
static void
handle_toplevel_focus_out(GtkMozArea *aArea,
GtkMozEmbed *aEmbed);
// globals for this type of widget
static GtkBinClass *parent_class;
@ -103,7 +126,7 @@ gtk_moz_embed_class_init(GtkMozEmbedClass *klass)
container_class = GTK_CONTAINER_CLASS(klass);
bin_class = GTK_BIN_CLASS(klass);
widget_class = GTK_WIDGET_CLASS(klass);
object_class = (GtkObjectClass *)klass;
object_class = GTK_OBJECT_CLASS(klass);
parent_class = (GtkBinClass *)gtk_type_class(gtk_bin_get_type());
@ -373,6 +396,34 @@ gtk_moz_embed_realize(GtkWidget *widget)
if (embedPrivate->mURI.Length())
embedPrivate->LoadCurrentURI();
// connect to the focus out event for the child
GtkWidget *child_widget = GTK_BIN(widget)->child;
gtk_signal_connect_while_alive(GTK_OBJECT(child_widget),
"focus_out_event",
GTK_SIGNAL_FUNC(handle_child_focus_out),
embed,
GTK_OBJECT(child_widget));
gtk_signal_connect_while_alive(GTK_OBJECT(child_widget),
"focus_in_event",
GTK_SIGNAL_FUNC(handle_child_focus_in),
embed,
GTK_OBJECT(child_widget));
// connect to the toplevel focus out events for the child
GtkMozArea *mozarea = GTK_MOZAREA(child_widget);
gtk_signal_connect_while_alive(GTK_OBJECT(mozarea),
"toplevel_focus_in",
GTK_SIGNAL_FUNC(handle_toplevel_focus_in),
embed,
GTK_OBJECT(mozarea));
gtk_signal_connect_while_alive(GTK_OBJECT(mozarea),
"toplevel_focus_out",
GTK_SIGNAL_FUNC(handle_toplevel_focus_out),
embed,
GTK_OBJECT(mozarea));
}
static void
@ -452,7 +503,54 @@ gtk_moz_embed_unmap(GtkWidget *widget)
gdk_window_hide(widget->window);
embedPrivate->Hide();
}
static gint
handle_child_focus_in(GtkWidget *aWidget,
GdkEventFocus *aGdkFocusEvent,
GtkMozEmbed *aEmbed)
{
EmbedPrivate *embedPrivate;
embedPrivate = (EmbedPrivate *)aEmbed->data;
embedPrivate->ChildFocusIn();
return FALSE;
}
static gint
handle_child_focus_out(GtkWidget *aWidget,
GdkEventFocus *aGdkFocusEvent,
GtkMozEmbed *aEmbed)
{
EmbedPrivate *embedPrivate;
embedPrivate = (EmbedPrivate *)aEmbed->data;
embedPrivate->ChildFocusOut();
return FALSE;
}
static void
handle_toplevel_focus_in (GtkMozArea *aArea,
GtkMozEmbed *aEmbed)
{
EmbedPrivate *embedPrivate;
embedPrivate = (EmbedPrivate *)aEmbed->data;
embedPrivate->TopLevelFocusIn();
}
static void
handle_toplevel_focus_out(GtkMozArea *aArea,
GtkMozEmbed *aEmbed)
{
EmbedPrivate *embedPrivate;
embedPrivate = (EmbedPrivate *)aEmbed->data;
embedPrivate->TopLevelFocusOut();
}
// Widget methods

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

@ -1331,16 +1331,6 @@ nsEventStatus PR_CALLBACK nsWebBrowser::HandleEvent(nsGUIEvent *aEvent)
switch(aEvent->message) {
case NS_ACTIVATE: {
browser->Activate();
break;
}
case NS_DEACTIVATE: {
browser->Deactivate();
break;
}
case NS_PAINT: {
nsRect *rect = NS_STATIC_CAST(nsPaintEvent *, aEvent)->rect;
browser->FillBackground(*rect);

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

@ -1109,28 +1109,30 @@ nsWindow::SetFocus(PRBool aRaise)
printf("top moz area is %p\n", NS_STATIC_CAST(void *, top_mozarea));
#endif
// If there is a top_mozarea and it doesn't have focus then we're
// going to ignore this request if we're part of the browser. If
// we're embedded then we do grab focus on our toplevel window since
// it doesn't hurt anything and the widget has to have focus inside
// of the GtkWindow. If it doesn't then we will not get the right
// events when we do actually get focus.
// see if the toplevel window has focus
gboolean toplevel_focus =
gtk_mozarea_get_toplevel_focus(GTK_MOZAREA(top_mozarea));
// we need to grab focus or make sure that we get focus the next
// time that the toplevel gets focus.
if (top_mozarea && !GTK_WIDGET_HAS_FOCUS(top_mozarea)) {
// If the toplevel window doesn't have an nsWindow data pointer
// then we are embedded.
gpointer data = gtk_object_get_data(GTK_OBJECT(toplevel), "nsWindow");
// We're embedded so always set focus unconditionally.
if (!data) {
data = gtk_object_get_data(GTK_OBJECT(top_mozarea), "nsWindow");
nsWindow *mozAreaWindow = NS_STATIC_CAST(nsWindow *, data);
mozAreaWindow->mBlockMozAreaFocusIn = PR_TRUE;
gtk_widget_grab_focus(top_mozarea);
mozAreaWindow->mBlockMozAreaFocusIn = PR_FALSE;
}
// We're not embedded. Just return.
else {
return NS_OK;
}
gpointer data = gtk_object_get_data(GTK_OBJECT(top_mozarea), "nsWindow");
nsWindow *mozAreaWindow = NS_STATIC_CAST(nsWindow *, data);
mozAreaWindow->mBlockMozAreaFocusIn = PR_TRUE;
gtk_widget_grab_focus(top_mozarea);
mozAreaWindow->mBlockMozAreaFocusIn = PR_FALSE;
// !!hack alert!! This works around bugs in version of gtk older
// than 1.2.9, which is what most people use. If the toplevel
// window doesn't have focus then we have to unset the focus flag
// on this widget since it was probably just set incorrectly.
if (!toplevel_focus)
GTK_WIDGET_UNSET_FLAGS(top_mozarea, GTK_HAS_FOCUS);
// always dispatch a set focus event
DispatchSetFocusEvent();
return NS_OK;
}
if (mHasFocus)
@ -1285,11 +1287,15 @@ void nsWindow::HandleMozAreaFocusIn(void)
// want to generate extra focus in events so just return.
if (mBlockMozAreaFocusIn)
return;
// otherwise, dispatch our focus events
#ifdef DEBUG_FOCUS
printf("nsWindow::HandleMozAreaFocusIn %p\n", NS_STATIC_CAST(void *, this));
#endif /* DEBUG_FOCUS */
gJustGotActivate = PR_TRUE;
// we only set the gJustGotActivate signal if we're the toplevel
// window. embedding handles activate semantics for us.
if (mIsToplevel)
gJustGotActivate = PR_TRUE;
DispatchSetFocusEvent();
}
@ -1334,7 +1340,10 @@ void nsWindow::HandleMozAreaFocusOut(void)
nsCOMPtr<nsIWidget> focusWidgetGuard(focusWidget);
focusWidget->DispatchLostFocusEvent();
focusWidget->DispatchDeactivateEvent();
// we only send activate/deactivate events for toplevel windows.
// activation and deactivation is handled by embedders.
if (mIsToplevel)
focusWidget->DispatchDeactivateEvent();
focusWidget->LoseFocus();
}
}
@ -2715,30 +2724,30 @@ gint handle_mozarea_focus_in(GtkWidget * aWidget,
gpointer aData)
{
if (!aWidget)
return PR_TRUE;
return FALSE;
if (!aGdkFocusEvent)
return PR_TRUE;
return FALSE;
nsWindow *widget = (nsWindow *)aData;
if (!widget)
return PR_TRUE;
return FALSE;
#ifdef DEBUG_FOCUS
printf("handle_mozarea_focus_in\n");
#endif
// make sure that we set our focus flag
GTK_WIDGET_SET_FLAGS(aWidget, GTK_HAS_FOCUS);
#ifdef DEBUG_FOCUS
printf("aWidget is %p\n", NS_STATIC_CAST(void *, aWidget));
#endif
// set the flag since got a focus in event
GTK_WIDGET_SET_FLAGS(aWidget, GTK_HAS_FOCUS);
widget->HandleMozAreaFocusIn();
return TRUE;
return FALSE;
}
gint handle_mozarea_focus_out(GtkWidget * aWidget,
@ -2750,17 +2759,17 @@ gint handle_mozarea_focus_out(GtkWidget * aWidget,
#endif
if (!aWidget) {
return PR_TRUE;
return FALSE;
}
if (!aGdkFocusEvent) {
return PR_TRUE;
return FALSE;
}
nsWindow *widget = (nsWindow *) aData;
if (!widget) {
return PR_TRUE;
return FALSE;
}
// make sure that we unset our focus flag
@ -2768,7 +2777,7 @@ gint handle_mozarea_focus_out(GtkWidget * aWidget,
widget->HandleMozAreaFocusOut();
return TRUE;
return FALSE;
}

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

@ -16,16 +16,37 @@
* Owen Taylor and Christopher Blizzard. All Rights Reserved. */
#include "gtkmozarea.h"
#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
static void gtk_mozarea_class_init (GtkMozAreaClass *klass);
static void gtk_mozarea_init (GtkMozArea *mozarea);
static void gtk_mozarea_realize (GtkWidget *widget);
static void gtk_mozarea_unrealize (GtkWidget *widget);
static void gtk_mozarea_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
static void gtk_mozarea_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void
attach_toplevel_listener(GtkMozArea *mozarea);
static Window
get_real_toplevel(Window aWindow);
static GdkFilterReturn
toplevel_window_filter(GdkXEvent *aGdkXEvent,
GdkEvent *aEvent,
gpointer data);
GtkWidgetClass *parent_class = NULL;
enum {
TOPLEVEL_FOCUS_IN,
TOPLEVEL_FOCUS_OUT,
LAST_SIGNAL
};
static guint mozarea_signals[LAST_SIGNAL] = { 0 };
GtkType
gtk_mozarea_get_type (void)
{
@ -55,8 +76,10 @@ static void
gtk_mozarea_class_init (GtkMozAreaClass *klass)
{
GtkWidgetClass *widget_class;
GtkObjectClass *object_class;
widget_class = GTK_WIDGET_CLASS (klass);
object_class = GTK_OBJECT_CLASS (klass);
widget_class->realize = gtk_mozarea_realize;
widget_class->unrealize = gtk_mozarea_unrealize;
@ -64,12 +87,31 @@ gtk_mozarea_class_init (GtkMozAreaClass *klass)
parent_class = gtk_type_class(gtk_widget_get_type());
/* set up our signals */
mozarea_signals[TOPLEVEL_FOCUS_IN] =
gtk_signal_new("toplevel_focus_in",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET(GtkMozAreaClass, toplevel_focus_in),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
mozarea_signals[TOPLEVEL_FOCUS_OUT] =
gtk_signal_new("toplevel_focus_out",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET(GtkMozAreaClass, toplevel_focus_out),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
gtk_object_class_add_signals(object_class, mozarea_signals, LAST_SIGNAL);
}
static void
gtk_mozarea_init (GtkMozArea *mozarea)
{
mozarea->superwin = NULL;
mozarea->toplevel_focus = FALSE;
}
static void
@ -84,8 +126,10 @@ gtk_mozarea_realize (GtkWidget *widget)
mozarea = GTK_MOZAREA (widget);
mozarea->superwin = gdk_superwin_new (gtk_widget_get_parent_window (widget),
widget->allocation.x, widget->allocation.y,
widget->allocation.width, widget->allocation.height);
widget->allocation.x,
widget->allocation.y,
widget->allocation.width,
widget->allocation.height);
gdk_window_set_user_data (mozarea->superwin->shell_window, mozarea);
widget->window = mozarea->superwin->shell_window;
widget->style = gtk_style_attach (widget->style, widget->window);
@ -93,6 +137,9 @@ gtk_mozarea_realize (GtkWidget *widget)
if we don't then it will be destroyed by both the superwin
destroy method and the widget class destructor */
gdk_window_ref(widget->window);
/* attach the toplevel X listener */
attach_toplevel_listener(mozarea);
}
static void
@ -138,3 +185,122 @@ gtk_mozarea_new (GdkWindow *parent_window)
return GTK_WIDGET (gtk_type_new (GTK_TYPE_MOZAREA));
}
gboolean
gtk_mozarea_get_toplevel_focus(GtkMozArea *area)
{
g_return_val_if_fail(GTK_IS_MOZAREA(area), FALSE);
return area->toplevel_focus;
}
/* this function will attach a listener to this widget's real toplevel
window */
static void
attach_toplevel_listener(GtkMozArea *mozarea)
{
/* get the native window for this widget */
GtkWidget *widget = GTK_WIDGET(mozarea);
Window window = GDK_WINDOW_XWINDOW(widget->window);
Window toplevel = get_real_toplevel(window);
/* check to see if this is an already registered window with the
type system. */
GdkWindow *gdk_window = gdk_window_lookup(toplevel);
/* This isn't our window? It is now, fool! */
if (!gdk_window) {
/* import it into the type system */
gdk_window = gdk_window_foreign_new(toplevel);
/* make sure that we are listening for the right events on it. */
gdk_window_set_events(gdk_window, GDK_FOCUS_CHANGE_MASK);
}
/* attach our passive filter. when the window is destroyed it will
automatically be removed. */
gdk_window_add_filter(gdk_window, toplevel_window_filter, mozarea);
}
/* this function will try to find the real toplevel for a gdk window. */
static Window
get_real_toplevel(Window aWindow)
{
Window current = aWindow;
Atom atom;
/* get the atom for the WM_STATE variable that you get on WM
managed windows. */
atom = XInternAtom(GDK_DISPLAY(), "WM_STATE", FALSE);
while (current) {
Atom type = None;
int format;
unsigned long nitems, after;
unsigned char *data;
Window root_return;
Window parent_return;
Window *children_return = NULL;
unsigned int nchildren_return;
/* check for the atom on this window */
XGetWindowProperty(GDK_DISPLAY(), current, atom,
0, 0, /* offsets */
False, /* don't delete */
AnyPropertyType,
&type, &format, &nitems, &after, &data);
/* did we get something? */
if (type != None) {
XFree(data);
data = NULL;
/* ok, this is the toplevel window since it has the property set
on it. */
break;
}
/* what is the parent? */
XQueryTree(GDK_DISPLAY(), current, &root_return,
&parent_return, &children_return,
&nchildren_return);
if (children_return)
XFree(children_return);
/* If the parent window of this window is the root window then
there is no window manager running so the current window is the
toplevel window. */
if (parent_return == root_return)
break;
current = parent_return;
}
return current;
}
static GdkFilterReturn
toplevel_window_filter(GdkXEvent *aGdkXEvent,
GdkEvent *aEvent,
gpointer data)
{
XEvent *xevent = (XEvent *)aGdkXEvent;
GtkMozArea *mozarea = (GtkMozArea *)data;
switch (xevent->xany.type) {
case FocusIn:
mozarea->toplevel_focus = TRUE;
gtk_signal_emit(GTK_OBJECT(mozarea), mozarea_signals[TOPLEVEL_FOCUS_IN]);
break;
case FocusOut:
mozarea->toplevel_focus = FALSE;
gtk_signal_emit(GTK_OBJECT(mozarea), mozarea_signals[TOPLEVEL_FOCUS_OUT]);
break;
default:
break;
}
return GDK_FILTER_CONTINUE;
}

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

@ -38,15 +38,21 @@ struct _GtkMozArea
{
GtkWidget widget;
GdkSuperWin *superwin;
gboolean toplevel_focus;
};
struct _GtkMozAreaClass
{
GtkWindowClass window_class;
/* signals */
void (* toplevel_focus_in ) (GtkMozArea *area);
void (* toplevel_focus_out) (GtkMozArea *area);
};
GtkType gtk_mozarea_get_type (void);
GtkWidget *gtk_mozarea_new ();
gboolean gtk_mozarea_get_toplevel_focus(GtkMozArea *area);
#ifdef __cplusplus
}