InputBlockState.h (22186B)
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_layers_InputBlockState_h 8 #define mozilla_layers_InputBlockState_h 9 10 #include "InputData.h" // for MultiTouchInput 11 #include "Units.h" 12 #include "mozilla/RefCounted.h" // for RefCounted 13 #include "mozilla/RefPtr.h" // for RefPtr 14 #include "mozilla/StaticPrefs_apz.h" 15 #include "mozilla/gfx/Matrix.h" // for Matrix4x4 16 #include "mozilla/layers/APZUtils.h" 17 #include "mozilla/layers/LayersTypes.h" // for TouchBehaviorFlags 18 #include "mozilla/layers/AsyncDragMetrics.h" 19 #include "mozilla/layers/TouchCounter.h" 20 #include "mozilla/TimeStamp.h" // for TimeStamp 21 #include "nsTArray.h" // for nsTArray 22 23 namespace mozilla { 24 namespace layers { 25 26 class AsyncPanZoomController; 27 class OverscrollHandoffChain; 28 class CancelableBlockState; 29 class TouchBlockState; 30 class WheelBlockState; 31 class DragBlockState; 32 class PanGestureBlockState; 33 class PinchGestureBlockState; 34 class KeyboardBlockState; 35 class InputQueueIterator; 36 enum class BrowserGestureResponse : bool; 37 38 /** 39 * A base class that stores state common to various input blocks. 40 * Note that the InputBlockState constructor acquires the tree lock, so callers 41 * from inside AsyncPanZoomController should ensure that the APZC lock is not 42 * held. 43 */ 44 class InputBlockState : public RefCounted<InputBlockState> { 45 public: 46 MOZ_DECLARE_REFCOUNTED_TYPENAME(InputBlockState) 47 48 static const uint64_t NO_BLOCK_ID = 0; 49 50 enum class TargetConfirmationState : uint8_t { 51 eUnconfirmed, 52 eTimedOut, 53 eConfirmed 54 }; 55 56 InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 57 TargetConfirmationFlags aFlags); 58 virtual ~InputBlockState() = default; 59 60 virtual CancelableBlockState* AsCancelableBlock() { return nullptr; } 61 virtual TouchBlockState* AsTouchBlock() { return nullptr; } 62 virtual const TouchBlockState* AsTouchBlock() const { return nullptr; } 63 virtual WheelBlockState* AsWheelBlock() { return nullptr; } 64 virtual DragBlockState* AsDragBlock() { return nullptr; } 65 virtual PanGestureBlockState* AsPanGestureBlock() { return nullptr; } 66 virtual PinchGestureBlockState* AsPinchGestureBlock() { return nullptr; } 67 virtual KeyboardBlockState* AsKeyboardBlock() { return nullptr; } 68 virtual Maybe<LayersId> WheelTransactionLayersId() const { return Nothing(); } 69 70 virtual bool SetConfirmedTargetApzc( 71 const RefPtr<AsyncPanZoomController>& aTargetApzc, 72 TargetConfirmationState aState, InputQueueIterator aFirstInput, 73 bool aForScrollbarDrag); 74 const RefPtr<AsyncPanZoomController>& GetTargetApzc() const; 75 const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const; 76 uint64_t GetBlockId() const; 77 78 bool IsTargetConfirmed() const; 79 80 virtual bool ShouldDropEvents() const; 81 82 void SetScrolledApzc(AsyncPanZoomController* aApzc); 83 AsyncPanZoomController* GetScrolledApzc() const; 84 bool IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const; 85 86 /** 87 * Dispatch the event to the target APZC. Mostly this is a hook for 88 * subclasses to do any per-event processing they need to. 89 */ 90 virtual void DispatchEvent(const InputData& aEvent) const; 91 92 /** 93 * Return true if this input block must stay active if it would otherwise 94 * be removed as the last item in the pending queue. 95 */ 96 virtual bool MustStayActive() = 0; 97 98 const ScreenToParentLayerMatrix4x4& GetTransformToApzc() const { 99 return mTransformToApzc; 100 } 101 102 protected: 103 virtual void UpdateTargetApzc( 104 const RefPtr<AsyncPanZoomController>& aTargetApzc); 105 106 const AsyncPanZoomController* TargetApzc() const { return mTargetApzc.get(); } 107 108 private: 109 // Checks whether |aA| is an ancestor of |aB| (or the same as |aB|) in 110 // |mOverscrollHandoffChain|. 111 bool IsDownchainOf(AsyncPanZoomController* aA, 112 AsyncPanZoomController* aB) const; 113 114 private: 115 RefPtr<AsyncPanZoomController> mTargetApzc; 116 bool mRequiresTargetConfirmation; 117 const uint64_t mBlockId; 118 119 // The APZC that was actually scrolled by events in this input block. 120 // This is used in configurations where a single input block is only 121 // allowed to scroll a single APZC (configurations where 122 // StaticPrefs::apz_allow_immediate_handoff() is false). Set the first time an 123 // input event in this block scrolls an APZC. 124 RefPtr<AsyncPanZoomController> mScrolledApzc; 125 126 protected: 127 TargetConfirmationState mTargetConfirmed; 128 RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain; 129 130 // Used to transform events from global screen space to |mTargetApzc|'s 131 // screen space. It's cached at the beginning of the input block so that 132 // all events in the block are in the same coordinate space. 133 ScreenToParentLayerMatrix4x4 mTransformToApzc; 134 }; 135 136 /** 137 * This class represents a set of events that can be cancelled by web content 138 * via event listeners. 139 * 140 * Each cancelable input block can be cancelled by web content, and 141 * this information is stored in the mPreventDefault flag. Because web 142 * content runs on the Gecko main thread, we cannot always wait for web 143 * content's response. Instead, there is a timeout that sets this flag in the 144 * case where web content doesn't respond in time. The mContentResponded and 145 * mContentResponseTimerExpired flags indicate which of these scenarios 146 * occurred. 147 */ 148 class CancelableBlockState : public InputBlockState { 149 public: 150 CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 151 TargetConfirmationFlags aFlags); 152 153 CancelableBlockState* AsCancelableBlock() override { return this; } 154 155 /** 156 * Record whether or not content cancelled this block of events. 157 * @param aPreventDefault true iff the block is cancelled. 158 * @return false if this block has already received a response from 159 * web content, true if not. 160 */ 161 virtual bool SetContentResponse(bool aPreventDefault); 162 163 /** 164 * Record that content didn't respond in time. 165 * @return false if this block already timed out, true if not. 166 */ 167 virtual bool TimeoutContentResponse(); 168 169 /** 170 * Checks if the content response timer has already expired. 171 */ 172 bool IsContentResponseTimerExpired() const; 173 174 /** 175 * Checks if the content has responded. 176 */ 177 bool HasContentResponded() const { return mContentResponded; } 178 179 /** 180 * @return true iff web content cancelled this block of events. 181 */ 182 bool IsDefaultPrevented() const; 183 184 /** 185 * @return true iff this block has received all the information needed 186 * to properly dispatch the events in the block. 187 */ 188 virtual bool IsReadyForHandling() const; 189 190 /** 191 * Return a descriptive name for the block kind. 192 */ 193 virtual const char* Type() = 0; 194 195 bool ShouldDropEvents() const override; 196 197 bool HasStateBeenReset() const { return mHasStateBeenReset; }; 198 void ResetState() { mHasStateBeenReset = true; } 199 200 void ResetContentResponseTimerExpired() { 201 mContentResponseTimerExpired = false; 202 mContentResponded = false; 203 } 204 205 private: 206 bool mPreventDefault; 207 bool mContentResponded; 208 bool mContentResponseTimerExpired; 209 bool mHasStateBeenReset; 210 }; 211 212 /** 213 * A single block of wheel events. 214 */ 215 class WheelBlockState : public CancelableBlockState { 216 public: 217 WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 218 TargetConfirmationFlags aFlags, 219 const ScrollWheelInput& aEvent); 220 221 bool SetContentResponse(bool aPreventDefault) override; 222 bool MustStayActive() override; 223 const char* Type() override; 224 bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc, 225 TargetConfirmationState aState, 226 InputQueueIterator aFirstInput, 227 bool aForScrollbarDrag) override; 228 229 WheelBlockState* AsWheelBlock() override { return this; } 230 231 /** 232 * Determine whether this wheel block is accepting new events. 233 */ 234 bool ShouldAcceptNewEvent() const; 235 236 /** 237 * Call to check whether a wheel event will cause the current transaction to 238 * timeout. 239 */ 240 bool MaybeTimeout(const ScrollWheelInput& aEvent); 241 242 /** 243 * Called from APZCTM when a mouse move or drag+drop event occurs, before 244 * the event has been processed. 245 */ 246 void OnMouseMove(const ScreenIntPoint& aPoint, 247 const Maybe<ScrollableLayerGuid>& aTargetGuid); 248 249 /** 250 * Returns whether or not the block is participating in a wheel transaction. 251 * This means that the block is the most recent input block to be created, 252 * and no events have occurred that would require scrolling a different 253 * frame. 254 * 255 * @return True if in a transaction, false otherwise. 256 */ 257 bool InTransaction() const; 258 259 /** 260 * Mark the block as no longer participating in a wheel transaction. This 261 * will force future wheel events to begin a new input block. 262 */ 263 void EndTransaction(); 264 265 /** 266 * @return Whether or not overscrolling is prevented for this wheel block. 267 */ 268 bool AllowScrollHandoff() const; 269 270 /** 271 * Called to check and possibly end the transaction due to a timeout. 272 * 273 * @return True if the transaction ended, false otherwise. 274 */ 275 bool MaybeTimeout(const TimeStamp& aTimeStamp); 276 277 /** 278 * Update the wheel transaction state for a new event. 279 */ 280 void Update(ScrollWheelInput& aEvent); 281 282 ScrollDirections GetAllowedScrollDirections() const { 283 return mAllowedScrollDirections; 284 } 285 286 Maybe<LayersId> WheelTransactionLayersId() const override; 287 288 protected: 289 void UpdateTargetApzc( 290 const RefPtr<AsyncPanZoomController>& aTargetApzc) override; 291 292 private: 293 TimeStamp mLastEventTime; 294 TimeStamp mLastMouseMove; 295 uint32_t mScrollSeriesCounter; 296 bool mTransactionEnded; 297 bool mIsScrollable = true; 298 ScrollDirections mAllowedScrollDirections; 299 }; 300 301 /** 302 * A block of mouse events that are part of a drag 303 */ 304 class DragBlockState : public CancelableBlockState { 305 public: 306 DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 307 TargetConfirmationFlags aFlags, const MouseInput& aEvent); 308 309 bool MustStayActive() override; 310 const char* Type() override; 311 312 bool HasReceivedMouseUp(); 313 void MarkMouseUpReceived(); 314 315 DragBlockState* AsDragBlock() override { return this; } 316 317 void SetInitialThumbPos(OuterCSSCoord aThumbPos); 318 void SetDragMetrics(const AsyncDragMetrics& aDragMetrics, 319 const CSSRect& aScrollableRect); 320 321 void DispatchEvent(const InputData& aEvent) const override; 322 323 private: 324 AsyncDragMetrics mDragMetrics; 325 OuterCSSCoord mInitialThumbPos; 326 CSSRect mInitialScrollableRect; 327 bool mReceivedMouseUp; 328 }; 329 330 /** 331 * A single block of pan gesture events. 332 */ 333 class PanGestureBlockState : public CancelableBlockState { 334 public: 335 PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 336 TargetConfirmationFlags aFlags, 337 const PanGestureInput& aEvent); 338 339 bool SetContentResponse(bool aPreventDefault) override; 340 bool IsReadyForHandling() const override; 341 bool MustStayActive() override; 342 const char* Type() override; 343 bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc, 344 TargetConfirmationState aState, 345 InputQueueIterator aFirstInput, 346 bool aForScrollbarDrag) override; 347 348 PanGestureBlockState* AsPanGestureBlock() override { return this; } 349 350 bool ShouldDropEvents() const override; 351 352 bool TimeoutContentResponse() override; 353 354 /** 355 * @return Whether or not overscrolling is prevented for this block. 356 */ 357 bool AllowScrollHandoff() const; 358 359 bool WasInterrupted() const { return mInterrupted; } 360 361 void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse); 362 void SetNeedsToWaitForBrowserGestureResponse( 363 bool aWaitForBrowserGestureResponse); 364 void SetBrowserGestureResponse(BrowserGestureResponse aResponse); 365 366 ScrollDirections GetAllowedScrollDirections() const { 367 return mAllowedScrollDirections; 368 } 369 370 bool IsWaitingForBrowserGestureResponse() const { 371 return mWaitingForBrowserGestureResponse; 372 } 373 bool IsWaitingForContentResponse() const { 374 return mWaitingForContentResponse; 375 } 376 Maybe<LayersId> WheelTransactionLayersId() const override; 377 378 void ConfirmForHoldGesture() { 379 // Hold gestures get their own input block, but do not generate 380 // any events that get to web content (because the PANGESTURE_MAYSTART 381 // event has a zero delta). As a result, do not wait for a content 382 // response for them because it will never arrive. 383 mTargetConfirmed = InputBlockState::TargetConfirmationState::eConfirmed; 384 } 385 386 private: 387 bool mInterrupted; 388 bool mWaitingForContentResponse; 389 // A pan gesture may be used for browser's swipe gestures so APZ needs to wait 390 // for the response from the browser whether the gesture has been used for 391 // swipe or not. This `mWaitingForBrowserGestureResponse` flag represents the 392 // waiting state. And below `mStartedBrowserGesture` represents the response 393 // from the browser. 394 bool mWaitingForBrowserGestureResponse; 395 bool mStartedBrowserGesture; 396 ScrollDirections mAllowedScrollDirections; 397 }; 398 399 /** 400 * A single block of pinch gesture events. 401 */ 402 class PinchGestureBlockState : public CancelableBlockState { 403 public: 404 PinchGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 405 TargetConfirmationFlags aFlags); 406 407 bool SetContentResponse(bool aPreventDefault) override; 408 bool IsReadyForHandling() const override; 409 bool MustStayActive() override; 410 const char* Type() override; 411 412 PinchGestureBlockState* AsPinchGestureBlock() override { return this; } 413 414 bool WasInterrupted() const { return mInterrupted; } 415 416 void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse); 417 418 bool IsWaitingForContentResponse() const { 419 return mWaitingForContentResponse; 420 } 421 422 private: 423 bool mInterrupted; 424 bool mWaitingForContentResponse; 425 }; 426 427 /** 428 * This class represents a single touch block. A touch block is 429 * a set of touch events that can be cancelled by web content via 430 * touch event listeners. 431 * 432 * Every touch-start event creates a new touch block. In this case, the 433 * touch block consists of the touch-start, followed by all touch events 434 * up to but not including the next touch-start (except in the case where 435 * a long-tap happens, see below). Note that in particular we cannot know 436 * when a touch block ends until the next one is started. Most touch 437 * blocks are created by receipt of a touch-start event. 438 * 439 * Every long-tap event also creates a new touch block, since it can also 440 * be consumed by web content. In this case, when the long-tap event is 441 * dispatched to web content, a new touch block is started to hold the remaining 442 * touch events, up to but not including the next touch start (or long-tap). 443 * 444 * Additionally, if touch-action is enabled, each touch block should 445 * have a set of allowed touch behavior flags; one for each touch point. 446 * This also requires running code on the Gecko main thread, and so may 447 * be populated with some latency. The mAllowedTouchBehaviorSet and 448 * mAllowedTouchBehaviors variables track this information. 449 */ 450 class TouchBlockState : public CancelableBlockState { 451 public: 452 explicit TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 453 TargetConfirmationFlags aFlags, 454 TouchCounter& aTouchCounter); 455 456 TouchBlockState* AsTouchBlock() override { return this; } 457 const TouchBlockState* AsTouchBlock() const override { return this; } 458 459 /** 460 * Set the allowed touch behavior flags for this block. 461 * @return false if this block already has these flags set, true if not. 462 */ 463 bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors); 464 /** 465 * If the allowed touch behaviors have been set, populate them into 466 * |aOutBehaviors| and return true. Else, return false. 467 */ 468 bool GetAllowedTouchBehaviors( 469 nsTArray<TouchBehaviorFlags>& aOutBehaviors) const; 470 471 /** 472 * Returns true if the allowed touch behaviours have been set, or if touch 473 * action is disabled. 474 */ 475 bool HasAllowedTouchBehaviors() const; 476 477 /** 478 * Copy various properties from another block. 479 */ 480 void CopyPropertiesFrom(const TouchBlockState& aOther); 481 482 /** 483 * @return true iff this block has received all the information needed 484 * to properly dispatch the events in the block. 485 */ 486 bool IsReadyForHandling() const override; 487 488 /** 489 * Sets a flag that indicates this input block occurred while the APZ was 490 * in a state of fast flinging. This affects gestures that may be produced 491 * from input events in this block. 492 */ 493 void SetDuringFastFling(); 494 /** 495 * @return true iff SetDuringFastFling was called on this block. 496 */ 497 bool IsDuringFastFling() const; 498 /** 499 * Set the single-tap state flag that indicates that this touch block 500 * triggered (1) a click, (2) not a click, or (3) not yet sure it will trigger 501 * a click or not. 502 */ 503 void SetSingleTapState(apz::SingleTapState aState); 504 apz::SingleTapState SingleTapState() const { return mSingleTapState; } 505 506 /** 507 * @return false iff touch-action is enabled and the allowed touch behaviors 508 * for this touch block do not allow pinch-zooming. 509 */ 510 bool TouchActionAllowsPinchZoom() const; 511 /** 512 * @return false iff touch-action is enabled and the allowed touch behaviors 513 * for this touch block do not allow double-tap zooming. 514 */ 515 bool TouchActionAllowsDoubleTapZoom() const; 516 /** 517 * @return false iff touch-action is enabled and the allowed touch behaviors 518 * for the first touch point do not allow panning in the specified 519 * direction(s). 520 */ 521 bool TouchActionAllowsPanningX() const; 522 bool TouchActionAllowsPanningY() const; 523 bool TouchActionAllowsPanningXY() const; 524 525 /** 526 * Notifies the input block of an incoming touch event so that the block can 527 * update its internal slop state. "Slop" refers to the area around the 528 * initial touchstart where we drop touchmove events so that content doesn't 529 * see them. The |aApzcCanConsumeEvents| parameter is factored into how large 530 * the slop area is - if this is true the slop area is larger. 531 * @return true iff the provided event is a touchmove in the slop area and 532 * so should not be sent to content. 533 */ 534 bool UpdateSlopState(const MultiTouchInput& aInput, 535 bool aApzcCanConsumeEvents); 536 bool IsInSlop() const; 537 bool ForLongTap() const { return mForLongTap; } 538 void SetForLongTap() { mForLongTap = true; } 539 bool WasLongTapProcessed() const { return mLongTapWasProcessed; } 540 void SetLongTapProcessed() { 541 MOZ_ASSERT(!mForLongTap); 542 mLongTapWasProcessed = true; 543 mIsWaitingLongTapResult = false; 544 } 545 546 void SetWaitingLongTapResult(bool aResult) { 547 MOZ_ASSERT(!mForLongTap); 548 mIsWaitingLongTapResult = aResult; 549 } 550 bool IsWaitingLongTapResult() const { return mIsWaitingLongTapResult; } 551 552 void SetNeedsToWaitTouchMove(bool aNeedsWaitTouchMove) { 553 mNeedsWaitTouchMove = aNeedsWaitTouchMove; 554 } 555 bool IsReadyForCallback() const { return !mNeedsWaitTouchMove; }; 556 557 /** 558 * Based on the slop origin and the given input event, return a best guess 559 * as to the pan direction of this touch block. Returns Nothing() if no guess 560 * can be made. 561 */ 562 Maybe<ScrollDirection> GetBestGuessPanDirection( 563 const MultiTouchInput& aInput) const; 564 565 /** 566 * Returns the number of touch points currently active. 567 */ 568 uint32_t GetActiveTouchCount() const; 569 570 void DispatchEvent(const InputData& aEvent) const override; 571 bool MustStayActive() override; 572 const char* Type() override; 573 TimeDuration GetTimeSinceBlockStart() const; 574 bool IsTargetOriginallyConfirmed() const; 575 576 private: 577 nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors; 578 bool mAllowedTouchBehaviorSet; 579 bool mDuringFastFling; 580 bool mInSlop; 581 // A long tap involves two touch blocks: the original touch 582 // block containing the `touchstart`, and a second one 583 // specifically for the long tap. `mForLongTap` is set on the 584 // second touch block. `mLongTapWasProcessed` is set 585 // on the first touch block after the long tap was processed. 586 bool mForLongTap; 587 bool mLongTapWasProcessed; 588 589 // A flag representing a state while we are waiting for a content response for 590 // the long tap. 591 // The reason why we have this flag separately from `mLongTapWasProcessed` is 592 // the block is not ready to be processed during the wait, and is ready once 593 // after `mLongTapWasProcessed` became true. 594 bool mIsWaitingLongTapResult; 595 // A flag representing a state that this block still needs to wait for a 596 // content response for a touch move event. It will be set just before 597 // triggering a long-press event. 598 bool mNeedsWaitTouchMove; 599 apz::SingleTapState mSingleTapState; 600 ScreenIntPoint mSlopOrigin; 601 // A reference to the InputQueue's touch counter 602 TouchCounter& mTouchCounter; 603 TimeStamp mStartTime; 604 // The original `mTargetConfirmed`. This is necessary to tell whether there's 605 // any APZ-aware event listener in the content after we've got a content 606 // response, because in the case of a long-tap event we need to wait a content 607 // response again. 608 TargetConfirmationState mOriginalTargetConfirmedState; 609 }; 610 611 /** 612 * This class represents a set of keyboard inputs targeted at the same Apzc. 613 */ 614 class KeyboardBlockState : public InputBlockState { 615 public: 616 explicit KeyboardBlockState( 617 const RefPtr<AsyncPanZoomController>& aTargetApzc); 618 619 KeyboardBlockState* AsKeyboardBlock() override { return this; } 620 621 bool MustStayActive() override { return false; } 622 623 /** 624 * @return Whether or not overscrolling is prevented for this keyboard block. 625 */ 626 bool AllowScrollHandoff() const { return false; } 627 }; 628 629 } // namespace layers 630 } // namespace mozilla 631 632 #endif // mozilla_layers_InputBlockState_h