Bug 1225044 - Workaround GTK windows freezing on unmap. r=karlt, a=gchang
authorAndrew Comminos <andrew@comminos.com>
Fri, 24 Jun 2016 14:51:28 -0400
changeset 342325 41530926a90ce3ce703a1e9da2cac7665cd61cd8
parent 342324 9fc9fb9f8ae843b1841ce5b08ce1634107490aa8
child 342326 a90a1d791c7d6bd46d81c19bc6505a0fda0bf9f2
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, gchang
bugs1225044
milestone49.0
Bug 1225044 - Workaround GTK windows freezing on unmap. r=karlt, a=gchang MozReview-Commit-ID: AMLk2Pe05JY
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -211,16 +211,19 @@ static gboolean visibility_notify_event_
                                            GdkEventVisibility *event);
 static void     hierarchy_changed_cb      (GtkWidget *widget,
                                            GtkWidget *previous_toplevel);
 static gboolean window_state_event_cb     (GtkWidget *widget,
                                            GdkEventWindowState *event);
 static void     theme_changed_cb          (GtkSettings *settings,
                                            GParamSpec *pspec,
                                            nsWindow *data);
+static void     check_resize_cb           (GtkContainer* container,
+                                           gpointer user_data);
+
 #if (MOZ_WIDGET_GTK == 3)
 static void     scale_changed_cb          (GtkWidget* widget,
                                            GParamSpec* aPSpec,
                                            gpointer aPointer);
 #endif
 #if GTK_CHECK_VERSION(3,4,0)
 static gboolean touch_event_cb            (GtkWidget* aWidget,
                                            GdkEventTouch* aEvent);
@@ -469,16 +472,17 @@ nsWindow::nsWindow()
     mTransparencyBitmapWidth  = 0;
     mTransparencyBitmapHeight = 0;
 
 #if GTK_CHECK_VERSION(3,4,0)
     mLastScrollEventTime = GDK_CURRENT_TIME;
 #endif
 
     mFallbackSurface = nullptr;
+    mPendingConfigures = 0;
 }
 
 nsWindow::~nsWindow()
 {
     LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
 
     delete[] mTransparencyBitmap;
     mTransparencyBitmap = nullptr;
@@ -2412,16 +2416,18 @@ nsWindow::OnConfigureEvent(GtkWidget *aW
     //   the client window.
     //
     //   Override-redirect windows are children of the root window so parent
     //   coordinates are root coordinates.
 
     LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
          aEvent->x, aEvent->y, aEvent->width, aEvent->height));
 
+    mPendingConfigures--;
+
     LayoutDeviceIntRect screenBounds;
     GetScreenBounds(screenBounds);
 
     if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
         // This check avoids unwanted rollup on spurious configure events from
         // Cygwin/X (bug 672103).
         if (mBounds.x != screenBounds.x ||
             mBounds.y != screenBounds.y) {
@@ -3400,16 +3406,22 @@ nsWindow::OnDPIChanged()
       presShell->BackingScaleFactorChanged();
       // Update menu's font size etc
       presShell->ThemeChanged();
     }
   }
 }
 
 void
