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 const Maybe<ScrollableLayerGuid>& aTargetGuid); 231 232 /** 233 * Returns whether or not the block is participating in a wheel transaction. 234 * This means that the block is the most recent input block to be created, 235 * and no events have occurred that would require scrolling a different 236 * frame. 237 * 238 * @return True if in a transaction, false otherwise. 239 */ 240 bool InTransaction() const; 241 242 /** 243 * Mark the block as no longer participating in a wheel transaction. This 244 * will force future wheel events to begin a new input block. 245 */ 246 void EndTransaction(); 247 248 /** 249 * @return Whether or not overscrolling is prevented for this wheel block. 250 */ 251 bool AllowScrollHandoff() const; 252 253 /** 254 * Called to check and possibly end the transaction due to a timeout. 255 * 256 * @return True if the transaction ended, false otherwise. 257 */ 258 bool MaybeTimeout(const TimeStamp& aTimeStamp); 259 260 /** 261 * Update the wheel transaction state for a new event. 262 */ 263 void Update(ScrollWheelInput& aEvent); 264 GetAllowedScrollDirections()265 ScrollDirections GetAllowedScrollDirections() const { 266 return mAllowedScrollDirections; 267 } 268 269 protected: 270 void UpdateTargetApzc( 271 const RefPtr<AsyncPanZoomController>& aTargetApzc) override; 272 273 private: 274 TimeStamp mLastEventTime; 275 TimeStamp mLastMouseMove; 276 uint32_t mScrollSeriesCounter; 277 bool mTransactionEnded; 278 bool mIsScrollable = true; 279 ScrollDirections mAllowedScrollDirections; 280 }; 281 282 /** 283 * A block of mouse events that are part of a drag 284 */ 285 class DragBlockState : public CancelableBlockState { 286 public: 287 DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 288 TargetConfirmationFlags aFlags, const MouseInput& aEvent); 289 290 bool MustStayActive() override; 291 const char* Type() override; 292 293 bool HasReceivedMouseUp(); 294 void MarkMouseUpReceived(); 295 AsDragBlock()296 DragBlockState* AsDragBlock() override { return this; } 297 298 void SetInitialThumbPos(CSSCoord aThumbPos); 299 void SetDragMetrics(const AsyncDragMetrics& aDragMetrics); 300 301 void DispatchEvent(const InputData& aEvent) const override; 302 303 private: 304 AsyncDragMetrics mDragMetrics; 305 CSSCoord mInitialThumbPos; 306 bool mReceivedMouseUp; 307 }; 308 309 /** 310 * A single block of pan gesture events. 311 */ 312 class PanGestureBlockState : public CancelableBlockState { 313 public: 314 PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 315 TargetConfirmationFlags aFlags, 316 const PanGestureInput& aEvent); 317 318 bool SetContentResponse(bool aPreventDefault) override; 319 bool HasReceivedAllContentNotifications() const override; 320 bool IsReadyForHandling() const override; 321 bool MustStayActive() override; 322 const char* Type() override; 323 bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc, 324 TargetConfirmationState aState, 325 InputData* aFirstInput, 326 bool aForScrollbarDrag) override; 327 AsPanGestureBlock()328 PanGestureBlockState* AsPanGestureBlock() override { return this; } 329 330 /** 331 * @return Whether or not overscrolling is prevented for this block. 332 */ 333 bool AllowScrollHandoff() const; 334 WasInterrupted()335 bool WasInterrupted() const { return mInterrupted; } 336 337 void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse); 338 GetAllowedScrollDirections()339 ScrollDirections GetAllowedScrollDirections() const { 340 return mAllowedScrollDirections; 341 } 342 343 private: 344 bool mInterrupted; 345 bool mWaitingForContentResponse; 346 ScrollDirections mAllowedScrollDirections; 347 }; 348 349 /** 350 * A single block of pinch gesture events. 351 */ 352 class PinchGestureBlockState : public CancelableBlockState { 353 public: 354 PinchGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 355 TargetConfirmationFlags aFlags); 356 357 bool SetContentResponse(bool aPreventDefault) override; 358 bool HasReceivedAllContentNotifications() const override; 359 bool IsReadyForHandling() const override; 360 bool MustStayActive() override; 361 const char* Type() override; 362 AsPinchGestureBlock()363 PinchGestureBlockState* AsPinchGestureBlock() override { return this; } 364 WasInterrupted()365 bool WasInterrupted() const { return mInterrupted; } 366 367 void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse); 368 369 private: 370 bool mInterrupted; 371 bool mWaitingForContentResponse; 372 }; 373 374 /** 375 * This class represents a single touch block. A touch block is 376 * a set of touch events that can be cancelled by web content via 377 * touch event listeners. 378 * 379 * Every touch-start event creates a new touch block. In this case, the 380 * touch block consists of the touch-start, followed by all touch events 381 * up to but not including the next touch-start (except in the case where 382 * a long-tap happens, see below). Note that in particular we cannot know 383 * when a touch block ends until the next one is started. Most touch 384 * blocks are created by receipt of a touch-start event. 385 * 386 * Every long-tap event also creates a new touch block, since it can also 387 * be consumed by web content. In this case, when the long-tap event is 388 * dispatched to web content, a new touch block is started to hold the remaining 389 * touch events, up to but not including the next touch start (or long-tap). 390 * 391 * Additionally, if touch-action is enabled, each touch block should 392 * have a set of allowed touch behavior flags; one for each touch point. 393 * This also requires running code on the Gecko main thread, and so may 394 * be populated with some latency. The mAllowedTouchBehaviorSet and 395 * mAllowedTouchBehaviors variables track this information. 396 */ 397 class TouchBlockState : public CancelableBlockState { 398 public: 399 explicit TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc, 400 TargetConfirmationFlags aFlags, 401 TouchCounter& aTouchCounter); 402 AsTouchBlock()403 TouchBlockState* AsTouchBlock() override { return this; } 404 405 /** 406 * Set the allowed touch behavior flags for this block. 407 * @return false if this block already has these flags set, true if not. 408 */ 409 bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors); 410 /** 411 * If the allowed touch behaviors have been set, populate them into 412 * |aOutBehaviors| and return true. Else, return false. 413 */ 414 bool GetAllowedTouchBehaviors( 415 nsTArray<TouchBehaviorFlags>& aOutBehaviors) const; 416 417 /** 418 * Returns true if the allowed touch behaviours have been set, or if touch 419 * action is disabled. 420 */ 421 bool HasAllowedTouchBehaviors() const; 422 423 /** 424 * Copy various properties from another block. 425 */ 426 void CopyPropertiesFrom(const TouchBlockState& aOther); 427 428 /* 429 * @return true iff this block has received all the information it could 430 * have gotten from the content thread. 431 */ 432 bool HasReceivedAllContentNotifications() const override; 433 434 /** 435 * @return true iff this block has received all the information needed 436 * to properly dispatch the events in the block. 437 */ 438 bool IsReadyForHandling() const override; 439 440 /** 441 * Sets a flag that indicates this input block occurred while the APZ was 442 * in a state of fast flinging. This affects gestures that may be produced 443 * from input events in this block. 444 */ 445 void SetDuringFastFling(); 446 /** 447 * @return true iff SetDuringFastFling was called on this block. 448 */ 449 bool IsDuringFastFling() const; 450 /** 451 * Set the single-tap-occurred flag that indicates that this touch block 452 * triggered a single tap event. 453 */ 454 void SetSingleTapOccurred(); 455 /** 456 * @return true iff the single-tap-occurred flag is set on this block. 457 */ 458 bool SingleTapOccurred() const; 459 460 /** 461 * @return false iff touch-action is enabled and the allowed touch behaviors 462 * for this touch block do not allow pinch-zooming. 463 */ 464 bool TouchActionAllowsPinchZoom() const; 465 /** 466 * @return false iff touch-action is enabled and the allowed touch behaviors 467 * for this touch block do not allow double-tap zooming. 468 */ 469 bool TouchActionAllowsDoubleTapZoom() const; 470 /** 471 * @return false iff touch-action is enabled and the allowed touch behaviors 472 * for the first touch point do not allow panning in the specified 473 * direction(s). 474 */ 475 bool TouchActionAllowsPanningX() const; 476 bool TouchActionAllowsPanningY() const; 477 bool TouchActionAllowsPanningXY() const; 478 479 /** 480 * Notifies the input block of an incoming touch event so that the block can 481 * update its internal slop state. "Slop" refers to the area around the 482 * initial touchstart where we drop touchmove events so that content doesn't 483 * see them. The |aApzcCanConsumeEvents| parameter is factored into how large 484 * the slop area is - if this is true the slop area is larger. 485 * @return true iff the provided event is a touchmove in the slop area and 486 * so should not be sent to content. 487 */ 488 bool UpdateSlopState(const MultiTouchInput& aInput, 489 bool aApzcCanConsumeEvents); 490 bool IsInSlop() const; 491 492 /** 493 * Based on the slop origin and the given input event, return a best guess 494 * as to the pan direction of this touch block. Returns Nothing() if no guess 495 * can be made. 496 */ 497 Maybe<ScrollDirection> GetBestGuessPanDirection( 498 const MultiTouchInput& aInput); 499 500 /** 501 * Returns the number of touch points currently active. 502 */ 503 uint32_t GetActiveTouchCount() const; 504 505 void DispatchEvent(const InputData& aEvent) const override; 506 bool MustStayActive() override; 507 const char* Type() override; 508 TimeDuration GetTimeSinceBlockStart() const; 509 510 private: 511 nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors; 512 bool mAllowedTouchBehaviorSet; 513 bool mDuringFastFling; 514 bool mSingleTapOccurred; 515 bool mInSlop; 516 ScreenIntPoint mSlopOrigin; 517 // A reference to the InputQueue's touch counter 518 TouchCounter& mTouchCounter; 519 TimeStamp mStartTime; 520 }; 521 522 /** 523 * This class represents a set of keyboard inputs targeted at the same Apzc. 524 */ 525 class KeyboardBlockState : public InputBlockState { 526 public: 527 explicit KeyboardBlockState( 528 const RefPtr<AsyncPanZoomController>& aTargetApzc); 529 AsKeyboardBlock()530 KeyboardBlockState* AsKeyboardBlock() override { return this; } 531 MustStayActive()532 bool MustStayActive() override { return false; } 533 534 /** 535 * @return Whether or not overscrolling is prevented for this keyboard block. 536 */ AllowScrollHandoff()537 bool AllowScrollHandoff() const { return false; } 538 }; 539 540 } // namespace layers 541 } // namespace mozilla 542 543 #endif // mozilla_layers_InputBlockState_h 544