|
1 /* -*- Mode: C++; tab-width: 2; 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 #include "ActiveElementManager.h" |
|
7 #include "mozilla/EventStates.h" |
|
8 #include "mozilla/Preferences.h" |
|
9 #include "mozilla/Services.h" |
|
10 #include "inIDOMUtils.h" |
|
11 #include "nsIDOMDocument.h" |
|
12 #include "nsIDOMElement.h" |
|
13 #include "nsIDOMEventTarget.h" |
|
14 #include "base/message_loop.h" |
|
15 #include "base/task.h" |
|
16 |
|
17 namespace mozilla { |
|
18 namespace layers { |
|
19 |
|
20 static int32_t sActivationDelayMs = 100; |
|
21 static bool sActivationDelayMsSet = false; |
|
22 |
|
23 ActiveElementManager::ActiveElementManager() |
|
24 : mDomUtils(services::GetInDOMUtils()), |
|
25 mCanBePan(false), |
|
26 mCanBePanSet(false), |
|
27 mSetActiveTask(nullptr) |
|
28 { |
|
29 if (!sActivationDelayMsSet) { |
|
30 Preferences::AddIntVarCache(&sActivationDelayMs, |
|
31 "ui.touch_activation.delay_ms", |
|
32 sActivationDelayMs); |
|
33 sActivationDelayMsSet = true; |
|
34 } |
|
35 } |
|
36 |
|
37 ActiveElementManager::~ActiveElementManager() {} |
|
38 |
|
39 void |
|
40 ActiveElementManager::SetTargetElement(nsIDOMEventTarget* aTarget) |
|
41 { |
|
42 if (mTarget) { |
|
43 // Multiple fingers on screen (since HandleTouchEnd clears mTarget). |
|
44 CancelTask(); |
|
45 ResetActive(); |
|
46 mTarget = nullptr; |
|
47 return; |
|
48 } |
|
49 |
|
50 mTarget = do_QueryInterface(aTarget); |
|
51 TriggerElementActivation(); |
|
52 } |
|
53 |
|
54 void |
|
55 ActiveElementManager::HandleTouchStart(bool aCanBePan) |
|
56 { |
|
57 mCanBePan = aCanBePan; |
|
58 mCanBePanSet = true; |
|
59 TriggerElementActivation(); |
|
60 } |
|
61 |
|
62 void |
|
63 ActiveElementManager::TriggerElementActivation() |
|
64 { |
|
65 // Both HandleTouchStart() and SetTargetElement() call this. They can be |
|
66 // called in either order. One will set mCanBePanSet, and the other, mTarget. |
|
67 // We want to actually trigger the activation once both are set. |
|
68 if (!(mTarget && mCanBePanSet)) { |
|
69 return; |
|
70 } |
|
71 |
|
72 // If the touch cannot be a pan, make mTarget :active right away. |
|
73 // Otherwise, wait a bit to see if the user will pan or not. |
|
74 if (!mCanBePan) { |
|
75 SetActive(mTarget); |
|
76 } else { |
|
77 mSetActiveTask = NewRunnableMethod( |
|
78 this, &ActiveElementManager::SetActiveTask, mTarget); |
|
79 MessageLoop::current()->PostDelayedTask( |
|
80 FROM_HERE, mSetActiveTask, sActivationDelayMs); |
|
81 } |
|
82 } |
|
83 |
|
84 void |
|
85 ActiveElementManager::HandlePanStart() |
|
86 { |
|
87 // The user started to pan, so we don't want mTarget to be :active. |
|
88 // Make it not :active, and clear any pending task to make it :active. |
|
89 CancelTask(); |
|
90 ResetActive(); |
|
91 } |
|
92 |
|
93 void |
|
94 ActiveElementManager::HandleTouchEnd(bool aWasClick) |
|
95 { |
|
96 // If the touch was a click, make mTarget :active right away. |
|
97 // nsEventStateManager will reset the active element when processing |
|
98 // the mouse-down event generated by the click. |
|
99 CancelTask(); |
|
100 if (aWasClick) { |
|
101 SetActive(mTarget); |
|
102 } |
|
103 |
|
104 // Clear mTarget for next touch. |
|
105 mTarget = nullptr; |
|
106 } |
|
107 |
|
108 void |
|
109 ActiveElementManager::SetActive(nsIDOMElement* aTarget) |
|
110 { |
|
111 if (mDomUtils) { |
|
112 mDomUtils->SetContentState(aTarget, NS_EVENT_STATE_ACTIVE.GetInternalValue());; |
|
113 } |
|
114 } |
|
115 |
|
116 void |
|
117 ActiveElementManager::ResetActive() |
|
118 { |
|
119 // Clear the :active flag from mTarget by setting it on the document root. |
|
120 if (mTarget) { |
|
121 nsCOMPtr<nsIDOMDocument> doc; |
|
122 mTarget->GetOwnerDocument(getter_AddRefs(doc)); |
|
123 if (doc) { |
|
124 nsCOMPtr<nsIDOMElement> root; |
|
125 doc->GetDocumentElement(getter_AddRefs(root)); |
|
126 if (root) { |
|
127 SetActive(root); |
|
128 } |
|
129 } |
|
130 } |
|
131 } |
|
132 |
|
133 void |
|
134 ActiveElementManager::SetActiveTask(nsIDOMElement* aTarget) |
|
135 { |
|
136 // This gets called from mSetActiveTask's Run() method. The message loop |
|
137 // deletes the task right after running it, so we need to null out |
|
138 // mSetActiveTask to make sure we're not left with a dangling pointer. |
|
139 mSetActiveTask = nullptr; |
|
140 SetActive(aTarget); |
|
141 } |
|
142 |
|
143 void |
|
144 ActiveElementManager::CancelTask() |
|
145 { |
|
146 if (mSetActiveTask) { |
|
147 mSetActiveTask->Cancel(); |
|
148 mSetActiveTask = nullptr; |
|
149 } |
|
150 } |
|
151 |
|
152 } |
|
153 } |