Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et 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 "InputBlockState.h"
8 : #include "AsyncPanZoomController.h" // for AsyncPanZoomController
9 : #include "AsyncScrollBase.h" // for kScrollSeriesTimeoutMs
10 : #include "gfxPrefs.h" // for gfxPrefs
11 : #include "mozilla/MouseEvents.h"
12 : #include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
13 : #include "mozilla/Telemetry.h" // for Telemetry
14 : #include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
15 : #include "OverscrollHandoffState.h"
16 : #include "QueuedInput.h"
17 :
18 : #define TBS_LOG(...)
19 : // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
20 :
21 : namespace mozilla {
22 : namespace layers {
23 :
24 : static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
25 :
26 0 : InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
27 0 : bool aTargetConfirmed)
28 : : mTargetApzc(aTargetApzc)
29 0 : , mTargetConfirmed(aTargetConfirmed ? TargetConfirmationState::eConfirmed
30 : : TargetConfirmationState::eUnconfirmed)
31 0 : , mBlockId(sBlockCounter++)
32 0 : , mTransformToApzc(aTargetApzc->GetTransformToThis())
33 : {
34 : // We should never be constructed with a nullptr target.
35 0 : MOZ_ASSERT(mTargetApzc);
36 0 : mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
37 0 : }
38 :
39 : bool
40 0 : InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
41 : TargetConfirmationState aState,
42 : InputData* aFirstInput)
43 : {
44 0 : MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed
45 : || aState == TargetConfirmationState::eTimedOut);
46 :
47 0 : if (mTargetConfirmed == TargetConfirmationState::eTimedOut &&
48 : aState == TargetConfirmationState::eConfirmed) {
49 : // The main thread finally responded. We had already timed out the
50 : // confirmation, but we want to update the state internally so that we
51 : // can record the time for telemetry purposes.
52 0 : mTargetConfirmed = TargetConfirmationState::eTimedOutAndMainThreadResponded;
53 : }
54 0 : if (mTargetConfirmed != TargetConfirmationState::eUnconfirmed) {
55 0 : return false;
56 : }
57 0 : mTargetConfirmed = aState;
58 :
59 : TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get());
60 0 : if (mTargetApzc == aTargetApzc) {
61 : // The confirmed target is the same as the tentative one, so we're done.
62 0 : return true;
63 : }
64 :
65 : TBS_LOG("%p replacing unconfirmed target %p with real target %p\n",
66 : this, mTargetApzc.get(), aTargetApzc.get());
67 :
68 0 : UpdateTargetApzc(aTargetApzc);
69 0 : return true;
70 : }
71 :
72 : void
73 0 : InputBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
74 : {
75 : // note that aTargetApzc MAY be null here.
76 0 : mTargetApzc = aTargetApzc;
77 0 : mTransformToApzc = aTargetApzc ? aTargetApzc->GetTransformToThis() : ScreenToParentLayerMatrix4x4();
78 0 : mOverscrollHandoffChain = (mTargetApzc ? mTargetApzc->BuildOverscrollHandoffChain() : nullptr);
79 0 : }
80 :
81 : const RefPtr<AsyncPanZoomController>&
82 0 : InputBlockState::GetTargetApzc() const
83 : {
84 0 : return mTargetApzc;
85 : }
86 :
87 : const RefPtr<const OverscrollHandoffChain>&
88 0 : InputBlockState::GetOverscrollHandoffChain() const
89 : {
90 0 : return mOverscrollHandoffChain;
91 : }
92 :
93 : uint64_t
94 0 : InputBlockState::GetBlockId() const
95 : {
96 0 : return mBlockId;
97 : }
98 :
99 : bool
100 0 : InputBlockState::IsTargetConfirmed() const
101 : {
102 0 : return mTargetConfirmed != TargetConfirmationState::eUnconfirmed;
103 : }
104 :
105 : bool
106 0 : InputBlockState::HasReceivedRealConfirmedTarget() const
107 : {
108 0 : return mTargetConfirmed == TargetConfirmationState::eConfirmed ||
109 0 : mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded;
110 : }
111 :
112 : bool
113 0 : InputBlockState::IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const
114 : {
115 0 : if (aA == aB) {
116 0 : return true;
117 : }
118 :
119 0 : bool seenA = false;
120 0 : for (size_t i = 0; i < mOverscrollHandoffChain->Length(); ++i) {
121 0 : AsyncPanZoomController* apzc = mOverscrollHandoffChain->GetApzcAtIndex(i);
122 0 : if (apzc == aB) {
123 0 : return seenA;
124 : }
125 0 : if (apzc == aA) {
126 0 : seenA = true;
127 : }
128 : }
129 0 : return false;
130 : }
131 :
132 :
133 : void
134 0 : InputBlockState::SetScrolledApzc(AsyncPanZoomController* aApzc)
135 : {
136 : // An input block should only have one scrolled APZC.
137 0 : MOZ_ASSERT(!mScrolledApzc || (gfxPrefs::APZAllowImmediateHandoff() ? IsDownchainOf(mScrolledApzc, aApzc) : mScrolledApzc == aApzc));
138 :
139 0 : mScrolledApzc = aApzc;
140 0 : }
141 :
142 : AsyncPanZoomController*
143 0 : InputBlockState::GetScrolledApzc() const
144 : {
145 0 : return mScrolledApzc;
146 : }
147 :
148 : bool
149 0 : InputBlockState::IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const
150 : {
151 0 : MOZ_ASSERT(aApzc && mScrolledApzc);
152 :
153 0 : return IsDownchainOf(mScrolledApzc, aApzc);
154 : }
155 :
156 : void
157 0 : InputBlockState::DispatchEvent(const InputData& aEvent) const
158 : {
159 0 : GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
160 0 : }
161 :
162 0 : CancelableBlockState::CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
163 0 : bool aTargetConfirmed)
164 : : InputBlockState(aTargetApzc, aTargetConfirmed)
165 : , mPreventDefault(false)
166 : , mContentResponded(false)
167 0 : , mContentResponseTimerExpired(false)
168 : {
169 0 : }
170 :
171 : bool
172 0 : CancelableBlockState::SetContentResponse(bool aPreventDefault)
173 : {
174 0 : if (mContentResponded) {
175 0 : return false;
176 : }
177 : TBS_LOG("%p got content response %d with timer expired %d\n",
178 : this, aPreventDefault, mContentResponseTimerExpired);
179 0 : mPreventDefault = aPreventDefault;
180 0 : mContentResponded = true;
181 0 : return true;
182 : }
183 :
184 : void
185 0 : CancelableBlockState::StartContentResponseTimer()
186 : {
187 0 : MOZ_ASSERT(mContentResponseTimer.IsNull());
188 0 : mContentResponseTimer = TimeStamp::Now();
189 0 : }
190 :
191 : bool
192 0 : CancelableBlockState::TimeoutContentResponse()
193 : {
194 0 : if (mContentResponseTimerExpired) {
195 0 : return false;
196 : }
197 : TBS_LOG("%p got content timer expired with response received %d\n",
198 : this, mContentResponded);
199 0 : if (!mContentResponded) {
200 0 : mPreventDefault = false;
201 : }
202 0 : mContentResponseTimerExpired = true;
203 0 : return true;
204 : }
205 :
206 : bool
207 0 : CancelableBlockState::IsContentResponseTimerExpired() const
208 : {
209 0 : return mContentResponseTimerExpired;
210 : }
211 :
212 : bool
213 0 : CancelableBlockState::IsDefaultPrevented() const
214 : {
215 0 : MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
216 0 : return mPreventDefault;
217 : }
218 :
219 : bool
220 0 : CancelableBlockState::HasReceivedAllContentNotifications() const
221 : {
222 0 : return HasReceivedRealConfirmedTarget() && mContentResponded;
223 : }
224 :
225 : bool
226 0 : CancelableBlockState::IsReadyForHandling() const
227 : {
228 0 : if (!IsTargetConfirmed()) {
229 0 : return false;
230 : }
231 0 : return mContentResponded || mContentResponseTimerExpired;
232 : }
233 :
234 : void
235 0 : CancelableBlockState::RecordContentResponseTime()
236 : {
237 0 : if (!mContentResponseTimer) {
238 : // We might get responses from content even though we didn't wait for them.
239 : // In that case, ignore the time on them, because they're not relevant for
240 : // tuning our timeout value. Also this function might get called multiple
241 : // times on the same input block, so we should only record the time from the
242 : // first successful call.
243 0 : return;
244 : }
245 0 : if (!HasReceivedAllContentNotifications()) {
246 : // Not done yet, we'll get called again
247 0 : return;
248 : }
249 0 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::CONTENT_RESPONSE_DURATION,
250 0 : (uint32_t)(TimeStamp::Now() - mContentResponseTimer).ToMilliseconds());
251 0 : mContentResponseTimer = TimeStamp();
252 : }
253 :
254 0 : DragBlockState::DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
255 : bool aTargetConfirmed,
256 0 : const MouseInput& aInitialEvent)
257 : : CancelableBlockState(aTargetApzc, aTargetConfirmed)
258 0 : , mReceivedMouseUp(false)
259 : {
260 0 : }
261 :
262 : bool
263 0 : DragBlockState::HasReceivedMouseUp()
264 : {
265 0 : return mReceivedMouseUp;
266 : }
267 :
268 : void
269 0 : DragBlockState::MarkMouseUpReceived()
270 : {
271 0 : mReceivedMouseUp = true;
272 0 : }
273 :
274 : void
275 0 : DragBlockState::SetInitialThumbPos(CSSCoord aThumbPos)
276 : {
277 0 : mInitialThumbPos = aThumbPos;
278 0 : }
279 :
280 : void
281 0 : DragBlockState::SetDragMetrics(const AsyncDragMetrics& aDragMetrics)
282 : {
283 0 : mDragMetrics = aDragMetrics;
284 0 : }
285 :
286 : void
287 0 : DragBlockState::DispatchEvent(const InputData& aEvent) const
288 : {
289 0 : MouseInput mouseInput = aEvent.AsMouseInput();
290 0 : if (!mouseInput.TransformToLocal(mTransformToApzc)) {
291 0 : return;
292 : }
293 :
294 0 : GetTargetApzc()->HandleDragEvent(mouseInput, mDragMetrics, mInitialThumbPos);
295 : }
296 :
297 : bool
298 0 : DragBlockState::MustStayActive()
299 : {
300 0 : return !mReceivedMouseUp;
301 : }
302 :
303 : const char*
304 0 : DragBlockState::Type()
305 : {
306 0 : return "drag";
307 : }
308 : // This is used to track the current wheel transaction.
309 : static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
310 :
311 0 : WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
312 : bool aTargetConfirmed,
313 0 : const ScrollWheelInput& aInitialEvent)
314 : : CancelableBlockState(aTargetApzc, aTargetConfirmed)
315 : , mScrollSeriesCounter(0)
316 0 : , mTransactionEnded(false)
317 : {
318 0 : sLastWheelBlockId = GetBlockId();
319 :
320 0 : if (aTargetConfirmed) {
321 : // Find the nearest APZC in the overscroll handoff chain that is scrollable.
322 : // If we get a content confirmation later that the apzc is different, then
323 : // content should have found a scrollable apzc, so we don't need to handle
324 : // that case.
325 : RefPtr<AsyncPanZoomController> apzc =
326 0 : mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
327 :
328 : // If nothing is scrollable, we don't consider this block as starting a
329 : // transaction.
330 0 : if (!apzc) {
331 0 : EndTransaction();
332 0 : return;
333 : }
334 :
335 0 : if (apzc != GetTargetApzc()) {
336 0 : UpdateTargetApzc(apzc);
337 : }
338 : }
339 : }
340 :
341 : bool
342 0 : WheelBlockState::SetContentResponse(bool aPreventDefault)
343 : {
344 0 : if (aPreventDefault) {
345 0 : EndTransaction();
346 : }
347 0 : return CancelableBlockState::SetContentResponse(aPreventDefault);
348 : }
349 :
350 : bool
351 0 : WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
352 : TargetConfirmationState aState,
353 : InputData* aFirstInput)
354 : {
355 : // The APZC that we find via APZCCallbackHelpers may not be the same APZC
356 : // ESM or OverscrollHandoff would have computed. Make sure we get the right
357 : // one by looking for the first apzc the next pending event can scroll.
358 0 : RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
359 0 : if (apzc && aFirstInput) {
360 0 : apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
361 : }
362 :
363 0 : InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
364 0 : return true;
365 : }
366 :
367 : void
368 0 : WheelBlockState::Update(ScrollWheelInput& aEvent)
369 : {
370 : // We might not be in a transaction if the block never started in a
371 : // transaction - for example, if nothing was scrollable.
372 0 : if (!InTransaction()) {
373 0 : return;
374 : }
375 :
376 : // The current "scroll series" is a like a sub-transaction. It has a separate
377 : // timeout of 80ms. Since we need to compute wheel deltas at different phases
378 : // of a transaction (for example, when it is updated, and later when the
379 : // event action is taken), we affix the scroll series counter to the event.
380 : // This makes GetScrollWheelDelta() consistent.
381 0 : if (!mLastEventTime.IsNull() &&
382 0 : (aEvent.mTimeStamp - mLastEventTime).ToMilliseconds() > kScrollSeriesTimeoutMs)
383 : {
384 0 : mScrollSeriesCounter = 0;
385 : }
386 0 : aEvent.mScrollSeriesNumber = ++mScrollSeriesCounter;
387 :
388 : // If we can't scroll in the direction of the wheel event, we don't update
389 : // the last move time. This allows us to timeout a transaction even if the
390 : // mouse isn't moving.
391 : //
392 : // We skip this check if the target is not yet confirmed, so that when it is
393 : // confirmed, we don't timeout the transaction.
394 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
395 0 : if (IsTargetConfirmed() && !apzc->CanScroll(aEvent)) {
396 0 : return;
397 : }
398 :
399 : // Update the time of the last known good event, and reset the mouse move
400 : // time to null. This will reset the delays on both the general transaction
401 : // timeout and the mouse-move-in-frame timeout.
402 0 : mLastEventTime = aEvent.mTimeStamp;
403 0 : mLastMouseMove = TimeStamp();
404 : }
405 :
406 : bool
407 0 : WheelBlockState::MustStayActive()
408 : {
409 0 : return !mTransactionEnded;
410 : }
411 :
412 : const char*
413 0 : WheelBlockState::Type()
414 : {
415 0 : return "scroll wheel";
416 : }
417 :
418 : bool
419 0 : WheelBlockState::ShouldAcceptNewEvent() const
420 : {
421 0 : if (!InTransaction()) {
422 : // If we're not in a transaction, start a new one.
423 0 : return false;
424 : }
425 :
426 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
427 0 : if (apzc->IsDestroyed()) {
428 0 : return false;
429 : }
430 :
431 0 : return true;
432 : }
433 :
434 : bool
435 0 : WheelBlockState::MaybeTimeout(const ScrollWheelInput& aEvent)
436 : {
437 0 : MOZ_ASSERT(InTransaction());
438 :
439 0 : if (MaybeTimeout(aEvent.mTimeStamp)) {
440 0 : return true;
441 : }
442 :
443 0 : if (!mLastMouseMove.IsNull()) {
444 : // If there's a recent mouse movement, we can time out the transaction early.
445 0 : TimeDuration duration = TimeStamp::Now() - mLastMouseMove;
446 0 : if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
447 : TBS_LOG("%p wheel transaction timed out after mouse move\n", this);
448 0 : EndTransaction();
449 0 : return true;
450 : }
451 : }
452 :
453 0 : return false;
454 : }
455 :
456 : bool
457 0 : WheelBlockState::MaybeTimeout(const TimeStamp& aTimeStamp)
458 : {
459 0 : MOZ_ASSERT(InTransaction());
460 :
461 : // End the transaction if the event occurred > 1.5s after the most recently
462 : // seen wheel event.
463 0 : TimeDuration duration = aTimeStamp - mLastEventTime;
464 0 : if (duration.ToMilliseconds() < gfxPrefs::MouseWheelTransactionTimeoutMs()) {
465 0 : return false;
466 : }
467 :
468 : TBS_LOG("%p wheel transaction timed out\n", this);
469 :
470 0 : if (gfxPrefs::MouseScrollTestingEnabled()) {
471 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
472 0 : apzc->NotifyMozMouseScrollEvent(NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"));
473 : }
474 :
475 0 : EndTransaction();
476 0 : return true;
477 : }
478 :
479 : void
480 0 : WheelBlockState::OnMouseMove(const ScreenIntPoint& aPoint)
481 : {
482 0 : MOZ_ASSERT(InTransaction());
483 :
484 0 : if (!GetTargetApzc()->Contains(aPoint)) {
485 0 : EndTransaction();
486 0 : return;
487 : }
488 :
489 0 : if (mLastMouseMove.IsNull()) {
490 : // If the cursor is moving inside the frame, and it is more than the
491 : // ignoremovedelay time since the last scroll operation, we record
492 : // this as the most recent mouse movement.
493 0 : TimeStamp now = TimeStamp::Now();
494 0 : TimeDuration duration = now - mLastEventTime;
495 0 : if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
496 0 : mLastMouseMove = now;
497 : }
498 : }
499 : }
500 :
501 : void
502 0 : WheelBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
503 : {
504 0 : InputBlockState::UpdateTargetApzc(aTargetApzc);
505 :
506 : // If we found there was no target apzc, then we end the transaction.
507 0 : if (!GetTargetApzc()) {
508 0 : EndTransaction();
509 : }
510 0 : }
511 :
512 : bool
513 0 : WheelBlockState::InTransaction() const
514 : {
515 : // We consider a wheel block to be in a transaction if it has a confirmed
516 : // target and is the most recent wheel input block to be created.
517 0 : if (GetBlockId() != sLastWheelBlockId) {
518 0 : return false;
519 : }
520 :
521 0 : if (mTransactionEnded) {
522 0 : return false;
523 : }
524 :
525 0 : MOZ_ASSERT(GetTargetApzc());
526 0 : return true;
527 : }
528 :
529 : bool
530 0 : WheelBlockState::AllowScrollHandoff() const
531 : {
532 : // If we're in a wheel transaction, we do not allow overscroll handoff until
533 : // a new event ends the wheel transaction.
534 0 : return !IsTargetConfirmed() || !InTransaction();
535 : }
536 :
537 : void
538 0 : WheelBlockState::EndTransaction()
539 : {
540 : TBS_LOG("%p ending wheel transaction\n", this);
541 0 : mTransactionEnded = true;
542 0 : }
543 :
544 0 : PanGestureBlockState::PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
545 : bool aTargetConfirmed,
546 0 : const PanGestureInput& aInitialEvent)
547 : : CancelableBlockState(aTargetApzc, aTargetConfirmed)
548 : , mInterrupted(false)
549 0 : , mWaitingForContentResponse(false)
550 : {
551 0 : if (aTargetConfirmed) {
552 : // Find the nearest APZC in the overscroll handoff chain that is scrollable.
553 : // If we get a content confirmation later that the apzc is different, then
554 : // content should have found a scrollable apzc, so we don't need to handle
555 : // that case.
556 : RefPtr<AsyncPanZoomController> apzc =
557 0 : mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
558 :
559 0 : if (apzc && apzc != GetTargetApzc()) {
560 0 : UpdateTargetApzc(apzc);
561 : }
562 : }
563 0 : }
564 :
565 : bool
566 0 : PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
567 : TargetConfirmationState aState,
568 : InputData* aFirstInput)
569 : {
570 : // The APZC that we find via APZCCallbackHelpers may not be the same APZC
571 : // ESM or OverscrollHandoff would have computed. Make sure we get the right
572 : // one by looking for the first apzc the next pending event can scroll.
573 0 : RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
574 0 : if (apzc && aFirstInput) {
575 : RefPtr<AsyncPanZoomController> scrollableApzc =
576 0 : apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput);
577 0 : if (scrollableApzc) {
578 0 : apzc = scrollableApzc;
579 : }
580 : }
581 :
582 0 : InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
583 0 : return true;
584 : }
585 :
586 : bool
587 0 : PanGestureBlockState::MustStayActive()
588 : {
589 0 : return !mInterrupted;
590 : }
591 :
592 : const char*
593 0 : PanGestureBlockState::Type()
594 : {
595 0 : return "pan gesture";
596 : }
597 :
598 : bool
599 0 : PanGestureBlockState::SetContentResponse(bool aPreventDefault)
600 : {
601 0 : if (aPreventDefault) {
602 : TBS_LOG("%p setting interrupted flag\n", this);
603 0 : mInterrupted = true;
604 : }
605 0 : bool stateChanged = CancelableBlockState::SetContentResponse(aPreventDefault);
606 0 : if (mWaitingForContentResponse) {
607 0 : mWaitingForContentResponse = false;
608 0 : stateChanged = true;
609 : }
610 0 : return stateChanged;
611 : }
612 :
613 : bool
614 0 : PanGestureBlockState::HasReceivedAllContentNotifications() const
615 : {
616 0 : return CancelableBlockState::HasReceivedAllContentNotifications()
617 0 : && !mWaitingForContentResponse;
618 : }
619 :
620 : bool
621 0 : PanGestureBlockState::IsReadyForHandling() const
622 : {
623 0 : if (!CancelableBlockState::IsReadyForHandling()) {
624 0 : return false;
625 : }
626 0 : return !mWaitingForContentResponse ||
627 0 : IsContentResponseTimerExpired();
628 : }
629 :
630 : bool
631 0 : PanGestureBlockState::AllowScrollHandoff() const
632 : {
633 0 : return false;
634 : }
635 :
636 : void
637 0 : PanGestureBlockState::SetNeedsToWaitForContentResponse(bool aWaitForContentResponse)
638 : {
639 0 : mWaitingForContentResponse = aWaitForContentResponse;
640 0 : }
641 :
642 0 : TouchBlockState::TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
643 0 : bool aTargetConfirmed, TouchCounter& aCounter)
644 : : CancelableBlockState(aTargetApzc, aTargetConfirmed)
645 : , mAllowedTouchBehaviorSet(false)
646 : , mDuringFastFling(false)
647 : , mSingleTapOccurred(false)
648 : , mInSlop(false)
649 0 : , mTouchCounter(aCounter)
650 : {
651 : TBS_LOG("Creating %p\n", this);
652 0 : if (!gfxPrefs::TouchActionEnabled()) {
653 0 : mAllowedTouchBehaviorSet = true;
654 : }
655 0 : }
656 :
657 : bool
658 0 : TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
659 : {
660 0 : if (mAllowedTouchBehaviorSet) {
661 0 : return false;
662 : }
663 : TBS_LOG("%p got allowed touch behaviours for %" PRIuSIZE " points\n", this, aBehaviors.Length());
664 0 : mAllowedTouchBehaviors.AppendElements(aBehaviors);
665 0 : mAllowedTouchBehaviorSet = true;
666 0 : return true;
667 : }
668 :
669 : bool
670 0 : TouchBlockState::GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const
671 : {
672 0 : if (!mAllowedTouchBehaviorSet) {
673 0 : return false;
674 : }
675 0 : aOutBehaviors.AppendElements(mAllowedTouchBehaviors);
676 0 : return true;
677 : }
678 :
679 : void
680 0 : TouchBlockState::CopyPropertiesFrom(const TouchBlockState& aOther)
681 : {
682 : TBS_LOG("%p copying properties from %p\n", this, &aOther);
683 0 : if (gfxPrefs::TouchActionEnabled()) {
684 0 : MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet || aOther.IsContentResponseTimerExpired());
685 0 : SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
686 : }
687 0 : mTransformToApzc = aOther.mTransformToApzc;
688 0 : }
689 :
690 : bool
691 0 : TouchBlockState::HasReceivedAllContentNotifications() const
692 : {
693 0 : return CancelableBlockState::HasReceivedAllContentNotifications()
694 : // See comment in TouchBlockState::IsReadyforHandling()
695 0 : && (!gfxPrefs::TouchActionEnabled() || mAllowedTouchBehaviorSet);
696 : }
697 :
698 : bool
699 0 : TouchBlockState::IsReadyForHandling() const
700 : {
701 0 : if (!CancelableBlockState::IsReadyForHandling()) {
702 0 : return false;
703 : }
704 :
705 0 : if (!gfxPrefs::TouchActionEnabled()) {
706 : // If TouchActionEnabled() was false when this block was created, then
707 : // mAllowedTouchBehaviorSet is guaranteed to the true. However, the pref
708 : // may have been flipped to false after the block was created. In that case,
709 : // we should eventually get the touch-behaviour notification, or expire the
710 : // content response timeout, but we don't really need to wait for those,
711 : // since we don't care about the touch-behaviour values any more.
712 0 : return true;
713 : }
714 :
715 0 : return mAllowedTouchBehaviorSet || IsContentResponseTimerExpired();
716 : }
717 :
718 : void
719 0 : TouchBlockState::SetDuringFastFling()
720 : {
721 : TBS_LOG("%p setting fast-motion flag\n", this);
722 0 : mDuringFastFling = true;
723 0 : }
724 :
725 : bool
726 0 : TouchBlockState::IsDuringFastFling() const
727 : {
728 0 : return mDuringFastFling;
729 : }
730 :
731 : void
732 0 : TouchBlockState::SetSingleTapOccurred()
733 : {
734 : TBS_LOG("%p setting single-tap-occurred flag\n", this);
735 0 : mSingleTapOccurred = true;
736 0 : }
737 :
738 : bool
739 0 : TouchBlockState::SingleTapOccurred() const
740 : {
741 0 : return mSingleTapOccurred;
742 : }
743 :
744 : bool
745 0 : TouchBlockState::MustStayActive()
746 : {
747 0 : return true;
748 : }
749 :
750 : const char*
751 0 : TouchBlockState::Type()
752 : {
753 0 : return "touch";
754 : }
755 :
756 : void
757 0 : TouchBlockState::DispatchEvent(const InputData& aEvent) const
758 : {
759 0 : MOZ_ASSERT(aEvent.mInputType == MULTITOUCH_INPUT);
760 0 : mTouchCounter.Update(aEvent.AsMultiTouchInput());
761 0 : CancelableBlockState::DispatchEvent(aEvent);
762 0 : }
763 :
764 : bool
765 0 : TouchBlockState::TouchActionAllowsPinchZoom() const
766 : {
767 0 : if (!gfxPrefs::TouchActionEnabled()) {
768 0 : return true;
769 : }
770 : // Pointer events specification requires that all touch points allow zoom.
771 0 : for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
772 0 : if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
773 0 : return false;
774 : }
775 : }
776 0 : return true;
777 : }
778 :
779 : bool
780 0 : TouchBlockState::TouchActionAllowsDoubleTapZoom() const
781 : {
782 0 : if (!gfxPrefs::TouchActionEnabled()) {
783 0 : return true;
784 : }
785 0 : for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
786 0 : if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
787 0 : return false;
788 : }
789 : }
790 0 : return true;
791 : }
792 :
793 : bool
794 0 : TouchBlockState::TouchActionAllowsPanningX() const
795 : {
796 0 : if (!gfxPrefs::TouchActionEnabled()) {
797 0 : return true;
798 : }
799 0 : if (mAllowedTouchBehaviors.IsEmpty()) {
800 : // Default to allowed
801 0 : return true;
802 : }
803 0 : TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
804 0 : return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
805 : }
806 :
807 : bool
808 0 : TouchBlockState::TouchActionAllowsPanningY() const
809 : {
810 0 : if (!gfxPrefs::TouchActionEnabled()) {
811 0 : return true;
812 : }
813 0 : if (mAllowedTouchBehaviors.IsEmpty()) {
814 : // Default to allowed
815 0 : return true;
816 : }
817 0 : TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
818 0 : return (flags & AllowedTouchBehavior::VERTICAL_PAN);
819 : }
820 :
821 : bool
822 0 : TouchBlockState::TouchActionAllowsPanningXY() const
823 : {
824 0 : if (!gfxPrefs::TouchActionEnabled()) {
825 0 : return true;
826 : }
827 0 : if (mAllowedTouchBehaviors.IsEmpty()) {
828 : // Default to allowed
829 0 : return true;
830 : }
831 0 : TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
832 0 : return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)
833 0 : && (flags & AllowedTouchBehavior::VERTICAL_PAN);
834 : }
835 :
836 : bool
837 0 : TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
838 : bool aApzcCanConsumeEvents)
839 : {
840 0 : if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
841 : // this is by definition the first event in this block. If it's the first
842 : // touch, then we enter a slop state.
843 0 : mInSlop = (aInput.mTouches.Length() == 1);
844 0 : if (mInSlop) {
845 0 : mSlopOrigin = aInput.mTouches[0].mScreenPoint;
846 : TBS_LOG("%p entering slop with origin %s\n", this, Stringify(mSlopOrigin).c_str());
847 : }
848 0 : return false;
849 : }
850 0 : if (mInSlop) {
851 : ScreenCoord threshold = aApzcCanConsumeEvents
852 : ? AsyncPanZoomController::GetTouchStartTolerance()
853 0 : : ScreenCoord(gfxPrefs::APZTouchMoveTolerance() * APZCTreeManager::GetDPI());
854 0 : bool stayInSlop = (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) &&
855 0 : (aInput.mTouches.Length() == 1) &&
856 0 : ((aInput.mTouches[0].mScreenPoint - mSlopOrigin).Length() < threshold);
857 0 : if (!stayInSlop) {
858 : // we're out of the slop zone, and will stay out for the remainder of
859 : // this block
860 : TBS_LOG("%p exiting slop\n", this);
861 0 : mInSlop = false;
862 : }
863 : }
864 0 : return mInSlop;
865 : }
866 :
867 : uint32_t
868 0 : TouchBlockState::GetActiveTouchCount() const
869 : {
870 0 : return mTouchCounter.GetActiveTouchCount();
871 : }
872 :
873 0 : KeyboardBlockState::KeyboardBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc)
874 0 : : InputBlockState(aTargetApzc, true)
875 : {
876 0 : }
877 :
878 : } // namespace layers
879 : } // namespace mozilla
|