FocusState.h (7419B)
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 /** 129 * Reset to initial state. 130 */ 131 void Reset(); 132 133 private: 134 /** 135 * Whether the current focus state is known to be current or else if an event 136 * has been processed that could change the focus but we have not received an 137 * update with a new confirmed target. 138 * This can only be called by methods that have already acquired mMutex; they 139 * have to pass their lock as compile-time proof. 140 */ 141 bool IsCurrent(const MutexAutoLock& aLock) const; 142 143 private: 144 // All methods should hold this lock, since this class is accessed via both 145 // the updater and controller threads. 146 mutable Mutex mMutex MOZ_UNANNOTATED; 147 148 // The set of focus targets received indexed by their layer tree ID 149 std::unordered_map<LayersId, FocusTarget, LayersId::HashFn> mFocusTree; 150 151 // The focus sequence number of the last potentially focus changing event 152 // processed by APZ. This number starts at one and increases monotonically. 153 // We don't worry about wrap around here because at a pace of 100 154 // increments/sec, it would take 5.85*10^9 years before we would wrap around. 155 // This number will never be zero as that is used to catch uninitialized focus 156 // sequence numbers on input events. 157 uint64_t mLastAPZProcessedEvent; 158 // The focus sequence number last received in a focus update. 159 uint64_t mLastContentProcessedEvent; 160 161 // A flag whether there is a key listener on the event target chain for the 162 // focused element 163 bool mFocusHasKeyEventListeners; 164 // A flag that is false until the first call to Update(). 165 bool mReceivedUpdate; 166 167 // The layer tree ID which contains the scrollable frame of the focused 168 // element 169 LayersId mFocusLayersId; 170 // The scrollable layer corresponding to the scrollable frame that is used to 171 // scroll the focused element. This depends on the direction the user is 172 // scrolling. 173 ScrollableLayerGuid::ViewID mFocusHorizontalTarget; 174 ScrollableLayerGuid::ViewID mFocusVerticalTarget; 175 }; 176 177 } // namespace layers 178 } // namespace mozilla 179 180 #endif // mozilla_layers_FocusState_h