Commit 40f7d75b authored by James Cook's avatar James Cook Committed by Commit Bot

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/1067591Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561950}
parent 12dc8ef2
......@@ -532,6 +532,8 @@ component("ash") {
"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 @@ test("ash_unittests") {
"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",
......
......@@ -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",
......
......@@ -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 @@ void BindShelfRequestOnMainThread(mojom::ShelfControllerRequest request) {
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 @@ void RegisterInterfaces(
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),
......
......@@ -43,6 +43,7 @@ mojom("interfaces_internal") {
"process_creation_time_recorder.mojom",
"session_controller.mojom",
"shelf.mojom",
"shell_state.mojom",
"shutdown.mojom",
"split_view.mojom",
"system_tray.mojom",
......
// 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);
};
......@@ -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 @@ void RootWindowController::ResetRootForNewWindowsIfNecessary() {
// 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);
}
}
......
......@@ -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 @@ namespace ash {
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
......@@ -19,6 +19,9 @@ namespace ash {
// 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);
......
......@@ -501,8 +501,8 @@ void ShelfView::ButtonPressed(views::Button* sender,
// 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;
......
......@@ -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 @@ aura::Window* Shell::GetPrimaryRootWindow() {
// 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::Shell(std::unique_ptr<ShellDelegate> shell_delegate,
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 @@ void Shell::Init(ui::ContextFactory* context_factory,
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 @@ void Shell::OnWindowActivated(
::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() {
......
......@@ -164,6 +164,7 @@ class ShelfWindowWatcher;
class ShellDelegate;
struct ShellInitParams;
class ShellObserver;
class ShellState;
class ShutdownController;
class SmsObserver;
class SplitViewController;
......@@ -498,6 +499,7 @@ class ASH_EXPORT Shell : public SessionObserver,
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 @@ class ASH_EXPORT Shell : public SessionObserver,
// 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 @@ class ASH_EXPORT Shell : public SessionObserver,
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 @@ class ASH_EXPORT Shell : public SessionObserver,
// 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_;
......
// 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
// 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_
// 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
......@@ -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 @@ void SetBoundsInScreen(aura::Window* window,
// 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);
}
......
......@@ -1865,6 +1865,8 @@ split_static_library("ui") {
"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",
......
......@@ -21,6 +21,7 @@ include_rules = [
specific_include_rules = {
".*test.*": [
"!ash",
"+ash/public",
],
# AshShellInit supports CLASSIC and MUS modes so allow ash/ includes.
"ash_shell_init\.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 @@ void ChromeBrowserMainExtraPartsAsh::PreProfileInit() {
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 @@ void ChromeBrowserMainExtraPartsAsh::PostMainMessageLoopRun() {
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();
......
......@@ -40,6 +40,7 @@ class MediaClient;
class NetworkConnectDelegateChromeOS;
class NightLightClient;
class SessionControllerClient;
class ShellStateClient;
class SystemTrayClient;
class TabletModeClient;
class VolumeController;
......@@ -97,6 +98,7 @@ class ChromeBrowserMainExtraPartsAsh : public ChromeBrowserMainExtraParts {
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_;
......
......@@ -12,9 +12,4 @@ specific_include_rules = {
"+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",
],
}
......@@ -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 @@ bool MenuItemHasLauncherContext(const extensions::MenuItem* item) {
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 @@ void ExtensionLauncherContextMenu::ExecuteCommand(int command_id,
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:
......
// 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);
}
// 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_
// 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
......@@ -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 @@ class DefaultStateProvider : public WindowSizer::StateProvider {
} // 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 @@ void WindowSizer::GetBrowserWindowBoundsAndShowState(
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 @@ void WindowSizer::AdjustBoundsToBeVisibleOnDisplay(
}
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 @@ ui::WindowShowState WindowSizer::GetWindowDefaultShowState() const {
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)
......@@ -29,10 +29,10 @@ class Screen;
// 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 @@ class WindowSizer {
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 @@ class WindowSizer {
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 @@ class WindowSizer {
// 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.
......
......@@ -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 @@ class WindowSizerAshTest : public ash::AshTestBase {
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_F(WindowSizerAshTest, PlaceNewBrowserWindowOnEmptyDesktop) {
// 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 @@ TEST_F(WindowSizerAshTest, PlaceNewWindowsOnMultipleDisplays) {
// 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 @@ TEST_F(WindowSizerAshTest, PlaceNewWindowsOnMultipleDisplays) {
// 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 @@ TEST_F(WindowSizerAshTest, PlaceNewWindowsOnMultipleDisplays) {
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 @@ TEST_F(WindowSizerAshTest, PlaceNewWindowsOnMultipleDisplays) {
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_F(WindowSizerAshTest, DefaultStateBecomesMaximized) {
// 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 @@ TEST_F(WindowSizerAshTest, DefaultBoundsInTargetDisplay) {
{
// 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(
......
......@@ -197,10 +197,8 @@ void WindowSizerTestUtil::GetWindowBoundsAndShowState(
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 @@ ui::WindowShowState WindowSizerTestUtil::GetWindowShowState(
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;
......
......@@ -3277,6 +3277,7 @@ test("unit_tests") {
"../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",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment