Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/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
  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
  {                                                                            \
117
    static class aClassName singleton;                                         \
118
    return &singleton;                                                         \
119
  }
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
0
  virtual const char* Name() const { return ""; }
196
197
  virtual nsEventStatus OnPress(AccessibleCaretEventHub* aContext,
198
                                const nsPoint& aPoint, int32_t aTouchId,
199
                                EventClassID aEventClass)
200
  {
201
    return nsEventStatus_eIgnore;
202
  }
203
204
  virtual nsEventStatus OnMove(AccessibleCaretEventHub* aContext,
205
                               const nsPoint& aPoint)
206
  {
207
    return nsEventStatus_eIgnore;
208
  }
209
210
  virtual nsEventStatus OnRelease(AccessibleCaretEventHub* aContext)
211
  {
212
    return nsEventStatus_eIgnore;
213
  }
214
215
  virtual nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
216
                                  const nsPoint& aPoint)
217
  {
218
    return nsEventStatus_eIgnore;
219
  }
220
221
  virtual void OnScrollStart(AccessibleCaretEventHub* aContext) {}
222
  virtual void OnScrollEnd(AccessibleCaretEventHub* aContext) {}
223
  virtual void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) {}
224
  virtual void OnBlur(AccessibleCaretEventHub* aContext,
225
                      bool aIsLeavingDocument) {}
226
  virtual void OnSelectionChanged(AccessibleCaretEventHub* aContext,
227
                                  nsIDocument* aDoc, dom::Selection* aSel,
228
                                  int16_t aReason) {}
229
  virtual void OnReflow(AccessibleCaretEventHub* aContext) {}
230
  virtual void Enter(AccessibleCaretEventHub* aContext) {}
231
  virtual void Leave(AccessibleCaretEventHub* aContext) {}
232
233
  explicit State() = default;
234
  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