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, &currentTouch)) {
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