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 3170 : 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 3170 : mTarget(aTarget)
142 : {
143 3170 : NS_ASSERTION(aTarget, "unexpected null pointer");
144 :
145 3170 : ++sCreatedCount;
146 3170 : }
147 :
148 9504 : 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 3168 : NS_ASSERTION(!mTarget, "didn't call Disconnect");
160 3168 : RemoveAllListeners();
161 :
162 12672 : }
163 :
164 : void
165 6336 : nsEventListenerManager::RemoveAllListeners()
166 : {
167 6336 : mListeners.Clear();
168 6336 : }
169 :
170 : void
171 1403 : nsEventListenerManager::Shutdown()
172 : {
173 1403 : nsDOMEvent::Shutdown();
174 1403 : }
175 :
176 1464 : 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 337 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsEventListenerManager)
182 337 : PRUint32 count = tmp->mListeners.Length();
183 721 : for (PRUint32 i = 0; i < count; i++) {
184 384 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mListener");
185 384 : cb.NoteXPCOMChild(tmp->mListeners.ElementAt(i).mListener.get());
186 : }
187 337 : 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 4584 : nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
215 : PRUint32 aType,
216 : nsIAtom* aTypeAtom,
217 : PRInt32 aFlags)
218 : {
219 4584 : NS_ABORT_IF_FALSE(aType && aTypeAtom, "Missing type");
220 :
221 4584 : if (!aListener) {
222 0 : return;
223 : }
224 :
225 9168 : nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = aListener;
226 :
227 : nsListenerStruct* ls;
228 4584 : PRUint32 count = mListeners.Length();
229 6056 : for (PRUint32 i = 0; i < count; i++) {
230 1472 : ls = &mListeners.ElementAt(i);
231 1472 : if (ls->mListener == aListener && ls->mFlags == aFlags &&
232 0 : EVENT_TYPE_EQUALS(ls, aType, aTypeAtom)) {
233 : return;
234 : }
235 : }
236 :
237 4584 : mNoListenerForEvent = NS_EVENT_TYPE_NULL;
238 4584 : mNoListenerForEventAtom = nsnull;
239 :
240 4584 : ls = mListeners.AppendElement();
241 4584 : ls->mListener = aListener;
242 4584 : ls->mEventType = aType;
243 4584 : ls->mTypeAtom = aTypeAtom;
244 4584 : ls->mWrappedJS = false;
245 4584 : ls->mFlags = aFlags;
246 4584 : ls->mHandlerIsString = false;
247 :
248 9168 : nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aListener);
249 4584 : if (wjs) {
250 4584 : ls->mWrappedJS = true;
251 : }
252 4584 : if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
253 0 : mMayHaveSystemGroupListeners = true;
254 : }
255 4584 : if (aFlags & NS_EVENT_FLAG_CAPTURE) {
256 2 : mMayHaveCapturingListeners = true;
257 : }
258 :
259 4584 : 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 4584 : } 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 4584 : } 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 4582 : } else if (aTypeAtom == nsGkAtoms::ondeviceorientation ||
287 : aTypeAtom == nsGkAtoms::ondevicemotion) {
288 0 : nsPIDOMWindow* window = GetInnerWindowForTarget();
289 0 : if (window)
290 0 : window->SetHasOrientationEventListener();
291 4582 : } 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 4582 : } 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 14072 : 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 12906 : (aLs->mTypeAtom == aEvent->userType) :
359 26978 : (aLs->mEventType == aEvent->message);
360 : }
361 :
362 : void
363 4584 : nsEventListenerManager::AddEventListenerByType(nsIDOMEventListener *aListener,
364 : const nsAString& aType,
365 : PRInt32 aFlags)
366 : {
367 9168 : nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aType);
368 4584 : PRUint32 type = nsContentUtils::GetEventId(atom);
369 4584 : AddEventListener(aListener, type, atom, aFlags);
370 4584 : }
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 : doc = node->OwnerDoc();
475 0 : global = doc->GetScriptGlobalObject();
476 : } else {
477 0 : nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mTarget));
478 0 : if (win) {
479 0 : NS_ASSERTION(win->IsInnerWindow(),
480 : "Event listener added to outer window!");
481 :
482 0 : nsCOMPtr<nsIDOMDocument> domdoc;
483 0 : win->GetDocument(getter_AddRefs(domdoc));
484 0 : doc = do_QueryInterface(domdoc);
485 0 : global = do_QueryInterface(win);
486 : } else {
487 0 : global = do_QueryInterface(mTarget);
488 : }
489 : }
490 :
491 0 : if (!global) {
492 : // This can happen; for example this document might have been
493 : // loaded as data.
494 0 : return NS_OK;
495 : }
496 :
497 0 : nsresult rv = NS_OK;
498 : // return early preventing the event listener from being added
499 : // 'doc' is fetched above
500 0 : if (doc) {
501 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
502 0 : rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
503 0 : NS_ENSURE_SUCCESS(rv, rv);
504 :
505 0 : if (csp) {
506 : bool inlineOK;
507 0 : rv = csp->GetAllowsInlineScript(&inlineOK);
508 0 : NS_ENSURE_SUCCESS(rv, rv);
509 :
510 0 : if ( !inlineOK ) {
511 : // gather information to log with violation report
512 0 : nsIURI* uri = doc->GetDocumentURI();
513 0 : nsCAutoString asciiSpec;
514 0 : if (uri)
515 0 : uri->GetAsciiSpec(asciiSpec);
516 0 : nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN"));
517 0 : aName->ToString(attr);
518 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget));
519 0 : if (domNode)
520 0 : domNode->GetNodeName(tagName);
521 : // build a "script sample" based on what we know about this element
522 0 : scriptSample.Assign(attr);
523 0 : scriptSample.AppendLiteral(" attribute on ");
524 0 : scriptSample.Append(tagName);
525 0 : scriptSample.AppendLiteral(" element");
526 0 : csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
527 0 : NS_ConvertUTF8toUTF16(asciiSpec),
528 : scriptSample,
529 0 : nsnull);
530 0 : return NS_OK;
531 : }
532 : }
533 : }
534 :
535 : // This might be the first reference to this language in the global
536 : // We must init the language before we attempt to fetch its context.
537 0 : if (NS_FAILED(global->EnsureScriptEnvironment(aLanguage))) {
538 0 : NS_WARNING("Failed to setup script environment for this language");
539 : // but fall through and let the inevitable failure below handle it.
540 : }
541 :
542 0 : nsIScriptContext* context = global->GetScriptContext(aLanguage);
543 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
544 :
545 0 : JSObject* scope = global->GetGlobalJSObject();
546 :
547 : nsListenerStruct *ls;
548 : rv = SetJSEventListener(context, scope, aName, nsnull,
549 0 : aPermitUntrustedEvents, &ls);
550 0 : NS_ENSURE_SUCCESS(rv, rv);
551 :
552 0 : if (!aDeferCompilation) {
553 0 : return CompileEventHandlerInternal(ls, true, &aBody);
554 : }
555 :
556 0 : return NS_OK;
557 : }
558 :
559 : void
560 0 : nsEventListenerManager::RemoveScriptEventListener(nsIAtom* aName)
561 : {
562 0 : PRUint32 eventType = nsContentUtils::GetEventId(aName);
563 0 : nsListenerStruct* ls = FindJSEventListener(eventType, aName);
564 :
565 0 : if (ls) {
566 0 : mListeners.RemoveElementAt(PRUint32(ls - &mListeners.ElementAt(0)));
567 0 : mNoListenerForEvent = NS_EVENT_TYPE_NULL;
568 0 : mNoListenerForEventAtom = nsnull;
569 : }
570 0 : }
571 :
572 : nsresult
573 0 : nsEventListenerManager::CompileEventHandlerInternal(nsListenerStruct *aListenerStruct,
574 : bool aNeedsCxPush,
575 : const nsAString* aBody)
576 : {
577 0 : NS_PRECONDITION(aListenerStruct->GetJSListener(),
578 : "Why do we not have a JS listener?");
579 0 : NS_PRECONDITION(aListenerStruct->mHandlerIsString,
580 : "Why are we compiling a non-string JS listener?");
581 :
582 0 : nsresult result = NS_OK;
583 :
584 0 : nsIJSEventListener *listener = aListenerStruct->GetJSListener();
585 0 : NS_ASSERTION(!listener->GetHandler(), "What is there to compile?");
586 :
587 0 : nsIScriptContext *context = listener->GetEventContext();
588 : nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner =
589 0 : do_QueryInterface(mTarget);
590 0 : nsScriptObjectHolder<JSObject> handler(context);
591 :
592 0 : if (handlerOwner) {
593 0 : result = handlerOwner->GetCompiledEventHandler(aListenerStruct->mTypeAtom,
594 0 : handler);
595 0 : if (NS_SUCCEEDED(result) && handler) {
596 0 : aListenerStruct->mHandlerIsString = false;
597 : } else {
598 : // Make sure there's nothing in the holder in the failure case
599 0 : handler.set(nsnull);
600 : }
601 : }
602 :
603 0 : if (aListenerStruct->mHandlerIsString) {
604 : // OK, we didn't find an existing compiled event handler. Flag us
605 : // as not a string so we don't keep trying to compile strings
606 : // which can't be compiled
607 0 : aListenerStruct->mHandlerIsString = false;
608 :
609 : // mTarget may not be an nsIContent if it's a window and we're
610 : // getting an inline event listener forwarded from <html:body> or
611 : // <html:frameset> or <xul:window> or the like.
612 : // XXX I don't like that we have to reference content from
613 : // here. The alternative is to store the event handler string on
614 : // the nsIJSEventListener itself, and that still doesn't address
615 : // the arg names issue.
616 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTarget);
617 0 : nsAutoString handlerBody;
618 0 : const nsAString* body = aBody;
619 0 : if (content && !aBody) {
620 0 : nsIAtom* attrName = aListenerStruct->mTypeAtom;
621 0 : if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGLoad)
622 0 : attrName = nsGkAtoms::onload;
623 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGUnload)
624 0 : attrName = nsGkAtoms::onunload;
625 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGAbort)
626 0 : attrName = nsGkAtoms::onabort;
627 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGError)
628 0 : attrName = nsGkAtoms::onerror;
629 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGResize)
630 0 : attrName = nsGkAtoms::onresize;
631 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGScroll)
632 0 : attrName = nsGkAtoms::onscroll;
633 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onSVGZoom)
634 0 : attrName = nsGkAtoms::onzoom;
635 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onbeginEvent)
636 0 : attrName = nsGkAtoms::onbegin;
637 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onrepeatEvent)
638 0 : attrName = nsGkAtoms::onrepeat;
639 0 : else if (aListenerStruct->mTypeAtom == nsGkAtoms::onendEvent)
640 0 : attrName = nsGkAtoms::onend;
641 :
642 0 : content->GetAttr(kNameSpaceID_None, attrName, handlerBody);
643 0 : body = &handlerBody;
644 : }
645 :
646 0 : PRUint32 lineNo = 0;
647 0 : nsCAutoString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
648 0 : nsCOMPtr<nsIDocument> doc;
649 0 : if (content) {
650 0 : doc = content->OwnerDoc();
651 : } else {
652 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
653 0 : if (win) {
654 0 : doc = do_QueryInterface(win->GetExtantDocument());
655 : }
656 : }
657 :
658 0 : if (doc) {
659 0 : nsIURI *uri = doc->GetDocumentURI();
660 0 : if (uri) {
661 0 : uri->GetSpec(url);
662 0 : lineNo = 1;
663 : }
664 : }
665 :
666 0 : nsCxPusher pusher;
667 0 : if (aNeedsCxPush && !pusher.Push(context->GetNativeContext())) {
668 0 : return NS_ERROR_FAILURE;
669 : }
670 :
671 :
672 0 : if (handlerOwner) {
673 : // Always let the handler owner compile the event
674 : // handler, as it may want to use a special
675 : // context or scope object.
676 0 : result = handlerOwner->CompileEventHandler(context,
677 : aListenerStruct->mTypeAtom,
678 : *body,
679 : url.get(), lineNo,
680 0 : handler);
681 : } else {
682 : PRUint32 argCount;
683 : const char **argNames;
684 : // If no content, then just use kNameSpaceID_None for the
685 : // namespace ID. In practice, it doesn't matter since SVG is
686 : // the only thing with weird arg names and SVG doesn't map event
687 : // listeners to the window.
688 : nsContentUtils::GetEventArgNames(content ?
689 0 : content->GetNameSpaceID() :
690 0 : kNameSpaceID_None,
691 : aListenerStruct->mTypeAtom,
692 0 : &argCount, &argNames);
693 :
694 : result = context->CompileEventHandler(aListenerStruct->mTypeAtom,
695 : argCount, argNames,
696 : *body,
697 : url.get(), lineNo,
698 : SCRIPTVERSION_DEFAULT, // for now?
699 0 : handler);
700 0 : if (result == NS_ERROR_ILLEGAL_VALUE) {
701 0 : NS_WARNING("Probably a syntax error in the event handler!");
702 0 : return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
703 : }
704 0 : NS_ENSURE_SUCCESS(result, result);
705 : }
706 : }
707 :
708 0 : if (handler) {
709 : // Bind it
710 0 : nsScriptObjectHolder<JSObject> boundHandler(context);
711 : context->BindCompiledEventHandler(mTarget, listener->GetEventScope(),
712 0 : handler.get(), boundHandler);
713 0 : listener->SetHandler(boundHandler.get());
714 : }
715 :
716 0 : return result;
717 : }
718 :
719 : nsresult
720 3972 : nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
721 : nsIDOMEventListener* aListener,
722 : nsIDOMEvent* aDOMEvent,
723 : nsIDOMEventTarget* aCurrentTarget,
724 : PRUint32 aPhaseFlags,
725 : nsCxPusher* aPusher)
726 : {
727 3972 : nsresult result = NS_OK;
728 :
729 : // If this is a script handler and we haven't yet
730 : // compiled the event handler itself
731 3972 : if ((aListenerStruct->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) &&
732 : aListenerStruct->mHandlerIsString) {
733 0 : nsIJSEventListener *jslistener = aListenerStruct->GetJSListener();
734 : result = CompileEventHandlerInternal(aListenerStruct,
735 0 : jslistener->GetEventContext() !=
736 0 : aPusher->GetCurrentScriptContext(),
737 0 : nsnull);
738 : }
739 :
740 3972 : if (NS_SUCCEEDED(result)) {
741 : // nsIDOMEvent::currentTarget is set in nsEventDispatcher.
742 3972 : result = aListener->HandleEvent(aDOMEvent);
743 : }
744 :
745 3972 : return result;
746 : }
747 :
748 : /**
749 : * Causes a check for event listeners and processing by them if they exist.
750 : * @param an event listener
751 : */
752 :
753 : void
754 8232 : nsEventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
755 : nsEvent* aEvent,
756 : nsIDOMEvent** aDOMEvent,
757 : nsIDOMEventTarget* aCurrentTarget,
758 : PRUint32 aFlags,
759 : nsEventStatus* aEventStatus,
760 : nsCxPusher* aPusher)
761 : {
762 16464 : SAMPLE_LABEL("nsEventListenerManager", "HandleEventInternal");
763 : //Set the value of the internal PreventDefault flag properly based on aEventStatus
764 8232 : if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
765 7 : aEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT;
766 : }
767 :
768 16464 : nsAutoTObserverArray<nsListenerStruct, 2>::EndLimitedIterator iter(mListeners);
769 16464 : nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
770 8232 : bool hasListener = false;
771 30536 : while (iter.HasMore()) {
772 14072 : if (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY) {
773 0 : break;
774 : }
775 14072 : nsListenerStruct* ls = &iter.GetNext();
776 : // Check that the phase is same in event and event listener.
777 : // Handle only trusted events, except when listener permits untrusted events.
778 14072 : if (ListenerCanHandle(ls, aEvent)) {
779 3974 : hasListener = true;
780 : // XXX The (mFlags & aFlags) test here seems fragile. Shouldn't we
781 : // specifically only test the capture/bubble flags.
782 3974 : if ((ls->mFlags & aFlags & ~NS_EVENT_FLAG_SYSTEM_EVENT) &&
783 : (ls->mFlags & NS_EVENT_FLAG_SYSTEM_EVENT) ==
784 : (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) &&
785 : (NS_IS_TRUSTED_EVENT(aEvent) ||
786 : ls->mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED)) {
787 3972 : if (!*aDOMEvent) {
788 : nsEventDispatcher::CreateEvent(aPresContext, aEvent,
789 0 : EmptyString(), aDOMEvent);
790 : }
791 3972 : if (*aDOMEvent) {
792 3972 : if (!aEvent->currentTarget) {
793 3972 : aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent();
794 3972 : if (!aEvent->currentTarget) {
795 0 : break;
796 : }
797 : }
798 7944 : nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = ls->mListener;
799 3972 : if (aPusher->RePush(aCurrentTarget)) {
800 3972 : if (NS_FAILED(HandleEventSubType(ls, ls->mListener, *aDOMEvent,
801 : aCurrentTarget, aFlags,
802 : aPusher))) {
803 9 : aEvent->flags |= NS_EVENT_FLAG_EXCEPTION_THROWN;
804 : }
805 : }
806 : }
807 : }
808 : }
809 : }
810 :
811 8232 : aEvent->currentTarget = nsnull;
812 :
813 8232 : if (!hasListener) {
814 4258 : mNoListenerForEvent = aEvent->message;
815 4258 : mNoListenerForEventAtom = aEvent->userType;
816 : }
817 :
818 8232 : if (aEvent->flags & NS_EVENT_FLAG_NO_DEFAULT) {
819 38 : *aEventStatus = nsEventStatus_eConsumeNoDefault;
820 : }
821 8232 : }
822 :
823 : void
824 3168 : nsEventListenerManager::Disconnect()
825 : {
826 3168 : mTarget = nsnull;
827 3168 : RemoveAllListeners();
828 3168 : }
829 :
830 : void
831 4584 : nsEventListenerManager::AddEventListener(const nsAString& aType,
832 : nsIDOMEventListener* aListener,
833 : bool aUseCapture,
834 : bool aWantsUntrusted)
835 : {
836 4584 : PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
837 :
838 4584 : if (aWantsUntrusted) {
839 2 : flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
840 : }
841 :
842 4584 : return AddEventListenerByType(aListener, aType, flags);
843 : }
844 :
845 : void
846 29 : nsEventListenerManager::RemoveEventListener(const nsAString& aType,
847 : nsIDOMEventListener* aListener,
848 : bool aUseCapture)
849 : {
850 29 : PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
851 :
852 29 : RemoveEventListenerByType(aListener, aType, flags);
853 29 : }
854 :
855 : bool
856 2 : nsEventListenerManager::HasMutationListeners()
857 : {
858 2 : if (mMayHaveMutationListeners) {
859 2 : PRUint32 count = mListeners.Length();
860 2 : for (PRUint32 i = 0; i < count; ++i) {
861 2 : nsListenerStruct* ls = &mListeners.ElementAt(i);
862 2 : if (ls->mEventType >= NS_MUTATION_START &&
863 : ls->mEventType <= NS_MUTATION_END) {
864 2 : return true;
865 : }
866 : }
867 : }
868 :
869 0 : return false;
870 : }
871 :
872 : PRUint32
873 0 : nsEventListenerManager::MutationListenerBits()
874 : {
875 0 : PRUint32 bits = 0;
876 0 : if (mMayHaveMutationListeners) {
877 0 : PRUint32 count = mListeners.Length();
878 0 : for (PRUint32 i = 0; i < count; ++i) {
879 0 : nsListenerStruct* ls = &mListeners.ElementAt(i);
880 0 : if (ls->mEventType >= NS_MUTATION_START &&
881 : ls->mEventType <= NS_MUTATION_END) {
882 0 : if (ls->mEventType == NS_MUTATION_SUBTREEMODIFIED) {
883 0 : return kAllMutationBits;
884 : }
885 0 : bits |= MutationBitForEventType(ls->mEventType);
886 : }
887 : }
888 : }
889 0 : return bits;
890 : }
891 :
892 : bool
893 1609 : nsEventListenerManager::HasListenersFor(const nsAString& aEventName)
894 : {
895 3218 : nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
896 :
897 1609 : PRUint32 count = mListeners.Length();
898 4803 : for (PRUint32 i = 0; i < count; ++i) {
899 3194 : nsListenerStruct* ls = &mListeners.ElementAt(i);
900 3194 : if (ls->mTypeAtom == atom) {
901 0 : return true;
902 : }
903 : }
904 1609 : return false;
905 : }
906 :
907 : bool
908 0 : nsEventListenerManager::HasListeners()
909 : {
910 0 : return !mListeners.IsEmpty();
911 : }
912 :
913 : nsresult
914 0 : nsEventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList)
915 : {
916 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mTarget);
917 0 : NS_ENSURE_STATE(target);
918 0 : aList->Clear();
919 0 : PRUint32 count = mListeners.Length();
920 0 : for (PRUint32 i = 0; i < count; ++i) {
921 0 : const nsListenerStruct& ls = mListeners.ElementAt(i);
922 0 : bool capturing = !!(ls.mFlags & NS_EVENT_FLAG_CAPTURE);
923 0 : bool systemGroup = !!(ls.mFlags & NS_EVENT_FLAG_SYSTEM_EVENT);
924 0 : bool allowsUntrusted = !!(ls.mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED);
925 : // If this is a script handler and we haven't yet
926 : // compiled the event handler itself go ahead and compile it
927 0 : if ((ls.mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) && ls.mHandlerIsString) {
928 : CompileEventHandlerInternal(const_cast<nsListenerStruct*>(&ls),
929 0 : true, nsnull);
930 : }
931 : const nsDependentSubstring& eventType =
932 0 : Substring(nsDependentAtomString(ls.mTypeAtom), 2);
933 : nsRefPtr<nsEventListenerInfo> info =
934 : new nsEventListenerInfo(eventType, ls.mListener, capturing,
935 0 : allowsUntrusted, systemGroup);
936 0 : NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
937 0 : aList->AppendObject(info);
938 : }
939 0 : return NS_OK;
940 : }
941 :
942 : bool
943 0 : nsEventListenerManager::HasUnloadListeners()
944 : {
945 0 : PRUint32 count = mListeners.Length();
946 0 : for (PRUint32 i = 0; i < count; ++i) {
947 0 : nsListenerStruct* ls = &mListeners.ElementAt(i);
948 0 : if (ls->mEventType == NS_PAGE_UNLOAD ||
949 : ls->mEventType == NS_BEFORE_PAGE_UNLOAD) {
950 0 : return true;
951 : }
952 : }
953 0 : return false;
954 : }
955 :
956 : nsresult
957 0 : nsEventListenerManager::SetJSEventListenerToJsval(nsIAtom *aEventName,
958 : JSContext *cx,
959 : JSObject* aScope,
960 : const jsval & v)
961 : {
962 : JSObject *handler;
963 0 : if (JSVAL_IS_PRIMITIVE(v) ||
964 0 : !JS_ObjectIsCallable(cx, handler = JSVAL_TO_OBJECT(v))) {
965 0 : RemoveScriptEventListener(aEventName);
966 0 : return NS_OK;
967 : }
968 :
969 : // We might not have a script context, e.g. if we're setting a listener
970 : // on a dead Window.
971 0 : nsIScriptContext *context = nsJSUtils::GetStaticScriptContext(cx, aScope);
972 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
973 :
974 0 : JSObject *scope = ::JS_GetGlobalForObject(cx, aScope);
975 : // Untrusted events are always permitted for non-chrome script
976 : // handlers.
977 : nsListenerStruct *ignored;
978 : return SetJSEventListener(context, scope, aEventName, handler,
979 0 : !nsContentUtils::IsCallerChrome(), &ignored);
980 : }
981 :
982 : void
983 0 : nsEventListenerManager::GetJSEventListener(nsIAtom *aEventName, jsval *vp)
984 : {
985 0 : PRUint32 eventType = nsContentUtils::GetEventId(aEventName);
986 0 : nsListenerStruct* ls = FindJSEventListener(eventType, aEventName);
987 :
988 0 : *vp = JSVAL_NULL;
989 :
990 0 : if (!ls) {
991 0 : return;
992 : }
993 :
994 0 : nsIJSEventListener *listener = ls->GetJSListener();
995 0 : if (listener->GetEventContext()->GetScriptTypeID() !=
996 : nsIProgrammingLanguage::JAVASCRIPT) {
997 : // Not JS, so no point doing anything with it.
998 0 : return;
999 : }
1000 :
1001 0 : if (ls->mHandlerIsString) {
1002 0 : CompileEventHandlerInternal(ls, true, nsnull);
1003 : }
1004 :
1005 0 : *vp = OBJECT_TO_JSVAL(listener->GetHandler());
1006 : }
1007 :
1008 : size_t
1009 0 : nsEventListenerManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
1010 : const
1011 : {
1012 0 : size_t n = aMallocSizeOf(this);
1013 0 : n += mListeners.SizeOfExcludingThis(aMallocSizeOf);
1014 0 : PRUint32 count = mListeners.Length();
1015 0 : for (PRUint32 i = 0; i < count; ++i) {
1016 0 : nsIJSEventListener* jsl = mListeners.ElementAt(i).GetJSListener();
1017 0 : if (jsl) {
1018 0 : n += jsl->SizeOfIncludingThis(aMallocSizeOf);
1019 : }
1020 : }
1021 0 : return n;
1022 : }
1023 :
1024 : void
1025 187 : nsEventListenerManager::UnmarkGrayJSListeners()
1026 : {
1027 187 : PRUint32 count = mListeners.Length();
1028 561 : for (PRUint32 i = 0; i < count; ++i) {
1029 374 : const nsListenerStruct& ls = mListeners.ElementAt(i);
1030 374 : nsIJSEventListener* jsl = ls.GetJSListener();
1031 374 : if (jsl) {
1032 0 : xpc_UnmarkGrayObject(jsl->GetHandler());
1033 0 : xpc_UnmarkGrayObject(jsl->GetEventScope());
1034 374 : } else if (ls.mWrappedJS) {
1035 748 : nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(ls.mListener);
1036 374 : xpc_UnmarkGrayObject(wjs);
1037 : }
1038 : }
1039 4579 : }
|