From: commit-queue@webkit.org Date: Sat, 17 Sep 2011 06:34:05 +0000 (+0000) Subject: Reduce EventTarget memory usage by deferring hash map allocation X-Git-Tag: 070512121124~24215 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c6eb1a7f68f909ebd93482e0e5fa6c057e13b3e2;p=profile%2Fivi%2Fwebkit-efl.git Reduce EventTarget memory usage by deferring hash map allocation until there are listeners for more than 1 event type. http://webkit.org/b/68105 Patch by Andreas Kling 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 --- diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt index a41d260..7b79dbe 100644 --- a/Source/WebCore/CMakeLists.txt +++ b/Source/WebCore/CMakeLists.txt @@ -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 diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index db25fa2..fd1ea66 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,79 @@ +2011-09-16 Andreas Kling + + 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 and James Kozianski Don't detach elements from the render tree when entering fullscreen mode diff --git a/Source/WebCore/GNUmakefile.list.am b/Source/WebCore/GNUmakefile.list.am index d87aa36..6213a11 100644 --- a/Source/WebCore/GNUmakefile.list.am +++ b/Source/WebCore/GNUmakefile.list.am @@ -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 \ diff --git a/Source/WebCore/WebCore.exp.in b/Source/WebCore/WebCore.exp.in index 91ee5d1..388daf5 100644 --- a/Source/WebCore/WebCore.exp.in +++ b/Source/WebCore/WebCore.exp.in @@ -1184,6 +1184,7 @@ __ZNK7WebCore15VisiblePosition14localCaretRectERPNS_12RenderObjectE __ZNK7WebCore15VisiblePosition19absoluteCaretBoundsEv __ZNK7WebCore15VisiblePosition4nextENS_27EditingBoundaryCrossingRuleE __ZNK7WebCore15VisiblePosition8previousENS_27EditingBoundaryCrossingRuleE +__ZNK7WebCore16EventListenerMap8containsERKN3WTF12AtomicStringE __ZNK7WebCore16FontFallbackList10fontDataAtEPKNS_4FontEj __ZNK7WebCore16HTMLInputElement11isTextFieldEv __ZNK7WebCore16HTMLInputElement14suggestedValueEv diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi index a1420e9..ac1b1a1 100644 --- a/Source/WebCore/WebCore.gypi +++ b/Source/WebCore/WebCore.gypi @@ -994,6 +994,7 @@ 'dom/Element.h', 'dom/Event.h', 'dom/EventListener.h', + 'dom/EventListenerMap.h', 'dom/EventNames.h', 'dom/EventTarget.h', 'dom/ExceptionCode.h', @@ -5276,6 +5277,7 @@ 'dom/EventDispatcher.cpp', 'dom/EventDispatcher.h', 'dom/EventException.h', + 'dom/EventListenerMap.cpp', 'dom/EventNames.cpp', 'dom/EventQueue.cpp', 'dom/EventQueue.h', diff --git a/Source/WebCore/WebCore.pro b/Source/WebCore/WebCore.pro index 79388a5..ab905fe 100644 --- a/Source/WebCore/WebCore.pro +++ b/Source/WebCore/WebCore.pro @@ -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 \ diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj index bef5705..cfea114 100755 --- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj +++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj @@ -45754,6 +45754,86 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index a0f21db..35e69cc 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -4093,6 +4093,8 @@ 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 */; }; @@ -10651,6 +10653,8 @@ ABC128760B33AA6D00C693D5 /* PopupMenuClient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PopupMenuClient.h; sourceTree = ""; }; ABDDFE730A5C6E6F00A3E11D /* RenderMenuList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMenuList.cpp; sourceTree = ""; }; ABDDFE740A5C6E7000A3E11D /* RenderMenuList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RenderMenuList.h; sourceTree = ""; }; + AD4495F1141FC08900541EDF /* EventListenerMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventListenerMap.cpp; sourceTree = ""; }; + AD4495F2141FC08900541EDF /* EventListenerMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventListenerMap.h; sourceTree = ""; }; ADDF1AD41257CD9A0003A759 /* RenderSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderSVGPath.cpp; sourceTree = ""; }; ADDF1AD51257CD9A0003A759 /* RenderSVGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderSVGPath.h; sourceTree = ""; }; B10B697D140C174000BC1C26 /* WebVTTToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTToken.h; sourceTree = ""; }; @@ -19958,6 +19962,8 @@ BC60D90A0D2A17CE00B9918F /* EventException.idl */, 935FBC4409BA00B900E230B1 /* EventListener.h */, 85AFA7410AAF298400E84305 /* EventListener.idl */, + AD4495F1141FC08900541EDF /* EventListenerMap.cpp */, + AD4495F2141FC08900541EDF /* EventListenerMap.h */, 939885C108B7E3D100E707C4 /* EventNames.cpp */, 939885C208B7E3D100E707C4 /* EventNames.h */, 8F67561A1288B17B0047ACA3 /* EventQueue.cpp */, @@ -20467,6 +20473,7 @@ 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 */, @@ -24451,6 +24458,7 @@ 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 index 0000000..8feb2e8 --- /dev/null +++ b/Source/WebCore/dom/EventListenerMap.cpp @@ -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 + * 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 +#include +#include + +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 EventListenerMap::eventTypes() const +{ + Vector 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 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 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 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 index 0000000..5b8c1ae --- /dev/null +++ b/Source/WebCore/dom/EventListenerMap.h @@ -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 + * 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 +#include +#include + +namespace WebCore { + +class EventTarget; + +typedef Vector EventListenerVector; + +class EventListenerMap { +public: + EventListenerMap(); + ~EventListenerMap(); + + bool isEmpty() const; + bool contains(const AtomicString& eventType) const; + + void clear(); + bool add(const AtomicString& eventType, PassRefPtr, bool useCapture); + bool remove(const AtomicString& eventType, EventListener*, bool useCapture, size_t& indexOfRemovedListener); + EventListenerVector* find(const AtomicString& eventType); + Vector eventTypes() const; + +#if ENABLE(SVG) + void removeFirstEventListenerCreatedFromMarkup(const AtomicString& eventType); + void copyEventListenersNotCreatedFromMarkupToTarget(EventTarget*); +#endif + +private: + friend class EventListenerIterator; + + struct EventListenerHashMapTraits : HashTraits { + static const int minimumTableSize = 32; + }; + typedef HashMap EventListenerHashMap; + + OwnPtr m_hashMap; + + AtomicString m_singleEventListenerType; + OwnPtr 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 diff --git a/Source/WebCore/dom/EventTarget.cpp b/Source/WebCore/dom/EventTarget.cpp index 42aea4a..f33c030 100644 --- a/Source/WebCore/dom/EventTarget.cpp +++ b/Source/WebCore/dom/EventTarget.cpp @@ -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 listener, bool useCapture) { EventTargetData* d = ensureEventTargetData(); - - pair 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 diff --git a/Source/WebCore/dom/EventTarget.h b/Source/WebCore/dom/EventTarget.h index bce08b7..7b2c060 100644 --- a/Source/WebCore/dom/EventTarget.h +++ b/Source/WebCore/dom/EventTarget.h @@ -32,8 +32,8 @@ #ifndef EventTarget_h #define EventTarget_h +#include "EventListenerMap.h" #include "EventNames.h" -#include "RegisteredEventListener.h" #include #include #include @@ -85,14 +85,6 @@ namespace WebCore { }; typedef Vector FiringEventIteratorVector; - typedef Vector EventListenerVector; - - struct EventListenerMapHashTraits : HashTraits { - static const int minimumTableSize = 32; - }; - - typedef HashMap 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 diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp index aca3d8f..b336349 100644 --- a/Source/WebCore/dom/Node.cpp +++ b/Source/WebCore/dom/Node.cpp @@ -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; diff --git a/Source/WebCore/inspector/InspectorDOMAgent.cpp b/Source/WebCore/inspector/InspectorDOMAgent.cpp index 7973572..06a40ad 100644 --- a/Source/WebCore/inspector/InspectorDOMAgent.cpp +++ b/Source/WebCore/inspector/InspectorDOMAgent.cpp @@ -815,11 +815,7 @@ void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPt return; // Get the list of event types this Node is concerned with - Vector 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 eventTypes = d->eventListenerMap.eventTypes(); // Quick break if no useful listeners size_t eventTypesLength = eventTypes.size(); diff --git a/Source/WebCore/svg/SVGUseElement.cpp b/Source/WebCore/svg/SVGUseElement.cpp index 9a3a214..a170c1c 100644 --- a/Source/WebCore/svg/SVGUseElement.cpp +++ b/Source/WebCore/svg/SVGUseElement.cpp @@ -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())