gfx/layers/apz/src/APZCTreeManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/layers/apz/src/APZCTreeManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1248 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "APZCTreeManager.h"
    1.10 +#include "Compositor.h"                 // for Compositor
    1.11 +#include "CompositorParent.h"           // for CompositorParent, etc
    1.12 +#include "InputData.h"                  // for InputData, etc
    1.13 +#include "Layers.h"                     // for ContainerLayer, Layer, etc
    1.14 +#include "gfx3DMatrix.h"                // for gfx3DMatrix
    1.15 +#include "mozilla/dom/Touch.h"          // for Touch
    1.16 +#include "mozilla/gfx/Point.h"          // for Point
    1.17 +#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
    1.18 +#include "mozilla/layers/AsyncPanZoomController.h"
    1.19 +#include "mozilla/MouseEvents.h"
    1.20 +#include "mozilla/mozalloc.h"           // for operator new
    1.21 +#include "mozilla/TouchEvents.h"
    1.22 +#include "mozilla/Preferences.h"        // for Preferences
    1.23 +#include "nsDebug.h"                    // for NS_WARNING
    1.24 +#include "nsPoint.h"                    // for nsIntPoint
    1.25 +#include "nsThreadUtils.h"              // for NS_IsMainThread
    1.26 +#include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
    1.27 +#include "UnitTransforms.h"             // for ViewAs
    1.28 +
    1.29 +#include <algorithm>                    // for std::stable_sort
    1.30 +
    1.31 +#define APZC_LOG(...)
    1.32 +// #define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
    1.33 +
    1.34 +namespace mozilla {
    1.35 +namespace layers {
    1.36 +
    1.37 +float APZCTreeManager::sDPI = 160.0;
    1.38 +
    1.39 +// Pref that enables printing of the APZC tree for debugging.
    1.40 +static bool gPrintApzcTree = false;
    1.41 +
    1.42 +APZCTreeManager::APZCTreeManager()
    1.43 +    : mTreeLock("APZCTreeLock"),
    1.44 +      mTouchCount(0),
    1.45 +      mApzcTreeLog("apzctree")
    1.46 +{
    1.47 +  MOZ_ASSERT(NS_IsMainThread());
    1.48 +  AsyncPanZoomController::InitializeGlobalState();
    1.49 +  Preferences::AddBoolVarCache(&gPrintApzcTree, "apz.printtree", gPrintApzcTree);
    1.50 +  mApzcTreeLog.ConditionOnPref(&gPrintApzcTree);
    1.51 +}
    1.52 +
    1.53 +APZCTreeManager::~APZCTreeManager()
    1.54 +{
    1.55 +}
    1.56 +
    1.57 +void
    1.58 +APZCTreeManager::GetAllowedTouchBehavior(WidgetInputEvent* aEvent,
    1.59 +                                         nsTArray<TouchBehaviorFlags>& aOutValues)
    1.60 +{
    1.61 +  WidgetTouchEvent *touchEvent = aEvent->AsTouchEvent();
    1.62 +
    1.63 +  aOutValues.Clear();
    1.64 +
    1.65 +  for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
    1.66 +    // If aEvent wasn't transformed previously we might need to
    1.67 +    // add transforming of the spt here.
    1.68 +    mozilla::ScreenIntPoint spt;
    1.69 +    spt.x = touchEvent->touches[i]->mRefPoint.x;
    1.70 +    spt.y = touchEvent->touches[i]->mRefPoint.y;
    1.71 +
    1.72 +    nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(spt);
    1.73 +    aOutValues.AppendElement(apzc
    1.74 +      ? apzc->GetAllowedTouchBehavior(spt)
    1.75 +      : AllowedTouchBehavior::UNKNOWN);
    1.76 +  }
    1.77 +}
    1.78 +
    1.79 +void
    1.80 +APZCTreeManager::SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
    1.81 +                                         const nsTArray<TouchBehaviorFlags> &aValues)
    1.82 +{
    1.83 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
    1.84 +  if (apzc) {
    1.85 +    apzc->SetAllowedTouchBehavior(aValues);
    1.86 +  }
    1.87 +}
    1.88 +
    1.89 +void
    1.90 +APZCTreeManager::AssertOnCompositorThread()
    1.91 +{
    1.92 +  Compositor::AssertOnCompositorThread();
    1.93 +}
    1.94 +
    1.95 +/* Flatten the tree of APZC instances into the given nsTArray */
    1.96 +static void
    1.97 +Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
    1.98 +{
    1.99 +  if (aApzc) {
   1.100 +    aCollection->AppendElement(aApzc);
   1.101 +    Collect(aApzc->GetLastChild(), aCollection);
   1.102 +    Collect(aApzc->GetPrevSibling(), aCollection);
   1.103 +  }
   1.104 +}
   1.105 +
   1.106 +void
   1.107 +APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
   1.108 +                                             bool aIsFirstPaint, uint64_t aFirstPaintLayersId)
   1.109 +{
   1.110 +  AssertOnCompositorThread();
   1.111 +
   1.112 +  MonitorAutoLock lock(mTreeLock);
   1.113 +
   1.114 +  // We do this business with collecting the entire tree into an array because otherwise
   1.115 +  // it's very hard to determine which APZC instances need to be destroyed. In the worst
   1.116 +  // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
   1.117 +  // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
   1.118 +  // completely different place. In scenario (a) we would want to destroy the APZC while
   1.119 +  // walking the layer tree and noticing that the layer/APZC is no longer there. But if
   1.120 +  // we do that then we run into a problem in scenario (b) because we might encounter that
   1.121 +  // layer later during the walk. To handle both of these we have to 'remember' that the
   1.122 +  // layer was not found, and then do the destroy only at the end of the tree walk after
   1.123 +  // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
   1.124 +  // as part of a recursive tree walk is hard and so maintaining a list and removing
   1.125 +  // APZCs that are still alive is much simpler.
   1.126 +  nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
   1.127 +  Collect(mRootApzc, &apzcsToDestroy);
   1.128 +  mRootApzc = nullptr;
   1.129 +
   1.130 +  if (aRoot) {
   1.131 +    mApzcTreeLog << "[start]\n";
   1.132 +    UpdatePanZoomControllerTree(aCompositor,
   1.133 +                                aRoot,
   1.134 +                                // aCompositor is null in gtest scenarios
   1.135 +                                aCompositor ? aCompositor->RootLayerTreeId() : 0,
   1.136 +                                gfx3DMatrix(), nullptr, nullptr,
   1.137 +                                aIsFirstPaint, aFirstPaintLayersId,
   1.138 +                                &apzcsToDestroy);
   1.139 +    mApzcTreeLog << "[end]\n";
   1.140 +  }
   1.141 +
   1.142 +  for (size_t i = 0; i < apzcsToDestroy.Length(); i++) {
   1.143 +    APZC_LOG("Destroying APZC at %p\n", apzcsToDestroy[i].get());
   1.144 +    apzcsToDestroy[i]->Destroy();
   1.145 +  }
   1.146 +}
   1.147 +
   1.148 +AsyncPanZoomController*
   1.149 +APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
   1.150 +                                             Layer* aLayer, uint64_t aLayersId,
   1.151 +                                             gfx3DMatrix aTransform,
   1.152 +                                             AsyncPanZoomController* aParent,
   1.153 +                                             AsyncPanZoomController* aNextSibling,
   1.154 +                                             bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
   1.155 +                                             nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
   1.156 +{
   1.157 +  mTreeLock.AssertCurrentThreadOwns();
   1.158 +
   1.159 +  ContainerLayer* container = aLayer->AsContainerLayer();
   1.160 +  AsyncPanZoomController* apzc = nullptr;
   1.161 +  mApzcTreeLog << aLayer->Name() << '\t';
   1.162 +  if (container) {
   1.163 +    const FrameMetrics& metrics = container->GetFrameMetrics();
   1.164 +    if (metrics.IsScrollable()) {
   1.165 +      const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
   1.166 +      if (state && state->mController.get()) {
   1.167 +        // If we get here, aLayer is a scrollable container layer and somebody
   1.168 +        // has registered a GeckoContentController for it, so we need to ensure
   1.169 +        // it has an APZC instance to manage its scrolling.
   1.170 +
   1.171 +        apzc = container->GetAsyncPanZoomController();
   1.172 +
   1.173 +        // If the content represented by the container layer has changed (which may
   1.174 +        // be possible because of DLBI heuristics) then we don't want to keep using
   1.175 +        // the same old APZC for the new content. Null it out so we run through the
   1.176 +        // code to find another one or create one.
   1.177 +        ScrollableLayerGuid guid(aLayersId, metrics);
   1.178 +        if (apzc && !apzc->Matches(guid)) {
   1.179 +          apzc = nullptr;
   1.180 +        }
   1.181 +
   1.182 +        // If the container doesn't have an APZC already, try to find one of our
   1.183 +        // pre-existing ones that matches. In particular, if we find an APZC whose
   1.184 +        // ScrollableLayerGuid is the same, then we know what happened is that the
   1.185 +        // layout of the page changed causing the layer tree to be rebuilt, but the
   1.186 +        // underlying content for which the APZC was originally created is still
   1.187 +        // there. So it makes sense to pick up that APZC instance again and use it here.
   1.188 +        if (apzc == nullptr) {
   1.189 +          for (size_t i = 0; i < aApzcsToDestroy->Length(); i++) {
   1.190 +            if (aApzcsToDestroy->ElementAt(i)->Matches(guid)) {
   1.191 +              apzc = aApzcsToDestroy->ElementAt(i);
   1.192 +              break;
   1.193 +            }
   1.194 +          }
   1.195 +        }
   1.196 +
   1.197 +        // The APZC we get off the layer may have been destroyed previously if the layer was inactive
   1.198 +        // or omitted from the layer tree for whatever reason from a layers update. If it later comes
   1.199 +        // back it will have a reference to a destroyed APZC and so we need to throw that out and make
   1.200 +        // a new one.
   1.201 +        bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
   1.202 +        if (newApzc) {
   1.203 +          apzc = new AsyncPanZoomController(aLayersId, this, state->mController,
   1.204 +                                            AsyncPanZoomController::USE_GESTURE_DETECTOR);
   1.205 +          apzc->SetCompositorParent(aCompositor);
   1.206 +          apzc->SetCrossProcessCompositorParent(state->mCrossProcessParent);
   1.207 +        } else {
   1.208 +          // If there was already an APZC for the layer clear the tree pointers
   1.209 +          // so that it doesn't continue pointing to APZCs that should no longer
   1.210 +          // be in the tree. These pointers will get reset properly as we continue
   1.211 +          // building the tree. Also remove it from the set of APZCs that are going
   1.212 +          // to be destroyed, because it's going to remain active.
   1.213 +          aApzcsToDestroy->RemoveElement(apzc);
   1.214 +          apzc->SetPrevSibling(nullptr);
   1.215 +          apzc->SetLastChild(nullptr);
   1.216 +        }
   1.217 +        APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer, aLayersId, container->GetFrameMetrics().GetScrollId());
   1.218 +
   1.219 +        apzc->NotifyLayersUpdated(metrics,
   1.220 +                                  aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
   1.221 +        apzc->SetScrollHandoffParentId(container->GetScrollHandoffParentId());
   1.222 +
   1.223 +        // Use the composition bounds as the hit test region.
   1.224 +        // Optionally, the GeckoContentController can provide a touch-sensitive
   1.225 +        // region that constrains all frames associated with the controller.
   1.226 +        // In this case we intersect the composition bounds with that region.
   1.227 +        ParentLayerRect visible(metrics.mCompositionBounds);
   1.228 +        CSSRect touchSensitiveRegion;
   1.229 +        if (state->mController->GetTouchSensitiveRegion(&touchSensitiveRegion)) {
   1.230 +          // Note: we assume here that touchSensitiveRegion is in the CSS pixels
   1.231 +          // of our parent layer, which makes this coordinate conversion
   1.232 +          // correct.
   1.233 +          visible = visible.Intersect(touchSensitiveRegion
   1.234 +                                      * metrics.mDevPixelsPerCSSPixel
   1.235 +                                      * metrics.GetParentResolution());
   1.236 +        }
   1.237 +        gfx3DMatrix transform;
   1.238 +        gfx::To3DMatrix(aLayer->GetTransform(), transform);
   1.239 +
   1.240 +        apzc->SetLayerHitTestData(visible, aTransform, transform);
   1.241 +        APZC_LOG("Setting rect(%f %f %f %f) as visible region for APZC %p\n", visible.x, visible.y,
   1.242 +                                                                              visible.width, visible.height,
   1.243 +                                                                              apzc);
   1.244 +
   1.245 +        mApzcTreeLog << "APZC " << guid
   1.246 +                     << "\tcb=" << visible
   1.247 +                     << "\tsr=" << container->GetFrameMetrics().mScrollableRect
   1.248 +                     << (aLayer->GetVisibleRegion().IsEmpty() ? "\tscrollinfo" : "")
   1.249 +                     << "\t" << container->GetFrameMetrics().GetContentDescription();
   1.250 +
   1.251 +        // Bind the APZC instance into the tree of APZCs
   1.252 +        if (aNextSibling) {
   1.253 +          aNextSibling->SetPrevSibling(apzc);
   1.254 +        } else if (aParent) {
   1.255 +          aParent->SetLastChild(apzc);
   1.256 +        } else {
   1.257 +          mRootApzc = apzc;
   1.258 +        }
   1.259 +
   1.260 +        // Let this apzc be the parent of other controllers when we recurse downwards
   1.261 +        aParent = apzc;
   1.262 +
   1.263 +        if (newApzc) {
   1.264 +          if (apzc->IsRootForLayersId()) {
   1.265 +            // If we just created a new apzc that is the root for its layers ID, then
   1.266 +            // we need to update its zoom constraints which might have arrived before this
   1.267 +            // was created
   1.268 +            ZoomConstraints constraints;
   1.269 +            if (state->mController->GetRootZoomConstraints(&constraints)) {
   1.270 +              apzc->UpdateZoomConstraints(constraints);
   1.271 +            }
   1.272 +          } else {
   1.273 +            // For an apzc that is not the root for its layers ID, we give it the
   1.274 +            // same zoom constraints as its parent. This ensures that if e.g.
   1.275 +            // user-scalable=no was specified, none of the APZCs allow double-tap
   1.276 +            // to zoom.
   1.277 +            apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
   1.278 +          }
   1.279 +        }
   1.280 +      }
   1.281 +    }
   1.282 +
   1.283 +    container->SetAsyncPanZoomController(apzc);
   1.284 +  }
   1.285 +  mApzcTreeLog << '\n';
   1.286 +
   1.287 +  // Accumulate the CSS transform between layers that have an APZC, but exclude any
   1.288 +  // any layers that do have an APZC, and reset the accumulation at those layers.
   1.289 +  if (apzc) {
   1.290 +    aTransform = gfx3DMatrix();
   1.291 +  } else {
   1.292 +    // Multiply child layer transforms on the left so they get applied first
   1.293 +    gfx3DMatrix matrix;
   1.294 +    gfx::To3DMatrix(aLayer->GetTransform(), matrix);
   1.295 +    aTransform = matrix * aTransform;
   1.296 +  }
   1.297 +
   1.298 +  uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId);
   1.299 +  // If there's no APZC at this level, any APZCs for our child layers will
   1.300 +  // have our siblings as siblings.
   1.301 +  AsyncPanZoomController* next = apzc ? nullptr : aNextSibling;
   1.302 +  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
   1.303 +    gfx::TreeAutoIndent indent(mApzcTreeLog);
   1.304 +    next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aTransform, aParent, next,
   1.305 +                                       aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy);
   1.306 +  }
   1.307 +
   1.308 +  // Return the APZC that should be the sibling of other APZCs as we continue
   1.309 +  // moving towards the first child at this depth in the layer tree.
   1.310 +  // If this layer doesn't have an APZC, we promote any APZCs in the subtree
   1.311 +  // upwards. Otherwise we fall back to the aNextSibling that was passed in.
   1.312 +  if (apzc) {
   1.313 +    return apzc;
   1.314 +  }
   1.315 +  if (next) {
   1.316 +    return next;
   1.317 +  }
   1.318 +  return aNextSibling;
   1.319 +}
   1.320 +
   1.321 +/*static*/ template<class T> void
   1.322 +ApplyTransform(gfx::PointTyped<T>* aPoint, const gfx3DMatrix& aMatrix)
   1.323 +{
   1.324 +  gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   1.325 +  aPoint->x = result.x;
   1.326 +  aPoint->y = result.y;
   1.327 +}
   1.328 +
   1.329 +/*static*/ template<class T> void
   1.330 +ApplyTransform(gfx::IntPointTyped<T>* aPoint, const gfx3DMatrix& aMatrix)
   1.331 +{
   1.332 +  gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   1.333 +  aPoint->x = NS_lround(result.x);
   1.334 +  aPoint->y = NS_lround(result.y);
   1.335 +}
   1.336 +
   1.337 +/*static*/ void
   1.338 +ApplyTransform(nsIntPoint* aPoint, const gfx3DMatrix& aMatrix)
   1.339 +{
   1.340 +  gfxPoint result = aMatrix.Transform(gfxPoint(aPoint->x, aPoint->y));
   1.341 +  aPoint->x = NS_lround(result.x);
   1.342 +  aPoint->y = NS_lround(result.y);
   1.343 +}
   1.344 +
   1.345 +nsEventStatus
   1.346 +APZCTreeManager::ReceiveInputEvent(const InputData& aEvent,
   1.347 +                                   ScrollableLayerGuid* aOutTargetGuid)
   1.348 +{
   1.349 +  nsEventStatus result = nsEventStatus_eIgnore;
   1.350 +  gfx3DMatrix transformToApzc;
   1.351 +  gfx3DMatrix transformToGecko;
   1.352 +  switch (aEvent.mInputType) {
   1.353 +    case MULTITOUCH_INPUT: {
   1.354 +      const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   1.355 +      if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
   1.356 +        // MULTITOUCH_START input contains all active touches of the current
   1.357 +        // session thus resetting mTouchCount.
   1.358 +        mTouchCount = multiTouchInput.mTouches.Length();
   1.359 +        mApzcForInputBlock = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
   1.360 +        if (multiTouchInput.mTouches.Length() == 1) {
   1.361 +          // If we have one touch point, this might be the start of a pan.
   1.362 +          // Prepare for possible overscroll handoff.
   1.363 +          BuildOverscrollHandoffChain(mApzcForInputBlock);
   1.364 +        }
   1.365 +        for (size_t i = 1; i < multiTouchInput.mTouches.Length(); i++) {
   1.366 +          nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[i].mScreenPoint));
   1.367 +          mApzcForInputBlock = CommonAncestor(mApzcForInputBlock.get(), apzc2.get());
   1.368 +          APZC_LOG("Using APZC %p as the common ancestor\n", mApzcForInputBlock.get());
   1.369 +          // For now, we only ever want to do pinching on the root APZC for a given layers id. So
   1.370 +          // when we find the common ancestor of multiple points, also walk up to the root APZC.
   1.371 +          mApzcForInputBlock = RootAPZCForLayersId(mApzcForInputBlock);
   1.372 +          APZC_LOG("Using APZC %p as the root APZC for multi-touch\n", mApzcForInputBlock.get());
   1.373 +        }
   1.374 +
   1.375 +        if (mApzcForInputBlock) {
   1.376 +          // Cache transformToApzc so it can be used for future events in this block.
   1.377 +          GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
   1.378 +          mCachedTransformToApzcForInputBlock = transformToApzc;
   1.379 +        } else {
   1.380 +          // Reset the cached apz transform
   1.381 +          mCachedTransformToApzcForInputBlock = gfx3DMatrix();
   1.382 +        }
   1.383 +      } else if (mApzcForInputBlock) {
   1.384 +        APZC_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
   1.385 +      }
   1.386 +      if (mApzcForInputBlock) {
   1.387 +        mApzcForInputBlock->GetGuid(aOutTargetGuid);
   1.388 +        // Use the cached transform to compute the point to send to the APZC.
   1.389 +        // This ensures that the sequence of touch points an APZC sees in an
   1.390 +        // input block are all in the same coordinate space.
   1.391 +        transformToApzc = mCachedTransformToApzcForInputBlock;
   1.392 +        MultiTouchInput inputForApzc(multiTouchInput);
   1.393 +        for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
   1.394 +          ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
   1.395 +        }
   1.396 +        result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
   1.397 +      }
   1.398 +      if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
   1.399 +          multiTouchInput.mType == MultiTouchInput::MULTITOUCH_END) {
   1.400 +        if (mTouchCount >= multiTouchInput.mTouches.Length()) {
   1.401 +          // MULTITOUCH_END input contains only released touches thus decrementing.
   1.402 +          mTouchCount -= multiTouchInput.mTouches.Length();
   1.403 +        } else {
   1.404 +          NS_WARNING("Got an unexpected touchend/touchcancel");
   1.405 +          mTouchCount = 0;
   1.406 +        }
   1.407 +        // If we have an mApzcForInputBlock and it's the end of the touch sequence
   1.408 +        // then null it out so we don't keep a dangling reference and leak things.
   1.409 +        if (mTouchCount == 0) {
   1.410 +          mApzcForInputBlock = nullptr;
   1.411 +          ClearOverscrollHandoffChain();
   1.412 +        }
   1.413 +      }
   1.414 +      break;
   1.415 +    } case PINCHGESTURE_INPUT: {
   1.416 +      const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
   1.417 +      nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint);
   1.418 +      if (apzc) {
   1.419 +        apzc->GetGuid(aOutTargetGuid);
   1.420 +        GetInputTransforms(apzc, transformToApzc, transformToGecko);
   1.421 +        PinchGestureInput inputForApzc(pinchInput);
   1.422 +        ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
   1.423 +        result = apzc->ReceiveInputEvent(inputForApzc);
   1.424 +      }
   1.425 +      break;
   1.426 +    } case TAPGESTURE_INPUT: {
   1.427 +      const TapGestureInput& tapInput = aEvent.AsTapGestureInput();
   1.428 +      nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint));
   1.429 +      if (apzc) {
   1.430 +        apzc->GetGuid(aOutTargetGuid);
   1.431 +        GetInputTransforms(apzc, transformToApzc, transformToGecko);
   1.432 +        TapGestureInput inputForApzc(tapInput);
   1.433 +        ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
   1.434 +        result = apzc->ReceiveInputEvent(inputForApzc);
   1.435 +      }
   1.436 +      break;
   1.437 +    }
   1.438 +  }
   1.439 +  return result;
   1.440 +}
   1.441 +
   1.442 +already_AddRefed<AsyncPanZoomController>
   1.443 +APZCTreeManager::GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent)
   1.444 +{
   1.445 +  ScreenPoint point = ScreenPoint(aEvent.touches[0]->mRefPoint.x, aEvent.touches[0]->mRefPoint.y);
   1.446 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(point);
   1.447 +  if (aEvent.touches.Length() == 1) {
   1.448 +    // If we have one touch point, this might be the start of a pan.
   1.449 +    // Prepare for possible overscroll handoff.
   1.450 +    BuildOverscrollHandoffChain(apzc);
   1.451 +  }
   1.452 +  for (size_t i = 1; i < aEvent.touches.Length(); i++) {
   1.453 +    point = ScreenPoint(aEvent.touches[i]->mRefPoint.x, aEvent.touches[i]->mRefPoint.y);
   1.454 +    nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(point);
   1.455 +    apzc = CommonAncestor(apzc.get(), apzc2.get());
   1.456 +    APZC_LOG("Using APZC %p as the common ancestor\n", apzc.get());
   1.457 +    // For now, we only ever want to do pinching on the root APZC for a given layers id. So
   1.458 +    // when we find the common ancestor of multiple points, also walk up to the root APZC.
   1.459 +    apzc = RootAPZCForLayersId(apzc);
   1.460 +    APZC_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
   1.461 +  }
   1.462 +  return apzc.forget();
   1.463 +}
   1.464 +
   1.465 +nsEventStatus
   1.466 +APZCTreeManager::ProcessTouchEvent(WidgetTouchEvent& aEvent,
   1.467 +                                   ScrollableLayerGuid* aOutTargetGuid)
   1.468 +{
   1.469 +  MOZ_ASSERT(NS_IsMainThread());
   1.470 +
   1.471 +  nsEventStatus ret = nsEventStatus_eIgnore;
   1.472 +  if (!aEvent.touches.Length()) {
   1.473 +    return ret;
   1.474 +  }
   1.475 +  if (aEvent.message == NS_TOUCH_START) {
   1.476 +    // NS_TOUCH_START event contains all active touches of the current
   1.477 +    // session thus resetting mTouchCount.
   1.478 +    mTouchCount = aEvent.touches.Length();
   1.479 +    mApzcForInputBlock = GetTouchInputBlockAPZC(aEvent);
   1.480 +    if (mApzcForInputBlock) {
   1.481 +      // Cache apz transform so it can be used for future events in this block.
   1.482 +      gfx3DMatrix transformToGecko;
   1.483 +      GetInputTransforms(mApzcForInputBlock, mCachedTransformToApzcForInputBlock, transformToGecko);
   1.484 +    } else {
   1.485 +      // Reset the cached apz transform
   1.486 +      mCachedTransformToApzcForInputBlock = gfx3DMatrix();
   1.487 +    }
   1.488 +  }
   1.489 +
   1.490 +  if (mApzcForInputBlock) {
   1.491 +    mApzcForInputBlock->GetGuid(aOutTargetGuid);
   1.492 +    // For computing the input for the APZC, used the cached transform.
   1.493 +    // This ensures that the sequence of touch points an APZC sees in an
   1.494 +    // input block are all in the same coordinate space.
   1.495 +    gfx3DMatrix transformToApzc = mCachedTransformToApzcForInputBlock;
   1.496 +    MultiTouchInput inputForApzc(aEvent);
   1.497 +    for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
   1.498 +      ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
   1.499 +    }
   1.500 +    ret = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
   1.501 +
   1.502 +    // For computing the event to pass back to Gecko, use the up-to-date transforms.
   1.503 +    // This ensures that transformToApzc and transformToGecko are in sync
   1.504 +    // (note that transformToGecko isn't cached).
   1.505 +    gfx3DMatrix transformToGecko;
   1.506 +    GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
   1.507 +    gfx3DMatrix outTransform = transformToApzc * transformToGecko;
   1.508 +    for (size_t i = 0; i < aEvent.touches.Length(); i++) {
   1.509 +      ApplyTransform(&(aEvent.touches[i]->mRefPoint), outTransform);
   1.510 +    }
   1.511 +  }
   1.512 +  // If we have an mApzcForInputBlock and it's the end of the touch sequence
   1.513 +  // then null it out so we don't keep a dangling reference and leak things.
   1.514 +  if (aEvent.message == NS_TOUCH_CANCEL ||
   1.515 +      aEvent.message == NS_TOUCH_END) {
   1.516 +    if (mTouchCount >= aEvent.touches.Length()) {
   1.517 +      // NS_TOUCH_END event contains only released touches thus decrementing.
   1.518 +      mTouchCount -= aEvent.touches.Length();
   1.519 +    } else {
   1.520 +      NS_WARNING("Got an unexpected touchend/touchcancel");
   1.521 +      mTouchCount = 0;
   1.522 +    }
   1.523 +    if (mTouchCount == 0) {
   1.524 +      mApzcForInputBlock = nullptr;
   1.525 +      ClearOverscrollHandoffChain();
   1.526 +    }
   1.527 +  }
   1.528 +  return ret;
   1.529 +}
   1.530 +
   1.531 +void
   1.532 +APZCTreeManager::TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
   1.533 +                                            LayoutDeviceIntPoint* aOutTransformedPoint)
   1.534 +{
   1.535 +  MOZ_ASSERT(aOutTransformedPoint);
   1.536 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aPoint);
   1.537 +  if (apzc && aOutTransformedPoint) {
   1.538 +    gfx3DMatrix transformToApzc;
   1.539 +    gfx3DMatrix transformToGecko;
   1.540 +    GetInputTransforms(apzc, transformToApzc, transformToGecko);
   1.541 +    gfx3DMatrix outTransform = transformToApzc * transformToGecko;
   1.542 +    aOutTransformedPoint->x = aPoint.x;
   1.543 +    aOutTransformedPoint->y = aPoint.y;
   1.544 +    ApplyTransform(aOutTransformedPoint, outTransform);
   1.545 +  }
   1.546 +}
   1.547 +
   1.548 +nsEventStatus
   1.549 +APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
   1.550 +                              ScrollableLayerGuid* aOutTargetGuid)
   1.551 +{
   1.552 +  MOZ_ASSERT(NS_IsMainThread());
   1.553 +
   1.554 +  // Transform the refPoint
   1.555 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(aEvent.refPoint.x, aEvent.refPoint.y));
   1.556 +  if (!apzc) {
   1.557 +    return nsEventStatus_eIgnore;
   1.558 +  }
   1.559 +  apzc->GetGuid(aOutTargetGuid);
   1.560 +  gfx3DMatrix transformToApzc;
   1.561 +  gfx3DMatrix transformToGecko;
   1.562 +  GetInputTransforms(apzc, transformToApzc, transformToGecko);
   1.563 +  gfx3DMatrix outTransform = transformToApzc * transformToGecko;
   1.564 +  ApplyTransform(&(aEvent.refPoint), outTransform);
   1.565 +  return nsEventStatus_eIgnore;
   1.566 +}
   1.567 +
   1.568 +nsEventStatus
   1.569 +APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
   1.570 +                                   ScrollableLayerGuid* aOutTargetGuid)
   1.571 +{
   1.572 +  MOZ_ASSERT(NS_IsMainThread());
   1.573 +
   1.574 +  switch (aEvent.eventStructType) {
   1.575 +    case NS_TOUCH_EVENT: {
   1.576 +      WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
   1.577 +      return ProcessTouchEvent(touchEvent, aOutTargetGuid);
   1.578 +    }
   1.579 +    default: {
   1.580 +      return ProcessEvent(aEvent, aOutTargetGuid);
   1.581 +    }
   1.582 +  }
   1.583 +}
   1.584 +
   1.585 +void
   1.586 +APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
   1.587 +                            const CSSRect& aRect)
   1.588 +{
   1.589 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   1.590 +  if (apzc) {
   1.591 +    apzc->ZoomToRect(aRect);
   1.592 +  }
   1.593 +}
   1.594 +
   1.595 +void
   1.596 +APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
   1.597 +                                      bool aPreventDefault)
   1.598 +{
   1.599 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   1.600 +  if (apzc) {
   1.601 +    apzc->ContentReceivedTouch(aPreventDefault);
   1.602 +  }
   1.603 +}
   1.604 +
   1.605 +void
   1.606 +APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
   1.607 +                                       const ZoomConstraints& aConstraints)
   1.608 +{
   1.609 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   1.610 +  // For a given layers id, non-root APZCs inherit the zoom constraints
   1.611 +  // of their root.
   1.612 +  if (apzc && apzc->IsRootForLayersId()) {
   1.613 +    MonitorAutoLock lock(mTreeLock);
   1.614 +    UpdateZoomConstraintsRecursively(apzc.get(), aConstraints);
   1.615 +  }
   1.616 +}
   1.617 +
   1.618 +void
   1.619 +APZCTreeManager::UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
   1.620 +                                                  const ZoomConstraints& aConstraints)
   1.621 +{
   1.622 +  mTreeLock.AssertCurrentThreadOwns();
   1.623 +
   1.624 +  aApzc->UpdateZoomConstraints(aConstraints);
   1.625 +  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
   1.626 +    // We can have subtrees with their own layers id - leave those alone.
   1.627 +    if (!child->IsRootForLayersId()) {
   1.628 +      UpdateZoomConstraintsRecursively(child, aConstraints);
   1.629 +    }
   1.630 +  }
   1.631 +}
   1.632 +
   1.633 +void
   1.634 +APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
   1.635 +{
   1.636 +  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   1.637 +  if (apzc) {
   1.638 +    apzc->CancelAnimation();
   1.639 +  }
   1.640 +}
   1.641 +
   1.642 +void
   1.643 +APZCTreeManager::ClearTree()
   1.644 +{
   1.645 +  MonitorAutoLock lock(mTreeLock);
   1.646 +
   1.647 +  // This can be done as part of a tree walk but it's easier to
   1.648 +  // just re-use the Collect method that we need in other places.
   1.649 +  // If this is too slow feel free to change it to a recursive walk.
   1.650 +  nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
   1.651 +  Collect(mRootApzc, &apzcsToDestroy);
   1.652 +  for (size_t i = 0; i < apzcsToDestroy.Length(); i++) {
   1.653 +    apzcsToDestroy[i]->Destroy();
   1.654 +  }
   1.655 +  mRootApzc = nullptr;
   1.656 +}
   1.657 +
   1.658 +/**
   1.659 + * Transform a displacement from the screen coordinates of a source APZC to
   1.660 + * the screen coordinates of a target APZC.
   1.661 + * @param aTreeManager the tree manager for the APZC tree containing |aSource|
   1.662 + *                     and |aTarget|
   1.663 + * @param aSource the source APZC
   1.664 + * @param aTarget the target APZC
   1.665 + * @param aStartPoint the start point of the displacement
   1.666 + * @param aEndPoint the end point of the displacement
   1.667 + */
   1.668 +static void
   1.669 +TransformDisplacement(APZCTreeManager* aTreeManager,
   1.670 +                      AsyncPanZoomController* aSource,
   1.671 +                      AsyncPanZoomController* aTarget,
   1.672 +                      ScreenPoint& aStartPoint,
   1.673 +                      ScreenPoint& aEndPoint) {
   1.674 +  gfx3DMatrix transformToApzc;
   1.675 +  gfx3DMatrix transformToGecko;  // ignored
   1.676 +
   1.677 +  // Convert start and end points to untransformed screen coordinates.
   1.678 +  aTreeManager->GetInputTransforms(aSource, transformToApzc, transformToGecko);
   1.679 +  ApplyTransform(&aStartPoint, transformToApzc.Inverse());
   1.680 +  ApplyTransform(&aEndPoint, transformToApzc.Inverse());
   1.681 +
   1.682 +  // Convert start and end points to aTarget's transformed screen coordinates.
   1.683 +  aTreeManager->GetInputTransforms(aTarget, transformToApzc, transformToGecko);
   1.684 +  ApplyTransform(&aStartPoint, transformToApzc);
   1.685 +  ApplyTransform(&aEndPoint, transformToApzc);
   1.686 +}
   1.687 +
   1.688 +void
   1.689 +APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
   1.690 +                                uint32_t aOverscrollHandoffChainIndex)
   1.691 +{
   1.692 +  nsRefPtr<AsyncPanZoomController> next;
   1.693 +  {
   1.694 +    // Grab tree lock to protect mOverscrollHandoffChain from concurrent
   1.695 +    // access from the input and compositor threads.
   1.696 +    // Release it before calling TransformDisplacement() as that grabs the
   1.697 +    // lock itself.
   1.698 +    MonitorAutoLock lock(mTreeLock);
   1.699 +
   1.700 +    // If we have reached the end of the overscroll handoff chain, there is
   1.701 +    // nothing more to scroll, so we ignore the rest of the pan gesture.
   1.702 +    if (aOverscrollHandoffChainIndex >= mOverscrollHandoffChain.length()) {
   1.703 +      // Nothing more to scroll - ignore the rest of the pan gesture.
   1.704 +      return;
   1.705 +    }
   1.706 +
   1.707 +    next = mOverscrollHandoffChain[aOverscrollHandoffChainIndex];
   1.708 +  }
   1.709 +
   1.710 +  if (next == nullptr)
   1.711 +    return;
   1.712 +
   1.713 +  // Convert the start and end points from |aPrev|'s coordinate space to
   1.714 +  // |next|'s coordinate space. Since |aPrev| may be the same as |next|
   1.715 +  // (if |aPrev| is the APZC that is initiating the scroll and there is no
   1.716 +  // scroll grabbing to grab the scroll from it), don't bother doing the
   1.717 +  // transformations in that case.
   1.718 +  if (next != aPrev) {
   1.719 +    TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint);
   1.720 +  }
   1.721 +
   1.722 +  // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
   1.723 +  // again with an incremented index.
   1.724 +  next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChainIndex);
   1.725 +}
   1.726 +
   1.727 +void
   1.728 +APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVelocity)
   1.729 +{
   1.730 +  // Build the overscroll handoff chain. This is necessary because it is
   1.731 +  // otherwise built on touch-start and cleared on touch-end, and a fling
   1.732 +  // happens after touch-end. Note that, unlike DispatchScroll() which is
   1.733 +  // called on every touch-move during overscroll panning,
   1.734 +  // HandleFlingOverscroll() is only called once during a fling handoff,
   1.735 +  // so it's not worth trying to avoid building the handoff chain here.
   1.736 +  BuildOverscrollHandoffChain(aPrev);
   1.737 +
   1.738 +  nsRefPtr<AsyncPanZoomController> next;  // will be used outside monitor block
   1.739 +  {
   1.740 +    // Grab tree lock to protect mOverscrollHandoffChain from concurrent
   1.741 +    // access from the input and compositor threads.
   1.742 +    // Release it before calling GetInputTransforms() as that grabs the
   1.743 +    // lock itself.
   1.744 +    MonitorAutoLock lock(mTreeLock);
   1.745 +
   1.746 +    // Find |aPrev| in the handoff chain.
   1.747 +    uint32_t i;
   1.748 +    for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
   1.749 +      if (mOverscrollHandoffChain[i] == aPrev) {
   1.750 +        break;
   1.751 +      }
   1.752 +    }
   1.753 +
   1.754 +    // Get the next APZC in the handoff chain, if any.
   1.755 +    if (i + 1 < mOverscrollHandoffChain.length()) {
   1.756 +      next = mOverscrollHandoffChain[i + 1];
   1.757 +    }
   1.758 +
   1.759 +    // Clear the handoff chain so we don't maintain references to APZCs
   1.760 +    // unnecessarily.
   1.761 +    mOverscrollHandoffChain.clear();
   1.762 +  }
   1.763 +
   1.764 +  // Nothing to hand off fling to.
   1.765 +  if (next == nullptr) {
   1.766 +    return;
   1.767 +  }
   1.768 +
   1.769 +  // The fling's velocity needs to be transformed from the screen coordinates
   1.770 +  // of |aPrev| to the screen coordinates of |next|. To transform a velocity
   1.771 +  // correctly, we need to convert it to a displacement. For now, we do this
   1.772 +  // by anchoring it to a start point of (0, 0).
   1.773 +  // TODO: For this to be correct in the presence of 3D transforms, we should
   1.774 +  // use the end point of the touch that started the fling as the start point
   1.775 +  // rather than (0, 0).
   1.776 +  ScreenPoint startPoint;  // (0, 0)
   1.777 +  ScreenPoint endPoint = startPoint + aVelocity;
   1.778 +  TransformDisplacement(this, aPrev, next, startPoint, endPoint);
   1.779 +  ScreenPoint transformedVelocity = endPoint - startPoint;
   1.780 +
   1.781 +  // Tell |next| to start a fling with the transformed velocity.
   1.782 +  next->TakeOverFling(transformedVelocity);
   1.783 +}
   1.784 +
   1.785 +bool
   1.786 +APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
   1.787 +{
   1.788 +  MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
   1.789 +  if (mOverscrollHandoffChain.length() == 0) {
   1.790 +    return false;
   1.791 +  }
   1.792 +  for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
   1.793 +    nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
   1.794 +    if (item) {
   1.795 +      item->FlushRepaintForOverscrollHandoff();
   1.796 +    }
   1.797 +  }
   1.798 +  return true;
   1.799 +}
   1.800 +
   1.801 +bool
   1.802 +APZCTreeManager::CanBePanned(AsyncPanZoomController* aApzc)
   1.803 +{
   1.804 +  MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
   1.805 +
   1.806 +  // Find |aApzc| in the handoff chain.
   1.807 +  uint32_t i;
   1.808 +  for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
   1.809 +    if (mOverscrollHandoffChain[i] == aApzc) {
   1.810 +      break;
   1.811 +    }
   1.812 +  }
   1.813 +
   1.814 +  // See whether any APZC in the handoff chain starting from |aApzc|
   1.815 +  // has room to be panned.
   1.816 +  for (uint32_t j = i; j < mOverscrollHandoffChain.length(); ++j) {
   1.817 +    if (mOverscrollHandoffChain[j]->IsPannable()) {
   1.818 +      return true;
   1.819 +    }
   1.820 +  }
   1.821 +
   1.822 +  return false;
   1.823 +}
   1.824 +
   1.825 +bool
   1.826 +APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
   1.827 +{
   1.828 +  MonitorAutoLock lock(mTreeLock);
   1.829 +  nsRefPtr<AsyncPanZoomController> target;
   1.830 +  // The root may have siblings, so check those too
   1.831 +  gfxPoint point(aPoint.x, aPoint.y);
   1.832 +  for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
   1.833 +    target = GetAPZCAtPoint(apzc, point);
   1.834 +    if (target) {
   1.835 +      return true;
   1.836 +    }
   1.837 +  }
   1.838 +  return false;
   1.839 +}
   1.840 +
   1.841 +already_AddRefed<AsyncPanZoomController>
   1.842 +APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
   1.843 +{
   1.844 +  MonitorAutoLock lock(mTreeLock);
   1.845 +  nsRefPtr<AsyncPanZoomController> target;
   1.846 +  // The root may have siblings, check those too
   1.847 +  for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
   1.848 +    target = FindTargetAPZC(apzc, aGuid);
   1.849 +    if (target) {
   1.850 +      break;
   1.851 +    }
   1.852 +  }
   1.853 +  return target.forget();
   1.854 +}
   1.855 +
   1.856 +struct CompareByScrollPriority
   1.857 +{
   1.858 +  bool operator()(const nsRefPtr<AsyncPanZoomController>& a, const nsRefPtr<AsyncPanZoomController>& b) {
   1.859 +    return a->HasScrollgrab() && !b->HasScrollgrab();
   1.860 +  }
   1.861 +};
   1.862 +
   1.863 +already_AddRefed<AsyncPanZoomController>
   1.864 +APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
   1.865 +{
   1.866 +  MonitorAutoLock lock(mTreeLock);
   1.867 +  nsRefPtr<AsyncPanZoomController> target;
   1.868 +  // The root may have siblings, so check those too
   1.869 +  gfxPoint point(aPoint.x, aPoint.y);
   1.870 +  for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
   1.871 +    target = GetAPZCAtPoint(apzc, point);
   1.872 +    if (target) {
   1.873 +      break;
   1.874 +    }
   1.875 +  }
   1.876 +  return target.forget();
   1.877 +}
   1.878 +
   1.879 +void
   1.880 +APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget)
   1.881 +{
   1.882 +  // Scroll grabbing is a mechanism that allows content to specify that
   1.883 +  // the initial target of a pan should be not the innermost scrollable
   1.884 +  // frame at the touch point (which is what GetTargetAPZC finds), but
   1.885 +  // something higher up in the tree.
   1.886 +  // It's not sufficient to just find the initial target, however, as
   1.887 +  // overscroll can be handed off to another APZC. Without scroll grabbing,
   1.888 +  // handoff just occurs from child to parent. With scroll grabbing, the
   1.889 +  // handoff order can be different, so we build a chain of APZCs in the
   1.890 +  // order in which scroll will be handed off to them.
   1.891 +
   1.892 +  // Grab tree lock to protect mOverscrollHandoffChain from concurrent
   1.893 +  // access between the input and compositor threads.
   1.894 +  MonitorAutoLock lock(mTreeLock);
   1.895 +
   1.896 +  mOverscrollHandoffChain.clear();
   1.897 +
   1.898 +  // Build the chain. If there is a scroll parent link, we use that. This is
   1.899 +  // needed to deal with scroll info layers, because they participate in handoff
   1.900 +  // but do not follow the expected layer tree structure. If there are no
   1.901 +  // scroll parent links we just walk up the tree to find the scroll parent.
   1.902 +  AsyncPanZoomController* apzc = aInitialTarget;
   1.903 +  while (apzc != nullptr) {
   1.904 +    if (!mOverscrollHandoffChain.append(apzc)) {
   1.905 +      NS_WARNING("Vector::append failed");
   1.906 +      mOverscrollHandoffChain.clear();
   1.907 +      return;
   1.908 +    }
   1.909 +    if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) {
   1.910 +      if (!apzc->IsRootForLayersId()) {
   1.911 +        // This probably indicates a bug or missed case in layout code
   1.912 +        NS_WARNING("Found a non-root APZ with no handoff parent");
   1.913 +      }
   1.914 +      apzc = apzc->GetParent();
   1.915 +      continue;
   1.916 +    }
   1.917 +
   1.918 +    // Find the AsyncPanZoomController instance with a matching layersId and
   1.919 +    // the scroll id that matches apzc->GetScrollHandoffParentId(). To do this
   1.920 +    // search the subtree with the same layersId for the apzc with the specified
   1.921 +    // scroll id.
   1.922 +    AsyncPanZoomController* scrollParent = nullptr;
   1.923 +    AsyncPanZoomController* parent = apzc;
   1.924 +    while (!parent->IsRootForLayersId()) {
   1.925 +      parent = parent->GetParent();
   1.926 +      // While walking up to find the root of the subtree, if we encounter the
   1.927 +      // handoff parent, we don't actually need to do the search so we can
   1.928 +      // just abort here.
   1.929 +      if (parent->GetGuid().mScrollId == apzc->GetScrollHandoffParentId()) {
   1.930 +        scrollParent = parent;
   1.931 +        break;
   1.932 +      }
   1.933 +    }
   1.934 +    if (!scrollParent) {
   1.935 +      scrollParent = FindTargetAPZC(parent, apzc->GetScrollHandoffParentId());
   1.936 +    }
   1.937 +    apzc = scrollParent;
   1.938 +  }
   1.939 +
   1.940 +  // Now adjust the chain to account for scroll grabbing. Sorting is a bit
   1.941 +  // of an overkill here, but scroll grabbing will likely be generalized
   1.942 +  // to scroll priorities, so we might as well do it this way.
   1.943 +  // The sorting being stable ensures that the relative order between
   1.944 +  // non-scrollgrabbing APZCs remains child -> parent.
   1.945 +  // (The relative order between scrollgrabbing APZCs will also remain
   1.946 +  // child -> parent, though that's just an artefact of the implementation
   1.947 +  // and users of 'scrollgrab' should not rely on this.)
   1.948 +  std::stable_sort(mOverscrollHandoffChain.begin(), mOverscrollHandoffChain.end(),
   1.949 +                   CompareByScrollPriority());
   1.950 +}
   1.951 +
   1.952 +/* Find the apzc in the subtree rooted at aApzc that has the same layers id as
   1.953 +   aApzc, and that has the given scroll id. Generally this function should be called
   1.954 +   with aApzc being the root of its layers id subtree. */
   1.955 +AsyncPanZoomController*
   1.956 +APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId)
   1.957 +{
   1.958 +  mTreeLock.AssertCurrentThreadOwns();
   1.959 +
   1.960 +  if (aApzc->GetGuid().mScrollId == aScrollId) {
   1.961 +    return aApzc;
   1.962 +  }
   1.963 +  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
   1.964 +    if (child->GetGuid().mLayersId != aApzc->GetGuid().mLayersId) {
   1.965 +      continue;
   1.966 +    }
   1.967 +    AsyncPanZoomController* match = FindTargetAPZC(child, aScrollId);
   1.968 +    if (match) {
   1.969 +      return match;
   1.970 +    }
   1.971 +  }
   1.972 +
   1.973 +  return nullptr;
   1.974 +}
   1.975 +
   1.976 +void
   1.977 +APZCTreeManager::ClearOverscrollHandoffChain()
   1.978 +{
   1.979 +  MonitorAutoLock lock(mTreeLock);
   1.980 +  mOverscrollHandoffChain.clear();
   1.981 +}
   1.982 +
   1.983 +AsyncPanZoomController*
   1.984 +APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid)
   1.985 +{
   1.986 +  mTreeLock.AssertCurrentThreadOwns();
   1.987 +
   1.988 +  // This walks the tree in depth-first, reverse order, so that it encounters
   1.989 +  // APZCs front-to-back on the screen.
   1.990 +  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
   1.991 +    AsyncPanZoomController* match = FindTargetAPZC(child, aGuid);
   1.992 +    if (match) {
   1.993 +      return match;
   1.994 +    }
   1.995 +  }
   1.996 +
   1.997 +  if (aApzc->Matches(aGuid)) {
   1.998 +    return aApzc;
   1.999 +  }
  1.1000 +  return nullptr;
  1.1001 +}
  1.1002 +
  1.1003 +AsyncPanZoomController*
  1.1004 +APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint)
  1.1005 +{
  1.1006 +  mTreeLock.AssertCurrentThreadOwns();
  1.1007 +
  1.1008 +  // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
  1.1009 +  // explained in the comment on GetInputTransforms. This function will recurse with aApzc at L and P, and the
  1.1010 +  // comments explain what values are stored in the variables at these two levels. All the comments
  1.1011 +  // use standard matrix notation where the leftmost matrix in a multiplication is applied first.
  1.1012 +
  1.1013 +  // ancestorUntransform takes points from aApzc's parent APZC's layer coordinates
  1.1014 +  // to aApzc's parent layer's layer coordinates.
  1.1015 +  // It is OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
  1.1016 +  //   and RC.Inverse() * QC.Inverse()                at recursion level for P.
  1.1017 +  gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
  1.1018 +
  1.1019 +  // Hit testing for this layer takes place in our parent layer coordinates,
  1.1020 +  // since the composition bounds (used to initialize the visible rect against
  1.1021 +  // which we hit test are in those coordinates).
  1.1022 +  gfxPoint hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
  1.1023 +  APZC_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
  1.1024 +           aHitTestPoint.x, aHitTestPoint.y,
  1.1025 +           hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
  1.1026 +
  1.1027 +  // childUntransform takes points from aApzc's parent APZC's layer coordinates
  1.1028 +  // to aApzc's layer coordinates (which are aApzc's children's ParentLayer coordinates).
  1.1029 +  // It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LA.Inverse() at L
  1.1030 +  //   and RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()                at P.
  1.1031 +  gfx3DMatrix childUntransform = ancestorUntransform
  1.1032 +                               * aApzc->GetCSSTransform().Inverse()
  1.1033 +                               * gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
  1.1034 +  gfxPoint hitTestPointForChildLayers = childUntransform.ProjectPoint(aHitTestPoint);
  1.1035 +  APZC_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
  1.1036 +           aHitTestPoint.x, aHitTestPoint.y,
  1.1037 +           hitTestPointForChildLayers.x, hitTestPointForChildLayers.y, aApzc);
  1.1038 +
  1.1039 +  // This walks the tree in depth-first, reverse order, so that it encounters
  1.1040 +  // APZCs front-to-back on the screen.
  1.1041 +  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
  1.1042 +    AsyncPanZoomController* match = GetAPZCAtPoint(child, hitTestPointForChildLayers);
  1.1043 +    if (match) {
  1.1044 +      return match;
  1.1045 +    }
  1.1046 +  }
  1.1047 +  if (aApzc->VisibleRegionContains(ViewAs<ParentLayerPixel>(hitTestPointForThisLayer))) {
  1.1048 +    APZC_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p\n",
  1.1049 +             hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
  1.1050 +    return aApzc;
  1.1051 +  }
  1.1052 +  return nullptr;
  1.1053 +}
  1.1054 +
  1.1055 +/* This function sets the aTransformToApzcOut and aTransformToGeckoOut out-parameters
  1.1056 +   to some useful transformations that input events may need applied. This is best
  1.1057 +   illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
  1.1058 +   is the layer that corresponds to the argument |aApzc|, and layer R is the root
  1.1059 +   of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
  1.1060 +   When layer L is displayed to the screen by the compositor, the set of transforms that
  1.1061 +   are applied to L are (in order from top to bottom):
  1.1062 +
  1.1063 +        L's transient async transform       (hereafter referred to as transform matrix LT)
  1.1064 +        L's nontransient async transform    (hereafter referred to as transform matrix LN)
  1.1065 +        L's CSS transform                   (hereafter referred to as transform matrix LC)
  1.1066 +        M's transient async transform       (hereafter referred to as transform matrix MT)
  1.1067 +        M's nontransient async transform    (hereafter referred to as transform matrix MN)
  1.1068 +        M's CSS transform                   (hereafter referred to as transform matrix MC)
  1.1069 +        ...
  1.1070 +        R's transient async transform       (hereafter referred to as transform matrix RT)
  1.1071 +        R's nontransient async transform    (hereafter referred to as transform matrix RN)
  1.1072 +        R's CSS transform                   (hereafter referred to as transform matrix RC)
  1.1073 +
  1.1074 +   Also, for any layer, the async transform is the combination of its transient and non-transient
  1.1075 +   parts. That is, for any layer L:
  1.1076 +                  LA === LT * LN
  1.1077 +        LA.Inverse() === LN.Inverse() * LT.Inverse()
  1.1078 +
  1.1079 +   If we want user input to modify L's transient async transform, we have to first convert
  1.1080 +   user input from screen space to the coordinate space of L's transient async transform. Doing
  1.1081 +   this involves applying the following transforms (in order from top to bottom):
  1.1082 +        RC.Inverse()
  1.1083 +        RN.Inverse()
  1.1084 +        RT.Inverse()
  1.1085 +        ...
  1.1086 +        MC.Inverse()
  1.1087 +        MN.Inverse()
  1.1088 +        MT.Inverse()
  1.1089 +        LC.Inverse()
  1.1090 +        LN.Inverse()
  1.1091 +   This combined transformation is returned in the aTransformToApzcOut out-parameter.
  1.1092 +
  1.1093 +   Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
  1.1094 +   out all of the async transforms that are involved in this chain. This is because async
  1.1095 +   transforms are stored only in the compositor and gecko does not account for them when
  1.1096 +   doing display-list-based hit-testing for event dispatching.
  1.1097 +   Furthermore, because these input events are processed by Gecko in a FIFO queue that
  1.1098 +   includes other things (specifically paint requests), it is possible that by time the
  1.1099 +   input event reaches gecko, it will have painted something else. Therefore, we need to
  1.1100 +   apply another transform to the input events to account for the possible disparity between
  1.1101 +   what we know gecko last painted and the last paint request we sent to gecko. Let this
  1.1102 +   transform be represented by LD, MD, ... RD.
  1.1103 +   Therefore, given a user input in screen space, the following transforms need to be applied
  1.1104 +   (in order from top to bottom):
  1.1105 +        RC.Inverse()
  1.1106 +        RN.Inverse()
  1.1107 +        RT.Inverse()
  1.1108 +        ...
  1.1109 +        MC.Inverse()
  1.1110 +        MN.Inverse()
  1.1111 +        MT.Inverse()
  1.1112 +        LC.Inverse()
  1.1113 +        LN.Inverse()
  1.1114 +        LT.Inverse()
  1.1115 +        LD
  1.1116 +        LC
  1.1117 +        MD
  1.1118 +        MC
  1.1119 +        ...
  1.1120 +        RD
  1.1121 +        RC
  1.1122 +   This sequence can be simplified and refactored to the following:
  1.1123 +        aTransformToApzcOut
  1.1124 +        LT.Inverse()
  1.1125 +        LD
  1.1126 +        LC
  1.1127 +        MD
  1.1128 +        MC
  1.1129 +        ...
  1.1130 +        RD
  1.1131 +        RC
  1.1132 +   Since aTransformToApzcOut is already one of the out-parameters, we set aTransformToGeckoOut
  1.1133 +   to the remaining transforms (LT.Inverse() * LD * ... * RC), so that the caller code can
  1.1134 +   combine it with aTransformToApzcOut to get the final transform required in this case.
  1.1135 +
  1.1136 +   Note that for many of these layers, there will be no AsyncPanZoomController attached, and
  1.1137 +   so the async transform will be the identity transform. So, in the example above, if layers
  1.1138 +   L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
  1.1139 +   RN and RD will be identity transforms.
  1.1140 +   Additionally, for space-saving purposes, each APZC instance stores its layer's individual
  1.1141 +   CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
  1.1142 +   layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
  1.1143 +   The APZC instances track the last dispatched paint request and so are able to calculate LD and
  1.1144 +   PD using those internally stored values.
  1.1145 +   The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
  1.1146 +   required can be generated.
  1.1147 + */
  1.1148 +void
  1.1149 +APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix& aTransformToApzcOut,
  1.1150 +                                    gfx3DMatrix& aTransformToGeckoOut)
  1.1151 +{
  1.1152 +  MonitorAutoLock lock(mTreeLock);
  1.1153 +
  1.1154 +  // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
  1.1155 +  // explained in the comment above. This function is called with aApzc at L, and the loop
  1.1156 +  // below performs one iteration, where parent is at P. The comments explain what values are stored
  1.1157 +  // in the variables at these two levels. All the comments use standard matrix notation where the
  1.1158 +  // leftmost matrix in a multiplication is applied first.
  1.1159 +
  1.1160 +  // ancestorUntransform is OC.Inverse() * NC.Inverse() * MC.Inverse()
  1.1161 +  gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
  1.1162 +  // asyncUntransform is LA.Inverse()
  1.1163 +  gfx3DMatrix asyncUntransform = gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
  1.1164 +  // nontransientAsyncTransform is LN
  1.1165 +  gfx3DMatrix nontransientAsyncTransform = aApzc->GetNontransientAsyncTransform();
  1.1166 +  // transientAsyncUntransform is LT.Inverse()
  1.1167 +  gfx3DMatrix transientAsyncUntransform = nontransientAsyncTransform * asyncUntransform;
  1.1168 +
  1.1169 +  // aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
  1.1170 +  aTransformToApzcOut = ancestorUntransform * aApzc->GetCSSTransform().Inverse() * nontransientAsyncTransform.Inverse();
  1.1171 +  // aTransformToGeckoOut is initialized to LT.Inverse() * LD * LC * MC * NC * OC
  1.1172 +  aTransformToGeckoOut = transientAsyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
  1.1173 +
  1.1174 +  for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
  1.1175 +    // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
  1.1176 +    ancestorUntransform = parent->GetAncestorTransform().Inverse();
  1.1177 +    // asyncUntransform is updated to PA.Inverse() when parent == P
  1.1178 +    asyncUntransform = gfx3DMatrix(parent->GetCurrentAsyncTransform()).Inverse();
  1.1179 +    // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()
  1.1180 +    gfx3DMatrix untransformSinceLastApzc = ancestorUntransform * parent->GetCSSTransform().Inverse() * asyncUntransform;
  1.1181 +
  1.1182 +    // aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
  1.1183 +    aTransformToApzcOut = untransformSinceLastApzc * aTransformToApzcOut;
  1.1184 +    // aTransformToGeckoOut is LT.Inverse() * LD * LC * MC * NC * OC * PD * PC * QC * RC
  1.1185 +    aTransformToGeckoOut = aTransformToGeckoOut * parent->GetTransformToLastDispatchedPaint() * parent->GetCSSTransform() * parent->GetAncestorTransform();
  1.1186 +
  1.1187 +    // The above values for aTransformToApzcOut and aTransformToGeckoOut when parent == P match
  1.1188 +    // the required output as explained in the comment above this method. Note that any missing
  1.1189 +    // terms are guaranteed to be identity transforms.
  1.1190 +  }
  1.1191 +}
  1.1192 +
  1.1193 +already_AddRefed<AsyncPanZoomController>
  1.1194 +APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2)
  1.1195 +{
  1.1196 +  MonitorAutoLock lock(mTreeLock);
  1.1197 +  nsRefPtr<AsyncPanZoomController> ancestor;
  1.1198 +
  1.1199 +  // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this function
  1.1200 +  // will return null.
  1.1201 +
  1.1202 +  // Calculate depth of the APZCs in the tree
  1.1203 +  int depth1 = 0, depth2 = 0;
  1.1204 +  for (AsyncPanZoomController* parent = aApzc1; parent; parent = parent->GetParent()) {
  1.1205 +    depth1++;
  1.1206 +  }
  1.1207 +  for (AsyncPanZoomController* parent = aApzc2; parent; parent = parent->GetParent()) {
  1.1208 +    depth2++;
  1.1209 +  }
  1.1210 +
  1.1211 +  // At most one of the following two loops will be executed; the deeper APZC pointer
  1.1212 +  // will get walked up to the depth of the shallower one.
  1.1213 +  int minDepth = depth1 < depth2 ? depth1 : depth2;
  1.1214 +  while (depth1 > minDepth) {
  1.1215 +    depth1--;
  1.1216 +    aApzc1 = aApzc1->GetParent();
  1.1217 +  }
  1.1218 +  while (depth2 > minDepth) {
  1.1219 +    depth2--;
  1.1220 +    aApzc2 = aApzc2->GetParent();
  1.1221 +  }
  1.1222 +
  1.1223 +  // Walk up the ancestor chains of both APZCs, always staying at the same depth for
  1.1224 +  // either APZC, and return the the first common ancestor encountered.
  1.1225 +  while (true) {
  1.1226 +    if (aApzc1 == aApzc2) {
  1.1227 +      ancestor = aApzc1;
  1.1228 +      break;
  1.1229 +    }
  1.1230 +    if (depth1 <= 0) {
  1.1231 +      break;
  1.1232 +    }
  1.1233 +    aApzc1 = aApzc1->GetParent();
  1.1234 +    aApzc2 = aApzc2->GetParent();
  1.1235 +  }
  1.1236 +  return ancestor.forget();
  1.1237 +}
  1.1238 +
  1.1239 +already_AddRefed<AsyncPanZoomController>
  1.1240 +APZCTreeManager::RootAPZCForLayersId(AsyncPanZoomController* aApzc)
  1.1241 +{
  1.1242 +  MonitorAutoLock lock(mTreeLock);
  1.1243 +  nsRefPtr<AsyncPanZoomController> apzc = aApzc;
  1.1244 +  while (apzc && !apzc->IsRootForLayersId()) {
  1.1245 +    apzc = apzc->GetParent();
  1.1246 +  }
  1.1247 +  return apzc.forget();
  1.1248 +}
  1.1249 +
  1.1250 +}
  1.1251 +}

mercurial