Bug 1491808 - [Linux/CSD/Titlebar] Force toplevel window repaint when titlebar rendering is enabled and its state changes, r=bzbarsky
authorMartin Stransky <stransky@redhat.com>
Mon, 03 Dec 2018 12:48:52 +0000
changeset 508460 1a6f9251c41f32feca557aa9f9ace21070bf803e
parent 508459 d1fc8c77ad7fc8098a798cb0df05587d7f374b95
child 508484 cac01bf337321891bb372357235494c760f28cb5
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1491808
milestone65.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 1491808 - [Linux/CSD/Titlebar] Force toplevel window repaint when titlebar rendering is enabled and its state changes, r=bzbarsky Titlebar (StyleAppearance::MozWindowTitlebar) style depends on toplevel window focus and we need to redraw it when toplevel window focus changes. Unfortunately according to https://gitlab.gnome.org/GNOME/gtk/issues/1395 the toplevel window focus can't be used here as we can have active but unfocused toplevel window during drag & drop. Gtk+ controls window active appearance by window-state-event signal but gecko uses focus-in/out signals, so we need to repaint the titlebar when window-state-event comes *after* focus-in/out signals. We can't call mWidgetListener->WindowActivated() (and WindowDeactivated()) as it was already called from focus-in/out handlers before window-state-event. Depends on D13051 Differential Revision: https://phabricator.services.mozilla.com/D13052
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -28,16 +28,18 @@
 #include "nsIRollupListener.h"
 #include "nsINode.h"
 
 #include "nsWidgetsCID.h"
 #include "nsDragService.h"
 #include "nsIWidgetListener.h"
 #include "nsIScreenManager.h"
 #include "SystemTimeConverter.h"
+#include "nsIPresShell.h"
+#include "nsViewManager.h"
 
 #include "nsGtkKeyUtils.h"
 #include "nsGtkCursors.h"
 #include "ScreenHelperGTK.h"
 
 #include <gtk/gtk.h>
 #include <gtk/gtkx.h>
 
@@ -3084,26 +3086,25 @@ void nsWindow::OnWindowStateEvent(GtkWid
         aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
   } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
              aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
     aEvent->changed_mask = static_cast<GdkWindowState>(
         aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
   }
 
   // This is a workaround for https://gitlab.gnome.org/GNOME/gtk/issues/1395
-  // Gtk+ controls window active appearance by window-state-event signal
-  // but gecko uses focus-in/out signals. So we need to repaint the titlebar
-  // when window-state-event comes.
+  // Gtk+ controls window active appearance by window-state-event signal.
   if (mDrawInTitlebar && (aEvent->changed_mask & GDK_WINDOW_STATE_FOCUSED)) {
     // Emulate what Gtk+ does at gtk_window_state_event().
     // We can't check GTK_STATE_FLAG_BACKDROP directly as it's set by Gtk+
     // *after* this window-state-event handler.
     mTitlebarBackdropState =
         !(aEvent->new_window_state & GDK_WINDOW_STATE_FOCUSED);
 
+    ForceTitlebarRedraw();
     return;
   }
 
   // We don't care about anything but changes in the maximized/icon/fullscreen
   // states
   if ((aEvent->changed_mask &
        (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED |
         GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
@@ -6728,8 +6729,48 @@ bool nsWindow::GetTopLevelWindowActiveSt
     window = get_window_for_gtk_widget(toplevelWidget);
     if (!window) {
       return false;
     }
   }
 
   return !window->mTitlebarBackdropState;
 }
+
+static nsIFrame *FindTitlebarFrame(nsIFrame *aFrame) {
+  for (nsIFrame *childFrame : aFrame->PrincipalChildList()) {
+    const nsStyleDisplay *frameDisp = childFrame->StyleDisplay();
+    if (frameDisp->mAppearance == StyleAppearance::MozWindowTitlebar ||
+        frameDisp->mAppearance == StyleAppearance::MozWindowTitlebarMaximized) {
+      return childFrame;
+    }
+
+    if (nsIFrame *foundFrame = FindTitlebarFrame(childFrame)) {
+      return foundFrame;
+    }
+  }
+  return nullptr;
+}
+
+void nsWindow::ForceTitlebarRedraw(void) {
+  MOZ_ASSERT(mDrawInTitlebar, "We should not redraw invisible titlebar.");
+
+  nsIPresShell *shell =
+      mWidgetListener ? mWidgetListener->GetPresShell() : nullptr;
+  if (!shell) {
+    return;
+  }
+  nsView *view = nsView::GetViewFor(this);
+  if (!view) {
+    return;
+  }
+  nsIFrame *frame = view->GetFrame();
+  if (!frame) {
+    return;
+  }
+
+  frame = FindTitlebarFrame(frame);
+  if (frame) {
+    nsLayoutUtils::PostRestyleEvent(frame->GetContent()->AsElement(),
+                                    nsRestyleHint(0),
+                                    nsChangeHint_RepaintFrame);
+  }
+}
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -620,16 +620,18 @@ class nsWindow final : public nsBaseWidg
       LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT) override;
 
   void SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) override;
 
   void CleanLayerManagerRecursive();
 
   virtual int32_t RoundsWidgetCoordinatesTo() override;
 
+  void ForceTitlebarRedraw();
+
   /**
    * |mIMContext| takes all IME related stuff.
    *
    * This is owned by the top-level nsWindow or the topmost child
    * nsWindow embedded in a non-Gecko widget.
    *
    * The instance is created when the top level widget is created.  And when
    * the widget is destroyed, it's released.  All child windows refer its