HitTestingTreeNode.cpp (13258B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "HitTestingTreeNode.h" 6 #include <stack> 7 8 #include "AsyncPanZoomController.h" // for AsyncPanZoomController 9 #include "mozilla/StaticPrefs_layout.h" 10 #include "mozilla/gfx/Point.h" // for Point4D 11 #include "mozilla/layers/APZUtils.h" // for AsyncTransform, CompleteAsyncTransform 12 #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics 13 #include "mozilla/ToString.h" // for ToString 14 #include "nsPrintfCString.h" // for nsPrintfCString 15 #include "UnitTransforms.h" // for ViewAs 16 17 static mozilla::LazyLogModule sApzMgrLog("apz.manager"); 18 19 namespace mozilla { 20 namespace layers { 21 22 using gfx::CompositorHitTestInfo; 23 24 HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc, 25 bool aIsPrimaryHolder, 26 LayersId aLayersId) 27 : mApzc(aApzc), 28 mIsPrimaryApzcHolder(aIsPrimaryHolder), 29 mLockCount(0), 30 mLayersId(aLayersId), 31 mFixedPosTarget(ScrollableLayerGuid::NULL_SCROLL_ID), 32 mStickyPosTarget(ScrollableLayerGuid::NULL_SCROLL_ID), 33 mOverride(EventRegionsOverride::NoOverride) { 34 if (mIsPrimaryApzcHolder) { 35 MOZ_ASSERT(mApzc); 36 } 37 MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId); 38 } 39 40 void HitTestingTreeNode::RecycleWith( 41 const RecursiveMutexAutoLock& aProofOfTreeLock, 42 AsyncPanZoomController* aApzc, LayersId aLayersId) { 43 MOZ_ASSERT(IsRecyclable(aProofOfTreeLock)); 44 Destroy(); // clear out tree pointers 45 mApzc = aApzc; 46 mLayersId = aLayersId; 47 MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId); 48 // The caller is expected to call appropriate setters (SetHitTestData, 49 // SetScrollbarData, SetFixedPosData, SetStickyPosData, etc.) to repopulate 50 // all the data fields before using this node for "real work". Otherwise 51 // those data fields may contain stale information from the previous use 52 // of this node object. 53 } 54 55 HitTestingTreeNode::~HitTestingTreeNode() = default; 56 57 void HitTestingTreeNode::Destroy() { 58 // This runs on the updater thread, it's not worth passing around extra raw 59 // pointers just to assert it. 60 61 mPrevSibling = nullptr; 62 mLastChild = nullptr; 63 mParent = nullptr; 64 65 if (mApzc) { 66 if (mIsPrimaryApzcHolder) { 67 mApzc->Destroy(); 68 } 69 mApzc = nullptr; 70 } 71 } 72 73 bool HitTestingTreeNode::IsRecyclable( 74 const RecursiveMutexAutoLock& aProofOfTreeLock) { 75 return !(IsPrimaryHolder() || (mLockCount > 0)); 76 } 77 78 void HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild) { 79 mLastChild = aChild; 80 if (aChild) { 81 aChild->mParent = this; 82 83 if (aChild->GetApzc()) { 84 AsyncPanZoomController* parent = GetNearestContainingApzc(); 85 // We assume that HitTestingTreeNodes with an ancestor/descendant 86 // relationship cannot both point to the same APZC instance. This 87 // assertion only covers a subset of cases in which that might occur, 88 // but it's better than nothing. 89 MOZ_ASSERT(aChild->GetApzc() != parent); 90 aChild->SetApzcParent(parent); 91 } 92 } 93 } 94 95 void HitTestingTreeNode::SetScrollbarData( 96 const Maybe<uint64_t>& aScrollbarAnimationId, 97 const ScrollbarData& aScrollbarData) { 98 mScrollbarAnimationId = aScrollbarAnimationId; 99 mScrollbarData = aScrollbarData; 100 } 101 102 bool HitTestingTreeNode::MatchesScrollDragMetrics( 103 const AsyncDragMetrics& aDragMetrics, LayersId aLayersId) const { 104 return IsScrollThumbNode() && mLayersId == aLayersId && 105 mScrollbarData.mDirection == aDragMetrics.mDirection && 106 mScrollbarData.mTargetViewId == aDragMetrics.mViewId; 107 } 108 109 bool HitTestingTreeNode::IsScrollThumbNode() const { 110 return mScrollbarData.mScrollbarLayerType == 111 layers::ScrollbarLayerType::Thumb; 112 } 113 114 bool HitTestingTreeNode::IsScrollbarNode() const { 115 return mScrollbarData.mScrollbarLayerType != layers::ScrollbarLayerType::None; 116 } 117 118 bool HitTestingTreeNode::IsScrollbarContainerNode() const { 119 return mScrollbarData.mScrollbarLayerType == 120 layers::ScrollbarLayerType::Container; 121 } 122 123 ScrollDirection HitTestingTreeNode::GetScrollbarDirection() const { 124 MOZ_ASSERT(IsScrollbarNode()); 125 MOZ_ASSERT(mScrollbarData.mDirection.isSome()); 126 return *mScrollbarData.mDirection; 127 } 128 129 ScrollableLayerGuid::ViewID HitTestingTreeNode::GetScrollTargetId() const { 130 return mScrollbarData.mTargetViewId; 131 } 132 133 Maybe<uint64_t> HitTestingTreeNode::GetScrollbarAnimationId() const { 134 return mScrollbarAnimationId; 135 } 136 137 const ScrollbarData& HitTestingTreeNode::GetScrollbarData() const { 138 return mScrollbarData; 139 } 140 141 void HitTestingTreeNode::SetFixedPosData( 142 ScrollableLayerGuid::ViewID aFixedPosTarget, SideBits aFixedPosSides, 143 const Maybe<uint64_t>& aFixedPositionAnimationId) { 144 mFixedPosTarget = aFixedPosTarget; 145 mFixedPosSides = aFixedPosSides; 146 mFixedPositionAnimationId = aFixedPositionAnimationId; 147 } 148 149 ScrollableLayerGuid::ViewID HitTestingTreeNode::GetFixedPosTarget() const { 150 return mFixedPosTarget; 151 } 152 153 SideBits HitTestingTreeNode::GetFixedPosSides() const { return mFixedPosSides; } 154 155 Maybe<uint64_t> HitTestingTreeNode::GetFixedPositionAnimationId() const { 156 return mFixedPositionAnimationId; 157 } 158 159 void HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling) { 160 mPrevSibling = aSibling; 161 if (aSibling) { 162 aSibling->mParent = mParent; 163 164 if (aSibling->GetApzc()) { 165 AsyncPanZoomController* parent = 166 mParent ? mParent->GetNearestContainingApzc() : nullptr; 167 aSibling->SetApzcParent(parent); 168 } 169 } 170 } 171 172 void HitTestingTreeNode::SetStickyPosData( 173 ScrollableLayerGuid::ViewID aStickyPosTarget, 174 const LayerRectAbsolute& aScrollRangeOuter, 175 const LayerRectAbsolute& aScrollRangeInner, 176 const Maybe<uint64_t>& aStickyPositionAnimationId) { 177 mStickyPosTarget = aStickyPosTarget; 178 mStickyScrollRangeOuter = aScrollRangeOuter; 179 mStickyScrollRangeInner = aScrollRangeInner; 180 mStickyPositionAnimationId = aStickyPositionAnimationId; 181 } 182 183 ScrollableLayerGuid::ViewID HitTestingTreeNode::GetStickyPosTarget() const { 184 return mStickyPosTarget; 185 } 186 187 const LayerRectAbsolute& HitTestingTreeNode::GetStickyScrollRangeOuter() const { 188 return mStickyScrollRangeOuter; 189 } 190 191 const LayerRectAbsolute& HitTestingTreeNode::GetStickyScrollRangeInner() const { 192 return mStickyScrollRangeInner; 193 } 194 195 Maybe<uint64_t> HitTestingTreeNode::GetStickyPositionAnimationId() const { 196 return mStickyPositionAnimationId; 197 } 198 199 void HitTestingTreeNode::MakeRoot() { 200 mParent = nullptr; 201 202 if (GetApzc()) { 203 SetApzcParent(nullptr); 204 } 205 } 206 207 HitTestingTreeNode* HitTestingTreeNode::GetFirstChild() const { 208 HitTestingTreeNode* child = GetLastChild(); 209 while (child && child->GetPrevSibling()) { 210 child = child->GetPrevSibling(); 211 } 212 return child; 213 } 214 215 HitTestingTreeNode* HitTestingTreeNode::GetLastChild() const { 216 return mLastChild; 217 } 218 219 HitTestingTreeNode* HitTestingTreeNode::GetPrevSibling() const { 220 return mPrevSibling; 221 } 222 223 HitTestingTreeNode* HitTestingTreeNode::GetParent() const { return mParent; } 224 225 bool HitTestingTreeNode::IsAncestorOf(const HitTestingTreeNode* aOther) const { 226 for (const HitTestingTreeNode* cur = aOther; cur; cur = cur->GetParent()) { 227 if (cur == this) { 228 return true; 229 } 230 } 231 return false; 232 } 233 234 AsyncPanZoomController* HitTestingTreeNode::GetApzc() const { return mApzc; } 235 236 AsyncPanZoomController* HitTestingTreeNode::GetNearestContainingApzc() const { 237 for (const HitTestingTreeNode* n = this; n; n = n->GetParent()) { 238 if (n->GetApzc()) { 239 return n->GetApzc(); 240 } 241 } 242 return nullptr; 243 } 244 245 bool HitTestingTreeNode::IsPrimaryHolder() const { 246 return mIsPrimaryApzcHolder; 247 } 248 249 LayersId HitTestingTreeNode::GetLayersId() const { return mLayersId; } 250 251 void HitTestingTreeNode::SetHitTestData( 252 const LayerIntRect& aVisibleRect, const LayerIntSize& aRemoteDocumentSize, 253 const CSSTransformMatrix& aTransform, const EventRegionsOverride& aOverride, 254 const Maybe<ScrollableLayerGuid::ViewID>& aAsyncZoomContainerId) { 255 mVisibleRect = aVisibleRect; 256 mRemoteDocumentSize = aRemoteDocumentSize; 257 mTransform = aTransform; 258 mOverride = aOverride; 259 mAsyncZoomContainerId = aAsyncZoomContainerId; 260 } 261 262 EventRegionsOverride HitTestingTreeNode::GetEventRegionsOverride() const { 263 return mOverride; 264 } 265 266 const CSSTransformMatrix& HitTestingTreeNode::GetTransform() const { 267 return mTransform; 268 } 269 270 LayerToScreenMatrix4x4 HitTestingTreeNode::GetTransformToGecko( 271 LayersId aRemoteLayersId) const { 272 if (mParent) { 273 LayerToParentLayerMatrix4x4 thisToParent = 274 mTransform * AsyncTransformMatrix(); 275 if (mApzc) { 276 thisToParent = 277 thisToParent * ViewAs<AsyncTransformComponentMatrix>( 278 mApzc->GetTransformToLastDispatchedPaint( 279 LayoutAndVisual, aRemoteLayersId)); 280 } 281 ParentLayerToScreenMatrix4x4 parentToRoot = 282 ViewAs<ParentLayerToScreenMatrix4x4>( 283 mParent->GetTransformToGecko(aRemoteLayersId), 284 PixelCastJustification::MovingDownToChildren); 285 return thisToParent * parentToRoot; 286 } 287 288 return ViewAs<LayerToScreenMatrix4x4>( 289 mTransform * AsyncTransformMatrix(), 290 PixelCastJustification::ScreenIsParentLayerForRoot); 291 } 292 293 const LayerIntRect& HitTestingTreeNode::GetVisibleRect() const { 294 return mVisibleRect; 295 } 296 297 ScreenRect HitTestingTreeNode::GetRemoteDocumentScreenRect( 298 LayersId aRemoteDocumentLayersId) const { 299 ScreenRect result = TransformBy( 300 GetTransformToGecko(aRemoteDocumentLayersId), 301 IntRectToRect(LayerIntRect(LayerIntPoint(), mRemoteDocumentSize))); 302 303 for (const HitTestingTreeNode* node = this; node; node = node->GetParent()) { 304 if (!node->GetApzc()) { 305 continue; 306 } 307 308 ParentLayerRect compositionBounds = node->GetApzc()->GetCompositionBounds(); 309 if (compositionBounds.IsEmpty()) { 310 return ScreenRect(); 311 } 312 313 ScreenRect scrollPortOnScreenCoordinate = TransformBy( 314 node->GetParent() 315 ? node->GetParent()->GetTransformToGecko(node->GetLayersId()) 316 : LayerToScreenMatrix4x4(), 317 ViewAs<LayerPixel>(compositionBounds, 318 PixelCastJustification::MovingDownToChildren)); 319 if (scrollPortOnScreenCoordinate.IsEmpty()) { 320 return ScreenRect(); 321 } 322 323 result = result.Intersect(scrollPortOnScreenCoordinate); 324 if (result.IsEmpty()) { 325 return ScreenRect(); 326 } 327 } 328 return result; 329 } 330 331 Maybe<ScrollableLayerGuid::ViewID> HitTestingTreeNode::GetAsyncZoomContainerId() 332 const { 333 return mAsyncZoomContainerId; 334 } 335 336 void HitTestingTreeNode::Dump(const char* aPrefix) const { 337 MOZ_LOG( 338 sApzMgrLog, LogLevel::Debug, 339 ("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%s t=(%s) " 340 "%s%s\n", 341 aPrefix, this, mApzc.get(), 342 mApzc ? ToString(mApzc->GetGuid()).c_str() 343 : nsPrintfCString("l=0x%" PRIx64, uint64_t(mLayersId)).get(), 344 (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " 345 : "", 346 (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "", 347 (mFixedPosTarget != ScrollableLayerGuid::NULL_SCROLL_ID) 348 ? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get() 349 : "", 350 ToString(mTransform).c_str(), 351 mScrollbarData.mDirection.isSome() ? " scrollbar" : "", 352 IsScrollThumbNode() ? " scrollthumb" : "")); 353 354 if (!mLastChild) { 355 return; 356 } 357 358 // Dump the children in order from first child to last child 359 std::stack<HitTestingTreeNode*> children; 360 for (HitTestingTreeNode* child = mLastChild.get(); child; 361 child = child->mPrevSibling) { 362 children.push(child); 363 } 364 nsPrintfCString childPrefix("%s ", aPrefix); 365 while (!children.empty()) { 366 children.top()->Dump(childPrefix.get()); 367 children.pop(); 368 } 369 } 370 371 void HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent) { 372 // precondition: GetApzc() is non-null 373 MOZ_ASSERT(GetApzc() != nullptr); 374 if (IsPrimaryHolder()) { 375 GetApzc()->SetParent(aParent); 376 } else { 377 MOZ_ASSERT(GetApzc()->GetParent() == aParent); 378 } 379 } 380 381 void HitTestingTreeNode::Lock(const RecursiveMutexAutoLock& aProofOfTreeLock) { 382 mLockCount++; 383 } 384 385 void HitTestingTreeNode::Unlock( 386 const RecursiveMutexAutoLock& aProofOfTreeLock) { 387 MOZ_ASSERT(mLockCount > 0); 388 mLockCount--; 389 } 390 391 HitTestingTreeNodeAutoLock::HitTestingTreeNodeAutoLock() 392 : mTreeMutex(nullptr) {} 393 394 HitTestingTreeNodeAutoLock::~HitTestingTreeNodeAutoLock() { Clear(); } 395 396 void HitTestingTreeNodeAutoLock::Initialize( 397 const RecursiveMutexAutoLock& aProofOfTreeLock, 398 already_AddRefed<HitTestingTreeNode> aNode, RecursiveMutex& aTreeMutex) { 399 MOZ_ASSERT(!mNode); 400 401 mNode = aNode; 402 mTreeMutex = &aTreeMutex; 403 404 mNode->Lock(aProofOfTreeLock); 405 } 406 407 void HitTestingTreeNodeAutoLock::Clear() { 408 if (!mNode) { 409 return; 410 } 411 MOZ_ASSERT(mTreeMutex); 412 413 { // scope lock 414 RecursiveMutexAutoLock lock(*mTreeMutex); 415 mNode->Unlock(lock); 416 } 417 mNode = nullptr; 418 mTreeMutex = nullptr; 419 } 420 421 } // namespace layers 422 } // namespace mozilla