Line data Source code
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 AccessibleCaretEventHub_h
8 : #define AccessibleCaretEventHub_h
9 :
10 : #include "mozilla/EventForwards.h"
11 : #include "mozilla/UniquePtr.h"
12 : #include "mozilla/WeakPtr.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsIFrame.h"
15 : #include "nsIReflowObserver.h"
16 : #include "nsIScrollObserver.h"
17 : #include "nsISelectionListener.h"
18 : #include "nsPoint.h"
19 : #include "mozilla/RefPtr.h"
20 : #include "nsWeakReference.h"
21 :
22 : class nsDocShell;
23 : class nsIPresShell;
24 : class nsITimer;
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 : public nsIReflowObserver,
64 : public nsIScrollObserver,
65 : public nsISelectionListener,
66 : public nsSupportsWeakReference
67 : {
68 : public:
69 : explicit AccessibleCaretEventHub(nsIPresShell* aPresShell);
70 : void Init();
71 : void Terminate();
72 :
73 : nsEventStatus HandleEvent(WidgetEvent* aEvent);
74 :
75 : // Call this function to notify the blur event happened.
76 : void NotifyBlur(bool aIsLeavingDocument);
77 :
78 : NS_DECL_ISUPPORTS
79 : NS_DECL_NSIREFLOWOBSERVER
80 : NS_DECL_NSISELECTIONLISTENER
81 :
82 : // Override nsIScrollObserver methods.
83 : virtual void ScrollPositionChanged() override;
84 : virtual void AsyncPanZoomStarted() override;
85 : virtual void AsyncPanZoomStopped() override;
86 :
87 : // Base state
88 : class State;
89 : State* GetState() const;
90 :
91 : protected:
92 : virtual ~AccessibleCaretEventHub();
93 :
94 : #define MOZ_DECL_STATE_CLASS_GETTER(aClassName) \
95 : class aClassName; \
96 : static State* aClassName();
97 :
98 : #define MOZ_IMPL_STATE_CLASS_GETTER(aClassName) \
99 : AccessibleCaretEventHub::State* AccessibleCaretEventHub::aClassName() \
100 : { \
101 : static class aClassName singleton; \
102 : return &singleton; \
103 : }
104 :
105 : // Concrete state getters
106 : MOZ_DECL_STATE_CLASS_GETTER(NoActionState)
107 : MOZ_DECL_STATE_CLASS_GETTER(PressCaretState)
108 : MOZ_DECL_STATE_CLASS_GETTER(DragCaretState)
109 : MOZ_DECL_STATE_CLASS_GETTER(PressNoCaretState)
110 : MOZ_DECL_STATE_CLASS_GETTER(ScrollState)
111 : MOZ_DECL_STATE_CLASS_GETTER(PostScrollState)
112 : MOZ_DECL_STATE_CLASS_GETTER(LongTapState)
113 :
114 : void SetState(State* aState);
115 :
116 : nsEventStatus HandleMouseEvent(WidgetMouseEvent* aEvent);
117 : nsEventStatus HandleTouchEvent(WidgetTouchEvent* aEvent);
118 : nsEventStatus HandleKeyboardEvent(WidgetKeyboardEvent* aEvent);
119 :
120 : virtual nsPoint GetTouchEventPosition(WidgetTouchEvent* aEvent,
121 : int32_t aIdentifier) const;
122 : virtual nsPoint GetMouseEventPosition(WidgetMouseEvent* aEvent) const;
123 :
124 : bool MoveDistanceIsLarge(const nsPoint& aPoint) const;
125 :
126 : void LaunchLongTapInjector();
127 : void CancelLongTapInjector();
128 : static void FireLongTap(nsITimer* aTimer, void* aAccessibleCaretEventHub);
129 :
130 : void LaunchScrollEndInjector();
131 : void CancelScrollEndInjector();
132 : static void FireScrollEnd(nsITimer* aTimer, void* aAccessibleCaretEventHub);
133 :
134 : // Member variables
135 0 : State* mState = NoActionState();
136 :
137 : // Will be set to nullptr in Terminate().
138 : nsIPresShell* MOZ_NON_OWNING_REF mPresShell = nullptr;
139 :
140 : UniquePtr<AccessibleCaretManager> mManager;
141 :
142 : WeakPtr<nsDocShell> mDocShell;
143 :
144 : // Use this timer for injecting a long tap event when APZ is disabled. If APZ
145 : // is enabled, it will send long tap event to us.
146 : nsCOMPtr<nsITimer> mLongTapInjectorTimer;
147 :
148 : // Use this timer for injecting a simulated scroll end.
149 : nsCOMPtr<nsITimer> mScrollEndInjectorTimer;
150 :
151 : // Last mouse button down event or touch start event point.
152 : nsPoint mPressPoint{ NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE };
153 :
154 : // For filter multitouch event
155 : int32_t mActiveTouchId = kInvalidTouchId;
156 :
157 : // Flag to indicate the class has been initialized.
158 : bool mInitialized = false;
159 :
160 : // Flag to avoid calling Reflow() callback recursively.
161 : bool mIsInReflowCallback = false;
162 :
163 : // Simulate long tap if the platform does not support eMouseLongTap events.
164 : static bool sUseLongTapInjector;
165 :
166 : static const int32_t kScrollEndTimerDelay = 300;
167 : static const int32_t kMoveStartToleranceInPixel = 5;
168 : static const int32_t kInvalidTouchId = -1;
169 : static const int32_t kDefaultTouchId = 0; // For mouse event
170 : };
171 :
172 : // -----------------------------------------------------------------------------
173 : // The base class for concrete states. A concrete state should inherit from this
174 : // class, and override the methods to handle the events or callbacks. A concrete
175 : // state is also responsible for transforming itself to the next concrete state.
176 : //
177 : class AccessibleCaretEventHub::State
178 : {
179 : public:
180 0 : virtual const char* Name() const { return ""; }
181 :
182 0 : virtual nsEventStatus OnPress(AccessibleCaretEventHub* aContext,
183 : const nsPoint& aPoint, int32_t aTouchId,
184 : EventClassID aEventClass)
185 : {
186 0 : return nsEventStatus_eIgnore;
187 : }
188 :
189 0 : virtual nsEventStatus OnMove(AccessibleCaretEventHub* aContext,
190 : const nsPoint& aPoint)
191 : {
192 0 : return nsEventStatus_eIgnore;
193 : }
194 :
195 0 : virtual nsEventStatus OnRelease(AccessibleCaretEventHub* aContext)
196 : {
197 0 : return nsEventStatus_eIgnore;
198 : }
199 :
200 0 : virtual nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
201 : const nsPoint& aPoint)
202 : {
203 0 : return nsEventStatus_eIgnore;
204 : }
205 :
206 0 : virtual void OnScrollStart(AccessibleCaretEventHub* aContext) {}
207 0 : virtual void OnScrollEnd(AccessibleCaretEventHub* aContext) {}
208 0 : virtual void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) {}
209 0 : virtual void OnBlur(AccessibleCaretEventHub* aContext,
210 0 : bool aIsLeavingDocument) {}
211 0 : virtual void OnSelectionChanged(AccessibleCaretEventHub* aContext,
212 : nsIDOMDocument* aDoc, nsISelection* aSel,
213 0 : int16_t aReason) {}
214 0 : virtual void OnReflow(AccessibleCaretEventHub* aContext) {}
215 0 : virtual void Enter(AccessibleCaretEventHub* aContext) {}
216 0 : virtual void Leave(AccessibleCaretEventHub* aContext) {}
217 :
218 : explicit State() = default;
219 0 : virtual ~State() = default;
220 : State(const State&) = delete;
221 : State& operator=(const State&) = delete;
222 : };
223 :
224 : } // namespace mozilla
225 :
226 : #endif // AccessibleCaretEventHub_h
|