Blob Blame History Raw
diff -up chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.cc.gtk2fix chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.cc
--- chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.cc.gtk2fix	2017-08-03 10:26:36.816787227 -0400
+++ chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.cc	2017-08-03 10:56:53.345661368 -0400
@@ -60,6 +60,7 @@
 #include "ui/views/controls/button/blue_button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/label_button_border.h"
+#include "ui/views/linux_ui/device_scale_factor_observer.h"
 #include "ui/views/linux_ui/window_button_order_observer.h"
 #include "ui/views/resources/grit/views_resources.h"
 
@@ -315,17 +316,6 @@ gfx::FontRenderParams GetGtkFontRenderPa
   return params;
 }
 
-float GetRawDeviceScaleFactor() {
-  if (display::Display::HasForceDeviceScaleFactor())
-    return display::Display::GetForcedDeviceScaleFactor();
-
-  GdkScreen* screen = gdk_screen_get_default();
-  gint scale = gdk_screen_get_monitor_scale_factor(
-      screen, gdk_screen_get_primary_monitor(screen));
-  gdouble resolution = gdk_screen_get_resolution(screen);
-  return resolution <= 0 ? scale : resolution * scale / kDefaultDPI;
-}
-
 views::LinuxUI::NonClientMiddleClickAction GetDefaultMiddleClickAction() {
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   switch (base::nix::GetDesktopEnvironment(env.get())) {
@@ -414,19 +404,17 @@ GtkUi::GtkUi() : middle_click_action_(Ge
 #if GTK_MAJOR_VERSION == 2
   native_theme_ = NativeThemeGtk2::instance();
   fake_window_ = chrome_gtk_frame_new();
-  gtk_widget_realize(fake_window_);  // Is this necessary?
 #elif GTK_MAJOR_VERSION == 3
   native_theme_ = NativeThemeGtk3::instance();
-  (void)fake_window_;  // Silence the unused warning.
+  fake_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 #else
 #error "Unsupported GTK version"
 #endif
+  gtk_widget_realize(fake_window_);
 }
 
 GtkUi::~GtkUi() {
-#if GTK_MAJOR_VERSION == 2
   gtk_widget_destroy(fake_window_);
-#endif
 }
 
 void OnThemeChanged(GObject* obj, GParamSpec* param, GtkUi* gtkui) {
@@ -440,6 +428,17 @@ void GtkUi::Initialize() {
   g_signal_connect_after(settings, "notify::gtk-icon-theme-name",
                          G_CALLBACK(OnThemeChanged), this);
 
+  GdkScreen* screen = gdk_screen_get_default();
+  // Listen for DPI changes.
+  g_signal_connect_after(screen, "notify::resolution",
+                         G_CALLBACK(OnDeviceScaleFactorMaybeChangedThunk),
+                         this);
+  // Listen for scale factor changes.  We would prefer to listen on
+  // |screen|, but there is no scale-factor property, so use an
+  // unmapped window instead.
+  g_signal_connect(fake_window_, "notify::scale-factor",
+                   G_CALLBACK(OnDeviceScaleFactorMaybeChangedThunk), this);
+
   LoadGtkValues();
 
 #if BUILDFLAG(ENABLE_BASIC_PRINTING)
@@ -701,12 +700,12 @@ void GtkUi::AddWindowButtonOrderObserver
     observer->OnWindowButtonOrderingChange(leading_buttons_, trailing_buttons_);
   }
 
-  observer_list_.AddObserver(observer);
+  window_button_order_observer_list_.AddObserver(observer);
 }
 
 void GtkUi::RemoveWindowButtonOrderObserver(
     views::WindowButtonOrderObserver* observer) {
-  observer_list_.RemoveObserver(observer);
+  window_button_order_observer_list_.RemoveObserver(observer);
 }
 
 void GtkUi::SetWindowButtonOrdering(
@@ -715,8 +714,10 @@ void GtkUi::SetWindowButtonOrdering(
   leading_buttons_ = leading_buttons;
   trailing_buttons_ = trailing_buttons;
 
-  for (views::WindowButtonOrderObserver& observer : observer_list_)
+  for (views::WindowButtonOrderObserver& observer :
+       window_button_order_observer_list_) {
     observer.OnWindowButtonOrderingChange(leading_buttons_, trailing_buttons_);
+  }
 }
 
 void GtkUi::SetNonClientMiddleClickAction(NonClientMiddleClickAction action) {
@@ -768,6 +769,16 @@ void GtkUi::NotifyWindowManagerStartupCo
   gdk_notify_startup_complete();
 }
 
+void GtkUi::AddDeviceScaleFactorObserver(
+    views::DeviceScaleFactorObserver* observer) {
+  device_scale_factor_observer_list_.AddObserver(observer);
+}
+
+void GtkUi::RemoveDeviceScaleFactorObserver(
+    views::DeviceScaleFactorObserver* observer) {
+  device_scale_factor_observer_list_.RemoveObserver(observer);
+}
+
 bool GtkUi::MatchEvent(const ui::Event& event,
                        std::vector<ui::TextEditCommandAuraLinux>* commands) {
   // Ensure that we have a keyboard handler.
@@ -777,6 +788,10 @@ bool GtkUi::MatchEvent(const ui::Event&
   return key_bindings_handler_->MatchEvent(event, commands);
 }
 
+void GtkUi::OnDeviceScaleFactorMaybeChanged(void*, GParamSpec*) {
+  UpdateDeviceScaleFactor();
+}
+
 void GtkUi::SetScrollbarColors() {
   thumb_active_color_ = SkColorSetRGB(244, 244, 244);
   thumb_inactive_color_ = SkColorSetRGB(234, 234, 234);
@@ -1037,14 +1052,38 @@ void GtkUi::ResetStyle() {
   native_theme_->NotifyObservers();
 }
 
-void GtkUi::UpdateDeviceScaleFactor() {
-  // Note: Linux chrome currently does not support dynamic DPI
-  // changes.  This is to allow flags to override the DPI settings
-  // during startup.
-  float scale = GetRawDeviceScaleFactor();
+float GtkUi::GetRawDeviceScaleFactor() {
+  if (display::Display::HasForceDeviceScaleFactor())
+    return display::Display::GetForcedDeviceScaleFactor();
+
+#if GTK_MAJOR_VERSION == 2
+  GtkSettings* gtk_settings = gtk_settings_get_default();
+  gint gtk_dpi = -1;
+  g_object_get(gtk_settings, "gtk-xft-dpi", &gtk_dpi, nullptr);
+  const float scale_factor = gtk_dpi / (1024 * kDefaultDPI);
+#else
+  GdkScreen* screen = gdk_screen_get_default();
+  gint scale = gtk_widget_get_scale_factor(fake_window_);
+  DCHECK_GT(scale, 0);
+  gdouble resolution = gdk_screen_get_resolution(screen);
+  const float scale_factor =
+      resolution <= 0 ? scale : resolution * scale / kDefaultDPI;
+#endif
+
   // Blacklist scaling factors <120% (crbug.com/484400) and round
   // to 1 decimal to prevent rendering problems (crbug.com/485183).
-  device_scale_factor_ = scale < 1.2f ? 1.0f : roundf(scale * 10) / 10;
+  return scale_factor < 1.2f ? 1.0f : roundf(scale_factor * 10) / 10;
+}
+
+void GtkUi::UpdateDeviceScaleFactor() {
+  float old_device_scale_factor = device_scale_factor_;
+  device_scale_factor_ = GetRawDeviceScaleFactor();
+  if (device_scale_factor_ != old_device_scale_factor) {
+    for (views::DeviceScaleFactorObserver& observer :
+         device_scale_factor_observer_list_) {
+      observer.OnDeviceScaleFactorChanged();
+    }
+  }
   UpdateDefaultFont();
 }
 
diff -up chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.h.gtk2fix chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.h
--- chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.h.gtk2fix	2017-08-03 10:35:28.031517634 -0400
+++ chromium-60.0.3112.78/chrome/browser/ui/libgtkui/gtk_ui.h	2017-08-03 10:37:29.300173415 -0400
@@ -18,12 +18,14 @@
 #include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/window/frame_buttons.h"
 
+typedef struct _GParamSpec GParamSpec;
 typedef struct _GtkStyle GtkStyle;
 typedef struct _GtkWidget GtkWidget;
 
 namespace libgtkui {
 class Gtk2KeyBindingsHandler;
 class GConfListener;
+class DeviceScaleFactorObserver;
 
 // Interface to GTK2 desktop features.
 //
@@ -97,6 +99,10 @@ class GtkUi : public views::LinuxUI {
   bool UnityIsRunning() override;
   NonClientMiddleClickAction GetNonClientMiddleClickAction() override;
   void NotifyWindowManagerStartupComplete() override;
+  void AddDeviceScaleFactorObserver(
+      views::DeviceScaleFactorObserver* observer) override;
+  void RemoveDeviceScaleFactorObserver(
+      views::DeviceScaleFactorObserver* observer) override;
 
   // ui::TextEditKeybindingDelegate:
   bool MatchEvent(const ui::Event& event,
@@ -110,6 +116,12 @@ class GtkUi : public views::LinuxUI {
   typedef std::map<int, SkColor> ColorMap;
   typedef std::map<int, color_utils::HSL> TintMap;
 
+  CHROMEG_CALLBACK_1(GtkUi,
+                     void,
+                     OnDeviceScaleFactorMaybeChanged,
+                     void*,
+                     GParamSpec*);
+
   // This method returns the colors webkit will use for the scrollbars. When no
   // colors are specified by the GTK+ theme, this function averages of the
   // thumb part and of the track colors.
@@ -129,9 +141,12 @@ class GtkUi : public views::LinuxUI {
   bool GetChromeStyleColor(const char* sytle_property,
                            SkColor* ret_color) const;
 
+  float GetRawDeviceScaleFactor();
+
   ui::NativeTheme* native_theme_;
 
-  // A GtkWindow object with the class "ChromeGtkFrame".
+  // On Gtk2, A GtkWindow object with the class "ChromeGtkFrame".  On
+  // Gtk3, a regular GtkWindow.
   GtkWidget* fake_window_;
 
   // Colors calculated by LoadGtkValues() that are given to the
@@ -171,7 +186,12 @@ class GtkUi : public views::LinuxUI {
   std::unique_ptr<Gtk2KeyBindingsHandler> key_bindings_handler_;
 
   // Objects to notify when the window frame button order changes.
-  base::ObserverList<views::WindowButtonOrderObserver> observer_list_;
+  base::ObserverList<views::WindowButtonOrderObserver>
+      window_button_order_observer_list_;
+
+  // Objects to notify when the device scale factor changes.
+  base::ObserverList<views::DeviceScaleFactorObserver>
+      device_scale_factor_observer_list_;
 
   // Whether we should lower the window on a middle click to the non client
   // area.
diff -up chromium-60.0.3112.78/ui/views/linux_ui/device_scale_factor_observer.h.gtk2fix chromium-60.0.3112.78/ui/views/linux_ui/device_scale_factor_observer.h
--- chromium-60.0.3112.78/ui/views/linux_ui/device_scale_factor_observer.h.gtk2fix	2017-08-03 10:39:23.600963296 -0400
+++ chromium-60.0.3112.78/ui/views/linux_ui/device_scale_factor_observer.h	2017-08-03 10:39:39.024664885 -0400
@@ -0,0 +1,19 @@
+// 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 UI_VIEWS_LINUX_UI_DEVICE_SCALE_FACTOR_OBSERVER_H_
+#define UI_VIEWS_LINUX_UI_DEVICE_SCALE_FACTOR_OBSERVER_H_
+
+namespace views {
+
+class DeviceScaleFactorObserver {
+ public:
+  virtual ~DeviceScaleFactorObserver() {}
+
+  virtual void OnDeviceScaleFactorChanged() = 0;
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_LINUX_UI_DEVICE_SCALE_FACTOR_OBSERVER_H_
diff -up chromium-60.0.3112.78/ui/views/linux_ui/linux_ui.h.gtk2fix chromium-60.0.3112.78/ui/views/linux_ui/linux_ui.h
--- chromium-60.0.3112.78/ui/views/linux_ui/linux_ui.h.gtk2fix	2017-08-03 10:39:48.866474799 -0400
+++ chromium-60.0.3112.78/ui/views/linux_ui/linux_ui.h	2017-08-03 10:41:05.598991893 -0400
@@ -38,6 +38,7 @@ class NativeTheme;
 
 namespace views {
 class Border;
+class DeviceScaleFactorObserver;
 class LabelButton;
 class LabelButtonBorder;
 class WindowButtonOrderObserver;
@@ -157,6 +158,16 @@ class VIEWS_EXPORT LinuxUI : public ui::
 
   // Determines the device scale factor of the primary screen.
   virtual float GetDeviceScaleFactor() const = 0;
+
+  // Registers |observer| to be notified about changes to the device
+  // scale factor.
+  virtual void AddDeviceScaleFactorObserver(
+      DeviceScaleFactorObserver* observer) = 0;
+
+  // Unregisters |observer| from receiving changes to the device scale
+  // factor.
+  virtual void RemoveDeviceScaleFactorObserver(
+      DeviceScaleFactorObserver* observer) = 0;
 };
 
 }  // namespace views
diff -up chromium-60.0.3112.78/ui/views/test/desktop_screen_x11_test_api.cc.gtk2fix chromium-60.0.3112.78/ui/views/test/desktop_screen_x11_test_api.cc
--- chromium-60.0.3112.78/ui/views/test/desktop_screen_x11_test_api.cc.gtk2fix	2017-08-03 10:41:28.014558483 -0400
+++ chromium-60.0.3112.78/ui/views/test/desktop_screen_x11_test_api.cc	2017-08-03 10:41:49.326146074 -0400
@@ -13,7 +13,7 @@ namespace test {
 void DesktopScreenX11TestApi::UpdateDisplays() {
   DesktopScreenX11* screen =
       static_cast<DesktopScreenX11*>(display::Screen::GetScreen());
-  screen->ConfigureTimerFired();
+  screen->UpdateDisplays();
 }
 
 }  // namespace test
diff -up chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.cc.gtk2fix chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.cc
--- chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.cc.gtk2fix	2017-08-03 10:41:59.219954866 -0400
+++ chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.cc	2017-08-03 10:47:16.589818323 -0400
@@ -11,6 +11,7 @@
 #undef RootWindow
 
 #include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -40,10 +41,6 @@ const char* const kAtomsToCache[] = {
   nullptr
 };
 
-// The delay to perform configuration after RRNotify.  See the comment
-// in |Dispatch()|.
-const int64_t kConfigureDelayMs = 500;
-
 double GetDeviceScaleFactor() {
   float device_scale_factor = 1.0f;
   if (views::LinuxUI::instance()) {
@@ -95,7 +92,10 @@ DesktopScreenX11::DesktopScreenX11()
       has_xrandr_(false),
       xrandr_event_base_(0),
       primary_display_index_(0),
+      weak_factory_(this),
       atom_cache_(xdisplay_, kAtomsToCache) {
+  if (views::LinuxUI::instance())
+    views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this);
   // We only support 1.3+. There were library changes before this and we should
   // use the new interface instead of the 1.2 one.
   int randr_version_major = 0;
@@ -124,6 +124,8 @@ DesktopScreenX11::DesktopScreenX11()
 }
 
 DesktopScreenX11::~DesktopScreenX11() {
+  if (views::LinuxUI::instance())
+    views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this);
   if (has_xrandr_ && ui::PlatformEventSource::GetInstance())
     ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
 }
@@ -251,18 +253,7 @@ uint32_t DesktopScreenX11::DispatchEvent
   } else if (event->type - xrandr_event_base_ == RRNotify ||
              (event->type == PropertyNotify &&
               event->xproperty.atom == atom_cache_.GetAtom("_NET_WORKAREA"))) {
-    // There's some sort of observer dispatch going on here, but I don't think
-    // it's the screen's?
-    if (configure_timer_.get() && configure_timer_->IsRunning()) {
-      configure_timer_->Reset();
-    } else {
-      configure_timer_.reset(new base::OneShotTimer());
-      configure_timer_->Start(
-          FROM_HERE,
-          base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
-          this,
-          &DesktopScreenX11::ConfigureTimerFired);
-    }
+    RestartDelayedConfigurationTask();
   } else {
     NOTREACHED();
   }
@@ -270,11 +261,15 @@ uint32_t DesktopScreenX11::DispatchEvent
   return ui::POST_DISPATCH_NONE;
 }
 
+void DesktopScreenX11::OnDeviceScaleFactorChanged() {
+  RestartDelayedConfigurationTask();
+}
+
 // static
 void DesktopScreenX11::UpdateDeviceScaleFactorForTest() {
   DesktopScreenX11* screen =
       static_cast<DesktopScreenX11*>(display::Screen::GetScreen());
-  screen->ConfigureTimerFired();
+  screen->UpdateDisplays();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -288,7 +283,11 @@ DesktopScreenX11::DesktopScreenX11(
       xrandr_event_base_(0),
       displays_(test_displays),
       primary_display_index_(0),
-      atom_cache_(xdisplay_, kAtomsToCache) {}
+      weak_factory_(this),
+      atom_cache_(xdisplay_, kAtomsToCache) {
+  if (views::LinuxUI::instance())
+    views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this);
+}
 
 std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
   std::vector<display::Display> displays;
@@ -390,7 +389,14 @@ std::vector<display::Display> DesktopScr
   return displays;
 }
 
-void DesktopScreenX11::ConfigureTimerFired() {
+void DesktopScreenX11::RestartDelayedConfigurationTask() {
+  delayed_configuration_task_.Reset(base::Bind(
+      &DesktopScreenX11::UpdateDisplays, weak_factory_.GetWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, delayed_configuration_task_.callback());
+}
+
+void DesktopScreenX11::UpdateDisplays() {
   std::vector<display::Display> old_displays = displays_;
   SetDisplaysInternal(BuildDisplaysFromXRandRInfo());
   change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
diff -up chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.h.gtk2fix chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.h
--- chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.h.gtk2fix	2017-08-03 10:47:28.734583288 -0400
+++ chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_screen_x11.h	2017-08-03 10:49:54.850756813 -0400
@@ -9,11 +9,12 @@
 
 #include <memory>
 
+#include "base/cancelable_callback.h"
 #include "base/macros.h"
-#include "base/timer/timer.h"
 #include "ui/display/display_change_notifier.h"
 #include "ui/display/screen.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/views/linux_ui/device_scale_factor_observer.h"
 #include "ui/gfx/x/x11_atom_cache.h"
 #include "ui/views/views_export.h"
 
@@ -30,7 +31,8 @@ class DesktopScreenX11TestApi;
 
 // Our singleton screen implementation that talks to xrandr.
 class VIEWS_EXPORT DesktopScreenX11 : public display::Screen,
-                                      public ui::PlatformEventDispatcher {
+                                      public ui::PlatformEventDispatcher,
+                                      public views::DeviceScaleFactorObserver {
  public:
   DesktopScreenX11();
 
@@ -56,6 +58,9 @@ class VIEWS_EXPORT DesktopScreenX11 : pu
   bool CanDispatchEvent(const ui::PlatformEvent& event) override;
   uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
 
+  // views::DeviceScaleFactorObserver:
+  void OnDeviceScaleFactorChanged() override;
+
   static void UpdateDeviceScaleFactorForTest();
 
  private:
@@ -69,10 +74,15 @@ class VIEWS_EXPORT DesktopScreenX11 : pu
   // the X server.
   std::vector<display::Display> BuildDisplaysFromXRandRInfo();
 
-  // We delay updating the display so we can coalesce events.
-  void ConfigureTimerFired();
+  // Removes |delayed_configuration_task_| from the task queue (if
+  // it's in the queue) and adds it back at the end of the queue.
+  void RestartDelayedConfigurationTask();
+
+  // Updates |displays_| with the latest XRandR info.
+  void UpdateDisplays();
 
-  // Updates |displays_| and sets FontRenderParams's scale factor.
+  // Updates |displays_| from |displays| and sets FontRenderParams's scale
+  // factor.
   void SetDisplaysInternal(const std::vector<display::Display>& displays);
 
   Display* xdisplay_;
@@ -91,12 +101,14 @@ class VIEWS_EXPORT DesktopScreenX11 : pu
   // The index into displays_ that represents the primary display.
   size_t primary_display_index_;
 
-  // The timer to delay configuring outputs. See also the comments in
-  // Dispatch().
-  std::unique_ptr<base::OneShotTimer> configure_timer_;
+  // The task to delay configuring outputs.  We delay updating the
+  // display so we can coalesce events.
+  base::CancelableCallback<void()> delayed_configuration_task_;
 
   display::DisplayChangeNotifier change_notifier_;
 
+  base::WeakPtrFactory<DesktopScreenX11> weak_factory_;
+
   ui::X11AtomCache atom_cache_;
 
   DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11);
diff -up chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc.gtk2fix chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
--- chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc.gtk2fix	2017-08-03 10:50:14.858370042 -0400
+++ chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc	2017-08-03 10:52:58.367207187 -0400
@@ -212,13 +212,16 @@ DesktopWindowTreeHostX11::DesktopWindowT
       has_pointer_focus_(false),
       modal_dialog_counter_(0),
       close_widget_factory_(this),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  display::Screen::GetScreen()->AddObserver(this);
+}
 
 DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() {
   window()->ClearProperty(kHostForRootWindow);
   aura::client::SetWindowMoveClient(window(), NULL);
   desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this);
   DestroyDispatcher();
+  display::Screen::GetScreen()->RemoveObserver(this);
 }
 
 // static
@@ -1360,6 +1363,30 @@ void DesktopWindowTreeHostX11::OnCursorV
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// DesktopWindowTreeHostX11, display::DisplayObserver implementation:
+
+void DesktopWindowTreeHostX11::OnDisplayAdded(
+    const display::Display& new_display) {}
+
+void DesktopWindowTreeHostX11::OnDisplayRemoved(
+    const display::Display& old_display) {}
+
+void DesktopWindowTreeHostX11::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  if ((changed_metrics & DISPLAY_METRIC_DEVICE_SCALE_FACTOR) &&
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window()).id() ==
+          display.id()) {
+    // When the scale factor changes, also pretend that a resize
+    // occured so that the window layout will be refreshed and a
+    // compositor redraw will be scheduled.  This is weird, but works.
+    // TODO(thomasanderson): Figure out a more direct way of doing
+    // this.
+    RestartDelayedResizeTask();
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostX11, private:
 
 void DesktopWindowTreeHostX11::InitX11Window(
@@ -2079,13 +2106,8 @@ uint32_t DesktopWindowTreeHostX11::Dispa
       if (origin_changed)
         OnHostMovedInPixels(bounds_in_pixels_.origin());
 
-      if (size_changed) {
-        delayed_resize_task_.Reset(base::Bind(
-            &DesktopWindowTreeHostX11::DelayedResize,
-            close_widget_factory_.GetWeakPtr(), bounds_in_pixels.size()));
-        base::ThreadTaskRunnerHandle::Get()->PostTask(
-            FROM_HERE, delayed_resize_task_.callback());
-      }
+      if (size_changed)
+        RestartDelayedResizeTask();
       break;
     }
     case GenericEvent: {
@@ -2358,6 +2380,14 @@ void DesktopWindowTreeHostX11::EnableEve
     targeter_for_modal_.reset();
 }
 
+void DesktopWindowTreeHostX11::RestartDelayedResizeTask() {
+  delayed_resize_task_.Reset(
+      base::Bind(&DesktopWindowTreeHostX11::DelayedResize,
+                 close_widget_factory_.GetWeakPtr(), bounds_in_pixels_.size()));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, delayed_resize_task_.callback());
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHost, public:
 
diff -up chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h.gtk2fix chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
--- chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h.gtk2fix	2017-08-03 10:54:34.484348046 -0400
+++ chromium-60.0.3112.78/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h	2017-08-03 10:54:43.564172318 -0400
@@ -18,6 +18,7 @@
 #include "ui/aura/scoped_window_targeter.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor_loader_x11.h"
+#include "ui/display/display_observer.h"
 #include "ui/events/platform/platform_event_dispatcher.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
@@ -45,7 +46,8 @@ class X11DesktopWindowMoveClient;
 class VIEWS_EXPORT DesktopWindowTreeHostX11
     : public DesktopWindowTreeHost,
       public aura::WindowTreeHost,
-      public ui::PlatformEventDispatcher {
+      public ui::PlatformEventDispatcher,
+      public display::DisplayObserver {
  public:
   DesktopWindowTreeHostX11(
       internal::NativeWidgetDelegate* native_widget_delegate,
@@ -170,6 +172,12 @@ class VIEWS_EXPORT DesktopWindowTreeHost
       const gfx::Point& location_in_pixels) override;
   void OnCursorVisibilityChangedNative(bool show) override;
 
+  // Overridden from display::DisplayObserver:
+  void OnDisplayAdded(const display::Display& new_display) override;
+  void OnDisplayRemoved(const display::Display& old_display) override;
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t changed_metrics) override;
+
  private:
   friend class DesktopWindowTreeHostX11HighDPITest;
   // Initializes our X11 surface to draw on. This method performs all
@@ -278,6 +286,10 @@ class VIEWS_EXPORT DesktopWindowTreeHost
   // Enables event listening after closing |dialog|.
   void EnableEventListening();
 
+  // Removes |delayed_resize_task_| from the task queue (if it's in
+  // the queue) and adds it back at the end of the queue.
+  void RestartDelayedResizeTask();
+
   // X11 things
   // The display and the native X window hosting the root window.
   XDisplay* xdisplay_;