// Copyright (c) 2013 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/shelf/shelf_widget.h" #include "ash/bubble/bubble_constants.h" #include "ash/constants/ash_features.h" #include "ash/constants/ash_switches.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" #include "ash/keyboard/ui/keyboard_util.h" #include "ash/keyboard/ui/test/keyboard_test_util.h" #include "ash/public/cpp/keyboard/keyboard_switches.h" #include "ash/public/cpp/shelf_config.h" #include "ash/public/cpp/shelf_types.h" #include "ash/public/cpp/tablet_mode.h" #include "ash/root_window_controller.h" #include "ash/screen_util.h" #include "ash/shelf/drag_handle.h" #include "ash/shelf/hotseat_transition_animator.h" #include "ash/shelf/login_shelf_view.h" #include "ash/shelf/shelf.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_navigation_widget.h" #include "ash/shelf/shelf_test_util.h" #include "ash/shelf/shelf_view.h" #include "ash/shelf/shelf_view_test_api.h" #include "ash/shell.h" #include "ash/style/dark_light_mode_controller_impl.h" #include "ash/system/message_center/unified_message_center_bubble.h" #include "ash/system/status_area_widget.h" #include "ash/system/unified/unified_system_tray.h" #include "ash/system/unified/unified_system_tray_bubble.h" #include "ash/test/ash_test_base.h" #include "ash/test/ash_test_helper.h" #include "ash/test_shell_delegate.h" #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" #include "ash/wm/window_util.h" #include "base/callback_helpers.h" #include "base/command_line.h" #include "base/i18n/rtl.h" #include "base/run_loop.h" #include "base/test/icu_test_util.h" #include "base/test/scoped_feature_list.h" #include "chromeos/constants/chromeos_features.h" #include "components/session_manager/session_manager_types.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/base/models/image_model.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/display/display.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" #include "ui/message_center/message_center.h" #include "ui/message_center/public/cpp/notification.h" #include "ui/message_center/public/cpp/notification_types.h" #include "ui/message_center/public/cpp/notifier_id.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" #include "url/gurl.h" namespace ash { namespace { ShelfWidget* GetShelfWidget() { return AshTestBase::GetPrimaryShelf()->shelf_widget(); } ShelfLayoutManager* GetShelfLayoutManager() { return GetShelfWidget()->shelf_layout_manager(); } void TestLauncherAlignment(aura::Window* root, ShelfAlignment alignment, const gfx::Rect& expected) { Shelf::ForWindow(root)->SetAlignment(alignment); EXPECT_EQ(expected.ToString(), display::Screen::GetScreen() ->GetDisplayNearestWindow(root) .work_area() .ToString()); } using SessionState = session_manager::SessionState; using ShelfWidgetTest = AshTestBase; TEST_F(ShelfWidgetTest, TestAlignment) { UpdateDisplay("401x400"); const int bottom_inset = 400 - ShelfConfig::Get()->shelf_size(); { SCOPED_TRACE("Single Bottom"); TestLauncherAlignment(Shell::GetPrimaryRootWindow(), ShelfAlignment::kBottom, gfx::Rect(0, 0, 401, bottom_inset)); } { SCOPED_TRACE("Single Locked"); TestLauncherAlignment(Shell::GetPrimaryRootWindow(), ShelfAlignment::kBottomLocked, gfx::Rect(0, 0, 401, bottom_inset)); } { SCOPED_TRACE("Single Right"); TestLauncherAlignment(Shell::GetPrimaryRootWindow(), ShelfAlignment::kRight, gfx::Rect(0, 0, bottom_inset + 1, 400)); } { SCOPED_TRACE("Single Left"); TestLauncherAlignment( Shell::GetPrimaryRootWindow(), ShelfAlignment::kLeft, gfx::Rect(ShelfConfig::Get()->shelf_size(), 0, bottom_inset + 1, 400)); } } TEST_F(ShelfWidgetTest, TestAlignmentForMultipleDisplays) { UpdateDisplay("301x300,501x500"); const int shelf_inset_first = 300 - ShelfConfig::Get()->shelf_size(); const int shelf_inset_second = 500 - ShelfConfig::Get()->shelf_size(); aura::Window::Windows root_windows = Shell::GetAllRootWindows(); { SCOPED_TRACE("Primary Bottom"); TestLauncherAlignment(root_windows[0], ShelfAlignment::kBottom, gfx::Rect(0, 0, 301, shelf_inset_first)); } { SCOPED_TRACE("Primary Locked"); TestLauncherAlignment(root_windows[0], ShelfAlignment::kBottomLocked, gfx::Rect(0, 0, 301, shelf_inset_first)); } { SCOPED_TRACE("Primary Right"); TestLauncherAlignment(root_windows[0], ShelfAlignment::kRight, gfx::Rect(0, 0, shelf_inset_first + 1, 300)); } { SCOPED_TRACE("Primary Left"); TestLauncherAlignment(root_windows[0], ShelfAlignment::kLeft, gfx::Rect(ShelfConfig::Get()->shelf_size(), 0, shelf_inset_first + 1, 300)); } { SCOPED_TRACE("Secondary Bottom"); TestLauncherAlignment(root_windows[1], ShelfAlignment::kBottom, gfx::Rect(301, 0, 501, shelf_inset_second)); } { SCOPED_TRACE("Secondary Locked"); TestLauncherAlignment(root_windows[1], ShelfAlignment::kBottomLocked, gfx::Rect(301, 0, 501, shelf_inset_second)); } { SCOPED_TRACE("Secondary Right"); TestLauncherAlignment(root_windows[1], ShelfAlignment::kRight, gfx::Rect(301, 0, shelf_inset_second + 1, 500)); } { SCOPED_TRACE("Secondary Left"); TestLauncherAlignment(root_windows[1], ShelfAlignment::kLeft, gfx::Rect(301 + ShelfConfig::Get()->shelf_size(), 0, shelf_inset_second + 1, 500)); } } class ShelfWidgetDarkLightModeTest : public ShelfWidgetTest { public: ShelfWidgetDarkLightModeTest() { scoped_feature_list_.InitAndEnableFeature( chromeos::features::kDarkLightMode); } void SetUp() override { ShelfWidgetTest::SetUp(); // Enable tablet mode transition screenshots to simulate production behavior // where shelf layers get recreated during the tablet mode transition. TabletModeController::SetUseScreenshotForTest(true); } private: base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(ShelfWidgetDarkLightModeTest, TabletModeTransition) { ShelfWidget* const shelf_widget = GetShelfWidget(); TabletMode::Waiter enter_waiter(/*enable=*/true); TabletModeControllerTestApi().EnterTabletMode(); enter_waiter.Wait(); shelf_widget->background_animator_for_testing() ->CompleteAnimationForTesting(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kTransparent60), shelf_widget->GetShelfBackgroundColor()); EXPECT_EQ(0.0, shelf_widget->GetOpaqueBackground()->background_blur()); auto* dark_light_mode_controller = ash::DarkLightModeControllerImpl::Get(); dark_light_mode_controller->ToggleColorMode(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kTransparent60), shelf_widget->GetShelfBackgroundColor()); EXPECT_EQ(0.0f, shelf_widget->GetOpaqueBackground()->background_blur()); TabletMode::Waiter leave_waiter(/*enable=*/false); TabletModeControllerTestApi().LeaveTabletMode(); leave_waiter.Wait(); shelf_widget->background_animator_for_testing() ->CompleteAnimationForTesting(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kTransparent80), shelf_widget->GetShelfBackgroundColor()); EXPECT_GT(shelf_widget->GetOpaqueBackground()->background_blur(), 0.0f); dark_light_mode_controller->ToggleColorMode(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kTransparent80), shelf_widget->GetShelfBackgroundColor()); EXPECT_GT(shelf_widget->GetOpaqueBackground()->background_blur(), 0.0f); } TEST_F(ShelfWidgetDarkLightModeTest, TabletModeTransitionWithWindowOpen) { ShelfWidget* const shelf_widget = GetShelfWidget(); auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); TabletMode::Waiter enter_waiter(/*enable=*/true); TabletModeControllerTestApi().EnterTabletMode(); enter_waiter.Wait(); shelf_widget->background_animator_for_testing() ->CompleteAnimationForTesting(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kOpaque), shelf_widget->GetShelfBackgroundColor()); EXPECT_EQ(0.0f, shelf_widget->GetOpaqueBackground()->background_blur()); auto* dark_light_mode_controller = ash::DarkLightModeControllerImpl::Get(); dark_light_mode_controller->ToggleColorMode(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kOpaque), shelf_widget->GetShelfBackgroundColor()); EXPECT_EQ(0.0f, shelf_widget->GetOpaqueBackground()->background_blur()); TabletMode::Waiter leave_waiter(/*enable=*/false); TabletModeControllerTestApi().LeaveTabletMode(); leave_waiter.Wait(); shelf_widget->background_animator_for_testing() ->CompleteAnimationForTesting(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kTransparent80), shelf_widget->GetShelfBackgroundColor()); EXPECT_GT(shelf_widget->GetOpaqueBackground()->background_blur(), 0.0f); dark_light_mode_controller->ToggleColorMode(); EXPECT_EQ(AshColorProvider::Get()->GetBaseLayerColor( AshColorProvider::BaseLayerType::kTransparent80), shelf_widget->GetShelfBackgroundColor()); EXPECT_GT(shelf_widget->GetOpaqueBackground()->background_blur(), 0.0f); } class ShelfWidgetLayoutBasicsTest : public ShelfWidgetTest, public testing::WithParamInterface> { public: ShelfWidgetLayoutBasicsTest() { scoped_feature_list_.InitWithFeatureState( features::kHideShelfControlsInTabletMode, std::get<1>(GetParam())); } ~ShelfWidgetLayoutBasicsTest() override = default; void SetUp() override { ShelfWidgetTest::SetUp(); Shell::Get()->tablet_mode_controller()->SetEnabledForTest( std::get<0>(GetParam())); } private: base::test::ScopedFeatureList scoped_feature_list_; }; // First parameter - whether test should run in tablet mode. // Second parameter - whether shelf navigation buttons are enabled in tablet // mode. INSTANTIATE_TEST_SUITE_P(All, ShelfWidgetLayoutBasicsTest, testing::Combine(testing::Bool(), testing::Bool())); // Makes sure the shelf is initially sized correctly in clamshell/tablet. TEST_P(ShelfWidgetLayoutBasicsTest, LauncherInitiallySized) { ShelfWidget* shelf_widget = GetShelfWidget(); ShelfLayoutManager* shelf_layout_manager = GetShelfLayoutManager(); ASSERT_TRUE(shelf_layout_manager); ASSERT_TRUE(shelf_widget->status_area_widget()); int status_width = shelf_widget->status_area_widget()->GetWindowBoundsInScreen().width(); // Test only makes sense if the status is > 0, which it better be. EXPECT_GT(status_width, 0); const int total_width = screen_util::GetDisplayBoundsWithShelf(shelf_widget->GetNativeWindow()) .width(); const gfx::Rect nav_widget_clip_rect = shelf_widget->navigation_widget()->GetLayer()->clip_rect(); if (!nav_widget_clip_rect.IsEmpty()) EXPECT_EQ(gfx::Point(), nav_widget_clip_rect.origin()); int nav_width = nav_widget_clip_rect.IsEmpty() ? shelf_widget->navigation_widget()->GetWindowBoundsInScreen().width() : nav_widget_clip_rect.width(); ASSERT_GE(nav_width, 0); if (nav_width) { nav_width -= ShelfConfig::Get()->control_button_edge_spacing( true /* is_primary_axis_edge */); } const int hotseat_width = shelf_widget->hotseat_widget()->GetWindowBoundsInScreen().width(); const int margins = 2 * ShelfConfig::Get()->GetAppIconGroupMargin(); EXPECT_EQ(status_width, total_width - nav_width - hotseat_width - margins); } TEST_F(ShelfWidgetTest, CheckVerticalShelfCornersInOverviewMode) { // Verify corners of the shelf on the left alignment. Shelf* shelf = GetPrimaryShelf(); shelf->SetAlignment(ShelfAlignment::kLeft); EXPECT_EQ(ShelfAlignment::kLeft, GetPrimaryShelf()->alignment()); ShelfWidget* const shelf_widget = GetShelfWidget(); ui::Layer* opaque_background_layer = shelf_widget->GetDelegateViewOpaqueBackgroundLayerForTesting(); // The gfx::RoundedCornersF object is considered empty when all of the // corners are squared (no effective radius). EXPECT_FALSE(opaque_background_layer->rounded_corner_radii().IsEmpty()); OverviewController* overview_controller = Shell::Get()->overview_controller(); // Enter overview mode. Expect the shelf with square corners. EnterOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_TRUE(opaque_background_layer->rounded_corner_radii().IsEmpty()); // Exit overview mode. Expect the shelf with rounded corners. ExitOverview(); WaitForOverviewAnimation(/*enter=*/false); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_FALSE(opaque_background_layer->rounded_corner_radii().IsEmpty()); // Verify corners of the shelf on the right alignment. shelf->SetAlignment(ShelfAlignment::kRight); EXPECT_EQ(ShelfAlignment::kRight, GetPrimaryShelf()->alignment()); EXPECT_FALSE(opaque_background_layer->rounded_corner_radii().IsEmpty()); // Enter overview mode. Expect the shelf with square corners. EnterOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_TRUE(opaque_background_layer->rounded_corner_radii().IsEmpty()); // Exit overview mode. Expect the shelf with rounded corners. ExitOverview(); WaitForOverviewAnimation(/*enter=*/false); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_FALSE(opaque_background_layer->rounded_corner_radii().IsEmpty()); } // Verifies when the shell is deleted with a full screen window we don't crash. TEST_F(ShelfWidgetTest, DontReferenceShelfAfterDeletion) { views::Widget* widget = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.bounds = gfx::Rect(0, 0, 200, 200); params.context = GetContext(); // Widget is now owned by the parent window. widget->Init(std::move(params)); widget->SetFullscreen(true); } // Verifies shelf is created with correct size after user login and when its // container and status widget has finished sizing. // See http://crbug.com/252533 TEST_F(ShelfWidgetTest, ShelfInitiallySizedAfterLogin) { ClearLogin(); UpdateDisplay("300x200,400x300"); // Both displays have a shelf controller. aura::Window::Windows roots = Shell::GetAllRootWindows(); Shelf* shelf1 = Shelf::ForWindow(roots[0]); Shelf* shelf2 = Shelf::ForWindow(roots[1]); ASSERT_TRUE(shelf1); ASSERT_TRUE(shelf2); // Both shelf controllers have a shelf widget. ShelfWidget* shelf_widget1 = shelf1->shelf_widget(); ShelfWidget* shelf_widget2 = shelf2->shelf_widget(); ASSERT_TRUE(shelf_widget1); ASSERT_TRUE(shelf_widget2); // Simulate login. CreateUserSessions(1); const int total_width1 = screen_util::GetDisplayBoundsWithShelf(shelf_widget1->GetNativeWindow()) .width(); const int nav_width1 = shelf_widget1->navigation_widget()->GetWindowBoundsInScreen().width() - ShelfConfig::Get()->control_button_edge_spacing( true /* is_primary_axis_edge */); const int hotseat_width1 = shelf_widget1->hotseat_widget()->GetWindowBoundsInScreen().width(); const int margins = 2 * ShelfConfig::Get()->GetAppIconGroupMargin(); const int total_width2 = screen_util::GetDisplayBoundsWithShelf(shelf_widget2->GetNativeWindow()) .width(); const int nav_width2 = shelf_widget2->navigation_widget()->GetWindowBoundsInScreen().width() - ShelfConfig::Get()->control_button_edge_spacing( true /* is_primary_axis_edge */); const int hotseat_width2 = shelf_widget2->hotseat_widget()->GetWindowBoundsInScreen().width(); // The shelf view and status area horizontally fill the shelf widget. const int status_width1 = shelf_widget1->status_area_widget()->GetWindowBoundsInScreen().width(); EXPECT_GT(status_width1, 0); EXPECT_EQ(total_width1, nav_width1 + hotseat_width1 + margins + status_width1); const int status_width2 = shelf_widget2->status_area_widget()->GetWindowBoundsInScreen().width(); EXPECT_GT(status_width2, 0); EXPECT_EQ(total_width2, nav_width2 + hotseat_width2 + margins + status_width2); } // Tests that the shelf has a slightly larger hit-region for touch-events when // it's in the auto-hidden state. TEST_F(ShelfWidgetTest, HiddenShelfHitTestTouch) { Shelf* shelf = GetPrimaryShelf(); ShelfWidget* shelf_widget = GetShelfWidget(); gfx::Rect shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); EXPECT_TRUE(!shelf_bounds.IsEmpty()); ShelfLayoutManager* shelf_layout_manager = shelf_widget->shelf_layout_manager(); ASSERT_TRUE(shelf_layout_manager); EXPECT_EQ(SHELF_VISIBLE, shelf_layout_manager->visibility_state()); // Create a widget to make sure that the shelf does auto-hide. views::Widget* widget = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.bounds = gfx::Rect(0, 0, 200, 200); params.context = GetContext(); // Widget is now owned by the parent window. widget->Init(std::move(params)); widget->Show(); aura::Window* root = shelf_widget->GetNativeWindow()->GetRootWindow(); ui::EventTargeter* targeter = root->GetHost()->dispatcher()->GetDefaultEventTargeter(); // Touch just over the shelf. Since the shelf is visible, the window-targeter // should not find the shelf as the target. { gfx::Point event_location(20, shelf_bounds.y() - 1); ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, event_location, ui::EventTimeForNow(), ui::PointerDetails(ui::EventPointerType::kTouch, 0)); EXPECT_NE(shelf_widget->GetNativeWindow(), targeter->FindTargetForEvent(root, &touch)); } // Now auto-hide (hidden) the shelf. shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); shelf_layout_manager->LayoutShelf(); EXPECT_EQ(SHELF_AUTO_HIDE, shelf_layout_manager->visibility_state()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_layout_manager->auto_hide_state()); shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); EXPECT_TRUE(!shelf_bounds.IsEmpty()); // Touch just over the shelf again. This time, the targeter should find the // shelf as the target. { gfx::Point event_location(20, shelf_bounds.y() - 1); ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, event_location, ui::EventTimeForNow(), ui::PointerDetails(ui::EventPointerType::kTouch, 0)); EXPECT_EQ(shelf_widget->GetNativeWindow(), targeter->FindTargetForEvent(root, &touch)); } } class LtrRtlShelfWidgetTest : public ShelfWidgetTest, public testing::WithParamInterface { public: LtrRtlShelfWidgetTest() : scoped_locale_(GetParam() ? "he" : "") {} LtrRtlShelfWidgetTest(const LtrRtlShelfWidgetTest&) = delete; LtrRtlShelfWidgetTest& operator=(const LtrRtlShelfWidgetTest&) = delete; ~LtrRtlShelfWidgetTest() override = default; private: // Restores locale to the default when destructor is called. base::test::ScopedRestoreICUDefaultLocale scoped_locale_; }; INSTANTIATE_TEST_SUITE_P(All, LtrRtlShelfWidgetTest, testing::Bool()); TEST_P(LtrRtlShelfWidgetTest, MessageCenterBounds) { // Add a notification to force the message center bubble to show with the // UnifiedSystemTrayBubble. message_center::MessageCenter::Get()->AddNotification( std::make_unique( message_center::NOTIFICATION_TYPE_SIMPLE, base::NumberToString(0), u"test title", u"test message", ui::ImageModel(), std::u16string(), GURL(), message_center::NotifierId(), message_center::RichNotificationData(), new message_center::NotificationDelegate())); auto shelf_alignments = {ShelfAlignment::kBottom, ShelfAlignment::kLeft, ShelfAlignment::kRight}; for (const auto& alignment : shelf_alignments) { GetPrimaryShelf()->SetAlignment(alignment); // Show UnifiedSystemTrayBubble, which shows a message center bubble as well // in a separate widget. GetPrimaryUnifiedSystemTray()->ShowBubble(); gfx::Rect message_center_bubble_bounds_in_screen = GetPrimaryUnifiedSystemTray() ->message_center_bubble() ->GetBoundsInScreen(); gfx::Rect unified_system_tray_bubble_bounds_in_screen = GetPrimaryUnifiedSystemTray()->bubble()->GetBoundsInScreen(); // The MessageCenterBubble and UnifiedSystemTrayBubble should be flush // despite the shelf alignment. EXPECT_EQ(message_center_bubble_bounds_in_screen.x(), unified_system_tray_bubble_bounds_in_screen.x()); EXPECT_EQ(message_center_bubble_bounds_in_screen.width(), unified_system_tray_bubble_bounds_in_screen.width()); GetPrimaryUnifiedSystemTray()->CloseBubble(); } } // Tests that the shelf lets mouse-events close to the edge fall through to the // window underneath. TEST_F(ShelfWidgetTest, ShelfEdgeOverlappingWindowHitTestMouse) { UpdateDisplay("500x400"); ShelfWidget* shelf_widget = GetShelfWidget(); gfx::Rect shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); EXPECT_TRUE(!shelf_bounds.IsEmpty()); ShelfLayoutManager* shelf_layout_manager = shelf_widget->shelf_layout_manager(); ASSERT_TRUE(shelf_layout_manager); EXPECT_EQ(SHELF_VISIBLE, shelf_layout_manager->visibility_state()); // Create a Widget which overlaps the shelf in both left and bottom // alignments. const int kOverlapSize = 15; const int kWindowHeight = 200; const int kWindowWidth = 200; const gfx::Rect bounds(shelf_bounds.height() - kOverlapSize, shelf_bounds.y() - kWindowHeight + kOverlapSize, kWindowWidth, kWindowHeight); views::Widget* widget = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.bounds = bounds; params.context = GetContext(); // Widget is now owned by the parent window. widget->Init(std::move(params)); // Explicitly set the bounds which will allow the widget to overlap the shelf. widget->SetBounds(bounds); widget->Show(); gfx::Rect widget_bounds = widget->GetWindowBoundsInScreen(); EXPECT_TRUE(widget_bounds.Intersects(shelf_bounds)); aura::Window* root = widget->GetNativeWindow()->GetRootWindow(); ui::EventTargeter* targeter = root->GetHost()->dispatcher()->GetDefaultEventTargeter(); { // Create a mouse-event targeting the top of the shelf widget. The // window-targeter should find |widget| as the target (instead of the // shelf). gfx::Point event_location(widget_bounds.x() + 5, shelf_bounds.y() + 1); ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location, ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse); EXPECT_EQ(widget->GetNativeWindow(), target); } // Change shelf alignment to verify that the targeter insets are updated. Shelf* shelf = GetPrimaryShelf(); shelf->SetAlignment(ShelfAlignment::kLeft); shelf_layout_manager->LayoutShelf(); shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); { // Create a mouse-event targeting the right edge of the shelf widget. The // window-targeter should find |widget| as the target (instead of the // shelf). gfx::Point event_location(shelf_bounds.right() - 1, widget_bounds.y() + 5); ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location, ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse); EXPECT_EQ(widget->GetNativeWindow(), target); } // Now restore shelf alignment (bottom) and auto-hide (hidden) the shelf. shelf->SetAlignment(ShelfAlignment::kBottom); shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); shelf_layout_manager->LayoutShelf(); EXPECT_EQ(SHELF_AUTO_HIDE, shelf_layout_manager->visibility_state()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_layout_manager->auto_hide_state()); shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); EXPECT_TRUE(!shelf_bounds.IsEmpty()); // Move |widget| so it still overlaps the shelf. widget->SetBounds(gfx::Rect(0, shelf_bounds.y() - kWindowHeight + kOverlapSize, kWindowWidth, kWindowHeight)); widget_bounds = widget->GetWindowBoundsInScreen(); EXPECT_TRUE(widget_bounds.Intersects(shelf_bounds)); { // Create a mouse-event targeting the top of the shelf widget. This time, // window-target should find the shelf as the target. gfx::Point event_location(widget_bounds.x() + 5, shelf_bounds.y() + 1); ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location, ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse); EXPECT_EQ(shelf_widget->GetNativeWindow(), target); } } class TransitionAnimationWaiter : public HotseatTransitionAnimator::TestObserver { public: explicit TransitionAnimationWaiter( HotseatTransitionAnimator* hotseat_transition_animator) : hotseat_transition_animator_(hotseat_transition_animator) { hotseat_transition_animator_->SetTestObserver(this); } ~TransitionAnimationWaiter() override { hotseat_transition_animator_->SetTestObserver(nullptr); } void Wait() { run_loop_ = std::make_unique(); run_loop_->Run(); } private: void OnTransitionTestAnimationEnded() override { DCHECK(run_loop_.get()); run_loop_->Quit(); } HotseatTransitionAnimator* hotseat_transition_animator_ = nullptr; std::unique_ptr run_loop_; }; // Tests the drag handle bounds and visibility when the in-app shelf is shown. TEST_F(ShelfWidgetTest, OpaqueBackgroundAndDragHandleTransition) { ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); UpdateDisplay("800x700"); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); // Create a window to transition to the in-app shelf. auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); { TransitionAnimationWaiter waiter( GetShelfWidget()->hotseat_transition_animator()); waiter.Wait(); } // Ensure the ShelfWidget and drag handle have the correct bounds and // visibility for in-app shelf. EXPECT_EQ(GetShelfWidget()->GetWindowBoundsInScreen(), gfx::Rect(0, 660, 800, 40)); EXPECT_EQ(GetShelfWidget()->GetDragHandle()->bounds(), gfx::Rect(360, 18, 80, 4)); ASSERT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); window->Hide(); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); } // Tests the shelf widget does not animate for hotseat transitions during tablet // mode start. TEST_F(ShelfWidgetTest, NoAnimatingBackgroundDuringTabletModeStartToInApp) { UpdateDisplay("800x700"); // Create a window so tablet mode uses in-app shelf. auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); EXPECT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); EXPECT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); } // Tests the shelf widget does not animate for hotseat transitions during tablet // mode end. TEST_F(ShelfWidgetTest, NoAnimatingBackgroundDuringTabletModeEndFromInApp) { UpdateDisplay("800x700"); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); // Create a window so tablet mode uses in-app shelf. auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); EXPECT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false); EXPECT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); EXPECT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); } // Tests the shelf widget does not animate for hotseat transitions during tablet // mode start with no app windows. TEST_F(ShelfWidgetTest, NoAnimatingBackgroundDuringTabletModeStartToHome) { UpdateDisplay("800x700"); EXPECT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); EXPECT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); } // Tests the shelf widget does not animate for hotseat transitions during tablet // mode end with no app windows. TEST_F(ShelfWidgetTest, NoAnimatingBackgroundDuringTabletModeEndFromHome) { UpdateDisplay("800x700"); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); EXPECT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false); EXPECT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); EXPECT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); } // Tests the shelf widget does not animate for hotseat transitions if the screen // is locked. TEST_F(ShelfWidgetTest, NoAnimatingBackgroundOnLockScreen) { Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); UpdateDisplay("800x700"); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); // Create a window to transition to the in-app shelf. auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); // At this point animations have zero duration, so the transition happens // immediately. ASSERT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); GetSessionControllerClient()->SetSessionState(SessionState::LOCKED); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); // Hide the test window, and verify that does not start shelf animation. window->Hide(); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); // Ensure the ShelfWidget and the drag handle are still hidden (i.e. in home // screen state) when the screen is unlocked. GetSessionControllerClient()->SetSessionState(SessionState::ACTIVE); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); } // Tests the shelf widget animations for hotseat transitions are stopped when // the screen is locked. TEST_F(ShelfWidgetTest, ScreenLockStopsHotseatTransitionAnimation) { Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); UpdateDisplay("800x700"); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); // Create a window to transition to the in-app shelf. auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_TRUE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_TRUE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); GetSessionControllerClient()->SetSessionState(SessionState::LOCKED); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_FALSE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); GetSessionControllerClient()->SetSessionState(SessionState::ACTIVE); // Ensure the ShelfWidget and the drag handle have the correct bounds and // visibility for in-app shelf when the screen is unlocked. ASSERT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); EXPECT_EQ(GetShelfWidget()->GetWindowBoundsInScreen(), gfx::Rect(0, 660, 800, 40)); EXPECT_EQ(GetShelfWidget()->GetDragHandle()->bounds(), gfx::Rect(360, 18, 80, 4)); } // Tests the shelf widget's opaque background gets reshown if hotseat transition // from kShown state gets interrupted by a transition back to kShown state. TEST_F(ShelfWidgetTest, OpaqueBackgroundReshownAfterTransitionFromHomeChangesBackToHome) { Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); UpdateDisplay("800x700"); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); // Create a window to transition to the in-app shelf. auto window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_TRUE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_TRUE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); const gfx::Rect animating_background_bounds = GetShelfWidget()->GetAnimatingBackground()->bounds(); EXPECT_NE(GetShelfWidget()->GetAnimatingBackground()->transform(), GetShelfWidget()->GetAnimatingBackground()->GetTargetTransform()); // Go back home before the transition to in-app shelf finishes. window->Hide(); // The shelf background should still be animating at this point, but to // different bounds. ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_TRUE(GetShelfWidget()->GetAnimatingBackground()->visible()); ASSERT_TRUE(GetShelfWidget() ->GetAnimatingBackground() ->GetAnimator() ->is_animating()); EXPECT_EQ(animating_background_bounds, GetShelfWidget()->GetAnimatingBackground()->bounds()); // The original animation did not have a chance to update the transform yet, // so the current transform matches the original state, that matches the new // target state. EXPECT_EQ(GetShelfWidget()->GetAnimatingBackground()->transform(), GetShelfWidget()->GetAnimatingBackground()->GetTargetTransform()); // Run the current animation to the end, end verify the opaque background is // reshown after ending tablet mode. GetShelfWidget()->GetAnimatingBackground()->GetAnimator()->StopAnimating(); ASSERT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); ASSERT_TRUE(GetShelfWidget()->GetOpaqueBackground()->visible()); ASSERT_FALSE(GetShelfWidget()->GetAnimatingBackground()->visible()); } class ShelfWidgetAfterLoginTest : public AshTestBase { public: ShelfWidgetAfterLoginTest() { set_start_session(false); } ShelfWidgetAfterLoginTest(const ShelfWidgetAfterLoginTest&) = delete; ShelfWidgetAfterLoginTest& operator=(const ShelfWidgetAfterLoginTest&) = delete; ~ShelfWidgetAfterLoginTest() override = default; void TestShelf(ShelfAlignment alignment, ShelfAutoHideBehavior auto_hide_behavior, ShelfVisibilityState expected_shelf_visibility_state, ShelfAutoHideState expected_shelf_auto_hide_state) { // Simulate login. CreateUserSessions(1); // Simulate shelf settings being applied from profile prefs. Shelf* shelf = GetPrimaryShelf(); ASSERT_NE(nullptr, shelf); shelf->SetAlignment(alignment); shelf->SetAutoHideBehavior(auto_hide_behavior); shelf->UpdateVisibilityState(); // Ensure settings are applied correctly. EXPECT_EQ(alignment, shelf->alignment()); EXPECT_EQ(auto_hide_behavior, shelf->auto_hide_behavior()); EXPECT_EQ(expected_shelf_visibility_state, shelf->GetVisibilityState()); EXPECT_EQ(expected_shelf_auto_hide_state, shelf->GetAutoHideState()); } }; TEST_F(ShelfWidgetAfterLoginTest, InitialValues) { // Ensure shelf components are created. Shelf* shelf = GetPrimaryShelf(); ASSERT_NE(nullptr, shelf); ShelfWidget* shelf_widget = GetShelfWidget(); ASSERT_NE(nullptr, shelf_widget); ASSERT_NE(nullptr, shelf_widget->shelf_view_for_testing()); ASSERT_NE(nullptr, shelf_widget->GetLoginShelfView()); ASSERT_NE(nullptr, shelf_widget->shelf_layout_manager()); // Ensure settings are correct before login. EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); EXPECT_EQ(ShelfAlignment::kBottomLocked, shelf->alignment()); EXPECT_EQ(ShelfAutoHideBehavior::kAlwaysHidden, shelf->auto_hide_behavior()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); // Simulate login. CreateUserSessions(1); // Ensure settings are correct after login. EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); EXPECT_EQ(ShelfAlignment::kBottom, shelf->alignment()); EXPECT_EQ(ShelfAutoHideBehavior::kNever, shelf->auto_hide_behavior()); // "Hidden" is the default state when auto-hide is turned off. EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); } TEST_F(ShelfWidgetAfterLoginTest, CreateAutoHideAlwaysShelf) { // The actual auto hide state is shown because there are no open windows. TestShelf(ShelfAlignment::kBottom, ShelfAutoHideBehavior::kAlways, SHELF_AUTO_HIDE, SHELF_AUTO_HIDE_SHOWN); } TEST_F(ShelfWidgetAfterLoginTest, CreateAutoHideNeverShelf) { // The auto hide state 'HIDDEN' is returned for any non-auto-hide behavior. TestShelf(ShelfAlignment::kLeft, ShelfAutoHideBehavior::kNever, SHELF_VISIBLE, SHELF_AUTO_HIDE_HIDDEN); } TEST_F(ShelfWidgetAfterLoginTest, CreateAutoHideAlwaysHideShelf) { // The auto hide state 'HIDDEN' is returned for any non-auto-hide behavior. TestShelf(ShelfAlignment::kRight, ShelfAutoHideBehavior::kAlwaysHidden, SHELF_HIDDEN, SHELF_AUTO_HIDE_HIDDEN); } TEST_F(ShelfWidgetAfterLoginTest, CreateLockedShelf) { // The auto hide state 'HIDDEN' is returned for any non-auto-hide behavior. TestShelf(ShelfAlignment::kBottomLocked, ShelfAutoHideBehavior::kNever, SHELF_VISIBLE, SHELF_AUTO_HIDE_HIDDEN); } class ShelfWidgetViewsVisibilityTest : public AshTestBase { public: ShelfWidgetViewsVisibilityTest() { set_start_session(false); } ShelfWidgetViewsVisibilityTest(const ShelfWidgetViewsVisibilityTest&) = delete; ShelfWidgetViewsVisibilityTest& operator=( const ShelfWidgetViewsVisibilityTest&) = delete; ~ShelfWidgetViewsVisibilityTest() override = default; enum ShelfVisibility { kNone, // Shelf views hidden. kShelf, // ShelfView visible. kLoginShelf, // LoginShelfView visible. }; void InitShelfVariables() { // Create setup with 2 displays primary and secondary. UpdateDisplay("800x600,800x600"); aura::Window::Windows root_windows = Shell::GetAllRootWindows(); ASSERT_EQ(2u, root_windows.size()); primary_shelf_widget_ = Shelf::ForWindow(root_windows[0])->shelf_widget(); ASSERT_NE(nullptr, primary_shelf_widget_); secondary_shelf_widget_ = Shelf::ForWindow(root_windows[1])->shelf_widget(); ASSERT_NE(nullptr, secondary_shelf_widget_); } void ExpectVisible(session_manager::SessionState state, ShelfVisibility primary_shelf_visibility, ShelfVisibility secondary_shelf_visibility) { GetSessionControllerClient()->SetSessionState(state); EXPECT_EQ(primary_shelf_visibility == kNone, !primary_shelf_widget_->IsVisible()); if (primary_shelf_visibility != kNone) { EXPECT_EQ(primary_shelf_visibility == kLoginShelf, primary_shelf_widget_->GetLoginShelfView()->GetVisible()); EXPECT_EQ(primary_shelf_visibility == kShelf, primary_shelf_widget_->shelf_view_for_testing()->GetVisible()); } EXPECT_EQ(secondary_shelf_visibility == kNone, !secondary_shelf_widget_->IsVisible()); if (secondary_shelf_visibility != kNone) { EXPECT_EQ(secondary_shelf_visibility == kLoginShelf, secondary_shelf_widget_->GetLoginShelfView()->GetVisible()); EXPECT_EQ( secondary_shelf_visibility == kShelf, secondary_shelf_widget_->shelf_view_for_testing()->GetVisible()); } } private: ShelfWidget* primary_shelf_widget_ = nullptr; ShelfWidget* secondary_shelf_widget_ = nullptr; }; TEST_F(ShelfWidgetViewsVisibilityTest, LoginViewsLockViews) { ASSERT_NO_FATAL_FAILURE(InitShelfVariables()); ExpectVisible(SessionState::UNKNOWN, kNone /*primary*/, kNone /*secondary*/); ExpectVisible(SessionState::OOBE, kLoginShelf, kNone); ExpectVisible(SessionState::LOGIN_PRIMARY, kLoginShelf, kNone); SimulateUserLogin("user1@test.com"); ExpectVisible(SessionState::LOGGED_IN_NOT_ACTIVE, kLoginShelf, kNone); ExpectVisible(SessionState::ACTIVE, kShelf, kShelf); ExpectVisible(SessionState::LOCKED, kLoginShelf, kNone); ExpectVisible(SessionState::ACTIVE, kShelf, kShelf); ExpectVisible(SessionState::LOGIN_SECONDARY, kLoginShelf, kNone); ExpectVisible(SessionState::ACTIVE, kShelf, kShelf); } class ShelfWidgetVirtualKeyboardTest : public AshTestBase { protected: void SetUp() override { base::CommandLine::ForCurrentProcess()->AppendSwitch( keyboard::switches::kEnableVirtualKeyboard); AshTestBase::SetUp(); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); keyboard::test::WaitUntilLoaded(); // These tests only apply to the floating virtual keyboard, as it is the // only case where both the virtual keyboard and the shelf are visible. const gfx::Rect keyboard_bounds(0, 0, 1, 1); keyboard_ui_controller()->SetContainerType( keyboard::ContainerType::kFloating, keyboard_bounds, base::DoNothing()); } keyboard::KeyboardUIController* keyboard_ui_controller() { return keyboard::KeyboardUIController::Get(); } }; TEST_F(ShelfWidgetVirtualKeyboardTest, ClickingHidesVirtualKeyboard) { keyboard_ui_controller()->ShowKeyboard(false /* locked */); ASSERT_TRUE(keyboard::WaitUntilShown()); ui::test::EventGenerator* generator = GetEventGenerator(); generator->set_current_screen_location( GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint()); generator->ClickLeftButton(); // Times out if test fails. ASSERT_TRUE(keyboard::WaitUntilHidden()); } TEST_F(ShelfWidgetVirtualKeyboardTest, TappingHidesVirtualKeyboard) { keyboard_ui_controller()->ShowKeyboard(false /* locked */); ASSERT_TRUE(keyboard::WaitUntilShown()); ui::test::EventGenerator* generator = GetEventGenerator(); generator->set_current_screen_location( GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint()); generator->PressTouch(); // Times out if test fails. ASSERT_TRUE(keyboard::WaitUntilHidden()); } TEST_F(ShelfWidgetVirtualKeyboardTest, DoesNotHideLockedVirtualKeyboard) { keyboard_ui_controller()->ShowKeyboard(true /* locked */); ASSERT_TRUE(keyboard::WaitUntilShown()); ui::test::EventGenerator* generator = GetEventGenerator(); generator->set_current_screen_location( GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint()); generator->ClickLeftButton(); EXPECT_FALSE(keyboard::IsKeyboardHiding()); generator->PressTouch(); EXPECT_FALSE(keyboard::IsKeyboardHiding()); } } // namespace } // namespace ash