Bug 1737068 [Linux] Enable/Disable rendering to GdkWindow when we're mapped/unmapped r=rmader
authorstransky <stransky@redhat.com>
Mon, 25 Oct 2021 09:30:06 +0000
changeset 596813 00569d0fc9b92fc99ab56ef014425b3c4f9c3ab3
parent 596812 334e3d59d932dd2850dec7c582f32f49b504e450
child 596814 88ecfc06a2672fcf016d3a6cd5ec36ddd0d19ecf
push id38912
push userncsoregi@mozilla.com
push dateMon, 25 Oct 2021 21:41:06 +0000
treeherdermozilla-central@6c01444e1721 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrmader
bugs1737068
milestone95.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1737068 [Linux] Enable/Disable rendering to GdkWindow when we're mapped/unmapped r=rmader This patch does: - Track nsWindow real visibility by widget_map_cb / widget_unrealize_cb callbacks and set mIsMapped attribute properly. - Clear mGdkWindow attribute when mGdkWindow is not visible and we can't paint into it. - Implement and use ConfigureGdkWindow() to set up mGdkWindow when it's visible, start compositor, VSync. - Implement and use ReleaseGdkWindow() to clear up mGdkWindow when it's not visible, stop compositor, Vsync. - Make sure nsWindow works when mGdkWindow is null. - Configure Drag popup accordingly. Differential Revision: https://phabricator.services.mozilla.com/D126895
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -188,17 +188,18 @@ static GdkWindow* get_inner_gdk_window(G
                                        gint* retx, gint* rety);
 
 static int is_parent_ungrab_enter(GdkEventCrossing* aEvent);
 static int is_parent_grab_leave(GdkEventCrossing* aEvent);
 
 /* callbacks from widgets */
 static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr);
 static gboolean configure_event_cb(GtkWidget* widget, GdkEventConfigure* event);
-static void container_unrealize_cb(GtkWidget* widget);
+static void widget_map_cb(GtkWidget* widget);
+static void widget_unrealize_cb(GtkWidget* widget);
 static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation);
 static void toplevel_window_size_allocate_cb(GtkWidget* widget,
                                              GtkAllocation* allocation);
 static gboolean delete_event_cb(GtkWidget* widget, GdkEventAny* event);
 static gboolean enter_notify_event_cb(GtkWidget* widget,
                                       GdkEventCrossing* event);
 static gboolean leave_notify_event_cb(GtkWidget* widget,
                                       GdkEventCrossing* event);
@@ -412,16 +413,17 @@ void GetWindowOrigin(GdkWindow* aWindow,
 #endif
 }
 
 nsWindow::nsWindow()
     : mIsDestroyed(false),
       mNeedsDispatchResized(false),
       mIsShown(false),
       mNeedsShow(false),
+      mIsMapped(false),
       mEnabled(true),
       mCreated(false),
       mHandleTouchEvent(false),
       mIsDragPopup(false),
       mPopupHint(),
       mWindowScaleFactorChanged(true),
       mWindowScaleFactor(1),
       mCompositedScreen(gdk_screen_is_composited(gdk_screen_get_default())),
@@ -442,16 +444,18 @@ nsWindow::nsWindow()
       mPendingConfigures(0),
       mGtkWindowDecoration(GTK_DECORATION_NONE),
       mDrawToContainer(false),
       mDrawInTitlebar(false),
       mTitlebarBackdropState(false),
       mIsPIPWindow(false),
       mIsWaylandPanelWindow(false),
       mAlwaysOnTop(false),
+      mNoAutoHide(false),
+      mMouseTransparent(false),
       mIsTransparent(false),
       mTransparencyBitmap(nullptr),
       mTransparencyBitmapWidth(0),
       mTransparencyBitmapHeight(0),
       mTransparencyBitmapForTitlebar(false),
       mHasAlphaVisual(false),
       mLastMotionPressure(0),
       mLastSizeMode(nsSizeMode_Normal),
@@ -477,17 +481,18 @@ nsWindow::nsWindow()
 #ifdef ACCESSIBILITY
       ,
       mRootAccessible(nullptr)
 #endif
 #ifdef MOZ_X11
       ,
       mXWindow(X11None),
       mXVisual(nullptr),
-      mXDepth(0)
+      mXDepth(0),
+      mIsShaped(false)
 #endif
 #ifdef MOZ_WAYLAND
       ,
       mNativePointerLockCenter(LayoutDeviceIntPoint()),
       mLockedPointer(nullptr),
       mRelativePointer(nullptr)
 #endif
 {
@@ -1612,17 +1617,19 @@ nsWindow* nsWindow::WaylandPopupGetTopmo
         return parentnsWindow;
       }
     }
   }
   return nullptr;
 }
 
 bool nsWindow::WaylandPopupNeedsTrackInHierarchy() {
-  MOZ_RELEASE_ASSERT(!mIsDragPopup);
+  if (mIsDragPopup) {
+    return false;
+  }
 
   if (mPopupTrackInHierarchyConfigured) {
     return mPopupTrackInHierarchy;
   }
 
   nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
   if (!popupFrame) {
     return false;
@@ -1987,16 +1994,25 @@ void nsWindow::WaylandPopupSetDirectPosi
   GdkPoint position = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
   GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
 
   LOG_POPUP("nsWindow::WaylandPopupSetDirectPosition %d,%d -> %d x %d\n",
             position.x, position.y, size.width, size.height);
 
   mPopupPosition = position;
 
+  if (mIsDragPopup) {
+    gtk_window_move(GTK_WINDOW(mShell), mPopupPosition.x, mPopupPosition.y);
+    gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
+    // DND window is placed inside container so we need to make hard size
+    // request to ensure parent container is resized too.
+    gtk_widget_set_size_request(GTK_WIDGET(mShell), size.width, size.height);
+    return;
+  }
+
   GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
   nsWindow* window = get_window_for_gtk_widget(GTK_WIDGET(parentGtkWindow));
   GdkWindow* gdkWindow =
       gtk_widget_get_window(GTK_WIDGET(window->GetMozContainer()));
 
   int parentWidth = gdk_window_get_width(gdkWindow);
   int popupWidth = size.width;
 
@@ -2364,25 +2380,32 @@ void nsWindow::SetZIndex(int32_t aZIndex
   if (GetPrevSibling() == oldPrev) {
     return;
   }
 
   NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
 
   // We skip the nsWindows that don't have mGdkWindows.
   // These are probably in the process of being destroyed.
+  if (!mGdkWindow) {
+    return;
+  }
 
   if (!GetNextSibling()) {
     // We're to be on top.
-    if (mGdkWindow) gdk_window_raise(mGdkWindow);
+    if (mGdkWindow) {
+      gdk_window_raise(mGdkWindow);
+    }
   } else {
     // All the siblings before us need to be below our widget.
     for (nsWindow* w = this; w;
          w = static_cast<nsWindow*>(w->GetPrevSibling())) {
-      if (w->mGdkWindow) gdk_window_lower(w->mGdkWindow);
+      if (w->mGdkWindow) {
+        gdk_window_lower(w->mGdkWindow);
+      }
     }
   }
 }
 
 void nsWindow::SetSizeMode(nsSizeMode aMode) {
   LOG("nsWindow::SetSizeMode %d\n", aMode);
 
   // Save the requested state.
@@ -2887,21 +2910,22 @@ LayoutDeviceIntPoint nsWindow::GetClient
 }
 
 gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget,
                                          GdkEventProperty* aEvent) {
   if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) {
     UpdateClientOffsetFromFrameExtents();
     return FALSE;
   }
-
+  if (!mGdkWindow) {
+    return FALSE;
+  }
   if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) {
     return TRUE;
   }
