Don't regenerate browser frame on native theme changes.

BrowserView was calling BrowserFrame::FrameTypeChanged() whenever the
native theme changed. While Chrome theme changes can indeed cause frame
changes, native theme changes should largely be color-based and should
not require re-creation of the frame and re-composition of the entire
window. This was causing flicker, which was especially pronounced on
older machines and computers running Windows 8.x.

See attached bug for details.

TBR=dfried@chromium.org

(cherry picked from commit b153488cab7173359d93cc0c64af13125d857619)

Bug: 945138
Change-Id: I002a6535de507866b781643ce9afb81002c6946c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1538984
Commit-Queue: Dana Fried <dfried@chromium.org>
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#644544}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1544153
Reviewed-by: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/branch-heads/3729@{#511}
Cr-Branched-From: d4a8972e30b604f090aeda5dfff68386ae656267-refs/heads/master@{#638880}
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index b31b3d5..939a669 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1975,7 +1975,7 @@
   switch (type) {
 #if !defined(OS_ANDROID)
     case chrome::NOTIFICATION_BROWSER_THEME_CHANGED:
-      window()->UserChangedTheme();
+      window()->UserChangedTheme(BrowserThemeChangeType::kBrowserTheme);
       break;
 #endif
 
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index fb5ead3..3f42805 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -89,6 +89,8 @@
   EDITABLE_FIELD_IS_ACTIVE,
 };
 
+enum class BrowserThemeChangeType { kBrowserTheme, kNativeTheme };
+
 ////////////////////////////////////////////////////////////////////////////////
 // BrowserWindow interface
 //  An interface implemented by the "view" of the Browser window.
@@ -372,7 +374,7 @@
 
   // ThemeService calls this when a user has changed their theme, indicating
   // that it's time to redraw everything.
-  virtual void UserChangedTheme() = 0;
+  virtual void UserChangedTheme(BrowserThemeChangeType theme_change_type) = 0;
 
   // Shows the app menu (for accessibility).
   virtual void ShowAppMenu() = 0;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index cb3d4bde..5d0eb9a 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1496,8 +1496,33 @@
       GetNativeWindow(), download_count, dialog_type, app_modal, callback);
 }
 
-void BrowserView::UserChangedTheme() {
-  frame_->FrameTypeChanged();
+void BrowserView::UserChangedTheme(BrowserThemeChangeType theme_change_type) {
+  // When the native theme changes in a way that doesn't change the frame type
+  // required, we can skip a frame regeneration. Frame regeneration can cause
+  // visible flicker (see crbug/945138) so it's best avoided if all that has
+  // changed is, for example, the titlebar color, or the user has switched from
+  // light to dark mode.
+  const bool should_use_native_frame = frame_->ShouldUseNativeFrame();
+  bool must_regenerate_frame;
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  // GTK and user theme changes can both change frame buttons, so the frame
+  // always needs to be regenerated on Linux.
+  must_regenerate_frame = true;
+#else
+  must_regenerate_frame =
+      theme_change_type == BrowserThemeChangeType::kBrowserTheme ||
+      using_native_frame_ != should_use_native_frame;
+#endif
+  if (must_regenerate_frame) {
+    // This is a heavyweight theme change that requires regenerating the frame
+    // as well as repainting the browser window.
+    frame_->FrameTypeChanged();
+  } else {
+    // This is a lightweight theme change, so just refresh the theme on all
+    // views in the browser window.
+    GetWidget()->ThemeChanged();
+  }
+  using_native_frame_ = should_use_native_frame;
 }
 
 void BrowserView::ShowAppMenu() {
@@ -1920,7 +1945,7 @@
     return;
   // Don't infinitely recurse.
   if (!handling_theme_changed_)
-    UserChangedTheme();
+    UserChangedTheme(BrowserThemeChangeType::kNativeTheme);
   MaybeShowInvertBubbleView(this);
 }
 
@@ -2576,6 +2601,7 @@
 
   frame_->OnBrowserViewInitViewsComplete();
   frame_->GetFrameView()->UpdateMinimumSize();
+  using_native_frame_ = frame_->ShouldUseNativeFrame();
 }
 
 void BrowserView::LoadingAnimationCallback() {
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 55464d7..417ba26 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -398,7 +398,7 @@
       Browser::DownloadClosePreventionType dialog_type,
       bool app_modal,
       const base::Callback<void(bool)>& callback) override;
-  void UserChangedTheme() override;
+  void UserChangedTheme(BrowserThemeChangeType theme_change_type) override;
   void ShowAppMenu() override;
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
@@ -789,6 +789,9 @@
   // OnThemeChanged()).
   bool handling_theme_changed_ = false;
 
+  // True if (as of the last time it was checked) the frame type is native.
+  bool using_native_frame_ = true;
+
   // True when in ProcessFullscreen(). The flag is used to avoid reentrance and
   // to ignore requests to layout while in ProcessFullscreen() to reduce
   // jankiness.
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index d6ac6e9..72d532d 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -147,7 +147,7 @@
       Browser::DownloadClosePreventionType dialog_type,
       bool app_modal,
       const base::Callback<void(bool)>& callback) override {}
-  void UserChangedTheme() override {}
+  void UserChangedTheme(BrowserThemeChangeType theme_change_type) override {}
   void CutCopyPaste(int command_id) override {}
   FindBar* CreateFindBar() override;
   web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()