gfx/layers/apz/src/APZCTreeManager.cpp

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

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

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

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

mercurial