Source code

Revision control

Other Tools

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