chromeos: Update browser window placement for out-of-process ash

WindowSizerAsh needs synchronous access to the target display for each
new browser window.

* Create ash::ShellState and move root_window_for_new_windows_ there
* Introduce mojom::ShellState interface in ash
* Create a ShellStateClient in chrome to cache the target display id
  for new windows

The interface is not implemented by ash::Shell because ash::Shell
is already massive (1500 lines). Also, shell.h is included in 900
places in the code base and I don't want to add mojo generated headers
to shell.h.

Windows still don't appear on the secondary display with
--enable-features=Mash, but chrome is getting the correct display
id and setting the window bounds properly. I think ash needs to be
fixed to support top-level bounds on the secondary display, but that
will need to be a follow-up CL.

Bug: 764009, 768908
Test: ash_unittests, unit_tests for WindowSizer
Change-Id: Ib77834f8ed5dc4e65f02ff83ab81acbf3331db61
Reviewed-on: https://chromium-review.googlesource.com/1067591
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Evan Stade <estade@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561950}
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f43a097b..650442e 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -532,6 +532,8 @@
     "shell_port_mash.h",
     "shell_port_mus.cc",
     "shell_port_mus.h",
+    "shell_state.cc",
+    "shell_state.h",
     "shutdown_controller.cc",
     "shutdown_controller.h",
     "shutdown_reason.cc",
@@ -1731,6 +1733,7 @@
     "shelf/shelf_view_unittest.cc",
     "shelf/shelf_widget_unittest.cc",
     "shelf/shelf_window_watcher_unittest.cc",
+    "shell_state_unittest.cc",
     "shell_unittest.cc",
     "sticky_keys/sticky_keys_overlay_unittest.cc",
     "sticky_keys/sticky_keys_unittest.cc",
diff --git a/ash/manifest.json b/ash/manifest.json
index 7fad2f3..04a503a 100644
--- a/ash/manifest.json
+++ b/ash/manifest.json
@@ -31,6 +31,7 @@
           "ash.mojom.ProcessCreationTimeRecorder",
           "ash.mojom.SessionController",
           "ash.mojom.ShelfController",
+          "ash.mojom.ShellState",
           "ash.mojom.ShutdownController",
           "ash.mojom.SplitViewController",
           "ash.mojom.SystemTray",
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index 22ce0fbf..24797a30 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -31,6 +31,7 @@
 #include "ash/shelf/shelf_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/shell_state.h"
 #include "ash/shutdown_controller.h"
 #include "ash/system/locale/locale_notification_controller.h"
 #include "ash/system/network/vpn_list.h"
@@ -166,6 +167,10 @@
   Shell::Get()->shelf_controller()->BindRequest(std::move(request));
 }
 
+void BindShellStateOnMainThread(mojom::ShellStateRequest request) {
+  Shell::Get()->shell_state()->BindRequest(std::move(request));
+}
+
 void BindShutdownControllerRequestOnMainThread(
     mojom::ShutdownControllerRequest request) {
   Shell::Get()->shutdown_controller()->BindRequest(std::move(request));
@@ -274,6 +279,8 @@
       main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindSessionControllerRequestOnMainThread),
                          main_thread_task_runner);
+  registry->AddInterface(base::Bind(&BindShellStateOnMainThread),
+                         main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindShelfRequestOnMainThread),
                          main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindShutdownControllerRequestOnMainThread),
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index 259efb1..31a8f43 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -43,6 +43,7 @@
     "process_creation_time_recorder.mojom",
     "session_controller.mojom",
     "shelf.mojom",
+    "shell_state.mojom",
     "shutdown.mojom",
     "split_view.mojom",
     "system_tray.mojom",
diff --git a/ash/public/interfaces/shell_state.mojom b/ash/public/interfaces/shell_state.mojom
new file mode 100644
index 0000000..675cfc0
--- /dev/null
+++ b/ash/public/interfaces/shell_state.mojom
@@ -0,0 +1,17 @@
+// Copyright 2018 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.
+
+module ash.mojom;
+
+// Allows access to ash::Shell state.
+interface ShellState {
+  // The client is immediately notified with the initial state.
+  AddClient(ShellStateClient client);
+};
+
+interface ShellStateClient {
+  // Updates the client's cache of the display id to use for new top-level
+  // windows.
+  SetDisplayIdForNewWindows(int64 display_id);
+};
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 10b42a54..f694351 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -33,6 +33,7 @@
 #include "ash/shelf/shelf_window_targeter.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
+#include "ash/shell_state.h"
 #include "ash/system/status_area_layout_manager.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/touch/touch_hud_debug.h"
@@ -1035,7 +1036,7 @@
     // The root window for new windows is being destroyed. Switch to the primary
     // root window if possible.
     aura::Window* primary_root = Shell::GetPrimaryRootWindow();
-    Shell::Get()->set_root_window_for_new_windows(
+    Shell::Get()->shell_state()->SetRootWindowForNewWindows(
         primary_root == root ? nullptr : primary_root);
   }
 }
diff --git a/ash/scoped_root_window_for_new_windows.cc b/ash/scoped_root_window_for_new_windows.cc
index 46a14e2..9dc0963 100644
--- a/ash/scoped_root_window_for_new_windows.cc
+++ b/ash/scoped_root_window_for_new_windows.cc
@@ -5,6 +5,7 @@
 #include "ash/scoped_root_window_for_new_windows.h"
 
 #include "ash/shell.h"
