2009-09-28 Joseph Pecoraro <joepeck@webkit.org>
authorpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Sep 2009 12:36:44 +0000 (12:36 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Sep 2009 12:36:44 +0000 (12:36 +0000)
        Reviewed by Timothy Hatcher.

        Inspector Should Show Event Listeners/Handlers Registered on each Node
        https://bugs.webkit.org/show_bug.cgi?id=17429

          Extracted a method from dispatchEvent to get the event ancestor chain

        * dom/Node.cpp:
        (WebCore::Node::eventAncestors): the extracted method
        (WebCore::Node::dispatchGenericEvent): use eventAncestors
        * dom/Node.h:

          Asynchronous Flow For the Inspector, Backend -> DOM Agent -> Frontend
          The DOMAgent's getEventListenersForNode handles the logic of finding
          all the relevant listeners in the event flow.

        * inspector/InspectorBackend.cpp:
        (WebCore::InspectorBackend::getEventListenersForNode):
        * inspector/InspectorBackend.h:
        * inspector/InspectorBackend.idl:
        * inspector/InspectorDOMAgent.cpp:
        (WebCore::InspectorDOMAgent::getEventListenersForNode):
        (WebCore::InspectorDOMAgent::buildObjectForEventListener):
        (WebCore::InspectorDOMAgent::buildObjectForNode): added localName
        * inspector/InspectorDOMAgent.h:
        (WebCore::EventListenerInfo::EventListenerInfo):
        * inspector/InspectorFrontend.cpp:
        (WebCore::InspectorFrontend::didGetEventListenersForNode):
        * inspector/InspectorFrontend.h:
        (WebCore::InspectorFrontend::scriptState):
        * inspector/front-end/DOMAgent.js: added localName to WebInspector.DOMNode from payload
        (WebInspector.EventListeners.getEventListenersForNodeAsync.mycallback):
        (WebInspector.EventListeners.getEventListenersForNodeAsync):

          New Sidebar Pane in the Element's Panel
          Includes Gear Menu for filtering the Event Listeners on the
          "Selected Node Only" or "All Nodes"

        * inspector/front-end/ElementsPanel.js: Handles refreshing the Pane when necessary
        (WebInspector.ElementsPanel.this.treeOutline.focusedNodeChanged):
        (WebInspector.ElementsPanel):
        (WebInspector.ElementsPanel.prototype.updateEventListeners):
        * inspector/front-end/EventListenersSidebarPane.js: Added.
        (WebInspector.EventListenersSidebarPane): The 1st level in the Pane
        (WebInspector.EventListenersSidebarPane.prototype.update.callback):
        (WebInspector.EventListenersSidebarPane.prototype.update):
        (WebInspector.EventListenersSection): The 2nd level in the Pane
        (WebInspector.EventListenersSection.prototype.update): filters on Preference
        (WebInspector.EventListenersSection.prototype.addListener):
        (WebInspector.EventListenerBar): The 3rd level in the Pane
        (WebInspector.EventListenerBar.prototype._getNodeDisplayName):
        (WebInspector.EventListenerBar.prototype._getFunctionDisplayName):
        (WebInspector.EventListenersSidebarPane.prototype._changeSetting): For the Gear Menu

          Consolidated "appropriateSelectorForNode"

        * inspector/front-end/StylesSidebarPane.js:
        * inspector/front-end/utilities.js:

          Miscellaneous Updates

        * English.lproj/localizedStrings.js: "Event Listeners", "No Event Listeners", "Selected Node Only", "All Nodes"
        * WebCore.gypi: included the new inspector files
        * WebCore.vcproj/WebCore.vcproj: included source files that were missing
        * inspector/front-end/Images/grayConnectorPoint.png: Added. Thanks to Timothy Hatcher.
        * inspector/front-end/Images/whiteConnectorPoint.png: Added. Thanks to Timothy Hatcher.
        * inspector/front-end/inspector.js: Preferences for the Gear Menu Event Listeners filter
        * inspector/front-end/inspector.css: reused as much as possible
        * inspector/front-end/inspector.html: include the new script
        * inspector/front-end/WebKit.qrc: included the new inspector files

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

24 files changed:
WebCore/ChangeLog
WebCore/English.lproj/localizedStrings.js
WebCore/WebCore.gypi
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/dom/Node.cpp
WebCore/dom/Node.h
WebCore/inspector/InspectorBackend.cpp
WebCore/inspector/InspectorBackend.h
WebCore/inspector/InspectorBackend.idl
WebCore/inspector/InspectorDOMAgent.cpp
WebCore/inspector/InspectorDOMAgent.h
WebCore/inspector/InspectorFrontend.cpp
WebCore/inspector/InspectorFrontend.h
WebCore/inspector/front-end/DOMAgent.js
WebCore/inspector/front-end/ElementsPanel.js
WebCore/inspector/front-end/EventListenersSidebarPane.js [new file with mode: 0644]
WebCore/inspector/front-end/Images/grayConnectorPoint.png [new file with mode: 0644]
WebCore/inspector/front-end/Images/whiteConnectorPoint.png [new file with mode: 0644]
WebCore/inspector/front-end/StylesSidebarPane.js
WebCore/inspector/front-end/WebKit.qrc
WebCore/inspector/front-end/inspector.css
WebCore/inspector/front-end/inspector.html
WebCore/inspector/front-end/inspector.js
WebCore/inspector/front-end/utilities.js

index 036fb5e..3098fbb 100644 (file)
@@ -1,3 +1,76 @@
+2009-09-28  Joseph Pecoraro  <joepeck@webkit.org>
+
+        Reviewed by Timothy Hatcher.
+
+        Inspector Should Show Event Listeners/Handlers Registered on each Node
+        https://bugs.webkit.org/show_bug.cgi?id=17429
+        
+          Extracted a method from dispatchEvent to get the event ancestor chain
+        
+        * dom/Node.cpp:
+        (WebCore::Node::eventAncestors): the extracted method
+        (WebCore::Node::dispatchGenericEvent): use eventAncestors
+        * dom/Node.h:
+
+          Asynchronous Flow For the Inspector, Backend -> DOM Agent -> Frontend
+          The DOMAgent's getEventListenersForNode handles the logic of finding
+          all the relevant listeners in the event flow.
+
+        * inspector/InspectorBackend.cpp:
+        (WebCore::InspectorBackend::getEventListenersForNode):
+        * inspector/InspectorBackend.h:
+        * inspector/InspectorBackend.idl:
+        * inspector/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::getEventListenersForNode):
+        (WebCore::InspectorDOMAgent::buildObjectForEventListener):
+        (WebCore::InspectorDOMAgent::buildObjectForNode): added localName
+        * inspector/InspectorDOMAgent.h:
+        (WebCore::EventListenerInfo::EventListenerInfo):
+        * inspector/InspectorFrontend.cpp:
+        (WebCore::InspectorFrontend::didGetEventListenersForNode):
+        * inspector/InspectorFrontend.h:
+        (WebCore::InspectorFrontend::scriptState):
+        * inspector/front-end/DOMAgent.js: added localName to WebInspector.DOMNode from payload
+        (WebInspector.EventListeners.getEventListenersForNodeAsync.mycallback):
+        (WebInspector.EventListeners.getEventListenersForNodeAsync):
+
+          New Sidebar Pane in the Element's Panel
+          Includes Gear Menu for filtering the Event Listeners on the
+          "Selected Node Only" or "All Nodes"
+
+        * inspector/front-end/ElementsPanel.js: Handles refreshing the Pane when necessary
+        (WebInspector.ElementsPanel.this.treeOutline.focusedNodeChanged):
+        (WebInspector.ElementsPanel):
+        (WebInspector.ElementsPanel.prototype.updateEventListeners):
+        * inspector/front-end/EventListenersSidebarPane.js: Added. 
+        (WebInspector.EventListenersSidebarPane): The 1st level in the Pane
+        (WebInspector.EventListenersSidebarPane.prototype.update.callback):
+        (WebInspector.EventListenersSidebarPane.prototype.update):
+        (WebInspector.EventListenersSection): The 2nd level in the Pane
+        (WebInspector.EventListenersSection.prototype.update): filters on Preference
+        (WebInspector.EventListenersSection.prototype.addListener):
+        (WebInspector.EventListenerBar): The 3rd level in the Pane
+        (WebInspector.EventListenerBar.prototype._getNodeDisplayName):
+        (WebInspector.EventListenerBar.prototype._getFunctionDisplayName):
+        (WebInspector.EventListenersSidebarPane.prototype._changeSetting): For the Gear Menu
+        
+          Consolidated "appropriateSelectorForNode"
+
+        * inspector/front-end/StylesSidebarPane.js:
+        * inspector/front-end/utilities.js:
+
+          Miscellaneous Updates
+
+        * English.lproj/localizedStrings.js: "Event Listeners", "No Event Listeners", "Selected Node Only", "All Nodes"
+        * WebCore.gypi: included the new inspector files
+        * WebCore.vcproj/WebCore.vcproj: included source files that were missing
+        * inspector/front-end/Images/grayConnectorPoint.png: Added. Thanks to Timothy Hatcher.
+        * inspector/front-end/Images/whiteConnectorPoint.png: Added. Thanks to Timothy Hatcher.
+        * inspector/front-end/inspector.js: Preferences for the Gear Menu Event Listeners filter
+        * inspector/front-end/inspector.css: reused as much as possible
+        * inspector/front-end/inspector.html: include the new script
+        * inspector/front-end/WebKit.qrc: included the new inspector files
+
 2009-09-27  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Dan Bernstein.
