|
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 "nsContentUtils.h" |
|
7 #include "nsIDocument.h" |
|
8 #include "prprf.h" |
|
9 #include "nsGlobalWindow.h" |
|
10 #include "ScriptSettings.h" |
|
11 #include "mozilla/DOMEventTargetHelper.h" |
|
12 #include "mozilla/EventDispatcher.h" |
|
13 #include "mozilla/EventListenerManager.h" |
|
14 #include "mozilla/Likely.h" |
|
15 |
|
16 namespace mozilla { |
|
17 |
|
18 using namespace dom; |
|
19 |
|
20 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMEventTargetHelper) |
|
21 |
|
22 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMEventTargetHelper) |
|
23 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
|
24 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
25 |
|
26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(DOMEventTargetHelper) |
|
27 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
|
28 char name[512]; |
|
29 nsAutoString uri; |
|
30 if (tmp->mOwnerWindow && tmp->mOwnerWindow->GetExtantDoc()) { |
|
31 tmp->mOwnerWindow->GetExtantDoc()->GetDocumentURI(uri); |
|
32 } |
|
33 PR_snprintf(name, sizeof(name), "DOMEventTargetHelper %s", |
|
34 NS_ConvertUTF16toUTF8(uri).get()); |
|
35 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
|
36 } else { |
|
37 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(DOMEventTargetHelper, tmp->mRefCnt.get()) |
|
38 } |
|
39 |
|
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) |
|
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
43 |
|
44 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMEventTargetHelper) |
|
45 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
|
46 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) |
|
47 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
48 |
|
49 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(DOMEventTargetHelper) |
|
50 if (tmp->IsBlack()) { |
|
51 if (tmp->mListenerManager) { |
|
52 tmp->mListenerManager->MarkForCC(); |
|
53 } |
|
54 return true; |
|
55 } |
|
56 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
|
57 |
|
58 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(DOMEventTargetHelper) |
|
59 return tmp->IsBlackAndDoesNotNeedTracing(tmp); |
|
60 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
|
61 |
|
62 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(DOMEventTargetHelper) |
|
63 return tmp->IsBlack(); |
|
64 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
|
65 |
|
66 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMEventTargetHelper) |
|
67 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
68 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
69 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) |
|
70 NS_INTERFACE_MAP_ENTRY(dom::EventTarget) |
|
71 NS_INTERFACE_MAP_ENTRY(DOMEventTargetHelper) |
|
72 NS_INTERFACE_MAP_END |
|
73 |
|
74 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMEventTargetHelper) |
|
75 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(DOMEventTargetHelper, |
|
76 LastRelease()) |
|
77 |
|
78 NS_IMPL_DOMTARGET_DEFAULTS(DOMEventTargetHelper) |
|
79 |
|
80 DOMEventTargetHelper::~DOMEventTargetHelper() |
|
81 { |
|
82 if (nsPIDOMWindow* owner = GetOwner()) { |
|
83 static_cast<nsGlobalWindow*>(owner)->RemoveEventTargetObject(this); |
|
84 } |
|
85 if (mListenerManager) { |
|
86 mListenerManager->Disconnect(); |
|
87 } |
|
88 ReleaseWrapper(this); |
|
89 } |
|
90 |
|
91 void |
|
92 DOMEventTargetHelper::BindToOwner(nsPIDOMWindow* aOwner) |
|
93 { |
|
94 MOZ_ASSERT(!aOwner || aOwner->IsInnerWindow()); |
|
95 nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOwner); |
|
96 BindToOwner(glob); |
|
97 } |
|
98 |
|
99 void |
|
100 DOMEventTargetHelper::BindToOwner(nsIGlobalObject* aOwner) |
|
101 { |
|
102 if (mParentObject) { |
|
103 if (mOwnerWindow) { |
|
104 static_cast<nsGlobalWindow*>(mOwnerWindow)->RemoveEventTargetObject(this); |
|
105 mOwnerWindow = nullptr; |
|
106 } |
|
107 mParentObject = nullptr; |
|
108 mHasOrHasHadOwnerWindow = false; |
|
109 } |
|
110 if (aOwner) { |
|
111 mParentObject = aOwner; |
|
112 // Let's cache the result of this QI for fast access and off main thread usage |
|
113 mOwnerWindow = nsCOMPtr<nsPIDOMWindow>(do_QueryInterface(aOwner)).get(); |
|
114 if (mOwnerWindow) { |
|
115 mHasOrHasHadOwnerWindow = true; |
|
116 static_cast<nsGlobalWindow*>(mOwnerWindow)->AddEventTargetObject(this); |
|
117 } |
|
118 } |
|
119 } |
|
120 |
|
121 void |
|
122 DOMEventTargetHelper::BindToOwner(DOMEventTargetHelper* aOther) |
|
123 { |
|
124 if (mOwnerWindow) { |
|
125 static_cast<nsGlobalWindow*>(mOwnerWindow)->RemoveEventTargetObject(this); |
|
126 mOwnerWindow = nullptr; |
|
127 mParentObject = nullptr; |
|
128 mHasOrHasHadOwnerWindow = false; |
|
129 } |
|
130 if (aOther) { |
|
131 mHasOrHasHadOwnerWindow = aOther->HasOrHasHadOwner(); |
|
132 if (aOther->GetParentObject()) { |
|
133 mParentObject = aOther->GetParentObject(); |
|
134 // Let's cache the result of this QI for fast access and off main thread usage |
|
135 mOwnerWindow = nsCOMPtr<nsPIDOMWindow>(do_QueryInterface(mParentObject)).get(); |
|
136 if (mOwnerWindow) { |
|
137 mHasOrHasHadOwnerWindow = true; |
|
138 static_cast<nsGlobalWindow*>(mOwnerWindow)->AddEventTargetObject(this); |
|
139 } |
|
140 } |
|
141 } |
|
142 } |
|
143 |
|
144 void |
|
145 DOMEventTargetHelper::DisconnectFromOwner() |
|
146 { |
|
147 mOwnerWindow = nullptr; |
|
148 mParentObject = nullptr; |
|
149 // Event listeners can't be handled anymore, so we can release them here. |
|
150 if (mListenerManager) { |
|
151 mListenerManager->Disconnect(); |
|
152 mListenerManager = nullptr; |
|
153 } |
|
154 } |
|
155 |
|
156 NS_IMETHODIMP |
|
157 DOMEventTargetHelper::RemoveEventListener(const nsAString& aType, |
|
158 nsIDOMEventListener* aListener, |
|
159 bool aUseCapture) |
|
160 { |
|
161 EventListenerManager* elm = GetExistingListenerManager(); |
|
162 if (elm) { |
|
163 elm->RemoveEventListener(aType, aListener, aUseCapture); |
|
164 } |
|
165 |
|
166 return NS_OK; |
|
167 } |
|
168 |
|
169 NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(DOMEventTargetHelper) |
|
170 |
|
171 NS_IMETHODIMP |
|
172 DOMEventTargetHelper::AddEventListener(const nsAString& aType, |
|
173 nsIDOMEventListener* aListener, |
|
174 bool aUseCapture, |
|
175 bool aWantsUntrusted, |
|
176 uint8_t aOptionalArgc) |
|
177 { |
|
178 NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1, |
|
179 "Won't check if this is chrome, you want to set " |
|
180 "aWantsUntrusted to false or make the aWantsUntrusted " |
|
181 "explicit by making aOptionalArgc non-zero."); |
|
182 |
|
183 if (aOptionalArgc < 2) { |
|
184 nsresult rv = WantsUntrusted(&aWantsUntrusted); |
|
185 NS_ENSURE_SUCCESS(rv, rv); |
|
186 } |
|
187 |
|
188 EventListenerManager* elm = GetOrCreateListenerManager(); |
|
189 NS_ENSURE_STATE(elm); |
|
190 elm->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted); |
|
191 return NS_OK; |
|
192 } |
|
193 |
|
194 void |
|
195 DOMEventTargetHelper::AddEventListener(const nsAString& aType, |
|
196 EventListener* aListener, |
|
197 bool aUseCapture, |
|
198 const Nullable<bool>& aWantsUntrusted, |
|
199 ErrorResult& aRv) |
|
200 { |
|
201 bool wantsUntrusted; |
|
202 if (aWantsUntrusted.IsNull()) { |
|
203 nsresult rv = WantsUntrusted(&wantsUntrusted); |
|
204 if (NS_FAILED(rv)) { |
|
205 aRv.Throw(rv); |
|
206 return; |
|
207 } |
|
208 } else { |
|
209 wantsUntrusted = aWantsUntrusted.Value(); |
|
210 } |
|
211 |
|
212 EventListenerManager* elm = GetOrCreateListenerManager(); |
|
213 if (!elm) { |
|
214 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
215 return; |
|
216 } |
|
217 elm->AddEventListener(aType, aListener, aUseCapture, wantsUntrusted); |
|
218 } |
|
219 |
|
220 NS_IMETHODIMP |
|
221 DOMEventTargetHelper::AddSystemEventListener(const nsAString& aType, |
|
222 nsIDOMEventListener* aListener, |
|
223 bool aUseCapture, |
|
224 bool aWantsUntrusted, |
|
225 uint8_t aOptionalArgc) |
|
226 { |
|
227 NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1, |
|
228 "Won't check if this is chrome, you want to set " |
|
229 "aWantsUntrusted to false or make the aWantsUntrusted " |
|
230 "explicit by making aOptionalArgc non-zero."); |
|
231 |
|
232 if (aOptionalArgc < 2) { |
|
233 nsresult rv = WantsUntrusted(&aWantsUntrusted); |
|
234 NS_ENSURE_SUCCESS(rv, rv); |
|
235 } |
|
236 |
|
237 return NS_AddSystemEventListener(this, aType, aListener, aUseCapture, |
|
238 aWantsUntrusted); |
|
239 } |
|
240 |
|
241 NS_IMETHODIMP |
|
242 DOMEventTargetHelper::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal) |
|
243 { |
|
244 nsEventStatus status = nsEventStatus_eIgnore; |
|
245 nsresult rv = |
|
246 EventDispatcher::DispatchDOMEvent(this, nullptr, aEvent, nullptr, &status); |
|
247 |
|
248 *aRetVal = (status != nsEventStatus_eConsumeNoDefault); |
|
249 return rv; |
|
250 } |
|
251 |
|
252 nsresult |
|
253 DOMEventTargetHelper::DispatchTrustedEvent(const nsAString& aEventName) |
|
254 { |
|
255 nsCOMPtr<nsIDOMEvent> event; |
|
256 NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); |
|
257 nsresult rv = event->InitEvent(aEventName, false, false); |
|
258 NS_ENSURE_SUCCESS(rv, rv); |
|
259 |
|
260 return DispatchTrustedEvent(event); |
|
261 } |
|
262 |
|
263 nsresult |
|
264 DOMEventTargetHelper::DispatchTrustedEvent(nsIDOMEvent* event) |
|
265 { |
|
266 event->SetTrusted(true); |
|
267 |
|
268 bool dummy; |
|
269 return DispatchEvent(event, &dummy); |
|
270 } |
|
271 |
|
272 nsresult |
|
273 DOMEventTargetHelper::SetEventHandler(nsIAtom* aType, |
|
274 JSContext* aCx, |
|
275 const JS::Value& aValue) |
|
276 { |
|
277 nsRefPtr<EventHandlerNonNull> handler; |
|
278 JS::Rooted<JSObject*> callable(aCx); |
|
279 if (aValue.isObject() && |
|
280 JS_ObjectIsCallable(aCx, callable = &aValue.toObject())) { |
|
281 handler = new EventHandlerNonNull(callable, dom::GetIncumbentGlobal()); |
|
282 } |
|
283 SetEventHandler(aType, EmptyString(), handler); |
|
284 return NS_OK; |
|
285 } |
|
286 |
|
287 void |
|
288 DOMEventTargetHelper::GetEventHandler(nsIAtom* aType, |
|
289 JSContext* aCx, |
|
290 JS::Value* aValue) |
|
291 { |
|
292 EventHandlerNonNull* handler = GetEventHandler(aType, EmptyString()); |
|
293 if (handler) { |
|
294 *aValue = JS::ObjectValue(*handler->Callable()); |
|
295 } else { |
|
296 *aValue = JS::NullValue(); |
|
297 } |
|
298 } |
|
299 |
|
300 nsresult |
|
301 DOMEventTargetHelper::PreHandleEvent(EventChainPreVisitor& aVisitor) |
|
302 { |
|
303 aVisitor.mCanHandle = true; |
|
304 aVisitor.mParentTarget = nullptr; |
|
305 return NS_OK; |
|
306 } |
|
307 |
|
308 nsresult |
|
309 DOMEventTargetHelper::PostHandleEvent(EventChainPostVisitor& aVisitor) |
|
310 { |
|
311 return NS_OK; |
|
312 } |
|
313 |
|
314 nsresult |
|
315 DOMEventTargetHelper::DispatchDOMEvent(WidgetEvent* aEvent, |
|
316 nsIDOMEvent* aDOMEvent, |
|
317 nsPresContext* aPresContext, |
|
318 nsEventStatus* aEventStatus) |
|
319 { |
|
320 return EventDispatcher::DispatchDOMEvent(this, aEvent, aDOMEvent, |
|
321 aPresContext, aEventStatus); |
|
322 } |
|
323 |
|
324 EventListenerManager* |
|
325 DOMEventTargetHelper::GetOrCreateListenerManager() |
|
326 { |
|
327 if (!mListenerManager) { |
|
328 mListenerManager = new EventListenerManager(this); |
|
329 } |
|
330 |
|
331 return mListenerManager; |
|
332 } |
|
333 |
|
334 EventListenerManager* |
|
335 DOMEventTargetHelper::GetExistingListenerManager() const |
|
336 { |
|
337 return mListenerManager; |
|
338 } |
|
339 |
|
340 nsIScriptContext* |
|
341 DOMEventTargetHelper::GetContextForEventHandlers(nsresult* aRv) |
|
342 { |
|
343 *aRv = CheckInnerWindowCorrectness(); |
|
344 if (NS_FAILED(*aRv)) { |
|
345 return nullptr; |
|
346 } |
|
347 nsPIDOMWindow* owner = GetOwner(); |
|
348 return owner ? static_cast<nsGlobalWindow*>(owner)->GetContextInternal() |
|
349 : nullptr; |
|
350 } |
|
351 |
|
352 nsresult |
|
353 DOMEventTargetHelper::WantsUntrusted(bool* aRetVal) |
|
354 { |
|
355 nsresult rv; |
|
356 nsIScriptContext* context = GetContextForEventHandlers(&rv); |
|
357 NS_ENSURE_SUCCESS(rv, rv); |
|
358 nsCOMPtr<nsIDocument> doc = |
|
359 nsContentUtils::GetDocumentFromScriptContext(context); |
|
360 // We can let listeners on workers to always handle all the events. |
|
361 *aRetVal = (doc && !nsContentUtils::IsChromeDoc(doc)) || !NS_IsMainThread(); |
|
362 return rv; |
|
363 } |
|
364 |
|
365 void |
|
366 DOMEventTargetHelper::EventListenerAdded(nsIAtom* aType) |
|
367 { |
|
368 ErrorResult rv; |
|
369 EventListenerWasAdded(Substring(nsDependentAtomString(aType), 2), rv); |
|
370 } |
|
371 |
|
372 void |
|
373 DOMEventTargetHelper::EventListenerRemoved(nsIAtom* aType) |
|
374 { |
|
375 ErrorResult rv; |
|
376 EventListenerWasRemoved(Substring(nsDependentAtomString(aType), 2), rv); |
|
377 } |
|
378 |
|
379 } // namespace mozilla |