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 25419 : {
67 : private:
68 : nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
69 : nsEventTargetChainItem* aChild = nsnull);
70 :
71 : public:
72 25419 : static nsEventTargetChainItem* Create(nsFixedSizeAllocator* aAllocator,
73 : nsIDOMEventTarget* aTarget,
74 : nsEventTargetChainItem* aChild = nsnull)
75 : {
76 25419 : void* place = nsnull;
77 25419 : if (gCachedETCI) {
78 24956 : place = gCachedETCI;
79 24956 : gCachedETCI = gCachedETCI->mNext;
80 : } else {
81 463 : place = aAllocator->Alloc(sizeof(nsEventTargetChainItem));
82 : }
83 : return place
84 25419 : ? ::new (place) nsEventTargetChainItem(aTarget, aChild)
85 50838 : : nsnull;
86 : }
87 :
88 16381 : static void Destroy(nsFixedSizeAllocator* aAllocator,
89 : nsEventTargetChainItem* aItem)
90 : {
91 : // ::Destroy deletes ancestor chain.
92 16381 : nsEventTargetChainItem* item = aItem;
93 16381 : if (item->mChild) {
94 0 : item->mChild->mParent = nsnull;
95 0 : item->mChild = nsnull;
96 : }
97 58181 : while (item) {
98 25419 : nsEventTargetChainItem* parent = item->mParent;
99 25419 : item->~nsEventTargetChainItem();
100 25419 : item->mNext = gCachedETCI;
101 25419 : gCachedETCI = item;
102 25419 : --sCurrentEtciCount;
103 25419 : item = parent;
104 : }
105 16381 : }
106 :
107 25419 : bool IsValid()
108 : {
109 25419 : NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!");
110 25419 : return !!(mTarget);
111 : }
112 :
113 36152 : nsIDOMEventTarget* GetNewTarget()
114 : {
115 36152 : return mNewTarget;
116 : }
117 :
118 16381 : void SetNewTarget(nsIDOMEventTarget* aNewTarget)
119 : {
120 16381 : mNewTarget = aNewTarget;
121 16381 : }
122 :
123 25419 : void SetForceContentDispatch(bool aForce)
124 : {
125 25419 : if (aForce) {
126 6409 : mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH;
127 : } else {
128 19010 : mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH;
129 : }
130 25419 : }
131 :
132 0 : bool ForceContentDispatch()
133 : {
134 0 : return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH);
135 : }
136 :
137 25419 : void SetWantsWillHandleEvent(bool aWants)
138 : {
139 25419 : if (aWants) {
140 0 : mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT;
141 : } else {
142 25419 : mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT;
143 : }
144 25419 : }
145 :
146 51006 : bool WantsWillHandleEvent()
147 : {
148 51006 : return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT);
149 : }
150 :
151 25419 : void SetMayHaveListenerManager(bool aMayHave)
152 : {
153 25419 : if (aMayHave) {
154 25363 : mFlags |= NS_TARGET_CHAIN_MAY_HAVE_MANAGER;
155 : } else {
156 56 : mFlags &= ~NS_TARGET_CHAIN_MAY_HAVE_MANAGER;
157 : }
158 25419 : }
159 :
160 41103 : bool MayHaveListenerManager()
161 : {
162 41103 : return !!(mFlags & NS_TARGET_CHAIN_MAY_HAVE_MANAGER);
163 : }
164 :
165 31028 : nsIDOMEventTarget* CurrentTarget()
166 : {
167 31028 : 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 51006 : nsresult HandleEvent(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags,
193 : bool aMayHaveNewListenerManagers,
194 : nsCxPusher* aPusher)
195 : {
196 51006 : if (WantsWillHandleEvent()) {
197 0 : mTarget->WillHandleEvent(aVisitor);
198 : }
199 51006 : if (aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
200 0 : return NS_OK;
201 : }
202 51006 : if (!mManager) {
203 41103 : if (!MayHaveListenerManager() && !aMayHaveNewListenerManagers) {
204 140 : return NS_OK;
205 : }
206 : mManager =
207 40963 : static_cast<nsEventListenerManager*>(mTarget->GetListenerManager(false));
208 : }
209 50866 : if (mManager) {
210 20010 : 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 20010 : aPusher);
217 20010 : NS_ASSERTION(aVisitor.mEvent->currentTarget == nsnull,
218 : "CurrentTarget should be null!");
219 : }
220 50866 : 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 15691 : static PRUint32 MaxEtciCount() { return sMaxEtciCount; }
230 :
231 1403 : static void ResetMaxEtciCount()
232 : {
233 1403 : NS_ASSERTION(!sCurrentEtciCount, "Wrong time to call ResetMaxEtciCount()!");
234 1403 : sMaxEtciCount = 0;
235 1403 : }
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 25419 : nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
260 : nsEventTargetChainItem* aChild)
261 25419 : : mChild(aChild), mParent(nsnull), mFlags(0), mItemFlags(0)
262 : {
263 25419 : mTarget = aTarget->GetTargetForEventTargetChain();
264 25419 : if (mChild) {
265 9038 : mChild->mParent = this;
266 : }
267 :
268 25419 : if (++sCurrentEtciCount > sMaxEtciCount) {
269 463 : sMaxEtciCount = sCurrentEtciCount;
270 : }
271 25419 : }
272 :
273 : nsresult
274 25419 : nsEventTargetChainItem::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
275 : {
276 25419 : aVisitor.Reset();
277 25419 : nsresult rv = mTarget->PreHandleEvent(aVisitor);
278 25419 : SetForceContentDispatch(aVisitor.mForceContentDispatch);
279 25419 : SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent);
280 25419 : SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager);
281 25419 : mItemFlags = aVisitor.mItemFlags;
282 25419 : mItemData = aVisitor.mItemData;
283 25419 : return rv;
284 : }
285 :
286 : nsresult
287 16475 : nsEventTargetChainItem::PostHandleEvent(nsEventChainPostVisitor& aVisitor,
288 : nsCxPusher* aPusher)
289 : {
290 16475 : aPusher->Pop();
291 16475 : aVisitor.mItemFlags = mItemFlags;
292 16475 : aVisitor.mItemData = mItemData;
293 16475 : mTarget->PostHandleEvent(aVisitor);
294 16475 : return NS_OK;
295 : }
296 :
297 : nsresult
298 32762 : nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags,
299 : nsDispatchingCallback* aCallback,
300 : bool aMayHaveNewListenerManagers,
301 : nsCxPusher* aPusher)
302 : {
303 32762 : PRUint32 createdELMs = nsEventListenerManager::sCreatedCount;
304 : // Save the target so that it can be restored later.
305 65524 : nsCOMPtr<nsIDOMEventTarget> firstTarget = aVisitor.mEvent->target;
306 :
307 : // Capture
308 32762 : nsEventTargetChainItem* item = this;
309 32762 : aVisitor.mEvent->flags |= NS_EVENT_FLAG_CAPTURE;
310 32762 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
311 83600 : while (item->mChild) {
312 36152 : if ((!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
313 0 : item->ForceContentDispatch()) &&
314 18076 : !(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH)) {
315 : item->HandleEvent(aVisitor, aFlags & NS_EVENT_CAPTURE_MASK,
316 : aMayHaveNewListenerManagers ||
317 : createdELMs != nsEventListenerManager::sCreatedCount,
318 18076 : aPusher);
319 : }
320 :
321 18076 : 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 18076 : item = item->mChild;
335 : }
336 :
337 : // Target
338 32762 : aVisitor.mEvent->flags |= NS_EVENT_FLAG_BUBBLE;
339 65524 : if (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) &&
340 32762 : (!(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 32762 : aPusher);
349 : }
350 32762 : if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
351 16381 : item->PostHandleEvent(aVisitor, aPusher);
352 : }
353 :
354 : // Bubble
355 32762 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_CAPTURE;
356 32762 : item = item->mParent;
357 83600 : while (item) {
358 18076 : nsIDOMEventTarget* newTarget = item->GetNewTarget();
359 18076 : 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 18076 : 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 18076 : item = item->mParent;
378 : }
379 32762 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
380 :
381 32762 : 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 16381 : ~(NS_EVENT_FLAG_STOP_DISPATCH | NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY);
386 :
387 : // Setting back the original target of the event.
388 16381 : aVisitor.mEvent->target = aVisitor.mEvent->originalTarget;
389 :
390 : // Special handling if PresShell (or some other caller)
391 : // used a callback object.
392 16381 : 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 16381 : aVisitor.mEvent->target = firstTarget;
400 : HandleEventTargetChain(aVisitor, aFlags | NS_EVENT_FLAG_SYSTEM_EVENT,
401 : aCallback,
402 : createdELMs != nsEventListenerManager::sCreatedCount,
403 16381 : 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 16381 : ~(NS_EVENT_FLAG_STOP_DISPATCH | NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY);
409 : }
410 :
411 32762 : return NS_OK;
412 : }
413 :
414 : #define NS_CHAIN_POOL_SIZE 128
415 :
416 : class ChainItemPool {
417 : public:
418 16381 : ChainItemPool() {
419 16381 : if (!sEtciPool) {
420 304 : sEtciPool = new nsFixedSizeAllocator();
421 304 : 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 304 : kNumBuckets, kInitialPoolSize);
428 304 : if (NS_FAILED(rv)) {
429 0 : delete sEtciPool;
430 0 : sEtciPool = nsnull;
431 : }
432 : }
433 : }
434 16381 : if (sEtciPool) {
435 16381 : ++sEtciPoolUsers;
436 : }
437 16381 : }
438 :
439 16381 : ~ChainItemPool() {
440 16381 : if (sEtciPool) {
441 16381 : --sEtciPoolUsers;
442 : }
443 16381 : if (!sEtciPoolUsers) {
444 15691 : 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 16381 : }
452 :
453 1403 : static void Shutdown()
454 : {
455 1403 : if (!sEtciPoolUsers) {
456 1403 : gCachedETCI = nsnull;
457 1403 : delete sEtciPool;
458 1403 : sEtciPool = nsnull;
459 1403 : nsEventTargetChainItem::ResetMaxEtciCount();
460 : }
461 1403 : }
462 :
463 58181 : 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 1403 : void NS_ShutdownChainItemPool() { ChainItemPool::Shutdown(); }
473 :
474 : /* static */ nsresult
475 16381 : 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 16381 : NS_ASSERTION(aEvent, "Trying to dispatch without nsEvent!");
484 16381 : NS_ENSURE_TRUE(!NS_IS_EVENT_IN_DISPATCH(aEvent),
485 : NS_ERROR_ILLEGAL_VALUE);
486 16381 : 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 16381 : 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 32762 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aTarget);
501 :
502 16381 : bool retargeted = false;
503 :
504 16381 : 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 16381 : 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 16381 : 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 16381 : if (aDOMEvent) {
565 30686 : nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(aDOMEvent));
566 15343 : if (privEvt) {
567 15343 : nsEvent* innerEvent = privEvt->GetInternalNSEvent();
568 15343 : NS_ASSERTION(innerEvent == aEvent,
569 : "The inner event of aDOMEvent is not the same as aEvent!");
570 : }
571 : }
572 : #endif
573 :
574 16381 : nsresult rv = NS_OK;
575 16381 : bool externalDOMEvent = !!(aDOMEvent);
576 :
577 : // If we have a PresContext, make sure it doesn't die before
578 : // event dispatching is finished.
579 32762 : nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
580 32762 : ChainItemPool pool;
581 16381 : 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 16381 : nsEventTargetChainItem::Create(pool.GetPool(), target);
586 16381 : NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY);
587 16381 : 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 16381 : if (!aEvent->target) {
595 : // Note, CurrentTarget() points always to the object returned by
596 : // GetTargetForEventTargetChain().
597 11018 : 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 5363 : aEvent->target = aEvent->target->GetTargetForEventTargetChain();
605 5363 : NS_ENSURE_STATE(aEvent->target);
606 : }
607 :
608 16381 : if (retargeted) {
609 : aEvent->originalTarget =
610 0 : aEvent->originalTarget->GetTargetForEventTargetChain();
611 0 : NS_ENSURE_STATE(aEvent->originalTarget);
612 : }
613 : else {
614 16381 : aEvent->originalTarget = aEvent->target;
615 : }
616 :
617 32762 : nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->originalTarget);
618 16381 : bool isInAnon = (content && content->IsInAnonymousSubtree());
619 :
620 16381 : NS_MARK_EVENT_DISPATCH_STARTED(aEvent);
621 :
622 : // Create visitor object and start event dispatching.
623 : // PreHandleEvent for the original target.
624 16381 : nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
625 : nsEventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
626 32762 : isInAnon);
627 16381 : targetEtci->PreHandleEvent(preVisitor);
628 :
629 16381 : if (preVisitor.mCanHandle) {
630 : // At least the original target can handle the event.
631 : // Setting the retarget to the |target| simplifies retargeting code.
632 32762 : nsCOMPtr<nsIDOMEventTarget> t = aEvent->target;
633 16381 : targetEtci->SetNewTarget(t);
634 16381 : nsEventTargetChainItem* topEtci = targetEtci;
635 41800 : while (preVisitor.mParentTarget) {
636 : nsEventTargetChainItem* parentEtci =
637 : nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget,
638 9038 : topEtci);
639 9038 : if (!parentEtci) {
640 0 : rv = NS_ERROR_OUT_OF_MEMORY;
641 0 : break;
642 : }
643 9038 : if (!parentEtci->IsValid()) {
644 0 : rv = NS_ERROR_FAILURE;
645 0 : break;
646 : }
647 :
648 : // Item needs event retargetting.
649 9038 : 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 9038 : parentEtci->PreHandleEvent(preVisitor);
657 9038 : if (preVisitor.mCanHandle) {
658 9038 : topEtci = parentEtci;
659 : } else {
660 0 : nsEventTargetChainItem::Destroy(pool.GetPool(), parentEtci);
661 0 : parentEtci = nsnull;
662 0 : break;
663 : }
664 : }
665 16381 : if (NS_SUCCEEDED(rv)) {
666 16381 : 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 32762 : nsEventChainPostVisitor postVisitor(preVisitor);
676 32762 : nsCxPusher pusher;
677 : rv = topEtci->HandleEventTargetChain(postVisitor,
678 : NS_EVENT_FLAG_BUBBLE |
679 : NS_EVENT_FLAG_CAPTURE,
680 : aCallback,
681 : false,
682 16381 : &pusher);
683 :
684 16381 : preVisitor.mEventStatus = postVisitor.mEventStatus;
685 : // If the DOM event was created during event flow.
686 16381 : if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
687 0 : preVisitor.mDOMEvent = postVisitor.mDOMEvent;
688 : }
689 : }
690 : }
691 : }
692 :
693 16381 : nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci);
694 16381 : targetEtci = nsnull;
695 :
696 16381 : NS_MARK_EVENT_DISPATCH_DONE(aEvent);
697 :
698 16381 : 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 16381 : if (aEventStatus) {
711 10265 : *aEventStatus = preVisitor.mEventStatus;
712 : }
713 16381 : return rv;
714 : }
715 :
716 : /* static */ nsresult
717 15343 : nsEventDispatcher::DispatchDOMEvent(nsISupports* aTarget,
718 : nsEvent* aEvent,
719 : nsIDOMEvent* aDOMEvent,
720 : nsPresContext* aPresContext,
721 : nsEventStatus* aEventStatus)
722 : {
723 15343 : if (aDOMEvent) {
724 30686 : nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(aDOMEvent));
725 15343 : if (privEvt) {
726 15343 : nsEvent* innerEvent = privEvt->GetInternalNSEvent();
727 15343 : NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
728 :
729 15343 : bool dontResetTrusted = false;
730 15343 : if (innerEvent->flags & NS_EVENT_DISPATCHED) {
731 0 : innerEvent->target = nsnull;
732 0 : innerEvent->originalTarget = nsnull;
733 : }
734 : else {
735 30686 : nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(privEvt));
736 15343 : nsevent->GetIsTrusted(&dontResetTrusted);
737 : }
738 :
739 15343 : 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 15343 : 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 11525 : nsEventDispatcher::CreateEvent(nsPresContext* aPresContext,
756 : nsEvent* aEvent,
757 : const nsAString& aEventType,
758 : nsIDOMEvent** aDOMEvent)
759 : {
760 11525 : *aDOMEvent = nsnull;
761 :
762 11525 : 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 34439 : if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
830 11479 : aEventType.LowerCaseEqualsLiteral("mouseevents") ||
831 11479 : aEventType.LowerCaseEqualsLiteral("popupevents"))
832 2 : return NS_NewDOMMouseEvent(aDOMEvent, aPresContext, nsnull);
833 11479 : if (aEventType.LowerCaseEqualsLiteral("mousescrollevents"))
834 0 : return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext, nsnull);
835 22958 : if (aEventType.LowerCaseEqualsLiteral("dragevent") ||
836 11479 : aEventType.LowerCaseEqualsLiteral("dragevents"))
837 0 : return NS_NewDOMDragEvent(aDOMEvent, aPresContext, nsnull);
838 22958 : if (aEventType.LowerCaseEqualsLiteral("keyboardevent") ||
839 11479 : aEventType.LowerCaseEqualsLiteral("keyevents"))
840 0 : return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext, nsnull);
841 11479 : if (aEventType.LowerCaseEqualsLiteral("compositionevent"))
842 0 : return NS_NewDOMCompositionEvent(aDOMEvent, aPresContext, nsnull);
843 22958 : if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
844 11479 : aEventType.LowerCaseEqualsLiteral("mutationevents"))
845 0 : return NS_NewDOMMutationEvent(aDOMEvent, aPresContext, nsnull);
846 22958 : if (aEventType.LowerCaseEqualsLiteral("textevent") ||
847 11479 : aEventType.LowerCaseEqualsLiteral("textevents"))
848 0 : return NS_NewDOMTextEvent(aDOMEvent, aPresContext, nsnull);
849 11479 : if (aEventType.LowerCaseEqualsLiteral("popupblockedevents"))
850 0 : return NS_NewDOMPopupBlockedEvent(aDOMEvent, aPresContext, nsnull);
851 11479 : if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent"))
852 0 : return NS_NewDOMDeviceOrientationEvent(aDOMEvent, aPresContext, nsnull);
853 11479 : if (aEventType.LowerCaseEqualsLiteral("devicemotionevent"))
854 0 : return NS_NewDOMDeviceMotionEvent(aDOMEvent, aPresContext, nsnull);
855 22958 : if (aEventType.LowerCaseEqualsLiteral("uievent") ||
856 11479 : aEventType.LowerCaseEqualsLiteral("uievents"))
857 0 : return NS_NewDOMUIEvent(aDOMEvent, aPresContext, nsnull);
858 25221 : if (aEventType.LowerCaseEqualsLiteral("event") ||
859 11479 : aEventType.LowerCaseEqualsLiteral("events") ||
860 2263 : aEventType.LowerCaseEqualsLiteral("htmlevents"))
861 9216 : return NS_NewDOMEvent(aDOMEvent, aPresContext, nsnull);
862 4526 : if (aEventType.LowerCaseEqualsLiteral("svgevent") ||
863 2263 : aEventType.LowerCaseEqualsLiteral("svgevents"))
864 0 : return NS_NewDOMSVGEvent(aDOMEvent, aPresContext, nsnull);
865 4526 : if (aEventType.LowerCaseEqualsLiteral("svgzoomevent") ||
866 2263 : aEventType.LowerCaseEqualsLiteral("svgzoomevents"))
867 0 : return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext, nsnull);
868 4526 : if (aEventType.LowerCaseEqualsLiteral("timeevent") ||
869 2263 : aEventType.LowerCaseEqualsLiteral("timeevents"))
870 0 : return NS_NewDOMTimeEvent(aDOMEvent, aPresContext, nsnull);
871 4526 : if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
872 2263 : aEventType.LowerCaseEqualsLiteral("xulcommandevents"))
873 0 : return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext, nsnull);
874 4526 : if (aEventType.LowerCaseEqualsLiteral("commandevent") ||
875 2263 : aEventType.LowerCaseEqualsLiteral("commandevents"))
876 0 : return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nsnull);
877 4526 : if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") ||
878 2263 : aEventType.LowerCaseEqualsLiteral("datacontainerevents"))
879 0 : return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nsnull);
880 2263 : if (aEventType.LowerCaseEqualsLiteral("messageevent"))
881 0 : return NS_NewDOMMessageEvent(aDOMEvent, aPresContext, nsnull);
882 2263 : if (aEventType.LowerCaseEqualsLiteral("progressevent"))
883 2263 : 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 : }
|