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
#ifndef mozilla_layers_FocusState_h
8
#define mozilla_layers_FocusState_h
9
10
#include <unordered_map> // for std::unordered_map
11
#include <unordered_set> // for std::unordered_set
12
13
#include "mozilla/layers/FocusTarget.h" // for FocusTarget
14
#include "mozilla/layers/ScrollableLayerGuid.h" // for ViewID
15
#include "mozilla/Mutex.h" // for Mutex
16
17
namespace mozilla {
18
namespace layers {
19
20
/**
21
* This class is used for tracking chrome and content focus targets and
22
* calculating global focus information from them for use by APZCTreeManager
23
* for async keyboard scrolling.
24
*
25
* # Calculating the element to scroll
26
*
27
* Chrome and content processes have independently focused elements. This makes
28
* it difficult to calculate the global focused element and its scrollable
29
* frame from the chrome or content side. So instead we send the local focus
30
* information from each process to here and then calculate the global focus
31
* information. This local information resides in a `focus target`.
32
*
33
* A focus target indicates that either:
34
* 1. The focused element is a remote browser along with its layer tree ID
35
* 2. The focused element is not scrollable
36
* 3. The focused element is scrollable along with the ViewID's of its
37
scrollable layers
38
*
39
* Using this information we can determine the global focus information by
40
* starting at the focus target of the root layer tree ID and following remote
41
* browsers until we reach a scrollable or non-scrollable focus target.
42
*
43
* # Determinism and sequence numbers
44
*
45
* The focused element in content can be changed within any javascript code. And
46
* javascript can run in response to an event or at any moment from `setTimeout`
47
* and others. This makes it impossible to always have the current focus
48
* information in APZ as it can be changed asynchronously at any moment. If we
49
* don't have the latest focus information, we may incorrectly scroll a target
50
* when we shouldn't.
51
*
52
* A tradeoff is designed here whereby we will maintain deterministic focus
53
* changes for user input, but not for other javascript code. The reasoning
54
* here is that `setTimeout` and others are already non-deterministic and so it
55
* might not be as breaking to web content.
56
*
57
* To maintain deterministic focus changes for a given stream of user inputs,
58
* we invalidate our focus state whenever we receive a user input that may
59
* trigger event listeners. We then attach a new sequence number to these
60
* events and dispatch them to content. Content will then include the latest
61
* sequence number it has processed to every focus update. Using this we can
62
* determine whether any potentially focus changing events have yet to be
63
* handled by content.
64
*
65
* Once we have received the latest focus sequence number from content, we know
66
* that all event listeners triggered by user inputs, and their resulting focus
67
* changes, have been processed and so we have a current target that we can use
68
* again.
69
*/
70
class FocusState final {
71
public:
72
FocusState();
73
74
/**
75
* The sequence number of the last potentially focus changing event processed
76
* by APZ. This number starts at one and increases monotonically. This number
77
* will never be zero as that is used to catch uninitialized focus sequence
78
* numbers on input events.
79
*/
80
uint64_t LastAPZProcessedEvent() const;
81
82
/**
83
* Notify focus state of a potentially focus changing event. This will
84
* increment the current focus sequence number. The new value can be gotten
85
* from LastAPZProcessedEvent().
86
*/
87
void ReceiveFocusChangingEvent();
88
89
/**
90
* Update the internal focus tree and recalculate the global focus target for
91
* a focus target update received from chrome or content.
92
*
93
* @param aRootLayerTreeId the layer tree ID of the root layer for the
94
parent APZCTreeManager
95
* @param aOriginatingLayersId the layer tree ID that this focus target
96
belongs to
97
*/
98
void Update(LayersId aRootLayerTreeId, LayersId aOriginatingLayersId,
99
const FocusTarget& aTarget);
100
101
/**
102
* Removes a focus target by its layer tree ID.
103
*/
104
void RemoveFocusTarget(LayersId aLayersId);
105
106
/**
107
* Gets the scrollable layer that should be horizontally scrolled for a key
108
* event, if any. The returned ScrollableLayerGuid doesn't contain a
109
* presShellId, and so it should not be used in comparisons.
110
*
111
* No scrollable layer is returned if any of the following are true:
112
* 1. We don't have a current focus target
113
* 2. There are event listeners that could change the focus
114
* 3. The target has not been layerized
115
*/
116
Maybe<ScrollableLayerGuid> GetHorizontalTarget() const;
117
/**
118
* The same as GetHorizontalTarget() but for vertical scrolling.
119
*/
120
Maybe<ScrollableLayerGuid> GetVerticalTarget() const;
121
122
/**
123
* Gets whether it is safe to not increment the focus sequence number for an
124
* unmatched keyboard event.
125
*/
126
bool CanIgnoreKeyboardShortcutMisses() const;
127
128
private:
129
/**
130
* Whether the current focus state is known to be current or else if an event
131
* has been processed that could change the focus but we have not received an
132
* update with a new confirmed target.
133
* This can only be called by methods that have already acquired mMutex; they
134
* have to pass their lock as compile-time proof.
135
*/
136
bool IsCurrent(const MutexAutoLock& aLock) const;
137
138
private:
139
// All methods should hold this lock, since this class is accessed via both
140
// the updater and controller threads.
141
mutable Mutex mMutex;
142
143
// The set of focus targets received indexed by their layer tree ID
144
std::unordered_map<LayersId, FocusTarget, LayersId::HashFn> mFocusTree;
145
146
// The focus sequence number of the last potentially focus changing event
147
// processed by APZ. This number starts at one and increases monotonically.
148
// We don't worry about wrap around here because at a pace of 100
149
// increments/sec, it would take 5.85*10^9 years before we would wrap around.
150
// This number will never be zero as that is used to catch uninitialized focus
151
// sequence numbers on input events.
152
uint64_t mLastAPZProcessedEvent;
153
// The focus sequence number last received in a focus update.
154
uint64_t mLastContentProcessedEvent;
155
156
// A flag whether there is a key listener on the event target chain for the
157
// focused element
158
bool mFocusHasKeyEventListeners;
159
// A flag that is false until the first call to Update().
160
bool mReceivedUpdate;
161
162
// The layer tree ID which contains the scrollable frame of the focused
163
// element
164
LayersId mFocusLayersId;
165
// The scrollable layer corresponding to the scrollable frame that is used to
166
// scroll the focused element. This depends on the direction the user is
167
// scrolling.
168
ScrollableLayerGuid::ViewID mFocusHorizontalTarget;
169
ScrollableLayerGuid::ViewID mFocusVerticalTarget;
170
};
171
172
} // namespace layers
173
} // namespace mozilla
174
175
#endif // mozilla_layers_FocusState_h