index c53cd2f..93385b2 100644 (file)
Binary files a/WebCore/English.lproj/localizedStrings.js and b/WebCore/English.lproj/localizedStrings.js differ
index e91076b..18cfb0c 100644 (file)
             'inspector/front-end/Drawer.js',
             'inspector/front-end/ElementsPanel.js',
             'inspector/front-end/ElementsTreeOutline.js',
+            'inspector/front-end/EventListenersSidebarPane.js',
             'inspector/front-end/FontView.js',
             'inspector/front-end/ImageView.js',
             'inspector/front-end/InjectedScript.js',
             'inspector/front-end/Images/goArrow.png',
             'inspector/front-end/Images/graphLabelCalloutLeft.png',
             'inspector/front-end/Images/graphLabelCalloutRight.png',
+            'inspector/front-end/Images/grayConnectorPoint.png',
             'inspector/front-end/Images/largerResourcesButtonGlyph.png',
             'inspector/front-end/Images/localStorage.png',
             'inspector/front-end/Images/nodeSearchButtonGlyph.png',
             'inspector/front-end/Images/warningIcon.png',
             'inspector/front-end/Images/warningMediumIcon.png',
             'inspector/front-end/Images/warningsErrors.png',
+            'inspector/front-end/Images/whiteConnectorPoint.png'
         ],
     }
 }
index 6cb0b7e..4c6c43d 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
-                                       RelativePath="..\inspector\front-end\DatabasesPanel.js"\r
-                                       >\r
-                               </File>\r
-                               <File\r
                                        RelativePath="..\inspector\front-end\DatabaseTableView.js"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\inspector\front-end\ElementsTreeOutline.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath="..\inspector\front-end\EventListenersSidebarPane.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\inspector\front-end\FontView.js"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\inspector\front-end\StoragePanel.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\inspector\front-end\StylesSidebarPane.js"\r
                                        >\r
                                </File>\r
index c899f3d..612bf18 100644 (file)
@@ -2422,6 +2422,20 @@ static inline EventTarget* eventTargetRespectingSVGTargetRules(Node* referenceNo
     return referenceNode;
 }
 
