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