tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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