+void Node::eventAncestors(Vector<RefPtr<ContainerNode> > &ancestors)
+{
+    if (inDocument()) {
+        for (ContainerNode* ancestor = eventParentNode(); ancestor; ancestor = ancestor->eventParentNode()) {
+#if ENABLE(SVG)
+            // Skip <use> shadow tree elements.
+            if (ancestor->isSVGElement() && ancestor->isShadowNode())
+                continue;
+#endif
+            ancestors.append(ancestor);
+        }
+    }
+}
+
 bool Node::dispatchEvent(PassRefPtr<Event> prpEvent)
 {
     RefPtr<EventTarget> protect = this;
@@ -2452,16 +2466,7 @@ bool Node::dispatchGenericEvent(PassRefPtr<Event> prpEvent)
     // Be sure to ref all of nodes since event handlers could result in the last reference going away.
     RefPtr<Node> thisNode(this);
     Vector<RefPtr<ContainerNode> > ancestors;
-    if (inDocument()) {
-        for (ContainerNode* ancestor = eventParentNode(); ancestor; ancestor = ancestor->eventParentNode()) {
-#if ENABLE(SVG)
-            // Skip <use> shadow tree elements.
-            if (ancestor->isSVGElement() && ancestor->isShadowNode())
-                continue;
-#endif
-            ancestors.append(ancestor);
-        }
-    }
+    eventAncestors(ancestors);
 
     // Set up a pointer to indicate whether / where to dispatch window events.
     // We don't dispatch load events to the window. That quirk was originally
index f3bebc6..35be6d3 100644 (file)
@@ -206,6 +206,9 @@ public:
     // The node's parent for the purpose of event capture and bubbling.
     virtual ContainerNode* eventParentNode();
 
+    // Node ancestors when concerned about event flow
+    void eventAncestors(Vector<RefPtr<ContainerNode> > &ancestors);
+
     bool isBlockFlow() const;
     bool isBlockFlowOrBlockTable() const;
     
index ad0c510..752ec0c 100644 (file)
@@ -276,19 +276,6 @@ bool InspectorBackend::timelineEnabled() const
     return false;
 }
 
