123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- // Copyright (c) 2012 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.h"
- #include <algorithm>
- #include <memory>
- #include <queue>
- #include <vector>
- #include "ash/display/mouse_cursor_event_filter.h"
- #include "ash/drag_drop/drag_drop_controller.h"
- #include "ash/drag_drop/drag_drop_controller_test_api.h"
- #include "ash/keyboard/ui/keyboard_ui_controller.h"
- #include "ash/keyboard/ui/keyboard_util.h"
- #include "ash/public/cpp/ash_prefs.h"
- #include "ash/public/cpp/keyboard/keyboard_switches.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/public/cpp/test/shell_test_api.h"
- #include "ash/root_window_controller.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/session/test_session_controller_client.h"
- #include "ash/shelf/home_button.h"
- #include "ash/shelf/shelf.h"
- #include "ash/shelf/shelf_layout_manager.h"
- #include "ash/shelf/shelf_navigation_widget.h"
- #include "ash/shelf/shelf_widget.h"
- #include "ash/system/status_area_widget.h"
- #include "ash/test/ash_test_base.h"
- #include "ash/test/ash_test_helper.h"
- #include "ash/test/test_widget_builder.h"
- #include "ash/test_shell_delegate.h"
- #include "ash/wallpaper/wallpaper_widget_controller.h"
- #include "ash/wm/desks/desks_util.h"
- #include "ash/wm/overview/overview_controller.h"
- #include "base/command_line.h"
- #include "base/containers/flat_set.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "components/account_id/account_id.h"
- #include "ui/aura/env.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_event_dispatcher.h"
- #include "ui/base/models/simple_menu_model.h"
- #include "ui/display/scoped_display_for_new_windows.h"
- #include "ui/events/test/event_generator.h"
- #include "ui/events/test/events_test_utils.h"
- #include "ui/events/test/test_event_handler.h"
- #include "ui/gfx/geometry/size.h"
- #include "ui/views/controls/menu/menu_controller.h"
- #include "ui/views/controls/menu/menu_runner.h"
- #include "ui/views/widget/widget.h"
- #include "ui/views/widget/widget_delegate.h"
- #include "ui/views/window/dialog_delegate.h"
- using aura::RootWindow;
- namespace ash {
- namespace {
- aura::Window* GetActiveDeskContainer() {
- return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
- desks_util::GetActiveDeskContainerId());
- }
- aura::Window* GetAlwaysOnTopContainer() {
- return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
- kShellWindowId_AlwaysOnTopContainer);
- }
- // Expect ALL the containers!
- void ExpectAllContainers() {
- aura::Window* root_window = Shell::GetPrimaryRootWindow();
- // Validate no duplicate container IDs.
- base::flat_set<int> container_ids;
- std::queue<aura::Window*> window_queue;
- window_queue.push(root_window);
- while (!window_queue.empty()) {
- aura::Window* current_window = window_queue.front();
- window_queue.pop();
- for (aura::Window* child : current_window->children())
- window_queue.push(child);
- const int id = current_window->GetId();
- // Skip windows with no IDs.
- if (id == aura::Window::kInitialId)
- continue;
- EXPECT_TRUE(container_ids.insert(id).second)
- << "Found duplicate ID: " << id
- << " at window: " << current_window->GetName();
- }
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_WallpaperContainer));
- for (int desk_id : desks_util::GetDesksContainersIds())
- EXPECT_TRUE(Shell::GetContainer(root_window, desk_id));
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_AlwaysOnTopContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window, kShellWindowId_ShelfContainer));
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_SystemModalContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window,
- kShellWindowId_LockScreenWallpaperContainer));
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_LockScreenContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window,
- kShellWindowId_LockSystemModalContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window, kShellWindowId_MenuContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window,
- kShellWindowId_DragImageAndTooltipContainer));
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_SettingBubbleContainer));
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_OverlayContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window,
- kShellWindowId_ImeWindowParentContainer));
- EXPECT_TRUE(Shell::GetContainer(root_window,
- kShellWindowId_VirtualKeyboardContainer));
- EXPECT_TRUE(
- Shell::GetContainer(root_window, kShellWindowId_MouseCursorContainer));
- // Phantom window is not a container.
- EXPECT_EQ(0u, container_ids.count(kShellWindowId_PhantomWindow));
- EXPECT_FALSE(Shell::GetContainer(root_window, kShellWindowId_PhantomWindow));
- }
- std::unique_ptr<views::WidgetDelegateView> CreateModalWidgetDelegate() {
- auto delegate = std::make_unique<views::WidgetDelegateView>();
- delegate->SetCanResize(true);
- delegate->SetModalType(ui::MODAL_TYPE_SYSTEM);
- delegate->SetOwnedByWidget(true);
- delegate->SetTitle(u"Modal Window");
- return delegate;
- }
- class SimpleMenuDelegate : public ui::SimpleMenuModel::Delegate {
- public:
- SimpleMenuDelegate() = default;
- SimpleMenuDelegate(const SimpleMenuDelegate&) = delete;
- SimpleMenuDelegate& operator=(const SimpleMenuDelegate&) = delete;
- ~SimpleMenuDelegate() override = default;
- bool IsCommandIdChecked(int command_id) const override { return false; }
- bool IsCommandIdEnabled(int command_id) const override { return true; }
- void ExecuteCommand(int command_id, int event_flags) override {}
- };
- } // namespace
- class ShellTest : public AshTestBase {
- public:
- void TestCreateWindow(views::Widget::InitParams::Type type,
- bool always_on_top,
- aura::Window* expected_container) {
- TestWidgetBuilder builder;
- if (always_on_top)
- builder.SetZOrderLevel(ui::ZOrderLevel::kFloatingWindow);
- views::Widget* widget =
- builder.SetWidgetType(type).BuildOwnedByNativeWidget();
- EXPECT_TRUE(
- expected_container->Contains(widget->GetNativeWindow()->parent()))
- << "TestCreateWindow: type=" << type
- << ", always_on_top=" << always_on_top;
- widget->Close();
- }
- void LockScreenAndVerifyMenuClosed() {
- // Verify a menu is open before locking.
- views::MenuController* menu_controller =
- views::MenuController::GetActiveInstance();
- DCHECK(menu_controller);
- EXPECT_EQ(views::MenuController::ExitType::kNone,
- menu_controller->exit_type());
- // Create a LockScreen window.
- views::Widget* lock_widget =
- TestWidgetBuilder()
- .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW)
- .SetShow(false)
- .BuildOwnedByNativeWidget();
- Shell::GetContainer(Shell::GetPrimaryRootWindow(),
- kShellWindowId_LockScreenContainer)
- ->AddChild(lock_widget->GetNativeView());
- lock_widget->Show();
- // Simulate real screen locker to change session state to LOCKED
- // when it is shown.
- GetSessionControllerClient()->LockScreen();
- SessionControllerImpl* controller = Shell::Get()->session_controller();
- EXPECT_TRUE(controller->IsScreenLocked());
- EXPECT_TRUE(lock_widget->GetNativeView()->HasFocus());
- // Verify menu is closed.
- EXPECT_EQ(nullptr, views::MenuController::GetActiveInstance());
- lock_widget->Close();
- GetSessionControllerClient()->UnlockScreen();
- }
- };
- TEST_F(ShellTest, CreateWindow) {
- // Normal window should be created in default container.
- TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW,
- false, // always_on_top
- GetActiveDeskContainer());
- TestCreateWindow(views::Widget::InitParams::TYPE_POPUP,
- false, // always_on_top
- GetActiveDeskContainer());
- // Always-on-top window and popup are created in always-on-top container.
- TestCreateWindow(views::Widget::InitParams::TYPE_WINDOW,
- true, // always_on_top
- GetAlwaysOnTopContainer());
- TestCreateWindow(views::Widget::InitParams::TYPE_POPUP,
- true, // always_on_top
- GetAlwaysOnTopContainer());
- }
- // Verifies that a window with a preferred size is created centered on the
- // default display for new windows. Mojo apps like shortcut_viewer rely on this
- // behavior.
- TEST_F(ShellTest, CreateWindowWithPreferredSize) {
- UpdateDisplay("1024x768,800x600");
- aura::Window* secondary_root = Shell::GetAllRootWindows()[1];
- display::ScopedDisplayForNewWindows scoped_display(secondary_root);
- views::Widget::InitParams params;
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- // Don't specify bounds, parent or context.
- {
- auto delegate = std::make_unique<views::WidgetDelegateView>();
- delegate->SetPreferredSize(gfx::Size(400, 300));
- params.delegate = delegate.release();
- }
- views::Widget widget;
- params.context = GetContext();
- widget.Init(std::move(params));
- // Widget is centered on secondary display.
- EXPECT_EQ(secondary_root, widget.GetNativeWindow()->GetRootWindow());
- EXPECT_EQ(GetSecondaryDisplay().work_area().CenterPoint(),
- widget.GetRestoredBounds().CenterPoint());
- }
- TEST_F(ShellTest, ChangeZOrderLevel) {
- // Creates a normal window.
- views::Widget* widget = TestWidgetBuilder().BuildOwnedByNativeWidget();
- // It should be in the active desk container.
- EXPECT_TRUE(
- GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
- // Set the z-order to float.
- widget->SetZOrderLevel(ui::ZOrderLevel::kFloatingWindow);
- // And it should in always on top container now.
- EXPECT_EQ(GetAlwaysOnTopContainer(), widget->GetNativeWindow()->parent());
- // Put the z-order back to normal.
- widget->SetZOrderLevel(ui::ZOrderLevel::kNormal);
- // It should go back to the active desk container.
- EXPECT_TRUE(
- GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
- // Set the z-order again to the normal value.
- widget->SetZOrderLevel(ui::ZOrderLevel::kNormal);
- // Should have no effect and we are still in the the active desk container.
- EXPECT_TRUE(
- GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
- widget->Close();
- }
- TEST_F(ShellTest, CreateModalWindow) {
- // Create a normal window.
- views::Widget* widget = TestWidgetBuilder().BuildOwnedByNativeWidget();
- // It should be in the active desk container.
- EXPECT_TRUE(
- GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
- // Create a modal window.
- views::Widget* modal_widget = views::Widget::CreateWindowWithParent(
- CreateModalWidgetDelegate(), widget->GetNativeView());
- modal_widget->Show();
- // It should be in modal container.
- aura::Window* modal_container = Shell::GetContainer(
- Shell::GetPrimaryRootWindow(), kShellWindowId_SystemModalContainer);
- EXPECT_EQ(modal_container, modal_widget->GetNativeWindow()->parent());
- modal_widget->Close();
- widget->Close();
- }
- TEST_F(ShellTest, CreateLockScreenModalWindow) {
- // Create a normal window.
- views::Widget* widget = TestWidgetBuilder().BuildOwnedByNativeWidget();
- EXPECT_TRUE(widget->GetNativeView()->HasFocus());
- // It should be in the active desk container.
- EXPECT_TRUE(
- GetActiveDeskContainer()->Contains(widget->GetNativeWindow()->parent()));
- GetSessionControllerClient()->LockScreen();
- // Create a LockScreen window.
- views::Widget* lock_widget =
- TestWidgetBuilder().SetShow(false).BuildOwnedByNativeWidget();
- Shell::GetContainer(Shell::GetPrimaryRootWindow(),
- kShellWindowId_LockScreenContainer)
- ->AddChild(lock_widget->GetNativeView());
- lock_widget->Show();
- EXPECT_TRUE(lock_widget->GetNativeView()->HasFocus());
- // It should be in LockScreen container.
- aura::Window* lock_screen = Shell::GetContainer(
- Shell::GetPrimaryRootWindow(), kShellWindowId_LockScreenContainer);
- EXPECT_EQ(lock_screen, lock_widget->GetNativeWindow()->parent());
- // Create a modal window with a lock window as parent.
- views::Widget* lock_modal_widget = views::Widget::CreateWindowWithParent(
- CreateModalWidgetDelegate(), lock_widget->GetNativeView());
- lock_modal_widget->Show();
- EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus());
- // It should be in LockScreen modal container.
- aura::Window* lock_modal_container =
- Shell::GetContainer(Shell::GetPrimaryRootWindow(),
- kShellWindowId_LockSystemModalContainer);
- EXPECT_EQ(lock_modal_container,
- lock_modal_widget->GetNativeWindow()->parent());
- // Create a modal window with a normal window as parent.
- views::Widget* modal_widget = views::Widget::CreateWindowWithParent(
- CreateModalWidgetDelegate(), widget->GetNativeView());
- modal_widget->Show();
- // Window on lock screen shouldn't lost focus.
- EXPECT_FALSE(modal_widget->GetNativeView()->HasFocus());
- EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus());
- // It should be in non-LockScreen modal container.
- aura::Window* modal_container = Shell::GetContainer(
- Shell::GetPrimaryRootWindow(), kShellWindowId_SystemModalContainer);
- EXPECT_EQ(modal_container, modal_widget->GetNativeWindow()->parent());
- // Modal widget without parent, caused crash see crbug.com/226141
- views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
- CreateModalWidgetDelegate(), GetContext(), nullptr);
- modal_dialog->Show();
- EXPECT_FALSE(modal_dialog->GetNativeView()->HasFocus());
- EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus());
- modal_dialog->Close();
- modal_widget->Close();
- modal_widget->Close();
- lock_modal_widget->Close();
- lock_widget->Close();
- widget->Close();
- }
- TEST_F(ShellTest, IsScreenLocked) {
- SessionControllerImpl* controller = Shell::Get()->session_controller();
- GetSessionControllerClient()->LockScreen();
- EXPECT_TRUE(controller->IsScreenLocked());
- GetSessionControllerClient()->UnlockScreen();
- EXPECT_FALSE(controller->IsScreenLocked());
- }
- TEST_F(ShellTest, LockScreenClosesActiveMenu) {
- SimpleMenuDelegate menu_delegate;
- std::unique_ptr<ui::SimpleMenuModel> menu_model(
- new ui::SimpleMenuModel(&menu_delegate));
- menu_model->AddItem(0, u"Menu item");
- views::Widget* widget = Shell::GetPrimaryRootWindowController()
- ->wallpaper_widget_controller()
- ->GetWidget();
- std::unique_ptr<views::MenuRunner> menu_runner(
- new views::MenuRunner(menu_model.get(), views::MenuRunner::CONTEXT_MENU));
- menu_runner->RunMenuAt(widget, nullptr, gfx::Rect(),
- views::MenuAnchorPosition::kTopLeft,
- ui::MENU_SOURCE_MOUSE);
- LockScreenAndVerifyMenuClosed();
- }
- TEST_F(ShellTest, ManagedWindowModeBasics) {
- // We start with the usual window containers.
- ExpectAllContainers();
- // Shelf is visible.
- ShelfWidget* shelf_widget = GetPrimaryShelf()->shelf_widget();
- EXPECT_TRUE(shelf_widget->IsVisible());
- // Shelf is at bottom-left of screen.
- EXPECT_EQ(0, shelf_widget->GetWindowBoundsInScreen().x());
- EXPECT_EQ(
- Shell::GetPrimaryRootWindow()->GetHost()->GetBoundsInPixels().height(),
- shelf_widget->GetWindowBoundsInScreen().bottom());
- // We have a wallpaper but not a bare layer.
- // TODO (antrim): enable once we find out why it fails component build.
- // WallpaperWidgetController* wallpaper =
- // Shell::GetPrimaryRootWindow()->
- // GetProperty(kWindowDesktopComponent);
- // EXPECT_TRUE(wallpaper);
- // EXPECT_TRUE(wallpaper->widget());
- // EXPECT_FALSE(wallpaper->layer());
- // Create a normal window. It is not maximized.
- views::Widget* widget = TestWidgetBuilder()
- .SetBounds(gfx::Rect(11, 22, 300, 400))
- .BuildOwnedByNativeWidget();
- EXPECT_FALSE(widget->IsMaximized());
- // Clean up.
- widget->Close();
- }
- // Tests that the cursor-filter is ahead of the drag-drop controller in the
- // pre-target list.
- TEST_F(ShellTest, TestPreTargetHandlerOrder) {
- Shell* shell = Shell::Get();
- ui::EventTargetTestApi test_api(shell);
- ShellTestApi shell_test_api;
- ui::EventHandlerList handlers = test_api.GetPreTargetHandlers();
- ui::EventHandlerList::const_iterator cursor_filter =
- std::find(handlers.begin(), handlers.end(), shell->mouse_cursor_filter());
- ui::EventHandlerList::const_iterator drag_drop = std::find(
- handlers.begin(), handlers.end(), shell_test_api.drag_drop_controller());
- EXPECT_NE(handlers.end(), cursor_filter);
- EXPECT_NE(handlers.end(), drag_drop);
- EXPECT_GT(drag_drop, cursor_filter);
- }
- // Verifies an EventHandler added to Env gets notified from EventGenerator.
- TEST_F(ShellTest, EnvPreTargetHandler) {
- ui::test::TestEventHandler event_handler;
- aura::Env::GetInstance()->AddPreTargetHandler(&event_handler);
- ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
- generator.MoveMouseBy(1, 1);
- EXPECT_NE(0, event_handler.num_mouse_events());
- aura::Env::GetInstance()->RemovePreTargetHandler(&event_handler);
- }
- // Verifies that pressing tab on an empty shell (one with no windows visible)
- // will put focus on the shelf. This enables keyboard only users to get to the
- // shelf without knowing the more obscure accelerators. Tab should move focus to
- // the home button, shift + tab to the status widget. From there, normal shelf
- // tab behaviour takes over, and the shell no longer catches that event.
- TEST_F(ShellTest, NoWindowTabFocus) {
- ExpectAllContainers();
- StatusAreaWidget* status_area_widget =
- GetPrimaryShelf()->status_area_widget();
- ShelfNavigationWidget* home_button = GetPrimaryShelf()->navigation_widget();
- // Create a normal window. It is not maximized.
- auto widget = CreateTestWidget();
- // Hit tab with window open, and expect that focus is not on the navigation
- // widget or status widget.
- PressAndReleaseKey(ui::VKEY_TAB);
- EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
- EXPECT_FALSE(status_area_widget->GetNativeView()->HasFocus());
- // Minimize the window, hit tab and expect that focus is on the launcher.
- widget->Minimize();
- PressAndReleaseKey(ui::VKEY_TAB);
- EXPECT_TRUE(home_button->GetNativeView()->HasFocus());
- // Show (to steal focus back before continuing testing) and close the window.
- widget->Show();
- widget->Close();
- EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
- // Confirm that pressing tab when overview mode is open does not go to home
- // button. Tab should be handled by overview mode and not hit the shell event
- // handler.
- EnterOverview();
- PressAndReleaseKey(ui::VKEY_TAB);
- EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
- ExitOverview();
- // Hit shift tab and expect that focus is on status widget.
- PressAndReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
- EXPECT_TRUE(status_area_widget->GetNativeView()->HasFocus());
- }
- // This verifies WindowObservers are removed when a window is destroyed after
- // the Shell is destroyed. This scenario (aura::Windows being deleted after the
- // Shell) occurs if someone is holding a reference to an unparented Window, as
- // is the case with a RenderWidgetHostViewAura that isn't on screen. As long as
- // everything is ok, we won't crash. If there is a bug, window's destructor will
- // notify some deleted object (say VideoDetector or ActivationController) and
- // this will crash.
- class ShellTest2 : public AshTestBase {
- public:
- ShellTest2() = default;
- ShellTest2(const ShellTest2&) = delete;
- ShellTest2& operator=(const ShellTest2&) = delete;
- ~ShellTest2() override = default;
- protected:
- std::unique_ptr<aura::Window> window_;
- };
- TEST_F(ShellTest2, DontCrashWhenWindowDeleted) {
- window_ = std::make_unique<aura::Window>(nullptr,
- aura::client::WINDOW_TYPE_UNKNOWN);
- window_->Init(ui::LAYER_NOT_DRAWN);
- }
- using ShellLoginTest = NoSessionAshTestBase;
- TEST_F(ShellLoginTest, DragAndDropDisabledBeforeLogin) {
- DragDropController* drag_drop_controller =
- ShellTestApi().drag_drop_controller();
- DragDropControllerTestApi drag_drop_controller_test_api(drag_drop_controller);
- EXPECT_FALSE(drag_drop_controller_test_api.enabled());
- SimulateUserLogin("user1@test.com");
- EXPECT_TRUE(drag_drop_controller_test_api.enabled());
- }
- using NoDuplicateShellContainerIdsTest = AshTestBase;
- TEST_F(NoDuplicateShellContainerIdsTest, ValidateContainersIds) {
- ExpectAllContainers();
- }
- } // namespace ash
|