+#include "ash/shell_state.h"
 #include "base/logging.h"
 
 namespace ash {
@@ -12,11 +13,11 @@
 ScopedRootWindowForNewWindows::ScopedRootWindowForNewWindows(
     aura::Window* new_root) {
   DCHECK(new_root);
-  Shell::Get()->scoped_root_window_for_new_windows_ = new_root;
+  Shell::Get()->shell_state()->SetScopedRootWindowForNewWindows(new_root);
 }
 
 ScopedRootWindowForNewWindows::~ScopedRootWindowForNewWindows() {
-  Shell::Get()->scoped_root_window_for_new_windows_ = nullptr;
+  Shell::Get()->shell_state()->SetScopedRootWindowForNewWindows(nullptr);
 }
 
 }  // namespace ash
diff --git a/ash/scoped_root_window_for_new_windows.h b/ash/scoped_root_window_for_new_windows.h
index 861f351..103820d 100644
--- a/ash/scoped_root_window_for_new_windows.h
+++ b/ash/scoped_root_window_for_new_windows.h
@@ -19,6 +19,9 @@
 // in the same window where a user interaction happened.
 // An example usage is to specify the target root window when creating
 // a new window using launcher's icon.
+// NOTE: This is not "scoped" in the usual sense. It is a temporary
+// override and does not maintain a stack of scoped values. Opening
+// windows from the app list relies on this behavior.
 class ASH_EXPORT ScopedRootWindowForNewWindows {
  public:
   explicit ScopedRootWindowForNewWindows(aura::Window* new_root);
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index ab1a9175..e46ece42e 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -501,8 +501,8 @@
 
   // Place new windows on the same display as the button.
   aura::Window* window = sender->GetWidget()->GetNativeWindow();
-  scoped_root_window_for_new_windows_.reset(
-      new ScopedRootWindowForNewWindows(window->GetRootWindow()));
+  scoped_root_window_for_new_windows_ =
+      std::make_unique<ScopedRootWindowForNewWindows>(window->GetRootWindow());
 
   // Slow down activation animations if shift key is pressed.
   std::unique_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
diff --git a/ash/shell.cc b/ash/shell.cc
index 76dfbe8..6a1e14e 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -84,6 +84,7 @@
 #include "ash/shell_observer.h"
 #include "ash/shell_port.h"
 #include "ash/shell_port_classic.h"
+#include "ash/shell_state.h"
 #include "ash/shutdown_controller.h"
 #include "ash/sticky_keys/sticky_keys_controller.h"
 #include "ash/system/bluetooth/bluetooth_notification_controller.h"
@@ -339,11 +340,7 @@
 
 // static
 aura::Window* Shell::GetRootWindowForNewWindows() {
-  CHECK(Shell::HasInstance());
-  Shell* shell = Shell::Get();
-  if (shell->scoped_root_window_for_new_windows_)
-    return shell->scoped_root_window_for_new_windows_;
-  return shell->root_window_for_new_windows_;
+  return Shell::Get()->shell_state_->GetRootWindowForNewWindows();
 }
 
 // static
@@ -697,6 +694,7 @@
           shell_delegate->GetShellConnector())),
       note_taking_controller_(std::make_unique<NoteTakingController>()),
       shell_delegate_(std::move(shell_delegate)),
+      shell_state_(std::make_unique<ShellState>()),
       shutdown_controller_(std::make_unique<ShutdownController>()),
       system_tray_controller_(std::make_unique<SystemTrayController>()),
       system_tray_notifier_(std::make_unique<SystemTrayNotifier>()),
@@ -1087,7 +1085,7 @@
   time_to_first_present_recorder_ =
       std::make_unique<TimeToFirstPresentRecorder>(GetPrimaryRootWindow());
 
-  root_window_for_new_windows_ = GetPrimaryRootWindow();
+  shell_state_->SetRootWindowForNewWindows(GetPrimaryRootWindow());
 
   resolution_notification_controller_ =
       std::make_unique<ResolutionNotificationController>();
