Line data Source code
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 <stack>
7 : #include <unordered_set>
8 : #include "APZCTreeManager.h"
9 : #include "AsyncPanZoomController.h"
10 : #include "Compositor.h" // for Compositor
11 : #include "DragTracker.h" // for DragTracker
12 : #include "gfxPrefs.h" // for gfxPrefs
13 : #include "HitTestingTreeNode.h" // for HitTestingTreeNode
14 : #include "InputBlockState.h" // for InputBlockState
15 : #include "InputData.h" // for InputData, etc
16 : #include "Layers.h" // for Layer, etc
17 : #include "mozilla/dom/Touch.h" // for Touch
18 : #include "mozilla/gfx/GPUParent.h" // for GPUParent
19 : #include "mozilla/gfx/Logging.h" // for gfx::TreeLog
20 : #include "mozilla/gfx/Point.h" // for Point
21 : #include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
22 : #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
23 : #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
24 : #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
25 : #include "mozilla/layers/FocusState.h" // for FocusState
26 : #include "mozilla/layers/LayerMetricsWrapper.h"
27 : #include "mozilla/layers/WebRenderScrollDataWrapper.h"
28 : #include "mozilla/MouseEvents.h"
29 : #include "mozilla/mozalloc.h" // for operator new
30 : #include "mozilla/TouchEvents.h"
31 : #include "mozilla/Preferences.h" // for Preferences
32 : #include "mozilla/EventStateManager.h" // for WheelPrefs
33 : #include "mozilla/webrender/WebRenderAPI.h"
34 : #include "nsDebug.h" // for NS_WARNING
35 : #include "nsPoint.h" // for nsIntPoint
36 : #include "nsThreadUtils.h" // for NS_IsMainThread
37 : #include "OverscrollHandoffState.h" // for OverscrollHandoffState
38 : #include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc
39 : #include "LayersLogging.h" // for Stringify
40 : #include "Units.h" // for ParentlayerPixel
41 : #include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled
42 : #include "UnitTransforms.h" // for ViewAs
43 :
44 : #define ENABLE_APZCTM_LOGGING 0
45 : // #define ENABLE_APZCTM_LOGGING 1
46 :
47 : #if ENABLE_APZCTM_LOGGING
48 : # define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
49 : #else
50 : # define APZCTM_LOG(...)
51 : #endif
52 :
53 : // #define APZ_KEY_LOG(...) printf_stderr("APZKEY: " __VA_ARGS__)
54 : #define APZ_KEY_LOG(...)
55 :
56 : namespace mozilla {
57 : namespace layers {
58 :
59 : typedef mozilla::gfx::Point Point;
60 : typedef mozilla::gfx::Point4D Point4D;
61 : typedef mozilla::gfx::Matrix4x4 Matrix4x4;
62 :
63 : typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
64 :
65 : float APZCTreeManager::sDPI = 160.0;
66 :
67 28 : struct APZCTreeManager::TreeBuildingState {
68 28 : TreeBuildingState(const LayerTreeState* const aLayerTreeState,
69 : bool aIsFirstPaint, uint64_t aOriginatingLayersId,
70 : APZTestData* aTestData, uint32_t aPaintSequence)
71 28 : : mLayerTreeState(aLayerTreeState)
72 : , mIsFirstPaint(aIsFirstPaint)
73 : , mOriginatingLayersId(aOriginatingLayersId)
74 28 : , mPaintLogger(aTestData, aPaintSequence)
75 : {
76 28 : }
77 :
78 : // State that doesn't change as we recurse in the tree building
79 : const LayerTreeState* const mLayerTreeState;
80 : const bool mIsFirstPaint;
81 : const uint64_t mOriginatingLayersId;
82 : const APZPaintLogHelper mPaintLogger;
83 :
84 : // State that is updated as we perform the tree build
85 :
86 : // A list of nodes that need to be destroyed at the end of the tree building.
87 : // This is initialized with all nodes in the old tree, and nodes are removed
88 : // from it as we reuse them in the new tree.
89 : nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
90 : // A set of layer trees that are no longer in the hit testing tree. This is
91 : // used to destroy unneeded focus targets at the end of tree building. This
92 : // is needed in addition to mNodesToDestroy because a hit testing node for a
93 : // layer tree can be removed without the whole layer tree being removed.
94 : std::unordered_set<uint64_t> mLayersIdsToDestroy;
95 :
96 : // This map is populated as we place APZCs into the new tree. Its purpose is
97 : // to facilitate re-using the same APZC for different layers that scroll
98 : // together (and thus have the same ScrollableLayerGuid).
99 : std::unordered_map<ScrollableLayerGuid, AsyncPanZoomController*, ScrollableLayerGuidHash> mApzcMap;
100 : };
101 :
102 : class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
103 : public:
104 : NS_DECL_ISUPPORTS
105 : NS_DECL_NSIOBSERVER
106 :
107 1 : explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
108 1 : : mTreeManager(aTreeManager)
109 : {
110 1 : MOZ_ASSERT(NS_IsMainThread());
111 2 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
112 1 : MOZ_ASSERT(obsSvc);
113 1 : if (obsSvc) {
114 1 : obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
115 : }
116 1 : }
117 :
118 0 : void Unregister()
119 : {
120 0 : MOZ_ASSERT(NS_IsMainThread());
121 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
122 0 : if (obsSvc) {
123 0 : obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
124 : }
125 0 : mTreeManager = nullptr;
126 0 : }
127 :
128 : protected:
129 0 : virtual ~CheckerboardFlushObserver() {}
130 :
131 : private:
132 : RefPtr<APZCTreeManager> mTreeManager;
133 : };
134 :
135 2 : NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
136 :
137 : NS_IMETHODIMP
138 0 : APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
139 : const char* aTopic,
140 : const char16_t*)
141 : {
142 0 : MOZ_ASSERT(NS_IsMainThread());
143 0 : MOZ_ASSERT(mTreeManager.get());
144 :
145 0 : MutexAutoLock lock(mTreeManager->mTreeLock);
146 0 : if (mTreeManager->mRootNode) {
147 0 : ForEachNode<ReverseIterator>(mTreeManager->mRootNode.get(),
148 0 : [](HitTestingTreeNode* aNode)
149 : {
150 0 : if (aNode->IsPrimaryHolder()) {
151 0 : MOZ_ASSERT(aNode->GetApzc());
152 0 : aNode->GetApzc()->FlushActiveCheckerboardReport();
153 : }
154 0 : });
155 : }
156 0 : if (XRE_IsGPUProcess()) {
157 0 : if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
158 0 : nsCString topic("APZ:FlushActiveCheckerboard:Done");
159 0 : Unused << gpu->SendNotifyUiObservers(topic);
160 : }
161 : } else {
162 0 : MOZ_ASSERT(XRE_IsParentProcess());
163 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
164 0 : if (obsSvc) {
165 0 : obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", nullptr);
166 : }
167 : }
168 0 : return NS_OK;
169 : }
170 :
171 : /**
172 : * A RAII class used for setting the focus sequence number on input events
173 : * as they are being processed. Any input event is assumed to be potentially
174 : * focus changing unless explicitly marked otherwise.
175 : */
176 : class MOZ_RAII AutoFocusSequenceNumberSetter
177 : {
178 : public:
179 4 : AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent)
180 4 : : mFocusState(aFocusState)
181 : , mEvent(aEvent)
182 4 : , mMayChangeFocus(true)
183 4 : { }
184 :
185 0 : void MarkAsNonFocusChanging() { mMayChangeFocus = false; }
186 :
187 4 : ~AutoFocusSequenceNumberSetter()
188 4 : {
189 4 : if (mMayChangeFocus) {
190 4 : mFocusState.ReceiveFocusChangingEvent();
191 :
192 : APZ_KEY_LOG("Marking input with type=%d as focus changing with seq=%" PRIu64 "\n",
193 : (int)mEvent.mInputType,
194 : mFocusState.LastAPZProcessedEvent());
195 : } else {
196 : APZ_KEY_LOG("Marking input with type=%d as non focus changing with seq=%" PRIu64 "\n",
197 : (int)mEvent.mInputType,
198 : mFocusState.LastAPZProcessedEvent());
199 : }
200 :
201 4 : mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
202 4 : }
203 :
204 : private:
205 : FocusState& mFocusState;
206 : InputData& mEvent;
207 : bool mMayChangeFocus;
208 : };
209 :
210 : /*static*/ const ScreenMargin
211 0 : APZCTreeManager::CalculatePendingDisplayPort(
212 : const FrameMetrics& aFrameMetrics,
213 : const ParentLayerPoint& aVelocity)
214 : {
215 : return AsyncPanZoomController::CalculatePendingDisplayPort(
216 0 : aFrameMetrics, aVelocity);
217 : }
218 :
219 1 : APZCTreeManager::APZCTreeManager()
220 1 : : mInputQueue(new InputQueue()),
221 : mTreeLock("APZCTreeLock"),
222 : mHitResultForInputBlock(HitNothing),
223 : mRetainedTouchIdentifier(-1),
224 2 : mApzcTreeLog("apzctree")
225 : {
226 2 : RefPtr<APZCTreeManager> self(this);
227 2 : NS_DispatchToMainThread(
228 6 : NS_NewRunnableFunction("layers::APZCTreeManager::APZCTreeManager", [self] {
229 2 : self->mFlushObserver = new CheckerboardFlushObserver(self);
230 2 : }));
231 1 : AsyncPanZoomController::InitializeGlobalState();
232 1 : mApzcTreeLog.ConditionOnPrefFunction(gfxPrefs::APZPrintTree);
233 : #if defined(MOZ_WIDGET_ANDROID)
234 : mToolbarAnimator = new AndroidDynamicToolbarAnimator();
235 : #endif // (MOZ_WIDGET_ANDROID)
236 1 : }
237 :
238 0 : APZCTreeManager::~APZCTreeManager()
239 : {
240 0 : }
241 :
242 : /*static*/ void
243 0 : APZCTreeManager::InitializeGlobalState()
244 : {
245 0 : MOZ_ASSERT(NS_IsMainThread());
246 0 : AsyncPanZoomController::InitializeGlobalState();
247 0 : }
248 :
249 : AsyncPanZoomController*
250 2 : APZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
251 : GeckoContentController* aController)
252 : {
253 : return new AsyncPanZoomController(aLayersId, this, mInputQueue,
254 2 : aController, AsyncPanZoomController::USE_GESTURE_DETECTOR);
255 : }
256 :
257 : TimeStamp
258 4 : APZCTreeManager::GetFrameTime()
259 : {
260 4 : return TimeStamp::Now();
261 : }
262 :
263 : void
264 0 : APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
265 : const nsTArray<TouchBehaviorFlags> &aValues)
266 : {
267 0 : mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
268 0 : }
269 :
270 : template<class ScrollNode> void // ScrollNode is a LayerMetricsWrapper or a WebRenderScrollDataWrapper
271 28 : APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
272 : const ScrollNode& aRoot,
273 : bool aIsFirstPaint,
274 : uint64_t aOriginatingLayersId,
275 : uint32_t aPaintSequenceNumber)
276 : {
277 28 : APZThreadUtils::AssertOnCompositorThread();
278 :
279 56 : MutexAutoLock lock(mTreeLock);
280 :
281 : // For testing purposes, we log some data to the APZTestData associated with
282 : // the layers id that originated this update.
283 28 : APZTestData* testData = nullptr;
284 28 : if (gfxPrefs::APZTestLoggingEnabled()) {
285 0 : if (LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
286 0 : testData = &state->mApzTestData;
287 0 : testData->StartNewPaint(aPaintSequenceNumber);
288 : }
289 : }
290 :
291 : const LayerTreeState* treeState =
292 28 : CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
293 28 : MOZ_ASSERT(treeState);
294 : TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId,
295 56 : testData, aPaintSequenceNumber);
296 :
297 : // We do this business with collecting the entire tree into an array because otherwise
298 : // it's very hard to determine which APZC instances need to be destroyed. In the worst
299 : // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
300 : // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
301 : // completely different place. In scenario (a) we would want to destroy the APZC while
302 : // walking the layer tree and noticing that the layer/APZC is no longer there. But if
303 : // we do that then we run into a problem in scenario (b) because we might encounter that
304 : // layer later during the walk. To handle both of these we have to 'remember' that the
305 : // layer was not found, and then do the destroy only at the end of the tree walk after
306 : // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
307 : // as part of a recursive tree walk is hard and so maintaining a list and removing
308 : // APZCs that are still alive is much simpler.
309 28 : ForEachNode<ReverseIterator>(mRootNode.get(),
310 204 : [&state] (HitTestingTreeNode* aNode)
311 204 : {
312 204 : state.mNodesToDestroy.AppendElement(aNode);
313 204 : });
314 28 : state.mLayersIdsToDestroy = mFocusState.GetFocusTargetLayerIds();
315 28 : mRootNode = nullptr;
316 :
317 28 : if (aRoot) {
318 56 : std::stack<gfx::TreeAutoIndent> indents;
319 56 : std::stack<gfx::Matrix4x4> ancestorTransforms;
320 28 : HitTestingTreeNode* parent = nullptr;
321 28 : HitTestingTreeNode* next = nullptr;
322 28 : uint64_t layersId = aRootLayerTreeId;
323 28 : ancestorTransforms.push(Matrix4x4());
324 :
325 28 : state.mLayersIdsToDestroy.erase(aRootLayerTreeId);
326 :
327 28 : mApzcTreeLog << "[start]\n";
328 28 : mTreeLock.AssertCurrentThreadOwns();
329 :
330 28 : ForEachNode<ReverseIterator>(aRoot,
331 211 : [&](ScrollNode aLayerMetrics)
332 : {
333 844 : mApzcTreeLog << aLayerMetrics.Name() << '\t';
334 :
335 : HitTestingTreeNode* node = PrepareNodeForLayer(aLayerMetrics,
336 650 : aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
337 1083 : parent, next, state);
338 211 : MOZ_ASSERT(node);
339 211 : AsyncPanZoomController* apzc = node->GetApzc();
340 211 : aLayerMetrics.SetApzc(apzc);
341 :
342 422 : mApzcTreeLog << '\n';
343 :
344 : // Accumulate the CSS transform between layers that have an APZC.
345 : // In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
346 : // we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
347 : // compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
348 : // transform to layer L when we recurse into the children below. If we are at a layer
349 : // with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
350 : // the new accumulation as we go down.
351 : // If a transform is a perspective transform, it's ignored for this purpose
352 : // (see bug 1168263).
353 211 : Matrix4x4 currentTransform = aLayerMetrics.TransformIsPerspective() ? Matrix4x4() : aLayerMetrics.GetTransform();
354 211 : if (!apzc) {
355 172 : currentTransform = currentTransform * ancestorTransforms.top();
356 : }
357 211 : ancestorTransforms.push(currentTransform);
358 :
359 : // Note that |node| at this point will not have any children, otherwise we
360 : // we would have to set next to node->GetFirstChild().
361 211 : MOZ_ASSERT(!node->GetFirstChild());
362 211 : parent = node;
363 211 : next = nullptr;
364 :
365 : // Update the layersId if we have a new one
366 422 : if (Maybe<uint64_t> newLayersId = aLayerMetrics.GetReferentId()) {
367 28 : layersId = *newLayersId;
368 :
369 : // Mark that this layer tree is being used
370 28 : state.mLayersIdsToDestroy.erase(layersId);
371 : }
372 :
373 211 : indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
374 211 : },
375 211 : [&](ScrollNode aLayerMetrics)
376 : {
377 422 : next = parent;
378 211 : parent = parent->GetParent();
379 422 : layersId = next->GetLayersId();
380 211 : ancestorTransforms.pop();
381 211 : indents.pop();
382 211 : });
383 :
384 28 : mApzcTreeLog << "[end]\n";
385 : }
386 :
387 : // We do not support tree structures where the root node has siblings.
388 28 : MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
389 :
390 34 : for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
391 : APZCTM_LOG("Destroying node at %p with APZC %p\n",
392 : state.mNodesToDestroy[i].get(),
393 : state.mNodesToDestroy[i]->GetApzc());
394 6 : state.mNodesToDestroy[i]->Destroy();
395 : }
396 :
397 : // Clear out any focus targets that are no longer needed
398 28 : for (auto layersId : state.mLayersIdsToDestroy) {
399 0 : mFocusState.RemoveFocusTarget(layersId);
400 : }
401 :
402 : #if ENABLE_APZCTM_LOGGING
403 : // Make the hit-test tree line up with the layer dump
404 : printf_stderr("APZCTreeManager (%p)\n", this);
405 : mRootNode->Dump(" ");
406 : #endif
407 28 : }
408 :
409 : void
410 28 : APZCTreeManager::UpdateFocusState(uint64_t aRootLayerTreeId,
411 : uint64_t aOriginatingLayersId,
412 : const FocusTarget& aFocusTarget)
413 : {
414 28 : if (!gfxPrefs::APZKeyboardEnabled()) {
415 28 : return;
416 : }
417 :
418 0 : mFocusState.Update(aRootLayerTreeId,
419 : aOriginatingLayersId,
420 0 : aFocusTarget);
421 : }
422 :
423 : void
424 28 : APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
425 : Layer* aRoot,
426 : bool aIsFirstPaint,
427 : uint64_t aOriginatingLayersId,
428 : uint32_t aPaintSequenceNumber)
429 : {
430 28 : LayerMetricsWrapper root(aRoot);
431 28 : UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
432 28 : aOriginatingLayersId, aPaintSequenceNumber);
433 28 : }
434 :
435 : void
436 0 : APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
437 : const WebRenderScrollData& aScrollData,
438 : bool aIsFirstPaint,
439 : uint64_t aOriginatingLayersId,
440 : uint32_t aPaintSequenceNumber)
441 : {
442 0 : WebRenderScrollDataWrapper wrapper(&aScrollData);
443 0 : UpdateHitTestingTreeImpl(aRootLayerTreeId, wrapper, aIsFirstPaint,
444 0 : aOriginatingLayersId, aPaintSequenceNumber);
445 0 : }
446 :
447 : bool
448 0 : APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
449 : const TimeStamp& aSampleTime,
450 : nsTArray<WrTransformProperty>& aTransformArray)
451 : {
452 0 : APZThreadUtils::AssertOnCompositorThread();
453 0 : MOZ_ASSERT(aWrApi);
454 :
455 0 : MutexAutoLock lock(mTreeLock);
456 :
457 : // During the first pass through the tree, we build a cache of guid->HTTN so
458 : // that we can find the relevant APZC instances quickly in subsequent passes,
459 : // such as the one below to generate scrollbar transforms. Without this, perf
460 : // could end up being O(n^2) instead of O(n log n) because we'd have to search
461 : // the tree to find the corresponding APZC every time we hit a thumb node.
462 0 : std::unordered_map<ScrollableLayerGuid, HitTestingTreeNode*, ScrollableLayerGuidHash> httnMap;
463 :
464 0 : bool activeAnimations = false;
465 0 : uint64_t lastLayersId = -1;
466 : WrPipelineId lastPipelineId;
467 :
468 : // We iterate backwards here because the HitTestingTreeNode is optimized
469 : // for backwards iteration. The equivalent code in AsyncCompositionManager
470 : // iterates forwards, but the direction shouldn't really matter in practice
471 : // so we do what's faster. In the future, if we need to start doing the
472 : // equivalent of AlignFixedAndStickyLayers here, then the order will become
473 : // important and we'll need to take that into consideration.
474 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
475 0 : [&](HitTestingTreeNode* aNode)
476 : {
477 0 : if (!aNode->IsPrimaryHolder()) {
478 0 : return;
479 : }
480 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
481 0 : MOZ_ASSERT(apzc);
482 :
483 0 : if (aNode->GetLayersId() != lastLayersId) {
484 : // If we walked into or out of a subtree, we need to get the new
485 : // pipeline id.
486 0 : const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aNode->GetLayersId());
487 0 : if (!(state && state->mWrBridge)) {
488 : // During shutdown we might have layer tree information for stuff
489 : // that has already been torn down. In that case just skip over
490 : // those layers.
491 0 : return;
492 : }
493 0 : lastPipelineId = state->mWrBridge->PipelineId();
494 0 : lastLayersId = aNode->GetLayersId();
495 : }
496 :
497 : // Use a 0 presShellId because when we do a lookup in this map for the
498 : // scrollbar below we don't have (or care about) the presShellId.
499 0 : ScrollableLayerGuid guid(lastLayersId, 0, apzc->GetGuid().mScrollId);
500 0 : httnMap.emplace(guid, aNode);
501 :
502 0 : ParentLayerPoint layerTranslation = apzc->GetCurrentAsyncTransform(
503 0 : AsyncPanZoomController::eForCompositing).mTranslation;
504 : // The positive translation means the painted content is supposed to
505 : // move down (or to the right), and that corresponds to a reduction in
506 : // the scroll offset. Since we are effectively giving WR the async
507 : // scroll delta here, we want to negate the translation.
508 0 : ParentLayerPoint asyncScrollDelta = -layerTranslation;
509 0 : aWrApi->UpdateScrollPosition(lastPipelineId, apzc->GetGuid().mScrollId,
510 0 : wr::ToWrPoint(asyncScrollDelta));
511 :
512 0 : apzc->ReportCheckerboard(aSampleTime);
513 0 : activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
514 0 : });
515 :
516 : // Now we iterate over the nodes again, and generate the transforms needed
517 : // for scrollbar thumbs. Although we *could* do this as part of the previous
518 : // iteration, it's cleaner and more efficient to do it as a separate pass
519 : // because now we have a populated httnMap which allows O(log n) lookup here,
520 : // resulting in O(n log n) runtime.
521 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
522 0 : [&](HitTestingTreeNode* aNode)
523 : {
524 0 : if (!aNode->IsScrollThumbNode()) {
525 0 : return;
526 : }
527 0 : ScrollableLayerGuid guid(aNode->GetLayersId(), 0, aNode->GetScrollTargetId());
528 0 : auto it = httnMap.find(guid);
529 0 : if (it == httnMap.end()) {
530 : // A scrollbar for content which didn't have an APZC. Possibly the
531 : // content isn't layerized. Regardless, we can't async-scroll it so
532 : // we can skip the async transform on the scrollbar.
533 0 : return;
534 : }
535 :
536 0 : HitTestingTreeNode* scrollTargetNode = it->second;
537 0 : AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
538 0 : MOZ_ASSERT(scrollTargetApzc);
539 : LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
540 0 : [&](const FrameMetrics& aMetrics) {
541 : return AsyncCompositionManager::ComputeTransformForScrollThumb(
542 0 : aNode->GetTransform() * AsyncTransformMatrix(),
543 0 : scrollTargetNode->GetTransform().ToUnknownMatrix(),
544 0 : scrollTargetApzc,
545 : aMetrics,
546 : aNode->GetScrollThumbData(),
547 0 : scrollTargetNode->IsAncestorOf(aNode),
548 : nullptr);
549 0 : });
550 0 : aTransformArray.AppendElement(wr::ToWrTransformProperty(
551 0 : aNode->GetScrollbarAnimationId(),
552 0 : transform));
553 0 : });
554 :
555 0 : return activeAnimations;
556 : }
557 :
558 : // Compute the clip region to be used for a layer with an APZC. This function
559 : // is only called for layers which actually have scrollable metrics and an APZC.
560 : template<class ScrollNode> static ParentLayerIntRegion
561 39 : ComputeClipRegion(GeckoContentController* aController,
562 : const ScrollNode& aLayer)
563 : {
564 39 : ParentLayerIntRegion clipRegion;
565 39 : if (aLayer.GetClipRect()) {
566 11 : clipRegion = *aLayer.GetClipRect();
567 : } else {
568 : // if there is no clip on this layer (which should only happen for the
569 : // root scrollable layer in a process, or for some of the LayerMetrics
570 : // expansions of a multi-metrics layer), fall back to using the comp
571 : // bounds which should be equivalent.
572 28 : clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds());
573 : }
574 :
575 39 : return clipRegion;
576 : }
577 :
578 : template<class ScrollNode> void
579 39 : APZCTreeManager::PrintAPZCInfo(const ScrollNode& aLayer,
580 : const AsyncPanZoomController* apzc)
581 : {
582 39 : const FrameMetrics& metrics = aLayer.Metrics();
583 78 : mApzcTreeLog << "APZC " << apzc->GetGuid()
584 78 : << "\tcb=" << metrics.GetCompositionBounds()
585 78 : << "\tsr=" << metrics.GetScrollableRect()
586 117 : << (aLayer.IsScrollInfoLayer() ? "\tscrollinfo" : "")
587 117 : << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
588 78 : << aLayer.Metadata().GetContentDescription().get();
589 39 : }
590 :
591 : void
592 211 : APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
593 : HitTestingTreeNode* aParent,
594 : HitTestingTreeNode* aNextSibling)
595 : {
596 211 : if (aNextSibling) {
597 93 : aNextSibling->SetPrevSibling(aNode);
598 118 : } else if (aParent) {
599 90 : aParent->SetLastChild(aNode);
600 : } else {
601 28 : MOZ_ASSERT(!mRootNode);
602 28 : mRootNode = aNode;
603 28 : aNode->MakeRoot();
604 : }
605 211 : }
606 :
607 : template<class ScrollNode> static EventRegions
608 211 : GetEventRegions(const ScrollNode& aLayer)
609 : {
610 211 : if (aLayer.IsScrollInfoLayer()) {
611 0 : ParentLayerIntRect compositionBounds(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
612 0 : nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
613 0 : EventRegions eventRegions(hitRegion);
614 0 : eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
615 0 : return eventRegions;
616 : }
617 211 : return aLayer.GetEventRegions();
618 : }
619 :
620 :
621 :
622 : already_AddRefed<HitTestingTreeNode>
623 177 : APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState,
624 : AsyncPanZoomController* aApzc,
625 : uint64_t aLayersId)
626 : {
627 : // Find a node without an APZC and return it. Note that unless the layer tree
628 : // actually changes, this loop should generally do an early-return on the
629 : // first iteration, so it should be cheap in the common case.
630 177 : for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
631 166 : RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
632 166 : if (!node->IsPrimaryHolder()) {
633 166 : aState.mNodesToDestroy.RemoveElement(node);
634 166 : node->RecycleWith(aApzc, aLayersId);
635 166 : return node.forget();
636 : }
637 : }
638 22 : RefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false, aLayersId);
639 11 : return node.forget();
640 : }
641 :
642 : template<class ScrollNode> static EventRegionsOverride
643 211 : GetEventRegionsOverride(HitTestingTreeNode* aParent,
644 : const ScrollNode& aLayer)
645 : {
646 : // Make it so that if the flag is set on the layer tree, it automatically
647 : // propagates to all the nodes in the corresponding subtree rooted at that
648 : // layer in the hit-test tree. This saves having to walk up the tree every
649 : // we want to see if a hit-test node is affected by this flag.
650 211 : EventRegionsOverride result = aLayer.GetEventRegionsOverride();
651 211 : if (aParent) {
652 183 : result |= aParent->GetEventRegionsOverride();
653 : }
654 211 : return result;
655 : }
656 :
657 : void
658 0 : APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
659 : const AsyncDragMetrics& aDragMetrics)
660 : {
661 :
662 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
663 0 : if (!apzc) {
664 0 : NotifyScrollbarDragRejected(aGuid);
665 0 : return;
666 : }
667 :
668 0 : uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
669 0 : mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
670 : }
671 :
672 : void
673 0 : APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const
674 : {
675 0 : const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aGuid.mLayersId);
676 0 : MOZ_ASSERT(state && state->mController);
677 0 : state->mController->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
678 0 : }
679 :
680 : template<class ScrollNode> HitTestingTreeNode*
681 211 : APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
682 : const FrameMetrics& aMetrics,
683 : uint64_t aLayersId,
684 : const gfx::Matrix4x4& aAncestorTransform,
685 : HitTestingTreeNode* aParent,
686 : HitTestingTreeNode* aNextSibling,
687 : TreeBuildingState& aState)
688 : {
689 211 : mTreeLock.AssertCurrentThreadOwns();
690 :
691 211 : bool needsApzc = true;
692 211 : if (!aMetrics.IsScrollable()) {
693 172 : needsApzc = false;
694 : }
695 :
696 211 : const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
697 211 : if (!(state && state->mController.get())) {
698 0 : needsApzc = false;
699 : }
700 :
701 422 : RefPtr<HitTestingTreeNode> node = nullptr;
702 211 : if (!needsApzc) {
703 : // Note: if layer properties must be propagated to nodes, RecvUpdate in
704 : // LayerTransactionParent.cpp must ensure that APZ will be notified
705 : // when those properties change.
706 172 : node = RecycleOrCreateNode(aState, nullptr, aLayersId);
707 172 : AttachNodeToTree(node, aParent, aNextSibling);
708 172 : node->SetHitTestData(
709 : GetEventRegions(aLayer),
710 : aLayer.GetVisibleRegion(),
711 : aLayer.GetTransformTyped(),
712 344 : aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
713 344 : GetEventRegionsOverride(aParent, aLayer));
714 172 : node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
715 : aLayer.GetScrollbarAnimationId(),
716 : aLayer.GetScrollThumbData(),
717 : aLayer.IsScrollbarContainer());
718 172 : node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
719 172 : return node;
720 : }
721 :
722 39 : AsyncPanZoomController* apzc = nullptr;
723 : // If we get here, aLayer is a scrollable layer and somebody
724 : // has registered a GeckoContentController for it, so we need to ensure
725 : // it has an APZC instance to manage its scrolling.
726 :
727 : // aState.mApzcMap allows reusing the exact same APZC instance for different layers
728 : // with the same FrameMetrics data. This is needed because in some cases content
729 : // that is supposed to scroll together is split into multiple layers because of
730 : // e.g. non-scrolling content interleaved in z-index order.
731 78 : ScrollableLayerGuid guid(aLayersId, aMetrics);
732 39 : auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
733 39 : if (!insertResult.second) {
734 5 : apzc = insertResult.first->second;
735 5 : PrintAPZCInfo(aLayer, apzc);
736 : }
737 : APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
738 :
739 : // If we haven't encountered a layer already with the same metrics, then we need to
740 : // do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
741 : // below.
742 39 : if (apzc == nullptr) {
743 34 : apzc = aLayer.GetApzc();
744 :
745 : // If the content represented by the scrollable layer has changed (which may
746 : // be possible because of DLBI heuristics) then we don't want to keep using
747 : // the same old APZC for the new content. Also, when reparenting a tab into a
748 : // new window a layer might get moved to a different layer tree with a
749 : // different APZCTreeManager. In these cases we don't want to reuse the same
750 : // APZC, so null it out so we run through the code to find another one or
751 : // create one.
752 34 : if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
753 0 : apzc = nullptr;
754 : }
755 :
756 : // See if we can find an APZC from the previous tree that matches the
757 : // ScrollableLayerGuid from this layer. If there is one, then we know that
758 : // the layout of the page changed causing the layer tree to be rebuilt, but
759 : // the underlying content for the APZC is still there somewhere. Therefore,
760 : // we want to find the APZC instance and continue using it here.
761 : //
762 : // We particularly want to find the primary-holder node from the previous
763 : // tree that matches, because we don't want that node to get destroyed. If
764 : // it does get destroyed, then the APZC will get destroyed along with it by
765 : // definition, but we want to keep that APZC around in the new tree.
766 : // We leave non-primary-holder nodes in the destroy list because we don't
767 : // care about those nodes getting destroyed.
768 40 : for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
769 44 : RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
770 38 : if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) {
771 32 : node = n;
772 32 : if (apzc != nullptr) {
773 : // If there is an APZC already then it should match the one from the
774 : // old primary-holder node
775 32 : MOZ_ASSERT(apzc == node->GetApzc());
776 : }
777 32 : apzc = node->GetApzc();
778 32 : break;
779 : }
780 : }
781 :
782 : // The APZC we get off the layer may have been destroyed previously if the
783 : // layer was inactive or omitted from the layer tree for whatever reason
784 : // from a layers update. If it later comes back it will have a reference to
785 : // a destroyed APZC and so we need to throw that out and make a new one.
786 34 : bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
787 34 : if (newApzc) {
788 2 : MOZ_ASSERT(aState.mLayerTreeState);
789 2 : apzc = NewAPZCInstance(aLayersId, state->mController);
790 2 : apzc->SetCompositorController(aState.mLayerTreeState->GetCompositorController());
791 2 : if (state->mCrossProcessParent) {
792 1 : apzc->SetMetricsSharingController(state->CrossProcessSharingController());
793 : } else {
794 1 : apzc->SetMetricsSharingController(aState.mLayerTreeState->InProcessSharingController());
795 : }
796 2 : MOZ_ASSERT(node == nullptr);
797 2 : node = new HitTestingTreeNode(apzc, true, aLayersId);
798 : } else {
799 : // If we are re-using a node for this layer clear the tree pointers
800 : // so that it doesn't continue pointing to nodes that might no longer
801 : // be in the tree. These pointers will get reset properly as we continue
802 : // building the tree. Also remove it from the set of nodes that are going
803 : // to be destroyed, because it's going to remain active.
804 32 : aState.mNodesToDestroy.RemoveElement(node);
805 32 : node->SetPrevSibling(nullptr);
806 32 : node->SetLastChild(nullptr);
807 : }
808 :
809 : APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
810 :
811 34 : apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
812 34 : aLayersId == aState.mOriginatingLayersId);
813 :
814 : // Since this is the first time we are encountering an APZC with this guid,
815 : // the node holding it must be the primary holder. It may be newly-created
816 : // or not, depending on whether it went through the newApzc branch above.
817 34 : MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
818 :
819 68 : ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
820 34 : node->SetHitTestData(
821 : GetEventRegions(aLayer),
822 : aLayer.GetVisibleRegion(),
823 : aLayer.GetTransformTyped(),
824 : Some(clipRegion),
825 68 : GetEventRegionsOverride(aParent, aLayer));
826 34 : apzc->SetAncestorTransform(aAncestorTransform);
827 :
828 34 : PrintAPZCInfo(aLayer, apzc);
829 :
830 : // Bind the APZC instance into the tree of APZCs
831 34 : AttachNodeToTree(node, aParent, aNextSibling);
832 :
833 : // For testing, log the parent scroll id of every APZC that has a
834 : // parent. This allows test code to reconstruct the APZC tree.
835 : // Note that we currently only do this for APZCs in the layer tree
836 : // that originated the update, because the only identifying information
837 : // we are logging about APZCs is the scroll id, and otherwise we could
838 : // confuse APZCs from different layer trees with the same scroll id.
839 34 : if (aLayersId == aState.mOriginatingLayersId) {
840 26 : if (apzc->HasNoParentWithSameLayersId()) {
841 26 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
842 : "hasNoParentWithSameLayersId", true);
843 : } else {
844 0 : MOZ_ASSERT(apzc->GetParent());
845 0 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
846 0 : "parentScrollId", apzc->GetParent()->GetGuid().mScrollId);
847 : }
848 26 : if (aMetrics.IsRootContent()) {
849 2 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
850 : "isRootContent", true);
851 : }
852 : // Note that the async scroll offset is in ParentLayer pixels
853 26 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "asyncScrollOffset",
854 52 : apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForHitTesting));
855 : }
856 :
857 34 : if (newApzc) {
858 2 : auto it = mZoomConstraints.find(guid);
859 2 : if (it != mZoomConstraints.end()) {
860 : // We have a zoomconstraints for this guid, apply it.
861 1 : apzc->UpdateZoomConstraints(it->second);
862 1 : } else if (!apzc->HasNoParentWithSameLayersId()) {
863 : // This is a sub-APZC, so inherit the zoom constraints from its parent.
864 : // This ensures that if e.g. user-scalable=no was specified, none of the
865 : // APZCs for that subtree allow double-tap to zoom.
866 0 : apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
867 : }
868 : // Otherwise, this is the root of a layers id, but we didn't have a saved
869 : // zoom constraints. Leave it empty for now.
870 : }
871 :
872 : // Add a guid -> APZC mapping for the newly created APZC.
873 34 : insertResult.first->second = apzc;
874 : } else {
875 : // We already built an APZC earlier in this tree walk, but we have another layer
876 : // now that will also be using that APZC. The hit-test region on the APZC needs
877 : // to be updated to deal with the new layer's hit region.
878 :
879 5 : node = RecycleOrCreateNode(aState, apzc, aLayersId);
880 5 : AttachNodeToTree(node, aParent, aNextSibling);
881 :
882 : // Even though different layers associated with a given APZC may be at
883 : // different levels in the layer tree (e.g. one being an uncle of another),
884 : // we require from Layout that the CSS transforms up to their common
885 : // ancestor be roughly the same. There are cases in which the transforms
886 : // are not exactly the same, for example if the parent is container layer
887 : // for an opacity, and this container layer has a resolution-induced scale
888 : // as its base transform and a prescale that is supposed to undo that scale.
889 : // Due to floating point inaccuracies those transforms can end up not quite
890 : // canceling each other. That's why we're using a fuzzy comparison here
891 : // instead of an exact one.
892 5 : MOZ_ASSERT(aAncestorTransform.FuzzyEqualsMultiplicative(apzc->GetAncestorTransform()));
893 :
894 10 : ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
895 5 : node->SetHitTestData(
896 : GetEventRegions(aLayer),
897 : aLayer.GetVisibleRegion(),
898 : aLayer.GetTransformTyped(),
899 : Some(clipRegion),
900 10 : GetEventRegionsOverride(aParent, aLayer));
901 : }
902 :
903 : // Note: if layer properties must be propagated to nodes, RecvUpdate in
904 : // LayerTransactionParent.cpp must ensure that APZ will be notified
905 : // when those properties change.
906 39 : node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
907 : aLayer.GetScrollbarAnimationId(),
908 : aLayer.GetScrollThumbData(),
909 : aLayer.IsScrollbarContainer());
910 39 : node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
911 39 : return node;
912 : }
913 :
914 : template<typename PanGestureOrScrollWheelInput>
915 : static bool
916 0 : WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
917 : {
918 0 : if (!NS_IsMainThread()) {
919 0 : return true;
920 : }
921 :
922 0 : WidgetWheelEvent wheelEvent = aPanInput.ToWidgetWheelEvent(nullptr);
923 0 : return IAPZCTreeManager::WillHandleWheelEvent(&wheelEvent);
924 : }
925 :
926 : void
927 0 : APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
928 : {
929 : // Previously, paints were throttled and therefore this method was used to
930 : // ensure any pending paints were flushed. Now, paints are flushed
931 : // immediately, so it is safe to simply send a notification now.
932 : APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId);
933 : const LayerTreeState* state =
934 0 : CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
935 0 : MOZ_ASSERT(state && state->mController);
936 0 : state->mController->DispatchToRepaintThread(
937 0 : NewRunnableMethod("layers::GeckoContentController::NotifyFlushComplete",
938 : state->mController,
939 0 : &GeckoContentController::NotifyFlushComplete));
940 0 : }
941 :
942 : nsEventStatus
943 4 : APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
944 : ScrollableLayerGuid* aOutTargetGuid,
945 : uint64_t* aOutInputBlockId)
946 : {
947 4 : APZThreadUtils::AssertOnControllerThread();
948 :
949 : // Use a RAII class for updating the focus sequence number of this event
950 8 : AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
951 :
952 : #if defined(MOZ_WIDGET_ANDROID)
953 : MOZ_ASSERT(mToolbarAnimator);
954 : ScreenPoint scrollOffset;
955 : {
956 : MutexAutoLock lock(mTreeLock);
957 : RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
958 : if (apzc) {
959 : scrollOffset = ViewAs<ScreenPixel>(apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForHitTesting),
960 : PixelCastJustification::ScreenIsParentLayerForRoot);
961 : }
962 : }
963 : nsEventStatus isConsumed = mToolbarAnimator->ReceiveInputEvent(aEvent, scrollOffset);
964 : // Check if the mToolbarAnimator consumed the event.
965 : if (isConsumed == nsEventStatus_eConsumeNoDefault) {
966 : APZCTM_LOG("Dynamic toolbar consumed event");
967 : return isConsumed;
968 : }
969 : #endif // (MOZ_WIDGET_ANDROID)
970 :
971 : // Initialize aOutInputBlockId to a sane value, and then later we overwrite
972 : // it if the input event goes into a block.
973 4 : if (aOutInputBlockId) {
974 4 : *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
975 : }
976 4 : nsEventStatus result = nsEventStatus_eIgnore;
977 4 : HitTestResult hitResult = HitNothing;
978 4 : switch (aEvent.mInputType) {
979 : case MULTITOUCH_INPUT: {
980 0 : MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
981 0 : result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
982 0 : break;
983 : } case MOUSE_INPUT: {
984 4 : MouseInput& mouseInput = aEvent.AsMouseInput();
985 4 : mouseInput.mHandledByAPZ = true;
986 :
987 4 : bool startsDrag = DragTracker::StartsDrag(mouseInput);
988 4 : if (startsDrag) {
989 : // If this is the start of a drag we need to unambiguously know if it's
990 : // going to land on a scrollbar or not. We can't apply an untransform
991 : // here without knowing that, so we need to ensure the untransform is
992 : // a no-op.
993 0 : FlushRepaintsToClearScreenToGeckoTransform();
994 : }
995 :
996 4 : HitTestingTreeNode* hitScrollbarNode = nullptr;
997 8 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
998 8 : &hitResult, &hitScrollbarNode);
999 4 : bool hitScrollbar = hitScrollbarNode;
1000 :
1001 : // When the mouse is outside the window we still want to handle dragging
1002 : // but we won't find an APZC. Fallback to root APZC then.
1003 : { // scope lock
1004 8 : MutexAutoLock lock(mTreeLock);
1005 4 : if (!apzc && mRootNode) {
1006 0 : apzc = mRootNode->GetApzc();
1007 : }
1008 : }
1009 :
1010 4 : if (apzc) {
1011 4 : bool targetConfirmed = (hitResult != HitNothing && hitResult != HitDispatchToContentRegion);
1012 4 : bool apzDragEnabled = gfxPrefs::APZDragEnabled();
1013 4 : if (apzDragEnabled && hitScrollbar) {
1014 : // If scrollbar dragging is enabled and we hit a scrollbar, wait
1015 : // for the main-thread confirmation because it contains drag metrics
1016 : // that we need.
1017 0 : targetConfirmed = false;
1018 : }
1019 4 : result = mInputQueue->ReceiveInputEvent(
1020 : apzc, targetConfirmed,
1021 4 : mouseInput, aOutInputBlockId);
1022 :
1023 : // If we're starting an async scrollbar drag
1024 8 : if (apzDragEnabled && startsDrag && hitScrollbarNode &&
1025 0 : hitScrollbarNode->IsScrollThumbNode() &&
1026 4 : hitScrollbarNode->GetScrollThumbData().mIsAsyncDraggable &&
1027 0 : mInputQueue->GetCurrentDragBlock()) {
1028 0 : DragBlockState* dragBlock = mInputQueue->GetCurrentDragBlock();
1029 0 : const ScrollThumbData& thumbData = hitScrollbarNode->GetScrollThumbData();
1030 :
1031 : // Record the thumb's position at the start of the drag.
1032 : // We snap back to this position if, during the drag, the mouse
1033 : // gets sufficiently far away from the scrollbar.
1034 0 : dragBlock->SetInitialThumbPos(thumbData.mThumbStart);
1035 :
1036 : // Under some conditions, we can confirm the drag block right away.
1037 : // Otherwise, we have to wait for a main-thread confirmation.
1038 0 : if (gfxPrefs::APZDragInitiationEnabled() &&
1039 : // check that the scrollbar's target scroll frame is layerized
1040 0 : hitScrollbarNode->GetScrollTargetId() == apzc->GetGuid().mScrollId &&
1041 0 : !apzc->IsScrollInfoLayer()) {
1042 0 : uint64_t dragBlockId = dragBlock->GetBlockId();
1043 : // AsyncPanZoomController::HandleInputEvent() will call
1044 : // TransformToLocal() on the event, but we need its mLocalOrigin now
1045 : // to compute a drag start offset for the AsyncDragMetrics.
1046 0 : mouseInput.TransformToLocal(apzc->GetTransformToThis());
1047 : CSSCoord dragStart = apzc->ConvertScrollbarPoint(
1048 0 : mouseInput.mLocalOrigin, thumbData);
1049 : // ConvertScrollbarPoint() got the drag start offset relative to
1050 : // the scroll track. Now get it relative to the thumb.
1051 : // ScrollThumbData::mThumbStart stores the offset of the thumb
1052 : // relative to the scroll track at the time of the last paint.
1053 : // Since that paint, the thumb may have acquired an async transform
1054 : // due to async scrolling, so look that up and apply it.
1055 0 : LayerToParentLayerMatrix4x4 thumbTransform;
1056 : {
1057 0 : MutexAutoLock lock(mTreeLock);
1058 0 : thumbTransform = ComputeTransformForNode(hitScrollbarNode);
1059 : }
1060 : // Only consider the translation, since we do not support both
1061 : // zooming and scrollbar dragging on any platform.
1062 : CSSCoord thumbStart = thumbData.mThumbStart
1063 0 : + ((thumbData.mDirection == ScrollDirection::HORIZONTAL)
1064 0 : ? thumbTransform._41 : thumbTransform._42);
1065 0 : dragStart -= thumbStart;
1066 :
1067 : // Content can't prevent scrollbar dragging with preventDefault(),
1068 : // so we don't need to wait for a content response. It's important
1069 : // to do this before calling ConfirmDragBlock() since that can
1070 : // potentially process and consume the block.
1071 0 : dragBlock->SetContentResponse(false);
1072 :
1073 0 : mInputQueue->ConfirmDragBlock(
1074 : dragBlockId, apzc,
1075 0 : AsyncDragMetrics(apzc->GetGuid().mScrollId,
1076 0 : apzc->GetGuid().mPresShellId,
1077 : dragBlockId,
1078 : dragStart,
1079 0 : thumbData.mDirection));
1080 : }
1081 : }
1082 :
1083 4 : if (result == nsEventStatus_eConsumeDoDefault) {
1084 : // This input event is part of a drag block, so whether or not it is
1085 : // directed at a scrollbar depends on whether the drag block started
1086 : // on a scrollbar.
1087 0 : hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
1088 : }
1089 :
1090 : // Update the out-parameters so they are what the caller expects.
1091 4 : apzc->GetGuid(aOutTargetGuid);
1092 :
1093 4 : if (!hitScrollbar) {
1094 : // The input was not targeted at a scrollbar, so we untransform it
1095 : // like we do for other content. Scrollbars are "special" because they
1096 : // have special handling in AsyncCompositionManager when resolution is
1097 : // applied. TODO: we should find a better way to deal with this.
1098 4 : ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
1099 4 : ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
1100 4 : ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
1101 : Maybe<ScreenPoint> untransformedRefPoint = UntransformBy(
1102 8 : outTransform, mouseInput.mOrigin);
1103 4 : if (untransformedRefPoint) {
1104 4 : mouseInput.mOrigin = *untransformedRefPoint;
1105 : }
1106 : } else {
1107 : // Likewise, if the input was targeted at a scrollbar, we don't want to
1108 : // apply the callback transform in the main thread, so we remove the
1109 : // scrollid from the guid. We need to keep the layersId intact so
1110 : // that the response from the child process doesn't get discarded.
1111 0 : aOutTargetGuid->mScrollId = FrameMetrics::NULL_SCROLL_ID;
1112 : }
1113 : }
1114 4 : break;
1115 : } case SCROLLWHEEL_INPUT: {
1116 0 : FlushRepaintsToClearScreenToGeckoTransform();
1117 :
1118 0 : ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
1119 0 : wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
1120 0 : if (!wheelInput.mHandledByAPZ) {
1121 0 : return result;
1122 : }
1123 :
1124 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
1125 0 : &hitResult);
1126 0 : if (apzc) {
1127 0 : MOZ_ASSERT(hitResult != HitNothing);
1128 :
1129 : // For wheel events, the call to ReceiveInputEvent below may result in
1130 : // scrolling, which changes the async transform. However, the event we
1131 : // want to pass to gecko should be the pre-scroll event coordinates,
1132 : // transformed into the gecko space. (pre-scroll because the mouse
1133 : // cursor is stationary during wheel scrolling, unlike touchmove
1134 : // events). Since we just flushed the pending repaints the transform to
1135 : // gecko space should only consist of overscroll-cancelling transforms.
1136 0 : ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
1137 0 : * GetApzcToGeckoTransform(apzc);
1138 : Maybe<ScreenPoint> untransformedOrigin = UntransformBy(
1139 0 : transformToGecko, wheelInput.mOrigin);
1140 :
1141 0 : if (!untransformedOrigin) {
1142 0 : return result;
1143 : }
1144 :
1145 0 : result = mInputQueue->ReceiveInputEvent(
1146 : apzc,
1147 : /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
1148 0 : wheelInput, aOutInputBlockId);
1149 :
1150 : // Update the out-parameters so they are what the caller expects.
1151 0 : apzc->GetGuid(aOutTargetGuid);
1152 0 : wheelInput.mOrigin = *untransformedOrigin;
1153 : }
1154 0 : break;
1155 : } case PANGESTURE_INPUT: {
1156 0 : FlushRepaintsToClearScreenToGeckoTransform();
1157 :
1158 0 : PanGestureInput& panInput = aEvent.AsPanGestureInput();
1159 0 : panInput.mHandledByAPZ = WillHandleInput(panInput);
1160 0 : if (!panInput.mHandledByAPZ) {
1161 0 : return result;
1162 : }
1163 :
1164 : // If/when we enable support for pan inputs off-main-thread, we'll need
1165 : // to duplicate this EventStateManager code or something. See the call to
1166 : // GetUserPrefsForWheelEvent in IAPZCTreeManager.cpp for why these fields
1167 : // are stored separately.
1168 0 : MOZ_ASSERT(NS_IsMainThread());
1169 0 : WidgetWheelEvent wheelEvent = panInput.ToWidgetWheelEvent(nullptr);
1170 0 : EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
1171 : &panInput.mUserDeltaMultiplierX,
1172 0 : &panInput.mUserDeltaMultiplierY);
1173 :
1174 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
1175 0 : &hitResult);
1176 0 : if (apzc) {
1177 0 : MOZ_ASSERT(hitResult != HitNothing);
1178 :
1179 : // For pan gesture events, the call to ReceiveInputEvent below may result in
1180 : // scrolling, which changes the async transform. However, the event we
1181 : // want to pass to gecko should be the pre-scroll event coordinates,
1182 : // transformed into the gecko space. (pre-scroll because the mouse
1183 : // cursor is stationary during pan gesture scrolling, unlike touchmove
1184 : // events). Since we just flushed the pending repaints the transform to
1185 : // gecko space should only consist of overscroll-cancelling transforms.
1186 0 : ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
1187 0 : * GetApzcToGeckoTransform(apzc);
1188 : Maybe<ScreenPoint> untransformedStartPoint = UntransformBy(
1189 0 : transformToGecko, panInput.mPanStartPoint);
1190 : Maybe<ScreenPoint> untransformedDisplacement = UntransformVector(
1191 0 : transformToGecko, panInput.mPanDisplacement, panInput.mPanStartPoint);
1192 :
1193 0 : if (!untransformedStartPoint || !untransformedDisplacement) {
1194 0 : return result;
1195 : }
1196 :
1197 0 : result = mInputQueue->ReceiveInputEvent(
1198 : apzc,
1199 : /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
1200 0 : panInput, aOutInputBlockId);
1201 :
1202 : // Update the out-parameters so they are what the caller expects.
1203 0 : apzc->GetGuid(aOutTargetGuid);
1204 0 : panInput.mPanStartPoint = *untransformedStartPoint;
1205 0 : panInput.mPanDisplacement = *untransformedDisplacement;
1206 : }
1207 0 : break;
1208 : } case PINCHGESTURE_INPUT: { // note: no one currently sends these
1209 0 : PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
1210 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint,
1211 0 : &hitResult);
1212 0 : if (apzc) {
1213 0 : MOZ_ASSERT(hitResult != HitNothing);
1214 :
1215 0 : ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
1216 0 : * GetApzcToGeckoTransform(apzc);
1217 : Maybe<ScreenPoint> untransformedFocusPoint = UntransformBy(
1218 0 : outTransform, pinchInput.mFocusPoint);
1219 :
1220 0 : if (!untransformedFocusPoint) {
1221 0 : return result;
1222 : }
1223 :
1224 0 : result = mInputQueue->ReceiveInputEvent(
1225 : apzc,
1226 : /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
1227 0 : pinchInput, aOutInputBlockId);
1228 :
1229 : // Update the out-parameters so they are what the caller expects.
1230 0 : apzc->GetGuid(aOutTargetGuid);
1231 0 : pinchInput.mFocusPoint = *untransformedFocusPoint;
1232 : }
1233 0 : break;
1234 : } case TAPGESTURE_INPUT: { // note: no one currently sends these
1235 0 : TapGestureInput& tapInput = aEvent.AsTapGestureInput();
1236 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(tapInput.mPoint,
1237 0 : &hitResult);
1238 0 : if (apzc) {
1239 0 : MOZ_ASSERT(hitResult != HitNothing);
1240 :
1241 0 : ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
1242 0 : * GetApzcToGeckoTransform(apzc);
1243 : Maybe<ScreenIntPoint> untransformedPoint =
1244 0 : UntransformBy(outTransform, tapInput.mPoint);
1245 :
1246 0 : if (!untransformedPoint) {
1247 0 : return result;
1248 : }
1249 :
1250 0 : result = mInputQueue->ReceiveInputEvent(
1251 : apzc,
1252 : /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
1253 0 : tapInput, aOutInputBlockId);
1254 :
1255 : // Update the out-parameters so they are what the caller expects.
1256 0 : apzc->GetGuid(aOutTargetGuid);
1257 0 : tapInput.mPoint = *untransformedPoint;
1258 : }
1259 0 : break;
1260 : } case KEYBOARD_INPUT: {
1261 : // Disable async keyboard scrolling when accessibility.browsewithcaret is enabled
1262 0 : if (!gfxPrefs::APZKeyboardEnabled() ||
1263 0 : gfxPrefs::AccessibilityBrowseWithCaret()) {
1264 : APZ_KEY_LOG("Skipping key input from invalid prefs\n");
1265 0 : return result;
1266 : }
1267 :
1268 0 : KeyboardInput& keyInput = aEvent.AsKeyboardInput();
1269 :
1270 : // Try and find a matching shortcut for this keyboard input
1271 0 : Maybe<KeyboardShortcut> shortcut = mKeyboardMap.FindMatch(keyInput);
1272 :
1273 0 : if (!shortcut) {
1274 : APZ_KEY_LOG("Skipping key input with no shortcut\n");
1275 :
1276 : // If we don't have a shortcut for this key event, then we can keep our focus
1277 : // only if we know there are no key event listeners for this target
1278 0 : if (mFocusState.CanIgnoreKeyboardShortcutMisses()) {
1279 0 : focusSetter.MarkAsNonFocusChanging();
1280 : }
1281 0 : return result;
1282 : }
1283 :
1284 : // Check if this shortcut needs to be dispatched to content. Anything matching
1285 : // this is assumed to be able to change focus.
1286 0 : if (shortcut->mDispatchToContent) {
1287 : APZ_KEY_LOG("Skipping key input with dispatch-to-content shortcut\n");
1288 0 : return result;
1289 : }
1290 :
1291 : // We know we have an action to execute on whatever is the current focus target
1292 0 : const KeyboardScrollAction& action = shortcut->mAction;
1293 :
1294 : // The current focus target depends on which direction the scroll is to happen
1295 0 : Maybe<ScrollableLayerGuid> targetGuid;
1296 0 : switch (action.mType)
1297 : {
1298 : case KeyboardScrollAction::eScrollCharacter: {
1299 0 : targetGuid = mFocusState.GetHorizontalTarget();
1300 0 : break;
1301 : }
1302 : case KeyboardScrollAction::eScrollLine:
1303 : case KeyboardScrollAction::eScrollPage:
1304 : case KeyboardScrollAction::eScrollComplete: {
1305 0 : targetGuid = mFocusState.GetVerticalTarget();
1306 0 : break;
1307 : }
1308 : }
1309 :
1310 : // If we don't have a scroll target then either we have a stale focus target,
1311 : // the focused element has event listeners, or the focused element doesn't have a
1312 : // layerized scroll frame. In any case we need to dispatch to content.
1313 0 : if (!targetGuid) {
1314 : APZ_KEY_LOG("Skipping key input with no current focus target\n");
1315 0 : return result;
1316 : }
1317 :
1318 0 : RefPtr<AsyncPanZoomController> targetApzc = GetTargetAPZC(targetGuid->mLayersId,
1319 0 : targetGuid->mScrollId);
1320 :
1321 0 : if (!targetApzc) {
1322 : APZ_KEY_LOG("Skipping key input with focus target but no APZC\n");
1323 0 : return result;
1324 : }
1325 :
1326 : // Attach the keyboard scroll action to the input event for processing
1327 : // by the input queue.
1328 0 : keyInput.mAction = action;
1329 :
1330 : APZ_KEY_LOG("Dispatching key input with apzc=%p\n",
1331 : targetApzc.get());
1332 :
1333 : // Dispatch the event to the input queue.
1334 0 : result = mInputQueue->ReceiveInputEvent(
1335 : targetApzc,
1336 : /* aTargetConfirmed = */ true,
1337 0 : keyInput, aOutInputBlockId);
1338 :
1339 : // Any keyboard event that is dispatched to the input queue at this point
1340 : // should have been consumed
1341 0 : MOZ_ASSERT(result == nsEventStatus_eConsumeNoDefault);
1342 :
1343 0 : keyInput.mHandledByAPZ = true;
1344 0 : focusSetter.MarkAsNonFocusChanging();
1345 :
1346 0 : break;
1347 : }
1348 : }
1349 4 : return result;
1350 : }
1351 :
1352 : static TouchBehaviorFlags
1353 0 : ConvertToTouchBehavior(HitTestResult result)
1354 : {
1355 0 : switch (result) {
1356 : case HitNothing:
1357 0 : return AllowedTouchBehavior::NONE;
1358 : case HitLayer:
1359 : return AllowedTouchBehavior::VERTICAL_PAN
1360 : | AllowedTouchBehavior::HORIZONTAL_PAN
1361 : | AllowedTouchBehavior::PINCH_ZOOM
1362 0 : | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
1363 : case HitLayerTouchActionNone:
1364 0 : return AllowedTouchBehavior::NONE;
1365 : case HitLayerTouchActionPanX:
1366 0 : return AllowedTouchBehavior::HORIZONTAL_PAN;
1367 : case HitLayerTouchActionPanY:
1368 0 : return AllowedTouchBehavior::VERTICAL_PAN;
1369 : case HitLayerTouchActionPanXY:
1370 : return AllowedTouchBehavior::HORIZONTAL_PAN
1371 0 : | AllowedTouchBehavior::VERTICAL_PAN;
1372 : case HitDispatchToContentRegion:
1373 0 : return AllowedTouchBehavior::UNKNOWN;
1374 : }
1375 0 : MOZ_ASSERT_UNREACHABLE("Invalid value");
1376 : return AllowedTouchBehavior::UNKNOWN;
1377 : }
1378 :
1379 : already_AddRefed<AsyncPanZoomController>
1380 0 : APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
1381 : nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors,
1382 : HitTestResult* aOutHitResult)
1383 : {
1384 0 : RefPtr<AsyncPanZoomController> apzc;
1385 0 : if (aEvent.mTouches.Length() == 0) {
1386 0 : return apzc.forget();
1387 : }
1388 :
1389 0 : FlushRepaintsToClearScreenToGeckoTransform();
1390 :
1391 : HitTestResult hitResult;
1392 0 : apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, &hitResult);
1393 0 : if (aOutTouchBehaviors) {
1394 0 : aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
1395 : }
1396 0 : for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
1397 0 : RefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, &hitResult);
1398 0 : if (aOutTouchBehaviors) {
1399 0 : aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
1400 : }
1401 0 : apzc = GetMultitouchTarget(apzc, apzc2);
1402 : APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
1403 : }
1404 :
1405 0 : if (aOutHitResult) {
1406 : // XXX we should probably be combining the hit results from the different
1407 : // touch points somehow, instead of just using the last one.
1408 0 : *aOutHitResult = hitResult;
1409 : }
1410 0 : return apzc.forget();
1411 : }
1412 :
1413 : nsEventStatus
1414 0 : APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
1415 : ScrollableLayerGuid* aOutTargetGuid,
1416 : uint64_t* aOutInputBlockId)
1417 : {
1418 0 : aInput.mHandledByAPZ = true;
1419 0 : nsTArray<TouchBehaviorFlags> touchBehaviors;
1420 0 : if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
1421 : // If we are panned into overscroll and a second finger goes down,
1422 : // ignore that second touch point completely. The touch-start for it is
1423 : // dropped completely; subsequent touch events until the touch-end for it
1424 : // will have this touch point filtered out.
1425 : // (By contrast, if we're in overscroll but not panning, such as after
1426 : // putting two fingers down during an overscroll animation, we process the
1427 : // second touch and proceed to pinch.)
1428 0 : if (mApzcForInputBlock &&
1429 0 : mApzcForInputBlock->IsInPanningState() &&
1430 0 : BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
1431 0 : if (mRetainedTouchIdentifier == -1) {
1432 0 : mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
1433 : }
1434 0 : return nsEventStatus_eConsumeNoDefault;
1435 : }
1436 :
1437 0 : mHitResultForInputBlock = HitNothing;
1438 0 : mApzcForInputBlock = GetTouchInputBlockAPZC(aInput, &touchBehaviors, &mHitResultForInputBlock);
1439 0 : MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length());
1440 0 : for (size_t i = 0; i < touchBehaviors.Length(); i++) {
1441 : APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n", touchBehaviors[i]);
1442 0 : if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) {
1443 : // If there's any unknown items in the list, throw it out and we'll
1444 : // wait for the main thread to send us a notification.
1445 0 : touchBehaviors.Clear();
1446 0 : break;
1447 : }
1448 : }
1449 0 : } else if (mApzcForInputBlock) {
1450 : APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
1451 : }
1452 :
1453 : // If we receive a touch-cancel, it means all touches are finished, so we
1454 : // can stop ignoring any that we were ignoring.
1455 0 : if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
1456 0 : mRetainedTouchIdentifier = -1;
1457 : }
1458 :
1459 : // If we are currently ignoring any touch points, filter them out from the
1460 : // set of touch points included in this event. Note that we modify aInput
1461 : // itself, so that the touch points are also filtered out when the caller
1462 : // passes the event on to content.
1463 0 : if (mRetainedTouchIdentifier != -1) {
1464 0 : for (size_t j = 0; j < aInput.mTouches.Length(); ++j) {
1465 0 : if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
1466 0 : aInput.mTouches.RemoveElementAt(j);
1467 0 : if (!touchBehaviors.IsEmpty()) {
1468 0 : MOZ_ASSERT(touchBehaviors.Length() > j);
1469 0 : touchBehaviors.RemoveElementAt(j);
1470 : }
1471 0 : --j;
1472 : }
1473 : }
1474 0 : if (aInput.mTouches.IsEmpty()) {
1475 0 : return nsEventStatus_eConsumeNoDefault;
1476 : }
1477 : }
1478 :
1479 0 : nsEventStatus result = nsEventStatus_eIgnore;
1480 0 : if (mApzcForInputBlock) {
1481 0 : MOZ_ASSERT(mHitResultForInputBlock != HitNothing);
1482 :
1483 0 : mApzcForInputBlock->GetGuid(aOutTargetGuid);
1484 0 : uint64_t inputBlockId = 0;
1485 0 : result = mInputQueue->ReceiveInputEvent(mApzcForInputBlock,
1486 0 : /* aTargetConfirmed = */ mHitResultForInputBlock != HitDispatchToContentRegion,
1487 0 : aInput, &inputBlockId);
1488 0 : if (aOutInputBlockId) {
1489 0 : *aOutInputBlockId = inputBlockId;
1490 : }
1491 0 : if (!touchBehaviors.IsEmpty()) {
1492 0 : mInputQueue->SetAllowedTouchBehavior(inputBlockId, touchBehaviors);
1493 : }
1494 :
1495 : // For computing the event to pass back to Gecko, use up-to-date transforms
1496 : // (i.e. not anything cached in an input block).
1497 : // This ensures that transformToApzc and transformToGecko are in sync.
1498 0 : ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
1499 0 : ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
1500 0 : ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
1501 :
1502 0 : for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
1503 0 : SingleTouchData& touchData = aInput.mTouches[i];
1504 : Maybe<ScreenIntPoint> untransformedScreenPoint = UntransformBy(
1505 0 : outTransform, touchData.mScreenPoint);
1506 0 : if (!untransformedScreenPoint) {
1507 0 : return nsEventStatus_eIgnore;
1508 : }
1509 0 : touchData.mScreenPoint = *untransformedScreenPoint;
1510 : }
1511 : }
1512 :
1513 0 : mTouchCounter.Update(aInput);
1514 :
1515 : // If it's the end of the touch sequence then clear out variables so we
1516 : // don't keep dangling references and leak things.
1517 0 : if (mTouchCounter.GetActiveTouchCount() == 0) {
1518 0 : mApzcForInputBlock = nullptr;
1519 0 : mHitResultForInputBlock = HitNothing;
1520 0 : mRetainedTouchIdentifier = -1;
1521 : }
1522 :
1523 0 : return result;
1524 : }
1525 :
1526 : void
1527 6 : APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
1528 : EventMessage aEventMessage)
1529 : {
1530 6 : WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction();
1531 6 : if (!txn) {
1532 6 : return;
1533 : }
1534 :
1535 : // If the transaction has simply timed out, we don't need to do anything
1536 : // else.
1537 0 : if (txn->MaybeTimeout(TimeStamp::Now())) {
1538 0 : return;
1539 : }
1540 :
1541 0 : switch (aEventMessage) {
1542 : case eMouseMove:
1543 : case eDragOver: {
1544 :
1545 : ScreenIntPoint point =
1546 : ViewAs<ScreenPixel>(aRefPoint,
1547 0 : PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
1548 :
1549 0 : txn->OnMouseMove(point);
1550 :
1551 0 : return;
1552 : }
1553 : case eKeyPress:
1554 : case eKeyUp:
1555 : case eKeyDown:
1556 : case eMouseUp:
1557 : case eMouseDown:
1558 : case eMouseDoubleClick:
1559 : case eMouseAuxClick:
1560 : case eMouseClick:
1561 : case eContextMenu:
1562 : case eDrop:
1563 0 : txn->EndTransaction();
1564 0 : return;
1565 : default:
1566 0 : break;
1567 : }
1568 : }
1569 :
1570 : void
1571 2 : APZCTreeManager::ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
1572 : ScrollableLayerGuid* aOutTargetGuid,
1573 : uint64_t* aOutFocusSequenceNumber)
1574 : {
1575 : // Transform the aRefPoint.
1576 : // If the event hits an overscrolled APZC, instruct the caller to ignore it.
1577 2 : HitTestResult hitResult = HitNothing;
1578 2 : PixelCastJustification LDIsScreen = PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
1579 : ScreenIntPoint refPointAsScreen =
1580 2 : ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
1581 4 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(refPointAsScreen, &hitResult);
1582 2 : if (apzc) {
1583 1 : MOZ_ASSERT(hitResult != HitNothing);
1584 1 : apzc->GetGuid(aOutTargetGuid);
1585 1 : ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
1586 1 : ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
1587 1 : ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
1588 : Maybe<ScreenIntPoint> untransformedRefPoint =
1589 2 : UntransformBy(outTransform, refPointAsScreen);
1590 1 : if (untransformedRefPoint) {
1591 : *aRefPoint =
1592 1 : ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
1593 : }
1594 : }
1595 :
1596 : // Update the focus sequence number and attach it to the event
1597 2 : mFocusState.ReceiveFocusChangingEvent();
1598 2 : *aOutFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
1599 2 : }
1600 :
1601 : void
1602 0 : APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
1603 : {
1604 0 : if (mApzcForInputBlock) {
1605 0 : mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
1606 : }
1607 0 : }
1608 :
1609 : void
1610 0 : APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap)
1611 : {
1612 0 : mKeyboardMap = aKeyboardMap;
1613 0 : }
1614 :
1615 : void
1616 0 : APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
1617 : const CSSRect& aRect,
1618 : const uint32_t aFlags)
1619 : {
1620 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
1621 0 : if (apzc) {
1622 0 : apzc->ZoomToRect(aRect, aFlags);
1623 : }
1624 0 : }
1625 :
1626 : void
1627 0 : APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault)
1628 : {
1629 0 : APZThreadUtils::AssertOnControllerThread();
1630 :
1631 0 : mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
1632 0 : }
1633 :
1634 : void
1635 0 : APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
1636 : const nsTArray<ScrollableLayerGuid>& aTargets)
1637 : {
1638 0 : APZThreadUtils::AssertOnControllerThread();
1639 :
1640 0 : RefPtr<AsyncPanZoomController> target = nullptr;
1641 0 : if (aTargets.Length() > 0) {
1642 0 : target = GetTargetAPZC(aTargets[0]);
1643 : }
1644 0 : for (size_t i = 1; i < aTargets.Length(); i++) {
1645 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
1646 0 : target = GetMultitouchTarget(target, apzc);
1647 : }
1648 0 : mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
1649 0 : }
1650 :
1651 : void
1652 0 : APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget)
1653 : {
1654 0 : APZThreadUtils::AssertOnControllerThread();
1655 :
1656 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
1657 0 : mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
1658 0 : }
1659 :
1660 : void
1661 1 : APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
1662 : const Maybe<ZoomConstraints>& aConstraints)
1663 : {
1664 2 : MutexAutoLock lock(mTreeLock);
1665 2 : RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
1666 1 : MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
1667 :
1668 : // Propagate the zoom constraints down to the subtree, stopping at APZCs
1669 : // which have their own zoom constraints or are in a different layers id.
1670 1 : if (aConstraints) {
1671 : APZCTM_LOG("Recording constraints %s for guid %s\n",
1672 : Stringify(aConstraints.value()).c_str(), Stringify(aGuid).c_str());
1673 1 : mZoomConstraints[aGuid] = aConstraints.ref();
1674 : } else {
1675 : APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
1676 0 : mZoomConstraints.erase(aGuid);
1677 : }
1678 1 : if (node && aConstraints) {
1679 0 : ForEachNode<ReverseIterator>(node.get(),
1680 0 : [&aConstraints, &node, this](HitTestingTreeNode* aNode)
1681 0 : {
1682 0 : if (aNode != node) {
1683 0 : if (AsyncPanZoomController* childApzc = aNode->GetApzc()) {
1684 : // We can have subtrees with their own zoom constraints or separate layers
1685 : // id - leave these alone.
1686 0 : if (childApzc->HasNoParentWithSameLayersId() ||
1687 0 : this->mZoomConstraints.find(childApzc->GetGuid()) != this->mZoomConstraints.end()) {
1688 0 : return TraversalFlag::Skip;
1689 : }
1690 : }
1691 : }
1692 0 : if (aNode->IsPrimaryHolder()) {
1693 0 : MOZ_ASSERT(aNode->GetApzc());
1694 0 : aNode->GetApzc()->UpdateZoomConstraints(aConstraints.ref());
1695 : }
1696 0 : return TraversalFlag::Continue;
1697 0 : });
1698 : }
1699 1 : }
1700 :
1701 : void
1702 0 : APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform()
1703 : {
1704 : // As the name implies, we flush repaint requests for the entire APZ tree in
1705 : // order to clear the screen-to-gecko transform (aka the "untransform" applied
1706 : // to incoming input events before they can be passed on to Gecko).
1707 : //
1708 : // The primary reason we do this is to avoid the problem where input events,
1709 : // after being untransformed, end up hit-testing differently in Gecko. This
1710 : // might happen in cases where the input event lands on content that is async-
1711 : // scrolled into view, but Gecko still thinks it is out of view given the
1712 : // visible area of a scrollframe.
1713 : //
1714 : // Another reason we want to clear the untransform is that if our APZ hit-test
1715 : // hits a dispatch-to-content region then that's an ambiguous result and we
1716 : // need to ask Gecko what actually got hit. In order to do this we need to
1717 : // untransform the input event into Gecko space - but to do that we need to
1718 : // know which APZC got hit! This leads to a circular dependency; the only way
1719 : // to get out of it is to make sure that the untransform for all the possible
1720 : // matched APZCs is the same. It is simplest to ensure that by flushing the
1721 : // pending repaint requests, which makes all of the untransforms empty (and
1722 : // therefore equal).
1723 0 : MutexAutoLock lock(mTreeLock);
1724 0 : mTreeLock.AssertCurrentThreadOwns();
1725 :
1726 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
1727 0 : [](HitTestingTreeNode* aNode)
1728 : {
1729 0 : if (aNode->IsPrimaryHolder()) {
1730 0 : MOZ_ASSERT(aNode->GetApzc());
1731 0 : aNode->GetApzc()->FlushRepaintForNewInputBlock();
1732 : }
1733 0 : });
1734 0 : }
1735 :
1736 : void
1737 0 : APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
1738 : {
1739 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
1740 0 : if (apzc) {
1741 0 : apzc->CancelAnimation();
1742 : }
1743 0 : }
1744 :
1745 : void
1746 0 : APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
1747 : {
1748 0 : MutexAutoLock lock(mTreeLock);
1749 0 : RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
1750 0 : if (apzc) {
1751 0 : apzc->AdjustScrollForSurfaceShift(aShift);
1752 : }
1753 0 : }
1754 :
1755 : void
1756 0 : APZCTreeManager::ClearTree()
1757 : {
1758 : // Ensure that no references to APZCs are alive in any lingering input
1759 : // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
1760 : // the InputQueue.
1761 0 : APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
1762 0 : "layers::InputQueue::Clear", mInputQueue, &InputQueue::Clear));
1763 :
1764 0 : MutexAutoLock lock(mTreeLock);
1765 :
1766 : // Collect the nodes into a list, and then destroy each one.
1767 : // We can't destroy them as we collect them, because ForEachNode()
1768 : // does a pre-order traversal of the tree, and Destroy() nulls out
1769 : // the fields needed to reach the children of the node.
1770 0 : nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy;
1771 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
1772 0 : [&nodesToDestroy](HitTestingTreeNode* aNode)
1773 0 : {
1774 0 : nodesToDestroy.AppendElement(aNode);
1775 0 : });
1776 :
1777 0 : for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
1778 0 : nodesToDestroy[i]->Destroy();
1779 : }
1780 0 : mRootNode = nullptr;
1781 :
1782 0 : RefPtr<APZCTreeManager> self(this);
1783 0 : NS_DispatchToMainThread(
1784 0 : NS_NewRunnableFunction("layers::APZCTreeManager::ClearTree", [self] {
1785 0 : self->mFlushObserver->Unregister();
1786 0 : self->mFlushObserver = nullptr;
1787 0 : }));
1788 0 : }
1789 :
1790 : RefPtr<HitTestingTreeNode>
1791 0 : APZCTreeManager::GetRootNode() const
1792 : {
1793 0 : MutexAutoLock lock(mTreeLock);
1794 0 : return mRootNode;
1795 : }
1796 :
1797 : /**
1798 : * Transform a displacement from the ParentLayer coordinates of a source APZC
1799 : * to the ParentLayer coordinates of a target APZC.
1800 : * @param aTreeManager the tree manager for the APZC tree containing |aSource|
1801 : * and |aTarget|
1802 : * @param aSource the source APZC
1803 : * @param aTarget the target APZC
1804 : * @param aStartPoint the start point of the displacement
1805 : * @param aEndPoint the end point of the displacement
1806 : * @return true on success, false if aStartPoint or aEndPoint cannot be transformed into target's coordinate space
1807 : */
1808 : static bool
1809 0 : TransformDisplacement(APZCTreeManager* aTreeManager,
1810 : AsyncPanZoomController* aSource,
1811 : AsyncPanZoomController* aTarget,
1812 : ParentLayerPoint& aStartPoint,
1813 : ParentLayerPoint& aEndPoint) {
1814 0 : if (aSource == aTarget) {
1815 0 : return true;
1816 : }
1817 :
1818 : // Convert start and end points to Screen coordinates.
1819 0 : ParentLayerToScreenMatrix4x4 untransformToApzc = aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
1820 0 : ScreenPoint screenStart = TransformBy(untransformToApzc, aStartPoint);
1821 0 : ScreenPoint screenEnd = TransformBy(untransformToApzc, aEndPoint);
1822 :
1823 : // Convert start and end points to aTarget's ParentLayer coordinates.
1824 0 : ScreenToParentLayerMatrix4x4 transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
1825 0 : Maybe<ParentLayerPoint> startPoint = UntransformBy(transformToApzc, screenStart);
1826 0 : Maybe<ParentLayerPoint> endPoint = UntransformBy(transformToApzc, screenEnd);
1827 0 : if (!startPoint || !endPoint) {
1828 0 : return false;
1829 : }
1830 0 : aEndPoint = *endPoint;
1831 0 : aStartPoint = *startPoint;
1832 :
1833 0 : return true;
1834 : }
1835 :
1836 : void
1837 0 : APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
1838 : ParentLayerPoint& aStartPoint,
1839 : ParentLayerPoint& aEndPoint,
1840 : OverscrollHandoffState& aOverscrollHandoffState)
1841 : {
1842 0 : const OverscrollHandoffChain& overscrollHandoffChain = aOverscrollHandoffState.mChain;
1843 0 : uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex;
1844 0 : RefPtr<AsyncPanZoomController> next;
1845 : // If we have reached the end of the overscroll handoff chain, there is
1846 : // nothing more to scroll, so we ignore the rest of the pan gesture.
1847 0 : if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) {
1848 : // Nothing more to scroll - ignore the rest of the pan gesture.
1849 0 : return;
1850 : }
1851 :
1852 0 : next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex);
1853 :
1854 0 : if (next == nullptr || next->IsDestroyed()) {
1855 0 : return;
1856 : }
1857 :
1858 : // Convert the start and end points from |aPrev|'s coordinate space to
1859 : // |next|'s coordinate space.
1860 0 : if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) {
1861 0 : return;
1862 : }
1863 :
1864 : // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
1865 : // again with an incremented index.
1866 0 : if (!next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState)) {
1867 : // Transform |aStartPoint| and |aEndPoint| (which now represent the
1868 : // portion of the displacement that wasn't consumed by APZCs later
1869 : // in the handoff chain) back into |aPrev|'s coordinate space. This
1870 : // allows the caller (which is |aPrev|) to interpret the unconsumed
1871 : // displacement in its own coordinate space, and make use of it
1872 : // (e.g. by going into overscroll).
1873 0 : if (!TransformDisplacement(this, next, aPrev, aStartPoint, aEndPoint)) {
1874 0 : NS_WARNING("Failed to untransform scroll points during dispatch");
1875 : }
1876 : }
1877 : }
1878 :
1879 : void
1880 0 : APZCTreeManager::DispatchFling(AsyncPanZoomController* aPrev,
1881 : FlingHandoffState& aHandoffState)
1882 : {
1883 : // If immediate handoff is disallowed, do not allow handoff beyond the
1884 : // single APZC that's scrolled by the input block that triggered this fling.
1885 0 : if (aHandoffState.mIsHandoff &&
1886 0 : !gfxPrefs::APZAllowImmediateHandoff() &&
1887 0 : aHandoffState.mScrolledApzc == aPrev) {
1888 0 : return;
1889 : }
1890 :
1891 0 : const OverscrollHandoffChain* chain = aHandoffState.mChain;
1892 0 : RefPtr<AsyncPanZoomController> current;
1893 0 : uint32_t overscrollHandoffChainLength = chain->Length();
1894 : uint32_t startIndex;
1895 :
1896 : // This will store any velocity left over after the entire handoff.
1897 0 : ParentLayerPoint finalResidualVelocity = aHandoffState.mVelocity;
1898 :
1899 : // The fling's velocity needs to be transformed from the screen coordinates
1900 : // of |aPrev| to the screen coordinates of |next|. To transform a velocity
1901 : // correctly, we need to convert it to a displacement. For now, we do this
1902 : // by anchoring it to a start point of (0, 0).
1903 : // TODO: For this to be correct in the presence of 3D transforms, we should
1904 : // use the end point of the touch that started the fling as the start point
1905 : // rather than (0, 0).
1906 0 : ParentLayerPoint startPoint; // (0, 0)
1907 0 : ParentLayerPoint endPoint;
1908 :
1909 0 : if (aHandoffState.mIsHandoff) {
1910 0 : startIndex = chain->IndexOf(aPrev) + 1;
1911 :
1912 : // IndexOf will return aOverscrollHandoffChain->Length() if
1913 : // |aPrev| is not found.
1914 0 : if (startIndex >= overscrollHandoffChainLength) {
1915 0 : return;
1916 : }
1917 : } else {
1918 0 : startIndex = 0;
1919 : }
1920 :
1921 0 : for (; startIndex < overscrollHandoffChainLength; startIndex++) {
1922 0 : current = chain->GetApzcAtIndex(startIndex);
1923 :
1924 : // Make sure the apcz about to be handled can be handled
1925 0 : if (current == nullptr || current->IsDestroyed()) {
1926 0 : return;
1927 : }
1928 :
1929 0 : endPoint = startPoint + aHandoffState.mVelocity;
1930 :
1931 : // Only transform when current apcz can be transformed with previous
1932 0 : if (startIndex > 0) {
1933 0 : if (!TransformDisplacement(this,
1934 0 : chain->GetApzcAtIndex(startIndex - 1),
1935 : current,
1936 : startPoint,
1937 : endPoint)) {
1938 0 : return;
1939 : }
1940 : }
1941 :
1942 0 : ParentLayerPoint transformedVelocity = endPoint - startPoint;
1943 0 : aHandoffState.mVelocity = transformedVelocity;
1944 :
1945 0 : if (current->AttemptFling(aHandoffState)) {
1946 : // Coming out of AttemptFling(), the handoff state's velocity is the
1947 : // residual velocity after attempting to fling |current|.
1948 0 : ParentLayerPoint residualVelocity = aHandoffState.mVelocity;
1949 :
1950 : // If there's no residual velocity, there's nothing more to hand off.
1951 0 : if (IsZero(residualVelocity)) {
1952 0 : finalResidualVelocity = ParentLayerPoint();
1953 0 : break;
1954 : }
1955 :
1956 : // If there is residual velocity, subtract the proportion of used
1957 : // velocity from finalResidualVelocity and continue handoff along the
1958 : // chain.
1959 0 : if (!FuzzyEqualsAdditive(transformedVelocity.x,
1960 : residualVelocity.x, COORDINATE_EPSILON)) {
1961 0 : finalResidualVelocity.x *= (residualVelocity.x / transformedVelocity.x);
1962 : }
1963 0 : if (!FuzzyEqualsAdditive(transformedVelocity.y,
1964 : residualVelocity.y, COORDINATE_EPSILON)) {
1965 0 : finalResidualVelocity.y *= (residualVelocity.y / transformedVelocity.y);
1966 : }
1967 : }
1968 : }
1969 :
1970 : // Set the handoff state's velocity to any residual velocity left over
1971 : // after the entire handoff process.
1972 0 : aHandoffState.mVelocity = finalResidualVelocity;
1973 : }
1974 :
1975 : bool
1976 0 : APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
1977 : {
1978 0 : RefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
1979 0 : return target != nullptr;
1980 : }
1981 :
1982 : already_AddRefed<AsyncPanZoomController>
1983 0 : APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
1984 : {
1985 0 : MutexAutoLock lock(mTreeLock);
1986 0 : RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
1987 0 : MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
1988 0 : RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
1989 0 : return apzc.forget();
1990 : }
1991 :
1992 : static bool
1993 0 : GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne, const ScrollableLayerGuid& aTwo)
1994 : {
1995 0 : return aOne.mLayersId == aTwo.mLayersId
1996 0 : && aOne.mScrollId == aTwo.mScrollId;
1997 : }
1998 :
1999 : already_AddRefed<AsyncPanZoomController>
2000 0 : APZCTreeManager::GetTargetAPZC(const uint64_t& aLayersId,
2001 : const FrameMetrics::ViewID& aScrollId)
2002 : {
2003 0 : MutexAutoLock lock(mTreeLock);
2004 0 : ScrollableLayerGuid guid(aLayersId, 0, aScrollId);
2005 0 : RefPtr<HitTestingTreeNode> node = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
2006 0 : MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
2007 0 : RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
2008 0 : return apzc.forget();
2009 : }
2010 :
2011 : already_AddRefed<HitTestingTreeNode>
2012 1 : APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
2013 : GuidComparator aComparator) const
2014 : {
2015 1 : mTreeLock.AssertCurrentThreadOwns();
2016 : RefPtr<HitTestingTreeNode> target = DepthFirstSearchPostOrder<ReverseIterator>(mRootNode.get(),
2017 9 : [&aGuid, &aComparator](HitTestingTreeNode* node)
2018 2 : {
2019 9 : bool matches = false;
2020 9 : if (node->GetApzc()) {
2021 1 : if (aComparator) {
2022 0 : matches = aComparator(aGuid, node->GetApzc()->GetGuid());
2023 : } else {
2024 1 : matches = node->GetApzc()->Matches(aGuid);
2025 : }
2026 : }
2027 9 : return matches;
2028 : }
2029 2 : );
2030 2 : return target.forget();
2031 : }
2032 :
2033 : already_AddRefed<AsyncPanZoomController>
2034 6 : APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint,
2035 : HitTestResult* aOutHitResult,
2036 : HitTestingTreeNode** aOutHitScrollbar)
2037 : {
2038 12 : MutexAutoLock lock(mTreeLock);
2039 6 : HitTestResult hitResult = HitNothing;
2040 : ParentLayerPoint point = ViewAs<ParentLayerPixel>(aPoint,
2041 6 : PixelCastJustification::ScreenIsParentLayerForRoot);
2042 : RefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, point,
2043 12 : &hitResult, aOutHitScrollbar);
2044 :
2045 6 : if (aOutHitResult) {
2046 6 : *aOutHitResult = hitResult;
2047 : }
2048 12 : return target.forget();
2049 : }
2050 :
2051 : RefPtr<const OverscrollHandoffChain>
2052 0 : APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget)
2053 : {
2054 : // Scroll grabbing is a mechanism that allows content to specify that
2055 : // the initial target of a pan should be not the innermost scrollable
2056 : // frame at the touch point (which is what GetTargetAPZC finds), but
2057 : // something higher up in the tree.
2058 : // It's not sufficient to just find the initial target, however, as
2059 : // overscroll can be handed off to another APZC. Without scroll grabbing,
2060 : // handoff just occurs from child to parent. With scroll grabbing, the
2061 : // handoff order can be different, so we build a chain of APZCs in the
2062 : // order in which scroll will be handed off to them.
2063 :
2064 : // Grab tree lock since we'll be walking the APZC tree.
2065 0 : MutexAutoLock lock(mTreeLock);
2066 :
2067 : // Build the chain. If there is a scroll parent link, we use that. This is
2068 : // needed to deal with scroll info layers, because they participate in handoff
2069 : // but do not follow the expected layer tree structure. If there are no
2070 : // scroll parent links we just walk up the tree to find the scroll parent.
2071 0 : OverscrollHandoffChain* result = new OverscrollHandoffChain;
2072 0 : AsyncPanZoomController* apzc = aInitialTarget;
2073 0 : while (apzc != nullptr) {
2074 0 : result->Add(apzc);
2075 :
2076 0 : if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) {
2077 0 : if (!apzc->IsRootForLayersId()) {
2078 : // This probably indicates a bug or missed case in layout code
2079 0 : NS_WARNING("Found a non-root APZ with no handoff parent");
2080 : }
2081 0 : apzc = apzc->GetParent();
2082 0 : continue;
2083 : }
2084 :
2085 : // Guard against a possible infinite-loop condition. If we hit this, the
2086 : // layout code that generates the handoff parents did something wrong.
2087 0 : MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId);
2088 :
2089 : // Find the AsyncPanZoomController instance with a matching layersId and
2090 : // the scroll id that matches apzc->GetScrollHandoffParentId().
2091 : // As an optimization, we start by walking up the APZC tree from 'apzc'
2092 : // until we reach the top of the layer subtree for this layers id.
2093 0 : AsyncPanZoomController* scrollParent = nullptr;
2094 0 : AsyncPanZoomController* parent = apzc;
2095 0 : while (!parent->HasNoParentWithSameLayersId()) {
2096 0 : parent = parent->GetParent();
2097 : // While walking up to find the root of the subtree, if we encounter the
2098 : // handoff parent, we don't actually need to do the search so we can
2099 : // just abort here.
2100 0 : if (parent->GetGuid().mScrollId == apzc->GetScrollHandoffParentId()) {
2101 0 : scrollParent = parent;
2102 0 : break;
2103 : }
2104 : }
2105 : // If that heuristic didn't turn up the scroll parent, do a full tree search.
2106 0 : if (!scrollParent) {
2107 0 : ScrollableLayerGuid guid(parent->GetGuid().mLayersId, 0, apzc->GetScrollHandoffParentId());
2108 0 : RefPtr<HitTestingTreeNode> node = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
2109 0 : MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
2110 0 : scrollParent = node ? node->GetApzc() : nullptr;
2111 : }
2112 0 : apzc = scrollParent;
2113 : }
2114 :
2115 : // Now adjust the chain to account for scroll grabbing. Sorting is a bit
2116 : // of an overkill here, but scroll grabbing will likely be generalized
2117 : // to scroll priorities, so we might as well do it this way.
2118 0 : result->SortByScrollPriority();
2119 :
2120 : // Print the overscroll chain for debugging.
2121 0 : for (uint32_t i = 0; i < result->Length(); ++i) {
2122 : APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, result->GetApzcAtIndex(i).get());
2123 : }
2124 :
2125 0 : return result;
2126 : }
2127 :
2128 : void
2129 0 : APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
2130 : {
2131 : APZThreadUtils::RunOnControllerThread(
2132 0 : NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
2133 0 : }
2134 :
2135 : RefPtr<HitTestingTreeNode>
2136 0 : APZCTreeManager::FindScrollThumbNode(const AsyncDragMetrics& aDragMetrics)
2137 : {
2138 0 : MutexAutoLock lock(mTreeLock);
2139 :
2140 : return DepthFirstSearch<ReverseIterator>(mRootNode.get(),
2141 0 : [&aDragMetrics](HitTestingTreeNode* aNode) {
2142 : return aNode->MatchesScrollDragMetrics(aDragMetrics);
2143 0 : });
2144 : }
2145 :
2146 : AsyncPanZoomController*
2147 5 : APZCTreeManager::GetTargetApzcForNode(HitTestingTreeNode* aNode)
2148 : {
2149 20 : for (const HitTestingTreeNode* n = aNode;
2150 10 : n && n->GetLayersId() == aNode->GetLayersId();
2151 : n = n->GetParent()) {
2152 10 : if (n->GetApzc()) {
2153 : APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc());
2154 5 : return n->GetApzc();
2155 : }
2156 5 : if (n->GetFixedPosTarget() != FrameMetrics::NULL_SCROLL_ID) {
2157 0 : ScrollableLayerGuid guid(n->GetLayersId(), 0, n->GetFixedPosTarget());
2158 0 : RefPtr<HitTestingTreeNode> fpNode = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
2159 : APZCTM_LOG("Found target node %p using fixed-pos lookup on %" PRIu64 "\n", fpNode.get(), n->GetFixedPosTarget());
2160 0 : return fpNode ? fpNode->GetApzc() : nullptr;
2161 : }
2162 : }
2163 0 : return nullptr;
2164 : }
2165 :
2166 : AsyncPanZoomController*
2167 6 : APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
2168 : const ParentLayerPoint& aHitTestPoint,
2169 : HitTestResult* aOutHitResult,
2170 : HitTestingTreeNode** aOutScrollbarNode)
2171 : {
2172 6 : mTreeLock.AssertCurrentThreadOwns();
2173 :
2174 : // This walks the tree in depth-first, reverse order, so that it encounters
2175 : // APZCs front-to-back on the screen.
2176 : HitTestingTreeNode* resultNode;
2177 6 : HitTestingTreeNode* root = aNode;
2178 12 : std::stack<LayerPoint> hitTestPoints;
2179 12 : hitTestPoints.push(ViewAs<LayerPixel>(aHitTestPoint,
2180 6 : PixelCastJustification::MovingDownToChildren));
2181 :
2182 12 : ForEachNode<ReverseIterator>(root,
2183 61 : [&hitTestPoints, this](HitTestingTreeNode* aNode) {
2184 21 : ParentLayerPoint hitTestPointForParent = ViewAs<ParentLayerPixel>(hitTestPoints.top(),
2185 21 : PixelCastJustification::MovingDownToChildren);
2186 21 : if (aNode->IsOutsideClip(hitTestPointForParent)) {
2187 : // If the point being tested is outside the clip region for this node
2188 : // then we don't need to test against this node or any of its children.
2189 : // Just skip it and move on.
2190 : APZCTM_LOG("Point %f %f outside clip for node %p\n",
2191 : hitTestPoints.top().x, hitTestPoints.top().y, aNode);
2192 1 : return TraversalFlag::Skip;
2193 : }
2194 : // First check the subtree rooted at this node, because deeper nodes
2195 : // are more "in front".
2196 : Maybe<LayerPoint> hitTestPoint = aNode->Untransform(
2197 40 : hitTestPointForParent, ComputeTransformForNode(aNode));
2198 : APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n",
2199 : Stringify(hitTestPointForParent).c_str(),
2200 : hitTestPoint ? Stringify(hitTestPoint.ref()).c_str() : "nil");
2201 20 : if (!hitTestPoint) {
2202 0 : return TraversalFlag::Skip;
2203 : }
2204 40 : hitTestPoints.push(hitTestPoint.ref());
2205 20 : return TraversalFlag::Continue;
2206 : },
2207 40 : [&resultNode, &hitTestPoints, &aOutHitResult](HitTestingTreeNode* aNode) {
2208 15 : HitTestResult hitResult = aNode->HitTest(hitTestPoints.top());
2209 15 : hitTestPoints.pop();
2210 : APZCTM_LOG("Testing Layer point %s against node %p\n",
2211 : Stringify(hitTestPoints.top()).c_str(), aNode);
2212 15 : if (hitResult != HitTestResult::HitNothing) {
2213 5 : resultNode = aNode;
2214 : // If event regions are disabled, *aOutHitResult will be HitLayer
2215 5 : *aOutHitResult = hitResult;
2216 5 : return TraversalFlag::Abort;
2217 : }
2218 10 : return TraversalFlag::Continue;
2219 : }
2220 6 : );
2221 :
2222 6 : if (*aOutHitResult != HitNothing) {
2223 5 : MOZ_ASSERT(resultNode);
2224 15 : for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
2225 10 : if (n->IsScrollbarNode()) {
2226 0 : if (aOutScrollbarNode) {
2227 0 : *aOutScrollbarNode = n;
2228 : }
2229 : // If we hit a scrollbar, target the APZC for the content scrolled
2230 : // by the scrollbar. (The scrollbar itself doesn't scroll with the
2231 : // scrolled content, so it doesn't carry the scrolled content's
2232 : // scroll metadata).
2233 0 : ScrollableLayerGuid guid(n->GetLayersId(), 0, n->GetScrollTargetId());
2234 0 : if (RefPtr<HitTestingTreeNode> scrollTarget = GetTargetNode(guid, &GuidComparatorIgnoringPresShell)) {
2235 0 : MOZ_ASSERT(scrollTarget->GetApzc());
2236 0 : return scrollTarget->GetApzc();
2237 : }
2238 : }
2239 : }
2240 :
2241 5 : AsyncPanZoomController* result = GetTargetApzcForNode(resultNode);
2242 5 : if (!result) {
2243 0 : result = FindRootApzcForLayersId(resultNode->GetLayersId());
2244 0 : MOZ_ASSERT(result);
2245 : APZCTM_LOG("Found target %p using root lookup\n", result);
2246 : }
2247 : APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
2248 : result, resultNode, *aOutHitResult);
2249 5 : return result;
2250 : }
2251 :
2252 1 : return nullptr;
2253 : }
2254 :
2255 : AsyncPanZoomController*
2256 0 : APZCTreeManager::FindRootApzcForLayersId(uint64_t aLayersId) const
2257 : {
2258 0 : mTreeLock.AssertCurrentThreadOwns();
2259 :
2260 0 : HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2261 0 : [aLayersId](HitTestingTreeNode* aNode) {
2262 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2263 : return apzc
2264 0 : && apzc->GetLayersId() == aLayersId
2265 0 : && apzc->IsRootForLayersId();
2266 0 : });
2267 0 : return resultNode ? resultNode->GetApzc() : nullptr;
2268 : }
2269 :
2270 : AsyncPanZoomController*
2271 0 : APZCTreeManager::FindRootContentApzcForLayersId(uint64_t aLayersId) const
2272 : {
2273 0 : mTreeLock.AssertCurrentThreadOwns();
2274 :
2275 0 : HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2276 0 : [aLayersId](HitTestingTreeNode* aNode) {
2277 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2278 : return apzc
2279 0 : && apzc->GetLayersId() == aLayersId
2280 0 : && apzc->IsRootContent();
2281 0 : });
2282 0 : return resultNode ? resultNode->GetApzc() : nullptr;
2283 : }
2284 :
2285 : AsyncPanZoomController*
2286 0 : APZCTreeManager::FindRootContentOrRootApzc() const
2287 : {
2288 0 : mTreeLock.AssertCurrentThreadOwns();
2289 :
2290 : // Note: this is intended to find the same "root" that would be found
2291 : // by AsyncCompositionManager::ApplyAsyncContentTransformToTree inside
2292 : // the MOZ_WIDGET_ANDROID block. That is, it should find the RCD node if there
2293 : // is one, or the root APZC if there is not.
2294 : // Since BreadthFirstSearch is a pre-order search, we first do a search for
2295 : // the RCD, and then if we don't find one, we do a search for the root APZC.
2296 0 : HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2297 0 : [](HitTestingTreeNode* aNode) {
2298 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2299 0 : return apzc && apzc->IsRootContent();
2300 0 : });
2301 0 : if (resultNode) {
2302 0 : return resultNode->GetApzc();
2303 : }
2304 0 : resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2305 0 : [](HitTestingTreeNode* aNode) {
2306 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2307 0 : return (apzc != nullptr);
2308 0 : });
2309 0 : return resultNode ? resultNode->GetApzc() : nullptr;
2310 : }
2311 :
2312 : /* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return
2313 : some useful transformations that input events may need applied. This is best
2314 : illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
2315 : is the layer that corresponds to the argument |aApzc|, and layer R is the root
2316 : of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
2317 : When layer L is displayed to the screen by the compositor, the set of transforms that
2318 : are applied to L are (in order from top to bottom):
2319 :
2320 : L's CSS transform (hereafter referred to as transform matrix LC)
2321 : L's nontransient async transform (hereafter referred to as transform matrix LN)
2322 : L's transient async transform (hereafter referred to as transform matrix LT)
2323 : M's CSS transform (hereafter referred to as transform matrix MC)
2324 : M's nontransient async transform (hereafter referred to as transform matrix MN)
2325 : M's transient async transform (hereafter referred to as transform matrix MT)
2326 : ...
2327 : R's CSS transform (hereafter referred to as transform matrix RC)
2328 : R's nontransient async transform (hereafter referred to as transform matrix RN)
2329 : R's transient async transform (hereafter referred to as transform matrix RT)
2330 :
2331 : Also, for any layer, the async transform is the combination of its transient and non-transient
2332 : parts. That is, for any layer L:
2333 : LA === LN * LT
2334 : LA.Inverse() === LT.Inverse() * LN.Inverse()
2335 :
2336 : If we want user input to modify L's transient async transform, we have to first convert
2337 : user input from screen space to the coordinate space of L's transient async transform. Doing
2338 : this involves applying the following transforms (in order from top to bottom):
2339 : RT.Inverse()
2340 : RN.Inverse()
2341 : RC.Inverse()
2342 : ...
2343 : MT.Inverse()
2344 : MN.Inverse()
2345 : MC.Inverse()
2346 : This combined transformation is returned by GetScreenToApzcTransform().
2347 :
2348 : Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
2349 : out all of the async transforms that are involved in this chain. This is because async
2350 : transforms are stored only in the compositor and gecko does not account for them when
2351 : doing display-list-based hit-testing for event dispatching.
2352 : Furthermore, because these input events are processed by Gecko in a FIFO queue that
2353 : includes other things (specifically paint requests), it is possible that by time the
2354 : input event reaches gecko, it will have painted something else. Therefore, we need to
2355 : apply another transform to the input events to account for the possible disparity between
2356 : what we know gecko last painted and the last paint request we sent to gecko. Let this
2357 : transform be represented by LD, MD, ... RD.
2358 : Therefore, given a user input in screen space, the following transforms need to be applied
2359 : (in order from top to bottom):
2360 : RT.Inverse()
2361 : RN.Inverse()
2362 : RC.Inverse()
2363 : ...
2364 : MT.Inverse()
2365 : MN.Inverse()
2366 : MC.Inverse()
2367 : LT.Inverse()
2368 : LN.Inverse()
2369 : LC.Inverse()
2370 : LC
2371 : LD
2372 : MC
2373 : MD
2374 : ...
2375 : RC
2376 : RD
2377 : This sequence can be simplified and refactored to the following:
2378 : GetScreenToApzcTransform()
2379 : LA.Inverse()
2380 : LD
2381 : MC
2382 : MD
2383 : ...
2384 : RC
2385 : RD
2386 : Since GetScreenToApzcTransform() can be obtained by calling that function, GetApzcToGeckoTransform()
2387 : returns the remaining transforms (LA.Inverse() * LD * ... * RD), so that the caller code can
2388 : combine it with GetScreenToApzcTransform() to get the final transform required in this case.
2389 :
2390 : Note that for many of these layers, there will be no AsyncPanZoomController attached, and
2391 : so the async transform will be the identity transform. So, in the example above, if layers
2392 : L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
2393 : RN and RD will be identity transforms.
2394 : Additionally, for space-saving purposes, each APZC instance stores its layer's individual
2395 : CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
2396 : layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
2397 : The APZC instances track the last dispatched paint request and so are able to calculate LD and
2398 : PD using those internally stored values.
2399 : The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
2400 : required can be generated.
2401 : */
2402 :
2403 : /*
2404 : * See the long comment above for a detailed explanation of this function.
2405 : */
2406 : ScreenToParentLayerMatrix4x4
2407 5 : APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const
2408 : {
2409 5 : Matrix4x4 result;
2410 10 : MutexAutoLock lock(mTreeLock);
2411 :
2412 : // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
2413 : // explained in the comment above. This function is called with aApzc at L, and the loop
2414 : // below performs one iteration, where parent is at P. The comments explain what values are stored
2415 : // in the variables at these two levels. All the comments use standard matrix notation where the
2416 : // leftmost matrix in a multiplication is applied first.
2417 :
2418 : // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
2419 5 : Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
2420 :
2421 : // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
2422 5 : result = ancestorUntransform;
2423 :
2424 5 : for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
2425 : // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
2426 0 : ancestorUntransform = parent->GetAncestorTransform().Inverse();
2427 : // asyncUntransform is updated to PA.Inverse() when parent == P
2428 0 : Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting).Inverse().ToUnknownMatrix();
2429 : // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
2430 0 : Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
2431 :
2432 : // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
2433 0 : result = untransformSinceLastApzc * result;
2434 :
2435 : // The above value for result when parent == P matches the required output
2436 : // as explained in the comment above this method. Note that any missing
2437 : // terms are guaranteed to be identity transforms.
2438 : }
2439 :
2440 10 : return ViewAs<ScreenToParentLayerMatrix4x4>(result);
2441 : }
2442 :
2443 : /*
2444 : * See the long comment above GetScreenToApzcTransform() for a detailed
2445 : * explanation of this function.
2446 : */
2447 : ParentLayerToScreenMatrix4x4
2448 5 : APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const
2449 : {
2450 5 : Matrix4x4 result;
2451 10 : MutexAutoLock lock(mTreeLock);
2452 :
2453 : // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
2454 : // explained in the comment above. This function is called with aApzc at L, and the loop
2455 : // below performs one iteration, where parent is at P. The comments explain what values are stored
2456 : // in the variables at these two levels. All the comments use standard matrix notation where the
2457 : // leftmost matrix in a multiplication is applied first.
2458 :
2459 : // asyncUntransform is LA.Inverse()
2460 5 : Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting).Inverse().ToUnknownMatrix();
2461 :
2462 : // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
2463 5 : result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
2464 :
2465 5 : for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
2466 : // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
2467 0 : result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
2468 :
2469 : // The above value for result when parent == P matches the required output
2470 : // as explained in the comment above this method. Note that any missing
2471 : // terms are guaranteed to be identity transforms.
2472 : }
2473 :
2474 10 : return ViewAs<ParentLayerToScreenMatrix4x4>(result);
2475 : }
2476 :
2477 : already_AddRefed<AsyncPanZoomController>
2478 0 : APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
2479 : {
2480 0 : MutexAutoLock lock(mTreeLock);
2481 0 : RefPtr<AsyncPanZoomController> apzc;
2482 : // For now, we only ever want to do pinching on the root-content APZC for
2483 : // a given layers id.
2484 0 : if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
2485 : // If the two APZCs have the same layers id, find the root-content APZC
2486 : // for that layers id. Don't call CommonAncestor() because there may not
2487 : // be a common ancestor for the layers id (e.g. if one APZCs is inside a
2488 : // fixed-position element).
2489 0 : apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId());
2490 : } else {
2491 : // Otherwise, find the common ancestor (to reach a common layers id), and
2492 : // get the root-content APZC for that layers id.
2493 0 : apzc = CommonAncestor(aApzc1, aApzc2);
2494 0 : if (apzc) {
2495 0 : apzc = FindRootContentApzcForLayersId(apzc->GetLayersId());
2496 : }
2497 : }
2498 0 : return apzc.forget();
2499 : }
2500 :
2501 : already_AddRefed<AsyncPanZoomController>
2502 0 : APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
2503 : {
2504 0 : mTreeLock.AssertCurrentThreadOwns();
2505 0 : RefPtr<AsyncPanZoomController> ancestor;
2506 :
2507 : // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this function
2508 : // will return null.
2509 :
2510 : // Calculate depth of the APZCs in the tree
2511 0 : int depth1 = 0, depth2 = 0;
2512 0 : for (AsyncPanZoomController* parent = aApzc1; parent; parent = parent->GetParent()) {
2513 0 : depth1++;
2514 : }
2515 0 : for (AsyncPanZoomController* parent = aApzc2; parent; parent = parent->GetParent()) {
2516 0 : depth2++;
2517 : }
2518 :
2519 : // At most one of the following two loops will be executed; the deeper APZC pointer
2520 : // will get walked up to the depth of the shallower one.
2521 0 : int minDepth = depth1 < depth2 ? depth1 : depth2;
2522 0 : while (depth1 > minDepth) {
2523 0 : depth1--;
2524 0 : aApzc1 = aApzc1->GetParent();
2525 : }
2526 0 : while (depth2 > minDepth) {
2527 0 : depth2--;
2528 0 : aApzc2 = aApzc2->GetParent();
2529 : }
2530 :
2531 : // Walk up the ancestor chains of both APZCs, always staying at the same depth for
2532 : // either APZC, and return the the first common ancestor encountered.
2533 : while (true) {
2534 0 : if (aApzc1 == aApzc2) {
2535 0 : ancestor = aApzc1;
2536 0 : break;
2537 : }
2538 0 : if (depth1 <= 0) {
2539 0 : break;
2540 : }
2541 0 : aApzc1 = aApzc1->GetParent();
2542 0 : aApzc2 = aApzc2->GetParent();
2543 : }
2544 0 : return ancestor.forget();
2545 : }
2546 :
2547 : LayerToParentLayerMatrix4x4
2548 20 : APZCTreeManager::ComputeTransformForNode(const HitTestingTreeNode* aNode) const
2549 : {
2550 20 : if (AsyncPanZoomController* apzc = aNode->GetApzc()) {
2551 : // If the node represents scrollable content, apply the async transform
2552 : // from its APZC.
2553 5 : return aNode->GetTransform() *
2554 10 : CompleteAsyncTransform(
2555 15 : apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting));
2556 15 : } else if (aNode->IsScrollThumbNode()) {
2557 : // If the node represents a scrollbar thumb, compute and apply the
2558 : // transformation that will be applied to the thumb in
2559 : // AsyncCompositionManager.
2560 0 : ScrollableLayerGuid guid{aNode->GetLayersId(), 0, aNode->GetScrollTargetId()};
2561 0 : if (RefPtr<HitTestingTreeNode> scrollTargetNode = GetTargetNode(guid, &GuidComparatorIgnoringPresShell)) {
2562 0 : AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
2563 0 : MOZ_ASSERT(scrollTargetApzc);
2564 : return scrollTargetApzc->CallWithLastContentPaintMetrics(
2565 0 : [&](const FrameMetrics& aMetrics) {
2566 : return AsyncCompositionManager::ComputeTransformForScrollThumb(
2567 0 : aNode->GetTransform() * AsyncTransformMatrix(),
2568 0 : scrollTargetNode->GetTransform().ToUnknownMatrix(),
2569 0 : scrollTargetApzc,
2570 : aMetrics,
2571 : aNode->GetScrollThumbData(),
2572 0 : scrollTargetNode->IsAncestorOf(aNode),
2573 : nullptr);
2574 0 : });
2575 : }
2576 : }
2577 : // Otherwise, the node does not have an async transform.
2578 15 : return aNode->GetTransform() * AsyncTransformMatrix();
2579 : }
2580 :
2581 : #if defined(MOZ_WIDGET_ANDROID)
2582 : void
2583 : APZCTreeManager::InitializeDynamicToolbarAnimator(const int64_t& aRootLayerTreeId)
2584 : {
2585 : MOZ_ASSERT(mToolbarAnimator);
2586 : mToolbarAnimator->Initialize(aRootLayerTreeId);
2587 : }
2588 :
2589 : AndroidDynamicToolbarAnimator*
2590 : APZCTreeManager::GetAndroidDynamicToolbarAnimator()
2591 : {
2592 : return mToolbarAnimator;
2593 : }
2594 : #endif // defined(MOZ_WIDGET_ANDROID)
2595 :
2596 : } // namespace layers
2597 : } // namespace mozilla
|