c11d20f12da3ebdb60de79aa0a90848dfb507d17
[WebKit-https.git] / Source / WebCore / dom / EventTarget.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6  * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7  *           (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
29  *
30  */
31
32 #include "config.h"
33 #include "EventTarget.h"
34
35 #include "Event.h"
36 #include "EventException.h"
37 #include "InspectorInstrumentation.h"
38 #include "WebKitTransitionEvent.h"
39 #include <wtf/MainThread.h>
40 #include <wtf/StdLibExtras.h>
41 #include <wtf/Vector.h>
42
43 using namespace WTF;
44
45 namespace WebCore {
46
47 EventTargetData::EventTargetData()
48 {
49 }
50
51 EventTargetData::~EventTargetData()
52 {
53 }
54
55 EventTarget::~EventTarget()
56 {
57 }
58
59 Node* EventTarget::toNode()
60 {
61     return 0;
62 }
63
64 DOMWindow* EventTarget::toDOMWindow()
65 {
66     return 0;
67 }
68
69 bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
70 {
71     EventTargetData* d = ensureEventTargetData();
72     return d->eventListenerMap.add(eventType, listener, useCapture);
73 }
74
75 bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
76 {
77     EventTargetData* d = eventTargetData();
78     if (!d)
79         return false;
80
81     size_t indexOfRemovedListener;
82
83     if (!d->eventListenerMap.remove(eventType, listener, useCapture, indexOfRemovedListener))
84         return false;
85
86     // Notify firing events planning to invoke the listener at 'index' that
87     // they have one less listener to invoke.
88     if (!d->firingEventIterators)
89         return true;
90     for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
91         FiringEventIterator& firingIterator = d->firingEventIterators->at(i);
92         if (eventType != firingIterator.eventType)
93             continue;
94
95         if (indexOfRemovedListener >= firingIterator.end)
96             continue;
97
98         --firingIterator.end;
99         if (indexOfRemovedListener <= firingIterator.iterator)
100             --firingIterator.iterator;
101     }
102
103     return true;
104 }
105
106 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
107 {
108     clearAttributeEventListener(eventType);
109     if (!listener)
110         return false;
111     return addEventListener(eventType, listener, false);
112 }
113
114 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
115 {
116     const EventListenerVector& entry = getEventListeners(eventType);
117     for (size_t i = 0; i < entry.size(); ++i) {
118         if (entry[i].listener->isAttribute())
119             return entry[i].listener.get();
120     }
121     return 0;
122 }
123
124 bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
125 {
126     EventListener* listener = getAttributeEventListener(eventType);
127     if (!listener)
128         return false;
129     return removeEventListener(eventType, listener, false);
130 }
131
132 bool EventTarget::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
133 {
134     if (!event || event->type().isEmpty()) {
135         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
136         return false;
137     }
138
139     if (event->isBeingDispatched()) {
140         ec = EventException::DISPATCH_REQUEST_ERR;
141         return false;
142     }
143
144     if (!scriptExecutionContext())
145         return false;
146
147     return dispatchEvent(event);
148 }
149
150 bool EventTarget::dispatchEvent(PassRefPtr<Event> event)
151 {
152     event->setTarget(this);
153     event->setCurrentTarget(this);
154     event->setEventPhase(Event::AT_TARGET);
155     bool defaultPrevented = fireEventListeners(event.get());
156     event->setEventPhase(0);
157     return defaultPrevented;
158 }
159
160 void EventTarget::uncaughtExceptionInEventHandler()
161 {
162 }
163
164 static PassRefPtr<Event> createMatchingPrefixedEvent(const Event* event)
165 {
166     if (event->type() == eventNames().transitionendEvent) {
167         const WebKitTransitionEvent* transitionEvent = static_cast<const WebKitTransitionEvent*>(event);
168         RefPtr<Event> prefixedEvent = WebKitTransitionEvent::create(eventNames().webkitTransitionEndEvent, transitionEvent->propertyName(), transitionEvent->elapsedTime(), transitionEvent->pseudoElement());
169         prefixedEvent->setTarget(event->target());
170         prefixedEvent->setCurrentTarget(event->currentTarget());
171         prefixedEvent->setEventPhase(event->eventPhase());
172         return prefixedEvent.release();
173     }
174     ASSERT_NOT_REACHED();
175     return 0;
176 }
177
178 static AtomicString prefixedType(const Event* event)
179 {
180     if (event->type() == eventNames().transitionendEvent)
181         return eventNames().webkitTransitionEndEvent;
182
183     return emptyString();
184 }
185
186 bool EventTarget::fireEventListeners(Event* event)
187 {
188     ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
189     ASSERT(event && !event->type().isEmpty());
190
191     EventTargetData* d = eventTargetData();
192     if (!d)
193         return true;
194
195     EventListenerVector* listenerPrefixedVector = 0;
196     AtomicString prefixedTypeName = prefixedType(event);
197     if (!prefixedTypeName.isEmpty())
198         listenerPrefixedVector = d->eventListenerMap.find(prefixedTypeName);
199
200     EventListenerVector* listenerUnprefixedVector = d->eventListenerMap.find(event->type());
201
202     if (listenerUnprefixedVector)
203         fireEventListeners(event, d, *listenerUnprefixedVector);
204     else if (listenerPrefixedVector)
205         fireEventListeners(createMatchingPrefixedEvent(event).get(), d, *listenerPrefixedVector);
206
207     if (!prefixedTypeName.isEmpty()) {
208         ScriptExecutionContext* context = scriptExecutionContext();
209         if (context && context->isDocument()) {
210             Document* document = static_cast<Document*>(context);
211             if (document->domWindow()) {
212                 if (listenerPrefixedVector)
213                     if (listenerUnprefixedVector)
214                         FeatureObserver::observe(document->domWindow(), FeatureObserver::PrefixedAndUnprefixedTransitionEndEvent);
215                     else
216                         FeatureObserver::observe(document->domWindow(), FeatureObserver::PrefixedTransitionEndEvent);
217                 else if (listenerUnprefixedVector)
218                     FeatureObserver::observe(document->domWindow(), FeatureObserver::UnprefixedTransitionEndEvent);
219             }
220         }
221     }
222
223     return !event->defaultPrevented();
224 }
225         
226 void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry)
227 {
228     RefPtr<EventTarget> protect = this;
229
230     // Fire all listeners registered for this event. Don't fire listeners removed
231     // during event dispatch. Also, don't fire event listeners added during event
232     // dispatch. Conveniently, all new event listeners will be added after 'end',
233     // so iterating to 'end' naturally excludes new event listeners.
234
235     bool userEventWasHandled = false;
236     size_t i = 0;
237     size_t end = entry.size();
238     if (!d->firingEventIterators)
239         d->firingEventIterators = adoptPtr(new FiringEventIteratorVector);
240     d->firingEventIterators->append(FiringEventIterator(event->type(), i, end));
241     for ( ; i < end; ++i) {
242         RegisteredEventListener& registeredListener = entry[i];
243         if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
244             continue;
245         if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
246             continue;
247
248         // If stopImmediatePropagation has been called, we just break out immediately, without
249         // handling any more events on this target.
250         if (event->immediatePropagationStopped())
251             break;
252
253         ScriptExecutionContext* context = scriptExecutionContext();
254         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event);
255         // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
256         // event listeners, even though that violates some versions of the DOM spec.
257         registeredListener.listener->handleEvent(context, event);
258         if (!userEventWasHandled && ScriptController::processingUserGesture())
259             userEventWasHandled = true;
260         InspectorInstrumentation::didHandleEvent(cookie);
261     }
262     d->firingEventIterators->removeLast();
263     if (userEventWasHandled) {
264         ScriptExecutionContext* context = scriptExecutionContext();
265         if (context && context->isDocument()) {
266             Document* document = static_cast<Document*>(context);
267             document->resetLastHandledUserGestureTimestamp();
268         }
269     }
270 }
271
272 const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
273 {
274     DEFINE_STATIC_LOCAL(EventListenerVector, emptyVector, ());
275
276     EventTargetData* d = eventTargetData();
277     if (!d)
278         return emptyVector;
279
280     EventListenerVector* listenerVector = d->eventListenerMap.find(eventType);
281     if (!listenerVector)
282         return emptyVector;
283
284     return *listenerVector;
285 }
286
287 void EventTarget::removeAllEventListeners()
288 {
289     EventTargetData* d = eventTargetData();
290     if (!d)
291         return;
292     d->eventListenerMap.clear();
293
294     // Notify firing events planning to invoke the listener at 'index' that
295     // they have one less listener to invoke.
296     if (d->firingEventIterators) {
297         for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
298             d->firingEventIterators->at(i).iterator = 0;
299             d->firingEventIterators->at(i).end = 0;
300         }
301     }
302 }
303
304 } // namespace WebCore