@@ -1455,8 +1453,10 @@
     ::wm::ActivationChangeObserver::ActivationReason reason,
     aura::Window* gained_active,
     aura::Window* lost_active) {
-  if (gained_active)
-    root_window_for_new_windows_ = gained_active->GetRootWindow();
+  if (!gained_active)
+    return;
+
+  shell_state_->SetRootWindowForNewWindows(gained_active->GetRootWindow());
 }
 
 void Shell::OnFirstSessionStarted() {
diff --git a/ash/shell.h b/ash/shell.h
index de4d7dde..d40f8b5 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -164,6 +164,7 @@
 class ShellDelegate;
 struct ShellInitParams;
 class ShellObserver;
+class ShellState;
 class ShutdownController;
 class SmsObserver;
 class SplitViewController;
@@ -498,6 +499,7 @@
   ShelfController* shelf_controller() { return shelf_controller_.get(); }
   ShelfModel* shelf_model();
   ShellDelegate* shell_delegate() { return shell_delegate_.get(); }
+  ShellState* shell_state() { return shell_state_.get(); }
   ShutdownController* shutdown_controller() {
     return shutdown_controller_.get();
   }
@@ -581,11 +583,6 @@
   // Starts the animation that occurs on first login.
   void DoInitialWorkspaceAnimation();
 
-  // NOTE: Prefer ScopedRootWindowForNewWindows when setting temporarily.
-  void set_root_window_for_new_windows(aura::Window* root) {
-    root_window_for_new_windows_ = root;
-  }
-
   void SetLargeCursorSizeInDip(int large_cursor_size_in_dip);
 
   // Updates cursor compositing on/off. Native cursor is disabled when cursor
@@ -766,6 +763,7 @@
   std::unique_ptr<ShelfController> shelf_controller_;
   std::unique_ptr<ShelfWindowWatcher> shelf_window_watcher_;
   std::unique_ptr<ShellDelegate> shell_delegate_;
+  std::unique_ptr<ShellState> shell_state_;
   std::unique_ptr<ShutdownController> shutdown_controller_;
   std::unique_ptr<SystemNotificationController> system_notification_controller_;
   std::unique_ptr<SystemTrayController> system_tray_controller_;
@@ -892,10 +890,6 @@
   // For testing only: simulate that a modal window is open
   bool simulate_modal_window_open_for_test_ = false;
 
-  // See comment for GetRootWindowForNewWindows().
-  aura::Window* root_window_for_new_windows_ = nullptr;
-  aura::Window* scoped_root_window_for_new_windows_ = nullptr;
-
   std::unique_ptr<ImmersiveHandlerFactoryAsh> immersive_handler_factory_;
 
   std::unique_ptr<MessageCenterController> message_center_controller_;
diff --git a/ash/shell_state.cc b/ash/shell_state.cc
new file mode 100644
index 0000000..3df1253
--- /dev/null
+++ b/ash/shell_state.cc
@@ -0,0 +1,69 @@
+// Copyright 2018 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 "ash/shell_state.h"
+
+#include <memory>
+#include <utility>
+
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+
+namespace ash {
+
+ShellState::ShellState() = default;
+
+ShellState::~ShellState() = default;
+
+void ShellState::BindRequest(mojom::ShellStateRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+aura::Window* ShellState::GetRootWindowForNewWindows() const {
+  if (scoped_root_window_for_new_windows_)
+    return scoped_root_window_for_new_windows_;
+  return root_window_for_new_windows_;
+}
+
+void ShellState::SetRootWindowForNewWindows(aura::Window* root) {
+  if (root == root_window_for_new_windows_)
+    return;
+  root_window_for_new_windows_ = root;
+  NotifyAllClients();
+}
+
+void ShellState::AddClient(mojom::ShellStateClientPtr client) {
+  mojom::ShellStateClient* client_impl = client.get();
+  clients_.AddPtr(std::move(client));
+  client_impl->SetDisplayIdForNewWindows(GetDisplayIdForNewWindows());
+}
+
+void ShellState::FlushMojoForTest() {
+  clients_.FlushForTesting();
+}
+
+void ShellState::NotifyAllClients() {
+  const int64_t display_id = GetDisplayIdForNewWindows();
+  clients_.ForAllPtrs([display_id](mojom::ShellStateClient* client) {
+    client->SetDisplayIdForNewWindows(display_id);
+  });
+}
+
+int64_t ShellState::GetDisplayIdForNewWindows() const {
+  // GetDisplayNearestWindow() handles null.
+  return display::Screen::GetScreen()
+      ->GetDisplayNearestWindow(GetRootWindowForNewWindows())
+      .id();
+}
+
+void ShellState::SetScopedRootWindowForNewWindows(aura::Window* root) {
+  if (root == scoped_root_window_for_new_windows_)
+    return;
+  // Only allow set and clear, not switch.
+  DCHECK(!scoped_root_window_for_new_windows_ || !root);
+  scoped_root_window_for_new_windows_ = root;
+  NotifyAllClients();
+}
+
+}  // namespace ash
diff --git a/ash/shell_state.h b/ash/shell_state.h
new file mode 100644
index 0000000..b40f8d9
--- /dev/null
+++ b/ash/shell_state.h
@@ -0,0 +1,75 @@
+// Copyright 2018 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 ASH_SHELL_STATE_H_
+#define ASH_SHELL_STATE_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/shell_state.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+
+// Provides access via mojo to ash::Shell state.
+class ASH_EXPORT ShellState : public mojom::ShellState {
+ public:
+  ShellState();
+  ~ShellState() override;
+
+  // Binds the mojom::ShellState interface to this object.
+  void BindRequest(mojom::ShellStateRequest request);
+
+  // Returns the root window that newly created windows should be added to.
+  // Value can be temporarily overridden using ScopedRootWindowForNewWindows.
+  // NOTE: This returns the root; newly created windows should be added to the
+  // appropriate container in the returned window.
+  aura::Window* GetRootWindowForNewWindows() const;
+
+  // Updates the root window and notifies observers.
+  // NOTE: Prefer ScopedRootWindowForNewWindows.
+  void SetRootWindowForNewWindows(aura::Window* root);
+
+  // mojom::ShellState:
+  void AddClient(mojom::ShellStateClientPtr client) override;
+
+  void FlushMojoForTest();
+
+ private:
+  friend class ScopedRootWindowForNewWindows;
+
+  // Sends a state update to all clients.
+  void NotifyAllClients();
+
+  int64_t GetDisplayIdForNewWindows() const;
+
+  // Sets the value and updates clients.
+  void SetScopedRootWindowForNewWindows(aura::Window* root);
+
+  // Binding for mojom::ShellState interface.
+  mojo::BindingSet<mojom::ShellState> bindings_;
+
+  // Clients (e.g. chrome browser, other mojo apps).
+  mojo::InterfacePtrSet<mojom::ShellStateClient> clients_;
+
+  aura::Window* root_window_for_new_windows_ = nullptr;
+
+  // See ScopedRootWindowForNewWindows.
+  aura::Window* scoped_root_window_for_new_windows_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellState);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SHELL_STATE_H_
diff --git a/ash/shell_state_unittest.cc b/ash/shell_state_unittest.cc
new file mode 100644
index 0000000..7fd1095
--- /dev/null
+++ b/ash/shell_state_unittest.cc
@@ -0,0 +1,67 @@
+// 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 "ash/shell_state.h"
+
+#include <stdint.h>
+
+#include "ash/public/interfaces/shell_state.mojom.h"
+#include "ash/scoped_root_window_for_new_windows.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/display/manager/display_manager.h"
+
+namespace ash {
+namespace {
+
+// Simulates the client interface in chrome.
+class TestShellStateClient : public mojom::ShellStateClient {
+ public:
+  TestShellStateClient() = default;
+  ~TestShellStateClient() override = default;
+
+  mojom::ShellStateClientPtr CreateInterfacePtrAndBind() {
+    mojom::ShellStateClientPtr ptr;
+    binding_.Bind(mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  // mojom::ShellStateClient:
+  void SetDisplayIdForNewWindows(int64_t display_id) override {
+    last_display_id_ = display_id;
+  }
+
+  int64_t last_display_id_ = 0;
+
+ private:
+  mojo::Binding<mojom::ShellStateClient> binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(TestShellStateClient);
+};
+
+using ShellStateTest = AshTestBase;
+
+TEST_F(ShellStateTest, Basics) {
+  UpdateDisplay("1024x768,800x600");
+  const int64_t primary_display_id = display_manager()->GetDisplayAt(0).id();
+  const int64_t secondary_display_id = display_manager()->GetDisplayAt(1).id();
+
+  ShellState* shell_state = Shell::Get()->shell_state();
+  TestShellStateClient client;
+
+  // Adding a client notifies it with the initial display id.
+  shell_state->AddClient(client.CreateInterfacePtrAndBind());
+  shell_state->FlushMojoForTest();
+  EXPECT_EQ(primary_display_id, client.last_display_id_);
+
+  // Setting a root window for new windows notifies the client.
+  ScopedRootWindowForNewWindows scoped_root(Shell::GetAllRootWindows()[1]);
+  shell_state->FlushMojoForTest();
+  EXPECT_EQ(secondary_display_id, client.last_display_id_);
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/wm/window_positioning_utils.cc b/ash/wm/window_positioning_utils.cc
index 8c39d2b..543e9db 100644
--- a/ash/wm/window_positioning_utils.cc
+++ b/ash/wm/window_positioning_utils.cc
@@ -9,6 +9,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
+#include "ash/shell_state.h"
 #include "ash/wm/system_modal_container_layout_manager.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state.h"
@@ -161,7 +162,8 @@
       // Restore focused/active window.
       if (focused && tracker.Contains(focused)) {
         aura::client::GetFocusClient(focused)->FocusWindow(focused);
-        Shell::Get()->set_root_window_for_new_windows(focused->GetRootWindow());
+        Shell::Get()->shell_state()->SetRootWindowForNewWindows(
+            focused->GetRootWindow());
       } else if (active && tracker.Contains(active)) {
         wm::ActivateWindow(active);
       }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0c7623a..b6bb714 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1865,6 +1865,8 @@
       "ash/session_controller_client.h",
       "ash/session_util.cc",
       "ash/session_util.h",
+      "ash/shell_state_client.cc",
+      "ash/shell_state_client.h",
       "ash/system_tray_client.cc",
       "ash/system_tray_client.h",
       "ash/tab_scrubber.cc",
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index e9ba790..6d91a54e 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -21,6 +21,7 @@
 specific_include_rules = {
   ".*test.*": [
    "!ash",
+   "+ash/public",
   ],
   # AshShellInit supports CLASSIC and MUS modes so allow ash/ includes.
   "ash_shell_init\.cc": [
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 40e140f..b9595d47 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/ui/ash/network/network_connect_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/network/network_portal_notification_controller.h"
 #include "chrome/browser/ui/ash/session_controller_client.h"
+#include "chrome/browser/ui/ash/shell_state_client.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
 #include "chrome/browser/ui/ash/tab_scrubber.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
@@ -199,6 +200,9 @@
   session_controller_client_ = std::make_unique<SessionControllerClient>();
   session_controller_client_->Init();
 
+  shell_state_client_ = std::make_unique<ShellStateClient>();
+  shell_state_client_->Init();
+
   system_tray_client_ = std::make_unique<SystemTrayClient>();
 
   // Makes mojo request to TabletModeController in ash.
@@ -294,6 +298,7 @@
   volume_controller_.reset();
 
   system_tray_client_.reset();
+  shell_state_client_.reset();
   session_controller_client_.reset();
   chrome_new_window_client_.reset();
   network_portal_notification_controller_.reset();
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 48dbfbc..fc29747 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -40,6 +40,7 @@
 class NetworkConnectDelegateChromeOS;
 class NightLightClient;
 class SessionControllerClient;
+class ShellStateClient;
 class SystemTrayClient;
 class TabletModeClient;
 class VolumeController;
@@ -97,6 +98,7 @@
   std::unique_ptr<ChromeNewWindowClient> chrome_new_window_client_;
   std::unique_ptr<ImeControllerClient> ime_controller_client_;
   std::unique_ptr<SessionControllerClient> session_controller_client_;
+  std::unique_ptr<ShellStateClient> shell_state_client_;
   std::unique_ptr<SystemTrayClient> system_tray_client_;
   std::unique_ptr<TabletModeClient> tablet_mode_client_;
   std::unique_ptr<VolumeController> volume_controller_;
diff --git a/chrome/browser/ui/ash/launcher/DEPS b/chrome/browser/ui/ash/launcher/DEPS
index a0670a7..b38958e 100644
--- a/chrome/browser/ui/ash/launcher/DEPS
+++ b/chrome/browser/ui/ash/launcher/DEPS
@@ -12,9 +12,4 @@
     "+ash/shell.h",
     "+ash/strings/grit/ash_strings.h",
   ],
-  # https://crbug.com/768908
-  "extension_launcher_context_menu\.cc": [
-    "+ash/scoped_root_window_for_new_windows.h",
-    "+ash/shell.h",
-  ],
 }
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index b75c60d..b9fd53ee 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -6,8 +6,6 @@
 
 #include <utility>
 
-#include "ash/scoped_root_window_for_new_windows.h"  // mash-ok
-#include "ash/shell.h"                               // mash-ok
 #include "base/bind.h"
 #include "chrome/browser/chromeos/ash_config.h"
 #include "chrome/browser/extensions/context_menu_matcher.h"
@@ -18,6 +16,7 @@
 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
+#include "chrome/browser/ui/ash/shell_state_client.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -34,6 +33,27 @@
   return item->contexts().Contains(extensions::MenuItem::LAUNCHER);
 }
 
+// Temporarily sets the display for new windows. Only use this when it's
+// guaranteed messages won't be received from ash to update the display.
+// For example, it's OK to use temporarily at function scope, but don't
+// heap-allocate one and hang on to it.
+class ScopedDisplayIdForNewWindows {
+ public:
+  explicit ScopedDisplayIdForNewWindows(int64_t display_id)
+      : old_display_id_(display_id) {
+    ShellStateClient::Get()->SetDisplayIdForNewWindows(display_id);
+  }
+
+  ~ScopedDisplayIdForNewWindows() {
+    ShellStateClient::Get()->SetDisplayIdForNewWindows(old_display_id_);
+  }
+
+ private:
+  const int64_t old_display_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedDisplayIdForNewWindows);
+};
+
 }  // namespace
 
 ExtensionLauncherContextMenu::ExtensionLauncherContextMenu(
@@ -94,12 +114,7 @@
     return;
 
   // Place new windows on the same display as the context menu.
-  // TODO(crbug.com/768908): Fix this in mash (where Chrome can't use Shell).
-  std::unique_ptr<ash::ScopedRootWindowForNewWindows> scoped_root;
-  if (chromeos::GetAshConfig() != ash::Config::MASH) {
-    aura::Window* root = ash::Shell::GetRootWindowForDisplayId(display_id());
-    scoped_root = std::make_unique<ash::ScopedRootWindowForNewWindows>(root);
-  }
+  ScopedDisplayIdForNewWindows scoped_display(display_id());
 
   switch (static_cast<MenuItem>(command_id)) {
     case LAUNCH_TYPE_PINNED_TAB:
diff --git a/chrome/browser/ui/ash/shell_state_client.cc b/chrome/browser/ui/ash/shell_state_client.cc
new file mode 100644
index 0000000..6a6cefd
--- /dev/null
+++ b/chrome/browser/ui/ash/shell_state_client.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 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/ash/shell_state_client.h"
+
+#include <utility>
+
+#include "ash/public/interfaces/constants.mojom.h"
+#include "chrome/browser/ui/window_sizer/window_sizer.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/display/types/display_constants.h"
+
+namespace {
+
+ShellStateClient* g_shell_state_client = nullptr;
+
+}  // namespace
+
+ShellStateClient::ShellStateClient()
+    : binding_(this), display_id_for_new_windows_(display::kInvalidDisplayId) {
+  DCHECK(!g_shell_state_client);
+  g_shell_state_client = this;
+}
+
+ShellStateClient::~ShellStateClient() {
+  DCHECK_EQ(this, g_shell_state_client);
+  g_shell_state_client = nullptr;
+}
+
+void ShellStateClient::Init() {
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &shell_state_ptr_);
+  BindAndAddClient();
+}
+
+void ShellStateClient::InitForTesting(ash::mojom::ShellStatePtr shell_state) {
+  shell_state_ptr_ = std::move(shell_state);
+  BindAndAddClient();
+}
+
+// static
+ShellStateClient* ShellStateClient::Get() {
+  return g_shell_state_client;
+}
+
+void ShellStateClient::SetDisplayIdForNewWindows(int64_t display_id) {
+  display_id_for_new_windows_ = display_id;
+}
+
+void ShellStateClient::FlushForTesting() {
+  shell_state_ptr_.FlushForTesting();
+}
+
+void ShellStateClient::BindAndAddClient() {
+  ash::mojom::ShellStateClientPtr client_ptr;
+  binding_.Bind(mojo::MakeRequest(&client_ptr));
+  shell_state_ptr_->AddClient(std::move(client_ptr));
+}
+
+// static
+display::Display WindowSizer::GetDisplayForNewWindow(display::Screen* screen,
+                                                     const gfx::Rect& bounds) {
+  // May be null in unit tests.
+  if (g_shell_state_client) {
+    // Prefer the display where the user last activated any window.
+    const int64_t id = g_shell_state_client->display_id_for_new_windows();
+    display::Display display;
+    if (screen->GetDisplayWithDisplayId(id, &display))
+      return display;
+  }
+
+  // Otherwise find the display that best matches the bounds.
+  return screen->GetDisplayMatching(bounds);
+}
diff --git a/chrome/browser/ui/ash/shell_state_client.h b/chrome/browser/ui/ash/shell_state_client.h
new file mode 100644
index 0000000..a2e76a6f
--- /dev/null
+++ b/chrome/browser/ui/ash/shell_state_client.h
@@ -0,0 +1,56 @@
+// Copyright 2018 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_ASH_SHELL_STATE_CLIENT_H_
+#define CHROME_BROWSER_UI_ASH_SHELL_STATE_CLIENT_H_
+
+#include <memory>
+
+#include "ash/public/interfaces/shell_state.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+// Caches ash::Shell state. The initial values are loaded asynchronously at
+// startup because we don't want Chrome to block on startup waiting for Ash.
+class ShellStateClient : public ash::mojom::ShellStateClient {
+ public:
+  ShellStateClient();
+  ~ShellStateClient() override;
+
+  // Initializes and connects to ash.
+  void Init();
+
+  // Tests can provide a mock mojo interface for the ash interface.
+  void InitForTesting(ash::mojom::ShellStatePtr shell_state_ptr);
+
+  static ShellStateClient* Get();
+
+  int64_t display_id_for_new_windows() const {
+    return display_id_for_new_windows_;
+  }
+
+  // ash::mojom::ShellStateClient:
+  void SetDisplayIdForNewWindows(int64_t display_id) override;
+
+  // Flushes the mojo pipe to ash.
+  void FlushForTesting();
+
+ private:
+  friend class ScopedDisplayIdForNewWindows;
+
+  // Binds this object to its mojo interface and registers it as an ash client.
+  void BindAndAddClient();
+
+  // The mojo interface in ash.
+  ash::mojom::ShellStatePtr shell_state_ptr_;
+
+  // Binds to the observer interface from ash.
+  mojo::Binding<ash::mojom::ShellStateClient> binding_;
+
+  int64_t display_id_for_new_windows_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShellStateClient);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_SHELL_STATE_CLIENT_H_
diff --git a/chrome/browser/ui/ash/shell_state_client_unittest.cc b/chrome/browser/ui/ash/shell_state_client_unittest.cc
new file mode 100644
index 0000000..e483284
--- /dev/null
+++ b/chrome/browser/ui/ash/shell_state_client_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 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/ash/shell_state_client.h"
+
+#include "ash/public/interfaces/shell_state.mojom.h"
+#include "base/macros.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestShellState : ash::mojom::ShellState {
+ public:
+  TestShellState() : binding_(this) {}
+  ~TestShellState() override = default;
+
+  ash::mojom::ShellStatePtr CreateInterfacePtr() {
+    ash::mojom::ShellStatePtr ptr;
+    binding_.Bind(mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  // ash::mojom::ShellState:
+  void AddClient(ash::mojom::ShellStateClientPtr client) override {
+    ++add_client_count_;
+  }
+
+  int add_client_count() const { return add_client_count_; }
+
+ private:
+  mojo::Binding<ash::mojom::ShellState> binding_;
+  int add_client_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TestShellState);
+};
+
+TEST(ShellStateClientTest, Basics) {
+  base::test::ScopedTaskEnvironment scoped_task_enviroment;
+  ShellStateClient client;
+  TestShellState ash_shell_state;
+  client.InitForTesting(ash_shell_state.CreateInterfacePtr());
+  client.FlushForTesting();
+
+  // Client was added to ash.
+  EXPECT_TRUE(ash_shell_state.add_client_count());
+
+  client.SetDisplayIdForNewWindows(123);
+  EXPECT_EQ(123, client.display_id_for_new_windows());
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/window_sizer/window_sizer.cc b/chrome/browser/ui/window_sizer/window_sizer.cc
index 8e260718..99468a13 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer.cc
@@ -22,12 +22,6 @@
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
-#if defined(OS_CHROMEOS)
-#include "ash/public/cpp/ash_switches.h"  // nogncheck
-#include "ash/shell.h"
-#include "chrome/browser/ui/ash/ash_util.h"
-#endif
-
 namespace {
 
 // Minimum height of the visible part of a window.
@@ -141,45 +135,21 @@
 
 }  // namespace
 
-WindowSizer::DefaultTargetDisplayProvider::DefaultTargetDisplayProvider() =
-    default;
-WindowSizer::DefaultTargetDisplayProvider::~DefaultTargetDisplayProvider() =
-    default;
-
-display::Display WindowSizer::DefaultTargetDisplayProvider::GetTargetDisplay(
-    const display::Screen* screen,
-    const gfx::Rect& bounds) const {
-#if defined(OS_CHROMEOS)
-  // Use the target display on ash.
-  if (ash_util::ShouldOpenAshOnStartup()) {
-    aura::Window* target = ash::Shell::GetRootWindowForNewWindows();
-    return screen->GetDisplayNearestWindow(target);
-  }
-#endif
-  // Find the size of the work area of the monitor that intersects the bounds
-  // of the anchor window.
-  return screen->GetDisplayMatching(bounds);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // WindowSizer, public:
 
 WindowSizer::WindowSizer(
     std::unique_ptr<StateProvider> state_provider,
-    std::unique_ptr<TargetDisplayProvider> target_display_provider,
     const Browser* browser)
     : WindowSizer(std::move(state_provider),
-                  std::move(target_display_provider),
                   display::Screen::GetScreen(),
                   browser) {}
 
 WindowSizer::WindowSizer(
     std::unique_ptr<StateProvider> state_provider,
-    std::unique_ptr<TargetDisplayProvider> target_display_provider,
     display::Screen* screen,
     const Browser* browser)
     : state_provider_(std::move(state_provider)),
-      target_display_provider_(std::move(target_display_provider)),
       screen_(screen),
       browser_(browser) {
   DCHECK(screen_);
@@ -196,10 +166,7 @@
     ui::WindowShowState* show_state) {
   std::unique_ptr<StateProvider> state_provider(
       new DefaultStateProvider(app_name, browser));
-  std::unique_ptr<TargetDisplayProvider> target_display_provider(
-      new DefaultTargetDisplayProvider);
-  const WindowSizer sizer(std::move(state_provider),
-                          std::move(target_display_provider), browser);
+  const WindowSizer sizer(std::move(state_provider), browser);
   sizer.DetermineWindowBoundsAndShowState(specified_bounds,
                                           window_bounds,
                                           show_state);
@@ -392,7 +359,7 @@
 }
 
 display::Display WindowSizer::GetTargetDisplay(const gfx::Rect& bounds) const {
-  return target_display_provider_->GetTargetDisplay(screen_, bounds);
+  return GetDisplayForNewWindow(screen_, bounds);
 }
 
 ui::WindowShowState WindowSizer::GetWindowDefaultShowState() const {
@@ -414,3 +381,12 @@
 
   return browser_->initial_show_state();
 }
+
+#if !defined(OS_CHROMEOS)
+// Chrome OS has an implementation in //chrome/browser/ui/ash.
+// static
+display::Display WindowSizer::GetDisplayForNewWindow(display::Screen* screen,
+                                                     const gfx::Rect& bounds) {
+  return screen->GetDisplayMatching(bounds);
+}
+#endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/window_sizer/window_sizer.h b/chrome/browser/ui/window_sizer/window_sizer.h
index 80899e2..b51cf5c 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.h
+++ b/chrome/browser/ui/window_sizer/window_sizer.h
@@ -29,10 +29,10 @@
 //  and persistent storage (using preferences) but can be overrided with mocks
 //  for testing.
 //
+// TODO(crbug.com/846736): Extract the platform-specific code out of this class.
 class WindowSizer {
  public:
   class StateProvider;
-  class TargetDisplayProvider;
 
   // An interface implemented by an object that can retrieve state from either a
   // persistent store or an existing window.
@@ -58,29 +58,6 @@
         ui::WindowShowState* show_state) const = 0;
   };
 
-  // An interface implemented by an object to identify on which
-  // display a new window should be located.
-  class TargetDisplayProvider {
-   public:
-    virtual ~TargetDisplayProvider() {}
-
-    virtual display::Display GetTargetDisplay(
-        const display::Screen* screen,
-        const gfx::Rect& bounds) const = 0;
-  };
-
-  class DefaultTargetDisplayProvider : public TargetDisplayProvider {
-   public:
-    DefaultTargetDisplayProvider();
-    ~DefaultTargetDisplayProvider() override;
-
-    display::Display GetTargetDisplay(const display::Screen* screen,
-                                      const gfx::Rect& bounds) const override;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(DefaultTargetDisplayProvider);
-  };
-
   // Determines the position and size for a window as it is created as well
   // as the initial state. This function uses several strategies to figure out
   // optimal size and placement, first looking for an existing active window,
@@ -129,16 +106,15 @@
   const StateProvider* state_provider() const { return state_provider_.get(); }
 
  private:
+  friend class WindowSizerAshTest;
   friend class WindowSizerTestUtil;
 
   // WindowSizer will use the platforms's display::Screen.
   WindowSizer(std::unique_ptr<StateProvider> state_provider,
-              std::unique_ptr<TargetDisplayProvider> target_display_provider,
               const Browser* browser);
 
   // As above, but uses the supplied |screen|. Used only for testing.
   WindowSizer(std::unique_ptr<StateProvider> state_provider,
-              std::unique_ptr<TargetDisplayProvider> target_display_provider,
               display::Screen* screen,
               const Browser* browser);
 
@@ -211,9 +187,13 @@
   // windows or at persistent information.
   ui::WindowShowState GetWindowDefaultShowState() const;
 
+  // Returns the target display for a new window with |bounds| in screen
+  // coordinates.
+  static display::Display GetDisplayForNewWindow(display::Screen* screen,
+                                                 const gfx::Rect& bounds);
+
   // Providers for persistent storage and monitor metrics.
   std::unique_ptr<StateProvider> state_provider_;
-  std::unique_ptr<TargetDisplayProvider> target_display_provider_;
   display::Screen* screen_;  // not owned.
 
   // Note that this browser handle might be NULL.
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
index 2d1ac84..d3c17a4 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
@@ -3,11 +3,10 @@
 // found in the LICENSE file.
 
 #include "ash/public/cpp/window_properties.h"
-#include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/ash/shell_state_client.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/window_sizer/window_sizer_common_unittest.h"
 #include "chrome/common/chrome_switches.h"
@@ -36,6 +35,25 @@
     return std::make_unique<Browser>(params);
   }
 
+  // Similar to WindowSizerTestUtil::GetWindowBounds() but takes an existing
+  // |display_id| instead of creating a TestScreen and new displays.
+  void GetWindowBounds(const Browser* browser,
+                       const gfx::Rect& passed_in,
+                       int64_t display_id,
+                       gfx::Rect* out_bounds) {
+    auto state_provider = std::make_unique<TestStateProvider>();
+    state_provider->SetPersistentState(gfx::Rect(), gfx::Rect(),
+                                       ui::SHOW_STATE_DEFAULT, true);
+    shell_state_client_.SetDisplayIdForNewWindows(display_id);
+
+    ui::WindowShowState ignored;
+    WindowSizer sizer(std::move(state_provider), browser);
+    sizer.DetermineWindowBoundsAndShowState(passed_in, out_bounds, &ignored);
+  }
+
+ protected:
+  ShellStateClient shell_state_client_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(WindowSizerAshTest);
 };
@@ -439,10 +457,11 @@
 // Test the placement of newly created windows on multiple dislays.
 TEST_F(WindowSizerAshTest, PlaceNewWindowsOnMultipleDisplays) {
   UpdateDisplay("1600x1200,1600x1200");
-  gfx::Rect primary_bounds =
-      display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
-  gfx::Rect secondary_bounds =
-      display_manager()->GetSecondaryDisplay().bounds();
+  display::Display primary_display =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+  display::Display second_display = display_manager()->GetSecondaryDisplay();
+  gfx::Rect primary_bounds = primary_display.bounds();
+  gfx::Rect secondary_bounds = second_display.bounds();
 
   std::unique_ptr<TestingProfile> profile(new TestingProfile());
 
@@ -476,9 +495,8 @@
   // First new window should be in the primary.
   {
     gfx::Rect window_bounds;
-    util::GetWindowBounds(p1600x1200, p1600x1200, secondary_bounds, gfx::Rect(),
-                          secondary_bounds, PERSISTED, new_browser.get(),
-                          gfx::Rect(), &window_bounds);
+    GetWindowBounds(new_browser.get(), gfx::Rect(), primary_display.id(),
+                    &window_bounds);
     // TODO(oshima): Use exact bounds when the window_sizer_ash is
     // moved to ash and changed to include the result from
     // RearrangeVisibleWindowOnShow.
@@ -488,9 +506,6 @@
   // Move the window to the right side of the secondary display and create a new
   // window. It should be opened then on the secondary display.
   {
-    display::Display second_display =
-        display::Screen::GetScreen()->GetDisplayNearestPoint(
-            gfx::Point(1600 + 100, 10));
     browser_window->GetNativeWindow()->SetBoundsInScreen(
         gfx::Rect(secondary_bounds.CenterPoint().x() - 100, 10, 200, 200),
         second_display);
@@ -499,11 +514,8 @@
     EXPECT_NE(ash::Shell::GetPrimaryRootWindow(),
               ash::Shell::GetRootWindowForNewWindows());
     gfx::Rect window_bounds;
-    ui::WindowShowState out_show_state = ui::SHOW_STATE_DEFAULT;
-    util::GetWindowBoundsAndShowState(
-        p1600x1200, p1600x1200, secondary_bounds, gfx::Rect(), secondary_bounds,
-        ui::SHOW_STATE_DEFAULT, ui::SHOW_STATE_DEFAULT, PERSISTED,
-        new_browser.get(), gfx::Rect(), 1u, &window_bounds, &out_show_state);
+    GetWindowBounds(new_browser.get(), gfx::Rect(), second_display.id(),
+                    &window_bounds);
     // TODO(oshima): Use exact bounds when the window_sizer_ash is
     // moved to ash and changed to include the result from
     // RearrangeVisibleWindowOnShow.
@@ -519,9 +531,8 @@
               ash::Shell::GetRootWindowForNewWindows());
 
     gfx::Rect window_bounds;
-    util::GetWindowBounds(p1600x1200, p1600x1200, secondary_bounds, gfx::Rect(),
-                          secondary_bounds, PERSISTED, new_browser.get(),
-                          gfx::Rect(), &window_bounds);
+    GetWindowBounds(new_browser.get(), gfx::Rect(), primary_display.id(),
+                    &window_bounds);
     // TODO(oshima): Use exact bounds when the window_sizer_ash is
     // moved to ash and changed to include the result from
     // RearrangeVisibleWindowOnShow.
@@ -679,10 +690,8 @@
 // Test that the target root window is used as the destination of
 // the non browser window. This differ from PersistedBoundsCase
 // in that this uses real ash shell implementations + StateProvider
-// TargetDisplayProvider, rather than mocks.
+// rather than mocks.
 TEST_F(WindowSizerAshTest, DefaultBoundsInTargetDisplay) {
-  if (!ash_util::ShouldOpenAshOnStartup())
-    return;
   UpdateDisplay("500x500,600x600");
 
   // By default windows are placed on the primary display.
@@ -697,7 +706,8 @@
   {
     // When the second display is active new windows are placed there.
     aura::Window* second_root = ash::Shell::GetAllRootWindows()[1];
-    ash::ScopedRootWindowForNewWindows tmp(second_root);
+    int64_t second_display_id = display_manager()->GetSecondaryDisplay().id();
+    shell_state_client_.SetDisplayIdForNewWindows(second_display_id);
     gfx::Rect bounds;
     ui::WindowShowState show_state;
     WindowSizer::GetBrowserWindowBoundsAndShowState(
diff --git a/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc
index 1a6b9b84..24aaae3ec 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc
@@ -197,10 +197,8 @@
     sp->SetPersistentState(bounds, work_area, show_state_persisted, true);
   if (source == LAST_ACTIVE || source == BOTH)
     sp->SetLastActiveState(bounds, show_state_last, true);
-  std::unique_ptr<WindowSizer::TargetDisplayProvider> tdp(
-      new WindowSizer::DefaultTargetDisplayProvider);
 
-  WindowSizer sizer(std::move(sp), std::move(tdp), &test_screen, browser);
+  WindowSizer sizer(std::move(sp), &test_screen, browser);
   sizer.DetermineWindowBoundsAndShowState(passed_in,
                                           out_bounds,
                                           out_show_state);
@@ -221,10 +219,8 @@
     sp->SetPersistentState(bounds, display_config, show_state_persisted, true);
   if (source == LAST_ACTIVE || source == BOTH)
     sp->SetLastActiveState(bounds, show_state_last, true);
-  std::unique_ptr<WindowSizer::TargetDisplayProvider> tdp(
-      new WindowSizer::DefaultTargetDisplayProvider);
 
-  WindowSizer sizer(std::move(sp), std::move(tdp), &test_screen, browser);
+  WindowSizer sizer(std::move(sp), &test_screen, browser);
 
   ui::WindowShowState out_show_state = ui::SHOW_STATE_DEFAULT;
   gfx::Rect out_bounds;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 83812e3..a363e62 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3277,6 +3277,7 @@
       "../browser/ui/ash/network/network_state_notifier_unittest.cc",
       "../browser/ui/ash/network/tether_notification_presenter_unittest.cc",
       "../browser/ui/ash/session_controller_client_unittest.cc",
+      "../browser/ui/ash/shell_state_client_unittest.cc",
       "../browser/ui/ash/tablet_mode_client_unittest.cc",
       "../browser/ui/ash/wallpaper_controller_client_unittest.cc",
       "../browser/ui/window_sizer/window_sizer_ash_unittest.cc",