Commit a5c785e8 authored by pfeldman@chromium.org's avatar pfeldman@chromium.org

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


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@48809 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 75b3d3a0
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.
Bvar localizedStrings = new Object;
......
......@@ -3546,6 +3546,7 @@
'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',
......@@ -3630,6 +3631,7 @@
'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',
......@@ -3712,6 +3714,7 @@
'inspector/front-end/Images/warningIcon.png',
'inspector/front-end/Images/warningMediumIcon.png',
'inspector/front-end/Images/warningsErrors.png',
'inspector/front-end/Images/whiteConnectorPoint.png'
],
}
}
......@@ -31426,10 +31426,6 @@
RelativePath="..\inspector\front-end\DatabaseQueryView.js"
>
</File>
<File
RelativePath="..\inspector\front-end\DatabasesPanel.js"
>
</File>
<File
RelativePath="..\inspector\front-end\DatabaseTableView.js"
>
......@@ -31462,6 +31458,14 @@
RelativePath="..\inspector\front-end\ElementsPanel.js"
>
</File>
<File
RelativePath="..\inspector\front-end\ElementsTreeOutline.js"
>
</File>
<File
RelativePath="..\inspector\front-end\EventListenersSidebarPane.js"
>
</File>
<File
RelativePath="..\inspector\front-end\FontView.js"
>
......@@ -31582,6 +31586,10 @@
RelativePath="..\inspector\front-end\StatusBarButton.js"
>
</File>
<File
RelativePath="..\inspector\front-end\StoragePanel.js"
>
</File>
<File
RelativePath="..\inspector\front-end\StylesSidebarPane.js"
>
......
......@@ -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
......
......@@ -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;
......
......@@ -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))
......
......@@ -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);
......
......@@ -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);
......
......@@ -56,6 +56,11 @@
#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();
......
......@@ -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);
......
......@@ -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"));
......
......@@ -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);
......
......@@ -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;
......@@ -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);
......
/*
* 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