APZCTreeManager.cpp (165054B)
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 "GenericFlingAnimation.h" // for FLING_LOG 14 #include "HitTestingTreeNode.h" // for HitTestingTreeNode 15 #include "InputBlockState.h" // for InputBlockState 16 #include "InputData.h" // for InputData, etc 17 #include "WRHitTester.h" // for WRHitTester 18 #include "apz/src/APZUtils.h" 19 #include "mozilla/RecursiveMutex.h" 20 #include "mozilla/dom/BrowserParent.h" // for AreRecordReplayTabsActive 21 #include "mozilla/dom/MouseEventBinding.h" // for MouseEvent constants 22 #include "mozilla/dom/InteractiveWidget.h" 23 #include "mozilla/dom/Touch.h" // for Touch 24 #include "mozilla/gfx/CompositorHitTestInfo.h" 25 #include "mozilla/gfx/LoggingConstants.h" 26 #include "mozilla/gfx/Matrix.h" 27 #include "mozilla/gfx/gfxVars.h" // for gfxVars 28 #include "mozilla/gfx/GPUParent.h" // for GPUParent 29 #include "mozilla/gfx/Logging.h" // for gfx::TreeLog 30 #include "mozilla/gfx/Point.h" // for Point 31 #include "mozilla/layers/APZSampler.h" // for APZSampler 32 #include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc 33 #include "mozilla/layers/APZUpdater.h" // for APZUpdater 34 #include "mozilla/layers/APZUtils.h" // for AsyncTransform 35 #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics 36 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc 37 #include "mozilla/layers/DoubleTapToZoom.h" // for ZoomTarget 38 #include "mozilla/layers/MatrixMessage.h" 39 #include "mozilla/layers/ScrollableLayerGuid.h" 40 #include "mozilla/layers/UiCompositorControllerParent.h" 41 #include "mozilla/layers/WebRenderScrollDataWrapper.h" 42 #include "mozilla/MouseEvents.h" 43 #include "mozilla/mozalloc.h" // for operator new 44 #include "mozilla/MozPromise.h" 45 #include "mozilla/Preferences.h" // for Preferences 46 #include "mozilla/StaticPrefs_accessibility.h" 47 #include "mozilla/StaticPrefs_apz.h" 48 #include "mozilla/StaticPrefs_layout.h" 49 #include "mozilla/ToString.h" 50 #include "mozilla/TouchEvents.h" 51 #include "mozilla/EventStateManager.h" // for WheelPrefs 52 #include "mozilla/webrender/WebRenderAPI.h" 53 #include "mozilla/webrender/WebRenderTypes.h" 54 #include "nsDebug.h" // for NS_WARNING 55 #include "nsPoint.h" // for nsIntPoint 56 #include "nsThreadUtils.h" // for NS_IsMainThread 57 #include "ScrollThumbUtils.h" // for ComputeTransformForScrollThumb 58 #include "OverscrollHandoffState.h" // for OverscrollHandoffState 59 #include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc 60 #include "Units.h" // for ParentlayerPixel 61 #include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled 62 #include "UnitTransforms.h" // for ViewAs 63 64 mozilla::LazyLogModule mozilla::layers::APZCTreeManager::sLog("apz.manager"); 65 #define APZCTM_LOG(...) \ 66 MOZ_LOG(APZCTreeManager::sLog, LogLevel::Debug, (__VA_ARGS__)) 67 #define APZCTM_LOGV(...) \ 68 MOZ_LOG(APZCTreeManager::sLog, LogLevel::Verbose, (__VA_ARGS__)) 69 70 static mozilla::LazyLogModule sApzKeyLog("apz.key"); 71 #define APZ_KEY_LOG(...) MOZ_LOG(sApzKeyLog, LogLevel::Debug, (__VA_ARGS__)) 72 73 namespace mozilla { 74 namespace layers { 75 76 using mozilla::gfx::CompositorHitTestFlags; 77 using mozilla::gfx::CompositorHitTestInfo; 78 using mozilla::gfx::CompositorHitTestInvisibleToHit; 79 using mozilla::gfx::LOG_DEFAULT; 80 81 typedef mozilla::gfx::Point Point; 82 typedef mozilla::gfx::Point4D Point4D; 83 typedef mozilla::gfx::Matrix4x4 Matrix4x4; 84 85 typedef CompositorBridgeParent::LayerTreeState LayerTreeState; 86 87 struct APZCTreeManager::TreeBuildingState { 88 TreeBuildingState(LayersId aRootLayersId, LayersId aOriginatingLayersId, 89 APZTestData* aTestData, uint32_t aPaintSequence, 90 bool aIsTestLoggingEnabled) 91 : mOriginatingLayersId(aOriginatingLayersId), 92 mPaintLogger(aTestData, aPaintSequence, aIsTestLoggingEnabled) { 93 CompositorBridgeParent::CallWithIndirectShadowTree( 94 aRootLayersId, [this](LayerTreeState& aState) -> void { 95 mCompositorController = aState.GetCompositorController(); 96 }); 97 } 98 99 typedef std::unordered_map<AsyncPanZoomController*, gfx::Matrix4x4> 100 DeferredTransformMap; 101 102 // State that doesn't change as we recurse in the tree building 103 RefPtr<CompositorController> mCompositorController; 104 const LayersId mOriginatingLayersId; 105 const APZPaintLogHelper mPaintLogger; 106 107 // State that is updated as we perform the tree build 108 109 // A list of nodes that need to be destroyed at the end of the tree building. 110 // This is initialized with all nodes in the old tree, and nodes are removed 111 // from it as we reuse them in the new tree. 112 nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy; 113 114 // This map is populated as we place APZCs into the new tree. Its purpose is 115 // to facilitate re-using the same APZC for different layers that scroll 116 // together (and thus have the same ScrollableLayerGuid). The presShellId 117 // doesn't matter for this purpose, and we move the map to the APZCTreeManager 118 // after we're done building, so it's useful to have the presshell-ignoring 119 // map for that. 120 std::unordered_map<ScrollableLayerGuid, ApzcMapData, 121 ScrollableLayerGuid::HashIgnoringPresShellFn, 122 ScrollableLayerGuid::EqualIgnoringPresShellFn> 123 mApzcMap; 124 125 // This is populated with all the HitTestingTreeNodes that are scroll thumbs 126 // and have a scrollthumb animation id (which indicates that they need to be 127 // sampled for WebRender on the sampler thread). 128 std::vector<HitTestingTreeNode*> mScrollThumbs; 129 // This is populated with all the scroll target nodes. We use in conjunction 130 // with mScrollThumbs to build APZCTreeManager::mScrollThumbInfo. 131 std::unordered_map<ScrollableLayerGuid, HitTestingTreeNode*, 132 ScrollableLayerGuid::HashIgnoringPresShellFn, 133 ScrollableLayerGuid::EqualIgnoringPresShellFn> 134 mScrollTargets; 135 136 // During the tree building process, the perspective transform component 137 // of the ancestor transforms of some APZCs can be "deferred" to their 138 // children, meaning they are added to the children's ancestor transforms 139 // instead. Those deferred transforms are tracked here. 140 DeferredTransformMap mPerspectiveTransformsDeferredToChildren; 141 142 // As we recurse down through the tree, this picks up the zoom animation id 143 // from a node in the layer tree, and propagates it downwards to the nearest 144 // APZC instance that is for an RCD node. Generally it will be set on the 145 // root node of the layers (sub-)tree, which may not be same as the RCD node 146 // for the subtree, and so we need this mechanism to ensure it gets propagated 147 // to the RCD's APZC instance. Once it is set on the APZC instance, the value 148 // is cleared back to Nothing(). 149 Maybe<uint64_t> mZoomAnimationId; 150 151 // See corresponding members of APZCTreeManager. These are the same thing, but 152 // on the tree-walking state. They are populated while walking the tree in 153 // a layers update, and then moved into APZCTreeManager. 154 std::vector<FixedPositionInfo> mFixedPositionInfo; 155 std::vector<RootScrollbarInfo> mRootScrollbarInfo; 156 std::vector<StickyPositionInfo> mStickyPositionInfo; 157 158 // As we recurse down through reflayers in the tree, this picks up the 159 // cumulative EventRegionsOverride flags from the reflayers, and is used to 160 // apply them to descendant layers. 161 std::stack<EventRegionsOverride> mOverrideFlags; 162 163 std::vector<LayersId> mUpdatedLayersIds; 164 }; 165 166 class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver { 167 public: 168 NS_DECL_ISUPPORTS 169 NS_DECL_NSIOBSERVER 170 171 explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager) 172 : mTreeManager(aTreeManager) { 173 MOZ_ASSERT(NS_IsMainThread()); 174 nsCOMPtr<nsIObserverService> obsSvc = 175 mozilla::services::GetObserverService(); 176 MOZ_ASSERT(obsSvc); 177 if (obsSvc) { 178 obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false); 179 } 180 } 181 182 void Unregister() { 183 MOZ_ASSERT(NS_IsMainThread()); 184 nsCOMPtr<nsIObserverService> obsSvc = 185 mozilla::services::GetObserverService(); 186 if (obsSvc) { 187 obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard"); 188 } 189 mTreeManager = nullptr; 190 } 191 192 protected: 193 virtual ~CheckerboardFlushObserver() = default; 194 195 private: 196 RefPtr<APZCTreeManager> mTreeManager; 197 }; 198 199 NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver) 200 201 NS_IMETHODIMP 202 APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject, 203 const char* aTopic, 204 const char16_t*) { 205 MOZ_ASSERT(NS_IsMainThread()); 206 MOZ_ASSERT(mTreeManager.get()); 207 208 RecursiveMutexAutoLock lock(mTreeManager->mTreeLock); 209 if (mTreeManager->mRootNode) { 210 ForEachNode<ReverseIterator>( 211 mTreeManager->mRootNode.get(), [](HitTestingTreeNode* aNode) { 212 if (aNode->IsPrimaryHolder()) { 213 MOZ_ASSERT(aNode->GetApzc()); 214 aNode->GetApzc()->FlushActiveCheckerboardReport(); 215 } 216 }); 217 } 218 if (XRE_IsGPUProcess()) { 219 if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) { 220 nsCString topic("APZ:FlushActiveCheckerboard:Done"); 221 (void)gpu->SendNotifyUiObservers(topic); 222 } 223 } else { 224 MOZ_ASSERT(XRE_IsParentProcess()); 225 nsCOMPtr<nsIObserverService> obsSvc = 226 mozilla::services::GetObserverService(); 227 if (obsSvc) { 228 obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", 229 nullptr); 230 } 231 } 232 return NS_OK; 233 } 234 235 /** 236 * A RAII class used for setting the focus sequence number on input events 237 * as they are being processed. Any input event is assumed to be potentially 238 * focus changing unless explicitly marked otherwise. 239 */ 240 class MOZ_RAII AutoFocusSequenceNumberSetter { 241 public: 242 AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent) 243 : mFocusState(aFocusState), mEvent(aEvent), mMayChangeFocus(true) {} 244 245 void MarkAsNonFocusChanging() { mMayChangeFocus = false; } 246 247 ~AutoFocusSequenceNumberSetter() { 248 if (mMayChangeFocus) { 249 mFocusState.ReceiveFocusChangingEvent(); 250 251 APZ_KEY_LOG( 252 "Marking input with type=%d as focus changing with seq=%" PRIu64 "\n", 253 static_cast<int>(mEvent.mInputType), 254 mFocusState.LastAPZProcessedEvent()); 255 } else { 256 APZ_KEY_LOG( 257 "Marking input with type=%d as non focus changing with seq=%" PRIu64 258 "\n", 259 static_cast<int>(mEvent.mInputType), 260 mFocusState.LastAPZProcessedEvent()); 261 } 262 263 mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent(); 264 } 265 266 private: 267 FocusState& mFocusState; 268 InputData& mEvent; 269 bool mMayChangeFocus; 270 }; 271 272 APZCTreeManager::APZCTreeManager(LayersId aRootLayersId, 273 UniquePtr<IAPZHitTester> aHitTester) 274 : mTestSampleTime(Nothing(), "APZCTreeManager::mTestSampleTime"), 275 mInputQueue(new InputQueue()), 276 mMapLock("APZCMapLock"), 277 mRootLayersId(aRootLayersId), 278 mSampler(nullptr), 279 mUpdater(nullptr), 280 mTreeLock("APZCTreeLock"), 281 mRetainedTouchIdentifier(-1), 282 mInScrollbarTouchDrag(false), 283 mCurrentMousePosition(ScreenPoint(), 284 "APZCTreeManager::mCurrentMousePosition"), 285 mApzcTreeLog("apzctree"), 286 mTestDataLock("APZTestDataLock"), 287 mDPI(160.0), 288 mHitTester(std::move(aHitTester)), 289 mScrollGenerationLock("APZScrollGenerationLock"), 290 mInteractiveWidget( 291 dom::InteractiveWidgetUtils::DefaultInteractiveWidgetMode()), 292 mIsSoftwareKeyboardVisible(false), 293 mHaveOOPIframes(false) { 294 AsyncPanZoomController::InitializeGlobalState(); 295 mApzcTreeLog.ConditionOnPrefFunction(StaticPrefs::apz_printtree); 296 297 if (!mHitTester) { 298 mHitTester = MakeUnique<WRHitTester>(); 299 } 300 mHitTester->Initialize(this); 301 } 302 303 APZCTreeManager::~APZCTreeManager() = default; 304 305 void APZCTreeManager::Init() { 306 RefPtr<APZCTreeManager> self(this); 307 NS_DispatchToMainThread(NS_NewRunnableFunction( 308 "layers::APZCTreeManager::Init", 309 [self] { self->mFlushObserver = new CheckerboardFlushObserver(self); })); 310 } 311 312 already_AddRefed<APZCTreeManager> APZCTreeManager::Create( 313 LayersId aRootLayersId, UniquePtr<IAPZHitTester> aHitTester) { 314 RefPtr<APZCTreeManager> manager = 315 new APZCTreeManager(aRootLayersId, std::move(aHitTester)); 316 manager->Init(); 317 return manager.forget(); 318 } 319 320 void APZCTreeManager::SetSampler(APZSampler* aSampler) { 321 // We're either setting the sampler or clearing it 322 MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr)); 323 mSampler = aSampler; 324 } 325 326 void APZCTreeManager::SetUpdater(APZUpdater* aUpdater) { 327 // We're either setting the updater or clearing it 328 MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr)); 329 mUpdater = aUpdater; 330 } 331 332 void APZCTreeManager::NotifyLayerTreeAdopted( 333 LayersId aLayersId, const RefPtr<APZCTreeManager>& aOldApzcTreeManager) { 334 AssertOnUpdaterThread(); 335 336 if (aOldApzcTreeManager) { 337 aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId); 338 // While we could move the focus target information from the old APZC tree 339 // manager into this one, it's safer to not do that, as we'll probably have 340 // that information repopulated soon anyway (on the next layers update). 341 } 342 343 // There may be focus updates from the tab's content process in flight 344 // triggered by events that were processed by the old tree manager, 345 // which this tree manager does not expect. 346 // Resetting the focus state avoids this (the state will sync with content 347 // on the next focus update). 348 mFocusState.Reset(); 349 350 UniquePtr<APZTestData> adoptedData; 351 if (aOldApzcTreeManager) { 352 MutexAutoLock lock(aOldApzcTreeManager->mTestDataLock); 353 auto it = aOldApzcTreeManager->mTestData.find(aLayersId); 354 if (it != aOldApzcTreeManager->mTestData.end()) { 355 adoptedData = std::move(it->second); 356 aOldApzcTreeManager->mTestData.erase(it); 357 } 358 } 359 if (adoptedData) { 360 MutexAutoLock lock(mTestDataLock); 361 mTestData[aLayersId] = std::move(adoptedData); 362 } 363 } 364 365 void APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId) { 366 AssertOnUpdaterThread(); 367 368 mFocusState.RemoveFocusTarget(aLayersId); 369 370 { // scope lock 371 MutexAutoLock lock(mTestDataLock); 372 mTestData.erase(aLayersId); 373 } 374 } 375 376 already_AddRefed<AsyncPanZoomController> APZCTreeManager::NewAPZCInstance( 377 LayersId aLayersId, GeckoContentController* aController) { 378 return MakeRefPtr<AsyncPanZoomController>( 379 aLayersId, this, mInputQueue, aController, 380 AsyncPanZoomController::USE_GESTURE_DETECTOR) 381 .forget(); 382 } 383 384 void APZCTreeManager::SetTestSampleTime(const Maybe<TimeStamp>& aTime) { 385 auto testSampleTime = mTestSampleTime.Lock(); 386 testSampleTime.ref() = aTime; 387 } 388 389 SampleTime APZCTreeManager::GetFrameTime() { 390 auto testSampleTime = mTestSampleTime.Lock(); 391 if (testSampleTime.ref()) { 392 return SampleTime::FromTest(*testSampleTime.ref()); 393 } 394 return SampleTime::FromNow(); 395 } 396 397 void APZCTreeManager::SetAllowedTouchBehavior( 398 uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) { 399 if (!APZThreadUtils::IsControllerThread()) { 400 APZThreadUtils::RunOnControllerThread( 401 NewRunnableMethod<uint64_t, nsTArray<TouchBehaviorFlags>>( 402 "layers::APZCTreeManager::SetAllowedTouchBehavior", this, 403 &APZCTreeManager::SetAllowedTouchBehavior, aInputBlockId, 404 aValues.Clone())); 405 return; 406 } 407 408 APZThreadUtils::AssertOnControllerThread(); 409 410 mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues); 411 } 412 413 void APZCTreeManager::SetBrowserGestureResponse( 414 uint64_t aInputBlockId, BrowserGestureResponse aResponse) { 415 if (!APZThreadUtils::IsControllerThread()) { 416 APZThreadUtils::RunOnControllerThread( 417 NewRunnableMethod<uint64_t, BrowserGestureResponse>( 418 "layers::APZCTreeManager::SetBrowserGestureResponse", this, 419 &APZCTreeManager::SetBrowserGestureResponse, aInputBlockId, 420 aResponse)); 421 return; 422 } 423 424 APZThreadUtils::AssertOnControllerThread(); 425 426 mInputQueue->SetBrowserGestureResponse(aInputBlockId, aResponse); 427 } 428 429 std::vector<LayersId> APZCTreeManager::UpdateHitTestingTree( 430 const WebRenderScrollDataWrapper& aRoot, LayersId aOriginatingLayersId, 431 uint32_t aPaintSequenceNumber) { 432 AssertOnUpdaterThread(); 433 434 RecursiveMutexAutoLock lock(mTreeLock); 435 436 // For testing purposes, we log some data to the APZTestData associated with 437 // the layers id that originated this update. 438 APZTestData* testData = nullptr; 439 const bool testLoggingEnabled = StaticPrefs::apz_test_logging_enabled(); 440 if (testLoggingEnabled) { 441 MutexAutoLock lock(mTestDataLock); 442 UniquePtr<APZTestData> ptr = MakeUnique<APZTestData>(); 443 auto result = 444 mTestData.insert(std::make_pair(aOriginatingLayersId, std::move(ptr))); 445 testData = result.first->second.get(); 446 testData->StartNewPaint(aPaintSequenceNumber); 447 } 448 449 TreeBuildingState state(mRootLayersId, aOriginatingLayersId, testData, 450 aPaintSequenceNumber, testLoggingEnabled); 451 452 mRootContentApzcs.ClearAndRetainStorage(); 453 454 // We do this business with collecting the entire tree into an array because 455 // otherwise it's very hard to determine which APZC instances need to be 456 // destroyed. In the worst case, there are two scenarios: (a) a layer with an 457 // APZC is removed from the layer tree and (b) a layer with an APZC is moved 458 // in the layer tree from one place to a completely different place. In 459 // scenario (a) we would want to destroy the APZC while walking the layer tree 460 // and noticing that the layer/APZC is no longer there. But if we do that then 461 // we run into a problem in scenario (b) because we might encounter that layer 462 // later during the walk. To handle both of these we have to 'remember' that 463 // the layer was not found, and then do the destroy only at the end of the 464 // tree walk after we are sure that the layer was removed and not just 465 // transplanted elsewhere. Doing that as part of a recursive tree walk is hard 466 // and so maintaining a list and removing APZCs that are still alive is much 467 // simpler. 468 ForEachNode<ReverseIterator>(mRootNode.get(), 469 [&state](HitTestingTreeNode* aNode) { 470 state.mNodesToDestroy.AppendElement(aNode); 471 }); 472 mRootNode = nullptr; 473 mHaveOOPIframes = false; 474 Maybe<LayersId> asyncZoomContainerSubtree = Nothing(); 475 LayersId currentRootContentLayersId{0}; 476 int asyncZoomContainerNestingDepth = 0; 477 bool haveNestedAsyncZoomContainers = false; 478 nsTArray<LayersId> subtreesWithRootContentOutsideAsyncZoomContainer; 479 480 if (aRoot) { 481 std::unordered_set<LayersId, LayersId::HashFn> seenLayersIds; 482 std::stack<gfx::TreeAutoIndent<gfx::LOG_CRITICAL>> indents; 483 std::stack<AncestorTransform> ancestorTransforms; 484 HitTestingTreeNode* parent = nullptr; 485 HitTestingTreeNode* next = nullptr; 486 LayersId layersId = mRootLayersId; 487 seenLayersIds.insert(mRootLayersId); 488 ancestorTransforms.push(AncestorTransform()); 489 state.mOverrideFlags.push(EventRegionsOverride::NoOverride); 490 nsTArray<Maybe<ZoomConstraints>> zoomConstraintsStack; 491 uint64_t fixedSubtreeDepth = 0; 492 493 // push a nothing to be used for anything outside an async zoom container 494 zoomConstraintsStack.AppendElement(Nothing()); 495 496 mApzcTreeLog << "[start]\n"; 497 mTreeLock.AssertCurrentThreadIn(); 498 499 ForEachNode<ReverseIterator>( 500 aRoot, 501 [&](ScrollNode aLayerMetrics) { 502 if (auto asyncZoomContainerId = 503 aLayerMetrics.GetAsyncZoomContainerId()) { 504 if (asyncZoomContainerNestingDepth > 0) { 505 haveNestedAsyncZoomContainers = true; 506 } 507 asyncZoomContainerSubtree = Some(layersId); 508 ++asyncZoomContainerNestingDepth; 509 510 auto it = mZoomConstraints.find( 511 ScrollableLayerGuid(layersId, 0, *asyncZoomContainerId)); 512 if (it != mZoomConstraints.end()) { 513 zoomConstraintsStack.AppendElement(Some(it->second)); 514 } else { 515 zoomConstraintsStack.AppendElement(Nothing()); 516 } 517 } 518 519 if (aLayerMetrics.Metrics().IsRootContent()) { 520 MutexAutoLock lock(mMapLock); 521 mGeckoFixedLayerMargins = 522 aLayerMetrics.Metrics().GetFixedLayerMargins(); 523 SetInteractiveWidgetMode( 524 aLayerMetrics.Metrics().GetInteractiveWidget(), lock); 525 SetIsSoftwareKeyboardVisible( 526 aLayerMetrics.Metrics().IsSoftwareKeyboardVisible(), lock); 527 currentRootContentLayersId = layersId; 528 } else { 529 MOZ_ASSERT(aLayerMetrics.Metrics().GetFixedLayerMargins() == 530 ScreenMargin(), 531 "fixed-layer-margins should be 0 on non-root layer"); 532 } 533 534 // Note that this check happens after the potential increment of 535 // asyncZoomContainerNestingDepth, to allow the root content 536 // metadata to be on the same node as the async zoom container. 537 if (aLayerMetrics.Metrics().IsRootContent() && 538 asyncZoomContainerNestingDepth == 0) { 539 subtreesWithRootContentOutsideAsyncZoomContainer.AppendElement( 540 layersId); 541 } 542 543 HitTestingTreeNode* node = PrepareNodeForLayer( 544 lock, aLayerMetrics, aLayerMetrics.Metrics(), layersId, 545 zoomConstraintsStack.LastElement(), ancestorTransforms.top(), 546 parent, next, state); 547 MOZ_ASSERT(node); 548 AsyncPanZoomController* apzc = node->GetApzc(); 549 aLayerMetrics.SetApzc(apzc); 550 551 // Note that a "valid" animation id is always nonzero, so we don't 552 // need to worry about handling the case where the animation id is 553 // zero. 554 if (node->GetScrollbarAnimationId()) { 555 if (node->IsScrollThumbNode()) { 556 state.mScrollThumbs.push_back(node); 557 } else if (node->IsScrollbarContainerNode()) { 558 // Only scrollbar containers for the root have an animation id. 559 state.mRootScrollbarInfo.emplace_back( 560 *(node->GetScrollbarAnimationId()), 561 node->GetScrollbarDirection()); 562 } 563 } 564 565 if (node->GetFixedPositionAnimationId().isSome()) { 566 // Only top-level fixed nodes should be adjusted 567 // for dynamic toolbar movement. 568 if (fixedSubtreeDepth == 0) { 569 state.mFixedPositionInfo.emplace_back(node); 570 } 571 fixedSubtreeDepth += 1; 572 } 573 if (node->GetStickyPositionAnimationId().isSome()) { 574 state.mStickyPositionInfo.emplace_back(node); 575 } 576 if (apzc && node->IsPrimaryHolder()) { 577 state.mScrollTargets[apzc->GetGuid()] = node; 578 if (aLayerMetrics.Metrics().IsRootContent()) { 579 mTreeLock.AssertCurrentThreadIn(); // for threadsafety analysis 580 mRootContentApzcs.AppendElement(apzc); 581 } 582 } 583 584 // Accumulate the CSS transform between layers that have an APZC. 585 // In the terminology of the big comment above 586 // APZCTreeManager::GetScreenToApzcTransform, if we are at layer M, 587 // then aAncestorTransform is NC * OC * PC, and we left-multiply MC 588 // and compute ancestorTransform to be MC * NC * OC * PC. This gets 589 // passed down as the ancestor transform to layer L when we recurse 590 // into the children below. If we are at a layer with an APZC, such as 591 // P, then we reset the ancestorTransform to just PC, to start the new 592 // accumulation as we go down. 593 AncestorTransform currentTransform{ 594 aLayerMetrics.GetTransform(), 595 aLayerMetrics.TransformIsPerspective()}; 596 if (!apzc) { 597 currentTransform = currentTransform * ancestorTransforms.top(); 598 } 599 ancestorTransforms.push(currentTransform); 600 601 // Note that |node| at this point will not have any children, 602 // otherwise we we would have to set next to node->GetFirstChild(). 603 MOZ_ASSERT(!node->GetFirstChild()); 604 parent = node; 605 next = nullptr; 606 607 // Update the layersId if we have a new one 608 if (Maybe<LayersId> newLayersId = aLayerMetrics.GetReferentId()) { 609 layersId = *newLayersId; 610 seenLayersIds.insert(layersId); 611 612 if (state.mOverrideFlags.size() > 1) { 613 // At this point, if `state.mOverrideFlags` has 2 or more 614 // elements, which means there's a node having a referent id 615 // corresponding to the top level content document and this node 616 // is an descendant of the node but for a different content 617 // process. 618 mHaveOOPIframes = true; 619 } 620 621 // Propagate any event region override flags down into all 622 // descendant nodes from the reflayer that has the flag. This is an 623 // optimization to avoid having to walk up the tree to check the 624 // override flags. Note that we don't keep the flags on the reflayer 625 // itself, because the semantics of the flags are that they apply 626 // to all content in the layer subtree being referenced. This 627 // matters with the WR hit-test codepath, because this reflayer may 628 // be just one of many nodes associated with a particular APZC, and 629 // calling GetTargetNode with a guid may return any one of the 630 // nodes. If different nodes have different flags on them that can 631 // make the WR hit-test result incorrect, but being strict about 632 // only putting the flags on descendant layers avoids this problem. 633 state.mOverrideFlags.push(state.mOverrideFlags.top() | 634 aLayerMetrics.GetEventRegionsOverride()); 635 } 636 637 indents.push(gfx::TreeAutoIndent<gfx::LOG_CRITICAL>(mApzcTreeLog)); 638 }, 639 [&](ScrollNode aLayerMetrics) { 640 if (aLayerMetrics.GetAsyncZoomContainerId()) { 641 --asyncZoomContainerNestingDepth; 642 zoomConstraintsStack.RemoveLastElement(); 643 } 644 if (aLayerMetrics.GetReferentId()) { 645 state.mOverrideFlags.pop(); 646 } 647 648 if (aLayerMetrics.GetFixedPositionAnimationId().isSome()) { 649 fixedSubtreeDepth -= 1; 650 } 651 652 next = parent; 653 parent = parent->GetParent(); 654 layersId = next->GetLayersId(); 655 ancestorTransforms.pop(); 656 indents.pop(); 657 }); 658 659 mApzcTreeLog << "[end]\n"; 660 661 MOZ_ASSERT( 662 !asyncZoomContainerSubtree || 663 !subtreesWithRootContentOutsideAsyncZoomContainer.Contains( 664 *asyncZoomContainerSubtree), 665 "If there is an async zoom container, all scroll nodes with root " 666 "content scroll metadata should be inside it"); 667 MOZ_ASSERT(!haveNestedAsyncZoomContainers, 668 "Should not have nested async zoom container"); 669 670 // If we have perspective transforms deferred to children, do another 671 // walk of the tree and actually apply them to the children. 672 // We can't do this "as we go" in the previous traversal, because by the 673 // time we realize we need to defer a perspective transform for an APZC, 674 // we may already have processed a previous layer (including children 675 // found in its subtree) that shares that APZC. 676 if (!state.mPerspectiveTransformsDeferredToChildren.empty()) { 677 ForEachNode<ReverseIterator>( 678 mRootNode.get(), [&state](HitTestingTreeNode* aNode) { 679 AsyncPanZoomController* apzc = aNode->GetApzc(); 680 if (!apzc) { 681 return; 682 } 683 if (!aNode->IsPrimaryHolder()) { 684 return; 685 } 686 687 AsyncPanZoomController* parent = apzc->GetParent(); 688 if (!parent) { 689 return; 690 } 691 692 auto it = 693 state.mPerspectiveTransformsDeferredToChildren.find(parent); 694 if (it != state.mPerspectiveTransformsDeferredToChildren.end()) { 695 apzc->SetAncestorTransform(AncestorTransform{ 696 it->second * apzc->GetAncestorTransform(), false}); 697 } 698 }); 699 } 700 701 // Remove any layers ids for which we no longer have content from 702 // mDetachedLayersIds. 703 for (auto iter = mDetachedLayersIds.begin(); 704 iter != mDetachedLayersIds.end();) { 705 // unordered_set::erase() invalidates the iterator pointing to the 706 // element being erased, but returns an iterator to the next element. 707 if (seenLayersIds.find(*iter) == seenLayersIds.end()) { 708 iter = mDetachedLayersIds.erase(iter); 709 } else { 710 ++iter; 711 } 712 } 713 } 714 715 // We do not support tree structures where the root node has siblings. 716 MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling())); 717 718 { // scope lock and update our mApzcMap before we destroy all the unused 719 // APZC instances 720 MutexAutoLock lock(mMapLock); 721 mApzcMap = std::move(state.mApzcMap); 722 723 for (auto& mapping : mApzcMap) { 724 AsyncPanZoomController* parent = mapping.second.apzc->GetParent(); 725 mapping.second.parent = parent ? Some(parent->GetGuid()) : Nothing(); 726 } 727 728 mScrollThumbInfo.clear(); 729 // For non-webrender, state.mScrollThumbs will be empty so this will be a 730 // no-op. 731 for (HitTestingTreeNode* thumb : state.mScrollThumbs) { 732 MOZ_ASSERT(thumb->IsScrollThumbNode()); 733 ScrollableLayerGuid targetGuid(thumb->GetLayersId(), 0, 734 thumb->GetScrollTargetId()); 735 auto it = state.mScrollTargets.find(targetGuid); 736 if (it == state.mScrollTargets.end()) { 737 // It could be that |thumb| is a scrollthumb for content which didn't 738 // have an APZC, for example if the content isn't layerized. Regardless, 739 // we can't async-scroll it so we don't need to worry about putting it 740 // in mScrollThumbInfo. 741 continue; 742 } 743 HitTestingTreeNode* target = it->second; 744 mScrollThumbInfo.emplace_back( 745 *(thumb->GetScrollbarAnimationId()), thumb->GetTransform(), 746 thumb->GetScrollbarData(), targetGuid, target->GetTransform(), 747 target->IsAncestorOf(thumb)); 748 } 749 750 mRootScrollbarInfo = std::move(state.mRootScrollbarInfo); 751 mFixedPositionInfo = std::move(state.mFixedPositionInfo); 752 mStickyPositionInfo = std::move(state.mStickyPositionInfo); 753 } 754 755 for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) { 756 APZCTM_LOG("Destroying node at %p with APZC %p\n", 757 state.mNodesToDestroy[i].get(), 758 state.mNodesToDestroy[i]->GetApzc()); 759 state.mNodesToDestroy[i]->Destroy(); 760 } 761 762 SetFixedLayerMarginsOnRootContentApzcs(lock); 763 764 APZCTM_LOG("APZCTreeManager (%p)\n", this); 765 if (mRootNode && MOZ_LOG_TEST(sLog, LogLevel::Debug)) { 766 mRootNode->Dump(" "); 767 } 768 SendSubtreeTransformsToChromeMainThread(nullptr); 769 770 return std::move(state.mUpdatedLayersIds); 771 } 772 773 void APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId, 774 LayersId aOriginatingLayersId, 775 const FocusTarget& aFocusTarget) { 776 AssertOnUpdaterThread(); 777 778 if (!StaticPrefs::apz_keyboard_enabled_AtStartup()) { 779 return; 780 } 781 782 mFocusState.Update(aRootLayerTreeId, aOriginatingLayersId, aFocusTarget); 783 } 784 785 void APZCTreeManager::SampleForWebRender(const Maybe<VsyncId>& aVsyncId, 786 wr::TransactionWrapper& aTxn, 787 const SampleTime& aSampleTime) { 788 AssertOnSamplerThread(); 789 MutexAutoLock lock(mMapLock); 790 791 RefPtr<WebRenderBridgeParent> wrBridgeParent; 792 RefPtr<CompositorController> controller; 793 CompositorBridgeParent::CallWithIndirectShadowTree( 794 mRootLayersId, [&](LayerTreeState& aState) -> void { 795 controller = aState.GetCompositorController(); 796 wrBridgeParent = aState.mWrBridge; 797 }); 798 799 const bool activeAnimations = AdvanceAnimationsInternal(lock, aSampleTime); 800 if (activeAnimations && controller) { 801 controller->ScheduleRenderOnCompositorThread( 802 wr::RenderReasons::ANIMATED_PROPERTY); 803 } 804 APZCTM_LOGV( 805 "APZCTreeManager(%p)::SampleForWebRender, want more composites: %d\n", 806 this, (activeAnimations && controller)); 807 808 nsTArray<wr::WrTransformProperty> transforms; 809 810 // Sample async transforms on scrollable layers. 811 for (const auto& [_, mapData] : mApzcMap) { 812 AsyncPanZoomController* apzc = mapData.apzc; 813 814 if (Maybe<CompositionPayload> payload = apzc->NotifyScrollSampling()) { 815 if (wrBridgeParent && aVsyncId) { 816 wrBridgeParent->AddPendingScrollPayload(*payload, *aVsyncId); 817 } 818 } 819 820 if (StaticPrefs::apz_test_logging_enabled()) { 821 MutexAutoLock lock(mTestDataLock); 822 823 ScrollableLayerGuid guid = apzc->GetGuid(); 824 auto it = mTestData.find(guid.mLayersId); 825 if (it != mTestData.end()) { 826 it->second->RecordSampledResult( 827 apzc->GetCurrentAsyncVisualViewport( 828 AsyncPanZoomController::eForCompositing) 829 .TopLeft(), 830 (aSampleTime.Time() - TimeStamp::ProcessCreation()) 831 .ToMicroseconds(), 832 guid.mLayersId, guid.mScrollId); 833 } 834 } 835 836 wr::LayoutTransform zoomForMinimap = 837 wr::ToLayoutTransform(gfx::Matrix4x4()); 838 if (Maybe<uint64_t> zoomAnimationId = apzc->GetZoomAnimationId()) { 839 // for now we only support zooming on root content APZCs 840 MOZ_ASSERT(apzc->IsRootContent()); 841 842 LayoutDeviceToParentLayerScale zoom = apzc->GetCurrentPinchZoomScale( 843 AsyncPanZoomController::eForCompositing); 844 845 AsyncTransform asyncVisualTransform = apzc->GetCurrentAsyncTransform( 846 AsyncPanZoomController::eForCompositing, 847 AsyncTransformComponents{AsyncTransformComponent::eVisual}); 848 849 wr::WrTransformProperty zoomTransform = wr::ToWrTransformProperty( 850 *zoomAnimationId, LayoutDeviceToParentLayerMatrix4x4::Scaling( 851 zoom.scale, zoom.scale, 1.0f) * 852 AsyncTransformComponentMatrix::Translation( 853 asyncVisualTransform.mTranslation)); 854 855 // Store the zoom transform to be added to the minimap data so that we 856 // can take it into account correctly during minimap rendering. 857 zoomForMinimap = zoomTransform.value; 858 859 transforms.AppendElement(zoomTransform); 860 aTxn.UpdateIsTransformAsyncZooming(*zoomAnimationId, 861 apzc->IsAsyncZooming()); 862 } 863 864 nsTArray<wr::SampledScrollOffset> sampledOffsets = 865 apzc->GetSampledScrollOffsets(); 866 wr::ExternalScrollId scrollId{apzc->GetGuid().mScrollId, 867 wr::AsPipelineId(apzc->GetGuid().mLayersId)}; 868 aTxn.UpdateScrollPosition(scrollId, sampledOffsets); 869 870 if (StaticPrefs::apz_minimap_enabled()) { 871 wr::MinimapData minimapData = apzc->GetMinimapData(); 872 minimapData.zoom_transform = zoomForMinimap; 873 // If this APZC is inside the subtree of a root content APZC, find the 874 // ID of that root content APZC. 875 ScrollableLayerGuid enclosingRootContentId; 876 ApzcMapData currentEntry = mapData; 877 AsyncPanZoomController* current = currentEntry.apzc; 878 while (current) { 879 if (current->IsRootContent()) { 880 enclosingRootContentId = current->GetGuid(); 881 break; 882 } 883 // We can't call AsyncPanZoomController::GetParent(), since that 884 // requires acquiring the tree lock, and doing that on the sampler 885 // thread would violate the lock ordering. Instead, get the parent 886 // from the mApzcMap entry. 887 if (auto parentGuid = currentEntry.parent) { 888 auto iter = mApzcMap.find(*parentGuid); 889 if (iter != mApzcMap.end()) { 890 currentEntry = iter->second; 891 current = currentEntry.apzc; 892 continue; 893 } 894 } 895 break; 896 } 897 minimapData.root_content_pipeline_id = 898 wr::AsPipelineId(enclosingRootContentId.mLayersId); 899 minimapData.root_content_scroll_id = enclosingRootContentId.mScrollId; 900 aTxn.AddMinimapData(scrollId, minimapData); 901 } 902 903 if (apzc->IsRootContent()) { 904 // Send the root frame metrics to java through the UIController 905 if (RefPtr<UiCompositorControllerParent> uiController = 906 UiCompositorControllerParent::GetFromRootLayerTreeId( 907 mRootLayersId)) { 908 for (const auto& update : apzc->GetCompositorScrollUpdates()) { 909 uiController->NotifyCompositorScrollUpdate(update); 910 } 911 } 912 } 913 } 914 915 // Now collect all the async transforms needed for the scrollthumbs. 916 for (const ScrollThumbInfo& info : mScrollThumbInfo) { 917 auto it = mApzcMap.find(info.mTargetGuid); 918 if (it == mApzcMap.end()) { 919 // It could be that |info| is a scrollthumb for content which didn't 920 // have an APZC, for example if the content isn't layerized. Regardless, 921 // we can't async-scroll it so we don't need to worry about putting it 922 // in mScrollThumbInfo. 923 continue; 924 } 925 AsyncPanZoomController* scrollTargetApzc = it->second.apzc; 926 MOZ_ASSERT(scrollTargetApzc); 927 LayerToParentLayerMatrix4x4 transform = 928 scrollTargetApzc->CallWithLastContentPaintMetrics( 929 [&](const FrameMetrics& aMetrics) { 930 return ComputeTransformForScrollThumb( 931 info.mThumbTransform * AsyncTransformMatrix(), 932 info.mTargetTransform.ToUnknownMatrix(), scrollTargetApzc, 933 aMetrics, info.mThumbData, info.mTargetIsAncestor); 934 }); 935 transforms.AppendElement( 936 wr::ToWrTransformProperty(info.mThumbAnimationId, transform)); 937 } 938 939 // Move the root scrollbar in response to the dynamic toolbar transition. 940 for (const RootScrollbarInfo& info : mRootScrollbarInfo) { 941 // We only care about the horizontal scrollbar. 942 if (info.mScrollDirection == ScrollDirection::eHorizontal) { 943 ScreenPoint translation = 944 ComputeFixedMarginsOffset(lock, SideBits::eBottom, ScreenMargin()); 945 946 LayerToParentLayerMatrix4x4 transform = 947 LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>( 948 translation, PixelCastJustification::ScreenIsParentLayerForRoot)); 949 950 transforms.AppendElement( 951 wr::ToWrTransformProperty(info.mScrollbarAnimationId, transform)); 952 } 953 } 954 955 for (const FixedPositionInfo& info : mFixedPositionInfo) { 956 MOZ_ASSERT(info.mFixedPositionAnimationId.isSome()); 957 if (!IsFixedToRootContent(info, lock)) { 958 continue; 959 } 960 961 ScreenPoint translation = ComputeFixedMarginsOffset( 962 lock, info.mFixedPosSides, mGeckoFixedLayerMargins); 963 964 LayerToParentLayerMatrix4x4 transform = 965 LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>( 966 translation, PixelCastJustification::ScreenIsParentLayerForRoot)); 967 968 transforms.AppendElement( 969 wr::ToWrTransformProperty(*info.mFixedPositionAnimationId, transform)); 970 } 971 972 for (const StickyPositionInfo& info : mStickyPositionInfo) { 973 MOZ_ASSERT(info.mStickyPositionAnimationId.isSome()); 974 SideBits sides = SidesStuckToRootContent( 975 info, AsyncTransformConsumer::eForCompositing, lock); 976 if (sides == SideBits::eNone) { 977 continue; 978 } 979 980 ScreenPoint translation = 981 ComputeFixedMarginsOffset(lock, sides, mGeckoFixedLayerMargins); 982 983 LayerToParentLayerMatrix4x4 transform = 984 LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>( 985 translation, PixelCastJustification::ScreenIsParentLayerForRoot)); 986 987 transforms.AppendElement( 988 wr::ToWrTransformProperty(*info.mStickyPositionAnimationId, transform)); 989 } 990 991 aTxn.AppendTransformProperties(transforms); 992 } 993 994 ParentLayerRect APZCTreeManager::ComputeClippedCompositionBounds( 995 const MutexAutoLock& aProofOfMapLock, ClippedCompositionBoundsMap& aDestMap, 996 ScrollableLayerGuid aGuid) { 997 if (auto iter = aDestMap.find(aGuid); iter != aDestMap.end()) { 998 // We already computed it for this one, early-exit. This might happen 999 // because on a later iteration of mApzcMap we might encounter an ancestor 1000 // of an APZC that we processed on an earlier iteration. In this case we 1001 // would have computed the ancestor's clipped composition bounds when 1002 // recursing up on the earlier iteration. 1003 return iter->second; 1004 } 1005 1006 ParentLayerRect bounds = mApzcMap[aGuid].apzc->GetCompositionBounds(); 1007 const auto& mapEntry = mApzcMap.find(aGuid); 1008 MOZ_ASSERT(mapEntry != mApzcMap.end()); 1009 if (mapEntry->second.parent.isNothing()) { 1010 // Recursion base case, where the APZC with guid `aGuid` has no parent. 1011 // In this case, we don't need to clip `bounds` any further and can just 1012 // early exit. 1013 aDestMap.emplace(aGuid, bounds); 1014 return bounds; 1015 } 1016 1017 ScrollableLayerGuid parentGuid = mapEntry->second.parent.value(); 1018 auto parentBoundsEntry = aDestMap.find(parentGuid); 1019 // If aDestMap doesn't contain the parent entry yet, we recurse to compute 1020 // that one first. 1021 ParentLayerRect parentClippedBounds = 1022 (parentBoundsEntry == aDestMap.end()) 1023 ? ComputeClippedCompositionBounds(aProofOfMapLock, aDestMap, 1024 parentGuid) 1025 : parentBoundsEntry->second; 1026 1027 // The parent layer's async transform applies to the current layer to take 1028 // `bounds` into the same coordinate space as `parentClippedBounds`. However, 1029 // we're going to do the inverse operation and unapply this transform to 1030 // `parentClippedBounds` to bring it into the same coordinate space as 1031 // `bounds`. 1032 AsyncTransform appliesToLayer = 1033 mApzcMap[parentGuid].apzc->GetCurrentAsyncTransform( 1034 AsyncPanZoomController::eForCompositing); 1035 1036 // Do the unapplication 1037 LayerRect parentClippedBoundsInParentLayerSpace = 1038 (parentClippedBounds - appliesToLayer.mTranslation) / 1039 appliesToLayer.mScale; 1040 1041 // And then clip `bounds` by the parent's comp bounds in the current space. 1042 bounds = bounds.Intersect( 1043 ViewAs<ParentLayerPixel>(parentClippedBoundsInParentLayerSpace, 1044 PixelCastJustification::MovingDownToChildren)); 1045 1046 // Done! 1047 aDestMap.emplace(aGuid, bounds); 1048 return bounds; 1049 } 1050 1051 bool APZCTreeManager::AdvanceAnimationsInternal( 1052 const MutexAutoLock& aProofOfMapLock, const SampleTime& aSampleTime) { 1053 ClippedCompositionBoundsMap clippedCompBounds; 1054 bool activeAnimations = false; 1055 for (const auto& mapping : mApzcMap) { 1056 AsyncPanZoomController* apzc = mapping.second.apzc; 1057 // Note that this call is recursive, but it early-exits if called again 1058 // with the same guid. So this loop is still amortized O(n) with respect to 1059 // the number of APZCs. 1060 ParentLayerRect clippedBounds = ComputeClippedCompositionBounds( 1061 aProofOfMapLock, clippedCompBounds, mapping.first); 1062 1063 apzc->ReportCheckerboard(aSampleTime, clippedBounds); 1064 activeAnimations |= apzc->AdvanceAnimations(aSampleTime); 1065 } 1066 return activeAnimations; 1067 } 1068 1069 void APZCTreeManager::PrintLayerInfo(const ScrollNode& aLayer) { 1070 if (StaticPrefs::apz_printtree() && aLayer.Dump(mApzcTreeLog) > 0) { 1071 mApzcTreeLog << "\n"; 1072 } 1073 } 1074 1075 // mTreeLock is held, and checked with static analysis 1076 void APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode, 1077 HitTestingTreeNode* aParent, 1078 HitTestingTreeNode* aNextSibling) { 1079 if (aNextSibling) { 1080 aNextSibling->SetPrevSibling(aNode); 1081 } else if (aParent) { 1082 aParent->SetLastChild(aNode); 1083 } else { 1084 MOZ_ASSERT(!mRootNode); 1085 mRootNode = aNode; 1086 aNode->MakeRoot(); 1087 } 1088 } 1089 1090 already_AddRefed<HitTestingTreeNode> APZCTreeManager::RecycleOrCreateNode( 1091 const RecursiveMutexAutoLock& aProofOfTreeLock, TreeBuildingState& aState, 1092 AsyncPanZoomController* aApzc, LayersId aLayersId) { 1093 // Find a node without an APZC and return it. Note that unless the layer tree 1094 // actually changes, this loop should generally do an early-return on the 1095 // first iteration, so it should be cheap in the common case. 1096 for (int32_t i = aState.mNodesToDestroy.Length() - 1; i >= 0; i--) { 1097 RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i]; 1098 if (node->IsRecyclable(aProofOfTreeLock)) { 1099 aState.mNodesToDestroy.RemoveElementAt(i); 1100 node->RecycleWith(aProofOfTreeLock, aApzc, aLayersId); 1101 return node.forget(); 1102 } 1103 } 1104 RefPtr<HitTestingTreeNode> node = 1105 new HitTestingTreeNode(aApzc, false, aLayersId); 1106 return node.forget(); 1107 } 1108 1109 void APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid, 1110 const AsyncDragMetrics& aDragMetrics) { 1111 if (!APZThreadUtils::IsControllerThread()) { 1112 APZThreadUtils::RunOnControllerThread( 1113 NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>( 1114 "layers::APZCTreeManager::StartScrollbarDrag", this, 1115 &APZCTreeManager::StartScrollbarDrag, aGuid, aDragMetrics)); 1116 return; 1117 } 1118 1119 APZThreadUtils::AssertOnControllerThread(); 1120 1121 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid); 1122 if (!apzc) { 1123 NotifyScrollbarDragRejected(aGuid); 1124 return; 1125 } 1126 1127 uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber; 1128 mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics); 1129 } 1130 1131 bool APZCTreeManager::StartAutoscroll(const ScrollableLayerGuid& aGuid, 1132 const ScreenPoint& aAnchorLocation) { 1133 APZThreadUtils::AssertOnControllerThread(); 1134 1135 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid); 1136 if (!apzc) { 1137 if (XRE_IsGPUProcess()) { 1138 // If we're in the compositor process, the "return false" will be 1139 // ignored because the query comes over the PAPZCTreeManager protocol 1140 // via an async message. In this case, send an explicit rejection 1141 // message to content. 1142 NotifyAutoscrollRejected(aGuid); 1143 } 1144 return false; 1145 } 1146 1147 apzc->StartAutoscroll(aAnchorLocation); 1148 return true; 1149 } 1150 1151 void APZCTreeManager::StopAutoscroll(const ScrollableLayerGuid& aGuid) { 1152 APZThreadUtils::AssertOnControllerThread(); 1153 1154 if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) { 1155 apzc->StopAutoscroll(); 1156 } 1157 } 1158 1159 void APZCTreeManager::NotifyScrollbarDragInitiated( 1160 uint64_t aDragBlockId, const ScrollableLayerGuid& aGuid, 1161 ScrollDirection aDirection) const { 1162 RefPtr<GeckoContentController> controller = 1163 GetContentController(aGuid.mLayersId); 1164 if (controller) { 1165 controller->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aGuid.mScrollId, 1166 aDirection); 1167 } 1168 } 1169 1170 void APZCTreeManager::NotifyScrollbarDragRejected( 1171 const ScrollableLayerGuid& aGuid) const { 1172 RefPtr<GeckoContentController> controller = 1173 GetContentController(aGuid.mLayersId); 1174 if (controller) { 1175 controller->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId); 1176 } 1177 } 1178 1179 void APZCTreeManager::NotifyAutoscrollRejected( 1180 const ScrollableLayerGuid& aGuid) const { 1181 RefPtr<GeckoContentController> controller = 1182 GetContentController(aGuid.mLayersId); 1183 MOZ_ASSERT(controller); 1184 controller->NotifyAsyncAutoscrollRejected(aGuid.mScrollId); 1185 } 1186 1187 void SetHitTestData(HitTestingTreeNode* aNode, 1188 const WebRenderScrollDataWrapper& aLayer, 1189 const EventRegionsOverride& aOverrideFlags) { 1190 aNode->SetHitTestData(aLayer.GetVisibleRect(), aLayer.GetRemoteDocumentSize(), 1191 aLayer.GetTransformTyped(), aOverrideFlags, 1192 aLayer.GetAsyncZoomContainerId()); 1193 } 1194 1195 HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer( 1196 const RecursiveMutexAutoLock& aProofOfTreeLock, const ScrollNode& aLayer, 1197 const FrameMetrics& aMetrics, LayersId aLayersId, 1198 const Maybe<ZoomConstraints>& aZoomConstraints, 1199 const AncestorTransform& aAncestorTransform, HitTestingTreeNode* aParent, 1200 HitTestingTreeNode* aNextSibling, TreeBuildingState& aState) { 1201 mTreeLock.AssertCurrentThreadIn(); // for static analysis 1202 bool needsApzc = true; 1203 if (!aMetrics.IsScrollable()) { 1204 needsApzc = false; 1205 } 1206 1207 // XXX: As a future optimization we can probably stick these things on the 1208 // TreeBuildingState, and update them as we change layers id during the 1209 // traversal 1210 RefPtr<GeckoContentController> geckoContentController; 1211 CompositorBridgeParent::CallWithIndirectShadowTree( 1212 aLayersId, [&](LayerTreeState& lts) -> void { 1213 geckoContentController = lts.mController; 1214 }); 1215 1216 if (!geckoContentController) { 1217 needsApzc = false; 1218 } 1219 1220 if (Maybe<uint64_t> zoomAnimationId = aLayer.GetZoomAnimationId()) { 1221 aState.mZoomAnimationId = zoomAnimationId; 1222 } 1223 1224 RefPtr<HitTestingTreeNode> node = nullptr; 1225 if (!needsApzc) { 1226 // Note: if layer properties must be propagated to nodes, RecvUpdate in 1227 // LayerTransactionParent.cpp must ensure that APZ will be notified 1228 // when those properties change. 1229 node = RecycleOrCreateNode(aProofOfTreeLock, aState, nullptr, aLayersId); 1230 AttachNodeToTree(node, aParent, aNextSibling); 1231 SetHitTestData(node, aLayer, aState.mOverrideFlags.top()); 1232 node->SetScrollbarData(aLayer.GetScrollbarAnimationId(), 1233 aLayer.GetScrollbarData()); 1234 node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId(), 1235 aLayer.GetFixedPositionSides(), 1236 aLayer.GetFixedPositionAnimationId()); 1237 node->SetStickyPosData(aLayer.GetStickyScrollContainerId(), 1238 aLayer.GetStickyScrollRangeOuter(), 1239 aLayer.GetStickyScrollRangeInner(), 1240 aLayer.GetStickyPositionAnimationId()); 1241 PrintLayerInfo(aLayer); 1242 return node; 1243 } 1244 1245 RefPtr<AsyncPanZoomController> apzc; 1246 // If we get here, aLayer is a scrollable layer and somebody 1247 // has registered a GeckoContentController for it, so we need to ensure 1248 // it has an APZC instance to manage its scrolling. 1249 1250 // aState.mApzcMap allows reusing the exact same APZC instance for different 1251 // layers with the same FrameMetrics data. This is needed because in some 1252 // cases content that is supposed to scroll together is split into multiple 1253 // layers because of e.g. non-scrolling content interleaved in z-index order. 1254 ScrollableLayerGuid guid(aLayersId, aMetrics.GetPresShellId(), 1255 aMetrics.GetScrollId()); 1256 auto insertResult = aState.mApzcMap.insert(std::make_pair( 1257 guid, 1258 ApzcMapData{static_cast<AsyncPanZoomController*>(nullptr), Nothing()})); 1259 if (!insertResult.second) { 1260 apzc = insertResult.first->second.apzc; 1261 PrintLayerInfo(aLayer); 1262 } 1263 APZCTM_LOG( 1264 "Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n", 1265 apzc.get(), aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId); 1266 1267 aState.mUpdatedLayersIds.push_back(aLayersId); 1268 1269 // If we haven't encountered a layer already with the same metrics, then we 1270 // need to do the full reuse-or-make-an-APZC algorithm, which is contained 1271 // inside the block below. 1272 if (apzc == nullptr) { 1273 apzc = aLayer.GetApzc(); 1274 1275 // If the content represented by the scrollable layer has changed (which may 1276 // be possible because of DLBI heuristics) then we don't want to keep using 1277 // the same old APZC for the new content. Also, when reparenting a tab into 1278 // a new window a layer might get moved to a different layer tree with a 1279 // different APZCTreeManager. In these cases we don't want to reuse the same 1280 // APZC, so null it out so we run through the code to find another one or 1281 // create one. 1282 if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) { 1283 apzc = nullptr; 1284 } 1285 1286 // See if we can find an APZC from the previous tree that matches the 1287 // ScrollableLayerGuid from this layer. If there is one, then we know that 1288 // the layout of the page changed causing the layer tree to be rebuilt, but 1289 // the underlying content for the APZC is still there somewhere. Therefore, 1290 // we want to find the APZC instance and continue using it here. 1291 // 1292 // We particularly want to find the primary-holder node from the previous 1293 // tree that matches, because we don't want that node to get destroyed. If 1294 // it does get destroyed, then the APZC will get destroyed along with it by 1295 // definition, but we want to keep that APZC around in the new tree. 1296 // We leave non-primary-holder nodes in the destroy list because we don't 1297 // care about those nodes getting destroyed. 1298 for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) { 1299 RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i]; 1300 if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) { 1301 node = n; 1302 if (apzc != nullptr) { 1303 // If there is an APZC already then it should match the one from the 1304 // old primary-holder node 1305 MOZ_ASSERT(apzc == node->GetApzc()); 1306 } 1307 apzc = node->GetApzc(); 1308 break; 1309 } 1310 } 1311 1312 // The APZC we get off the layer may have been destroyed previously if the 1313 // layer was inactive or omitted from the layer tree for whatever reason 1314 // from a layers update. If it later comes back it will have a reference to 1315 // a destroyed APZC and so we need to throw that out and make a new one. 1316 bool newApzc = (apzc == nullptr || apzc->IsDestroyed()); 1317 if (newApzc) { 1318 apzc = NewAPZCInstance(aLayersId, geckoContentController); 1319 apzc->SetCompositorController(aState.mCompositorController.get()); 1320 MOZ_ASSERT(node == nullptr); 1321 node = new HitTestingTreeNode(apzc, true, aLayersId); 1322 } else { 1323 // If we are re-using a node for this layer clear the tree pointers 1324 // so that it doesn't continue pointing to nodes that might no longer 1325 // be in the tree. These pointers will get reset properly as we continue 1326 // building the tree. Also remove it from the set of nodes that are going 1327 // to be destroyed, because it's going to remain active. 1328 aState.mNodesToDestroy.RemoveElement(node); 1329 node->SetPrevSibling(nullptr); 1330 node->SetLastChild(nullptr); 1331 } 1332 1333 if (aMetrics.IsRootContent()) { 1334 apzc->SetZoomAnimationId(aState.mZoomAnimationId); 1335 aState.mZoomAnimationId = Nothing(); 1336 } 1337 1338 APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRIx64 1339 " %" PRId64 "\n", 1340 apzc.get(), aLayer.GetLayer(), uint64_t(aLayersId), 1341 aMetrics.GetScrollId()); 1342 1343 apzc->NotifyLayersUpdated( 1344 aLayer.Metadata(), AsyncPanZoomController::LayersUpdateFlags{ 1345 .mIsFirstPaint = aLayer.IsFirstPaint(), 1346 .mThisLayerTreeUpdated = 1347 (aLayersId == aState.mOriginatingLayersId)}); 1348 1349 // Since this is the first time we are encountering an APZC with this guid, 1350 // the node holding it must be the primary holder. It may be newly-created 1351 // or not, depending on whether it went through the newApzc branch above. 1352 MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && 1353 node->GetApzc()->Matches(guid)); 1354 1355 SetHitTestData(node, aLayer, aState.mOverrideFlags.top()); 1356 apzc->SetAncestorTransform(aAncestorTransform); 1357 1358 PrintLayerInfo(aLayer); 1359 1360 // Bind the APZC instance into the tree of APZCs 1361 AttachNodeToTree(node, aParent, aNextSibling); 1362 1363 // For testing, log the parent scroll id of every APZC that has a 1364 // parent. This allows test code to reconstruct the APZC tree. 1365 // Note that we currently only do this for APZCs in the layer tree 1366 // that originated the update, because the only identifying information 1367 // we are logging about APZCs is the scroll id, and otherwise we could 1368 // confuse APZCs from different layer trees with the same scroll id. 1369 if (aLayersId == aState.mOriginatingLayersId) { 1370 if (apzc->HasNoParentWithSameLayersId()) { 1371 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), 1372 "hasNoParentWithSameLayersId", true); 1373 } else { 1374 MOZ_ASSERT(apzc->GetParent()); 1375 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), 1376 "parentScrollId", 1377 apzc->GetParent()->GetGuid().mScrollId); 1378 } 1379 if (aMetrics.IsRootContent()) { 1380 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "isRootContent", 1381 true); 1382 } 1383 // Note that the async scroll offset is in ParentLayer pixels 1384 aState.mPaintLogger.LogTestData( 1385 aMetrics.GetScrollId(), "asyncScrollOffset", 1386 apzc->GetCurrentAsyncScrollOffset( 1387 AsyncPanZoomController::eForEventHandling)); 1388 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), 1389 "hasAsyncKeyScrolled", 1390 apzc->TestHasAsyncKeyScrolled()); 1391 } 1392 1393 // We must update the zoom constraints even if the apzc isn't new because it 1394 // might have moved. 1395 if (node->IsPrimaryHolder()) { 1396 if (aZoomConstraints) { 1397 apzc->UpdateZoomConstraints(*aZoomConstraints); 1398 1399 #ifdef DEBUG 1400 auto it = mZoomConstraints.find(guid); 1401 if (it != mZoomConstraints.end()) { 1402 MOZ_ASSERT(it->second == *aZoomConstraints); 1403 } 1404 } else { 1405 // We'd like to assert these things (if the first doesn't hold then at 1406 // least the second) but neither are not true because xul root content 1407 // gets zoomable zoom constraints, but which is not zoomable because it 1408 // doesn't have a root scroll frame. 1409 // clang-format off 1410 // MOZ_ASSERT(mZoomConstraints.find(guid) == mZoomConstraints.end()); 1411 // auto it = mZoomConstraints.find(guid); 1412 // if (it != mZoomConstraints.end()) { 1413 // MOZ_ASSERT(!it->second.mAllowZoom && !it->second.mAllowDoubleTapZoom); 1414 // } 1415 // clang-format on 1416 #endif 1417 } 1418 } 1419 1420 // Add a guid -> APZC mapping for the newly created APZC. 1421 insertResult.first->second.apzc = apzc; 1422 } else { 1423 // We already built an APZC earlier in this tree walk, but we have another 1424 // layer now that will also be using that APZC. The hit-test region on the 1425 // APZC needs to be updated to deal with the new layer's hit region. 1426 1427 node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId); 1428 AttachNodeToTree(node, aParent, aNextSibling); 1429 1430 // Even though different layers associated with a given APZC may be at 1431 // different levels in the layer tree (e.g. one being an uncle of another), 1432 // we require from Layout that the CSS transforms up to their common 1433 // ancestor be roughly the same. There are cases in which the transforms 1434 // are not exactly the same, for example if the parent is container layer 1435 // for an opacity, and this container layer has a resolution-induced scale 1436 // as its base transform and a prescale that is supposed to undo that scale. 1437 // Due to floating point inaccuracies those transforms can end up not quite 1438 // canceling each other. That's why we're using a fuzzy comparison here 1439 // instead of an exact one. 1440 // In addition, two ancestor transforms are allowed to differ if one of 1441 // them contains a perspective transform component and the other does not. 1442 // This represents situations where some content in a scrollable frame 1443 // is subject to a perspective transform and other content does not. 1444 // In such cases, go with the one that does not include the perspective 1445 // component; the perspective transform is remembered and applied to the 1446 // children instead. 1447 auto ancestorTransform = aAncestorTransform.CombinedTransform(); 1448 auto existingAncestorTransform = apzc->GetAncestorTransform(); 1449 if (!ancestorTransform.FuzzyEqualsMultiplicative( 1450 existingAncestorTransform)) { 1451 typedef TreeBuildingState::DeferredTransformMap::value_type PairType; 1452 if (!aAncestorTransform.ContainsPerspectiveTransform() && 1453 !apzc->AncestorTransformContainsPerspective()) { 1454 // If this content is being presented in a paginated fashion (e.g. 1455 // print preview), the multiple layers may reflect multiple instances 1456 // of the same display item being rendered on different pages. In such 1457 // cases, it's expected that different instances can have different 1458 // transforms, since each page renders a different part of the item. 1459 if (!aLayer.Metadata().IsPaginatedPresentation()) { 1460 if (ancestorTransform.IsFinite() && 1461 existingAncestorTransform.IsFinite()) { 1462 // Log separately from the assert because assert doesn't allow 1463 // printf-style arguments, but it's important for debugging that the 1464 // log identifies *which* scroll frame violated the condition. 1465 MOZ_LOG(sLog, LogLevel::Error, 1466 ("Two layers that scroll together have different ancestor " 1467 "transforms (guid=%s)", 1468 ToString(apzc->GetGuid()).c_str())); 1469 MOZ_ASSERT(false, 1470 "Two layers that scroll together have different " 1471 "ancestor transforms"); 1472 } else { 1473 MOZ_ASSERT(ancestorTransform.IsFinite() == 1474 existingAncestorTransform.IsFinite()); 1475 } 1476 } 1477 } else if (!aAncestorTransform.ContainsPerspectiveTransform()) { 1478 aState.mPerspectiveTransformsDeferredToChildren.insert( 1479 PairType{apzc, apzc->GetAncestorTransformPerspective()}); 1480 apzc->SetAncestorTransform(aAncestorTransform); 1481 } else { 1482 aState.mPerspectiveTransformsDeferredToChildren.insert( 1483 PairType{apzc, aAncestorTransform.GetPerspectiveTransform()}); 1484 } 1485 } 1486 1487 SetHitTestData(node, aLayer, aState.mOverrideFlags.top()); 1488 } 1489 1490 // Note: if layer properties must be propagated to nodes, RecvUpdate in 1491 // LayerTransactionParent.cpp must ensure that APZ will be notified 1492 // when those properties change. 1493 node->SetScrollbarData(aLayer.GetScrollbarAnimationId(), 1494 aLayer.GetScrollbarData()); 1495 node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId(), 1496 aLayer.GetFixedPositionSides(), 1497 aLayer.GetFixedPositionAnimationId()); 1498 node->SetStickyPosData(aLayer.GetStickyScrollContainerId(), 1499 aLayer.GetStickyScrollRangeOuter(), 1500 aLayer.GetStickyScrollRangeInner(), 1501 aLayer.GetStickyPositionAnimationId()); 1502 return node; 1503 } 1504 1505 template <typename PanGestureOrScrollWheelInput> 1506 static bool WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput) { 1507 if (!XRE_IsParentProcess() || !NS_IsMainThread()) { 1508 return true; 1509 } 1510 1511 WidgetWheelEvent wheelEvent = aPanInput.ToWidgetEvent(nullptr); 1512 return APZInputBridge::ActionForWheelEvent(&wheelEvent).isSome(); 1513 } 1514 1515 /*static*/ 1516 void APZCTreeManager::FlushApzRepaints(LayersId aLayersId) { 1517 // Previously, paints were throttled and therefore this method was used to 1518 // ensure any pending paints were flushed. Now, paints are flushed 1519 // immediately, so it is safe to simply send a notification now. 1520 APZCTM_LOG("Flushing repaints for layers id 0x%" PRIx64 "\n", 1521 uint64_t(aLayersId)); 1522 RefPtr<GeckoContentController> controller = GetContentController(aLayersId); 1523 #ifndef MOZ_WIDGET_ANDROID 1524 // On Android, this code is run in production and may actually get a nullptr 1525 // controller here. On other platforms this code is test-only and should never 1526 // get a nullptr. 1527 MOZ_ASSERT(controller); 1528 #endif 1529 if (controller) { 1530 controller->DispatchToRepaintThread(NewRunnableMethod( 1531 "layers::GeckoContentController::NotifyFlushComplete", controller, 1532 &GeckoContentController::NotifyFlushComplete)); 1533 } 1534 } 1535 1536 void APZCTreeManager::MarkAsDetached(LayersId aLayersId) { 1537 RecursiveMutexAutoLock lock(mTreeLock); 1538 mDetachedLayersIds.insert(aLayersId); 1539 } 1540 1541 static bool HasNonLockModifier(Modifiers aModifiers) { 1542 return (aModifiers & 1543 (MODIFIER_ALT | MODIFIER_ALTGRAPH | MODIFIER_CONTROL | MODIFIER_FN | 1544 MODIFIER_META | MODIFIER_SHIFT | MODIFIER_SYMBOL)) != 0; 1545 } 1546 1547 APZEventResult APZCTreeManager::ReceiveInputEvent( 1548 InputData& aEvent, InputBlockCallback&& aCallback) { 1549 APZThreadUtils::AssertOnControllerThread(); 1550 InputHandlingState state{aEvent}; 1551 1552 // Use a RAII class for updating the focus sequence number of this event 1553 AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent); 1554 1555 switch (aEvent.mInputType) { 1556 case MULTITOUCH_INPUT: { 1557 MultiTouchInput& touchInput = aEvent.AsMultiTouchInput(); 1558 ProcessTouchInput(state, touchInput); 1559 break; 1560 } 1561 case MOUSE_INPUT: { 1562 MouseInput& mouseInput = aEvent.AsMouseInput(); 1563 MOZ_LOG(APZCTreeManager::sLog, 1564 mouseInput.mType == MouseInput::MOUSE_MOVE ? LogLevel::Verbose 1565 : LogLevel::Debug, 1566 ("Received mouse input type %d at %s\n", (int)mouseInput.mType, 1567 ToString(mouseInput.mOrigin).c_str())); 1568 mouseInput.mHandledByAPZ = true; 1569 1570 SetCurrentMousePosition(mouseInput.mOrigin); 1571 1572 bool startsDrag = DragTracker::StartsDrag(mouseInput); 1573 if (startsDrag) { 1574 // If this is the start of a drag we need to unambiguously know if it's 1575 // going to land on a scrollbar or not. We can't apply an untransform 1576 // here without knowing that, so we need to ensure the untransform is 1577 // a no-op. 1578 FlushRepaintsToClearScreenToGeckoTransform(); 1579 } 1580 const bool endsDrag = DragTracker::EndsDrag(mouseInput); 1581 if (endsDrag) { 1582 mDragBlockHitResult = HitTestResult(); 1583 } 1584 1585 state.mHit = GetTargetAPZCForMouseInput(mouseInput); 1586 1587 bool hitScrollbar = (bool)state.mHit.mScrollbarNode; 1588 if (startsDrag && hitScrollbar) { 1589 RecursiveMutexAutoLock lock(mTreeLock); 1590 mDragBlockHitResult = mHitTester->CloneHitTestResult(lock, state.mHit); 1591 } 1592 1593 // When the mouse is outside the window we still want to handle dragging 1594 // but we won't find an APZC. Fallback to root APZC then. 1595 { // scope lock 1596 RecursiveMutexAutoLock lock(mTreeLock); 1597 if (!state.mHit.mTargetApzc && mRootNode) { 1598 state.mHit.mTargetApzc = mRootNode->GetApzc(); 1599 } 1600 } 1601 1602 if (state.mHit.mTargetApzc) { 1603 if (StaticPrefs::apz_test_logging_enabled() && 1604 mouseInput.mType == MouseInput::MOUSE_HITTEST) { 1605 ScrollableLayerGuid guid = state.mHit.mTargetApzc->GetGuid(); 1606 1607 MutexAutoLock lock(mTestDataLock); 1608 auto it = mTestData.find(guid.mLayersId); 1609 MOZ_ASSERT(it != mTestData.end()); 1610 it->second->RecordHitResult(mouseInput.mOrigin, state.mHit.mHitResult, 1611 guid.mLayersId, guid.mScrollId); 1612 } 1613 1614 TargetConfirmationFlags confFlags{state.mHit.mHitResult}; 1615 state.mResult = mInputQueue->ReceiveInputEvent(state.mHit.mTargetApzc, 1616 confFlags, mouseInput); 1617 1618 // If we're starting an async scrollbar drag 1619 bool apzDragEnabled = StaticPrefs::apz_drag_enabled(); 1620 if (apzDragEnabled && startsDrag && state.mHit.mScrollbarNode && 1621 state.mHit.mScrollbarNode->IsScrollThumbNode() && 1622 state.mHit.mScrollbarNode->GetScrollbarData() 1623 .mThumbIsAsyncDraggable) { 1624 SetupScrollbarDrag(mouseInput, state.mHit.mScrollbarNode, 1625 state.mHit.mTargetApzc.get()); 1626 } 1627 1628 if (state.mResult.GetStatus() == nsEventStatus_eConsumeDoDefault) { 1629 // This input event is part of a drag block, so whether or not it is 1630 // directed at a scrollbar depends on whether the drag block started 1631 // on a scrollbar. 1632 hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar); 1633 } 1634 1635 if (!hitScrollbar) { 1636 // The input was not targeted at a scrollbar, so we untransform it 1637 // like we do for other content. Scrollbars are "special" because they 1638 // have special handling in AsyncCompositionManager when resolution is 1639 // applied. TODO: we should find a better way to deal with this. 1640 ScreenToParentLayerMatrix4x4 transformToApzc = 1641 GetScreenToApzcTransform(state.mHit.mTargetApzc); 1642 ParentLayerToScreenMatrix4x4 transformToGecko = 1643 GetApzcToGeckoTransformForHit(state.mHit); 1644 ScreenToScreenMatrix4x4 outTransform = 1645 transformToApzc * transformToGecko; 1646 Maybe<ScreenPoint> untransformedRefPoint = 1647 UntransformBy(outTransform, mouseInput.mOrigin); 1648 if (untransformedRefPoint) { 1649 mouseInput.mOrigin = *untransformedRefPoint; 1650 } 1651 } else { 1652 // Likewise, if the input was targeted at a scrollbar, we don't want 1653 // to apply the callback transform in the main thread, so we remove 1654 // the scrollid from the guid. We need to keep the layersId intact so 1655 // that the response from the child process doesn't get discarded. 1656 state.mResult.mTargetGuid.mScrollId = 1657 ScrollableLayerGuid::NULL_SCROLL_ID; 1658 } 1659 } 1660 break; 1661 } 1662 case SCROLLWHEEL_INPUT: { 1663 FlushRepaintsToClearScreenToGeckoTransform(); 1664 1665 // Do this before early return for Fission hit testing. 1666 ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput(); 1667 APZCTM_LOG("Received wheel input at %s with delta (%f, %f)\n", 1668 ToString(wheelInput.mOrigin).c_str(), wheelInput.mDeltaX, 1669 wheelInput.mDeltaY); 1670 state.mHit = GetTargetAPZC(wheelInput.mOrigin); 1671 1672 wheelInput.mHandledByAPZ = WillHandleInput(wheelInput); 1673 if (!wheelInput.mHandledByAPZ) { 1674 return state.Finish(*this, std::move(aCallback)); 1675 } 1676 1677 if (state.mHit.mTargetApzc) { 1678 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit); 1679 1680 if (wheelInput.mAPZAction == APZWheelAction::PinchZoom) { 1681 // The mousewheel may have hit a subframe, but we want to send the 1682 // pinch-zoom events to the root-content APZC. 1683 { 1684 RecursiveMutexAutoLock lock(mTreeLock); 1685 state.mHit.mTargetApzc = FindRootContentApzcForLayersId( 1686 state.mHit.mTargetApzc->GetLayersId()); 1687 } 1688 if (state.mHit.mTargetApzc) { 1689 SynthesizePinchGestureFromMouseWheel(wheelInput, 1690 state.mHit.mTargetApzc); 1691 } 1692 state.mResult.SetStatusAsConsumeNoDefault(); 1693 return state.Finish(*this, std::move(aCallback)); 1694 } 1695 1696 MOZ_ASSERT(wheelInput.mAPZAction == APZWheelAction::Scroll); 1697 1698 // For wheel events, the call to ReceiveInputEvent below may result in 1699 // scrolling, which changes the async transform. However, the event we 1700 // want to pass to gecko should be the pre-scroll event coordinates, 1701 // transformed into the gecko space. (pre-scroll because the mouse 1702 // cursor is stationary during wheel scrolling, unlike touchmove 1703 // events). Since we just flushed the pending repaints the transform to 1704 // gecko space should only consist of overscroll-cancelling transforms. 1705 ScreenToScreenMatrix4x4 transformToGecko = 1706 GetScreenToApzcTransform(state.mHit.mTargetApzc) * 1707 GetApzcToGeckoTransformForHit(state.mHit); 1708 Maybe<ScreenPoint> untransformedOrigin = 1709 UntransformBy(transformToGecko, wheelInput.mOrigin); 1710 1711 if (!untransformedOrigin) { 1712 return state.Finish(*this, std::move(aCallback)); 1713 } 1714 1715 state.mResult = mInputQueue->ReceiveInputEvent( 1716 state.mHit.mTargetApzc, 1717 TargetConfirmationFlags{state.mHit.mHitResult}, wheelInput); 1718 1719 // Update the out-parameters so they are what the caller expects. 1720 wheelInput.mOrigin = *untransformedOrigin; 1721 } 1722 break; 1723 } 1724 case PANGESTURE_INPUT: { 1725 FlushRepaintsToClearScreenToGeckoTransform(); 1726 1727 // Do this before early return for Fission hit testing. 1728 PanGestureInput& panInput = aEvent.AsPanGestureInput(); 1729 APZCTM_LOG("Received pan gesture input type %d at %s with delta %s\n", 1730 (int)panInput.mType, ToString(panInput.mPanStartPoint).c_str(), 1731 ToString(panInput.mPanDisplacement).c_str()); 1732 state.mHit = GetTargetAPZC(panInput.mPanStartPoint); 1733 1734 panInput.mHandledByAPZ = WillHandleInput(panInput); 1735 if (!panInput.mHandledByAPZ) { 1736 if (mInputQueue->GetCurrentPanGestureBlock()) { 1737 if (state.mHit.mTargetApzc && 1738 (panInput.mType == PanGestureInput::PANGESTURE_END || 1739 panInput.mType == PanGestureInput::PANGESTURE_CANCELLED)) { 1740 // If we've already been processing a pan gesture in an APZC but 1741 // fall into this _if_ branch, which means this pan-end or 1742 // pan-cancelled event will not be proccessed in the APZC, send a 1743 // pan-interrupted event to stop any on-going work for the pan 1744 // gesture, otherwise we will get stuck at an intermidiate state 1745 // becasue we might no longer receive any events which will be 1746 // handled by the APZC. 1747 PanGestureInput panInterrupted( 1748 PanGestureInput::PANGESTURE_INTERRUPTED, panInput.mTimeStamp, 1749 panInput.mPanStartPoint, panInput.mPanDisplacement, 1750 panInput.modifiers); 1751 (void)mInputQueue->ReceiveInputEvent( 1752 state.mHit.mTargetApzc, 1753 TargetConfirmationFlags{state.mHit.mHitResult}, panInterrupted); 1754 } 1755 } 1756 return state.Finish(*this, std::move(aCallback)); 1757 } 1758 1759 // If/when we enable support for pan inputs off-main-thread, we'll need 1760 // to duplicate this EventStateManager code or something. See the call to 1761 // GetUserPrefsForWheelEvent in APZInputBridge.cpp for why these fields 1762 // are stored separately. 1763 MOZ_ASSERT(NS_IsMainThread()); 1764 WidgetWheelEvent wheelEvent = panInput.ToWidgetEvent(nullptr); 1765 EventStateManager::GetUserPrefsForWheelEvent( 1766 &wheelEvent, &panInput.mUserDeltaMultiplierX, 1767 &panInput.mUserDeltaMultiplierY); 1768 1769 if (state.mHit.mTargetApzc) { 1770 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit); 1771 1772 // For pan gesture events, the call to ReceiveInputEvent below may 1773 // result in scrolling, which changes the async transform. However, the 1774 // event we want to pass to gecko should be the pre-scroll event 1775 // coordinates, transformed into the gecko space. (pre-scroll because 1776 // the mouse cursor is stationary during pan gesture scrolling, unlike 1777 // touchmove events). Since we just flushed the pending repaints the 1778 // transform to gecko space should only consist of overscroll-cancelling 1779 // transforms. 1780 ScreenToScreenMatrix4x4 transformToGecko = 1781 GetScreenToApzcTransform(state.mHit.mTargetApzc) * 1782 GetApzcToGeckoTransformForHit(state.mHit); 1783 Maybe<ScreenPoint> untransformedStartPoint = 1784 UntransformBy(transformToGecko, panInput.mPanStartPoint); 1785 Maybe<ScreenPoint> untransformedDisplacement = 1786 UntransformVector(transformToGecko, panInput.mPanDisplacement, 1787 panInput.mPanStartPoint); 1788 1789 if (!untransformedStartPoint || !untransformedDisplacement) { 1790 return state.Finish(*this, std::move(aCallback)); 1791 } 1792 1793 panInput.mOverscrollBehaviorAllowsSwipe = 1794 state.mHit.mTargetApzc->OverscrollBehaviorAllowsSwipe(); 1795 1796 state.mResult = mInputQueue->ReceiveInputEvent( 1797 state.mHit.mTargetApzc, 1798 TargetConfirmationFlags{state.mHit.mHitResult}, panInput); 1799 1800 // Update the out-parameters so they are what the caller expects. 1801 panInput.mPanStartPoint = *untransformedStartPoint; 1802 panInput.mPanDisplacement = *untransformedDisplacement; 1803 } 1804 break; 1805 } 1806 case PINCHGESTURE_INPUT: { 1807 PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput(); 1808 if (HasNonLockModifier(pinchInput.modifiers)) { 1809 APZCTM_LOG("Discarding pinch input due to modifiers 0x%x\n", 1810 pinchInput.modifiers); 1811 return state.Finish(*this, std::move(aCallback)); 1812 } 1813 1814 state.mHit = GetTargetAPZC(pinchInput.mFocusPoint); 1815 1816 // We always handle pinch gestures as pinch zooms. 1817 pinchInput.mHandledByAPZ = true; 1818 1819 if (state.mHit.mTargetApzc) { 1820 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit); 1821 1822 if (!state.mHit.mTargetApzc->IsRootContent()) { 1823 state.mHit.mTargetApzc = FindZoomableApzc(state.mHit.mTargetApzc); 1824 } 1825 } 1826 1827 if (state.mHit.mTargetApzc) { 1828 ScreenToScreenMatrix4x4 outTransform = 1829 GetScreenToApzcTransform(state.mHit.mTargetApzc) * 1830 GetApzcToGeckoTransformForHit(state.mHit); 1831 Maybe<ScreenPoint> untransformedFocusPoint = 1832 UntransformBy(outTransform, pinchInput.mFocusPoint); 1833 1834 if (!untransformedFocusPoint) { 1835 return state.Finish(*this, std::move(aCallback)); 1836 } 1837 1838 state.mResult = mInputQueue->ReceiveInputEvent( 1839 state.mHit.mTargetApzc, 1840 TargetConfirmationFlags{state.mHit.mHitResult}, pinchInput); 1841 1842 // Update the out-parameters so they are what the caller expects. 1843 pinchInput.mFocusPoint = *untransformedFocusPoint; 1844 } 1845 break; 1846 } 1847 case TAPGESTURE_INPUT: { // note: no one currently sends these 1848 TapGestureInput& tapInput = aEvent.AsTapGestureInput(); 1849 state.mHit = GetTargetAPZC(tapInput.mPoint); 1850 1851 if (state.mHit.mTargetApzc) { 1852 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit); 1853 1854 ScreenToScreenMatrix4x4 outTransform = 1855 GetScreenToApzcTransform(state.mHit.mTargetApzc) * 1856 GetApzcToGeckoTransformForHit(state.mHit); 1857 Maybe<ScreenIntPoint> untransformedPoint = 1858 UntransformBy(outTransform, tapInput.mPoint); 1859 1860 if (!untransformedPoint) { 1861 return state.Finish(*this, std::move(aCallback)); 1862 } 1863 1864 // Tap gesture events are not grouped into input blocks, and they're 1865 // never queued in InputQueue, but processed right away. So, we only 1866 // need to set |mTapGestureHitResult| for the duration of the 1867 // InputQueue::ReceiveInputEvent() call. 1868 { 1869 RecursiveMutexAutoLock lock(mTreeLock); 1870 mTapGestureHitResult = 1871 mHitTester->CloneHitTestResult(lock, state.mHit); 1872 } 1873 1874 state.mResult = mInputQueue->ReceiveInputEvent( 1875 state.mHit.mTargetApzc, 1876 TargetConfirmationFlags{state.mHit.mHitResult}, tapInput); 1877 1878 mTapGestureHitResult = HitTestResult(); 1879 1880 // Update the out-parameters so they are what the caller expects. 1881 tapInput.mPoint = *untransformedPoint; 1882 } 1883 break; 1884 } 1885 case KEYBOARD_INPUT: { 1886 // Disable async keyboard scrolling when accessibility.browsewithcaret is 1887 // enabled 1888 if (!StaticPrefs::apz_keyboard_enabled_AtStartup() || 1889 StaticPrefs::accessibility_browsewithcaret()) { 1890 APZ_KEY_LOG("Skipping key input from invalid prefs\n"); 1891 return state.Finish(*this, std::move(aCallback)); 1892 } 1893 1894 KeyboardInput& keyInput = aEvent.AsKeyboardInput(); 1895 1896 // Try and find a matching shortcut for this keyboard input 1897 Maybe<KeyboardShortcut> shortcut = mKeyboardMap.FindMatch(keyInput); 1898 1899 if (!shortcut) { 1900 APZ_KEY_LOG("Skipping key input with no shortcut\n"); 1901 1902 // If we don't have a shortcut for this key event, then we can keep our 1903 // focus only if we know there are no key event listeners for this 1904 // target 1905 if (mFocusState.CanIgnoreKeyboardShortcutMisses()) { 1906 focusSetter.MarkAsNonFocusChanging(); 1907 } 1908 return state.Finish(*this, std::move(aCallback)); 1909 } 1910 1911 // Check if this shortcut needs to be dispatched to content. Anything 1912 // matching this is assumed to be able to change focus. 1913 if (shortcut->mDispatchToContent) { 1914 APZ_KEY_LOG("Skipping key input with dispatch-to-content shortcut\n"); 1915 return state.Finish(*this, std::move(aCallback)); 1916 } 1917 1918 // We know we have an action to execute on whatever is the current focus 1919 // target 1920 const KeyboardScrollAction& action = shortcut->mAction; 1921 1922 // The current focus target depends on which direction the scroll is to 1923 // happen 1924 Maybe<ScrollableLayerGuid> targetGuid; 1925 switch (action.mType) { 1926 case KeyboardScrollAction::eScrollCharacter: { 1927 targetGuid = mFocusState.GetHorizontalTarget(); 1928 break; 1929 } 1930 case KeyboardScrollAction::eScrollLine: 1931 case KeyboardScrollAction::eScrollPage: 1932 case KeyboardScrollAction::eScrollComplete: { 1933 targetGuid = mFocusState.GetVerticalTarget(); 1934 break; 1935 } 1936 } 1937 1938 // If we don't have a scroll target then either we have a stale focus 1939 // target, the focused element has event listeners, or the focused element 1940 // doesn't have a layerized scroll frame. In any case we need to dispatch 1941 // to content. 1942 if (!targetGuid) { 1943 APZ_KEY_LOG("Skipping key input with no current focus target\n"); 1944 return state.Finish(*this, std::move(aCallback)); 1945 } 1946 1947 RefPtr<AsyncPanZoomController> targetApzc = 1948 GetTargetAPZC(targetGuid->mLayersId, targetGuid->mScrollId); 1949 1950 if (!targetApzc) { 1951 APZ_KEY_LOG("Skipping key input with focus target but no APZC\n"); 1952 return state.Finish(*this, std::move(aCallback)); 1953 } 1954 1955 // Attach the keyboard scroll action to the input event for processing 1956 // by the input queue. 1957 keyInput.mAction = action; 1958 1959 APZ_KEY_LOG("Dispatching key input with apzc=%p\n", targetApzc.get()); 1960 1961 // Dispatch the event to the input queue. 1962 state.mResult = mInputQueue->ReceiveInputEvent( 1963 targetApzc, TargetConfirmationFlags{true}, keyInput); 1964 1965 // Any keyboard event that is dispatched to the input queue at this point 1966 // should have been consumed 1967 MOZ_ASSERT(state.mResult.GetStatus() == nsEventStatus_eConsumeDoDefault || 1968 state.mResult.GetStatus() == nsEventStatus_eConsumeNoDefault); 1969 1970 keyInput.mHandledByAPZ = true; 1971 focusSetter.MarkAsNonFocusChanging(); 1972 1973 break; 1974 } 1975 } 1976 return state.Finish(*this, std::move(aCallback)); 1977 } 1978 1979 static TouchBehaviorFlags ConvertToTouchBehavior( 1980 const CompositorHitTestInfo& info) { 1981 TouchBehaviorFlags result = AllowedTouchBehavior::UNKNOWN; 1982 if (info == CompositorHitTestInvisibleToHit) { 1983 result = AllowedTouchBehavior::NONE; 1984 } else if (info.contains(CompositorHitTestFlags::eIrregularArea)) { 1985 // Note that eApzAwareListeners and eInactiveScrollframe are similar 1986 // to eIrregularArea in some respects, but are not relevant for the 1987 // purposes of this function, which deals specifically with touch-action. 1988 result = AllowedTouchBehavior::UNKNOWN; 1989 } else { 1990 result = AllowedTouchBehavior::VERTICAL_PAN | 1991 AllowedTouchBehavior::HORIZONTAL_PAN | 1992 AllowedTouchBehavior::PINCH_ZOOM | 1993 AllowedTouchBehavior::ANIMATING_ZOOM; 1994 if (info.contains(CompositorHitTestFlags::eTouchActionPanXDisabled)) { 1995 result &= ~AllowedTouchBehavior::HORIZONTAL_PAN; 1996 } 1997 if (info.contains(CompositorHitTestFlags::eTouchActionPanYDisabled)) { 1998 result &= ~AllowedTouchBehavior::VERTICAL_PAN; 1999 } 2000 if (info.contains(CompositorHitTestFlags::eTouchActionPinchZoomDisabled)) { 2001 result &= ~AllowedTouchBehavior::PINCH_ZOOM; 2002 } 2003 if (info.contains( 2004 CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled)) { 2005 result &= ~AllowedTouchBehavior::ANIMATING_ZOOM; 2006 } 2007 } 2008 return result; 2009 } 2010 2011 APZCTreeManager::HitTestResult APZCTreeManager::GetTouchInputBlockAPZC( 2012 const MultiTouchInput& aEvent, 2013 nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors) { 2014 HitTestResult hit; 2015 if (aEvent.mTouches.Length() == 0) { 2016 return hit; 2017 } 2018 2019 FlushRepaintsToClearScreenToGeckoTransform(); 2020 2021 hit = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint); 2022 // Don't set a layers id on multi-touch events. 2023 if (aEvent.mTouches.Length() != 1) { 2024 hit.mLayersId = LayersId{0}; 2025 } 2026 2027 aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hit.mHitResult)); 2028 for (size_t i = 1; i < aEvent.mTouches.Length(); i++) { 2029 HitTestResult hit2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint); 2030 aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hit2.mHitResult)); 2031 hit.mTargetApzc = GetZoomableTarget(hit.mTargetApzc, hit2.mTargetApzc); 2032 APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", 2033 hit.mTargetApzc.get()); 2034 // A multi-touch gesture will not be a scrollbar drag, even if the 2035 // first touch point happened to hit a scrollbar. 2036 hit.mScrollbarNode.Clear(); 2037 2038 // XXX we should probably be combining the hit results from the different 2039 // touch points somehow, instead of just using the last one. 2040 hit.mHitResult = hit2.mHitResult; 2041 } 2042 2043 return hit; 2044 } 2045 2046 APZEventResult APZCTreeManager::InputHandlingState::Finish( 2047 APZCTreeManager& aTreeManager, InputBlockCallback&& aCallback) { 2048 // The validity check here handles both the case where mHit was 2049 // never populated (because this event did not trigger a hit-test), 2050 // and the case where it was populated with an invalid LayersId 2051 // (which can happen e.g. for multi-touch events). 2052 if (mHit.mLayersId.IsValid()) { 2053 mEvent.mLayersId = mHit.mLayersId; 2054 } 2055 2056 if (mEvent.mInputType == SCROLLWHEEL_INPUT || 2057 mEvent.mInputType == PANGESTURE_INPUT) { 2058 aTreeManager.MaybeOverrideLayersIdForWheelEvent(mEvent); 2059 } 2060 2061 // Absorb events that are in targetted at a position in the gutter, 2062 // unless they are fixed position elements. 2063 if (mHit.mHitOverscrollGutter && mHit.mFixedPosSides == SideBits::eNone) { 2064 mResult.SetStatusAsConsumeNoDefault(); 2065 } 2066 2067 // If the event will have a delayed result then add the callback to the 2068 // APZCTreeManager. 2069 if (aCallback && mResult.WillHaveDelayedResult()) { 2070 aTreeManager.AddInputBlockCallback(mResult.mInputBlockId, 2071 std::move(aCallback)); 2072 } 2073 2074 return mResult; 2075 } 2076 2077 void APZCTreeManager::ProcessTouchInput(InputHandlingState& aState, 2078 MultiTouchInput& aInput) { 2079 APZCTM_LOG("Received touch input type %d with touch points [%s]\n", 2080 (int)aInput.mType, 2081 [&] { 2082 nsCString result; 2083 for (const auto& touch : aInput.mTouches) { 2084 result.AppendPrintf("%s", 2085 ToString(touch.mScreenPoint).c_str()); 2086 } 2087 return result; 2088 }() 2089 .get()); 2090 2091 aInput.mHandledByAPZ = true; 2092 nsTArray<TouchBehaviorFlags> touchBehaviors; 2093 HitTestingTreeNodeAutoLock hitScrollbarNode; 2094 if (aInput.mType == MultiTouchInput::MULTITOUCH_START) { 2095 // If we are panned into overscroll and a second finger goes down, 2096 // ignore that second touch point completely. The touch-start for it is 2097 // dropped completely; subsequent touch events until the touch-end for it 2098 // will have this touch point filtered out. 2099 // (By contrast, if we're in overscroll but not panning, such as after 2100 // putting two fingers down during an overscroll animation, we process the 2101 // second touch and proceed to pinch.) 2102 if (mTouchBlockHitResult.mTargetApzc && 2103 mTouchBlockHitResult.mTargetApzc->IsInPanningState() && 2104 BuildOverscrollHandoffChain(mTouchBlockHitResult.mTargetApzc) 2105 ->HasOverscrolledApzc()) { 2106 if (mRetainedTouchIdentifier == -1) { 2107 mRetainedTouchIdentifier = 2108 mTouchBlockHitResult.mTargetApzc->GetLastTouchIdentifier(); 2109 } 2110 2111 aState.mResult.SetStatusAsConsumeNoDefault(); 2112 return; 2113 } 2114 2115 aState.mHit = GetTouchInputBlockAPZC(aInput, &touchBehaviors); 2116 RecursiveMutexAutoLock lock(mTreeLock); 2117 // Repopulate mTouchBlockHitResult from the input state. 2118 mTouchBlockHitResult = mHitTester->CloneHitTestResult(lock, aState.mHit); 2119 hitScrollbarNode = std::move(aState.mHit.mScrollbarNode); 2120 2121 // Check if this event starts a scrollbar touch-drag. The conditions 2122 // checked are similar to the ones we check for MOUSE_INPUT starting 2123 // a scrollbar mouse-drag. 2124 mInScrollbarTouchDrag = 2125 StaticPrefs::apz_drag_enabled() && 2126 StaticPrefs::apz_drag_touch_enabled() && hitScrollbarNode && 2127 hitScrollbarNode->IsScrollThumbNode() && 2128 hitScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable; 2129 2130 MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length()); 2131 for (size_t i = 0; i < touchBehaviors.Length(); i++) { 2132 APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n", 2133 touchBehaviors[i]); 2134 if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) { 2135 // If there's any unknown items in the list, throw it out and we'll 2136 // wait for the main thread to send us a notification. 2137 touchBehaviors.Clear(); 2138 break; 2139 } 2140 } 2141 } else if (mTouchBlockHitResult.mTargetApzc) { 2142 APZCTM_LOG("Re-using APZC %p as continuation of event block\n", 2143 mTouchBlockHitResult.mTargetApzc.get()); 2144 RecursiveMutexAutoLock lock(mTreeLock); 2145 aState.mHit = mHitTester->CloneHitTestResult(lock, mTouchBlockHitResult); 2146 } 2147 2148 if (mInScrollbarTouchDrag) { 2149 aState.mResult = ProcessTouchInputForScrollbarDrag( 2150 aInput, hitScrollbarNode, mTouchBlockHitResult.mHitResult); 2151 } else { 2152 // If we receive a touch-cancel, it means all touches are finished, so we 2153 // can stop ignoring any that we were ignoring. 2154 if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) { 2155 mRetainedTouchIdentifier = -1; 2156 } 2157 2158 // If we are currently ignoring any touch points, filter them out from the 2159 // set of touch points included in this event. Note that we modify aInput 2160 // itself, so that the touch points are also filtered out when the caller 2161 // passes the event on to content. 2162 if (mRetainedTouchIdentifier != -1) { 2163 for (size_t j = 0; j < aInput.mTouches.Length(); ++j) { 2164 if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) { 2165 aInput.mTouches.RemoveElementAt(j); 2166 if (!touchBehaviors.IsEmpty()) { 2167 MOZ_ASSERT(touchBehaviors.Length() > j); 2168 touchBehaviors.RemoveElementAt(j); 2169 } 2170 --j; 2171 } 2172 } 2173 if (aInput.mTouches.IsEmpty()) { 2174 aState.mResult.SetStatusAsConsumeNoDefault(); 2175 return; 2176 } 2177 } 2178 2179 if (mTouchBlockHitResult.mTargetApzc) { 2180 MOZ_ASSERT(mTouchBlockHitResult.mHitResult != 2181 CompositorHitTestInvisibleToHit); 2182 2183 aState.mResult = mInputQueue->ReceiveInputEvent( 2184 mTouchBlockHitResult.mTargetApzc, 2185 TargetConfirmationFlags{mTouchBlockHitResult.mHitResult}, aInput, 2186 touchBehaviors.IsEmpty() ? Nothing() 2187 : Some(std::move(touchBehaviors))); 2188 2189 // For computing the event to pass back to Gecko, use up-to-date 2190 // transforms (i.e. not anything cached in an input block). This ensures 2191 // that transformToApzc and transformToGecko are in sync. 2192 // Note: we are not using ConvertToGecko() here, because we don't 2193 // want to multiply transformToApzc and transformToGecko once 2194 // for each touch point. 2195 ScreenToParentLayerMatrix4x4 transformToApzc = 2196 GetScreenToApzcTransform(mTouchBlockHitResult.mTargetApzc); 2197 ParentLayerToScreenMatrix4x4 transformToGecko = 2198 GetApzcToGeckoTransformForHit(mTouchBlockHitResult); 2199 ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko; 2200 2201 for (size_t i = 0; i < aInput.mTouches.Length(); i++) { 2202 SingleTouchData& touchData = aInput.mTouches[i]; 2203 Maybe<ScreenIntPoint> untransformedScreenPoint = 2204 UntransformBy(outTransform, touchData.mScreenPoint); 2205 if (!untransformedScreenPoint) { 2206 aState.mResult.SetStatusAsIgnore(); 2207 return; 2208 } 2209 touchData.mScreenPoint = *untransformedScreenPoint; 2210 AdjustEventPointForDynamicToolbar(touchData.mScreenPoint, 2211 mTouchBlockHitResult); 2212 } 2213 } 2214 } 2215 2216 mTouchCounter.Update(aInput); 2217 2218 // If it's the end of the touch sequence then clear out variables so we 2219 // don't keep dangling references and leak things. 2220 if (mTouchCounter.GetActiveTouchCount() == 0) { 2221 mTouchBlockHitResult = HitTestResult(); 2222 mRetainedTouchIdentifier = -1; 2223 mInScrollbarTouchDrag = false; 2224 } 2225 } 2226 2227 void APZCTreeManager::AdjustEventPointForDynamicToolbar( 2228 ScreenIntPoint& aEventPoint, const HitTestResult& aHit) { 2229 if (aHit.mFixedPosSides != SideBits::eNone) { 2230 MutexAutoLock lock(mMapLock); 2231 aEventPoint -= RoundedToInt(ComputeFixedMarginsOffset( 2232 lock, aHit.mFixedPosSides, mGeckoFixedLayerMargins)); 2233 } else if (aHit.mNode && aHit.mNode->GetStickyPositionAnimationId()) { 2234 SideBits sideBits = SideBits::eNone; 2235 { 2236 RecursiveMutexAutoLock lock(mTreeLock); 2237 sideBits = SidesStuckToRootContent( 2238 aHit.mNode.Get(lock), AsyncTransformConsumer::eForEventHandling); 2239 } 2240 MutexAutoLock lock(mMapLock); 2241 aEventPoint -= RoundedToInt( 2242 ComputeFixedMarginsOffset(lock, sideBits, mGeckoFixedLayerMargins)); 2243 } 2244 } 2245 2246 static MouseInput::MouseType MultiTouchTypeToMouseType( 2247 MultiTouchInput::MultiTouchType aType) { 2248 switch (aType) { 2249 case MultiTouchInput::MULTITOUCH_START: 2250 return MouseInput::MOUSE_DOWN; 2251 case MultiTouchInput::MULTITOUCH_MOVE: 2252 return MouseInput::MOUSE_MOVE; 2253 case MultiTouchInput::MULTITOUCH_END: 2254 case MultiTouchInput::MULTITOUCH_CANCEL: 2255 return MouseInput::MOUSE_UP; 2256 } 2257 MOZ_ASSERT_UNREACHABLE("Invalid multi-touch type"); 2258 return MouseInput::MOUSE_NONE; 2259 } 2260 2261 APZEventResult APZCTreeManager::ProcessTouchInputForScrollbarDrag( 2262 MultiTouchInput& aTouchInput, 2263 const HitTestingTreeNodeAutoLock& aScrollThumbNode, 2264 const gfx::CompositorHitTestInfo& aHitInfo) { 2265 MOZ_ASSERT(mRetainedTouchIdentifier == -1); 2266 MOZ_ASSERT(mTouchBlockHitResult.mTargetApzc); 2267 MOZ_ASSERT(aTouchInput.mTouches.Length() == 1); 2268 2269 // Synthesize a mouse event based on the touch event, so that we can 2270 // reuse code in InputQueue and APZC for handling scrollbar mouse-drags. 2271 MouseInput mouseInput{MultiTouchTypeToMouseType(aTouchInput.mType), 2272 MouseInput::PRIMARY_BUTTON, 2273 dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH, 2274 MouseButtonsFlag::ePrimaryFlag, 2275 aTouchInput.mTouches[0].mScreenPoint, 2276 aTouchInput.mTimeStamp, 2277 aTouchInput.modifiers}; 2278 mouseInput.mHandledByAPZ = true; 2279 2280 TargetConfirmationFlags targetConfirmed{aHitInfo}; 2281 APZEventResult result; 2282 result = mInputQueue->ReceiveInputEvent(mTouchBlockHitResult.mTargetApzc, 2283 targetConfirmed, mouseInput); 2284 2285 // |aScrollThumbNode| is non-null iff. this is the event that starts the drag. 2286 // If so, set up the drag. 2287 if (aScrollThumbNode) { 2288 SetupScrollbarDrag(mouseInput, aScrollThumbNode, 2289 mTouchBlockHitResult.mTargetApzc.get()); 2290 } 2291 2292 // Since the input was targeted at a scrollbar: 2293 // - The original touch event (which will be sent on to content) will 2294 // not be untransformed. 2295 // - We don't want to apply the callback transform in the main thread, 2296 // so we remove the scrollid from the guid. 2297 // Both of these match the behaviour of mouse events that target a scrollbar; 2298 // see the code for handling mouse events in ReceiveInputEvent() for 2299 // additional explanation. 2300 result.mTargetGuid.mScrollId = ScrollableLayerGuid::NULL_SCROLL_ID; 2301 2302 return result; 2303 } 2304 2305 void APZCTreeManager::SetupScrollbarDrag( 2306 MouseInput& aMouseInput, const HitTestingTreeNodeAutoLock& aScrollThumbNode, 2307 AsyncPanZoomController* aApzc) { 2308 DragBlockState* dragBlock = mInputQueue->GetCurrentDragBlock(); 2309 if (!dragBlock) { 2310 return; 2311 } 2312 2313 const ScrollbarData& thumbData = aScrollThumbNode->GetScrollbarData(); 2314 MOZ_ASSERT(thumbData.mDirection.isSome()); 2315 2316 // Record the thumb's position at the start of the drag. 2317 // We snap back to this position if, during the drag, the mouse 2318 // gets sufficiently far away from the scrollbar. 2319 dragBlock->SetInitialThumbPos(thumbData.mThumbStart); 2320 2321 // Under some conditions, we can confirm the drag block right away. 2322 // Otherwise, we have to wait for a main-thread confirmation. 2323 if (/* check that the scrollbar's target scroll frame is layerized */ 2324 aScrollThumbNode->GetScrollTargetId() == aApzc->GetGuid().mScrollId && 2325 !aApzc->IsScrollInfoLayer()) { 2326 uint64_t dragBlockId = dragBlock->GetBlockId(); 2327 // AsyncPanZoomController::HandleInputEvent() will call 2328 // TransformToLocal() on the event, but we need its mLocalOrigin now 2329 // to compute a drag start offset for the AsyncDragMetrics. 2330 aMouseInput.TransformToLocal(aApzc->GetTransformToThis()); 2331 OuterCSSCoord dragStart = 2332 aApzc->ConvertScrollbarPoint(aMouseInput.mLocalOrigin, thumbData); 2333 // ConvertScrollbarPoint() got the drag start offset relative to 2334 // the scroll track. Now get it relative to the thumb. 2335 // ScrollThumbData::mThumbStart stores the offset of the thumb 2336 // relative to the scroll track at the time of the last paint. 2337 // Since that paint, the thumb may have acquired an async transform 2338 // due to async scrolling, so look that up and apply it. 2339 LayerToParentLayerMatrix4x4 thumbTransform; 2340 { 2341 RecursiveMutexAutoLock lock(mTreeLock); 2342 thumbTransform = 2343 ComputeTransformForScrollThumbNode(aScrollThumbNode.Get(lock)); 2344 } 2345 // Only consider the translation, since we do not support both 2346 // zooming and scrollbar dragging on any platform. 2347 // FIXME: We do now. 2348 OuterCSSCoord thumbStart = 2349 thumbData.mThumbStart + 2350 ((*thumbData.mDirection == ScrollDirection::eHorizontal) 2351 ? thumbTransform._41 2352 : thumbTransform._42); 2353 dragStart -= thumbStart; 2354 2355 // Content can't prevent scrollbar dragging with preventDefault(), 2356 // so we don't need to wait for a content response. It's important 2357 // to do this before calling ConfirmDragBlock() since that can 2358 // potentially process and consume the block. 2359 dragBlock->SetContentResponse(false); 2360 2361 NotifyScrollbarDragInitiated(dragBlockId, aApzc->GetGuid(), 2362 *thumbData.mDirection); 2363 2364 mInputQueue->ConfirmDragBlock( 2365 dragBlockId, aApzc, 2366 AsyncDragMetrics(aApzc->GetGuid().mScrollId, 2367 aApzc->GetGuid().mPresShellId, dragBlockId, dragStart, 2368 *thumbData.mDirection)); 2369 } 2370 } 2371 2372 void APZCTreeManager::SynthesizePinchGestureFromMouseWheel( 2373 const ScrollWheelInput& aWheelInput, 2374 const RefPtr<AsyncPanZoomController>& aTarget) { 2375 MOZ_ASSERT(aTarget); 2376 2377 ScreenPoint focusPoint = aWheelInput.mOrigin; 2378 2379 // Compute span values based on the wheel delta. 2380 ScreenCoord oldSpan = 100; 2381 ScreenCoord newSpan = oldSpan + aWheelInput.mDeltaY; 2382 2383 // There's no ambiguity as to the target for pinch gesture events. 2384 TargetConfirmationFlags confFlags{true}; 2385 2386 PinchGestureInput pinchStart{PinchGestureInput::PINCHGESTURE_START, 2387 PinchGestureInput::MOUSEWHEEL, 2388 aWheelInput.mTimeStamp, 2389 ExternalPoint(0, 0), 2390 focusPoint, 2391 oldSpan, 2392 oldSpan, 2393 aWheelInput.modifiers}; 2394 PinchGestureInput pinchScale1{PinchGestureInput::PINCHGESTURE_SCALE, 2395 PinchGestureInput::MOUSEWHEEL, 2396 aWheelInput.mTimeStamp, 2397 ExternalPoint(0, 0), 2398 focusPoint, 2399 oldSpan, 2400 oldSpan, 2401 aWheelInput.modifiers}; 2402 PinchGestureInput pinchScale2{PinchGestureInput::PINCHGESTURE_SCALE, 2403 PinchGestureInput::MOUSEWHEEL, 2404 aWheelInput.mTimeStamp, 2405 ExternalPoint(0, 0), 2406 focusPoint, 2407 oldSpan, 2408 newSpan, 2409 aWheelInput.modifiers}; 2410 PinchGestureInput pinchEnd{PinchGestureInput::PINCHGESTURE_END, 2411 PinchGestureInput::MOUSEWHEEL, 2412 aWheelInput.mTimeStamp, 2413 ExternalPoint(0, 0), 2414 focusPoint, 2415 newSpan, 2416 newSpan, 2417 aWheelInput.modifiers}; 2418 2419 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchStart); 2420 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchScale1); 2421 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchScale2); 2422 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchEnd); 2423 } 2424 2425 void APZCTreeManager::MaybeOverrideLayersIdForWheelEvent(InputData& aEvent) { 2426 APZThreadUtils::AssertOnControllerThread(); 2427 2428 InputBlockState* txn = nullptr; 2429 if (aEvent.mInputType == SCROLLWHEEL_INPUT && 2430 aEvent.AsScrollWheelInput().mHandledByAPZ) { 2431 txn = mInputQueue->GetActiveWheelTransaction(); 2432 } else if (aEvent.mInputType == PANGESTURE_INPUT && 2433 aEvent.AsPanGestureInput().mHandledByAPZ) { 2434 txn = mInputQueue->GetCurrentPanGestureBlock(); 2435 } 2436 2437 APZCTM_LOG("Maybe override txn (0x%p) wheel transactions enabled=%d", txn, 2438 StaticPrefs::dom_event_wheel_event_groups_enabled()); 2439 2440 // If we're in a wheel transaction, subsequent events in the transaction 2441 // should be sent to the same content process as the first event, even 2442 // if content rendered by a different process has scrolled under the 2443 // cursor. 2444 if (!txn || !StaticPrefs::dom_event_wheel_event_groups_enabled()) { 2445 return; 2446 } 2447 2448 Maybe<LayersId> layersId = txn->WheelTransactionLayersId(); 2449 2450 APZCTM_LOG("Maybe override layers id (%s) -> (%s)", 2451 ToString(aEvent.mLayersId).c_str(), ToString(layersId).c_str()); 2452 2453 if (layersId.isSome() && *layersId != LayersId{0}) { 2454 aEvent.mLayersId = *layersId; 2455 } 2456 } 2457 2458 void APZCTreeManager::UpdateWheelTransaction( 2459 LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage, 2460 const Maybe<ScrollableLayerGuid>& aTargetGuid) { 2461 APZThreadUtils::AssertOnControllerThread(); 2462 2463 WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction(); 2464 if (!txn) { 2465 return; 2466 } 2467 2468 // If the transaction has simply timed out, we don't need to do anything 2469 // else. 2470 if (txn->MaybeTimeout(TimeStamp::Now())) { 2471 return; 2472 } 2473 2474 switch (aEventMessage) { 2475 case eMouseMove: 2476 case eDragOver: { 2477 ScreenIntPoint point = ViewAs<ScreenPixel>( 2478 aRefPoint, 2479 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent); 2480 2481 txn->OnMouseMove(point, aTargetGuid); 2482 2483 return; 2484 } 2485 case eKeyPress: 2486 case eKeyUp: 2487 case eKeyDown: 2488 case eMouseUp: 2489 case eMouseDown: 2490 case eMouseDoubleClick: 2491 case ePointerAuxClick: 2492 case ePointerClick: 2493 case eContextMenu: 2494 case eDrop: 2495 txn->EndTransaction(); 2496 return; 2497 default: 2498 break; 2499 } 2500 } 2501 2502 void APZCTreeManager::ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint, 2503 ScrollableLayerGuid* aOutTargetGuid, 2504 uint64_t* aOutFocusSequenceNumber, 2505 LayersId* aOutLayersId) { 2506 APZThreadUtils::AssertOnControllerThread(); 2507 2508 // Transform the aRefPoint. 2509 // If the event hits an overscrolled APZC, instruct the caller to ignore it. 2510 PixelCastJustification LDIsScreen = 2511 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent; 2512 ScreenIntPoint refPointAsScreen = ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen); 2513 HitTestResult hit = GetTargetAPZC(refPointAsScreen); 2514 if (aOutLayersId) { 2515 *aOutLayersId = hit.mLayersId; 2516 } 2517 if (hit.mTargetApzc) { 2518 MOZ_ASSERT(hit.mHitResult != CompositorHitTestInvisibleToHit); 2519 hit.mTargetApzc->GetGuid(aOutTargetGuid); 2520 ScreenToParentLayerMatrix4x4 transformToApzc = 2521 GetScreenToApzcTransform(hit.mTargetApzc); 2522 ParentLayerToScreenMatrix4x4 transformToGecko = 2523 GetApzcToGeckoTransformForHit(hit); 2524 ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko; 2525 Maybe<ScreenIntPoint> untransformedRefPoint = 2526 UntransformBy(outTransform, refPointAsScreen); 2527 if (untransformedRefPoint) { 2528 *aRefPoint = 2529 ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen); 2530 } 2531 } 2532 2533 // Update the focus sequence number and attach it to the event 2534 mFocusState.ReceiveFocusChangingEvent(); 2535 *aOutFocusSequenceNumber = mFocusState.LastAPZProcessedEvent(); 2536 } 2537 2538 void APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap) { 2539 if (!APZThreadUtils::IsControllerThread()) { 2540 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<KeyboardMap>( 2541 "layers::APZCTreeManager::SetKeyboardMap", this, 2542 &APZCTreeManager::SetKeyboardMap, aKeyboardMap)); 2543 return; 2544 } 2545 2546 APZThreadUtils::AssertOnControllerThread(); 2547 2548 mKeyboardMap = aKeyboardMap; 2549 } 2550 2551 void APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid, 2552 const ZoomTarget& aZoomTarget, 2553 const uint32_t aFlags) { 2554 if (!APZThreadUtils::IsControllerThread()) { 2555 APZThreadUtils::RunOnControllerThread( 2556 NewRunnableMethod<ScrollableLayerGuid, ZoomTarget, uint32_t>( 2557 "layers::APZCTreeManager::ZoomToRect", this, 2558 &APZCTreeManager::ZoomToRect, aGuid, aZoomTarget, aFlags)); 2559 return; 2560 } 2561 2562 // We could probably move this to run on the updater thread if needed, but 2563 // either way we should restrict it to a single thread. For now let's use the 2564 // controller thread. 2565 APZThreadUtils::AssertOnControllerThread(); 2566 2567 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid); 2568 if (aFlags & ZOOM_TO_FOCUSED_INPUT) { 2569 // In the case of ZoomToFocusInput the targetRect is in the in-process root 2570 // content coordinates. We need to convert it to the top level content 2571 // coordiates. 2572 if (apzc) { 2573 CSSRect transformedRect = 2574 ConvertRectInApzcToRoot(apzc, aZoomTarget.targetRect); 2575 2576 transformedRect.Inflate(15.0f, 0.0f); 2577 // Note that ZoomToFocusedInput doesn't use the other fields of 2578 // ZoomTarget. 2579 ZoomTarget zoomTarget{transformedRect}; 2580 2581 apzc = FindZoomableApzc(apzc); 2582 if (apzc) { 2583 uint32_t flags = aFlags; 2584 MutexAutoLock lock(mMapLock); 2585 if (IsSoftwareKeyboardVisible(lock) && 2586 InteractiveWidgetMode(lock) == 2587 dom::InteractiveWidget::ResizesVisual) { 2588 flags |= ZOOM_TO_FOCUSED_INPUT_ON_RESIZES_VISUAL; 2589 } 2590 apzc->ZoomToRect(zoomTarget, flags); 2591 } 2592 } 2593 return; 2594 } 2595 2596 if (apzc) { 2597 apzc = FindZoomableApzc(apzc); 2598 if (apzc) { 2599 apzc->ZoomToRect(aZoomTarget, aFlags); 2600 } 2601 } 2602 } 2603 2604 void APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId, 2605 bool aPreventDefault) { 2606 if (!APZThreadUtils::IsControllerThread()) { 2607 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<uint64_t, bool>( 2608 "layers::APZCTreeManager::ContentReceivedInputBlock", this, 2609 &APZCTreeManager::ContentReceivedInputBlock, aInputBlockId, 2610 aPreventDefault)); 2611 return; 2612 } 2613 2614 APZThreadUtils::AssertOnControllerThread(); 2615 2616 mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); 2617 } 2618 2619 void APZCTreeManager::SetTargetAPZC( 2620 uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) { 2621 if (!APZThreadUtils::IsControllerThread()) { 2622 APZThreadUtils::RunOnControllerThread( 2623 NewRunnableMethod<uint64_t, 2624 StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>( 2625 "layers::APZCTreeManager::SetTargetAPZC", this, 2626 &layers::APZCTreeManager::SetTargetAPZC, aInputBlockId, 2627 aTargets.Clone())); 2628 return; 2629 } 2630 2631 RefPtr<AsyncPanZoomController> target = nullptr; 2632 if (aTargets.Length() > 0) { 2633 target = GetTargetAPZC(aTargets[0]); 2634 } 2635 for (size_t i = 1; i < aTargets.Length(); i++) { 2636 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]); 2637 target = GetZoomableTarget(target, apzc); 2638 } 2639 if (InputBlockState* block = mInputQueue->GetBlockForId(aInputBlockId)) { 2640 if (block->AsPinchGestureBlock() && aTargets.Length() == 1) { 2641 target = FindZoomableApzc(target); 2642 } 2643 } 2644 mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target); 2645 } 2646 2647 void APZCTreeManager::UpdateZoomConstraints( 2648 const ScrollableLayerGuid& aGuid, 2649 const Maybe<ZoomConstraints>& aConstraints) { 2650 if (!GetUpdater()->IsUpdaterThread()) { 2651 // This can happen if we're in the UI process and got a call directly from 2652 // nsIWidget or from a content process over PAPZCTreeManager. In that 2653 // case we get this call on the compositor thread, which may be different 2654 // from the updater thread. It can also happen in the GPU process if that is 2655 // enabled, since the call will go over PAPZCTreeManager and arrive on the 2656 // compositor thread in the GPU process. 2657 GetUpdater()->RunOnUpdaterThread( 2658 aGuid.mLayersId, 2659 NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>( 2660 "APZCTreeManager::UpdateZoomConstraints", this, 2661 &APZCTreeManager::UpdateZoomConstraints, aGuid, aConstraints)); 2662 return; 2663 } 2664 2665 AssertOnUpdaterThread(); 2666 2667 // Propagate the zoom constraints down to the subtree, stopping at APZCs 2668 // which have their own zoom constraints or are in a different layers id. 2669 if (aConstraints) { 2670 APZCTM_LOG("Recording constraints %s for guid %s\n", 2671 ToString(aConstraints.value()).c_str(), ToString(aGuid).c_str()); 2672 mZoomConstraints[aGuid] = aConstraints.ref(); 2673 } else { 2674 APZCTM_LOG("Removing constraints for guid %s\n", ToString(aGuid).c_str()); 2675 mZoomConstraints.erase(aGuid); 2676 } 2677 2678 RecursiveMutexAutoLock lock(mTreeLock); 2679 RefPtr<HitTestingTreeNode> node = DepthFirstSearchPostOrder<ReverseIterator>( 2680 mRootNode.get(), [&aGuid](HitTestingTreeNode* aNode) { 2681 bool matches = false; 2682 if (auto zoomId = aNode->GetAsyncZoomContainerId()) { 2683 matches = ScrollableLayerGuid::EqualsIgnoringPresShell( 2684 aGuid, ScrollableLayerGuid(aNode->GetLayersId(), 0, *zoomId)); 2685 } 2686 return matches; 2687 }); 2688 2689 // This does not hold because we can get zoom constraints updates before the 2690 // layer tree update with the async zoom container (I assume). 2691 // clang-format off 2692 // MOZ_ASSERT(node || aConstraints.isNothing() || 2693 // (!aConstraints->mAllowZoom && !aConstraints->mAllowDoubleTapZoom)); 2694 // clang-format on 2695 2696 // If there is no async zoom container then the zoom constraints should not 2697 // allow zooming and building the HTT should have handled clearing the zoom 2698 // constraints from all nodes so we don't have to handle doing anything in 2699 // case there is no async zoom container. 2700 2701 if (node && aConstraints) { 2702 ForEachNode<ReverseIterator>(node.get(), [&aConstraints, &node, &aGuid, 2703 this](HitTestingTreeNode* aNode) { 2704 if (aNode != node) { 2705 // don't go into other async zoom containers 2706 if (auto zoomId = aNode->GetAsyncZoomContainerId()) { 2707 MOZ_ASSERT(!ScrollableLayerGuid::EqualsIgnoringPresShell( 2708 aGuid, ScrollableLayerGuid(aNode->GetLayersId(), 0, *zoomId))); 2709 return TraversalFlag::Skip; 2710 } 2711 if (AsyncPanZoomController* childApzc = aNode->GetApzc()) { 2712 if (!ScrollableLayerGuid::EqualsIgnoringPresShell( 2713 aGuid, childApzc->GetGuid())) { 2714 // We can have subtrees with their own zoom constraints - leave 2715 // these alone. 2716 if (this->mZoomConstraints.find(childApzc->GetGuid()) != 2717 this->mZoomConstraints.end()) { 2718 return TraversalFlag::Skip; 2719 } 2720 } 2721 } 2722 } 2723 if (aNode->IsPrimaryHolder()) { 2724 MOZ_ASSERT(aNode->GetApzc()); 2725 aNode->GetApzc()->UpdateZoomConstraints(aConstraints.ref()); 2726 } 2727 return TraversalFlag::Continue; 2728 }); 2729 } 2730 } 2731 2732 void APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform() { 2733 // As the name implies, we flush repaint requests for the entire APZ tree in 2734 // order to clear the screen-to-gecko transform (aka the "untransform" applied 2735 // to incoming input events before they can be passed on to Gecko). 2736 // 2737 // The primary reason we do this is to avoid the problem where input events, 2738 // after being untransformed, end up hit-testing differently in Gecko. This 2739 // might happen in cases where the input event lands on content that is async- 2740 // scrolled into view, but Gecko still thinks it is out of view given the 2741 // visible area of a scrollframe. 2742 // 2743 // Another reason we want to clear the untransform is that if our APZ hit-test 2744 // hits a dispatch-to-content region then that's an ambiguous result and we 2745 // need to ask Gecko what actually got hit. In order to do this we need to 2746 // untransform the input event into Gecko space - but to do that we need to 2747 // know which APZC got hit! This leads to a circular dependency; the only way 2748 // to get out of it is to make sure that the untransform for all the possible 2749 // matched APZCs is the same. It is simplest to ensure that by flushing the 2750 // pending repaint requests, which makes all of the untransforms empty (and 2751 // therefore equal). 2752 RecursiveMutexAutoLock lock(mTreeLock); 2753 2754 ForEachNode<ReverseIterator>(mRootNode.get(), [](HitTestingTreeNode* aNode) { 2755 if (aNode->IsPrimaryHolder()) { 2756 MOZ_ASSERT(aNode->GetApzc()); 2757 aNode->GetApzc()->FlushRepaintForNewInputBlock(); 2758 } 2759 }); 2760 } 2761 2762 void APZCTreeManager::ClearTree() { 2763 AssertOnUpdaterThread(); 2764 2765 // Ensure that no references to APZCs are alive in any lingering input 2766 // blocks. This breaks cycles from InputBlockState::mTargetApzc back to 2767 // the InputQueue. 2768 APZThreadUtils::RunOnControllerThread(NewRunnableMethod( 2769 "layers::InputQueue::Clear", mInputQueue, &InputQueue::Clear)); 2770 2771 RecursiveMutexAutoLock lock(mTreeLock); 2772 2773 // Collect the nodes into a list, and then destroy each one. 2774 // We can't destroy them as we collect them, because ForEachNode() 2775 // does a pre-order traversal of the tree, and Destroy() nulls out 2776 // the fields needed to reach the children of the node. 2777 nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy; 2778 ForEachNode<ReverseIterator>(mRootNode.get(), 2779 [&nodesToDestroy](HitTestingTreeNode* aNode) { 2780 nodesToDestroy.AppendElement(aNode); 2781 }); 2782 2783 mRootContentApzcs.Clear(); 2784 for (size_t i = 0; i < nodesToDestroy.Length(); i++) { 2785 nodesToDestroy[i]->Destroy(); 2786 } 2787 mRootNode = nullptr; 2788 2789 { 2790 // Also remove references to APZC instances in the map 2791 MutexAutoLock lock(mMapLock); 2792 mApzcMap.clear(); 2793 } 2794 2795 RefPtr<APZCTreeManager> self(this); 2796 NS_DispatchToMainThread( 2797 NS_NewRunnableFunction("layers::APZCTreeManager::ClearTree", [self] { 2798 self->mFlushObserver->Unregister(); 2799 self->mFlushObserver = nullptr; 2800 })); 2801 } 2802 2803 RefPtr<HitTestingTreeNode> APZCTreeManager::GetRootNode() const { 2804 RecursiveMutexAutoLock lock(mTreeLock); 2805 return mRootNode; 2806 } 2807 2808 /** 2809 * Transform a displacement from the ParentLayer coordinates of a source APZC 2810 * to the ParentLayer coordinates of a target APZC. 2811 * @param aTreeManager the tree manager for the APZC tree containing |aSource| 2812 * and |aTarget| 2813 * @param aSource the source APZC 2814 * @param aTarget the target APZC 2815 * @param aStartPoint the start point of the displacement 2816 * @param aEndPoint the end point of the displacement 2817 * @return true on success, false if aStartPoint or aEndPoint cannot be 2818 * transformed into target's coordinate space 2819 */ 2820 static bool TransformDisplacement(APZCTreeManager* aTreeManager, 2821 AsyncPanZoomController* aSource, 2822 AsyncPanZoomController* aTarget, 2823 ParentLayerPoint& aStartPoint, 2824 ParentLayerPoint& aEndPoint) { 2825 if (aSource == aTarget) { 2826 return true; 2827 } 2828 2829 // Convert start and end points to Screen coordinates. 2830 ParentLayerToScreenMatrix4x4 untransformToApzc = 2831 aTreeManager->GetScreenToApzcTransform(aSource).Inverse(); 2832 ScreenPoint screenStart = TransformBy(untransformToApzc, aStartPoint); 2833 ScreenPoint screenEnd = TransformBy(untransformToApzc, aEndPoint); 2834 2835 // Convert start and end points to aTarget's ParentLayer coordinates. 2836 ScreenToParentLayerMatrix4x4 transformToApzc = 2837 aTreeManager->GetScreenToApzcTransform(aTarget); 2838 Maybe<ParentLayerPoint> startPoint = 2839 UntransformBy(transformToApzc, screenStart); 2840 Maybe<ParentLayerPoint> endPoint = UntransformBy(transformToApzc, screenEnd); 2841 if (!startPoint || !endPoint) { 2842 return false; 2843 } 2844 aEndPoint = *endPoint; 2845 aStartPoint = *startPoint; 2846 2847 return true; 2848 } 2849 2850 bool APZCTreeManager::DispatchScroll( 2851 AsyncPanZoomController* aPrev, ParentLayerPoint& aStartPoint, 2852 ParentLayerPoint& aEndPoint, 2853 OverscrollHandoffState& aOverscrollHandoffState) { 2854 const OverscrollHandoffChain& overscrollHandoffChain = 2855 aOverscrollHandoffState.mChain; 2856 uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex; 2857 RefPtr<AsyncPanZoomController> next; 2858 // If we have reached the end of the overscroll handoff chain, there is 2859 // nothing more to scroll, so we ignore the rest of the pan gesture. 2860 if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) { 2861 // Nothing more to scroll - ignore the rest of the pan gesture. 2862 return false; 2863 } 2864 2865 next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex); 2866 2867 if (next == nullptr || next->IsDestroyed()) { 2868 return false; 2869 } 2870 2871 // Convert the start and end points from |aPrev|'s coordinate space to 2872 // |next|'s coordinate space. 2873 if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) { 2874 return false; 2875 } 2876 2877 // Scroll |next|. If this causes overscroll, it will call DispatchScroll() 2878 // again with an incremented index. 2879 if (!next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState)) { 2880 // Transform |aStartPoint| and |aEndPoint| (which now represent the 2881 // portion of the displacement that wasn't consumed by APZCs later 2882 // in the handoff chain) back into |aPrev|'s coordinate space. This 2883 // allows the caller (which is |aPrev|) to interpret the unconsumed 2884 // displacement in its own coordinate space, and make use of it 2885 // (e.g. by going into overscroll). 2886 if (!TransformDisplacement(this, next, aPrev, aStartPoint, aEndPoint)) { 2887 NS_WARNING("Failed to untransform scroll points during dispatch"); 2888 } 2889 return false; 2890 } 2891 2892 // Return true to indicate the scroll was consumed entirely. 2893 return true; 2894 } 2895 2896 ParentLayerPoint APZCTreeManager::DispatchFling( 2897 AsyncPanZoomController* aPrev, const FlingHandoffState& aHandoffState) { 2898 // If immediate handoff is disallowed, do not allow handoff beyond the 2899 // single APZC that's scrolled by the input block that triggered this fling. 2900 if (aHandoffState.mIsHandoff && !StaticPrefs::apz_allow_immediate_handoff() && 2901 aHandoffState.mScrolledApzc == aPrev) { 2902 FLING_LOG("APZCTM dropping handoff due to disallowed immediate handoff\n"); 2903 return aHandoffState.mVelocity; 2904 } 2905 2906 const OverscrollHandoffChain* chain = aHandoffState.mChain; 2907 RefPtr<AsyncPanZoomController> current; 2908 uint32_t overscrollHandoffChainLength = chain->Length(); 2909 uint32_t startIndex; 2910 2911 // The fling's velocity needs to be transformed from the screen coordinates 2912 // of |aPrev| to the screen coordinates of |next|. To transform a velocity 2913 // correctly, we need to convert it to a displacement. For now, we do this 2914 // by anchoring it to a start point of (0, 0). 2915 // TODO: For this to be correct in the presence of 3D transforms, we should 2916 // use the end point of the touch that started the fling as the start point 2917 // rather than (0, 0). 2918 ParentLayerPoint startPoint; // (0, 0) 2919 ParentLayerPoint endPoint; 2920 2921 if (aHandoffState.mIsHandoff) { 2922 startIndex = chain->IndexOf(aPrev) + 1; 2923 2924 // IndexOf will return aOverscrollHandoffChain->Length() if 2925 // |aPrev| is not found. 2926 if (startIndex >= overscrollHandoffChainLength) { 2927 return aHandoffState.mVelocity; 2928 } 2929 } else { 2930 startIndex = 0; 2931 } 2932 2933 // This will store any velocity left over after the entire handoff. 2934 ParentLayerPoint finalResidualVelocity = aHandoffState.mVelocity; 2935 2936 ParentLayerPoint currentVelocity = aHandoffState.mVelocity; 2937 for (; startIndex < overscrollHandoffChainLength; startIndex++) { 2938 current = chain->GetApzcAtIndex(startIndex); 2939 2940 // Make sure the apzc about to be handled can be handled 2941 if (current == nullptr || current->IsDestroyed()) { 2942 break; 2943 } 2944 2945 endPoint = startPoint + currentVelocity; 2946 2947 RefPtr<AsyncPanZoomController> prevApzc = 2948 (startIndex > 0) ? chain->GetApzcAtIndex(startIndex - 1) : nullptr; 2949 2950 // Only transform when current apzc can be transformed with previous 2951 if (prevApzc) { 2952 if (!TransformDisplacement(this, prevApzc, current, startPoint, 2953 endPoint)) { 2954 break; 2955 } 2956 } 2957 2958 ParentLayerPoint availableVelocity = (endPoint - startPoint); 2959 ParentLayerPoint residualVelocity; 2960 2961 FlingHandoffState transformedHandoffState = aHandoffState; 2962 transformedHandoffState.mVelocity = availableVelocity; 2963 2964 // Obey overscroll-behavior. 2965 if (prevApzc) { 2966 residualVelocity += prevApzc->AdjustHandoffVelocityForOverscrollBehavior( 2967 transformedHandoffState.mVelocity); 2968 } 2969 2970 residualVelocity += current->AttemptFling(transformedHandoffState); 2971 2972 // If there's no residual velocity, there's nothing more to hand off. 2973 if (current->IsZero(residualVelocity)) { 2974 return ParentLayerPoint(); 2975 } 2976 2977 // If any of the velocity available to be handed off was consumed, 2978 // subtract the proportion of consumed velocity from finalResidualVelocity. 2979 // Note: it's important to compare |residualVelocity| to |availableVelocity| 2980 // here and not to |transformedHandoffState.mVelocity|, since the latter 2981 // may have been modified by AdjustHandoffVelocityForOverscrollBehavior(). 2982 if (!current->IsZero(availableVelocity.x - residualVelocity.x)) { 2983 finalResidualVelocity.x *= (residualVelocity.x / availableVelocity.x); 2984 } 2985 if (!current->IsZero(availableVelocity.y - residualVelocity.y)) { 2986 finalResidualVelocity.y *= (residualVelocity.y / availableVelocity.y); 2987 } 2988 2989 currentVelocity = residualVelocity; 2990 } 2991 2992 // Return any residual velocity left over after the entire handoff process. 2993 return finalResidualVelocity; 2994 } 2995 2996 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC( 2997 const ScrollableLayerGuid& aGuid) { 2998 RecursiveMutexAutoLock lock(mTreeLock); 2999 RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr); 3000 MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC 3001 RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr; 3002 return apzc.forget(); 3003 } 3004 3005 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC( 3006 const LayersId& aLayersId, 3007 const ScrollableLayerGuid::ViewID& aScrollId) const { 3008 MutexAutoLock lock(mMapLock); 3009 return GetTargetAPZC(aLayersId, aScrollId, lock); 3010 } 3011 3012 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC( 3013 const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, 3014 const MutexAutoLock& aProofOfMapLock) const { 3015 mMapLock.AssertCurrentThreadOwns(); 3016 ScrollableLayerGuid guid(aLayersId, 0, aScrollId); 3017 auto it = mApzcMap.find(guid); 3018 RefPtr<AsyncPanZoomController> apzc = 3019 (it != mApzcMap.end() ? it->second.apzc : nullptr); 3020 return apzc.forget(); 3021 } 3022 3023 already_AddRefed<HitTestingTreeNode> APZCTreeManager::GetTargetNode( 3024 const ScrollableLayerGuid& aGuid, GuidComparator aComparator) const { 3025 mTreeLock.AssertCurrentThreadIn(); 3026 RefPtr<HitTestingTreeNode> target = 3027 DepthFirstSearchPostOrder<ReverseIterator>( 3028 mRootNode.get(), [&aGuid, &aComparator](HitTestingTreeNode* node) { 3029 bool matches = false; 3030 if (node->GetApzc()) { 3031 if (aComparator) { 3032 matches = aComparator(aGuid, node->GetApzc()->GetGuid()); 3033 } else { 3034 matches = node->GetApzc()->Matches(aGuid); 3035 } 3036 } 3037 return matches; 3038 }); 3039 return target.forget(); 3040 } 3041 3042 APZCTreeManager::HitTestResult APZCTreeManager::GetTargetAPZC( 3043 const ScreenPoint& aPoint) { 3044 RecursiveMutexAutoLock lock(mTreeLock); 3045 MOZ_ASSERT(mHitTester); 3046 return mHitTester->GetAPZCAtPoint(aPoint, lock); 3047 } 3048 3049 APZCTreeManager::HitTestResult APZCTreeManager::GetTargetAPZCForMouseInput( 3050 const MouseInput& aMouseInput) { 3051 // If the mouse input isn't move or we are in a wheel transaction, 3052 // we need to do hit testing anyway. 3053 if (!StaticPrefs::apz_mousemove_hittest_optimization_enabled() || 3054 aMouseInput.mType != MouseInput::MOUSE_MOVE || 3055 mInputQueue->GetActiveWheelTransaction()) { 3056 return GetTargetAPZC(aMouseInput.mOrigin); 3057 } 3058 3059 // If we are in dragging state, reuse the original target without 3060 // hit-testing again. 3061 if (mDragBlockHitResult.mTargetApzc) { 3062 RecursiveMutexAutoLock lock(mTreeLock); 3063 return mHitTester->CloneHitTestResult(lock, mDragBlockHitResult); 3064 } 3065 3066 // If we have no OOP iframes, we don't need to do hit-testing again since 3067 // the layersId will never be changed. 3068 RecursiveMutexAutoLock lock(mTreeLock); 3069 if (!mHaveOOPIframes) { 3070 return HitTestResult{}; 3071 } 3072 return GetTargetAPZC(aMouseInput.mOrigin); 3073 } 3074 3075 APZCTreeManager::TargetApzcForNodeResult APZCTreeManager::FindHandoffParent( 3076 const AsyncPanZoomController* aApzc) { 3077 RefPtr<HitTestingTreeNode> node = GetTargetNode(aApzc->GetGuid(), nullptr); 3078 while (node) { 3079 auto result = GetTargetApzcForNode(node->GetParent()); 3080 if (result.mApzc) { 3081 // avoid infinite recursion in the overscroll handoff chain. 3082 if (result.mApzc != aApzc) { 3083 return result; 3084 } 3085 } 3086 node = node->GetParent(); 3087 } 3088 3089 return {nullptr, false}; 3090 } 3091 3092 RefPtr<const OverscrollHandoffChain> 3093 APZCTreeManager::BuildOverscrollHandoffChain( 3094 const RefPtr<AsyncPanZoomController>& aInitialTarget) { 3095 // Scroll grabbing is a mechanism that allows content to specify that 3096 // the initial target of a pan should be not the innermost scrollable 3097 // frame at the touch point (which is what GetTargetAPZC finds), but 3098 // something higher up in the tree. 3099 // It's not sufficient to just find the initial target, however, as 3100 // overscroll can be handed off to another APZC. Without scroll grabbing, 3101 // handoff just occurs from child to parent. With scroll grabbing, the 3102 // handoff order can be different, so we build a chain of APZCs in the 3103 // order in which scroll will be handed off to them. 3104 3105 // Grab tree lock since we'll be walking the APZC tree. 3106 RecursiveMutexAutoLock lock(mTreeLock); 3107 3108 // Build the chain. If there is a scroll parent link, we use that. This is 3109 // needed to deal with scroll info layers, because they participate in handoff 3110 // but do not follow the expected layer tree structure. If there are no 3111 // scroll parent links we just walk up the tree to find the scroll parent. 3112 OverscrollHandoffChain* result = new OverscrollHandoffChain; 3113 AsyncPanZoomController* apzc = aInitialTarget; 3114 while (apzc != nullptr) { 3115 result->Add(apzc); 3116 3117 APZCTreeManager::TargetApzcForNodeResult handoffResult = 3118 FindHandoffParent(apzc); 3119 3120 if (!handoffResult.mIsFixed && !apzc->IsRootForLayersId() && 3121 apzc->GetScrollHandoffParentId() == 3122 ScrollableLayerGuid::NULL_SCROLL_ID) { 3123 // This probably indicates a bug or missed case in layout code 3124 NS_WARNING("Found a non-root APZ with no handoff parent"); 3125 } 3126 3127 // If `apzc` is inside fixed content, we want to hand off to the document's 3128 // root APZC next. The scroll parent id wouldn't give us this because it's 3129 // based on ASRs. 3130 if (handoffResult.mIsFixed || apzc->GetScrollHandoffParentId() == 3131 ScrollableLayerGuid::NULL_SCROLL_ID) { 3132 apzc = handoffResult.mApzc; 3133 continue; 3134 } 3135 3136 // Guard against a possible infinite-loop condition. If we hit this, the 3137 // layout code that generates the handoff parents did something wrong. 3138 MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId); 3139 RefPtr<AsyncPanZoomController> scrollParent = GetTargetAPZC( 3140 apzc->GetGuid().mLayersId, apzc->GetScrollHandoffParentId()); 3141 apzc = scrollParent.get(); 3142 } 3143 3144 // Print the overscroll chain for debugging. 3145 for (uint32_t i = 0; i < result->Length(); ++i) { 3146 APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, 3147 result->GetApzcAtIndex(i).get()); 3148 } 3149 3150 return result; 3151 } 3152 3153 void APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled) { 3154 if (!APZThreadUtils::IsControllerThread()) { 3155 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<bool>( 3156 "layers::APZCTreeManager::SetLongTapEnabled", this, 3157 &APZCTreeManager::SetLongTapEnabled, aLongTapEnabled)); 3158 return; 3159 } 3160 3161 APZThreadUtils::AssertOnControllerThread(); 3162 GestureEventListener::SetLongTapEnabled(aLongTapEnabled); 3163 } 3164 3165 void APZCTreeManager::AddInputBlockCallback(uint64_t aInputBlockId, 3166 InputBlockCallback&& aCallback) { 3167 APZThreadUtils::AssertOnControllerThread(); 3168 mInputQueue->AddInputBlockCallback(aInputBlockId, std::move(aCallback)); 3169 } 3170 3171 void APZCTreeManager::FindScrollThumbNode( 3172 const AsyncDragMetrics& aDragMetrics, LayersId aLayersId, 3173 HitTestingTreeNodeAutoLock& aOutThumbNode) { 3174 if (!aDragMetrics.mDirection) { 3175 // The AsyncDragMetrics has not been initialized yet - there will be 3176 // no matching node, so don't bother searching the tree. 3177 return; 3178 } 3179 3180 RecursiveMutexAutoLock lock(mTreeLock); 3181 3182 RefPtr<HitTestingTreeNode> result = DepthFirstSearch<ReverseIterator>( 3183 mRootNode.get(), [&aDragMetrics, &aLayersId](HitTestingTreeNode* aNode) { 3184 return aNode->MatchesScrollDragMetrics(aDragMetrics, aLayersId); 3185 }); 3186 if (result) { 3187 aOutThumbNode.Initialize(lock, result.forget(), mTreeLock); 3188 } 3189 } 3190 3191 APZCTreeManager::TargetApzcForNodeResult APZCTreeManager::GetTargetApzcForNode( 3192 const HitTestingTreeNode* aNode) { 3193 for (const HitTestingTreeNode* n = aNode; 3194 n && n->GetLayersId() == aNode->GetLayersId(); n = n->GetParent()) { 3195 // For a fixed node, GetApzc() may return an APZC for content in the 3196 // enclosing document, so we need to check GetFixedPosTarget() before 3197 // GetApzc(). 3198 if (n->GetFixedPosTarget() != ScrollableLayerGuid::NULL_SCROLL_ID) { 3199 RefPtr<AsyncPanZoomController> fpTarget = 3200 GetTargetAPZC(n->GetLayersId(), n->GetFixedPosTarget()); 3201 APZCTM_LOG("Found target APZC %p using fixed-pos lookup on %" PRIu64 "\n", 3202 fpTarget.get(), n->GetFixedPosTarget()); 3203 return {fpTarget.get(), true}; 3204 } 3205 if (n->GetApzc()) { 3206 APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc()); 3207 return {n->GetApzc(), false}; 3208 } 3209 } 3210 return {nullptr, false}; 3211 } 3212 3213 HitTestingTreeNode* APZCTreeManager::FindRootNodeForLayersId( 3214 LayersId aLayersId) const { 3215 mTreeLock.AssertCurrentThreadIn(); 3216 3217 HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>( 3218 mRootNode.get(), [aLayersId](HitTestingTreeNode* aNode) { 3219 AsyncPanZoomController* apzc = aNode->GetApzc(); 3220 return apzc && apzc->GetLayersId() == aLayersId && 3221 apzc->IsRootForLayersId(); 3222 }); 3223 return resultNode; 3224 } 3225 3226 already_AddRefed<AsyncPanZoomController> APZCTreeManager::FindZoomableApzc( 3227 AsyncPanZoomController* aStart) const { 3228 return GetZoomableTarget(aStart, aStart); 3229 } 3230 3231 ScreenMargin APZCTreeManager::GetCompositorFixedLayerMargins() const { 3232 MutexAutoLock lock(mMapLock); 3233 return GetCompositorFixedLayerMargins(lock); 3234 } 3235 3236 AsyncPanZoomController* APZCTreeManager::FindRootApzcFor( 3237 LayersId aLayersId) const { 3238 RecursiveMutexAutoLock lock(mTreeLock); 3239 3240 HitTestingTreeNode* resultNode = FindRootNodeForLayersId(aLayersId); 3241 return resultNode ? resultNode->GetApzc() : nullptr; 3242 } 3243 3244 AsyncPanZoomController* APZCTreeManager::FindRootContentApzcForLayersId( 3245 LayersId aLayersId) const { 3246 mTreeLock.AssertCurrentThreadIn(); 3247 3248 HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>( 3249 mRootNode.get(), [aLayersId](HitTestingTreeNode* aNode) { 3250 AsyncPanZoomController* apzc = aNode->GetApzc(); 3251 return apzc && apzc->GetLayersId() == aLayersId && 3252 apzc->IsRootContent(); 3253 }); 3254 return resultNode ? resultNode->GetApzc() : nullptr; 3255 } 3256 3257 // clang-format off 3258 /* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return 3259 some useful transformations that input events may need applied. This is best 3260 illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L 3261 is the layer that corresponds to the argument |aApzc|, and layer R is the root 3262 of the layer tree. Layer M is the parent of L, N is the parent of M, and so on. 3263 When layer L is displayed to the screen by the compositor, the set of transforms that 3264 are applied to L are (in order from top to bottom): 3265 3266 L's CSS transform (hereafter referred to as transform matrix LC) 3267 L's async transform (hereafter referred to as transform matrix LA) 3268 M's CSS transform (hereafter referred to as transform matrix MC) 3269 M's async transform (hereafter referred to as transform matrix MA) 3270 ... 3271 R's CSS transform (hereafter referred to as transform matrix RC) 3272 R's async transform (hereafter referred to as transform matrix RA) 3273 3274 If we want user input to modify L's async transform, we have to first convert 3275 user input from screen space to the coordinate space of L's async transform. Doing 3276 this involves applying the following transforms (in order from top to bottom): 3277 RA.Inverse() 3278 RC.Inverse() 3279 ... 3280 MA.Inverse() 3281 MC.Inverse() 3282 This combined transformation is returned by GetScreenToApzcTransform(). 3283 3284 Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip 3285 out all of the async transforms that are involved in this chain. This is because async 3286 transforms are stored only in the compositor and gecko does not account for them when 3287 doing display-list-based hit-testing for event dispatching. 3288 Furthermore, because these input events are processed by Gecko in a FIFO queue that 3289 includes other things (specifically paint requests), it is possible that by time the 3290 input event reaches gecko, it will have painted something else. Therefore, we need to 3291 apply another transform to the input events to account for the possible disparity between 3292 what we know gecko last painted and the last paint request we sent to gecko. Let this 3293 transform be represented by LD, MD, ... RD. 3294 Therefore, given a user input in screen space, the following transforms need to be applied 3295 (in order from top to bottom): 3296 RT.Inverse() 3297 RN.Inverse() 3298 RC.Inverse() 3299 ... 3300 MT.Inverse() 3301 MN.Inverse() 3302 MC.Inverse() 3303 LT.Inverse() 3304 LN.Inverse() 3305 LC.Inverse() 3306 LC 3307 LD 3308 MC 3309 MD 3310 ... 3311 RC 3312 RD 3313 This sequence can be simplified and refactored to the following: 3314 GetScreenToApzcTransform() 3315 LA.Inverse() 3316 LD 3317 MC 3318 MD 3319 ... 3320 RC 3321 RD 3322 Since GetScreenToApzcTransform() can be obtained by calling that function, GetApzcToGeckoTransform() 3323 returns the remaining transforms (LA.Inverse() * LD * ... * RD), so that the caller code can 3324 combine it with GetScreenToApzcTransform() to get the final transform required in this case. 3325 3326 Note that for many of these layers, there will be no AsyncPanZoomController attached, and 3327 so the async transform will be the identity transform. So, in the example above, if layers 3328 L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT, 3329 RN and RD will be identity transforms. 3330 Additionally, for space-saving purposes, each APZC instance stores its layer's individual 3331 CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for 3332 layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC). 3333 The APZC instances track the last dispatched paint request and so are able to calculate LD and 3334 PD using those internally stored values. 3335 The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations 3336 required can be generated. 3337 */ 3338 // clang-format on 3339 3340 /* 3341 * See the long comment above for a detailed explanation of this function. 3342 */ 3343 ScreenToParentLayerMatrix4x4 APZCTreeManager::GetScreenToApzcTransform( 3344 const AsyncPanZoomController* aApzc) const { 3345 Matrix4x4 result; 3346 RecursiveMutexAutoLock lock(mTreeLock); 3347 3348 // The comments below assume there is a chain of layers L..R with L and P 3349 // having APZC instances as explained in the comment above. This function is 3350 // called with aApzc at L, and the loop below performs one iteration, where 3351 // parent is at P. The comments explain what values are stored in the 3352 // variables at these two levels. All the comments use standard matrix 3353 // notation where the leftmost matrix in a multiplication is applied first. 3354 3355 // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * 3356 // MC.Inverse() 3357 Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse(); 3358 3359 // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * 3360 // MC.Inverse() 3361 result = ancestorUntransform; 3362 3363 for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; 3364 parent = parent->GetParent()) { 3365 // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent 3366 // == P 3367 ancestorUntransform = parent->GetAncestorTransform().Inverse(); 3368 // asyncUntransform is updated to PA.Inverse() when parent == P 3369 Matrix4x4 asyncUntransform = parent 3370 ->GetAsyncTransformForInputTransformation( 3371 LayoutAndVisual, aApzc->GetLayersId()) 3372 .Inverse() 3373 .ToUnknownMatrix(); 3374 // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse() 3375 Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform; 3376 3377 // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * 3378 // OC.Inverse() * NC.Inverse() * MC.Inverse() 3379 result = untransformSinceLastApzc * result; 3380 3381 // The above value for result when parent == P matches the required output 3382 // as explained in the comment above this method. Note that any missing 3383 // terms are guaranteed to be identity transforms. 3384 } 3385 3386 return ViewAs<ScreenToParentLayerMatrix4x4>(result); 3387 } 3388 3389 /* 3390 * See the long comment above GetScreenToApzcTransform() for a detailed 3391 * explanation of this function. 3392 */ 3393 ParentLayerToParentLayerMatrix4x4 APZCTreeManager::GetApzcToApzcTransform( 3394 const AsyncPanZoomController* aStartApzc, 3395 const AsyncPanZoomController* aStopApzc, 3396 const AsyncTransformComponents& aComponents) const { 3397 Matrix4x4 result; 3398 RecursiveMutexAutoLock lock(mTreeLock); 3399 3400 // The comments below assume there is a chain of layers L..R with L and P 3401 // having APZC instances as explained in the comment above, and |aStopApzc| is 3402 // nullptr. This function is called with aStartApzc at L, and the loop below 3403 // performs one iteration, where parent is at P. The comments explain what 3404 // values are stored in the variables at these two levels. All the comments 3405 // use standard matrix notation where the leftmost matrix in a multiplication 3406 // is applied first. 3407 3408 // asyncUntransform is LA.Inverse() 3409 Matrix4x4 asyncUntransform = aStartApzc 3410 ->GetAsyncTransformForInputTransformation( 3411 aComponents, aStartApzc->GetLayersId()) 3412 .Inverse() 3413 .ToUnknownMatrix(); 3414 3415 // result is initialized to LA.Inverse() * LD * MC * NC * OC * PC 3416 result = asyncUntransform * 3417 aStartApzc->GetTransformToLastDispatchedPaint( 3418 aComponents, aStartApzc->GetLayersId()) * 3419 aStartApzc->GetAncestorTransform(); 3420 3421 for (AsyncPanZoomController* parent = aStartApzc->GetParent(); 3422 parent && parent != aStopApzc; parent = parent->GetParent()) { 3423 // result is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC 3424 // 3425 // Note: Do not pass the async transform components for the current target 3426 // to the parent. 3427 result = result * 3428 parent->GetTransformToLastDispatchedPaint( 3429 LayoutAndVisual, aStartApzc->GetLayersId()) * 3430 parent->GetAncestorTransform(); 3431 3432 // The above value for result when parent == P matches the required output 3433 // as explained in the comment above this method. Note that any missing 3434 // terms are guaranteed to be identity transforms. 3435 } 3436 3437 return ViewAs<ParentLayerToParentLayerMatrix4x4>(result); 3438 } 3439 3440 ParentLayerToScreenMatrix4x4 APZCTreeManager::GetApzcToGeckoTransform( 3441 const AsyncPanZoomController* aApzc, 3442 const AsyncTransformComponents& aComponents) const { 3443 return ViewAs<ParentLayerToScreenMatrix4x4>( 3444 GetApzcToApzcTransform(aApzc, nullptr, aComponents), 3445 PixelCastJustification::ScreenIsParentLayerForRoot); 3446 } 3447 3448 ParentLayerToScreenMatrix4x4 APZCTreeManager::GetApzcToGeckoTransformForHit( 3449 HitTestResult& aHitResult) const { 3450 // Fixed content is only subject to the visual component of the async 3451 // transform. 3452 AsyncTransformComponents components = 3453 aHitResult.mFixedPosSides == SideBits::eNone 3454 ? LayoutAndVisual 3455 : AsyncTransformComponents{AsyncTransformComponent::eVisual}; 3456 return GetApzcToGeckoTransform(aHitResult.mTargetApzc, components); 3457 } 3458 3459 CSSToCSSMatrix4x4 APZCTreeManager::GetOopifToRootContentTransform( 3460 AsyncPanZoomController* aApzc) const { 3461 MOZ_ASSERT(aApzc->IsRootForLayersId()); 3462 3463 RefPtr<AsyncPanZoomController> rootContentApzc = FindZoomableApzc(aApzc); 3464 MOZ_ASSERT(aApzc->GetLayersId() != rootContentApzc->GetLayersId(), 3465 "aApzc must be out-of-process of the rootContentApzc"); 3466 if (!rootContentApzc || rootContentApzc == aApzc || 3467 rootContentApzc->GetLayersId() == aApzc->GetLayersId()) { 3468 return CSSToCSSMatrix4x4(); 3469 } 3470 ParentLayerToParentLayerMatrix4x4 result = 3471 GetApzcToApzcTransform(aApzc, rootContentApzc, 3472 AsyncTransformComponent::eLayout) * 3473 // We need to multiply by the root content APZC's 3474 // GetPaintedResolutionTransform() here; See 3475 // https://phabricator.services.mozilla.com/D184440?vs=755900&id=757585#6173584 3476 // for the details. 3477 ViewAs<AsyncTransformComponentMatrix>( 3478 rootContentApzc->GetPaintedResolutionTransform()); 3479 3480 CSSToParentLayerScale rootZoom = rootContentApzc->GetZoom(); 3481 3482 if (rootZoom == CSSToParentLayerScale(0)) { 3483 rootZoom = CSSToParentLayerScale(1.0f); 3484 } 3485 3486 CSSPoint rootScrollPosition = rootContentApzc->GetLayoutScrollOffset(); 3487 3488 return result.PreScale(aApzc->GetZoom()) 3489 .PostScale(rootZoom.Inverse()) 3490 .PostTranslate(rootScrollPosition); 3491 } 3492 3493 CSSRect APZCTreeManager::ConvertRectInApzcToRoot(AsyncPanZoomController* aApzc, 3494 const CSSRect& aRect) const { 3495 MOZ_ASSERT(aApzc->IsRootForLayersId()); 3496 RefPtr<AsyncPanZoomController> rootContentApzc = FindZoomableApzc(aApzc); 3497 if (!rootContentApzc) { 3498 return aRect; 3499 } 3500 3501 if (rootContentApzc == aApzc) { 3502 return aRect + rootContentApzc->GetLayoutScrollOffset(); 3503 } 3504 3505 return GetOopifToRootContentTransform(aApzc).TransformBounds(aRect); 3506 } 3507 3508 ScreenPoint APZCTreeManager::GetCurrentMousePosition() const { 3509 auto pos = mCurrentMousePosition.Lock(); 3510 return pos.ref(); 3511 } 3512 3513 void APZCTreeManager::SetCurrentMousePosition(const ScreenPoint& aNewPos) { 3514 auto pos = mCurrentMousePosition.Lock(); 3515 pos.ref() = aNewPos; 3516 } 3517 3518 static AsyncPanZoomController* GetApzcWithDifferentLayersIdByWalkingParents( 3519 AsyncPanZoomController* aApzc) { 3520 if (!aApzc) { 3521 return nullptr; 3522 } 3523 AsyncPanZoomController* parent = aApzc->GetParent(); 3524 while (parent && (parent->GetLayersId() == aApzc->GetLayersId())) { 3525 parent = parent->GetParent(); 3526 } 3527 return parent; 3528 } 3529 3530 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetZoomableTarget( 3531 AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const { 3532 RecursiveMutexAutoLock lock(mTreeLock); 3533 RefPtr<AsyncPanZoomController> apzc; 3534 // For now, we only ever want to do pinching on the root-content APZC for 3535 // a given layers id. 3536 if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) { 3537 // If the two APZCs have the same layers id, find the root-content APZC 3538 // for that layers id. Don't call CommonAncestor() because there may not 3539 // be a common ancestor for the layers id (e.g. if one APZCs is inside a 3540 // fixed-position element). 3541 apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId()); 3542 if (apzc) { 3543 return apzc.forget(); 3544 } 3545 } 3546 3547 // Otherwise, find the common ancestor (to reach a common layers id), and then 3548 // walk up the apzc tree until we find a root-content APZC. 3549 apzc = CommonAncestor(aApzc1, aApzc2); 3550 RefPtr<AsyncPanZoomController> zoomable; 3551 while (apzc && !zoomable) { 3552 zoomable = FindRootContentApzcForLayersId(apzc->GetLayersId()); 3553 apzc = GetApzcWithDifferentLayersIdByWalkingParents(apzc); 3554 } 3555 3556 return zoomable.forget(); 3557 } 3558 3559 Maybe<ScreenIntPoint> APZCTreeManager::ConvertToGecko( 3560 const ScreenIntPoint& aPoint, AsyncPanZoomController* aApzc) { 3561 RecursiveMutexAutoLock lock(mTreeLock); 3562 // TODO: The current check assumes that a touch gesture and a touchpad tap 3563 // gesture can't both be active at the same time. If we turn on double-tap- 3564 // to-zoom on a touchscreen platform like Windows or Linux, this assumption 3565 // would no longer be valid, and we'd have to instead have TapGestureInput 3566 // track and inform this function whether it was created from touch events. 3567 const HitTestResult& hit = mInputQueue->GetCurrentTouchBlock() 3568 ? mTouchBlockHitResult 3569 : mTapGestureHitResult; 3570 AsyncTransformComponents components = 3571 hit.mFixedPosSides == SideBits::eNone 3572 ? LayoutAndVisual 3573 : AsyncTransformComponents{AsyncTransformComponent::eVisual}; 3574 ScreenToScreenMatrix4x4 transformScreenToGecko = 3575 GetScreenToApzcTransform(aApzc) * 3576 GetApzcToGeckoTransform(aApzc, components); 3577 Maybe<ScreenIntPoint> geckoPoint = 3578 UntransformBy(transformScreenToGecko, aPoint); 3579 if (geckoPoint) { 3580 AdjustEventPointForDynamicToolbar(*geckoPoint, hit); 3581 } 3582 return geckoPoint; 3583 } 3584 3585 already_AddRefed<AsyncPanZoomController> APZCTreeManager::CommonAncestor( 3586 AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const { 3587 mTreeLock.AssertCurrentThreadIn(); 3588 RefPtr<AsyncPanZoomController> ancestor; 3589 3590 // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this 3591 // function will return null. 3592 3593 // Calculate depth of the APZCs in the tree 3594 int depth1 = 0, depth2 = 0; 3595 for (AsyncPanZoomController* parent = aApzc1; parent; 3596 parent = parent->GetParent()) { 3597 depth1++; 3598 } 3599 for (AsyncPanZoomController* parent = aApzc2; parent; 3600 parent = parent->GetParent()) { 3601 depth2++; 3602 } 3603 3604 // At most one of the following two loops will be executed; the deeper APZC 3605 // pointer will get walked up to the depth of the shallower one. 3606 int minDepth = depth1 < depth2 ? depth1 : depth2; 3607 while (depth1 > minDepth) { 3608 depth1--; 3609 aApzc1 = aApzc1->GetParent(); 3610 } 3611 while (depth2 > minDepth) { 3612 depth2--; 3613 aApzc2 = aApzc2->GetParent(); 3614 } 3615 3616 // Walk up the ancestor chains of both APZCs, always staying at the same depth 3617 // for either APZC, and return the the first common ancestor encountered. 3618 while (true) { 3619 if (aApzc1 == aApzc2) { 3620 ancestor = aApzc1; 3621 break; 3622 } 3623 if (depth1 <= 0) { 3624 break; 3625 } 3626 aApzc1 = aApzc1->GetParent(); 3627 aApzc2 = aApzc2->GetParent(); 3628 } 3629 return ancestor.forget(); 3630 } 3631 3632 bool APZCTreeManager::IsFixedToRootContent( 3633 const FixedPositionInfo& aFixedInfo, 3634 const MutexAutoLock& aProofOfMapLock) const { 3635 ScrollableLayerGuid::ViewID fixedTarget = aFixedInfo.mFixedPosTarget; 3636 if (fixedTarget == ScrollableLayerGuid::NULL_SCROLL_ID) { 3637 return false; 3638 } 3639 auto it = 3640 mApzcMap.find(ScrollableLayerGuid(aFixedInfo.mLayersId, 0, fixedTarget)); 3641 if (it == mApzcMap.end()) { 3642 return false; 3643 } 3644 RefPtr<AsyncPanZoomController> targetApzc = it->second.apzc; 3645 return targetApzc && targetApzc->IsRootContent(); 3646 } 3647 3648 SideBits APZCTreeManager::SidesStuckToRootContent( 3649 const HitTestingTreeNode* aNode, AsyncTransformConsumer aMode) const { 3650 MutexAutoLock lock(mMapLock); 3651 return SidesStuckToRootContent(StickyPositionInfo(aNode), aMode, lock); 3652 } 3653 3654 SideBits APZCTreeManager::SidesStuckToRootContent( 3655 const StickyPositionInfo& aStickyInfo, AsyncTransformConsumer aMode, 3656 const MutexAutoLock& aProofOfMapLock) const { 3657 SideBits result = SideBits::eNone; 3658 3659 ScrollableLayerGuid::ViewID stickyTarget = aStickyInfo.mStickyPosTarget; 3660 if (stickyTarget == ScrollableLayerGuid::NULL_SCROLL_ID) { 3661 return result; 3662 } 3663 3664 // We support the dynamic toolbar at top and bottom. 3665 if ((aStickyInfo.mFixedPosSides & SideBits::eTopBottom) == SideBits::eNone) { 3666 return result; 3667 } 3668 3669 auto it = mApzcMap.find( 3670 ScrollableLayerGuid(aStickyInfo.mLayersId, 0, stickyTarget)); 3671 if (it == mApzcMap.end()) { 3672 return result; 3673 } 3674 RefPtr<AsyncPanZoomController> stickyTargetApzc = it->second.apzc; 3675 if (!stickyTargetApzc || !stickyTargetApzc->IsRootContent()) { 3676 return result; 3677 } 3678 3679 ParentLayerPoint translation = 3680 stickyTargetApzc 3681 ->GetCurrentAsyncTransform( 3682 aMode, AsyncTransformComponents{AsyncTransformComponent::eLayout}) 3683 .mTranslation; 3684 3685 if (apz::IsStuckAtTop(translation.y, aStickyInfo.mStickyScrollRangeInner, 3686 aStickyInfo.mStickyScrollRangeOuter)) { 3687 result |= SideBits::eTop; 3688 } 3689 if (apz::IsStuckAtBottom(translation.y, aStickyInfo.mStickyScrollRangeInner, 3690 aStickyInfo.mStickyScrollRangeOuter)) { 3691 result |= SideBits::eBottom; 3692 } 3693 return result; 3694 } 3695 3696 LayerToParentLayerMatrix4x4 APZCTreeManager::ComputeTransformForScrollThumbNode( 3697 const HitTestingTreeNode* aNode) const { 3698 mTreeLock.AssertCurrentThreadIn(); 3699 // The async transform applied here is for hit-testing purposes, and is 3700 // intended to match the one we tell WebRender to apply in 3701 // SampleForWebRender for rendering purposes. 3702 MOZ_ASSERT(aNode->IsScrollThumbNode()); 3703 // If the scrollable element scrolled by the thumb is layerized, compute and 3704 // apply the transformation that will be applied to the thumb in 3705 // AsyncCompositionManager. 3706 ScrollableLayerGuid guid{aNode->GetLayersId(), 0, aNode->GetScrollTargetId()}; 3707 if (RefPtr<HitTestingTreeNode> scrollTargetNode = 3708 GetTargetNode(guid, &ScrollableLayerGuid::EqualsIgnoringPresShell)) { 3709 AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc(); 3710 MOZ_ASSERT(scrollTargetApzc); 3711 return scrollTargetApzc->CallWithLastContentPaintMetrics( 3712 [&](const FrameMetrics& aMetrics) { 3713 return ComputeTransformForScrollThumb( 3714 aNode->GetTransform() * AsyncTransformMatrix(), 3715 scrollTargetNode->GetTransform().ToUnknownMatrix(), 3716 scrollTargetApzc, aMetrics, aNode->GetScrollbarData(), 3717 scrollTargetNode->IsAncestorOf(aNode)); 3718 }); 3719 } 3720 // Otherwise, the node does not have an async transform. 3721 return aNode->GetTransform() * AsyncTransformMatrix(); 3722 } 3723 3724 already_AddRefed<wr::WebRenderAPI> APZCTreeManager::GetWebRenderAPI() const { 3725 RefPtr<wr::WebRenderAPI> api; 3726 CompositorBridgeParent::CallWithIndirectShadowTree( 3727 mRootLayersId, [&](LayerTreeState& aState) -> void { 3728 if (aState.mWrBridge) { 3729 api = aState.mWrBridge->GetWebRenderAPI(); 3730 } 3731 }); 3732 return api.forget(); 3733 } 3734 3735 /*static*/ 3736 already_AddRefed<GeckoContentController> APZCTreeManager::GetContentController( 3737 LayersId aLayersId) { 3738 RefPtr<GeckoContentController> controller; 3739 CompositorBridgeParent::CallWithIndirectShadowTree( 3740 aLayersId, 3741 [&](LayerTreeState& aState) -> void { controller = aState.mController; }); 3742 return controller.forget(); 3743 } 3744 3745 ScreenMargin APZCTreeManager::GetCompositorFixedLayerMargins( 3746 const MutexAutoLock& aProofOfMapLock) const { 3747 ScreenMargin result = mCompositorFixedLayerMargins; 3748 if (StaticPrefs::apz_fixed_margin_override_enabled()) { 3749 result.top = StaticPrefs::apz_fixed_margin_override_top(); 3750 result.bottom = StaticPrefs::apz_fixed_margin_override_bottom(); 3751 } 3752 return result; 3753 } 3754 3755 bool APZCTreeManager::GetAPZTestData(LayersId aLayersId, 3756 APZTestData* aOutData) { 3757 AssertOnUpdaterThread(); 3758 3759 { // copy the relevant test data into aOutData while holding the 3760 // mTestDataLock 3761 MutexAutoLock lock(mTestDataLock); 3762 auto it = mTestData.find(aLayersId); 3763 if (it == mTestData.end()) { 3764 return false; 3765 } 3766 *aOutData = *(it->second); 3767 } 3768 3769 { // add some additional "current state" into the returned APZTestData 3770 MutexAutoLock mapLock(mMapLock); 3771 3772 ClippedCompositionBoundsMap clippedCompBounds; 3773 for (const auto& mapping : mApzcMap) { 3774 if (mapping.first.mLayersId != aLayersId) { 3775 continue; 3776 } 3777 3778 ParentLayerRect clippedBounds = ComputeClippedCompositionBounds( 3779 mapLock, clippedCompBounds, mapping.first); 3780 AsyncPanZoomController* apzc = mapping.second.apzc; 3781 std::string viewId = std::to_string(mapping.first.mScrollId); 3782 std::string apzcState; 3783 if (apzc->GetCheckerboardMagnitude(clippedBounds)) { 3784 apzcState += "checkerboarding,"; 3785 } 3786 if (apzc->IsOverscrolled()) { 3787 apzcState += "overscrolled,"; 3788 } 3789 aOutData->RecordAdditionalData(viewId, apzcState); 3790 } 3791 } 3792 return true; 3793 } 3794 3795 void APZCTreeManager::SendSubtreeTransformsToChromeMainThread( 3796 const AsyncPanZoomController* aAncestor) { 3797 RefPtr<GeckoContentController> controller = 3798 GetContentController(mRootLayersId); 3799 if (!controller) { 3800 return; 3801 } 3802 nsTArray<MatrixMessage> messages; 3803 bool underAncestor = (aAncestor == nullptr); 3804 bool shouldNotify = false; 3805 { 3806 RecursiveMutexAutoLock lock(mTreeLock); 3807 if (!mRootNode) { 3808 // Event dispatched during shutdown, after ClearTree(). 3809 // Note, mRootNode needs to be checked with mTreeLock held. 3810 return; 3811 } 3812 // This formulation duplicates matrix multiplications closer 3813 // to the root of the tree. For now, aiming for separation 3814 // of concerns rather than minimum number of multiplications. 3815 ForEachNode<ReverseIterator>( 3816 mRootNode.get(), 3817 [&](HitTestingTreeNode* aNode) { 3818 mTreeLock.AssertCurrentThreadIn(); 3819 bool atAncestor = (aAncestor && aNode->GetApzc() == aAncestor); 3820 MOZ_ASSERT(!(underAncestor && atAncestor)); 3821 underAncestor |= atAncestor; 3822 if (!underAncestor) { 3823 return; 3824 } 3825 LayersId layersId = aNode->GetLayersId(); 3826 HitTestingTreeNode* parent = aNode->GetParent(); 3827 if (!parent) { 3828 messages.AppendElement(MatrixMessage(Some(LayerToScreenMatrix4x4()), 3829 ScreenRect(), layersId)); 3830 } else if (layersId != parent->GetLayersId()) { 3831 if (mDetachedLayersIds.find(layersId) != mDetachedLayersIds.end()) { 3832 messages.AppendElement( 3833 MatrixMessage(Nothing(), ScreenRect(), layersId)); 3834 } else { 3835 // It's important to pass aRemoteLayersId=layersId here (not 3836 // parent->GetLayersId()). layersId is the LayersId of the 3837 // remote content for which this transform is being computed. 3838 messages.AppendElement(MatrixMessage( 3839 Some(parent->GetTransformToGecko(layersId)), 3840 parent->GetRemoteDocumentScreenRect(layersId), layersId)); 3841 } 3842 } 3843 }, 3844 [&](HitTestingTreeNode* aNode) { 3845 bool atAncestor = (aAncestor && aNode->GetApzc() == aAncestor); 3846 if (atAncestor) { 3847 MOZ_ASSERT(underAncestor); 3848 underAncestor = false; 3849 } 3850 }); 3851 if (messages != mLastMessages) { 3852 mLastMessages = messages; 3853 shouldNotify = true; 3854 } 3855 } 3856 if (shouldNotify) { 3857 controller->NotifyLayerTransforms(std::move(messages)); 3858 } 3859 } 3860 3861 void APZCTreeManager::SetFixedLayerMargins(ScreenIntCoord aTop, 3862 ScreenIntCoord aBottom) { 3863 { 3864 MutexAutoLock lock(mMapLock); 3865 mCompositorFixedLayerMargins.top = ScreenCoord(aTop); 3866 mCompositorFixedLayerMargins.bottom = ScreenCoord(aBottom); 3867 } 3868 { 3869 RecursiveMutexAutoLock lock(mTreeLock); 3870 SetFixedLayerMarginsOnRootContentApzcs(lock); 3871 } 3872 } 3873 3874 void APZCTreeManager::SetFixedLayerMarginsOnRootContentApzcs( 3875 const RecursiveMutexAutoLock& aProofOfTreeLock) { 3876 ScreenMargin margins = GetCompositorFixedLayerMargins(); 3877 for (auto* apzc : mRootContentApzcs) { 3878 apzc->SetFixedLayerMargins(margins); 3879 } 3880 } 3881 3882 ScreenPoint APZCTreeManager::ComputeFixedMarginsOffset( 3883 const MutexAutoLock& aProofOfMapLock, SideBits aFixedSides, 3884 const ScreenMargin& aGeckoFixedLayerMargins) const { 3885 // If the software keyboard is visible and the interactive-widget is not 3886 // resizes-content, we don't need to move the position:fixed or sticky 3887 // elements at all. 3888 if (IsSoftwareKeyboardVisible(aProofOfMapLock) && 3889 InteractiveWidgetMode(aProofOfMapLock) != 3890 dom::InteractiveWidget::ResizesContent) { 3891 return ScreenPoint(0, 0); 3892 } 3893 3894 // Work out the necessary translation, in screen space. 3895 ScreenPoint translation; 3896 3897 ScreenMargin effectiveMargin = 3898 GetCompositorFixedLayerMargins(aProofOfMapLock) - aGeckoFixedLayerMargins; 3899 if (aFixedSides & SideBits::eLeft) { 3900 translation.x += effectiveMargin.left; 3901 } else if (aFixedSides & SideBits::eRight) { 3902 translation.x -= effectiveMargin.right; 3903 } 3904 3905 if (aFixedSides & SideBits::eTop) { 3906 translation.y += effectiveMargin.top; 3907 } else if (aFixedSides & SideBits::eBottom) { 3908 translation.y -= effectiveMargin.bottom; 3909 } 3910 3911 return translation; 3912 } 3913 3914 /*static*/ 3915 LayerToParentLayerMatrix4x4 APZCTreeManager::ComputeTransformForScrollThumb( 3916 const LayerToParentLayerMatrix4x4& aCurrentTransform, 3917 const Matrix4x4& aScrollableContentTransform, AsyncPanZoomController* aApzc, 3918 const FrameMetrics& aMetrics, const ScrollbarData& aScrollbarData, 3919 bool aScrollbarIsDescendant) { 3920 return apz::ComputeTransformForScrollThumb( 3921 aCurrentTransform, aScrollableContentTransform, aApzc, aMetrics, 3922 aScrollbarData, aScrollbarIsDescendant); 3923 } 3924 3925 APZSampler* APZCTreeManager::GetSampler() const { 3926 // We should always have a sampler here, since in practice the sampler 3927 // is destroyed at the same time that this APZCTreeMAnager instance is. 3928 MOZ_ASSERT(mSampler); 3929 return mSampler; 3930 } 3931 3932 void APZCTreeManager::AssertOnSamplerThread() { 3933 GetSampler()->AssertOnSamplerThread(); 3934 } 3935 3936 APZUpdater* APZCTreeManager::GetUpdater() const { 3937 // We should always have an updater here, since in practice the updater 3938 // is destroyed at the same time that this APZCTreeManager instance is. 3939 MOZ_ASSERT(mUpdater); 3940 return mUpdater; 3941 } 3942 3943 void APZCTreeManager::AssertOnUpdaterThread() { 3944 GetUpdater()->AssertOnUpdaterThread(); 3945 } 3946 3947 MOZ_PUSH_IGNORE_THREAD_SAFETY 3948 void APZCTreeManager::LockTree() { 3949 AssertOnUpdaterThread(); 3950 mTreeLock.Lock(); 3951 } 3952 3953 void APZCTreeManager::UnlockTree() { 3954 AssertOnUpdaterThread(); 3955 mTreeLock.Unlock(); 3956 } 3957 MOZ_POP_THREAD_SAFETY 3958 3959 void APZCTreeManager::SetDPI(float aDpiValue) { 3960 if (!APZThreadUtils::IsControllerThread()) { 3961 APZThreadUtils::RunOnControllerThread( 3962 NewRunnableMethod<float>("layers::APZCTreeManager::SetDPI", this, 3963 &APZCTreeManager::SetDPI, aDpiValue)); 3964 return; 3965 } 3966 3967 APZThreadUtils::AssertOnControllerThread(); 3968 mDPI = aDpiValue; 3969 } 3970 3971 float APZCTreeManager::GetDPI() const { 3972 APZThreadUtils::AssertOnControllerThread(); 3973 return mDPI; 3974 } 3975 3976 void APZCTreeManager::EndWheelTransaction( 3977 PWebRenderBridgeParent::EndWheelTransactionResolver&& aResolver) { 3978 RefPtr<nsISerialEventTarget> controllerThread = 3979 APZThreadUtils::GetControllerThread(); 3980 InvokeAsync(controllerThread, __func__, 3981 [self = RefPtr{this}] { 3982 if (WheelBlockState* txn = 3983 self->mInputQueue->GetActiveWheelTransaction()) { 3984 txn->EndTransaction(); 3985 } 3986 return GenericPromise::CreateAndResolve(true, __func__); 3987 }) 3988 // The resolver needs to be resolved on the thread where it was initiated. 3989 ->Then(GetCurrentSerialEventTarget(), __func__, 3990 [resolver = std::move(aResolver)]( 3991 GenericPromise::ResolveOrRejectValue&&) { resolver(true); }); 3992 } 3993 3994 APZCTreeManager::FixedPositionInfo::FixedPositionInfo( 3995 const HitTestingTreeNode* aNode) { 3996 mFixedPositionAnimationId = aNode->GetFixedPositionAnimationId(); 3997 mFixedPosSides = aNode->GetFixedPosSides(); 3998 mFixedPosTarget = aNode->GetFixedPosTarget(); 3999 mLayersId = aNode->GetLayersId(); 4000 } 4001 4002 APZCTreeManager::StickyPositionInfo::StickyPositionInfo( 4003 const HitTestingTreeNode* aNode) { 4004 mStickyPositionAnimationId = aNode->GetStickyPositionAnimationId(); 4005 mFixedPosSides = aNode->GetFixedPosSides(); 4006 mStickyPosTarget = aNode->GetStickyPosTarget(); 4007 mLayersId = aNode->GetLayersId(); 4008 mStickyScrollRangeInner = aNode->GetStickyScrollRangeInner(); 4009 mStickyScrollRangeOuter = aNode->GetStickyScrollRangeOuter(); 4010 } 4011 4012 } // namespace layers 4013 } // namespace mozilla