blob: 9d60d10441a3fd63e48cb387e898a7587427900b [file] [log] [blame]
// Copyright 2016 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 <stdint.h>
#include <string>
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "services/ui/common/types.h"
#include "services/ui/common/util.h"
#include "services/ui/display/viewport_metrics.h"
#include "services/ui/public/interfaces/window_tree.mojom.h"
#include "services/ui/ws/display_manager.h"
#include "services/ui/ws/ids.h"
#include "services/ui/ws/platform_display.h"
#include "services/ui/ws/platform_display_factory.h"
#include "services/ui/ws/server_window.h"
#include "services/ui/ws/test_utils.h"
#include "services/ui/ws/window_manager_display_root.h"
#include "services/ui/ws/window_manager_state.h"
#include "services/ui/ws/window_server.h"
#include "services/ui/ws/window_server_delegate.h"
#include "services/ui/ws/window_tree.h"
#include "services/ui/ws/window_tree_binding.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/rect.h"
namespace ui {
namespace ws {
namespace test {
namespace {
const UserId kTestId1 = "2";
const UserId kTestId2 = "21";
ClientWindowId ClientWindowIdForFirstRoot(WindowTree* tree) {
if (tree->roots().empty())
return ClientWindowId();
return ClientWindowIdForWindow(tree, *tree->roots().begin());
}
WindowManagerState* GetWindowManagerStateForUser(Display* display,
const UserId& user_id) {
WindowManagerDisplayRoot* display_root =
display->GetWindowManagerDisplayRootForUser(user_id);
return display_root ? display_root->window_manager_state() : nullptr;
}
// Returns the root ServerWindow for the specified Display.
ServerWindow* GetRootOnDisplay(WindowTree* tree, Display* display) {
for (const ServerWindow* root : tree->roots()) {
if (tree->GetDisplay(root) == display)
return const_cast<ServerWindow*>(root);
}
return nullptr;
}
// Tracks destruction of a ServerWindow, setting a bool* to true when
// OnWindowDestroyed() is called
class ServerWindowDestructionObserver : public ServerWindowObserver {
public:
ServerWindowDestructionObserver(ServerWindow* window, bool* destroyed)
: window_(window), destroyed_(destroyed) {
window_->AddObserver(this);
}
~ServerWindowDestructionObserver() override {
if (window_)
window_->RemoveObserver(this);
}
// ServerWindowObserver:
void OnWindowDestroyed(ServerWindow* window) override {
*destroyed_ = true;
window_->RemoveObserver(this);
window_ = nullptr;
}
private:
ServerWindow* window_;
bool* destroyed_;
DISALLOW_COPY_AND_ASSIGN(ServerWindowDestructionObserver);
};
} // namespace
// -----------------------------------------------------------------------------
class DisplayTest : public testing::Test {
public:
DisplayTest() {}
~DisplayTest() override {}
WindowServer* window_server() { return ws_test_helper_.window_server(); }
DisplayManager* display_manager() {
return window_server()->display_manager();
}
TestWindowServerDelegate* window_server_delegate() {
return ws_test_helper_.window_server_delegate();
}
TestScreenManager& screen_manager() { return screen_manager_; }
protected:
// testing::Test:
void SetUp() override {
screen_manager_.Init(window_server()->display_manager());
window_server()->user_id_tracker()->AddUserId(kTestId1);
window_server()->user_id_tracker()->AddUserId(kTestId2);
}
private:
WindowServerTestHelper ws_test_helper_;
TestScreenManager screen_manager_;
DISALLOW_COPY_AND_ASSIGN(DisplayTest);
};
TEST_F(DisplayTest, CreateDisplay) {
AddWindowManager(window_server(), kTestId1);
const int64_t display_id =
screen_manager().AddDisplay(MakeDisplay(0, 0, 1024, 768, 1.0f));
ASSERT_EQ(1u, display_manager()->displays().size());
Display* display = display_manager()->GetDisplayById(display_id);
// Display should have root window with correct size.
ASSERT_NE(nullptr, display->root_window());
EXPECT_EQ("0,0 1024x768", display->root_window()->bounds().ToString());
// Display should have a WM root window with the correct size too.
EXPECT_EQ(1u, display->num_window_manager_states());
WindowManagerDisplayRoot* root1 =
display->GetWindowManagerDisplayRootForUser(kTestId1);
ASSERT_NE(nullptr, root1);
ASSERT_NE(nullptr, root1->root());
EXPECT_EQ("0,0 1024x768", root1->root()->bounds().ToString());
}
TEST_F(DisplayTest, CreateDisplayBeforeWM) {
// Add one display, no WM exists yet.
const int64_t display_id =
screen_manager().AddDisplay(MakeDisplay(0, 0, 1024, 768, 1.0f));
EXPECT_EQ(1u, display_manager()->displays().size());
Display* display = display_manager()->GetDisplayById(display_id);
// Display should have root window with correct size.
ASSERT_NE(nullptr, display->root_window());
EXPECT_EQ("0,0 1024x768", display->root_window()->bounds().ToString());
// There should be no WM state for display yet.
EXPECT_EQ(0u, display->num_window_manager_states());
EXPECT_EQ(nullptr, display->GetWindowManagerDisplayRootForUser(kTestId1));
AddWindowManager(window_server(), kTestId1);
// After adding a WM display should have WM state and WM root for the display.
EXPECT_EQ(1u, display->num_window_manager_states());
WindowManagerDisplayRoot* root1 =
display->GetWindowManagerDisplayRootForUser(kTestId1);
ASSERT_NE(nullptr, root1);
ASSERT_NE(nullptr, root1->root());
EXPECT_EQ("0,0 1024x768", root1->root()->bounds().ToString());
}
TEST_F(DisplayTest, CreateDisplayWithTwoWindowManagers) {
AddWindowManager(window_server(), kTestId1);
const int64_t display_id = screen_manager().AddDisplay();
Display* display = display_manager()->GetDisplayById(display_id);
// There should be only be one WM at this point.
ASSERT_EQ(1u, display->num_window_manager_states());
EXPECT_NE(nullptr, display->GetWindowManagerDisplayRootForUser(kTestId1));
EXPECT_EQ(nullptr, display->GetWindowManagerDisplayRootForUser(kTestId2));
AddWindowManager(window_server(), kTestId2);
// There should now be two WMs.
ASSERT_EQ(2u, display->num_window_manager_states());
WindowManagerDisplayRoot* root1 =
display->GetWindowManagerDisplayRootForUser(kTestId1);
ASSERT_NE(nullptr, root1);
WindowManagerDisplayRoot* root2 =
display->GetWindowManagerDisplayRootForUser(kTestId2);
ASSERT_NE(nullptr, root2);
// Verify the two WMs have different roots but with the same bounds.
EXPECT_NE(root1, root2);
EXPECT_NE(root1->root(), root2->root());
EXPECT_EQ(root1->root()->bounds(), root2->root()->bounds());
}
TEST_F(DisplayTest, CreateDisplayWithDeviceScaleFactor) {
// The display bounds should be the pixel_size / device_scale_factor.
display::Display display = MakeDisplay(0, 0, 1024, 768, 2.0f);
EXPECT_EQ("0,0 512x384", display.bounds().ToString());
const int64_t display_id = screen_manager().AddDisplay(display);
display.set_id(display_id);
Display* ws_display = display_manager()->GetDisplayById(display_id);
// The root ServerWindow bounds should be in PP.
EXPECT_EQ("0,0 1024x768", ws_display->root_window()->bounds().ToString());
// Modify the display work area to be 48 DIPs smaller.
display::Display modified_display = display;
gfx::Rect modified_work_area = display.work_area();
modified_work_area.set_height(modified_work_area.height() - 48);
modified_display.set_work_area(modified_work_area);
screen_manager().ModifyDisplay(modified_display);
// The display work area should have changed.
EXPECT_EQ("0,0 512x336", ws_display->GetDisplay().work_area().ToString());
// The root ServerWindow should still be in PP after updating the work area.
EXPECT_EQ("0,0 1024x768", ws_display->root_window()->bounds().ToString());
}
TEST_F(DisplayTest, Destruction) {
AddWindowManager(window_server(), kTestId1);
int64_t display_id = screen_manager().AddDisplay();
// Add a second WM.
AddWindowManager(window_server(), kTestId2);
ASSERT_EQ(1u, display_manager()->displays().size());
Display* display = display_manager()->GetDisplayById(display_id);
ASSERT_EQ(2u, display->num_window_manager_states());
// There should be two trees, one for each windowmanager.
EXPECT_EQ(2u, window_server()->num_trees());
{
WindowManagerState* state = GetWindowManagerStateForUser(display, kTestId1);
// Destroy the tree associated with |state|. Should result in deleting
// |state|.
window_server()->DestroyTree(state->window_tree());
ASSERT_EQ(1u, display->num_window_manager_states());
EXPECT_FALSE(GetWindowManagerStateForUser(display, kTestId1));
EXPECT_EQ(1u, display_manager()->displays().size());
EXPECT_EQ(1u, window_server()->num_trees());
}
EXPECT_FALSE(window_server_delegate()->got_on_no_more_displays());
screen_manager().RemoveDisplay(display_id);
// There is still one tree left.
EXPECT_EQ(1u, window_server()->num_trees());
EXPECT_TRUE(window_server_delegate()->got_on_no_more_displays());
}
TEST_F(DisplayTest, EventStateResetOnUserSwitch) {
const int64_t display_id = screen_manager().AddDisplay();
AddWindowManager(window_server(), kTestId1);
AddWindowManager(window_server(), kTestId2);
window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
ASSERT_EQ(1u, display_manager()->displays().size());
Display* display = display_manager()->GetDisplayById(display_id);
WindowManagerState* active_wms =
display->GetActiveWindowManagerDisplayRoot()->window_manager_state();
ASSERT_TRUE(active_wms);
EXPECT_EQ(kTestId1, active_wms->user_id());
static_cast<PlatformDisplayDelegate*>(display)->OnEvent(ui::PointerEvent(
ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(20, 25),
gfx::Point(20, 25), base::TimeTicks(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)));
EXPECT_TRUE(EventDispatcherTestApi(active_wms->event_dispatcher())
.AreAnyPointersDown());
EXPECT_EQ(gfx::Point(20, 25),
active_wms->event_dispatcher()->mouse_pointer_last_location());
// Switch the user. Should trigger resetting state in old event dispatcher
// and update state in new event dispatcher.
window_server()->user_id_tracker()->SetActiveUserId(kTestId2);
EXPECT_NE(
active_wms,
display->GetActiveWindowManagerDisplayRoot()->window_manager_state());
EXPECT_FALSE(EventDispatcherTestApi(active_wms->event_dispatcher())
.AreAnyPointersDown());
active_wms =
display->GetActiveWindowManagerDisplayRoot()->window_manager_state();
EXPECT_EQ(kTestId2, active_wms->user_id());
EXPECT_EQ(gfx::Point(20, 25),
active_wms->event_dispatcher()->mouse_pointer_last_location());
EXPECT_FALSE(EventDispatcherTestApi(active_wms->event_dispatcher())
.AreAnyPointersDown());
}
// Verifies capture fails when wm is inactive and succeeds when wm is active.
TEST_F(DisplayTest, SetCaptureFromWindowManager) {
const int64_t display_id = screen_manager().AddDisplay();
AddWindowManager(window_server(), kTestId1);
AddWindowManager(window_server(), kTestId2);
window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
ASSERT_EQ(1u, display_manager()->displays().size());
Display* display = display_manager()->GetDisplayById(display_id);
WindowManagerState* wms_for_id2 =
GetWindowManagerStateForUser(display, kTestId2);
ASSERT_TRUE(wms_for_id2);
EXPECT_FALSE(wms_for_id2->IsActive());
// Create a child of the root that we can set capture on.
WindowTree* tree = wms_for_id2->window_tree();
ClientWindowId child_window_id;
ASSERT_TRUE(NewWindowInTree(tree, &child_window_id));
WindowTreeTestApi(tree).EnableCapture();
// SetCapture() should fail for user id2 as it is inactive.
EXPECT_FALSE(tree->SetCapture(child_window_id));
// Make the second user active and verify capture works.
window_server()->user_id_tracker()->SetActiveUserId(kTestId2);
EXPECT_TRUE(wms_for_id2->IsActive());
EXPECT_TRUE(tree->SetCapture(child_window_id));
}
TEST_F(DisplayTest, FocusFailsForInactiveUser) {
const int64_t display_id = screen_manager().AddDisplay();
AddWindowManager(window_server(), kTestId1);
TestWindowTreeClient* window_tree_client1 =
window_server_delegate()->last_client();
ASSERT_TRUE(window_tree_client1);
AddWindowManager(window_server(), kTestId2);
window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
ASSERT_EQ(1u, display_manager()->displays().size());
Display* display = display_manager()->GetDisplayById(display_id);
WindowManagerState* wms_for_id2 =
GetWindowManagerStateForUser(display, kTestId2);
wms_for_id2->window_tree()->AddActivationParent(
ClientWindowIdForFirstRoot(wms_for_id2->window_tree()));
ASSERT_TRUE(wms_for_id2);
EXPECT_FALSE(wms_for_id2->IsActive());
ClientWindowId child2_id;
NewWindowInTree(wms_for_id2->window_tree(), &child2_id);
// Focus should fail for windows in inactive window managers.
EXPECT_FALSE(wms_for_id2->window_tree()->SetFocus(child2_id));
// Focus should succeed for the active window manager.
WindowManagerState* wms_for_id1 =
GetWindowManagerStateForUser(display, kTestId1);
ASSERT_TRUE(wms_for_id1);
wms_for_id1->window_tree()->AddActivationParent(
ClientWindowIdForFirstRoot(wms_for_id1->window_tree()));
ClientWindowId child1_id;
NewWindowInTree(wms_for_id1->window_tree(), &child1_id);
EXPECT_TRUE(wms_for_id1->IsActive());
EXPECT_TRUE(wms_for_id1->window_tree()->SetFocus(child1_id));
}
// Verifies a single tree is used for multiple displays.
TEST_F(DisplayTest, MultipleDisplays) {
screen_manager().AddDisplay();
screen_manager().AddDisplay();
AddWindowManager(window_server(), kTestId1);
window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
ASSERT_EQ(1u, window_server_delegate()->bindings()->size());
TestWindowTreeBinding* window_tree_binding =
(*window_server_delegate()->bindings())[0];
WindowTree* tree = window_tree_binding->tree();
ASSERT_EQ(2u, tree->roots().size());
std::set<const ServerWindow*> roots = tree->roots();
auto it = roots.begin();
ServerWindow* root1 = tree->GetWindow((*it)->id());
++it;
ServerWindow* root2 = tree->GetWindow((*it)->id());
ASSERT_NE(root1, root2);
Display* display1 = tree->GetDisplay(root1);
WindowManagerState* display1_wms =
display1->GetWindowManagerDisplayRootForUser(kTestId1)
->window_manager_state();
Display* display2 = tree->GetDisplay(root2);
WindowManagerState* display2_wms =
display2->GetWindowManagerDisplayRootForUser(kTestId1)
->window_manager_state();
EXPECT_EQ(display1_wms->window_tree(), display2_wms->window_tree());
}
// Assertions around destroying a secondary display.
TEST_F(DisplayTest, DestroyingDisplayDoesntDelete) {
AddWindowManager(window_server(), kTestId1);
screen_manager().AddDisplay();
const int64_t secondary_display_id = screen_manager().AddDisplay();
window_server()->user_id_tracker()->SetActiveUserId(kTestId1);
ASSERT_EQ(1u, window_server_delegate()->bindings()->size());
WindowTree* tree = (*window_server_delegate()->bindings())[0]->tree();
ASSERT_EQ(2u, tree->roots().size());
Display* secondary_display =
display_manager()->GetDisplayById(secondary_display_id);
ASSERT_TRUE(secondary_display);
bool secondary_root_destroyed = false;
ServerWindow* secondary_root = GetRootOnDisplay(tree, secondary_display);
ASSERT_TRUE(secondary_root);
ServerWindowDestructionObserver observer(secondary_root,
&secondary_root_destroyed);
ClientWindowId secondary_root_id =
ClientWindowIdForWindow(tree, secondary_root);
TestWindowTreeClient* tree_client =
static_cast<TestWindowTreeClient*>(tree->client());
tree_client->tracker()->changes()->clear();
TestWindowManager* test_window_manager =
window_server_delegate()->last_binding()->window_manager();
EXPECT_FALSE(test_window_manager->got_display_removed());
screen_manager().RemoveDisplay(secondary_display_id);
// Destroying the display should result in the following:
// . The WindowManager should be told it was removed with the right id.
EXPECT_TRUE(test_window_manager->got_display_removed());
EXPECT_EQ(secondary_display_id, test_window_manager->display_removed_id());
EXPECT_FALSE(secondary_root_destroyed);
// The window should still be valid on the server side.
ASSERT_TRUE(tree->GetWindowByClientId(secondary_root_id));
// No changes.
ASSERT_EQ(0u, tree_client->tracker()->changes()->size());
// The window should be destroyed when the client says so.
ASSERT_TRUE(tree->DeleteWindow(secondary_root_id));
EXPECT_TRUE(secondary_root_destroyed);
}
} // namespace test
} // namespace ws
} // namespace ui