gfx/layers/apz/src/APZCTreeManager.cpp

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

mercurial