-void InspectorBackend::getCookies(long callId)
-{
-    if (InspectorDOMAgent* domAgent = inspectorDOMAgent())
-        domAgent->getCookies(callId);
-}
-
-void InspectorBackend::deleteCookie(const String& cookieName)
-{
-    if (!m_inspectorController)
-        return;
-    m_inspectorController->deleteCookie(cookieName);
-}
-
 #if ENABLE(JAVASCRIPT_DEBUGGER)
 const ProfilesArray& InspectorBackend::profiles() const
 {
@@ -449,6 +436,12 @@ void InspectorBackend::setTextNodeValue(long callId, long nodeId, const String&
         domAgent->setTextNodeValue(callId, nodeId, value);
 }
 
+void InspectorBackend::getEventListenersForNode(long callId, long nodeId)
+{
+    if (InspectorDOMAgent* domAgent = inspectorDOMAgent())
+        domAgent->getEventListenersForNode(callId, nodeId);
+}
+
 void InspectorBackend::copyNode(long nodeId)
 {
     Node* node = nodeForId(nodeId);
@@ -458,6 +451,19 @@ void InspectorBackend::copyNode(long nodeId)
     Pasteboard::generalPasteboard()->writePlainText(markup);
 }
 
+void InspectorBackend::getCookies(long callId)
+{
+    if (InspectorDOMAgent* domAgent = inspectorDOMAgent())
+        domAgent->getCookies(callId);
+}
+
+void InspectorBackend::deleteCookie(const String& cookieName)
+{
+    if (!m_inspectorController)
+        return;
+    m_inspectorController->deleteCookie(cookieName);
+}
+
 void InspectorBackend::highlight(long nodeId)
 {
     if (Node* node = nodeForId(nodeId))
index 739d28e..72278a3 100644 (file)
@@ -99,9 +99,6 @@ public:
     void disableTimeline(bool always);
     bool timelineEnabled() const;
 
-    void getCookies(long callId);
-    void deleteCookie(const String& cookieName);
-
 #if ENABLE(JAVASCRIPT_DEBUGGER)
     const ProfilesArray& profiles() const;
 
@@ -138,8 +135,12 @@ public:
     void setAttribute(long callId, long elementId, const String& name, const String& value);
     void removeAttribute(long callId, long elementId, const String& name);
     void setTextNodeValue(long callId, long nodeId, const String& value);
+    void getEventListenersForNode(long callId, long nodeId);
     void copyNode(long nodeId);
 
+    void getCookies(long callId);
+    void deleteCookie(const String& cookieName);
+
     // Generic code called from custom implementations.
     void highlight(long nodeId);
     Node* nodeForId(long nodeId);
index d8ccf9f..5c6babb 100644 (file)
@@ -71,9 +71,6 @@ module core {
         void disableResourceTracking(in boolean always);
         void storeLastActivePanel(in DOMString panelName);
 
-        void getCookies(in long callId);
-        void deleteCookie(in DOMString cookieName);
-
 #if defined(ENABLE_JAVASCRIPT_DEBUGGER) && ENABLE_JAVASCRIPT_DEBUGGER
         boolean debuggerEnabled();
         void enableDebugger(in boolean always);
@@ -109,8 +106,12 @@ module core {
         void setAttribute(in long callId, in long elementId, in DOMString name, in DOMString value);
         void removeAttribute(in long callId, in long elementId, in DOMString name);
         void setTextNodeValue(in long callId, in long nodeId, in DOMString value);
+        void getEventListenersForNode(in long callId, in long nodeId);
         void copyNode(in long nodeId);
 
+        void getCookies(in long callId);
+        void deleteCookie(in DOMString cookieName);
+
         // Called from InjectedScript.
         [Custom] DOMObject nodeForId(in long nodeId);
         [Custom] long wrapObject(in DOMObject object);
index d893796..db983a1 100644 (file)
 #include <wtf/OwnPtr.h>
 #include <wtf/Vector.h>
 
+#if USE(JSC)
+#include "JSDOMWindow.h"
+#include <runtime/JSObject.h>
+#endif
+
 namespace WebCore {
 
 InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend* frontend)
@@ -363,6 +368,80 @@ void InspectorDOMAgent::setTextNodeValue(long callId, long nodeId, const String&
     }
 }
 
+void InspectorDOMAgent::getEventListenersForNode(long callId, long nodeId)
+{
+    Node* node = nodeForId(nodeId);
+    ScriptArray listenersArray = m_frontend->newScriptArray();
+    unsigned counter = 0;
+    EventTargetData* d;
+
+    // Quick break if a null node or no listeners at all
+    if (!node || !(d = node->eventTargetData())) {
+        m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray);
+        return;
+    }
+
+    // Get the list of event types this Node is concerned with
+    Vector<AtomicString> eventTypes;
+    const EventListenerMap& listenerMap = d->eventListenerMap;
+    HashMap<AtomicString, EventListenerVector>::const_iterator end = listenerMap.end();
+    for (HashMap<AtomicString, EventListenerVector>::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
+        eventTypes.append(iter->first);
+
+    // Quick break if no useful listeners
+    size_t eventTypesLength = eventTypes.size();
+    if (eventTypesLength == 0) {
+        m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray);
+        return;
+    }
+
+    // The Node's Event Ancestors (not including self)
+    Vector<RefPtr<ContainerNode> > ancestors;
+    node->eventAncestors(ancestors);
+
+    // Nodes and their Listeners for the concerned event types (order is top to bottom)
+    Vector<EventListenerInfo> eventInformation;
+    for (size_t i = ancestors.size(); i; --i) {
+        ContainerNode* ancestor = ancestors[i - 1].get();
+        for (size_t j = 0; j < eventTypesLength; ++j) {
+            AtomicString& type = eventTypes[j];
+            if (ancestor->hasEventListeners(type))
+                eventInformation.append(EventListenerInfo(static_cast<Node*>(ancestor), type, ancestor->getEventListeners(type)));
+        }
+    }
+
+    // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
+    for (size_t i = 0; i < eventTypesLength; ++i) {
+        const AtomicString& type = eventTypes[i];
+        eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type)));
+    }
+
+    // Get Capturing Listeners (in this order)
+    size_t eventInformationLength = eventInformation.size();
+    for (size_t i = 0; i < eventInformationLength; ++i) {
+        const EventListenerInfo& info = eventInformation[i];
+        const EventListenerVector& vector = info.eventListenerVector;
+        for (size_t j = 0; j < vector.size(); ++j) {
+            const RegisteredEventListener& listener = vector[j];
+            if (listener.useCapture)
+                listenersArray.set(counter++, buildObjectForEventListener(listener, info.eventType, info.node));
+        }
+    }
+
+    // Get Bubbling Listeners (reverse order)
+    for (size_t i = eventInformationLength; i; --i) {
+        const EventListenerInfo& info = eventInformation[i - 1];
+        const EventListenerVector& vector = info.eventListenerVector;
+        for (size_t j = 0; j < vector.size(); ++j) {
+            const RegisteredEventListener& listener = vector[j];
+            if (!listener.useCapture)
+                listenersArray.set(counter++, buildObjectForEventListener(listener, info.eventType, info.node));
+        }
+    }
+
+    m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray);
+}
+
 void InspectorDOMAgent::getCookies(long callId)
 {
     Document* doc = mainFrameDocument();
@@ -381,6 +460,7 @@ ScriptObject InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeTo
 
     long id = bind(node, nodesMap);
     String nodeName;
+    String localName;
     String nodeValue;
 
     switch (node->nodeType()) {
@@ -389,18 +469,22 @@ ScriptObject InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeTo
             nodeValue = node->nodeValue();
             break;
         case Node::ATTRIBUTE_NODE:
+            localName = node->localName();
+            break;
         case Node::DOCUMENT_FRAGMENT_NODE:
             break;
         case Node::DOCUMENT_NODE:
         case Node::ELEMENT_NODE:
         default:
             nodeName = node->nodeName();
+            localName = node->localName();
             break;
     }
 
     value.set("id", static_cast<int>(id));
     value.set("nodeType", node->nodeType());
     value.set("nodeName", nodeName);
+    value.set("localName", localName);
     value.set("nodeValue", nodeValue);
 
     if (node->nodeType() == Node::ELEMENT_NODE) {
@@ -457,6 +541,22 @@ ScriptArray InspectorDOMAgent::buildArrayForContainerChildren(Node* container, i
     return children;
 }
 
+ScriptObject InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
+{
+    RefPtr<EventListener> eventListener = registeredEventListener.listener;
+    ScriptObject value = m_frontend->newScriptObject();
+    value.set("type", eventType);
+    value.set("useCapture", registeredEventListener.useCapture);
+    value.set("isAttribute", eventListener->isAttribute());
+    value.set("nodeId", static_cast<long long>(pushNodePathToFrontend(node)));
+#if USE(JSC)
+    JSC::JSObject* functionObject = eventListener->jsFunction();
+    if (functionObject)
+        value.set("listener", ScriptObject(m_frontend->scriptState(), functionObject));
+#endif
+    return value;
+}
+
 ScriptObject InspectorDOMAgent::buildObjectForCookie(const Cookie& cookie)
 {
     ScriptObject value = m_frontend->newScriptObject();
index c430c57..890b4f9 100644 (file)
@@ -30,7 +30,9 @@
 #ifndef InspectorDOMAgent_h
 #define InspectorDOMAgent_h
 
+#include "AtomicString.h"
 #include "EventListener.h"
+#include "EventTarget.h"
 #include "ScriptArray.h"
 #include "ScriptObject.h"
 #include "ScriptState.h"
@@ -53,6 +55,19 @@ namespace WebCore {
 
     struct Cookie;
 
+    struct EventListenerInfo {
+        EventListenerInfo(Node* node, const AtomicString& eventType, const EventListenerVector& eventListenerVector)
+            : node(node)
+            , eventType(eventType)
+            , eventListenerVector(eventListenerVector)
+        {
+        }
+
+        Node* node;
+        const AtomicString eventType;
+        const EventListenerVector eventListenerVector;
+    };
+
     class InspectorDOMAgent : public EventListener {
     public:
         static const InspectorDOMAgent* cast(const EventListener* listener)
@@ -72,6 +87,7 @@ namespace WebCore {
         void setAttribute(long callId, long elementId, const String& name, const String& value);
         void removeAttribute(long callId, long elementId, const String& name);
         void setTextNodeValue(long callId, long nodeId, const String& value);
+        void getEventListenersForNode(long callId, long nodeId);
         void getCookies(long callId);
 
         // Methods called from the InspectorController.
@@ -98,6 +114,8 @@ namespace WebCore {
         ScriptArray buildArrayForElementAttributes(Element* element);
         ScriptArray buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap);
 
+        ScriptObject buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node);
+
         ScriptObject buildObjectForCookie(const Cookie& cookie);
         ScriptArray buildArrayForCookies(const Vector<Cookie>& cookiesList);
 
index c9793cb..9f329a6 100644 (file)
@@ -368,6 +368,15 @@ void InspectorFrontend::didApplyDomChange(int callId, bool success)
     function->call();
 }
 
+void InspectorFrontend::didGetEventListenersForNode(int callId, int nodeId, ScriptArray& listenersArray)
+{
+    OwnPtr<ScriptFunctionCall> function(newFunctionCall("didGetEventListenersForNode"));
+    function->appendArgument(callId);
+    function->appendArgument(nodeId);
+    function->appendArgument(listenersArray);
+    function->call();
+}
+
 void InspectorFrontend::didGetCookies(int callId, const ScriptArray& cookies, const String& cookiesString)
 {
     OwnPtr<ScriptFunctionCall> function(newFunctionCall("didGetCookies"));
index f9d3ba1..397868b 100644 (file)
@@ -115,6 +115,7 @@ namespace WebCore {
         void attributesUpdated(int id, const ScriptArray& attributes);
         void didGetChildNodes(int callId);
         void didApplyDomChange(int callId, bool success);
+        void didGetEventListenersForNode(int callId, int nodeId, ScriptArray& listenersArray);
 
         void timelineWasEnabled();
         void timelineWasDisabled();
@@ -125,6 +126,8 @@ namespace WebCore {
 
         void addNodesToSearchResult(const String& nodeIds);
 
+        ScriptState* scriptState() const { return m_scriptState; }
+
     private:
         PassOwnPtr<ScriptFunctionCall> newFunctionCall(const String& functionName);
         void callSimpleFunction(const String& functionName);
index 47c8041..3207c25 100644 (file)
@@ -35,6 +35,7 @@ WebInspector.DOMNode = function(doc, payload) {
     this.id = payload.id;
     this.nodeType = payload.nodeType;
     this.nodeName = payload.nodeName;
+    this.localName = payload.localName;
     this._nodeValue = payload.nodeValue;
     this.textContent = this.nodeValue;
 
@@ -473,6 +474,17 @@ WebInspector.Cookies.buildCookiesFromString = function(rawCookieString)
     return cookies;
 }
 
+WebInspector.EventListeners = {}
+
+WebInspector.EventListeners.getEventListenersForNodeAsync = function(node, callback)
+{
+    if (!node)
+        return;
+
+    var callId = WebInspector.Callback.wrap(callback);
+    InspectorController.getEventListenersForNode(callId, node.id);
+}
+
 WebInspector.CSSStyleDeclaration = function(payload)
 {
     this.id = payload.id;
@@ -648,3 +660,4 @@ WebInspector.didPerformSearch = WebInspector.Callback.processCallback;
 WebInspector.didApplyDomChange = WebInspector.Callback.processCallback;
 WebInspector.didRemoveAttribute = WebInspector.Callback.processCallback;
 WebInspector.didSetTextNodeValue = WebInspector.Callback.processCallback;
+WebInspector.didGetEventListenersForNode = WebInspector.Callback.processCallback;
index 928da52..1b61a7b 100644 (file)
@@ -56,6 +56,7 @@ WebInspector.ElementsPanel = function()
         this.panel.updateStyles(true);
         this.panel.updateMetrics();
         this.panel.updateProperties();
+        this.panel.updateEventListeners();
 
         if (InspectorController.searchingForNode()) {
             InspectorController.toggleNodeSearch();
@@ -76,10 +77,12 @@ WebInspector.ElementsPanel = function()
     this.sidebarPanes.styles = new WebInspector.StylesSidebarPane();
     this.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane();
     this.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane();
+    this.sidebarPanes.eventListeners = new WebInspector.EventListenersSidebarPane();
 
     this.sidebarPanes.styles.onexpand = this.updateStyles.bind(this);
     this.sidebarPanes.metrics.onexpand = this.updateMetrics.bind(this);
     this.sidebarPanes.properties.onexpand = this.updateProperties.bind(this);
+    this.sidebarPanes.eventListeners.onexpand = this.updateEventListeners.bind(this);
 
     this.sidebarPanes.styles.expanded = true;
 
@@ -93,6 +96,7 @@ WebInspector.ElementsPanel = function()
     this.sidebarElement.appendChild(this.sidebarPanes.styles.element);
     this.sidebarElement.appendChild(this.sidebarPanes.metrics.element);
     this.sidebarElement.appendChild(this.sidebarPanes.properties.element);
+    this.sidebarElement.appendChild(this.sidebarPanes.eventListeners.element);
 
     this.sidebarResizeElement = document.createElement("div");
     this.sidebarResizeElement.className = "sidebar-resizer-vertical";
@@ -977,6 +981,16 @@ WebInspector.ElementsPanel.prototype = {
         propertiesSidebarPane.needsUpdate = false;
     },
 
+    updateEventListeners: function()
+    {
+        var eventListenersSidebarPane = this.sidebarPanes.eventListeners;
+        if (!eventListenersSidebarPane.expanded || !eventListenersSidebarPane.needsUpdate)
+            return;
+
+        eventListenersSidebarPane.update(this.focusedDOMNode);
+        eventListenersSidebarPane.needsUpdate = false;
+    },
+
     handleKeyEvent: function(event)
     {
         this.treeOutline.handleKeyEvent(event);
diff --git a/WebCore/inspector/front-end/EventListenersSidebarPane.js b/WebCore/inspector/front-end/EventListenersSidebarPane.js
new file mode 100644 (file)
index 0000000..a593cb8
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
+ *
+ * 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.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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.
+ */
+
+WebInspector.EventListenersSidebarPane = function()
+{
+    WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listeners"));
+    this.bodyElement.addStyleClass("events-pane");
+
+    this.sections = [];
+
+    this.settingsSelectElement = document.createElement("select");
+
+    var option = document.createElement("option");
+    option.value = "all";
+    if (Preferences.eventListenersFilter === "all")
+        option.selected = true;
+    option.label = WebInspector.UIString("All Nodes");
+    this.settingsSelectElement.appendChild(option);
+
+    option = document.createElement("option");
+    option.value = "selected";
+    if (Preferences.eventListenersFilter === "selected")
+        option.selected = true;
+    option.label = WebInspector.UIString("Selected Node Only");
+    this.settingsSelectElement.appendChild(option);
+
+    this.settingsSelectElement.addEventListener("click", function(event) { event.stopPropagation() }, false);
+    this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
+
+    this.titleElement.appendChild(this.settingsSelectElement);
+}
+
+WebInspector.EventListenersSidebarPane.prototype = {
+    update: function(node)
+    {
+        var body = this.bodyElement;
+        body.removeChildren();
+        this.sections = [];
+
+        var self = this;
+        function callback(nodeId, eventListeners) {
+            var sectionNames = [];
+            var sectionMap = {};
+            for (var i = 0; i < eventListeners.length; ++i) {
+                var eventListener = eventListeners[i];
+                eventListener._selectedNode = (eventListener.nodeId === nodeId);
+                eventListener.node = WebInspector.domAgent.nodeForId(eventListener.nodeId);
+                delete eventListener.nodeId; // no longer needed
+                var type = eventListener.type;
+                var section = sectionMap[type];
+                if (!section) {
+                    section = new WebInspector.EventListenersSection(type);
+                    sectionMap[type] = section;
+                    sectionNames.push(type);
+                    self.sections.push(section);
+                }
+                section.addListener(eventListener);
+            }
+            
+            if (sectionNames.length === 0) {
+                var div = document.createElement("div");
+                div.className = "info";
+                div.textContent = WebInspector.UIString("No Event Listeners");
+                body.appendChild(div);
+                return;
+            }
+
+            sectionNames.sort();
+            for (var i = 0; i < sectionNames.length; ++i) {
+                var section = sectionMap[sectionNames[i]];
+                section.update();
+                body.appendChild(section.element);
+            }
+        }
+
+        WebInspector.EventListeners.getEventListenersForNodeAsync(node, callback);
+    },
+
+    _changeSetting: function(event)
+    {
+        var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex];
+        Preferences.eventListenersFilter = selectedOption.value;
+
+        InspectorController.setSetting("event-listeners-filter", Preferences.eventListenersFilter);
+
+        for (var i = 0; i < this.sections.length; ++i)
+            this.sections[i].update();
+    }
+}
+
+WebInspector.EventListenersSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
+
+WebInspector.EventListenersSection = function(title, subtitle)
+{
+    this.eventListeners = [];
+    WebInspector.PropertiesSection.call(this, title, subtitle);
+
+    // Changed from a Properties List
+    this.propertiesElement.parentNode.removeChild(this.propertiesElement);
+    delete this.propertiesElement;
+    delete this.propertiesTreeOutline;
+
+    this.eventBars = document.createElement("div");
+    this.eventBars.className = "event-bars";
+    this.element.appendChild(this.eventBars);
+}
+
+WebInspector.EventListenersSection.prototype = {
+    update: function()
+    {
+        // A Filtered Array simplifies when to create connectors
+        var filteredEventListeners = this.eventListeners;
+        if (Preferences.eventListenersFilter === "selected") {
+            filteredEventListeners = [];
+            for (var i = 0; i < this.eventListeners.length; ++i) {
+                var eventListener = this.eventListeners[i];
+                if (eventListener._selectedNode)
+                    filteredEventListeners.push(eventListener);
+            }
+        }
+
+        this.eventBars.removeChildren();
+        var length = filteredEventListeners.length;
+        for (var i = 0; i < length; ++i) {
+            var eventListener = filteredEventListeners[i];
+            var eventListenerBar = new WebInspector.EventListenerBar(eventListener);
+            if (i < length - 1) {
+                var connector = document.createElement("div");
+                connector.className = "event-bar-connector";
+                eventListenerBar.element.appendChild(connector);
+            }
+
+            this.eventBars.appendChild(eventListenerBar.element);
+        }
+    },
+
+    addListener: function(eventListener)
+    {
+        this.eventListeners.push(eventListener);
+    }
+}
+
+WebInspector.EventListenersSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
+
+WebInspector.EventListenerBar = function(eventListener)
+{
+    this.eventListener = eventListener;
+    WebInspector.ObjectPropertiesSection.call(this, InspectorController.wrapObject(eventListener), this._getFunctionDisplayName(), this._getNodeDisplayName());
+    this.element.className = "event-bar"; /* Changed from "section" */
+    this.propertiesElement.className = "event-properties"; /* Changed from "properties" */
+}
+
+WebInspector.EventListenerBar.prototype = {
+    _getNodeDisplayName: function()
+    {
+        var node = this.eventListener.node;
+        if (!node)
+            return "";
+
+        if (node.nodeType === Node.DOCUMENT_NODE)
+            return "document";
+        
+        return appropriateSelectorForNode(node);
+    },
+
+    _getFunctionDisplayName: function()
+    {
+        // TODO: v8 does not yet provide the raw function, this handles such a case with a placeholder
+        // I didn't make this a UIString because it should be implemented eventually.
+        if (!this.eventListener.listener)
+            return "(listener)";
+
+        // Requires that Function.toString() return at least the function's signature
+        var match = this.eventListener.listener.toString().match(/function ([^\(]+?)\(/);
+        return (match ? match[1] : WebInspector.UIString("(anonymous function)"));
+    }
+}
+
+WebInspector.EventListenerBar.prototype.__proto__ = WebInspector.ObjectPropertiesSection.prototype;
diff --git a/WebCore/inspector/front-end/Images/grayConnectorPoint.png b/WebCore/inspector/front-end/Images/grayConnectorPoint.png
new file mode 100644 (file)
index 0000000..fddc7ea
Binary files /dev/null and b/WebCore/inspector/front-end/Images/grayConnectorPoint.png differ
diff --git a/WebCore/inspector/front-end/Images/whiteConnectorPoint.png b/WebCore/inspector/front-end/Images/whiteConnectorPoint.png
new file mode 100644 (file)
index 0000000..c8fb1cf
Binary files /dev/null and b/WebCore/inspector/front-end/Images/whiteConnectorPoint.png differ
index 6185aff..3582f96 100644 (file)
@@ -348,7 +348,7 @@ WebInspector.StylesSidebarPane.prototype = {
 
     addBlankSection: function()
     {
-        var blankSection = new WebInspector.BlankStylePropertiesSection(this.appropriateSelectorForNode());
+        var blankSection = new WebInspector.BlankStylePropertiesSection(appropriateSelectorForNode(this.node, true));
         blankSection.pane = this;
 
         var elementStyleSection = this.sections[1];        
@@ -367,27 +367,6 @@ WebInspector.StylesSidebarPane.prototype = {
         this.sections.splice(index, 1);
         if (section.element.parentNode)
             section.element.parentNode.removeChild(section.element);
-    },
-
-    appropriateSelectorForNode: function()
-    {
-        var node = this.node;
-        if (!node)
-            return "";
-
-        var id = node.getAttribute("id");
-        if (id)
-            return "#" + id;
-
-        var className = node.getAttribute("class");
-        if (className)
-            return "." + className.replace(/\s+/, ".");
-
-        var nodeName = node.nodeName.toLowerCase();
-        if (nodeName === "input" && node.getAttribute("type"))
-            return nodeName + "[type=\"" + node.getAttribute("type") + "\"]";
-
-        return nodeName;
     }
 }
 
index 0c50bb7..1aaeb3d 100644 (file)
@@ -21,6 +21,7 @@
     <file>Drawer.js</file>
     <file>ElementsPanel.js</file>
     <file>ElementsTreeOutline.js</file>
+    <file>EventListenersSidebarPane.js</file>
     <file>FontView.js</file>
     <file>ImageView.js</file>
     <file>InjectedScript.js</file>
     <file>Images/goArrow.png</file>
     <file>Images/graphLabelCalloutLeft.png</file>
     <file>Images/graphLabelCalloutRight.png</file>
+    <file>Images/grayConnectorPoint.png</file>
     <file>Images/largerResourcesButtonGlyph.png</file>
     <file>Images/localStorage.png</file>
     <file>Images/nodeSearchButtonGlyph.png</file>
     <file>Images/warningMediumIcon.png</file>
     <file>Images/warningOrangeDot.png</file>
     <file>Images/warningsErrors.png</file>
+    <file>Images/whiteConnectorPoint.png</file>
 </qresource>
 </RCC>
index 4513886..358c13c 100644 (file)
@@ -1222,7 +1222,7 @@ body.inactive .placard.selected {
     margin-top: 1px;
 }
 
-.section:nth-last-of-type(1) {
+.section:nth-last-of-type(1), .event-bar:nth-last-of-type(1) {
     margin-bottom: 1px;
 }
 
@@ -1230,6 +1230,14 @@ body.inactive .placard.selected {
     text-align: center;
 }
 
+.event-bar:first-child {
+    margin-top: 1px;
+}
+
+.event-bar:nth-last-of-type(1) .header {
+    border-bottom: 1px solid rgb(163, 163, 163);
+}
+
 .section .header {
     padding: 2px 8px 4px 18px;
     border-top: 1px solid rgb(145, 160, 192);
@@ -1261,7 +1269,7 @@ body.inactive .placard.selected {
     content: url(Images/treeDownTriangleWhite.png);
 }
 
-.section .header .title {
+.section .header .title, .event-bar .header .title {
     color: white;
     font-weight: bold;
     word-wrap: break-word;
@@ -1272,11 +1280,11 @@ body.inactive .placard.selected {
     font-style: italic;
 }
 
-.section .header label {
+.section .header label, .event-bar .header label {
     display: none;
 }
 
-.section.expanded .header label {
+.section.expanded .header label, .event-bar.expanded .header label {
     display: inline;
 }
 
@@ -1289,7 +1297,7 @@ body.inactive .placard.selected {
     vertical-align: 2px;
 }
 
-.section .header .subtitle {
+.section .header .subtitle, .event-bar .header .subtitle {
     float: right;
     font-size: 10px;
     margin-left: 5px;
@@ -1303,7 +1311,7 @@ body.inactive .placard.selected {
     color: inherit;
 }
 
-.section .properties {
+.section .properties, .event-bar .event-properties {
     display: none;
     margin: 0;
     padding: 2px 6px 3px;
@@ -1320,11 +1328,11 @@ body.inactive .placard.selected {
     opacity: 1.0;
 }
 
-.section.expanded .properties {
+.section.expanded .properties, .event-bar.expanded .event-properties {
     display: block;
 }
 
-.section .properties li {
+.section .properties li, .event-properties li {
     margin-left: 12px;
     white-space: nowrap;
     text-overflow: ellipsis;
@@ -1333,22 +1341,22 @@ body.inactive .placard.selected {
     cursor: auto;
 }
 
-.section .properties li.parent {
+.section .properties li.parent, .event-properties li.parent {
     margin-left: 1px;
 }
 
-.section .properties ol {
+.section .properties ol, .event-properties ol {
     display: none;
     margin: 0;
     -webkit-padding-start: 12px;
     list-style: none;
 }
 
-.section .properties ol.expanded {
+.section .properties ol.expanded, .event-properties ol.expanded {
     display: block;
 }
 
-.section .properties li.parent::before {
+.section .properties li.parent::before, .event-properties li.parent::before {
     content: url(Images/treeRightTriangleBlack.png);
     opacity: 0.75;
     float: left;
@@ -1360,16 +1368,79 @@ body.inactive .placard.selected {
     cursor: default;
 }
 
-.section .properties li.parent.expanded::before {
+.section .properties li.parent.expanded::before, .event-properties li.parent.expanded::before {
     content: url(Images/treeDownTriangleBlack.png);
     margin-top: 1px;
 }
 
-.section .properties li .info {
+.section .properties li .info, .event-properties li .info {
     padding-top: 4px;
     padding-bottom: 3px;
 }
 
+.section .event-bars {
+    display: none;
+}
+
+.section.expanded .event-bars {
+    display: block;
+}
+
+.event-bar {
+    position: relative;
+}
+
+.event-bar-connector {
+    position: absolute;
+    left: 75%;
+    bottom: -7px;
+    margin-left: -7px;
+    content: url(Images/grayConnectorPoint.png);
+    z-index: 3;
+}
+
+.event-bar.expanded .event-bar-connector {
+    content: url(Images/whiteConnectorPoint.png);
+}
+
+.event-bars .event-bar .header {
+    padding: 2px 8px 4px 18px;
+    border-top: 1px solid rgb(163, 163, 163);
+    background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(243, 243, 243)), to(rgb(207, 207, 207)));
+    min-height: 18px;
+    white-space: nowrap;
+    -webkit-background-origin: padding;
+    -webkit-background-clip: padding;
+}
+
+.event-bars .event-bar.expanded .header {
+    border-bottom: 1px solid rgb(163, 163, 163);
+}
+
+.event-bars .event-bar .header .title {
+    font-weight: bold;
+    color: #333;
+    text-shadow: white 0 1px 0; 
+}
+
+.event-bars .event-bar .header .subtitle {
+    color: rgba(90, 90, 90, 0.742188);
+}
+
+.event-bars .event-bar .header::before {
+    position: absolute;
+    top: 4px;
+    left: 7px;
+    width: 8px;
+    height: 8px;
+    opacity: 0.75;
+    content: url(Images/treeRightTriangleBlack.png);
+}
+
+.event-bars .event-bar.expanded .header::before {
+    content: url(Images/treeDownTriangleBlack.png);
+}
+
 .editing {
     -webkit-user-select: text;
     -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px;
@@ -1461,7 +1532,7 @@ li.editing .swatch, li.editing .enabled-button,  li.editing-sub-part .delete-but
     display: block;
 }
 
-.section .properties .name {
+.section .properties .name, .event-properties .name {
     color: rgb(136, 19, 145);
 }
 
@@ -1469,7 +1540,7 @@ li.editing .swatch, li.editing .enabled-button,  li.editing-sub-part .delete-but
     color: rgb(100, 100, 100);
 }
 
-.section .properties .number {
+.section .properties .number, .event-properties .number {
     color: blue;
 }
 
@@ -1477,11 +1548,11 @@ li.editing .swatch, li.editing .enabled-button,  li.editing-sub-part .delete-but
     color: rgb(128, 0, 0);
 }
 
-.section .properties .keyword {
+.section .properties .keyword, .event-properties .keyword {
     color: rgb(136, 19, 79);
 }
 
-.section .properties .color {
+.section .properties .color, .event-properties .color {
     color: rgb(118, 15, 21);
 }
 
index 7f544fe..f54e302 100644 (file)
@@ -65,6 +65,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="WatchExpressionsSidebarPane.js"></script>
     <script type="text/javascript" src="MetricsSidebarPane.js"></script>
     <script type="text/javascript" src="PropertiesSidebarPane.js"></script>
+    <script type="text/javascript" src="EventListenersSidebarPane.js"></script>
     <script type="text/javascript" src="Color.js"></script>
     <script type="text/javascript" src="StylesSidebarPane.js"></script>
     <script type="text/javascript" src="Panel.js"></script>
index 921bb7a..b6de65e 100644 (file)
@@ -42,7 +42,8 @@ var Preferences = {
     heapProfilerPresent: false,
     samplingCPUProfiler: false,
     showColorNicknames: true,
-    colorFormat: "hex"
+    colorFormat: "hex",
+    eventListenersFilter: "all"
 }
 
 var WebInspector = {
@@ -354,6 +355,10 @@ WebInspector.loaded = function()
     if (colorFormat)
         Preferences.colorFormat = colorFormat;
 
+    var eventListenersFilter = InspectorController.setting("event-listeners-filter");
+    if (eventListenersFilter)
+        Preferences.eventListenersFilter = eventListenersFilter;
+
     this.drawer = new WebInspector.Drawer();
     this.console = new WebInspector.ConsoleView(this.drawer);
     // TODO: Uncomment when enabling the Changes Panel
index e83c7c0..5f41b56 100644 (file)
@@ -621,11 +621,38 @@ function nodeTitleInfo(hasChildren, linkify)
     return info;
 }
 
-function getDocumentForNode(node) {
+function appropriateSelectorForNode(node, justSelector)
+{
+    if (!node)
+        return "";
+
+    var lowerCaseName = node.localName || node.nodeName.toLowerCase();
+
+    var id = node.getAttribute("id");
+    if (id) {
+        var selector = "#" + id;
+        return (justSelector ? selector : lowerCaseName + selector);
+    }
+
+    var className = node.getAttribute("class");
+    if (className) {
+        var selector = "." + className.replace(/\s+/, ".");
+        return (justSelector ? selector : lowerCaseName + selector);
+    }
+
+    if (lowerCaseName === "input" && node.getAttribute("type"))
+        return lowerCaseName + "[type=\"" + node.getAttribute("type") + "\"]";
+
+    return lowerCaseName;
+}
+
+function getDocumentForNode(node)
+{
     return node.nodeType == Node.DOCUMENT_NODE ? node : node.ownerDocument;
 }
 
-function parentNode(node) {
+function parentNode(node)
+{
     return node.parentNode;
 }