-
   return FALSE;
 }
 
 static GdkCursor* GetCursorForImage(const nsIWidget::Cursor& aCursor,
                                     int32_t aWidgetScaleFactor) {
   if (!aCursor.IsCustom()) {
     return nullptr;
   }
@@ -3146,19 +3170,20 @@ void nsWindow::SetIcon(const nsAString& 
 
   // leave the default icon intact if no matching icons were found
   if (foundIcon) {
     gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
   }
 }
 
 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
-  nsIntPoint origin;
-  GetWindowOrigin(mGdkWindow, &origin.x, &origin.y);
-
+  nsIntPoint origin(0, 0);
+  if (mGdkWindow) {
+    GetWindowOrigin(mGdkWindow, &origin.x, &origin.y);
+  }
   return GdkPointToDevicePixels({origin.x, origin.y});
 }
 
 void nsWindow::CaptureMouse(bool aCapture) {
   LOG("nsWindow::CaptureMouse() [%p]\n", (void*)this);
 
   if (!mGdkWindow) return;
 
@@ -3703,25 +3728,34 @@ gboolean nsWindow::OnConfigureEvent(GtkW
     gtk_window_get_size(GTK_WINDOW(mShell), &allocation.width,
                         &allocation.height);
     OnSizeAllocate(&allocation);
   }
 
   return FALSE;
 }
 
-void nsWindow::OnContainerUnrealize() {
+void nsWindow::OnMap() {
+  LOG(("nsWindow::OnMap [%p]\n", (void*)this));
+  // Gtk mapped out widget to screen. Configure underlying GdkWindow properly
+  // as our rendering target.
+  // This call means we have X11 (or Wayland) window we can render to by GL
+  // so we need to notify compositor about it.
+  mIsMapped = true;
+  ConfigureGdkWindow();
+}
+
+void nsWindow::OnUnrealize() {
   // 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.
-
-  if (mGdkWindow) {
-    g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
-    mGdkWindow = nullptr;
-  }
+  // This call means we *don't* have X11 (or Wayland) window we can render to.
+  LOG(("nsWindow::OnUnrealize [%p] GdkWindow %p\n", (void*)this, mGdkWindow));
+  mIsMapped = false;
+  ReleaseGdkWindow();
 }
 
 void nsWindow::OnSizeAllocate(GtkAllocation* aAllocation) {
   LOG("nsWindow::OnSizeAllocate %d,%d -> %d x %d\n", aAllocation->x,
       aAllocation->y, aAllocation->width, aAllocation->height);
 
   // Client offset are updated by _NET_FRAME_EXTENTS on X11 when system titlebar
   // is enabled. In either cases (Wayland or system titlebar is off on X11)
@@ -3740,25 +3774,27 @@ void nsWindow::OnSizeAllocate(GtkAllocat
     LOG("  Already the same size");
     // We were already resized at nsWindow::OnConfigureEvent() so skip it.
     return;
   }
 
   // Invalidate the new part of the window now for the pending paint to
   // minimize background flashes (GDK does not do this for external resizes
   // of toplevels.)
-  if (mBounds.width < size.width) {
-    GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
-        mBounds.width, 0, size.width - mBounds.width, size.height));
-    gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
-  }
-  if (mBounds.height < size.height) {
-    GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
-        0, mBounds.height, size.width, size.height - mBounds.height));
-    gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
+  if (mGdkWindow) {
+    if (mBounds.width < size.width) {
+      GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
+          mBounds.width, 0, size.width - mBounds.width, size.height));
+      gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
+    }
+    if (mBounds.height < size.height) {
+      GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
+          0, mBounds.height, size.width, size.height - mBounds.height));
+      gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
+    }
   }
 
   mBounds.SizeTo(size);
 
   // Notify the GtkCompositorWidget of a ClientSizeChange
   if (mCompositorWidgetDelegate) {
     mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
   }
