Use GTK frame button layout when available

BUG=21438
R=erg@chromium.org

Change-Id: I9c596a297a48ab7b4405fbe13b99ddede99c5d54
Reviewed-on: https://chromium-review.googlesource.com/629317
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Elliot Glaysher <erg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#497169}
diff --git a/chrome/browser/ui/libgtkui/BUILD.gn b/chrome/browser/ui/libgtkui/BUILD.gn
index d53109c..33b4f820 100644
--- a/chrome/browser/ui/libgtkui/BUILD.gn
+++ b/chrome/browser/ui/libgtkui/BUILD.gn
@@ -47,6 +47,7 @@
                 "libgtkui_export.h",
                 "menu_util.cc",
                 "menu_util.h",
+                "nav_button_layout_manager.h",
                 "print_dialog_gtk.cc",
                 "print_dialog_gtk.h",
                 "printing_gtk_util.cc",
@@ -68,8 +69,8 @@
 
     if (use_gconf) {
       sources += [
-        "gconf_listener.cc",
-        "gconf_listener.h",
+        "nav_button_layout_manager_gconf.cc",
+        "nav_button_layout_manager_gconf.h",
       ]
       configs += [ "//build/config/linux/gconf" ]
     }
@@ -127,6 +128,8 @@
     sources = [
       "native_theme_gtk3.cc",
       "native_theme_gtk3.h",
+      "nav_button_layout_manager_gtk3.cc",
+      "nav_button_layout_manager_gtk3.h",
       "nav_button_provider_gtk3.cc",
       "nav_button_provider_gtk3.h",
     ]
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index 668d049..f1c3a3d 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -68,14 +68,16 @@
 #include "chrome/browser/ui/libgtkui/native_theme_gtk2.h"  // nogncheck
 #elif GTK_MAJOR_VERSION == 3
 #include "chrome/browser/ui/libgtkui/native_theme_gtk3.h"  // nogncheck
+#include "chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.h"  // nogncheck
 #include "chrome/browser/ui/libgtkui/nav_button_provider_gtk3.h"  // nogncheck
 #endif
 
 #if BUILDFLAG(ENABLE_BASIC_PRINTING)
 #include "printing/printing_context_linux.h"
 #endif
+
 #if defined(USE_GCONF)
-#include "chrome/browser/ui/libgtkui/gconf_listener.h"
+#include "chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.h"
 #endif
 
 // A minimized port of GtkThemeService into something that can provide colors
