blob: e4ac1a1233252c128d6871da8979d38ddf667f68 [file] [log] [blame]
// Copyright 2014 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/common/system/web_notification/ash_popup_alignment_delegate.h"
#include <utility>
#include <vector>
#include "ash/common/shelf/wm_shelf.h"
#include "ash/common/wm_lookup.h"
#include "ash/common/wm_window.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/keyboard/keyboard_switches.h"
#include "ui/keyboard/keyboard_util.h"
#include "ui/message_center/message_center_style.h"
namespace ash {
// TODO(jamescook): Move this to //ash/common. http://crbug.com/620955
class AshPopupAlignmentDelegateTest : public test::AshTestBase {
public:
AshPopupAlignmentDelegateTest() {}
~AshPopupAlignmentDelegateTest() override {}
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
keyboard::switches::kEnableVirtualKeyboard);
test::AshTestBase::SetUp();
SetAlignmentDelegate(
base::MakeUnique<AshPopupAlignmentDelegate>(GetPrimaryShelf()));
}
void TearDown() override {
alignment_delegate_.reset();
test::AshTestBase::TearDown();
}
protected:
enum Position { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, OUTSIDE };
AshPopupAlignmentDelegate* alignment_delegate() {
return alignment_delegate_.get();
}
void UpdateWorkArea(AshPopupAlignmentDelegate* alignment_delegate,
const display::Display& display) {
alignment_delegate->StartObserving(display::Screen::GetScreen(), display);
// Update the layout
alignment_delegate->UpdateWorkArea();
}
void SetAlignmentDelegate(
std::unique_ptr<AshPopupAlignmentDelegate> delegate) {
if (!delegate.get()) {
alignment_delegate_.reset();
return;
}
alignment_delegate_ = std::move(delegate);
UpdateWorkArea(alignment_delegate_.get(),
display::Screen::GetScreen()->GetPrimaryDisplay());
}
Position GetPositionInDisplay(const gfx::Point& point) {
const gfx::Rect work_area =
display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
const gfx::Point center_point = work_area.CenterPoint();
if (work_area.x() > point.x() || work_area.y() > point.y() ||
work_area.right() < point.x() || work_area.bottom() < point.y()) {
return OUTSIDE;
}
if (center_point.x() < point.x())
return (center_point.y() < point.y()) ? BOTTOM_RIGHT : TOP_RIGHT;
else
return (center_point.y() < point.y()) ? BOTTOM_LEFT : TOP_LEFT;
}
gfx::Rect GetWorkArea() { return alignment_delegate_->work_area_; }
private:
std::unique_ptr<AshPopupAlignmentDelegate> alignment_delegate_;
DISALLOW_COPY_AND_ASSIGN(AshPopupAlignmentDelegateTest);
};
TEST_F(AshPopupAlignmentDelegateTest, ShelfAlignment) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
gfx::Point toast_point;
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
GetPrimaryShelf()->SetAlignment(SHELF_ALIGNMENT_RIGHT);
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
GetPrimaryShelf()->SetAlignment(SHELF_ALIGNMENT_LEFT);
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_LEFT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_TRUE(alignment_delegate()->IsFromLeft());
}
TEST_F(AshPopupAlignmentDelegateTest, LockScreen) {
const gfx::Rect toast_size(0, 0, 10, 10);
GetPrimaryShelf()->SetAlignment(SHELF_ALIGNMENT_LEFT);
gfx::Point toast_point;
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_LEFT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_TRUE(alignment_delegate()->IsFromLeft());
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
}
TEST_F(AshPopupAlignmentDelegateTest, AutoHide) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
// Create a window, otherwise autohide doesn't work.
std::unique_ptr<views::Widget> widget = CreateTestWidget(
nullptr, kShellWindowId_DefaultContainer, gfx::Rect(0, 0, 50, 50));
WmShelf* shelf = GetPrimaryShelf();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_LT(baseline, alignment_delegate()->GetBaseLine());
}
// Verify that docked window doesn't affect the popup alignment.
TEST_F(AshPopupAlignmentDelegateTest, DockedWindow) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
std::unique_ptr<views::Widget> widget = CreateTestWidget(
nullptr, kShellWindowId_DockedContainer, gfx::Rect(0, 0, 50, 50));
// Left-side dock should not affect popup alignment
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_EQ(baseline, alignment_delegate()->GetBaseLine());
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
// Force dock to right-side
WmShelf* shelf = GetPrimaryShelf();
shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
// Right-side dock should not affect popup alignment
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_EQ(baseline, alignment_delegate()->GetBaseLine());
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
}
TEST_F(AshPopupAlignmentDelegateTest, DisplayResize) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
UpdateDisplay("800x800");
EXPECT_LT(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_LT(baseline, alignment_delegate()->GetBaseLine());
UpdateDisplay("400x400");
EXPECT_GT(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_GT(baseline, alignment_delegate()->GetBaseLine());
}
TEST_F(AshPopupAlignmentDelegateTest, DockedMode) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
// Emulate the docked mode; enter to an extended mode, then invoke
// OnNativeDisplaysChanged() with the info for the secondary display only.
UpdateDisplay("600x600,800x800");
std::vector<display::ManagedDisplayInfo> new_info;
new_info.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1u).id()));
display_manager()->OnNativeDisplaysChanged(new_info);
EXPECT_LT(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_LT(baseline, alignment_delegate()->GetBaseLine());
}
TEST_F(AshPopupAlignmentDelegateTest, TrayHeight) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
// Simulate the system tray bubble being open.
const int kTrayHeight = 100;
alignment_delegate()->SetTrayBubbleHeight(kTrayHeight);
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_EQ(baseline - kTrayHeight - message_center::kMarginBetweenItems,
alignment_delegate()->GetBaseLine());
}
TEST_F(AshPopupAlignmentDelegateTest, Extended) {
UpdateDisplay("600x600,800x800");
SetAlignmentDelegate(
base::MakeUnique<AshPopupAlignmentDelegate>(GetPrimaryShelf()));
display::Display second_display = display_manager()->GetDisplayAt(1u);
WmShelf* second_shelf =
WmLookup::Get()
->GetRootWindowControllerWithDisplayId(second_display.id())
->GetShelf();
AshPopupAlignmentDelegate for_2nd_display(second_shelf);
UpdateWorkArea(&for_2nd_display, second_display);
// Make sure that the toast position on the secondary display is
// positioned correctly.
EXPECT_LT(1300, for_2nd_display.GetToastOriginX(gfx::Rect(0, 0, 10, 10)));
EXPECT_LT(700, for_2nd_display.GetBaseLine());
}
TEST_F(AshPopupAlignmentDelegateTest, Unified) {
display_manager()->SetUnifiedDesktopEnabled(true);
// Reset the delegate as the primary display's shelf will be destroyed during
// transition.
SetAlignmentDelegate(nullptr);
UpdateDisplay("600x600,800x800");
SetAlignmentDelegate(
base::MakeUnique<AshPopupAlignmentDelegate>(GetPrimaryShelf()));
EXPECT_GT(600,
alignment_delegate()->GetToastOriginX(gfx::Rect(0, 0, 10, 10)));
}
// Tests that when the keyboard is showing that notifications appear above it,
// and that they return to normal once the keyboard is gone.
TEST_F(AshPopupAlignmentDelegateTest, KeyboardShowing) {
ASSERT_TRUE(keyboard::IsKeyboardEnabled());
ASSERT_TRUE(keyboard::IsKeyboardOverscrollEnabled());
UpdateDisplay("600x600");
int baseline = alignment_delegate()->GetBaseLine();
WmShelf* shelf = GetPrimaryShelf();
gfx::Rect keyboard_bounds(0, 300, 600, 300);
shelf->SetVirtualKeyboardBoundsForTesting(keyboard_bounds);
int keyboard_baseline = alignment_delegate()->GetBaseLine();
EXPECT_NE(baseline, keyboard_baseline);
EXPECT_GT(keyboard_bounds.y(), keyboard_baseline);
shelf->SetVirtualKeyboardBoundsForTesting(gfx::Rect());
EXPECT_EQ(baseline, alignment_delegate()->GetBaseLine());
}
} // namespace ash