mozilla-bmo1399611-csd.patch
author Wolfgang Rosenauer <wr@rosenauer.org>
Wed, 03 Jan 2018 14:41:12 +0100
branchfirefox57
changeset 1014 963651ec972e
parent 1012 0c59a30173da
permissions -rw-r--r--
real fix for Bug 1069962 - Tumbleweed: OpenSuse's Firefox version crashes when "cancel" pressed in "File Upload" dialogue, e.g. TYPO3, BUGZILLA

diff -up firefox-57.0b8/browser/app/profile/firefox.js.1399611 firefox-57.0b8/browser/app/profile/firefox.js
--- firefox-57.0b8/browser/app/profile/firefox.js.1399611	2017-10-16 12:11:45.364240654 +0200
+++ firefox-57.0b8/browser/app/profile/firefox.js	2017-10-16 12:28:03.860720910 +0200
@@ -457,11 +457,7 @@ pref("browser.tabs.loadBookmarksInBackgr
 pref("browser.tabs.loadBookmarksInTabs", false);
 pref("browser.tabs.tabClipWidth", 140);
 pref("browser.tabs.tabMinWidth", 76);
-#ifdef UNIX_BUT_NOT_MAC
-pref("browser.tabs.drawInTitlebar", false);
-#else
 pref("browser.tabs.drawInTitlebar", true);
-#endif
 
 // Offer additional drag space to the user. The drag space
 // will only be shown if browser.tabs.drawInTitlebar is true.
diff -up firefox-57.0b8/browser/base/content/browser-tabsintitlebar.js.1399611 firefox-57.0b8/browser/base/content/browser-tabsintitlebar.js
--- firefox-57.0b8/browser/base/content/browser-tabsintitlebar.js.1399611	2017-10-09 22:17:13.000000000 +0200
+++ firefox-57.0b8/browser/base/content/browser-tabsintitlebar.js	2017-10-16 12:11:45.364240654 +0200
@@ -14,6 +14,11 @@ var TabsInTitlebar = {
     this._readPref();
     Services.prefs.addObserver(this._prefName, this);
 
+    // Always disable on unsupported GTK versions.
+    if (AppConstants.MOZ_WIDGET_TOOLKIT == "gtk3") {
+      this.allowedBy("gtk", window.matchMedia("(-moz-gtk-csd-available)"));
+    }
+
     // We need to update the appearance of the titlebar when the menu changes
     // from the active to the inactive state. We can't, however, rely on
     // DOMMenuBarInactive, because the menu fires this event and then removes
diff -up firefox-57.0b8/browser/base/moz.build.1399611 firefox-57.0b8/browser/base/moz.build
--- firefox-57.0b8/browser/base/moz.build.1399611	2017-09-29 18:16:45.000000000 +0200
+++ firefox-57.0b8/browser/base/moz.build	2017-10-16 12:11:45.364240654 +0200
@@ -57,7 +57,7 @@ DEFINES['APP_LICENSE_BLOCK'] = '%s/conte
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
     DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa', 'gtk3'):
     DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
diff -up firefox-57.0b8/browser/themes/linux/browser.css.1399611 firefox-57.0b8/browser/themes/linux/browser.css
--- firefox-57.0b8/browser/themes/linux/browser.css.1399611	2017-10-05 06:17:37.000000000 +0200
+++ firefox-57.0b8/browser/themes/linux/browser.css	2017-10-16 12:11:45.365240651 +0200
@@ -556,7 +556,9 @@ html|span.ac-emphasize-text-url {
 
 #nav-bar,
 #toolbar-menubar:not([autohide="true"]):not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
-#TabsToolbar:not(:-moz-lwtheme):-moz-system-metric(menubar-drag) {
+#TabsToolbar:not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
+#main-window[tabsintitlebar] #toolbar-menubar:not([autohide="true"]),
+#main-window[tabsintitlebar] #TabsToolbar {
   -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-drag");
 }
 
@@ -713,3 +715,85 @@ html|span.ac-emphasize-text-url {
 .restore-tabs-button:hover:active:not([disabled="true"]) {
   padding: 3px;
 }
+
+@media not all and (-moz-gtk-csd-available) {
+  #main-window > #titlebar {
+    /* We need to hide the titlebar explicitly on versions of GTK without CSD. */
+    display: none;
+  }
+}
+
+/* Titlebar/CSD */
+@media (-moz-gtk-csd-available) {
+  #main-window[tabsintitlebar][sizemode="normal"] > #titlebar {
+    min-height: calc(var(--tab-min-height) + 12px);
+  }
+
+  #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
+    visibility: hidden;
+  }
+  #main-window[tabsintitlebar] #titlebar-content:-moz-lwtheme {
+    visibility: visible;
+  }
+
+  #main-window[tabsintitlebar][sizemode="normal"] > #titlebar {
+    -moz-appearance: -moz-window-titlebar;
+  }
+  #main-window[tabsintitlebar][sizemode="maximized"] > #titlebar {
+    -moz-appearance: -moz-window-titlebar-maximized;
+  }
+
+  /* The button box must appear on top of the navigator-toolbox in order for
+   * click and hover mouse events to work properly for the button in the restored
+   * window state. Otherwise, elements in the navigator-toolbox, like the menubar,
+   * can swallow those events.
+   */
+  #titlebar-buttonbox {
+    z-index: 1;
+  }
+
+  /* titlebar command buttons */
+  /* Use full scale icons here as the Gtk+ does. */
+  @media (-moz-gtk-csd-minimize-button) {
+    #titlebar-min {
+      list-style-image: url("moz-icon://stock/window-minimize-symbolic");
+      -moz-appearance: -moz-window-button-minimize;
+    }
+  }
+  @media not all and (-moz-gtk-csd-minimize-button) {
+    #titlebar-min {
+      display: none;
+    }
+  }
+
+  @media (-moz-gtk-csd-maximize-button) {
+    #titlebar-max {
+      list-style-image: url("moz-icon://stock/window-maximize-symbolic");
+      -moz-appearance: -moz-window-button-maximize;
+    }
+    #main-window[sizemode="maximized"] #titlebar-max {
+      list-style-image: url("moz-icon://stock/window-restore-symbolic");
+      -moz-appearance: -moz-window-button-restore;
+    }
+  }
+  @media not all and (-moz-gtk-csd-maximize-button) {
+    #titlebar-max {
+      display: none;
+    }
+    #main-window[sizemode="maximized"] #titlebar-max {
+      display: none;
+    }
+  }
+
+  @media (-moz-gtk-csd-close-button) {
+    #titlebar-close {
+      list-style-image: url("moz-icon://stock/window-close-symbolic");
+      -moz-appearance: -moz-window-button-close;
+    }
+  }
+  @media not all and (-moz-gtk-csd-close-button) {
+    #titlebar-close {
+      display: none;
+    }
+  }
+}
diff -up firefox-57.0b8/dom/base/nsGkAtomList.h.1399611 firefox-57.0b8/dom/base/nsGkAtomList.h
--- firefox-57.0b8/dom/base/nsGkAtomList.h.1399611	2017-09-15 06:15:41.000000000 +0200
+++ firefox-57.0b8/dom/base/nsGkAtomList.h	2017-10-16 12:11:45.365240651 +0200
@@ -2270,6 +2270,10 @@ GK_ATOM(touch_enabled, "touch-enabled")
 GK_ATOM(menubar_drag, "menubar-drag")
 GK_ATOM(swipe_animation_enabled, "swipe-animation-enabled")
 GK_ATOM(physical_home_button, "physical-home-button")
+GK_ATOM(gtk_csd_available, "gtk-csd-available")
+GK_ATOM(gtk_csd_minimize_button, "gtk-csd-minimize-button")
+GK_ATOM(gtk_csd_maximize_button, "gtk-csd-maximize-button")
+GK_ATOM(gtk_csd_close_button, "gtk-csd-close-button")
 
 // windows theme selector metrics
 GK_ATOM(windows_classic, "windows-classic")
@@ -2306,6 +2310,10 @@ GK_ATOM(_moz_device_orientation, "-moz-d
 GK_ATOM(_moz_is_resource_document, "-moz-is-resource-document")
 GK_ATOM(_moz_swipe_animation_enabled, "-moz-swipe-animation-enabled")
 GK_ATOM(_moz_physical_home_button, "-moz-physical-home-button")
+GK_ATOM(_moz_gtk_csd_available, "-moz-gtk-csd-available")
+GK_ATOM(_moz_gtk_csd_minimize_button, "-moz-gtk-csd-minimize-button")
+GK_ATOM(_moz_gtk_csd_maximize_button, "-moz-gtk-csd-maximize-button")
+GK_ATOM(_moz_gtk_csd_close_button, "-moz-gtk-csd-close-button")
 
 // application commands
 GK_ATOM(Back, "Back")
diff -up firefox-57.0b8/gfx/src/nsThemeConstants.h.1399611 firefox-57.0b8/gfx/src/nsThemeConstants.h
--- firefox-57.0b8/gfx/src/nsThemeConstants.h.1399611	2017-06-12 18:37:10.000000000 +0200
+++ firefox-57.0b8/gfx/src/nsThemeConstants.h	2017-10-16 12:11:45.365240651 +0200
@@ -299,6 +299,7 @@ enum ThemeWidgetType : uint8_t {
   NS_THEME_MAC_SOURCE_LIST,
   NS_THEME_MAC_SOURCE_LIST_SELECTION,
   NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION,
+  NS_THEME_GTK_WINDOW_DECORATION,
 
   ThemeWidgetType_COUNT
 };
diff -up firefox-57.0b8/layout/style/nsCSSRuleProcessor.cpp.1399611 firefox-57.0b8/layout/style/nsCSSRuleProcessor.cpp
--- firefox-57.0b8/layout/style/nsCSSRuleProcessor.cpp.1399611	2017-08-02 14:27:54.000000000 +0200
+++ firefox-57.0b8/layout/style/nsCSSRuleProcessor.cpp	2017-10-16 12:11:45.365240651 +0200
@@ -1180,6 +1180,30 @@ nsCSSRuleProcessor::InitSystemMetrics()
     sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
   }
 
