| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/accessibility/chromevox/touch_exploration_controller.h" |
| |
| #include <memory> |
| |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "base/macros.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/ash/accessibility/accessibility_manager.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "ui/aura/client/cursor_client.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/compositor/compositor.h" |
| #include "ui/compositor/test/draw_waiter_for_test.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/events/test/test_event_handler.h" |
| |
| namespace ash { |
| |
| class TouchExplorationTest : public InProcessBrowserTest { |
| public: |
| TouchExplorationTest() : simulated_clock_(new base::SimpleTestTickClock()) { |
| // Tests fail if time is ever 0. |
| simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(10)); |
| } |
| ~TouchExplorationTest() override {} |
| |
| protected: |
| void SetUpOnMainThread() override { |
| // The RenderView for WebContents is created as a result of the |
| // navigation to the New Tab page which is done as part of the test |
| // SetUp. The creation involves sending a resize message to the renderer |
| // process. Here we wait for the resize ack to be received, because |
| // currently WindowEventDispatcher has code to hold touch and mouse |
| // move events until resize is complete (crbug.com/384342) which |
| // interferes with this test. |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::WaitForResizeComplete(web_contents); |
| root_window_ = Shell::Get()->GetPrimaryRootWindow(); |
| event_handler_ = std::make_unique<ui::test::TestEventHandler>(); |
| root_window_->AddPreTargetHandler(event_handler_.get()); |
| } |
| |
| void TearDownOnMainThread() override { |
| SwitchTouchExplorationMode(false); |
| root_window_->RemovePreTargetHandler(event_handler_.get()); |
| } |
| |
| void SwitchTouchExplorationMode(bool on) { |
| AccessibilityManager::Get()->EnableSpokenFeedback(on); |
| } |
| |
| base::TimeTicks Now() { return simulated_clock_->NowTicks(); } |
| |
| ui::GestureDetector::Config gesture_detector_config_; |
| base::SimpleTestTickClock* simulated_clock_; |
| aura::Window* root_window_; |
| std::unique_ptr<ui::test::TestEventHandler> event_handler_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TouchExplorationTest); |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // crbug.com/422943 |
| #define MAYBE_NoRewritingEventsWhenOff DISABLED_NoRewritingEventsWhenOff |
| #else |
| #define MAYBE_NoRewritingEventsWhenOff NoRewritingEventsWhenOff |
| #endif |
| |
| // This test turns the touch exploration mode off and confirms that events |
| // aren't modified. |
| IN_PROC_BROWSER_TEST_F(TouchExplorationTest, MAYBE_NoRewritingEventsWhenOff) { |
| SwitchTouchExplorationMode(false); |
| ui::test::EventGenerator generator(root_window_); |
| |
| base::TimeTicks initial_time = Now(); |
| ui::TouchEvent initial_press( |
| ui::ET_TOUCH_PRESSED, gfx::Point(99, 200), initial_time, |
| ui::PointerDetails(ui::EventPointerType::kTouch, 1)); |
| generator.Dispatch(&initial_press); |
| |
| // Since the touch exploration controller doesn't know if the user is |
| // double-tapping or not, touch exploration is only initiated if the |
| // 300 ms has elapsed and the finger does not move fast enough to begin |
| // gestures. Here, the touch move event is not important as a move, but |
| // a way to create time advancement. |
| ui::TouchEvent touch_time_advance( |
| ui::ET_TOUCH_MOVED, gfx::Point(100, 200), |
| initial_time + gesture_detector_config_.double_tap_timeout + |
| base::TimeDelta::FromMilliseconds(1), |
| ui::PointerDetails(ui::EventPointerType::kTouch, 1)); |
| generator.Dispatch(&touch_time_advance); |
| |
| EXPECT_EQ(0, event_handler_->num_mouse_events()); |
| EXPECT_EQ(2, event_handler_->num_touch_events()); |
| event_handler_->Reset(); |
| |
| generator.MoveTouchId(gfx::Point(11, 12), 1); |
| EXPECT_EQ(0, event_handler_->num_mouse_events()); |
| EXPECT_EQ(1, event_handler_->num_touch_events()); |
| event_handler_->Reset(); |
| |
| initial_time = Now(); |
| ui::TouchEvent second_initial_press( |
| ui::ET_TOUCH_PRESSED, gfx::Point(499, 600), initial_time, |
| ui::PointerDetails(ui::EventPointerType::kTouch, 2)); |
| generator.Dispatch(&second_initial_press); |
| ui::TouchEvent second_touch_time_advance( |
| ui::ET_TOUCH_MOVED, gfx::Point(500, 600), |
| initial_time + gesture_detector_config_.double_tap_timeout + |
| base::TimeDelta::FromMilliseconds(1), |
| ui::PointerDetails(ui::EventPointerType::kTouch, 2)); |
| generator.Dispatch(&second_touch_time_advance); |
| EXPECT_EQ(0, event_handler_->num_mouse_events()); |
| EXPECT_EQ(2, event_handler_->num_touch_events()); |
| } |
| |
| // This test turns the touch exploration mode on and confirms that events get |
| // rewritten. |
| // Disabling due to failing over 10% of the time. (crbug.com/469119) |
| IN_PROC_BROWSER_TEST_F(TouchExplorationTest, DISABLED_RewritesEventsWhenOn) { |
| SwitchTouchExplorationMode(true); |
| ui::test::EventGenerator generator(root_window_); |
| |
| base::TimeTicks initial_time = Now(); |
| ui::TouchEvent initial_press( |
| ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), initial_time, |
| ui::PointerDetails(ui::EventPointerType::kTouch, 1)); |
| generator.Dispatch(&initial_press); |
| |
| // Since the touch exploration controller doesn't know if the user is |
| // double-tapping or not, touch exploration is only initiated if the |
| // 300 ms has elapsed and the finger does not move fast enough to begin |
| // gestures. Here, the touch move event is not important as a move, but |
| // a way to create time advancement. |
| ui::TouchEvent touch_time_advance( |
| ui::ET_TOUCH_MOVED, gfx::Point(100, 200), |
| initial_time + gesture_detector_config_.double_tap_timeout + |
| base::TimeDelta::FromMilliseconds(1), |
| ui::PointerDetails(ui::EventPointerType::kTouch, 1)); |
| generator.Dispatch(&touch_time_advance); |
| |
| // Number of mouse events may be greater than 1 because of ET_MOUSE_ENTERED. |
| EXPECT_GT(event_handler_->num_mouse_events(), 0); |
| EXPECT_EQ(0, event_handler_->num_touch_events()); |
| event_handler_->Reset(); |
| |
| initial_time = Now(); |
| ui::TouchEvent second_initial_press( |
| ui::ET_TOUCH_PRESSED, gfx::Point(500, 600), initial_time, |
| ui::PointerDetails(ui::EventPointerType::kTouch, 2)); |
| generator.Dispatch(&second_initial_press); |
| ui::TouchEvent second_touch_time_advance( |
| ui::ET_TOUCH_MOVED, gfx::Point(500, 600), |
| initial_time + gesture_detector_config_.double_tap_timeout + |
| base::TimeDelta::FromMilliseconds(1), |
| ui::PointerDetails(ui::EventPointerType::kTouch, 2)); |
| generator.Dispatch(&second_touch_time_advance); |
| EXPECT_GT(event_handler_->num_mouse_events(), 0); |
| EXPECT_EQ(1, event_handler_->num_touch_events()); |
| event_handler_->Reset(); |
| |
| // Stop the pending long press event. In some configurations, shutting down |
| // the browser can take longer than the long press timeout, and a long press |
| // event can come after the browser is already partly shut down, which causes |
| // the test to crash. |
| ui::TouchEvent release_second_touch( |
| ui::ET_TOUCH_RELEASED, gfx::Point(500, 600), |
| initial_time + gesture_detector_config_.double_tap_timeout + |
| base::TimeDelta::FromMilliseconds(1), |
| ui::PointerDetails(ui::EventPointerType::kTouch, 2)); |
| generator.Dispatch(&release_second_touch); |
| EXPECT_GT(event_handler_->num_mouse_events(), 0); |
| EXPECT_EQ(1, event_handler_->num_touch_events()); |
| } |
| |
| // This test makes sure that after the user clicks with split tap, |
| // they continue to touch exploration mode if the original touch exploration |
| // finger is still on the screen. |
| // Disabled due to failing upwards of 50% of the time (crbug.com/475923) |
| IN_PROC_BROWSER_TEST_F(TouchExplorationTest, DISABLED_SplitTapExplore) { |
| SwitchTouchExplorationMode(true); |
| ui::test::EventGenerator generator(root_window_); |
| aura::client::CursorClient* cursor_client = |
| aura::client::GetCursorClient(root_window_); |
| |
| // Mouse events should show the cursor. |
| generator.MoveMouseTo(gfx::Point(30, 31)); |
| EXPECT_TRUE(cursor_client->IsMouseEventsEnabled()); |
| EXPECT_TRUE(cursor_client->IsCursorVisible()); |
| |
| // The cursor should be shown immediately after the press, and hidden |
| // after the move. |
| base::TimeTicks initial_time = Now(); |
| ui::TouchEvent initial_press( |
| ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), initial_time, |
| ui::PointerDetails(ui::EventPointerType::kTouch, 1)); |
| generator.Dispatch(&initial_press); |
| EXPECT_TRUE(cursor_client->IsMouseEventsEnabled()); |
| EXPECT_TRUE(cursor_client->IsCursorVisible()); |
| |
| // Initiate touch explore by waiting for the tap timer timeout. Time is |
| // advanced by sending a move event after the timeout period. |
| ui::TouchEvent touch_time_advance( |
| ui::ET_TOUCH_MOVED, gfx::Point(100, 200), |
| initial_time + gesture_detector_config_.double_tap_timeout + |
| base::TimeDelta::FromMilliseconds(1), |
| ui::PointerDetails(ui::EventPointerType::kTouch, 1)); |
| generator.Dispatch(&touch_time_advance); |
| EXPECT_TRUE(cursor_client->IsMouseEventsEnabled()); |
| EXPECT_FALSE(cursor_client->IsCursorVisible()); |
| event_handler_->Reset(); |
| |
| // Press and release with a second finger for split tap. This should send |
| // touch press and release events which should send a click press and release. |
| // Once the press is passed through, mouse events should be disabled. |
| // Mouse events are reenabled after the release. |
| generator.set_current_screen_location(gfx::Point(102, 202)); |
| generator.PressTouchId(2); |
| EXPECT_FALSE(cursor_client->IsMouseEventsEnabled()); |
| EXPECT_FALSE(cursor_client->IsCursorVisible()); |
| generator.ReleaseTouchId(2); |
| EXPECT_TRUE(cursor_client->IsMouseEventsEnabled()); |
| EXPECT_FALSE(cursor_client->IsCursorVisible()); |
| EXPECT_EQ(2, event_handler_->num_touch_events()); |
| event_handler_->Reset(); |
| |
| // Continuing to move the touch exploration finger should send more mouse |
| // events. |
| generator.MoveTouchId(gfx::Point(509, 609), 1); |
| EXPECT_EQ(0, event_handler_->num_touch_events()); |
| EXPECT_TRUE(cursor_client->IsMouseEventsEnabled()); |
| EXPECT_FALSE(cursor_client->IsCursorVisible()); |
| } |
| |
| } // namespace ash |