@@ -90,6 +92,15 @@
 
 namespace {
 
+// We would like this to be a feature flag, but GtkUi gets initialized
+// earlier than the feature flag registry, so just use a simple bool.
+// The reason for wanting a flag is so that we can release the GTK3
+// nav button layout manager and the GTK3 nav button provider at the
+// same time (so users don't have to deal with things changing twice).
+// Since this was never really intended to be toggled by users, this
+// is fine for now.
+const bool kUseGtkNavButtonLayoutManager = false;
+
 const double kDefaultDPI = 96;
 
 class GtkButtonImageSource : public gfx::ImageSkiaSource {
@@ -268,6 +279,18 @@
 // The unknown content type.
 const char* kUnknownContentType = "application/octet-stream";
 
+std::unique_ptr<NavButtonLayoutManager> CreateNavButtonLayoutManager(
+    GtkUi* gtk_ui) {
+#if GTK_MAJOR_VERSION == 3
+  if (GtkVersionCheck(3, 10) && kUseGtkNavButtonLayoutManager)
+    return std::make_unique<NavButtonLayoutManagerGtk3>(gtk_ui);
+#endif
+#if defined(USE_GCONF)
+  return std::make_unique<NavButtonLayoutManagerGconf>(gtk_ui);
+#endif
+  return nullptr;
+}
+
 // Returns a gfx::FontRenderParams corresponding to GTK's configuration.
 gfx::FontRenderParams GetGtkFontRenderParams() {
   GtkSettings* gtk_settings = gtk_settings_get_default();
@@ -449,10 +472,8 @@
       &GetPdfPaperSizeDeviceUnitsGtk);
 #endif
 
-#if defined(USE_GCONF)
   // We must build this after GTK gets initialized.
-  gconf_listener_.reset(new GConfListener(this));
-#endif  // defined(USE_GCONF)
+  nav_button_layout_manager_ = CreateNavButtonLayoutManager(this);
 
   indicators_count = 0;
 
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.h b/chrome/browser/ui/libgtkui/gtk_ui.h
index 201414c..c3e7721 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.h
+++ b/chrome/browser/ui/libgtkui/gtk_ui.h
@@ -24,8 +24,8 @@
 
 namespace libgtkui {
 class Gtk2KeyBindingsHandler;
-class GConfListener;
 class DeviceScaleFactorObserver;
+class NavButtonLayoutManager;
 
 // Interface to GTK2 desktop features.
 //
@@ -37,7 +37,7 @@
   typedef base::Callback<ui::NativeTheme*(aura::Window* window)>
       NativeThemeGetter;
 
-  // Setters used by GConfListener:
+  // Setters used by NavButtonLayoutManager:
   void SetWindowButtonOrdering(
       const std::vector<views::FrameButton>& leading_buttons,
       const std::vector<views::FrameButton>& trailing_buttons);
@@ -171,11 +171,7 @@
   gfx::Font::Weight default_font_weight_ = gfx::Font::Weight::NORMAL;
   gfx::FontRenderParams default_font_render_params_;
 
-#if defined(USE_GCONF)
-  // Currently, the only source of window button configuration. This will
-  // change if we ever have to support XFCE's configuration system or KDE's.
-  std::unique_ptr<GConfListener> gconf_listener_;
-#endif  // defined(USE_GCONF)
+  std::unique_ptr<NavButtonLayoutManager> nav_button_layout_manager_;
 
   // If either of these vectors are non-empty, they represent the current
   // window button configuration.
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index a5224ea..09191e00 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -198,6 +198,34 @@
   g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, nullptr);
 }
 
+void ParseButtonLayout(const std::string& button_string,
+                       std::vector<views::FrameButton>* leading_buttons,
+                       std::vector<views::FrameButton>* trailing_buttons) {
+  leading_buttons->clear();
+  trailing_buttons->clear();
+  bool left_side = true;
+  base::StringTokenizer tokenizer(button_string, ":,");
+  tokenizer.set_options(base::StringTokenizer::RETURN_DELIMS);
+  while (tokenizer.GetNext()) {
+    if (tokenizer.token_is_delim()) {
+      if (*tokenizer.token_begin() == ':')
+        left_side = false;
+    } else {
+      base::StringPiece token = tokenizer.token_piece();
+      if (token == "minimize") {
+        (left_side ? leading_buttons : trailing_buttons)
+            ->push_back(views::FRAME_BUTTON_MINIMIZE);
+      } else if (token == "maximize") {
+        (left_side ? leading_buttons : trailing_buttons)
+            ->push_back(views::FRAME_BUTTON_MAXIMIZE);
+      } else if (token == "close") {
+        (left_side ? leading_buttons : trailing_buttons)
+            ->push_back(views::FRAME_BUTTON_CLOSE);
+      }
+    }
+  }
+}
+
 #if GTK_MAJOR_VERSION > 2
 void* GetGdkSharedLibrary() {
   std::string lib_name =
diff --git a/chrome/browser/ui/libgtkui/gtk_util.h b/chrome/browser/ui/libgtkui/gtk_util.h
index cc6725b..1b0bf63 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.h
+++ b/chrome/browser/ui/libgtkui/gtk_util.h
@@ -10,6 +10,7 @@
 
 #include "ui/base/glib/scoped_gobject.h"
 #include "ui/native_theme/native_theme.h"
+#include "ui/views/window/frame_buttons.h"
 
 namespace aura {
 class Window;
@@ -76,6 +77,14 @@
 // Clears the transient parent for |dialog|.
 void ClearAuraTransientParent(GtkWidget* dialog);
 
+// Parses |button_string| into |leading_buttons| and
+// |trailing_buttons|.  The string is of the format
+// "<button>*:<button*>", for example, "close:minimize:maximize".
+// This format is used by GTK3 settings and several gconf settings.
+void ParseButtonLayout(const std::string& button_string,
+                       std::vector<views::FrameButton>* leading_buttons,
+                       std::vector<views::FrameButton>* trailing_buttons);
+
 #if GTK_MAJOR_VERSION > 2
 // These constants are defined in gtk/gtkenums.h in Gtk3.12 or later.
 // They are added here as a convenience to avoid version checks, and
diff --git a/chrome/browser/ui/libgtkui/nav_button_layout_manager.h b/chrome/browser/ui/libgtkui/nav_button_layout_manager.h
new file mode 100644
index 0000000..018bba78
--- /dev/null
+++ b/chrome/browser/ui/libgtkui/nav_button_layout_manager.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_H_
+#define CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+namespace libgtkui {
+
+// This class is just a switch between NavButtonLayoutManagerGconf and
+// NavButtonLayoutManagerGtk3.  Currently, it is empty and it's only
+// purpose is so that GtkUi can store just a
+// std::unique_ptr<NavButtonLayoutManager> and not have to have the
+// two impls each guarded by their own macros.
+class NavButtonLayoutManager {
+ public:
+  virtual ~NavButtonLayoutManager() {}
+
+ protected:
+  // Even though this class is not pure virtual, it should not be
+  // instantiated directly.  Use NavButtonLayoutManagerGconf or
+  // NavButtonLayoutManagerGtk3 instead.
+  NavButtonLayoutManager() {}
+};
+
+}  // namespace libgtkui
+
+#endif  // CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_H_
diff --git a/chrome/browser/ui/libgtkui/gconf_listener.cc b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.cc
similarity index 71%
rename from chrome/browser/ui/libgtkui/gconf_listener.cc
rename to chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.cc
index 9beea250..946ff163 100644
--- a/chrome/browser/ui/libgtkui/gconf_listener.cc
+++ b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/libgtkui/gconf_listener.h"
+#include "chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.h"
 
 #include <gtk/gtk.h>
 
@@ -12,9 +12,8 @@
 #include "base/callback.h"
 #include "base/environment.h"
 #include "base/nix/xdg_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_tokenizer.h"
 #include "chrome/browser/ui/libgtkui/gtk_ui.h"
+#include "chrome/browser/ui/libgtkui/gtk_util.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/views/window/frame_buttons.h"
 
@@ -42,7 +41,7 @@
 
 // Public interface:
 
-GConfListener::GConfListener(GtkUi* delegate)
+NavButtonLayoutManagerGconf::NavButtonLayoutManagerGconf(GtkUi* delegate)
     : delegate_(delegate), client_(nullptr) {
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   base::nix::DesktopEnvironment de =
@@ -62,27 +61,28 @@
         return;
 
       // Get the initial value of the keys we're interested in.
-      GetAndRegister(kButtonLayoutKey,
-                     base::Bind(&GConfListener::ParseAndStoreButtonValue,
-                                base::Unretained(this)));
-      GetAndRegister(kMiddleClickActionKey,
-                     base::Bind(&GConfListener::ParseAndStoreMiddleClickValue,
-                                base::Unretained(this)));
+      GetAndRegister(
+          kButtonLayoutKey,
+          base::Bind(&NavButtonLayoutManagerGconf::ParseAndStoreButtonValue,
+                     base::Unretained(this)));
+      GetAndRegister(
+          kMiddleClickActionKey,
+          base::Bind(
+              &NavButtonLayoutManagerGconf::ParseAndStoreMiddleClickValue,
+              base::Unretained(this)));
     }
   }
 }
 