+  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDAvailable,
+                           &metricResult);
+  if (NS_SUCCEEDED(rv) && metricResult) {
+    sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_available);
+  }
+
+  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDMinimizeButton,
+                           &metricResult);
+  if (NS_SUCCEEDED(rv) && metricResult) {
+    sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_minimize_button);
+  }
+
+  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDMaximizeButton,
+                           &metricResult);
+  if (NS_SUCCEEDED(rv) && metricResult) {
+    sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_maximize_button);
+  }
+
+  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDCloseButton,
+                           &metricResult);
+  if (NS_SUCCEEDED(rv) && metricResult) {
+    sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_close_button);
+  }
+
 #ifdef XP_WIN
   if (NS_SUCCEEDED(
         LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
diff -up firefox-57.0b8/layout/style/nsMediaFeatures.cpp.1399611 firefox-57.0b8/layout/style/nsMediaFeatures.cpp
--- firefox-57.0b8/layout/style/nsMediaFeatures.cpp.1399611	2017-09-15 06:15:42.000000000 +0200
+++ firefox-57.0b8/layout/style/nsMediaFeatures.cpp	2017-10-16 12:11:45.366240647 +0200
@@ -788,6 +788,42 @@ nsMediaFeatures::features[] = {
     GetSystemMetric
   },
 
+  {
+    &nsGkAtoms::_moz_gtk_csd_available,
+    nsMediaFeature::eMinMaxNotAllowed,
+    nsMediaFeature::eBoolInteger,
+    nsMediaFeature::eNoRequirements,
+    { &nsGkAtoms::gtk_csd_available },
+    GetSystemMetric
+  },
+
+  {
+    &nsGkAtoms::_moz_gtk_csd_minimize_button,
+    nsMediaFeature::eMinMaxNotAllowed,
+    nsMediaFeature::eBoolInteger,
+    nsMediaFeature::eNoRequirements,
+    { &nsGkAtoms::gtk_csd_minimize_button },
+    GetSystemMetric
+  },
+
+  {
+    &nsGkAtoms::_moz_gtk_csd_maximize_button,
+    nsMediaFeature::eMinMaxNotAllowed,
+    nsMediaFeature::eBoolInteger,
+    nsMediaFeature::eNoRequirements,
+    { &nsGkAtoms::gtk_csd_maximize_button },
+    GetSystemMetric
+  },
+
+  {
+    &nsGkAtoms::_moz_gtk_csd_close_button,
+    nsMediaFeature::eMinMaxNotAllowed,
+    nsMediaFeature::eBoolInteger,
+    nsMediaFeature::eNoRequirements,
+    { &nsGkAtoms::gtk_csd_close_button },
+    GetSystemMetric
+  },
+
   // Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
   // Internal because it is really only useful in the user agent anyway
   //  and therefore not worth standardizing.
diff -up firefox-57.0b8/modules/libpref/init/all.js.1399611 firefox-57.0b8/modules/libpref/init/all.js
--- firefox-57.0b8/modules/libpref/init/all.js.1399611	2017-10-12 18:12:09.000000000 +0200
+++ firefox-57.0b8/modules/libpref/init/all.js	2017-10-16 12:11:45.366240647 +0200
@@ -4911,6 +4911,7 @@ pref("gfx.apitrace.enabled",false);
 pref("gfx.xrender.enabled",false);
 pref("widget.chrome.allow-gtk-dark-theme", false);
 pref("widget.content.allow-gtk-dark-theme", false);
+pref("widget.allow-client-side-decoration", false);
 #endif
 #endif
 
diff -up firefox-57.0b8/toolkit/modules/moz.build.1399611 firefox-57.0b8/toolkit/modules/moz.build
--- firefox-57.0b8/toolkit/modules/moz.build.1399611	2017-09-15 06:15:40.000000000 +0200
+++ firefox-57.0b8/toolkit/modules/moz.build	2017-10-16 12:11:45.366240647 +0200
@@ -259,7 +259,7 @@ EXTRA_JS_MODULES.sessionstore += [
 ]
 
 DEFINES['INSTALL_COMPACT_THEMES'] = 1
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa', 'gtk3'):
     DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
diff -up firefox-57.0b8/widget/gtk/gtk3drawing.cpp.1399611 firefox-57.0b8/widget/gtk/gtk3drawing.cpp
--- firefox-57.0b8/widget/gtk/gtk3drawing.cpp.1399611	2017-09-15 06:15:40.000000000 +0200
+++ firefox-57.0b8/widget/gtk/gtk3drawing.cpp	2017-10-16 12:11:45.367240644 +0200
@@ -17,6 +17,7 @@
 #include "WidgetStyleCache.h"
 
 #include <math.h>
+#include <dlfcn.h>
 
 static gboolean checkbox_check_state;
 static gboolean notebook_has_tab_gap;
@@ -39,9 +40,25 @@ static gint
 moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect,
                         GtkWidgetState* state, GtkTextDirection direction);
 
+static void
+moz_gtk_add_style_margin(GtkStyleContext* style,
+                         gint* left, gint* top, gint* right, gint* bottom);
+static void
+moz_gtk_add_style_border(GtkStyleContext* style,
+                         gint* left, gint* top, gint* right, gint* bottom);
+static void
+moz_gtk_add_style_padding(GtkStyleContext* style,
+                          gint* left, gint* top, gint* right, gint* bottom);
+static void moz_gtk_add_margin_border_padding(GtkStyleContext *style,
+                                              gint* left, gint* top,
+                                              gint* right, gint* bottom);
+static void moz_gtk_add_border_padding(GtkStyleContext *style,
+                                       gint* left, gint* top,
+                                       gint* right, gint* bottom);
 static GtkBorder
 GetMarginBorderPadding(GtkStyleContext* aStyle);
 
+
 // GetStateFlagsFromGtkWidgetState() can be safely used for the specific
 // GtkWidgets that set both prelight and active flags.  For other widgets,
 // either the GtkStateFlags or Gecko's GtkWidgetState need to be carefully
@@ -233,6 +250,43 @@ moz_gtk_splitter_get_metrics(gint orient
     return MOZ_GTK_SUCCESS;
 }
 
+void
+moz_gtk_get_window_border(gint* top, gint* right, gint* bottom, gint* left)
+{
+  MOZ_ASSERT(gtk_check_version(3, 20, 0) == nullptr,
+             "Window decorations are only supported on GTK 3.20+.");
+
+  GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW);
+
+  *top = *right = *bottom = *left = 0;
+  moz_gtk_add_border_padding(style, left, top, right, bottom);
+  GtkBorder windowMargin;
+  gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &windowMargin);
+
+  style = GetStyleContext(MOZ_GTK_WINDOW_DECORATION);
+
+  // Available on GTK 3.20+.
+  static auto sGtkRenderBackgroundGetClip =
+    (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*))
+    dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip");
+
+  GdkRectangle shadowClip;
+  sGtkRenderBackgroundGetClip(style, 0, 0, 0, 0, &shadowClip);
+
+  // Transfer returned inset rectangle to GtkBorder
+  GtkBorder shadowBorder = {
+      static_cast<gint16>(-shadowClip.x),                    // left
+      static_cast<gint16>(shadowClip.width + shadowClip.x),  // right
+      static_cast<gint16>(-shadowClip.y),                    // top
+      static_cast<gint16>(shadowClip.height + shadowClip.y), // bottom
+  };
+
+  *left += MAX(windowMargin.left, shadowBorder.left);
+  *right += MAX(windowMargin.right, shadowBorder.right);
+  *top += MAX(windowMargin.top, shadowBorder.top);
+  *bottom += MAX(windowMargin.bottom, shadowBorder.bottom);
+}
+
 static gint
 moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect,
                      GtkTextDirection direction)
@@ -302,6 +356,24 @@ moz_gtk_button_paint(cairo_t *cr, GdkRec
 }
 
 static gint
+moz_gtk_header_bar_button_paint(cairo_t *cr, GdkRectangle* rect,
+                                GtkWidgetState* state,
+                                GtkReliefStyle relief, GtkWidget* widget,
+                                GtkTextDirection direction)
+{
+    GtkBorder margin;
+    GtkStyleContext* style = gtk_widget_get_style_context(widget);
+    gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin);
+
+    rect->x += margin.left;
+    rect->y += margin.top;
+    rect->width -= margin.left + margin.right;
+    rect->height -= margin.top + margin.bottom;
+
+    return moz_gtk_button_paint(cr, rect, state, relief, widget, direction);
+}
+
+static gint
 moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect,
                      GtkWidgetState* state,
                      gboolean selected, gboolean inconsistent,
