select_to_speak_event_handler.cc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/events/select_to_speak_event_handler.h"
  5. #include "ash/accessibility/accessibility_controller_impl.h"
  6. #include "ash/public/cpp/select_to_speak_event_handler_delegate.h"
  7. #include "ash/shell.h"
  8. namespace ash {
  9. const ui::KeyboardCode kSpeakSelectionKey = ui::VKEY_S;
  10. SelectToSpeakEventHandler::SelectToSpeakEventHandler(
  11. SelectToSpeakEventHandlerDelegate* delegate)
  12. : delegate_(delegate) {
  13. DCHECK(delegate_);
  14. Shell::Get()->AddPreTargetHandler(this,
  15. ui::EventTarget::Priority::kAccessibility);
  16. }
  17. SelectToSpeakEventHandler::~SelectToSpeakEventHandler() {
  18. Shell::Get()->RemovePreTargetHandler(this);
  19. }
  20. bool SelectToSpeakEventHandler::IsSelectToSpeakEnabled() {
  21. return Shell::Get()->accessibility_controller()->select_to_speak().enabled();
  22. }
  23. void SelectToSpeakEventHandler::SetSelectToSpeakStateSelecting(
  24. bool is_selecting) {
  25. if (is_selecting && state_ == INACTIVE) {
  26. // The extension has requested that it enter SELECTING state, and we
  27. // aren't already in a SELECTING state. Prepare to start capturing events
  28. // from stylus, mouse or touch.
  29. // If we are already in any state besides INACTIVE then there is no
  30. // work that needs to be done.
  31. state_ = SELECTION_REQUESTED;
  32. } else if (!is_selecting) {
  33. // If we were using search + mouse, continue to wait for the search key
  34. // up event by not resetting the state to INACTIVE.
  35. if (state_ != MOUSE_RELEASED)
  36. state_ = INACTIVE;
  37. touch_id_ = ui::kPointerIdUnknown;
  38. touch_type_ = ui::EventPointerType::kUnknown;
  39. }
  40. }
  41. void SelectToSpeakEventHandler::OnKeyEvent(ui::KeyEvent* event) {
  42. DCHECK(IsSelectToSpeakEnabled());
  43. DCHECK(event);
  44. ui::KeyboardCode key_code = event->key_code();
  45. bool cancel_event = false;
  46. // Update the state when pressing and releasing the Search key (VKEY_LWIN).
  47. if (key_code == ui::VKEY_LWIN) {
  48. if (event->type() == ui::ET_KEY_PRESSED && state_ == INACTIVE) {
  49. state_ = SEARCH_DOWN;
  50. } else if (event->type() == ui::ET_KEY_RELEASED) {
  51. if (state_ == CAPTURING_MOUSE) {
  52. cancel_event = true;
  53. state_ = WAIT_FOR_MOUSE_RELEASE;
  54. } else if (state_ == MOUSE_RELEASED) {
  55. cancel_event = true;
  56. state_ = INACTIVE;
  57. } else if (state_ == CAPTURING_SPEAK_SELECTION_KEY) {
  58. cancel_event = true;
  59. state_ = WAIT_FOR_SPEAK_SELECTION_KEY_RELEASE;
  60. } else if (state_ == SPEAK_SELECTION_KEY_RELEASED) {
  61. cancel_event = true;
  62. state_ = INACTIVE;
  63. } else if (state_ == SEARCH_DOWN) {
  64. // They just tapped the search key without clicking the mouse.
  65. // Don't cancel this event -- the search key may still be used
  66. // by another part of Chrome, and we didn't use it here.
  67. state_ = INACTIVE;
  68. }
  69. }
  70. } else if (key_code == kSpeakSelectionKey) {
  71. if (event->type() == ui::ET_KEY_PRESSED &&
  72. (state_ == SEARCH_DOWN || state_ == SPEAK_SELECTION_KEY_RELEASED)) {
  73. // They pressed the S key while search was down.
  74. // It's possible to press the selection key multiple times to read
  75. // the same region over and over, so state S_RELEASED can become state
  76. // CAPTURING_SPEAK_SELECTION_KEY if the search key is not lifted.
  77. cancel_event = true;
  78. state_ = CAPTURING_SPEAK_SELECTION_KEY;
  79. } else if (event->type() == ui::ET_KEY_RELEASED) {
  80. if (state_ == CAPTURING_SPEAK_SELECTION_KEY) {
  81. // They released the speak selection key while it was being captured.
  82. cancel_event = true;
  83. state_ = SPEAK_SELECTION_KEY_RELEASED;
  84. } else if (state_ == WAIT_FOR_SPEAK_SELECTION_KEY_RELEASE) {
  85. // They have already released the search key
  86. cancel_event = true;
  87. state_ = INACTIVE;
  88. }
  89. }
  90. } else if (state_ == SEARCH_DOWN) {
  91. state_ = INACTIVE;
  92. }
  93. // Forward the key to the chrome process for the extension.
  94. delegate_->DispatchKeyEvent(*event);
  95. if (cancel_event)
  96. CancelEvent(event);
  97. }
  98. void SelectToSpeakEventHandler::OnMouseEvent(ui::MouseEvent* event) {
  99. DCHECK(IsSelectToSpeakEnabled());
  100. DCHECK(event);
  101. if (state_ == INACTIVE)
  102. return;
  103. if (event->type() == ui::ET_MOUSE_PRESSED) {
  104. if (state_ == SEARCH_DOWN || state_ == MOUSE_RELEASED)
  105. state_ = CAPTURING_MOUSE;
  106. else if (state_ == SELECTION_REQUESTED)
  107. state_ = CAPTURING_MOUSE_ONLY;
  108. }
  109. if (state_ == WAIT_FOR_MOUSE_RELEASE &&
  110. event->type() == ui::ET_MOUSE_RELEASED) {
  111. state_ = INACTIVE;
  112. return;
  113. }
  114. // Only forward the event to the extension if we are capturing mouse
  115. // events.
  116. if (state_ != CAPTURING_MOUSE && state_ != CAPTURING_MOUSE_ONLY)
  117. return;
  118. if (event->type() == ui::ET_MOUSE_RELEASED) {
  119. if (state_ == CAPTURING_MOUSE)
  120. state_ = MOUSE_RELEASED;
  121. else if (state_ == CAPTURING_MOUSE_ONLY)
  122. state_ = INACTIVE;
  123. }
  124. delegate_->DispatchMouseEvent(*event);
  125. if (event->type() == ui::ET_MOUSE_PRESSED ||
  126. event->type() == ui::ET_MOUSE_RELEASED)
  127. CancelEvent(event);
  128. }
  129. void SelectToSpeakEventHandler::OnTouchEvent(ui::TouchEvent* event) {
  130. DCHECK(IsSelectToSpeakEnabled());
  131. DCHECK(event);
  132. // Only capture touch events if selection was requested or we are capturing
  133. // touch events already.
  134. if (state_ != SELECTION_REQUESTED && state_ != CAPTURING_TOUCH_ONLY)
  135. return;
  136. // On a touch-down event, if selection was requested, we begin capturing
  137. // touch events.
  138. if (event->type() == ui::ET_TOUCH_PRESSED && state_ == SELECTION_REQUESTED &&
  139. touch_id_ == ui::kPointerIdUnknown) {
  140. state_ = CAPTURING_TOUCH_ONLY;
  141. touch_id_ = event->pointer_details().id;
  142. touch_type_ = event->pointer_details().pointer_type;
  143. }
  144. if (touch_id_ != event->pointer_details().id ||
  145. touch_type_ != event->pointer_details().pointer_type) {
  146. // If this was a different pointer, cancel the event and return early.
  147. // We only want to track one touch pointer at a time.
  148. CancelEvent(event);
  149. return;
  150. }
  151. // On a touch-up event, we go back to inactive state, but still forward the
  152. // event to the extension.
  153. if (event->type() == ui::ET_TOUCH_RELEASED &&
  154. state_ == CAPTURING_TOUCH_ONLY) {
  155. state_ = INACTIVE;
  156. touch_id_ = ui::kPointerIdUnknown;
  157. touch_type_ = ui::EventPointerType::kUnknown;
  158. }
  159. // Create a mouse event to send to the extension, describing the touch.
  160. // This is done because there is no RenderWidgetHost::ForwardTouchEvent,
  161. // and we already have mouse event plumbing in place for Select-to-Speak.
  162. ui::EventType type;
  163. switch (event->type()) {
  164. case ui::ET_TOUCH_PRESSED:
  165. type = ui::ET_MOUSE_PRESSED;
  166. break;
  167. case ui::ET_TOUCH_RELEASED:
  168. case ui::ET_TOUCH_CANCELLED:
  169. type = ui::ET_MOUSE_RELEASED;
  170. break;
  171. case ui::ET_TOUCH_MOVED:
  172. type = ui::ET_MOUSE_DRAGGED;
  173. break;
  174. default:
  175. return;
  176. }
  177. int flags = ui::EF_LEFT_MOUSE_BUTTON;
  178. // Get screen coordinates if available.
  179. gfx::Point root_location = event->target()
  180. ? event->target()->GetScreenLocation(*event)
  181. : event->root_location();
  182. ui::MouseEvent event_to_send(type, event->location(), root_location,
  183. event->time_stamp(), flags, flags);
  184. delegate_->DispatchMouseEvent(event_to_send);
  185. if (event->type() != ui::ET_TOUCH_MOVED) {
  186. // Don't cancel move events in case focus needs to change.
  187. CancelEvent(event);
  188. }
  189. }
  190. void SelectToSpeakEventHandler::CancelEvent(ui::Event* event) {
  191. DCHECK(event);
  192. if (event->cancelable()) {
  193. event->SetHandled();
  194. event->StopPropagation();
  195. }
  196. }
  197. } // namespace ash