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