@@ -1948,6 +2020,38 @@ moz_gtk_info_bar_paint(cairo_t *cr, GdkR
     return MOZ_GTK_SUCCESS;
 }
 
+static gint
+moz_gtk_header_bar_paint(WidgetNodeType widgetType,
+                         cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state)
+{
+    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
+    GtkStyleContext *style;
+
+    style = GetStyleContext(widgetType, GTK_TEXT_DIR_LTR,
+                            state_flags);
+    InsetByMargin(rect, style);
+    gtk_render_background(style, cr, rect->x, rect->y, rect->width,
+                          rect->height);
+    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
+
+    return MOZ_GTK_SUCCESS;
+}
+
+void
+moz_gtk_header_bar_paint(cairo_t *cr, GdkRectangle* rect)
+{
+    static GtkWidgetState state;
+    moz_gtk_header_bar_paint(MOZ_GTK_HEADER_BAR, cr, rect, &state);
+}
+
+void
+moz_gtk_get_header_bar_border(gint* top, gint* right, gint* bottom, gint* left)
+{
+    *left = *top = *right = *bottom = 0;
+    moz_gtk_add_border_padding(GetStyleContext(MOZ_GTK_HEADER_BAR),
+                               left, top, right, bottom);
+}
+
 static void
 moz_gtk_add_style_margin(GtkStyleContext* style,
                          gint* left, gint* top, gint* right, gint* bottom)
@@ -1999,6 +2103,14 @@ static void moz_gtk_add_margin_border_pa
     moz_gtk_add_style_padding(style, left, top, right, bottom);
 }
 
