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 "core/events/EventTarget.h"
34
35 #include "bindings/core/v8/ExceptionState.h"
36 #include "bindings/core/v8/V8DOMActivityLogger.h"
37 #include "core/dom/ExceptionCode.h"
38 #include "core/editing/Editor.h"
39 #include "core/events/Event.h"
40 #include "core/inspector/InspectorInstrumentation.h"
41 #include "core/frame/LocalDOMWindow.h"
42 #include "core/frame/UseCounter.h"
43 #include "platform/EventDispatchForbiddenScope.h"
44 #include "platform/RuntimeEnabledFeatures.h"
45 #include "wtf/StdLibExtras.h"
46 #include "wtf/Vector.h"
47
48 using namespace WTF;
49
50 namespace blink {
51
EventTargetData()52 EventTargetData::EventTargetData()
53 {
54 }
55
~EventTargetData()56 EventTargetData::~EventTargetData()
57 {
58 }
59
EventTarget()60 EventTarget::EventTarget()
61 {
62 }
63
~EventTarget()64 EventTarget::~EventTarget()
65 {
66 }
67
toNode()68 Node* EventTarget::toNode()
69 {
70 return 0;
71 }
72
toDOMWindow()73 LocalDOMWindow* EventTarget::toDOMWindow()
74 {
75 return 0;
76 }
77
toMessagePort()78 MessagePort* EventTarget::toMessagePort()
79 {
80 return 0;
81 }
82
executingWindow()83 inline LocalDOMWindow* EventTarget::executingWindow()
84 {
85 if (ExecutionContext* context = executionContext())
86 return context->executingWindow();
87 return 0;
88 }
89
addEventListener(const AtomicString & eventType,PassRefPtr<EventListener> listener,bool useCapture)90 bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
91 {
92 // FIXME: listener null check should throw TypeError (and be done in
93 // generated bindings), but breaks legacy content. http://crbug.com/249598
94 if (!listener)
95 return false;
96
97 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
98 if (activityLogger) {
99 Vector<String> argv;
100 argv.append(toNode() ? toNode()->nodeName() : interfaceName());
101 argv.append(eventType);
102 activityLogger->logEvent("blinkAddEventListener", argv.size(), argv.data());
103 }
104
105 return ensureEventTargetData().eventListenerMap.add(eventType, listener, useCapture);
106 }
107
removeEventListener(const AtomicString & eventType,PassRefPtr<EventListener> listener,bool useCapture)108 bool EventTarget::removeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
109 {
110 // FIXME: listener null check should throw TypeError (and be done in
111 // generated bindings), but breaks legacy content. http://crbug.com/249598
112 if (!listener)
113 return false;
114
115 EventTargetData* d = eventTargetData();
116 if (!d)
117 return false;
118
119 size_t indexOfRemovedListener;
120
121 if (!d->eventListenerMap.remove(eventType, listener.get(), useCapture, indexOfRemovedListener))
122 return false;
123
124 // Notify firing events planning to invoke the listener at 'index' that
125 // they have one less listener to invoke.
126 if (!d->firingEventIterators)
127 return true;
128 for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
129 FiringEventIterator& firingIterator = d->firingEventIterators->at(i);
130 if (eventType != firingIterator.eventType)
131 continue;
132
133 if (indexOfRemovedListener >= firingIterator.end)
134 continue;
135
136 --firingIterator.end;
137 if (indexOfRemovedListener <= firingIterator.iterator)
138 --firingIterator.iterator;
139 }
140
141 return true;
142 }
143
setAttributeEventListener(const AtomicString & eventType,PassRefPtr<EventListener> listener)144 bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener)
145 {
146 clearAttributeEventListener(eventType);
147 if (!listener)
148 return false;
149 return addEventListener(eventType, listener, false);
150 }
151
getAttributeEventListener(const AtomicString & eventType)152 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
153 {
154 const EventListenerVector& entry = getEventListeners(eventType);
155 for (size_t i = 0; i < entry.size(); ++i) {
156 EventListener* listener = entry[i].listener.get();
157 if (listener->isAttribute() && listener->belongsToTheCurrentWorld())
158 return listener;
159 }
160 return 0;
161 }
162
clearAttributeEventListener(const AtomicString & eventType)163 bool EventTarget::clearAttributeEventListener(const AtomicString& eventType)
164 {
165 EventListener* listener = getAttributeEventListener(eventType);
166 if (!listener)
167 return false;
168 return removeEventListener(eventType, listener, false);
169 }
170
dispatchEvent(PassRefPtrWillBeRawPtr<Event> event,ExceptionState & exceptionState)171 bool EventTarget::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event, ExceptionState& exceptionState)
172 {
173 if (!event) {
174 exceptionState.throwDOMException(InvalidStateError, "The event provided is null.");
175 return false;
176 }
177 if (event->type().isEmpty()) {
178 exceptionState.throwDOMException(InvalidStateError, "The event provided is uninitialized.");
179 return false;
180 }
181 if (event->isBeingDispatched()) {
182 exceptionState.throwDOMException(InvalidStateError, "The event is already being dispatched.");
183 return false;
184 }
185
186 if (!executionContext())
187 return false;
188
189 return dispatchEvent(event);
190 }
191
dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)192 bool EventTarget::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
193 {
194 event->setTarget(this);
195 event->setCurrentTarget(this);
196 event->setEventPhase(Event::AT_TARGET);
197 bool defaultPrevented = fireEventListeners(event.get());
198 event->setEventPhase(0);
199 return defaultPrevented;
200 }
201
uncaughtExceptionInEventHandler()202 void EventTarget::uncaughtExceptionInEventHandler()
203 {
204 }
205
legacyType(const Event * event)206 static const AtomicString& legacyType(const Event* event)
207 {
208 if (event->type() == EventTypeNames::transitionend)
209 return EventTypeNames::webkitTransitionEnd;
210
211 if (event->type() == EventTypeNames::animationstart)
212 return EventTypeNames::webkitAnimationStart;
213
214 if (event->type() == EventTypeNames::animationend)
215 return EventTypeNames::webkitAnimationEnd;
216
217 if (event->type() == EventTypeNames::animationiteration)
218 return EventTypeNames::webkitAnimationIteration;
219
220 if (event->type() == EventTypeNames::wheel)
221 return EventTypeNames::mousewheel;
222
223 return emptyAtom;
224 }
225
countLegacyEvents(const AtomicString & legacyTypeName,EventListenerVector * listenersVector,EventListenerVector * legacyListenersVector)226 void EventTarget::countLegacyEvents(const AtomicString& legacyTypeName, EventListenerVector* listenersVector, EventListenerVector* legacyListenersVector)
227 {
228 UseCounter::Feature unprefixedFeature;
229 UseCounter::Feature prefixedFeature;
230 UseCounter::Feature prefixedAndUnprefixedFeature;
231 if (legacyTypeName == EventTypeNames::webkitTransitionEnd) {
232 prefixedFeature = UseCounter::PrefixedTransitionEndEvent;
233 unprefixedFeature = UseCounter::UnprefixedTransitionEndEvent;
234 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedTransitionEndEvent;
235 } else if (legacyTypeName == EventTypeNames::webkitAnimationEnd) {
236 prefixedFeature = UseCounter::PrefixedAnimationEndEvent;
237 unprefixedFeature = UseCounter::UnprefixedAnimationEndEvent;
238 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedAnimationEndEvent;
239 } else if (legacyTypeName == EventTypeNames::webkitAnimationStart) {
240 prefixedFeature = UseCounter::PrefixedAnimationStartEvent;
241 unprefixedFeature = UseCounter::UnprefixedAnimationStartEvent;
242 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedAnimationStartEvent;
243 } else if (legacyTypeName == EventTypeNames::webkitAnimationIteration) {
244 prefixedFeature = UseCounter::PrefixedAnimationIterationEvent;
245 unprefixedFeature = UseCounter::UnprefixedAnimationIterationEvent;
246 prefixedAndUnprefixedFeature = UseCounter::PrefixedAndUnprefixedAnimationIterationEvent;
247 } else {
248 return;
249 }
250
251 if (LocalDOMWindow* executingWindow = this->executingWindow()) {
252 if (legacyListenersVector) {
253 if (listenersVector)
254 UseCounter::count(executingWindow->document(), prefixedAndUnprefixedFeature);
255 else
256 UseCounter::count(executingWindow->document(), prefixedFeature);
257 } else if (listenersVector) {
258 UseCounter::count(executingWindow->document(), unprefixedFeature);
259 }
260 }
261 }
262
fireEventListeners(Event * event)263 bool EventTarget::fireEventListeners(Event* event)
264 {
265 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
266 ASSERT(event && !event->type().isEmpty());
267
268 EventTargetData* d = eventTargetData();
269 if (!d)
270 return true;
271
272 EventListenerVector* legacyListenersVector = 0;
273 AtomicString legacyTypeName = legacyType(event);
274 if (!legacyTypeName.isEmpty())
275 legacyListenersVector = d->eventListenerMap.find(legacyTypeName);
276
277 EventListenerVector* listenersVector = d->eventListenerMap.find(event->type());
278 if (!RuntimeEnabledFeatures::cssAnimationUnprefixedEnabled() && (event->type() == EventTypeNames::animationiteration || event->type() == EventTypeNames::animationend
279 || event->type() == EventTypeNames::animationstart)
280 // Some code out-there uses custom events to dispatch unprefixed animation events manually,
281 // we can safely remove all this block when cssAnimationUnprefixedEnabled is always on, this
282 // is really a special case. DO NOT ADD MORE EVENTS HERE.
283 && event->interfaceName() != EventNames::CustomEvent)
284 listenersVector = 0;
285
286 if (listenersVector) {
287 fireEventListeners(event, d, *listenersVector);
288 } else if (legacyListenersVector) {
289 AtomicString unprefixedTypeName = event->type();
290 event->setType(legacyTypeName);
291 fireEventListeners(event, d, *legacyListenersVector);
292 event->setType(unprefixedTypeName);
293 }
294
295 Editor::countEvent(executionContext(), event);
296 countLegacyEvents(legacyTypeName, listenersVector, legacyListenersVector);
297 return !event->defaultPrevented();
298 }
299
fireEventListeners(Event * event,EventTargetData * d,EventListenerVector & entry)300 void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry)
301 {
302 RefPtrWillBeRawPtr<EventTarget> protect(this);
303
304 // Fire all listeners registered for this event. Don't fire listeners removed
305 // during event dispatch. Also, don't fire event listeners added during event
306 // dispatch. Conveniently, all new event listeners will be added after or at
307 // index |size|, so iterating up to (but not including) |size| naturally excludes
308 // new event listeners.
309
310 if (event->type() == EventTypeNames::beforeunload) {
311 if (LocalDOMWindow* executingWindow = this->executingWindow()) {
312 if (executingWindow->top())
313 UseCounter::count(executingWindow->document(), UseCounter::SubFrameBeforeUnloadFired);
314 UseCounter::count(executingWindow->document(), UseCounter::DocumentBeforeUnloadFired);
315 }
316 } else if (event->type() == EventTypeNames::unload) {
317 if (LocalDOMWindow* executingWindow = this->executingWindow())
318 UseCounter::count(executingWindow->document(), UseCounter::DocumentUnloadFired);
319 } else if (event->type() == EventTypeNames::DOMFocusIn || event->type() == EventTypeNames::DOMFocusOut) {
320 if (LocalDOMWindow* executingWindow = this->executingWindow())
321 UseCounter::count(executingWindow->document(), UseCounter::DOMFocusInOutEvent);
322 } else if (event->type() == EventTypeNames::focusin || event->type() == EventTypeNames::focusout) {
323 if (LocalDOMWindow* executingWindow = this->executingWindow())
324 UseCounter::count(executingWindow->document(), UseCounter::FocusInOutEvent);
325 }
326
327 size_t i = 0;
328 size_t size = entry.size();
329 if (!d->firingEventIterators)
330 d->firingEventIterators = adoptPtr(new FiringEventIteratorVector);
331 d->firingEventIterators->append(FiringEventIterator(event->type(), i, size));
332 for ( ; i < size; ++i) {
333 RegisteredEventListener& registeredListener = entry[i];
334 if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
335 continue;
336 if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
337 continue;
338
339 // If stopImmediatePropagation has been called, we just break out immediately, without
340 // handling any more events on this target.
341 if (event->immediatePropagationStopped())
342 break;
343
344 ExecutionContext* context = executionContext();
345 if (!context)
346 break;
347
348 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(this, event, registeredListener.listener.get(), registeredListener.useCapture);
349 // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
350 // event listeners, even though that violates some versions of the DOM spec.
351 registeredListener.listener->handleEvent(context, event);
352 InspectorInstrumentation::didHandleEvent(cookie);
353 }
354 d->firingEventIterators->removeLast();
355 }
356
getEventListeners(const AtomicString & eventType)357 const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType)
358 {
359 DEFINE_STATIC_LOCAL(EventListenerVector, emptyVector, ());
360
361 EventTargetData* d = eventTargetData();
362 if (!d)
363 return emptyVector;
364
365 EventListenerVector* listenerVector = d->eventListenerMap.find(eventType);
366 if (!listenerVector)
367 return emptyVector;
368
369 return *listenerVector;
370 }
371
eventTypes()372 Vector<AtomicString> EventTarget::eventTypes()
373 {
374 EventTargetData* d = eventTargetData();
375 return d ? d->eventListenerMap.eventTypes() : Vector<AtomicString>();
376 }
377
removeAllEventListeners()378 void EventTarget::removeAllEventListeners()
379 {
380 EventTargetData* d = eventTargetData();
381 if (!d)
382 return;
383 d->eventListenerMap.clear();
384
385 // Notify firing events planning to invoke the listener at 'index' that
386 // they have one less listener to invoke.
387 if (d->firingEventIterators) {
388 for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
389 d->firingEventIterators->at(i).iterator = 0;
390 d->firingEventIterators->at(i).end = 0;
391 }
392 }
393 }
394
395 } // namespace blink
396