blob: 00a1f007685ff3613d4017cc577d758f82494c58 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/functional/callback_forward.h"
#include "base/test/test_future.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/scoped_display_for_new_windows.h"
namespace ash {
using ScreenAshTest = AshTestBase;
// Tests that ScreenAsh::GetWindowAtScreenPoint() returns the correct window on
// the correct display.
TEST_F(ScreenAshTest, TestGetWindowAtScreenPoint) {
UpdateDisplay("300x200,500x400");
aura::test::TestWindowDelegate delegate;
std::unique_ptr<aura::Window> win1(CreateTestWindowInShellWithDelegate(
&delegate, 0, gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> win2(CreateTestWindowInShellWithDelegate(
&delegate, 1, gfx::Rect(300, 200, 100, 100)));
ASSERT_NE(win1->GetRootWindow(), win2->GetRootWindow());
EXPECT_EQ(win1.get(),
display::Screen::Get()->GetWindowAtScreenPoint(gfx::Point(50, 60)));
EXPECT_EQ(win2.get(), display::Screen::Get()->GetWindowAtScreenPoint(
gfx::Point(350, 260)));
}
TEST_F(ScreenAshTest, GetDisplayForNewWindows) {
UpdateDisplay("300x200,500x400");
display::Screen* screen = display::Screen::Get();
const std::vector<display::Display> displays = screen->GetAllDisplays();
ASSERT_EQ(2u, displays.size());
// The display for new windows defaults to primary display.
EXPECT_EQ(displays[0].id(), screen->GetDisplayForNewWindows().id());
// The display for new windows is updated when the root window for new windows
// changes.
display::ScopedDisplayForNewWindows scoped_display(
Shell::GetAllRootWindows()[1]);
EXPECT_EQ(displays[1].id(), screen->GetDisplayForNewWindows().id());
}
namespace {
// Simulates an observer that tries to get the primary display when notified of
// displays addition or removal when switching to or from the Unified Desktop
// mode.
class TestDisplayRemoveObserver : public display::DisplayObserver {
public:
TestDisplayRemoveObserver() = default;
TestDisplayRemoveObserver(const TestDisplayRemoveObserver&) = delete;
TestDisplayRemoveObserver& operator=(const TestDisplayRemoveObserver&) =
delete;
~TestDisplayRemoveObserver() override = default;
int added_displays() const { return added_displays_; }
int removed_displays() const { return removed_displays_; }
// display::DisplayObserver:
void OnDisplayAdded(const display::Display& new_display) override {
TestPrimaryDisplay();
++added_displays_;
}
void OnDisplaysRemoved(const display::Displays& removed_displays) override {
TestPrimaryDisplay();
removed_displays_ += removed_displays.size();
}
private:
void TestPrimaryDisplay() const {
auto display = display::Screen::Get()->GetPrimaryDisplay();
DCHECK_NE(display.id(), display::kInvalidDisplayId);
}
int added_displays_ = 0;
int removed_displays_ = 0;
};
// Invokes the given callback when the code is inside the destructor of the
// root window.
class RootWindowDestructorObserver : aura::WindowObserver {
public:
RootWindowDestructorObserver(aura::Window* child_window,
base::OnceClosure callback)
: callback_(std::move(callback)),
root_window_(child_window->GetRootWindow()) {
root_window_->AddObserver(this);
}
~RootWindowDestructorObserver() override {
if (root_window_) {
root_window_->RemoveObserver(this);
}
}
private:
void OnWindowDestroying(aura::Window* window) override {
CHECK_EQ(window, root_window_);
std::move(callback_).Run();
root_window_ = nullptr;
}
base::OnceClosure callback_;
raw_ptr<aura::Window> root_window_;
};
} // namespace
// Switching to Unified Desktop removes all current displays (including primary
// display) and replaces them with the unified display. The display manager
// notifies observers of display removals before display additions. At this
// point if an observer tries to get the primary display, it could lead to a
// crash because all displays have been removed. This test makes sure doesn't
// happen anymore. https://crbug.com/866714.
TEST_F(ScreenAshTest, TestNoCrashesOnGettingPrimaryDisplayOnDisplayRemoved) {
UpdateDisplay("400x500,300x200");
TestDisplayRemoveObserver observer;
display_manager()->AddDisplayObserver(&observer);
// Enter Unified Mode.
display_manager()->SetUnifiedDesktopEnabled(true);
EXPECT_TRUE(display_manager()->IsInUnifiedMode());
EXPECT_EQ(observer.added_displays(), 1);
EXPECT_EQ(observer.removed_displays(), 2);
// Exit Unified Mode, there shouldn't be any crashes either.
display_manager()->SetUnifiedDesktopEnabled(false);
EXPECT_FALSE(display_manager()->IsInUnifiedMode());
EXPECT_EQ(observer.added_displays(), 3);
EXPECT_EQ(observer.removed_displays(), 3);
display_manager()->RemoveDisplayObserver(&observer);
}
TEST_F(ScreenAshTest,
GetDisplayNearestWindowShouldNotCrashWhenWindowIsBeingDestroyed) {
UpdateDisplay("400x500,300x200");
std::unique_ptr<aura::Window> window_on_second_display(
CreateTestWindow(gfx::Rect(400, 0, 100, 100)));
base::test::TestFuture<void> root_window_destroyed_waiter;
RootWindowDestructorObserver observer(
window_on_second_display.get(),
base::BindOnce(
[](aura::Window* window) {
// This callback is invoked from inside the destructor of the root
// window. Calling `GetDisplayNearestWindow` from here used to
// crash (https://crbug.com/376575664).
// This tests it doesn't.
display::Screen::Get()->GetDisplayNearestWindow(window);
},
window_on_second_display.get())
.Then(root_window_destroyed_waiter.GetCallback()));
// Destroy the second display
UpdateDisplay("400x500");
EXPECT_TRUE(root_window_destroyed_waiter.Wait());
}
} // namespace ash