gfx/layers/apz/src/APZCTreeManager.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "APZCTreeManager.h"
     7 #include "Compositor.h"                 // for Compositor
     8 #include "CompositorParent.h"           // for CompositorParent, etc
     9 #include "InputData.h"                  // for InputData, etc
    10 #include "Layers.h"                     // for ContainerLayer, Layer, etc
    11 #include "gfx3DMatrix.h"                // for gfx3DMatrix
    12 #include "mozilla/dom/Touch.h"          // for Touch
    13 #include "mozilla/gfx/Point.h"          // for Point
    14 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
    15 #include "mozilla/layers/AsyncPanZoomController.h"
    16 #include "mozilla/MouseEvents.h"
    17 #include "mozilla/mozalloc.h"           // for operator new
    18 #include "mozilla/TouchEvents.h"
    19 #include "mozilla/Preferences.h"        // for Preferences
    20 #include "nsDebug.h"                    // for NS_WARNING
    21 #include "nsPoint.h"                    // for nsIntPoint
    22 #include "nsThreadUtils.h"              // for NS_IsMainThread
    23 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
    24 #include "UnitTransforms.h"             // for ViewAs
    26 #include <algorithm>                    // for std::stable_sort
    28 #define APZC_LOG(...)
    29 // #define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
    31 namespace mozilla {
    32 namespace layers {
    34 float APZCTreeManager::sDPI = 160.0;
    36 // Pref that enables printing of the APZC tree for debugging.
    37 static bool gPrintApzcTree = false;
    39 APZCTreeManager::APZCTreeManager()
    40     : mTreeLock("APZCTreeLock"),
    41       mTouchCount(0),
    42       mApzcTreeLog("apzctree")
    43 {
    44   MOZ_ASSERT(NS_IsMainThread());
    45   AsyncPanZoomController::InitializeGlobalState();
    46   Preferences::AddBoolVarCache(&gPrintApzcTree, "apz.printtree", gPrintApzcTree);
    47   mApzcTreeLog.ConditionOnPref(&gPrintApzcTree);
    48 }
    50 APZCTreeManager::~APZCTreeManager()
    51 {
    52 }
    54 void
    55 APZCTreeManager::GetAllowedTouchBehavior(WidgetInputEvent* aEvent,
    56                                          nsTArray<TouchBehaviorFlags>& aOutValues)
    57 {
    58   WidgetTouchEvent *touchEvent = aEvent->AsTouchEvent();
    60   aOutValues.Clear();
    62   for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
    63     // If aEvent wasn't transformed previously we might need to
    64     // add transforming of the spt here.
    65     mozilla::ScreenIntPoint spt;
    66     spt.x = touchEvent->touches[i]->mRefPoint.x;
    67     spt.y = touchEvent->touches[i]->mRefPoint.y;
    69     nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(spt);
    70     aOutValues.AppendElement(apzc
    71       ? apzc->GetAllowedTouchBehavior(spt)
    72       : AllowedTouchBehavior::UNKNOWN);
    73   }
    74 }
    76 void
    77 APZCTreeManager::SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
    78                                          const nsTArray<TouchBehaviorFlags> &aValues)
    79 {
    80   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
    81   if (apzc) {
    82     apzc->SetAllowedTouchBehavior(aValues);
    83   }
    84 }
    86 void
    87 APZCTreeManager::AssertOnCompositorThread()
    88 {
    89   Compositor::AssertOnCompositorThread();
    90 }
    92 /* Flatten the tree of APZC instances into the given nsTArray */
    93 static void
    94 Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
    95 {
    96   if (aApzc) {
    97     aCollection->AppendElement(aApzc);
    98     Collect(aApzc->GetLastChild(), aCollection);
    99     Collect(aApzc->GetPrevSibling(), aCollection);
   100   }
   101 }
   103 void
   104 APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
   105                                              bool aIsFirstPaint, uint64_t aFirstPaintLayersId)
   106 {
   107   AssertOnCompositorThread();
   109   MonitorAutoLock lock(mTreeLock);
   111   // We do this business with collecting the entire tree into an array because otherwise
   112   // it's very hard to determine which APZC instances need to be destroyed. In the worst
   113   // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
   114   // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
   115   // completely different place. In scenario (a) we would want to destroy the APZC while
   116   // walking the layer tree and noticing that the layer/APZC is no longer there. But if
   117   // we do that then we run into a problem in scenario (b) because we might encounter that
   118   // layer later during the walk. To handle both of these we have to 'remember' that the
   119   // layer was not found, and then do the destroy only at the end of the tree walk after
   120   // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
   121   // as part of a recursive tree walk is hard and so maintaining a list and removing
   122   // APZCs that are still alive is much simpler.
   123   nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
   124   Collect(mRootApzc, &apzcsToDestroy);
   125   mRootApzc = nullptr;
   127   if (aRoot) {
   128     mApzcTreeLog << "[start]\n";
   129     UpdatePanZoomControllerTree(aCompositor,
   130                                 aRoot,
   131                                 // aCompositor is null in gtest scenarios
   132                                 aCompositor ? aCompositor->RootLayerTreeId() : 0,
   133                                 gfx3DMatrix(), nullptr, nullptr,
   134                                 aIsFirstPaint, aFirstPaintLayersId,
   135                                 &apzcsToDestroy);
   136     mApzcTreeLog << "[end]\n";
   137   }
   139   for (size_t i = 0; i < apzcsToDestroy.Length(); i++) {
   140     APZC_LOG("Destroying APZC at %p\n", apzcsToDestroy[i].get());
   141     apzcsToDestroy[i]->Destroy();
   142   }
   143 }
   145 AsyncPanZoomController*
   146 APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
   147                                              Layer* aLayer, uint64_t aLayersId,
   148                                              gfx3DMatrix aTransform,
   149                                              AsyncPanZoomController* aParent,
   150                                              AsyncPanZoomController* aNextSibling,
   151                                              bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
   152                                              nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
   153 {
   154   mTreeLock.AssertCurrentThreadOwns();
   156   ContainerLayer* container = aLayer->AsContainerLayer();
   157   AsyncPanZoomController* apzc = nullptr;
   158   mApzcTreeLog << aLayer->Name() << '\t';
   159   if (container) {
   160     const FrameMetrics& metrics = container->GetFrameMetrics();
   161     if (metrics.IsScrollable()) {
   162       const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
   163       if (state && state->mController.get()) {
   164         // If we get here, aLayer is a scrollable container layer and somebody
   165         // has registered a GeckoContentController for it, so we need to ensure
   166         // it has an APZC instance to manage its scrolling.
   168         apzc = container->GetAsyncPanZoomController();
   170         // If the content represented by the container layer has changed (which may
   171         // be possible because of DLBI heuristics) then we don't want to keep using
   172         // the same old APZC for the new content. Null it out so we run through the
   173         // code to find another one or create one.
   174         ScrollableLayerGuid guid(aLayersId, metrics);
   175         if (apzc && !apzc->Matches(guid)) {
   176           apzc = nullptr;
   177         }
   179         // If the container doesn't have an APZC already, try to find one of our
   180         // pre-existing ones that matches. In particular, if we find an APZC whose
   181         // ScrollableLayerGuid is the same, then we know what happened is that the
   182         // layout of the page changed causing the layer tree to be rebuilt, but the
   183         // underlying content for which the APZC was originally created is still
   184         // there. So it makes sense to pick up that APZC instance again and use it here.
   185         if (apzc == nullptr) {
   186           for (size_t i = 0; i < aApzcsToDestroy->Length(); i++) {
   187             if (aApzcsToDestroy->ElementAt(i)->Matches(guid)) {
   188               apzc = aApzcsToDestroy->ElementAt(i);
   189               break;
   190             }
   191           }
   192         }
   194         // The APZC we get off the layer may have been destroyed previously if the layer was inactive
   195         // or omitted from the layer tree for whatever reason from a layers update. If it later comes
   196         // back it will have a reference to a destroyed APZC and so we need to throw that out and make
   197         // a new one.
   198         bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
   199         if (newApzc) {
   200           apzc = new AsyncPanZoomController(aLayersId, this, state->mController,
   201                                             AsyncPanZoomController::USE_GESTURE_DETECTOR);
   202           apzc->SetCompositorParent(aCompositor);
   203           apzc->SetCrossProcessCompositorParent(state->mCrossProcessParent);
   204         } else {
   205           // If there was already an APZC for the layer clear the tree pointers
   206           // so that it doesn't continue pointing to APZCs that should no longer
   207           // be in the tree. These pointers will get reset properly as we continue
   208           // building the tree. Also remove it from the set of APZCs that are going
   209           // to be destroyed, because it's going to remain active.
   210           aApzcsToDestroy->RemoveElement(apzc);
   211           apzc->SetPrevSibling(nullptr);
   212           apzc->SetLastChild(nullptr);
   213         }
   214         APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer, aLayersId, container->GetFrameMetrics().GetScrollId());
   216         apzc->NotifyLayersUpdated(metrics,
   217                                   aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
   218         apzc->SetScrollHandoffParentId(container->GetScrollHandoffParentId());
   220         // Use the composition bounds as the hit test region.
   221         // Optionally, the GeckoContentController can provide a touch-sensitive
   222         // region that constrains all frames associated with the controller.
   223         // In this case we intersect the composition bounds with that region.
   224         ParentLayerRect visible(metrics.mCompositionBounds);
   225         CSSRect touchSensitiveRegion;
   226         if (state->mController->GetTouchSensitiveRegion(&touchSensitiveRegion)) {
   227           // Note: we assume here that touchSensitiveRegion is in the CSS pixels
   228           // of our parent layer, which makes this coordinate conversion
   229           // correct.
   230           visible = visible.Intersect(touchSensitiveRegion
   231                                       * metrics.mDevPixelsPerCSSPixel
   232                                       * metrics.GetParentResolution());
   233         }
   234         gfx3DMatrix transform;
   235         gfx::To3DMatrix(aLayer->GetTransform(), transform);
   237         apzc->SetLayerHitTestData(visible, aTransform, transform);
   238         APZC_LOG("Setting rect(%f %f %f %f) as visible region for APZC %p\n", visible.x, visible.y,
   239                                                                               visible.width, visible.height,
   240                                                                               apzc);
   242         mApzcTreeLog << "APZC " << guid
   243                      << "\tcb=" << visible
   244                      << "\tsr=" << container->GetFrameMetrics().mScrollableRect
   245                      << (aLayer->GetVisibleRegion().IsEmpty() ? "\tscrollinfo" : "")
   246                      << "\t" << container->GetFrameMetrics().GetContentDescription();
   248         // Bind the APZC instance into the tree of APZCs
   249         if (aNextSibling) {
   250           aNextSibling->SetPrevSibling(apzc);
   251         } else if (aParent) {
   252           aParent->SetLastChild(apzc);
   253         } else {
   254           mRootApzc = apzc;
   255         }
   257         // Let this apzc be the parent of other controllers when we recurse downwards
   258         aParent = apzc;
   260         if (newApzc) {
   261           if (apzc->IsRootForLayersId()) {
   262             // If we just created a new apzc that is the root for its layers ID, then
   263             // we need to update its zoom constraints which might have arrived before this
   264             // was created
   265             ZoomConstraints constraints;
   266             if (state->mController->GetRootZoomConstraints(&constraints)) {
   267               apzc->UpdateZoomConstraints(constraints);
   268             }
   269           } else {
   270             // For an apzc that is not the root for its layers ID, we give it the
   271             // same zoom constraints as its parent. This ensures that if e.g.
   272             // user-scalable=no was specified, none of the APZCs allow double-tap
   273             // to zoom.
   274             apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
   275           }
   276         }
   277       }
   278     }
   280     container->SetAsyncPanZoomController(apzc);
   281   }
   282   mApzcTreeLog << '\n';
   284   // Accumulate the CSS transform between layers that have an APZC, but exclude any
   285   // any layers that do have an APZC, and reset the accumulation at those layers.
   286   if (apzc) {
   287     aTransform = gfx3DMatrix();
   288   } else {
   289     // Multiply child layer transforms on the left so they get applied first
   290     gfx3DMatrix matrix;
   291     gfx::To3DMatrix(aLayer->GetTransform(), matrix);
   292     aTransform = matrix * aTransform;
   293   }
   295   uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId);
   296   // If there's no APZC at this level, any APZCs for our child layers will
   297   // have our siblings as siblings.
   298   AsyncPanZoomController* next = apzc ? nullptr : aNextSibling;
   299   for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
   300     gfx::TreeAutoIndent indent(mApzcTreeLog);
   301     next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aTransform, aParent, next,
   302                                        aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy);
   303   }
   305   // Return the APZC that should be the sibling of other APZCs as we continue
   306   // moving towards the first child at this depth in the layer tree.
   307   // If this layer doesn't have an APZC, we promote any APZCs in the subtree
   308   // upwards. Otherwise we fall back to the aNextSibling that was passed in.
   309   if (apzc) {
   310     return apzc;
   311   }
   312   if (next) {
   313     return next;
   314   }
   315   return aNextSibling;
   316 }
   318 /*static*/ template<class T> void
   319 ApplyTransform(gfx::PointTyped<T>* aPoint, const gfx3DMatrix& aMatrix)
   320 {
   321   gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   322   aPoint->x = result.x;
   323   aPoint->y = result.y;
   324 }
   326 /*static*/ template<class T> void
   327 ApplyTransform(gfx::IntPointTyped<T>* aPoint, const gfx3DMatrix& aMatrix)
   328 {
   329   gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   330   aPoint->x = NS_lround(result.x);
   331   aPoint->y = NS_lround(result.y);
   332 }
   334 /*static*/ void
   335 ApplyTransform(nsIntPoint* aPoint, const gfx3DMatrix& aMatrix)
   336 {
   337   gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   338   aPoint->x = NS_lround(result.x);
   339   aPoint->y = NS_lround(result.y);
   340 }
   342 nsEventStatus
   343 APZCTreeManager::ReceiveInputEvent(const InputData& aEvent,
   344                                    ScrollableLayerGuid* aOutTargetGuid)
   345 {
   346   nsEventStatus result = nsEventStatus_eIgnore;
   347   gfx3DMatrix transformToApzc;
   348   gfx3DMatrix transformToGecko;
   349   switch (aEvent.mInputType) {
   350     case MULTITOUCH_INPUT: {
   351       const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   352       if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
   353         // MULTITOUCH_START input contains all active touches of the current
   354         // session thus resetting mTouchCount.
   355         mTouchCount = multiTouchInput.mTouches.Length();
   356         mApzcForInputBlock = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
   357         if (multiTouchInput.mTouches.Length() == 1) {
   358           // If we have one touch point, this might be the start of a pan.
   359           // Prepare for possible overscroll handoff.
   360           BuildOverscrollHandoffChain(mApzcForInputBlock);
   361         }
   362         for (size_t i = 1; i < multiTouchInput.mTouches.Length(); i++) {
   363           nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[i].mScreenPoint));
   364           mApzcForInputBlock = CommonAncestor(mApzcForInputBlock.get(), apzc2.get());
   365           APZC_LOG("Using APZC %p as the common ancestor\n", mApzcForInputBlock.get());
   366           // For now, we only ever want to do pinching on the root APZC for a given layers id. So
   367           // when we find the common ancestor of multiple points, also walk up to the root APZC.
   368           mApzcForInputBlock = RootAPZCForLayersId(mApzcForInputBlock);
   369           APZC_LOG("Using APZC %p as the root APZC for multi-touch\n", mApzcForInputBlock.get());
   370         }
   372         if (mApzcForInputBlock) {
   373           // Cache transformToApzc so it can be used for future events in this block.
   374           GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
   375           mCachedTransformToApzcForInputBlock = transformToApzc;
   376         } else {
   377           // Reset the cached apz transform
   378           mCachedTransformToApzcForInputBlock = gfx3DMatrix();
   379         }
   380       } else if (mApzcForInputBlock) {
   381         APZC_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
   382       }
   383       if (mApzcForInputBlock) {
   384         mApzcForInputBlock->GetGuid(aOutTargetGuid);
   385         // Use the cached transform to compute the point to send to the APZC.
   386         // This ensures that the sequence of touch points an APZC sees in an
   387         // input block are all in the same coordinate space.
   388         transformToApzc = mCachedTransformToApzcForInputBlock;
   389         MultiTouchInput inputForApzc(multiTouchInput);
   390         for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
   391           ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
   392         }
   393         result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
   394       }
   395       if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
   396           multiTouchInput.mType == MultiTouchInput::MULTITOUCH_END) {
   397         if (mTouchCount >= multiTouchInput.mTouches.Length()) {
   398           // MULTITOUCH_END input contains only released touches thus decrementing.
   399           mTouchCount -= multiTouchInput.mTouches.Length();
   400         } else {
   401           NS_WARNING("Got an unexpected touchend/touchcancel");
   402           mTouchCount = 0;
   403         }
   404         // If we have an mApzcForInputBlock and it's the end of the touch sequence
   405         // then null it out so we don't keep a dangling reference and leak things.
   406         if (mTouchCount == 0) {
   407           mApzcForInputBlock = nullptr;
   408           ClearOverscrollHandoffChain();
   409         }
   410       }
   411       break;
   412     } case PINCHGESTURE_INPUT: {
   413       const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
   414       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint);
   415       if (apzc) {
   416         apzc->GetGuid(aOutTargetGuid);
   417         GetInputTransforms(apzc, transformToApzc, transformToGecko);
   418         PinchGestureInput inputForApzc(pinchInput);
   419         ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
   420         result = apzc->ReceiveInputEvent(inputForApzc);
   421       }
   422       break;
   423     } case TAPGESTURE_INPUT: {
   424       const TapGestureInput& tapInput = aEvent.AsTapGestureInput();
   425       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint));
   426       if (apzc) {
   427         apzc->GetGuid(aOutTargetGuid);
   428         GetInputTransforms(apzc, transformToApzc, transformToGecko);
   429         TapGestureInput inputForApzc(tapInput);
   430         ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
   431         result = apzc->ReceiveInputEvent(inputForApzc);
   432       }
   433       break;
   434     }
   435   }
   436   return result;
   437 }
   439 already_AddRefed<AsyncPanZoomController>
   440 APZCTreeManager::GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent)
   441 {
   442   ScreenPoint point = ScreenPoint(aEvent.touches[0]->mRefPoint.x, aEvent.touches[0]->mRefPoint.y);
   443   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(point);
   444   if (aEvent.touches.Length() == 1) {
   445     // If we have one touch point, this might be the start of a pan.
   446     // Prepare for possible overscroll handoff.
   447     BuildOverscrollHandoffChain(apzc);
   448   }
   449   for (size_t i = 1; i < aEvent.touches.Length(); i++) {
   450     point = ScreenPoint(aEvent.touches[i]->mRefPoint.x, aEvent.touches[i]->mRefPoint.y);
   451     nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(point);
   452     apzc = CommonAncestor(apzc.get(), apzc2.get());
   453     APZC_LOG("Using APZC %p as the common ancestor\n", apzc.get());
   454     // For now, we only ever want to do pinching on the root APZC for a given layers id. So
   455     // when we find the common ancestor of multiple points, also walk up to the root APZC.
   456     apzc = RootAPZCForLayersId(apzc);
   457     APZC_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
   458   }
   459   return apzc.forget();
   460 }
   462 nsEventStatus
   463 APZCTreeManager::ProcessTouchEvent(WidgetTouchEvent& aEvent,
   464                                    ScrollableLayerGuid* aOutTargetGuid)
   465 {
   466   MOZ_ASSERT(NS_IsMainThread());
   468   nsEventStatus ret = nsEventStatus_eIgnore;
   469   if (!aEvent.touches.Length()) {
   470     return ret;
   471   }
   472   if (aEvent.message == NS_TOUCH_START) {
   473     // NS_TOUCH_START event contains all active touches of the current
   474     // session thus resetting mTouchCount.
   475     mTouchCount = aEvent.touches.Length();
   476     mApzcForInputBlock = GetTouchInputBlockAPZC(aEvent);
   477     if (mApzcForInputBlock) {
   478       // Cache apz transform so it can be used for future events in this block.
   479       gfx3DMatrix transformToGecko;
   480       GetInputTransforms(mApzcForInputBlock, mCachedTransformToApzcForInputBlock, transformToGecko);
   481     } else {
   482       // Reset the cached apz transform
   483       mCachedTransformToApzcForInputBlock = gfx3DMatrix();
   484     }
   485   }
   487   if (mApzcForInputBlock) {
   488     mApzcForInputBlock->GetGuid(aOutTargetGuid);
   489     // For computing the input for the APZC, used the cached transform.
   490     // This ensures that the sequence of touch points an APZC sees in an
   491     // input block are all in the same coordinate space.
   492     gfx3DMatrix transformToApzc = mCachedTransformToApzcForInputBlock;
   493     MultiTouchInput inputForApzc(aEvent);
   494     for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
   495       ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
   496     }
   497     ret = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
   499     // For computing the event to pass back to Gecko, use the up-to-date transforms.
   500     // This ensures that transformToApzc and transformToGecko are in sync
   501     // (note that transformToGecko isn't cached).
   502     gfx3DMatrix transformToGecko;
   503     GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
   504     gfx3DMatrix outTransform = transformToApzc * transformToGecko;
   505     for (size_t i = 0; i < aEvent.touches.Length(); i++) {
   506       ApplyTransform(&(aEvent.touches[i]->mRefPoint), outTransform);
   507     }
   508   }
   509   // If we have an mApzcForInputBlock and it's the end of the touch sequence
   510   // then null it out so we don't keep a dangling reference and leak things.
   511   if (aEvent.message == NS_TOUCH_CANCEL ||
   512       aEvent.message == NS_TOUCH_END) {
   513     if (mTouchCount >= aEvent.touches.Length()) {
   514       // NS_TOUCH_END event contains only released touches thus decrementing.
   515       mTouchCount -= aEvent.touches.Length();
   516     } else {
   517       NS_WARNING("Got an unexpected touchend/touchcancel");
   518       mTouchCount = 0;
   519     }
   520     if (mTouchCount == 0) {
   521       mApzcForInputBlock = nullptr;
   522       ClearOverscrollHandoffChain();
   523     }
   524   }
   525   return ret;
   526 }
   528 void
   529 APZCTreeManager::TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
   530                                             LayoutDeviceIntPoint* aOutTransformedPoint)
   531 {
   532   MOZ_ASSERT(aOutTransformedPoint);
   533   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aPoint);
   534   if (apzc && aOutTransformedPoint) {
   535     gfx3DMatrix transformToApzc;
   536     gfx3DMatrix transformToGecko;
   537     GetInputTransforms(apzc, transformToApzc, transformToGecko);
   538     gfx3DMatrix outTransform = transformToApzc * transformToGecko;
   539     aOutTransformedPoint->x = aPoint.x;
   540     aOutTransformedPoint->y = aPoint.y;
   541     ApplyTransform(aOutTransformedPoint, outTransform);
   542   }
   543 }
   545 nsEventStatus
   546 APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
   547                               ScrollableLayerGuid* aOutTargetGuid)
   548 {
   549   MOZ_ASSERT(NS_IsMainThread());
   551   // Transform the refPoint
   552   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(aEvent.refPoint.x, aEvent.refPoint.y));
   553   if (!apzc) {
   554     return nsEventStatus_eIgnore;
   555   }
   556   apzc->GetGuid(aOutTargetGuid);
   557   gfx3DMatrix transformToApzc;
   558   gfx3DMatrix transformToGecko;
   559   GetInputTransforms(apzc, transformToApzc, transformToGecko);
   560   gfx3DMatrix outTransform = transformToApzc * transformToGecko;
   561   ApplyTransform(&(aEvent.refPoint), outTransform);
   562   return nsEventStatus_eIgnore;
   563 }
   565 nsEventStatus
   566 APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
   567                                    ScrollableLayerGuid* aOutTargetGuid)
   568 {
   569   MOZ_ASSERT(NS_IsMainThread());
   571   switch (aEvent.eventStructType) {
   572     case NS_TOUCH_EVENT: {
   573       WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
   574       return ProcessTouchEvent(touchEvent, aOutTargetGuid);
   575     }
   576     default: {
   577       return ProcessEvent(aEvent, aOutTargetGuid);
   578     }
   579   }
   580 }
   582 void
   583 APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
   584                             const CSSRect& aRect)
   585 {
   586   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   587   if (apzc) {
   588     apzc->ZoomToRect(aRect);
   589   }
   590 }
   592 void
   593 APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
   594                                       bool aPreventDefault)
   595 {
   596   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   597   if (apzc) {
   598     apzc->ContentReceivedTouch(aPreventDefault);
   599   }
   600 }
   602 void
   603 APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
   604                                        const ZoomConstraints& aConstraints)
   605 {
   606   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   607   // For a given layers id, non-root APZCs inherit the zoom constraints
   608   // of their root.
   609   if (apzc && apzc->IsRootForLayersId()) {
   610     MonitorAutoLock lock(mTreeLock);
   611     UpdateZoomConstraintsRecursively(apzc.get(), aConstraints);
   612   }
   613 }
   615 void
   616 APZCTreeManager::UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
   617                                                   const ZoomConstraints& aConstraints)
   618 {
   619   mTreeLock.AssertCurrentThreadOwns();
   621   aApzc->UpdateZoomConstraints(aConstraints);
   622   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
   623     // We can have subtrees with their own layers id - leave those alone.
   624     if (!child->IsRootForLayersId()) {
   625       UpdateZoomConstraintsRecursively(child, aConstraints);
   626     }
   627   }
   628 }
   630 void
   631 APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
   632 {
   633   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   634   if (apzc) {
   635     apzc->CancelAnimation();
   636   }
   637 }
   639 void
   640 APZCTreeManager::ClearTree()
   641 {
   642   MonitorAutoLock lock(mTreeLock);
   644   // This can be done as part of a tree walk but it's easier to
   645   // just re-use the Collect method that we need in other places.
   646   // If this is too slow feel free to change it to a recursive walk.
   647   nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
   648   Collect(mRootApzc, &apzcsToDestroy);
   649   for (size_t i = 0; i < apzcsToDestroy.Length(); i++) {
   650     apzcsToDestroy[i]->Destroy();
   651   }
   652   mRootApzc = nullptr;
   653 }
   655 /**
   656  * Transform a displacement from the screen coordinates of a source APZC to
   657  * the screen coordinates of a target APZC.
   658  * @param aTreeManager the tree manager for the APZC tree containing |aSource|
   659  *                     and |aTarget|
   660  * @param aSource the source APZC
   661  * @param aTarget the target APZC
   662  * @param aStartPoint the start point of the displacement
   663  * @param aEndPoint the end point of the displacement
   664  */
   665 static void
   666 TransformDisplacement(APZCTreeManager* aTreeManager,
   667                       AsyncPanZoomController* aSource,
   668                       AsyncPanZoomController* aTarget,
   669                       ScreenPoint& aStartPoint,
   670                       ScreenPoint& aEndPoint) {
   671   gfx3DMatrix transformToApzc;
   672   gfx3DMatrix transformToGecko;  // ignored
   674   // Convert start and end points to untransformed screen coordinates.
   675   aTreeManager->GetInputTransforms(aSource, transformToApzc, transformToGecko);
   676   ApplyTransform(&aStartPoint, transformToApzc.Inverse());
   677   ApplyTransform(&aEndPoint, transformToApzc.Inverse());
   679   // Convert start and end points to aTarget's transformed screen coordinates.
   680   aTreeManager->GetInputTransforms(aTarget, transformToApzc, transformToGecko);
   681   ApplyTransform(&aStartPoint, transformToApzc);
   682   ApplyTransform(&aEndPoint, transformToApzc);
   683 }
   685 void
   686 APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
   687                                 uint32_t aOverscrollHandoffChainIndex)
   688 {
   689   nsRefPtr<AsyncPanZoomController> next;
   690   {
   691     // Grab tree lock to protect mOverscrollHandoffChain from concurrent
   692     // access from the input and compositor threads.
   693     // Release it before calling TransformDisplacement() as that grabs the
   694     // lock itself.
   695     MonitorAutoLock lock(mTreeLock);
   697     // If we have reached the end of the overscroll handoff chain, there is
   698     // nothing more to scroll, so we ignore the rest of the pan gesture.
   699     if (aOverscrollHandoffChainIndex >= mOverscrollHandoffChain.length()) {
   700       // Nothing more to scroll - ignore the rest of the pan gesture.
   701       return;
   702     }
   704     next = mOverscrollHandoffChain[aOverscrollHandoffChainIndex];
   705   }
   707   if (next == nullptr)
   708     return;
   710   // Convert the start and end points from |aPrev|'s coordinate space to
   711   // |next|'s coordinate space. Since |aPrev| may be the same as |next|
   712   // (if |aPrev| is the APZC that is initiating the scroll and there is no
   713   // scroll grabbing to grab the scroll from it), don't bother doing the
   714   // transformations in that case.
   715   if (next != aPrev) {
   716     TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint);
   717   }
   719   // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
   720   // again with an incremented index.
   721   next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChainIndex);
   722 }
   724 void
   725 APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVelocity)
   726 {
   727   // Build the overscroll handoff chain. This is necessary because it is
   728   // otherwise built on touch-start and cleared on touch-end, and a fling
   729   // happens after touch-end. Note that, unlike DispatchScroll() which is
   730   // called on every touch-move during overscroll panning,
   731   // HandleFlingOverscroll() is only called once during a fling handoff,
   732   // so it's not worth trying to avoid building the handoff chain here.
   733   BuildOverscrollHandoffChain(aPrev);
   735   nsRefPtr<AsyncPanZoomController> next;  // will be used outside monitor block
   736   {
   737     // Grab tree lock to protect mOverscrollHandoffChain from concurrent
   738     // access from the input and compositor threads.
   739     // Release it before calling GetInputTransforms() as that grabs the
   740     // lock itself.
   741     MonitorAutoLock lock(mTreeLock);
   743     // Find |aPrev| in the handoff chain.
   744     uint32_t i;
   745     for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
   746       if (mOverscrollHandoffChain[i] == aPrev) {
   747         break;
   748       }
   749     }
   751     // Get the next APZC in the handoff chain, if any.
   752     if (i + 1 < mOverscrollHandoffChain.length()) {
   753       next = mOverscrollHandoffChain[i + 1];
   754     }
   756     // Clear the handoff chain so we don't maintain references to APZCs
   757     // unnecessarily.
   758     mOverscrollHandoffChain.clear();
   759   }
   761   // Nothing to hand off fling to.
   762   if (next == nullptr) {
   763     return;
   764   }
   766   // The fling's velocity needs to be transformed from the screen coordinates
   767   // of |aPrev| to the screen coordinates of |next|. To transform a velocity
   768   // correctly, we need to convert it to a displacement. For now, we do this
   769   // by anchoring it to a start point of (0, 0).
   770   // TODO: For this to be correct in the presence of 3D transforms, we should
   771   // use the end point of the touch that started the fling as the start point
   772   // rather than (0, 0).
   773   ScreenPoint startPoint;  // (0, 0)
   774   ScreenPoint endPoint = startPoint + aVelocity;
   775   TransformDisplacement(this, aPrev, next, startPoint, endPoint);
   776   ScreenPoint transformedVelocity = endPoint - startPoint;
   778   // Tell |next| to start a fling with the transformed velocity.
   779   next->TakeOverFling(transformedVelocity);
   780 }
   782 bool
   783 APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
   784 {
   785   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
   786   if (mOverscrollHandoffChain.length() == 0) {
   787     return false;
   788   }
   789   for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
   790     nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
   791     if (item) {
   792       item->FlushRepaintForOverscrollHandoff();
   793     }
   794   }
   795   return true;
   796 }
   798 bool
   799 APZCTreeManager::CanBePanned(AsyncPanZoomController* aApzc)
   800 {
   801   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
   803   // Find |aApzc| in the handoff chain.
   804   uint32_t i;
   805   for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
   806     if (mOverscrollHandoffChain[i] == aApzc) {
   807       break;
   808     }
   809   }
   811   // See whether any APZC in the handoff chain starting from |aApzc|
   812   // has room to be panned.
   813   for (uint32_t j = i; j < mOverscrollHandoffChain.length(); ++j) {
   814     if (mOverscrollHandoffChain[j]->IsPannable()) {
   815       return true;
   816     }
   817   }
   819   return false;
   820 }
   822 bool
   823 APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
   824 {
   825   MonitorAutoLock lock(mTreeLock);
   826   nsRefPtr<AsyncPanZoomController> target;
   827   // The root may have siblings, so check those too
   828   gfxPoint point(aPoint.x, aPoint.y);
   829   for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
   830     target = GetAPZCAtPoint(apzc, point);
   831     if (target) {
   832       return true;
   833     }
   834   }
   835   return false;
   836 }
   838 already_AddRefed<AsyncPanZoomController>
   839 APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
   840 {
   841   MonitorAutoLock lock(mTreeLock);
   842   nsRefPtr<AsyncPanZoomController> target;
   843   // The root may have siblings, check those too
   844   for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
   845     target = FindTargetAPZC(apzc, aGuid);
   846     if (target) {
   847       break;
   848     }
   849   }
   850   return target.forget();
   851 }
   853 struct CompareByScrollPriority
   854 {
   855   bool operator()(const nsRefPtr<AsyncPanZoomController>& a, const nsRefPtr<AsyncPanZoomController>& b) {
   856     return a->HasScrollgrab() && !b->HasScrollgrab();
   857   }
   858 };
   860 already_AddRefed<AsyncPanZoomController>
   861 APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
   862 {
   863   MonitorAutoLock lock(mTreeLock);
   864   nsRefPtr<AsyncPanZoomController> target;
   865   // The root may have siblings, so check those too
   866   gfxPoint point(aPoint.x, aPoint.y);
   867   for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
   868     target = GetAPZCAtPoint(apzc, point);
   869     if (target) {
   870       break;
   871     }
   872   }
   873   return target.forget();
   874 }
   876 void
   877 APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget)
   878 {
   879   // Scroll grabbing is a mechanism that allows content to specify that
   880   // the initial target of a pan should be not the innermost scrollable
   881   // frame at the touch point (which is what GetTargetAPZC finds), but
   882   // something higher up in the tree.
   883   // It's not sufficient to just find the initial target, however, as
   884   // overscroll can be handed off to another APZC. Without scroll grabbing,
   885   // handoff just occurs from child to parent. With scroll grabbing, the
   886   // handoff order can be different, so we build a chain of APZCs in the
   887   // order in which scroll will be handed off to them.
   889   // Grab tree lock to protect mOverscrollHandoffChain from concurrent
   890   // access between the input and compositor threads.
   891   MonitorAutoLock lock(mTreeLock);
   893   mOverscrollHandoffChain.clear();
   895   // Build the chain. If there is a scroll parent link, we use that. This is
   896   // needed to deal with scroll info layers, because they participate in handoff
   897   // but do not follow the expected layer tree structure. If there are no
   898   // scroll parent links we just walk up the tree to find the scroll parent.
   899   AsyncPanZoomController* apzc = aInitialTarget;
   900   while (apzc != nullptr) {
   901     if (!mOverscrollHandoffChain.append(apzc)) {
   902       NS_WARNING("Vector::append failed");
   903       mOverscrollHandoffChain.clear();
   904       return;
   905     }
   906     if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) {
   907       if (!apzc->IsRootForLayersId()) {
   908         // This probably indicates a bug or missed case in layout code
   909         NS_WARNING("Found a non-root APZ with no handoff parent");
   910       }
   911       apzc = apzc->GetParent();
   912       continue;
   913     }
   915     // Find the AsyncPanZoomController instance with a matching layersId and
   916     // the scroll id that matches apzc->GetScrollHandoffParentId(). To do this
   917     // search the subtree with the same layersId for the apzc with the specified
   918     // scroll id.
   919     AsyncPanZoomController* scrollParent = nullptr;
   920     AsyncPanZoomController* parent = apzc;
   921     while (!parent->IsRootForLayersId()) {
   922       parent = parent->GetParent();
   923       // While walking up to find the root of the subtree, if we encounter the
   924       // handoff parent, we don't actually need to do the search so we can
   925       // just abort here.
   926       if (parent->GetGuid().mScrollId == apzc->GetScrollHandoffParentId()) {
   927         scrollParent = parent;
   928         break;
   929       }
   930     }
   931     if (!scrollParent) {
   932       scrollParent = FindTargetAPZC(parent, apzc->GetScrollHandoffParentId());
   933     }
   934     apzc = scrollParent;
   935   }
   937   // Now adjust the chain to account for scroll grabbing. Sorting is a bit
   938   // of an overkill here, but scroll grabbing will likely be generalized
   939   // to scroll priorities, so we might as well do it this way.
   940   // The sorting being stable ensures that the relative order between
   941   // non-scrollgrabbing APZCs remains child -> parent.
   942   // (The relative order between scrollgrabbing APZCs will also remain
   943   // child -> parent, though that's just an artefact of the implementation
   944   // and users of 'scrollgrab' should not rely on this.)
   945   std::stable_sort(mOverscrollHandoffChain.begin(), mOverscrollHandoffChain.end(),
   946                    CompareByScrollPriority());
   947 }
   949 /* Find the apzc in the subtree rooted at aApzc that has the same layers id as
   950    aApzc, and that has the given scroll id. Generally this function should be called
   951    with aApzc being the root of its layers id subtree. */
   952 AsyncPanZoomController*
   953 APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId)
   954 {
   955   mTreeLock.AssertCurrentThreadOwns();
   957   if (aApzc->GetGuid().mScrollId == aScrollId) {
   958     return aApzc;
   959   }
   960   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
   961     if (child->GetGuid().mLayersId != aApzc->GetGuid().mLayersId) {
   962       continue;
   963     }
   964     AsyncPanZoomController* match = FindTargetAPZC(child, aScrollId);
   965     if (match) {
   966       return match;
   967     }
   968   }
   970   return nullptr;
   971 }
   973 void
   974 APZCTreeManager::ClearOverscrollHandoffChain()
   975 {
   976   MonitorAutoLock lock(mTreeLock);
   977   mOverscrollHandoffChain.clear();
   978 }
   980 AsyncPanZoomController*
   981 APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid)
   982 {
   983   mTreeLock.AssertCurrentThreadOwns();
   985   // This walks the tree in depth-first, reverse order, so that it encounters
   986   // APZCs front-to-back on the screen.
   987   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
   988     AsyncPanZoomController* match = FindTargetAPZC(child, aGuid);
   989     if (match) {
   990       return match;
   991     }
   992   }
   994   if (aApzc->Matches(aGuid)) {
   995     return aApzc;
   996   }
   997   return nullptr;
   998 }
  1000 AsyncPanZoomController*
  1001 APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint)
  1003   mTreeLock.AssertCurrentThreadOwns();
  1005   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
  1006   // explained in the comment on GetInputTransforms. This function will recurse with aApzc at L and P, and the
  1007   // comments explain what values are stored in the variables at these two levels. All the comments
  1008   // use standard matrix notation where the leftmost matrix in a multiplication is applied first.
  1010   // ancestorUntransform takes points from aApzc's parent APZC's layer coordinates
  1011   // to aApzc's parent layer's layer coordinates.
  1012   // It is OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
  1013   //   and RC.Inverse() * QC.Inverse()                at recursion level for P.
  1014   gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
  1016   // Hit testing for this layer takes place in our parent layer coordinates,
  1017   // since the composition bounds (used to initialize the visible rect against
  1018   // which we hit test are in those coordinates).
  1019   gfxPoint hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
  1020   APZC_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
  1021            aHitTestPoint.x, aHitTestPoint.y,
  1022            hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
  1024   // childUntransform takes points from aApzc's parent APZC's layer coordinates
  1025   // to aApzc's layer coordinates (which are aApzc's children's ParentLayer coordinates).
  1026   // It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LA.Inverse() at L
  1027   //   and RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()                at P.
  1028   gfx3DMatrix childUntransform = ancestorUntransform
  1029                                * aApzc->GetCSSTransform().Inverse()
  1030                                * gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
  1031   gfxPoint hitTestPointForChildLayers = childUntransform.ProjectPoint(aHitTestPoint);
  1032   APZC_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
  1033            aHitTestPoint.x, aHitTestPoint.y,
  1034            hitTestPointForChildLayers.x, hitTestPointForChildLayers.y, aApzc);
  1036   // This walks the tree in depth-first, reverse order, so that it encounters
  1037   // APZCs front-to-back on the screen.
  1038   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
  1039     AsyncPanZoomController* match = GetAPZCAtPoint(child, hitTestPointForChildLayers);
  1040     if (match) {
  1041       return match;
  1044   if (aApzc->VisibleRegionContains(ViewAs<ParentLayerPixel>(hitTestPointForThisLayer))) {
  1045     APZC_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p\n",
  1046              hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
  1047     return aApzc;
  1049   return nullptr;
  1052 /* This function sets the aTransformToApzcOut and aTransformToGeckoOut out-parameters
  1053    to some useful transformations that input events may need applied. This is best
  1054    illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
  1055    is the layer that corresponds to the argument |aApzc|, and layer R is the root
  1056    of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
  1057    When layer L is displayed to the screen by the compositor, the set of transforms that
  1058    are applied to L are (in order from top to bottom):
  1060         L's transient async transform       (hereafter referred to as transform matrix LT)
  1061         L's nontransient async transform    (hereafter referred to as transform matrix LN)
  1062         L's CSS transform                   (hereafter referred to as transform matrix LC)
  1063         M's transient async transform       (hereafter referred to as transform matrix MT)
  1064         M's nontransient async transform    (hereafter referred to as transform matrix MN)
  1065         M's CSS transform                   (hereafter referred to as transform matrix MC)
  1066         ...
  1067         R's transient async transform       (hereafter referred to as transform matrix RT)
  1068         R's nontransient async transform    (hereafter referred to as transform matrix RN)
  1069         R's CSS transform                   (hereafter referred to as transform matrix RC)
  1071    Also, for any layer, the async transform is the combination of its transient and non-transient
  1072    parts. That is, for any layer L:
  1073                   LA === LT * LN
  1074         LA.Inverse() === LN.Inverse() * LT.Inverse()
  1076    If we want user input to modify L's transient async transform, we have to first convert
  1077    user input from screen space to the coordinate space of L's transient async transform. Doing
  1078    this involves applying the following transforms (in order from top to bottom):
  1079         RC.Inverse()
  1080         RN.Inverse()
  1081         RT.Inverse()
  1082         ...
  1083         MC.Inverse()
  1084         MN.Inverse()
  1085         MT.Inverse()
  1086         LC.Inverse()
  1087         LN.Inverse()
  1088    This combined transformation is returned in the aTransformToApzcOut out-parameter.
  1090    Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
  1091    out all of the async transforms that are involved in this chain. This is because async
  1092    transforms are stored only in the compositor and gecko does not account for them when
  1093    doing display-list-based hit-testing for event dispatching.
  1094    Furthermore, because these input events are processed by Gecko in a FIFO queue that
  1095    includes other things (specifically paint requests), it is possible that by time the
  1096    input event reaches gecko, it will have painted something else. Therefore, we need to
  1097    apply another transform to the input events to account for the possible disparity between
  1098    what we know gecko last painted and the last paint request we sent to gecko. Let this
  1099    transform be represented by LD, MD, ... RD.
  1100    Therefore, given a user input in screen space, the following transforms need to be applied
  1101    (in order from top to bottom):
  1102         RC.Inverse()
  1103         RN.Inverse()
  1104         RT.Inverse()
  1105         ...
  1106         MC.Inverse()
  1107         MN.Inverse()
  1108         MT.Inverse()
  1109         LC.Inverse()
  1110         LN.Inverse()
  1111         LT.Inverse()
  1112         LD
  1113         LC
  1114         MD
  1115         MC
  1116         ...
  1117         RD
  1118         RC
  1119    This sequence can be simplified and refactored to the following:
  1120         aTransformToApzcOut
  1121         LT.Inverse()
  1122         LD
  1123         LC
  1124         MD
  1125         MC
  1126         ...
  1127         RD
  1128         RC
  1129    Since aTransformToApzcOut is already one of the out-parameters, we set aTransformToGeckoOut
  1130    to the remaining transforms (LT.Inverse() * LD * ... * RC), so that the caller code can
  1131    combine it with aTransformToApzcOut to get the final transform required in this case.
  1133    Note that for many of these layers, there will be no AsyncPanZoomController attached, and
  1134    so the async transform will be the identity transform. So, in the example above, if layers
  1135    L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
  1136    RN and RD will be identity transforms.
  1137    Additionally, for space-saving purposes, each APZC instance stores its layer's individual
  1138    CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
  1139    layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
  1140    The APZC instances track the last dispatched paint request and so are able to calculate LD and
  1141    PD using those internally stored values.
  1142    The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
  1143    required can be generated.
  1144  */
  1145 void
  1146 APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix& aTransformToApzcOut,
  1147                                     gfx3DMatrix& aTransformToGeckoOut)
  1149   MonitorAutoLock lock(mTreeLock);
  1151   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
  1152   // explained in the comment above. This function is called with aApzc at L, and the loop
  1153   // below performs one iteration, where parent is at P. The comments explain what values are stored
  1154   // in the variables at these two levels. All the comments use standard matrix notation where the
  1155   // leftmost matrix in a multiplication is applied first.
  1157   // ancestorUntransform is OC.Inverse() * NC.Inverse() * MC.Inverse()
  1158   gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
  1159   // asyncUntransform is LA.Inverse()
  1160   gfx3DMatrix asyncUntransform = gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
  1161   // nontransientAsyncTransform is LN
  1162   gfx3DMatrix nontransientAsyncTransform = aApzc->GetNontransientAsyncTransform();
  1163   // transientAsyncUntransform is LT.Inverse()
  1164   gfx3DMatrix transientAsyncUntransform = nontransientAsyncTransform * asyncUntransform;
  1166   // aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
  1167   aTransformToApzcOut = ancestorUntransform * aApzc->GetCSSTransform().Inverse() * nontransientAsyncTransform.Inverse();
  1168   // aTransformToGeckoOut is initialized to LT.Inverse() * LD * LC * MC * NC * OC
  1169   aTransformToGeckoOut = transientAsyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
  1171   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
  1172     // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
  1173     ancestorUntransform = parent->GetAncestorTransform().Inverse();
  1174     // asyncUntransform is updated to PA.Inverse() when parent == P
  1175     asyncUntransform = gfx3DMatrix(parent->GetCurrentAsyncTransform()).Inverse();
  1176     // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()
  1177     gfx3DMatrix untransformSinceLastApzc = ancestorUntransform * parent->GetCSSTransform().Inverse() * asyncUntransform;
  1179     // aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
  1180     aTransformToApzcOut = untransformSinceLastApzc * aTransformToApzcOut;
  1181     // aTransformToGeckoOut is LT.Inverse() * LD * LC * MC * NC * OC * PD * PC * QC * RC
  1182     aTransformToGeckoOut = aTransformToGeckoOut * parent->GetTransformToLastDispatchedPaint() * parent->GetCSSTransform() * parent->GetAncestorTransform();
  1184     // The above values for aTransformToApzcOut and aTransformToGeckoOut when parent == P match
  1185     // the required output as explained in the comment above this method. Note that any missing
  1186     // terms are guaranteed to be identity transforms.
  1190 already_AddRefed<AsyncPanZoomController>
  1191 APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2)
  1193   MonitorAutoLock lock(mTreeLock);
  1194   nsRefPtr<AsyncPanZoomController> ancestor;
  1196   // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this function
  1197   // will return null.
  1199   // Calculate depth of the APZCs in the tree
  1200   int depth1 = 0, depth2 = 0;
  1201   for (AsyncPanZoomController* parent = aApzc1; parent; parent = parent->GetParent()) {
  1202     depth1++;
  1204   for (AsyncPanZoomController* parent = aApzc2; parent; parent = parent->GetParent()) {
  1205     depth2++;
  1208   // At most one of the following two loops will be executed; the deeper APZC pointer
  1209   // will get walked up to the depth of the shallower one.
  1210   int minDepth = depth1 < depth2 ? depth1 : depth2;
  1211   while (depth1 > minDepth) {
  1212     depth1--;
  1213     aApzc1 = aApzc1->GetParent();
  1215   while (depth2 > minDepth) {
  1216     depth2--;
  1217     aApzc2 = aApzc2->GetParent();
  1220   // Walk up the ancestor chains of both APZCs, always staying at the same depth for
  1221   // either APZC, and return the the first common ancestor encountered.
  1222   while (true) {
  1223     if (aApzc1 == aApzc2) {
  1224       ancestor = aApzc1;
  1225       break;
  1227     if (depth1 <= 0) {
  1228       break;
  1230     aApzc1 = aApzc1->GetParent();
  1231     aApzc2 = aApzc2->GetParent();
  1233   return ancestor.forget();
  1236 already_AddRefed<AsyncPanZoomController>
  1237 APZCTreeManager::RootAPZCForLayersId(AsyncPanZoomController* aApzc)
  1239   MonitorAutoLock lock(mTreeLock);
  1240   nsRefPtr<AsyncPanZoomController> apzc = aApzc;
  1241   while (apzc && !apzc->IsRootForLayersId()) {
  1242     apzc = apzc->GetParent();
  1244   return apzc.forget();

mercurial