summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/FocusState.cpp
blob: 0230676e7ef4b9ab33a2dbf85e85c93a1adc7c69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "FocusState.h"

#include "mozilla/Logging.h"
#include "mozilla/layers/APZThreadUtils.h"

static mozilla::LazyLogModule sApzFstLog("apz.focusstate");
#define FS_LOG(...) MOZ_LOG(sApzFstLog, LogLevel::Debug, (__VA_ARGS__))

namespace mozilla {
namespace layers {

FocusState::FocusState()
    : mMutex("FocusStateMutex"),
      mLastAPZProcessedEvent(1),
      mLastContentProcessedEvent(0),
      mFocusHasKeyEventListeners(false),
      mReceivedUpdate(false),
      mFocusLayersId{0},
      mFocusHorizontalTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
      mFocusVerticalTarget(ScrollableLayerGuid::NULL_SCROLL_ID) {}

uint64_t FocusState::LastAPZProcessedEvent() const {
  APZThreadUtils::AssertOnControllerThread();
  MutexAutoLock lock(mMutex);

  return mLastAPZProcessedEvent;
}

bool FocusState::IsCurrent(const MutexAutoLock& aProofOfLock) const {
  FS_LOG("Checking IsCurrent() with cseq=%" PRIu64 ", aseq=%" PRIu64 "\n",
         mLastContentProcessedEvent, mLastAPZProcessedEvent);
  MOZ_ASSERT(mLastContentProcessedEvent <= mLastAPZProcessedEvent);
  return mLastContentProcessedEvent == mLastAPZProcessedEvent;
}

void FocusState::ReceiveFocusChangingEvent() {
  APZThreadUtils::AssertOnControllerThread();
  MutexAutoLock lock(mMutex);

  if (!mReceivedUpdate) {
    // In the initial state don't advance mLastAPZProcessedEvent because we
    // might blow away the information that we're in a freshly-restarted GPU
    // process. This information (i.e. that mLastAPZProcessedEvent == 1) needs
    // to be preserved until the first call to Update() which will then advance
    // mLastAPZProcessedEvent to match the content-side sequence number.
    return;
  }
  mLastAPZProcessedEvent += 1;
  FS_LOG("Focus changing event incremented aseq to %" PRIu64 ", (%p)\n",
         mLastAPZProcessedEvent, this);
}

void FocusState::Update(LayersId aRootLayerTreeId,
                        LayersId aOriginatingLayersId,
                        const FocusTarget& aState) {
  // This runs on the updater thread, it's not worth passing around extra raw
  // pointers just to assert it.

  MutexAutoLock lock(mMutex);

  FS_LOG("Update with rlt=%" PRIu64 ", olt=%" PRIu64 ", ft=(%s, %" PRIu64 ")\n",
         aRootLayerTreeId.mId, aOriginatingLayersId.mId, aState.Type(),
         aState.mSequenceNumber);
  mReceivedUpdate = true;

  // Update the focus tree with the latest target
  mFocusTree[aOriginatingLayersId] = aState;

  // Reset our internal state so we can recalculate it
  mFocusHasKeyEventListeners = false;
  mFocusLayersId = aRootLayerTreeId;
  mFocusHorizontalTarget = ScrollableLayerGuid::NULL_SCROLL_ID;
  mFocusVerticalTarget = ScrollableLayerGuid::NULL_SCROLL_ID;

  // To update the focus state for the entire APZCTreeManager, we need
  // to traverse the focus tree to find the current leaf which is the global
  // focus target we can use for async keyboard scrolling
  while (true) {
    auto currentNode = mFocusTree.find(mFocusLayersId);
    if (currentNode == mFocusTree.end()) {
      FS_LOG("Setting target to nil (cannot find lt=%" PRIu64 ")\n",
             mFocusLayersId.mId);
      return;
    }

    const FocusTarget& target = currentNode->second;

    // Accumulate event listener flags on the path to the focus target
    mFocusHasKeyEventListeners |= target.mFocusHasKeyEventListeners;

    // Match on the data stored in mData
    // The match functions return true or false depending on whether the
    // enclosing method, FocusState::Update, should return or continue to the
    // next iteration of the while loop, respectively.
    struct FocusTargetDataMatcher {
      FocusState& mFocusState;
      const uint64_t mSequenceNumber;

      bool operator()(const FocusTarget::NoFocusTarget& aNoFocusTarget) {
        FS_LOG("Setting target to nil (reached a nil target) with seq=%" PRIu64
               ", (%p)\n",
               mSequenceNumber, &mFocusState);

        // Mark what sequence number this target has for debugging purposes so
        // we can always accurately report on whether we are stale or not
        mFocusState.mLastContentProcessedEvent = mSequenceNumber;

        // If this focus state was just created and content has experienced more
        // events then us, then assume we were recreated and sync focus sequence
        // numbers.
        if (mFocusState.mLastAPZProcessedEvent == 1 &&
            mFocusState.mLastContentProcessedEvent >
                mFocusState.mLastAPZProcessedEvent) {
          mFocusState.mLastAPZProcessedEvent =
              mFocusState.mLastContentProcessedEvent;
        }
        return true;
      }

      bool operator()(const LayersId& aRefLayerId) {
        // Guard against infinite loops
        MOZ_ASSERT(mFocusState.mFocusLayersId != aRefLayerId);
        if (mFocusState.mFocusLayersId == aRefLayerId) {
          FS_LOG(
              "Setting target to nil (bailing out of infinite loop, lt=%" PRIu64
              ")\n",
              mFocusState.mFocusLayersId.mId);
          return true;
        }

        FS_LOG("Looking for target in lt=%" PRIu64 "\n", aRefLayerId.mId);

        // The focus target is in a child layer tree
        mFocusState.mFocusLayersId = aRefLayerId;
        return false;
      }

      bool operator()(const FocusTarget::ScrollTargets& aScrollTargets) {
        FS_LOG("Setting target to h=%" PRIu64 ", v=%" PRIu64
               ", and seq=%" PRIu64 "(%p)\n",
               aScrollTargets.mHorizontal, aScrollTargets.mVertical,
               mSequenceNumber, &mFocusState);

        // This is the global focus target
        mFocusState.mFocusHorizontalTarget = aScrollTargets.mHorizontal;
        mFocusState.mFocusVerticalTarget = aScrollTargets.mVertical;

        // Mark what sequence number this target has so we can determine whether
        // it is stale or not
        mFocusState.mLastContentProcessedEvent = mSequenceNumber;

        // If this focus state was just created and content has experienced more
        // events then us, then assume we were recreated and sync focus sequence
        // numbers.
        if (mFocusState.mLastAPZProcessedEvent == 1 &&
            mFocusState.mLastContentProcessedEvent >
                mFocusState.mLastAPZProcessedEvent) {
          mFocusState.mLastAPZProcessedEvent =
              mFocusState.mLastContentProcessedEvent;
        }
        return true;
      }
    };  // struct FocusTargetDataMatcher

    if (target.mData.match(
            FocusTargetDataMatcher{*this, target.mSequenceNumber})) {
      return;
    }
  }
}

void FocusState::RemoveFocusTarget(LayersId aLayersId) {
  // This runs on the updater thread, it's not worth passing around extra raw
  // pointers just to assert it.
  MutexAutoLock lock(mMutex);

  mFocusTree.erase(aLayersId);
}

Maybe<ScrollableLayerGuid> FocusState::GetHorizontalTarget() const {
  APZThreadUtils::AssertOnControllerThread();
  MutexAutoLock lock(mMutex);

  // There is not a scrollable layer to async scroll if
  //   1. We aren't current
  //   2. There are event listeners that could change the focus
  //   3. The target has not been layerized
  if (!IsCurrent(lock) || mFocusHasKeyEventListeners ||
      mFocusHorizontalTarget == ScrollableLayerGuid::NULL_SCROLL_ID) {
    return Nothing();
  }
  return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusHorizontalTarget));
}