+static void moz_gtk_add_border_padding(GtkStyleContext *style,
+                                       gint* left, gint* top,
+                                       gint* right, gint* bottom)
+{
+    moz_gtk_add_style_border(style, left, top, right, bottom);
+    moz_gtk_add_style_padding(style, left, top, right, bottom);
+}
+
 static GtkBorder
 GetMarginBorderPadding(GtkStyleContext* aStyle)
 {
@@ -2054,8 +2166,7 @@ moz_gtk_get_widget_border(WidgetNodeType
             // XXX: Subtract 1 pixel from the padding to account for the default
             // padding in forms.css. See bug 1187385.
             *left = *top = *right = *bottom = -1;
-            moz_gtk_add_style_padding(style, left, top, right, bottom);
-            moz_gtk_add_style_border(style, left, top, right, bottom);
+            moz_gtk_add_border_padding(style, left, top, right, bottom);
 
             return MOZ_GTK_SUCCESS;
         }
@@ -2076,10 +2187,8 @@ moz_gtk_get_widget_border(WidgetNodeType
             *left = *top = *right = *bottom =
                 gtk_container_get_border_width(GTK_CONTAINER(
                                                GetWidget(MOZ_GTK_TREE_HEADER_CELL)));
-
             style = GetStyleContext(MOZ_GTK_TREE_HEADER_CELL);
-            moz_gtk_add_style_border(style, left, top, right, bottom);
-            moz_gtk_add_style_padding(style, left, top, right, bottom);
+            moz_gtk_add_border_padding(style, left, top, right, bottom);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_TREE_HEADER_SORTARROW:
@@ -2105,8 +2214,7 @@ moz_gtk_get_widget_border(WidgetNodeType
                 gtk_container_get_border_width(GTK_CONTAINER(
                                                GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
             style = GetStyleContext(MOZ_GTK_COMBOBOX_BUTTON);
-            moz_gtk_add_style_padding(style, left, top, right, bottom);
-            moz_gtk_add_style_border(style, left, top, right, bottom);
+            moz_gtk_add_border_padding(style, left, top, right, bottom);
 
             /* If there is no separator, don't try to count its width. */
             separator_width = 0;
@@ -2160,10 +2268,8 @@ moz_gtk_get_widget_border(WidgetNodeType
             style = gtk_widget_get_style_context(w);
 
             *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w));
-            moz_gtk_add_style_border(style,
-                                     left, top, right, bottom);
-            moz_gtk_add_style_padding(style,
-                                      left, top, right, bottom);
+            moz_gtk_add_border_padding(style,
+                                       left, top, right, bottom);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_MENUPOPUP:
@@ -2210,6 +2316,21 @@ moz_gtk_get_widget_border(WidgetNodeType
 
             return MOZ_GTK_SUCCESS;
         }
+    case MOZ_GTK_HEADER_BAR:
+    case MOZ_GTK_HEADER_BAR_MAXIMIZED:
+        {
+            style = GetStyleContext(widget);
+            moz_gtk_add_border_padding(style, left, top, right, bottom);
+            return MOZ_GTK_SUCCESS;
+        }
+    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+        {
+            style = GetStyleContext(widget);
+            moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
+            return MOZ_GTK_SUCCESS;
+        }
 
     /* These widgets have no borders, since they are not containers. */
     case MOZ_GTK_CHECKBUTTON_LABEL:
@@ -2646,6 +2767,36 @@ GetScrollbarMetrics(GtkOrientation aOrie
     return metrics;
 }
 
+void
+moz_gtk_window_decoration_paint(cairo_t *cr, GdkRectangle* rect)
+{
+    gint top, right, bottom, left;
+    moz_gtk_get_window_border(&top, &right, &bottom, &left);
+
+    cairo_save(cr);
+    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+    cairo_rectangle(cr, rect->x, rect->y, left, rect->height);
+    cairo_fill(cr);
+    cairo_rectangle(cr, rect->x+rect->width-right, rect->y, right, rect->height);
+    cairo_fill(cr);
+    cairo_rectangle(cr, rect->x, rect->y, rect->width, top);
+    cairo_fill(cr);
+    cairo_rectangle(cr, rect->x, rect->height-bottom, rect->width, bottom);
+    cairo_fill(cr);
+    cairo_restore(cr);
+
+    GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW_DECORATION,
+                                               GTK_TEXT_DIR_NONE);
+    rect->x += left;
+    rect->y += top;
+    rect->width -= left + right;
+    rect->height -= top + bottom;
+
+    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
+    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
+}
+
+
 /* cairo_t *cr argument has to be a system-cairo. */
 gint
 moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
@@ -2671,6 +2822,14 @@ moz_gtk_widget_paint(WidgetNodeType widg
                                     GetWidget(MOZ_GTK_BUTTON),
                                     direction);
         break;
+    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+        return moz_gtk_header_bar_button_paint(cr, rect, state,
+                                               (GtkReliefStyle) flags,
+                                               GetWidget(widget),
+                                               direction);
+        break;
     case MOZ_GTK_CHECKBUTTON:
     case MOZ_GTK_RADIOBUTTON:
         return moz_gtk_toggle_paint(cr, rect, state,
@@ -2877,6 +3036,10 @@ moz_gtk_widget_paint(WidgetNodeType widg
     case MOZ_GTK_INFO_BAR:
         return moz_gtk_info_bar_paint(cr, rect, state);
         break;
+    case MOZ_GTK_HEADER_BAR:
+    case MOZ_GTK_HEADER_BAR_MAXIMIZED:
+        return moz_gtk_header_bar_paint(widget, cr, rect, state);
+        break;
     default:
         g_warning("Unknown widget type: %d", widget);
     }
diff -up firefox-57.0b8/widget/gtk/gtkdrawing.h.1399611 firefox-57.0b8/widget/gtk/gtkdrawing.h
--- firefox-57.0b8/widget/gtk/gtkdrawing.h.1399611	2017-06-12 18:37:20.000000000 +0200
+++ firefox-57.0b8/widget/gtk/gtkdrawing.h	2017-10-16 12:11:45.367240644 +0200
@@ -268,8 +268,14 @@ typedef enum {
   MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL,
   /* Paints the background of a window, dialog or page. */
   MOZ_GTK_WINDOW,
+  /* Used only as a container for MOZ_GTK_HEADER_BAR_MAXIMIZED. */
+  MOZ_GTK_WINDOW_MAXIMIZED,
   /* Window container for all widgets */
   MOZ_GTK_WINDOW_CONTAINER,
+  /* Window with the 'csd' style class. */
+  MOZ_GTK_WINDOW_CSD,
+  /* Client-side window decoration node. Available on GTK 3.20+. */
+  MOZ_GTK_WINDOW_DECORATION,
   /* Paints a GtkInfoBar, for notifications. */
   MOZ_GTK_INFO_BAR,
   /* Used for widget tree construction. */
@@ -290,6 +296,14 @@ typedef enum {
   MOZ_GTK_COMBOBOX_ENTRY_ARROW,
   /* Used for scrolled window shell. */
   MOZ_GTK_SCROLLED_WINDOW,
+  /* Paints a GtkHeaderBar */
+  MOZ_GTK_HEADER_BAR,
+  /* Paints a GtkHeaderBar in maximized state */
+  MOZ_GTK_HEADER_BAR_MAXIMIZED,
+  /* Paints a GtkHeaderBar title buttons */
+  MOZ_GTK_HEADER_BAR_BUTTON_CLOSE,
+  MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE,
+  MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE,
 
   MOZ_GTK_WIDGET_NODE_COUNT
 } WidgetNodeType;
@@ -542,6 +556,32 @@ gint moz_gtk_get_menu_separator_height(g
  */
 gint moz_gtk_splitter_get_metrics(gint orientation, gint* size);
 
+#if (MOZ_WIDGET_GTK == 3)
+/**
+ * Gets the margins to be used for window decorations, typically the extra space
+ * required to draw a drop shadow (obtained from gtk_render_background_get_clip).
+ * Only available on GTK 3.20+.
+ */
+void moz_gtk_get_window_border(gint* top, gint* right, gint* bottom, gint* left);
+
+/**
+ * Draw window decorations, typically a shadow.
+ * Only available on GTK 3.20+.
+ */
+void moz_gtk_window_decoration_paint(cairo_t *cr, GdkRectangle* rect);
+
+/**
+ * Gets the border of window header bar, only available on GTK 3.20+.
+ */
+void moz_gtk_get_header_bar_border(gint* top, gint* right, gint* bottom, gint* left);
+
+/**
+ * Draw window header bar, only available on GTK 3.20+.
+ */
+void moz_gtk_header_bar_paint(cairo_t *cr, GdkRectangle* rect);
+
+#endif
+
 /**
  * Get the YTHICKNESS of a tab (notebook extension).
  */
diff -up firefox-57.0b8/widget/gtk/mozgtk/mozgtk.c.1399611 firefox-57.0b8/widget/gtk/mozgtk/mozgtk.c
--- firefox-57.0b8/widget/gtk/mozgtk/mozgtk.c.1399611	2017-10-09 22:17:13.000000000 +0200
+++ firefox-57.0b8/widget/gtk/mozgtk/mozgtk.c	2017-10-16 12:11:45.367240644 +0200
@@ -580,6 +580,8 @@ STUB(gtk_style_context_set_state)
 STUB(gtk_style_properties_lookup_property)
 STUB(gtk_tree_view_column_get_button)
 STUB(gtk_widget_get_preferred_size)
+STUB(gtk_widget_get_preferred_width)
+STUB(gtk_widget_get_preferred_height)
 STUB(gtk_widget_get_state_flags)
 STUB(gtk_widget_get_style_context)
 STUB(gtk_widget_path_append_type)
@@ -589,6 +591,10 @@ STUB(gtk_widget_path_iter_add_class)
 STUB(gtk_widget_path_get_object_type)
 STUB(gtk_widget_path_new)
 STUB(gtk_widget_path_unref)
+STUB(gtk_widget_set_margin_left)
+STUB(gtk_widget_set_margin_right)
+STUB(gtk_widget_set_margin_top)
+STUB(gtk_widget_set_margin_bottom)
 STUB(gtk_widget_set_visual)
 STUB(gtk_app_chooser_dialog_new_for_content_type)
 STUB(gtk_app_chooser_get_type)
@@ -601,6 +607,7 @@ STUB(gtk_color_chooser_get_type)
 STUB(gtk_color_chooser_set_rgba)
 STUB(gtk_color_chooser_get_rgba)
 STUB(gtk_color_chooser_set_use_alpha)
+STUB(gtk_window_get_size)
 #endif
 
 #ifdef GTK2_SYMBOLS
diff -up firefox-57.0b8/widget/gtk/nsLookAndFeel.cpp.1399611 firefox-57.0b8/widget/gtk/nsLookAndFeel.cpp
--- firefox-57.0b8/widget/gtk/nsLookAndFeel.cpp.1399611	2017-09-21 06:10:10.000000000 +0200
+++ firefox-57.0b8/widget/gtk/nsLookAndFeel.cpp	2017-10-16 12:11:45.367240644 +0200
@@ -642,6 +642,22 @@ nsLookAndFeel::GetIntImpl(IntID aID, int
     case eIntID_ContextMenuOffsetHorizontal:
         aResult = 2;
         break;
+    case eIntID_GTKCSDAvailable:
+        EnsureInit();
+        aResult = sCSDAvailable;
+        break;
+    case eIntID_GTKCSDMaximizeButton:
+        EnsureInit();
+        aResult = sCSDMaximizeButton;
+        break;
+    case eIntID_GTKCSDMinimizeButton:
+        EnsureInit();
+        aResult = sCSDMinimizeButton;
+        break;
+    case eIntID_GTKCSDCloseButton:
+        EnsureInit();
+        aResult = sCSDCloseButton;
+        break;
     default:
         aResult = 0;
         res     = NS_ERROR_FAILURE;
@@ -1048,6 +1064,40 @@ nsLookAndFeel::EnsureInit()
 
     gtk_widget_destroy(window);
     g_object_unref(labelWidget);
+
+    // Require GTK 3.20 for client-side decoration support.
+    // 3.20 exposes gtk_render_background_get_clip, which is required for
+    // calculating shadow metrics for decorated windows.
+    sCSDAvailable = gtk_check_version(3, 20, 0) == nullptr;
+    if (sCSDAvailable) {
+        sCSDAvailable =
+            mozilla::Preferences::GetBool("widget.allow-client-side-decoration",
+                                          false);
+    }
+
+    const gchar* decorationLayout = nullptr;
+    if (gtk_check_version(3, 12, 0) == nullptr) {
+        static auto sGtkHeaderBarGetDecorationLayoutPtr =
+          (const gchar* (*)(GtkWidget*))
+          dlsym(RTLD_DEFAULT, "gtk_header_bar_get_decoration_layout");
+
+        GtkWidget* headerBar = GetWidget(MOZ_GTK_HEADER_BAR);
+        decorationLayout = sGtkHeaderBarGetDecorationLayoutPtr(headerBar);
+        if (!decorationLayout) {
+            g_object_get(settings, "gtk-decoration-layout", &decorationLayout,
+                         nullptr);
+        }
+    }
+
+    if (decorationLayout) {
+        sCSDCloseButton = (strstr(decorationLayout, "close") != nullptr);
+        sCSDMaximizeButton = (strstr(decorationLayout, "maximize") != nullptr);
+        sCSDMinimizeButton = (strstr(decorationLayout, "minimize") != nullptr);
+    } else {
+        sCSDCloseButton = true;
+        sCSDMaximizeButton = true;
+        sCSDMinimizeButton = true;
+    }
 }
 
 // virtual
diff -up firefox-57.0b8/widget/gtk/nsLookAndFeel.h.1399611 firefox-57.0b8/widget/gtk/nsLookAndFeel.h
--- firefox-57.0b8/widget/gtk/nsLookAndFeel.h.1399611	2017-09-21 06:10:10.000000000 +0200
+++ firefox-57.0b8/widget/gtk/nsLookAndFeel.h	2017-10-16 12:11:45.367240644 +0200
@@ -32,6 +32,8 @@ public:
     virtual char16_t GetPasswordCharacterImpl();
     virtual bool GetEchoPasswordImpl();
 
+    bool IsCSDAvailable() const { return sCSDAvailable; }
+
 protected:
 
     // Cached fonts
@@ -82,6 +84,10 @@ protected:
     char16_t sInvisibleCharacter;
     float   sCaretRatio;
     bool    sMenuSupportsDrag;
+    bool    sCSDAvailable;
+    bool    sCSDMaximizeButton;
+    bool    sCSDMinimizeButton;
+    bool    sCSDCloseButton;
     bool    mInitialized;
 
     void EnsureInit();
diff -up firefox-57.0b8/widget/gtk/nsNativeThemeGTK.cpp.1399611 firefox-57.0b8/widget/gtk/nsNativeThemeGTK.cpp
--- firefox-57.0b8/widget/gtk/nsNativeThemeGTK.cpp.1399611	2017-09-19 06:18:28.000000000 +0200
+++ firefox-57.0b8/widget/gtk/nsNativeThemeGTK.cpp	2017-10-16 12:11:45.368240640 +0200
@@ -23,6 +23,7 @@
 #include "nsIDOMHTMLInputElement.h"
 #include "nsGkAtoms.h"
 #include "nsAttrValueInlines.h"
+#include "nsWindow.h"
 
 #include "mozilla/EventStates.h"
 #include "mozilla/Services.h"
@@ -703,6 +704,24 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u
   case NS_THEME_GTK_INFO_BAR:
     aGtkWidgetType = MOZ_GTK_INFO_BAR;
     break;
+  case NS_THEME_WINDOW_TITLEBAR:
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR;
+    break;
+  case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED;
+    break;
+  case NS_THEME_WINDOW_BUTTON_CLOSE:
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE;
+    break;
+  case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
+    break;
+  case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
+    break;
+  case NS_THEME_WINDOW_BUTTON_RESTORE:
+    aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
+    break;
   default:
     return false;
   }
@@ -1627,6 +1646,10 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
   case NS_THEME_MENULIST:
   case NS_THEME_TOOLBARBUTTON:
   case NS_THEME_TREEHEADERCELL:
+  case NS_THEME_WINDOW_BUTTON_CLOSE:
+  case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+  case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+  case NS_THEME_WINDOW_BUTTON_RESTORE:
     {
       if (aWidgetType == NS_THEME_MENULIST) {
         // Include the arrow size.
@@ -1892,9 +1915,21 @@ nsNativeThemeGTK::ThemeSupportsWidget(ns
   case NS_THEME_DIALOG:
 #if (MOZ_WIDGET_GTK == 3)
   case NS_THEME_GTK_INFO_BAR:
+  case NS_THEME_GTK_WINDOW_DECORATION:
 #endif
     return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 
+  case NS_THEME_WINDOW_BUTTON_CLOSE:
+  case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+  case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+  case NS_THEME_WINDOW_BUTTON_RESTORE:
+  case NS_THEME_WINDOW_TITLEBAR:
+  case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+    // GtkHeaderBar is available on GTK 3.10+, which is used for styling
+    // title bars and title buttons.
+    return gtk_check_version(3, 10, 0) == nullptr &&
+           !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
+
   case NS_THEME_MENULIST_BUTTON:
     if (aFrame && aFrame->GetWritingMode().IsVertical()) {
       return false;
@@ -1978,6 +2013,13 @@ nsNativeThemeGTK::GetWidgetTransparency(
 #else
     return eTransparent;
 #endif
+  case NS_THEME_GTK_WINDOW_DECORATION:
+  {
+    nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget());
+    if (window)
+      return window->IsComposited() ? eTransparent : eOpaque;
+    return eOpaque;
+  }
   }
 
   return eUnknownTransparency;
diff -up firefox-57.0b8/widget/gtk/nsWindow.cpp.1399611 firefox-57.0b8/widget/gtk/nsWindow.cpp
--- firefox-57.0b8/widget/gtk/nsWindow.cpp.1399611	2017-10-16 12:11:45.361240666 +0200
+++ firefox-57.0b8/widget/gtk/nsWindow.cpp	2017-10-16 12:11:45.369240636 +0200
@@ -85,6 +85,7 @@
 #include "nsIPropertyBag2.h"
 #include "GLContext.h"
 #include "gfx2DGlue.h"
+#include "nsLookAndFeel.h"
 
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/Accessible.h"
@@ -139,6 +140,8 @@ using namespace mozilla::widget;
 
 #include "mozilla/layers/APZCTreeManager.h"
 
+#include "gtkdrawing.h"
+
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::widget;
@@ -186,6 +189,8 @@ static gboolean expose_event_cb
 #else
 static gboolean expose_event_cb           (GtkWidget *widget,
                                            cairo_t *rect);
+static gboolean expose_event_decoration_draw_cb (GtkWidget *widget,
+                                                cairo_t *cr);
 #endif
 static gboolean configure_event_cb        (GtkWidget *widget,
                                            GdkEventConfigure *event);
@@ -231,7 +236,6 @@ static void     screen_composited_change
                                                   gpointer user_data);
 static void     widget_composited_changed_cb     (GtkWidget* widget,
                                                   gpointer user_data);
-
 #if (MOZ_WIDGET_GTK == 3)
 static void     scale_changed_cb          (GtkWidget* widget,
                                            GParamSpec* aPSpec,
@@ -440,6 +444,7 @@ nsWindow::nsWindow()
 
     mContainer           = nullptr;
     mGdkWindow           = nullptr;
+    mIsCSDEnabled        = false;
     mShell               = nullptr;
     mCompositorWidgetDelegate = nullptr;
     mHasMappedToplevel   = false;
@@ -481,6 +486,9 @@ nsWindow::nsWindow()
     mLastScrollEventTime = GDK_CURRENT_TIME;
 #endif
     mPendingConfigures = 0;
+    mDrawWindowDecoration = false;
+    mDecorationSize = {0,0,0,0};
+    mCSDSupportLevel = CSD_SUPPORT_UNKNOWN;
 }
 
 nsWindow::~nsWindow()
@@ -1479,8 +1487,8 @@ LayoutDeviceIntRect
 nsWindow::GetScreenBounds()
 {
     LayoutDeviceIntRect rect;
-    if (mIsTopLevel && mContainer) {
-        // use the point including window decorations
+    if (mIsTopLevel && mContainer && !IsClientDecorated()) {
+        // use the point including default Gtk+ window decorations
         gint x, y;
         gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
         rect.MoveTo(GdkPointToDevicePixels({ x, y }));
@@ -1606,6 +1614,10 @@ nsWindow::SetCursor(nsCursor aCursor)
                 return;
 
             gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
+            if (IsClientDecorated()) {
+                gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mShell)),
+                                      newCursor);
+            }
         }
     }
 }
@@ -1662,6 +1674,10 @@ nsWindow::SetCursor(imgIContainer* aCurs
     if (cursor) {
         if (mContainer) {
             gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
+            if (IsClientDecorated()) {
+                gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mShell)),
+                                      cursor);
+            }
             rv = NS_OK;
         }
 #if (MOZ_WIDGET_GTK == 3)
@@ -2176,6 +2192,12 @@ nsWindow::OnExposeEvent(cairo_t *cr)
         return TRUE;
     }
 
+    // Clip upper part of the mContainer to get visible rounded corners
+    // of GtkHeaderBar which is renderd to mShell.
+    if (mIsCSDEnabled) {
+        ApplyCSDClipping();
+    }
+
     // If this widget uses OMTC...
     if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT ||
         GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR) {
@@ -2586,6 +2608,53 @@ nsWindow::OnMotionNotifyEvent(GdkEventMo
         }
     }
 #endif /* MOZ_X11 */
+    // Client is decorated and we're getting the motion event for mShell
+    // window which draws the CSD decorations around mContainer.
+    if (IsClientDecorated()) {
+        if (aEvent->window == gtk_widget_get_window(mShell)) {
+            GdkWindowEdge edge;
+            LayoutDeviceIntPoint refPoint =
+                GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
+            if (CheckResizerEdge(refPoint, edge)) {
+                nsCursor cursor = eCursor_none;
+                switch (edge) {
+                case GDK_WINDOW_EDGE_NORTH:
+                    cursor = eCursor_n_resize;
+                    break;
+                case GDK_WINDOW_EDGE_NORTH_WEST:
+                    cursor = eCursor_nw_resize;
+                    break;
+                case GDK_WINDOW_EDGE_NORTH_EAST:
+                    cursor = eCursor_ne_resize;
+                    break;
+                case GDK_WINDOW_EDGE_WEST:
+                    cursor = eCursor_w_resize;
+                    break;
+                case GDK_WINDOW_EDGE_EAST:
+                    cursor = eCursor_e_resize;
+                    break;
+                case GDK_WINDOW_EDGE_SOUTH:
+                    cursor = eCursor_s_resize;
+                    break;
+                case GDK_WINDOW_EDGE_SOUTH_WEST:
+                    cursor = eCursor_sw_resize;
+                    break;
+                case GDK_WINDOW_EDGE_SOUTH_EAST:
+                    cursor = eCursor_se_resize;
+                    break;
+                }
+                SetCursor(cursor);
+                return;
+            }
+        }
+        // We're not on resize handle - check if we need to reset cursor back.
+        if (mCursor == eCursor_n_resize || mCursor == eCursor_nw_resize ||
+            mCursor == eCursor_ne_resize ||  mCursor == eCursor_w_resize ||
+            mCursor == eCursor_e_resize || mCursor == eCursor_s_resize ||
+            mCursor == eCursor_sw_resize || mCursor == eCursor_se_resize) {
+            SetCursor(eCursor_standard);
+        }
+    }
 
     WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
 
@@ -2756,6 +2825,20 @@ nsWindow::OnButtonPressEvent(GdkEventBut
     if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
         return;
 
+    if (IsClientDecorated() && aEvent->window == gtk_widget_get_window(mShell)) {
+        // Check to see if the event is within our window's resize region
+        GdkWindowEdge edge;
+        LayoutDeviceIntPoint refPoint =
+            GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
+        if (CheckResizerEdge(refPoint, edge)) {
+            gdk_window_begin_resize_drag(gtk_widget_get_window(mShell),
+                                         edge, aEvent->button,
+                                         aEvent->x_root, aEvent->y_root,
+                                         aEvent->time);
+            return;
+        }
+    }
+
     gdouble pressure = 0;
     gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
     mLastMotionPressure = pressure;
@@ -3341,6 +3424,8 @@ nsWindow::OnWindowStateEvent(GtkWidget *
 #endif //ACCESSIBILITY
     }
 
+    UpdateClientDecorations();
+
     if (mWidgetListener) {
       mWidgetListener->SizeModeChanged(mSizeState);
       if (aEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
@@ -3405,6 +3490,7 @@ nsWindow::OnCompositedChanged()
       presShell->ThemeChanged();
     }
   }
+  UpdateClientDecorations();
 }
 
 void
@@ -3593,7 +3679,8 @@ nsWindow::Create(nsIWidget* aParent,
     GtkWindow      *topLevelParent = nullptr;
     nsWindow       *parentnsWindow = nullptr;
     GtkWidget      *eventWidget = nullptr;
-    bool            shellHasCSD = false;
+    GtkWidget      *drawWidget = nullptr;
+    bool            drawToContainer = false;
 
     if (aParent) {
         parentnsWindow = static_cast<nsWindow*>(aParent);
@@ -3640,29 +3727,47 @@ nsWindow::Create(nsIWidget* aParent,
               GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
         mShell = gtk_window_new(type);
 
-        bool useAlphaVisual = (mWindowType == eWindowType_popup &&
-                               aInitData->mSupportTranslucency);
+        bool useAlphaVisual = false;
+#if (MOZ_WIDGET_GTK == 3)
+        // When CSD is available we can emulate it for toplevel windows.
+        // Content is rendered to mContainer and transparent decorations to mShell.
+        if (GetCSDSupportLevel() != CSD_SUPPORT_NONE &&
+            mWindowType == eWindowType_toplevel) {
+            int32_t isCSDAvailable = false;
+            nsresult rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDAvailable,
+                                             &isCSDAvailable);
+            if (NS_SUCCEEDED(rv)) {
+                mIsCSDEnabled = useAlphaVisual = isCSDAvailable;
+            }
+        } else
+#endif
+        if (mWindowType == eWindowType_popup) {
+            useAlphaVisual = aInitData->mSupportTranslucency;
+        }
 
         // mozilla.widget.use-argb-visuals is a hidden pref defaulting to false
         // to allow experimentation
         if (Preferences::GetBool("mozilla.widget.use-argb-visuals", false))
             useAlphaVisual = true;
 
+        // An ARGB visual is only useful if we are on a compositing
+        // window manager.
+        GdkScreen *screen = gtk_widget_get_screen(mShell);
+        if (useAlphaVisual && !gdk_screen_is_composited(screen)) {
+            useAlphaVisual = false;
+        }
+
         // We need to select an ARGB visual here instead of in
         // SetTransparencyMode() because it has to be done before the
-        // widget is realized.  An ARGB visual is only useful if we
-        // are on a compositing window manager.
+        // widget is realized.
         if (useAlphaVisual) {
-            GdkScreen *screen = gtk_widget_get_screen(mShell);
-            if (gdk_screen_is_composited(screen)) {
 #if (MOZ_WIDGET_GTK == 2)
-                GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
-                gtk_widget_set_colormap(mShell, colormap);
+            GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
+            gtk_widget_set_colormap(mShell, colormap);
 #else
-                GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
-                gtk_widget_set_visual(mShell, visual);
+            GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
+            gtk_widget_set_visual(mShell, visual);
 #endif
-            }
         }
 
         // We only move a general managed toplevel window if someone has
@@ -3756,24 +3861,56 @@ nsWindow::Create(nsIWidget* aParent,
         mContainer = MOZ_CONTAINER(container);
 
 #if (MOZ_WIDGET_GTK == 3)
-        // "csd" style is set when widget is realized so we need to call
-        // it explicitly now.
-        gtk_widget_realize(mShell);
-
-        // We can't draw directly to top-level window when client side
-        // decorations are enabled. We use container with GdkWindow instead.
-        GtkStyleContext* style = gtk_widget_get_style_context(mShell);
-        shellHasCSD = gtk_style_context_has_class(style, "csd");
-#endif
-        if (!shellHasCSD) {
-            // Use mShell's window for drawing and events.
-            gtk_widget_set_has_window(container, FALSE);
-            // Prevent GtkWindow from painting a background to flicker.
-            gtk_widget_set_app_paintable(mShell, TRUE);
-        }
-        // Set up event widget
-        eventWidget = shellHasCSD ? container : mShell;
+        /* There are tree possible situations here:
+         *
+         * 1) We're running on Gtk+ < 3.20 without any decorations. Content
+         *    is rendered to mShell window and we listen Gtk+ events on mShell.
+         * 2) We're running on Gtk+ > 3.20 and window decorations are drawn
+         *    by default by Gtk+. Content is rendered to mContainer,
+         *    we listen events on mContainer. mShell contains default Gtk+
+         *    window decorations rendered by Gtk+.
+         * 3) We're running on Gtk+ > 3.20 and both window decorations and
+         *    content is rendered by gecko. We emulate Gtk+ decoration rendering
+         *    to mShell and we need to listen Gtk events on both mShell
+         *    and mContainer.
+         */
+
+        // When client side decorations are enabled (rendered by us or by Gtk+)
+        // the decoration is rendered to mShell (toplevel) window and
+        // we draw our content to mContainer.
+        if (mIsCSDEnabled) {
+            drawToContainer = true;
+        } else {
+            // mIsCSDEnabled can be disabled by preference so look at actual
+            // toplevel window style to to detect active "csd" style.
+            // The "csd" style is set when widget is realized so we need to call
+            // it explicitly now.
+            gtk_widget_realize(mShell);
+
+            GtkStyleContext* style = gtk_widget_get_style_context(mShell);
+            drawToContainer = gtk_style_context_has_class(style, "csd");
+        }
+#endif
+        drawWidget = (drawToContainer) ? container : mShell;
+        // When we draw decorations on our own we need to handle resize events
+        // because Gtk+ does not provide resizers for undecorated windows.
+        // The CSD on mShell borders act as resize handlers
+        // so we need to listen there.
+        eventWidget = (drawToContainer && !mIsCSDEnabled) ? container : mShell;
+
         gtk_widget_add_events(eventWidget, kEvents);
+        if (eventWidget != drawWidget) {
+            // CSD is rendered by us (not by Gtk+) so we also need to listen
+            // at mShell window for events.
+            gtk_widget_add_events(drawWidget, kEvents);
+        }
+
+        // Prevent GtkWindow from painting a background to flicker.
+        gtk_widget_set_app_paintable(drawWidget, TRUE);
+
+        // gtk_container_add() realizes the child widget so we need to
+        // set it now.
+        gtk_widget_set_has_window(container, drawToContainer);
 
         gtk_container_add(GTK_CONTAINER(mShell), container);
         gtk_widget_realize(container);
@@ -3783,7 +3920,7 @@ nsWindow::Create(nsIWidget* aParent,
         gtk_widget_grab_focus(container);
 
         // the drawing window
-        mGdkWindow = gtk_widget_get_window(eventWidget);
+        mGdkWindow = gtk_widget_get_window(drawWidget);
 
         if (mWindowType == eWindowType_popup) {
             // gdk does not automatically set the cursor for "temporary"
@@ -3856,6 +3993,11 @@ nsWindow::Create(nsIWidget* aParent,
 
     // label the drawing window with this object so we can find our way home
     g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
+    if (mIsCSDEnabled) {
+        // label the CSD window with this object so we can find our 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);
@@ -3893,6 +4035,10 @@ nsWindow::Create(nsIWidget* aParent,
         g_signal_connect_after(default_settings,
                                "notify::gtk-font-name",
                                G_CALLBACK(theme_changed_cb), this);
+        if (mIsCSDEnabled) {
+            g_signal_connect(G_OBJECT(mShell), "draw",
+                             G_CALLBACK(expose_event_decoration_draw_cb), nullptr);
+        }
     }
 
     if (mContainer) {
@@ -3943,7 +4089,7 @@ nsWindow::Create(nsIWidget* aParent,
                          G_CALLBACK(drag_data_received_event_cb), nullptr);
 
         GtkWidget *widgets[] = { GTK_WIDGET(mContainer),
-                                 !shellHasCSD ? mShell : nullptr };
+                                 !drawToContainer ? mShell : nullptr };
         for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
             // Visibility events are sent to the owning widget of the relevant
             // window but do not propagate to parent widgets so connect on
@@ -3973,7 +4119,6 @@ nsWindow::Create(nsIWidget* aParent,
         // Don't let GTK mess with the shapes of our GdkWindows
         GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK);
 #endif
-
         // These events are sent to the owning widget of the relevant window
         // and propagate up to the first widget that handles the events, so we
         // need only connect on mShell, if it exists, to catch events on its
@@ -4110,6 +4255,12 @@ nsWindow::NativeResize()
          size.width, size.height));
 
     if (mIsTopLevel) {
+        // When we draw decorations add extra space to draw shadows
+        // around the main window.
+        if (mDrawWindowDecoration) {
+            size.width += mDecorationSize.left + mDecorationSize.right;
+            size.height += mDecorationSize.top + mDecorationSize.bottom;
+        }
         gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
     }
     else if (mContainer) {
@@ -4166,6 +4317,11 @@ nsWindow::NativeMoveResize()
     if (mIsTopLevel) {
         // x and y give the position of the window manager frame top-left.
         gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
+
+        if (mDrawWindowDecoration) {
+            size.width += mDecorationSize.left + mDecorationSize.right;
+            size.height += mDecorationSize.top + mDecorationSize.bottom;
+        }
         // This sets the client window size.
         gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
     }
@@ -5524,6 +5680,33 @@ expose_event_cb(GtkWidget *widget, cairo
 
     return FALSE;
 }
+
+/* static */
+gboolean
+expose_event_decoration_draw_cb(GtkWidget *widget, cairo_t *cr)
+{
+  GdkWindow* gdkWindow = gtk_widget_get_window(widget);
+  if (gtk_cairo_should_draw_window(cr, gdkWindow)) {
+      RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
+      if (!window) {
+          NS_WARNING("Cannot get nsWindow from GtkWidget");
+      }
+      else if (window->IsClientDecorated()) {
+          cairo_save(cr);
+          gtk_cairo_transform_to_window(cr, widget, gdkWindow);
+
+          GdkRectangle rect = {0,0,0,0};
+          gtk_window_get_size(GTK_WINDOW(widget), &rect.width, &rect.height);
+          moz_gtk_window_decoration_paint(cr, &rect);
+
+          rect.height = 40;
+          moz_gtk_header_bar_paint(cr, &rect);
+          cairo_restore(cr);
+      }
+  }
+  return TRUE;
+}
+
 #endif //MOZ_WIDGET_GTK == 2
 
 static gboolean
@@ -6576,6 +6759,28 @@ nsWindow::ClearCachedResources()
     }
 }
 
+NS_IMETHODIMP
+nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins)
+{
+  SetDrawsInTitlebar(aMargins.top == 0);
+  return NS_OK;
+}
+
+void
+nsWindow::SetDrawsInTitlebar(bool aState)
+{
+  if (!mIsCSDEnabled || aState == mDrawWindowDecoration)
+    return;
+
+  if (mShell) {
+    gtk_window_set_decorated(GTK_WINDOW(mShell), !aState);
+    gtk_widget_set_app_paintable(mShell, aState);
+  }
+
+  mDrawWindowDecoration = aState;
+  UpdateClientDecorations();
+}
+
 gint
 nsWindow::GdkScaleFactor()
 {
@@ -6846,6 +7051,157 @@ nsWindow::SynthesizeNativeTouchPoint(uin
 }
 #endif
 
+bool
+nsWindow::IsClientDecorated() const
+{
+    return mDrawWindowDecoration && mSizeState == nsSizeMode_Normal;
+}
+
+int
+nsWindow::GetClientResizerSize()
+{
+  if (!mShell)
+    return 0;
+
+  // GTK uses a default size of 20px as of 3.20.
+  gint size = 20;
+  gtk_widget_style_get(mShell, "decoration-resize-handle", &size, nullptr);
+
+  return GdkCoordToDevicePixels(size);
+}
+
+nsWindow::CSDSupportLevel
+nsWindow::GetCSDSupportLevel() {
+    if (mCSDSupportLevel != CSD_SUPPORT_UNKNOWN) {
+        return mCSDSupportLevel;
+    }
+    // TODO: MATE
+    const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
+    if (currentDesktop) {
+        if (strcmp(currentDesktop, "GNOME") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_FULL;
+        } else if (strcmp(currentDesktop, "XFCE") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_FULL;
+        } else if (strcmp(currentDesktop, "X-Cinnamon") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_FULL;
+        } else if (strcmp(currentDesktop, "KDE") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_FLAT;
+        } else if (strcmp(currentDesktop, "LXDE") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_FLAT;
+        } else if (strcmp(currentDesktop, "openbox") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_FLAT;
+        } else if (strcmp(currentDesktop, "i3") == 0) {
+            mCSDSupportLevel = CSD_SUPPORT_NONE;
+        } else {
+            mCSDSupportLevel = CSD_SUPPORT_NONE;
+        }
+    }
+    return mCSDSupportLevel;
+}
+
+void
+nsWindow::UpdateClientDecorations()
+{
+  // When the CSD is not fully supported by window manager (ie. WM is not
+  // expecting that application is going to draw window shadows) we can't
+  // add shadows widths to the window margin. That would lead to completely
+  // opaque black border of the window.
+  if (!mDrawWindowDecoration || GetCSDSupportLevel() != CSD_SUPPORT_FULL)
+      return;
+
+  gint top = 0, right = 0, bottom = 0, left = 0;
+  if (mSizeState == nsSizeMode_Normal) {
+      moz_gtk_get_window_border(&top, &right, &bottom, &left);
+  }
+
+  static auto sGdkWindowSetShadowWidth =
+     (void (*)(GdkWindow*, gint, gint, gint, gint))
+     dlsym(RTLD_DEFAULT, "gdk_window_set_shadow_width");
+  sGdkWindowSetShadowWidth(gtk_widget_get_window(mShell),
+                           left, right, top, bottom);
+
+  mDecorationSize.left = left;
+  mDecorationSize.right = right;
+  mDecorationSize.top = top;
+  mDecorationSize.bottom = bottom;
+
+  // Gtk+ doesn't like when we set mContainer margin bigger than actual
+  // mContainer window size. That happens when we're called early and the
+  // mShell/mContainer is not allocated/resized yet and has default 1x1 size.
+  // Just resize to some minimal value which will be changed
+  // by Gecko soon.
+  GtkAllocation allocation;
+  gtk_widget_get_allocation(GTK_WIDGET(mContainer), &allocation);
+  if (allocation.width < left + right || allocation.height < top + bottom) {
+      gtk_widget_get_preferred_width(GTK_WIDGET(mContainer), nullptr,
+                                     &allocation.width);
+      gtk_widget_get_preferred_height(GTK_WIDGET(mContainer), nullptr,
+                                      &allocation.height);
+      allocation.width += left + right + 1;
+      allocation.height += top + bottom + 1;
+      gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
+  }
+
+  gtk_widget_set_margin_left(GTK_WIDGET(mContainer), mDecorationSize.left);
+  gtk_widget_set_margin_right(GTK_WIDGET(mContainer), mDecorationSize.right);
+  gtk_widget_set_margin_top(GTK_WIDGET(mContainer), mDecorationSize.top);
+  gtk_widget_set_margin_bottom(GTK_WIDGET(mContainer), mDecorationSize.bottom);
+}
+
+void
+nsWindow::ApplyCSDClipping()
+{
+  if (IsClientDecorated()) {
+      gint top, right, bottom, left;
+      moz_gtk_get_header_bar_border(&top, &right, &bottom, &left);
+      cairo_rectangle_int_t rect = { 0, top, mBounds.width, mBounds.height};
+      cairo_region_t *region = cairo_region_create_rectangle(&rect);
+      gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
+      cairo_region_destroy(region);
+  } else {
+      gdk_window_shape_combine_region(mGdkWindow, nullptr, 0, 0);
+  }
+}
+
+bool
+nsWindow::CheckResizerEdge(LayoutDeviceIntPoint aPoint, GdkWindowEdge& aOutEdge)
+{
+  gint scale = GdkScaleFactor();
+  gint left = scale * mDecorationSize.left;
+  gint top = scale * mDecorationSize.top;
+  gint right = scale * mDecorationSize.right;
+  gint bottom = scale * mDecorationSize.bottom;
+
+  int resizerSize = GetClientResizerSize();
+  int topDist = aPoint.y;
+  int leftDist = aPoint.x;
+  int rightDist = mBounds.width - aPoint.x;
+  int bottomDist = mBounds.height - aPoint.y;
+
+  //TODO -> wrong sizes
+
+  if (leftDist <= resizerSize && topDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_NORTH_WEST;
+  } else if (rightDist <= resizerSize && topDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_NORTH_EAST;
+  } else if (leftDist <= resizerSize && bottomDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_SOUTH_WEST;
+  } else if (rightDist <= resizerSize && bottomDist <= resizerSize) {
+    aOutEdge = GDK_WINDOW_EDGE_SOUTH_EAST;
+  } else if (topDist <= top) {
+    aOutEdge = GDK_WINDOW_EDGE_NORTH;
+  } else if (leftDist <= left) {
+    aOutEdge = GDK_WINDOW_EDGE_WEST;
+  } else if (rightDist <= right) {
+    aOutEdge = GDK_WINDOW_EDGE_EAST;
+  } else if (bottomDist <= bottom) {
+    aOutEdge = GDK_WINDOW_EDGE_SOUTH;
+  } else {
+    return false;
+  }
+  return true;
+}
+
 int32_t
 nsWindow::RoundsWidgetCoordinatesTo()
 {
diff -up firefox-57.0b8/widget/gtk/nsWindow.h.1399611 firefox-57.0b8/widget/gtk/nsWindow.h
--- firefox-57.0b8/widget/gtk/nsWindow.h.1399611	2017-09-15 06:15:40.000000000 +0200
+++ firefox-57.0b8/widget/gtk/nsWindow.h	2017-10-16 12:11:45.369240636 +0200
@@ -123,6 +123,7 @@ public:
                                          double aHeight,
                                          bool   aRepaint) override;
     virtual bool       IsEnabled() const override;
+    bool               IsComposited() const;
 
     void               SetZIndex(int32_t aZIndex) override;
     virtual void       SetSizeMode(nsSizeMode aMode) override;
@@ -351,6 +352,9 @@ public:
 #endif
     virtual void GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) override;
 
+    NS_IMETHOD SetNonClientMargins(LayoutDeviceIntMargin& aMargins) override;
+    void SetDrawsInTitlebar(bool aState) override;
+
     // HiDPI scale conversion
     gint GdkScaleFactor();
 
@@ -367,6 +371,9 @@ public:
     LayoutDeviceIntRect GdkRectToDevicePixels(GdkRectangle rect);
 
     virtual bool WidgetTypeSupportsAcceleration() override;
+
+    // Decorations
+    bool IsClientDecorated() const;
 protected:
     virtual ~nsWindow();
 
@@ -384,6 +391,16 @@ protected:
 
     virtual void RegisterTouchWindow() override;
 
+    int GetClientResizerSize();
+
+    // Informs the window manager about the size of the shadows surrounding
+    // a client-side decorated window.
+    void UpdateClientDecorations();
+
+    // Returns true if the given point (in device pixels) is within a resizer
+    // region of the window. Only used when drawing decorations client side.
+    bool CheckResizerEdge(LayoutDeviceIntPoint aPoint, GdkWindowEdge& aOutEdge);
+
     nsCOMPtr<nsIWidget> mParent;
     // Is this a toplevel window?
     bool                mIsTopLevel;
@@ -432,12 +449,12 @@ private:
                                    gint* aRootX, gint* aRootY);
     void               ClearCachedResources();
     nsIWidgetListener* GetListener();
-    bool               IsComposited() const;
-
+    void               ApplyCSDClipping();
 
     GtkWidget          *mShell;
     MozContainer       *mContainer;
     GdkWindow          *mGdkWindow;
+    bool                mIsCSDEnabled;
     PlatformCompositorWidgetDelegate* mCompositorWidgetDelegate;
 
 
@@ -536,6 +553,10 @@ private:
     // leaving fullscreen
     nsSizeMode         mLastSizeMode;
 
+    // If true, draw our own window decorations (where supported).
+    bool              mDrawWindowDecoration;
+    GtkBorder         mDecorationSize;
+
     static bool DragInProgress(void);
 
     void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent);
@@ -567,6 +588,17 @@ private:
     RefPtr<mozilla::widget::IMContextWrapper> mIMContext;
 
     mozilla::UniquePtr<mozilla::CurrentX11TimeGetter> mCurrentTimeGetter;
+    typedef enum { CSD_SUPPORT_FULL,    // CSD including shadows
+                   CSD_SUPPORT_FLAT,    // CSD without shadows
+                   CSD_SUPPORT_NONE,    // WM does not support CSD at all
+                   CSD_SUPPORT_UNKNOWN
+    } CSDSupportLevel;
+    /**
+     * Get the support of Client Side Decoration by checking
+     * the XDG_CURRENT_DESKTOP environment variable.
+     */
+    CSDSupportLevel GetCSDSupportLevel();
+    CSDSupportLevel mCSDSupportLevel;
 };
 
 #endif /* __nsWindow_h__ */
diff -up firefox-57.0b8/widget/gtk/WidgetStyleCache.cpp.1399611 firefox-57.0b8/widget/gtk/WidgetStyleCache.cpp
--- firefox-57.0b8/widget/gtk/WidgetStyleCache.cpp.1399611	2017-09-15 06:15:40.000000000 +0200
+++ firefox-57.0b8/widget/gtk/WidgetStyleCache.cpp	2017-10-16 12:11:45.369240636 +0200
@@ -26,10 +26,14 @@ static GtkStyleContext*
 GetCssNodeStyleInternal(WidgetNodeType aNodeType);
 
 static GtkWidget*
-CreateWindowWidget()
+CreateWindowWidget(WidgetNodeType type)
 {
   GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
   gtk_widget_set_name(widget, "MozillaGtkWidget");
+  if (type == MOZ_GTK_WINDOW_CSD) {
+      GtkStyleContext* style = gtk_widget_get_style_context(widget);
+      gtk_style_context_add_class(style, "csd");
+  }
   return widget;
 }
 
@@ -101,7 +105,7 @@ CreateTooltipWidget()
 {
   MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
              "CreateTooltipWidget should be used for Gtk < 3.20 only.");
-  GtkWidget* widget = CreateWindowWidget();
+  GtkWidget* widget = CreateWindowWidget(MOZ_GTK_WINDOW);
   GtkStyleContext* style = gtk_widget_get_style_context(widget);
   gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
   return widget;
@@ -529,11 +533,82 @@ CreateNotebookWidget()
 }
 
 static GtkWidget*
