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