Maybe<ScrollableLayerGuid> FocusState::GetVerticalTarget() const {
  APZThreadUtils::AssertOnControllerThread();
  MutexAutoLock lock(mMutex);

  // There is not a scrollable layer to async scroll if:
  //   1. We aren't current
  //   2. There are event listeners that could change the focus
  //   3. The target has not been layerized
  if (!IsCurrent(lock) || mFocusHasKeyEventListeners ||
      mFocusVerticalTarget == ScrollableLayerGuid::NULL_SCROLL_ID) {
    return Nothing();
  }
  return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusVerticalTarget));
}

bool FocusState::CanIgnoreKeyboardShortcutMisses() const {
  APZThreadUtils::AssertOnControllerThread();
  MutexAutoLock lock(mMutex);

  return IsCurrent(lock) && !mFocusHasKeyEventListeners;
}

void FocusState::Reset() {
  MutexAutoLock lock(mMutex);

  mLastAPZProcessedEvent = 1;
  mLastContentProcessedEvent = 0;
  mFocusHasKeyEventListeners = false;
  mReceivedUpdate = false;
  mFocusLayersId = {0};
  mFocusHorizontalTarget = ScrollableLayerGuid::NULL_SCROLL_ID;
  mFocusVerticalTarget = ScrollableLayerGuid::NULL_SCROLL_ID;
  mFocusTree = {};
}

}  // namespace layers
}  // namespace mozilla