+CreateHeaderBar(bool aMaximized)
+{
+  MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
+             "GtkHeaderBar is only available on GTK 3.10+.");
+  if (gtk_check_version(3, 10, 0) != nullptr)
+    return nullptr;
+
+  static auto sGtkHeaderBarNewPtr = (GtkWidget* (*)())
+    dlsym(RTLD_DEFAULT, "gtk_header_bar_new");
+  static const char* MOZ_GTK_STYLE_CLASS_TITLEBAR = "titlebar";
+
+  GtkWidget* headerbar = sGtkHeaderBarNewPtr();
+  if (aMaximized) {
+    GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP);
+    gtk_widget_set_name(window, "MozillaMaximizedGtkWidget");
+    GtkStyleContext* style = gtk_widget_get_style_context(window);
+    gtk_style_context_add_class(style, "maximized");
+    GtkWidget *fixed = gtk_fixed_new();
+    gtk_container_add(GTK_CONTAINER(window), fixed);
+    gtk_container_add(GTK_CONTAINER(fixed), headerbar);
+    // Save the window container so we don't leak it.
+    sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED] = window;
+  } else {
+    AddToWindowContainer(headerbar);
+  }
+
+  // Emulate what create_titlebar() at gtkwindow.c does.
+  GtkStyleContext* style = gtk_widget_get_style_context(headerbar);
+  gtk_style_context_add_class(style, MOZ_GTK_STYLE_CLASS_TITLEBAR);
+  gtk_style_context_add_class(style, "default-decoration");
+
+  return headerbar;
+}
+
+// TODO - Also return style for buttons located at Maximized toolbar.
+static GtkWidget*
+CreateHeaderBarButton(WidgetNodeType aWidgetType)
+{
+  MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
+             "GtkHeaderBar is only available on GTK 3.10+.");
+
+  if (gtk_check_version(3, 10, 0) != nullptr)
+    return nullptr;
+
+  static const char* MOZ_GTK_STYLE_CLASS_TITLEBUTTON = "titlebutton";
+
+  GtkWidget* widget = gtk_button_new();
+  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_HEADER_BAR)), widget);
+
+  GtkStyleContext* style = gtk_widget_get_style_context(widget);
+  gtk_style_context_add_class(style, MOZ_GTK_STYLE_CLASS_TITLEBUTTON);
+
+  switch (aWidgetType) {
+    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
+      gtk_style_context_add_class(style, "close");
+      break;
+    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
+      gtk_style_context_add_class(style, "minimize");
+      break;
+    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+      gtk_style_context_add_class(style, "maximize");
+      break;
+    default:
+      break;
+  }
+
+  return widget;
+}
+
+static GtkWidget*
 CreateWidget(WidgetNodeType aWidgetType)
 {
   switch (aWidgetType) {
     case MOZ_GTK_WINDOW:
-      return CreateWindowWidget();
+    case MOZ_GTK_WINDOW_CSD:
+      return CreateWindowWidget(aWidgetType);
     case MOZ_GTK_WINDOW_CONTAINER:
       return CreateWindowContainerWidget();
     case MOZ_GTK_CHECKBUTTON_CONTAINER:
@@ -610,6 +685,13 @@ CreateWidget(WidgetNodeType aWidgetType)
       return CreateComboBoxEntryButtonWidget();
     case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
       return CreateComboBoxEntryArrowWidget();
+    case MOZ_GTK_HEADER_BAR:
+    case MOZ_GTK_HEADER_BAR_MAXIMIZED:
+      return CreateHeaderBar(aWidgetType == MOZ_GTK_HEADER_BAR_MAXIMIZED);
+    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
+    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
+      return CreateHeaderBarButton(aWidgetType);
     default:
       /* Not implemented */
       return nullptr;
@@ -1049,6 +1131,10 @@ GetCssNodeStyleInternal(WidgetNodeType a
       GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
       return gtk_widget_get_style_context(widget);
     }
+    case MOZ_GTK_WINDOW_DECORATION:
+      style = CreateChildCSSNode("decoration",
+                                 MOZ_GTK_WINDOW_CSD);
+      break;
     default:
       return GetWidgetRootStyle(aNodeType);
   }
@@ -1214,6 +1300,8 @@ ResetWidgetCache(void)
   /* This will destroy all of our widgets */
   if (sWidgetStorage[MOZ_GTK_WINDOW])
     gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
+  if (sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED])
+    gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED]);
 
   /* Clear already freed arrays */
   mozilla::PodArrayZero(sWidgetStorage);
diff -up firefox-57.0b8/widget/LookAndFeel.h.1399611 firefox-57.0b8/widget/LookAndFeel.h
--- firefox-57.0b8/widget/LookAndFeel.h.1399611	2017-09-16 18:22:54.000000000 +0200
+++ firefox-57.0b8/widget/LookAndFeel.h	2017-10-16 12:11:45.369240636 +0200
@@ -405,6 +405,30 @@ public:
      eIntID_PhysicalHomeButton,
 
      /*
+      * A boolean value indicating whether client-side decorations are
+      * supported by the user's GTK version.
+      */
+     eIntID_GTKCSDAvailable,
+
+     /*
+      * A boolean value indicating whether client-side decorations should
+      * contain a minimize button.
+      */
+     eIntID_GTKCSDMinimizeButton,
+
+     /*
+      * A boolean value indicating whether client-side decorations should
+      * contain a maximize button.
+      */
+     eIntID_GTKCSDMaximizeButton,
+
+     /*
+      * A boolean value indicating whether client-side decorations should
+      * contain a close button.
+      */
+     eIntID_GTKCSDCloseButton,
+
+     /*
       * Controls whether overlay scrollbars display when the user moves
       * the mouse in a scrollable frame.
       */