@@ -3819,17 +3855,19 @@ void nsWindow::OnLeaveNotifyEvent(GdkEve
   // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
   // events when the pointer leaves a child window.  If the destination
   // window is a Gecko window then we'll catch the corresponding event on
   // that window.
   //
   // XXXkt However, we will miss toplevel exits when the pointer directly
   // leaves a foreign (plugin) child window without passing over a visible
   // portion of a Gecko window.
-  if (aEvent->subwindow != nullptr) return;
+  if (!mGdkWindow || aEvent->subwindow != nullptr) {
+    return;
+  }
 
   WidgetMouseEvent event(true, eMouseExitFromWidget, this,
                          WidgetMouseEvent::eReal);
 
   event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
   event.AssignEventTime(GetWidgetEventTime(aEvent->time));
 
   event.mExitFrom = Some(is_top_level_mouse_exit(mGdkWindow, aEvent)
@@ -3892,16 +3930,20 @@ static LayoutDeviceIntPoint GetRefPoint(
   // XXX we're never quite sure which GdkWindow the event came from due to our
   // custom bubbling in scroll_event_cb(), so use ScreenToWidget to translate
   // the screen root coordinates into coordinates relative to this widget.
   return aWindow->GdkEventCoordsToDevicePixels(aEvent->x_root, aEvent->y_root) -
          aWindow->WidgetToScreenOffset();
 }
 
 void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
+  if (!mGdkWindow) {
+    return;
+  }
+
   if (mWindowShouldStartDragging) {
     mWindowShouldStartDragging = false;
     // find the top-level window
     GdkWindow* gdk_window = gdk_window_get_toplevel(mGdkWindow);
     MOZ_ASSERT(gdk_window, "gdk_window_get_toplevel should not return null");
 
     bool canDrag = true;
     if (GdkIsX11Display()) {
@@ -4169,16 +4211,19 @@ void nsWindow::OnButtonPressEvent(GdkEve
   if (!StaticPrefs::ui_context_menus_after_mouseup() &&
       eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
     DispatchContextMenuEventFromMouseEvent(domButton, aEvent);
   }
 }
 
 void nsWindow::OnButtonReleaseEvent(GdkEventButton* aEvent) {
   LOG("Button %u release\n", aEvent->button);
+  if (!mGdkWindow) {
+    return;
+  }
 
   if (mWindowShouldStartDragging) {
     mWindowShouldStartDragging = false;
   }
 
   uint16_t domButton;
   switch (aEvent->button) {
     case 1:
@@ -5009,16 +5054,134 @@ nsCString nsWindow::GetPopupTypeName() {
       return nsCString("Tooltip");
     case ePopupTypePanel:
       return nsCString("Panel/Utility");
     default:
       return nsCString("Unknown");
   }
 }
 
+// Disables all rendering of GtkWidget from Gtk side.
+// We do our best to persuade Gtk/Gdk to ignore all painting
+// to the widget.
+static void GtkWidgetDisableUpdates(GtkWidget* aWidget) {
+  // clear draw signal from widget class
+  GTK_WIDGET_GET_CLASS(aWidget)->draw = nullptr;
+
+  // Clear exposure mask - it disabled synthesized events.
+  GdkWindow* window = gtk_widget_get_window(aWidget);
+  gdk_window_set_events(window, (GdkEventMask)(gdk_window_get_events(window) &
+                                               (~GDK_EXPOSURE_MASK)));
+
+  // Remove before/after paint handles from frame clock.
+  // It disables widget content updates.
+  GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window);
+  g_signal_handlers_disconnect_by_data(frame_clock, window);
+}
+
+void nsWindow::ConfigureGdkWindow() {
+  LOG(("nsWindow::ConfigureGdkWindow() [%p]", this));
+
+  mGdkWindow =
+      gtk_widget_get_window(mDrawToContainer ? GTK_WIDGET(mContainer) : mShell);
+  g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
+
+#ifdef MOZ_X11
+  if (GdkIsX11Display()) {
+    mXWindow = gdk_x11_window_get_xid(mGdkWindow);
+
+    GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow);
+    mXVisual = gdk_x11_visual_get_xvisual(gdkVisual);
+    mXDepth = gdk_visual_get_depth(gdkVisual);
+    mIsShaped =
+        mIsTransparent && !mHasAlphaVisual && !mTransparencyBitmapForTitlebar;
+    mSurfaceProvider.Initialize(mXWindow, mXVisual, mXDepth, mIsShaped);
+
+    if (mIsTopLevel) {
+      // Set window manager hint to keep fullscreen windows composited.
+      //
+      // If the window were to get unredirected, there could be visible
+      // tearing because Gecko does not align its framebuffer updates with
+      // vblank.
+      SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
+    }
+    // Dummy call to a function in mozgtk to prevent the linker from removing
+    // the dependency with --as-needed.
+    XShmQueryExtension(DefaultXDisplay());
+  }
+#endif
+#ifdef MOZ_WAYLAND
+  if (GdkIsWaylandDisplay()) {
+    mSurfaceProvider.Initialize(this);
+  }
+#endif
+
+  if (mCompositorWidgetDelegate) {
+    mCompositorWidgetDelegate->EnableRendering(mXWindow, mIsShaped);
+  }
+
+  if (mIsDragPopup && GdkIsWaylandDisplay()) {
+    GtkWidget* parent = gtk_widget_get_parent(mShell);
+    if (parent) {
+      GtkWidgetDisableUpdates(parent);
+    }
+    GtkWidgetDisableUpdates(mShell);
+    GtkWidgetDisableUpdates(GTK_WIDGET(mContainer));
+  }
+
+  if (mWindowType == eWindowType_popup) {
+    if (mNoAutoHide) {
+      gint wmd = ConvertBorderStyles(mBorderStyle);
+      if (wmd != -1) {
+        gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd);
+      }
+    }
+    // If the popup ignores mouse events, set an empty input shape.
+    SetWindowMouseTransparent(mMouseTransparent);
+  }
+
+  RefreshWindowClass();
+
+  if (mCompositorState == COMPOSITOR_PAUSED_INITIALLY) {
+    mCompositorState = COMPOSITOR_PAUSED_MISSING_EGL_WINDOW;
+  }
+
+  ResumeCompositorHiddenWindow();
+  WaylandStartVsync();
+
+  LOG(("  finished, new GdkWindow %p XID 0x%lx\n", mGdkWindow,
+       GdkIsX11Display() ? gdk_x11_window_get_xid(mGdkWindow) : 0));
+}
+
+void nsWindow::ReleaseGdkWindow() {
+  LOG(("nsWindow::ReleaseGdkWindow() [%p]", this));
+
+  WaylandStopVsync();
+  PauseCompositorHiddenWindow();
+
+  if (mGdkWindow) {
+    g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
+    mGdkWindow = nullptr;
+  }
+
+  mSurfaceProvider.CleanupResources();
+
+  if (mCompositorWidgetDelegate) {
+    mCompositorWidgetDelegate->DisableRendering();
+  }
+#ifdef MOZ_X11
+  if (GdkIsX11Display()) {
+    mXWindow = X11None;
+    mXVisual = nullptr;
+    mXDepth = 0;
+    mIsShaped = false;
+  }
+#endif
+}
+
 nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
                           const LayoutDeviceIntRect& aRect,
                           nsWidgetInitData* aInitData) {
   LOG("nsWindow::Create\n");
 
   // only set the base parent if we're going to be a dialog or a
   // toplevel
   nsIWidget* baseParent =
@@ -5088,16 +5251,18 @@ nsresult nsWindow::Create(nsIWidget* aPa
     // mIsWaylandPanelWindow is a special toplevel window on Wayland which
     // emulates X11 popup window without parent.
     mIsWaylandPanelWindow = true;
     mWindowType = eWindowType_toplevel;
   }
 
   mAlwaysOnTop = aInitData && aInitData->mAlwaysOnTop;
   mIsPIPWindow = aInitData && aInitData->mPIPWindow;
+  mNoAutoHide = aInitData && aInitData->mNoAutoHide;
+  mMouseTransparent = aInitData && aInitData->mMouseTransparent;
 
   // ok, create our windows
   switch (mWindowType) {
     case eWindowType_dialog:
     case eWindowType_popup:
     case eWindowType_toplevel:
     case eWindowType_invisible: {
       // Popups that are not noautohide are only temporary. The are used
@@ -5108,17 +5273,17 @@ nsresult nsWindow::Create(nsIWidget* aPa
       // For long-lived windows, their stacking order is managed by the
       // window manager, as indicated by GTK_WINDOW_TOPLEVEL.
       // For Wayland we have to always use GTK_WINDOW_POPUP to control
       // popup window position.
       GtkWindowType type = GTK_WINDOW_TOPLEVEL;
       if (mWindowType == eWindowType_popup) {
         MOZ_ASSERT(aInitData);
         type = GTK_WINDOW_POPUP;
-        if (GdkIsX11Display() && aInitData->mNoAutoHide) {
+        if (GdkIsX11Display() && mNoAutoHide) {
           type = GTK_WINDOW_TOPLEVEL;
         }
       }
       mShell = gtk_window_new(type);
 
       // Ensure gfxPlatform is initialized, since that is what initializes
       // gfxVars, used below.
       Unused << gfxPlatform::GetPlatform();
@@ -5198,17 +5363,17 @@ nsresult nsWindow::Create(nsIWidget* aPa
 
       } else if (mWindowType == eWindowType_popup) {
         MOZ_ASSERT(aInitData);
         mGtkWindowRoleName = "Popup";
         mPopupHint = aInitData->mPopupHint;
 
         LOG("nsWindow::Create() Popup [%p]\n", this);
 
-        if (aInitData->mNoAutoHide) {
+        if (mNoAutoHide) {
           // ... but the window manager does not decorate this window,
           // nor provide a separate taskbar icon.
           if (mBorderStyle == eBorderStyle_default) {
             gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
           } else {
             bool decorate = mBorderStyle & eBorderStyle_title;
             gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
             if (decorate) {
@@ -5231,16 +5396,17 @@ nsresult nsWindow::Create(nsIWidget* aPa
           }
 #endif
         }
 
         if (aInitData->mIsDragPopup) {
           gtk_window_set_type_hint(GTK_WINDOW(mShell),
                                    GDK_WINDOW_TYPE_HINT_DND);
           mIsDragPopup = true;
+          LOG_POPUP(("nsWindow::Create() Drag popup [%p]\n", this));
         } else if (GdkIsX11Display()) {
           // Set the window hints on X11 only. Wayland popups are configured
           // at WaylandPopupNeedsTrackInHierarchy().
           GdkWindowTypeHint gtkTypeHint;
           switch (mPopupHint) {
             case ePopupTypeMenu:
               gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
               break;
@@ -5294,29 +5460,24 @@ nsresult nsWindow::Create(nsIWidget* aPa
 
       if (mAlwaysOnTop) {
         gtk_window_set_keep_above(GTK_WINDOW(mShell), TRUE);
       }
 
       // Create a container to hold child windows and child GtkWidgets.
       GtkWidget* container = moz_container_new();
       mContainer = MOZ_CONTAINER(container);
-#ifdef MOZ_WAYLAND
-      if (GdkIsWaylandDisplay() && mIsAccelerated) {
+
+      // Don't render to invisible window.
+      if (mWindowType == eWindowType_invisible) {
         mCompositorState = COMPOSITOR_PAUSED_INITIALLY;
-        RefPtr<nsWindow> self(this);
-        moz_container_wayland_add_initial_draw_callback(
-            mContainer, [this, self]() -> void {
-              LOG("moz_container_wayland initial create "
-                  "ResumeCompositorHiddenWindow()");
-              self->mCompositorState = COMPOSITOR_PAUSED_MISSING_EGL_WINDOW;
-              self->ResumeCompositorHiddenWindow();
-            });
       }
-#endif
+      if (mIsAccelerated && (GdkIsWaylandDisplay() || gfxVars::UseEGL())) {
+        mCompositorState = COMPOSITOR_PAUSED_INITIALLY;
+      }
 
       // "csd" style is set when widget is realized so we need to call
       // it explicitly now.
       gtk_widget_realize(mShell);
 
       /* There are several cases here:
        *
        * 1) We're running on Gtk+ without client side decorations.
@@ -5333,16 +5494,17 @@ nsresult nsWindow::Create(nsIWidget* aPa
                          (mGtkWindowDecoration == GTK_DECORATION_CLIENT) ||
                          gtk_style_context_has_class(style, "csd");
       eventWidget = mDrawToContainer ? container : mShell;
 
       // Prevent GtkWindow from painting a background to avoid flickering.
       gtk_widget_set_app_paintable(eventWidget, gTransparentWindows);
 
       gtk_widget_add_events(eventWidget, kEvents);
+
       if (mDrawToContainer) {
         gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
         gtk_widget_set_app_paintable(mShell, gTransparentWindows);
       }
       if (mTransparencyBitmapForTitlebar) {
         moz_container_force_default_visual(mContainer);
       }
 
@@ -5362,51 +5524,42 @@ nsresult nsWindow::Create(nsIWidget* aPa
 
       // make sure this is the focus widget in the container
       gtk_widget_show(container);
 
       if (!mAlwaysOnTop) {
         gtk_widget_grab_focus(container);
       }
 
-      // the drawing window
-      mGdkWindow = gtk_widget_get_window(eventWidget);
-
       if (mIsWaylandPanelWindow) {
         gtk_window_set_decorated(GTK_WINDOW(mShell), false);
       }
 
+#ifdef MOZ_WAYLAND
+      if (mIsDragPopup && GdkIsWaylandDisplay()) {
+        moz_container_wayland_set_commit_to_parent(mContainer);
+      }
+#endif
+
       if (mWindowType == eWindowType_popup) {
         MOZ_ASSERT(aInitData);
         // gdk does not automatically set the cursor for "temporary"
         // windows, which are what gtk uses for popups.
 
         // force SetCursor to actually set the cursor, even though our internal
         // state indicates that we already have the standard cursor.
         mUpdateCursor = true;
         SetCursor(Cursor{eCursor_standard});
-
-        if (aInitData->mNoAutoHide) {
-          gint wmd = ConvertBorderStyles(mBorderStyle);
-          if (wmd != -1) {
-            gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd);
-          }
-        }
-
-        // If the popup ignores mouse events, set an empty input shape.
-        SetWindowMouseTransparent(aInitData->mMouseTransparent);
       }
     } break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType");
       return NS_ERROR_FAILURE;
   }
 
-  // label the drawing window with this object so we can find our way home
-  g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
   if (mDrawToContainer) {
     // Also label mShell toplevel window,
     // property_notify_event_cb callback also needs to find its way home
     g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), "nsWindow",
                       this);
   }
 
   if (mContainer) g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
@@ -5422,16 +5575,19 @@ nsresult nsWindow::Create(nsIWidget* aPa
     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);
     g_signal_connect(mShell, "composited-changed",
                      G_CALLBACK(widget_composited_changed_cb), nullptr);
     g_signal_connect(mShell, "property-notify-event",
                      G_CALLBACK(property_notify_event_cb), nullptr);
+    g_signal_connect(mShell, "map", G_CALLBACK(widget_map_cb), nullptr);
+    g_signal_connect(mShell, "unrealize", G_CALLBACK(widget_unrealize_cb),
+                     nullptr);
 
     if (mWindowType == eWindowType_toplevel) {
       g_signal_connect_after(mShell, "size_allocate",
                              G_CALLBACK(toplevel_window_size_allocate_cb),
                              nullptr);
     }
 
     GdkScreen* screen = gtk_widget_get_screen(mShell);
@@ -5444,18 +5600,16 @@ nsresult nsWindow::Create(nsIWidget* aPa
 
     GtkSettings* default_settings = gtk_settings_get_default();
     g_signal_connect_after(default_settings, "notify::gtk-xft-dpi",
                            G_CALLBACK(settings_xft_dpi_changed_cb), this);
   }
 
   if (mContainer) {
     // Widget signals
-    g_signal_connect(mContainer, "unrealize",
-                     G_CALLBACK(container_unrealize_cb), nullptr);
     g_signal_connect_after(mContainer, "size_allocate",
                            G_CALLBACK(size_allocate_cb), nullptr);
     g_signal_connect(mContainer, "hierarchy-changed",
                      G_CALLBACK(hierarchy_changed_cb), nullptr);
     g_signal_connect(mContainer, "notify::scale-factor",
                      G_CALLBACK(scale_changed_cb), nullptr);
     // Initialize mHasMappedToplevel.
     hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
@@ -5534,51 +5688,20 @@ nsresult nsWindow::Create(nsIWidget* aPa
                      nullptr);
   }
 
   LOG("nsWindow [%p] type %d %s\n", (void*)this, mWindowType,
       mIsPIPWindow ? "PIP window" : "");
   LOG("\tmShell %p mContainer %p mGdkWindow %p XID 0x%lx\n", mShell, mContainer,
       mGdkWindow, GdkIsX11Display() ? gdk_x11_window_get_xid(mGdkWindow) : 0);
 
-#ifdef MOZ_X11
-  if (GdkIsX11Display() && mGdkWindow) {
-    mXWindow = gdk_x11_window_get_xid(mGdkWindow);
-
-    GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow);
-    mXVisual = gdk_x11_visual_get_xvisual(gdkVisual);
-    mXDepth = gdk_visual_get_depth(gdkVisual);
-    bool shaped = popupNeedsAlphaVisual && !mHasAlphaVisual;
-
-    mSurfaceProvider.Initialize(mXWindow, mXVisual, mXDepth, shaped);
-
-    // Set window manager hint to keep fullscreen windows composited.
-    //
-    // If the window were to get unredirected, there could be visible
-    // tearing because Gecko does not align its framebuffer updates with
-    // vblank.
-    SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
-
-    // Dummy call to a function in mozgtk to prevent the linker from removing
-    // the dependency with --as-needed.
-    XShmQueryExtension(DefaultXDisplay());
-  }
-#endif
-#ifdef MOZ_WAYLAND
-  if (GdkIsWaylandDisplay()) {
-    mSurfaceProvider.Initialize(this);
-    WaylandStartVsync();
-  }
-#endif
-
   // Set default application name when it's empty.
   if (mGtkWindowAppName.IsEmpty()) {
     mGtkWindowAppName = gAppData->name;
   }
-  RefreshWindowClass();
 
   return NS_OK;
 }
 
 void nsWindow::RefreshWindowClass(void) {
   GdkWindow* gdkWindow = gtk_widget_get_window(mShell);
   if (!gdkWindow) {
     return;
@@ -5703,73 +5826,65 @@ void nsWindow::NativeMoveResize(bool aMo
   if (mNeedsShow && mIsShown && aResized) {
     NativeShow(true);
   }
 }
 
 void nsWindow::ResumeCompositorHiddenWindow() {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  if (mIsDestroyed || mCompositorState == COMPOSITOR_ENABLED ||
-      mCompositorState == COMPOSITOR_PAUSED_INITIALLY) {
+  LOG("nsWindow::ResumeCompositorHiddenWindow\n";
+  if (mIsDestroyed || mCompositorState == COMPOSITOR_ENABLED) {
+    LOG("  early quit, mCompositorState = %d\n", mCompositorState);
     return;
   }
 
   if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
-    LOG("nsWindow::ResumeCompositorHiddenWindow\n");
+    LOG("  resume\n");
     MOZ_ASSERT(mCompositorWidgetDelegate);
     if (mCompositorWidgetDelegate) {
       mCompositorState = COMPOSITOR_ENABLED;
       remoteRenderer->SendResumeAsync();
     }
     remoteRenderer->SendForcePresent(wr::RenderReasons::WIDGET);
+  } else {
+    LOG("  quit, failed to get remote renderer.\n");
   }
 }
 
 // Because wl_egl_window is destroyed on moz_container_unmap(),
 // the current compositor cannot use it anymore. To avoid crash,
 // pause the compositor and destroy EGLSurface & resume the compositor
 // and re-create EGLSurface on next expose event.
 void nsWindow::PauseCompositorHiddenWindow() {
+  LOG(("nsWindow::PauseCompositorHiddenWindow [%p]\n", (void*)this));
+
   // TODO: The compositor backend currently relies on the pause event to work
   // around a Gnome specific bug. Remove again once the fix is widely available.
   // See bug 1721298
   if ((!mIsAccelerated && !gfx::gfxVars::UseWebRenderCompositor()) ||
-      mIsDestroyed || mCompositorState == COMPOSITOR_PAUSED_INITIALLY) {
-    return;
-  }
-
-  LOG("nsWindow::PauseCompositorHiddenWindow\n");
+      mIsDestroyed) {
+    LOG(("  quit early, compositor state %d", mCompositorState));
+    return;
+  }
 
   mCompositorState = COMPOSITOR_PAUSED_MISSING_EGL_WINDOW;
 
   // Without remote widget / renderer we can't pause compositor.
   // So delete LayerManager to avoid EGLSurface access.
   CompositorBridgeChild* remoteRenderer = GetRemoteRenderer();
   if (!remoteRenderer || !mCompositorWidgetDelegate) {
     LOG("  deleted layer manager");
     DestroyLayerManager();
     return;
   }
 
   // XXX slow sync IPC
   LOG("  paused compositor");
   remoteRenderer->SendPause();
-#ifdef MOZ_WAYLAND
-  if (GdkIsWaylandDisplay()) {
-    // Re-request initial draw callback
-    RefPtr<nsWindow> self(this);
-    moz_container_wayland_add_initial_draw_callback(
-        mContainer, [this, self]() -> void {
-          LOG("moz_container_wayland resume callback "
-              "ResumeCompositorHiddenWindow()");
-          self->ResumeCompositorHiddenWindow();
-        });
-  }
-#endif
 }
 
 static int WindowResumeCompositor(void* data) {
   nsWindow* window = static_cast<nsWindow*>(data);
   window->ResumeCompositor();
   return true;
 }
 
@@ -5848,16 +5963,18 @@ void nsWindow::WaylandStartVsync() {
 #ifdef MOZ_WAYLAND
   // only use for toplevel windows for now - see bug 1619246
   if (!GdkIsWaylandDisplay() ||
       !StaticPrefs::widget_wayland_vsync_enabled_AtStartup() ||
       mWindowType != eWindowType_toplevel) {
     return;
   }
 
+  LOG(("nsWindow::WaylandStartVsync() [%p]\n", (void*)this));
+
   if (!mWaylandVsyncSource) {
     mWaylandVsyncSource = new WaylandVsyncSource();
   }
 
   WaylandVsyncSource::WaylandDisplay& display =
       static_cast<WaylandVsyncSource::WaylandDisplay&>(
           mWaylandVsyncSource->GetGlobalDisplay());
 
@@ -5872,16 +5989,17 @@ void nsWindow::WaylandStartVsync() {
   }
   display.EnableMonitor();
 #endif
 }
 
 void nsWindow::WaylandStopVsync() {
 #ifdef MOZ_WAYLAND
   if (mWaylandVsyncSource) {
+    LOG(("nsWindow::WaylandStopVsync() [%p]\n", (void*)this));
     // The widget is going to be hidden, so clear the surface of our
     // vsync source.
     WaylandVsyncSource::WaylandDisplay& display =
         static_cast<WaylandVsyncSource::WaylandDisplay&>(
             mWaylandVsyncSource->GetGlobalDisplay());
     display.DisableMonitor();
     display.MaybeUpdateSource(nullptr);
   }
@@ -5907,34 +6025,32 @@ void nsWindow::NativeShow(bool aAction) 
       }
     }
     // Set up usertime/startupID metadata for the created window.
     if (mWindowType != eWindowType_invisible) {
       SetUserTimeAndStartupIDForActivatedWindow(mShell);
     }
     if (GdkIsWaylandDisplay()) {
       ShowWaylandWindow();
-      WaylandStartVsync();
     } else {
       LOG("  calling gtk_widget_show(mShell)\n");
       gtk_widget_show(mShell);
     }
 
     if (mHiddenPopupPositioned && IsPopup()) {
       gtk_window_move(GTK_WINDOW(mShell), mPopupPosition.x, mPopupPosition.y);
       mHiddenPopupPositioned = false;
     }
   } else {
     // There's a chance that when the popup will be shown again it might be
     // resized because parent could be moved meanwhile.
     mPreferredPopupRect = nsRect(0, 0, 0, 0);
     mPreferredPopupRectFlushed = false;
     LOG("nsWindow::NativeShow hide\n");
     if (GdkIsWaylandDisplay()) {
-      WaylandStopVsync();
       if (IsWaylandPopup()) {
         // We can't close tracked popups directly as they may have visible
         // child popups. Just mark is as closed and let
         // UpdateWaylandPopupHierarchy() do the job.
         if (IsInPopupHierarchy()) {
           WaylandPopupMarkAsClosed();
           UpdateWaylandPopupHierarchy();
         } else {
@@ -6323,17 +6439,19 @@ void nsWindow::ClearTransparencyBitmap()
   delete[] mTransparencyBitmap;
   mTransparencyBitmap = nullptr;
   mTransparencyBitmapWidth = 0;
   mTransparencyBitmapHeight = 0;
 
   if (!mShell) return;
 
 #ifdef MOZ_X11
-  if (!mGdkWindow) return;
+  if (MOZ_UNLIKELY(!mGdkWindow)) {
+    return;
+  }
 
   Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
   Window xWindow = gdk_x11_window_get_xid(mGdkWindow);
 
   XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet);
 #endif
 }
 
@@ -7251,17 +7369,17 @@ static GdkCursor* get_gtk_cursor(nsCurso
 
   return gdkcursor;
 }
 
 // gtk callbacks
 
 void draw_window_of_widget(GtkWidget* widget, GdkWindow* aWindow, cairo_t* cr) {
   if (gtk_cairo_should_draw_window(cr, aWindow)) {
-    RefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
+    RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
     if (!window) {
       NS_WARNING("Cannot get nsWindow from GtkWidget");
     } else {
       cairo_save(cr);
       gtk_cairo_transform_to_window(cr, widget, aWindow);
       // TODO - window->OnExposeEvent() can destroy this or other windows,
       // do we need to handle it somehow?
       window->OnExposeEvent(cr);
@@ -7293,23 +7411,33 @@ static gboolean configure_event_cb(GtkWi
   RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
   if (!window) {
     return FALSE;
   }
 
   return window->OnConfigureEvent(widget, event);
 }
 
-static void container_unrealize_cb(GtkWidget* widget) {
+// Some Gtk widget code may call gtk_widget_unrealize() which destroys
+// mGdkWindow. We need to listen on this signal and re-create
+// mGdkWindow when we're already mapped.
+static void widget_map_cb(GtkWidget* widget) {
   RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
   if (!window) {
     return;
   }
-
-  window->OnContainerUnrealize();
+  window->OnMap();
+}
+
+static void widget_unrealize_cb(GtkWidget* widget) {
+  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+  if (!window) {
+    return;
+  }
+  window->OnUnrealize();
 }
 
 static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation) {
   RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
   if (!window) {
     return;
   }
 
@@ -8119,45 +8247,58 @@ nsIWidget::WindowRenderer* nsWindow::Get
     // LayerManager/Compositor during shutdown. Just return what we currently
     // have, which is most likely null.
     return mWindowRenderer;
   }
 
   return nsBaseWidget::GetWindowRenderer();
 }
 
+/* nsWindow::SetCompositorWidgetDelegate() sets remote GtkCompositorWidget
+ * to render into with compositor.
+ *
+ * If we're already visible we need to recreate compositor/vsync state.
+ */
 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {
   LOG("nsWindow::SetCompositorWidgetDelegate %p\n", delegate);
 
+  // There's a change of remote widget - stop compositor and VSync as
+  // we're going re-init it.
+  if (mCompositorWidgetDelegate && mIsMapped) {
+    PauseCompositorHiddenWindow();
+    WaylandStopVsync();
+  }
+
   if (delegate) {
     mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
     MOZ_ASSERT(mCompositorWidgetDelegate,
                "nsWindow::SetCompositorWidgetDelegate called with a "
                "non-PlatformCompositorWidgetDelegate");
-    if (GdkIsX11Display() && gfxVars::UseEGL() && mIsAccelerated) {
-      // This is called from nsBaseWidget::CreateCompositor() in which case
-      // we need to create a new EGL surface in RenderCompositorEGL on X11
-      mCompositorState = COMPOSITOR_PAUSED_MISSING_EGL_WINDOW;
-    }
-    ResumeCompositorHiddenWindow();
-    WaylandStartVsync();
+    // This is called from nsBaseWidget::CreateCompositor() in which case
+    // we need to create a new EGL surface in RenderCompositorEGL on X11
+    if (mIsMapped) {
+      ResumeCompositorHiddenWindow();
+      WaylandStartVsync();
+    }
   } else {
-    WaylandStopVsync();
     mCompositorWidgetDelegate = nullptr;
   }
 }
 
 /* nsWindow::UpdateClientOffsetFromCSDWindow() is designed to be called from
  * nsWindow::OnConfigureEvent() when mContainer window is already positioned.
  *
  * It works only for CSD decorated GtkWindow.
  */
 void nsWindow::UpdateClientOffsetFromCSDWindow() {
-  int x, y;
-  gdk_window_get_position(mGdkWindow, &x, &y);
+  int x = 0, y = 0;
+
+  if (mGdkWindow) {
+    gdk_window_get_position(mGdkWindow, &x, &y);
+  }
 
   x = GdkCoordToDevicePixels(x);
   y = GdkCoordToDevicePixels(y);
 
   if (mClientOffset.x != x || mClientOffset.y != y) {
     mClientOffset = nsIntPoint(x, y);
 
     LOG("nsWindow::UpdateClientOffsetFromCSDWindow %d, %d\n", mClientOffset.x,
@@ -8254,21 +8395,16 @@ void nsWindow::SetDrawsInTitlebar(bool a
       gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
     }
 
     if (visible) {
       mNeedsShow = true;
       NativeShow(true);
     }
 
-#ifdef MOZ_X11
-    SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
-#endif
-    RefreshWindowClass();
-
     gtk_widget_destroy(tmpWindow);
   }
 
   mDrawInTitlebar = aState;
 
   if (mTransparencyBitmapForTitlebar) {
     if (mDrawInTitlebar && mSizeState == nsSizeMode_Normal && !mIsTiled) {
       UpdateTitlebarTransparencyBitmap();
@@ -8290,44 +8426,42 @@ GtkWindow* nsWindow::GetCurrentTopmostWi
 
 gint nsWindow::GdkCeiledScaleFactor() {
   // We depend on notify::scale-factor callback which is reliable for toplevel
   // windows only, so don't use scale cache for popup windows.
   if (mWindowType == eWindowType_toplevel && !mWindowScaleFactorChanged) {
     return mWindowScaleFactor;
   }
 
-  GdkWindow* scaledGdkWindow = mGdkWindow;
+  GdkWindow* scaledGdkWindow = nullptr;
   if (GdkIsWaylandDisplay()) {
     // For popup windows/dialogs with parent window we need to get scale factor
     // of the topmost window. Otherwise the scale factor of the popup is
     // not updated during it's hidden.
     if (mWindowType == eWindowType_popup || mWindowType == eWindowType_dialog) {
       // Get toplevel window for scale factor:
       GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
       if (topmostParentWindow) {
         scaledGdkWindow =
             gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
       } else {
         NS_WARNING("Popup/Dialog has no parent.");
       }
-      // Fallback for windows which parent has been unrealized.
-      if (!scaledGdkWindow) {
-        scaledGdkWindow = mGdkWindow;
-      }
-    }
-  }
-
+    }
+  }
+  // Fallback for windows which parent has been unrealized.
+  if (!scaledGdkWindow) {
+    scaledGdkWindow = mGdkWindow;
+  }
   if (scaledGdkWindow) {
     mWindowScaleFactor = gdk_window_get_scale_factor(scaledGdkWindow);
     mWindowScaleFactorChanged = false;
   } else {
     mWindowScaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor();
   }
-
   return mWindowScaleFactor;
 }
 
 bool nsWindow::UseFractionalScale() {
 #ifdef MOZ_WAYLAND
   return (GdkIsWaylandDisplay() &&
           StaticPrefs::widget_wayland_fractional_buffer_scale_AtStartup() > 0 &&
           WaylandDisplayGet()->GetViewporter());
@@ -8740,22 +8874,19 @@ void nsWindow::GetCompositorWidgetInitDa
 
   if (GdkIsX11Display() && mXWindow != X11None) {
     // Make sure the window XID is propagated to X server, we can fail otherwise
     // in GPU process (Bug 1401634).
     Display* display = DefaultXDisplay();
     XFlush(display);
     displayName = nsCString(XDisplayString(display));
   }
-
-  bool isShaped =
-      mIsTransparent && !mHasAlphaVisual && !mTransparencyBitmapForTitlebar;
   *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
       (mXWindow != X11None) ? mXWindow : (uintptr_t) nullptr, displayName,
-      isShaped, GdkIsX11Display(), GetClientSize());
+      mIsShaped, GdkIsX11Display(), GetClientSize());
 }
 
 #ifdef MOZ_X11
 /* XApp progress support currently works by setting a property
  * on a window with this Atom name.  A supporting window manager
  * will notice this and pass it along to whatever handling has
  * been implemented on that end (e.g. passing it on to a taskbar
  * widget.)  There is no issue if WM support is lacking, this is
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -196,17 +196,18 @@ class nsWindow final : public nsBaseWidg
   mozilla::widget::IMContextWrapper* GetIMContext() const { return mIMContext; }
 
   bool DispatchCommandEvent(nsAtom* aCommand);
   bool DispatchContentCommandEvent(mozilla::EventMessage aMsg);
 
   // event callbacks
   gboolean OnExposeEvent(cairo_t* cr);
   gboolean OnConfigureEvent(GtkWidget* aWidget, GdkEventConfigure* aEvent);
-  void OnContainerUnrealize();
+  void OnMap();
+  void OnUnrealize();
   void OnSizeAllocate(GtkAllocation* aAllocation);
   void OnDeleteEvent();
   void OnEnterNotifyEvent(GdkEventCrossing* aEvent);
   void OnLeaveNotifyEvent(GdkEventCrossing* aEvent);
   void OnMotionNotifyEvent(GdkEventMotion* aEvent);
   void OnButtonPressEvent(GdkEventButton* aEvent);
   void OnButtonReleaseEvent(GdkEventButton* aEvent);
   void OnContainerFocusInEvent(GdkEventFocus* aEvent);
@@ -258,16 +259,17 @@ class nsWindow final : public nsBaseWidg
   GtkWidget* GetMozContainerWidget();
   GdkWindow* GetGdkWindow() { return mGdkWindow; }
   GtkWidget* GetGtkWidget() { return mShell; }
   nsIFrame* GetFrame() const;
   bool IsDestroyed() const { return mIsDestroyed; }
   bool IsPopup() const;
   bool IsWaylandPopup() const;
   bool IsPIPWindow() const { return mIsPIPWindow; };
+  bool IsDragPopup() { return mIsDragPopup; };
 
   nsAutoCString GetDebugTag() const;
 
   void DispatchDragEvent(mozilla::EventMessage aMsg,
                          const LayoutDeviceIntPoint& aRefPoint, guint aTime);
   static void UpdateDragStatus(GdkDragContext* aDragContext,
                                nsIDragService* aDragService);
 
@@ -425,30 +427,35 @@ class nsWindow final : public nsBaseWidg
 
   // event handling code
   void DispatchActivateEvent(void);
   void DispatchDeactivateEvent(void);
   void MaybeDispatchResized();
 
   virtual void RegisterTouchWindow() override;
   virtual bool CompositorInitiallyPaused() override {
-#ifdef MOZ_WAYLAND
     return mCompositorState == COMPOSITOR_PAUSED_INITIALLY;
-#else
-    return false;
-#endif
   }
   nsCOMPtr<nsIWidget> mParent;
   // Has this widget been destroyed yet?
   bool mIsDestroyed;
   // Does WindowResized need to be called on listeners?
   bool mNeedsDispatchResized;
-  // This flag tracks if we're hidden or shown.
+  // mIsShown tracks requested visible status from browser perspective, i.e.
+  // if the window should be visible or now.
   bool mIsShown;
+  // mNeedsShow is set when browser requested to show this window but we failed
+  // to do so for some reason (wrong window size for instance).
+  // In such case we set mIsShown = true and mNeedsShow = true to indicate
+  // that the window is not actually visible but we report to browser that
+  // it is visible (mIsShown == true).
   bool mNeedsShow;
+  // This track real window visibility from OS perspective.
+  // It's set by OnMap/OnUnrealize which is based on Gtk events.
+  bool mIsMapped;
   // is this widget enabled?
   bool mEnabled;
   // has the native window for this been created yet?
   bool mCreated;
   // whether we handle touch event
   bool mHandleTouchEvent;
   // true if this is a drag and drop feedback popup
   bool mIsDragPopup;
@@ -558,16 +565,18 @@ class nsWindow final : public nsBaseWidg
   LayoutDeviceIntRegion mDraggableRegion;
   // It's PictureInPicture window.
   bool mIsPIPWindow;
   // It's undecorated popup utility window, without resizers/titlebar,
   // movable by mouse. Used on Wayland as a workaround for popups without
   // parent (for instance WebRTC sharing indicator).
   bool mIsWaylandPanelWindow;
   bool mAlwaysOnTop;
+  bool mNoAutoHide;
+  bool mMouseTransparent;
 
   // The cursor cache
   static GdkCursor* gsGtkCursorCache[eCursorCount];
 
   // Transparency
   bool mIsTransparent;
   // This bitmap tracks which pixels are transparent. We don't support
   // full translucency at this time; each pixel is either fully opaque
@@ -595,16 +604,22 @@ class nsWindow final : public nsBaseWidg
   // to force update mBounds after a size state change from a configure
   // event.
   bool mBoundsAreValid;
 
   static bool DragInProgress(void);
 
   void DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent);
 
+  // When window widget gets mapped/unmapped we need to configure
+  // underlying GdkWindow properly. Otherwise we'll end up with
+  // rendering to released window.
+  void ConfigureGdkWindow();
+  void ReleaseGdkWindow();
+
   // nsBaseWidget
   virtual WindowRenderer* GetWindowRenderer() override;
 
   void SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) override;
 
   void CleanLayerManagerRecursive();
 
   virtual int32_t RoundsWidgetCoordinatesTo() override;
@@ -793,20 +808,16 @@ class nsWindow final : public nsBaseWidg
    */
   RefPtr<mozilla::widget::IMContextWrapper> mIMContext;
 
   mozilla::UniquePtr<mozilla::CurrentX11TimeGetter> mCurrentTimeGetter;
   static GtkWindowDecoration sGtkWindowDecoration;
 
   static bool sTransparentMainWindow;
 
-  /* Used for software rendering
-   */
-  mozilla::widget::WindowSurfaceProvider mSurfaceProvider;
-
 #ifdef ACCESSIBILITY
   RefPtr<mozilla::a11y::LocalAccessible> mRootAccessible;
 
   /**
    * Request to create the accessible for this window if it is top level.
    */
   void CreateRootAccessible();
 
@@ -853,18 +864,20 @@ class nsWindow final : public nsBaseWidg
                 GTK_WIDGET_COMPOSIDED_DISABLED = 1,
                 GTK_WIDGET_COMPOSIDED_ENABLED = 2} WindowComposeRequest;
   void SetCompositorHint(WindowComposeRequest aState);
   bool ConfigureX11GLVisual();
 
   Window mXWindow;
   Visual* mXVisual;
   int mXDepth;
+  bool mIsShaped;
 #endif
 #ifdef MOZ_WAYLAND
   RefPtr<mozilla::gfx::VsyncSource> mWaylandVsyncSource;
   LayoutDeviceIntPoint mNativePointerLockCenter;
   zwp_locked_pointer_v1* mLockedPointer;
   zwp_relative_pointer_v1* mRelativePointer;
 #endif
+  mozilla::widget::WindowSurfaceProvider mSurfaceProvider;
 };
 
 #endif /* __nsWindow_h__ */