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 : * Olli Pettay (Olli.Pettay@helsinki.fi)
19 : * Portions created by the Initial Developer are Copyright (C) 2006
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 "nsEventDispatcher.h"
39 : #include "nsDOMEvent.h"
40 : #include "nsIDOMEventTarget.h"
41 : #include "nsPresContext.h"
42 : #include "nsIPrivateDOMEvent.h"
43 : #include "nsEventListenerManager.h"
44 : #include "nsContentUtils.h"
45 : #include "nsDOMError.h"
46 : #include "mozilla/FunctionTimer.h"
47 : #include "nsMutationEvent.h"
48 : #include NEW_H
49 : #include "nsFixedSizeAllocator.h"
50 : #include "nsINode.h"
51 : #include "nsPIDOMWindow.h"
52 : #include "nsDOMPopStateEvent.h"
53 : #include "nsDOMHashChangeEvent.h"
54 : #include "nsFrameLoader.h"
55 : #include "nsDOMTouchEvent.h"
56 : #include "nsDOMStorage.h"
57 :
58 : #define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0)
59 : #define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1)
60 : #define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2)
61 :
62 : static nsEventTargetChainItem* gCachedETCI = nsnull;
63 :
64 : // nsEventTargetChainItem represents a single item in the event target chain.
65 : class nsEventTargetChainItem
66 26270 : {
67 : private:
68 : nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
69 : nsEventTargetChainItem* aChild = nsnull);
70 :
71 : public:
72 26270 : static nsEventTargetChainItem* Create(nsFixedSizeAllocator* aAllocator,
73 : nsIDOMEventTarget* aTarget,
74 : nsEventTargetChainItem* aChild = nsnull)
75 : {
76 26270 : void* place = nsnull;
77 26270 : if (gCachedETCI) {
78 25810 : place = gCachedETCI;
79 25810 : gCachedETCI = gCachedETCI->mNext;
80 : } else {
81 460 : place = aAllocator->Alloc(sizeof(nsEventTargetChainItem));
82 : }
83 : return place
84 26270 : ? ::new (place) nsEventTargetChainItem(aTarget, aChild)
85 52540 : : nsnull;
86 : }
87 :
88 16694 : static void Destroy(nsFixedSizeAllocator* aAllocator,
89 : nsEventTargetChainItem* aItem)
90 : {
91 : // ::Destroy deletes ancestor chain.
92 16694 : nsEventTargetChainItem* item = aItem;
93 16694 : if (item->mChild) {
94 0 : item->mChild->mParent = nsnull;
95 0 : item->mChild = nsnull;
96 : }
97 59658 : while (item) {
98 26270 : nsEventTargetChainItem* parent = item->mParent;
99 26270 : item->~nsEventTargetChainItem();
100 26270 : item->mNext = gCachedETCI;
101 26270 : gCachedETCI = item;
102 26270 : --sCurrentEtciCount;
103 26270 : item = parent;
104 : }
105 16694 : }
106 :
107 26270 : bool IsValid()
108 : {
109 26270 : NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!");
110 26270 : return !!(mTarget);
111 : }
112 :
113 38304 : nsIDOMEventTarget* GetNewTarget()
114 : {
115 38304 : return mNewTarget;
116 : }
117 :
118 16694 : void SetNewTarget(nsIDOMEventTarget* aNewTarget)
119 : {
120 16694 : mNewTarget = aNewTarget;
121 16694 : }
122 :
123 26270 : void SetForceContentDispatch(bool aForce)
124 : {
125 26270 : if (aForce) {
126 6367 : mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH;
127 : } else {
128 19903 : mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH;
129 : }
130 26270 : }
131 :
132 0 : bool ForceContentDispatch()
133 : {
134 0 : return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH);
135 : }
136 :
137 26270 : void SetWantsWillHandleEvent(bool aWants)
138 : {
139 26270 : if (aWants) {
140 0 : mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT;
141 : } else {
142 26270 : mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT;
143 : }
144 26270 : }
145 :
146 52708 : bool WantsWillHandleEvent()
147 : {
148 52708 : return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT);
149 : }
150 :
151 26270 : void SetMayHaveListenerManager(bool aMayHave)
152 : {
153 26270 : if (aMayHave) {
154 26214 : mFlags |= NS_TARGET_CHAIN_MAY_HAVE_MANAGER;
155 : } else {
156 56 : mFlags &= ~NS_TARGET_CHAIN_MAY_HAVE_MANAGER;
157 : }
158 26270 : }
159 :
160 42627 : bool MayHaveListenerManager()
161 : {
162 42627 : return !!(mFlags & NS_TARGET_CHAIN_MAY_HAVE_MANAGER);
163 : }
164 :
165 31735 : nsIDOMEventTarget* CurrentTarget()
166 : {
167 31735 : return mTarget;
168 : }
169 :
170 : /**
171 : * Dispatches event through the event target chain.
172 : * Handles capture, target and bubble phases both in default
173 : * and system event group and calls also PostHandleEvent for each
174 : * item in the chain.
175 : */
176 : nsresult HandleEventTargetChain(nsEventChainPostVisitor& aVisitor,
177 : PRUint32 aFlags,
178 : nsDispatchingCallback* aCallback,
179 : bool aMayHaveNewListenerManagers,
180 : nsCxPusher* aPusher);
181 :
182 : /**
183 : * Resets aVisitor object and calls PreHandleEvent.
184 : * Copies mItemFlags and mItemData to the current nsEventTargetChainItem.
185 : */
186 : nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
187 :
188 : /**
189 : * If the current item in the event target chain has an event listener
190 : * manager, this method calls nsEventListenerManager::HandleEvent().
191 : */
192 52708 : nsresult HandleEvent(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags,
193 : bool aMayHaveNewListenerManagers,
194 : nsCxPusher* aPusher)
195 : {
196 52708 : if (WantsWillHandleEvent()) {
197 0 : mTarget->WillHandleEvent(aVisitor);
198 : }
199 52708 : if (aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
200 0 : return NS_OK;
201 : }
202 52708 : if (!mManager) {
203 42627 : if (!MayHaveListenerManager() && !aMayHaveNewListenerManagers) {
204 140 : return NS_OK;
205 : }
206 : mManager =
207 42487 : static_cast<nsEventListenerManager*>(mTarget->GetListenerManager(false));
208 : }
209 52568 : if (mManager) {
210 20366 : NS_ASSERTION(aVisitor.mEvent->currentTarget == nsnull,
211 : "CurrentTarget should be null!");
212 : mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
213 : &aVisitor.mDOMEvent,
214 : CurrentTarget(), aFlags,
215 : &aVisitor.mEventStatus,
216 20366 : aPusher);
217 20366 : NS_ASSERTION(aVisitor.mEvent->currentTarget == nsnull,
218 : "CurrentTarget should be null!");
219 : }
220 52568 : return NS_OK;
221 : }
222 :
223 : /**
224 : * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent.
225 : */
226 : nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor,
227 : nsCxPusher* aPusher);
228 :
229 16004 : static PRUint32 MaxEtciCount() { return sMaxEtciCount; }
230 :
231 1364 : static void ResetMaxEtciCount()
232 : {
233 1364 : NS_ASSERTION(!sCurrentEtciCount, "Wrong time to call ResetMaxEtciCount()!");
234 1364 : sMaxEtciCount = 0;
235 1364 : }
236 :
237 : nsCOMPtr<nsIDOMEventTarget> mTarget;
238 : nsEventTargetChainItem* mChild;
239 : union {
240 : nsEventTargetChainItem* mParent;
241 : // This is used only when caching ETCI objects.
242 : nsEventTargetChainItem* mNext;
243 : };
244 : PRUint16 mFlags;
245 : PRUint16 mItemFlags;
246 : nsCOMPtr<nsISupports> mItemData;
247 : // Event retargeting must happen whenever mNewTarget is non-null.
248 : nsCOMPtr<nsIDOMEventTarget> mNewTarget;
249 : // Cache mTarget's event listener manager.
250 : nsRefPtr<nsEventListenerManager> mManager;
251 :
252 : static PRUint32 sMaxEtciCount;
253 : static PRUint32 sCurrentEtciCount;
254 : };
255 :
256 : PRUint32 nsEventTargetChainItem::sMaxEtciCount = 0;
257 : PRUint32 nsEventTargetChainItem::sCurrentEtciCount = 0;
258 :
259 26270 : nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
260 : nsEventTargetChainItem* aChild)
261 26270 : : mChild(aChild), mParent(nsnull), mFlags(0), mItemFlags(0)
262 : {
263 26270 : mTarget = aTarget->GetTargetForEventTargetChain();
264 26270 : if (mChild) {
265 9576 : mChild->mParent = this;
266 : }
267 :
268 26270 : if (++sCurrentEtciCount > sMaxEtciCount) {
269 460 : sMaxEtciCount = sCurrentEtciCount;
270 : }
271 26270 : }
272 :
273 : nsresult
274 26270 : nsEventTargetChainItem::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
275 : {
276 26270 : aVisitor.Reset();
277 26270 : nsresult rv = mTarget->PreHandleEvent(aVisitor);
278 26270 : SetForceContentDispatch(aVisitor.mForceContentDispatch);
279 26270 : SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent);
280 26270 : SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager);
281 26270 : mItemFlags = aVisitor.mItemFlags;
282 26270 : mItemData = aVisitor.mItemData;
283 26270 : return rv;
284 : }
285 :
286 : nsresult
287 16788 : nsEventTargetChainItem::PostHandleEvent(nsEventChainPostVisitor& aVisitor,
288 : nsCxPusher* aPusher)
289 : {
290 16788 : aPusher->Pop();
291 16788 : aVisitor.mItemFlags = mItemFlags;
292 16788 : aVisitor.mItemData = mItemData;
293 16788 : mTarget->PostHandleEvent(aVisitor);
294 16788 : return NS_OK;
295 : }
296 :
297 : nsresult
298 33388 : nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags,
299 : nsDispatchingCallback* aCallback,
300 : bool aMayHaveNewListenerManagers,
301 : nsCxPusher* aPusher)
302 : {
303 33388 : PRUint32 createdELMs = nsEventListenerManager::sCreatedCount;
304 : // Save the target so that it can be restored later.
305 66776 : nsCOMPtr<nsIDOMEventTarget> firstTarget = aVisitor.mEvent->target;
306 :
307 : // Capture
308 33388 : nsEventTargetChainItem* item = this;
309 33388 : aVisitor.mEvent->flags |= NS_EVENT_FLAG_CAPTURE;
310 33388 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
311 85928 : while (item->mChild) {
312 38304 : if ((!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
313 0 : item->ForceContentDispatch()) &&
314 19152 : !(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH)) {
315 : item->HandleEvent(aVisitor, aFlags & NS_EVENT_CAPTURE_MASK,
316 : aMayHaveNewListenerManagers ||
317 : createdELMs != nsEventListenerManager::sCreatedCount,
318 19152 : aPusher);
319 : }
320 :
321 19152 : if (item->GetNewTarget()) {
322 : // item is at anonymous boundary. Need to retarget for the child items.
323 0 : nsEventTargetChainItem* nextTarget = item->mChild;
324 0 : while (nextTarget) {
325 0 : nsIDOMEventTarget* newTarget = nextTarget->GetNewTarget();
326 0 : if (newTarget) {
327 0 : aVisitor.mEvent->target = newTarget;
328 0 : break;
329 : }
330 0 : nextTarget = nextTarget->mChild;
331 : }
332 : }
333 :
334 19152 : item = item->mChild;
335 : }
336 :
337 : // Target
338 33388 : aVisitor.mEvent->flags |= NS_EVENT_FLAG_BUBBLE;
339 66776 : if (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) &&
340 33388 : (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
341 0 : item->ForceContentDispatch())) {
342 : // FIXME Should use aFlags & NS_EVENT_BUBBLE_MASK because capture phase
343 : // event listeners should not be fired. But it breaks at least
344 : // <xul:dialog>'s buttons. Bug 235441.
345 : item->HandleEvent(aVisitor, aFlags,
346 : aMayHaveNewListenerManagers ||
347 : createdELMs != nsEventListenerManager::sCreatedCount,
348 33388 : aPusher);
349 : }
350 33388 : if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
351 16694 : item->PostHandleEvent(aVisitor, aPusher);
352 : }
353 :
354 : // Bubble
355 33388 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_CAPTURE;
356 33388 : item = item->mParent;
357 85928 : while (item) {
358 19152 : nsIDOMEventTarget* newTarget = item->GetNewTarget();
359 19152 : if (newTarget) {
360 : // Item is at anonymous boundary. Need to retarget for the current item
361 : // and for parent items.
362 0 : aVisitor.mEvent->target = newTarget;
363 : }
364 :
365 19152 : if (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_CANT_BUBBLE) || newTarget) {
366 376 : if ((!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
367 0 : item->ForceContentDispatch()) &&
368 188 : !(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH)) {
369 : item->HandleEvent(aVisitor, aFlags & NS_EVENT_BUBBLE_MASK,
370 : createdELMs != nsEventListenerManager::sCreatedCount,
371 168 : aPusher);
372 : }
373 188 : if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
374 94 : item->PostHandleEvent(aVisitor, aPusher);
375 : }
376 : }
377 19152 : item = item->mParent;
378 : }
379 33388 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
380 :
381 33388 : if (!(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT)) {
382 : // Dispatch to the system event group. Make sure to clear the
383 : // STOP_DISPATCH flag since this resets for each event group.
384 : aVisitor.mEvent->flags &=
385 16694 : ~(NS_EVENT_FLAG_STOP_DISPATCH | NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY);
386 :
387 : // Setting back the original target of the event.
388 16694 : aVisitor.mEvent->target = aVisitor.mEvent->originalTarget;
389 :
390 : // Special handling if PresShell (or some other caller)
391 : // used a callback object.
392 16694 : if (aCallback) {
393 0 : aPusher->Pop();
394 0 : aCallback->HandleEvent(aVisitor);
395 : }
396 :
397 : // Retarget for system event group (which does the default handling too).
398 : // Setting back the target which was used also for default event group.
399 16694 : aVisitor.mEvent->target = firstTarget;
400 : HandleEventTargetChain(aVisitor, aFlags | NS_EVENT_FLAG_SYSTEM_EVENT,
401 : aCallback,
402 : createdELMs != nsEventListenerManager::sCreatedCount,
403 16694 : aPusher);
404 :
405 : // After dispatch, clear all the propagation flags so that
406 : // system group listeners don't affect to the event.
407 : aVisitor.mEvent->flags &=
408 16694 : ~(NS_EVENT_FLAG_STOP_DISPATCH | NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY);
409 : }
410 :
411 33388 : return NS_OK;
412 : }
413 :
414 : #define NS_CHAIN_POOL_SIZE 128
415 :
416 : class ChainItemPool {
417 : public:
418 16694 : ChainItemPool() {
419 16694 : if (!sEtciPool) {
420 301 : sEtciPool = new nsFixedSizeAllocator();
421 301 : if (sEtciPool) {
422 : static const size_t kBucketSizes[] = { sizeof(nsEventTargetChainItem) };
423 : static const PRInt32 kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t);
424 : static const PRInt32 kInitialPoolSize =
425 : NS_SIZE_IN_HEAP(sizeof(nsEventTargetChainItem)) * NS_CHAIN_POOL_SIZE;
426 : nsresult rv = sEtciPool->Init("EventTargetChainItem Pool", kBucketSizes,
427 301 : kNumBuckets, kInitialPoolSize);
428 301 : if (NS_FAILED(rv)) {
429 0 : delete sEtciPool;
430 0 : sEtciPool = nsnull;
431 : }
432 : }
433 : }
434 16694 : if (sEtciPool) {
435 16694 : ++sEtciPoolUsers;
436 : }
437 16694 : }
438 :
439 16694 : ~ChainItemPool() {
440 16694 : if (sEtciPool) {
441 16694 : --sEtciPoolUsers;
442 : }
443 16694 : if (!sEtciPoolUsers) {
444 16004 : if (nsEventTargetChainItem::MaxEtciCount() > NS_CHAIN_POOL_SIZE) {
445 0 : gCachedETCI = nsnull;
446 0 : delete sEtciPool;
447 0 : sEtciPool = nsnull;
448 0 : nsEventTargetChainItem::ResetMaxEtciCount();
449 : }
450 : }
451 16694 : }
452 :
453 1364 : static void Shutdown()
454 : {
455 1364 : if (!sEtciPoolUsers) {
456 1364 : gCachedETCI = nsnull;
457 1364 : delete sEtciPool;
458 1364 : sEtciPool = nsnull;
459 1364 : nsEventTargetChainItem::ResetMaxEtciCount();
460 : }
461 1364 : }
462 :
463 59658 : nsFixedSizeAllocator* GetPool() { return sEtciPool; }
464 :
465 : static nsFixedSizeAllocator* sEtciPool;
466 : static PRInt32 sEtciPoolUsers;
467 : };
468 :
469 : nsFixedSizeAllocator* ChainItemPool::sEtciPool = nsnull;
470 : PRInt32 ChainItemPool::sEtciPoolUsers = 0;
471 :
472 1364 : void NS_ShutdownChainItemPool() { ChainItemPool::Shutdown(); }
473 :
474 : /* static */ nsresult
475 16694 : nsEventDispatcher::Dispatch(nsISupports* aTarget,
476 : nsPresContext* aPresContext,
477 : nsEvent* aEvent,
478 : nsIDOMEvent* aDOMEvent,
479 : nsEventStatus* aEventStatus,
480 : nsDispatchingCallback* aCallback,
481 : nsCOMArray<nsIDOMEventTarget>* aTargets)
482 : {
483 16694 : NS_ASSERTION(aEvent, "Trying to dispatch without nsEvent!");
484 16694 : NS_ENSURE_TRUE(!NS_IS_EVENT_IN_DISPATCH(aEvent),
485 : NS_ERROR_ILLEGAL_VALUE);
486 16694 : NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
487 :
488 : // If we're dispatching an already created DOMEvent object, make
489 : // sure it is initialized!
490 : // If aTargets is non-null, the event isn't going to be dispatched.
491 16694 : NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets,
492 : NS_ERROR_DOM_INVALID_STATE_ERR);
493 :
494 : #ifdef NS_FUNCTION_TIMER
495 : const char* timer_event_name = nsDOMEvent::GetEventName(aEvent->message);
496 : NS_TIME_FUNCTION_MIN_FMT(20, "Dispatching '%s' event",
497 : timer_event_name ? timer_event_name : "<other>");
498 : #endif
499 :
500 33388 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aTarget);
501 :
502 16694 : bool retargeted = false;
503 :
504 16694 : if (aEvent->flags & NS_EVENT_RETARGET_TO_NON_NATIVE_ANONYMOUS) {
505 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(target);
506 0 : if (content && content->IsInNativeAnonymousSubtree()) {
507 : nsCOMPtr<nsPIDOMEventTarget> newTarget =
508 0 : do_QueryInterface(content->FindFirstNonNativeAnonymous());
509 0 : NS_ENSURE_STATE(newTarget);
510 :
511 0 : aEvent->originalTarget = target;
512 0 : target = newTarget;
513 0 : retargeted = true;
514 : }
515 : }
516 :
517 16694 : if (aEvent->flags & NS_EVENT_FLAG_ONLY_CHROME_DISPATCH) {
518 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
519 0 : if (!node) {
520 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget);
521 0 : if (win) {
522 0 : node = do_QueryInterface(win->GetExtantDocument());
523 : }
524 : }
525 :
526 0 : NS_ENSURE_STATE(node);
527 0 : nsIDocument* doc = node->OwnerDoc();
528 0 : if (!nsContentUtils::IsChromeDoc(doc)) {
529 0 : nsPIDOMWindow* win = doc ? doc->GetInnerWindow() : nsnull;
530 : // If we can't dispatch the event to chrome, do nothing.
531 0 : nsIDOMEventTarget* piTarget = win ? win->GetChromeEventHandler() : nsnull;
532 0 : NS_ENSURE_TRUE(piTarget, NS_OK);
533 :
534 0 : nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(piTarget);
535 0 : if (flo) {
536 0 : nsRefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
537 0 : if (fl) {
538 0 : nsIDOMEventTarget* t = fl->GetTabChildGlobalAsEventTarget();
539 0 : piTarget = t ? t : piTarget;
540 : }
541 : }
542 :
543 : // Set the target to be the original dispatch target,
544 0 : aEvent->target = target;
545 : // but use chrome event handler or TabChildGlobal for event target chain.
546 0 : target = piTarget;
547 : }
548 : }
549 :
550 : #ifdef DEBUG
551 16694 : if (!nsContentUtils::IsSafeToRunScript()) {
552 0 : nsresult rv = NS_ERROR_FAILURE;
553 0 : if (target->GetContextForEventHandlers(&rv) ||
554 0 : NS_FAILED(rv)) {
555 0 : nsCOMPtr<nsINode> node = do_QueryInterface(target);
556 0 : if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) {
557 0 : NS_WARNING("Fix the caller!");
558 : } else {
559 0 : NS_ERROR("This is unsafe! Fix the caller!");
560 : }
561 : }
562 : }
563 :
564 16694 : if (aDOMEvent) {
565 31320 : nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(aDOMEvent));
566 15660 : if (privEvt) {
567 15660 : nsEvent* innerEvent = privEvt->GetInternalNSEvent();
568 15660 : NS_ASSERTION(innerEvent == aEvent,
569 : "The inner event of aDOMEvent is not the same as aEvent!");
570 : }
571 : }
572 : #endif
573 :
574 16694 : nsresult rv = NS_OK;
575 16694 : bool externalDOMEvent = !!(aDOMEvent);
576 :
577 : // If we have a PresContext, make sure it doesn't die before
578 : // event dispatching is finished.
579 33388 : nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
580 33388 : ChainItemPool pool;
581 16694 : NS_ENSURE_TRUE(pool.GetPool(), NS_ERROR_OUT_OF_MEMORY);
582 :
583 : // Create the event target chain item for the event target.
584 : nsEventTargetChainItem* targetEtci =
585 16694 : nsEventTargetChainItem::Create(pool.GetPool(), target);
586 16694 : NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY);
587 16694 : if (!targetEtci->IsValid()) {
588 0 : nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci);
589 0 : return NS_ERROR_FAILURE;
590 : }
591 :
592 : // Make sure that nsIDOMEvent::target and nsIDOMNSEvent::originalTarget
593 : // point to the last item in the chain.
594 16694 : if (!aEvent->target) {
595 : // Note, CurrentTarget() points always to the object returned by
596 : // GetTargetForEventTargetChain().
597 11369 : aEvent->target = targetEtci->CurrentTarget();
598 : } else {
599 : // XXX But if the target is already set, use that. This is a hack
600 : // for the 'load', 'beforeunload' and 'unload' events,
601 : // which are dispatched to |window| but have document as their target.
602 : //
603 : // Make sure that the event target points to the right object.
604 5325 : aEvent->target = aEvent->target->GetTargetForEventTargetChain();
605 5325 : NS_ENSURE_STATE(aEvent->target);
606 : }
607 :
608 16694 : if (retargeted) {
609 : aEvent->originalTarget =
610 0 : aEvent->originalTarget->GetTargetForEventTargetChain();
611 0 : NS_ENSURE_STATE(aEvent->originalTarget);
612 : }
613 : else {
614 16694 : aEvent->originalTarget = aEvent->target;
615 : }
616 :
617 33388 : nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->originalTarget);
618 16694 : bool isInAnon = (content && content->IsInAnonymousSubtree());
619 :
620 16694 : NS_MARK_EVENT_DISPATCH_STARTED(aEvent);
621 :
622 : // Create visitor object and start event dispatching.
623 : // PreHandleEvent for the original target.
624 16694 : nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
625 : nsEventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
626 33388 : isInAnon);
627 16694 : targetEtci->PreHandleEvent(preVisitor);
628 :
629 16694 : if (preVisitor.mCanHandle) {
630 : // At least the original target can handle the event.
631 : // Setting the retarget to the |target| simplifies retargeting code.
632 33388 : nsCOMPtr<nsIDOMEventTarget> t = aEvent->target;
633 16694 : targetEtci->SetNewTarget(t);
634 16694 : nsEventTargetChainItem* topEtci = targetEtci;
635 42964 : while (preVisitor.mParentTarget) {
636 : nsEventTargetChainItem* parentEtci =
637 : nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget,
638 9576 : topEtci);
639 9576 : if (!parentEtci) {
640 0 : rv = NS_ERROR_OUT_OF_MEMORY;
641 0 : break;
642 : }
643 9576 : if (!parentEtci->IsValid()) {
644 0 : rv = NS_ERROR_FAILURE;
645 0 : break;
646 : }
647 :
648 : // Item needs event retargetting.
649 9576 : if (preVisitor.mEventTargetAtParent) {
650 : // Need to set the target of the event
651 : // so that also the next retargeting works.
652 0 : preVisitor.mEvent->target = preVisitor.mEventTargetAtParent;
653 0 : parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
654 : }
655 :
656 9576 : parentEtci->PreHandleEvent(preVisitor);
657 9576 : if (preVisitor.mCanHandle) {
658 9576 : topEtci = parentEtci;
659 : } else {
660 0 : nsEventTargetChainItem::Destroy(pool.GetPool(), parentEtci);
661 0 : parentEtci = nsnull;
662 0 : break;
663 : }
664 : }
665 16694 : if (NS_SUCCEEDED(rv)) {
666 16694 : if (aTargets) {
667 0 : aTargets->Clear();
668 0 : nsEventTargetChainItem* item = targetEtci;
669 0 : while(item) {
670 0 : aTargets->AppendObject(item->CurrentTarget()->GetTargetForDOMEvent());
671 0 : item = item->mParent;
672 : }
673 : } else {
674 : // Event target chain is created. Handle the chain.
675 33388 : nsEventChainPostVisitor postVisitor(preVisitor);
676 33388 : nsCxPusher pusher;
677 : rv = topEtci->HandleEventTargetChain(postVisitor,
678 : NS_EVENT_FLAG_BUBBLE |
679 : NS_EVENT_FLAG_CAPTURE,
680 : aCallback,
681 : false,
682 16694 : &pusher);
683 :
684 16694 : preVisitor.mEventStatus = postVisitor.mEventStatus;
685 : // If the DOM event was created during event flow.
686 16694 : if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
687 0 : preVisitor.mDOMEvent = postVisitor.mDOMEvent;
688 : }
689 : }
690 : }
691 : }
692 :
693 16694 : nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci);
694 16694 : targetEtci = nsnull;
695 :
696 16694 : NS_MARK_EVENT_DISPATCH_DONE(aEvent);
697 :
698 16694 : if (!externalDOMEvent && preVisitor.mDOMEvent) {
699 : // An nsDOMEvent was created while dispatching the event.
700 : // Duplicate private data if someone holds a pointer to it.
701 0 : nsrefcnt rc = 0;
702 0 : NS_RELEASE2(preVisitor.mDOMEvent, rc);
703 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
704 0 : do_QueryInterface(preVisitor.mDOMEvent);
705 0 : if (privateEvent) {
706 0 : privateEvent->DuplicatePrivateData();
707 : }
708 : }
709 :
710 16694 : if (aEventStatus) {
711 10586 : *aEventStatus = preVisitor.mEventStatus;
712 : }
713 16694 : return rv;
714 : }
715 :
716 : /* static */ nsresult
717 15660 : nsEventDispatcher::DispatchDOMEvent(nsISupports* aTarget,
718 : nsEvent* aEvent,
719 : nsIDOMEvent* aDOMEvent,
720 : nsPresContext* aPresContext,
721 : nsEventStatus* aEventStatus)
722 : {
723 15660 : if (aDOMEvent) {
724 31320 : nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(aDOMEvent));
725 15660 : if (privEvt) {
726 15660 : nsEvent* innerEvent = privEvt->GetInternalNSEvent();
727 15660 : NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
728 :
729 15660 : bool dontResetTrusted = false;
730 15660 : if (innerEvent->flags & NS_EVENT_DISPATCHED) {
731 0 : innerEvent->target = nsnull;
732 0 : innerEvent->originalTarget = nsnull;
733 : }
734 : else {
735 31320 : nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(privEvt));
736 15660 : nsevent->GetIsTrusted(&dontResetTrusted);
737 : }
738 :
739 15660 : if (!dontResetTrusted) {
740 : //Check security state to determine if dispatcher is trusted
741 0 : privEvt->SetTrusted(nsContentUtils::IsCallerTrustedForWrite());
742 : }
743 :
744 : return nsEventDispatcher::Dispatch(aTarget, aPresContext, innerEvent,
745 15660 : aDOMEvent, aEventStatus);
746 : }
747 0 : } else if (aEvent) {
748 : return nsEventDispatcher::Dispatch(aTarget, aPresContext, aEvent,
749 0 : aDOMEvent, aEventStatus);
750 : }
751 0 : return NS_ERROR_ILLEGAL_VALUE;
752 : }
753 :
754 : /* static */ nsresult
755 11475 : nsEventDispatcher::CreateEvent(nsPresContext* aPresContext,
756 : nsEvent* aEvent,
757 : const nsAString& aEventType,
758 : nsIDOMEvent** aDOMEvent)
759 : {
760 11475 : *aDOMEvent = nsnull;
761 :
762 11475 : if (aEvent) {
763 44 : switch(aEvent->eventStructType) {
764 : case NS_MUTATION_EVENT:
765 : return NS_NewDOMMutationEvent(aDOMEvent, aPresContext,
766 44 : static_cast<nsMutationEvent*>(aEvent));
767 : case NS_GUI_EVENT:
768 : case NS_SCROLLPORT_EVENT:
769 : case NS_UI_EVENT:
770 : return NS_NewDOMUIEvent(aDOMEvent, aPresContext,
771 0 : static_cast<nsGUIEvent*>(aEvent));
772 : case NS_SCROLLAREA_EVENT:
773 : return NS_NewDOMScrollAreaEvent(aDOMEvent, aPresContext,
774 0 : static_cast<nsScrollAreaEvent *>(aEvent));
775 : case NS_KEY_EVENT:
776 : return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext,
777 0 : static_cast<nsKeyEvent*>(aEvent));
778 : case NS_COMPOSITION_EVENT:
779 : return NS_NewDOMCompositionEvent(
780 0 : aDOMEvent, aPresContext, static_cast<nsCompositionEvent*>(aEvent));
781 : case NS_MOUSE_EVENT:
782 : case NS_POPUP_EVENT:
783 : return NS_NewDOMMouseEvent(aDOMEvent, aPresContext,
784 0 : static_cast<nsInputEvent*>(aEvent));
785 : case NS_MOUSE_SCROLL_EVENT:
786 : return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext,
787 0 : static_cast<nsInputEvent*>(aEvent));
788 : case NS_DRAG_EVENT:
789 : return NS_NewDOMDragEvent(aDOMEvent, aPresContext,
790 0 : static_cast<nsDragEvent*>(aEvent));
791 : case NS_TEXT_EVENT:
792 : return NS_NewDOMTextEvent(aDOMEvent, aPresContext,
793 0 : static_cast<nsTextEvent*>(aEvent));
794 : case NS_SVG_EVENT:
795 : return NS_NewDOMSVGEvent(aDOMEvent, aPresContext,
796 0 : aEvent);
797 : case NS_SVGZOOM_EVENT:
798 : return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext,
799 0 : static_cast<nsGUIEvent*>(aEvent));
800 : case NS_SMIL_TIME_EVENT:
801 0 : return NS_NewDOMTimeEvent(aDOMEvent, aPresContext, aEvent);
802 :
803 : case NS_COMMAND_EVENT:
804 : return NS_NewDOMCommandEvent(aDOMEvent, aPresContext,
805 0 : static_cast<nsCommandEvent*>(aEvent));
806 : case NS_SIMPLE_GESTURE_EVENT:
807 : return NS_NewDOMSimpleGestureEvent(aDOMEvent, aPresContext,
808 0 : static_cast<nsSimpleGestureEvent*>(aEvent));
809 : case NS_MOZTOUCH_EVENT:
810 : return NS_NewDOMMozTouchEvent(aDOMEvent, aPresContext,
811 0 : static_cast<nsMozTouchEvent*>(aEvent));
812 : case NS_TOUCH_EVENT:
813 : return NS_NewDOMTouchEvent(aDOMEvent, aPresContext,
814 0 : static_cast<nsTouchEvent*>(aEvent));
815 : case NS_TRANSITION_EVENT:
816 : return NS_NewDOMTransitionEvent(aDOMEvent, aPresContext,
817 0 : static_cast<nsTransitionEvent*>(aEvent));
818 : case NS_ANIMATION_EVENT:
819 : return NS_NewDOMAnimationEvent(aDOMEvent, aPresContext,
820 0 : static_cast<nsAnimationEvent*>(aEvent));
821 : }
822 :
823 : // For all other types of events, create a vanilla event object.
824 0 : return NS_NewDOMEvent(aDOMEvent, aPresContext, aEvent);
825 : }
826 :
827 : // And if we didn't get an event, check the type argument.
828 :
829 34289 : if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
830 11429 : aEventType.LowerCaseEqualsLiteral("mouseevents") ||
831 11429 : aEventType.LowerCaseEqualsLiteral("popupevents"))
832 2 : return NS_NewDOMMouseEvent(aDOMEvent, aPresContext, nsnull);
833 11429 : if (aEventType.LowerCaseEqualsLiteral("mousescrollevents"))
834 0 : return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext, nsnull);
835 22858 : if (aEventType.LowerCaseEqualsLiteral("dragevent") ||
836 11429 : aEventType.LowerCaseEqualsLiteral("dragevents"))
837 0 : return NS_NewDOMDragEvent(aDOMEvent, aPresContext, nsnull);
838 22858 : if (aEventType.LowerCaseEqualsLiteral("keyboardevent") ||
839 11429 : aEventType.LowerCaseEqualsLiteral("keyevents"))
840 0 : return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext, nsnull);
841 11429 : if (aEventType.LowerCaseEqualsLiteral("compositionevent"))
842 0 : return NS_NewDOMCompositionEvent(aDOMEvent, aPresContext, nsnull);
843 22858 : if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
844 11429 : aEventType.LowerCaseEqualsLiteral("mutationevents"))
845 0 : return NS_NewDOMMutationEvent(aDOMEvent, aPresContext, nsnull);
846 22858 : if (aEventType.LowerCaseEqualsLiteral("textevent") ||
847 11429 : aEventType.LowerCaseEqualsLiteral("textevents"))
848 0 : return NS_NewDOMTextEvent(aDOMEvent, aPresContext, nsnull);
849 11429 : if (aEventType.LowerCaseEqualsLiteral("popupblockedevents"))
850 0 : return NS_NewDOMPopupBlockedEvent(aDOMEvent, aPresContext, nsnull);
851 11429 : if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent"))
852 0 : return NS_NewDOMDeviceOrientationEvent(aDOMEvent, aPresContext, nsnull);
853 11429 : if (aEventType.LowerCaseEqualsLiteral("devicemotionevent"))
854 0 : return NS_NewDOMDeviceMotionEvent(aDOMEvent, aPresContext, nsnull);
855 22858 : if (aEventType.LowerCaseEqualsLiteral("uievent") ||
856 11429 : aEventType.LowerCaseEqualsLiteral("uievents"))
857 0 : return NS_NewDOMUIEvent(aDOMEvent, aPresContext, nsnull);
858 25119 : if (aEventType.LowerCaseEqualsLiteral("event") ||
859 11429 : aEventType.LowerCaseEqualsLiteral("events") ||
860 2261 : aEventType.LowerCaseEqualsLiteral("htmlevents"))
861 9168 : return NS_NewDOMEvent(aDOMEvent, aPresContext, nsnull);
862 4522 : if (aEventType.LowerCaseEqualsLiteral("svgevent") ||
863 2261 : aEventType.LowerCaseEqualsLiteral("svgevents"))
864 0 : return NS_NewDOMSVGEvent(aDOMEvent, aPresContext, nsnull);
865 4522 : if (aEventType.LowerCaseEqualsLiteral("svgzoomevent") ||
866 2261 : aEventType.LowerCaseEqualsLiteral("svgzoomevents"))
867 0 : return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext, nsnull);
868 4522 : if (aEventType.LowerCaseEqualsLiteral("timeevent") ||
869 2261 : aEventType.LowerCaseEqualsLiteral("timeevents"))
870 0 : return NS_NewDOMTimeEvent(aDOMEvent, aPresContext, nsnull);
871 4522 : if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
872 2261 : aEventType.LowerCaseEqualsLiteral("xulcommandevents"))
873 0 : return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext, nsnull);
874 4522 : if (aEventType.LowerCaseEqualsLiteral("commandevent") ||
875 2261 : aEventType.LowerCaseEqualsLiteral("commandevents"))
876 0 : return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nsnull);
877 4522 : if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") ||
878 2261 : aEventType.LowerCaseEqualsLiteral("datacontainerevents"))
879 0 : return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nsnull);
880 2261 : if (aEventType.LowerCaseEqualsLiteral("messageevent"))
881 0 : return NS_NewDOMMessageEvent(aDOMEvent, aPresContext, nsnull);
882 2261 : if (aEventType.LowerCaseEqualsLiteral("progressevent"))
883 2261 : return NS_NewDOMProgressEvent(aDOMEvent, aPresContext, nsnull);
884 0 : if (aEventType.LowerCaseEqualsLiteral("notifypaintevent"))
885 0 : return NS_NewDOMNotifyPaintEvent(aDOMEvent, aPresContext, nsnull);
886 0 : if (aEventType.LowerCaseEqualsLiteral("simplegestureevent"))
887 0 : return NS_NewDOMSimpleGestureEvent(aDOMEvent, aPresContext, nsnull);
888 0 : if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent"))
889 0 : return NS_NewDOMBeforeUnloadEvent(aDOMEvent, aPresContext, nsnull);
890 0 : if (aEventType.LowerCaseEqualsLiteral("pagetransition"))
891 0 : return NS_NewDOMPageTransitionEvent(aDOMEvent, aPresContext, nsnull);
892 0 : if (aEventType.LowerCaseEqualsLiteral("moztouchevent"))
893 0 : return NS_NewDOMMozTouchEvent(aDOMEvent, aPresContext, nsnull);
894 0 : if (aEventType.LowerCaseEqualsLiteral("scrollareaevent"))
895 0 : return NS_NewDOMScrollAreaEvent(aDOMEvent, aPresContext, nsnull);
896 : // FIXME: Should get spec to say what the right string is here! This
897 : // is probably wrong!
898 0 : if (aEventType.LowerCaseEqualsLiteral("transitionevent"))
899 0 : return NS_NewDOMTransitionEvent(aDOMEvent, aPresContext, nsnull);
900 0 : if (aEventType.LowerCaseEqualsLiteral("animationevent"))
901 0 : return NS_NewDOMAnimationEvent(aDOMEvent, aPresContext, nsnull);
902 0 : if (aEventType.LowerCaseEqualsLiteral("popstateevent"))
903 0 : return NS_NewDOMPopStateEvent(aDOMEvent, aPresContext, nsnull);
904 0 : if (aEventType.LowerCaseEqualsLiteral("mozaudioavailableevent"))
905 0 : return NS_NewDOMAudioAvailableEvent(aDOMEvent, aPresContext, nsnull);
906 0 : if (aEventType.LowerCaseEqualsLiteral("closeevent"))
907 0 : return NS_NewDOMCloseEvent(aDOMEvent, aPresContext, nsnull);
908 0 : if (aEventType.LowerCaseEqualsLiteral("touchevent") &&
909 0 : nsDOMTouchEvent::PrefEnabled())
910 0 : return NS_NewDOMTouchEvent(aDOMEvent, aPresContext, nsnull);
911 0 : if (aEventType.LowerCaseEqualsLiteral("hashchangeevent"))
912 0 : return NS_NewDOMHashChangeEvent(aDOMEvent, aPresContext, nsnull);
913 0 : if (aEventType.LowerCaseEqualsLiteral("customevent"))
914 0 : return NS_NewDOMCustomEvent(aDOMEvent, aPresContext, nsnull);
915 0 : if (aEventType.LowerCaseEqualsLiteral("mozsmsevent"))
916 0 : return NS_NewDOMSmsEvent(aDOMEvent, aPresContext, nsnull);
917 0 : if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
918 0 : NS_ADDREF(*aDOMEvent = static_cast<nsDOMEvent*>(new nsDOMStorageEvent()));
919 0 : return NS_OK;
920 : }
921 :
922 :
923 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
924 : }
|