blob: 3b1a0ee6d3b114f38c7d82f53f0e81adef81b677 [file] [log] [blame]
// 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/events/select_to_speak_event_handler.h"
#include <memory>
#include <set>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/public/cpp/select_to_speak_event_handler_delegate.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "ash/test/ash_test_views_delegate.h"
#include "base/memory/raw_ptr.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/window.h"
#include "ui/display/manager/display_manager.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/test/events_test_utils.h"
#include "ui/events/types/event_type.h"
namespace ash {
namespace {
// Records all key events for testing.
class EventCapturer : public ui::EventHandler {
public:
EventCapturer() {}
EventCapturer(const EventCapturer&) = delete;
EventCapturer& operator=(const EventCapturer&) = delete;
~EventCapturer() override {}
void Reset() {
last_key_event_.reset();
last_mouse_event_.reset();
last_touch_event_.reset();
}
ui::KeyEvent* last_key_event() { return last_key_event_.get(); }
ui::MouseEvent* last_mouse_event() { return last_mouse_event_.get(); }
ui::TouchEvent* last_touch_event() { return last_touch_event_.get(); }
private:
void OnMouseEvent(ui::MouseEvent* event) override {
last_mouse_event_ = std::make_unique<ui::MouseEvent>(*event);
}
void OnKeyEvent(ui::KeyEvent* event) override {
last_key_event_ = std::make_unique<ui::KeyEvent>(*event);
}
void OnTouchEvent(ui::TouchEvent* event) override {
last_touch_event_ = std::make_unique<ui::TouchEvent>(*event);
}
std::unique_ptr<ui::KeyEvent> last_key_event_;
std::unique_ptr<ui::MouseEvent> last_mouse_event_;
std::unique_ptr<ui::TouchEvent> last_touch_event_;
};
class TestDelegate : public SelectToSpeakEventHandlerDelegate {
public:
TestDelegate() = default;
TestDelegate(const TestDelegate&) = delete;
TestDelegate& operator=(const TestDelegate&) = delete;
virtual ~TestDelegate() = default;
bool CapturedMouseEvent(ui::EventType event_type) {
return base::Contains(mouse_events_captured_, event_type);
}
void Reset() {
mouse_events_captured_.clear();
last_mouse_location_.SetPoint(0, 0);
last_mouse_root_location_.SetPoint(0, 0);
}
gfx::Point last_mouse_event_location() { return last_mouse_location_; }
gfx::Point last_mouse_event_root_location() {
return last_mouse_root_location_;
}
private:
// SelectToSpeakEventHandlerDelegate:
void DispatchMouseEvent(const ui::MouseEvent& event) override {
mouse_events_captured_.insert(event.type());
last_mouse_location_ = event.location();
last_mouse_root_location_ = event.root_location();
}
void DispatchKeysCurrentlyDown(
const std::set<ui::KeyboardCode>& pressed_keys) override {
// Unused for now.
}
gfx::Point last_mouse_location_;
gfx::Point last_mouse_root_location_;
std::set<ui::EventType> mouse_events_captured_;
};
class SelectToSpeakEventHandlerTest : public AshTestBase {
public:
SelectToSpeakEventHandlerTest() = default;
SelectToSpeakEventHandlerTest(const SelectToSpeakEventHandlerTest&) = delete;
SelectToSpeakEventHandlerTest& operator=(
const SelectToSpeakEventHandlerTest&) = delete;
~SelectToSpeakEventHandlerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
// This test triggers a resize of WindowTreeHost, which will end up
// throttling events. set_throttle_input_on_resize_for_testing() disables
// this.
aura::Env::GetInstance()->set_throttle_input_on_resize_for_testing(false);
// Make sure the display is initialized so we don't fail the test due to any
// input events caused from creating the display.
Shell::Get()->display_manager()->UpdateDisplays();
base::RunLoop().RunUntilIdle();
delegate_ = std::make_unique<TestDelegate>();
generator_ = AshTestBase::GetEventGenerator();
GetContext()->AddPreTargetHandler(&event_capturer_);
controller_ = Shell::Get()->accessibility_controller();
controller_->select_to_speak().SetEnabled(true);
controller_->SetSelectToSpeakEventHandlerDelegate(delegate_.get());
}
void TearDown() override {
GetContext()->RemovePreTargetHandler(&event_capturer_);
generator_ = nullptr;
controller_ = nullptr;
AshTestBase::TearDown();
}
protected:
raw_ptr<ui::test::EventGenerator> generator_ = nullptr;
EventCapturer event_capturer_;
raw_ptr<AccessibilityController> controller_ = nullptr;
std::unique_ptr<TestDelegate> delegate_;
};
TEST_F(SelectToSpeakEventHandlerTest, PressAndReleaseSearchNotHandled) {
// If the user presses and releases the Search key, with no mouse
// presses, the key events won't be handled by the SelectToSpeakEventHandler
// and the normal behavior will occur.
EXPECT_FALSE(event_capturer_.last_key_event());
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
}
// Note: when running these tests locally on desktop Linux, you may need
// to use xvfb-run, otherwise simulating the Search key press may not work.
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusClick) {
// If the user holds the Search key and then clicks the mouse button,
// the mouse events and the key release event get handled by the
// SelectToSpeakEventHandler, and mouse events are forwarded to the
// extension.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
gfx::Point click_location = gfx::Point(100, 12);
generator_->set_current_screen_location(click_location);
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
EXPECT_EQ(click_location, delegate_->last_mouse_event_location());
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
EXPECT_EQ(click_location, delegate_->last_mouse_event_location());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusDrag) {
// Mouse move events should also be captured.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
gfx::Point click_location = gfx::Point(100, 12);
generator_->set_current_screen_location(click_location);
generator_->PressLeftButton();
EXPECT_EQ(click_location, delegate_->last_mouse_event_location());
// Drags are not blocked.
gfx::Point drag_location = gfx::Point(120, 32);
generator_->DragMouseTo(drag_location);
EXPECT_EQ(drag_location, delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_DRAGGED));
EXPECT_FALSE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseLeftButton();
EXPECT_EQ(drag_location, delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusMove) {
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
gfx::Point initial_mouse_location = gfx::Point(100, 12);
generator_->set_current_screen_location(initial_mouse_location);
// Hovers are not passed through.
gfx::Point move_location = gfx::Point(120, 32);
generator_->MoveMouseTo(move_location);
EXPECT_FALSE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusDragOnLargeDisplay) {
// This display has twice the number of pixels per DIP. This means that
// each event coming in in px needs to be divided by two to be converted
// to DIPs.
UpdateDisplay("800x600*2");
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
gfx::Point click_location_px = gfx::Point(100, 12);
generator_->set_current_screen_location(click_location_px);
generator_->PressLeftButton();
EXPECT_EQ(gfx::Point(click_location_px.x() / 2, click_location_px.y() / 2),
delegate_->last_mouse_event_location());
gfx::Point drag_location_px = gfx::Point(120, 32);
generator_->DragMouseTo(drag_location_px);
EXPECT_EQ(gfx::Point(drag_location_px.x() / 2, drag_location_px.y() / 2),
delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_DRAGGED));
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseLeftButton();
EXPECT_EQ(gfx::Point(drag_location_px.x() / 2, drag_location_px.y() / 2),
delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
}
TEST_F(SelectToSpeakEventHandlerTest, RepeatSearchKey) {
// Holding the Search key may generate key repeat events. Make sure it's
// still treated as if the search key is down.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
generator_->set_current_screen_location(gfx::Point(100, 12));
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest, TapSearchKey) {
// Tapping the search key should not steal future events.
event_capturer_.Reset();
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event()->handled());
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event()->handled());
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusClickTwice) {
// Same as SearchPlusClick, above, but test that the user can keep
// holding down Search and click again.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
generator_->set_current_screen_location(gfx::Point(100, 12));
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
delegate_->Reset();
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusKeyIgnoresClicks) {
// If the user presses the Search key and then some other key
// besides 's', we should assume the user does not want select-to-speak,
// and click events should be ignored.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
generator_->PressKey(ui::VKEY_I, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
generator_->set_current_screen_location(gfx::Point(100, 12));
generator_->PressLeftButton();
ASSERT_TRUE(event_capturer_.last_mouse_event());
EXPECT_FALSE(event_capturer_.last_mouse_event()->handled());
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->ReleaseLeftButton();
ASSERT_TRUE(event_capturer_.last_mouse_event());
EXPECT_FALSE(event_capturer_.last_mouse_event()->handled());
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_I, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusSIsCaptured) {
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
// Press and release S, key presses should be captured.
event_capturer_.Reset();
generator_->PressKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
generator_->ReleaseKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
// Press and release again while still holding down search.
// The events should continue to be captured.
generator_->PressKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
generator_->ReleaseKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
// S alone is not captured
generator_->PressKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_TRUE(event_capturer_.last_key_event());
ASSERT_FALSE(event_capturer_.last_key_event()->handled());
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusSIgnoresMouse) {
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
// Press S
event_capturer_.Reset();
generator_->PressKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
// Mouse events are passed through like normal.
generator_->PressLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
generator_->ReleaseKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ASSERT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest, SearchPlusMouseIgnoresS) {
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
// Press the mouse
event_capturer_.Reset();
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
// S key events are passed through like normal.
generator_->PressKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(event_capturer_.last_key_event());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_S, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(event_capturer_.last_key_event());
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest, DoesntStartSelectionModeIfNotInactive) {
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
// This shouldn't cause any changes since the state is not inactive.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
// Mouse event still captured.
gfx::Point click_location = gfx::Point(100, 12);
generator_->set_current_screen_location(click_location);
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
// This shouldn't cause any changes since the state is not inactive.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
generator_->ReleaseLeftButton();
// Releasing the search key is still captured per the end of the search+click
// mode.
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest,
CancelSearchKeyUpAfterEarlyInactiveStateChange) {
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
gfx::Point click_location = gfx::Point(100, 12);
generator_->set_current_screen_location(click_location);
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
// Set the state to inactive.
// This is realistic because Select-to-Speak will set the state to inactive
// after the hittest / search for the focused node callbacks, which may occur
// before the user actually releases the search key.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateInactive);
// The search key release should still be captured.
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_FALSE(event_capturer_.last_key_event());
}
TEST_F(SelectToSpeakEventHandlerTest, PassesCtrlKey) {
generator_->PressKey(ui::VKEY_CONTROL, /*flags=*/0);
ASSERT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_CONTROL, /*flags=*/0);
EXPECT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
}
TEST_F(SelectToSpeakEventHandlerTest, SelectionRequestedWorksWithMouse) {
gfx::Point click_location = gfx::Point(100, 12);
generator_->set_current_screen_location(click_location);
// Mouse events are let through normally before entering selecting state.
// Another mouse event is let through normally.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateInactive);
generator_->PressLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
// Start selection mode.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
generator_->PressLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
event_capturer_.Reset();
gfx::Point drag_location = gfx::Point(120, 32);
generator_->DragMouseTo(drag_location);
EXPECT_EQ(drag_location, delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_DRAGGED));
EXPECT_FALSE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
// Mouse up is the last event captured in the sequence
generator_->ReleaseLeftButton();
EXPECT_FALSE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
// Another mouse event is let through normally.
generator_->PressLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
}
TEST_F(SelectToSpeakEventHandlerTest, SelectionRequestedWorksWithTouch) {
gfx::Point touch_location = gfx::Point(100, 12);
generator_->set_current_screen_location(touch_location);
// Mouse events are let through normally before entering selecting state.
// Another mouse event is let through normally.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateInactive);
generator_->PressTouch();
EXPECT_TRUE(event_capturer_.last_touch_event());
event_capturer_.Reset();
generator_->ReleaseTouch();
EXPECT_TRUE(event_capturer_.last_touch_event());
event_capturer_.Reset();
// Start selection mode.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
generator_->PressTouch();
EXPECT_FALSE(event_capturer_.last_touch_event());
// Touch events are converted to mouse events for the extension.
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
event_capturer_.Reset();
gfx::Point drag_location = gfx::Point(120, 32);
generator_->MoveTouch(drag_location);
EXPECT_EQ(drag_location, delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_DRAGGED));
EXPECT_TRUE(event_capturer_.last_touch_event());
event_capturer_.Reset();
// Touch up is the last event captured in the sequence
generator_->ReleaseTouch();
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
event_capturer_.Reset();
// Another touch event is let through normally.
generator_->PressTouch();
EXPECT_TRUE(event_capturer_.last_touch_event());
event_capturer_.Reset();
}
TEST_F(SelectToSpeakEventHandlerTest, SelectionRequestedIgnoresOtherInput) {
// Start selection mode.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
// Search key events are not impacted.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(event_capturer_.last_key_event());
event_capturer_.Reset();
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(event_capturer_.last_key_event());
event_capturer_.Reset();
// Start a touch selection, it should get captured and forwarded.
generator_->PressTouch();
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
event_capturer_.Reset();
// Mouse event happening during the touch selection are not impacted;
// we are locked into a touch selection mode.
generator_->PressLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
generator_->ReleaseLeftButton();
EXPECT_TRUE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
// Complete the touch selection.
generator_->ReleaseTouch();
EXPECT_FALSE(event_capturer_.last_touch_event());
event_capturer_.Reset();
}
TEST_F(SelectToSpeakEventHandlerTest, SelectionRequestedPreventsHovers) {
// Start selection mode.
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
// Set the mouse.
gfx::Point initial_mouse_location = gfx::Point(100, 12);
generator_->set_current_screen_location(initial_mouse_location);
// Hovers are not passed through.
gfx::Point move_location = gfx::Point(120, 32);
generator_->MoveMouseTo(move_location);
EXPECT_FALSE(event_capturer_.last_mouse_event());
event_capturer_.Reset();
}
TEST_F(SelectToSpeakEventHandlerTest, TrackingTouchIgnoresOtherTouchPointers) {
gfx::Point touch_location = gfx::Point(100, 12);
gfx::Point drag_location = gfx::Point(120, 32);
generator_->set_current_screen_location(touch_location);
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
// The first touch event is captured and sent to the extension.
generator_->PressTouchId(1);
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
event_capturer_.Reset();
delegate_->Reset();
// A second touch event up and down is canceled but not sent to the extension.
generator_->PressTouchId(2);
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->MoveTouchId(drag_location, 2);
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_DRAGGED));
generator_->ReleaseTouchId(2);
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_RELEASED));
// A pointer type event will not be sent either, as we are tracking touch,
// even if the ID is the same.
generator_->EnterPenPointerMode();
generator_->PressTouchId(1);
EXPECT_FALSE(event_capturer_.last_touch_event());
EXPECT_FALSE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_PRESSED));
generator_->ExitPenPointerMode();
// The first pointer is still tracked.
generator_->MoveTouchId(drag_location, 1);
EXPECT_EQ(drag_location, delegate_->last_mouse_event_location());
EXPECT_TRUE(delegate_->CapturedMouseEvent(ui::ET_MOUSE_DRAGGED));
EXPECT_TRUE(event_capturer_.last_touch_event());
event_capturer_.Reset();
// Touch up is the last event captured in the sequence
generator_->ReleaseTouchId(1);
EXPECT_FALSE(event_capturer_.last_touch_event());
event_capturer_.Reset();
// Another touch event is let through normally.
generator_->PressTouchId(3);
EXPECT_TRUE(event_capturer_.last_touch_event());
event_capturer_.Reset();
}
TEST_F(SelectToSpeakEventHandlerTest, TouchFirstOfMultipleDisplays) {
UpdateDisplay("1+0-800x700,801+1-800x700");
// On the first display.
gfx::Point touch_location(200, 200);
generator_->set_current_screen_location(touch_location);
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
generator_->PressTouch();
EXPECT_EQ(touch_location, delegate_->last_mouse_event_root_location());
}
TEST_F(SelectToSpeakEventHandlerTest, TouchSecondOfMultipleDisplays) {
UpdateDisplay("1+0-800x700,801+1-800x700");
// On the second display.
gfx::Point touch_location(1000, 200);
generator_->set_current_screen_location(touch_location);
controller_->SetSelectToSpeakState(
SelectToSpeakState::kSelectToSpeakStateSelecting);
generator_->PressTouch();
EXPECT_EQ(touch_location, delegate_->last_mouse_event_root_location());
}
} // namespace
} // namespace ash