dom/EventContext.cpp
dom/EventDispatchMediator.cpp
dom/EventDispatcher.cpp
+ dom/EventListenerMap.cpp
dom/EventNames.cpp
dom/EventTarget.cpp
dom/EventQueue.cpp
+2011-09-16 Andreas Kling <kling@webkit.org>
+
+ Reduce EventTarget memory usage by deferring hash map allocation
+ until there are listeners for more than 1 event type.
+
+ http://webkit.org/b/68105
+
+ Reviewed by Darin Adler.
+
+ Introduce an EventListenerMap class which manages a map of event types that have
+ one or more listeners connected.
+
+ When there is only one event type, it's stored directly on the EventListenerMap
+ internally, and when more are added it moves to a hash map. It only goes back
+ from the hash map if all the listeners are removed at once (i.e clear() is called.)
+
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * WebCore.gypi:
+ * WebCore.pro:
+ * WebCore.vcproj/WebCore.vcproj:
+ * WebCore.xcodeproj/project.pbxproj:
+
+ Adding files.
+
+ * WebCore.exp.in:
+
+ Export EventListenerMap::contains() for WebKit/mac.
+
+ * dom/EventListenerMap.cpp: Added.
+ (WebCore::EventListenerMap::EventListenerMap):
+ (WebCore::EventListenerMap::~EventListenerMap):
+ (WebCore::EventListenerMap::isEmpty):
+ (WebCore::EventListenerMap::contains):
+ (WebCore::EventListenerMap::clear):
+ (WebCore::EventListenerMap::eventTypes):
+ (WebCore::addListenerToVector):
+ (WebCore::EventListenerMap::add):
+ (WebCore::removeListenerFromVector):
+ (WebCore::EventListenerMap::remove):
+ (WebCore::EventListenerMap::find):
+ (WebCore::removeFirstListenerCreatedFromMarkup):
+ (WebCore::EventListenerMap::removeFirstEventListenerCreatedFromMarkup):
+ (WebCore::copyListenersNotCreatedFromMarkupToTarget):
+ (WebCore::EventListenerMap::copyEventListenersNotCreatedFromMarkupToTarget):
+ (WebCore::EventListenerIterator::EventListenerIterator):
+ (WebCore::EventListenerIterator::nextListener):
+ * dom/EventListenerMap.h: Added.
+
+ * dom/EventTarget.cpp:
+ (WebCore::EventTargetData::~EventTargetData):
+ (WebCore::EventTarget::addEventListener):
+ (WebCore::EventTarget::removeEventListener):
+ (WebCore::EventTarget::fireEventListeners):
+ (WebCore::EventTarget::getEventListeners):
+ (WebCore::EventTarget::removeAllEventListeners):
+
+ * dom/EventTarget.h:
+ (WebCore::EventTarget::visitJSEventListeners):
+
+ Use EventListenerIterator to visit listeners. (JSC specific.)
+
+ * inspector/InspectorDOMAgent.cpp:
+ (WebCore::InspectorDOMAgent::getEventListenersForNode):
+
+ Call EventListenerMap::eventTypes() go get the list of event types
+ currently listened for.
+
+ * dom/Node.cpp:
+ (WebCore::Node::removeEventListener):
+ * svg/SVGUseElement.cpp:
+ (WebCore::SVGUseElement::transferEventListenersToShadowTree):
+
+ Move implementations of SVG-specific hacks into EventListenerMap and
+ call them from here.
+
2011-09-16 Jeremy Apthorp <jeremya@chromium.org> and James Kozianski <koz@chromium.org>
Don't detach elements from the render tree when entering fullscreen mode
Source/WebCore/dom/EventDispatcher.h \
Source/WebCore/dom/EventException.h \
Source/WebCore/dom/EventListener.h \
+ Source/WebCore/dom/EventListenerMap.cpp \
+ Source/WebCore/dom/EventListenerMap.h \
Source/WebCore/dom/EventNames.cpp \
Source/WebCore/dom/EventNames.h \
Source/WebCore/dom/EventTarget.cpp \
__ZNK7WebCore15VisiblePosition19absoluteCaretBoundsEv
__ZNK7WebCore15VisiblePosition4nextENS_27EditingBoundaryCrossingRuleE
__ZNK7WebCore15VisiblePosition8previousENS_27EditingBoundaryCrossingRuleE
+__ZNK7WebCore16EventListenerMap8containsERKN3WTF12AtomicStringE
__ZNK7WebCore16FontFallbackList10fontDataAtEPKNS_4FontEj
__ZNK7WebCore16HTMLInputElement11isTextFieldEv
__ZNK7WebCore16HTMLInputElement14suggestedValueEv
'dom/Element.h',
'dom/Event.h',
'dom/EventListener.h',
+ 'dom/EventListenerMap.h',
'dom/EventNames.h',
'dom/EventTarget.h',
'dom/ExceptionCode.h',
'dom/EventDispatcher.cpp',
'dom/EventDispatcher.h',
'dom/EventException.h',
+ 'dom/EventListenerMap.cpp',
'dom/EventNames.cpp',
'dom/EventQueue.cpp',
'dom/EventQueue.h',
dom/EventContext.cpp \
dom/EventDispatchMediator.cpp \
dom/EventDispatcher.cpp \
+ dom/EventListenerMap.cpp \
dom/EventNames.cpp \
dom/EventTarget.cpp \
dom/EventQueue.cpp \
dom/EntityReference.h \
dom/Event.h \
dom/EventDispatchMediator.h \
+ dom/EventListenerMap.h \
dom/EventNames.h \
dom/EventTarget.h \
dom/ExceptionBase.h \
>
</File>
<File
+ RelativePath="..\dom\EventListenerMap.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug_Cairo_CFLite|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release_Cairo_CFLite|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug_All|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Production|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug_Internal|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug_Cairo|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release_Cairo|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\dom\EventListenerMap.h"
+ >
+ </File>
+ <File
RelativePath="..\dom\EventListener.h"
>
</File>
ABC128770B33AA6D00C693D5 /* PopupMenuClient.h in Headers */ = {isa = PBXBuildFile; fileRef = ABC128760B33AA6D00C693D5 /* PopupMenuClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
ABDDFE790A5C6E7000A3E11D /* RenderMenuList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ABDDFE730A5C6E6F00A3E11D /* RenderMenuList.cpp */; };
ABDDFE7A0A5C6E7000A3E11D /* RenderMenuList.h in Headers */ = {isa = PBXBuildFile; fileRef = ABDDFE740A5C6E7000A3E11D /* RenderMenuList.h */; };
+ AD4495F3141FC08900541EDF /* EventListenerMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD4495F1141FC08900541EDF /* EventListenerMap.cpp */; };
+ AD4495F4141FC08900541EDF /* EventListenerMap.h in Headers */ = {isa = PBXBuildFile; fileRef = AD4495F2141FC08900541EDF /* EventListenerMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
ADDF1AD71257CD9A0003A759 /* RenderSVGPath.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF1AD51257CD9A0003A759 /* RenderSVGPath.h */; };
B10B6980140C174000BC1C26 /* WebVTTToken.h in Headers */ = {isa = PBXBuildFile; fileRef = B10B697D140C174000BC1C26 /* WebVTTToken.h */; };
B10B6981140C174000BC1C26 /* WebVTTTokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B10B697E140C174000BC1C26 /* WebVTTTokenizer.cpp */; };
ABC128760B33AA6D00C693D5 /* PopupMenuClient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PopupMenuClient.h; sourceTree = "<group>"; };
ABDDFE730A5C6E6F00A3E11D /* RenderMenuList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMenuList.cpp; sourceTree = "<group>"; };
ABDDFE740A5C6E7000A3E11D /* RenderMenuList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RenderMenuList.h; sourceTree = "<group>"; };
+ AD4495F1141FC08900541EDF /* EventListenerMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventListenerMap.cpp; sourceTree = "<group>"; };
+ AD4495F2141FC08900541EDF /* EventListenerMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventListenerMap.h; sourceTree = "<group>"; };
ADDF1AD41257CD9A0003A759 /* RenderSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderSVGPath.cpp; sourceTree = "<group>"; };
ADDF1AD51257CD9A0003A759 /* RenderSVGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderSVGPath.h; sourceTree = "<group>"; };
B10B697D140C174000BC1C26 /* WebVTTToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTToken.h; sourceTree = "<group>"; };
BC60D90A0D2A17CE00B9918F /* EventException.idl */,
935FBC4409BA00B900E230B1 /* EventListener.h */,
85AFA7410AAF298400E84305 /* EventListener.idl */,
+ AD4495F1141FC08900541EDF /* EventListenerMap.cpp */,
+ AD4495F2141FC08900541EDF /* EventListenerMap.h */,
939885C108B7E3D100E707C4 /* EventNames.cpp */,
939885C208B7E3D100E707C4 /* EventNames.h */,
8F67561A1288B17B0047ACA3 /* EventQueue.cpp */,
29A8122C0FBB9C1D00510293 /* AccessibilityList.h in Headers */,
29A812430FBB9C1D00510293 /* AccessibilityListBox.h in Headers */,
29A812420FBB9C1D00510293 /* AccessibilityListBoxOption.h in Headers */,
+ AD4495F4141FC08900541EDF /* EventListenerMap.h in Headers */,
07B0113F1032242200FBDC33 /* AccessibilityMediaControls.h in Headers */,
76CDD2F31103DA6600680521 /* AccessibilityMenuList.h in Headers */,
76CDD2F71103DA6600680521 /* AccessibilityMenuListOption.h in Headers */,
418A06D1133C04D500CD379C /* EventDispatcher.cpp in Sources */,
93C09A810B064F00005ABD4D /* EventHandler.cpp in Sources */,
93C09A7F0B064EEF005ABD4D /* EventHandlerMac.mm in Sources */,
+ AD4495F3141FC08900541EDF /* EventListenerMap.cpp in Sources */,
1CA19E050DC255950065A994 /* EventLoopMac.mm in Sources */,
939885C308B7E3D100E707C4 /* EventNames.cpp in Sources */,
8F67561C1288B17B0047ACA3 /* EventQueue.cpp in Sources */,
--- /dev/null
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2011 Andreas Kling (kling@webkit.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+#include "EventListenerMap.h"
+
+#include "Event.h"
+#include "EventException.h"
+#include <wtf/MainThread.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+EventListenerMap::EventListenerMap()
+{
+}
+
+EventListenerMap::~EventListenerMap()
+{
+ clear();
+}
+
+bool EventListenerMap::isEmpty() const
+{
+ if (m_hashMap)
+ return m_hashMap->isEmpty();
+ return !m_singleEventListenerVector;
+}
+
+bool EventListenerMap::contains(const AtomicString& eventType) const
+{
+ if (m_hashMap)
+ return m_hashMap->contains(eventType);
+ return m_singleEventListenerType == eventType;
+}
+
+void EventListenerMap::clear()
+{
+ if (m_hashMap) {
+ deleteAllValues(*m_hashMap);
+ m_hashMap.clear();
+ } else {
+ m_singleEventListenerType = nullAtom;
+ m_singleEventListenerVector.clear();
+ }
+}
+
+Vector<AtomicString> EventListenerMap::eventTypes() const
+{
+ Vector<AtomicString> types;
+
+ if (m_hashMap) {
+ EventListenerHashMap::iterator it = m_hashMap->begin();
+ EventListenerHashMap::iterator end = m_hashMap->end();
+ for (; it != end; ++it)
+ types.append(it->first);
+ } else if (m_singleEventListenerVector)
+ types.append(m_singleEventListenerType);
+
+ return types;
+}
+
+static bool addListenerToVector(EventListenerVector* vector, PassRefPtr<EventListener> listener, bool useCapture)
+{
+ RegisteredEventListener registeredListener(listener, useCapture);
+
+ if (vector->find(registeredListener) != notFound)
+ return false; // Duplicate listener.
+
+ vector->append(registeredListener);
+ return true;
+}
+
+bool EventListenerMap::add(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
+{
+ if (m_singleEventListenerVector && m_singleEventListenerType != eventType) {
+ // We already have a single (first) listener vector, and this event is not
+ // of that type, so create the hash map and move the first listener vector there.
+ ASSERT(!m_hashMap);
+ m_hashMap = adoptPtr(new EventListenerHashMap);
+ m_hashMap->add(m_singleEventListenerType, m_singleEventListenerVector.leakPtr());
+ m_singleEventListenerType = nullAtom;
+ }
+
+ if (m_hashMap) {
+ pair<EventListenerHashMap::iterator, bool> result = m_hashMap->add(eventType, 0);
+ if (result.second)
+ result.first->second = new EventListenerVector;
+
+ return addListenerToVector(result.first->second, listener, useCapture);
+ }
+
+ if (!m_singleEventListenerVector) {
+ m_singleEventListenerType = eventType;
+ m_singleEventListenerVector = adoptPtr(new EventListenerVector);
+ }
+
+ ASSERT(m_singleEventListenerType == eventType);
+ return addListenerToVector(m_singleEventListenerVector.get(), listener, useCapture);
+}
+
+static bool removeListenerFromVector(EventListenerVector* listenerVector, EventListener* listener, bool useCapture, size_t& indexOfRemovedListener)
+{
+ RegisteredEventListener registeredListener(listener, useCapture);
+ indexOfRemovedListener = listenerVector->find(registeredListener);
+ if (indexOfRemovedListener == notFound)
+ return false;
+ listenerVector->remove(indexOfRemovedListener);
+ return true;
+}
+
+bool EventListenerMap::remove(const AtomicString& eventType, EventListener* listener, bool useCapture, size_t& indexOfRemovedListener)
+{
+ if (!m_hashMap) {
+ if (m_singleEventListenerType != eventType)
+ return false;
+ bool wasRemoved = removeListenerFromVector(m_singleEventListenerVector.get(), listener, useCapture, indexOfRemovedListener);
+ if (m_singleEventListenerVector->isEmpty()) {
+ m_singleEventListenerVector.clear();
+ m_singleEventListenerType = nullAtom;
+ }
+ return wasRemoved;
+ }
+
+ EventListenerHashMap::iterator it = m_hashMap->find(eventType);
+ if (it == m_hashMap->end())
+ return false;
+
+ bool wasRemoved = removeListenerFromVector(it->second, listener, useCapture, indexOfRemovedListener);
+ if (it->second->isEmpty()) {
+ delete it->second;
+ m_hashMap->remove(it);
+ }
+ return wasRemoved;
+}
+
+EventListenerVector* EventListenerMap::find(const AtomicString& eventType)
+{
+ if (m_hashMap) {
+ EventListenerHashMap::iterator it = m_hashMap->find(eventType);
+ if (it == m_hashMap->end())
+ return 0;
+ return it->second;
+ }
+
+ if (m_singleEventListenerType == eventType)
+ return m_singleEventListenerVector.get();
+
+ return 0;
+}
+
+#if ENABLE(SVG)
+
+static void removeFirstListenerCreatedFromMarkup(EventListenerVector* listenerVector)
+{
+ bool foundListener = false;
+
+ for (size_t i = 0; i < listenerVector->size(); ++i) {
+ if (!listenerVector->at(i).listener->wasCreatedFromMarkup())
+ continue;
+ foundListener = true;
+ listenerVector->remove(i);
+ break;
+ }
+
+ ASSERT_UNUSED(foundListener, foundListener);
+}
+
+void EventListenerMap::removeFirstEventListenerCreatedFromMarkup(const AtomicString& eventType)
+{
+ if (m_hashMap) {
+ EventListenerHashMap::iterator result = m_hashMap->find(eventType);
+ ASSERT(result != m_hashMap->end());
+
+ EventListenerVector* listenerVector = result->second;
+ ASSERT(listenerVector);
+
+ removeFirstListenerCreatedFromMarkup(listenerVector);
+
+ if (listenerVector->isEmpty()) {
+ delete listenerVector;
+ m_hashMap->remove(result);
+ }
+
+ return;
+ }
+
+ ASSERT(m_singleEventListenerVector);
+ ASSERT(m_singleEventListenerType == eventType);
+
+ removeFirstListenerCreatedFromMarkup(m_singleEventListenerVector.get());
+ if (m_singleEventListenerVector->isEmpty()) {
+ m_singleEventListenerVector.clear();
+ m_singleEventListenerType = nullAtom;
+ }
+}
+
+static void copyListenersNotCreatedFromMarkupToTarget(const AtomicString& eventType, EventListenerVector* listenerVector, EventTarget* target)
+{
+ for (size_t i = 0; i < listenerVector->size(); ++i) {
+ // Event listeners created from markup have already been transfered to the shadow tree during cloning.
+ if ((*listenerVector)[i].listener->wasCreatedFromMarkup())
+ continue;
+ target->addEventListener(eventType, (*listenerVector)[i].listener, (*listenerVector)[i].useCapture);
+ }
+}
+
+void EventListenerMap::copyEventListenersNotCreatedFromMarkupToTarget(EventTarget* target)
+{
+ if (m_hashMap) {
+ EventListenerHashMap::iterator end = m_hashMap->end();
+ for (EventListenerHashMap::iterator it = m_hashMap->begin(); it != end; ++it)
+ copyListenersNotCreatedFromMarkupToTarget(it->first, it->second, target);
+ return;
+ }
+
+ if (!m_singleEventListenerVector)
+ return;
+
+ copyListenersNotCreatedFromMarkupToTarget(m_singleEventListenerType, m_singleEventListenerVector.get(), target);
+}
+
+#endif // ENABLE(SVG)
+
+EventListenerIterator::EventListenerIterator()
+ : m_map(0)
+ , m_index(0)
+{
+}
+
+EventListenerIterator::EventListenerIterator(EventTarget* target)
+ : m_map(0)
+ , m_index(0)
+{
+ ASSERT(target);
+ EventTargetData* data = target->eventTargetData();
+
+ if (!data)
+ return;
+
+ m_map = &data->eventListenerMap;
+
+ if (m_map->m_hashMap) {
+ m_mapIterator = m_map->m_hashMap->begin();
+ m_mapEnd = m_map->m_hashMap->end();
+ }
+}
+
+EventListener* EventListenerIterator::nextListener()
+{
+ if (!m_map)
+ return 0;
+
+ if (m_map->m_hashMap) {
+ for (; m_mapIterator != m_mapEnd; ++m_mapIterator) {
+ EventListenerVector& listeners = *m_mapIterator->second;
+ if (m_index < listeners.size())
+ return listeners[m_index++].listener.get();
+ m_index = 0;
+ }
+ return 0;
+ }
+
+ if (!m_map->m_singleEventListenerVector)
+ return 0;
+ EventListenerVector& listeners = *m_map->m_singleEventListenerVector;
+ if (m_index < listeners.size())
+ return listeners[m_index++].listener.get();
+ return 0;
+}
+
+} // namespace WebCore
--- /dev/null
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2011 Andreas Kling (kling@webkit.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EventListenerMap_h
+#define EventListenerMap_h
+
+#include "RegisteredEventListener.h"
+#include <wtf/Forward.h>
+#include <wtf/HashMap.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+class EventTarget;
+
+typedef Vector<RegisteredEventListener, 1> EventListenerVector;
+
+class EventListenerMap {
+public:
+ EventListenerMap();
+ ~EventListenerMap();
+
+ bool isEmpty() const;
+ bool contains(const AtomicString& eventType) const;
+
+ void clear();
+ bool add(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
+ bool remove(const AtomicString& eventType, EventListener*, bool useCapture, size_t& indexOfRemovedListener);
+ EventListenerVector* find(const AtomicString& eventType);
+ Vector<AtomicString> eventTypes() const;
+
+#if ENABLE(SVG)
+ void removeFirstEventListenerCreatedFromMarkup(const AtomicString& eventType);
+ void copyEventListenersNotCreatedFromMarkupToTarget(EventTarget*);
+#endif
+
+private:
+ friend class EventListenerIterator;
+
+ struct EventListenerHashMapTraits : HashTraits<WTF::AtomicString> {
+ static const int minimumTableSize = 32;
+ };
+ typedef HashMap<AtomicString, EventListenerVector*, AtomicStringHash, EventListenerHashMapTraits> EventListenerHashMap;
+
+ OwnPtr<EventListenerHashMap> m_hashMap;
+
+ AtomicString m_singleEventListenerType;
+ OwnPtr<EventListenerVector> m_singleEventListenerVector;
+};
+
+class EventListenerIterator {
+ WTF_MAKE_NONCOPYABLE(EventListenerIterator);
+public:
+ EventListenerIterator();
+
+ // EventTarget must not be modified while an iterator is active.
+ EventListenerIterator(EventTarget*);
+
+ EventListener* nextListener();
+
+private:
+ EventListenerMap* m_map;
+ EventListenerMap::EventListenerHashMap::iterator m_mapIterator;
+ EventListenerMap::EventListenerHashMap::iterator m_mapEnd;
+ unsigned m_index;
+};
+
+} // namespace WebCore
+
+#endif // EventListenerMap_h
EventTargetData::~EventTargetData()
{
- deleteAllValues(eventListenerMap);
}
EventTarget::~EventTarget()
bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
{
EventTargetData* d = ensureEventTargetData();
-
- pair<EventListenerMap::iterator, bool> result = d->eventListenerMap.add(eventType, 0);
- EventListenerVector*& entry = result.first->second;
- const bool isNewEntry = result.second;
- if (isNewEntry)
- entry = new EventListenerVector();
-
- RegisteredEventListener registeredListener(listener, useCapture);
- if (!isNewEntry) {
- if (entry->find(registeredListener) != notFound) // duplicate listener
- return false;
- }
-
- entry->append(registeredListener);
- return true;
+ return d->eventListenerMap.add(eventType, listener, useCapture);
}
bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
if (!d)
return false;
- EventListenerMap::iterator result = d->eventListenerMap.find(eventType);
- if (result == d->eventListenerMap.end())
- return false;
- EventListenerVector* entry = result->second;
+ size_t indexOfRemovedListener;
- RegisteredEventListener registeredListener(listener, useCapture);
- size_t index = entry->find(registeredListener);
- if (index == notFound)
+ if (!d->eventListenerMap.remove(eventType, listener, useCapture, indexOfRemovedListener))
return false;
- entry->remove(index);
- if (entry->isEmpty()) {
- delete entry;
- d->eventListenerMap.remove(result);
- }
-
// Notify firing events planning to invoke the listener at 'index' that
// they have one less listener to invoke.
for (size_t i = 0; i < d->firingEventIterators.size(); ++i) {
if (eventType != d->firingEventIterators[i].eventType)
continue;
- if (index >= d->firingEventIterators[i].end)
+ if (indexOfRemovedListener >= d->firingEventIterators[i].end)
continue;
--d->firingEventIterators[i].end;
- if (index <= d->firingEventIterators[i].iterator)
+ if (indexOfRemovedListener <= d->firingEventIterators[i].iterator)
--d->firingEventIterators[i].iterator;
}
if (!d)
return true;
- EventListenerMap::iterator result = d->eventListenerMap.find(event->type());
- if (result != d->eventListenerMap.end())
- fireEventListeners(event, d, *result->second);
+ EventListenerVector* listenerVector = d->eventListenerMap.find(event->type());
+
+ if (listenerVector)
+ fireEventListeners(event, d, *listenerVector);
return !event->defaultPrevented();
}
EventTargetData* d = eventTargetData();
if (!d)
return emptyVector;
- EventListenerMap::iterator it = d->eventListenerMap.find(eventType);
- if (it == d->eventListenerMap.end())
+
+ EventListenerVector* listenerVector = d->eventListenerMap.find(eventType);
+ if (!listenerVector)
return emptyVector;
- return *it->second;
+
+ return *listenerVector;
}
void EventTarget::removeAllEventListeners()
EventTargetData* d = eventTargetData();
if (!d)
return;
- deleteAllValues(d->eventListenerMap);
d->eventListenerMap.clear();
// Notify firing events planning to invoke the listener at 'index' that
}
}
-EventListenerIterator::EventListenerIterator()
- : m_index(0)
-{
-}
-
-EventListenerIterator::EventListenerIterator(EventTarget* target)
- : m_index(0)
-{
- EventTargetData* data = target->eventTargetData();
- if (!data)
- return;
- m_mapIterator = data->eventListenerMap.begin();
- m_mapEnd = data->eventListenerMap.end();
-}
-
-EventListener* EventListenerIterator::nextListener()
-{
- for (; m_mapIterator != m_mapEnd; ++m_mapIterator) {
- EventListenerVector& listeners = *m_mapIterator->second;
- if (m_index < listeners.size())
- return listeners[m_index++].listener.get();
- m_index = 0;
- }
- return 0;
-}
-
} // namespace WebCore
#ifndef EventTarget_h
#define EventTarget_h
+#include "EventListenerMap.h"
#include "EventNames.h"
-#include "RegisteredEventListener.h"
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/text/AtomicStringHash.h>
};
typedef Vector<FiringEventIterator, 1> FiringEventIteratorVector;
- typedef Vector<RegisteredEventListener, 1> EventListenerVector;
-
- struct EventListenerMapHashTraits : HashTraits<WTF::AtomicString> {
- static const int minimumTableSize = 32;
- };
-
- typedef HashMap<AtomicString, EventListenerVector*, AtomicStringHash, EventListenerMapHashTraits> EventListenerMap;
-
struct EventTargetData {
WTF_MAKE_NONCOPYABLE(EventTargetData); WTF_MAKE_FAST_ALLOCATED;
public:
friend class EventListenerIterator;
};
- class EventListenerIterator {
- public:
- EventListenerIterator();
-
- // EventTarget must not be modified while an iterator is active.
- EventListenerIterator(EventTarget*);
-
- EventListener* nextListener();
-
- private:
- EventListenerMap::iterator m_mapIterator;
- EventListenerMap::iterator m_mapEnd;
- unsigned m_index;
- };
-
// FIXME: These macros should be split into separate DEFINE and DECLARE
// macros to avoid causing so many header includes.
#define DEFINE_ATTRIBUTE_EVENT_LISTENER(attribute) \
#if USE(JSC)
inline void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor)
{
- EventTargetData* d = eventTargetData();
- if (!d)
- return;
-
- EventListenerMap::iterator end = d->eventListenerMap.end();
- for (EventListenerMap::iterator it = d->eventListenerMap.begin(); it != end; ++it) {
- EventListenerVector& entry = *it->second;
- for (size_t i = 0; i < entry.size(); ++i)
- entry[i].listener->visitJSFunction(visitor);
- }
+ EventListenerIterator iterator(this);
+ while (EventListener* listener = iterator.nextListener())
+ listener->visitJSFunction(visitor);
}
#endif
EventTargetData* data = shadowTreeElement->eventTargetData();
ASSERT(data);
- EventListenerMap::iterator result = data->eventListenerMap.find(eventType);
- ASSERT(result != data->eventListenerMap.end());
-
- EventListenerVector* entry = result->second;
- ASSERT(entry);
-
- unsigned int index = 0;
- bool foundListener = false;
-
- EventListenerVector::iterator end = entry->end();
- for (EventListenerVector::iterator it = entry->begin(); it != end; ++it) {
- if (!(*it).listener->wasCreatedFromMarkup()) {
- ++index;
- continue;
- }
-
- foundListener = true;
- entry->remove(index);
- break;
- }
-
- ASSERT_UNUSED(foundListener, foundListener);
-
- if (entry->isEmpty()) {
- delete entry;
- data->eventListenerMap.remove(result);
- }
+ data->eventListenerMap.removeFirstEventListenerCreatedFromMarkup(eventType);
}
return true;
return;
// Get the list of event types this Node is concerned with
- Vector<AtomicString> eventTypes;
- const EventListenerMap& listenerMap = d->eventListenerMap;
- EventListenerMap::const_iterator end = listenerMap.end();
- for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
- eventTypes.append(iter->first);
+ Vector<AtomicString> eventTypes = d->eventListenerMap.eventTypes();
// Quick break if no useful listeners
size_t eventTypesLength = eventTypes.size();
ASSERT(originalElement);
if (SVGElement* shadowTreeElement = target->shadowTreeElement()) {
- if (EventTargetData* d = originalElement->eventTargetData()) {
- EventListenerMap& map = d->eventListenerMap;
- EventListenerMap::iterator end = map.end();
- for (EventListenerMap::iterator it = map.begin(); it != end; ++it) {
- EventListenerVector& entry = *it->second;
- for (size_t i = 0; i < entry.size(); ++i) {
- // Event listeners created from markup have already been transfered to the shadow tree during cloning.
- if (entry[i].listener->wasCreatedFromMarkup())
- continue;
- shadowTreeElement->addEventListener(it->first, entry[i].listener, entry[i].useCapture);
- }
- }
- }
+ if (EventTargetData* data = originalElement->eventTargetData())
+ data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(shadowTreeElement);
}
for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling())