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