зеркало из https://github.com/mozilla/gecko-dev.git
b=485125 nsIWidget::SetParent(nsnull) for gtk. r=roc
This commit is contained in:
Родитель
b30669d3f7
Коммит
a60b90eae7
|
@ -375,7 +375,7 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget)
|
|||
{
|
||||
MozContainerChild *child;
|
||||
MozContainer *moz_container;
|
||||
GList *tmp_list;
|
||||
GdkWindow* parent_window;
|
||||
|
||||
g_return_if_fail (IS_MOZ_CONTAINER(container));
|
||||
g_return_if_fail (GTK_IS_WIDGET(child_widget));
|
||||
|
@ -385,8 +385,34 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget)
|
|||
child = moz_container_get_child (moz_container, child_widget);
|
||||
g_return_if_fail (child);
|
||||
|
||||
if(child->widget == child_widget) {
|
||||
gtk_widget_unparent(child_widget);
|
||||
/* gtk_widget_unparent will remove the parent window (as well as the
|
||||
* parent widget), but, in Mozilla's window hierarchy, the parent window
|
||||
* may need to be kept because it may be part of a GdkWindow sub-hierarchy
|
||||
* that is being moved to another MozContainer.
|
||||
*
|
||||
* (In a conventional GtkWidget hierarchy, GdkWindows being reparented
|
||||
* would have their own GtkWidget and that widget would be the one being
|
||||
* reparented. In Mozilla's hierarchy, the parent_window needs to be
|
||||
* retained so that the GdkWindow sub-hierarchy is maintained.)
|
||||
*/
|
||||
parent_window = gtk_widget_get_parent_window(child_widget);
|
||||
if (parent_window)
|
||||
g_object_ref(parent_window);
|
||||
|
||||
gtk_widget_unparent(child_widget);
|
||||
|
||||
if (parent_window) {
|
||||
/* The child_widget will always still exist because g_signal_emit,
|
||||
* which invokes this function, holds a reference.
|
||||
*
|
||||
* If parent_window is the container's root window then it will not be
|
||||
* the parent_window if the child_widget is placed in another
|
||||
* container.
|
||||
*/
|
||||
if (parent_window != GTK_WIDGET(container)->window)
|
||||
gtk_widget_set_parent_window(child_widget, parent_window);
|
||||
|
||||
g_object_unref(parent_window);
|
||||
}
|
||||
|
||||
moz_container->children = g_list_remove(moz_container->children, child);
|
||||
|
|
|
@ -45,6 +45,37 @@
|
|||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* MozContainer
|
||||
*
|
||||
* This class serves two purposes in the nsIWidget implementation.
|
||||
*
|
||||
* - It provides objects to receive signals from GTK for events on native
|
||||
* windows.
|
||||
*
|
||||
* - It provides a container parent for GtkWidgets. The only GtkWidgets
|
||||
* that need this in Mozilla are the GtkSockets for windowed plugins (Xt
|
||||
* and XEmbed).
|
||||
*
|
||||
* Note that the window hierarchy in Mozilla differs from conventional
|
||||
* GtkWidget hierarchies.
|
||||
*
|
||||
* Mozilla's hierarchy exists through the GdkWindow hierarchy, and all child
|
||||
* GdkWindows (within a child nsIWidget hierarchy) belong to one MozContainer
|
||||
* GtkWidget. If the MozContainer is unrealized or its GdkWindows are
|
||||
* destroyed for some other reason, then the hierarchy no longer exists. (In
|
||||
* conventional GTK clients, the hierarchy is recorded by the GtkWidgets, and
|
||||
* so can be re-established after destruction of the GdkWindows.)
|
||||
*
|
||||
* One consequence of this is that the MozContainer does not know which of its
|
||||
* GdkWindows should parent child GtkWidgets. (Conventional GtkContainers
|
||||
* determine which GdkWindow to assign child GtkWidgets.)
|
||||
*
|
||||
* Therefore, when adding a child GtkWidget to a MozContainer,
|
||||
* gtk_widget_set_parent_window should be called on the child GtkWidget before
|
||||
* it is realized.
|
||||
*/
|
||||
|
||||
#define MOZ_CONTAINER_TYPE (moz_container_get_type())
|
||||
#define MOZ_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOZ_CONTAINER_TYPE, MozContainer))
|
||||
#define MOZ_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOZ_CONTAINER_TYPE, MozContainerClass))
|
||||
|
|
|
@ -124,24 +124,6 @@ moz_drawingarea_reparent (MozDrawingarea *drawingarea, GdkWindow *aNewParent)
|
|||
aNewParent, 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
nullify_widget_pointers (gpointer data, GObject *widget)
|
||||
{
|
||||
MozDrawingarea *drawingarea = data;
|
||||
|
||||
#ifdef DEBUG
|
||||
gpointer user_data;
|
||||
/* This function may get called twice before widget is destroyed,
|
||||
so the user_data may have already been nullified. */
|
||||
gdk_window_get_user_data(drawingarea->inner_window, &user_data);
|
||||
if (user_data && widget && user_data != widget)
|
||||
g_critical("user_data does not match widget");
|
||||
#endif
|
||||
|
||||
gdk_window_set_user_data(drawingarea->inner_window, NULL);
|
||||
gdk_window_set_user_data(drawingarea->clip_window, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
moz_drawingarea_create_windows (MozDrawingarea *drawingarea, GdkWindow *parent,
|
||||
GtkWidget *widget, GdkVisual *visual)
|
||||
|
@ -189,8 +171,6 @@ moz_drawingarea_create_windows (MozDrawingarea *drawingarea, GdkWindow *parent,
|
|||
&attributes, attributes_mask);
|
||||
gdk_window_set_user_data(drawingarea->inner_window, widget);
|
||||
|
||||
g_object_weak_ref(G_OBJECT(widget), nullify_widget_pointers, drawingarea);
|
||||
|
||||
/* set the default pixmap to None so that you don't end up with the
|
||||
gtk default which is BlackPixel. */
|
||||
gdk_window_set_back_pixmap(drawingarea->inner_window, NULL, FALSE);
|
||||
|
@ -210,12 +190,6 @@ moz_drawingarea_finalize (GObject *object)
|
|||
|
||||
drawingarea = MOZ_DRAWINGAREA(object);
|
||||
|
||||
gdk_window_get_user_data(drawingarea->inner_window, &user_data);
|
||||
if (user_data) {
|
||||
g_object_weak_unref(user_data, nullify_widget_pointers, drawingarea);
|
||||
nullify_widget_pointers(drawingarea, NULL);
|
||||
}
|
||||
|
||||
gdk_window_destroy(drawingarea->inner_window);
|
||||
gdk_window_destroy(drawingarea->clip_window);
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ typedef struct _MozDrawingareaClass MozDrawingareaClass;
|
|||
struct _MozDrawingarea
|
||||
{
|
||||
GObject parent_instance;
|
||||
/* AFAIK this clip_window (and thus this whole class) exists solely to
|
||||
* make gdk_window_scroll() smooth for nsIWidget::Scroll(). */
|
||||
GdkWindow *clip_window;
|
||||
GdkWindow *inner_window;
|
||||
};
|
||||
|
|
|
@ -177,6 +177,7 @@ static gboolean expose_event_cb (GtkWidget *widget,
|
|||
GdkEventExpose *event);
|
||||
static gboolean configure_event_cb (GtkWidget *widget,
|
||||
GdkEventConfigure *event);
|
||||
static void container_unrealize_cb (GtkWidget *widget);
|
||||
static void size_allocate_cb (GtkWidget *widget,
|
||||
GtkAllocation *allocation);
|
||||
static gboolean delete_event_cb (GtkWidget *widget,
|
||||
|
@ -355,6 +356,8 @@ PRBool gDisableNativeTheme = PR_FALSE;
|
|||
// created for exposes, even if the display has a different depth
|
||||
static PRBool gForce24bpp = PR_FALSE;
|
||||
|
||||
static GtkWidget *gInvisibleContainer = NULL;
|
||||
|
||||
nsWindow::nsWindow()
|
||||
{
|
||||
mIsTopLevel = PR_FALSE;
|
||||
|
@ -642,6 +645,79 @@ nsWindow::Create(nsNativeWidget aParent,
|
|||
return rv;
|
||||
}
|
||||
|
||||
static GtkWidget*
|
||||
EnsureInvisibleContainer()
|
||||
{
|
||||
if (!gInvisibleContainer) {
|
||||
// GtkWidgets need to be anchored to a GtkWindow to be realized (to
|
||||
// have a window). Using GTK_WINDOW_POPUP rather than
|
||||
// GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
|
||||
// initialization and window manager interaction.
|
||||
GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
|
||||
gInvisibleContainer = moz_container_new();
|
||||
gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
|
||||
gtk_widget_realize(gInvisibleContainer);
|
||||
|
||||
}
|
||||
return gInvisibleContainer;
|
||||
}
|
||||
|
||||
static void
|
||||
CheckDestroyInvisibleContainer()
|
||||
{
|
||||
NS_PRECONDITION(gInvisibleContainer, "oh, no");
|
||||
|
||||
if (!gdk_window_peek_children(gInvisibleContainer->window)) {
|
||||
// No children, so not in use.
|
||||
// Make sure to destroy the GtkWindow also.
|
||||
gtk_widget_destroy(gInvisibleContainer->parent);
|
||||
gInvisibleContainer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
|
||||
// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
|
||||
// the GdkWindow hierarchy. If aNewWidget is NULL, the reference to
|
||||
// aOldWidget is removed from its GdkWindows, and child GtkWidgets are
|
||||
// destroyed.
|
||||
static void
|
||||
SetWidgetForHierarchy(GdkWindow *aWindow,
|
||||
GtkWidget *aOldWidget,
|
||||
GtkWidget *aNewWidget)
|
||||
{
|
||||
gpointer data;
|
||||
gdk_window_get_user_data(aWindow, &data);
|
||||
|
||||
if (data != aOldWidget) {
|
||||
if (!GTK_IS_WIDGET(data))
|
||||
return;
|
||||
|
||||
GtkWidget* widget = static_cast<GtkWidget*>(data);
|
||||
if (widget->parent != aOldWidget)
|
||||
return;
|
||||
|
||||
// This window belongs to a child widget, which will no longer be a
|
||||
// child of aOldWidget.
|
||||
if (aNewWidget) {
|
||||
gtk_widget_reparent(widget, aNewWidget);
|
||||
} else {
|
||||
// aNewWidget == NULL indicates that the window is about to be
|
||||
// destroyed.
|
||||
gtk_widget_destroy(widget);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (GList *list = gdk_window_peek_children(aWindow);
|
||||
list;
|
||||
list = list->next) {
|
||||
SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
|
||||
}
|
||||
|
||||
gdk_window_set_user_data(aWindow, aNewWidget);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::Destroy(void)
|
||||
{
|
||||
|
@ -724,6 +800,24 @@ nsWindow::Destroy(void)
|
|||
mDragLeaveTimer = nsnull;
|
||||
}
|
||||
|
||||
GtkWidget *owningWidget = GetMozContainerWidget();
|
||||
if (mShell) {
|
||||
gtk_widget_destroy(mShell);
|
||||
mShell = nsnull;
|
||||
mContainer = nsnull;
|
||||
}
|
||||
else if (mContainer) {
|
||||
gtk_widget_destroy(GTK_WIDGET(mContainer));
|
||||
mContainer = nsnull;
|
||||
}
|
||||
else if (owningWidget) {
|
||||
// Remove references from GdkWindows back to their container
|
||||
// widget while the GdkWindow hierarchy is still available.
|
||||
// (OnContainerUnrealize does this when the MozContainer widget is
|
||||
// destroyed.)
|
||||
SetWidgetForHierarchy(mDrawingarea->clip_window, owningWidget, NULL);
|
||||
}
|
||||
|
||||
if (mDrawingarea) {
|
||||
g_object_set_data(G_OBJECT(mDrawingarea->clip_window),
|
||||
"nsWindow", NULL);
|
||||
|
@ -735,18 +829,15 @@ nsWindow::Destroy(void)
|
|||
g_object_set_data(G_OBJECT(mDrawingarea->inner_window),
|
||||
"mozdrawingarea", NULL);
|
||||
|
||||
NS_ASSERTION(!get_gtk_widget_for_gdk_window(mDrawingarea->inner_window),
|
||||
"widget reference not removed");
|
||||
|
||||
g_object_unref(mDrawingarea);
|
||||
mDrawingarea = nsnull;
|
||||
}
|
||||
|
||||
if (mShell) {
|
||||
gtk_widget_destroy(mShell);
|
||||
mShell = nsnull;
|
||||
mContainer = nsnull;
|
||||
}
|
||||
else if (mContainer) {
|
||||
gtk_widget_destroy(GTK_WIDGET(mContainer));
|
||||
mContainer = nsnull;
|
||||
if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
|
||||
CheckDestroyInvisibleContainer();
|
||||
}
|
||||
|
||||
OnDestroy();
|
||||
|
@ -769,29 +860,63 @@ nsWindow::GetParent(void)
|
|||
NS_IMETHODIMP
|
||||
nsWindow::SetParent(nsIWidget *aNewParent)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNewParent);
|
||||
|
||||
GdkWindow* newParentWindow =
|
||||
static_cast<GdkWindow*>(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
|
||||
NS_ASSERTION(newParentWindow, "Parent widget has a null native window handle");
|
||||
|
||||
if (!mShell && mDrawingarea) {
|
||||
#ifdef DEBUG
|
||||
if (!mContainer) {
|
||||
// Check that the new Parent window has the same MozContainer
|
||||
gpointer old_container;
|
||||
gdk_window_get_user_data(mDrawingarea->inner_window,
|
||||
&old_container);
|
||||
gpointer new_container;
|
||||
gdk_window_get_user_data(newParentWindow, &new_container);
|
||||
NS_ASSERTION(old_container == new_container,
|
||||
"FIXME: Wrong MozContainer on MozDrawingarea");
|
||||
}
|
||||
#endif
|
||||
moz_drawingarea_reparent(mDrawingarea, newParentWindow);
|
||||
} else {
|
||||
if (mContainer || !mDrawingarea || !mParent) {
|
||||
NS_NOTREACHED("nsWindow::SetParent - reparenting a non-child window");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// nsBaseWidget::SetZIndex adds child widgets to the parent's list.
|
||||
nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
|
||||
mParent->RemoveChild(this);
|
||||
|
||||
mParent = aNewParent;
|
||||
|
||||
GtkWidget* oldContainer = GetMozContainerWidget();
|
||||
if (!oldContainer) {
|
||||
// The GdkWindows have been destroyed so there is nothing else to
|
||||
// reparent.
|
||||
NS_ABORT_IF_FALSE(GDK_WINDOW_OBJECT(mDrawingarea->inner_window)->destroyed,
|
||||
"live GdkWindow with no widget");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(mDrawingarea->inner_window)->destroyed,
|
||||
"destroyed GdkWindow with widget");
|
||||
|
||||
GdkWindow* newParentWindow = NULL;
|
||||
GtkWidget* newContainer = NULL;
|
||||
if (aNewParent) {
|
||||
newParentWindow = static_cast<GdkWindow*>
|
||||
(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
|
||||
if (newParentWindow) {
|
||||
newContainer = get_gtk_widget_for_gdk_window(newParentWindow);
|
||||
}
|
||||
} else {
|
||||
// aNewParent is NULL, but reparent to a hidden window to avoid
|
||||
// destroying the GdkWindow and its descendants.
|
||||
// An invisible container widget is needed to hold descendant
|
||||
// GtkWidgets.
|
||||
newContainer = EnsureInvisibleContainer();
|
||||
newParentWindow = newContainer->window;
|
||||
}
|
||||
|
||||
if (!newContainer) {
|
||||
// The new parent GdkWindow has been destroyed.
|
||||
NS_ABORT_IF_FALSE(!newParentWindow ||
|
||||
GDK_WINDOW_OBJECT(newParentWindow)->destroyed,
|
||||
"live GdkWindow with no widget");
|
||||
Destroy();
|
||||
} else {
|
||||
if (newContainer != oldContainer) {
|
||||
NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(newParentWindow)->destroyed,
|
||||
"destroyed GdkWindow with widget");
|
||||
SetWidgetForHierarchy(mDrawingarea->clip_window, oldContainer,
|
||||
newContainer);
|
||||
}
|
||||
|
||||
moz_drawingarea_reparent(mDrawingarea, newParentWindow);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2336,6 +2461,21 @@ nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::OnContainerUnrealize(GtkWidget *aWidget)
|
||||
{
|
||||
// The GdkWindows are about to be destroyed (but not deleted), so remove
|
||||
// their references back to their container widget while the GdkWindow
|
||||
// hierarchy is still available.
|
||||
|
||||
NS_ASSERTION(mContainer == MOZ_CONTAINER(aWidget),
|
||||
"unexpected \"unrealize\" signal");
|
||||
|
||||
if (mDrawingarea) {
|
||||
SetWidgetForHierarchy(mDrawingarea->clip_window, aWidget, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::OnSizeAllocate(GtkWidget *aWidget, GtkAllocation *aAllocation)
|
||||
{
|
||||
|
@ -3907,6 +4047,8 @@ nsWindow::NativeCreate(nsIWidget *aParent,
|
|||
}
|
||||
|
||||
if (mContainer) {
|
||||
g_signal_connect(G_OBJECT(mContainer), "unrealize",
|
||||
G_CALLBACK(container_unrealize_cb), NULL);
|
||||
g_signal_connect_after(G_OBJECT(mContainer), "size_allocate",
|
||||
G_CALLBACK(size_allocate_cb), NULL);
|
||||
g_signal_connect(G_OBJECT(mContainer), "expose_event",
|
||||
|
@ -5224,6 +5366,17 @@ configure_event_cb(GtkWidget *widget,
|
|||
return window->OnConfigureEvent(widget, event);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
container_unrealize_cb (GtkWidget *widget)
|
||||
{
|
||||
nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window->OnContainerUnrealize(widget);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)
|
||||
|
|
|
@ -219,6 +219,7 @@ public:
|
|||
GdkEventExpose *aEvent);
|
||||
gboolean OnConfigureEvent(GtkWidget *aWidget,
|
||||
GdkEventConfigure *aEvent);
|
||||
void OnContainerUnrealize(GtkWidget *aWidget);
|
||||
void OnSizeAllocate(GtkWidget *aWidget,
|
||||
GtkAllocation *aAllocation);
|
||||
void OnDeleteEvent(GtkWidget *aWidget,
|
||||
|
|
Загрузка…
Ссылка в новой задаче