Reduce EventTarget memory usage by deferring hash map allocation
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 17 Sep 2011 06:34:05 +0000 (06:34 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 17 Sep 2011 06:34:05 +0000 (06:34 +0000)
until there are listeners for more than 1 event type.

http://webkit.org/b/68105

Patch by Andreas Kling <kling@webkit.org> on 2011-09-16
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.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@95372 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/WebCore.exp.in
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.pro
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/EventListenerMap.cpp [new file with mode: 0644]
Source/WebCore/dom/EventListenerMap.h [new file with mode: 0644]
Source/WebCore/dom/EventTarget.cpp
Source/WebCore/dom/EventTarget.h
Source/WebCore/dom/Node.cpp
Source/WebCore/inspector/InspectorDOMAgent.cpp
Source/WebCore/svg/SVGUseElement.cpp

index a41d260..7b79dbe 100644 (file)
@@ -529,6 +529,7 @@ SET(WebCore_SOURCES
     dom/EventContext.cpp
     dom/EventDispatchMediator.cpp
     dom/EventDispatcher.cpp
+    dom/EventListenerMap.cpp
     dom/EventNames.cpp
     dom/EventTarget.cpp
     dom/EventQueue.cpp
index db25fa2..fd1ea66 100644 (file)
@@ -1,3 +1,79 @@
+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
index d87aa36..6213a11 100644 (file)
@@ -1177,6 +1177,8 @@ webcore_sources += \
        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 \
index 91ee5d1..388daf5 100644 (file)
@@ -1184,6 +1184,7 @@ __ZNK7WebCore15VisiblePosition14localCaretRectERPNS_12RenderObjectE
 __ZNK7WebCore15VisiblePosition19absoluteCaretBoundsEv
 __ZNK7WebCore15VisiblePosition4nextENS_27EditingBoundaryCrossingRuleE
 __ZNK7WebCore15VisiblePosition8previousENS_27EditingBoundaryCrossingRuleE
+__ZNK7WebCore16EventListenerMap8containsERKN3WTF12AtomicStringE
 __ZNK7WebCore16FontFallbackList10fontDataAtEPKNS_4FontEj
 __ZNK7WebCore16HTMLInputElement11isTextFieldEv
 __ZNK7WebCore16HTMLInputElement14suggestedValueEv
index a1420e9..ac1b1a1 100644 (file)
             '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',
index 79388a5..ab905fe 100644 (file)
@@ -499,6 +499,7 @@ SOURCES += \
     dom/EventContext.cpp \
     dom/EventDispatchMediator.cpp \
     dom/EventDispatcher.cpp \
+    dom/EventListenerMap.cpp \
     dom/EventNames.cpp \
     dom/EventTarget.cpp \
     dom/EventQueue.cpp \
@@ -1506,6 +1507,7 @@ HEADERS += \
     dom/EntityReference.h \
     dom/Event.h \
     dom/EventDispatchMediator.h \
+    dom/EventListenerMap.h \
     dom/EventNames.h \
     dom/EventTarget.h \
     dom/ExceptionBase.h \
index bef5705..cfea114 100755 (executable)
                                >
                        </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>
index a0f21db..35e69cc 100644 (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 */,
diff --git a/Source/WebCore/dom/EventListenerMap.cpp b/Source/WebCore/dom/EventListenerMap.cpp
new file mode 100644 (file)
index 0000000..8feb2e8
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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
diff --git a/Source/WebCore/dom/EventListenerMap.h b/Source/WebCore/dom/EventListenerMap.h
new file mode 100644 (file)
index 0000000..5b8c1ae
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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
index 42aea4a..f33c030 100644 (file)
@@ -74,7 +74,6 @@ EventTargetData::EventTargetData()
 
 EventTargetData::~EventTargetData()
 {
-    deleteAllValues(eventListenerMap);
 }
 
 EventTarget::~EventTarget()
@@ -226,21 +225,7 @@ PeerConnection* EventTarget::toPeerConnection()
 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)
@@ -249,33 +234,22 @@ bool EventTarget::removeEventListener(const AtomicString& eventType, EventListen
     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;
     }
 
@@ -349,9 +323,10 @@ bool EventTarget::fireEventListeners(Event* event)
     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();
 }
@@ -394,10 +369,12 @@ const EventListenerVector& EventTarget::getEventListeners(const AtomicString& ev
     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()
@@ -405,7 +382,6 @@ 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
@@ -416,30 +392,4 @@ void EventTarget::removeAllEventListeners()
     }
 }
 
-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
index bce08b7..7b2c060 100644 (file)
@@ -32,8 +32,8 @@
 #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>
@@ -85,14 +85,6 @@ namespace WebCore {
     };
     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:
@@ -202,21 +194,6 @@ namespace WebCore {
         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) \
@@ -255,16 +232,9 @@ namespace WebCore {
 #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
 
index aca3d8f..b336349 100644 (file)
@@ -2620,33 +2620,7 @@ bool Node::removeEventListener(const AtomicString& eventType, EventListener* lis
         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;
index 7973572..06a40ad 100644 (file)
@@ -815,11 +815,7 @@ void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPt
         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();
index 9a3a214..a170c1c 100644 (file)
@@ -939,19 +939,8 @@ void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* targe
     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())