-GConfListener::~GConfListener() {
-}
+NavButtonLayoutManagerGconf::~NavButtonLayoutManagerGconf() {}
 
 // Private:
 
-void GConfListener::GetAndRegister(
+void NavButtonLayoutManagerGconf::GetAndRegister(
     const char* key_to_subscribe,
     const base::Callback<void(GConfValue*)>& initial_setter) {
   GError* error = nullptr;
-  GConfValue* gconf_value = gconf_client_get(client_, key_to_subscribe,
-                                             &error);
+  GConfValue* gconf_value = gconf_client_get(client_, key_to_subscribe, &error);
   if (HandleGError(error, key_to_subscribe))
     return;
   initial_setter.Run(gconf_value);
@@ -99,9 +99,9 @@
     return;
 }
 
-void GConfListener::OnChangeNotification(GConfClient* client,
-                                         guint cnxn_id,
-                                         GConfEntry* entry) {
+void NavButtonLayoutManagerGconf::OnChangeNotification(GConfClient* client,
+                                                       guint cnxn_id,
+                                                       GConfEntry* entry) {
   if (strcmp(gconf_entry_get_key(entry), kButtonLayoutKey) == 0) {
     GConfValue* gconf_value = gconf_entry_get_value(entry);
     ParseAndStoreButtonValue(gconf_value);
@@ -111,7 +111,7 @@
   }
 }
 
-bool GConfListener::HandleGError(GError* error, const char* key) {
+bool NavButtonLayoutManagerGconf::HandleGError(GError* error, const char* key) {
   if (error != nullptr) {
     LOG(ERROR) << "Error with gconf key '" << key << "': " << error->message;
     g_error_free(error);
@@ -122,7 +122,8 @@
   return false;
 }
 
-void GConfListener::ParseAndStoreButtonValue(GConfValue* gconf_value) {
+void NavButtonLayoutManagerGconf::ParseAndStoreButtonValue(
+    GConfValue* gconf_value) {
   std::string button_string;
   if (gconf_value) {
     const char* value = gconf_value_get_string(gconf_value);
@@ -131,35 +132,14 @@
     button_string = kDefaultButtonString;
   }
 
-  // Parse the button_layout string.
   std::vector<views::FrameButton> leading_buttons;
   std::vector<views::FrameButton> trailing_buttons;
-  bool left_side = true;
-  base::StringTokenizer tokenizer(button_string, ":,");
-  tokenizer.set_options(base::StringTokenizer::RETURN_DELIMS);
-  while (tokenizer.GetNext()) {
-    if (tokenizer.token_is_delim()) {
-      if (*tokenizer.token_begin() == ':')
-        left_side = false;
-    } else {
-      base::StringPiece token = tokenizer.token_piece();
-      if (token == "minimize") {
-        (left_side ? leading_buttons : trailing_buttons).push_back(
-            views::FRAME_BUTTON_MINIMIZE);
-      } else if (token == "maximize") {
-        (left_side ? leading_buttons : trailing_buttons).push_back(
-            views::FRAME_BUTTON_MAXIMIZE);
-      } else if (token == "close") {
-        (left_side ? leading_buttons : trailing_buttons).push_back(
-            views::FRAME_BUTTON_CLOSE);
-      }
-    }
-  }
-
+  ParseButtonLayout(button_string, &leading_buttons, &trailing_buttons);
   delegate_->SetWindowButtonOrdering(leading_buttons, trailing_buttons);
 }
 
-void GConfListener::ParseAndStoreMiddleClickValue(GConfValue* gconf_value) {
+void NavButtonLayoutManagerGconf::ParseAndStoreMiddleClickValue(
+    GConfValue* gconf_value) {
   GtkUi::NonClientMiddleClickAction action =
       views::LinuxUI::MIDDLE_CLICK_ACTION_LOWER;
   if (gconf_value) {
diff --git a/chrome/browser/ui/libgtkui/gconf_listener.h b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.h
similarity index 65%
rename from chrome/browser/ui/libgtkui/gconf_listener.h
rename to chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.h
index 75d33696..4d3b80b5 100644
--- a/chrome/browser/ui/libgtkui/gconf_listener.h
+++ b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gconf.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_LIBGTKUI_GCONF_LISTENER_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_GCONF_LISTENER_H_
+#ifndef CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_GCONF_H_
+#define CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_GCONF_H_
 
 #include <gconf/gconf-client.h>
 #include <gtk/gtk.h>
@@ -14,6 +14,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/libgtkui/gtk_signal.h"
+#include "chrome/browser/ui/libgtkui/nav_button_layout_manager.h"
 
 namespace libgtkui {
 class GtkUi;
@@ -21,16 +22,20 @@
 // On GNOME desktops, subscribes to the gconf key which controlls button order.
 // Everywhere else, SetTiltebarButtons() just calls back into BrowserTitlebar
 // with the default ordering.
-class GConfListener {
+class NavButtonLayoutManagerGconf : public NavButtonLayoutManager {
  public:
   // Sends data to the GtkUi when available.
-  explicit GConfListener(GtkUi* delegate);
-  ~GConfListener();
+  explicit NavButtonLayoutManagerGconf(GtkUi* delegate);
+  ~NavButtonLayoutManagerGconf() override;
 
  private:
   // Called whenever the metacity key changes.
-  CHROMEG_CALLBACK_2(GConfListener, void, OnChangeNotification,
-                     GConfClient*, guint, GConfEntry*);
+  CHROMEG_CALLBACK_2(NavButtonLayoutManagerGconf,
+                     void,
+                     OnChangeNotification,
+                     GConfClient*,
+                     guint,
+                     GConfEntry*);
 
   void GetAndRegister(const char* key_to_subscribe,
                       const base::Callback<void(GConfValue*)>& initial_setter);
@@ -50,9 +55,9 @@
   // gconf.
   GConfClient* client_;
 
-  DISALLOW_COPY_AND_ASSIGN(GConfListener);
+  DISALLOW_COPY_AND_ASSIGN(NavButtonLayoutManagerGconf);
 };
 
 }  // namespace libgtkui
 
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_GCONF_LISTENER_H_
+#endif  // CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_GCONF_H_
diff --git a/chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.cc b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.cc
new file mode 100644
index 0000000..5fdc45d3
--- /dev/null
+++ b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.cc
@@ -0,0 +1,93 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.h"
+
+#include "base/strings/string_split.h"
+#include "chrome/browser/ui/libgtkui/gtk_ui.h"
+#include "chrome/browser/ui/libgtkui/gtk_util.h"
+
+namespace libgtkui {
+
+namespace {
+
+const char kDefaultGtkLayout[] = "menu:minimize,maximize,close";
+
+std::string GetGtkSettingsStringProperty(GtkSettings* settings,
+                                         const gchar* prop_name) {
+  GValue layout = G_VALUE_INIT;
+  g_value_init(&layout, G_TYPE_STRING);
+  g_object_get_property(G_OBJECT(settings), prop_name, &layout);
+  DCHECK(G_VALUE_HOLDS_STRING(&layout));
+  std::string prop_value(g_value_get_string(&layout));
+  g_value_unset(&layout);
+  return prop_value;
+}
+
+std::string GetDecorationLayoutFromGtkWindow() {
+  static ScopedStyleContext context;
+  if (!context) {
+    context = GetStyleContextFromCss("");
+    gtk_style_context_add_class(context, "csd");
+  }
+
+  gchar* layout_c = nullptr;
+  gtk_style_context_get_style(context, "decoration-button-layout", &layout_c,
+                              nullptr);
+  DCHECK(layout_c);
+  std::string layout(layout_c);
+  g_free(layout_c);
+  return layout;
+}
+
+}  // namespace
+
+NavButtonLayoutManagerGtk3::NavButtonLayoutManagerGtk3(GtkUi* delegate)
+    : delegate_(delegate), signal_id_(0) {
+  DCHECK(delegate_);
+  GtkSettings* settings = gtk_settings_get_default();
+  if (GtkVersionCheck(3, 14)) {
+    signal_id_ = g_signal_connect(
+        settings, "notify::gtk-decoration-layout",
+        G_CALLBACK(OnDecorationButtonLayoutChangedThunk), this);
+    DCHECK(signal_id_);
+    OnDecorationButtonLayoutChanged(settings, nullptr);
+  } else if (GtkVersionCheck(3, 10, 3)) {
+    signal_id_ = g_signal_connect_after(settings, "notify::gtk-theme-name",
+                                        G_CALLBACK(OnThemeChangedThunk), this);
+    DCHECK(signal_id_);
+    OnThemeChanged(settings, nullptr);
+  } else {
+    // On versions older than 3.10.3, the layout was hardcoded.
+    SetWindowButtonOrderingFromGtkLayout(kDefaultGtkLayout);
+  }
+}
+
+NavButtonLayoutManagerGtk3::~NavButtonLayoutManagerGtk3() {
+  if (signal_id_)
+    g_signal_handler_disconnect(gtk_settings_get_default(), signal_id_);
+}
+
+void NavButtonLayoutManagerGtk3::SetWindowButtonOrderingFromGtkLayout(
+    const std::string& gtk_layout) {
+  std::vector<views::FrameButton> leading_buttons;
+  std::vector<views::FrameButton> trailing_buttons;
+  ParseButtonLayout(gtk_layout, &leading_buttons, &trailing_buttons);
+  delegate_->SetWindowButtonOrdering(leading_buttons, trailing_buttons);
+}
+
+void NavButtonLayoutManagerGtk3::OnDecorationButtonLayoutChanged(
+    GtkSettings* settings,
+    GParamSpec* param) {
+  SetWindowButtonOrderingFromGtkLayout(
+      GetGtkSettingsStringProperty(settings, "gtk-decoration-layout"));
+}
+
+void NavButtonLayoutManagerGtk3::OnThemeChanged(GtkSettings* settings,
+                                                GParamSpec* param) {
+  std::string layout = GetDecorationLayoutFromGtkWindow();
+  SetWindowButtonOrderingFromGtkLayout(layout);
+}
+
+}  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.h b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.h
new file mode 100644
index 0000000..938a084
--- /dev/null
+++ b/chrome/browser/ui/libgtkui/nav_button_layout_manager_gtk3.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_GTK3_H_
+#define CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_GTK3_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/libgtkui/nav_button_layout_manager.h"
+#include "ui/base/glib/glib_signal.h"
+
+typedef struct _GParamSpec GParamSpec;
+typedef struct _GtkSettings GtkSettings;
+
+namespace libgtkui {
+
+class GtkUi;
+
+class NavButtonLayoutManagerGtk3 : public NavButtonLayoutManager {
+ public:
+  explicit NavButtonLayoutManagerGtk3(GtkUi* delegate);
+  ~NavButtonLayoutManagerGtk3() override;
+
+ private:
+  void SetWindowButtonOrderingFromGtkLayout(const std::string& gtk_layout);
+
+  CHROMEG_CALLBACK_1(NavButtonLayoutManagerGtk3,
+                     void,
+                     OnDecorationButtonLayoutChanged,
+                     GtkSettings*,
+                     GParamSpec*);
+
+  CHROMEG_CALLBACK_1(NavButtonLayoutManagerGtk3,
+                     void,
+                     OnThemeChanged,
+                     GtkSettings*,
+                     GParamSpec*);
+
+  GtkUi* delegate_;
+
+  unsigned long signal_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavButtonLayoutManagerGtk3);
+};
+
+}  // namespace libgtkui
+
+#endif  // CHROME_BROWSER_UI_LIBGTKUI_NAV_BUTTON_LAYOUT_MANAGER_GTK3_H_