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