+nsWindow::OnCheckResize()
+{
+    mPendingConfigures++;
+}
+
+void
 nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefPoint,
                             guint aTime)
 {
     WidgetDragEvent event(true, aMsg, this);
 
     if (aMsg == eDragOver) {
         InitDragEvent(event);
     }
@@ -3855,16 +3867,18 @@ nsWindow::Create(nsIWidget* aParent,
     // attach listeners for events
     if (mShell) {
         g_signal_connect(mShell, "configure_event",
                          G_CALLBACK(configure_event_cb), nullptr);
         g_signal_connect(mShell, "delete_event",
                          G_CALLBACK(delete_event_cb), nullptr);
         g_signal_connect(mShell, "window_state_event",
                          G_CALLBACK(window_state_event_cb), nullptr);
+        g_signal_connect(mShell, "check-resize",
+                         G_CALLBACK(check_resize_cb), nullptr);
 
         GtkSettings* default_settings = gtk_settings_get_default();
         g_signal_connect_after(default_settings,
                                "notify::gtk-theme-name",
                                G_CALLBACK(theme_changed_cb), this);
         g_signal_connect_after(default_settings,
                                "notify::gtk-font-name",
                                G_CALLBACK(theme_changed_cb), this);
@@ -4174,17 +4188,41 @@ nsWindow::NativeShow(bool aAction)
             gtk_widget_show(GTK_WIDGET(mContainer));
         }
         else if (mGdkWindow) {
             gdk_window_show_unraised(mGdkWindow);
         }
     }
     else {
         if (mIsTopLevel) {
-            gtk_widget_hide(GTK_WIDGET(mShell));
+            // Workaround window freezes on GTK versions before 3.21.2 by
+            // ensuring that configure events get dispatched to windows before
+            // they are unmapped. See bug 1225044.
+            if (gtk_check_version(3, 21, 2) != nullptr && mPendingConfigures > 0) {
+                GtkAllocation allocation;
+                gtk_widget_get_allocation(GTK_WIDGET(mShell), &allocation);
+
+                GdkEventConfigure event;
+                PodZero(&event);
+                event.type = GDK_CONFIGURE;
+                event.window = mGdkWindow;
+                event.send_event = TRUE;
+                event.x = allocation.x;
+                event.y = allocation.y;
+                event.width = allocation.width;
+                event.height = allocation.height;
+
+                auto shellClass = GTK_WIDGET_GET_CLASS(mShell);
+                for (int i = 0; i < mPendingConfigures; i++) {
+                    Unused << shellClass->configure_event(mShell, &event);
+                }
+                mPendingConfigures = 0;
+            }
+
+            gtk_widget_hide(mShell);
 
             ClearTransparencyBitmap(); // Release some resources
         }
         else if (mContainer) {
             gtk_widget_hide(GTK_WIDGET(mContainer));
         }
         else if (mGdkWindow) {
             gdk_window_hide(mGdkWindow);
@@ -6029,16 +6067,26 @@ window_state_event_cb (GtkWidget *widget
 
 static void
 theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
 {
     RefPtr<nsWindow> window = data;
     window->ThemeChanged();
 }
 
+static void
+check_resize_cb (GtkContainer* container, gpointer user_data)
+{
+    RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container));
+    if (!window) {
+      return;
+    }
+    window->OnCheckResize();
+}
+
 #if (MOZ_WIDGET_GTK == 3)
 static void
 scale_changed_cb (GtkWidget* widget, GParamSpec* aPSpec, gpointer aPointer)
 {
     RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
     if (!window) {
       return;
     }
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -249,16 +249,17 @@ public:
     void               SetPluginType(PluginType aPluginType);
 #ifdef MOZ_X11
     void               SetNonXEmbedPluginFocus(void);
     void               LoseNonXEmbedPluginFocus(void);
 #endif /* MOZ_X11 */
 
     void               ThemeChanged(void);
     void               OnDPIChanged(void);
+    void               OnCheckResize(void);
 
 #ifdef MOZ_X11
     Window             mOldFocusWindow;
 #endif /* MOZ_X11 */
 
     static guint32     sLastButtonPressTime;
 
     NS_IMETHOD         BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
@@ -466,16 +467,20 @@ private:
     // If we're using xshm rendering
     RefPtr<nsShmImage>  mFrontShmImage;
     RefPtr<nsShmImage>  mBackShmImage;
 #endif
 
     // A fallback image surface when a SHM surface is unavailable.
     cairo_surface_t* mFallbackSurface;
 
+    // Upper bound on pending ConfigureNotify events to be dispatched to the
+    // window. See bug 1225044.
+    int mPendingConfigures;
+
 #ifdef ACCESSIBILITY
     RefPtr<mozilla::a11y::Accessible> mRootAccessible;
 
     /**
      * Request to create the accessible for this window if it is top level.
      */
     void                CreateRootAccessible();