Bug 1441665 - [Gtk] Update window offset explicitly when titlebar is disabled in CSD mode, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Tue, 24 Apr 2018 10:10:16 +0200
changeset 468810 de3a44bd989c495492b95556fec146fdf3a90e50
parent 468809 dfbc948fadaa5455d4a9b0edebd2c7b98f22fb69
child 468811 f6812edca4eac80b3150067f20dc2327040c7809
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1441665
milestone61.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 1441665 - [Gtk] Update window offset explicitly when titlebar is disabled in CSD mode, r=jhorak When system titlebar rendering is disabled and we're in CSD window mode, the window decorations are rendered by client (application/Gtk) and we don't get _NET_FRAME_EXTENTS property (decoration size) update for our toplevel window. So we need to calculate the decoration/shadow size as Gtk+ does, we emulate get_shadow_width() which is not exported by Gtk+. MozReview-Commit-ID: K7o2rUPt6Yc
widget/gtk/WidgetStyleCache.cpp
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -1280,16 +1280,24 @@ GetCssNodeStyleInternal(WidgetNodeType a
       return gtk_widget_get_style_context(widget);
     }
     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
     {
       NS_ASSERTION(false,
           "MOZ_GTK_HEADER_BAR_BUTTON_RESTORE is used as an icon only!");
       return nullptr;
     }
+    case MOZ_GTK_WINDOW_DECORATION:
+    {
+      GtkStyleContext* parentStyle =
+          CreateSubStyleWithClass(MOZ_GTK_WINDOW, "csd");
+      style = CreateCSSNode("decoration", parentStyle);
+      g_object_unref(parentStyle);
+      break;
+    }
     default:
       return GetWidgetRootStyle(aNodeType);
   }
 
   MOZ_ASSERT(style, "missing style context for node type");
   sStyleStorage[aNodeType] = style;
   return style;
 }
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -33,16 +33,26 @@ static ToolbarGTKMetrics sToolbarMetrics
 #define ARROW_DOWN    G_PI
 #define ARROW_RIGHT   G_PI_2
 #define ARROW_LEFT    (G_PI+G_PI_2)
 
 #if !GTK_CHECK_VERSION(3,14,0)
 #define GTK_STATE_FLAG_CHECKED (1 << 11)
 #endif
 
+static GtkBorder
+operator+=(GtkBorder& first, const GtkBorder& second)
+{
+    first.left += second.left;
+    first.right += second.right;
+    first.top += second.top;
+    first.bottom += second.bottom;
+    return first;
+}
+
 static gint
 moz_gtk_get_tab_thickness(GtkStyleContext *style);
 
 static gint
 moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect,
                         GtkWidgetState* state, GtkTextDirection direction);
 
 static GtkBorder
@@ -3051,16 +3061,78 @@ GetScrollbarMetrics(GtkOrientation aOrie
     g_object_unref(style);
 
     metrics->size.scrollbar =
         trackSizeForThumb + contentsBorder + metrics->border.scrollbar;
 
     return metrics;
 }
 
