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 "mozilla/layers/AndroidDynamicToolbarAnimator.h"
8
9 #include <cmath>
10
11 #include "APZCTreeManager.h"
12 #include "FrameMetrics.h"
13 #include "gfxPrefs.h"
14 #include "mozilla/EventForwards.h"
15 #include "mozilla/FloatingPoint.h"
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/gfx/Types.h"
18 #include "mozilla/layers/APZThreadUtils.h"
19 #include "mozilla/layers/AsyncCompositionManager.h"
20 #include "mozilla/layers/CompositorBridgeParent.h"
21 #include "mozilla/layers/CompositorOGL.h"
22 #include "mozilla/layers/CompositorThread.h"
23 #include "mozilla/layers/UiCompositorControllerMessageTypes.h"
24 #include "mozilla/layers/UiCompositorControllerParent.h"
25 #include "mozilla/MathAlgorithms.h"
26 #include "mozilla/Move.h"
27 #include "mozilla/Unused.h"
28
29 namespace {
30
31 // Internal flags and constants
32 static const float ANIMATION_DURATION =
33 0.15f; // How many seconds the complete animation should span
34 static const int32_t MOVE_TOOLBAR_DOWN =
35 1; // Multiplier to move the toolbar down
36 static const int32_t MOVE_TOOLBAR_UP = -1; // Multiplier to move the toolbar up
37 static const float SHRINK_FACTOR = 0.95f; // Amount to shrink the either the
38 // full content for small pages or
39 // the amount left See:
40 // PageTooSmallEnsureToolbarVisible()
41 } // namespace
42
43 namespace mozilla {
44 namespace layers {
45
AndroidDynamicToolbarAnimator(APZCTreeManager * aApz)46 AndroidDynamicToolbarAnimator::AndroidDynamicToolbarAnimator(
47 APZCTreeManager* aApz)
48 : mRootLayerTreeId(0),
49 mApz(aApz)
50 // Read/Write Compositor Thread, Read only Controller thread
51 ,
52 mToolbarState(eToolbarVisible),
53 mPinnedFlags(0)
54 // Controller thread only
55 ,
56 mControllerScrollingRootContent(false),
57 mControllerDragThresholdReached(false),
58 mControllerCancelTouchTracking(false),
59 mControllerDragChangedDirection(false),
60 mControllerResetOnNextMove(false),
61 mControllerStartTouch(0),
62 mControllerPreviousTouch(0),
63 mControllerTotalDistance(0),
64 mControllerMaxToolbarHeight(0),
65 mControllerToolbarHeight(0),
66 mControllerSurfaceHeight(0),
67 mControllerCompositionHeight(0),
68 mControllerRootScrollY(0.0f),
69 mControllerLastDragDirection(0),
70 mControllerTouchCount(0),
71 mControllerLastEventTimeStamp(0),
72 mControllerState(eNothingPending)
73 // Compositor thread only
74 ,
75 mCompositorShutdown(false),
76 mCompositorAnimationDeferred(false),
77 mCompositorLayersUpdateEnabled(false),
78 mCompositorAnimationStarted(false),
79 mCompositorReceivedFirstPaint(false),
80 mCompositorWaitForPageResize(false),
81 mCompositorToolbarShowRequested(false),
82 mCompositorSendResponseForSnapshotUpdate(false),
83 mCompositorAnimationStyle(eAnimate),
84 mCompositorMaxToolbarHeight(0),
85 mCompositorToolbarHeight(0),
86 mCompositorSurfaceHeight(0),
87 mCompositorAnimationDirection(0),
88 mCompositorAnimationStartHeight(0) {}
89
Initialize(uint64_t aRootLayerTreeId)90 void AndroidDynamicToolbarAnimator::Initialize(uint64_t aRootLayerTreeId) {
91 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
92 mRootLayerTreeId = aRootLayerTreeId;
93 RefPtr<UiCompositorControllerParent> uiController =
94 UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
95 MOZ_ASSERT(uiController);
96 uiController->RegisterAndroidDynamicToolbarAnimator(this);
97
98 // Send queued messages that were posted before Initialize() was called.
99 for (QueuedMessage* message = mCompositorQueuedMessages.getFirst();
100 message != nullptr; message = message->getNext()) {
101 uiController->ToolbarAnimatorMessageFromCompositor(message->mMessage);
102 }
103 mCompositorQueuedMessages.clear();
104 }
105
ClearTreeManager()106 void AndroidDynamicToolbarAnimator::ClearTreeManager() {
107 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
108 mApz = nullptr;
109 }
110
GetTouchY(MultiTouchInput & multiTouch,ScreenIntCoord * value)111 static bool GetTouchY(MultiTouchInput& multiTouch, ScreenIntCoord* value) {
112 MOZ_ASSERT(value);
113 if (multiTouch.mTouches.Length() == 1) {
114 *value = multiTouch.mTouches[0].mScreenPoint.y;
115 return true;
116 }
117
118 return false;
119 }
120
ReceiveInputEvent(const RefPtr<APZCTreeManager> & aApz,InputData & aEvent,const ScreenPoint & aScrollOffset)121 nsEventStatus AndroidDynamicToolbarAnimator::ReceiveInputEvent(
122 const RefPtr<APZCTreeManager>& aApz, InputData& aEvent,
123 const ScreenPoint& aScrollOffset) {
124 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
125
126 mControllerRootScrollY = aScrollOffset.y;
127
128 // Only process and adjust touch events. Wheel events (aka scroll events) are
129 // adjusted in the NativePanZoomController
130 if (aEvent.mInputType != MULTITOUCH_INPUT) {
131 return nsEventStatus_eIgnore;
132 }
133
134 MultiTouchInput& multiTouch = aEvent.AsMultiTouchInput();
135
136 if (PageTooSmallEnsureToolbarVisible()) {
137 TranslateTouchEvent(multiTouch);
138 return nsEventStatus_eIgnore;
139 }
140
141 switch (multiTouch.mType) {
142 case MultiTouchInput::MULTITOUCH_START:
143 mControllerTouchCount = multiTouch.mTouches.Length();
144 break;
145 case MultiTouchInput::MULTITOUCH_END:
146 case MultiTouchInput::MULTITOUCH_CANCEL:
147 mControllerTouchCount -= multiTouch.mTouches.Length();
148 break;
149 case MultiTouchInput::MULTITOUCH_MOVE:
150 break;
151 }
152
153 if (mControllerTouchCount > 1) {
154 mControllerResetOnNextMove = true;
155 }
156
157 ScreenIntCoord currentTouch = 0;
158
159 if (mPinnedFlags || !GetTouchY(multiTouch, ¤tTouch)) {
160 TranslateTouchEvent(multiTouch);
161 return nsEventStatus_eIgnore;
162 }
163
164 // Only the return value from ProcessTouchDelta should
165 // change status to nsEventStatus_eConsumeNoDefault
166 nsEventStatus status = nsEventStatus_eIgnore;
167
168 const StaticToolbarState currentToolbarState = mToolbarState;
169 switch (multiTouch.mType) {
170 case MultiTouchInput::MULTITOUCH_START:
171 mControllerCancelTouchTracking = false;
172 mControllerStartTouch = mControllerPreviousTouch = currentTouch;
173 // We don't want to stop the animation if we are near the bottom of the
174 // page.
175 if (!ScrollOffsetNearBottom() &&
176 (currentToolbarState == eToolbarAnimating)) {
177 StopCompositorAnimation();
178 }
179 break;
180 case MultiTouchInput::MULTITOUCH_MOVE: {
181 CheckForResetOnNextMove(currentTouch);
182 if ((mControllerState != eAnimationStartPending) &&
183 (mControllerState != eAnimationStopPending) &&
184 (currentToolbarState != eToolbarAnimating) &&
185 !mControllerCancelTouchTracking) {
186 // Don't move the toolbar if we are near the page bottom
187 // and the toolbar is not in transition
188 if (ScrollOffsetNearBottom() && !ToolbarInTransition()) {
189 ShowToolbarIfNotVisible(currentToolbarState);
190 break;
191 }
192
193 ScreenIntCoord delta = currentTouch - mControllerPreviousTouch;
194 mControllerPreviousTouch = currentTouch;
195 mControllerTotalDistance += delta;
196 if (delta != 0) {
197 ScreenIntCoord direction =
198 (delta > 0 ? MOVE_TOOLBAR_DOWN : MOVE_TOOLBAR_UP);
199 if (mControllerLastDragDirection &&
200 (direction != mControllerLastDragDirection)) {
201 mControllerDragChangedDirection = true;
202 }
203 mControllerLastDragDirection = direction;
204 }
205 // NOTE: gfxPrefs::ToolbarScrollThreshold() returns a percentage as an
206 // int32_t. So multiply it by 0.01f to convert.
207 const uint32_t dragThreshold =
208 Abs(std::lround(0.01f * gfxPrefs::ToolbarScrollThreshold() *
209 mControllerCompositionHeight));
210 if ((Abs(mControllerTotalDistance.value) > dragThreshold) &&
211 (delta != 0)) {
212 mControllerDragThresholdReached = true;
213 status = ProcessTouchDelta(aApz, currentToolbarState, delta,
214 multiTouch.mTime);
215 }
216 mControllerLastEventTimeStamp = multiTouch.mTime;
217 }
218 break;
219 }
220 case MultiTouchInput::MULTITOUCH_END:
221 case MultiTouchInput::MULTITOUCH_CANCEL:
222 // last finger was lifted
223 if (mControllerTouchCount == 0) {
224 HandleTouchEnd(currentToolbarState, currentTouch);
225 }
226 break;
227 }
228
229 TranslateTouchEvent(multiTouch);
230
231 return status;
232 }
233
SetMaxToolbarHeight(ScreenIntCoord aHeight)234 void AndroidDynamicToolbarAnimator::SetMaxToolbarHeight(
235 ScreenIntCoord aHeight) {
236 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
237 UpdateControllerToolbarHeight(aHeight, aHeight);
238 mCompositorMaxToolbarHeight = aHeight;
239 UpdateCompositorToolbarHeight(aHeight);
240 }
241
SetPinned(bool aPinned,int32_t aReason)242 void AndroidDynamicToolbarAnimator::SetPinned(bool aPinned, int32_t aReason) {
243 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
244 MOZ_ASSERT(aReason < 32);
245 uint32_t bit = 0x01 << aReason;
246 uint32_t current = mPinnedFlags;
247 if (aPinned) {
248 mPinnedFlags = current | bit;
249 } else {
250 mPinnedFlags = current & (~bit);
251 }
252 }
253
GetMaxToolbarHeight() const254 ScreenIntCoord AndroidDynamicToolbarAnimator::GetMaxToolbarHeight() const {
255 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
256 return mCompositorMaxToolbarHeight;
257 }
258
GetCurrentToolbarHeight() const259 ScreenIntCoord AndroidDynamicToolbarAnimator::GetCurrentToolbarHeight() const {
260 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
261 return mCompositorToolbarHeight;
262 }
263
GetCurrentContentOffset() const264 ScreenIntCoord AndroidDynamicToolbarAnimator::GetCurrentContentOffset() const {
265 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
266 if (mCompositorAnimationStarted && (mToolbarState == eToolbarAnimating)) {
267 return 0;
268 }
269
270 return mCompositorToolbarHeight;
271 }
272
GetCurrentSurfaceHeight() const273 ScreenIntCoord AndroidDynamicToolbarAnimator::GetCurrentSurfaceHeight() const {
274 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
275 return mCompositorSurfaceHeight;
276 }
277
GetCompositionHeight() const278 ScreenIntCoord AndroidDynamicToolbarAnimator::GetCompositionHeight() const {
279 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
280 return mCompositorCompositionSize.height;
281 }
282
SetScrollingRootContent()283 void AndroidDynamicToolbarAnimator::SetScrollingRootContent() {
284 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
285 mControllerScrollingRootContent = true;
286 }
287
ToolbarAnimatorMessageFromUI(int32_t aMessage)288 void AndroidDynamicToolbarAnimator::ToolbarAnimatorMessageFromUI(
289 int32_t aMessage) {
290 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
291 switch (aMessage) {
292 case STATIC_TOOLBAR_NEEDS_UPDATE:
293 break;
294 case STATIC_TOOLBAR_READY:
295 break;
296 case TOOLBAR_HIDDEN:
297 // If the toolbar is animating, then it is already unlocked.
298 if (mToolbarState != eToolbarAnimating) {
299 mToolbarState = eToolbarUnlocked;
300 if (mCompositorAnimationDeferred) {
301 StartCompositorAnimation(
302 mCompositorAnimationDirection, mCompositorAnimationStyle,
303 mCompositorToolbarHeight, mCompositorWaitForPageResize);
304 }
305 } else {
306 // Animation already running so just make sure it is going the right
307 // direction.
308 StartCompositorAnimation(MOVE_TOOLBAR_UP, mCompositorAnimationStyle,
309 mCompositorToolbarHeight,
310 mCompositorWaitForPageResize);
311 }
312 break;
313 case TOOLBAR_VISIBLE:
314 // If we are currently animating, let the animation finish.
315 if (mToolbarState != eToolbarAnimating) {
316 mToolbarState = eToolbarVisible;
317 }
318 break;
319 case TOOLBAR_SHOW:
320 break;
321 case FIRST_PAINT:
322 break;
323 case REQUEST_SHOW_TOOLBAR_IMMEDIATELY:
324 NotifyControllerPendingAnimation(MOVE_TOOLBAR_DOWN, eImmediate);
325 break;
326 case REQUEST_SHOW_TOOLBAR_ANIMATED:
327 NotifyControllerPendingAnimation(MOVE_TOOLBAR_DOWN, eAnimate);
328 break;
329 case REQUEST_HIDE_TOOLBAR_IMMEDIATELY:
330 NotifyControllerPendingAnimation(MOVE_TOOLBAR_UP, eImmediate);
331 break;
332 case REQUEST_HIDE_TOOLBAR_ANIMATED:
333 NotifyControllerPendingAnimation(MOVE_TOOLBAR_UP, eAnimate);
334 break;
335 case TOOLBAR_SNAPSHOT_FAILED:
336 mToolbarState = eToolbarVisible;
337 NotifyControllerSnapshotFailed();
338 break;
339 default:
340 break;
341 }
342 }
343
UpdateAnimation(const TimeStamp & aCurrentFrame)344 bool AndroidDynamicToolbarAnimator::UpdateAnimation(
345 const TimeStamp& aCurrentFrame) {
346 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
347 if ((mToolbarState != eToolbarAnimating) || mCompositorShutdown) {
348 return false;
349 }
350
351 CompositorBridgeParent* parent =
352 CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
353 mRootLayerTreeId);
354 if (!parent) {
355 return false;
356 }
357 MOZ_ASSERT(mApz); // because parent is non-null
358
359 AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
360 if (!manager) {
361 return false;
362 }
363
364 if (mCompositorSurfaceHeight != mCompositorCompositionSize.height) {
365 // Waiting for the composition to resize
366 if (mCompositorWaitForPageResize && mCompositorAnimationStarted) {
367 mCompositorWaitForPageResize = false;
368 } else {
369 return true;
370 }
371 } else if (!mCompositorAnimationStarted) {
372 mApz->AdjustScrollForSurfaceShift(
373 ScreenPoint(0.0f, (float)(-mCompositorToolbarHeight)));
374 manager->SetFixedLayerMargins(mCompositorToolbarHeight, 0);
375 mCompositorAnimationStarted = true;
376 mCompositorReceivedFirstPaint = false;
377 mCompositorToolbarShowRequested = false;
378 // Reset the start time so the toolbar does not jump on the first animation
379 // frame
380 mCompositorAnimationStartTimeStamp = aCurrentFrame;
381 // Since the delta time for this frame will be zero. Just return, the
382 // animation will start on the next frame.
383 return true;
384 }
385
386 bool continueAnimating = true;
387
388 if (mCompositorAnimationStyle == eImmediate) {
389 if (mCompositorAnimationDirection == MOVE_TOOLBAR_DOWN) {
390 mCompositorToolbarHeight = mCompositorMaxToolbarHeight;
391 } else if (mCompositorAnimationDirection == MOVE_TOOLBAR_UP) {
392 mCompositorToolbarHeight = 0;
393 }
394 } else if (mCompositorAnimationStyle == eAnimate) {
395 const float rate =
396 ((float)mCompositorMaxToolbarHeight) / ANIMATION_DURATION;
397 float deltaTime =
398 (aCurrentFrame - mCompositorAnimationStartTimeStamp).ToSeconds();
399 // This animation was started in the future!
400 if (deltaTime < 0.0f) {
401 deltaTime = 0.0f;
402 }
403 mCompositorToolbarHeight =
404 mCompositorAnimationStartHeight +
405 ((int32_t)(rate * deltaTime) * mCompositorAnimationDirection);
406 }
407
408 if ((mCompositorAnimationDirection == MOVE_TOOLBAR_DOWN) &&
409 (mCompositorToolbarHeight >= mCompositorMaxToolbarHeight)) {
410 // if the toolbar is being animated and the page is at the end, the
411 // animation needs to wait for the page to resize before ending the
412 // animation so that the page may be scrolled
413 if (!mCompositorReceivedFirstPaint && mCompositorWaitForPageResize) {
414 continueAnimating = true;
415 } else {
416 continueAnimating = false;
417 mToolbarState = eToolbarVisible;
418 }
419 // Make sure we only send one show request per animation
420 if (!mCompositorToolbarShowRequested) {
421 PostMessage(TOOLBAR_SHOW);
422 mCompositorToolbarShowRequested = true;
423 }
424 mCompositorToolbarHeight = mCompositorMaxToolbarHeight;
425 } else if ((mCompositorAnimationDirection == MOVE_TOOLBAR_UP) &&
426 (mCompositorToolbarHeight <= 0)) {
427 continueAnimating = false;
428 mToolbarState = eToolbarUnlocked;
429 mCompositorToolbarHeight = 0;
430 }
431
432 if (continueAnimating) {
433 manager->SetFixedLayerMargins(mCompositorToolbarHeight, 0);
434 } else {
435 if (mCompositorAnimationDirection == MOVE_TOOLBAR_DOWN) {
436 if (!mCompositorReceivedFirstPaint) {
437 mApz->AdjustScrollForSurfaceShift(
438 ScreenPoint(0.0f, (float)mCompositorMaxToolbarHeight));
439 }
440 manager->SetFixedLayerMargins(0, GetFixedLayerMarginsBottom());
441 } else {
442 manager->SetFixedLayerMargins(0, 0);
443 }
444 }
445
446 if (!continueAnimating) {
447 NotifyControllerAnimationStopped(mCompositorToolbarHeight);
448 } else {
449 UpdateControllerToolbarHeight(mCompositorToolbarHeight);
450 }
451
452 return continueAnimating;
453 }
454
FirstPaint()455 void AndroidDynamicToolbarAnimator::FirstPaint() {
456 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
457 mCompositorReceivedFirstPaint = true;
458 PostMessage(FIRST_PAINT);
459 }
460
UpdateRootFrameMetrics(const FrameMetrics & aMetrics)461 void AndroidDynamicToolbarAnimator::UpdateRootFrameMetrics(
462 const FrameMetrics& aMetrics) {
463 CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(
464 aMetrics.GetZoom().ToScaleFactor(),
465 PixelCastJustification::ScreenIsParentLayerForRoot);
466 ScreenPoint scrollOffset = aMetrics.GetScrollOffset() * scale;
467 CSSRect cssPageRect = aMetrics.GetScrollableRect();
468
469 UpdateFrameMetrics(scrollOffset, scale, cssPageRect);
470 }
471
472 void AndroidDynamicToolbarAnimator::
MaybeUpdateCompositionSizeAndRootFrameMetrics(const FrameMetrics & aMetrics)473 MaybeUpdateCompositionSizeAndRootFrameMetrics(
474 const FrameMetrics& aMetrics) {
475 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
476 CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(
477 aMetrics.GetZoom().ToScaleFactor(),
478 PixelCastJustification::ScreenIsParentLayerForRoot);
479 ScreenIntSize size =
480 ScreenIntSize::Round(aMetrics.GetRootCompositionSize() * scale);
481
482 if (mCompositorCompositionSize == size) {
483 return;
484 }
485
486 ScreenIntSize prevSize = mCompositorCompositionSize;
487 mCompositorCompositionSize = size;
488
489 // The width has changed so the static snapshot needs to be updated
490 if ((prevSize.width != size.width) && (mToolbarState == eToolbarUnlocked)) {
491 // No need to set mCompositorSendResponseForSnapshotUpdate. If it is already
492 // true we don't want to change it.
493 PostMessage(STATIC_TOOLBAR_NEEDS_UPDATE);
494 }
495
496 if (prevSize.height != size.height) {
497 UpdateControllerCompositionHeight(size.height);
498 UpdateFixedLayerMargins();
499 }
500
501 UpdateRootFrameMetrics(aMetrics);
502 }
503
504 // Layers updates are need by Robocop test which enables them
EnableLayersUpdateNotifications(bool aEnable)505 void AndroidDynamicToolbarAnimator::EnableLayersUpdateNotifications(
506 bool aEnable) {
507 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
508 mCompositorLayersUpdateEnabled = aEnable;
509 }
510
NotifyLayersUpdated()511 void AndroidDynamicToolbarAnimator::NotifyLayersUpdated() {
512 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
513 if (mCompositorLayersUpdateEnabled) {
514 PostMessage(LAYERS_UPDATED);
515 }
516 }
517
AdoptToolbarPixels(mozilla::ipc::Shmem && aMem,const ScreenIntSize & aSize)518 void AndroidDynamicToolbarAnimator::AdoptToolbarPixels(
519 mozilla::ipc::Shmem&& aMem, const ScreenIntSize& aSize) {
520 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
521 mCompositorToolbarPixels = Some(Move(aMem));
522 mCompositorToolbarPixelsSize = aSize;
523 }
524
UpdateToolbarSnapshotTexture(CompositorOGL * gl)525 void AndroidDynamicToolbarAnimator::UpdateToolbarSnapshotTexture(
526 CompositorOGL* gl) {
527 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
528 // if the compositor has shutdown, do not create any new rendering objects.
529 if (mCompositorShutdown) {
530 return;
531 }
532
533 if (mCompositorToolbarPixels) {
534 RefPtr<gfx::DataSourceSurface> surface =
535 gfx::Factory::CreateWrappingDataSourceSurface(
536 mCompositorToolbarPixels.ref().get<uint8_t>(),
537 mCompositorToolbarPixelsSize.width * 4,
538 gfx::IntSize(mCompositorToolbarPixelsSize.width,
539 mCompositorToolbarPixelsSize.height),
540 gfx::SurfaceFormat::B8G8R8A8);
541
542 if (!mCompositorToolbarTexture) {
543 mCompositorToolbarTexture = gl->CreateDataTextureSource();
544 mCompositorToolbarEffect = nullptr;
545 }
546
547 if (!mCompositorToolbarTexture->Update(surface)) {
548 // Upload failed!
549 mCompositorToolbarTexture = nullptr;
550 }
551
552 RefPtr<UiCompositorControllerParent> uiController =
553 UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
554 uiController->DeallocShmem(mCompositorToolbarPixels.ref());
555 mCompositorToolbarPixels.reset();
556 // Send notification that texture is ready after the current composition has
557 // completed.
558 if (mCompositorToolbarTexture && mCompositorSendResponseForSnapshotUpdate) {
559 mCompositorSendResponseForSnapshotUpdate = false;
560 CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(
561 "AndroidDynamicToolbarAnimator::PostToolbarReady", this,
562 &AndroidDynamicToolbarAnimator::PostToolbarReady));
563 }
564 }
565 }
566
GetToolbarEffect()567 Effect* AndroidDynamicToolbarAnimator::GetToolbarEffect() {
568 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
569 // if the compositor has shutdown, do not create any new rendering objects.
570 if (mCompositorShutdown) {
571 return nullptr;
572 }
573
574 if (mCompositorToolbarTexture) {
575 if (!mCompositorToolbarEffect) {
576 mCompositorToolbarEffect = new EffectRGB(mCompositorToolbarTexture, true,
577 gfx::SamplingFilter::LINEAR);
578 }
579
580 float ratioVisible =
581 (float)mCompositorToolbarHeight / (float)mCompositorMaxToolbarHeight;
582 mCompositorToolbarEffect->mTextureCoords.y = 1.0f - ratioVisible;
583 mCompositorToolbarEffect->mTextureCoords.height = ratioVisible;
584 }
585
586 return mCompositorToolbarEffect.get();
587 }
588
Shutdown()589 void AndroidDynamicToolbarAnimator::Shutdown() {
590 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
591 mCompositorShutdown = true;
592 mCompositorToolbarEffect = nullptr;
593 mCompositorToolbarTexture = nullptr;
594 mCompositorQueuedMessages.clear();
595 if (mCompositorToolbarPixels) {
596 RefPtr<UiCompositorControllerParent> uiController =
597 UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
598 uiController->DeallocShmem(mCompositorToolbarPixels.ref());
599 mCompositorToolbarPixels.reset();
600 }
601 }
602
ProcessTouchDelta(const RefPtr<APZCTreeManager> & aApz,StaticToolbarState aCurrentToolbarState,ScreenIntCoord aDelta,uint32_t aTimeStamp)603 nsEventStatus AndroidDynamicToolbarAnimator::ProcessTouchDelta(
604 const RefPtr<APZCTreeManager>& aApz,
605 StaticToolbarState aCurrentToolbarState, ScreenIntCoord aDelta,
606 uint32_t aTimeStamp) {
607 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
608 MOZ_ASSERT(aApz);
609 nsEventStatus status = nsEventStatus_eIgnore;
610
611 const bool tryingToHideToolbar = aDelta < 0;
612
613 if (tryingToHideToolbar && !mControllerScrollingRootContent) {
614 // This prevent the toolbar from hiding if a subframe is being scrolled up.
615 // The toolbar will always become visible regardless what is being scrolled
616 // down.
617 return status;
618 }
619
620 if (aCurrentToolbarState == eToolbarVisible) {
621 if (tryingToHideToolbar && (mControllerState != eUnlockPending)) {
622 mCompositorSendResponseForSnapshotUpdate = true;
623 PostMessage(STATIC_TOOLBAR_NEEDS_UPDATE);
624 mControllerState = eUnlockPending;
625 }
626 return status;
627 }
628
629 if (aCurrentToolbarState != eToolbarUnlocked) {
630 return status;
631 }
632
633 if ((mControllerState != eUnlockPending) &&
634 (mControllerState != eNothingPending)) {
635 return status;
636 }
637
638 mControllerState = eNothingPending;
639 if ((tryingToHideToolbar && (mControllerToolbarHeight > 0)) ||
640 (!tryingToHideToolbar &&
641 (mControllerToolbarHeight < mControllerMaxToolbarHeight))) {
642 ScreenIntCoord deltaRemainder = 0;
643 mControllerToolbarHeight += aDelta;
644 if (tryingToHideToolbar && (mControllerToolbarHeight <= 0)) {
645 deltaRemainder = mControllerToolbarHeight;
646 mControllerToolbarHeight = 0;
647 } else if (!tryingToHideToolbar &&
648 (mControllerToolbarHeight >= mControllerMaxToolbarHeight)) {
649 deltaRemainder = mControllerToolbarHeight - mControllerMaxToolbarHeight;
650 mControllerToolbarHeight = mControllerMaxToolbarHeight;
651 mControllerState = eShowPending;
652 PostMessage(TOOLBAR_SHOW);
653 }
654
655 UpdateCompositorToolbarHeight(mControllerToolbarHeight);
656 RequestComposite();
657 // If there was no delta left over, the event was completely consumed.
658 if (deltaRemainder == 0) {
659 status = nsEventStatus_eConsumeNoDefault;
660 }
661
662 uint32_t timeDelta = aTimeStamp - mControllerLastEventTimeStamp;
663 if (mControllerLastEventTimeStamp && timeDelta && aDelta) {
664 float speed = -(float)aDelta / (float)timeDelta;
665 // we can't use mApz because we're on the controller thread, so we have
666 // the caller provide a RefPtr to the same underlying object, which should
667 // be safe to use.
668 aApz->ProcessTouchVelocity(aTimeStamp, speed);
669 }
670 }
671
672 return status;
673 }
674
HandleTouchEnd(StaticToolbarState aCurrentToolbarState,ScreenIntCoord aCurrentTouch)675 void AndroidDynamicToolbarAnimator::HandleTouchEnd(
676 StaticToolbarState aCurrentToolbarState, ScreenIntCoord aCurrentTouch) {
677 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
678 // If there was no move before the reset flag was set and the touch ended,
679 // check for it here. if mControllerResetOnNextMove is true, it will be set to
680 // false here
681 CheckForResetOnNextMove(aCurrentTouch);
682 int32_t direction = mControllerLastDragDirection;
683 mControllerLastDragDirection = 0;
684 bool isRoot = mControllerScrollingRootContent;
685 mControllerScrollingRootContent = false;
686 bool dragChangedDirection = mControllerDragChangedDirection;
687 mControllerDragChangedDirection = false;
688
689 // If the drag direction changed and the toolbar is partially visible, hide in
690 // the direction with the least distance to travel.
691 if (dragChangedDirection && ToolbarInTransition()) {
692 direction = ((float)mControllerToolbarHeight /
693 (float)mControllerMaxToolbarHeight) < 0.5f
694 ? MOVE_TOOLBAR_UP
695 : MOVE_TOOLBAR_DOWN;
696 }
697
698 // If the last touch didn't have a drag direction, use start of touch to find
699 // direction
700 if (!direction) {
701 if (mControllerToolbarHeight == mControllerMaxToolbarHeight) {
702 direction = MOVE_TOOLBAR_DOWN;
703 } else if (mControllerToolbarHeight == 0) {
704 direction = MOVE_TOOLBAR_UP;
705 } else {
706 direction =
707 ((aCurrentTouch - mControllerStartTouch) > 0 ? MOVE_TOOLBAR_DOWN
708 : MOVE_TOOLBAR_UP);
709 }
710 // If there still isn't a direction, default to show just to be safe
711 if (!direction) {
712 direction = MOVE_TOOLBAR_DOWN;
713 }
714 }
715 mControllerStartTouch = 0;
716 mControllerPreviousTouch = 0;
717 mControllerTotalDistance = 0;
718 bool dragThresholdReached = mControllerDragThresholdReached;
719 mControllerDragThresholdReached = false;
720 mControllerLastEventTimeStamp = 0;
721 bool cancelTouchTracking = mControllerCancelTouchTracking;
722 mControllerCancelTouchTracking = false;
723
724 // Animation is in progress, bail out.
725 if (aCurrentToolbarState == eToolbarAnimating) {
726 return;
727 }
728
729 // Received a UI thread request to show or hide the snapshot during a touch.
730 // This overrides the touch event so just return.
731 if (cancelTouchTracking) {
732 return;
733 }
734
735 // The drag threshold has not been reach and the toolbar is either completely
736 // visible or completely hidden.
737 if (!dragThresholdReached && !ToolbarInTransition()) {
738 ShowToolbarIfNotVisible(aCurrentToolbarState);
739 return;
740 }
741
742 // The toolbar is already where it needs to be so just return.
743 if (((direction == MOVE_TOOLBAR_DOWN) &&
744 (mControllerToolbarHeight == mControllerMaxToolbarHeight)) ||
745 ((direction == MOVE_TOOLBAR_UP) && (mControllerToolbarHeight == 0))) {
746 ShowToolbarIfNotVisible(aCurrentToolbarState);
747 return;
748 }
749
750 // Don't animate up if not scrolling root content. Even though
751 // ShowToolbarIfNotVisible checks if snapshot toolbar is completely visible
752 // before showing, we don't want to enter this if block if the snapshot
753 // toolbar isn't completely visible to avoid early return.
754 if (!isRoot && ((direction == MOVE_TOOLBAR_UP) &&
755 (mControllerToolbarHeight == mControllerMaxToolbarHeight))) {
756 ShowToolbarIfNotVisible(aCurrentToolbarState);
757 return;
758 }
759
760 if (ScrollOffsetNearBottom()) {
761 if (ToolbarInTransition()) {
762 // Toolbar is partially visible so make it visible since we are near the
763 // end of the page
764 direction = MOVE_TOOLBAR_DOWN;
765 } else {
766 // Don't start an animation if near the bottom of page and toolbar is
767 // completely visible or hidden
768 ShowToolbarIfNotVisible(aCurrentToolbarState);
769 return;
770 }
771 }
772
773 StartCompositorAnimation(direction, eAnimate, mControllerToolbarHeight,
774 ScrollOffsetNearBottom());
775 }
776
PostMessage(int32_t aMessage)777 void AndroidDynamicToolbarAnimator::PostMessage(int32_t aMessage) {
778 // if the root layer tree id is zero then Initialize() has not been called yet
779 // so queue the message until Initialize() is called.
780 if (mRootLayerTreeId == 0) {
781 QueueMessage(aMessage);
782 return;
783 }
784
785 RefPtr<UiCompositorControllerParent> uiController =
786 UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
787 if (!uiController) {
788 // Looks like IPC may be shutdown.
789 return;
790 }
791
792 // ToolbarAnimatorMessageFromCompositor may be called from any thread.
793 uiController->ToolbarAnimatorMessageFromCompositor(aMessage);
794 }
795
UpdateCompositorToolbarHeight(ScreenIntCoord aHeight)796 void AndroidDynamicToolbarAnimator::UpdateCompositorToolbarHeight(
797 ScreenIntCoord aHeight) {
798 if (!CompositorThreadHolder::IsInCompositorThread()) {
799 CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<ScreenIntCoord>(
800 "AndroidDynamicToolbarAnimator::UpdateCompositorToolbarHeight", this,
801 &AndroidDynamicToolbarAnimator::UpdateCompositorToolbarHeight,
802 aHeight));
803 return;
804 }
805
806 mCompositorToolbarHeight = aHeight;
807 UpdateFixedLayerMargins();
808 }
809
UpdateControllerToolbarHeight(ScreenIntCoord aHeight,ScreenIntCoord aMaxHeight)810 void AndroidDynamicToolbarAnimator::UpdateControllerToolbarHeight(
811 ScreenIntCoord aHeight, ScreenIntCoord aMaxHeight) {
812 if (!APZThreadUtils::IsControllerThread()) {
813 APZThreadUtils::RunOnControllerThread(
814 NewRunnableMethod<ScreenIntCoord, ScreenIntCoord>(
815 "AndroidDynamicToolbarAnimator::UpdateControllerToolbarHeight",
816 this, &AndroidDynamicToolbarAnimator::UpdateControllerToolbarHeight,
817 aHeight, aMaxHeight));
818 return;
819 }
820
821 mControllerToolbarHeight = aHeight;
822 if (aMaxHeight >= 0) {
823 mControllerMaxToolbarHeight = aMaxHeight;
824 }
825 }
826
UpdateControllerSurfaceHeight(ScreenIntCoord aHeight)827 void AndroidDynamicToolbarAnimator::UpdateControllerSurfaceHeight(
828 ScreenIntCoord aHeight) {
829 if (!APZThreadUtils::IsControllerThread()) {
830 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord>(
831 "AndroidDynamicToolbarAnimator::UpdateControllerSurfaceHeight", this,
832 &AndroidDynamicToolbarAnimator::UpdateControllerSurfaceHeight,
833 aHeight));
834 return;
835 }
836
837 mControllerSurfaceHeight = aHeight;
838 }
839
UpdateControllerCompositionHeight(ScreenIntCoord aHeight)840 void AndroidDynamicToolbarAnimator::UpdateControllerCompositionHeight(
841 ScreenIntCoord aHeight) {
842 if (!APZThreadUtils::IsControllerThread()) {
843 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord>(
844 "AndroidDynamicToolbarAnimator::UpdateControllerCompositionHeight",
845 this, &AndroidDynamicToolbarAnimator::UpdateControllerCompositionHeight,
846 aHeight));
847 return;
848 }
849
850 mControllerCompositionHeight = aHeight;
851 }
852
853 // Ensures the margin for the fixed layers match the position of the toolbar
UpdateFixedLayerMargins()854 void AndroidDynamicToolbarAnimator::UpdateFixedLayerMargins() {
855 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
856 if (mCompositorShutdown) {
857 return;
858 }
859 CompositorBridgeParent* parent =
860 CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
861 mRootLayerTreeId);
862 if (parent) {
863 ScreenIntCoord surfaceHeight = parent->GetEGLSurfaceSize().height;
864 if (surfaceHeight != mCompositorSurfaceHeight) {
865 mCompositorSurfaceHeight = surfaceHeight;
866 UpdateControllerSurfaceHeight(mCompositorSurfaceHeight);
867 }
868 AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
869 if (manager) {
870 if ((mToolbarState == eToolbarAnimating) && mCompositorAnimationStarted) {
871 manager->SetFixedLayerMargins(mCompositorToolbarHeight, 0);
872 } else {
873 manager->SetFixedLayerMargins(0, GetFixedLayerMarginsBottom());
874 }
875 }
876 }
877 }
878
NotifyControllerPendingAnimation(int32_t aDirection,AnimationStyle aAnimationStyle)879 void AndroidDynamicToolbarAnimator::NotifyControllerPendingAnimation(
880 int32_t aDirection, AnimationStyle aAnimationStyle) {
881 if (!APZThreadUtils::IsControllerThread()) {
882 APZThreadUtils::RunOnControllerThread(
883 NewRunnableMethod<int32_t, AnimationStyle>(
884 "AndroidDynamicToolbarAnimator::NotifyControllerPendingAnimation",
885 this,
886 &AndroidDynamicToolbarAnimator::NotifyControllerPendingAnimation,
887 aDirection, aAnimationStyle));
888 return;
889 }
890
891 mControllerCancelTouchTracking = true;
892
893 // If the toolbar is already where it needs to be, just abort the request.
894 if (((mControllerToolbarHeight == mControllerMaxToolbarHeight) &&
895 (aDirection == MOVE_TOOLBAR_DOWN)) ||
896 ((mControllerToolbarHeight == 0) && (aDirection == MOVE_TOOLBAR_UP))) {
897 // We received a show request but the real toolbar is hidden, so tell it to
898 // show now.
899 if ((aDirection == MOVE_TOOLBAR_DOWN) &&
900 (mToolbarState == eToolbarUnlocked)) {
901 PostMessage(TOOLBAR_SHOW);
902 }
903 return;
904 }
905
906 // NOTE: StartCompositorAnimation will set mControllerState to
907 // eAnimationStartPending
908 StartCompositorAnimation(aDirection, aAnimationStyle,
909 mControllerToolbarHeight, ScrollOffsetNearBottom());
910 MOZ_ASSERT(mControllerState == eAnimationStartPending);
911 }
912
StartCompositorAnimation(int32_t aDirection,AnimationStyle aAnimationStyle,ScreenIntCoord aHeight,bool aWaitForPageResize)913 void AndroidDynamicToolbarAnimator::StartCompositorAnimation(
914 int32_t aDirection, AnimationStyle aAnimationStyle, ScreenIntCoord aHeight,
915 bool aWaitForPageResize) {
916 if (!CompositorThreadHolder::IsInCompositorThread()) {
917 mControllerState = eAnimationStartPending;
918 CompositorThreadHolder::Loop()->PostTask(
919 NewRunnableMethod<int32_t, AnimationStyle, ScreenIntCoord, bool>(
920 "AndroidDynamicToolbarAnimator::StartCompositorAnimation", this,
921 &AndroidDynamicToolbarAnimator::StartCompositorAnimation,
922 aDirection, aAnimationStyle, aHeight, aWaitForPageResize));
923 return;
924 }
925
926 MOZ_ASSERT(aDirection == MOVE_TOOLBAR_UP || aDirection == MOVE_TOOLBAR_DOWN);
927
928 const StaticToolbarState initialToolbarState = mToolbarState;
929 mCompositorAnimationDirection = aDirection;
930 mCompositorAnimationStartHeight = mCompositorToolbarHeight = aHeight;
931 mCompositorAnimationStyle = aAnimationStyle;
932 mCompositorWaitForPageResize = aWaitForPageResize;
933 // If the snapshot is not unlocked, request the UI thread update the snapshot
934 // and defer animation until it has been unlocked
935 if ((initialToolbarState != eToolbarUnlocked) &&
936 (initialToolbarState != eToolbarAnimating)) {
937 mCompositorAnimationDeferred = true;
938 mCompositorSendResponseForSnapshotUpdate = true;
939 PostMessage(STATIC_TOOLBAR_NEEDS_UPDATE);
940 } else {
941 // Toolbar is either unlocked or already animating so animation may begin
942 // immediately
943 mCompositorAnimationDeferred = false;
944 mToolbarState = eToolbarAnimating;
945 if (initialToolbarState != eToolbarAnimating) {
946 mCompositorAnimationStarted = false;
947 }
948 // Let the controller know we are starting an animation so it may clear the
949 // AnimationStartPending flag.
950 NotifyControllerAnimationStarted();
951 // Only reset the time stamp and start compositor animation if not already
952 // animating.
953 if (initialToolbarState != eToolbarAnimating) {
954 if (mApz) {
955 mCompositorAnimationStartTimeStamp = mApz->GetFrameTime();
956 }
957 // Kick the compositor to start the animation if we aren't already
958 // animating.
959 RequestComposite();
960 }
961 }
962 }
963
NotifyControllerAnimationStarted()964 void AndroidDynamicToolbarAnimator::NotifyControllerAnimationStarted() {
965 if (!APZThreadUtils::IsControllerThread()) {
966 APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
967 "AndroidDynamicToolbarAnimator::NotifyControllerAnimationStarted", this,
968 &AndroidDynamicToolbarAnimator::NotifyControllerAnimationStarted));
969 return;
970 }
971
972 // It is possible there was a stop request after the start request so only set
973 // to NothingPending if start is what were are still waiting for.
974 if (mControllerState == eAnimationStartPending) {
975 mControllerState = eNothingPending;
976 }
977 }
978
StopCompositorAnimation()979 void AndroidDynamicToolbarAnimator::StopCompositorAnimation() {
980 if (!CompositorThreadHolder::IsInCompositorThread()) {
981 mControllerState = eAnimationStopPending;
982 CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(
983 "AndroidDynamicToolbarAnimator::StopCompositorAnimation", this,
984 &AndroidDynamicToolbarAnimator::StopCompositorAnimation));
985 return;
986 }
987
988 if (mToolbarState == eToolbarAnimating) {
989 if (mCompositorAnimationStarted) {
990 mCompositorAnimationStarted = false;
991 CompositorBridgeParent* parent =
992 CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
993 mRootLayerTreeId);
994 if (parent) {
995 AsyncCompositionManager* manager =
996 parent->GetCompositionManager(nullptr);
997 if (manager) {
998 MOZ_ASSERT(mApz);
999 mApz->AdjustScrollForSurfaceShift(
1000 ScreenPoint(0.0f, (float)(mCompositorToolbarHeight)));
1001 RequestComposite();
1002 }
1003 }
1004 }
1005 mToolbarState = eToolbarUnlocked;
1006 }
1007
1008 NotifyControllerAnimationStopped(mCompositorToolbarHeight);
1009 }
1010
NotifyControllerAnimationStopped(ScreenIntCoord aHeight)1011 void AndroidDynamicToolbarAnimator::NotifyControllerAnimationStopped(
1012 ScreenIntCoord aHeight) {
1013 if (!APZThreadUtils::IsControllerThread()) {
1014 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord>(
1015 "AndroidDynamicToolbarAnimator::NotifyControllerAnimationStopped", this,
1016 &AndroidDynamicToolbarAnimator::NotifyControllerAnimationStopped,
1017 aHeight));
1018 return;
1019 }
1020
1021 if (mControllerState == eAnimationStopPending) {
1022 mControllerState = eNothingPending;
1023 }
1024
1025 mControllerToolbarHeight = aHeight;
1026 }
1027
RequestComposite()1028 void AndroidDynamicToolbarAnimator::RequestComposite() {
1029 if (!CompositorThreadHolder::IsInCompositorThread()) {
1030 CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(
1031 "AndroidDynamicToolbarAnimator::RequestComposite", this,
1032 &AndroidDynamicToolbarAnimator::RequestComposite));
1033 return;
1034 }
1035
1036 if (mCompositorShutdown) {
1037 return;
1038 }
1039
1040 CompositorBridgeParent* parent =
1041 CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
1042 mRootLayerTreeId);
1043 if (parent) {
1044 AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
1045 if (manager) {
1046 if ((mToolbarState == eToolbarAnimating) && mCompositorAnimationStarted) {
1047 manager->SetFixedLayerMargins(mCompositorToolbarHeight, 0);
1048 } else {
1049 manager->SetFixedLayerMargins(0, GetFixedLayerMarginsBottom());
1050 }
1051 parent->Invalidate();
1052 parent->ScheduleComposition();
1053 }
1054 }
1055 }
1056
PostToolbarReady()1057 void AndroidDynamicToolbarAnimator::PostToolbarReady() {
1058 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
1059 RequestComposite();
1060 // Notify the UI thread the static toolbar is being rendered so the real
1061 // toolbar needs to be hidden. Once the TOOLBAR_HIDDEN message is
1062 // received, a pending animation may start or the toolbar snapshot may be
1063 // translated.
1064 PostMessage(STATIC_TOOLBAR_READY);
1065 if (mToolbarState != eToolbarAnimating) {
1066 mToolbarState = eToolbarUpdated;
1067 } else {
1068 // The compositor is already animating the toolbar so no need to defer.
1069 mCompositorAnimationDeferred = false;
1070 }
1071 }
1072
UpdateFrameMetrics(ScreenPoint aScrollOffset,CSSToScreenScale aScale,CSSRect aCssPageRect)1073 void AndroidDynamicToolbarAnimator::UpdateFrameMetrics(
1074 ScreenPoint aScrollOffset, CSSToScreenScale aScale, CSSRect aCssPageRect) {
1075 if (!APZThreadUtils::IsControllerThread()) {
1076 APZThreadUtils::RunOnControllerThread(
1077 NewRunnableMethod<ScreenPoint, CSSToScreenScale, CSSRect>(
1078 "AndroidDynamicToolbarAnimator::UpdateFrameMetrics", this,
1079 &AndroidDynamicToolbarAnimator::UpdateFrameMetrics, aScrollOffset,
1080 aScale, aCssPageRect));
1081 return;
1082 }
1083
1084 mControllerRootScrollY = aScrollOffset.y;
1085
1086 if (mControllerFrameMetrics.Update(aScrollOffset, aScale, aCssPageRect)) {
1087 if (FuzzyEqualsMultiplicative(
1088 mControllerFrameMetrics.mPageRect.YMost(),
1089 mControllerCompositionHeight +
1090 mControllerFrameMetrics.mScrollOffset.y) &&
1091 (mControllerFrameMetrics.mPageRect.YMost() >
1092 (mControllerSurfaceHeight * 2)) &&
1093 (mControllerToolbarHeight != mControllerMaxToolbarHeight) &&
1094 !mPinnedFlags) {
1095 // The end of the page has been reached, the page is twice the height of
1096 // the visible height, and the toolbar is not completely visible so
1097 // animate it into view.
1098 StartCompositorAnimation(MOVE_TOOLBAR_DOWN, eAnimate,
1099 mControllerToolbarHeight,
1100 /* wait for page resize */ true);
1101 }
1102 RefPtr<UiCompositorControllerParent> uiController =
1103 UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
1104 MOZ_ASSERT(uiController);
1105 CompositorThreadHolder::Loop()->PostTask(
1106 NewRunnableMethod<ScreenPoint, CSSToScreenScale>(
1107 "UiCompositorControllerParent::SendRootFrameMetrics", uiController,
1108 &UiCompositorControllerParent::SendRootFrameMetrics, aScrollOffset,
1109 aScale));
1110 }
1111 }
1112
PageTooSmallEnsureToolbarVisible()1113 bool AndroidDynamicToolbarAnimator::PageTooSmallEnsureToolbarVisible() {
1114 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1115 // if the page is too small then the toolbar can not be hidden
1116 if ((float)mControllerSurfaceHeight >=
1117 (mControllerFrameMetrics.mPageRect.YMost() * SHRINK_FACTOR)) {
1118 if (!mPinnedFlags) {
1119 // If the toolbar is partial hidden, show it.
1120 if (mControllerToolbarHeight != mControllerMaxToolbarHeight) {
1121 StartCompositorAnimation(MOVE_TOOLBAR_DOWN, eImmediate,
1122 mControllerToolbarHeight,
1123 /* wait for page resize */ true);
1124 } else {
1125 // If the static snapshot is visible, then make sure the real toolbar is
1126 // visible
1127 ShowToolbarIfNotVisible(mToolbarState);
1128 }
1129 }
1130 return true;
1131 }
1132
1133 return false;
1134 }
1135
ShowToolbarIfNotVisible(StaticToolbarState aCurrentToolbarState)1136 void AndroidDynamicToolbarAnimator::ShowToolbarIfNotVisible(
1137 StaticToolbarState aCurrentToolbarState) {
1138 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1139 if ((mControllerToolbarHeight == mControllerMaxToolbarHeight) &&
1140 (aCurrentToolbarState != eToolbarVisible) &&
1141 (mControllerState != eShowPending)) {
1142 mControllerState = eShowPending;
1143 PostMessage(TOOLBAR_SHOW);
1144 }
1145 }
1146
Update(const ScreenPoint & aScrollOffset,const CSSToScreenScale & aScale,const CSSRect & aCssPageRect)1147 bool AndroidDynamicToolbarAnimator::FrameMetricsState::Update(
1148 const ScreenPoint& aScrollOffset, const CSSToScreenScale& aScale,
1149 const CSSRect& aCssPageRect) {
1150 if (!FuzzyEqualsMultiplicative(aScrollOffset.x, mScrollOffset.x) ||
1151 !FuzzyEqualsMultiplicative(aScrollOffset.y, mScrollOffset.y) ||
1152 !FuzzyEqualsMultiplicative(aScale.scale, mScale.scale) ||
1153 !FuzzyEqualsMultiplicative(aCssPageRect.width, mCssPageRect.width) ||
1154 !FuzzyEqualsMultiplicative(aCssPageRect.height, mCssPageRect.height) ||
1155 !FuzzyEqualsMultiplicative(aCssPageRect.x, mCssPageRect.x) ||
1156 !FuzzyEqualsMultiplicative(aCssPageRect.y, mCssPageRect.y)) {
1157 mScrollOffset = aScrollOffset;
1158 mScale = aScale;
1159 mCssPageRect = aCssPageRect;
1160 mPageRect = mCssPageRect * mScale;
1161 return true;
1162 }
1163
1164 return false;
1165 }
1166
TranslateTouchEvent(MultiTouchInput & aTouchEvent)1167 void AndroidDynamicToolbarAnimator::TranslateTouchEvent(
1168 MultiTouchInput& aTouchEvent) {
1169 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1170 if (mControllerToolbarHeight > 0) {
1171 aTouchEvent.Translate(ScreenPoint(0.0f, -(float)mControllerToolbarHeight));
1172 }
1173 }
1174
GetFixedLayerMarginsBottom()1175 ScreenIntCoord AndroidDynamicToolbarAnimator::GetFixedLayerMarginsBottom() {
1176 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
1177 return mCompositorToolbarHeight -
1178 (mCompositorSurfaceHeight - mCompositorCompositionSize.height);
1179 }
1180
NotifyControllerSnapshotFailed()1181 void AndroidDynamicToolbarAnimator::NotifyControllerSnapshotFailed() {
1182 if (!APZThreadUtils::IsControllerThread()) {
1183 APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
1184 "AndroidDynamicToolbarAnimator::NotifyControllerSnapshotFailed", this,
1185 &AndroidDynamicToolbarAnimator::NotifyControllerSnapshotFailed));
1186 return;
1187 }
1188
1189 mControllerToolbarHeight = 0;
1190 mControllerState = eNothingPending;
1191 UpdateCompositorToolbarHeight(mControllerToolbarHeight);
1192 }
1193
CheckForResetOnNextMove(ScreenIntCoord aCurrentTouch)1194 void AndroidDynamicToolbarAnimator::CheckForResetOnNextMove(
1195 ScreenIntCoord aCurrentTouch) {
1196 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1197 if (mControllerResetOnNextMove) {
1198 mControllerTotalDistance = 0;
1199 mControllerLastDragDirection = 0;
1200 mControllerStartTouch = mControllerPreviousTouch = aCurrentTouch;
1201 mControllerDragThresholdReached = false;
1202 mControllerResetOnNextMove = false;
1203 }
1204 }
1205
ScrollOffsetNearBottom() const1206 bool AndroidDynamicToolbarAnimator::ScrollOffsetNearBottom() const {
1207 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1208 // Twice the toolbar's height is considered near the bottom of the page.
1209 if ((mControllerToolbarHeight * 2) >=
1210 (mControllerFrameMetrics.mPageRect.YMost() -
1211 (mControllerRootScrollY + ScreenCoord(mControllerCompositionHeight)))) {
1212 return true;
1213 }
1214 return false;
1215 }
1216
ToolbarInTransition()1217 bool AndroidDynamicToolbarAnimator::ToolbarInTransition() {
1218 if (APZThreadUtils::IsControllerThread()) {
1219 return (mControllerToolbarHeight != mControllerMaxToolbarHeight) &&
1220 (mControllerToolbarHeight != 0);
1221 }
1222
1223 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
1224 return (mCompositorToolbarHeight != mCompositorMaxToolbarHeight) &&
1225 (mCompositorToolbarHeight != 0);
1226 }
1227
QueueMessage(int32_t aMessage)1228 void AndroidDynamicToolbarAnimator::QueueMessage(int32_t aMessage) {
1229 if (!CompositorThreadHolder::IsInCompositorThread()) {
1230 CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<int32_t>(
1231 "AndroidDynamicToolbarAnimator::QueueMessage", this,
1232 &AndroidDynamicToolbarAnimator::QueueMessage, aMessage));
1233 return;
1234 }
1235
1236 // If the root layer tree id is no longer zero, Initialize() was called before
1237 // QueueMessage was processed so just post the message now.
1238 if (mRootLayerTreeId != 0) {
1239 PostMessage(aMessage);
1240 return;
1241 }
1242
1243 mCompositorQueuedMessages.insertBack(new QueuedMessage(aMessage));
1244 }
1245
1246 } // namespace layers
1247 } // namespace mozilla
1248