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 +}