| // Copyright 2018 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/accessibility/magnifier/docked_magnifier_controller.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "ash/accessibility/magnifier/magnifier_test_utils.h" |
| #include "ash/capture_mode/capture_mode_controller.h" |
| #include "ash/capture_mode/capture_mode_metrics.h" |
| #include "ash/capture_mode/capture_mode_session.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/constants/ash_switches.h" |
| #include "ash/display/display_util.h" |
| #include "ash/display/window_tree_host_manager.h" |
| #include "ash/host/ash_window_tree_host.h" |
| #include "ash/public/cpp/shelf_config.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/session/test_session_controller_client.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test/ash_test_helper.h" |
| #include "ash/test/test_window_builder.h" |
| #include "ash/wm/desks/legacy_desk_bar_view.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/overview/overview_grid.h" |
| #include "ash/wm/overview/overview_item.h" |
| #include "ash/wm/overview/overview_item_view.h" |
| #include "ash/wm/overview/overview_test_util.h" |
| #include "ash/wm/overview/overview_utils.h" |
| #include "ash/wm/splitview/split_view_controller.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" |
| #include "ash/wm/window_mini_view_header_view.h" |
| #include "ash/wm/window_state.h" |
| #include "base/command_line.h" |
| #include "chromeos/constants/chromeos_features.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/session_manager/session_manager_types.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/display/display.h" |
| #include "ui/display/manager/managed_display_info.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kUser1Email[] = "user1@dockedmagnifier"; |
| constexpr char kUser2Email[] = "user2@dockedmagnifier"; |
| |
| // Returns the magnifier area height given the display height. |
| int GetMagnifierHeight(int display_height) { |
| return (display_height / |
| DockedMagnifierController::kDefaultScreenHeightDivisor) + |
| DockedMagnifierController::kSeparatorHeight; |
| } |
| |
| class DockedMagnifierTest : public NoSessionAshTestBase { |
| public: |
| DockedMagnifierTest() = default; |
| ~DockedMagnifierTest() override = default; |
| |
| DockedMagnifierController* controller() const { |
| return Shell::Get()->docked_magnifier_controller(); |
| } |
| |
| SplitViewController* split_view_controller() { |
| return SplitViewController::Get(Shell::GetPrimaryRootWindow()); |
| } |
| |
| PrefService* user1_pref_service() { |
| return Shell::Get()->session_controller()->GetUserPrefServiceForUser( |
| AccountId::FromUserEmail(kUser1Email)); |
| } |
| |
| PrefService* user2_pref_service() { |
| return Shell::Get()->session_controller()->GetUserPrefServiceForUser( |
| AccountId::FromUserEmail(kUser2Email)); |
| } |
| |
| // AshTestBase: |
| void SetUp() override { |
| // Explicitly enable --ash-constrain-pointer-to-root to be able to test |
| // mouse cursor confinement outside the magnifier viewport. |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAshConstrainPointerToRoot); |
| |
| NoSessionAshTestBase::SetUp(); |
| |
| // Create user 1 session and simulate its login. |
| SimulateUserLogin(kUser1Email); |
| |
| // Create user 2 session. |
| GetSessionControllerClient()->AddUserSession(kUser2Email); |
| |
| // Place the cursor in the first display. |
| GetEventGenerator()->MoveMouseTo(gfx::Point(0, 0)); |
| } |
| |
| void SwitchActiveUser(const std::string& email) { |
| GetSessionControllerClient()->SwitchActiveUser( |
| AccountId::FromUserEmail(email)); |
| } |
| |
| // Tests that when the magnifier layer's transform is applied on the point in |
| // the |root_window| coordinates that corresponds to the |
| // |point_of_interest_in_screen|, the resulting point is at the center of the |
| // magnifier viewport widget. |
| void TestMagnifierLayerTransform( |
| const gfx::Point& point_of_interest_in_screen, |
| const aura::Window* root_window) { |
| // Convert to root coordinates. |
| gfx::Point point_of_interest_in_root = point_of_interest_in_screen; |
| ::wm::ConvertPointFromScreen(root_window, &point_of_interest_in_root); |
| // Account for point of interest being outside the minimum height threshold. |
| // Do this in gfx::PointF to avoid rounding errors. |
| gfx::PointF point_of_interest_in_root_f(point_of_interest_in_root); |
| const float min_pov_height = |
| controller()->GetMinimumPointOfInterestHeightForTesting(); |
| if (point_of_interest_in_root_f.y() < min_pov_height) |
| point_of_interest_in_root_f.set_y(min_pov_height); |
| |
| const ui::Layer* magnifier_layer = |
| controller()->GetViewportMagnifierLayerForTesting(); |
| // The magnifier layer's transform, when applied to the point of interest |
| // (in root coordinates), should take it to the point at the center of the |
| // viewport widget (also in root coordinates). |
| point_of_interest_in_root_f = |
| magnifier_layer->transform().MapPoint(point_of_interest_in_root_f); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| const gfx::Point viewport_center_in_root = |
| viewport_widget->GetNativeWindow() |
| ->GetBoundsInRootWindow() |
| .CenterPoint(); |
| EXPECT_EQ(viewport_center_in_root, |
| gfx::ToFlooredPoint(point_of_interest_in_root_f)); |
| } |
| |
| void TouchPoint(const gfx::Point& touch_point_in_screen) { |
| // TODO(oshima): Currently touch event doesn't update the |
| // event dispatcher in the event generator. Fix it and use |
| // touch event insteead. |
| auto* generator = GetEventGenerator(); |
| generator->GestureTapAt(touch_point_in_screen); |
| } |
| |
| std::unique_ptr<views::Widget> CreateLockSystemModalWindow( |
| const gfx::Rect& bounds) { |
| auto* widget_delegate_view = new views::WidgetDelegateView(); |
| widget_delegate_view->SetModalType(ui::MODAL_TYPE_SYSTEM); |
| return CreateTestWidget(widget_delegate_view, |
| kShellWindowId_LockSystemModalContainer, bounds); |
| } |
| |
| // Test that display work area and a modal window is adjusted correctly |
| // after enabling and disabling a docked magnifier. |
| void TestDisplayWorkAreaAndLockSystemModalBoundsUpdated() { |
| // Start with the docked magnifier disabled. |
| EXPECT_FALSE(controller()->GetEnabled()); |
| |
| // Create a lock system modal window. |
| auto lock_system_modal_widget = |
| CreateLockSystemModalWindow(gfx::Rect(800, 600)); |
| |
| // Enable the docked magnifier. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| |
| // Expect that the modal window fits inside the shrunk valid area. |
| const gfx::Rect modal_bounds = |
| lock_system_modal_widget->GetWindowBoundsInScreen(); |
| const gfx::Rect valid_area = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow()) |
| .work_area(); |
| const gfx::Rect docked_magnifier_bounds = |
| controller()->GetViewportWidgetForTesting()->GetWindowBoundsInScreen(); |
| // Check that display work area does not overlap with a docked magnifier. |
| EXPECT_FALSE(docked_magnifier_bounds.Intersects(valid_area)); |
| // Check that modal window fits inside the display work area, |valid_area|, |
| // to make sure the |modal_bounds| size does not overflow. |
| EXPECT_TRUE(valid_area.Contains(modal_bounds)); |
| |
| // Disable the docked magnifier. |
| controller()->SetEnabled(false); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| |
| const gfx::Rect modal_bounds_no_magnifier = |
| lock_system_modal_widget->GetWindowBoundsInScreen(); |
| |
| // Expect that the window stays inside the valid area. |
| const gfx::Rect valid_area_no_magnifier = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow()) |
| .work_area(); |
| EXPECT_TRUE(valid_area_no_magnifier.Contains(modal_bounds_no_magnifier)); |
| // With larger work area, |modal_bounds_no_magnifier| size must not shrink. |
| // Even stricter, the size must remain the same as |modal_bounds| size |
| // because a centered system modal only shrinks but never expands according |
| // to SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds() |
| // ClampToCenteredSize. |
| EXPECT_EQ(modal_bounds.size(), modal_bounds_no_magnifier.size()); |
| // Expect y offset of the modal window to be centered correctly. |
| EXPECT_EQ((valid_area_no_magnifier.height() - |
| modal_bounds_no_magnifier.height()) / |
| 2, |
| modal_bounds_no_magnifier.y()); |
| } |
| |
| // Test that bounds of a modal window are the same when initially created and |
| // updated. views::NativeWidgetAura::CenterWindow() sets the initial modal |
| // bounds, while |
| // SystemModalContainerLayoutManager::GetCenteredAndOrFittedBounds() updates |
| // the modal window bounds. Thus, this test makes sure the bounds are the same |
| // in both cases. |
| void TestLockSystemModalBoundUpdateAndCreationConsistency() { |
| // Start with the docked magnifier disabled. |
| EXPECT_FALSE(controller()->GetEnabled()); |
| |
| // Create a lock system modal window for an update case. |
| auto lock_system_modal_widget_update_case = |
| CreateLockSystemModalWindow(gfx::Rect(800, 600)); |
| |
| // Enable the docked magnifier. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| |
| // Create a lock system modal window for a creation case. |
| auto lock_system_modal_widget = |
| CreateLockSystemModalWindow(gfx::Rect(800, 600)); |
| |
| // Expect that both modal windows fit inside the shrunk area |
| // and have the exact same bounds. |
| const gfx::Rect modal_bounds = |
| lock_system_modal_widget->GetWindowBoundsInScreen(); |
| const gfx::Rect modal_bounds_update_case = |
| lock_system_modal_widget_update_case->GetWindowBoundsInScreen(); |
| const gfx::Rect valid_area = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(Shell::GetPrimaryRootWindow()) |
| .work_area(); |
| const gfx::Rect docked_magnifier_bounds = |
| controller()->GetViewportWidgetForTesting()->GetWindowBoundsInScreen(); |
| EXPECT_FALSE(docked_magnifier_bounds.Intersects(valid_area)); |
| EXPECT_TRUE(valid_area.Contains(modal_bounds)); |
| EXPECT_EQ(modal_bounds, modal_bounds_update_case); |
| |
| // Disable the docked magnifier. |
| controller()->SetEnabled(false); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| } |
| }; |
| |
| // If not signed in, test that display work area and window bounds |
| // are updated correctly after enabling and disabling a docked magnifier. |
| TEST_F(DockedMagnifierTest, WindowBoundsChangeInNonActiveState) { |
| UpdateDisplay("800x600"); |
| |
| struct { |
| std::string trace; |
| session_manager::SessionState state; |
| } kNonActiveStatesTestCases[] = { |
| {"oobe", session_manager::SessionState::OOBE}, |
| {"login_primary", session_manager::SessionState::LOGIN_PRIMARY}, |
| {"locked", session_manager::SessionState::LOCKED}, |
| {"login_secondary", session_manager::SessionState::LOGIN_SECONDARY}, |
| }; |
| |
| // For each of the states which is not ACTIVE, LOGGED_IN_NOT_ACTIVE, and |
| // UNKNOWN, set the session state and make sure that work area and window |
| // bounds are as expected after enabling and disabling the docked magnifier. |
| // In LOGGED_IN_NOT_ACTIVE state, no window can be added to |
| // LockSystemModalContainer. |
| for (auto test_case : kNonActiveStatesTestCases) { |
| SCOPED_TRACE(test_case.trace); |
| GetSessionControllerClient()->SetSessionState(test_case.state); |
| // Test that display work area and the modal window position is dynamically |
| // adjusted regarding the existence of a docked magnifier. |
| TestDisplayWorkAreaAndLockSystemModalBoundsUpdated(); |
| // Test that bounds of a lock system modal window are |
| // the same during creation and update. |
| TestLockSystemModalBoundUpdateAndCreationConsistency(); |
| } |
| } |
| |
| // Tests that the Fullscreen and Docked Magnifiers are mutually exclusive. |
| // TODO(afakhry): Update this test to use ash::MagnificationController once |
| // refactored. https://crbug.com/817157. |
| TEST_F(DockedMagnifierTest, MutuallyExclusiveMagnifiers) { |
| // Start with both magnifiers disabled. |
| EXPECT_FALSE(controller()->GetEnabled()); |
| EXPECT_FALSE(controller()->GetFullscreenMagnifierEnabled()); |
| |
| // Enabling one disables the other. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| EXPECT_FALSE(controller()->GetFullscreenMagnifierEnabled()); |
| |
| controller()->SetFullscreenMagnifierEnabled(true); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| EXPECT_TRUE(controller()->GetFullscreenMagnifierEnabled()); |
| |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| EXPECT_FALSE(controller()->GetFullscreenMagnifierEnabled()); |
| |
| controller()->SetEnabled(false); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| EXPECT_FALSE(controller()->GetFullscreenMagnifierEnabled()); |
| } |
| |
| // Tests the changes in the magnifier's status, user switches. |
| TEST_F(DockedMagnifierTest, TestEnableAndDisable) { |
| // Enable for user 1, and switch to user 2. User 2 should have it disabled. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| SwitchActiveUser(kUser2Email); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| |
| // Switch back to user 1, expect it to be enabled. |
| SwitchActiveUser(kUser1Email); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| } |
| |
| // Tests the magnifier's scale changes. |
| TEST_F(DockedMagnifierTest, TestScale) { |
| // Scale changes are persisted even when the Docked Magnifier is disabled. |
| EXPECT_FALSE(controller()->GetEnabled()); |
| controller()->SetScale(5.0f); |
| EXPECT_FLOAT_EQ(5.0f, controller()->GetScale()); |
| |
| // Scale values are clamped to a minimum of 1.0f (which means no scale). |
| controller()->SetScale(0.0f); |
| EXPECT_FLOAT_EQ(1.0f, controller()->GetScale()); |
| |
| // Switch to user 2, change the scale, then switch back to user 1. User 1's |
| // scale should not change. |
| SwitchActiveUser(kUser2Email); |
| controller()->SetScale(6.5f); |
| EXPECT_FLOAT_EQ(6.5f, controller()->GetScale()); |
| SwitchActiveUser(kUser1Email); |
| EXPECT_FLOAT_EQ(1.0f, controller()->GetScale()); |
| } |
| |
| // Tests that updates of the Docked Magnifier user prefs from outside the |
| // DockedMagnifierController (such as Settings UI) are observed and applied. |
| TEST_F(DockedMagnifierTest, TestOutsidePrefsUpdates) { |
| EXPECT_FALSE(controller()->GetEnabled()); |
| user1_pref_service()->SetBoolean(prefs::kDockedMagnifierEnabled, true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| user1_pref_service()->SetDouble(prefs::kDockedMagnifierScale, 7.3f); |
| EXPECT_FLOAT_EQ(7.3f, controller()->GetScale()); |
| user1_pref_service()->SetBoolean(prefs::kDockedMagnifierEnabled, false); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| } |
| |
| // Tests that the workareas of displays are adjusted properly when the Docked |
| // Magnifier's viewport moves from one display to the next. |
| TEST_F(DockedMagnifierTest, DisplaysWorkAreas) { |
| UpdateDisplay("800x600,800+0-400x300"); |
| const auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(2u, root_windows.size()); |
| |
| // Place the cursor in the first display. |
| GetEventGenerator()->MoveMouseTo(gfx::Point(0, 0)); |
| |
| // Before the magnifier is enabled, the work areas of both displays are their |
| // full size minus the shelf height. |
| const display::Display& display_1 = display_manager()->GetDisplayAt(0); |
| const gfx::Rect disp_1_bounds(0, 0, 800, 600); |
| EXPECT_EQ(disp_1_bounds, display_1.bounds()); |
| gfx::Rect disp_1_workarea_no_magnifier = disp_1_bounds; |
| disp_1_workarea_no_magnifier.Inset( |
| gfx::Insets::TLBR(0, 0, ShelfConfig::Get()->shelf_size(), 0)); |
| EXPECT_EQ(disp_1_workarea_no_magnifier, display_1.work_area()); |
| // At this point, normal mouse cursor confinement should be used. |
| AshWindowTreeHost* host1 = |
| Shell::Get() |
| ->window_tree_host_manager() |
| ->GetAshWindowTreeHostForDisplayId(display_1.id()); |
| EXPECT_EQ(host1->GetLastCursorConfineBoundsInPixels(), |
| gfx::Rect(gfx::Point(0, 0), disp_1_bounds.size())); |
| |
| const display::Display& display_2 = display_manager()->GetDisplayAt(1); |
| const gfx::Rect disp_2_bounds(800, 0, 400, 300); |
| EXPECT_EQ(disp_2_bounds, display_2.bounds()); |
| gfx::Rect disp_2_workarea_no_magnifier = disp_2_bounds; |
| disp_2_workarea_no_magnifier.Inset( |
| gfx::Insets::TLBR(0, 0, ShelfConfig::Get()->shelf_size(), 0)); |
| EXPECT_EQ(disp_2_workarea_no_magnifier, display_2.work_area()); |
| AshWindowTreeHost* host2 = |
| Shell::Get() |
| ->window_tree_host_manager() |
| ->GetAshWindowTreeHostForDisplayId(display_2.id()); |
| EXPECT_EQ(host2->GetLastCursorConfineBoundsInPixels(), |
| gfx::Rect(gfx::Point(0, 0), disp_2_bounds.size())); |
| |
| // Enable the magnifier and the check the workareas. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| const views::Widget* viewport_1_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_1_widget); |
| EXPECT_EQ(root_windows[0], |
| viewport_1_widget->GetNativeView()->GetRootWindow()); |
| // Since the cursor is in the first display, the height of its workarea will |
| // be further shrunk from the top by 1/4th of its full height + the height of |
| // the separator layer. |
| gfx::Rect disp_1_workspace_with_magnifier = disp_1_workarea_no_magnifier; |
| const int disp_1_magnifier_height = |
| GetMagnifierHeight(disp_1_bounds.height()); |
| disp_1_workspace_with_magnifier.Inset( |
| gfx::Insets::TLBR(disp_1_magnifier_height, 0, 0, 0)); |
| EXPECT_EQ(disp_1_bounds, display_1.bounds()); |
| EXPECT_EQ(disp_1_workspace_with_magnifier, display_1.work_area()); |
| // The first display should confine the mouse movement outside of the |
| // viewport. |
| gfx::Rect disp_1_confine_bounds( |
| 0, disp_1_magnifier_height, disp_1_bounds.width(), |
| disp_1_bounds.height() - disp_1_magnifier_height); |
| disp_1_confine_bounds.Inset( |
| gfx::Insets().set_top(-DockedMagnifierController::kSeparatorHeight)); |
| EXPECT_EQ(host1->GetLastCursorConfineBoundsInPixels(), disp_1_confine_bounds); |
| |
| // The second display should remain unaffected. |
| EXPECT_EQ(disp_2_bounds, display_2.bounds()); |
| EXPECT_EQ(disp_2_workarea_no_magnifier, display_2.work_area()); |
| EXPECT_EQ(host2->GetLastCursorConfineBoundsInPixels(), |
| gfx::Rect(gfx::Point(0, 0), disp_2_bounds.size())); |
| |
| // Now, move mouse cursor to display 2, and expect that the workarea of |
| // display 1 is restored to its original value, while that of display 2 is |
| // shrunk to fit the Docked Magnifier's viewport. |
| GetEventGenerator()->MoveMouseTo(gfx::Point(800, 0)); |
| const views::Widget* viewport_2_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_2_widget); |
| EXPECT_NE(viewport_1_widget, viewport_2_widget); // It's a different widget. |
| EXPECT_EQ(root_windows[1], |
| viewport_2_widget->GetNativeView()->GetRootWindow()); |
| EXPECT_EQ(disp_1_bounds, display_1.bounds()); |
| EXPECT_EQ(disp_1_workarea_no_magnifier, display_1.work_area()); |
| // Display 1 goes back to the normal mouse confinement. |
| EXPECT_EQ(host1->GetLastCursorConfineBoundsInPixels(), |
| gfx::Rect(gfx::Point(0, 0), disp_1_bounds.size())); |
| EXPECT_EQ(disp_2_bounds, display_2.bounds()); |
| gfx::Rect disp_2_workspace_with_magnifier = disp_2_workarea_no_magnifier; |
| const int disp_2_magnifier_height = |
| GetMagnifierHeight(disp_2_bounds.height()); |
| disp_2_workspace_with_magnifier.Inset( |
| gfx::Insets().set_top(disp_2_magnifier_height)); |
| EXPECT_EQ(disp_2_workspace_with_magnifier, display_2.work_area()); |
| // Display 2's mouse is confined outside the viewport. |
| gfx::Rect disp_2_confine_bounds( |
| 0, disp_2_magnifier_height, disp_2_bounds.width(), |
| disp_2_bounds.height() - disp_2_magnifier_height); |
| disp_2_confine_bounds.Inset( |
| gfx::Insets().set_top(-DockedMagnifierController::kSeparatorHeight)); |
| EXPECT_EQ(host2->GetLastCursorConfineBoundsInPixels(), disp_2_confine_bounds); |
| |
| // Now, disable the magnifier, and expect both displays to return back to |
| // their original state. |
| controller()->SetEnabled(false); |
| EXPECT_FALSE(controller()->GetEnabled()); |
| EXPECT_EQ(disp_1_bounds, display_1.bounds()); |
| EXPECT_EQ(disp_1_workarea_no_magnifier, display_1.work_area()); |
| EXPECT_EQ(disp_2_bounds, display_2.bounds()); |
| EXPECT_EQ(disp_2_workarea_no_magnifier, display_2.work_area()); |
| // Normal mouse confinement for both displays. |
| EXPECT_EQ(host1->GetLastCursorConfineBoundsInPixels(), |
| gfx::Rect(gfx::Point(0, 0), disp_1_bounds.size())); |
| EXPECT_EQ(host2->GetLastCursorConfineBoundsInPixels(), |
| gfx::Rect(gfx::Point(0, 0), disp_2_bounds.size())); |
| } |
| |
| // Test that we exit overview mode when enabling the docked magnifier. |
| TEST_F(DockedMagnifierTest, DisplaysWorkAreasOverviewMode) { |
| std::unique_ptr<aura::Window> window = |
| TestWindowBuilder() |
| .SetBounds(gfx::Rect(0, 0, 200, 200)) |
| .AllowAllWindowStates() |
| .Build(); |
| WindowState::Get(window.get())->Maximize(); |
| |
| // Enable overview mode followed by the magnifier. |
| auto* overview_controller = Shell::Get()->overview_controller(); |
| EnterOverview(); |
| EXPECT_TRUE(overview_controller->InOverviewSession()); |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| |
| // Expect that overview mode is exited, the display's work area is updated, |
| // and the window's bounds are updated to be equal to the new display's work |
| // area bounds. |
| EXPECT_FALSE(overview_controller->InOverviewSession()); |
| const display::Display& display = display_manager()->GetDisplayAt(0); |
| gfx::Rect workarea = display.bounds(); |
| const int magnifier_height = GetMagnifierHeight(display.bounds().height()); |
| workarea.Inset(gfx::Insets::TLBR(magnifier_height, 0, |
| ShelfConfig::Get()->shelf_size(), 0)); |
| EXPECT_EQ(workarea, display.work_area()); |
| EXPECT_EQ(workarea, window->bounds()); |
| EXPECT_TRUE(WindowState::Get(window.get())->IsMaximized()); |
| } |
| |
| TEST_F(DockedMagnifierTest, OverviewTabbing) { |
| auto window = CreateTestWindow(); |
| controller()->SetEnabled(true); |
| |
| EnterOverview(); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); |
| |
| auto* root_window = Shell::GetPrimaryRootWindow(); |
| const auto* desk_bar_view = GetOverviewSession() |
| ->GetGridWithRootWindow(root_window) |
| ->desks_bar_view(); |
| |
| auto* default_desk_button = desk_bar_view->default_desk_button(); |
| auto* new_desk_button = desk_bar_view->new_desk_button(); |
| |
| // Tab once. The viewport should be centered on the beginning of the overview |
| // item's title. |
| SendKey(ui::VKEY_TAB); |
| auto* item = GetOverviewItemForWindow(window.get()); |
| ASSERT_TRUE(item); |
| TestMagnifierLayerTransform(item->GetMagnifierFocusPointInScreen(), |
| root_window); |
| |
| // Tab one more time. The viewport should be centered on the center of the |
| // default desk button in the zero state desks bar. |
| SendKey(ui::VKEY_TAB); |
| TestMagnifierLayerTransform( |
| default_desk_button->GetBoundsInScreen().CenterPoint(), root_window); |
| |
| // Tab one more time. The viewport should be centered on the center of the |
| // new desk button in the zero state desks bar. |
| SendKey(ui::VKEY_TAB); |
| TestMagnifierLayerTransform( |
| new_desk_button->GetBoundsInScreen().CenterPoint(), root_window); |
| } |
| |
| // Test that we exist split view and over view modes when a single window is |
| // snapped and the other snap region is hosting overview mode. |
| TEST_F(DockedMagnifierTest, DisplaysWorkAreasSingleSplitView) { |
| // Verify that we're in tablet mode. |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode()); |
| |
| std::unique_ptr<aura::Window> window = |
| TestWindowBuilder() |
| .SetBounds(gfx::Rect(0, 0, 200, 200)) |
| .AllowAllWindowStates() |
| .Build(); |
| WindowState::Get(window.get())->Maximize(); |
| |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::State::kNoSnap); |
| EXPECT_EQ(split_view_controller()->InSplitViewMode(), false); |
| |
| // Simulate going into split view, by enabling overview mode, and snapping |
| // a window to the left. |
| auto* overview_controller = Shell::Get()->overview_controller(); |
| EnterOverview(); |
| EXPECT_TRUE(overview_controller->InOverviewSession()); |
| split_view_controller()->SnapWindow(window.get(), SnapPosition::kPrimary); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::State::kPrimarySnapped); |
| EXPECT_EQ(split_view_controller()->primary_window(), window.get()); |
| EXPECT_TRUE(overview_controller->InOverviewSession()); |
| |
| // Enable the docked magnifier and expect that both overview and split view |
| // modes are exited, and the window remains maximized, and its bounds are |
| // updated to match the new display's work area. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| EXPECT_FALSE(overview_controller->InOverviewSession()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::State::kNoSnap); |
| EXPECT_EQ(split_view_controller()->InSplitViewMode(), false); |
| const display::Display& display = display_manager()->GetDisplayAt(0); |
| const int magnifier_height = GetMagnifierHeight(display.bounds().height()); |
| gfx::Rect work_area = display.bounds(); |
| work_area.Inset(gfx::Insets::TLBR(magnifier_height, 0, |
| ShelfConfig::Get()->shelf_size(), 0)); |
| EXPECT_EQ(work_area, display.work_area()); |
| EXPECT_EQ(work_area, window->bounds()); |
| EXPECT_TRUE(WindowState::Get(window.get())->IsMaximized()); |
| } |
| |
| // Test that we don't exit split view with two windows snapped on both sides |
| // when we enable the docked magnifier, but rather their bounds are updated. |
| TEST_F(DockedMagnifierTest, DisplaysWorkAreasDoubleSplitView) { |
| // Verify that we're in tablet mode. |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| EXPECT_TRUE(display::Screen::GetScreen()->InTabletMode()); |
| |
| std::unique_ptr<aura::Window> window1 = |
| TestWindowBuilder() |
| .SetBounds(gfx::Rect(0, 0, 200, 200)) |
| .AllowAllWindowStates() |
| .Build(); |
| std::unique_ptr<aura::Window> window2 = |
| TestWindowBuilder() |
| .SetBounds(gfx::Rect(0, 0, 200, 200)) |
| .AllowAllWindowStates() |
| .Build(); |
| |
| auto* overview_controller = Shell::Get()->overview_controller(); |
| EnterOverview(); |
| EXPECT_TRUE(overview_controller->InOverviewSession()); |
| |
| EXPECT_EQ(split_view_controller()->InSplitViewMode(), false); |
| split_view_controller()->SnapWindow(window1.get(), SnapPosition::kPrimary); |
| split_view_controller()->SnapWindow(window2.get(), SnapPosition::kSecondary); |
| EXPECT_EQ(split_view_controller()->InSplitViewMode(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::State::kBothSnapped); |
| |
| // Snapping both windows should exit overview mode. |
| EXPECT_FALSE(overview_controller->InOverviewSession()); |
| |
| // Enable the docked magnifier, and expect that split view does not exit, and |
| // the two windows heights are updated to be equal to the height of the |
| // updated display's work area. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| EXPECT_EQ(split_view_controller()->InSplitViewMode(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::State::kBothSnapped); |
| const display::Display& display = display_manager()->GetDisplayAt(0); |
| const int magnifier_height = GetMagnifierHeight(display.bounds().height()); |
| gfx::Rect work_area = display.bounds(); |
| work_area.Inset(gfx::Insets::TLBR(magnifier_height, 0, |
| ShelfConfig::Get()->shelf_size(), 0)); |
| EXPECT_EQ(work_area, display.work_area()); |
| EXPECT_EQ(work_area.height(), window1->bounds().height()); |
| EXPECT_EQ(work_area.height(), window2->bounds().height()); |
| } |
| |
| // Tests that the Docked Magnifier follows touch events. |
| TEST_F(DockedMagnifierTest, TouchEvents) { |
| UpdateDisplay("800x600,800+0-400x300"); |
| const auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(2u, root_windows.size()); |
| |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| controller()->SetScale(4.0f); |
| |
| // Generate some touch events in both displays and expect the magnifier |
| // viewport moves accordingly. |
| gfx::Point touch_point(200, 350); |
| TouchPoint(touch_point); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| TestMagnifierLayerTransform(touch_point, root_windows[0]); |
| |
| // Touch a new point in the other display. |
| touch_point = gfx::Point(900, 200); |
| TouchPoint(touch_point); |
| |
| // New viewport widget is created in the second display. |
| ASSERT_NE(viewport_widget, controller()->GetViewportWidgetForTesting()); |
| viewport_widget = controller()->GetViewportWidgetForTesting(); |
| EXPECT_EQ(root_windows[1], viewport_widget->GetNativeView()->GetRootWindow()); |
| TestMagnifierLayerTransform(touch_point, root_windows[1]); |
| } |
| |
| // Tests the behavior of the magnifier when displays are added or removed. |
| TEST_F(DockedMagnifierTest, AddRemoveDisplays) { |
| // Start with a single display. |
| const auto disp_1_info = display::ManagedDisplayInfo::CreateFromSpecWithID( |
| "0+0-600x800", 101 /* id */); |
| std::vector<display::ManagedDisplayInfo> info_list; |
| info_list.push_back(disp_1_info); |
| display_manager()->OnNativeDisplaysChanged(info_list); |
| auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(1u, root_windows.size()); |
| |
| // Enable the magnifier, and validate the state of the viewport widget. |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_widget); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| const int viewport_1_height = |
| 800 / DockedMagnifierController::kDefaultScreenHeightDivisor; |
| EXPECT_EQ(gfx::Rect(0, 0, 600, viewport_1_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| // Adding a new display should not affect where the viewport currently is. |
| const auto disp_2_info = display::ManagedDisplayInfo::CreateFromSpecWithID( |
| "600+0-400x600", 102 /* id */); |
| info_list.push_back(disp_2_info); |
| display_manager()->OnNativeDisplaysChanged(info_list); |
| root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(2u, root_windows.size()); |
| // Same viewport widget in same root window. |
| EXPECT_EQ(viewport_widget, controller()->GetViewportWidgetForTesting()); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| EXPECT_EQ(gfx::Rect(0, 0, 600, viewport_1_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| // Move the cursor to the second display, expect the viewport widget to get |
| // updated accordingly. |
| GetEventGenerator()->MoveMouseTo(gfx::Point(800, 0)); |
| // New viewport widget is created. |
| ASSERT_NE(viewport_widget, controller()->GetViewportWidgetForTesting()); |
| viewport_widget = controller()->GetViewportWidgetForTesting(); |
| EXPECT_EQ(root_windows[1], viewport_widget->GetNativeView()->GetRootWindow()); |
| const int viewport_2_height = |
| 600 / DockedMagnifierController::kDefaultScreenHeightDivisor; |
| EXPECT_EQ(gfx::Rect(600, 0, 400, viewport_2_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| // Now, remove display 2 ** while ** the magnifier viewport is there. This |
| // should cause no crashes, the viewport widget should be recreated in |
| // display 1. |
| info_list.clear(); |
| info_list.push_back(disp_1_info); |
| display_manager()->OnNativeDisplaysChanged(info_list); |
| // We need to spin this run loop to wait for a new mouse event to be |
| // dispatched so that the viewport widget is re-created. |
| base::RunLoop().RunUntilIdle(); |
| root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(1u, root_windows.size()); |
| viewport_widget = controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_widget); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| EXPECT_EQ(gfx::Rect(0, 0, 600, viewport_1_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| } |
| |
| // Tests various magnifier layer transform in the simple cases (i.e. no device |
| // scale factors or screen rotations). |
| TEST_F(DockedMagnifierTest, TransformSimple) { |
| UpdateDisplay("800x700"); |
| const auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(1u, root_windows.size()); |
| |
| controller()->SetEnabled(true); |
| const float scale1 = 2.0f; |
| controller()->SetScale(scale1); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| EXPECT_FLOAT_EQ(scale1, controller()->GetScale()); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_widget); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| const int viewport_height = |
| root_windows[0]->bounds().height() / |
| DockedMagnifierController::kDefaultScreenHeightDivisor; |
| EXPECT_EQ(gfx::Rect(0, 0, 800, viewport_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| // Move the cursor to the center of the screen. |
| gfx::Point point_of_interest(400, 400); |
| GetEventGenerator()->MoveMouseTo(point_of_interest); |
| TestMagnifierLayerTransform(point_of_interest, root_windows[0]); |
| |
| // Move the cursor to the bottom right corner. |
| point_of_interest = gfx::Point(799, 799); |
| GetEventGenerator()->MoveMouseTo(point_of_interest); |
| TestMagnifierLayerTransform(point_of_interest, root_windows[0]); |
| |
| // Tricky: Move the cursor to the top right corner, such that the cursor is |
| // over the magnifier viewport. The transform should be such that the viewport |
| // doesn't show itself. |
| point_of_interest = gfx::Point(799, 0); |
| GetEventGenerator()->MoveMouseTo(point_of_interest); |
| TestMagnifierLayerTransform(point_of_interest, root_windows[0]); |
| // In this case, our point of interest is changed to be at the bottom of the |
| // separator, and it should go to the center of the top *edge* of the viewport |
| // widget. |
| point_of_interest.set_y(viewport_height + |
| DockedMagnifierController::kSeparatorHeight); |
| const gfx::Point viewport_center = |
| viewport_widget->GetNativeWindow()->GetBoundsInRootWindow().CenterPoint(); |
| gfx::Point viewport_top_edge_center = viewport_center; |
| viewport_top_edge_center.set_y(0); |
| const ui::Layer* magnifier_layer = |
| controller()->GetViewportMagnifierLayerForTesting(); |
| EXPECT_EQ(viewport_top_edge_center, |
| magnifier_layer->transform().MapPoint(point_of_interest)); |
| // The minimum height for the point of interest is the bottom of the viewport |
| // + the height of the separator + half the height of the viewport when scaled |
| // back to the non-magnified space. |
| EXPECT_FLOAT_EQ(viewport_height + |
| DockedMagnifierController::kSeparatorHeight + |
| (viewport_center.y() / scale1), |
| controller()->GetMinimumPointOfInterestHeightForTesting()); |
| |
| // Leave the mouse cursor where it is, and only change the magnifier's scale. |
| const float scale2 = 5.3f; |
| controller()->SetScale(scale2); |
| EXPECT_FLOAT_EQ(scale2, controller()->GetScale()); |
| // The transform behaves exactly as above even with a different scale. |
| point_of_interest = gfx::Point(799, 0); |
| TestMagnifierLayerTransform(point_of_interest, root_windows[0]); |
| point_of_interest.set_y(viewport_height + |
| DockedMagnifierController::kSeparatorHeight); |
| EXPECT_EQ(viewport_top_edge_center, |
| magnifier_layer->transform().MapPoint(point_of_interest)); |
| |
| EXPECT_FLOAT_EQ(viewport_height + |
| DockedMagnifierController::kSeparatorHeight + |
| (viewport_center.y() / scale2), |
| controller()->GetMinimumPointOfInterestHeightForTesting()); |
| } |
| |
| // Tests resizing docked magnifier by dragging the separator. |
| TEST_F(DockedMagnifierTest, ResizeDockedMagnifier) { |
| UpdateDisplay("800x600"); |
| const auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(1u, root_windows.size()); |
| |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_widget); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| const int viewport_height = |
| root_windows[0]->bounds().height() / |
| DockedMagnifierController::kDefaultScreenHeightDivisor; |
| EXPECT_EQ(gfx::Rect(0, 0, 800, viewport_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| ::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager(); |
| EXPECT_NE(ui::mojom::CursorType::kNorthSouthResize, |
| cursor_manager->GetCursor().type()); |
| EXPECT_FALSE(cursor_manager->IsCursorLocked()); |
| |
| // Move cursor over docked magnifier separator (to drag for resizing). |
| gfx::Point mouse_location(400, viewport_height); |
| GetEventGenerator()->MoveMouseTo(mouse_location); |
| EXPECT_EQ(ui::mojom::CursorType::kNorthSouthResize, |
| cursor_manager->GetCursor().type()); |
| EXPECT_TRUE(cursor_manager->IsCursorLocked()); |
| |
| // Drag separator 100 pixels down. |
| mouse_location = gfx::Point(400, viewport_height + 100); |
| GetEventGenerator()->DragMouseTo(mouse_location); |
| EXPECT_EQ(ui::mojom::CursorType::kNorthSouthResize, |
| cursor_manager->GetCursor().type()); |
| EXPECT_TRUE(cursor_manager->IsCursorLocked()); |
| |
| // Assert docked magnifier viewport is now taller. |
| EXPECT_EQ(gfx::Rect(0, 0, 800, viewport_height + 100), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| // Move off of the separator. The cursor should reset. |
| GetEventGenerator()->MoveMouseTo(400, viewport_height + 200); |
| EXPECT_NE(ui::mojom::CursorType::kNorthSouthResize, |
| cursor_manager->GetCursor().type()); |
| EXPECT_FALSE(cursor_manager->IsCursorLocked()); |
| |
| // Drag again, but turn off docked magnifier during drag (simulating keyboard |
| // shortcut). The cursor should reset. |
| GetEventGenerator()->MoveMouseTo(400, viewport_height + 100); |
| GetEventGenerator()->DragMouseTo(gfx::Point(400, viewport_height)); |
| EXPECT_EQ(ui::mojom::CursorType::kNorthSouthResize, |
| cursor_manager->GetCursor().type()); |
| EXPECT_TRUE(cursor_manager->IsCursorLocked()); |
| controller()->SetEnabled(false); |
| EXPECT_NE(ui::mojom::CursorType::kNorthSouthResize, |
| cursor_manager->GetCursor().type()); |
| EXPECT_FALSE(cursor_manager->IsCursorLocked()); |
| } |
| |
| // Tests to verify dragging above separator does not resize docked magnifier. |
| TEST_F(DockedMagnifierTest, DragAboveSeparatorDoesNotResizeDockedMagnifier) { |
| UpdateDisplay("800x600"); |
| const auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(1u, root_windows.size()); |
| |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_widget); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| const int viewport_height = |
| root_windows[0]->bounds().height() / |
| DockedMagnifierController::kDefaultScreenHeightDivisor; |
| EXPECT_EQ(gfx::Rect(0, 0, 800, viewport_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| // Move cursor 2px above the docked magnifier separator, in the viewport area, |
| // where dragging should not work. |
| gfx::Point mouse_location(400, viewport_height - 2); |
| GetEventGenerator()->MoveMouseTo(mouse_location); |
| |
| // Drag 100 pixels down. |
| mouse_location = gfx::Point(400, viewport_height + 100); |
| GetEventGenerator()->DragMouseTo(mouse_location); |
| |
| // Assert docked magnifier viewport size remains at old height. |
| EXPECT_EQ(gfx::Rect(0, 0, 800, viewport_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| } |
| |
| // Tests to verify hovering and resizing the docked magnifier moves the cursor |
| // in front of the viewport. |
| TEST_F(DockedMagnifierTest, HoverAndResizeDockedMagnifierMovesCursorInFront) { |
| UpdateDisplay("800x600"); |
| const auto root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(1u, root_windows.size()); |
| |
| controller()->SetEnabled(true); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| const views::Widget* viewport_widget = |
| controller()->GetViewportWidgetForTesting(); |
| ASSERT_NE(nullptr, viewport_widget); |
| EXPECT_EQ(root_windows[0], viewport_widget->GetNativeView()->GetRootWindow()); |
| const int viewport_height = |
| root_windows[0]->bounds().height() / |
| DockedMagnifierController::kDefaultScreenHeightDivisor; |
| EXPECT_EQ(gfx::Rect(0, 0, 800, viewport_height), |
| viewport_widget->GetWindowBoundsInScreen()); |
| |
| CursorWindowController* cursor_window_controller = |
| Shell::Get()->window_tree_host_manager()->cursor_window_controller(); |
| |
| // Verify mouse is in layer behind separator. |
| EXPECT_EQ(cursor_window_controller->GetContainerForTest()->GetId(), |
| ash::kShellWindowId_MouseCursorContainer); |
| |
| // Move cursor over the docked magnifier separator. |
| gfx::Point mouse_location(400, viewport_height); |
| GetEventGenerator()->MoveMouseTo(mouse_location); |
| |
| // Verify mouse is in layer on top of separator |
| EXPECT_EQ(cursor_window_controller->GetContainerForTest()->GetId(), |
| ash::kShellWindowId_DockedMagnifierContainer); |
| |
| // Drag mouse 100 pixels down. |
| mouse_location = gfx::Point(400, viewport_height + 100); |
| GetEventGenerator()->DragMouseTo(mouse_location); |
| |
| // Assert mouse is still in layer on top of separator. |
| EXPECT_EQ(cursor_window_controller->GetContainerForTest()->GetId(), |
| ash::kShellWindowId_DockedMagnifierContainer); |
| |
| // Move mouse 50 pixels down. |
| mouse_location = gfx::Point(400, viewport_height + 50); |
| GetEventGenerator()->MoveMouseTo(mouse_location); |
| |
| // Assert mouse is back in layer behind separator. |
| EXPECT_EQ(cursor_window_controller->GetContainerForTest()->GetId(), |
| ash::kShellWindowId_MouseCursorContainer); |
| } |
| |
| // Tests that there are no crashes observed when the docked magnifier switches |
| // displays, moving away from a display with a maximized window that has a |
| // focused text input field. Changing the old display's work area bounds should |
| // not cause recursive caret bounds change notifications into the docked |
| // magnifier. https://crbug.com/1000903. |
| TEST_F(DockedMagnifierTest, NoCrashDueToRecursion) { |
| UpdateDisplay("600x900,800x600"); |
| const auto roots = Shell::GetAllRootWindows(); |
| ASSERT_EQ(2u, roots.size()); |
| |
| MagnifierTextInputTestHelper text_input_helper; |
| text_input_helper.CreateAndShowTextInputViewInRoot(gfx::Rect(0, 0, 600, 900), |
| roots[0]); |
| text_input_helper.MaximizeWidget(); |
| |
| // Enable the docked magnifier. |
| controller()->SetEnabled(true); |
| const float scale1 = 2.0f; |
| controller()->SetScale(scale1); |
| EXPECT_TRUE(controller()->GetEnabled()); |
| EXPECT_FLOAT_EQ(scale1, controller()->GetScale()); |
| |
| // Move the mouse to the second display and expect no crashes. |
| GetEventGenerator()->MoveMouseTo(1000, 300); |
| } |
| |
| TEST_F(DockedMagnifierTest, CaptureMode) { |
| UpdateDisplay("600x900"); |
| |
| controller()->SetEnabled(true); |
| controller()->SetScale(2.f); |
| |
| auto* capture_mode_controller = CaptureModeController::Get(); |
| capture_mode_controller->Start(CaptureModeEntryType::kQuickSettings); |
| |
| // Test that the magnifier viewport follows the cursor when it moves to |
| // various points even though capture mode consumes mouse events. |
| auto* event_generator = GetEventGenerator(); |
| gfx::Point point_of_interest{10, 20}; |
| event_generator->MoveMouseTo(point_of_interest); |
| auto* root = Shell::GetPrimaryRootWindow(); |
| TestMagnifierLayerTransform(point_of_interest, root); |
| point_of_interest = gfx::Point{510, 820}; |
| event_generator->MoveMouseTo(point_of_interest); |
| TestMagnifierLayerTransform(point_of_interest, root); |
| |
| // And the magnifier viewport follows the cursor when it's above the capture |
| // mode bar. |
| const auto* bar_widget = capture_mode_controller->capture_mode_session() |
| ->GetCaptureModeBarWidget(); |
| point_of_interest = bar_widget->GetWindowBoundsInScreen().CenterPoint(); |
| event_generator->MoveMouseTo(point_of_interest); |
| TestMagnifierLayerTransform(point_of_interest, root); |
| } |
| |
| // TODO(afakhry): Expand tests: |
| // - Test magnifier viewport's layer transforms with screen rotation, |
| // multi display, and unified mode. |
| // - Test adjust scale using scroll events. |
| |
| } // namespace |
| |
| } // namespace ash |