AccessibleCaretEventHub.h (8730B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_AccessibleCaretEventHub_h 8 #define mozilla_AccessibleCaretEventHub_h 9 10 #include "LayoutConstants.h" 11 #include "mozilla/EventForwards.h" 12 #include "mozilla/MouseEvents.h" 13 #include "mozilla/UniquePtr.h" 14 #include "mozilla/WeakPtr.h" 15 #include "nsCOMPtr.h" 16 #include "nsDocShell.h" 17 #include "nsIReflowObserver.h" 18 #include "nsIScrollObserver.h" 19 #include "nsPoint.h" 20 #include "nsWeakReference.h" 21 22 class nsITimer; 23 24 namespace mozilla { 25 class AccessibleCaretManager; 26 class PresShell; 27 class WidgetKeyboardEvent; 28 class WidgetMouseEvent; 29 class WidgetTouchEvent; 30 31 // ----------------------------------------------------------------------------- 32 // Each PresShell holds a shared pointer to an AccessibleCaretEventHub; each 33 // AccessibleCaretEventHub holds a unique pointer to an AccessibleCaretManager. 34 // Thus, there's one AccessibleCaretManager per PresShell. 35 // 36 // AccessibleCaretEventHub implements a state pattern. It receives events from 37 // PresShell and callbacks by observers and listeners, and then relays them to 38 // the current concrete state which calls necessary event-handling methods in 39 // AccessibleCaretManager. 40 // 41 // We separate AccessibleCaretEventHub from AccessibleCaretManager to make the 42 // state transitions in AccessibleCaretEventHub testable. We put (nearly) all 43 // the operations involving PresShell, Selection, and AccessibleCaret 44 // manipulation in AccessibleCaretManager so that we can mock methods in 45 // AccessibleCaretManager in gtest. We test the correctness of the state 46 // transitions by giving events, callbacks, and the return values by mocked 47 // methods of AccessibleCaretEventHub. See TestAccessibleCaretEventHub.cpp. 48 // 49 // Besides dealing with real events, AccessibleCaretEventHub could also 50 // synthesize fake long-tap events and inject those events to itself on the 51 // platform lacks eMouseLongTap. Turn on this preference 52 // "layout.accessiblecaret.use_long_tap_injector" for the fake long-tap events. 53 // 54 // Please see the in-tree document for state transition diagram and more 55 // information. 56 // HTML: https://firefox-source-docs.mozilla.org/layout/AccessibleCaret.html 57 // Source rst: layout/docs/AccessibleCaret.rst 58 // 59 class AccessibleCaretEventHub : public nsIReflowObserver, 60 public nsIScrollObserver, 61 public nsSupportsWeakReference { 62 public: 63 explicit AccessibleCaretEventHub(PresShell* aPresShell); 64 void Init(); 65 void Terminate(); 66 67 MOZ_CAN_RUN_SCRIPT 68 nsEventStatus HandleEvent(WidgetEvent* aEvent); 69 70 // Call this function to notify the blur event happened. 71 MOZ_CAN_RUN_SCRIPT 72 void NotifyBlur(bool aIsLeavingDocument); 73 74 NS_DECL_ISUPPORTS 75 76 // nsIReflowObserver 77 MOZ_CAN_RUN_SCRIPT_BOUNDARY 78 NS_IMETHOD Reflow(DOMHighResTimeStamp start, DOMHighResTimeStamp end) final; 79 MOZ_CAN_RUN_SCRIPT_BOUNDARY 80 NS_IMETHOD ReflowInterruptible(DOMHighResTimeStamp start, 81 DOMHighResTimeStamp end) final; 82 83 // Override nsIScrollObserver methods. 84 MOZ_CAN_RUN_SCRIPT_BOUNDARY 85 virtual void ScrollPositionChanged() override; 86 MOZ_CAN_RUN_SCRIPT 87 virtual void AsyncPanZoomStarted() override; 88 MOZ_CAN_RUN_SCRIPT 89 virtual void AsyncPanZoomStopped() override; 90 91 // Base state 92 class State; 93 State* GetState() const; 94 95 MOZ_CAN_RUN_SCRIPT 96 void OnSelectionChange(dom::Document* aDocument, dom::Selection* aSelection, 97 int16_t aReason); 98 99 bool ShouldDisableApz() const; 100 101 protected: 102 virtual ~AccessibleCaretEventHub() = default; 103 104 #define MOZ_DECL_STATE_CLASS_GETTER(aClassName) \ 105 class aClassName; \ 106 static State* aClassName(); 107 108 #define MOZ_IMPL_STATE_CLASS_GETTER(aClassName) \ 109 AccessibleCaretEventHub::State* AccessibleCaretEventHub::aClassName() { \ 110 static class aClassName singleton; \ 111 return &singleton; \ 112 } 113 114 // Concrete state getters 115 MOZ_DECL_STATE_CLASS_GETTER(NoActionState) 116 MOZ_DECL_STATE_CLASS_GETTER(PressCaretState) 117 MOZ_DECL_STATE_CLASS_GETTER(DragCaretState) 118 MOZ_DECL_STATE_CLASS_GETTER(PressNoCaretState) 119 MOZ_DECL_STATE_CLASS_GETTER(ScrollState) 120 MOZ_DECL_STATE_CLASS_GETTER(LongTapState) 121 122 void SetState(State* aState); 123 124 MOZ_CAN_RUN_SCRIPT 125 nsEventStatus HandleMouseEvent(WidgetMouseEvent* aEvent); 126 MOZ_CAN_RUN_SCRIPT 127 nsEventStatus HandleTouchEvent(WidgetTouchEvent* aEvent); 128 MOZ_CAN_RUN_SCRIPT 129 nsEventStatus HandleKeyboardEvent(WidgetKeyboardEvent* aEvent); 130 131 virtual nsPoint GetTouchEventPosition(WidgetTouchEvent* aEvent, 132 int32_t aIdentifier) const; 133 virtual nsPoint GetMouseEventPosition(WidgetMouseEvent* aEvent) const; 134 135 bool MoveDistanceIsLarge(const nsPoint& aPoint) const; 136 137 void LaunchLongTapInjector(); 138 void CancelLongTapInjector(); 139 140 MOZ_CAN_RUN_SCRIPT_BOUNDARY 141 static void FireLongTap(nsITimer* aTimer, void* aAccessibleCaretEventHub); 142 143 void LaunchScrollEndInjector(); 144 void CancelScrollEndInjector(); 145 146 MOZ_CAN_RUN_SCRIPT_BOUNDARY 147 static void FireScrollEnd(nsITimer* aTimer, void* aAccessibleCaretEventHub); 148 149 // Member variables 150 State* mState = NoActionState(); 151 152 // Will be set to nullptr in Terminate(). 153 PresShell* MOZ_NON_OWNING_REF mPresShell = nullptr; 154 155 UniquePtr<AccessibleCaretManager> mManager; 156 157 WeakPtr<nsDocShell> mDocShell; 158 159 // Use this timer for injecting a long tap event when APZ is disabled. If APZ 160 // is enabled, it will send long tap event to us. 161 nsCOMPtr<nsITimer> mLongTapInjectorTimer; 162 163 // Last mouse button down event or touch start event point. 164 nsPoint mPressPoint{NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE}; 165 166 // For filter multitouch event 167 int32_t mActiveTouchId = kInvalidTouchId; 168 169 // Flag to indicate the class has been initialized. 170 bool mInitialized = false; 171 172 // Flag to avoid calling Reflow() callback recursively. 173 bool mIsInReflowCallback = false; 174 175 static const int32_t kMoveStartToleranceInPixel = 5; 176 static const int32_t kInvalidTouchId = -1; 177 static const int32_t kDefaultTouchId = 0; // For mouse event 178 }; 179 180 // ----------------------------------------------------------------------------- 181 // The base class for concrete states. A concrete state should inherit from this 182 // class, and override the methods to handle the events or callbacks. A concrete 183 // state is also responsible for transforming itself to the next concrete state. 184 // 185 class AccessibleCaretEventHub::State { 186 public: 187 virtual const char* Name() const { return ""; } 188 189 MOZ_CAN_RUN_SCRIPT 190 virtual nsEventStatus OnPress(AccessibleCaretEventHub* aContext, 191 const nsPoint& aPoint, int32_t aTouchId, 192 EventClassID aEventClass) { 193 return nsEventStatus_eIgnore; 194 } 195 196 MOZ_CAN_RUN_SCRIPT 197 virtual nsEventStatus OnMove(AccessibleCaretEventHub* aContext, 198 const nsPoint& aPoint, 199 WidgetMouseEvent::Reason aReason) { 200 return nsEventStatus_eIgnore; 201 } 202 203 MOZ_CAN_RUN_SCRIPT 204 virtual nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) { 205 return nsEventStatus_eIgnore; 206 } 207 208 MOZ_CAN_RUN_SCRIPT 209 virtual nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext, 210 const nsPoint& aPoint) { 211 return nsEventStatus_eIgnore; 212 } 213 214 MOZ_CAN_RUN_SCRIPT 215 virtual void OnScrollStart(AccessibleCaretEventHub* aContext) {} 216 MOZ_CAN_RUN_SCRIPT 217 virtual void OnScrollEnd(AccessibleCaretEventHub* aContext) {} 218 MOZ_CAN_RUN_SCRIPT 219 virtual void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) {} 220 MOZ_CAN_RUN_SCRIPT 221 virtual void OnBlur(AccessibleCaretEventHub* aContext, 222 bool aIsLeavingDocument) {} 223 MOZ_CAN_RUN_SCRIPT 224 virtual void OnSelectionChanged(AccessibleCaretEventHub* aContext, 225 dom::Document* aDoc, dom::Selection* aSel, 226 int16_t aReason) {} 227 MOZ_CAN_RUN_SCRIPT 228 virtual void OnReflow(AccessibleCaretEventHub* aContext) {} 229 virtual void Enter(AccessibleCaretEventHub* aContext) {} 230 virtual void Leave(AccessibleCaretEventHub* aContext) {} 231 232 explicit State() = default; 233 virtual ~State() = default; 234 State(const State&) = delete; 235 State& operator=(const State&) = delete; 236 }; 237 238 } // namespace mozilla 239 240 #endif // mozilla_AccessibleCaretEventHub_h