Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include <stack>
8 : #include <unordered_set>
9 : #include "APZCTreeManager.h"
10 : #include "AsyncPanZoomController.h"
11 : #include "Compositor.h" // for Compositor
12 : #include "DragTracker.h" // for DragTracker
13 : #include "gfxPrefs.h" // for gfxPrefs
14 : #include "HitTestingTreeNode.h" // for HitTestingTreeNode
15 : #include "InputBlockState.h" // for InputBlockState
16 : #include "InputData.h" // for InputData, etc
17 : #include "Layers.h" // for Layer, etc
18 : #include "mozilla/dom/MouseEventBinding.h" // for MouseEvent constants
19 : #include "mozilla/dom/Touch.h" // for Touch
20 : #include "mozilla/gfx/gfxVars.h" // for gfxVars
21 : #include "mozilla/gfx/GPUParent.h" // for GPUParent
22 : #include "mozilla/gfx/Logging.h" // for gfx::TreeLog
23 : #include "mozilla/gfx/Point.h" // for Point
24 : #include "mozilla/layers/APZSampler.h" // for APZSampler
25 : #include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
26 : #include "mozilla/layers/APZUpdater.h" // for APZUpdater
27 : #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
28 : #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
29 : #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
30 : #include "mozilla/layers/LayerMetricsWrapper.h"
31 : #include "mozilla/layers/WebRenderScrollDataWrapper.h"
32 : #include "mozilla/MouseEvents.h"
33 : #include "mozilla/mozalloc.h" // for operator new
34 : #include "mozilla/TouchEvents.h"
35 : #include "mozilla/Preferences.h" // for Preferences
36 : #include "mozilla/EventStateManager.h" // for WheelPrefs
37 : #include "mozilla/webrender/WebRenderAPI.h"
38 : #include "nsDebug.h" // for NS_WARNING
39 : #include "nsPoint.h" // for nsIntPoint
40 : #include "nsThreadUtils.h" // for NS_IsMainThread
41 : #include "OverscrollHandoffState.h" // for OverscrollHandoffState
42 : #include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc
43 : #include "LayersLogging.h" // for Stringify
44 : #include "Units.h" // for ParentlayerPixel
45 : #include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled
46 : #include "UnitTransforms.h" // for ViewAs
47 :
48 : #define ENABLE_APZCTM_LOGGING 0
49 : // #define ENABLE_APZCTM_LOGGING 1
50 :
51 : #if ENABLE_APZCTM_LOGGING
52 : # define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
53 : #else
54 : # define APZCTM_LOG(...)
55 : #endif
56 :
57 : // #define APZ_KEY_LOG(...) printf_stderr("APZKEY: " __VA_ARGS__)
58 : #define APZ_KEY_LOG(...)
59 :
60 : namespace mozilla {
61 : namespace layers {
62 :
63 : using mozilla::gfx::CompositorHitTestInfo;
64 :
65 : typedef mozilla::gfx::Point Point;
66 : typedef mozilla::gfx::Point4D Point4D;
67 : typedef mozilla::gfx::Matrix4x4 Matrix4x4;
68 :
69 : typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
70 :
71 0 : struct APZCTreeManager::TreeBuildingState {
72 0 : TreeBuildingState(LayersId aRootLayersId,
73 : bool aIsFirstPaint, LayersId aOriginatingLayersId,
74 : APZTestData* aTestData, uint32_t aPaintSequence)
75 0 : : mIsFirstPaint(aIsFirstPaint)
76 : , mOriginatingLayersId(aOriginatingLayersId)
77 0 : , mPaintLogger(aTestData, aPaintSequence)
78 : {
79 0 : CompositorBridgeParent::CallWithIndirectShadowTree(aRootLayersId,
80 0 : [this](LayerTreeState& aState) -> void {
81 0 : mCompositorController = aState.GetCompositorController();
82 0 : mInProcessSharingController = aState.InProcessSharingController();
83 0 : });
84 0 : }
85 :
86 : typedef std::unordered_map<AsyncPanZoomController*, gfx::Matrix4x4> DeferredTransformMap;
87 :
88 : // State that doesn't change as we recurse in the tree building
89 : RefPtr<CompositorController> mCompositorController;
90 : RefPtr<MetricsSharingController> mInProcessSharingController;
91 : const bool mIsFirstPaint;
92 : const LayersId mOriginatingLayersId;
93 : const APZPaintLogHelper mPaintLogger;
94 :
95 : // State that is updated as we perform the tree build
96 :
97 : // A list of nodes that need to be destroyed at the end of the tree building.
98 : // This is initialized with all nodes in the old tree, and nodes are removed
99 : // from it as we reuse them in the new tree.
100 : nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
101 :
102 : // This map is populated as we place APZCs into the new tree. Its purpose is
103 : // to facilitate re-using the same APZC for different layers that scroll
104 : // together (and thus have the same ScrollableLayerGuid). The presShellId
105 : // doesn't matter for this purpose, and we move the map to the APZCTreeManager
106 : // after we're done building, so it's useful to have the presshell-ignoring
107 : // map for that.
108 : std::unordered_map<ScrollableLayerGuid,
109 : RefPtr<AsyncPanZoomController>,
110 : ScrollableLayerGuid::HashIgnoringPresShellFn,
111 : ScrollableLayerGuid::EqualIgnoringPresShellFn> mApzcMap;
112 :
113 : // This is populated with all the HitTestingTreeNodes that are scroll thumbs
114 : // and have a scrollthumb animation id (which indicates that they need to be
115 : // sampled for WebRender on the sampler thread).
116 : std::vector<HitTestingTreeNode*> mScrollThumbs;
117 : // This is populated with all the scroll target nodes. We use in conjunction
118 : // with mScrollThumbs to build APZCTreeManager::mScrollThumbInfo.
119 : std::unordered_map<ScrollableLayerGuid,
120 : HitTestingTreeNode*,
121 : ScrollableLayerGuid::HashIgnoringPresShellFn,
122 : ScrollableLayerGuid::EqualIgnoringPresShellFn> mScrollTargets;
123 :
124 : // As the tree is traversed, the top element of this stack tracks whether
125 : // the parent scroll node has a perspective transform.
126 : std::stack<bool> mParentHasPerspective;
127 :
128 : // During the tree building process, the perspective transform component
129 : // of the ancestor transforms of some APZCs can be "deferred" to their
130 : // children, meaning they are added to the children's ancestor transforms
131 : // instead. Those deferred transforms are tracked here.
132 : DeferredTransformMap mPerspectiveTransformsDeferredToChildren;
133 : };
134 :
135 : class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
136 : public:
137 : NS_DECL_ISUPPORTS
138 : NS_DECL_NSIOBSERVER
139 :
140 0 : explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
141 0 : : mTreeManager(aTreeManager)
142 : {
143 0 : MOZ_ASSERT(NS_IsMainThread());
144 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
145 0 : MOZ_ASSERT(obsSvc);
146 0 : if (obsSvc) {
147 0 : obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
148 : }
149 0 : }
150 :
151 0 : void Unregister()
152 : {
153 0 : MOZ_ASSERT(NS_IsMainThread());
154 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
155 0 : if (obsSvc) {
156 0 : obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
157 : }
158 0 : mTreeManager = nullptr;
159 0 : }
160 :
161 : protected:
162 0 : virtual ~CheckerboardFlushObserver() = default;
163 :
164 : private:
165 : RefPtr<APZCTreeManager> mTreeManager;
166 : };
167 :
168 0 : NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
169 :
170 : NS_IMETHODIMP
171 0 : APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
172 : const char* aTopic,
173 : const char16_t*)
174 : {
175 0 : MOZ_ASSERT(NS_IsMainThread());
176 0 : MOZ_ASSERT(mTreeManager.get());
177 :
178 0 : RecursiveMutexAutoLock lock(mTreeManager->mTreeLock);
179 0 : if (mTreeManager->mRootNode) {
180 0 : ForEachNode<ReverseIterator>(mTreeManager->mRootNode.get(),
181 0 : [](HitTestingTreeNode* aNode)
182 : {
183 0 : if (aNode->IsPrimaryHolder()) {
184 0 : MOZ_ASSERT(aNode->GetApzc());
185 0 : aNode->GetApzc()->FlushActiveCheckerboardReport();
186 : }
187 0 : });
188 : }
189 0 : if (XRE_IsGPUProcess()) {
190 0 : if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
191 0 : nsCString topic("APZ:FlushActiveCheckerboard:Done");
192 0 : Unused << gpu->SendNotifyUiObservers(topic);
193 : }
194 : } else {
195 0 : MOZ_ASSERT(XRE_IsParentProcess());
196 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
197 0 : if (obsSvc) {
198 0 : obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", nullptr);
199 : }
200 : }
201 0 : return NS_OK;
202 : }
203 :
204 : /**
205 : * A RAII class used for setting the focus sequence number on input events
206 : * as they are being processed. Any input event is assumed to be potentially
207 : * focus changing unless explicitly marked otherwise.
208 : */
209 : class MOZ_RAII AutoFocusSequenceNumberSetter
210 : {
211 : public:
212 : AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent)
213 0 : : mFocusState(aFocusState)
214 : , mEvent(aEvent)
215 0 : , mMayChangeFocus(true)
216 : { }
217 :
218 0 : void MarkAsNonFocusChanging() { mMayChangeFocus = false; }
219 :
220 0 : ~AutoFocusSequenceNumberSetter()
221 0 : {
222 0 : if (mMayChangeFocus) {
223 0 : mFocusState.ReceiveFocusChangingEvent();
224 :
225 : APZ_KEY_LOG("Marking input with type=%d as focus changing with seq=%" PRIu64 "\n",
226 : static_cast<int>(mEvent.mInputType),
227 : mFocusState.LastAPZProcessedEvent());
228 : } else {
229 : APZ_KEY_LOG("Marking input with type=%d as non focus changing with seq=%" PRIu64 "\n",
230 : static_cast<int>(mEvent.mInputType),
231 : mFocusState.LastAPZProcessedEvent());
232 : }
233 :
234 0 : mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
235 0 : }
236 :
237 : private:
238 : FocusState& mFocusState;
239 : InputData& mEvent;
240 : bool mMayChangeFocus;
241 : };
242 :
243 0 : APZCTreeManager::APZCTreeManager(LayersId aRootLayersId)
244 0 : : mInputQueue(new InputQueue()),
245 : mRootLayersId(aRootLayersId),
246 : mSampler(nullptr),
247 : mUpdater(nullptr),
248 : mTreeLock("APZCTreeLock"),
249 : mMapLock("APZCMapLock"),
250 : mHitResultForInputBlock(CompositorHitTestInfo::eInvisibleToHitTest),
251 : mRetainedTouchIdentifier(-1),
252 : mInScrollbarTouchDrag(false),
253 : mApzcTreeLog("apzctree"),
254 : mTestDataLock("APZTestDataLock"),
255 0 : mDPI(160.0)
256 : {
257 0 : RefPtr<APZCTreeManager> self(this);
258 0 : NS_DispatchToMainThread(
259 0 : NS_NewRunnableFunction("layers::APZCTreeManager::APZCTreeManager", [self] {
260 0 : self->mFlushObserver = new CheckerboardFlushObserver(self);
261 0 : }));
262 0 : AsyncPanZoomController::InitializeGlobalState();
263 0 : mApzcTreeLog.ConditionOnPrefFunction(gfxPrefs::APZPrintTree);
264 : #if defined(MOZ_WIDGET_ANDROID)
265 : mToolbarAnimator = new AndroidDynamicToolbarAnimator(this);
266 : #endif // (MOZ_WIDGET_ANDROID)
267 0 : }
268 :
269 : APZCTreeManager::~APZCTreeManager() = default;
270 :
271 : void
272 0 : APZCTreeManager::SetSampler(APZSampler* aSampler)
273 : {
274 : // We're either setting the sampler or clearing it
275 0 : MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr));
276 0 : mSampler = aSampler;
277 0 : }
278 :
279 : void
280 0 : APZCTreeManager::SetUpdater(APZUpdater* aUpdater)
281 : {
282 : // We're either setting the updater or clearing it
283 0 : MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr));
284 0 : mUpdater = aUpdater;
285 0 : }
286 :
287 : void
288 0 : APZCTreeManager::NotifyLayerTreeAdopted(LayersId aLayersId,
289 : const RefPtr<APZCTreeManager>& aOldApzcTreeManager)
290 : {
291 0 : AssertOnUpdaterThread();
292 :
293 0 : if (aOldApzcTreeManager) {
294 0 : aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
295 : // While we could move the focus target information from the old APZC tree
296 : // manager into this one, it's safer to not do that, as we'll probably have
297 : // that information repopulated soon anyway (on the next layers update).
298 : }
299 :
300 0 : UniquePtr<APZTestData> adoptedData;
301 0 : if (aOldApzcTreeManager) {
302 0 : MutexAutoLock lock(aOldApzcTreeManager->mTestDataLock);
303 0 : auto it = aOldApzcTreeManager->mTestData.find(aLayersId);
304 0 : if (it != aOldApzcTreeManager->mTestData.end()) {
305 0 : adoptedData = std::move(it->second);
306 0 : aOldApzcTreeManager->mTestData.erase(it);
307 : }
308 : }
309 0 : if (adoptedData) {
310 0 : MutexAutoLock lock(mTestDataLock);
311 0 : mTestData[aLayersId] = std::move(adoptedData);
312 : }
313 0 : }
314 :
315 : void
316 0 : APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId)
317 : {
318 0 : AssertOnUpdaterThread();
319 :
320 0 : mFocusState.RemoveFocusTarget(aLayersId);
321 :
322 : { // scope lock
323 0 : MutexAutoLock lock(mTestDataLock);
324 0 : mTestData.erase(aLayersId);
325 : }
326 0 : }
327 :
328 : AsyncPanZoomController*
329 0 : APZCTreeManager::NewAPZCInstance(LayersId aLayersId,
330 : GeckoContentController* aController)
331 : {
332 : return new AsyncPanZoomController(aLayersId, this, mInputQueue,
333 0 : aController, AsyncPanZoomController::USE_GESTURE_DETECTOR);
334 : }
335 :
336 : TimeStamp
337 0 : APZCTreeManager::GetFrameTime()
338 : {
339 0 : return TimeStamp::Now();
340 : }
341 :
342 : void
343 0 : APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
344 : const nsTArray<TouchBehaviorFlags> &aValues)
345 : {
346 0 : APZThreadUtils::AssertOnControllerThread();
347 :
348 0 : mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
349 0 : }
350 :
351 : template<class ScrollNode> void // ScrollNode is a LayerMetricsWrapper or a WebRenderScrollDataWrapper
352 0 : APZCTreeManager::UpdateHitTestingTreeImpl(LayersId aRootLayerTreeId,
353 : const ScrollNode& aRoot,
354 : bool aIsFirstPaint,
355 : LayersId aOriginatingLayersId,
356 : uint32_t aPaintSequenceNumber)
357 : {
358 0 : RecursiveMutexAutoLock lock(mTreeLock);
359 :
360 : // For testing purposes, we log some data to the APZTestData associated with
361 : // the layers id that originated this update.
362 0 : APZTestData* testData = nullptr;
363 0 : if (gfxPrefs::APZTestLoggingEnabled()) {
364 0 : MutexAutoLock lock(mTestDataLock);
365 0 : UniquePtr<APZTestData> ptr = MakeUnique<APZTestData>();
366 0 : auto result = mTestData.insert(std::make_pair(aOriginatingLayersId, std::move(ptr)));
367 0 : testData = result.first->second.get();
368 0 : testData->StartNewPaint(aPaintSequenceNumber);
369 : }
370 :
371 : TreeBuildingState state(aRootLayerTreeId, aIsFirstPaint, aOriginatingLayersId,
372 0 : testData, aPaintSequenceNumber);
373 :
374 : // We do this business with collecting the entire tree into an array because otherwise
375 : // it's very hard to determine which APZC instances need to be destroyed. In the worst
376 : // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
377 : // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
378 : // completely different place. In scenario (a) we would want to destroy the APZC while
379 : // walking the layer tree and noticing that the layer/APZC is no longer there. But if
380 : // we do that then we run into a problem in scenario (b) because we might encounter that
381 : // layer later during the walk. To handle both of these we have to 'remember' that the
382 : // layer was not found, and then do the destroy only at the end of the tree walk after
383 : // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
384 : // as part of a recursive tree walk is hard and so maintaining a list and removing
385 : // APZCs that are still alive is much simpler.
386 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
387 0 : [&state] (HitTestingTreeNode* aNode)
388 0 : {
389 0 : state.mNodesToDestroy.AppendElement(aNode);
390 0 : });
391 0 : mRootNode = nullptr;
392 :
393 0 : if (aRoot) {
394 0 : std::stack<gfx::TreeAutoIndent> indents;
395 0 : std::stack<AncestorTransform> ancestorTransforms;
396 0 : HitTestingTreeNode* parent = nullptr;
397 0 : HitTestingTreeNode* next = nullptr;
398 0 : LayersId layersId = aRootLayerTreeId;
399 0 : ancestorTransforms.push(AncestorTransform());
400 0 : state.mParentHasPerspective.push(false);
401 :
402 0 : mApzcTreeLog << "[start]\n";
403 0 : mTreeLock.AssertCurrentThreadIn();
404 :
405 0 : ForEachNode<ReverseIterator>(aRoot,
406 0 : [&](ScrollNode aLayerMetrics)
407 : {
408 0 : mApzcTreeLog << aLayerMetrics.Name() << '\t';
409 :
410 0 : HitTestingTreeNode* node = PrepareNodeForLayer(lock, aLayerMetrics,
411 0 : aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
412 0 : parent, next, state);
413 0 : MOZ_ASSERT(node);
414 0 : AsyncPanZoomController* apzc = node->GetApzc();
415 0 : aLayerMetrics.SetApzc(apzc);
416 :
417 : // GetScrollbarAnimationId is only non-zero when webrender is enabled,
418 : // which limits the extra thumb mapping work to the webrender-enabled
419 : // case where it is needed.
420 : // Note also that when webrender is enabled, a "valid" animation id
421 : // is always nonzero, so we don't need to worry about handling the
422 : // case where WR is enabled and the animation id is zero.
423 0 : if (node->IsScrollThumbNode() && node->GetScrollbarAnimationId()) {
424 0 : state.mScrollThumbs.push_back(node);
425 : }
426 0 : if (apzc && node->IsPrimaryHolder()) {
427 0 : state.mScrollTargets[apzc->GetGuid()] = node;
428 : }
429 :
430 0 : mApzcTreeLog << '\n';
431 :
432 : // Accumulate the CSS transform between layers that have an APZC.
433 : // In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
434 : // we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
435 : // compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
436 : // transform to layer L when we recurse into the children below. If we are at a layer
437 : // with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
438 : // the new accumulation as we go down.
439 : AncestorTransform currentTransform{aLayerMetrics.GetTransform(),
440 0 : aLayerMetrics.TransformIsPerspective()};
441 0 : if (!apzc) {
442 0 : currentTransform = currentTransform * ancestorTransforms.top();
443 : }
444 0 : ancestorTransforms.push(currentTransform);
445 :
446 : // Note that |node| at this point will not have any children, otherwise we
447 : // we would have to set next to node->GetFirstChild().
448 0 : MOZ_ASSERT(!node->GetFirstChild());
449 0 : parent = node;
450 0 : next = nullptr;
451 :
452 : // Update the layersId if we have a new one
453 0 : if (Maybe<LayersId> newLayersId = aLayerMetrics.GetReferentId()) {
454 0 : layersId = *newLayersId;
455 : }
456 :
457 0 : indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
458 0 : state.mParentHasPerspective.push(aLayerMetrics.TransformIsPerspective());
459 0 : },
460 0 : [&](ScrollNode aLayerMetrics)
461 : {
462 0 : next = parent;
463 0 : parent = parent->GetParent();
464 0 : layersId = next->GetLayersId();
465 0 : ancestorTransforms.pop();
466 0 : indents.pop();
467 0 : state.mParentHasPerspective.pop();
468 0 : });
469 :
470 0 : mApzcTreeLog << "[end]\n";
471 :
472 : // If we have perspective transforms deferred to children, do another
473 : // walk of the tree and actually apply them to the children.
474 : // We can't do this "as we go" in the previous traversal, because by the
475 : // time we realize we need to defer a perspective transform for an APZC,
476 : // we may already have processed a previous layer (including children
477 : // found in its subtree) that shares that APZC.
478 0 : if (!state.mPerspectiveTransformsDeferredToChildren.empty()) {
479 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
480 0 : [&state](HitTestingTreeNode* aNode) {
481 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
482 0 : if (!apzc) {
483 0 : return;
484 : }
485 0 : if (!aNode->IsPrimaryHolder()) {
486 : return;
487 : }
488 :
489 0 : AsyncPanZoomController* parent = apzc->GetParent();
490 0 : if (!parent) {
491 : return;
492 : }
493 :
494 0 : auto it = state.mPerspectiveTransformsDeferredToChildren.find(parent);
495 0 : if (it != state.mPerspectiveTransformsDeferredToChildren.end()) {
496 0 : apzc->SetAncestorTransform(AncestorTransform{
497 0 : it->second * apzc->GetAncestorTransform(), false
498 : });
499 : }
500 : });
501 : }
502 : }
503 :
504 : // We do not support tree structures where the root node has siblings.
505 0 : MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
506 :
507 : { // scope lock and update our mApzcMap before we destroy all the unused
508 : // APZC instances
509 0 : MutexAutoLock lock(mMapLock);
510 0 : mApzcMap = std::move(state.mApzcMap);
511 0 : mScrollThumbInfo.clear();
512 : // For non-webrender, state.mScrollThumbs will be empty so this will be a
513 : // no-op.
514 0 : for (HitTestingTreeNode* thumb : state.mScrollThumbs) {
515 0 : MOZ_ASSERT(thumb->IsScrollThumbNode());
516 0 : ScrollableLayerGuid targetGuid(thumb->GetLayersId(), 0, thumb->GetScrollTargetId());
517 0 : auto it = state.mScrollTargets.find(targetGuid);
518 0 : if (it == state.mScrollTargets.end()) {
519 : // It could be that |thumb| is a scrollthumb for content which didn't
520 : // have an APZC, for example if the content isn't layerized. Regardless,
521 : // we can't async-scroll it so we don't need to worry about putting it
522 : // in mScrollThumbInfo.
523 0 : continue;
524 : }
525 0 : HitTestingTreeNode* target = it->second;
526 0 : mScrollThumbInfo.emplace_back(
527 0 : thumb->GetScrollbarAnimationId(),
528 0 : thumb->GetTransform(),
529 : thumb->GetScrollbarData(),
530 : targetGuid,
531 0 : target->GetTransform(),
532 0 : target->IsAncestorOf(thumb));
533 : }
534 : }
535 :
536 0 : for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
537 : APZCTM_LOG("Destroying node at %p with APZC %p\n",
538 : state.mNodesToDestroy[i].get(),
539 : state.mNodesToDestroy[i]->GetApzc());
540 0 : state.mNodesToDestroy[i]->Destroy();
541 : }
542 :
543 : #if ENABLE_APZCTM_LOGGING
544 : // Make the hit-test tree line up with the layer dump
545 : printf_stderr("APZCTreeManager (%p)\n", this);
546 : mRootNode->Dump(" ");
547 : #endif
548 0 : }
549 :
550 : void
551 0 : APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
552 : LayersId aOriginatingLayersId,
553 : const FocusTarget& aFocusTarget)
554 : {
555 0 : AssertOnUpdaterThread();
556 :
557 0 : if (!gfxPrefs::APZKeyboardEnabled()) {
558 : return;
559 : }
560 :
561 0 : mFocusState.Update(aRootLayerTreeId,
562 : aOriginatingLayersId,
563 0 : aFocusTarget);
564 : }
565 :
566 : void
567 0 : APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
568 : Layer* aRoot,
569 : bool aIsFirstPaint,
570 : LayersId aOriginatingLayersId,
571 : uint32_t aPaintSequenceNumber)
572 : {
573 0 : AssertOnUpdaterThread();
574 :
575 0 : LayerMetricsWrapper root(aRoot);
576 0 : UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
577 0 : aOriginatingLayersId, aPaintSequenceNumber);
578 0 : }
579 :
580 : void
581 0 : APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
582 : const WebRenderScrollDataWrapper& aScrollWrapper,
583 : bool aIsFirstPaint,
584 : LayersId aOriginatingLayersId,
585 : uint32_t aPaintSequenceNumber)
586 : {
587 0 : AssertOnUpdaterThread();
588 :
589 0 : UpdateHitTestingTreeImpl(aRootLayerTreeId, aScrollWrapper, aIsFirstPaint,
590 0 : aOriginatingLayersId, aPaintSequenceNumber);
591 0 : }
592 :
593 : void
594 0 : APZCTreeManager::SampleForWebRender(wr::TransactionWrapper& aTxn,
595 : const TimeStamp& aSampleTime)
596 : {
597 0 : AssertOnSamplerThread();
598 0 : MutexAutoLock lock(mMapLock);
599 :
600 0 : bool activeAnimations = false;
601 0 : for (const auto& mapping : mApzcMap) {
602 0 : AsyncPanZoomController* apzc = mapping.second;
603 0 : ParentLayerPoint layerTranslation = apzc->GetCurrentAsyncTransform(
604 0 : AsyncPanZoomController::eForCompositing).mTranslation;
605 :
606 : // The positive translation means the painted content is supposed to
607 : // move down (or to the right), and that corresponds to a reduction in
608 : // the scroll offset. Since we are effectively giving WR the async
609 : // scroll delta here, we want to negate the translation.
610 0 : ParentLayerPoint asyncScrollDelta = -layerTranslation;
611 : // XXX figure out what zoom-related conversions need to happen here.
612 : aTxn.UpdateScrollPosition(
613 0 : wr::AsPipelineId(apzc->GetGuid().mLayersId),
614 0 : apzc->GetGuid().mScrollId,
615 0 : wr::ToLayoutPoint(LayoutDevicePoint::FromUnknownPoint(asyncScrollDelta.ToUnknownPoint())));
616 :
617 0 : apzc->ReportCheckerboard(aSampleTime);
618 0 : activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
619 : }
620 :
621 : // Now collect all the async transforms needed for the scrollthumbs.
622 0 : nsTArray<wr::WrTransformProperty> scrollbarTransforms;
623 0 : for (const ScrollThumbInfo& info : mScrollThumbInfo) {
624 0 : auto it = mApzcMap.find(info.mTargetGuid);
625 0 : if (it == mApzcMap.end()) {
626 : // It could be that |info| is a scrollthumb for content which didn't
627 : // have an APZC, for example if the content isn't layerized. Regardless,
628 : // we can't async-scroll it so we don't need to worry about putting it
629 : // in mScrollThumbInfo.
630 0 : continue;
631 : }
632 0 : AsyncPanZoomController* scrollTargetApzc = it->second;
633 0 : MOZ_ASSERT(scrollTargetApzc);
634 : LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
635 0 : [&](const FrameMetrics& aMetrics) {
636 : return ComputeTransformForScrollThumb(
637 0 : info.mThumbTransform * AsyncTransformMatrix(),
638 0 : info.mTargetTransform.ToUnknownMatrix(),
639 0 : scrollTargetApzc,
640 : aMetrics,
641 : info.mThumbData,
642 0 : info.mTargetIsAncestor,
643 : nullptr);
644 0 : });
645 0 : scrollbarTransforms.AppendElement(wr::ToWrTransformProperty(
646 0 : info.mThumbAnimationId,
647 0 : transform));
648 : }
649 0 : aTxn.AppendTransformProperties(scrollbarTransforms);
650 :
651 0 : if (activeAnimations) {
652 0 : RefPtr<CompositorController> controller;
653 0 : CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
654 0 : [&](LayerTreeState& aState) -> void {
655 0 : controller = aState.GetCompositorController();
656 0 : });
657 0 : if (controller) {
658 0 : controller->ScheduleRenderOnCompositorThread();
659 : }
660 : }
661 0 : }
662 :
663 : // Compute the clip region to be used for a layer with an APZC. This function
664 : // is only called for layers which actually have scrollable metrics and an APZC.
665 : template<class ScrollNode> static ParentLayerIntRegion
666 0 : ComputeClipRegion(const ScrollNode& aLayer)
667 : {
668 0 : ParentLayerIntRegion clipRegion;
669 0 : if (aLayer.GetClipRect()) {
670 0 : clipRegion = *aLayer.GetClipRect();
671 : } else {
672 : // if there is no clip on this layer (which should only happen for the
673 : // root scrollable layer in a process, or for some of the LayerMetrics
674 : // expansions of a multi-metrics layer), fall back to using the comp
675 : // bounds which should be equivalent.
676 0 : clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds());
677 : }
678 :
679 0 : return clipRegion;
680 : }
681 :
682 : template<class ScrollNode> void
683 0 : APZCTreeManager::PrintAPZCInfo(const ScrollNode& aLayer,
684 : const AsyncPanZoomController* apzc)
685 : {
686 0 : const FrameMetrics& metrics = aLayer.Metrics();
687 0 : mApzcTreeLog << "APZC " << apzc->GetGuid()
688 0 : << "\tcb=" << metrics.GetCompositionBounds()
689 0 : << "\tsr=" << metrics.GetScrollableRect()
690 0 : << (metrics.IsScrollInfoLayer() ? "\tscrollinfo" : "")
691 0 : << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
692 0 : << aLayer.Metadata().GetContentDescription().get();
693 0 : }
694 :
695 : void
696 0 : APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
697 : HitTestingTreeNode* aParent,
698 : HitTestingTreeNode* aNextSibling)
699 : {
700 0 : if (aNextSibling) {
701 0 : aNextSibling->SetPrevSibling(aNode);
702 0 : } else if (aParent) {
703 0 : aParent->SetLastChild(aNode);
704 : } else {
705 0 : MOZ_ASSERT(!mRootNode);
706 0 : mRootNode = aNode;
707 0 : aNode->MakeRoot();
708 : }
709 0 : }
710 :
711 : template<class ScrollNode> static EventRegions
712 0 : GetEventRegions(const ScrollNode& aLayer)
713 : {
714 0 : if (aLayer.Metrics().IsScrollInfoLayer()) {
715 0 : ParentLayerIntRect compositionBounds(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
716 0 : nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
717 0 : EventRegions eventRegions(hitRegion);
718 0 : eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
719 0 : return eventRegions;
720 : }
721 0 : return aLayer.GetEventRegions();
722 : }
723 :
724 :
725 :
726 : already_AddRefed<HitTestingTreeNode>
727 0 : APZCTreeManager::RecycleOrCreateNode(const RecursiveMutexAutoLock& aProofOfTreeLock,
728 : TreeBuildingState& aState,
729 : AsyncPanZoomController* aApzc,
730 : LayersId aLayersId)
731 : {
732 : // Find a node without an APZC and return it. Note that unless the layer tree
733 : // actually changes, this loop should generally do an early-return on the
734 : // first iteration, so it should be cheap in the common case.
735 0 : for (int32_t i = aState.mNodesToDestroy.Length() - 1; i >= 0; i--) {
736 0 : RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
737 0 : if (node->IsRecyclable(aProofOfTreeLock)) {
738 0 : aState.mNodesToDestroy.RemoveElementAt(i);
739 0 : node->RecycleWith(aProofOfTreeLock, aApzc, aLayersId);
740 0 : return node.forget();
741 : }
742 : }
743 0 : RefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false, aLayersId);
744 0 : return node.forget();
745 : }
746 :
747 : template<class ScrollNode> static EventRegionsOverride
748 0 : GetEventRegionsOverride(HitTestingTreeNode* aParent,
749 : const ScrollNode& aLayer)
750 : {
751 : // Make it so that if the flag is set on the layer tree, it automatically
752 : // propagates to all the nodes in the corresponding subtree rooted at that
753 : // layer in the hit-test tree. This saves having to walk up the tree every
754 : // we want to see if a hit-test node is affected by this flag.
755 0 : EventRegionsOverride result = aLayer.GetEventRegionsOverride();
756 0 : if (result != EventRegionsOverride::NoOverride) {
757 : // Overrides should only ever get set for ref layers.
758 0 : MOZ_ASSERT(aLayer.GetReferentId());
759 : }
760 0 : if (aParent) {
761 0 : result |= aParent->GetEventRegionsOverride();
762 : }
763 0 : return result;
764 : }
765 :
766 : void
767 0 : APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
768 : const AsyncDragMetrics& aDragMetrics)
769 : {
770 0 : APZThreadUtils::AssertOnControllerThread();
771 :
772 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
773 0 : if (!apzc) {
774 0 : NotifyScrollbarDragRejected(aGuid);
775 0 : return;
776 : }
777 :
778 0 : uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
779 0 : mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
780 : }
781 :
782 : bool
783 0 : APZCTreeManager::StartAutoscroll(const ScrollableLayerGuid& aGuid,
784 : const ScreenPoint& aAnchorLocation)
785 : {
786 0 : APZThreadUtils::AssertOnControllerThread();
787 :
788 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
789 0 : if (!apzc) {
790 0 : if (XRE_IsGPUProcess()) {
791 : // If we're in the compositor process, the "return false" will be
792 : // ignored because the query comes over the PAPZCTreeManager protocol
793 : // via an async message. In this case, send an explicit rejection
794 : // message to content.
795 0 : NotifyAutoscrollRejected(aGuid);
796 : }
797 : return false;
798 : }
799 :
800 0 : apzc->StartAutoscroll(aAnchorLocation);
801 0 : return true;
802 : }
803 :
804 : void
805 0 : APZCTreeManager::StopAutoscroll(const ScrollableLayerGuid& aGuid)
806 : {
807 0 : APZThreadUtils::AssertOnControllerThread();
808 :
809 0 : if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
810 0 : apzc->StopAutoscroll();
811 : }
812 0 : }
813 :
814 : void
815 0 : APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const
816 : {
817 : RefPtr<GeckoContentController> controller =
818 0 : GetContentController(aGuid.mLayersId);
819 0 : MOZ_ASSERT(controller);
820 0 : controller->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
821 0 : }
822 :
823 : void
824 0 : APZCTreeManager::NotifyAutoscrollRejected(const ScrollableLayerGuid& aGuid) const
825 : {
826 : RefPtr<GeckoContentController> controller =
827 0 : GetContentController(aGuid.mLayersId);
828 0 : MOZ_ASSERT(controller);
829 0 : controller->NotifyAsyncAutoscrollRejected(aGuid.mScrollId);
830 0 : }
831 :
832 : template<class ScrollNode> HitTestingTreeNode*
833 0 : APZCTreeManager::PrepareNodeForLayer(const RecursiveMutexAutoLock& aProofOfTreeLock,
834 : const ScrollNode& aLayer,
835 : const FrameMetrics& aMetrics,
836 : LayersId aLayersId,
837 : const AncestorTransform& aAncestorTransform,
838 : HitTestingTreeNode* aParent,
839 : HitTestingTreeNode* aNextSibling,
840 : TreeBuildingState& aState)
841 : {
842 0 : bool needsApzc = true;
843 0 : if (!aMetrics.IsScrollable()) {
844 0 : needsApzc = false;
845 : }
846 :
847 : // XXX: As a future optimization we can probably stick these things on the
848 : // TreeBuildingState, and update them as we change layers id during the
849 : // traversal
850 0 : RefPtr<GeckoContentController> geckoContentController;
851 0 : RefPtr<MetricsSharingController> crossProcessSharingController;
852 0 : CompositorBridgeParent::CallWithIndirectShadowTree(aLayersId,
853 0 : [&](LayerTreeState& lts) -> void {
854 0 : geckoContentController = lts.mController;
855 0 : crossProcessSharingController = lts.CrossProcessSharingController();
856 0 : });
857 :
858 0 : if (!geckoContentController) {
859 0 : needsApzc = false;
860 : }
861 :
862 0 : bool parentHasPerspective = aState.mParentHasPerspective.top();
863 :
864 0 : RefPtr<HitTestingTreeNode> node = nullptr;
865 0 : if (!needsApzc) {
866 : // Note: if layer properties must be propagated to nodes, RecvUpdate in
867 : // LayerTransactionParent.cpp must ensure that APZ will be notified
868 : // when those properties change.
869 0 : node = RecycleOrCreateNode(aProofOfTreeLock, aState, nullptr, aLayersId);
870 0 : AttachNodeToTree(node, aParent, aNextSibling);
871 0 : node->SetHitTestData(
872 : GetEventRegions(aLayer),
873 : aLayer.GetVisibleRegion(),
874 : aLayer.GetTransformTyped(),
875 0 : (!parentHasPerspective && aLayer.GetClipRect())
876 0 : ? Some(ParentLayerIntRegion(*aLayer.GetClipRect()))
877 : : Nothing(),
878 0 : GetEventRegionsOverride(aParent, aLayer),
879 : aLayer.IsBackfaceHidden());
880 0 : node->SetScrollbarData(aLayer.GetScrollbarAnimationId(),
881 : aLayer.GetScrollbarData());
882 0 : node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
883 0 : return node;
884 : }
885 :
886 0 : AsyncPanZoomController* apzc = nullptr;
887 : // If we get here, aLayer is a scrollable layer and somebody
888 : // has registered a GeckoContentController for it, so we need to ensure
889 : // it has an APZC instance to manage its scrolling.
890 :
891 : // aState.mApzcMap allows reusing the exact same APZC instance for different layers
892 : // with the same FrameMetrics data. This is needed because in some cases content
893 : // that is supposed to scroll together is split into multiple layers because of
894 : // e.g. non-scrolling content interleaved in z-index order.
895 0 : ScrollableLayerGuid guid(aLayersId, aMetrics);
896 0 : auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
897 0 : if (!insertResult.second) {
898 0 : apzc = insertResult.first->second;
899 0 : PrintAPZCInfo(aLayer, apzc);
900 : }
901 : APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n",
902 : apzc, aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId);
903 :
904 : // If we haven't encountered a layer already with the same metrics, then we need to
905 : // do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
906 : // below.
907 0 : if (apzc == nullptr) {
908 0 : apzc = aLayer.GetApzc();
909 :
910 : // If the content represented by the scrollable layer has changed (which may
911 : // be possible because of DLBI heuristics) then we don't want to keep using
912 : // the same old APZC for the new content. Also, when reparenting a tab into a
913 : // new window a layer might get moved to a different layer tree with a
914 : // different APZCTreeManager. In these cases we don't want to reuse the same
915 : // APZC, so null it out so we run through the code to find another one or
916 : // create one.
917 0 : if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
918 0 : apzc = nullptr;
919 : }
920 :
921 : // See if we can find an APZC from the previous tree that matches the
922 : // ScrollableLayerGuid from this layer. If there is one, then we know that
923 : // the layout of the page changed causing the layer tree to be rebuilt, but
924 : // the underlying content for the APZC is still there somewhere. Therefore,
925 : // we want to find the APZC instance and continue using it here.
926 : //
927 : // We particularly want to find the primary-holder node from the previous
928 : // tree that matches, because we don't want that node to get destroyed. If
929 : // it does get destroyed, then the APZC will get destroyed along with it by
930 : // definition, but we want to keep that APZC around in the new tree.
931 : // We leave non-primary-holder nodes in the destroy list because we don't
932 : // care about those nodes getting destroyed.
933 0 : for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
934 0 : RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
935 0 : if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) {
936 0 : node = n;
937 0 : if (apzc != nullptr) {
938 : // If there is an APZC already then it should match the one from the
939 : // old primary-holder node
940 0 : MOZ_ASSERT(apzc == node->GetApzc());
941 : }
942 0 : apzc = node->GetApzc();
943 0 : break;
944 : }
945 : }
946 :
947 : // The APZC we get off the layer may have been destroyed previously if the
948 : // layer was inactive or omitted from the layer tree for whatever reason
949 : // from a layers update. If it later comes back it will have a reference to
950 : // a destroyed APZC and so we need to throw that out and make a new one.
951 0 : bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
952 0 : if (newApzc) {
953 0 : apzc = NewAPZCInstance(aLayersId, geckoContentController);
954 0 : apzc->SetCompositorController(aState.mCompositorController.get());
955 0 : if (crossProcessSharingController) {
956 0 : apzc->SetMetricsSharingController(crossProcessSharingController);
957 : } else {
958 0 : apzc->SetMetricsSharingController(aState.mInProcessSharingController.get());
959 : }
960 0 : MOZ_ASSERT(node == nullptr);
961 0 : node = new HitTestingTreeNode(apzc, true, aLayersId);
962 : } else {
963 : // If we are re-using a node for this layer clear the tree pointers
964 : // so that it doesn't continue pointing to nodes that might no longer
965 : // be in the tree. These pointers will get reset properly as we continue
966 : // building the tree. Also remove it from the set of nodes that are going
967 : // to be destroyed, because it's going to remain active.
968 0 : aState.mNodesToDestroy.RemoveElement(node);
969 0 : node->SetPrevSibling(nullptr);
970 0 : node->SetLastChild(nullptr);
971 : }
972 :
973 : APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n",
974 : apzc, aLayer.GetLayer(), uint64_t(aLayersId), aMetrics.GetScrollId());
975 :
976 0 : apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
977 0 : aLayersId == aState.mOriginatingLayersId);
978 :
979 : // Since this is the first time we are encountering an APZC with this guid,
980 : // the node holding it must be the primary holder. It may be newly-created
981 : // or not, depending on whether it went through the newApzc branch above.
982 0 : MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
983 :
984 : Maybe<ParentLayerIntRegion> clipRegion = parentHasPerspective
985 : ? Nothing()
986 0 : : Some(ComputeClipRegion(aLayer));
987 0 : node->SetHitTestData(
988 : GetEventRegions(aLayer),
989 : aLayer.GetVisibleRegion(),
990 : aLayer.GetTransformTyped(),
991 : clipRegion,
992 0 : GetEventRegionsOverride(aParent, aLayer),
993 : aLayer.IsBackfaceHidden());
994 0 : apzc->SetAncestorTransform(aAncestorTransform);
995 :
996 0 : PrintAPZCInfo(aLayer, apzc);
997 :
998 : // Bind the APZC instance into the tree of APZCs
999 0 : AttachNodeToTree(node, aParent, aNextSibling);
1000 :
1001 : // For testing, log the parent scroll id of every APZC that has a
1002 : // parent. This allows test code to reconstruct the APZC tree.
1003 : // Note that we currently only do this for APZCs in the layer tree
1004 : // that originated the update, because the only identifying information
1005 : // we are logging about APZCs is the scroll id, and otherwise we could
1006 : // confuse APZCs from different layer trees with the same scroll id.
1007 0 : if (aLayersId == aState.mOriginatingLayersId) {
1008 0 : if (apzc->HasNoParentWithSameLayersId()) {
1009 0 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1010 : "hasNoParentWithSameLayersId", true);
1011 : } else {
1012 0 : MOZ_ASSERT(apzc->GetParent());
1013 0 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1014 0 : "parentScrollId", apzc->GetParent()->GetGuid().mScrollId);
1015 : }
1016 0 : if (aMetrics.IsRootContent()) {
1017 0 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1018 : "isRootContent", true);
1019 : }
1020 : // Note that the async scroll offset is in ParentLayer pixels
1021 0 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "asyncScrollOffset",
1022 0 : apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForHitTesting));
1023 0 : aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "hasAsyncKeyScrolled",
1024 0 : apzc->TestHasAsyncKeyScrolled());
1025 : }
1026 :
1027 0 : if (newApzc) {
1028 0 : auto it = mZoomConstraints.find(guid);
1029 0 : if (it != mZoomConstraints.end()) {
1030 : // We have a zoomconstraints for this guid, apply it.
1031 0 : apzc->UpdateZoomConstraints(it->second);
1032 0 : } else if (!apzc->HasNoParentWithSameLayersId()) {
1033 : // This is a sub-APZC, so inherit the zoom constraints from its parent.
1034 : // This ensures that if e.g. user-scalable=no was specified, none of the
1035 : // APZCs for that subtree allow double-tap to zoom.
1036 0 : apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
1037 : }
1038 : // Otherwise, this is the root of a layers id, but we didn't have a saved
1039 : // zoom constraints. Leave it empty for now.
1040 : }
1041 :
1042 : // Add a guid -> APZC mapping for the newly created APZC.
1043 0 : insertResult.first->second = apzc;
1044 : } else {
1045 : // We already built an APZC earlier in this tree walk, but we have another layer
1046 : // now that will also be using that APZC. The hit-test region on the APZC needs
1047 : // to be updated to deal with the new layer's hit region.
1048 :
1049 0 : node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId);
1050 0 : AttachNodeToTree(node, aParent, aNextSibling);
1051 :
1052 : // Even though different layers associated with a given APZC may be at
1053 : // different levels in the layer tree (e.g. one being an uncle of another),
1054 : // we require from Layout that the CSS transforms up to their common
1055 : // ancestor be roughly the same. There are cases in which the transforms
1056 : // are not exactly the same, for example if the parent is container layer
1057 : // for an opacity, and this container layer has a resolution-induced scale
1058 : // as its base transform and a prescale that is supposed to undo that scale.
1059 : // Due to floating point inaccuracies those transforms can end up not quite
1060 : // canceling each other. That's why we're using a fuzzy comparison here
1061 : // instead of an exact one.
1062 : // In addition, two ancestor transforms are allowed to differ if one of
1063 : // them contains a perspective transform component and the other does not.
1064 : // This represents situations where some content in a scrollable frame
1065 : // is subject to a perspective transform and other content does not.
1066 : // In such cases, go with the one that does not include the perspective
1067 : // component; the perspective transform is remembered and applied to the
1068 : // children instead.
1069 0 : if (!aAncestorTransform.CombinedTransform().FuzzyEqualsMultiplicative(apzc->GetAncestorTransform())) {
1070 : typedef TreeBuildingState::DeferredTransformMap::value_type PairType;
1071 0 : if (!aAncestorTransform.ContainsPerspectiveTransform() &&
1072 0 : !apzc->AncestorTransformContainsPerspective()) {
1073 0 : MOZ_ASSERT(false, "Two layers that scroll together have different ancestor transforms");
1074 0 : } else if (!aAncestorTransform.ContainsPerspectiveTransform()) {
1075 0 : aState.mPerspectiveTransformsDeferredToChildren.insert(
1076 : PairType{apzc, apzc->GetAncestorTransformPerspective()});
1077 0 : apzc->SetAncestorTransform(aAncestorTransform);
1078 : } else {
1079 0 : aState.mPerspectiveTransformsDeferredToChildren.insert(
1080 : PairType{apzc, aAncestorTransform.GetPerspectiveTransform()});
1081 : }
1082 : }
1083 :
1084 : Maybe<ParentLayerIntRegion> clipRegion = parentHasPerspective
1085 : ? Nothing()
1086 0 : : Some(ComputeClipRegion(aLayer));
1087 0 : node->SetHitTestData(
1088 : GetEventRegions(aLayer),
1089 : aLayer.GetVisibleRegion(),
1090 : aLayer.GetTransformTyped(),
1091 : clipRegion,
1092 0 : GetEventRegionsOverride(aParent, aLayer),
1093 : aLayer.IsBackfaceHidden());
1094 : }
1095 :
1096 : // Note: if layer properties must be propagated to nodes, RecvUpdate in
1097 : // LayerTransactionParent.cpp must ensure that APZ will be notified
1098 : // when those properties change.
1099 0 : node->SetScrollbarData(aLayer.GetScrollbarAnimationId(),
1100 : aLayer.GetScrollbarData());
1101 0 : node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
1102 0 : return node;
1103 : }
1104 :
1105 : template<typename PanGestureOrScrollWheelInput>
1106 : static bool
1107 0 : WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
1108 : {
1109 0 : if (!XRE_IsParentProcess() || !NS_IsMainThread()) {
1110 : return true;
1111 : }
1112 :
1113 0 : WidgetWheelEvent wheelEvent = aPanInput.ToWidgetWheelEvent(nullptr);
1114 0 : return APZInputBridge::ActionForWheelEvent(&wheelEvent).isSome();
1115 : }
1116 :
1117 : void
1118 0 : APZCTreeManager::FlushApzRepaints(LayersId aLayersId)
1119 : {
1120 : // Previously, paints were throttled and therefore this method was used to
1121 : // ensure any pending paints were flushed. Now, paints are flushed
1122 : // immediately, so it is safe to simply send a notification now.
1123 : APZCTM_LOG("Flushing repaints for layers id 0x%" PRIx64 "\n", uint64_t(aLayersId));
1124 0 : RefPtr<GeckoContentController> controller = GetContentController(aLayersId);
1125 0 : MOZ_ASSERT(controller);
1126 0 : controller->DispatchToRepaintThread(
1127 0 : NewRunnableMethod("layers::GeckoContentController::NotifyFlushComplete",
1128 : controller,
1129 0 : &GeckoContentController::NotifyFlushComplete));
1130 0 : }
1131 :
1132 : nsEventStatus
1133 0 : APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
1134 : ScrollableLayerGuid* aOutTargetGuid,
1135 : uint64_t* aOutInputBlockId)
1136 : {
1137 0 : APZThreadUtils::AssertOnControllerThread();
1138 :
1139 : // Use a RAII class for updating the focus sequence number of this event
1140 0 : AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
1141 :
1142 : #if defined(MOZ_WIDGET_ANDROID)
1143 : MOZ_ASSERT(mToolbarAnimator);
1144 : ScreenPoint scrollOffset;
1145 : {
1146 : RecursiveMutexAutoLock lock(mTreeLock);
1147 : RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
1148 : if (apzc) {
1149 : scrollOffset = ViewAs<ScreenPixel>(apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForHitTesting),
1150 : PixelCastJustification::ScreenIsParentLayerForRoot);
1151 : }
1152 : }
1153 : RefPtr<APZCTreeManager> self = this;
1154 : nsEventStatus isConsumed = mToolbarAnimator->ReceiveInputEvent(self, aEvent, scrollOffset);
1155 : // Check if the mToolbarAnimator consumed the event.
1156 : if (isConsumed == nsEventStatus_eConsumeNoDefault) {
1157 : APZCTM_LOG("Dynamic toolbar consumed event");
1158 : return isConsumed;
1159 : }
1160 : #endif // (MOZ_WIDGET_ANDROID)
1161 :
1162 : // Initialize aOutInputBlockId to a sane value, and then later we overwrite
1163 : // it if the input event goes into a block.
1164 0 : if (aOutInputBlockId) {
1165 0 : *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
1166 : }
1167 0 : nsEventStatus result = nsEventStatus_eIgnore;
1168 0 : CompositorHitTestInfo hitResult = CompositorHitTestInfo::eInvisibleToHitTest;
1169 0 : switch (aEvent.mInputType) {
1170 : case MULTITOUCH_INPUT: {
1171 0 : MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
1172 0 : result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
1173 0 : break;
1174 : } case MOUSE_INPUT: {
1175 0 : MouseInput& mouseInput = aEvent.AsMouseInput();
1176 0 : mouseInput.mHandledByAPZ = true;
1177 :
1178 0 : mCurrentMousePosition = mouseInput.mOrigin;
1179 :
1180 0 : bool startsDrag = DragTracker::StartsDrag(mouseInput);
1181 0 : if (startsDrag) {
1182 : // If this is the start of a drag we need to unambiguously know if it's
1183 : // going to land on a scrollbar or not. We can't apply an untransform
1184 : // here without knowing that, so we need to ensure the untransform is
1185 : // a no-op.
1186 0 : FlushRepaintsToClearScreenToGeckoTransform();
1187 : }
1188 :
1189 0 : HitTestingTreeNodeAutoLock hitScrollbarNode;
1190 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
1191 0 : &hitResult, &hitScrollbarNode);
1192 0 : bool hitScrollbar = (bool)hitScrollbarNode;
1193 :
1194 : // When the mouse is outside the window we still want to handle dragging
1195 : // but we won't find an APZC. Fallback to root APZC then.
1196 : { // scope lock
1197 0 : RecursiveMutexAutoLock lock(mTreeLock);
1198 0 : if (!apzc && mRootNode) {
1199 0 : apzc = mRootNode->GetApzc();
1200 : }
1201 : }
1202 :
1203 0 : if (apzc) {
1204 0 : if (gfxPrefs::APZTestLoggingEnabled() && mouseInput.mType == MouseInput::MOUSE_HITTEST) {
1205 0 : ScrollableLayerGuid guid = apzc->GetGuid();
1206 :
1207 0 : MutexAutoLock lock(mTestDataLock);
1208 0 : auto it = mTestData.find(guid.mLayersId);
1209 0 : MOZ_ASSERT(it != mTestData.end());
1210 0 : it->second->RecordHitResult(mouseInput.mOrigin, hitResult, guid.mScrollId);
1211 : }
1212 :
1213 0 : TargetConfirmationFlags confFlags{hitResult};
1214 0 : bool apzDragEnabled = gfxPrefs::APZDragEnabled();
1215 0 : if (apzDragEnabled && hitScrollbar) {
1216 : // If scrollbar dragging is enabled and we hit a scrollbar, wait
1217 : // for the main-thread confirmation because it contains drag metrics
1218 : // that we need.
1219 0 : confFlags.mTargetConfirmed = false;
1220 : }
1221 0 : result = mInputQueue->ReceiveInputEvent(
1222 0 : apzc, confFlags, mouseInput, aOutInputBlockId);
1223 :
1224 : // If we're starting an async scrollbar drag
1225 0 : if (apzDragEnabled && startsDrag && hitScrollbarNode &&
1226 0 : hitScrollbarNode->IsScrollThumbNode() &&
1227 0 : hitScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable) {
1228 0 : SetupScrollbarDrag(mouseInput, hitScrollbarNode, apzc.get());
1229 : }
1230 :
1231 0 : if (result == nsEventStatus_eConsumeDoDefault) {
1232 : // This input event is part of a drag block, so whether or not it is
1233 : // directed at a scrollbar depends on whether the drag block started
1234 : // on a scrollbar.
1235 0 : hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
1236 : }
1237 :
1238 : // Update the out-parameters so they are what the caller expects.
1239 0 : apzc->GetGuid(aOutTargetGuid);
1240 :
1241 0 : if (!hitScrollbar) {
1242 : // The input was not targeted at a scrollbar, so we untransform it
1243 : // like we do for other content. Scrollbars are "special" because they
1244 : // have special handling in AsyncCompositionManager when resolution is
1245 : // applied. TODO: we should find a better way to deal with this.
1246 0 : ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
1247 0 : ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
1248 0 : ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
1249 : Maybe<ScreenPoint> untransformedRefPoint = UntransformBy(
1250 0 : outTransform, mouseInput.mOrigin);
1251 0 : if (untransformedRefPoint) {
1252 0 : mouseInput.mOrigin = *untransformedRefPoint;
1253 : }
1254 : } else {
1255 : // Likewise, if the input was targeted at a scrollbar, we don't want to
1256 : // apply the callback transform in the main thread, so we remove the
1257 : // scrollid from the guid. We need to keep the layersId intact so
1258 : // that the response from the child process doesn't get discarded.
1259 0 : aOutTargetGuid->mScrollId = FrameMetrics::NULL_SCROLL_ID;
1260 : }
1261 : }
1262 : break;
1263 : } case SCROLLWHEEL_INPUT: {
1264 0 : FlushRepaintsToClearScreenToGeckoTransform();
1265 :
1266 0 : ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
1267 0 : wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
1268 0 : if (!wheelInput.mHandledByAPZ) {
1269 0 : return result;
1270 : }
1271 :
1272 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
1273 0 : &hitResult);
1274 0 : if (apzc) {
1275 0 : MOZ_ASSERT(hitResult != CompositorHitTestInfo::eInvisibleToHitTest);
1276 :
1277 0 : if (wheelInput.mAPZAction == APZWheelAction::PinchZoom) {
1278 : // The mousewheel may have hit a subframe, but we want to send the
1279 : // pinch-zoom events to the root-content APZC.
1280 : {
1281 0 : RecursiveMutexAutoLock lock(mTreeLock);
1282 0 : apzc = FindRootContentApzcForLayersId(apzc->GetLayersId());
1283 : }
1284 0 : if (apzc) {
1285 0 : SynthesizePinchGestureFromMouseWheel(wheelInput, apzc);
1286 : }
1287 0 : return nsEventStatus_eConsumeNoDefault;
1288 : }
1289 :
1290 0 : MOZ_ASSERT(wheelInput.mAPZAction == APZWheelAction::Scroll);
1291 :
1292 : // For wheel events, the call to ReceiveInputEvent below may result in
1293 : // scrolling, which changes the async transform. However, the event we
1294 : // want to pass to gecko should be the pre-scroll event coordinates,
1295 : // transformed into the gecko space. (pre-scroll because the mouse
1296 : // cursor is stationary during wheel scrolling, unlike touchmove
1297 : // events). Since we just flushed the pending repaints the transform to
1298 : // gecko space should only consist of overscroll-cancelling transforms.
1299 0 : ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
1300 0 : * GetApzcToGeckoTransform(apzc);
1301 : Maybe<ScreenPoint> untransformedOrigin = UntransformBy(
1302 0 : transformToGecko, wheelInput.mOrigin);
1303 :
1304 0 : if (!untransformedOrigin) {
1305 0 : return result;
1306 : }
1307 :
1308 0 : result = mInputQueue->ReceiveInputEvent(
1309 : apzc,
1310 : TargetConfirmationFlags{hitResult},
1311 0 : wheelInput, aOutInputBlockId);
1312 :
1313 : // Update the out-parameters so they are what the caller expects.
1314 0 : apzc->GetGuid(aOutTargetGuid);
1315 0 : wheelInput.mOrigin = *untransformedOrigin;
1316 : }
1317 0 : break;
1318 : } case PANGESTURE_INPUT: {
1319 0 : FlushRepaintsToClearScreenToGeckoTransform();
1320 :
1321 0 : PanGestureInput& panInput = aEvent.AsPanGestureInput();
1322 0 : panInput.mHandledByAPZ = WillHandleInput(panInput);
1323 0 : if (!panInput.mHandledByAPZ) {
1324 0 : return result;
1325 : }
1326 :
1327 : // If/when we enable support for pan inputs off-main-thread, we'll need
1328 : // to duplicate this EventStateManager code or something. See the call to
1329 : // GetUserPrefsForWheelEvent in IAPZCTreeManager.cpp for why these fields
1330 : // are stored separately.
1331 0 : MOZ_ASSERT(NS_IsMainThread());
1332 0 : WidgetWheelEvent wheelEvent = panInput.ToWidgetWheelEvent(nullptr);
1333 0 : EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
1334 : &panInput.mUserDeltaMultiplierX,
1335 0 : &panInput.mUserDeltaMultiplierY);
1336 :
1337 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
1338 0 : &hitResult);
1339 0 : if (apzc) {
1340 0 : MOZ_ASSERT(hitResult != CompositorHitTestInfo::eInvisibleToHitTest);
1341 :
1342 : // For pan gesture events, the call to ReceiveInputEvent below may result in
1343 : // scrolling, which changes the async transform. However, the event we
1344 : // want to pass to gecko should be the pre-scroll event coordinates,
1345 : // transformed into the gecko space. (pre-scroll because the mouse
1346 : // cursor is stationary during pan gesture scrolling, unlike touchmove
1347 : // events). Since we just flushed the pending repaints the transform to
1348 : // gecko space should only consist of overscroll-cancelling transforms.
1349 0 : ScreenToScreenMatrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
1350 0 : * GetApzcToGeckoTransform(apzc);
1351 : Maybe<ScreenPoint> untransformedStartPoint = UntransformBy(
1352 0 : transformToGecko, panInput.mPanStartPoint);
1353 : Maybe<ScreenPoint> untransformedDisplacement = UntransformVector(
1354 0 : transformToGecko, panInput.mPanDisplacement, panInput.mPanStartPoint);
1355 :
1356 0 : if (!untransformedStartPoint || !untransformedDisplacement) {
1357 0 : return result;
1358 : }
1359 :
1360 0 : result = mInputQueue->ReceiveInputEvent(
1361 : apzc,
1362 : TargetConfirmationFlags{hitResult},
1363 0 : panInput, aOutInputBlockId);
1364 :
1365 : // Update the out-parameters so they are what the caller expects.
1366 0 : apzc->GetGuid(aOutTargetGuid);
1367 0 : panInput.mPanStartPoint = *untransformedStartPoint;
1368 0 : panInput.mPanDisplacement = *untransformedDisplacement;
1369 :
1370 0 : panInput.mOverscrollBehaviorAllowsSwipe =
1371 0 : apzc->OverscrollBehaviorAllowsSwipe();
1372 : }
1373 0 : break;
1374 : } case PINCHGESTURE_INPUT: { // note: no one currently sends these
1375 0 : PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
1376 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint,
1377 0 : &hitResult);
1378 0 : if (apzc) {
1379 0 : MOZ_ASSERT(hitResult != CompositorHitTestInfo::eInvisibleToHitTest);
1380 :
1381 0 : ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
1382 0 : * GetApzcToGeckoTransform(apzc);
1383 : Maybe<ScreenPoint> untransformedFocusPoint = UntransformBy(
1384 0 : outTransform, pinchInput.mFocusPoint);
1385 :
1386 0 : if (!untransformedFocusPoint) {
1387 0 : return result;
1388 : }
1389 :
1390 0 : result = mInputQueue->ReceiveInputEvent(
1391 : apzc,
1392 : TargetConfirmationFlags{hitResult},
1393 0 : pinchInput, aOutInputBlockId);
1394 :
1395 : // Update the out-parameters so they are what the caller expects.
1396 0 : apzc->GetGuid(aOutTargetGuid);
1397 0 : pinchInput.mFocusPoint = *untransformedFocusPoint;
1398 : }
1399 0 : break;
1400 : } case TAPGESTURE_INPUT: { // note: no one currently sends these
1401 0 : TapGestureInput& tapInput = aEvent.AsTapGestureInput();
1402 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(tapInput.mPoint,
1403 0 : &hitResult);
1404 0 : if (apzc) {
1405 0 : MOZ_ASSERT(hitResult != CompositorHitTestInfo::eInvisibleToHitTest);
1406 :
1407 0 : ScreenToScreenMatrix4x4 outTransform = GetScreenToApzcTransform(apzc)
1408 0 : * GetApzcToGeckoTransform(apzc);
1409 : Maybe<ScreenIntPoint> untransformedPoint =
1410 0 : UntransformBy(outTransform, tapInput.mPoint);
1411 :
1412 0 : if (!untransformedPoint) {
1413 0 : return result;
1414 : }
1415 :
1416 0 : result = mInputQueue->ReceiveInputEvent(
1417 : apzc,
1418 : TargetConfirmationFlags{hitResult},
1419 0 : tapInput, aOutInputBlockId);
1420 :
1421 : // Update the out-parameters so they are what the caller expects.
1422 0 : apzc->GetGuid(aOutTargetGuid);
1423 0 : tapInput.mPoint = *untransformedPoint;
1424 : }
1425 0 : break;
1426 : } case KEYBOARD_INPUT: {
1427 : // Disable async keyboard scrolling when accessibility.browsewithcaret is enabled
1428 0 : if (!gfxPrefs::APZKeyboardEnabled() ||
1429 0 : gfxPrefs::AccessibilityBrowseWithCaret()) {
1430 : APZ_KEY_LOG("Skipping key input from invalid prefs\n");
1431 0 : return result;
1432 : }
1433 :
1434 0 : KeyboardInput& keyInput = aEvent.AsKeyboardInput();
1435 :
1436 : // Try and find a matching shortcut for this keyboard input
1437 0 : Maybe<KeyboardShortcut> shortcut = mKeyboardMap.FindMatch(keyInput);
1438 :
1439 0 : if (!shortcut) {
1440 : APZ_KEY_LOG("Skipping key input with no shortcut\n");
1441 :
1442 : // If we don't have a shortcut for this key event, then we can keep our focus
1443 : // only if we know there are no key event listeners for this target
1444 0 : if (mFocusState.CanIgnoreKeyboardShortcutMisses()) {
1445 0 : focusSetter.MarkAsNonFocusChanging();
1446 : }
1447 : return result;
1448 : }
1449 :
1450 : // Check if this shortcut needs to be dispatched to content. Anything matching
1451 : // this is assumed to be able to change focus.
1452 0 : if (shortcut->mDispatchToContent) {
1453 : APZ_KEY_LOG("Skipping key input with dispatch-to-content shortcut\n");
1454 : return result;
1455 : }
1456 :
1457 : // We know we have an action to execute on whatever is the current focus target
1458 0 : const KeyboardScrollAction& action = shortcut->mAction;
1459 :
1460 : // The current focus target depends on which direction the scroll is to happen
1461 0 : Maybe<ScrollableLayerGuid> targetGuid;
1462 0 : switch (action.mType)
1463 : {
1464 : case KeyboardScrollAction::eScrollCharacter: {
1465 0 : targetGuid = mFocusState.GetHorizontalTarget();
1466 0 : break;
1467 : }
1468 : case KeyboardScrollAction::eScrollLine:
1469 : case KeyboardScrollAction::eScrollPage:
1470 : case KeyboardScrollAction::eScrollComplete: {
1471 0 : targetGuid = mFocusState.GetVerticalTarget();
1472 0 : break;
1473 : }
1474 : }
1475 :
1476 : // If we don't have a scroll target then either we have a stale focus target,
1477 : // the focused element has event listeners, or the focused element doesn't have a
1478 : // layerized scroll frame. In any case we need to dispatch to content.
1479 0 : if (!targetGuid) {
1480 : APZ_KEY_LOG("Skipping key input with no current focus target\n");
1481 : return result;
1482 : }
1483 :
1484 0 : RefPtr<AsyncPanZoomController> targetApzc = GetTargetAPZC(targetGuid->mLayersId,
1485 0 : targetGuid->mScrollId);
1486 :
1487 0 : if (!targetApzc) {
1488 : APZ_KEY_LOG("Skipping key input with focus target but no APZC\n");
1489 0 : return result;
1490 : }
1491 :
1492 : // Attach the keyboard scroll action to the input event for processing
1493 : // by the input queue.
1494 0 : keyInput.mAction = action;
1495 :
1496 : APZ_KEY_LOG("Dispatching key input with apzc=%p\n",
1497 : targetApzc.get());
1498 :
1499 : // Dispatch the event to the input queue.
1500 0 : result = mInputQueue->ReceiveInputEvent(
1501 : targetApzc,
1502 : TargetConfirmationFlags{true},
1503 0 : keyInput, aOutInputBlockId);
1504 :
1505 : // Any keyboard event that is dispatched to the input queue at this point
1506 : // should have been consumed
1507 0 : MOZ_ASSERT(result == nsEventStatus_eConsumeDoDefault ||
1508 : result == nsEventStatus_eConsumeNoDefault);
1509 :
1510 0 : keyInput.mHandledByAPZ = true;
1511 0 : focusSetter.MarkAsNonFocusChanging();
1512 :
1513 0 : break;
1514 : }
1515 : }
1516 : return result;
1517 : }
1518 :
1519 : static TouchBehaviorFlags
1520 0 : ConvertToTouchBehavior(CompositorHitTestInfo info)
1521 : {
1522 0 : TouchBehaviorFlags result = AllowedTouchBehavior::UNKNOWN;
1523 0 : if (info == CompositorHitTestInfo::eInvisibleToHitTest) {
1524 : result = AllowedTouchBehavior::NONE;
1525 0 : } else if (info & CompositorHitTestInfo::eDispatchToContent) {
1526 : result = AllowedTouchBehavior::UNKNOWN;
1527 : } else {
1528 0 : result = AllowedTouchBehavior::VERTICAL_PAN
1529 : | AllowedTouchBehavior::HORIZONTAL_PAN
1530 : | AllowedTouchBehavior::PINCH_ZOOM
1531 : | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
1532 0 : if (info & CompositorHitTestInfo::eTouchActionPanXDisabled) {
1533 0 : result &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
1534 : }
1535 0 : if (info & CompositorHitTestInfo::eTouchActionPanYDisabled) {
1536 0 : result &= ~AllowedTouchBehavior::VERTICAL_PAN;
1537 : }
1538 0 : if (info & CompositorHitTestInfo::eTouchActionPinchZoomDisabled) {
1539 0 : result &= ~AllowedTouchBehavior::PINCH_ZOOM;
1540 : }
1541 0 : if (info & CompositorHitTestInfo::eTouchActionDoubleTapZoomDisabled) {
1542 0 : result &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
1543 : }
1544 : }
1545 0 : return result;
1546 : }
1547 :
1548 : already_AddRefed<AsyncPanZoomController>
1549 0 : APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
1550 : nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors,
1551 : CompositorHitTestInfo* aOutHitResult,
1552 : HitTestingTreeNodeAutoLock* aOutHitScrollbarNode)
1553 : {
1554 0 : RefPtr<AsyncPanZoomController> apzc;
1555 0 : if (aEvent.mTouches.Length() == 0) {
1556 : return apzc.forget();
1557 : }
1558 :
1559 0 : FlushRepaintsToClearScreenToGeckoTransform();
1560 :
1561 : CompositorHitTestInfo hitResult;
1562 0 : apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, &hitResult,
1563 0 : aOutHitScrollbarNode);
1564 0 : if (aOutTouchBehaviors) {
1565 0 : aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
1566 : }
1567 0 : for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
1568 0 : RefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, &hitResult);
1569 0 : if (aOutTouchBehaviors) {
1570 0 : aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hitResult));
1571 : }
1572 0 : apzc = GetMultitouchTarget(apzc, apzc2);
1573 : APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
1574 : // A multi-touch gesture will not be a scrollbar drag, even if the
1575 : // first touch point happened to hit a scrollbar.
1576 0 : aOutHitScrollbarNode->Clear();
1577 : }
1578 :
1579 0 : if (aOutHitResult) {
1580 : // XXX we should probably be combining the hit results from the different
1581 : // touch points somehow, instead of just using the last one.
1582 0 : *aOutHitResult = hitResult;
1583 : }
1584 : return apzc.forget();
1585 : }
1586 :
1587 : nsEventStatus
1588 0 : APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
1589 : ScrollableLayerGuid* aOutTargetGuid,
1590 : uint64_t* aOutInputBlockId)
1591 : {
1592 0 : aInput.mHandledByAPZ = true;
1593 0 : nsTArray<TouchBehaviorFlags> touchBehaviors;
1594 0 : HitTestingTreeNodeAutoLock hitScrollbarNode;
1595 0 : if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
1596 : // If we are panned into overscroll and a second finger goes down,
1597 : // ignore that second touch point completely. The touch-start for it is
1598 : // dropped completely; subsequent touch events until the touch-end for it
1599 : // will have this touch point filtered out.
1600 : // (By contrast, if we're in overscroll but not panning, such as after
1601 : // putting two fingers down during an overscroll animation, we process the
1602 : // second touch and proceed to pinch.)
1603 0 : if (mApzcForInputBlock &&
1604 0 : mApzcForInputBlock->IsInPanningState() &&
1605 0 : BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
1606 0 : if (mRetainedTouchIdentifier == -1) {
1607 0 : mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
1608 : }
1609 : return nsEventStatus_eConsumeNoDefault;
1610 : }
1611 :
1612 0 : mHitResultForInputBlock = CompositorHitTestInfo::eInvisibleToHitTest;
1613 0 : mApzcForInputBlock = GetTouchInputBlockAPZC(aInput, &touchBehaviors,
1614 0 : &mHitResultForInputBlock, &hitScrollbarNode);
1615 :
1616 : // Check if this event starts a scrollbar touch-drag. The conditions
1617 : // checked are similar to the ones we check for MOUSE_INPUT starting
1618 : // a scrollbar mouse-drag.
1619 0 : mInScrollbarTouchDrag = gfxPrefs::APZDragEnabled() &&
1620 0 : gfxPrefs::APZTouchDragEnabled() && hitScrollbarNode &&
1621 0 : hitScrollbarNode->IsScrollThumbNode() &&
1622 0 : hitScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable;
1623 :
1624 0 : MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length());
1625 0 : for (size_t i = 0; i < touchBehaviors.Length(); i++) {
1626 : APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n", touchBehaviors[i]);
1627 0 : if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) {
1628 : // If there's any unknown items in the list, throw it out and we'll
1629 : // wait for the main thread to send us a notification.
1630 0 : touchBehaviors.Clear();
1631 0 : break;
1632 : }
1633 : }
1634 : } else if (mApzcForInputBlock) {
1635 : APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
1636 : }
1637 :
1638 0 : nsEventStatus result = nsEventStatus_eIgnore;
1639 :
1640 0 : if (mInScrollbarTouchDrag) {
1641 : result = ProcessTouchInputForScrollbarDrag(aInput, hitScrollbarNode,
1642 0 : aOutTargetGuid, aOutInputBlockId);
1643 : } else {
1644 : // If we receive a touch-cancel, it means all touches are finished, so we
1645 : // can stop ignoring any that we were ignoring.
1646 0 : if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
1647 0 : mRetainedTouchIdentifier = -1;
1648 : }
1649 :
1650 : // If we are currently ignoring any touch points, filter them out from the
1651 : // set of touch points included in this event. Note that we modify aInput
1652 : // itself, so that the touch points are also filtered out when the caller
1653 : // passes the event on to content.
1654 0 : if (mRetainedTouchIdentifier != -1) {
1655 0 : for (size_t j = 0; j < aInput.mTouches.Length(); ++j) {
1656 0 : if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
1657 0 : aInput.mTouches.RemoveElementAt(j);
1658 0 : if (!touchBehaviors.IsEmpty()) {
1659 0 : MOZ_ASSERT(touchBehaviors.Length() > j);
1660 0 : touchBehaviors.RemoveElementAt(j);
1661 : }
1662 0 : --j;
1663 : }
1664 : }
1665 0 : if (aInput.mTouches.IsEmpty()) {
1666 : return nsEventStatus_eConsumeNoDefault;
1667 : }
1668 : }
1669 :
1670 0 : if (mApzcForInputBlock) {
1671 0 : MOZ_ASSERT(mHitResultForInputBlock != CompositorHitTestInfo::eInvisibleToHitTest);
1672 :
1673 0 : mApzcForInputBlock->GetGuid(aOutTargetGuid);
1674 0 : uint64_t inputBlockId = 0;
1675 0 : result = mInputQueue->ReceiveInputEvent(mApzcForInputBlock,
1676 : TargetConfirmationFlags{mHitResultForInputBlock},
1677 0 : aInput, &inputBlockId);
1678 0 : if (aOutInputBlockId) {
1679 0 : *aOutInputBlockId = inputBlockId;
1680 : }
1681 0 : if (!touchBehaviors.IsEmpty()) {
1682 0 : mInputQueue->SetAllowedTouchBehavior(inputBlockId, touchBehaviors);
1683 : }
1684 :
1685 : // For computing the event to pass back to Gecko, use up-to-date transforms
1686 : // (i.e. not anything cached in an input block).
1687 : // This ensures that transformToApzc and transformToGecko are in sync.
1688 0 : ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
1689 0 : ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
1690 0 : ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
1691 :
1692 0 : for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
1693 0 : SingleTouchData& touchData = aInput.mTouches[i];
1694 : Maybe<ScreenIntPoint> untransformedScreenPoint = UntransformBy(
1695 0 : outTransform, touchData.mScreenPoint);
1696 0 : if (!untransformedScreenPoint) {
1697 0 : return nsEventStatus_eIgnore;
1698 : }
1699 0 : touchData.mScreenPoint = *untransformedScreenPoint;
1700 : }
1701 : }
1702 : }
1703 :
1704 0 : mTouchCounter.Update(aInput);
1705 :
1706 : // If it's the end of the touch sequence then clear out variables so we
1707 : // don't keep dangling references and leak things.
1708 0 : if (mTouchCounter.GetActiveTouchCount() == 0) {
1709 0 : mApzcForInputBlock = nullptr;
1710 0 : mHitResultForInputBlock = CompositorHitTestInfo::eInvisibleToHitTest;
1711 0 : mRetainedTouchIdentifier = -1;
1712 0 : mInScrollbarTouchDrag = false;
1713 : }
1714 :
1715 : return result;
1716 : }
1717 :
1718 : MouseInput::MouseType
1719 0 : MultiTouchTypeToMouseType(MultiTouchInput::MultiTouchType aType)
1720 : {
1721 0 : switch (aType)
1722 : {
1723 : case MultiTouchInput::MULTITOUCH_START:
1724 : return MouseInput::MOUSE_DOWN;
1725 : case MultiTouchInput::MULTITOUCH_MOVE:
1726 0 : return MouseInput::MOUSE_MOVE;
1727 : case MultiTouchInput::MULTITOUCH_END:
1728 : case MultiTouchInput::MULTITOUCH_CANCEL:
1729 0 : return MouseInput::MOUSE_UP;
1730 : }
1731 0 : MOZ_ASSERT_UNREACHABLE("Invalid multi-touch type");
1732 : return MouseInput::MOUSE_NONE;
1733 : }
1734 :
1735 : nsEventStatus
1736 0 : APZCTreeManager::ProcessTouchInputForScrollbarDrag(MultiTouchInput& aTouchInput,
1737 : const HitTestingTreeNodeAutoLock& aScrollThumbNode,
1738 : ScrollableLayerGuid* aOutTargetGuid,
1739 : uint64_t* aOutInputBlockId)
1740 : {
1741 0 : MOZ_ASSERT(mRetainedTouchIdentifier == -1);
1742 0 : MOZ_ASSERT(mApzcForInputBlock);
1743 0 : MOZ_ASSERT(aTouchInput.mTouches.Length() == 1);
1744 :
1745 : // Synthesize a mouse event based on the touch event, so that we can
1746 : // reuse code in InputQueue and APZC for handling scrollbar mouse-drags.
1747 : MouseInput mouseInput{MultiTouchTypeToMouseType(aTouchInput.mType),
1748 : MouseInput::LEFT_BUTTON,
1749 : dom::MouseEventBinding::MOZ_SOURCE_TOUCH,
1750 : WidgetMouseEvent::eLeftButtonFlag,
1751 0 : aTouchInput.mTouches[0].mScreenPoint,
1752 : aTouchInput.mTime,
1753 : aTouchInput.mTimeStamp,
1754 0 : aTouchInput.modifiers};
1755 0 : mouseInput.mHandledByAPZ = true;
1756 :
1757 : // The value of |targetConfirmed| passed to InputQueue::ReceiveInputEvent()
1758 : // only matters for the first event, which creates the drag block. For
1759 : // that event, the correct value is false, since the drag block will, at the
1760 : // earliest, be confirmed in the subsequent SetupScrollbarDrag() call.
1761 0 : TargetConfirmationFlags targetConfirmed{false};
1762 :
1763 0 : nsEventStatus result = mInputQueue->ReceiveInputEvent(mApzcForInputBlock,
1764 0 : targetConfirmed, mouseInput, aOutInputBlockId);
1765 :
1766 : // |aScrollThumbNode| is non-null iff. this is the event that starts the drag.
1767 : // If so, set up the drag.
1768 0 : if (aScrollThumbNode) {
1769 0 : SetupScrollbarDrag(mouseInput, aScrollThumbNode, mApzcForInputBlock.get());
1770 : }
1771 :
1772 0 : mApzcForInputBlock->GetGuid(aOutTargetGuid);
1773 :
1774 : // Since the input was targeted at a scrollbar:
1775 : // - The original touch event (which will be sent on to content) will
1776 : // not be untransformed.
1777 : // - We don't want to apply the callback transform in the main thread,
1778 : // so we remove the scrollid from the guid.
1779 : // Both of these match the behaviour of mouse events that target a scrollbar;
1780 : // see the code for handling mouse events in ReceiveInputEvent() for
1781 : // additional explanation.
1782 0 : aOutTargetGuid->mScrollId = FrameMetrics::NULL_SCROLL_ID;
1783 :
1784 0 : return result;
1785 : }
1786 :
1787 : void
1788 0 : APZCTreeManager::SetupScrollbarDrag(MouseInput& aMouseInput,
1789 : const HitTestingTreeNodeAutoLock& aScrollThumbNode,
1790 : AsyncPanZoomController* aApzc)
1791 : {
1792 0 : DragBlockState* dragBlock = mInputQueue->GetCurrentDragBlock();
1793 0 : if (!dragBlock) {
1794 : return;
1795 : }
1796 :
1797 0 : const ScrollbarData& thumbData = aScrollThumbNode->GetScrollbarData();
1798 0 : MOZ_ASSERT(thumbData.mDirection.isSome());
1799 :
1800 : // Record the thumb's position at the start of the drag.
1801 : // We snap back to this position if, during the drag, the mouse
1802 : // gets sufficiently far away from the scrollbar.
1803 0 : dragBlock->SetInitialThumbPos(thumbData.mThumbStart);
1804 :
1805 : // Under some conditions, we can confirm the drag block right away.
1806 : // Otherwise, we have to wait for a main-thread confirmation.
1807 0 : if (gfxPrefs::APZDragInitiationEnabled() &&
1808 : // check that the scrollbar's target scroll frame is layerized
1809 0 : aScrollThumbNode->GetScrollTargetId() == aApzc->GetGuid().mScrollId &&
1810 0 : !aApzc->IsScrollInfoLayer()) {
1811 0 : uint64_t dragBlockId = dragBlock->GetBlockId();
1812 : // AsyncPanZoomController::HandleInputEvent() will call
1813 : // TransformToLocal() on the event, but we need its mLocalOrigin now
1814 : // to compute a drag start offset for the AsyncDragMetrics.
1815 0 : aMouseInput.TransformToLocal(aApzc->GetTransformToThis());
1816 : CSSCoord dragStart = aApzc->ConvertScrollbarPoint(
1817 0 : aMouseInput.mLocalOrigin, thumbData);
1818 : // ConvertScrollbarPoint() got the drag start offset relative to
1819 : // the scroll track. Now get it relative to the thumb.
1820 : // ScrollThumbData::mThumbStart stores the offset of the thumb
1821 : // relative to the scroll track at the time of the last paint.
1822 : // Since that paint, the thumb may have acquired an async transform
1823 : // due to async scrolling, so look that up and apply it.
1824 0 : LayerToParentLayerMatrix4x4 thumbTransform;
1825 : {
1826 0 : RecursiveMutexAutoLock lock(mTreeLock);
1827 0 : thumbTransform = ComputeTransformForNode(aScrollThumbNode.Get(lock));
1828 : }
1829 : // Only consider the translation, since we do not support both
1830 : // zooming and scrollbar dragging on any platform.
1831 : CSSCoord thumbStart = thumbData.mThumbStart
1832 0 : + ((*thumbData.mDirection == ScrollDirection::eHorizontal)
1833 0 : ? thumbTransform._41 : thumbTransform._42);
1834 0 : dragStart -= thumbStart;
1835 :
1836 : // Content can't prevent scrollbar dragging with preventDefault(),
1837 : // so we don't need to wait for a content response. It's important
1838 : // to do this before calling ConfirmDragBlock() since that can
1839 : // potentially process and consume the block.
1840 0 : dragBlock->SetContentResponse(false);
1841 :
1842 0 : mInputQueue->ConfirmDragBlock(
1843 : dragBlockId, aApzc,
1844 0 : AsyncDragMetrics(aApzc->GetGuid().mScrollId,
1845 0 : aApzc->GetGuid().mPresShellId,
1846 : dragBlockId,
1847 : dragStart,
1848 0 : *thumbData.mDirection));
1849 : }
1850 : }
1851 :
1852 : void
1853 0 : APZCTreeManager::SynthesizePinchGestureFromMouseWheel(
1854 : const ScrollWheelInput& aWheelInput,
1855 : const RefPtr<AsyncPanZoomController>& aTarget)
1856 : {
1857 0 : MOZ_ASSERT(aTarget);
1858 :
1859 0 : ScreenPoint focusPoint = aWheelInput.mOrigin;
1860 :
1861 : // Compute span values based on the wheel delta.
1862 : // See the PinchGestureInput constructor called below for why
1863 : // it's OK to use ParentLayer coordinates for the span values.
1864 0 : ParentLayerCoord oldSpan = 100;
1865 0 : ParentLayerCoord newSpan = oldSpan + aWheelInput.mDeltaY;
1866 :
1867 : // There's no ambiguity as to the target for pinch gesture events.
1868 0 : TargetConfirmationFlags confFlags{true};
1869 :
1870 : PinchGestureInput pinchStart{
1871 : PinchGestureInput::PINCHGESTURE_START,
1872 0 : aWheelInput.mTime,
1873 : aWheelInput.mTimeStamp,
1874 : focusPoint,
1875 : oldSpan,
1876 : oldSpan,
1877 0 : aWheelInput.modifiers};
1878 : PinchGestureInput pinchScale1{
1879 : PinchGestureInput::PINCHGESTURE_SCALE,
1880 0 : aWheelInput.mTime,
1881 : aWheelInput.mTimeStamp,
1882 : focusPoint,
1883 : oldSpan,
1884 : oldSpan,
1885 0 : aWheelInput.modifiers};
1886 : PinchGestureInput pinchScale2{
1887 : PinchGestureInput::PINCHGESTURE_SCALE,
1888 0 : aWheelInput.mTime,
1889 : aWheelInput.mTimeStamp,
1890 : focusPoint,
1891 : oldSpan,
1892 : newSpan,
1893 0 : aWheelInput.modifiers};
1894 : PinchGestureInput pinchEnd{
1895 : PinchGestureInput::PINCHGESTURE_END,
1896 0 : aWheelInput.mTime,
1897 : aWheelInput.mTimeStamp,
1898 0 : PinchGestureInput::BothFingersLifted<ScreenPixel>(),
1899 : newSpan,
1900 : newSpan,
1901 0 : aWheelInput.modifiers};
1902 :
1903 0 : mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchStart, nullptr);
1904 0 : mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchScale1, nullptr);
1905 0 : mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchScale2, nullptr);
1906 0 : mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchEnd, nullptr);
1907 0 : }
1908 :
1909 : void
1910 0 : APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
1911 : EventMessage aEventMessage)
1912 : {
1913 0 : APZThreadUtils::AssertOnControllerThread();
1914 :
1915 0 : WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction();
1916 0 : if (!txn) {
1917 : return;
1918 : }
1919 :
1920 : // If the transaction has simply timed out, we don't need to do anything
1921 : // else.
1922 0 : if (txn->MaybeTimeout(TimeStamp::Now())) {
1923 : return;
1924 : }
1925 :
1926 0 : switch (aEventMessage) {
1927 : case eMouseMove:
1928 : case eDragOver: {
1929 :
1930 : ScreenIntPoint point =
1931 : ViewAs<ScreenPixel>(aRefPoint,
1932 0 : PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
1933 :
1934 0 : txn->OnMouseMove(point);
1935 :
1936 : return;
1937 : }
1938 : case eKeyPress:
1939 : case eKeyUp:
1940 : case eKeyDown:
1941 : case eMouseUp:
1942 : case eMouseDown:
1943 : case eMouseDoubleClick:
1944 : case eMouseAuxClick:
1945 : case eMouseClick:
1946 : case eContextMenu:
1947 : case eDrop:
1948 0 : txn->EndTransaction();
1949 0 : return;
1950 : default:
1951 : break;
1952 : }
1953 : }
1954 :
1955 : void
1956 0 : APZCTreeManager::ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
1957 : ScrollableLayerGuid* aOutTargetGuid,
1958 : uint64_t* aOutFocusSequenceNumber)
1959 : {
1960 0 : APZThreadUtils::AssertOnControllerThread();
1961 :
1962 : // Transform the aRefPoint.
1963 : // If the event hits an overscrolled APZC, instruct the caller to ignore it.
1964 0 : CompositorHitTestInfo hitResult = CompositorHitTestInfo::eInvisibleToHitTest;
1965 0 : PixelCastJustification LDIsScreen = PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
1966 : ScreenIntPoint refPointAsScreen =
1967 0 : ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
1968 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(refPointAsScreen, &hitResult);
1969 0 : if (apzc) {
1970 0 : MOZ_ASSERT(hitResult != CompositorHitTestInfo::eInvisibleToHitTest);
1971 0 : apzc->GetGuid(aOutTargetGuid);
1972 0 : ScreenToParentLayerMatrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
1973 0 : ParentLayerToScreenMatrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
1974 0 : ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
1975 : Maybe<ScreenIntPoint> untransformedRefPoint =
1976 0 : UntransformBy(outTransform, refPointAsScreen);
1977 0 : if (untransformedRefPoint) {
1978 : *aRefPoint =
1979 0 : ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
1980 : }
1981 : }
1982 :
1983 : // Update the focus sequence number and attach it to the event
1984 0 : mFocusState.ReceiveFocusChangingEvent();
1985 0 : *aOutFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
1986 0 : }
1987 :
1988 : void
1989 0 : APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
1990 : {
1991 0 : if (mApzcForInputBlock) {
1992 0 : mApzcForInputBlock->HandleTouchVelocity(aTimestampMs, aSpeedY);
1993 : }
1994 0 : }
1995 :
1996 : void
1997 0 : APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap)
1998 : {
1999 0 : APZThreadUtils::AssertOnControllerThread();
2000 :
2001 0 : mKeyboardMap = aKeyboardMap;
2002 0 : }
2003 :
2004 : void
2005 0 : APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
2006 : const CSSRect& aRect,
2007 : const uint32_t aFlags)
2008 : {
2009 : // We could probably move this to run on the updater thread if needed, but
2010 : // either way we should restrict it to a single thread. For now let's use the
2011 : // controller thread.
2012 0 : APZThreadUtils::AssertOnControllerThread();
2013 :
2014 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
2015 0 : if (apzc) {
2016 0 : apzc->ZoomToRect(aRect, aFlags);
2017 : }
2018 0 : }
2019 :
2020 : void
2021 0 : APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault)
2022 : {
2023 0 : APZThreadUtils::AssertOnControllerThread();
2024 :
2025 0 : mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
2026 0 : }
2027 :
2028 : void
2029 0 : APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
2030 : const nsTArray<ScrollableLayerGuid>& aTargets)
2031 : {
2032 0 : APZThreadUtils::AssertOnControllerThread();
2033 :
2034 0 : RefPtr<AsyncPanZoomController> target = nullptr;
2035 0 : if (aTargets.Length() > 0) {
2036 0 : target = GetTargetAPZC(aTargets[0]);
2037 : }
2038 0 : for (size_t i = 1; i < aTargets.Length(); i++) {
2039 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
2040 0 : target = GetMultitouchTarget(target, apzc);
2041 : }
2042 0 : mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
2043 0 : }
2044 :
2045 : void
2046 0 : APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget)
2047 : {
2048 0 : APZThreadUtils::AssertOnControllerThread();
2049 :
2050 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
2051 0 : mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
2052 0 : }
2053 :
2054 : void
2055 0 : APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
2056 : const Maybe<ZoomConstraints>& aConstraints)
2057 : {
2058 0 : if (!GetUpdater()->IsUpdaterThread()) {
2059 : // This can happen if we're in the UI process and got a call directly from
2060 : // nsBaseWidget or from a content process over PAPZCTreeManager. In that case
2061 : // we get this call on the compositor thread, which may be different from
2062 : // the updater thread. It can also happen in the GPU process if that is
2063 : // enabled, since the call will go over PAPZCTreeManager and arrive on the
2064 : // compositor thread in the GPU process.
2065 0 : GetUpdater()->RunOnUpdaterThread(
2066 : aGuid.mLayersId,
2067 0 : NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
2068 : "APZCTreeManager::UpdateZoomConstraints",
2069 : this,
2070 : &APZCTreeManager::UpdateZoomConstraints,
2071 : aGuid,
2072 0 : aConstraints));
2073 0 : return;
2074 : }
2075 :
2076 0 : AssertOnUpdaterThread();
2077 :
2078 0 : RecursiveMutexAutoLock lock(mTreeLock);
2079 0 : RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
2080 0 : MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
2081 :
2082 : // Propagate the zoom constraints down to the subtree, stopping at APZCs
2083 : // which have their own zoom constraints or are in a different layers id.
2084 0 : if (aConstraints) {
2085 : APZCTM_LOG("Recording constraints %s for guid %s\n",
2086 : Stringify(aConstraints.value()).c_str(), Stringify(aGuid).c_str());
2087 0 : mZoomConstraints[aGuid] = aConstraints.ref();
2088 : } else {
2089 : APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
2090 0 : mZoomConstraints.erase(aGuid);
2091 : }
2092 0 : if (node && aConstraints) {
2093 0 : ForEachNode<ReverseIterator>(node.get(),
2094 0 : [&aConstraints, &node, this](HitTestingTreeNode* aNode)
2095 0 : {
2096 0 : if (aNode != node) {
2097 0 : if (AsyncPanZoomController* childApzc = aNode->GetApzc()) {
2098 : // We can have subtrees with their own zoom constraints or separate layers
2099 : // id - leave these alone.
2100 0 : if (childApzc->HasNoParentWithSameLayersId() ||
2101 0 : this->mZoomConstraints.find(childApzc->GetGuid()) != this->mZoomConstraints.end()) {
2102 : return TraversalFlag::Skip;
2103 : }
2104 : }
2105 : }
2106 0 : if (aNode->IsPrimaryHolder()) {
2107 0 : MOZ_ASSERT(aNode->GetApzc());
2108 0 : aNode->GetApzc()->UpdateZoomConstraints(aConstraints.ref());
2109 : }
2110 : return TraversalFlag::Continue;
2111 0 : });
2112 : }
2113 : }
2114 :
2115 : void
2116 0 : APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform()
2117 : {
2118 : // As the name implies, we flush repaint requests for the entire APZ tree in
2119 : // order to clear the screen-to-gecko transform (aka the "untransform" applied
2120 : // to incoming input events before they can be passed on to Gecko).
2121 : //
2122 : // The primary reason we do this is to avoid the problem where input events,
2123 : // after being untransformed, end up hit-testing differently in Gecko. This
2124 : // might happen in cases where the input event lands on content that is async-
2125 : // scrolled into view, but Gecko still thinks it is out of view given the
2126 : // visible area of a scrollframe.
2127 : //
2128 : // Another reason we want to clear the untransform is that if our APZ hit-test
2129 : // hits a dispatch-to-content region then that's an ambiguous result and we
2130 : // need to ask Gecko what actually got hit. In order to do this we need to
2131 : // untransform the input event into Gecko space - but to do that we need to
2132 : // know which APZC got hit! This leads to a circular dependency; the only way
2133 : // to get out of it is to make sure that the untransform for all the possible
2134 : // matched APZCs is the same. It is simplest to ensure that by flushing the
2135 : // pending repaint requests, which makes all of the untransforms empty (and
2136 : // therefore equal).
2137 0 : RecursiveMutexAutoLock lock(mTreeLock);
2138 :
2139 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
2140 0 : [](HitTestingTreeNode* aNode)
2141 : {
2142 0 : if (aNode->IsPrimaryHolder()) {
2143 0 : MOZ_ASSERT(aNode->GetApzc());
2144 0 : aNode->GetApzc()->FlushRepaintForNewInputBlock();
2145 : }
2146 0 : });
2147 0 : }
2148 :
2149 : void
2150 0 : APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
2151 : {
2152 0 : RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
2153 0 : if (apzc) {
2154 0 : apzc->CancelAnimation();
2155 : }
2156 0 : }
2157 :
2158 : void
2159 0 : APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
2160 : {
2161 0 : RecursiveMutexAutoLock lock(mTreeLock);
2162 0 : RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
2163 0 : if (apzc) {
2164 0 : apzc->AdjustScrollForSurfaceShift(aShift);
2165 : }
2166 0 : }
2167 :
2168 : void
2169 0 : APZCTreeManager::ClearTree()
2170 : {
2171 0 : AssertOnUpdaterThread();
2172 :
2173 : #if defined(MOZ_WIDGET_ANDROID)
2174 : mToolbarAnimator->ClearTreeManager();
2175 : #endif
2176 :
2177 : // Ensure that no references to APZCs are alive in any lingering input
2178 : // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
2179 : // the InputQueue.
2180 0 : APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
2181 0 : "layers::InputQueue::Clear", mInputQueue, &InputQueue::Clear));
2182 :
2183 0 : RecursiveMutexAutoLock lock(mTreeLock);
2184 :
2185 : // Collect the nodes into a list, and then destroy each one.
2186 : // We can't destroy them as we collect them, because ForEachNode()
2187 : // does a pre-order traversal of the tree, and Destroy() nulls out
2188 : // the fields needed to reach the children of the node.
2189 0 : nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy;
2190 0 : ForEachNode<ReverseIterator>(mRootNode.get(),
2191 0 : [&nodesToDestroy](HitTestingTreeNode* aNode)
2192 0 : {
2193 0 : nodesToDestroy.AppendElement(aNode);
2194 0 : });
2195 :
2196 0 : for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
2197 0 : nodesToDestroy[i]->Destroy();
2198 : }
2199 0 : mRootNode = nullptr;
2200 :
2201 0 : RefPtr<APZCTreeManager> self(this);
2202 0 : NS_DispatchToMainThread(
2203 0 : NS_NewRunnableFunction("layers::APZCTreeManager::ClearTree", [self] {
2204 0 : self->mFlushObserver->Unregister();
2205 0 : self->mFlushObserver = nullptr;
2206 0 : }));
2207 0 : }
2208 :
2209 : RefPtr<HitTestingTreeNode>
2210 0 : APZCTreeManager::GetRootNode() const
2211 : {
2212 0 : RecursiveMutexAutoLock lock(mTreeLock);
2213 0 : return mRootNode;
2214 : }
2215 :
2216 : /**
2217 : * Transform a displacement from the ParentLayer coordinates of a source APZC
2218 : * to the ParentLayer coordinates of a target APZC.
2219 : * @param aTreeManager the tree manager for the APZC tree containing |aSource|
2220 : * and |aTarget|
2221 : * @param aSource the source APZC
2222 : * @param aTarget the target APZC
2223 : * @param aStartPoint the start point of the displacement
2224 : * @param aEndPoint the end point of the displacement
2225 : * @return true on success, false if aStartPoint or aEndPoint cannot be transformed into target's coordinate space
2226 : */
2227 : static bool
2228 0 : TransformDisplacement(APZCTreeManager* aTreeManager,
2229 : AsyncPanZoomController* aSource,
2230 : AsyncPanZoomController* aTarget,
2231 : ParentLayerPoint& aStartPoint,
2232 : ParentLayerPoint& aEndPoint) {
2233 0 : if (aSource == aTarget) {
2234 : return true;
2235 : }
2236 :
2237 : // Convert start and end points to Screen coordinates.
2238 0 : ParentLayerToScreenMatrix4x4 untransformToApzc = aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
2239 0 : ScreenPoint screenStart = TransformBy(untransformToApzc, aStartPoint);
2240 0 : ScreenPoint screenEnd = TransformBy(untransformToApzc, aEndPoint);
2241 :
2242 : // Convert start and end points to aTarget's ParentLayer coordinates.
2243 0 : ScreenToParentLayerMatrix4x4 transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
2244 0 : Maybe<ParentLayerPoint> startPoint = UntransformBy(transformToApzc, screenStart);
2245 0 : Maybe<ParentLayerPoint> endPoint = UntransformBy(transformToApzc, screenEnd);
2246 0 : if (!startPoint || !endPoint) {
2247 : return false;
2248 : }
2249 0 : aEndPoint = *endPoint;
2250 0 : aStartPoint = *startPoint;
2251 :
2252 0 : return true;
2253 : }
2254 :
2255 : void
2256 0 : APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
2257 : ParentLayerPoint& aStartPoint,
2258 : ParentLayerPoint& aEndPoint,
2259 : OverscrollHandoffState& aOverscrollHandoffState)
2260 : {
2261 0 : const OverscrollHandoffChain& overscrollHandoffChain = aOverscrollHandoffState.mChain;
2262 0 : uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex;
2263 0 : RefPtr<AsyncPanZoomController> next;
2264 : // If we have reached the end of the overscroll handoff chain, there is
2265 : // nothing more to scroll, so we ignore the rest of the pan gesture.
2266 0 : if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) {
2267 : // Nothing more to scroll - ignore the rest of the pan gesture.
2268 0 : return;
2269 : }
2270 :
2271 0 : next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex);
2272 :
2273 0 : if (next == nullptr || next->IsDestroyed()) {
2274 : return;
2275 : }
2276 :
2277 : // Convert the start and end points from |aPrev|'s coordinate space to
2278 : // |next|'s coordinate space.
2279 0 : if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) {
2280 : return;
2281 : }
2282 :
2283 : // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
2284 : // again with an incremented index.
2285 0 : if (!next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState)) {
2286 : // Transform |aStartPoint| and |aEndPoint| (which now represent the
2287 : // portion of the displacement that wasn't consumed by APZCs later
2288 : // in the handoff chain) back into |aPrev|'s coordinate space. This
2289 : // allows the caller (which is |aPrev|) to interpret the unconsumed
2290 : // displacement in its own coordinate space, and make use of it
2291 : // (e.g. by going into overscroll).
2292 0 : if (!TransformDisplacement(this, next, aPrev, aStartPoint, aEndPoint)) {
2293 0 : NS_WARNING("Failed to untransform scroll points during dispatch");
2294 : }
2295 : }
2296 : }
2297 :
2298 : ParentLayerPoint
2299 0 : APZCTreeManager::DispatchFling(AsyncPanZoomController* aPrev,
2300 : const FlingHandoffState& aHandoffState)
2301 : {
2302 : // If immediate handoff is disallowed, do not allow handoff beyond the
2303 : // single APZC that's scrolled by the input block that triggered this fling.
2304 0 : if (aHandoffState.mIsHandoff &&
2305 0 : !gfxPrefs::APZAllowImmediateHandoff() &&
2306 0 : aHandoffState.mScrolledApzc == aPrev) {
2307 0 : return aHandoffState.mVelocity;
2308 : }
2309 :
2310 0 : const OverscrollHandoffChain* chain = aHandoffState.mChain;
2311 0 : RefPtr<AsyncPanZoomController> current;
2312 0 : uint32_t overscrollHandoffChainLength = chain->Length();
2313 : uint32_t startIndex;
2314 :
2315 : // The fling's velocity needs to be transformed from the screen coordinates
2316 : // of |aPrev| to the screen coordinates of |next|. To transform a velocity
2317 : // correctly, we need to convert it to a displacement. For now, we do this
2318 : // by anchoring it to a start point of (0, 0).
2319 : // TODO: For this to be correct in the presence of 3D transforms, we should
2320 : // use the end point of the touch that started the fling as the start point
2321 : // rather than (0, 0).
2322 0 : ParentLayerPoint startPoint; // (0, 0)
2323 0 : ParentLayerPoint endPoint;
2324 :
2325 0 : if (aHandoffState.mIsHandoff) {
2326 0 : startIndex = chain->IndexOf(aPrev) + 1;
2327 :
2328 : // IndexOf will return aOverscrollHandoffChain->Length() if
2329 : // |aPrev| is not found.
2330 0 : if (startIndex >= overscrollHandoffChainLength) {
2331 0 : return aHandoffState.mVelocity;
2332 : }
2333 : } else {
2334 : startIndex = 0;
2335 : }
2336 :
2337 : // This will store any velocity left over after the entire handoff.
2338 0 : ParentLayerPoint finalResidualVelocity = aHandoffState.mVelocity;
2339 :
2340 0 : ParentLayerPoint currentVelocity = aHandoffState.mVelocity;
2341 0 : for (; startIndex < overscrollHandoffChainLength; startIndex++) {
2342 0 : current = chain->GetApzcAtIndex(startIndex);
2343 :
2344 : // Make sure the apzc about to be handled can be handled
2345 0 : if (current == nullptr || current->IsDestroyed()) {
2346 : break;
2347 : }
2348 :
2349 0 : endPoint = startPoint + currentVelocity;
2350 :
2351 : RefPtr<AsyncPanZoomController> prevApzc = (startIndex > 0)
2352 : ? chain->GetApzcAtIndex(startIndex - 1)
2353 0 : : nullptr;
2354 :
2355 : // Only transform when current apzc can be transformed with previous
2356 0 : if (prevApzc) {
2357 0 : if (!TransformDisplacement(this,
2358 : prevApzc,
2359 : current,
2360 : startPoint,
2361 : endPoint)) {
2362 : break;
2363 : }
2364 : }
2365 :
2366 0 : ParentLayerPoint availableVelocity = (endPoint - startPoint);
2367 0 : ParentLayerPoint residualVelocity;
2368 :
2369 0 : FlingHandoffState transformedHandoffState = aHandoffState;
2370 0 : transformedHandoffState.mVelocity = availableVelocity;
2371 :
2372 : // Obey overscroll-behavior.
2373 0 : if (prevApzc) {
2374 0 : residualVelocity += prevApzc->AdjustHandoffVelocityForOverscrollBehavior(transformedHandoffState.mVelocity);
2375 : }
2376 :
2377 0 : residualVelocity += current->AttemptFling(transformedHandoffState);
2378 :
2379 : // If there's no residual velocity, there's nothing more to hand off.
2380 0 : if (IsZero(residualVelocity)) {
2381 0 : return ParentLayerPoint();
2382 : }
2383 :
2384 : // If any of the velocity available to be handed off was consumed,
2385 : // subtract the proportion of consumed velocity from finalResidualVelocity.
2386 : // Note: it's important to compare |residualVelocity| to |availableVelocity|
2387 : // here and not to |transformedHandoffState.mVelocity|, since the latter
2388 : // may have been modified by AdjustHandoffVelocityForOverscrollBehavior().
2389 0 : if (!FuzzyEqualsAdditive(availableVelocity.x,
2390 : residualVelocity.x, COORDINATE_EPSILON)) {
2391 0 : finalResidualVelocity.x *= (residualVelocity.x / availableVelocity.x);
2392 : }
2393 0 : if (!FuzzyEqualsAdditive(availableVelocity.y,
2394 : residualVelocity.y, COORDINATE_EPSILON)) {
2395 0 : finalResidualVelocity.y *= (residualVelocity.y / availableVelocity.y);
2396 : }
2397 :
2398 0 : currentVelocity = residualVelocity;
2399 : }
2400 :
2401 : // Return any residual velocity left over after the entire handoff process.
2402 0 : return finalResidualVelocity;
2403 : }
2404 :
2405 : bool
2406 0 : APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
2407 : {
2408 0 : RefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
2409 0 : return target != nullptr;
2410 : }
2411 :
2412 : already_AddRefed<AsyncPanZoomController>
2413 0 : APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
2414 : {
2415 0 : RecursiveMutexAutoLock lock(mTreeLock);
2416 0 : RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
2417 0 : MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
2418 0 : RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
2419 0 : return apzc.forget();
2420 : }
2421 :
2422 : static bool
2423 0 : GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne, const ScrollableLayerGuid& aTwo)
2424 : {
2425 0 : return aOne.mLayersId == aTwo.mLayersId
2426 0 : && aOne.mScrollId == aTwo.mScrollId;
2427 : }
2428 :
2429 : already_AddRefed<AsyncPanZoomController>
2430 0 : APZCTreeManager::GetTargetAPZC(const LayersId& aLayersId,
2431 : const FrameMetrics::ViewID& aScrollId)
2432 : {
2433 0 : MutexAutoLock lock(mMapLock);
2434 0 : ScrollableLayerGuid guid(aLayersId, 0, aScrollId);
2435 0 : auto it = mApzcMap.find(guid);
2436 0 : RefPtr<AsyncPanZoomController> apzc = (it != mApzcMap.end() ? it->second : nullptr);
2437 0 : return apzc.forget();
2438 : }
2439 :
2440 : already_AddRefed<HitTestingTreeNode>
2441 0 : APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
2442 : GuidComparator aComparator) const
2443 : {
2444 0 : mTreeLock.AssertCurrentThreadIn();
2445 : RefPtr<HitTestingTreeNode> target = DepthFirstSearchPostOrder<ReverseIterator>(mRootNode.get(),
2446 0 : [&aGuid, &aComparator](HitTestingTreeNode* node)
2447 0 : {
2448 0 : bool matches = false;
2449 0 : if (node->GetApzc()) {
2450 0 : if (aComparator) {
2451 0 : matches = aComparator(aGuid, node->GetApzc()->GetGuid());
2452 : } else {
2453 0 : matches = node->GetApzc()->Matches(aGuid);
2454 : }
2455 : }
2456 0 : return matches;
2457 : }
2458 0 : );
2459 0 : return target.forget();
2460 : }
2461 :
2462 : already_AddRefed<AsyncPanZoomController>
2463 0 : APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint,
2464 : CompositorHitTestInfo* aOutHitResult,
2465 : HitTestingTreeNodeAutoLock* aOutScrollbarNode)
2466 : {
2467 0 : RecursiveMutexAutoLock lock(mTreeLock);
2468 :
2469 0 : CompositorHitTestInfo hitResult = CompositorHitTestInfo::eInvisibleToHitTest;
2470 0 : HitTestingTreeNode* scrollbarNode = nullptr;
2471 0 : RefPtr<AsyncPanZoomController> target;
2472 0 : if (gfx::gfxVars::UseWebRender()) {
2473 0 : target = GetAPZCAtPointWR(aPoint, &hitResult, &scrollbarNode);
2474 : } else {
2475 0 : target = GetAPZCAtPoint(mRootNode, aPoint, &hitResult, &scrollbarNode);
2476 : }
2477 :
2478 0 : if (aOutHitResult) {
2479 0 : *aOutHitResult = hitResult;
2480 : }
2481 0 : if (aOutScrollbarNode && scrollbarNode) {
2482 0 : RefPtr<HitTestingTreeNode> scrollbarRef = scrollbarNode;
2483 0 : aOutScrollbarNode->Initialize(lock, scrollbarRef.forget(), mTreeLock);
2484 : }
2485 0 : return target.forget();
2486 : }
2487 :
2488 : already_AddRefed<AsyncPanZoomController>
2489 0 : APZCTreeManager::GetAPZCAtPointWR(const ScreenPoint& aHitTestPoint,
2490 : CompositorHitTestInfo* aOutHitResult,
2491 : HitTestingTreeNode** aOutScrollbarNode)
2492 : {
2493 0 : MOZ_ASSERT(aOutHitResult);
2494 0 : MOZ_ASSERT(aOutScrollbarNode);
2495 :
2496 0 : RefPtr<AsyncPanZoomController> result;
2497 0 : RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPI();
2498 0 : if (!wr) {
2499 : // If WebRender isn't running, fall back to the root APZC.
2500 : // This is mostly for the benefit of GTests which do not
2501 : // run a WebRender instance, but gracefully falling back
2502 : // here allows those tests which are not specifically
2503 : // testing the hit-test algorithm to still work.
2504 0 : result = FindRootApzcForLayersId(mRootLayersId);
2505 0 : *aOutHitResult = CompositorHitTestInfo::eVisibleToHitTest;
2506 : return result.forget();
2507 : }
2508 :
2509 : wr::WrPipelineId pipelineId;
2510 : FrameMetrics::ViewID scrollId;
2511 : gfx::CompositorHitTestInfo hitInfo;
2512 0 : bool hitSomething = wr->HitTest(wr::ToWorldPoint(aHitTestPoint),
2513 0 : pipelineId, scrollId, hitInfo);
2514 0 : if (!hitSomething) {
2515 : return result.forget();
2516 : }
2517 :
2518 0 : LayersId layersId = wr::AsLayersId(pipelineId);
2519 0 : result = GetTargetAPZC(layersId, scrollId);
2520 0 : if (!result) {
2521 : // It falls back to the root
2522 0 : MOZ_ASSERT(scrollId == FrameMetrics::NULL_SCROLL_ID);
2523 0 : result = FindRootApzcForLayersId(layersId);
2524 0 : MOZ_ASSERT(result);
2525 : }
2526 :
2527 0 : bool isScrollbar = bool(hitInfo & gfx::CompositorHitTestInfo::eScrollbar);
2528 0 : bool isScrollbarThumb = bool(hitInfo & gfx::CompositorHitTestInfo::eScrollbarThumb);
2529 0 : ScrollDirection direction = (hitInfo & gfx::CompositorHitTestInfo::eScrollbarVertical)
2530 0 : ? ScrollDirection::eVertical
2531 0 : : ScrollDirection::eHorizontal;
2532 0 : if (isScrollbar || isScrollbarThumb) {
2533 0 : *aOutScrollbarNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2534 0 : [&](HitTestingTreeNode* aNode) {
2535 0 : return (aNode->GetLayersId() == layersId) &&
2536 0 : (aNode->IsScrollbarNode() == isScrollbar) &&
2537 0 : (aNode->IsScrollThumbNode() == isScrollbarThumb) &&
2538 0 : (aNode->GetScrollbarDirection() == direction) &&
2539 0 : (aNode->GetScrollTargetId() == scrollId);
2540 0 : });
2541 : }
2542 :
2543 0 : *aOutHitResult = hitInfo;
2544 : return result.forget();
2545 : }
2546 :
2547 : RefPtr<const OverscrollHandoffChain>
2548 0 : APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget)
2549 : {
2550 : // Scroll grabbing is a mechanism that allows content to specify that
2551 : // the initial target of a pan should be not the innermost scrollable
2552 : // frame at the touch point (which is what GetTargetAPZC finds), but
2553 : // something higher up in the tree.
2554 : // It's not sufficient to just find the initial target, however, as
2555 : // overscroll can be handed off to another APZC. Without scroll grabbing,
2556 : // handoff just occurs from child to parent. With scroll grabbing, the
2557 : // handoff order can be different, so we build a chain of APZCs in the
2558 : // order in which scroll will be handed off to them.
2559 :
2560 : // Grab tree lock since we'll be walking the APZC tree.
2561 0 : RecursiveMutexAutoLock lock(mTreeLock);
2562 :
2563 : // Build the chain. If there is a scroll parent link, we use that. This is
2564 : // needed to deal with scroll info layers, because they participate in handoff
2565 : // but do not follow the expected layer tree structure. If there are no
2566 : // scroll parent links we just walk up the tree to find the scroll parent.
2567 0 : OverscrollHandoffChain* result = new OverscrollHandoffChain;
2568 0 : AsyncPanZoomController* apzc = aInitialTarget;
2569 0 : while (apzc != nullptr) {
2570 0 : result->Add(apzc);
2571 :
2572 0 : if (apzc->GetScrollHandoffParentId() == FrameMetrics::NULL_SCROLL_ID) {
2573 0 : if (!apzc->IsRootForLayersId()) {
2574 : // This probably indicates a bug or missed case in layout code
2575 0 : NS_WARNING("Found a non-root APZ with no handoff parent");
2576 : }
2577 0 : apzc = apzc->GetParent();
2578 0 : continue;
2579 : }
2580 :
2581 : // Guard against a possible infinite-loop condition. If we hit this, the
2582 : // layout code that generates the handoff parents did something wrong.
2583 0 : MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId);
2584 : RefPtr<AsyncPanZoomController> scrollParent =
2585 0 : GetTargetAPZC(apzc->GetGuid().mLayersId, apzc->GetScrollHandoffParentId());
2586 0 : apzc = scrollParent.get();
2587 : }
2588 :
2589 : // Now adjust the chain to account for scroll grabbing. Sorting is a bit
2590 : // of an overkill here, but scroll grabbing will likely be generalized
2591 : // to scroll priorities, so we might as well do it this way.
2592 0 : result->SortByScrollPriority();
2593 :
2594 : // Print the overscroll chain for debugging.
2595 0 : for (uint32_t i = 0; i < result->Length(); ++i) {
2596 : APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, result->GetApzcAtIndex(i).get());
2597 : }
2598 :
2599 0 : return result;
2600 : }
2601 :
2602 : void
2603 0 : APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
2604 : {
2605 0 : APZThreadUtils::AssertOnControllerThread();
2606 0 : GestureEventListener::SetLongTapEnabled(aLongTapEnabled);
2607 0 : }
2608 :
2609 : void
2610 0 : APZCTreeManager::FindScrollThumbNode(const AsyncDragMetrics& aDragMetrics,
2611 : HitTestingTreeNodeAutoLock& aOutThumbNode)
2612 : {
2613 0 : if (!aDragMetrics.mDirection) {
2614 : // The AsyncDragMetrics has not been initialized yet - there will be
2615 : // no matching node, so don't bother searching the tree.
2616 0 : return;
2617 : }
2618 :
2619 0 : RecursiveMutexAutoLock lock(mTreeLock);
2620 :
2621 : RefPtr<HitTestingTreeNode> result = DepthFirstSearch<ReverseIterator>(
2622 : mRootNode.get(),
2623 0 : [&aDragMetrics](HitTestingTreeNode* aNode) {
2624 : return aNode->MatchesScrollDragMetrics(aDragMetrics);
2625 0 : });
2626 0 : if (result) {
2627 0 : aOutThumbNode.Initialize(lock, result.forget(), mTreeLock);
2628 : }
2629 : }
2630 :
2631 : AsyncPanZoomController*
2632 0 : APZCTreeManager::GetTargetApzcForNode(HitTestingTreeNode* aNode)
2633 : {
2634 0 : for (const HitTestingTreeNode* n = aNode;
2635 0 : n && n->GetLayersId() == aNode->GetLayersId();
2636 : n = n->GetParent()) {
2637 0 : if (n->GetApzc()) {
2638 : APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc());
2639 0 : return n->GetApzc();
2640 : }
2641 0 : if (n->GetFixedPosTarget() != FrameMetrics::NULL_SCROLL_ID) {
2642 : RefPtr<AsyncPanZoomController> fpTarget =
2643 0 : GetTargetAPZC(n->GetLayersId(), n->GetFixedPosTarget());
2644 : APZCTM_LOG("Found target APZC %p using fixed-pos lookup on %" PRIu64 "\n", fpTarget.get(), n->GetFixedPosTarget());
2645 0 : return fpTarget.get();
2646 : }
2647 : }
2648 : return nullptr;
2649 : }
2650 :
2651 : AsyncPanZoomController*
2652 0 : APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
2653 : const ScreenPoint& aHitTestPoint,
2654 : CompositorHitTestInfo* aOutHitResult,
2655 : HitTestingTreeNode** aOutScrollbarNode)
2656 : {
2657 0 : mTreeLock.AssertCurrentThreadIn();
2658 :
2659 : // This walks the tree in depth-first, reverse order, so that it encounters
2660 : // APZCs front-to-back on the screen.
2661 : HitTestingTreeNode* resultNode;
2662 0 : HitTestingTreeNode* root = aNode;
2663 0 : std::stack<LayerPoint> hitTestPoints;
2664 : ParentLayerPoint point = ViewAs<ParentLayerPixel>(aHitTestPoint,
2665 0 : PixelCastJustification::ScreenIsParentLayerForRoot);
2666 0 : hitTestPoints.push(ViewAs<LayerPixel>(point,
2667 0 : PixelCastJustification::MovingDownToChildren));
2668 :
2669 0 : ForEachNode<ReverseIterator>(root,
2670 0 : [&hitTestPoints, this](HitTestingTreeNode* aNode) {
2671 0 : ParentLayerPoint hitTestPointForParent = ViewAs<ParentLayerPixel>(hitTestPoints.top(),
2672 0 : PixelCastJustification::MovingDownToChildren);
2673 0 : if (aNode->IsOutsideClip(hitTestPointForParent)) {
2674 : // If the point being tested is outside the clip region for this node
2675 : // then we don't need to test against this node or any of its children.
2676 : // Just skip it and move on.
2677 : APZCTM_LOG("Point %f %f outside clip for node %p\n",
2678 : hitTestPoints.top().x, hitTestPoints.top().y, aNode);
2679 : return TraversalFlag::Skip;
2680 : }
2681 : // First check the subtree rooted at this node, because deeper nodes
2682 : // are more "in front".
2683 : Maybe<LayerPoint> hitTestPoint = aNode->Untransform(
2684 0 : hitTestPointForParent, ComputeTransformForNode(aNode));
2685 : APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n",
2686 : Stringify(hitTestPointForParent).c_str(),
2687 : hitTestPoint ? Stringify(hitTestPoint.ref()).c_str() : "nil");
2688 0 : if (!hitTestPoint) {
2689 : return TraversalFlag::Skip;
2690 : }
2691 0 : hitTestPoints.push(hitTestPoint.ref());
2692 0 : return TraversalFlag::Continue;
2693 : },
2694 0 : [&resultNode, &hitTestPoints, &aOutHitResult](HitTestingTreeNode* aNode) {
2695 0 : CompositorHitTestInfo hitResult = aNode->HitTest(hitTestPoints.top());
2696 0 : hitTestPoints.pop();
2697 : APZCTM_LOG("Testing Layer point %s against node %p\n",
2698 : Stringify(hitTestPoints.top()).c_str(), aNode);
2699 0 : if (hitResult != CompositorHitTestInfo::eInvisibleToHitTest) {
2700 0 : resultNode = aNode;
2701 0 : *aOutHitResult = hitResult;
2702 0 : return TraversalFlag::Abort;
2703 : }
2704 : return TraversalFlag::Continue;
2705 : }
2706 0 : );
2707 :
2708 0 : if (*aOutHitResult != CompositorHitTestInfo::eInvisibleToHitTest) {
2709 0 : MOZ_ASSERT(resultNode);
2710 0 : for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
2711 0 : if (n->IsScrollbarNode()) {
2712 0 : *aOutScrollbarNode = n;
2713 0 : *aOutHitResult |= CompositorHitTestInfo::eScrollbar;
2714 0 : if (n->IsScrollThumbNode()) {
2715 0 : *aOutHitResult |= CompositorHitTestInfo::eScrollbarThumb;
2716 : }
2717 0 : if (n->GetScrollbarDirection() == ScrollDirection::eVertical) {
2718 0 : *aOutHitResult |= CompositorHitTestInfo::eScrollbarVertical;
2719 : }
2720 :
2721 : // If we hit a scrollbar, target the APZC for the content scrolled
2722 : // by the scrollbar. (The scrollbar itself doesn't scroll with the
2723 : // scrolled content, so it doesn't carry the scrolled content's
2724 : // scroll metadata).
2725 : RefPtr<AsyncPanZoomController> scrollTarget =
2726 0 : GetTargetAPZC(n->GetLayersId(), n->GetScrollTargetId());
2727 0 : if (scrollTarget) {
2728 0 : return scrollTarget.get();
2729 : }
2730 : }
2731 : }
2732 :
2733 0 : AsyncPanZoomController* result = GetTargetApzcForNode(resultNode);
2734 0 : if (!result) {
2735 0 : result = FindRootApzcForLayersId(resultNode->GetLayersId());
2736 0 : MOZ_ASSERT(result);
2737 : APZCTM_LOG("Found target %p using root lookup\n", result);
2738 : }
2739 : APZCTM_LOG("Successfully matched APZC %p via node %p (hit result 0x%x)\n",
2740 : result, resultNode, (int)*aOutHitResult);
2741 : return result;
2742 : }
2743 :
2744 : return nullptr;
2745 : }
2746 :
2747 : AsyncPanZoomController*
2748 0 : APZCTreeManager::FindRootApzcForLayersId(LayersId aLayersId) const
2749 : {
2750 0 : mTreeLock.AssertCurrentThreadIn();
2751 :
2752 0 : HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2753 0 : [aLayersId](HitTestingTreeNode* aNode) {
2754 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2755 : return apzc
2756 0 : && apzc->GetLayersId() == aLayersId
2757 0 : && apzc->IsRootForLayersId();
2758 0 : });
2759 0 : return resultNode ? resultNode->GetApzc() : nullptr;
2760 : }
2761 :
2762 : AsyncPanZoomController*
2763 0 : APZCTreeManager::FindRootContentApzcForLayersId(LayersId aLayersId) const
2764 : {
2765 0 : mTreeLock.AssertCurrentThreadIn();
2766 :
2767 0 : HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2768 0 : [aLayersId](HitTestingTreeNode* aNode) {
2769 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2770 : return apzc
2771 0 : && apzc->GetLayersId() == aLayersId
2772 0 : && apzc->IsRootContent();
2773 0 : });
2774 0 : return resultNode ? resultNode->GetApzc() : nullptr;
2775 : }
2776 :
2777 : AsyncPanZoomController*
2778 0 : APZCTreeManager::FindRootContentOrRootApzc() const
2779 : {
2780 0 : mTreeLock.AssertCurrentThreadIn();
2781 :
2782 : // Note: this is intended to find the same "root" that would be found
2783 : // by AsyncCompositionManager::ApplyAsyncContentTransformToTree inside
2784 : // the MOZ_WIDGET_ANDROID block. That is, it should find the RCD node if there
2785 : // is one, or the root APZC if there is not.
2786 : // Since BreadthFirstSearch is a pre-order search, we first do a search for
2787 : // the RCD, and then if we don't find one, we do a search for the root APZC.
2788 0 : HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2789 0 : [](HitTestingTreeNode* aNode) {
2790 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2791 0 : return apzc && apzc->IsRootContent();
2792 0 : });
2793 0 : if (resultNode) {
2794 0 : return resultNode->GetApzc();
2795 : }
2796 0 : resultNode = BreadthFirstSearch<ReverseIterator>(mRootNode.get(),
2797 0 : [](HitTestingTreeNode* aNode) {
2798 0 : AsyncPanZoomController* apzc = aNode->GetApzc();
2799 0 : return (apzc != nullptr);
2800 0 : });
2801 0 : return resultNode ? resultNode->GetApzc() : nullptr;
2802 : }
2803 :
2804 : /* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return
2805 : some useful transformations that input events may need applied. This is best
2806 : illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
2807 : is the layer that corresponds to the argument |aApzc|, and layer R is the root
2808 : of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
2809 : When layer L is displayed to the screen by the compositor, the set of transforms that
2810 : are applied to L are (in order from top to bottom):
2811 :
2812 : L's CSS transform (hereafter referred to as transform matrix LC)
2813 : L's nontransient async transform (hereafter referred to as transform matrix LN)
2814 : L's transient async transform (hereafter referred to as transform matrix LT)
2815 : M's CSS transform (hereafter referred to as transform matrix MC)
2816 : M's nontransient async transform (hereafter referred to as transform matrix MN)
2817 : M's transient async transform (hereafter referred to as transform matrix MT)
2818 : ...
2819 : R's CSS transform (hereafter referred to as transform matrix RC)
2820 : R's nontransient async transform (hereafter referred to as transform matrix RN)
2821 : R's transient async transform (hereafter referred to as transform matrix RT)
2822 :
2823 : Also, for any layer, the async transform is the combination of its transient and non-transient
2824 : parts. That is, for any layer L:
2825 : LA === LN * LT
2826 : LA.Inverse() === LT.Inverse() * LN.Inverse()
2827 :
2828 : If we want user input to modify L's transient async transform, we have to first convert
2829 : user input from screen space to the coordinate space of L's transient async transform. Doing
2830 : this involves applying the following transforms (in order from top to bottom):
2831 : RT.Inverse()
2832 : RN.Inverse()
2833 : RC.Inverse()
2834 : ...
2835 : MT.Inverse()
2836 : MN.Inverse()
2837 : MC.Inverse()
2838 : This combined transformation is returned by GetScreenToApzcTransform().
2839 :
2840 : Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
2841 : out all of the async transforms that are involved in this chain. This is because async
2842 : transforms are stored only in the compositor and gecko does not account for them when
2843 : doing display-list-based hit-testing for event dispatching.
2844 : Furthermore, because these input events are processed by Gecko in a FIFO queue that
2845 : includes other things (specifically paint requests), it is possible that by time the
2846 : input event reaches gecko, it will have painted something else. Therefore, we need to
2847 : apply another transform to the input events to account for the possible disparity between
2848 : what we know gecko last painted and the last paint request we sent to gecko. Let this
2849 : transform be represented by LD, MD, ... RD.
2850 : Therefore, given a user input in screen space, the following transforms need to be applied
2851 : (in order from top to bottom):
2852 : RT.Inverse()
2853 : RN.Inverse()
2854 : RC.Inverse()
2855 : ...
2856 : MT.Inverse()
2857 : MN.Inverse()
2858 : MC.Inverse()
2859 : LT.Inverse()
2860 : LN.Inverse()
2861 : LC.Inverse()
2862 : LC
2863 : LD
2864 : MC
2865 : MD
2866 : ...
2867 : RC
2868 : RD
2869 : This sequence can be simplified and refactored to the following:
2870 : GetScreenToApzcTransform()
2871 : LA.Inverse()
2872 : LD
2873 : MC
2874 : MD
2875 : ...
2876 : RC
2877 : RD
2878 : Since GetScreenToApzcTransform() can be obtained by calling that function, GetApzcToGeckoTransform()
2879 : returns the remaining transforms (LA.Inverse() * LD * ... * RD), so that the caller code can
2880 : combine it with GetScreenToApzcTransform() to get the final transform required in this case.
2881 :
2882 : Note that for many of these layers, there will be no AsyncPanZoomController attached, and
2883 : so the async transform will be the identity transform. So, in the example above, if layers
2884 : L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
2885 : RN and RD will be identity transforms.
2886 : Additionally, for space-saving purposes, each APZC instance stores its layer's individual
2887 : CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
2888 : layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
2889 : The APZC instances track the last dispatched paint request and so are able to calculate LD and
2890 : PD using those internally stored values.
2891 : The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
2892 : required can be generated.
2893 : */
2894 :
2895 : /*
2896 : * See the long comment above for a detailed explanation of this function.
2897 : */
2898 : ScreenToParentLayerMatrix4x4
2899 0 : APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const
2900 : {
2901 0 : Matrix4x4 result;
2902 0 : RecursiveMutexAutoLock lock(mTreeLock);
2903 :
2904 : // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
2905 : // explained in the comment above. This function is called with aApzc at L, and the loop
2906 : // below performs one iteration, where parent is at P. The comments explain what values are stored
2907 : // in the variables at these two levels. All the comments use standard matrix notation where the
2908 : // leftmost matrix in a multiplication is applied first.
2909 :
2910 : // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
2911 0 : Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
2912 :
2913 : // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
2914 0 : result = ancestorUntransform;
2915 :
2916 0 : for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
2917 : // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
2918 0 : ancestorUntransform = parent->GetAncestorTransform().Inverse();
2919 : // asyncUntransform is updated to PA.Inverse() when parent == P
2920 0 : Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting).Inverse().ToUnknownMatrix();
2921 : // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
2922 0 : Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
2923 :
2924 : // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
2925 0 : result = untransformSinceLastApzc * result;
2926 :
2927 : // The above value for result when parent == P matches the required output
2928 : // as explained in the comment above this method. Note that any missing
2929 : // terms are guaranteed to be identity transforms.
2930 : }
2931 :
2932 0 : return ViewAs<ScreenToParentLayerMatrix4x4>(result);
2933 : }
2934 :
2935 : /*
2936 : * See the long comment above GetScreenToApzcTransform() for a detailed
2937 : * explanation of this function.
2938 : */
2939 : ParentLayerToScreenMatrix4x4
2940 0 : APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const
2941 : {
2942 0 : Matrix4x4 result;
2943 0 : RecursiveMutexAutoLock lock(mTreeLock);
2944 :
2945 : // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
2946 : // explained in the comment above. This function is called with aApzc at L, and the loop
2947 : // below performs one iteration, where parent is at P. The comments explain what values are stored
2948 : // in the variables at these two levels. All the comments use standard matrix notation where the
2949 : // leftmost matrix in a multiplication is applied first.
2950 :
2951 : // asyncUntransform is LA.Inverse()
2952 0 : Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting).Inverse().ToUnknownMatrix();
2953 :
2954 : // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
2955 0 : result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
2956 :
2957 0 : for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
2958 : // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
2959 0 : result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
2960 :
2961 : // The above value for result when parent == P matches the required output
2962 : // as explained in the comment above this method. Note that any missing
2963 : // terms are guaranteed to be identity transforms.
2964 : }
2965 :
2966 0 : return ViewAs<ParentLayerToScreenMatrix4x4>(result);
2967 : }
2968 :
2969 : ScreenPoint
2970 0 : APZCTreeManager::GetCurrentMousePosition() const
2971 : {
2972 0 : return mCurrentMousePosition;
2973 : }
2974 :
2975 : already_AddRefed<AsyncPanZoomController>
2976 0 : APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
2977 : {
2978 0 : RecursiveMutexAutoLock lock(mTreeLock);
2979 0 : RefPtr<AsyncPanZoomController> apzc;
2980 : // For now, we only ever want to do pinching on the root-content APZC for
2981 : // a given layers id.
2982 0 : if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
2983 : // If the two APZCs have the same layers id, find the root-content APZC
2984 : // for that layers id. Don't call CommonAncestor() because there may not
2985 : // be a common ancestor for the layers id (e.g. if one APZCs is inside a
2986 : // fixed-position element).
2987 0 : apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId());
2988 : } else {
2989 : // Otherwise, find the common ancestor (to reach a common layers id), and
2990 : // get the root-content APZC for that layers id.
2991 0 : apzc = CommonAncestor(aApzc1, aApzc2);
2992 0 : if (apzc) {
2993 0 : apzc = FindRootContentApzcForLayersId(apzc->GetLayersId());
2994 : }
2995 : }
2996 0 : return apzc.forget();
2997 : }
2998 :
2999 : already_AddRefed<AsyncPanZoomController>
3000 0 : APZCTreeManager::CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
3001 : {
3002 0 : mTreeLock.AssertCurrentThreadIn();
3003 0 : RefPtr<AsyncPanZoomController> ancestor;
3004 :
3005 : // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this function
3006 : // will return null.
3007 :
3008 : // Calculate depth of the APZCs in the tree
3009 0 : int depth1 = 0, depth2 = 0;
3010 0 : for (AsyncPanZoomController* parent = aApzc1; parent; parent = parent->GetParent()) {
3011 0 : depth1++;
3012 : }
3013 0 : for (AsyncPanZoomController* parent = aApzc2; parent; parent = parent->GetParent()) {
3014 0 : depth2++;
3015 : }
3016 :
3017 : // At most one of the following two loops will be executed; the deeper APZC pointer
3018 : // will get walked up to the depth of the shallower one.
3019 0 : int minDepth = depth1 < depth2 ? depth1 : depth2;
3020 0 : while (depth1 > minDepth) {
3021 0 : depth1--;
3022 0 : aApzc1 = aApzc1->GetParent();
3023 : }
3024 0 : while (depth2 > minDepth) {
3025 0 : depth2--;
3026 0 : aApzc2 = aApzc2->GetParent();
3027 : }
3028 :
3029 : // Walk up the ancestor chains of both APZCs, always staying at the same depth for
3030 : // either APZC, and return the the first common ancestor encountered.
3031 : while (true) {
3032 0 : if (aApzc1 == aApzc2) {
3033 : ancestor = aApzc1;
3034 : break;
3035 : }
3036 0 : if (depth1 <= 0) {
3037 : break;
3038 : }
3039 0 : aApzc1 = aApzc1->GetParent();
3040 0 : aApzc2 = aApzc2->GetParent();
3041 : }
3042 0 : return ancestor.forget();
3043 : }
3044 :
3045 : LayerToParentLayerMatrix4x4
3046 0 : APZCTreeManager::ComputeTransformForNode(const HitTestingTreeNode* aNode) const
3047 : {
3048 0 : mTreeLock.AssertCurrentThreadIn();
3049 0 : if (AsyncPanZoomController* apzc = aNode->GetApzc()) {
3050 : // If the node represents scrollable content, apply the async transform
3051 : // from its APZC.
3052 0 : return aNode->GetTransform() *
3053 0 : CompleteAsyncTransform(
3054 0 : apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting));
3055 0 : } else if (aNode->IsScrollThumbNode()) {
3056 : // If the node represents a scrollbar thumb, compute and apply the
3057 : // transformation that will be applied to the thumb in
3058 : // AsyncCompositionManager.
3059 0 : ScrollableLayerGuid guid{aNode->GetLayersId(), 0, aNode->GetScrollTargetId()};
3060 0 : if (RefPtr<HitTestingTreeNode> scrollTargetNode = GetTargetNode(guid, &GuidComparatorIgnoringPresShell)) {
3061 0 : AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
3062 0 : MOZ_ASSERT(scrollTargetApzc);
3063 : return scrollTargetApzc->CallWithLastContentPaintMetrics(
3064 0 : [&](const FrameMetrics& aMetrics) {
3065 : return ComputeTransformForScrollThumb(
3066 0 : aNode->GetTransform() * AsyncTransformMatrix(),
3067 0 : scrollTargetNode->GetTransform().ToUnknownMatrix(),
3068 0 : scrollTargetApzc,
3069 : aMetrics,
3070 : aNode->GetScrollbarData(),
3071 0 : scrollTargetNode->IsAncestorOf(aNode),
3072 : nullptr);
3073 0 : });
3074 : }
3075 : }
3076 : // Otherwise, the node does not have an async transform.
3077 0 : return aNode->GetTransform() * AsyncTransformMatrix();
3078 : }
3079 :
3080 : already_AddRefed<wr::WebRenderAPI>
3081 0 : APZCTreeManager::GetWebRenderAPI() const
3082 : {
3083 0 : RefPtr<wr::WebRenderAPI> api;
3084 0 : CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
3085 0 : [&](LayerTreeState& aState) -> void {
3086 0 : if (aState.mWrBridge) {
3087 0 : api = aState.mWrBridge->GetWebRenderAPI();
3088 : }
3089 0 : });
3090 0 : return api.forget();
3091 : }
3092 :
3093 : already_AddRefed<GeckoContentController>
3094 0 : APZCTreeManager::GetContentController(LayersId aLayersId) const
3095 : {
3096 0 : RefPtr<GeckoContentController> controller;
3097 0 : CompositorBridgeParent::CallWithIndirectShadowTree(aLayersId,
3098 0 : [&](LayerTreeState& aState) -> void {
3099 0 : controller = aState.mController;
3100 0 : });
3101 0 : return controller.forget();
3102 : }
3103 :
3104 : bool
3105 0 : APZCTreeManager::GetAPZTestData(LayersId aLayersId,
3106 : APZTestData* aOutData)
3107 : {
3108 0 : AssertOnUpdaterThread();
3109 0 : MutexAutoLock lock(mTestDataLock);
3110 0 : auto it = mTestData.find(aLayersId);
3111 0 : if (it == mTestData.end()) {
3112 : return false;
3113 : }
3114 0 : *aOutData = *(it->second);
3115 0 : return true;
3116 : }
3117 :
3118 : /*static*/ LayerToParentLayerMatrix4x4
3119 0 : APZCTreeManager::ComputeTransformForScrollThumb(
3120 : const LayerToParentLayerMatrix4x4& aCurrentTransform,
3121 : const Matrix4x4& aScrollableContentTransform,
3122 : AsyncPanZoomController* aApzc,
3123 : const FrameMetrics& aMetrics,
3124 : const ScrollbarData& aScrollbarData,
3125 : bool aScrollbarIsDescendant,
3126 : AsyncTransformComponentMatrix* aOutClipTransform)
3127 : {
3128 : // We only apply the transform if the scroll-target layer has non-container
3129 : // children (i.e. when it has some possibly-visible content). This is to
3130 : // avoid moving scroll-bars in the situation that only a scroll information
3131 : // layer has been built for a scroll frame, as this would result in a
3132 : // disparity between scrollbars and visible content.
3133 0 : if (aMetrics.IsScrollInfoLayer()) {
3134 0 : return LayerToParentLayerMatrix4x4{};
3135 : }
3136 :
3137 0 : MOZ_RELEASE_ASSERT(aApzc);
3138 :
3139 : AsyncTransformComponentMatrix asyncTransform =
3140 0 : aApzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
3141 :
3142 : // |asyncTransform| represents the amount by which we have scrolled and
3143 : // zoomed since the last paint. Because the scrollbar was sized and positioned based
3144 : // on the painted content, we need to adjust it based on asyncTransform so that
3145 : // it reflects what the user is actually seeing now.
3146 0 : AsyncTransformComponentMatrix scrollbarTransform;
3147 0 : if (*aScrollbarData.mDirection == ScrollDirection::eVertical) {
3148 0 : const ParentLayerCoord asyncScrollY = asyncTransform._42;
3149 0 : const float asyncZoomY = asyncTransform._22;
3150 :
3151 : // The scroll thumb needs to be scaled in the direction of scrolling by the
3152 : // inverse of the async zoom. This is because zooming in decreases the
3153 : // fraction of the whole srollable rect that is in view.
3154 0 : const float yScale = 1.f / asyncZoomY;
3155 :
3156 : // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
3157 0 : const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().yScale * asyncZoomY);
3158 :
3159 : // Here we convert the scrollbar thumb ratio into a true unitless ratio by
3160 : // dividing out the conversion factor from the scrollframe's parent's space
3161 : // to the scrollframe's space.
3162 0 : const float ratio = aScrollbarData.mThumbRatio /
3163 0 : (aMetrics.GetPresShellResolution() * asyncZoomY);
3164 : // The scroll thumb needs to be translated in opposite direction of the
3165 : // async scroll. This is because scrolling down, which translates the layer
3166 : // content up, should result in moving the scroll thumb down.
3167 0 : ParentLayerCoord yTranslation = -asyncScrollY * ratio;
3168 :
3169 : // The scroll thumb additionally needs to be translated to compensate for
3170 : // the scale applied above. The origin with respect to which the scale is
3171 : // applied is the origin of the entire scrollbar, rather than the origin of
3172 : // the scroll thumb (meaning, for a vertical scrollbar it's at the top of
3173 : // the composition bounds). This means that empty space above the thumb
3174 : // is scaled too, effectively translating the thumb. We undo that
3175 : // translation here.
3176 : // (One can think of the adjustment being done to the translation here as
3177 : // a change of basis. We have a method to help with that,
3178 : // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
3179 : // cleaner in this case).
3180 0 : const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().y * ratio);
3181 0 : const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
3182 0 : const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
3183 0 : const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
3184 0 : yTranslation -= thumbOriginDeltaPL;
3185 :
3186 0 : if (aMetrics.IsRootContent()) {
3187 : // Scrollbar for the root are painted at the same resolution as the
3188 : // content. Since the coordinate space we apply this transform in includes
3189 : // the resolution, we need to adjust for it as well here. Note that in
3190 : // another metrics.IsRootContent() hunk below we apply a
3191 : // resolution-cancelling transform which ensures the scroll thumb isn't
3192 : // actually rendered at a larger scale.
3193 0 : yTranslation *= aMetrics.GetPresShellResolution();
3194 : }
3195 :
3196 0 : scrollbarTransform.PostScale(1.f, yScale, 1.f);
3197 0 : scrollbarTransform.PostTranslate(0, yTranslation, 0);
3198 : }
3199 0 : if (*aScrollbarData.mDirection == ScrollDirection::eHorizontal) {
3200 : // See detailed comments under the VERTICAL case.
3201 :
3202 0 : const ParentLayerCoord asyncScrollX = asyncTransform._41;
3203 0 : const float asyncZoomX = asyncTransform._11;
3204 :
3205 0 : const float xScale = 1.f / asyncZoomX;
3206 :
3207 0 : const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().xScale * asyncZoomX);
3208 :
3209 0 : const float ratio = aScrollbarData.mThumbRatio /
3210 0 : (aMetrics.GetPresShellResolution() * asyncZoomX);
3211 0 : ParentLayerCoord xTranslation = -asyncScrollX * ratio;
3212 :
3213 0 : const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().x * ratio);
3214 0 : const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
3215 0 : const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
3216 0 : const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
3217 0 : xTranslation -= thumbOriginDeltaPL;
3218 :
3219 0 : if (aMetrics.IsRootContent()) {
3220 0 : xTranslation *= aMetrics.GetPresShellResolution();
3221 : }
3222 :
3223 0 : scrollbarTransform.PostScale(xScale, 1.f, 1.f);
3224 0 : scrollbarTransform.PostTranslate(xTranslation, 0, 0);
3225 : }
3226 :
3227 : LayerToParentLayerMatrix4x4 transform =
3228 0 : aCurrentTransform * scrollbarTransform;
3229 :
3230 0 : AsyncTransformComponentMatrix compensation;
3231 : // If the scrollbar layer is for the root then the content's resolution
3232 : // applies to the scrollbar as well. Since we don't actually want the scroll
3233 : // thumb's size to vary with the zoom (other than its length reflecting the
3234 : // fraction of the scrollable length that's in view, which is taken care of
3235 : // above), we apply a transform to cancel out this resolution.
3236 0 : if (aMetrics.IsRootContent()) {
3237 0 : compensation =
3238 0 : AsyncTransformComponentMatrix::Scaling(
3239 : aMetrics.GetPresShellResolution(),
3240 : aMetrics.GetPresShellResolution(),
3241 0 : 1.0f).Inverse();
3242 : }
3243 : // If the scrollbar layer is a child of the content it is a scrollbar for,
3244 : // then we need to adjust for any async transform (including an overscroll
3245 : // transform) on the content. This needs to be cancelled out because layout
3246 : // positions and sizes the scrollbar on the assumption that there is no async
3247 : // transform, and without this adjustment the scrollbar will end up in the
3248 : // wrong place.
3249 : //
3250 : // Note that since the async transform is applied on top of the content's
3251 : // regular transform, we need to make sure to unapply the async transform in
3252 : // the same coordinate space. This requires applying the content transform
3253 : // and then unapplying it after unapplying the async transform.
3254 0 : if (aScrollbarIsDescendant) {
3255 : AsyncTransformComponentMatrix overscroll =
3256 0 : aApzc->GetOverscrollTransform(AsyncPanZoomController::eForCompositing);
3257 0 : Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
3258 0 : const Matrix4x4& contentTransform = aScrollableContentTransform;
3259 0 : Matrix4x4 contentUntransform = contentTransform.Inverse();
3260 :
3261 0 : compensation *= ViewAs<AsyncTransformComponentMatrix>(
3262 : contentTransform
3263 0 : * asyncUntransform
3264 0 : * contentUntransform);
3265 :
3266 : // Pass the total compensation out to the caller so that it can use it
3267 : // to transform clip transforms as needed.
3268 0 : if (aOutClipTransform) {
3269 0 : *aOutClipTransform = compensation;
3270 : }
3271 : }
3272 0 : transform = transform * compensation;
3273 :
3274 0 : return transform;
3275 : }
3276 :
3277 : APZSampler*
3278 16 : APZCTreeManager::GetSampler() const
3279 : {
3280 : // We should always have a sampler here, since in practice the sampler
3281 : // is destroyed at the same time that this APZCTreeMAnager instance is.
3282 16 : MOZ_ASSERT(mSampler);
3283 16 : return mSampler;
3284 : }
3285 :
3286 : void
3287 0 : APZCTreeManager::AssertOnSamplerThread()
3288 : {
3289 16 : GetSampler()->AssertOnSamplerThread();
3290 16 : }
3291 :
3292 : APZUpdater*
3293 18 : APZCTreeManager::GetUpdater() const
3294 : {
3295 : // We should always have an updater here, since in practice the updater
3296 : // is destroyed at the same time that this APZCTreeManager instance is.
3297 18 : MOZ_ASSERT(mUpdater);
3298 18 : return mUpdater;
3299 : }
3300 :
3301 : void
3302 0 : APZCTreeManager::AssertOnUpdaterThread()
3303 : {
3304 16 : GetUpdater()->AssertOnUpdaterThread();
3305 16 : }
3306 :
3307 : void
3308 0 : APZCTreeManager::LockTree()
3309 : {
3310 0 : AssertOnUpdaterThread();
3311 0 : mTreeLock.Lock();
3312 0 : }
3313 :
3314 : void
3315 0 : APZCTreeManager::UnlockTree()
3316 : {
3317 0 : AssertOnUpdaterThread();
3318 0 : mTreeLock.Unlock();
3319 0 : }
3320 :
3321 : void
3322 0 : APZCTreeManager::SetDPI(float aDpiValue)
3323 : {
3324 0 : APZThreadUtils::AssertOnControllerThread();
3325 1 : mDPI = aDpiValue;
3326 1 : }
3327 :
3328 : float
3329 0 : APZCTreeManager::GetDPI() const
3330 : {
3331 : APZThreadUtils::AssertOnControllerThread();
3332 : return mDPI;
3333 : }
3334 :
3335 : #if defined(MOZ_WIDGET_ANDROID)
3336 : AndroidDynamicToolbarAnimator*
3337 : APZCTreeManager::GetAndroidDynamicToolbarAnimator()
3338 : {
3339 : return mToolbarAnimator;
3340 : }
3341 : #endif // defined(MOZ_WIDGET_ANDROID)
3342 :
3343 : } // namespace layers
3344 : } // namespace mozilla
|