FocusState.cpp (8913B)
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 "FocusState.h" 8 9 #include "mozilla/Logging.h" 10 #include "mozilla/IntegerPrintfMacros.h" 11 #include "mozilla/layers/APZThreadUtils.h" 12 13 static mozilla::LazyLogModule sApzFstLog("apz.focusstate"); 14 #define FS_LOG(...) MOZ_LOG(sApzFstLog, LogLevel::Debug, (__VA_ARGS__)) 15 16 namespace mozilla { 17 namespace layers { 18 19 FocusState::FocusState() 20 : mMutex("FocusStateMutex"), 21 mLastAPZProcessedEvent(1), 22 mLastContentProcessedEvent(0), 23 mFocusHasKeyEventListeners(false), 24 mReceivedUpdate(false), 25 mFocusLayersId{0}, 26 mFocusHorizontalTarget(ScrollableLayerGuid::NULL_SCROLL_ID), 27 mFocusVerticalTarget(ScrollableLayerGuid::NULL_SCROLL_ID) {} 28 29 uint64_t FocusState::LastAPZProcessedEvent() const { 30 APZThreadUtils::AssertOnControllerThread(); 31 MutexAutoLock lock(mMutex); 32 33 return mLastAPZProcessedEvent; 34 } 35 36 bool FocusState::IsCurrent(const MutexAutoLock& aProofOfLock) const { 37 FS_LOG("Checking IsCurrent() with cseq=%" PRIu64 ", aseq=%" PRIu64 "\n", 38 mLastContentProcessedEvent, mLastAPZProcessedEvent); 39 MOZ_ASSERT(mLastContentProcessedEvent <= mLastAPZProcessedEvent); 40 return mLastContentProcessedEvent == mLastAPZProcessedEvent; 41 } 42 43 void FocusState::ReceiveFocusChangingEvent() { 44 APZThreadUtils::AssertOnControllerThread(); 45 MutexAutoLock lock(mMutex); 46 47 if (!mReceivedUpdate) { 48 // In the initial state don't advance mLastAPZProcessedEvent because we 49 // might blow away the information that we're in a freshly-restarted GPU 50 // process. This information (i.e. that mLastAPZProcessedEvent == 1) needs 51 // to be preserved until the first call to Update() which will then advance 52 // mLastAPZProcessedEvent to match the content-side sequence number. 53 return; 54 } 55 mLastAPZProcessedEvent += 1; 56 FS_LOG("Focus changing event incremented aseq to %" PRIu64 ", (%p)\n", 57 mLastAPZProcessedEvent, this); 58 } 59 60 void FocusState::Update(LayersId aRootLayerTreeId, 61 LayersId aOriginatingLayersId, 62 const FocusTarget& aState) { 63 // This runs on the updater thread, it's not worth passing around extra raw 64 // pointers just to assert it. 65 66 MutexAutoLock lock(mMutex); 67 68 FS_LOG("Update with rlt=%" PRIu64 ", olt=%" PRIu64 ", ft=(%s, %" PRIu64 ")\n", 69 aRootLayerTreeId.mId, aOriginatingLayersId.mId, aState.Type(), 70 aState.mSequenceNumber); 71 mReceivedUpdate = true; 72 73 // Update the focus tree with the latest target 74 mFocusTree[aOriginatingLayersId] = aState; 75 76 // Reset our internal state so we can recalculate it 77 mFocusHasKeyEventListeners = false; 78 mFocusLayersId = aRootLayerTreeId; 79 mFocusHorizontalTarget = ScrollableLayerGuid::NULL_SCROLL_ID; 80 mFocusVerticalTarget = ScrollableLayerGuid::NULL_SCROLL_ID; 81 82 // To update the focus state for the entire APZCTreeManager, we need 83 // to traverse the focus tree to find the current leaf which is the global 84 // focus target we can use for async keyboard scrolling 85 while (true) { 86 auto currentNode = mFocusTree.find(mFocusLayersId); 87 if (currentNode == mFocusTree.end()) { 88 FS_LOG("Setting target to nil (cannot find lt=%" PRIu64 ")\n", 89 mFocusLayersId.mId); 90 return; 91 } 92 93 const FocusTarget& target = currentNode->second; 94 95 // Accumulate event listener flags on the path to the focus target 96 mFocusHasKeyEventListeners |= target.mFocusHasKeyEventListeners; 97 98 // Match on the data stored in mData 99 // The match functions return true or false depending on whether the 100 // enclosing method, FocusState::Update, should return or continue to the 101 // next iteration of the while loop, respectively. 102 struct FocusTargetDataMatcher { 103 FocusState& mFocusState; 104 const uint64_t mSequenceNumber; 105 106 bool operator()(const FocusTarget::NoFocusTarget& aNoFocusTarget) { 107 FS_LOG("Setting target to nil (reached a nil target) with seq=%" PRIu64 108 ", (%p)\n", 109 mSequenceNumber, &mFocusState); 110 111 // Mark what sequence number this target has for debugging purposes so 112 // we can always accurately report on whether we are stale or not 113 mFocusState.mLastContentProcessedEvent = mSequenceNumber; 114 115 // If this focus state was just created and content has experienced more 116 // events then us, then assume we were recreated and sync focus sequence 117 // numbers. 118 if (mFocusState.mLastAPZProcessedEvent == 1 && 119 mFocusState.mLastContentProcessedEvent > 120 mFocusState.mLastAPZProcessedEvent) { 121 mFocusState.mLastAPZProcessedEvent = 122 mFocusState.mLastContentProcessedEvent; 123 } 124 return true; 125 } 126 127 bool operator()(const LayersId& aRefLayerId) { 128 // Guard against infinite loops 129 MOZ_ASSERT(mFocusState.mFocusLayersId != aRefLayerId); 130 if (mFocusState.mFocusLayersId == aRefLayerId) { 131 FS_LOG( 132 "Setting target to nil (bailing out of infinite loop, lt=%" PRIu64 133 ")\n", 134 mFocusState.mFocusLayersId.mId); 135 return true; 136 } 137 138 FS_LOG("Looking for target in lt=%" PRIu64 "\n", aRefLayerId.mId); 139 140 // The focus target is in a child layer tree 141 mFocusState.mFocusLayersId = aRefLayerId; 142 return false; 143 } 144 145 bool operator()(const FocusTarget::ScrollTargets& aScrollTargets) { 146 FS_LOG("Setting target to h=%" PRIu64 ", v=%" PRIu64 147 ", and seq=%" PRIu64 "(%p)\n", 148 aScrollTargets.mHorizontal, aScrollTargets.mVertical, 149 mSequenceNumber, &mFocusState); 150 151 // This is the global focus target 152 mFocusState.mFocusHorizontalTarget = aScrollTargets.mHorizontal; 153 mFocusState.mFocusVerticalTarget = aScrollTargets.mVertical; 154 155 // Mark what sequence number this target has so we can determine whether 156 // it is stale or not 157 mFocusState.mLastContentProcessedEvent = mSequenceNumber; 158 159 // If this focus state was just created and content has experienced more 160 // events then us, then assume we were recreated and sync focus sequence 161 // numbers. 162 if (mFocusState.mLastAPZProcessedEvent == 1 && 163 mFocusState.mLastContentProcessedEvent > 164 mFocusState.mLastAPZProcessedEvent) { 165 mFocusState.mLastAPZProcessedEvent = 166 mFocusState.mLastContentProcessedEvent; 167 } 168 return true; 169 } 170 }; // struct FocusTargetDataMatcher 171 172 if (target.mData.match( 173 FocusTargetDataMatcher{*this, target.mSequenceNumber})) { 174 return; 175 } 176 } 177 } 178 179 void FocusState::RemoveFocusTarget(LayersId aLayersId) { 180 // This runs on the updater thread, it's not worth passing around extra raw 181 // pointers just to assert it. 182 MutexAutoLock lock(mMutex); 183 184 mFocusTree.erase(aLayersId); 185 } 186 187 Maybe<ScrollableLayerGuid> FocusState::GetHorizontalTarget() const { 188 APZThreadUtils::AssertOnControllerThread(); 189 MutexAutoLock lock(mMutex); 190 191 // There is not a scrollable layer to async scroll if 192 // 1. We aren't current 193 // 2. There are event listeners that could change the focus 194 // 3. The target has not been layerized 195 if (!IsCurrent(lock) || mFocusHasKeyEventListeners || 196 mFocusHorizontalTarget == ScrollableLayerGuid::NULL_SCROLL_ID) { 197 return Nothing(); 198 } 199 return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusHorizontalTarget)); 200 } 201 202 Maybe<ScrollableLayerGuid> FocusState::GetVerticalTarget() const { 203 APZThreadUtils::AssertOnControllerThread(); 204 MutexAutoLock lock(mMutex); 205 206 // There is not a scrollable layer to async scroll if: 207 // 1. We aren't current 208 // 2. There are event listeners that could change the focus 209 // 3. The target has not been layerized 210 if (!IsCurrent(lock) || mFocusHasKeyEventListeners || 211 mFocusVerticalTarget == ScrollableLayerGuid::NULL_SCROLL_ID) { 212 return Nothing(); 213 } 214 return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusVerticalTarget)); 215 } 216 217 bool FocusState::CanIgnoreKeyboardShortcutMisses() const { 218 APZThreadUtils::AssertOnControllerThread(); 219 MutexAutoLock lock(mMutex); 220 221 return IsCurrent(lock) && !mFocusHasKeyEventListeners; 222 } 223 224 void FocusState::Reset() { 225 MutexAutoLock lock(mMutex); 226 227 mLastAPZProcessedEvent = 1; 228 mLastContentProcessedEvent = 0; 229 mFocusHasKeyEventListeners = false; 230 mReceivedUpdate = false; 231 mFocusLayersId = {0}; 232 mFocusHorizontalTarget = ScrollableLayerGuid::NULL_SCROLL_ID; 233 mFocusVerticalTarget = ScrollableLayerGuid::NULL_SCROLL_ID; 234 mFocusTree = {}; 235 } 236 237 } // namespace layers 238 } // namespace mozilla