/src/mozilla-central/gfx/layers/apz/util/ActiveElementManager.cpp
Line | Count | Source (jump to first uncovered line) |
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 "ActiveElementManager.h" |
8 | | #include "mozilla/EventStateManager.h" |
9 | | #include "mozilla/EventStates.h" |
10 | | #include "mozilla/Preferences.h" |
11 | | #include "base/message_loop.h" |
12 | | #include "base/task.h" |
13 | | #include "mozilla/dom/Element.h" |
14 | | #include "nsIDocument.h" |
15 | | |
16 | | #define AEM_LOG(...) |
17 | | // #define AEM_LOG(...) printf_stderr("AEM: " __VA_ARGS__) |
18 | | |
19 | | namespace mozilla { |
20 | | namespace layers { |
21 | | |
22 | | static int32_t sActivationDelayMs = 100; |
23 | | static bool sActivationDelayMsSet = false; |
24 | | |
25 | | ActiveElementManager::ActiveElementManager() |
26 | | : mCanBePan(false), |
27 | | mCanBePanSet(false), |
28 | | mSetActiveTask(nullptr) |
29 | 0 | { |
30 | 0 | if (!sActivationDelayMsSet) { |
31 | 0 | Preferences::AddIntVarCache(&sActivationDelayMs, |
32 | 0 | "ui.touch_activation.delay_ms", |
33 | 0 | sActivationDelayMs); |
34 | 0 | sActivationDelayMsSet = true; |
35 | 0 | } |
36 | 0 | } |
37 | | |
38 | 0 | ActiveElementManager::~ActiveElementManager() {} |
39 | | |
40 | | void |
41 | | ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget) |
42 | 0 | { |
43 | 0 | if (mTarget) { |
44 | 0 | // Multiple fingers on screen (since HandleTouchEnd clears mTarget). |
45 | 0 | AEM_LOG("Multiple fingers on-screen, clearing target element\n"); |
46 | 0 | CancelTask(); |
47 | 0 | ResetActive(); |
48 | 0 | ResetTouchBlockState(); |
49 | 0 | return; |
50 | 0 | } |
51 | 0 | |
52 | 0 | mTarget = do_QueryInterface(aTarget); |
53 | 0 | AEM_LOG("Setting target element to %p\n", mTarget.get()); |
54 | 0 | TriggerElementActivation(); |
55 | 0 | } |
56 | | |
57 | | void |
58 | | ActiveElementManager::HandleTouchStart(bool aCanBePan) |
59 | 0 | { |
60 | 0 | AEM_LOG("Touch start, aCanBePan: %d\n", aCanBePan); |
61 | 0 | if (mCanBePanSet) { |
62 | 0 | // Multiple fingers on screen (since HandleTouchEnd clears mCanBePanSet). |
63 | 0 | AEM_LOG("Multiple fingers on-screen, clearing touch block state\n"); |
64 | 0 | CancelTask(); |
65 | 0 | ResetActive(); |
66 | 0 | ResetTouchBlockState(); |
67 | 0 | return; |
68 | 0 | } |
69 | 0 | |
70 | 0 | mCanBePan = aCanBePan; |
71 | 0 | mCanBePanSet = true; |
72 | 0 | TriggerElementActivation(); |
73 | 0 | } |
74 | | |
75 | | void |
76 | | ActiveElementManager::TriggerElementActivation() |
77 | 0 | { |
78 | 0 | // Both HandleTouchStart() and SetTargetElement() call this. They can be |
79 | 0 | // called in either order. One will set mCanBePanSet, and the other, mTarget. |
80 | 0 | // We want to actually trigger the activation once both are set. |
81 | 0 | if (!(mTarget && mCanBePanSet)) { |
82 | 0 | return; |
83 | 0 | } |
84 | 0 | |
85 | 0 | // If the touch cannot be a pan, make mTarget :active right away. |
86 | 0 | // Otherwise, wait a bit to see if the user will pan or not. |
87 | 0 | if (!mCanBePan) { |
88 | 0 | SetActive(mTarget); |
89 | 0 | } else { |
90 | 0 | CancelTask(); // this is only needed because of bug 1169802. Fixing that |
91 | 0 | // bug properly should make this unnecessary. |
92 | 0 | MOZ_ASSERT(mSetActiveTask == nullptr); |
93 | 0 |
|
94 | 0 | RefPtr<CancelableRunnable> task = |
95 | 0 | NewCancelableRunnableMethod<nsCOMPtr<dom::Element>>( |
96 | 0 | "layers::ActiveElementManager::SetActiveTask", |
97 | 0 | this, |
98 | 0 | &ActiveElementManager::SetActiveTask, |
99 | 0 | mTarget); |
100 | 0 | mSetActiveTask = task; |
101 | 0 | MessageLoop::current()->PostDelayedTask(task.forget(), sActivationDelayMs); |
102 | 0 | AEM_LOG("Scheduling mSetActiveTask %p\n", mSetActiveTask); |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | void |
107 | | ActiveElementManager::ClearActivation() |
108 | 0 | { |
109 | 0 | AEM_LOG("Clearing element activation\n"); |
110 | 0 | CancelTask(); |
111 | 0 | ResetActive(); |
112 | 0 | } |
113 | | |
114 | | void |
115 | | ActiveElementManager::HandleTouchEndEvent(bool aWasClick) |
116 | 0 | { |
117 | 0 | AEM_LOG("Touch end event, aWasClick: %d\n", aWasClick); |
118 | 0 |
|
119 | 0 | // If the touch was a click, make mTarget :active right away. |
120 | 0 | // nsEventStateManager will reset the active element when processing |
121 | 0 | // the mouse-down event generated by the click. |
122 | 0 | CancelTask(); |
123 | 0 | if (aWasClick) { |
124 | 0 | // Scrollbar thumbs use a different mechanism for their active |
125 | 0 | // highlight (the "active" attribute), so don't set the active state |
126 | 0 | // on them because nothing will clear it. |
127 | 0 | if (!(mTarget && mTarget->IsXULElement(nsGkAtoms::thumb))) { |
128 | 0 | SetActive(mTarget); |
129 | 0 | } |
130 | 0 | } else { |
131 | 0 | // We might reach here if mCanBePan was false on touch-start and |
132 | 0 | // so we set the element active right away. Now it turns out the |
133 | 0 | // action was not a click so we need to reset the active element. |
134 | 0 | ResetActive(); |
135 | 0 | } |
136 | 0 |
|
137 | 0 | ResetTouchBlockState(); |
138 | 0 | } |
139 | | |
140 | | void |
141 | | ActiveElementManager::HandleTouchEnd() |
142 | 0 | { |
143 | 0 | AEM_LOG("Touch end, clearing pan state\n"); |
144 | 0 | mCanBePanSet = false; |
145 | 0 | } |
146 | | |
147 | | static nsPresContext* |
148 | | GetPresContextFor(nsIContent* aContent) |
149 | 0 | { |
150 | 0 | if (!aContent) { |
151 | 0 | return nullptr; |
152 | 0 | } |
153 | 0 | nsIPresShell* shell = aContent->OwnerDoc()->GetShell(); |
154 | 0 | if (!shell) { |
155 | 0 | return nullptr; |
156 | 0 | } |
157 | 0 | return shell->GetPresContext(); |
158 | 0 | } |
159 | | |
160 | | void |
161 | | ActiveElementManager::SetActive(dom::Element* aTarget) |
162 | 0 | { |
163 | 0 | AEM_LOG("Setting active %p\n", aTarget); |
164 | 0 |
|
165 | 0 | if (nsPresContext* pc = GetPresContextFor(aTarget)) { |
166 | 0 | pc->EventStateManager()->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | void |
171 | | ActiveElementManager::ResetActive() |
172 | 0 | { |
173 | 0 | AEM_LOG("Resetting active from %p\n", mTarget.get()); |
174 | 0 |
|
175 | 0 | // Clear the :active flag from mTarget by setting it on the document root. |
176 | 0 | if (mTarget) { |
177 | 0 | dom::Element* root = mTarget->OwnerDoc()->GetDocumentElement(); |
178 | 0 | if (root) { |
179 | 0 | AEM_LOG("Found root %p, making active\n", root); |
180 | 0 | SetActive(root); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | void |
186 | | ActiveElementManager::ResetTouchBlockState() |
187 | 0 | { |
188 | 0 | mTarget = nullptr; |
189 | 0 | mCanBePanSet = false; |
190 | 0 | } |
191 | | |
192 | | void |
193 | | ActiveElementManager::SetActiveTask(const nsCOMPtr<dom::Element>& aTarget) |
194 | 0 | { |
195 | 0 | AEM_LOG("mSetActiveTask %p running\n", mSetActiveTask); |
196 | 0 |
|
197 | 0 | // This gets called from mSetActiveTask's Run() method. The message loop |
198 | 0 | // deletes the task right after running it, so we need to null out |
199 | 0 | // mSetActiveTask to make sure we're not left with a dangling pointer. |
200 | 0 | mSetActiveTask = nullptr; |
201 | 0 | SetActive(aTarget); |
202 | 0 | } |
203 | | |
204 | | void |
205 | | ActiveElementManager::CancelTask() |
206 | 0 | { |
207 | 0 | AEM_LOG("Cancelling task %p\n", mSetActiveTask); |
208 | 0 |
|
209 | 0 | if (mSetActiveTask) { |
210 | 0 | mSetActiveTask->Cancel(); |
211 | 0 | mSetActiveTask = nullptr; |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | } // namespace layers |
216 | | } // namespace mozilla |