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