+/*
+ * get_shadow_width() from gtkwindow.c is not public so we need
+ * to implement it.
+ */
+bool
+GetCSDDecorationSize(GtkBorder* aDecorationSize)
+{
+    GtkStyleContext* context = GetStyleContext(MOZ_GTK_WINDOW_DECORATION);
+
+    /* Always sum border + padding */
+    GtkBorder padding;
+    GtkStateFlags state = gtk_style_context_get_state(context);
+    gtk_style_context_get_border(context, state, aDecorationSize);
+    gtk_style_context_get_padding(context, state, &padding);
+    *aDecorationSize += padding;
+
+    // Available on GTK 3.20+.
+    static auto sGtkRenderBackgroundGetClip =
+        (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*))
+        dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip");
+
+    GtkBorder margin;
+    gtk_style_context_get_margin(context, state, &margin);
+
+    GtkBorder extents = {0, 0, 0, 0};
+    if (sGtkRenderBackgroundGetClip) {
+        /* Get shadow extents but combine with style margin; use the bigger value.
+         */
+        GdkRectangle clip;
+        sGtkRenderBackgroundGetClip(context, 0, 0, 0, 0, &clip);
+
+        extents.top = -clip.y;
+        extents.right = clip.width + clip.x;
+        extents.bottom = clip.height + clip.y;
+        extents.left = -clip.x;
+
+        extents.top = MAX(extents.top, margin.top);
+        extents.right = MAX(extents.right, margin.right);
+        extents.bottom = MAX(extents.bottom, margin.bottom);
+        extents.left = MAX(extents.left, margin.left);
+    } else {
+        /* If we can't get shadow extents use decoration-resize-handle instead
+         * as a workaround. This is inspired by update_border_windows()
+         * from gtkwindow.c although this is not 100% accurate as we emulate
+         * the extents here.
+         */
+        gint handle;
+        gtk_widget_style_get(GetWidget(MOZ_GTK_WINDOW),
+                             "decoration-resize-handle", &handle,
+                             NULL);
+
+        extents.top = handle + margin.top;
+        extents.right = handle + margin.right;
+        extents.bottom = handle + margin.bottom;
+        extents.left = handle + margin.left;
+    }
+
+    *aDecorationSize += extents;
+
+    return (sGtkRenderBackgroundGetClip != nullptr);
+}
+
 /* cairo_t *cr argument has to be a system-cairo. */
 gint
 moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
                      GdkRectangle* rect,
                      GtkWidgetState* state, gint flags,
                      GtkTextDirection direction)
 {
     /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -329,16 +329,19 @@ typedef enum {
   MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE,
 
   /* MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE is a state of
    * MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE button and it's used as
    * an icon placeholder only.
    */
   MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE,
 
+  /* Client-side window decoration node. Available on GTK 3.20+. */
+  MOZ_GTK_WINDOW_DECORATION,
+
   MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
 
 /*** General library functions ***/
 /**
  * Initializes the drawing library.  You must call this function
  * prior to using any other functionality.
  * returns: MOZ_GTK_SUCCESS if there were no errors
@@ -601,9 +604,21 @@ GetToolbarButtonMetrics(WidgetNodeType a
  * aMaxButtonNums: [IN] Allocated aButtonLayout entries. Must be at least
                         TOOLBAR_BUTTONS wide.
  *
  * returns:    Number of returned entries at aButtonLayout.
  */
 int
 GetGtkHeaderBarButtonLayout(WidgetNodeType* aButtonLayout, int aMaxButtonNums);
 
+/**
+ * Get size of CSD window extents.
+ *
+ * aDecorationSize [OUT] Returns calculated (or estimated) decoration
+ *                       size of CSD window.
+ *
+ * returns:    True if we have extract decoration size (for GTK 3.20+)
+ *             False if we have only an estimation (for GTK+ before  3.20+)
+ */
+bool
+GetCSDDecorationSize(GtkBorder* aDecorationSize);
+
 #endif
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -124,16 +124,17 @@ using namespace mozilla::widget;
 #include "WindowSurfaceX11SHM.h"
 #include "WindowSurfaceXRender.h"
 #endif // MOZ_X11
 #ifdef MOZ_WAYLAND
 #include "nsIClipboard.h"
 #endif
 
 #include "nsShmImage.h"
+#include "gtkdrawing.h"
 
 #include "NativeKeyBindings.h"
 
 #include <dlfcn.h>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::widget;
@@ -6589,16 +6590,38 @@ nsWindow::ClearCachedResources()
     for (GList* list = children; list; list = list->next) {
         nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
         if (window) {
             window->ClearCachedResources();
         }
     }
 }
 
+/* nsWindow::UpdateClientOffsetForCSDWindow() is designed to be called from
+ * paint code to update mClientOffset any time. It also propagates
+ * the mClientOffset to child tabs.
+ *
+ * It works only for CSD decorated GtkWindow.
+ */
+void
+nsWindow::UpdateClientOffsetForCSDWindow()
+{
+    // _NET_FRAME_EXTENTS is not set on client decorated windows,
+    // so we need to read offset between mContainer and toplevel mShell
+    // window.
+    GtkBorder decorationSize;
+    GetCSDDecorationSize(&decorationSize);
+    mClientOffset = nsIntPoint(decorationSize.left, decorationSize.top);
+
+    // Send a WindowMoved notification. This ensures that TabParent
+    // picks up the new client offset and sends it to the child process
+    // if appropriate.
+    NotifyWindowMoved(mBounds.x, mBounds.y);
+}
+
 nsresult
 nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins)
 {
     SetDrawsInTitlebar(aMargins.top == 0);
     return NS_OK;
 }
 
 void
@@ -6663,16 +6686,18 @@ nsWindow::SetDrawsInTitlebar(bool aState
                                         &allocation.height);
         gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation);
 
         gtk_widget_realize(GTK_WIDGET(mShell));
         gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell));
         mNeedsShow = true;
         NativeResize();
 
+        UpdateClientOffsetForCSDWindow();
+
         gtk_widget_destroy(tmpWindow);
     }
 
     mDrawInTitlebar = aState;
 }
 
 gint
 nsWindow::GdkScaleFactor()
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -474,16 +474,18 @@ private:
 
     bool               GetDragInfo(mozilla::WidgetMouseEvent* aMouseEvent,
                                    GdkWindow** aWindow, gint* aButton,
                                    gint* aRootX, gint* aRootY);
     void               ClearCachedResources();
     nsIWidgetListener* GetListener();
     bool               IsComposited() const;
 
+    void               UpdateClientOffsetForCSDWindow();
+
     GtkWidget          *mShell;
     MozContainer       *mContainer;
     GdkWindow          *mGdkWindow;
     PlatformCompositorWidgetDelegate* mCompositorWidgetDelegate;
 
 
     uint32_t            mHasMappedToplevel : 1,
                         mIsFullyObscured : 1,