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 #include "AccessibleCaretEventHub.h"
8
9 #include "AccessibleCaretLogger.h"
10 #include "AccessibleCaretManager.h"
11 #include "Layers.h"
12
13 #include "mozilla/AutoRestore.h"
14 #include "mozilla/PresShell.h"
15 #include "mozilla/StaticPrefs_layout.h"
16 #include "mozilla/StaticPrefs_ui.h"
17 #include "mozilla/TextEvents.h"
18 #include "mozilla/TouchEvents.h"
19 #include "mozilla/dom/Document.h"
20 #include "mozilla/dom/MouseEventBinding.h"
21 #include "mozilla/dom/Selection.h"
22 #include "nsCanvasFrame.h"
23 #include "nsDocShell.h"
24 #include "nsFocusManager.h"
25 #include "nsFrameSelection.h"
26 #include "nsITimer.h"
27 #include "nsLayoutUtils.h"
28 #include "nsPresContext.h"
29
30 using namespace mozilla;
31 using namespace mozilla::dom;
32
33 namespace mozilla {
34
35 #undef AC_LOG
36 #define AC_LOG(message, ...) \
37 AC_LOG_BASE("AccessibleCaretEventHub (%p): " message, this, ##__VA_ARGS__);
38
39 #undef AC_LOGV
40 #define AC_LOGV(message, ...) \
41 AC_LOGV_BASE("AccessibleCaretEventHub (%p): " message, this, ##__VA_ARGS__);
42
43 NS_IMPL_ISUPPORTS(AccessibleCaretEventHub, nsIReflowObserver, nsIScrollObserver,
44 nsISupportsWeakReference);
45
46 // -----------------------------------------------------------------------------
47 // NoActionState
48 //
49 class AccessibleCaretEventHub::NoActionState
50 : public AccessibleCaretEventHub::State {
51 public:
Name() const52 const char* Name() const override { return "NoActionState"; }
53
54 MOZ_CAN_RUN_SCRIPT
OnPress(AccessibleCaretEventHub * aContext,const nsPoint & aPoint,int32_t aTouchId,EventClassID aEventClass)55 nsEventStatus OnPress(AccessibleCaretEventHub* aContext,
56 const nsPoint& aPoint, int32_t aTouchId,
57 EventClassID aEventClass) override {
58 nsEventStatus rv = nsEventStatus_eIgnore;
59
60 if (NS_SUCCEEDED(aContext->mManager->PressCaret(aPoint, aEventClass))) {
61 aContext->SetState(AccessibleCaretEventHub::PressCaretState());
62 rv = nsEventStatus_eConsumeNoDefault;
63 } else {
64 aContext->SetState(AccessibleCaretEventHub::PressNoCaretState());
65 }
66
67 aContext->mPressPoint = aPoint;
68 aContext->mActiveTouchId = aTouchId;
69
70 return rv;
71 }
72
73 MOZ_CAN_RUN_SCRIPT
OnScrollStart(AccessibleCaretEventHub * aContext)74 void OnScrollStart(AccessibleCaretEventHub* aContext) override {
75 aContext->mManager->OnScrollStart();
76 aContext->SetState(AccessibleCaretEventHub::ScrollState());
77 }
78
79 MOZ_CAN_RUN_SCRIPT
OnScrollPositionChanged(AccessibleCaretEventHub * aContext)80 void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) override {
81 aContext->mManager->OnScrollPositionChanged();
82 }
83
84 MOZ_CAN_RUN_SCRIPT
OnSelectionChanged(AccessibleCaretEventHub * aContext,Document * aDoc,dom::Selection * aSel,int16_t aReason)85 void OnSelectionChanged(AccessibleCaretEventHub* aContext, Document* aDoc,
86 dom::Selection* aSel, int16_t aReason) override {
87 aContext->mManager->OnSelectionChanged(aDoc, aSel, aReason);
88 }
89
90 MOZ_CAN_RUN_SCRIPT
OnBlur(AccessibleCaretEventHub * aContext,bool aIsLeavingDocument)91 void OnBlur(AccessibleCaretEventHub* aContext,
92 bool aIsLeavingDocument) override {
93 aContext->mManager->OnBlur();
94 }
95
96 MOZ_CAN_RUN_SCRIPT
OnReflow(AccessibleCaretEventHub * aContext)97 void OnReflow(AccessibleCaretEventHub* aContext) override {
98 aContext->mManager->OnReflow();
99 }
100
Enter(AccessibleCaretEventHub * aContext)101 void Enter(AccessibleCaretEventHub* aContext) override {
102 aContext->mPressPoint = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
103 aContext->mActiveTouchId = kInvalidTouchId;
104 }
105 };
106
107 // -----------------------------------------------------------------------------
108 // PressCaretState: Because we've pressed on the caret, always consume the
109 // event, both real and synthesized, so that other event handling code won't
110 // have a chance to do something else to interrupt caret dragging.
111 //
112 class AccessibleCaretEventHub::PressCaretState
113 : public AccessibleCaretEventHub::State {
114 public:
Name() const115 const char* Name() const override { return "PressCaretState"; }
116
117 MOZ_CAN_RUN_SCRIPT
OnMove(AccessibleCaretEventHub * aContext,const nsPoint & aPoint,WidgetMouseEvent::Reason aReason)118 nsEventStatus OnMove(AccessibleCaretEventHub* aContext, const nsPoint& aPoint,
119 WidgetMouseEvent::Reason aReason) override {
120 if (aReason == WidgetMouseEvent::eReal &&
121 aContext->MoveDistanceIsLarge(aPoint)) {
122 if (NS_SUCCEEDED(aContext->mManager->DragCaret(aPoint))) {
123 aContext->SetState(AccessibleCaretEventHub::DragCaretState());
124 }
125 }
126
127 return nsEventStatus_eConsumeNoDefault;
128 }
129
130 MOZ_CAN_RUN_SCRIPT
OnRelease(AccessibleCaretEventHub * aContext)131 nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override {
132 aContext->mManager->ReleaseCaret();
133 aContext->mManager->TapCaret(aContext->mPressPoint);
134 aContext->SetState(AccessibleCaretEventHub::NoActionState());
135
136 return nsEventStatus_eConsumeNoDefault;
137 }
138
OnLongTap(AccessibleCaretEventHub * aContext,const nsPoint & aPoint)139 nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
140 const nsPoint& aPoint) override {
141 return nsEventStatus_eConsumeNoDefault;
142 }
143 };
144
145 // -----------------------------------------------------------------------------
146 // DragCaretState: Because we've pressed on the caret, always consume the event,
147 // both real and synthesized, so that other event handling code won't have a
148 // chance to do something else to interrupt caret dragging.
149 //
150 class AccessibleCaretEventHub::DragCaretState
151 : public AccessibleCaretEventHub::State {
152 public:
Name() const153 const char* Name() const override { return "DragCaretState"; }
154
155 MOZ_CAN_RUN_SCRIPT
OnMove(AccessibleCaretEventHub * aContext,const nsPoint & aPoint,WidgetMouseEvent::Reason aReason)156 nsEventStatus OnMove(AccessibleCaretEventHub* aContext, const nsPoint& aPoint,
157 WidgetMouseEvent::Reason aReason) override {
158 if (aReason == WidgetMouseEvent::eReal) {
159 aContext->mManager->DragCaret(aPoint);
160 }
161
162 return nsEventStatus_eConsumeNoDefault;
163 }
164
165 MOZ_CAN_RUN_SCRIPT
OnRelease(AccessibleCaretEventHub * aContext)166 nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override {
167 aContext->mManager->ReleaseCaret();
168 aContext->SetState(AccessibleCaretEventHub::NoActionState());
169
170 return nsEventStatus_eConsumeNoDefault;
171 }
172 };
173
174 // -----------------------------------------------------------------------------
175 // PressNoCaretState
176 //
177 class AccessibleCaretEventHub::PressNoCaretState
178 : public AccessibleCaretEventHub::State {
179 public:
Name() const180 const char* Name() const override { return "PressNoCaretState"; }
181
OnMove(AccessibleCaretEventHub * aContext,const nsPoint & aPoint,WidgetMouseEvent::Reason aReason)182 nsEventStatus OnMove(AccessibleCaretEventHub* aContext, const nsPoint& aPoint,
183 WidgetMouseEvent::Reason aReason) override {
184 if (aContext->MoveDistanceIsLarge(aPoint)) {
185 aContext->SetState(AccessibleCaretEventHub::NoActionState());
186 }
187
188 return nsEventStatus_eIgnore;
189 }
190
OnRelease(AccessibleCaretEventHub * aContext)191 nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override {
192 aContext->SetState(AccessibleCaretEventHub::NoActionState());
193
194 return nsEventStatus_eIgnore;
195 }
196
197 MOZ_CAN_RUN_SCRIPT
OnLongTap(AccessibleCaretEventHub * aContext,const nsPoint & aPoint)198 nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
199 const nsPoint& aPoint) override {
200 aContext->SetState(AccessibleCaretEventHub::LongTapState());
201
202 return aContext->GetState()->OnLongTap(aContext, aPoint);
203 }
204
205 MOZ_CAN_RUN_SCRIPT
OnScrollStart(AccessibleCaretEventHub * aContext)206 void OnScrollStart(AccessibleCaretEventHub* aContext) override {
207 aContext->mManager->OnScrollStart();
208 aContext->SetState(AccessibleCaretEventHub::ScrollState());
209 }
210
211 MOZ_CAN_RUN_SCRIPT
OnBlur(AccessibleCaretEventHub * aContext,bool aIsLeavingDocument)212 void OnBlur(AccessibleCaretEventHub* aContext,
213 bool aIsLeavingDocument) override {
214 aContext->mManager->OnBlur();
215 if (aIsLeavingDocument) {
216 aContext->SetState(AccessibleCaretEventHub::NoActionState());
217 }
218 }
219
220 MOZ_CAN_RUN_SCRIPT
OnSelectionChanged(AccessibleCaretEventHub * aContext,Document * aDoc,dom::Selection * aSel,int16_t aReason)221 void OnSelectionChanged(AccessibleCaretEventHub* aContext, Document* aDoc,
222 dom::Selection* aSel, int16_t aReason) override {
223 aContext->mManager->OnSelectionChanged(aDoc, aSel, aReason);
224 }
225
226 MOZ_CAN_RUN_SCRIPT
OnReflow(AccessibleCaretEventHub * aContext)227 void OnReflow(AccessibleCaretEventHub* aContext) override {
228 aContext->mManager->OnReflow();
229 }
230
Enter(AccessibleCaretEventHub * aContext)231 void Enter(AccessibleCaretEventHub* aContext) override {
232 aContext->LaunchLongTapInjector();
233 }
234
Leave(AccessibleCaretEventHub * aContext)235 void Leave(AccessibleCaretEventHub* aContext) override {
236 aContext->CancelLongTapInjector();
237 }
238 };
239
240 // -----------------------------------------------------------------------------
241 // ScrollState
242 //
243 class AccessibleCaretEventHub::ScrollState
244 : public AccessibleCaretEventHub::State {
245 public:
Name() const246 const char* Name() const override { return "ScrollState"; }
247
248 MOZ_CAN_RUN_SCRIPT
OnScrollEnd(AccessibleCaretEventHub * aContext)249 void OnScrollEnd(AccessibleCaretEventHub* aContext) override {
250 aContext->mManager->OnScrollEnd();
251 aContext->SetState(AccessibleCaretEventHub::NoActionState());
252 }
253
254 MOZ_CAN_RUN_SCRIPT
OnScrollPositionChanged(AccessibleCaretEventHub * aContext)255 void OnScrollPositionChanged(AccessibleCaretEventHub* aContext) override {
256 aContext->mManager->OnScrollPositionChanged();
257 }
258
259 MOZ_CAN_RUN_SCRIPT
OnBlur(AccessibleCaretEventHub * aContext,bool aIsLeavingDocument)260 void OnBlur(AccessibleCaretEventHub* aContext,
261 bool aIsLeavingDocument) override {
262 aContext->mManager->OnBlur();
263 if (aIsLeavingDocument) {
264 aContext->SetState(AccessibleCaretEventHub::NoActionState());
265 }
266 }
267 };
268
269 // -----------------------------------------------------------------------------
270 // LongTapState
271 //
272 class AccessibleCaretEventHub::LongTapState
273 : public AccessibleCaretEventHub::State {
274 public:
Name() const275 const char* Name() const override { return "LongTapState"; }
276
277 MOZ_CAN_RUN_SCRIPT
OnLongTap(AccessibleCaretEventHub * aContext,const nsPoint & aPoint)278 nsEventStatus OnLongTap(AccessibleCaretEventHub* aContext,
279 const nsPoint& aPoint) override {
280 // In general text selection is lower-priority than the context menu. If
281 // we consume this long-press event, then it prevents the context menu from
282 // showing up on desktop Firefox (because that happens on long-tap-up, if
283 // the long-tap was not cancelled). So we return eIgnore instead.
284 aContext->mManager->SelectWordOrShortcut(aPoint);
285 return nsEventStatus_eIgnore;
286 }
287
OnRelease(AccessibleCaretEventHub * aContext)288 nsEventStatus OnRelease(AccessibleCaretEventHub* aContext) override {
289 aContext->SetState(AccessibleCaretEventHub::NoActionState());
290
291 // Do not consume the release since the press is not consumed in
292 // PressNoCaretState either.
293 return nsEventStatus_eIgnore;
294 }
295
296 MOZ_CAN_RUN_SCRIPT
OnScrollStart(AccessibleCaretEventHub * aContext)297 void OnScrollStart(AccessibleCaretEventHub* aContext) override {
298 aContext->mManager->OnScrollStart();
299 aContext->SetState(AccessibleCaretEventHub::ScrollState());
300 }
301
302 MOZ_CAN_RUN_SCRIPT
OnReflow(AccessibleCaretEventHub * aContext)303 void OnReflow(AccessibleCaretEventHub* aContext) override {
304 aContext->mManager->OnReflow();
305 }
306 };
307
308 // -----------------------------------------------------------------------------
309 // Implementation of AccessibleCaretEventHub methods
310 //
GetState() const311 AccessibleCaretEventHub::State* AccessibleCaretEventHub::GetState() const {
312 return mState;
313 }
314
SetState(State * aState)315 void AccessibleCaretEventHub::SetState(State* aState) {
316 MOZ_ASSERT(aState);
317
318 AC_LOG("%s -> %s", mState->Name(), aState->Name());
319
320 mState->Leave(this);
321 mState = aState;
322 mState->Enter(this);
323 }
324
325 MOZ_IMPL_STATE_CLASS_GETTER(NoActionState)
MOZ_IMPL_STATE_CLASS_GETTER(PressCaretState)326 MOZ_IMPL_STATE_CLASS_GETTER(PressCaretState)
327 MOZ_IMPL_STATE_CLASS_GETTER(DragCaretState)
328 MOZ_IMPL_STATE_CLASS_GETTER(PressNoCaretState)
329 MOZ_IMPL_STATE_CLASS_GETTER(ScrollState)
330 MOZ_IMPL_STATE_CLASS_GETTER(LongTapState)
331
332 AccessibleCaretEventHub::AccessibleCaretEventHub(PresShell* aPresShell)
333 : mPresShell(aPresShell) {}
334
Init()335 void AccessibleCaretEventHub::Init() {
336 if (mInitialized && mManager) {
337 mManager->OnFrameReconstruction();
338 }
339
340 if (mInitialized || !mPresShell || !mPresShell->GetCanvasFrame()) {
341 return;
342 }
343
344 // Without nsAutoScriptBlocker, the script might be run after constructing
345 // mFirstCaret in AccessibleCaretManager's constructor, which might destructs
346 // the whole frame tree. Therefore we'll fail to construct mSecondCaret
347 // because we cannot get root frame or canvas frame from mPresShell to inject
348 // anonymous content. To avoid that, we protect Init() by nsAutoScriptBlocker.
349 // To reproduce, run "./mach crashtest layout/base/crashtests/897852.html"
350 // without the following scriptBlocker.
351 nsAutoScriptBlocker scriptBlocker;
352
353 nsPresContext* presContext = mPresShell->GetPresContext();
354 MOZ_ASSERT(presContext, "PresContext should be given in PresShell::Init()");
355
356 nsDocShell* docShell = presContext->GetDocShell();
357 if (!docShell) {
358 return;
359 }
360
361 docShell->AddWeakReflowObserver(this);
362 docShell->AddWeakScrollObserver(this);
363
364 mDocShell = static_cast<nsDocShell*>(docShell);
365
366 if (StaticPrefs::layout_accessiblecaret_use_long_tap_injector()) {
367 mLongTapInjectorTimer = NS_NewTimer();
368 }
369
370 mManager = MakeUnique<AccessibleCaretManager>(mPresShell);
371
372 mInitialized = true;
373 }
374
Terminate()375 void AccessibleCaretEventHub::Terminate() {
376 if (!mInitialized) {
377 return;
378 }
379
380 if (mDocShell) {
381 mDocShell->RemoveWeakReflowObserver(this);
382 mDocShell->RemoveWeakScrollObserver(this);
383 }
384
385 if (mLongTapInjectorTimer) {
386 mLongTapInjectorTimer->Cancel();
387 }
388
389 mManager->Terminate();
390 mPresShell = nullptr;
391 mInitialized = false;
392 }
393
HandleEvent(WidgetEvent * aEvent)394 nsEventStatus AccessibleCaretEventHub::HandleEvent(WidgetEvent* aEvent) {
395 nsEventStatus status = nsEventStatus_eIgnore;
396
397 if (!mInitialized) {
398 return status;
399 }
400
401 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
402
403 switch (aEvent->mClass) {
404 case eMouseEventClass:
405 status = HandleMouseEvent(aEvent->AsMouseEvent());
406 break;
407
408 case eTouchEventClass:
409 status = HandleTouchEvent(aEvent->AsTouchEvent());
410 break;
411
412 case eKeyboardEventClass:
413 status = HandleKeyboardEvent(aEvent->AsKeyboardEvent());
414 break;
415
416 default:
417 MOZ_ASSERT_UNREACHABLE(
418 "PresShell should've filtered unwanted event classes!");
419 break;
420 }
421
422 return status;
423 }
424
HandleMouseEvent(WidgetMouseEvent * aEvent)425 nsEventStatus AccessibleCaretEventHub::HandleMouseEvent(
426 WidgetMouseEvent* aEvent) {
427 nsEventStatus rv = nsEventStatus_eIgnore;
428
429 if (aEvent->mButton != MouseButton::ePrimary) {
430 return rv;
431 }
432
433 int32_t id =
434 (mActiveTouchId == kInvalidTouchId ? kDefaultTouchId : mActiveTouchId);
435 nsPoint point = GetMouseEventPosition(aEvent);
436
437 if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
438 aEvent->mMessage == eMouseClick ||
439 aEvent->mMessage == eMouseDoubleClick ||
440 aEvent->mMessage == eMouseLongTap) {
441 // Don't reset the source on mouse movement since that can
442 // happen anytime, even randomly during a touch sequence.
443 mManager->SetLastInputSource(aEvent->mInputSource);
444 }
445
446 switch (aEvent->mMessage) {
447 case eMouseDown:
448 AC_LOGV("Before eMouseDown, state: %s", mState->Name());
449 rv = mState->OnPress(this, point, id, eMouseEventClass);
450 AC_LOGV("After eMouseDown, state: %s, consume: %d", mState->Name(), rv);
451 break;
452
453 case eMouseMove:
454 AC_LOGV("Before eMouseMove, state: %s", mState->Name());
455 // The mouse move events synthesized from the touch move events can have
456 // wrong point (bug 1549355). Workaround it by ignoring the events when
457 // dragging the caret because the caret doesn't really need them.
458 rv = mState->OnMove(this, point, aEvent->mReason);
459 AC_LOGV("After eMouseMove, state: %s, consume: %d", mState->Name(), rv);
460 break;
461
462 case eMouseUp:
463 AC_LOGV("Before eMouseUp, state: %s", mState->Name());
464 rv = mState->OnRelease(this);
465 AC_LOGV("After eMouseUp, state: %s, consume: %d", mState->Name(), rv);
466 break;
467
468 case eMouseLongTap:
469 AC_LOGV("Before eMouseLongTap, state: %s", mState->Name());
470 rv = mState->OnLongTap(this, point);
471 AC_LOGV("After eMouseLongTap, state: %s, consume: %d", mState->Name(),
472 rv);
473 break;
474
475 default:
476 break;
477 }
478
479 return rv;
480 }
481
HandleTouchEvent(WidgetTouchEvent * aEvent)482 nsEventStatus AccessibleCaretEventHub::HandleTouchEvent(
483 WidgetTouchEvent* aEvent) {
484 if (aEvent->mTouches.IsEmpty()) {
485 AC_LOG("%s: Receive a touch event without any touch data!", __FUNCTION__);
486 return nsEventStatus_eIgnore;
487 }
488
489 nsEventStatus rv = nsEventStatus_eIgnore;
490
491 int32_t id =
492 (mActiveTouchId == kInvalidTouchId ? aEvent->mTouches[0]->Identifier()
493 : mActiveTouchId);
494 nsPoint point = GetTouchEventPosition(aEvent, id);
495
496 mManager->SetLastInputSource(MouseEvent_Binding::MOZ_SOURCE_TOUCH);
497
498 switch (aEvent->mMessage) {
499 case eTouchStart:
500 AC_LOGV("Before eTouchStart, state: %s", mState->Name());
501 rv = mState->OnPress(this, point, id, eTouchEventClass);
502 AC_LOGV("After eTouchStart, state: %s, consume: %d", mState->Name(), rv);
503 break;
504
505 case eTouchMove:
506 AC_LOGV("Before eTouchMove, state: %s", mState->Name());
507 // There is no synthesized touch move event.
508 rv = mState->OnMove(this, point, WidgetMouseEvent::eReal);
509 AC_LOGV("After eTouchMove, state: %s, consume: %d", mState->Name(), rv);
510 break;
511
512 case eTouchEnd:
513 AC_LOGV("Before eTouchEnd, state: %s", mState->Name());
514 rv = mState->OnRelease(this);
515 AC_LOGV("After eTouchEnd, state: %s, consume: %d", mState->Name(), rv);
516 break;
517
518 case eTouchCancel:
519 AC_LOGV("Got eTouchCancel, state: %s", mState->Name());
520 // Do nothing since we don't really care eTouchCancel anyway.
521 break;
522
523 default:
524 break;
525 }
526
527 return rv;
528 }
529
HandleKeyboardEvent(WidgetKeyboardEvent * aEvent)530 nsEventStatus AccessibleCaretEventHub::HandleKeyboardEvent(
531 WidgetKeyboardEvent* aEvent) {
532 mManager->SetLastInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD);
533
534 switch (aEvent->mMessage) {
535 case eKeyUp:
536 AC_LOGV("eKeyUp, state: %s", mState->Name());
537 mManager->OnKeyboardEvent();
538 break;
539
540 case eKeyDown:
541 AC_LOGV("eKeyDown, state: %s", mState->Name());
542 mManager->OnKeyboardEvent();
543 break;
544
545 case eKeyPress:
546 AC_LOGV("eKeyPress, state: %s", mState->Name());
547 mManager->OnKeyboardEvent();
548 break;
549
550 default:
551 break;
552 }
553
554 return nsEventStatus_eIgnore;
555 }
556
MoveDistanceIsLarge(const nsPoint & aPoint) const557 bool AccessibleCaretEventHub::MoveDistanceIsLarge(const nsPoint& aPoint) const {
558 nsPoint delta = aPoint - mPressPoint;
559 return NS_hypot(delta.x, delta.y) >
560 AppUnitsPerCSSPixel() * kMoveStartToleranceInPixel;
561 }
562
LaunchLongTapInjector()563 void AccessibleCaretEventHub::LaunchLongTapInjector() {
564 if (!mLongTapInjectorTimer) {
565 return;
566 }
567
568 int32_t longTapDelay = StaticPrefs::ui_click_hold_context_menus_delay();
569 mLongTapInjectorTimer->InitWithNamedFuncCallback(
570 FireLongTap, this, longTapDelay, nsITimer::TYPE_ONE_SHOT,
571 "AccessibleCaretEventHub::LaunchLongTapInjector");
572 }
573
CancelLongTapInjector()574 void AccessibleCaretEventHub::CancelLongTapInjector() {
575 if (!mLongTapInjectorTimer) {
576 return;
577 }
578
579 mLongTapInjectorTimer->Cancel();
580 }
581
582 /* static */
FireLongTap(nsITimer * aTimer,void * aAccessibleCaretEventHub)583 void AccessibleCaretEventHub::FireLongTap(nsITimer* aTimer,
584 void* aAccessibleCaretEventHub) {
585 RefPtr<AccessibleCaretEventHub> self =
586 static_cast<AccessibleCaretEventHub*>(aAccessibleCaretEventHub);
587 self->mState->OnLongTap(self, self->mPressPoint);
588 }
589
590 NS_IMETHODIMP
Reflow(DOMHighResTimeStamp aStart,DOMHighResTimeStamp aEnd)591 AccessibleCaretEventHub::Reflow(DOMHighResTimeStamp aStart,
592 DOMHighResTimeStamp aEnd) {
593 if (!mInitialized) {
594 return NS_OK;
595 }
596
597 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
598
599 if (mIsInReflowCallback) {
600 return NS_OK;
601 }
602
603 AutoRestore<bool> autoRestoreIsInReflowCallback(mIsInReflowCallback);
604 mIsInReflowCallback = true;
605
606 AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
607 mState->OnReflow(this);
608 return NS_OK;
609 }
610
611 NS_IMETHODIMP
ReflowInterruptible(DOMHighResTimeStamp aStart,DOMHighResTimeStamp aEnd)612 AccessibleCaretEventHub::ReflowInterruptible(DOMHighResTimeStamp aStart,
613 DOMHighResTimeStamp aEnd) {
614 // Defer the error checking to Reflow().
615 return Reflow(aStart, aEnd);
616 }
617
AsyncPanZoomStarted()618 void AccessibleCaretEventHub::AsyncPanZoomStarted() {
619 if (!mInitialized) {
620 return;
621 }
622
623 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
624
625 AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
626 mState->OnScrollStart(this);
627 }
628
AsyncPanZoomStopped()629 void AccessibleCaretEventHub::AsyncPanZoomStopped() {
630 if (!mInitialized) {
631 return;
632 }
633
634 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
635
636 AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
637 mState->OnScrollEnd(this);
638 }
639
ScrollPositionChanged()640 void AccessibleCaretEventHub::ScrollPositionChanged() {
641 if (!mInitialized) {
642 return;
643 }
644
645 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
646
647 AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
648 mState->OnScrollPositionChanged(this);
649 }
650
OnSelectionChange(Document * aDoc,dom::Selection * aSel,int16_t aReason)651 void AccessibleCaretEventHub::OnSelectionChange(Document* aDoc,
652 dom::Selection* aSel,
653 int16_t aReason) {
654 if (!mInitialized) {
655 return;
656 }
657
658 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
659
660 AC_LOG("%s, state: %s, reason: %d", __FUNCTION__, mState->Name(), aReason);
661
662 // XXX Here we may be in a hot path. So, if we could avoid this virtual call,
663 // we should do so.
664 mState->OnSelectionChanged(this, aDoc, aSel, aReason);
665 }
666
ShouldDisableApz() const667 bool AccessibleCaretEventHub::ShouldDisableApz() const {
668 return mManager && mManager->ShouldDisableApz();
669 }
670
NotifyBlur(bool aIsLeavingDocument)671 void AccessibleCaretEventHub::NotifyBlur(bool aIsLeavingDocument) {
672 if (!mInitialized) {
673 return;
674 }
675
676 MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
677
678 AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
679 mState->OnBlur(this, aIsLeavingDocument);
680 }
681
GetTouchEventPosition(WidgetTouchEvent * aEvent,int32_t aIdentifier) const682 nsPoint AccessibleCaretEventHub::GetTouchEventPosition(
683 WidgetTouchEvent* aEvent, int32_t aIdentifier) const {
684 for (dom::Touch* touch : aEvent->mTouches) {
685 if (touch->Identifier() == aIdentifier) {
686 LayoutDeviceIntPoint touchIntPoint = touch->mRefPoint;
687
688 // Get event coordinate relative to root frame.
689 nsIFrame* rootFrame = mPresShell->GetRootFrame();
690 return nsLayoutUtils::GetEventCoordinatesRelativeTo(
691 aEvent, touchIntPoint, RelativeTo{rootFrame});
692 }
693 }
694 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
695 }
696
GetMouseEventPosition(WidgetMouseEvent * aEvent) const697 nsPoint AccessibleCaretEventHub::GetMouseEventPosition(
698 WidgetMouseEvent* aEvent) const {
699 LayoutDeviceIntPoint mouseIntPoint = aEvent->AsGUIEvent()->mRefPoint;
700
701 // Get event coordinate relative to root frame.
702 nsIFrame* rootFrame = mPresShell->GetRootFrame();
703 return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mouseIntPoint,
704 RelativeTo{rootFrame});
705 }
706
707 } // namespace mozilla
708