12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030 |
- // 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 <math.h>
- #include <stddef.h>
- #include <memory>
- #include <vector>
- #include "ash/accessibility/chromevox/mock_touch_exploration_controller_delegate.h"
- #include "base/memory/ptr_util.h"
- #include "base/test/simple_test_tick_clock.h"
- #include "base/time/time.h"
- #include "ui/accessibility/ax_enums.mojom.h"
- #include "ui/aura/client/cursor_client.h"
- #include "ui/aura/test/aura_test_base.h"
- #include "ui/aura/test/test_cursor_client.h"
- #include "ui/aura/window.h"
- #include "ui/events/event.h"
- #include "ui/events/event_utils.h"
- #include "ui/events/gestures/gesture_provider_aura.h"
- #include "ui/events/test/event_generator.h"
- #include "ui/events/test/events_test_utils.h"
- #include "ui/gfx/geometry/point.h"
- #include "ui/gfx/geometry/transform.h"
- #include "ui/gl/gl_implementation.h"
- #include "ui/gl/test/gl_surface_test_support.h"
- using EventList = std::vector<std::unique_ptr<ui::Event>>;
- namespace ash {
- namespace {
- // Records all mouse, touch, gesture, and key events.
- class EventCapturer : public ui::EventHandler {
- public:
- EventCapturer() {}
- EventCapturer(const EventCapturer&) = delete;
- EventCapturer& operator=(const EventCapturer&) = delete;
- ~EventCapturer() override {}
- void Reset() { events_.clear(); }
- void OnEvent(ui::Event* event) override {
- if (event->IsMouseEvent() || event->IsTouchEvent() ||
- event->IsGestureEvent() || event->IsKeyEvent()) {
- events_.push_back(event->Clone());
- } else {
- return;
- }
- // Stop event propagation so we don't click on random stuff that
- // might break test assumptions.
- event->StopPropagation();
- // If there is a possibility that we're in an infinite loop, we should
- // exit early with a sensible error rather than letting the test time out.
- ASSERT_LT(events_.size(), 100u);
- }
- const EventList& captured_events() const { return events_; }
- private:
- EventList events_;
- };
- int Factorial(int n) {
- if (n <= 0)
- return 0;
- if (n == 1)
- return 1;
- return n * Factorial(n - 1);
- }
- } // namespace
- class TouchExplorationControllerTestApi {
- public:
- TouchExplorationControllerTestApi(
- TouchExplorationController* touch_exploration_controller) {
- touch_exploration_controller_.reset(touch_exploration_controller);
- }
- TouchExplorationControllerTestApi(const TouchExplorationControllerTestApi&) =
- delete;
- TouchExplorationControllerTestApi& operator=(
- const TouchExplorationControllerTestApi&) = delete;
- void CallTapTimerNowForTesting() {
- DCHECK(touch_exploration_controller_->tap_timer_.IsRunning());
- touch_exploration_controller_->tap_timer_.Stop();
- touch_exploration_controller_->OnTapTimerFired();
- }
- void CallTapTimerNowIfRunningForTesting() {
- if (touch_exploration_controller_->tap_timer_.IsRunning()) {
- touch_exploration_controller_->tap_timer_.Stop();
- touch_exploration_controller_->OnTapTimerFired();
- }
- }
- void CallLongPressTimerNowIfRunningForTesting() {
- if (touch_exploration_controller_->long_press_timer_.IsRunning()) {
- touch_exploration_controller_->long_press_timer_.Stop();
- touch_exploration_controller_->OnLiftActivationLongPressTimerFired();
- }
- }
- bool IsInNoFingersDownStateForTesting() const {
- return touch_exploration_controller_->state_ ==
- touch_exploration_controller_->NO_FINGERS_DOWN;
- }
- bool IsInGestureInProgressStateForTesting() const {
- return touch_exploration_controller_->state_ ==
- touch_exploration_controller_->GESTURE_IN_PROGRESS;
- }
- bool IsInSlideGestureStateForTesting() const {
- return touch_exploration_controller_->state_ ==
- touch_exploration_controller_->SLIDE_GESTURE;
- }
- bool IsInTwoFingerTapStateForTesting() const {
- return touch_exploration_controller_->state_ ==
- touch_exploration_controller_->TWO_FINGER_TAP;
- }
- gfx::Rect BoundsOfRootWindowInDIPForTesting() const {
- return touch_exploration_controller_->root_window_->GetBoundsInScreen();
- }
- // VLOGs should be suppressed in tests that generate a lot of logs,
- // for example permutations of nine touch events.
- void SuppressVLOGsForTesting(bool suppress) {
- touch_exploration_controller_->VLOG_on_ = !suppress;
- }
- float GetMaxDistanceFromEdge() const {
- return touch_exploration_controller_->kMaxDistanceFromEdge;
- }
- float GetSlopDistanceFromEdge() const {
- return touch_exploration_controller_->kSlopDistanceFromEdge;
- }
- void SetTouchAccessibilityAnchorPoint(const gfx::Point& location) {
- touch_exploration_controller_->SetTouchAccessibilityAnchorPoint(location);
- }
- void SetExcludeBounds(const gfx::Rect& bounds) {
- touch_exploration_controller_->SetExcludeBounds(bounds);
- }
- void SetLiftActivationBounds(const gfx::Rect& bounds) {
- touch_exploration_controller_->SetLiftActivationBounds(bounds);
- }
- private:
- std::unique_ptr<TouchExplorationController> touch_exploration_controller_;
- };
- class TouchExplorationTest : public aura::test::AuraTestBase {
- public:
- TouchExplorationTest() {}
- TouchExplorationTest(const TouchExplorationTest&) = delete;
- TouchExplorationTest& operator=(const TouchExplorationTest&) = delete;
- ~TouchExplorationTest() override {}
- void SetUp() override {
- if (gl::GetGLImplementation() == gl::kGLImplementationNone)
- gl::GLSurfaceTestSupport::InitializeOneOff();
- aura::test::AuraTestBase::SetUp();
- cursor_client_ =
- std::make_unique<aura::test::TestCursorClient>(root_window());
- root_window()->AddPreTargetHandler(&event_capturer_);
- generator_ = std::make_unique<ui::test::EventGenerator>(root_window());
- // Tests fail if time is ever 0.
- simulated_clock_.Advance(base::Milliseconds(10));
- // ui takes ownership of the tick clock.
- ui::SetEventTickClockForTesting(&simulated_clock_);
- cursor_client()->ShowCursor();
- cursor_client()->DisableMouseEvents();
- }
- void TearDown() override {
- ui::SetEventTickClockForTesting(nullptr);
- root_window()->RemovePreTargetHandler(&event_capturer_);
- SwitchTouchExplorationMode(false);
- cursor_client_.reset();
- aura::test::AuraTestBase::TearDown();
- }
- protected:
- aura::client::CursorClient* cursor_client() { return cursor_client_.get(); }
- const EventList& GetCapturedEvents() {
- return event_capturer_.captured_events();
- }
- std::vector<ui::LocatedEvent*> GetCapturedLocatedEvents() {
- const EventList& all_events = GetCapturedEvents();
- std::vector<ui::LocatedEvent*> located_events;
- for (size_t i = 0; i < all_events.size(); ++i) {
- if (all_events[i]->IsMouseEvent() || all_events[i]->IsTouchEvent() ||
- all_events[i]->IsGestureEvent()) {
- located_events.push_back(
- static_cast<ui::LocatedEvent*>(all_events[i].get()));
- }
- }
- return located_events;
- }
- std::vector<ui::Event*> GetCapturedEventsOfType(int type) {
- const EventList& all_events = GetCapturedEvents();
- std::vector<ui::Event*> events;
- for (size_t i = 0; i < all_events.size(); ++i) {
- if (type == all_events[i]->type())
- events.push_back(all_events[i].get());
- }
- return events;
- }
- std::vector<ui::LocatedEvent*> GetCapturedLocatedEventsOfType(int type) {
- std::vector<ui::LocatedEvent*> located_events = GetCapturedLocatedEvents();
- std::vector<ui::LocatedEvent*> events;
- for (size_t i = 0; i < located_events.size(); ++i) {
- if (type == located_events[i]->type())
- events.push_back(located_events[i]);
- }
- return events;
- }
- std::vector<gfx::Point>& GetTouchExplorePoints() {
- return delegate_.GetTouchExplorePoints();
- }
- void ClearCapturedAndGestureEvents() {
- event_capturer_.Reset();
- GetTouchExplorePoints().clear();
- }
- void AdvanceSimulatedTimePastTapDelay() {
- simulated_clock_.Advance(gesture_detector_config_.double_tap_timeout);
- simulated_clock_.Advance(base::Milliseconds(1));
- touch_exploration_controller_->CallTapTimerNowForTesting();
- }
- void AdvanceSimulatedTimePastPotentialTapDelay() {
- simulated_clock_.Advance(base::Milliseconds(1000));
- touch_exploration_controller_->CallTapTimerNowIfRunningForTesting();
- }
- void AdvanceSimulatedTimePastLongPressDelay() {
- simulated_clock_.Advance(base::Milliseconds(5000));
- touch_exploration_controller_->CallLongPressTimerNowIfRunningForTesting();
- }
- void SuppressVLOGs(bool suppress) {
- touch_exploration_controller_->SuppressVLOGsForTesting(suppress);
- }
- void SwitchTouchExplorationMode(bool on) {
- if (!on && touch_exploration_controller_.get()) {
- touch_exploration_controller_.reset();
- } else if (on && !touch_exploration_controller_.get()) {
- touch_exploration_controller_ =
- std::make_unique<TouchExplorationControllerTestApi>(
- new TouchExplorationController(root_window(), &delegate_,
- nullptr));
- cursor_client()->ShowCursor();
- cursor_client()->DisableMouseEvents();
- }
- }
- void EnterTouchExplorationModeAtLocation(gfx::Point tap_location) {
- ui::TouchEvent touch_press(
- ui::ET_TOUCH_PRESSED, tap_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&touch_press);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInTouchToMouseMode());
- }
- bool IsInTouchToMouseMode() {
- aura::client::CursorClient* cursor_client =
- aura::client::GetCursorClient(root_window());
- return cursor_client && cursor_client->IsMouseEventsEnabled() &&
- !cursor_client->IsCursorVisible();
- }
- bool IsInNoFingersDownState() {
- return touch_exploration_controller_->IsInNoFingersDownStateForTesting();
- }
- bool IsInGestureInProgressState() {
- return touch_exploration_controller_
- ->IsInGestureInProgressStateForTesting();
- }
- bool IsInSlideGestureState() {
- return touch_exploration_controller_->IsInSlideGestureStateForTesting();
- }
- bool IsInTwoFingerTapState() {
- return touch_exploration_controller_->IsInTwoFingerTapStateForTesting();
- }
- gfx::Rect BoundsOfRootWindowInDIP() {
- return touch_exploration_controller_->BoundsOfRootWindowInDIPForTesting();
- }
- float GetMaxDistanceFromEdge() const {
- return touch_exploration_controller_->GetMaxDistanceFromEdge();
- }
- float GetSlopDistanceFromEdge() const {
- return touch_exploration_controller_->GetSlopDistanceFromEdge();
- }
- base::TimeTicks Now() { return ui::EventTimeForNow(); }
- void SetTouchAccessibilityAnchorPoint(const gfx::Point& location) {
- touch_exploration_controller_->SetTouchAccessibilityAnchorPoint(location);
- }
- void SetExcludeBounds(const gfx::Rect& bounds) {
- touch_exploration_controller_->SetExcludeBounds(bounds);
- }
- void SetLiftActivationBounds(const gfx::Rect& bounds) {
- touch_exploration_controller_->SetLiftActivationBounds(bounds);
- }
- // Taps at |tap_location|, waiting past tap delay to enter touch
- // exploration. Pass true to |set_anchor_point| to ensure any subsequent
- // gestures like a double tap go to |tap_location|. Usually, ChromeVox sets
- // the anchor, which is the center of the focused node and can differ from
- // |tap_location|.
- void TapAndVerifyTouchExplore(gfx::Point tap_location,
- bool set_anchor_point = false) {
- generator_->set_current_screen_location(tap_location);
- generator_->PressTouchId(1);
- generator_->ReleaseTouchId(1);
- AdvanceSimulatedTimePastTapDelay();
- if (set_anchor_point)
- SetTouchAccessibilityAnchorPoint(tap_location);
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_TRUE(events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- EXPECT_EQ(tap_location, GetTouchExplorePoints()[0]);
- ClearCapturedAndGestureEvents();
- }
- std::unique_ptr<ui::test::EventGenerator> generator_;
- ui::GestureDetector::Config gesture_detector_config_;
- base::SimpleTestTickClock simulated_clock_;
- MockTouchExplorationControllerDelegate delegate_;
- private:
- EventCapturer event_capturer_;
- std::unique_ptr<TouchExplorationControllerTestApi>
- touch_exploration_controller_;
- std::unique_ptr<aura::test::TestCursorClient> cursor_client_;
- };
- // Executes a number of assertions to confirm that |e1| and |e2| are touch
- // events and are equal to each other.
- void ConfirmEventsAreTouchAndEqual(ui::Event* e1, ui::Event* e2) {
- ASSERT_TRUE(e1->IsTouchEvent());
- ASSERT_TRUE(e2->IsTouchEvent());
- ui::TouchEvent* touch_event1 = e1->AsTouchEvent();
- ui::TouchEvent* touch_event2 = e2->AsTouchEvent();
- EXPECT_EQ(touch_event1->type(), touch_event2->type());
- EXPECT_EQ(touch_event1->location(), touch_event2->location());
- EXPECT_EQ(touch_event1->pointer_details().id,
- touch_event2->pointer_details().id);
- EXPECT_EQ(touch_event1->flags(), touch_event2->flags());
- EXPECT_EQ(touch_event1->time_stamp(), touch_event2->time_stamp());
- }
- // Executes a number of assertions to confirm that |e1| and |e2| are mouse
- // events and are equal to each other.
- void ConfirmEventsAreMouseAndEqual(ui::Event* e1, ui::Event* e2) {
- ASSERT_TRUE(e1->IsMouseEvent());
- ASSERT_TRUE(e2->IsMouseEvent());
- ui::MouseEvent* mouse_event1 = e1->AsMouseEvent();
- ui::MouseEvent* mouse_event2 = e2->AsMouseEvent();
- EXPECT_EQ(mouse_event1->type(), mouse_event2->type());
- EXPECT_EQ(mouse_event1->location(), mouse_event2->location());
- EXPECT_EQ(mouse_event1->root_location(), mouse_event2->root_location());
- EXPECT_EQ(mouse_event1->flags(), mouse_event2->flags());
- }
- // Executes a number of assertions to confirm that |e1| and |e2| are key events
- // and are equal to each other.
- void ConfirmEventsAreKeyAndEqual(ui::Event* e1, ui::Event* e2) {
- ASSERT_TRUE(e1->IsKeyEvent());
- ASSERT_TRUE(e2->IsKeyEvent());
- ui::KeyEvent* key_event1 = e1->AsKeyEvent();
- ui::KeyEvent* key_event2 = e2->AsKeyEvent();
- EXPECT_EQ(key_event1->type(), key_event2->type());
- EXPECT_EQ(key_event1->key_code(), key_event2->key_code());
- EXPECT_EQ(key_event1->code(), key_event2->code());
- EXPECT_EQ(key_event1->flags(), key_event2->flags());
- }
- #define CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(e1, e2) \
- ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreTouchAndEqual(e1, e2))
- #define CONFIRM_EVENTS_ARE_MOUSE_AND_EQUAL(e1, e2) \
- ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreMouseAndEqual(e1, e2))
- #define CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(e1, e2) \
- ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreKeyAndEqual(e1, e2))
- // TODO(mfomitchev): Need to investigate why we don't get mouse enter/exit
- // events when running these tests as part of ui_base_unittests. We do get them
- // when the tests are run as part of ash unit tests.
- TEST_F(TouchExplorationTest, EntersTouchToMouseModeAfterPressAndDelay) {
- SwitchTouchExplorationMode(true);
- EXPECT_FALSE(IsInTouchToMouseMode());
- generator_->PressTouch();
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInTouchToMouseMode());
- }
- TEST_F(TouchExplorationTest, EntersTouchToMouseModeAfterMoveOutsideSlop) {
- int slop = gesture_detector_config_.touch_slop;
- int half_slop = slop / 2;
- SwitchTouchExplorationMode(true);
- EXPECT_FALSE(IsInTouchToMouseMode());
- generator_->set_current_screen_location(gfx::Point(11, 12));
- generator_->PressTouch();
- generator_->MoveTouch(gfx::Point(11 + half_slop, 12));
- EXPECT_FALSE(IsInTouchToMouseMode());
- generator_->MoveTouch(gfx::Point(11, 12 + half_slop));
- EXPECT_FALSE(IsInTouchToMouseMode());
- AdvanceSimulatedTimePastTapDelay();
- generator_->MoveTouch(gfx::Point(11 + slop + 1, 12));
- EXPECT_TRUE(IsInTouchToMouseMode());
- }
- TEST_F(TouchExplorationTest, OneFingerTap) {
- SwitchTouchExplorationMode(true);
- gfx::Point location(11, 12);
- generator_->set_current_screen_location(location);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- AdvanceSimulatedTimePastTapDelay();
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_EQ(0U, events.size());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- EXPECT_EQ(location, GetTouchExplorePoints()[0]);
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- TEST_F(TouchExplorationTest, ActualMouseMovesUnaffected) {
- SwitchTouchExplorationMode(true);
- gfx::Point location_start(11, 12);
- gfx::Point location_end(13, 14);
- generator_->set_current_screen_location(location_start);
- generator_->PressTouch();
- AdvanceSimulatedTimePastTapDelay();
- generator_->MoveTouch(location_end);
- gfx::Point location_real_mouse_move(15, 16);
- ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, location_real_mouse_move,
- location_real_mouse_move, ui::EventTimeForNow(), 0,
- 0);
- generator_->Dispatch(&mouse_move);
- generator_->ReleaseTouch();
- AdvanceSimulatedTimePastTapDelay();
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- std::vector<gfx::Point> touch_explore_points = GetTouchExplorePoints();
- ASSERT_EQ(1U, events.size());
- ASSERT_EQ(3U, touch_explore_points.size());
- EXPECT_EQ(location_start, touch_explore_points[0]);
- EXPECT_EQ(location_end, touch_explore_points[1]);
- EXPECT_EQ(location_end, touch_explore_points[2]);
- // The real mouse move goes through.
- EXPECT_EQ(location_real_mouse_move, events[0]->location());
- CONFIRM_EVENTS_ARE_MOUSE_AND_EQUAL(events[0], &mouse_move);
- EXPECT_FALSE(events[0]->flags() & ui::EF_IS_SYNTHESIZED);
- EXPECT_FALSE(events[0]->flags() & ui::EF_TOUCH_ACCESSIBILITY);
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // Turn the touch exploration mode on in the middle of the touch gesture.
- // Confirm that events from the finger which was touching when the mode was
- // turned on don't get rewritten.
- TEST_F(TouchExplorationTest, TurnOnMidTouch) {
- SwitchTouchExplorationMode(false);
- generator_->PressTouchId(1);
- EXPECT_TRUE(cursor_client()->IsCursorVisible());
- ClearCapturedAndGestureEvents();
- // Enable touch exploration mode while the first finger is touching the
- // screen. Ensure that subsequent events from that first finger are not
- // affected by the touch exploration mode, while the touch events from another
- // finger get rewritten.
- SwitchTouchExplorationMode(true);
- ui::TouchEvent touch_move(
- ui::ET_TOUCH_MOVED, gfx::Point(11, 12), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&touch_move);
- EXPECT_TRUE(cursor_client()->IsCursorVisible());
- EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled());
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(1u, captured_events.size());
- CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events[0], &touch_move);
- ClearCapturedAndGestureEvents();
- // The press from the second finger should get rewritten.
- generator_->PressTouchId(2);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInTouchToMouseMode());
- ASSERT_FALSE(GetTouchExplorePoints().empty());
- // The release of the first finger shouldn't be affected.
- ui::TouchEvent touch_release(
- ui::ET_TOUCH_RELEASED, gfx::Point(11, 12), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&touch_release);
- captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(1u, captured_events.size());
- CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events[0], &touch_release);
- ClearCapturedAndGestureEvents();
- // The move and release from the second finger should get rewritten.
- generator_->MoveTouchId(gfx::Point(13, 14), 2);
- generator_->ReleaseTouchId(2);
- AdvanceSimulatedTimePastTapDelay();
- captured_events = GetCapturedLocatedEvents();
- ASSERT_TRUE(captured_events.empty());
- ASSERT_EQ(2U, GetTouchExplorePoints().size());
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // If an event is received after the double-tap timeout has elapsed, but
- // before the timer has fired, a mouse move should still be generated.
- TEST_F(TouchExplorationTest, TimerFiresLateDuringTouchExploration) {
- SwitchTouchExplorationMode(true);
- // Make sure the touch is not in a corner of the screen.
- generator_->MoveTouch(gfx::Point(100, 200));
- // Send a press, then add another finger after the double-tap timeout.
- generator_->PressTouchId(1);
- simulated_clock_.Advance(base::Milliseconds(1000));
- generator_->PressTouchId(2);
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_TRUE(events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- generator_->ReleaseTouchId(2);
- generator_->ReleaseTouchId(1);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // If a new tap is received after the double-tap timeout has elapsed from
- // a previous tap, but before the timer has fired, a mouse move should
- // still be generated from the old tap.
- TEST_F(TouchExplorationTest, TimerFiresLateAfterTap) {
- SwitchTouchExplorationMode(true);
- // Send a tap at location1.
- gfx::Point location0(11, 12);
- generator_->set_current_screen_location(location0);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- // Send a tap at location2, after the double-tap timeout, but before the
- // timer fires.
- gfx::Point location1(33, 34);
- generator_->set_current_screen_location(location1);
- simulated_clock_.Advance(base::Milliseconds(301));
- generator_->PressTouch();
- generator_->ReleaseTouch();
- AdvanceSimulatedTimePastTapDelay();
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_TRUE(events.empty());
- ASSERT_EQ(2U, GetTouchExplorePoints().size());
- EXPECT_EQ(location0, GetTouchExplorePoints()[0]);
- EXPECT_EQ(location1, GetTouchExplorePoints()[1]);
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // Double-tapping should send a touch press and release through to the location
- // of the last successful touch exploration.
- TEST_F(TouchExplorationTest, DoubleTap) {
- SwitchTouchExplorationMode(true);
- gfx::Point tap_location(51, 52);
- TapAndVerifyTouchExplore(tap_location);
- // Now double-tap at a different location. This should result in
- // no touches at all, but a click gesture to ChromeVox.
- gfx::Point double_tap_location(33, 34);
- generator_->set_current_screen_location(double_tap_location);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- generator_->PressTouch();
- generator_->ReleaseTouch();
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_TRUE(captured_events.empty());
- EXPECT_EQ(ax::mojom::Gesture::kClick, delegate_.GetLastGesture());
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // The press of the second tap in a double-tap must come within the double-tap
- // timeout, but the release of the second tap can come later.
- TEST_F(TouchExplorationTest, DoubleTapTiming) {
- SwitchTouchExplorationMode(true);
- gfx::Point tap_location(51, 52);
- TapAndVerifyTouchExplore(tap_location, true);
- // The press of the second tap happens in time, but the release does not.
- gfx::Point double_tap_location(33, 34);
- generator_->set_current_screen_location(double_tap_location);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- simulated_clock_.Advance(gesture_detector_config_.double_tap_timeout -
- base::Milliseconds(25));
- generator_->PressTouch();
- simulated_clock_.Advance(base::Milliseconds(50));
- generator_->ReleaseTouch();
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_EQ(ax::mojom::Gesture::kClick, delegate_.GetLastGesture());
- }
- // If an explicit anchor point is set during touch exploration, double-tapping
- // should send a 'click' gesture rather than a simulated touch press and
- // release.
- TEST_F(TouchExplorationTest, DoubleTapWithExplicitAnchorPoint) {
- SwitchTouchExplorationMode(true);
- gfx::Point tap_location(51, 52);
- TapAndVerifyTouchExplore(tap_location, true);
- // Now double-tap at a different location. This should result in
- // a click gesture.
- gfx::Point double_tap_location(33, 34);
- generator_->set_current_screen_location(double_tap_location);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- generator_->PressTouch();
- generator_->ReleaseTouch();
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_TRUE(IsInNoFingersDownState());
- EXPECT_EQ(ax::mojom::Gesture::kClick, delegate_.GetLastGesture());
- }
- // Double-tapping where the user holds their finger down for the second time
- // for a longer press should send a touch press and passthrough all further
- // events from that finger. Other finger presses should be ignored.
- TEST_F(TouchExplorationTest, DoubleTapPassthrough) {
- SwitchTouchExplorationMode(true);
- gfx::Point tap_location(11, 12);
- TapAndVerifyTouchExplore(tap_location);
- // Now double-tap and hold at a different location.
- // This should result in a single touch press at the location of the tap,
- // not at the location of the double-tap.
- gfx::Point first_tap_location(13, 14);
- generator_->set_current_screen_location(first_tap_location);
- generator_->PressTouchId(1);
- generator_->ReleaseTouchId(1);
- ASSERT_EQ(0U, delegate_.NumPassthroughSounds());
- gfx::Point second_tap_location(15, 16);
- generator_->set_current_screen_location(second_tap_location);
- generator_->PressTouchId(1);
- // Advance to the finger passing through.
- AdvanceSimulatedTimePastTapDelay();
- ASSERT_EQ(1U, delegate_.NumPassthroughSounds());
- gfx::Vector2d passthrough_offset = second_tap_location - tap_location;
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(1U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type());
- EXPECT_EQ(second_tap_location - passthrough_offset,
- captured_events[0]->location());
- EXPECT_TRUE(captured_events[0]->flags() & ui::EF_TOUCH_ACCESSIBILITY);
- ClearCapturedAndGestureEvents();
- // All events for the first finger should pass through now, displaced
- // relative to the last touch exploration location.
- gfx::Point first_move_location(17, 18);
- generator_->MoveTouchId(first_move_location, 1);
- gfx::Point second_move_location(12, 13);
- generator_->MoveTouchId(second_move_location, 1);
- captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(2U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[0]->type());
- EXPECT_EQ(first_move_location - passthrough_offset,
- captured_events[0]->location());
- EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[1]->type());
- EXPECT_EQ(second_move_location - passthrough_offset,
- captured_events[1]->location());
- EXPECT_TRUE(captured_events[1]->flags() & ui::EF_TOUCH_ACCESSIBILITY);
- ClearCapturedAndGestureEvents();
- // Events for other fingers should do nothing.
- generator_->PressTouchId(2);
- generator_->PressTouchId(3);
- generator_->MoveTouchId(gfx::Point(34, 36), 2);
- generator_->ReleaseTouchId(2);
- captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- // Even with finger 3 still down, events for the first finger should still
- // pass through.
- gfx::Point third_move_location(14, 15);
- generator_->MoveTouchId(third_move_location, 1);
- captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(1U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[0]->type());
- EXPECT_EQ(third_move_location - passthrough_offset,
- captured_events[0]->location());
- EXPECT_TRUE(captured_events[0]->flags() & ui::EF_TOUCH_ACCESSIBILITY);
- // No fingers down state is only reached when every finger is lifted.
- generator_->ReleaseTouchId(1);
- EXPECT_FALSE(IsInNoFingersDownState());
- generator_->ReleaseTouchId(3);
- EXPECT_TRUE(IsInNoFingersDownState());
- // There should have only ever been one pass through earcon played.
- ASSERT_EQ(1U, delegate_.NumPassthroughSounds());
- }
- // Double-tapping, going into passthrough, and holding for the longpress
- // time should send a touch press and released (right click)
- // to the location of the last successful touch exploration.
- TEST_F(TouchExplorationTest, DoubleTapLongPress) {
- SwitchTouchExplorationMode(true);
- gfx::Point tap_location(11, 12);
- TapAndVerifyTouchExplore(tap_location);
- // Now double-tap and hold at a different location.
- // This should result in a single touch long press and release
- // at the location of the tap, not at the location of the double-tap.
- // There should be a time delay between the touch press and release.
- gfx::Point first_tap_location(33, 34);
- generator_->set_current_screen_location(first_tap_location);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- gfx::Point second_tap_location(23, 24);
- generator_->set_current_screen_location(second_tap_location);
- generator_->PressTouch();
- // Advance to the finger passing through, and then to the longpress timeout.
- AdvanceSimulatedTimePastTapDelay();
- simulated_clock_.Advance(gesture_detector_config_.longpress_timeout);
- generator_->ReleaseTouch();
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(2U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type());
- EXPECT_EQ(tap_location, captured_events[0]->location());
- EXPECT_TRUE(captured_events[0]->flags() & ui::EF_TOUCH_ACCESSIBILITY);
- base::TimeTicks pressed_time = captured_events[0]->time_stamp();
- EXPECT_EQ(ui::ET_TOUCH_RELEASED, captured_events[1]->type());
- EXPECT_EQ(tap_location, captured_events[1]->location());
- EXPECT_TRUE(captured_events[1]->flags() & ui::EF_TOUCH_ACCESSIBILITY);
- base::TimeTicks released_time = captured_events[1]->time_stamp();
- EXPECT_EQ(released_time - pressed_time,
- gesture_detector_config_.longpress_timeout);
- }
- // Single-tapping should send a touch press and release through to the location
- // of the last successful touch exploration if the grace period has not
- // elapsed.
- TEST_F(TouchExplorationTest, SingleTap) {
- SwitchTouchExplorationMode(true);
- // Tap once to simulate a touch explore point.
- gfx::Point initial_location(11, 12);
- generator_->set_current_screen_location(initial_location);
- generator_->PressTouch();
- AdvanceSimulatedTimePastTapDelay();
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- // Move to another location for single tap
- gfx::Point tap_location(22, 23);
- generator_->MoveTouch(tap_location);
- generator_->ReleaseTouch();
- // Allow time to pass within the grace period of releasing before
- // tapping again.
- gfx::Point final_location(33, 34);
- generator_->set_current_screen_location(final_location);
- simulated_clock_.Advance(base::Milliseconds(250));
- generator_->PressTouch();
- generator_->ReleaseTouch();
- ASSERT_EQ(3U, GetTouchExplorePoints().size());
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_TRUE(captured_events.empty());
- EXPECT_EQ(ax::mojom::Gesture::kClick, delegate_.GetLastGesture());
- }
- // Double-tapping without coming from touch exploration (no previous touch
- // exploration event) should not generate any events.
- TEST_F(TouchExplorationTest, DoubleTapNoTouchExplore) {
- SwitchTouchExplorationMode(true);
- // Double-tap without any previous touch.
- // Touch exploration mode has not been entered, so there is no previous
- // touch exploration event. The double-tap should be discarded, and no events
- // should be generated at all.
- gfx::Point double_tap_location(33, 34);
- generator_->set_current_screen_location(double_tap_location);
- generator_->PressTouch();
- generator_->ReleaseTouch();
- generator_->PressTouch();
- // Since the state stays in single_tap_released, we need to make sure the
- // tap timer doesn't fire and set the state to no fingers down (since there
- // is still a finger down).
- AdvanceSimulatedTimePastPotentialTapDelay();
- EXPECT_FALSE(IsInNoFingersDownState());
- generator_->ReleaseTouch();
- EXPECT_TRUE(IsInNoFingersDownState());
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- }
- // Tapping and releasing with a second finger when in touch exploration mode
- // should send a touch press and released to the location of the last
- // successful touch exploration and return to touch explore.
- TEST_F(TouchExplorationTest, SplitTap) {
- SwitchTouchExplorationMode(true);
- gfx::Point initial_touch_location(11, 12);
- gfx::Point second_touch_location(33, 34);
- // Tap and hold at one location, and get a mouse move event in touch explore.
- EnterTouchExplorationModeAtLocation(initial_touch_location);
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_TRUE(events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- EXPECT_EQ(initial_touch_location, GetTouchExplorePoints()[0]);
- ClearCapturedAndGestureEvents();
- EXPECT_TRUE(IsInTouchToMouseMode());
- // Now tap and release at a different location. This should result in a
- // single touch and release at the location of the first (held) tap,
- // not at the location of the second tap and release.
- // After the release, there is still a finger in touch explore mode.
- ui::TouchEvent split_tap_press(
- ui::ET_TOUCH_PRESSED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_press);
- // To simulate the behavior of the real device, we manually disable
- // mouse events. To not rely on manually setting the state, this is also
- // tested in touch_exploration_controller_browsertest.
- cursor_client()->DisableMouseEvents();
- EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled());
- EXPECT_FALSE(cursor_client()->IsCursorVisible());
- EXPECT_FALSE(IsInGestureInProgressState());
- ui::TouchEvent split_tap_release(
- ui::ET_TOUCH_RELEASED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_release);
- // Releasing the second finger should re-enable mouse events putting us
- // back into the touch exploration mode.
- EXPECT_TRUE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInNoFingersDownState());
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_TRUE(captured_events.empty());
- EXPECT_EQ(ax::mojom::Gesture::kClick, delegate_.GetLastGesture());
- ClearCapturedAndGestureEvents();
- ui::TouchEvent touch_explore_release(
- ui::ET_TOUCH_RELEASED, initial_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&touch_explore_release);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // If split tap is started but the touch explore finger is released first,
- // there should still be a touch press and release sent to the location of
- // the last successful touch exploration.
- // Both fingers should be released after the click goes through.
- TEST_F(TouchExplorationTest, SplitTapRelease) {
- SwitchTouchExplorationMode(true);
- gfx::Point initial_touch_location(11, 12);
- gfx::Point second_touch_location(33, 34);
- // Tap and hold at one location, and get a mouse move event in touch explore.
- EnterTouchExplorationModeAtLocation(initial_touch_location);
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_TRUE(events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- // Now tap at a different location. Release at the first location,
- // then release at the second. This should result in a
- // click gesture to ChromeVox.
- ui::TouchEvent split_tap_press(
- ui::ET_TOUCH_PRESSED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_press);
- ui::TouchEvent touch_explore_release(
- ui::ET_TOUCH_RELEASED, initial_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&touch_explore_release);
- ui::TouchEvent split_tap_release(
- ui::ET_TOUCH_RELEASED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_release);
- EXPECT_TRUE(IsInNoFingersDownState());
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- EXPECT_TRUE(captured_events.empty());
- EXPECT_EQ(ax::mojom::Gesture::kClick, delegate_.GetLastGesture());
- }
- TEST_F(TouchExplorationTest, SplitTapMultiFinger) {
- SwitchTouchExplorationMode(true);
- gfx::Point initial_touch_location(11, 12);
- gfx::Point second_touch_location(33, 34);
- gfx::Point third_touch_location(16, 17);
- // Tap and hold at one location, and get a mouse move event in touch explore.
- EnterTouchExplorationModeAtLocation(initial_touch_location);
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED);
- ASSERT_TRUE(events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- EXPECT_EQ(initial_touch_location, GetTouchExplorePoints()[0]);
- ClearCapturedAndGestureEvents();
- // Now tap at a different location
- ui::TouchEvent split_tap_press(
- ui::ET_TOUCH_PRESSED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_press);
- simulated_clock_.Advance(gesture_detector_config_.longpress_timeout);
- // Placing a third finger on the screen should cancel the split tap and
- // enter the wait state.
- ui::TouchEvent third_press(
- ui::ET_TOUCH_PRESSED, third_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- generator_->Dispatch(&third_press);
- // When all three fingers are released, no events should be captured.
- // All fingers should then be up.
- ui::TouchEvent touch_explore_release(
- ui::ET_TOUCH_RELEASED, initial_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&touch_explore_release);
- ui::TouchEvent split_tap_release(
- ui::ET_TOUCH_RELEASED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_release);
- ui::TouchEvent third_tap_release(
- ui::ET_TOUCH_RELEASED, third_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- generator_->Dispatch(&third_tap_release);
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- TEST_F(TouchExplorationTest, SplitTapLeaveSlop) {
- SwitchTouchExplorationMode(true);
- gfx::Point first_touch_location(11, 12);
- gfx::Point second_touch_location(33, 34);
- gfx::Point first_move_location(
- first_touch_location.x() + gesture_detector_config_.touch_slop * 3 + 1,
- first_touch_location.y());
- gfx::Point second_move_location(
- second_touch_location.x() + gesture_detector_config_.touch_slop * 3 + 1,
- second_touch_location.y());
- // Tap and hold at one location, and get a mouse move event in touch explore.
- EnterTouchExplorationModeAtLocation(first_touch_location);
- ClearCapturedAndGestureEvents();
- // Now tap at a different location for split tap.
- ui::TouchEvent split_tap_press(
- ui::ET_TOUCH_PRESSED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_press);
- // Move the first finger out of slop and release both fingers. The split
- // tap should have been cancelled.
- ui::TouchEvent first_touch_move(
- ui::ET_TOUCH_MOVED, first_move_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&first_touch_move);
- ui::TouchEvent first_touch_release(
- ui::ET_TOUCH_RELEASED, first_move_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&first_touch_release);
- ui::TouchEvent second_touch_release(
- ui::ET_TOUCH_RELEASED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&second_touch_release);
- std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_TRUE(IsInNoFingersDownState());
- // Now do the same, but moving the split tap finger out of slop
- EnterTouchExplorationModeAtLocation(first_touch_location);
- ClearCapturedAndGestureEvents();
- ui::TouchEvent split_tap_press2(
- ui::ET_TOUCH_PRESSED, second_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&split_tap_press2);
- // Move the second finger out of slop and release both fingers. The split
- // tap should have been cancelled.
- ui::TouchEvent second_touch_move2(
- ui::ET_TOUCH_MOVED, second_move_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&second_touch_move2);
- ui::TouchEvent first_touch_release2(
- ui::ET_TOUCH_RELEASED, first_touch_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&first_touch_release2);
- ui::TouchEvent second_touch_release2(
- ui::ET_TOUCH_RELEASED, second_move_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&second_touch_release2);
- captured_events = GetCapturedLocatedEvents();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_TRUE(IsInNoFingersDownState());
- }
- // Finger must have moved more than slop, faster than the minimum swipe
- // velocity, and before the tap timer fires in order to enter
- // GestureInProgress state. Otherwise, if the tap timer fires before the a
- // gesture is completed, enter touch exploration.
- TEST_F(TouchExplorationTest, EnterGestureInProgressState) {
- SwitchTouchExplorationMode(true);
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- float distance = gesture_detector_config_.touch_slop + 1;
- ui::TouchEvent first_press(
- ui::ET_TOUCH_PRESSED, gfx::Point(0, 1), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- gfx::Point second_location(distance / 2, 1);
- gfx::Point third_location(distance, 1);
- gfx::Point touch_exploration_location(20, 21);
- generator_->Dispatch(&first_press);
- simulated_clock_.Advance(base::Milliseconds(10));
- // Since we are not out of the touch slop yet, we should not be in gesture in
- // progress.
- generator_->MoveTouch(second_location);
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- simulated_clock_.Advance(base::Milliseconds(10));
- // Once we are out of slop, we should be in GestureInProgress.
- generator_->MoveTouch(third_location);
- EXPECT_TRUE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- // Exit out of gesture mode once grace period is over and enter touch
- // exploration. There should be a touch explore gesture when entering touch
- // exploration and also for the touch move.
- AdvanceSimulatedTimePastTapDelay();
- generator_->MoveTouch(touch_exploration_location);
- ASSERT_TRUE(captured_events.empty());
- EXPECT_EQ(2U, GetTouchExplorePoints().size());
- EXPECT_TRUE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- }
- // A swipe+direction gesture should trigger a Shift+Search+Direction
- // keyboard event.
- TEST_F(TouchExplorationTest, GestureSwipe) {
- SwitchTouchExplorationMode(true);
- // Test all four swipe directions with 1 to 4 fingers.
- struct GestureInfo {
- int move_x;
- int move_y;
- int num_fingers;
- ax::mojom::Gesture expected_gesture;
- } gestures_to_test[] = {
- {-1, 0, 1, ax::mojom::Gesture::kSwipeLeft1},
- {0, -1, 1, ax::mojom::Gesture::kSwipeUp1},
- {1, 0, 1, ax::mojom::Gesture::kSwipeRight1},
- {0, 1, 1, ax::mojom::Gesture::kSwipeDown1},
- {-1, 0, 2, ax::mojom::Gesture::kSwipeLeft2},
- {0, -1, 2, ax::mojom::Gesture::kSwipeUp2},
- {1, 0, 2, ax::mojom::Gesture::kSwipeRight2},
- {0, 1, 2, ax::mojom::Gesture::kSwipeDown2},
- {-1, 0, 3, ax::mojom::Gesture::kSwipeLeft3},
- {0, -1, 3, ax::mojom::Gesture::kSwipeUp3},
- {1, 0, 3, ax::mojom::Gesture::kSwipeRight3},
- {0, 1, 3, ax::mojom::Gesture::kSwipeDown3},
- {-1, 0, 4, ax::mojom::Gesture::kSwipeLeft4},
- {0, -1, 4, ax::mojom::Gesture::kSwipeUp4},
- {1, 0, 4, ax::mojom::Gesture::kSwipeRight4},
- {0, 1, 4, ax::mojom::Gesture::kSwipeDown4},
- };
- // This value was taken from gesture_recognizer_unittest.cc in a swipe
- // detector test, since it seems to be about the right amount to get a swipe.
- const int kSteps = 15;
- for (size_t i = 0; i < std::size(gestures_to_test); ++i) {
- const float distance = 2 * gesture_detector_config_.touch_slop + 1;
- int move_x = gestures_to_test[i].move_x * distance;
- int move_y = gestures_to_test[i].move_y * distance;
- int num_fingers = gestures_to_test[i].num_fingers;
- ax::mojom::Gesture expected_gesture = gestures_to_test[i].expected_gesture;
- std::vector<gfx::Point> start_points;
- for (int j = 0; j < num_fingers; j++) {
- start_points.push_back(gfx::Point(j * 10 + 100, j * 10 + 200));
- }
- gfx::Point* start_points_array = &start_points[0];
- // A swipe is made when a fling starts
- float delta_time =
- distance / gesture_detector_config_.maximum_fling_velocity;
- // delta_time is in seconds, so we convert to ms.
- int delta_time_ms = floor(delta_time * 1000);
- generator_->GestureMultiFingerScroll(num_fingers, start_points_array,
- delta_time_ms, kSteps, move_x, move_y);
- EXPECT_EQ(expected_gesture, delegate_.GetLastGesture());
- EXPECT_TRUE(IsInNoFingersDownState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- ClearCapturedAndGestureEvents();
- }
- }
- TEST_F(TouchExplorationTest, GestureSwipePortrit) {
- // Rotate the window 90-degrees counter-clockwise.
- root_window()->GetHost()->SetRootTransform(
- gfx::Transform(0, 1, 0, 0, -1, 0, 0, root_window()->bounds().height(), 0,
- 0, 0, 0, 0, 0, 0, 0));
- SwitchTouchExplorationMode(true);
- // Test 2-4 finger gestures.
- struct GestureInfo {
- int move_x;
- int move_y;
- int num_fingers;
- ax::mojom::Gesture expected_gesture;
- } gestures_to_test[] = {
- {-1, 0, 2, ax::mojom::Gesture::kSwipeDown2},
- {0, -1, 2, ax::mojom::Gesture::kSwipeLeft2},
- {1, 0, 2, ax::mojom::Gesture::kSwipeUp2},
- {0, 1, 2, ax::mojom::Gesture::kSwipeRight2},
- {-1, 0, 3, ax::mojom::Gesture::kSwipeDown3},
- {0, -1, 3, ax::mojom::Gesture::kSwipeLeft3},
- {1, 0, 3, ax::mojom::Gesture::kSwipeUp3},
- {0, 1, 3, ax::mojom::Gesture::kSwipeRight3},
- {-1, 0, 4, ax::mojom::Gesture::kSwipeDown4},
- {0, -1, 4, ax::mojom::Gesture::kSwipeLeft4},
- {1, 0, 4, ax::mojom::Gesture::kSwipeUp4},
- {0, 1, 4, ax::mojom::Gesture::kSwipeRight4},
- };
- // This value was taken from gesture_recognizer_unittest.cc in a swipe
- // detector test, since it seems to be about the right amount to get a swipe.
- const int kSteps = 15;
- for (size_t i = 0; i < std::size(gestures_to_test); ++i) {
- const float distance = 2 * gesture_detector_config_.touch_slop + 1;
- int move_x = gestures_to_test[i].move_x * distance;
- int move_y = gestures_to_test[i].move_y * distance;
- int num_fingers = gestures_to_test[i].num_fingers;
- ax::mojom::Gesture expected_gesture = gestures_to_test[i].expected_gesture;
- std::vector<gfx::Point> start_points;
- for (int j = 0; j < num_fingers; j++) {
- start_points.push_back(gfx::Point(j * 10 + 100, j * 10 + 200));
- }
- gfx::Point* start_points_array = &start_points[0];
- // A swipe is made when a fling starts
- float delta_time =
- distance / gesture_detector_config_.maximum_fling_velocity;
- // delta_time is in seconds, so we convert to ms.
- int delta_time_ms = floor(delta_time * 1000);
- generator_->GestureMultiFingerScroll(num_fingers, start_points_array,
- delta_time_ms, kSteps, move_x, move_y);
- EXPECT_EQ(expected_gesture, delegate_.GetLastGesture());
- EXPECT_TRUE(IsInNoFingersDownState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- ClearCapturedAndGestureEvents();
- }
- }
- // Since there are so many permutations, this test is fairly slow. Therefore, it
- // is disabled and will be turned on to check during development.
- TEST_F(TouchExplorationTest, DISABLED_AllFingerPermutations) {
- SwitchTouchExplorationMode(true);
- SuppressVLOGs(true);
- // We will test all permutations of events from three different fingers
- // to ensure that we return to NO_FINGERS_DOWN when fingers have been
- // released.
- std::vector<std::unique_ptr<ui::TouchEvent>> all_events;
- // A copy of all events list which can be modified without destrying events.
- std::vector<ui::TouchEvent*> queued_events;
- for (int touch_id = 0; touch_id < 3; touch_id++) {
- int x = 10 * touch_id + 1;
- int y = 10 * touch_id + 2;
- all_events.push_back(std::make_unique<ui::TouchEvent>(
- ui::ET_TOUCH_PRESSED, gfx::Point(x++, y++), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, touch_id)));
- queued_events.push_back(all_events.back().get());
- all_events.push_back(std::make_unique<ui::TouchEvent>(
- ui::ET_TOUCH_MOVED, gfx::Point(x++, y++), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, touch_id)));
- queued_events.push_back(all_events.back().get());
- all_events.push_back(std::make_unique<ui::TouchEvent>(
- ui::ET_TOUCH_RELEASED, gfx::Point(x, y), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, touch_id)));
- queued_events.push_back(all_events.back().get());
- }
- // I'm going to explain this algorithm, and use an example in parentheses.
- // The example will be all permutations of a b c d.
- // There are four letters and 4! = 24 permutations.
- const int num_events = all_events.size();
- const int num_permutations = Factorial(num_events);
- for (int p = 0; p < num_permutations; p++) {
- std::vector<bool> fingers_pressed(3, false);
- int current_num_permutations = num_permutations;
- for (int events_left = num_events; events_left > 0; events_left--) {
- // |p| indexes to each permutation when there are num_permutations
- // permutations. (e.g. 0 is abcd, 1 is abdc, 2 is acbd, 3 is acdb...)
- // But how do we find the index for the current number of permutations?
- // To find the permutation within the part of the sequence we're
- // currently looking at, we need a number between 0 and
- // |current_num_permutations| - 1.
- // (e.g. if we already chose the first letter, there are 3! = 6
- // options left, so we do p % 6. So |current_permutation| would go
- // from 0 to 5 and then reset to 0 again, for all combinations of
- // whichever three letters are remaining, as we loop through the
- // permutations)
- int current_permutation = p % current_num_permutations;
- // Since this is is the total number of permutations starting with
- // this event and including future events, there could be multiple
- // values of current_permutation that will generate the same event
- // in this iteration.
- // (e.g. If we chose 'a' but have b c d to choose from, we choose b when
- // |current_permutation| = 0, 1 and c when |current_permutation| = 2, 3.
- // Note that each letter gets two numbers, which is the next
- // current_num_permutations, 2! for the two letters left.)
- // Branching out from the first event, there are num_permutations
- // permutations, and each value of |p| is associated with one of these
- // permutations. However, once the first event is chosen, there
- // are now |num_events| - 1 events left, so the number of permutations
- // for the rest of the events changes, and will always be equal to
- // the factorial of the events_left.
- // (e.g. There are 3! = 6 permutations that start with 'a', so if we
- // start with 'a' there will be 6 ways to then choose from b c d.)
- // So we now set-up for the next iteration by setting
- // current_num_permutations to the factorial of the next number of
- // events left.
- current_num_permutations /= events_left;
- // To figure out what current event we want to choose, we integer
- // divide the current permutation by the next current_num_permutations.
- // (e.g. If there are 4 letters a b c d and 24 permutations, we divide
- // by 24/4 = 6. Values 0 to 5 when divided by 6 equals 0, so the first
- // 6 permutations start with 'a', and the last 6 will start with 'd'.
- // Note that there are 6 that start with 'a' because there are 6
- // permutations for the next three letters that follow 'a'.)
- int index = current_permutation / current_num_permutations;
- ui::TouchEvent* next_dispatch = queued_events[index];
- ASSERT_TRUE(next_dispatch != NULL);
- // |next_dispatch| has to be put in this container so that its time
- // stamp can be changed to this point in the test, when it is being
- // dispatched..
- ui::EventTestApi test_dispatch(next_dispatch);
- test_dispatch.set_time_stamp(Now());
- generator_->Dispatch(next_dispatch);
- queued_events.erase(queued_events.begin() + index);
- // Keep track of what fingers have been pressed, to release
- // only those fingers at the end, so the check for being in
- // no fingers down can be accurate.
- if (next_dispatch->type() == ui::ET_TOUCH_PRESSED) {
- fingers_pressed[next_dispatch->pointer_details().id] = true;
- } else if (next_dispatch->type() == ui::ET_TOUCH_RELEASED) {
- fingers_pressed[next_dispatch->pointer_details().id] = false;
- }
- }
- ASSERT_EQ(queued_events.size(), 0u);
- // Release fingers recorded as pressed.
- for (int j = 0; j < int(fingers_pressed.size()); j++) {
- if (fingers_pressed[j] == true) {
- generator_->ReleaseTouchId(j);
- fingers_pressed[j] = false;
- }
- }
- AdvanceSimulatedTimePastPotentialTapDelay();
- EXPECT_TRUE(IsInNoFingersDownState());
- ClearCapturedAndGestureEvents();
- }
- }
- // With the simple swipe gestures, if additional fingers are added and the tap
- // timer times out, then the state should change to the wait for one finger
- // state.
- TEST_F(TouchExplorationTest, GestureAddedFinger) {
- SwitchTouchExplorationMode(true);
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- float distance = gesture_detector_config_.touch_slop + 1;
- ui::TouchEvent first_press(
- ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&first_press);
- simulated_clock_.Advance(base::Milliseconds(10));
- gfx::Point second_location(100 + distance, 200);
- generator_->MoveTouch(second_location);
- EXPECT_TRUE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- // Generate a second press, but time out past the gesture period so that
- // gestures are prevented from continuing to go through.
- ui::TouchEvent second_press(
- ui::ET_TOUCH_PRESSED, gfx::Point(20, 21), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&second_press);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- ASSERT_EQ(0U, captured_events.size());
- }
- TEST_F(TouchExplorationTest, EnterSlideGestureState) {
- SwitchTouchExplorationMode(true);
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- int window_right = BoundsOfRootWindowInDIP().right();
- float distance = gesture_detector_config_.touch_slop + 1;
- ui::TouchEvent first_press(
- ui::ET_TOUCH_PRESSED, gfx::Point(window_right, 1), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- gfx::Point second_location(window_right, 1 + distance / 2);
- gfx::Point third_location(window_right, 1 + distance);
- gfx::Point fourth_location(window_right, 35);
- generator_->Dispatch(&first_press);
- simulated_clock_.Advance(base::Milliseconds(10));
- // Since we haven't moved past slop yet, we should not be in slide gesture.
- generator_->MoveTouch(second_location);
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- simulated_clock_.Advance(base::Milliseconds(10));
- // Once we are out of slop, we should be in slide gesture since we are along
- // the edge of the screen.
- generator_->MoveTouch(third_location);
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_TRUE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- // Now that we are in slide gesture, we can adjust the volume.
- generator_->MoveTouch(fourth_location);
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- // Since we are at the right edge of the screen, but the sound timer has not
- // elapsed, there should have been a sound that fired and a volume
- // change.
- size_t num_adjust_sounds = delegate_.NumAdjustSounds();
- ASSERT_EQ(1U, num_adjust_sounds);
- ASSERT_EQ(1U, delegate_.VolumeChanges().size());
- // Exit out of slide gesture once touch is lifted, but not before even if the
- // grace period is over.
- AdvanceSimulatedTimePastPotentialTapDelay();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_TRUE(IsInSlideGestureState());
- generator_->ReleaseTouch();
- ASSERT_EQ(0U, captured_events.size());
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- }
- // If a press + move occurred outside the boundaries, but within the slop
- // boundaries and then moved into the boundaries of an edge, there still should
- // not be a slide gesture.
- TEST_F(TouchExplorationTest, AvoidEnteringSlideGesture) {
- SwitchTouchExplorationMode(true);
- gfx::Rect window = BoundsOfRootWindowInDIP();
- float distance = gesture_detector_config_.touch_slop + 1;
- ui::TouchEvent first_press(
- ui::ET_TOUCH_PRESSED,
- gfx::Point(window.right() - GetSlopDistanceFromEdge(), 1), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- gfx::Point out_of_slop(window.right() - GetSlopDistanceFromEdge() + distance,
- 1);
- gfx::Point into_boundaries(window.right() - GetMaxDistanceFromEdge() / 2, 1);
- generator_->Dispatch(&first_press);
- simulated_clock_.Advance(base::Milliseconds(10));
- generator_->MoveTouch(out_of_slop);
- EXPECT_FALSE(IsInTouchToMouseMode());
- EXPECT_TRUE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- simulated_clock_.Advance(base::Milliseconds(10));
- // Since we did not start moving while in the boundaries, we should not be in
- // slide gestures.
- generator_->MoveTouch(into_boundaries);
- EXPECT_TRUE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- generator_->ReleaseTouch();
- }
- // If the slide gesture begins within the boundaries and then moves
- // SlopDistanceFromEdge there should still be a sound change. If the finger
- // moves into the center screen, there should no longer be a sound change but it
- // should still be in slide gesture. If the finger moves back into the edges
- // without lifting, it should start changing sound again.
- TEST_F(TouchExplorationTest, TestingBoundaries) {
- SwitchTouchExplorationMode(true);
- gfx::Rect window = BoundsOfRootWindowInDIP();
- gfx::Point initial_press(window.right() - GetMaxDistanceFromEdge() / 2, 1);
- gfx::Point center_screen(window.right() / 2, window.bottom() / 2);
- ui::TouchEvent first_press(
- ui::ET_TOUCH_PRESSED, initial_press, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&first_press);
- simulated_clock_.Advance(base::Milliseconds(10));
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- // Move past the touch slop to begin slide gestures.
- // + slop + 1 to actually leave slop.
- gfx::Point touch_move(
- initial_press.x(),
- initial_press.y() + gesture_detector_config_.touch_slop + 1);
- generator_->MoveTouch(touch_move);
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_TRUE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- simulated_clock_.Advance(base::Milliseconds(10));
- // Move the touch into slop boundaries. It should still be in slide gestures
- // and adjust the volume.
- gfx::Point into_slop_boundaries(
- window.right() - GetSlopDistanceFromEdge() / 2, 1);
- generator_->MoveTouch(into_slop_boundaries);
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_TRUE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- // The sound is rate limiting so it only activates every 150ms.
- simulated_clock_.Advance(base::Milliseconds(200));
- size_t num_adjust_sounds = delegate_.NumAdjustSounds();
- ASSERT_EQ(1U, num_adjust_sounds);
- ASSERT_EQ(1U, delegate_.VolumeChanges().size());
- // Move the touch into the center of the window. It should still be in slide
- // gestures, but there should not be anymore volume adjustments.
- generator_->MoveTouch(center_screen);
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_TRUE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- simulated_clock_.Advance(base::Milliseconds(200));
- num_adjust_sounds = delegate_.NumAdjustSounds();
- ASSERT_EQ(1U, num_adjust_sounds);
- ASSERT_EQ(1U, delegate_.VolumeChanges().size());
- // Move the touch back into slop edge distance and volume should be changing
- // again, one volume change for each new move.
- generator_->MoveTouch(into_slop_boundaries);
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_TRUE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- generator_->MoveTouch(
- gfx::Point(into_slop_boundaries.x() + gesture_detector_config_.touch_slop,
- into_slop_boundaries.y()));
- simulated_clock_.Advance(base::Milliseconds(200));
- num_adjust_sounds = delegate_.NumAdjustSounds();
- ASSERT_EQ(2U, num_adjust_sounds);
- ASSERT_EQ(3U, delegate_.VolumeChanges().size());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- generator_->ReleaseTouch();
- }
- // Even if the gesture starts within bounds, if it has not moved past slop
- // within the grace period, it should go to touch exploration.
- TEST_F(TouchExplorationTest, InBoundariesTouchExploration) {
- SwitchTouchExplorationMode(true);
- gfx::Rect window = BoundsOfRootWindowInDIP();
- gfx::Point initial_press(window.right() - GetMaxDistanceFromEdge() / 2, 1);
- ui::TouchEvent first_press(
- ui::ET_TOUCH_PRESSED, initial_press, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&first_press);
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- EXPECT_FALSE(IsInTouchToMouseMode());
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_FALSE(IsInGestureInProgressState());
- EXPECT_FALSE(IsInSlideGestureState());
- EXPECT_TRUE(IsInTouchToMouseMode());
- }
- // If two fingers tap the screen at the same time and release before the tap
- // timer runs out, a control key event should be sent to silence chromevox.
- TEST_F(TouchExplorationTest, TwoFingerTap) {
- SwitchTouchExplorationMode(true);
- generator_->set_current_screen_location(gfx::Point(101, 102));
- generator_->PressTouchId(1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- generator_->PressTouchId(2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- generator_->ReleaseTouchId(1);
- EXPECT_TRUE(IsInTwoFingerTapState());
- generator_->ReleaseTouchId(2);
- EXPECT_EQ(0U, captured_events.size());
- ASSERT_EQ(ax::mojom::Gesture::kTap2, delegate_.GetLastGesture());
- }
- // If the fingers are not released before the tap timer runs out, a control
- // keyevent is not sent and the state will no longer be in two finger tap.
- TEST_F(TouchExplorationTest, TwoFingerTapAndHold) {
- SwitchTouchExplorationMode(true);
- generator_->PressTouchId(1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- generator_->PressTouchId(2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- AdvanceSimulatedTimePastTapDelay();
- // Since the tap delay has elapsed, it should no longer be in two finger tap.
- EXPECT_FALSE(IsInTwoFingerTapState());
- }
- // The next two tests set up two finger swipes to happen. If one of the fingers
- // moves out of slop before the tap timer fires, a two finger tap is not made.
- // In this first test, the first finger placed will move out of slop.
- TEST_F(TouchExplorationTest, TwoFingerTapAndMoveFirstFinger) {
- SwitchTouchExplorationMode(true);
- // Once one of the fingers leaves slop, it should no longer be in two finger
- // tap.
- ui::TouchEvent first_press_id_1(
- ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- ui::TouchEvent first_press_id_2(
- ui::ET_TOUCH_PRESSED, gfx::Point(110, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- ui::TouchEvent slop_move_id_1(
- ui::ET_TOUCH_MOVED,
- gfx::Point(100 + gesture_detector_config_.touch_slop, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- ui::TouchEvent slop_move_id_2(
- ui::ET_TOUCH_MOVED,
- gfx::Point(110 + gesture_detector_config_.touch_slop, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- ui::TouchEvent out_slop_id_1(
- ui::ET_TOUCH_MOVED,
- gfx::Point(100 + gesture_detector_config_.touch_slop + 1, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- // Dispatch the inital presses.
- generator_->Dispatch(&first_press_id_1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- generator_->Dispatch(&first_press_id_2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- // The presses have not moved out of slop yet so it should still be in
- // TwoFingerTap.
- generator_->Dispatch(&slop_move_id_1);
- EXPECT_TRUE(IsInTwoFingerTapState());
- generator_->Dispatch(&slop_move_id_2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- // Once one of the fingers moves out of slop, we are no longer in
- // TwoFingerTap.
- generator_->Dispatch(&out_slop_id_1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- }
- // Similar test to the previous test except the second finger placed will be the
- // one to move out of slop.
- TEST_F(TouchExplorationTest, TwoFingerTapAndMoveSecondFinger) {
- SwitchTouchExplorationMode(true);
- // Once one of the fingers leaves slop, it should no longer be in two finger
- // tap.
- ui::TouchEvent first_press_id_1(
- ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- ui::TouchEvent first_press_id_2(
- ui::ET_TOUCH_PRESSED, gfx::Point(110, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- ui::TouchEvent out_slop_id_2(
- ui::ET_TOUCH_MOVED,
- gfx::Point(100 + gesture_detector_config_.touch_slop + 1, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- generator_->Dispatch(&first_press_id_1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- generator_->Dispatch(&first_press_id_2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(0U, captured_events.size());
- generator_->Dispatch(&out_slop_id_2);
- EXPECT_FALSE(IsInTwoFingerTapState());
- }
- TEST_F(TouchExplorationTest, ExclusionArea) {
- SwitchTouchExplorationMode(true);
- gfx::Rect window = BoundsOfRootWindowInDIP();
- gfx::Rect exclude = window;
- exclude.Inset(gfx::Insets::TLBR(0, 0, 30, 0));
- SetExcludeBounds(exclude);
- gfx::Point in_pt = exclude.CenterPoint();
- gfx::Point in_mv_pt(in_pt.x(), (in_pt.y() + exclude.bottom()) / 2);
- gfx::Point out_pt(in_pt.x(), exclude.bottom() + 20);
- gfx::Point out_mv_pt(in_pt.x(), exclude.bottom() + 10);
- // Motion starting in exclusion bounds is passed-through unchanged.
- {
- generator_->set_current_screen_location(in_pt);
- generator_->PressTouchId(0);
- AdvanceSimulatedTimePastPotentialTapDelay();
- generator_->MoveTouchId(out_mv_pt, 0);
- generator_->ReleaseTouchId(0);
- EXPECT_TRUE(IsInNoFingersDownState());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(3U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type());
- EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[1]->type());
- EXPECT_EQ(ui::ET_TOUCH_RELEASED, captured_events[2]->type());
- ClearCapturedAndGestureEvents();
- }
- // Complete motion outside exclusion is rewritten.
- {
- generator_->set_current_screen_location(out_pt);
- generator_->PressTouchId(0);
- AdvanceSimulatedTimePastTapDelay();
- generator_->MoveTouchId(out_mv_pt, 0);
- generator_->ReleaseTouchId(0);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInNoFingersDownState());
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_TRUE(captured_events.empty());
- ASSERT_EQ(3U, GetTouchExplorePoints().size());
- ClearCapturedAndGestureEvents();
- }
- // For a motion starting outside: outside events are rewritten, inside
- // events are discarded unless they end the motion.
- {
- // finger 0 down outside, moves inside.
- generator_->set_current_screen_location(out_pt);
- generator_->PressTouchId(0);
- AdvanceSimulatedTimePastTapDelay();
- generator_->MoveTouchId(out_mv_pt, 0);
- generator_->MoveTouchId(in_mv_pt, 0);
- ASSERT_TRUE(GetCapturedEvents().empty());
- ASSERT_EQ(2U, GetTouchExplorePoints().size());
- ClearCapturedAndGestureEvents();
- // finger 1 down inside, moves outside
- generator_->set_current_screen_location(in_pt);
- generator_->PressTouchId(1);
- generator_->MoveTouchId(out_mv_pt, 1);
- generator_->ReleaseTouchId(1);
- ASSERT_TRUE(GetCapturedEvents().empty());
- ASSERT_TRUE(GetTouchExplorePoints().empty());
- EXPECT_FALSE(IsInNoFingersDownState());
- generator_->ReleaseTouchId(0);
- AdvanceSimulatedTimePastTapDelay();
- EXPECT_TRUE(IsInNoFingersDownState());
- ASSERT_TRUE(GetCapturedEvents().empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- }
- }
- TEST_F(TouchExplorationTest, SingleTapInLiftActivationArea) {
- SwitchTouchExplorationMode(true);
- gfx::Rect lift_activation = BoundsOfRootWindowInDIP();
- lift_activation.Inset(gfx::Insets::TLBR(0, 0, 30, 0));
- SetLiftActivationBounds(lift_activation);
- // Tap at one location, and get tap and mouse move events.
- gfx::Point tap_location = lift_activation.CenterPoint();
- // The user has to have previously selected something.
- SetTouchAccessibilityAnchorPoint(tap_location);
- generator_->set_current_screen_location(tap_location);
- generator_->PressTouchId(1);
- generator_->ReleaseTouchId(1);
- AdvanceSimulatedTimePastTapDelay();
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(2U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type());
- EXPECT_EQ(ui::ET_TOUCH_RELEASED, captured_events[1]->type());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- ClearCapturedAndGestureEvents();
- gfx::Point out_tap_location(tap_location.x(), lift_activation.bottom() + 20);
- SetTouchAccessibilityAnchorPoint(out_tap_location);
- generator_->set_current_screen_location(out_tap_location);
- generator_->PressTouchId(1);
- generator_->ReleaseTouchId(1);
- AdvanceSimulatedTimePastTapDelay();
- const EventList& out_captured_events = GetCapturedEvents();
- ASSERT_TRUE(out_captured_events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- }
- TEST_F(TouchExplorationTest, TouchExploreLiftInLiftActivationArea) {
- SwitchTouchExplorationMode(true);
- gfx::Rect lift_activation = BoundsOfRootWindowInDIP();
- lift_activation.Inset(gfx::Insets::TLBR(0, 0, 30, 0));
- SetLiftActivationBounds(lift_activation);
- // Explore at one location, and get tap and touch explore events.
- gfx::Point tap_location = lift_activation.CenterPoint();
- EnterTouchExplorationModeAtLocation(tap_location);
- ClearCapturedAndGestureEvents();
- ASSERT_EQ(0U, delegate_.NumTouchTypeSounds());
- // A touch release should trigger a tap.
- ui::TouchEvent touch_explore_release(
- ui::ET_TOUCH_RELEASED, tap_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&touch_explore_release);
- AdvanceSimulatedTimePastTapDelay();
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(2U, captured_events.size());
- EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type());
- EXPECT_EQ(ui::ET_TOUCH_RELEASED, captured_events[1]->type());
- ASSERT_EQ(1U, delegate_.NumTouchTypeSounds());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- ClearCapturedAndGestureEvents();
- delegate_.ResetCountersToZero();
- // Touch explore inside the activation bounds, but lift outside.
- gfx::Point out_tap_location(tap_location.x(), lift_activation.bottom() + 20);
- SetTouchAccessibilityAnchorPoint(out_tap_location);
- EnterTouchExplorationModeAtLocation(tap_location);
- ClearCapturedAndGestureEvents();
- ui::TouchEvent out_touch_explore_release(
- ui::ET_TOUCH_RELEASED, out_tap_location, Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 0));
- generator_->Dispatch(&out_touch_explore_release);
- AdvanceSimulatedTimePastTapDelay();
- const EventList& out_captured_events = GetCapturedEvents();
- ASSERT_TRUE(out_captured_events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- ASSERT_EQ(0U, delegate_.NumTouchTypeSounds());
- }
- // Ensure that any touch release events received after
- // TouchExplorationController starts up are canceled, if we haven't
- // seen any touch press events yet. http://crbug.com/751348
- TEST_F(TouchExplorationTest, AlreadyHeldFingersGetCanceled) {
- generator_->PressTouch();
- SwitchTouchExplorationMode(true);
- generator_->ReleaseTouch();
- std::vector<ui::LocatedEvent*> events =
- GetCapturedLocatedEventsOfType(ui::ET_TOUCH_CANCELLED);
- ASSERT_EQ(1U, events.size());
- }
- // Ensure 3 or 4 finger tap gets recognized correctly.
- TEST_F(TouchExplorationTest, ThreeOrFourFingerTap) {
- SwitchTouchExplorationMode(true);
- ui::TouchEvent press_id_1(
- ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- ui::TouchEvent release_id_1(
- ui::ET_TOUCH_RELEASED, gfx::Point(100, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 1));
- ui::TouchEvent press_id_2(
- ui::ET_TOUCH_PRESSED, gfx::Point(110, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- ui::TouchEvent release_id_2(
- ui::ET_TOUCH_RELEASED, gfx::Point(110, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 2));
- ui::TouchEvent press_id_3(
- ui::ET_TOUCH_PRESSED, gfx::Point(120, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 3));
- ui::TouchEvent release_id_3(
- ui::ET_TOUCH_RELEASED, gfx::Point(120, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 3));
- ui::TouchEvent press_id_4(
- ui::ET_TOUCH_PRESSED, gfx::Point(130, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 4));
- ui::TouchEvent release_id_4(
- ui::ET_TOUCH_RELEASED, gfx::Point(120, 200), Now(),
- ui::PointerDetails(ui::EventPointerType::kTouch, 4));
- // Three fingers down.
- generator_->Dispatch(&press_id_1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- generator_->Dispatch(&press_id_2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- generator_->Dispatch(&press_id_3);
- EXPECT_TRUE(IsInGestureInProgressState());
- // Three fingers up.
- generator_->Dispatch(&release_id_1);
- EXPECT_TRUE(IsInGestureInProgressState());
- generator_->Dispatch(&release_id_2);
- EXPECT_TRUE(IsInGestureInProgressState());
- ASSERT_EQ(ax::mojom::Gesture::kNone, delegate_.GetLastGesture());
- generator_->Dispatch(&release_id_3);
- EXPECT_TRUE(IsInNoFingersDownState());
- ASSERT_EQ(ax::mojom::Gesture::kTap3, delegate_.GetLastGesture());
- delegate_.ResetLastGesture();
- // Four fingers down.
- generator_->Dispatch(&press_id_1);
- EXPECT_FALSE(IsInTwoFingerTapState());
- generator_->Dispatch(&press_id_2);
- EXPECT_TRUE(IsInTwoFingerTapState());
- generator_->Dispatch(&press_id_3);
- EXPECT_TRUE(IsInGestureInProgressState());
- generator_->Dispatch(&press_id_4);
- EXPECT_TRUE(IsInGestureInProgressState());
- // Four fingers up.
- generator_->Dispatch(&release_id_1);
- EXPECT_TRUE(IsInGestureInProgressState());
- generator_->Dispatch(&release_id_2);
- EXPECT_TRUE(IsInGestureInProgressState());
- generator_->Dispatch(&release_id_3);
- EXPECT_TRUE(IsInGestureInProgressState());
- ASSERT_EQ(ax::mojom::Gesture::kNone, delegate_.GetLastGesture());
- generator_->Dispatch(&release_id_4);
- EXPECT_TRUE(IsInNoFingersDownState());
- ASSERT_EQ(ax::mojom::Gesture::kTap4, delegate_.GetLastGesture());
- }
- // Triggers right-click when anchor point remains in position after delay.
- TEST_F(TouchExplorationTest, TriggersRightClickAfterDelay) {
- SwitchTouchExplorationMode(true);
- gfx::Rect lift_activation = BoundsOfRootWindowInDIP();
- SetLiftActivationBounds(lift_activation);
- // Explore at one location.
- gfx::Point tap_location = lift_activation.CenterPoint();
- EnterTouchExplorationModeAtLocation(tap_location);
- ClearCapturedAndGestureEvents();
- ASSERT_EQ(0U, delegate_.NumTouchTypeSounds());
- // Stay in the same anchor point after delay.
- AdvanceSimulatedTimePastLongPressDelay();
- // Any event should be rewritten as a right-mouse click.
- generator_->set_current_screen_location(tap_location);
- generator_->MoveTouch(tap_location);
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_EQ(2U, captured_events.size());
- EXPECT_EQ(ui::ET_MOUSE_PRESSED, captured_events[0]->type());
- EXPECT_EQ(ui::ET_MOUSE_RELEASED, captured_events[1]->type());
- // We immediately go back to touch exploration so there will be a touch
- // explore event from the touch exploration.
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- EXPECT_EQ(1U, delegate_.NumLongPressRightClickSounds());
- ClearCapturedAndGestureEvents();
- delegate_.ResetCountersToZero();
- }
- // Does not trigger right-click when anchor point is not in lift activation
- // bounds.
- TEST_F(TouchExplorationTest,
- DoesNotTriggerRightClickInNonLiftActivationBounds) {
- SwitchTouchExplorationMode(true);
- // Explore at one location.
- gfx::Point tap_location = BoundsOfRootWindowInDIP().CenterPoint();
- EnterTouchExplorationModeAtLocation(tap_location);
- ClearCapturedAndGestureEvents();
- ASSERT_EQ(0U, delegate_.NumTouchTypeSounds());
- // Stay in the same anchor point after delay.
- AdvanceSimulatedTimePastLongPressDelay();
- generator_->set_current_screen_location(tap_location);
- generator_->MoveTouch(tap_location);
- const EventList& captured_events = GetCapturedEvents();
- ASSERT_TRUE(captured_events.empty());
- ASSERT_EQ(1U, GetTouchExplorePoints().size());
- EXPECT_EQ(0U, delegate_.NumLongPressRightClickSounds());
- ClearCapturedAndGestureEvents();
- delegate_.ResetCountersToZero();
- }
- } // namespace ash
|