1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsISupports.h"
39 : #include "nsGUIEvent.h"
40 : #include "nsDOMEvent.h"
41 : #include "nsEventListenerManager.h"
42 : #include "nsCaret.h"
43 : #include "nsIDOMNSEvent.h"
44 : #include "nsIDOMEventListener.h"
45 : #include "nsITextControlFrame.h"
46 : #include "nsGkAtoms.h"
47 : #include "nsPIDOMWindow.h"
48 : #include "nsIPrivateDOMEvent.h"
49 : #include "nsIJSEventListener.h"
50 : #include "prmem.h"
51 : #include "nsIScriptGlobalObject.h"
52 : #include "nsIScriptRuntime.h"
53 : #include "nsLayoutUtils.h"
54 : #include "nsINameSpaceManager.h"
55 : #include "nsIContent.h"
56 : #include "mozilla/dom/Element.h"
57 : #include "nsIFrame.h"
58 : #include "nsIView.h"
59 : #include "nsIViewManager.h"
60 : #include "nsCOMPtr.h"
61 : #include "nsIServiceManager.h"
62 : #include "nsIScriptSecurityManager.h"
63 : #include "nsDOMError.h"
64 : #include "nsIJSContextStack.h"
65 : #include "nsIDocument.h"
66 : #include "nsIPresShell.h"
67 : #include "nsMutationEvent.h"
68 : #include "nsIXPConnect.h"
69 : #include "nsDOMCID.h"
70 : #include "nsIScriptObjectOwner.h" // for nsIScriptEventHandlerOwner
71 : #include "nsFocusManager.h"
72 : #include "nsIDOMElement.h"
73 : #include "nsContentUtils.h"
74 : #include "nsJSUtils.h"
75 : #include "nsContentCID.h"
76 : #include "nsEventDispatcher.h"
77 : #include "nsDOMJSUtils.h"
78 : #include "nsDOMScriptObjectHolder.h"
79 : #include "nsDataHashtable.h"
80 : #include "nsCOMArray.h"
81 : #include "nsEventListenerService.h"
82 : #include "nsDOMEvent.h"
83 : #include "nsIContentSecurityPolicy.h"
84 : #include "nsJSEnvironment.h"
85 : #include "xpcpublic.h"
86 : #include "sampler.h"
87 :
88 : using namespace mozilla::dom;
89 :
90 : #define EVENT_TYPE_EQUALS( ls, type, userType ) \
91 : (ls->mEventType == type && \
92 : (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType))
93 :
94 : static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
95 : NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
96 :
97 : static const PRUint32 kAllMutationBits =
98 : NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED |
99 : NS_EVENT_BITS_MUTATION_NODEINSERTED |
100 : NS_EVENT_BITS_MUTATION_NODEREMOVED |
101 : NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT |
102 : NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT |
103 : NS_EVENT_BITS_MUTATION_ATTRMODIFIED |
104 : NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
105 :
106 : static PRUint32
107 0 : MutationBitForEventType(PRUint32 aEventType)
108 : {
109 0 : switch (aEventType) {
110 : case NS_MUTATION_SUBTREEMODIFIED:
111 0 : return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
112 : case NS_MUTATION_NODEINSERTED:
113 0 : return NS_EVENT_BITS_MUTATION_NODEINSERTED;
114 : case NS_MUTATION_NODEREMOVED:
115 0 : return NS_EVENT_BITS_MUTATION_NODEREMOVED;
116 : case NS_MUTATION_NODEREMOVEDFROMDOCUMENT:
117 0 : return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
118 : case NS_MUTATION_NODEINSERTEDINTODOCUMENT:
119 0 : return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
120 : case NS_MUTATION_ATTRMODIFIED:
121 0 : return NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
122 : case NS_MUTATION_CHARACTERDATAMODIFIED:
123 0 : return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
124 : default:
125 : break;
126 : }
127 0 : return 0;
128 : }
129 :
130 : PRUint32 nsEventListenerManager::sCreatedCount = 0;
131 :
132 3349 : nsEventListenerManager::nsEventListenerManager(nsISupports* aTarget) :
133 : mMayHavePaintEventListener(false),
134 : mMayHaveMutationListeners(false),
135 : mMayHaveCapturingListeners(false),
136 : mMayHaveSystemGroupListeners(false),
137 : mMayHaveAudioAvailableEventListener(false),
138 : mMayHaveTouchEventListener(false),
139 : mMayHaveMouseEnterLeaveEventListener(false),
140 : mNoListenerForEvent(0),
141 3349 : mTarget(aTarget)
142 : {
143 3349 : NS_ASSERTION(aTarget, "unexpected null pointer");
144 :
145 3349 : ++sCreatedCount;
146 3349 : }
147 :
148 10041 : nsEventListenerManager::~nsEventListenerManager()
149 : {
150 : // If your code fails this assertion, a possible reason is that
151 : // a class did not call our Disconnect() manually. Note that
152 : // this class can have Disconnect called in one of two ways:
153 : // if it is part of a cycle, then in Unlink() (such a cycle
154 : // would be with one of the listeners, not mTarget which is weak).
155 : // If not part of a cycle, then Disconnect must be called manually,
156 : // typically from the destructor of the owner class (mTarget).
157 : // XXX azakai: Is there any reason to not just call Disconnect
158 : // from right here, if not previously called?
159 3347 : NS_ASSERTION(!mTarget, "didn't call Disconnect");
160 3347 : RemoveAllListeners();
161 :
162 13388 : }
163 :
164 : void
165 6694 : nsEventListenerManager::RemoveAllListeners()
166 : {
167 6694 : mListeners.Clear();
168 6694 : }
169 :
170 : void
171 1364 : nsEventListenerManager::Shutdown()
172 : {
173 1364 : nsDOMEvent::Shutdown();
174 1364 : }
175 :
176 1396 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventListenerManager)
177 :
178 329 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsEventListenerManager, AddRef)
179 329 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsEventListenerManager, Release)
180 :
181 362 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsEventListenerManager)
182 362 : PRUint32 count = tmp->mListeners.Length();
183 796 : for (PRUint32 i = 0; i < count; i++) {
184 434 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mListener");
185 434 : cb.NoteXPCOMChild(tmp->mListeners.ElementAt(i).mListener.get());
186 : }
187 362 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
188 :
189 329 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsEventListenerManager)
190 329 : tmp->Disconnect();
191 329 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
192 :
193 :
194 : nsPIDOMWindow*
195 2 : nsEventListenerManager::GetInnerWindowForTarget()
196 : {
197 4 : nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
198 2 : if (node) {
199 : // XXX sXBL/XBL2 issue -- do we really want the owner here? What
200 : // if that's the XBL document?
201 2 : return node->OwnerDoc()->GetInnerWindow();
202 : }
203 :
204 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
205 0 : if (window) {
206 0 : NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
207 0 : return window;
208 : }
209 :
210 0 : return nsnull;
211 : }
212 :
213 : void
214 4942 : nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
215 : PRUint32 aType,
216 : nsIAtom* aTypeAtom,
217 : PRInt32 aFlags)
218 : {
219 4942 : NS_ABORT_IF_FALSE(aType && aTypeAtom, "Missing type");
220 :
221 4942 : if (!aListener) {
222 0 : return;
223 : }
224 :
225 9884 : nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = aListener;
226 :
227 : nsListenerStruct* ls;
228 4942 : PRUint32 count = mListeners.Length();
229 6593 : for (PRUint32 i = 0; i < count; i++) {
230 1651 : ls = &mListeners.ElementAt(i);
231 1651 : if (ls->mListener == aListener && ls->mFlags == aFlags &&
232 0 : EVENT_TYPE_EQUALS(ls, aType, aTypeAtom)) {
233 : return;
234 : }
235 : }
236 :
237 4942 : mNoListenerForEvent = NS_EVENT_TYPE_NULL;
238 4942 : mNoListenerForEventAtom = nsnull;
239 :
240 4942 : ls = mListeners.AppendElement();
241 4942 : ls->mListener = aListener;
242 4942 : ls->mEventType = aType;
243 4942 : ls->mTypeAtom = aTypeAtom;
244 4942 : ls->mWrappedJS = false;
245 4942 : ls->mFlags = aFlags;
246 4942 : ls->mHandlerIsString = false;
247 :
248 9884 : nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aListener);
249 4942 : if (wjs) {
250 4942 : ls->mWrappedJS = true;
251 : }
252 4942 : if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
253 0 : mMayHaveSystemGroupListeners = true;
254 : }
255 4942 : if (aFlags & NS_EVENT_FLAG_CAPTURE) {
256 2 : mMayHaveCapturingListeners = true;
257 : }
258 :
259 4942 : if (aType == NS_AFTERPAINT) {
260 0 : mMayHavePaintEventListener = true;
261 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
262 0 : if (window) {
263 0 : window->SetHasPaintEventListeners();
264 : }
265 : #ifdef MOZ_MEDIA
266 4942 : } else if (aType == NS_MOZAUDIOAVAILABLE) {
267 0 : mMayHaveAudioAvailableEventListener = true;
268 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
269 0 : if (window) {
270 0 : window->SetHasAudioAvailableEventListeners();
271 : }
272 : #endif // MOZ_MEDIA
273 4942 : } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
274 : // For mutation listeners, we need to update the global bit on the DOM window.
275 : // Otherwise we won't actually fire the mutation event.
276 2 : mMayHaveMutationListeners = true;
277 : // Go from our target to the nearest enclosing DOM window.
278 2 : nsPIDOMWindow* window = GetInnerWindowForTarget();
279 2 : if (window) {
280 : // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
281 : // mutations. nsContentUtils::HasMutationListeners relies on this.
282 : window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
283 : kAllMutationBits :
284 0 : MutationBitForEventType(aType));
285 2 : }
286 4940 : } else if (aTypeAtom == nsGkAtoms::ondeviceorientation ||
287 : aTypeAtom == nsGkAtoms::ondevicemotion) {
288 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
289 0 : if (window)
290 0 : window->SetHasOrientationEventListener();
291 4940 : } else if ((aType >= NS_MOZTOUCH_DOWN && aType <= NS_MOZTOUCH_UP) ||
292 : (aTypeAtom == nsGkAtoms::ontouchstart ||
293 : aTypeAtom == nsGkAtoms::ontouchend ||
294 : aTypeAtom == nsGkAtoms::ontouchmove ||
295 : aTypeAtom == nsGkAtoms::ontouchenter ||
296 : aTypeAtom == nsGkAtoms::ontouchleave ||
297 : aTypeAtom == nsGkAtoms::ontouchcancel)) {
298 0 : mMayHaveTouchEventListener = true;
299 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
300 0 : if (window)
301 0 : window->SetHasTouchEventListeners();
302 4940 : } else if (aTypeAtom == nsGkAtoms::onmouseenter ||
303 : aTypeAtom == nsGkAtoms::onmouseleave) {
304 0 : mMayHaveMouseEnterLeaveEventListener = true;
305 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
306 0 : if (window) {
307 : #ifdef DEBUG
308 0 : nsCOMPtr<nsIDocument> d = do_QueryInterface(window->GetExtantDocument());
309 0 : NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
310 : "Please do not use mouseenter/leave events in chrome. "
311 : "They are slower than mouseover/out!");
312 : #endif
313 0 : window->SetHasMouseEnterLeaveEventListeners();
314 : }
315 : }
316 : }
317 :
318 : void
319 29 : nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
320 : PRUint32 aType,
321 : nsIAtom* aUserType,
322 : PRInt32 aFlags)
323 : {
324 29 : if (!aListener || !aType) {
325 0 : return;
326 : }
327 :
328 : nsListenerStruct* ls;
329 29 : aFlags &= ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
330 :
331 29 : PRUint32 count = mListeners.Length();
332 52 : for (PRUint32 i = 0; i < count; ++i) {
333 52 : ls = &mListeners.ElementAt(i);
334 80 : if (ls->mListener == aListener &&
335 : ((ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) &&
336 28 : EVENT_TYPE_EQUALS(ls, aType, aUserType)) {
337 58 : nsRefPtr<nsEventListenerManager> kungFuDeathGrip = this;
338 29 : mListeners.RemoveElementAt(i);
339 29 : mNoListenerForEvent = NS_EVENT_TYPE_NULL;
340 29 : mNoListenerForEventAtom = nsnull;
341 29 : if (aType == NS_DEVICE_ORIENTATION) {
342 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
343 0 : if (window)
344 0 : window->RemoveOrientationEventListener();
345 : }
346 : break;
347 : }
348 : }
349 : }
350 :
351 : static inline bool
352 14430 : ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent)
353 : {
354 : // This is slightly different from EVENT_TYPE_EQUALS in that it returns
355 : // true even when aEvent->message == NS_USER_DEFINED_EVENT and
356 : // aLs=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are the same
357 : return aEvent->message == NS_USER_DEFINED_EVENT ?
358 13264 : (aLs->mTypeAtom == aEvent->userType) :
359 27694 : (aLs->mEventType == aEvent->message);
360 : }
361 :
362 : void
363 4942 : nsEventListenerManager::AddEventListenerByType(nsIDOMEventListener *aListener,
364 : const nsAString& aType,
365 : PRInt32 aFlags)
366 : {
367 9884 : nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aType);
368 4942 : PRUint32 type = nsContentUtils::GetEventId(atom);
369 4942 : AddEventListener(aListener, type, atom, aFlags);
370 4942 : }
371 :
372 : void
373 29 : nsEventListenerManager::RemoveEventListenerByType(nsIDOMEventListener *aListener,
374 : const nsAString& aType,
375 : PRInt32 aFlags)
376 : {
377 58 : nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aType);
378 29 : PRUint32 type = nsContentUtils::GetEventId(atom);
379 29 : RemoveEventListener(aListener, type, atom, aFlags);
380 29 : }
381 :
382 : nsListenerStruct*
383 0 : nsEventListenerManager::FindJSEventListener(PRUint32 aEventType,
384 : nsIAtom* aTypeAtom)
385 : {
386 : // Run through the listeners for this type and see if a script
387 : // listener is registered
388 : nsListenerStruct *ls;
389 0 : PRUint32 count = mListeners.Length();
390 0 : for (PRUint32 i = 0; i < count; ++i) {
391 0 : ls = &mListeners.ElementAt(i);
392 0 : if (EVENT_TYPE_EQUALS(ls, aEventType, aTypeAtom) &&
393 : ls->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
394 0 : return ls;
395 : }
396 : }
397 0 : return nsnull;
398 : }
399 :
400 : nsresult
401 0 : nsEventListenerManager::SetJSEventListener(nsIScriptContext *aContext,
402 : JSObject* aScopeObject,
403 : nsIAtom* aName,
404 : JSObject *aHandler,
405 : bool aPermitUntrustedEvents,
406 : nsListenerStruct **aListenerStruct)
407 : {
408 0 : nsresult rv = NS_OK;
409 0 : PRUint32 eventType = nsContentUtils::GetEventId(aName);
410 0 : nsListenerStruct* ls = FindJSEventListener(eventType, aName);
411 :
412 0 : if (!ls) {
413 : // If we didn't find a script listener or no listeners existed
414 : // create and add a new one.
415 0 : nsCOMPtr<nsIJSEventListener> scriptListener;
416 : rv = NS_NewJSEventListener(aContext, aScopeObject, mTarget, aName,
417 0 : aHandler, getter_AddRefs(scriptListener));
418 0 : if (NS_SUCCEEDED(rv)) {
419 : AddEventListener(scriptListener, eventType, aName,
420 0 : NS_EVENT_FLAG_BUBBLE | NS_PRIV_EVENT_FLAG_SCRIPT);
421 :
422 0 : ls = FindJSEventListener(eventType, aName);
423 : }
424 : } else {
425 0 : ls->GetJSListener()->SetHandler(aHandler);
426 : }
427 :
428 0 : if (NS_SUCCEEDED(rv) && ls) {
429 : // Set flag to indicate possible need for compilation later
430 0 : ls->mHandlerIsString = !aHandler;
431 0 : if (aPermitUntrustedEvents) {
432 0 : ls->mFlags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
433 : }
434 :
435 0 : *aListenerStruct = ls;
436 : } else {
437 0 : *aListenerStruct = nsnull;
438 : }
439 :
440 0 : return rv;
441 : }
442 :
443 : nsresult
444 0 : nsEventListenerManager::AddScriptEventListener(nsIAtom *aName,
445 : const nsAString& aBody,
446 : PRUint32 aLanguage,
447 : bool aDeferCompilation,
448 : bool aPermitUntrustedEvents)
449 : {
450 0 : NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN,
451 : "Must know the language for the script event listener");
452 :
453 : // |aPermitUntrustedEvents| is set to False for chrome - events
454 : // *generated* from an unknown source are not allowed.
455 : // However, for script languages with no 'sandbox', we want to reject
456 : // such scripts based on the source of their code, not just the source
457 : // of the event.
458 0 : if (aPermitUntrustedEvents &&
459 : aLanguage != nsIProgrammingLanguage::JAVASCRIPT) {
460 0 : NS_WARNING("Discarding non-JS event listener from untrusted source");
461 0 : return NS_ERROR_FAILURE;
462 : }
463 :
464 0 : nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
465 :
466 0 : nsCOMPtr<nsIDocument> doc;
467 :
468 0 : nsCOMPtr<nsIScriptGlobalObject> global;
469 :
470 0 : if (node) {
471 : // Try to get context from doc
472 : // XXX sXBL/XBL2 issue -- do we really want the owner here? What
473 : // if that's the XBL document?
474 0 : global = node->OwnerDoc()->GetScriptGlobalObject();
475 : } else {
476 0 : nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mTarget));
477 0 : if (win) {
478 0 : NS_ASSERTION(win->IsInnerWindow(),
479 : "Event listener added to outer window!");
480 :
481 0 : nsCOMPtr<nsIDOMDocument> domdoc;
482 0 : win->GetDocument(getter_AddRefs(domdoc));
483 0 : doc = do_QueryInterface(domdoc);
484 0 : global = do_QueryInterface(win);
485 : } else {
486 0 : global = do_QueryInterface(mTarget);
487 : }
488 : }
489 :
490 0 : if (!global) {
491 : // This can happen; for example this document might have been
492 : // loaded as data.
493 0 : return NS_OK;
494 : }
495 :
496 0 : nsresult rv = NS_OK;
497 : // return early preventing the event listener from being added
498 : // 'doc' is fetched above
499 0 : if (doc) {
500 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
501 0 : rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
502 0 : NS_ENSURE_SUCCESS(rv, rv);
503 :
504 0 : if (csp) {
505 : bool inlineOK;
506 0 : rv = csp->GetAllowsInlineScript(&inlineOK);
507 0 : NS_ENSURE_SUCCESS(rv, rv);
508 :
509 0 : if ( !inlineOK ) {
510 : // gather information to log with violation report
511 0 : nsIURI* uri = doc->GetDocumentURI();
512 0 : nsCAutoString asciiSpec;
513 0 : if (uri)
514 0 : uri->GetAsciiSpec(asciiSpec);
515 0 : nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN"));
516 0 : aName->ToString(attr);
517 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget));
518 0 : if (domNode)
519 0 : domNode->GetNodeName(tagName);
520 : // build a "script sample" based on what we know about this element
521 0 : scriptSample.Assign(attr);
522 0 : scriptSample.AppendLiteral(" attribute on ");
523 0 : scriptSample.Append(tagName);
524 0 : scriptSample.AppendLiteral(" element");
525 0 : csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
526 0 : NS_ConvertUTF8toUTF16(asciiSpec),
527 : scriptSample,
528 0 : nsnull);
529 0 : return NS_OK;
530 : }
531 : }
532 : }
533 :
534 : // This might be the first reference to this language in the global
535 : // We must init the language before we attempt to fetch its context.
536 0 : if (NS_FAILED(global->EnsureScriptEnvironment(aLanguage))) {
537 0 : NS_WARNING("Failed to setup script environment for this language");
538 : // but fall through and let the inevitable failure below handle it.
539 : }
540 :
541 0 : nsIScriptContext* context = global->GetScriptContext(aLanguage);
542 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
543 :
544 0 : JSObject* scope = global->GetGlobalJSObject();
545 :
546 : nsListenerStruct *ls;
547 : rv = SetJSEventListener(context, scope, aName, nsnull,
548 0 : aPermitUntrustedEvents, &ls);
549 0 : NS_ENSURE_SUCCESS(rv, rv);
550 :
551 0 : if (!aDeferCompilation) {
552 0 : return CompileEventHandlerInternal(ls, true, &aBody);
553 : }
554 :
555 0 : return NS_OK;
556 : }
557 :
558 : void
559 0 : nsEventListenerManager::RemoveScriptEventListener(nsIAtom* aName)
560 : {
561 0 : PRUint32 eventType = nsContentUtils::GetEventId(aName);
562 0 : nsListenerStruct* ls = FindJSEventListener(eventType, aName);
563 :
564 0 : if (ls) {
565 0 : mListeners.RemoveElementAt(PRUint32(ls - &mListeners.ElementAt(0)));
566 0 : mNoListenerForEvent = NS_EVENT_TYPE_NULL;
567 0 : mNoListenerForEventAtom = nsnull;
568 : }
569 0 : }
570 :
571 : nsresult
572 0 : nsEventListenerManager::CompileEventHandlerInternal(nsListenerStruct *aListenerStruct,
573 : bool aNeedsCxPush,
574 : const nsAString* aBody)
575 : {
576 0 : NS_PRECONDITION(aListenerStruct->GetJSListener(),
577 : "Why do we not have a JS listener?");
578 0 : NS_PRECONDITION(aListenerStruct->mHandlerIsString,
579 : "Why are we compiling a non-string JS listener?");
580 :
581 0 : nsresult result = NS_OK;
582 :
583 0 : nsIJSEventListener *listener = aListenerStruct->GetJSListener();
584 0 : NS_ASSERTION(!listener->GetHandler(), "What is there to compile?");
585 :
586 0 : nsIScriptContext *context = listener->GetEventContext();
587 : nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner =
588 0 : do_QueryInterface(mTarget);
589 0 : nsScriptObjectHolder<JSObject> handler(context);
590 :
591 0 : if (handlerOwner) {
592 0 : result = handlerOwner->GetCompiledEventHandler(aListenerStruct->mTypeAtom,
593 0 : handler);
594 0 : if (NS_SUCCEEDED(result) && handler) {
595 0 : aListenerStruct->mHandlerIsString = false;
596 : } else {
597 : // Make sure there's nothing in the holder in the failure case
598 0 : handler.set(nsnull);
599 : }
600 : }
601 :
602 0 : if (aListenerStruct->mHandlerIsString) {
603 : // OK, we didn't find an existing compiled event handler. Flag us
604 : // as not a string so we don't keep trying to compile strings
605 : // which can't be compiled
606 0 : aListenerStruct->mHandlerIsString = false;
607 :
608 : // mTarget may not be an nsIContent if it's a window and we're
609 : // getting an inline event listener forwarded from <html:body> or
610 : // <html:frameset> or <xul:window> or the like.
611 : // XXX I don't like that we have to reference content from
612 : // here. The alternative is to store the event handler string on
613 : // the nsIJSEventListener itself, and that still doesn't address
614 : // the arg names issue.
615 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTarget);
616 0 : nsAutoString handlerBody;
617 0 : const nsAString* body = aBody;
618 0 : if (content && !aBody) {
619 0 : nsIAtom* attrName = aListenerStruct->mTypeAtom;
620 0 : if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGLoad)
621 0 : attrName = nsGkAtoms::onload;
622 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGUnload)
623 0 : attrName = nsGkAtoms::onunload;
624 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGAbort)
625 0 : attrName = nsGkAtoms::onabort;
626 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGError)
627 0 : attrName = nsGkAtoms::onerror;
628 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGResize)
629 0 : attrName = nsGkAtoms::onresize;
630 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGScroll)
631 0 : attrName = nsGkAtoms::onscroll;
632 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGZoom)
633 0 : attrName = nsGkAtoms::onzoom;
634 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onbeginEvent)
635 0 : attrName = nsGkAtoms::onbegin;
636 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onrepeatEvent)
637 0 : attrName = nsGkAtoms::onrepeat;
638 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onendEvent)
639 0 : attrName = nsGkAtoms::onend;
640 :
641 0 : content->GetAttr(kNameSpaceID_None, attrName, handlerBody);
642 0 : body = &handlerBody;
643 : }
644 :
645 0 : PRUint32 lineNo = 0;
646 0 : nsCAutoString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
647 0 : nsCOMPtr<nsIDocument> doc;
648 0 : if (content) {
649 0 : doc = content->OwnerDoc();
650 : } else {
651 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
652 0 : if (win) {
653 0 : doc = do_QueryInterface(win->GetExtantDocument());
654 : }
655 : }
656 :
657 0 : if (doc) {
658 0 : nsIURI *uri = doc->GetDocumentURI();
659 0 : if (uri) {
660 0 : uri->GetSpec(url);
661 0 : lineNo = 1;
662 : }
663 : }
664 :
665 0 : nsCxPusher pusher;
666 0 : if (aNeedsCxPush && !pusher.Push(context->GetNativeContext())) {
667 0 : return NS_ERROR_FAILURE;
668 : }
669 :
670 :
671 0 : if (handlerOwner) {
672 : // Always let the handler owner compile the event
673 : // handler, as it may want to use a special
674 : // context or scope object.
675 0 : result = handlerOwner->CompileEventHandler(context,
676 : aListenerStruct->mTypeAtom,
677 : *body,
678 : url.get(), lineNo,
679 0 : handler);
680 : } else {
681 : PRUint32 argCount;
682 : const char **argNames;
683 : // If no content, then just use kNameSpaceID_None for the
684 : // namespace ID. In practice, it doesn't matter since SVG is
685 : // the only thing with weird arg names and SVG doesn't map event
686 : // listeners to the window.
687 : nsContentUtils::GetEventArgNames(content ?
688 0 : content->GetNameSpaceID() :
689 0 : kNameSpaceID_None,
690 : aListenerStruct->mTypeAtom,
691 0 : &argCount, &argNames);
692 :
693 : result = context->CompileEventHandler(aListenerStruct->mTypeAtom,
694 : argCount, argNames,
695 : *body,
696 : url.get(), lineNo,
697 : SCRIPTVERSION_DEFAULT, // for now?
698 0 : handler);
699 0 : if (result == NS_ERROR_ILLEGAL_VALUE) {
700 0 : NS_WARNING("Probably a syntax error in the event handler!");
701 0 : return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
702 : }
703 0 : NS_ENSURE_SUCCESS(result, result);
704 : }
705 : }
706 :
707 0 : if (handler) {
708 : // Bind it
709 0 : nsScriptObjectHolder<JSObject> boundHandler(context);
710 : context->BindCompiledEventHandler(mTarget, listener->GetEventScope(),
711 0 : handler.get(), boundHandler);
712 0 : listener->SetHandler(boundHandler.get());
713 : }
714 :
715 0 : return result;
716 : }
717 :
718 : nsresult
719 4151 : nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
720 : nsIDOMEventListener* aListener,
721 : nsIDOMEvent* aDOMEvent,
722 : nsIDOMEventTarget* aCurrentTarget,
723 : PRUint32 aPhaseFlags,
724 : nsCxPusher* aPusher)
725 : {
726 4151 : nsresult result = NS_OK;
727 :
728 : // If this is a script handler and we haven't yet
729 : // compiled the event handler itself
730 4151 : if ((aListenerStruct->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) &&
731 : aListenerStruct->mHandlerIsString) {
732 0 : nsIJSEventListener *jslistener = aListenerStruct->GetJSListener();
733 : result = CompileEventHandlerInternal(aListenerStruct,
734 0 : jslistener->GetEventContext() !=
735 0 : aPusher->GetCurrentScriptContext(),
736 0 : nsnull);
737 : }
738 :
739 4151 : if (NS_SUCCEEDED(result)) {
740 : // nsIDOMEvent::currentTarget is set in nsEventDispatcher.
741 4151 : result = aListener->HandleEvent(aDOMEvent);
742 : }
743 :
744 4151 : return result;
745 : }
746 :
747 : /**
748 : * Causes a check for event listeners and processing by them if they exist.
749 : * @param an event listener
750 : */
751 :
752 : void
753 8411 : nsEventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
754 : nsEvent* aEvent,
755 : nsIDOMEvent** aDOMEvent,
756 : nsIDOMEventTarget* aCurrentTarget,
757 : PRUint32 aFlags,
758 : nsEventStatus* aEventStatus,
759 : nsCxPusher* aPusher)
760 : {
761 16822 : SAMPLE_LABEL("nsEventListenerManager", "HandleEventInternal");
762 : //Set the value of the internal PreventDefault flag properly based on aEventStatus
763 8411 : if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
764 7 : aEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT;
765 : }
766 :
767 16822 : nsAutoTObserverArray<nsListenerStruct, 2>::EndLimitedIterator iter(mListeners);
768 16822 : nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
769 8411 : bool hasListener = false;
770 31252 : while (iter.HasMore()) {
771 14430 : if (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY) {
772 0 : break;
773 : }
774 14430 : nsListenerStruct* ls = &iter.GetNext();
775 : // Check that the phase is same in event and event listener.
776 : // Handle only trusted events, except when listener permits untrusted events.
777 14430 : if (ListenerCanHandle(ls, aEvent)) {
778 4153 : hasListener = true;
779 : // XXX The (mFlags & aFlags) test here seems fragile. Shouldn't we
780 : // specifically only test the capture/bubble flags.
781 4153 : if ((ls->mFlags & aFlags & ~NS_EVENT_FLAG_SYSTEM_EVENT) &&
782 : (ls->mFlags & NS_EVENT_FLAG_SYSTEM_EVENT) ==
783 : (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) &&
784 : (NS_IS_TRUSTED_EVENT(aEvent) ||
785 : ls->mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED)) {
786 4151 : if (!*aDOMEvent) {
787 : nsEventDispatcher::CreateEvent(aPresContext, aEvent,
788 0 : EmptyString(), aDOMEvent);
789 : }
790 4151 : if (*aDOMEvent) {
791 4151 : if (!aEvent->currentTarget) {
792 4151 : aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent();
793 4151 : if (!aEvent->currentTarget) {
794 0 : break;
795 : }
796 : }
797 8302 : nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = ls->mListener;
798 4151 : if (aPusher->RePush(aCurrentTarget)) {
799 4151 : if (NS_FAILED(HandleEventSubType(ls, ls->mListener, *aDOMEvent,
800 : aCurrentTarget, aFlags,
801 : aPusher))) {
802 9 : aEvent->flags |= NS_EVENT_FLAG_EXCEPTION_THROWN;
803 : }
804 : }
805 : }
806 : }
807 : }
808 : }
809 :
810 8411 : aEvent->currentTarget = nsnull;
811 :
812 8411 : if (!hasListener) {
813 4258 : mNoListenerForEvent = aEvent->message;
814 4258 : mNoListenerForEventAtom = aEvent->userType;
815 : }
816 :
817 8411 : if (aEvent->flags & NS_EVENT_FLAG_NO_DEFAULT) {
818 38 : *aEventStatus = nsEventStatus_eConsumeNoDefault;
819 : }
820 8411 : }
821 :
822 : void
823 3347 : nsEventListenerManager::Disconnect()
824 : {
825 3347 : mTarget = nsnull;
826 3347 : RemoveAllListeners();
827 3347 : }
828 :
829 : void
830 4942 : nsEventListenerManager::AddEventListener(const nsAString& aType,
831 : nsIDOMEventListener* aListener,
832 : bool aUseCapture,
833 : bool aWantsUntrusted)
834 : {
835 4942 : PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
836 :
837 4942 : if (aWantsUntrusted) {
838 2 : flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
839 : }
840 :
841 4942 : return AddEventListenerByType(aListener, aType, flags);
842 : }
843 :
844 : void
845 29 : nsEventListenerManager::RemoveEventListener(const nsAString& aType,
846 : nsIDOMEventListener* aListener,
847 : bool aUseCapture)
848 : {
849 29 : PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
850 :
851 29 : RemoveEventListenerByType(aListener, aType, flags);
852 29 : }
853 :
854 : bool
855 2 : nsEventListenerManager::HasMutationListeners()
856 : {
857 2 : if (mMayHaveMutationListeners) {
858 2 : PRUint32 count = mListeners.Length();
859 2 : for (PRUint32 i = 0; i < count; ++i) {
860 2 : nsListenerStruct* ls = &mListeners.ElementAt(i);
861 2 : if (ls->mEventType >= NS_MUTATION_START &&
862 : ls->mEventType <= NS_MUTATION_END) {
863 2 : return true;
864 : }
865 : }
866 : }
867 :
868 0 : return false;
869 : }
870 :
871 : PRUint32
872 0 : nsEventListenerManager::MutationListenerBits()
873 : {
874 0 : PRUint32 bits = 0;
875 0 : if (mMayHaveMutationListeners) {
876 0 : PRUint32 count = mListeners.Length();
877 0 : for (PRUint32 i = 0; i < count; ++i) {
878 0 : nsListenerStruct* ls = &mListeners.ElementAt(i);
879 0 : if (ls->mEventType >= NS_MUTATION_START &&
880 : ls->mEventType <= NS_MUTATION_END) {
881 0 : if (ls->mEventType == NS_MUTATION_SUBTREEMODIFIED) {
882 0 : return kAllMutationBits;
883 : }
884 0 : bits |= MutationBitForEventType(ls->mEventType);
885 : }
886 : }
887 : }
888 0 : return bits;
889 : }
890 :
891 : bool
892 1609 : nsEventListenerManager::HasListenersFor(const nsAString& aEventName)
893 : {
894 3218 : nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
895 :
896 1609 : PRUint32 count = mListeners.Length();
897 4803 : for (PRUint32 i = 0; i < count; ++i) {
898 3194 : nsListenerStruct* ls = &mListeners.ElementAt(i);
899 3194 : if (ls->mTypeAtom == atom) {
900 0 : return true;
901 : }
902 : }
903 1609 : return false;
904 : }
905 :
906 : bool
907 0 : nsEventListenerManager::HasListeners()
908 : {
909 0 : return !mListeners.IsEmpty();
910 : }
911 :
912 : nsresult
913 0 : nsEventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList)
914 : {
915 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mTarget);
916 0 : NS_ENSURE_STATE(target);
917 0 : aList->Clear();
918 0 : PRUint32 count = mListeners.Length();
919 0 : for (PRUint32 i = 0; i < count; ++i) {
920 0 : const nsListenerStruct& ls = mListeners.ElementAt(i);
921 0 : bool capturing = !!(ls.mFlags & NS_EVENT_FLAG_CAPTURE);
922 0 : bool systemGroup = !!(ls.mFlags & NS_EVENT_FLAG_SYSTEM_EVENT);
923 0 : bool allowsUntrusted = !!(ls.mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED);
924 : // If this is a script handler and we haven't yet
925 : // compiled the event handler itself go ahead and compile it
926 0 : if ((ls.mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) && ls.mHandlerIsString) {
927 : CompileEventHandlerInternal(const_cast<nsListenerStruct*>(&ls),
928 0 : true, nsnull);
929 : }
930 : const nsDependentSubstring& eventType =
931 0 : Substring(nsDependentAtomString(ls.mTypeAtom), 2);
932 : nsRefPtr<nsEventListenerInfo> info =
933 : new nsEventListenerInfo(eventType, ls.mListener, capturing,
934 0 : allowsUntrusted, systemGroup);
935 0 : NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
936 0 : aList->AppendObject(info);
937 : }
938 0 : return NS_OK;
939 : }
940 :
941 : bool
942 0 : nsEventListenerManager::HasUnloadListeners()
943 : {
944 0 : PRUint32 count = mListeners.Length();
945 0 : for (PRUint32 i = 0; i < count; ++i) {
946 0 : nsListenerStruct* ls = &mListeners.ElementAt(i);
947 0 : if (ls->mEventType == NS_PAGE_UNLOAD ||
948 : ls->mEventType == NS_BEFORE_PAGE_UNLOAD) {
949 0 : return true;
950 : }
951 : }
952 0 : return false;
953 : }
954 :
955 : nsresult
956 0 : nsEventListenerManager::SetJSEventListenerToJsval(nsIAtom *aEventName,
957 : JSContext *cx,
958 : JSObject* aScope,
959 : const jsval & v)
960 : {
961 : JSObject *handler;
962 0 : if (JSVAL_IS_PRIMITIVE(v) ||
963 0 : !JS_ObjectIsCallable(cx, handler = JSVAL_TO_OBJECT(v))) {
964 0 : RemoveScriptEventListener(aEventName);
965 0 : return NS_OK;
966 : }
967 :
968 : // We might not have a script context, e.g. if we're setting a listener
969 : // on a dead Window.
970 0 : nsIScriptContext *context = nsJSUtils::GetStaticScriptContext(cx, aScope);
971 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
972 :
973 0 : JSObject *scope = ::JS_GetGlobalForObject(cx, aScope);
974 : // Untrusted events are always permitted for non-chrome script
975 : // handlers.
976 : nsListenerStruct *ignored;
977 : return SetJSEventListener(context, scope, aEventName, handler,
978 0 : !nsContentUtils::IsCallerChrome(), &ignored);
979 : }
980 :
981 : void
982 0 : nsEventListenerManager::GetJSEventListener(nsIAtom *aEventName, jsval *vp)
983 : {
984 0 : PRUint32 eventType = nsContentUtils::GetEventId(aEventName);
985 0 : nsListenerStruct* ls = FindJSEventListener(eventType, aEventName);
986 :
987 0 : *vp = JSVAL_NULL;
988 :
989 0 : if (!ls) {
990 0 : return;
991 : }
992 :
993 0 : nsIJSEventListener *listener = ls->GetJSListener();
994 0 : if (listener->GetEventContext()->GetScriptTypeID() !=
995 : nsIProgrammingLanguage::JAVASCRIPT) {
996 : // Not JS, so no point doing anything with it.
997 0 : return;
998 : }
999 :
1000 0 : if (ls->mHandlerIsString) {
1001 0 : CompileEventHandlerInternal(ls, true, nsnull);
1002 : }
1003 :
1004 0 : *vp = OBJECT_TO_JSVAL(listener->GetHandler());
1005 : }
1006 :
1007 : size_t
1008 0 : nsEventListenerManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
1009 : const
1010 : {
1011 0 : size_t n = aMallocSizeOf(this);
1012 0 : n += mListeners.SizeOfExcludingThis(aMallocSizeOf);
1013 0 : PRUint32 count = mListeners.Length();
1014 0 : for (PRUint32 i = 0; i < count; ++i) {
1015 0 : nsIJSEventListener* jsl = mListeners.ElementAt(i).GetJSListener();
1016 0 : if (jsl) {
1017 0 : n += jsl->SizeOfIncludingThis(aMallocSizeOf);
1018 : }
1019 : }
1020 0 : return n;
1021 : }
1022 :
1023 : void
1024 226 : nsEventListenerManager::UnmarkGrayJSListeners()
1025 : {
1026 226 : PRUint32 count = mListeners.Length();
1027 677 : for (PRUint32 i = 0; i < count; ++i) {
1028 451 : const nsListenerStruct& ls = mListeners.ElementAt(i);
1029 451 : nsIJSEventListener* jsl = ls.GetJSListener();
1030 451 : if (jsl) {
1031 0 : xpc_UnmarkGrayObject(jsl->GetHandler());
1032 0 : xpc_UnmarkGrayObject(jsl->GetEventScope());
1033 451 : } else if (ls.mWrappedJS) {
1034 902 : nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(ls.mListener);
1035 451 : xpc_UnmarkGrayObject(wjs);
1036 : }
1037 : }
1038 4414 : }
|