https://bugs.webkit.org/show_bug.cgi?id=109284
Patch by Peter Rybin <prybin@chromium.org> on 2013-02-12
Reviewed by Yury Semikhatsky.
Source/WebCore:
The feature implies that we include a real handler function value into event listener description.
Protocol description, inspector DOM agent (with V8 and JSC backends) and front-end is patched accordingly.
* bindings/js/ScriptEventListener.cpp:
(WebCore::eventListenerHandler):
(WebCore):
(WebCore::eventListenerHandlerScriptState):
* bindings/js/ScriptEventListener.h:
(WebCore):
* bindings/v8/ScriptEventListener.cpp:
(WebCore::eventListenerHandler):
(WebCore):
(WebCore::eventListenerHandlerScriptState):
* bindings/v8/ScriptEventListener.h:
(WebCore):
* inspector/Inspector.json:
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::getEventListenersForNode):
(WebCore::InspectorDOMAgent::buildObjectForEventListener):
* inspector/InspectorDOMAgent.h:
(InspectorDOMAgent):
* inspector/front-end/DOMAgent.js:
(WebInspector.DOMNode.prototype.eventListeners):
* inspector/front-end/EventListenersSidebarPane.js:
(WebInspector.EventListenersSidebarPane.prototype.update):
(.):
LayoutTests:
Test is rebased.
* inspector/elements/event-listener-sidebar-expected.txt:
* inspector/elements/event-listeners-about-blank-expected.txt:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@142627
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2013-02-12 Peter Rybin <prybin@chromium.org>
+
+ Web Inspector: for event listener provide handler function value in protocol and in UI
+ https://bugs.webkit.org/show_bug.cgi?id=109284
+
+ Reviewed by Yury Semikhatsky.
+
+ Test is rebased.
+
+ * inspector/elements/event-listener-sidebar-expected.txt:
+ * inspector/elements/event-listeners-about-blank-expected.txt:
+
2013-02-12 Andrey Lushnikov <lushnikov@chromium.org>
Web Inspector: refactor some reusable functionality from BraceHighlighter
======== click ========
[expanded] document event-listener-sidebar.html:19
+ handler: function (event) { console.log("click - document - capturing"); }
isAttribute: false
lineNumber: 19
listenerBody: function (event) { console.log("click - document - capturing"); }
type: click
useCapture: true
[expanded] document (anonymous function)
+ handler: ObjectHandler
isAttribute: false
listenerBody: ObjectHandler
node: #document
type: click
useCapture: true
[expanded] button#node event-listener-sidebar.html:15
+ handler: function (event) { console.log("click - button - capturing"); }
isAttribute: false
lineNumber: 15
listenerBody: function (event) { console.log("click - button - capturing"); }
type: click
useCapture: true
[expanded] button#node event-listener-sidebar.html:12
+ handler: function clickHandler(event) { console.log("click - button - bubbling (registered before attribute)"); }
isAttribute: false
lineNumber: 12
listenerBody: function clickHandler(event) { console.log("click - button - bubbling (registered before attribute)"); }
type: click
useCapture: false
[expanded] button#node event-listener-sidebar.html:16
+ handler: function (event) { console.log("click - button - attribute"); }
isAttribute: true
lineNumber: 16
listenerBody: function (event) { console.log("click - button - attribute"); }
type: click
useCapture: false
[expanded] button#node event-listener-sidebar.html:17
+ handler: function (event) { console.log("click - button - bubbling (registered after attribute)"); }
isAttribute: false
lineNumber: 17
listenerBody: function (event) { console.log("click - button - bubbling (registered after attribute)"); }
type: click
useCapture: false
[expanded] document event-listener-sidebar.html:6
+ handler: function documentClickHandler(event) { console.log("click - document - attribute"); }
isAttribute: true
lineNumber: 6
listenerBody: function documentClickHandler(event) { console.log("click - document - attribute"); }
======== custom event ========
[expanded] body event-listener-sidebar.html:10
+ handler: function f() {}
isAttribute: false
lineNumber: 10
listenerBody: function f() {}
======== hover ========
[expanded] button#node event-listener-sidebar.html:14
+ handler: function hoverHandler(event) { console.log("hover - button - bubbling"); }
isAttribute: false
lineNumber: 14
listenerBody: function hoverHandler(event) { console.log("hover - button - bubbling"); }
======== click ========
[expanded] document event-listener-sidebar.html:19
+ handler: function (event) { console.log("click - document - capturing"); }
isAttribute: false
lineNumber: 19
listenerBody: function (event) { console.log("click - document - capturing"); }
type: click
useCapture: true
[expanded] document (anonymous function)
+ handler: ObjectHandler
isAttribute: false
listenerBody: ObjectHandler
node: #document
type: click
useCapture: true
[expanded] document event-listener-sidebar.html:6
+ handler: function documentClickHandler(event) { console.log("click - document - attribute"); }
isAttribute: true
lineNumber: 6
listenerBody: function documentClickHandler(event) { console.log("click - document - attribute"); }
======== custom event ========
[expanded] body event-listener-sidebar.html:10
+ handler: function f() {}
isAttribute: false
lineNumber: 10
listenerBody: function f() {}
======== click ========
[expanded] body event-listeners-about-blank.html:9
+ handler: function f() {}
isAttribute: false
lineNumber: 9
listenerBody: function f() {}
======== hover ========
[expanded] div#div-in-iframe event-listeners-about-blank.html:9
+ handler: function f() {}
isAttribute: false
lineNumber: 9
listenerBody: function f() {}
+2013-02-12 Peter Rybin <prybin@chromium.org>
+
+ Web Inspector: for event listener provide handler function value in protocol and in UI
+ https://bugs.webkit.org/show_bug.cgi?id=109284
+
+ Reviewed by Yury Semikhatsky.
+
+ The feature implies that we include a real handler function value into event listener description.
+ Protocol description, inspector DOM agent (with V8 and JSC backends) and front-end is patched accordingly.
+
+ * bindings/js/ScriptEventListener.cpp:
+ (WebCore::eventListenerHandler):
+ (WebCore):
+ (WebCore::eventListenerHandlerScriptState):
+ * bindings/js/ScriptEventListener.h:
+ (WebCore):
+ * bindings/v8/ScriptEventListener.cpp:
+ (WebCore::eventListenerHandler):
+ (WebCore):
+ (WebCore::eventListenerHandlerScriptState):
+ * bindings/v8/ScriptEventListener.h:
+ (WebCore):
+ * inspector/Inspector.json:
+ * inspector/InspectorDOMAgent.cpp:
+ (WebCore::InspectorDOMAgent::getEventListenersForNode):
+ (WebCore::InspectorDOMAgent::buildObjectForEventListener):
+ * inspector/InspectorDOMAgent.h:
+ (InspectorDOMAgent):
+ * inspector/front-end/DOMAgent.js:
+ (WebInspector.DOMNode.prototype.eventListeners):
+ * inspector/front-end/EventListenersSidebarPane.js:
+ (WebInspector.EventListenersSidebarPane.prototype.update):
+ (.):
+
2013-02-12 Yury Semikhatsky <yurys@chromium.org>
Web Inspector: add initial implementation of native memory graph to Timeline
return jsFunction->toString(scriptState)->value(scriptState);
}
+ScriptValue eventListenerHandler(Document* document, EventListener* eventListener)
+{
+ const JSEventListener* jsListener = JSEventListener::cast(eventListener);
+ ASSERT(jsListener);
+ if (!jsListener)
+ return ScriptValue();
+ JSLockHolder lock(jsListener->isolatedWorld()->globalData());
+ JSC::JSObject* jsFunction = jsListener->jsFunction(document);
+ if (!jsFunction)
+ return ScriptValue();
+ return ScriptValue(*jsListener->isolatedWorld()->globalData(), jsFunction);
+}
+
+ScriptState* eventListenerHandlerScriptState(Frame* frame, EventListener* eventListener)
+{
+ const JSEventListener* jsListener = JSEventListener::cast(eventListener);
+ ASSERT(jsListener);
+ if (!jsListener)
+ return 0;
+ if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
+ return 0;
+ DOMWrapperWorld* world = jsListener->isolatedWorld();
+ return frame->script()->globalObject(world)->globalExec();
+}
+
bool eventListenerHandlerLocation(Document* document, EventListener* eventListener, String& sourceName, String& scriptId, int& lineNumber)
{
const JSEventListener* jsListener = JSEventListener::cast(eventListener);
#include "JSLazyEventListener.h"
#include "ScriptState.h"
+#include "ScriptValue.h"
#include <wtf/PassRefPtr.h>
PassRefPtr<JSLazyEventListener> createAttributeEventListener(Node*, const QualifiedName&, const AtomicString& value);
PassRefPtr<JSLazyEventListener> createAttributeEventListener(Frame*, const QualifiedName&, const AtomicString& value);
String eventListenerHandlerBody(Document*, EventListener*);
+ ScriptValue eventListenerHandler(Document*, EventListener*);
+ ScriptState* eventListenerHandlerScriptState(Frame*, EventListener*);
bool eventListenerHandlerLocation(Document*, EventListener*, String& sourceName, String& scriptId, int& lineNumber);
} // namespace WebCore
return toWebCoreStringWithNullCheck(function);
}
+ScriptValue eventListenerHandler(Document* document, EventListener* listener)
+{
+ if (listener->type() != EventListener::JSEventListenerType)
+ return ScriptValue();
+
+ v8::HandleScope scope;
+ V8AbstractEventListener* v8Listener = static_cast<V8AbstractEventListener*>(listener);
+ v8::Handle<v8::Context> context = toV8Context(document, v8Listener->worldContext());
+ v8::Context::Scope contextScope(context);
+ v8::Handle<v8::Object> function = v8Listener->getListenerObject(document);
+ if (function.IsEmpty())
+ return ScriptValue();
+ return ScriptValue(function);
+}
+
+ScriptState* eventListenerHandlerScriptState(Frame* frame, EventListener* listener)
+{
+ if (listener->type() != EventListener::JSEventListenerType)
+ return 0;
+ V8AbstractEventListener* v8Listener = static_cast<V8AbstractEventListener*>(listener);
+ v8::HandleScope scope;
+ v8::Handle<v8::Context> v8Context = v8Listener->worldContext().adjustedContext(frame->script());
+ return ScriptState::forContext(*v8Context);
+}
+
bool eventListenerHandlerLocation(Document* document, EventListener* listener, String& sourceName, String& scriptId, int& lineNumber)
{
if (listener->type() != EventListener::JSEventListenerType)
#ifndef ScriptEventListener_h
#define ScriptEventListener_h
+#include "ScriptValue.h"
#include "V8LazyEventListener.h"
#include <wtf/PassRefPtr.h>
PassRefPtr<V8LazyEventListener> createAttributeEventListener(Node*, const QualifiedName&, const AtomicString& value);
PassRefPtr<V8LazyEventListener> createAttributeEventListener(Frame*, const QualifiedName&, const AtomicString& value);
String eventListenerHandlerBody(Document*, EventListener*);
+ ScriptValue eventListenerHandler(Document*, EventListener*);
+ ScriptState* eventListenerHandlerScriptState(Frame*, EventListener*);
bool eventListenerHandlerLocation(Document*, EventListener*, String& sourceName, String& scriptId, int& lineNumber);
} // namespace WebCore
{ "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." },
{ "name": "handlerBody", "type": "string", "description": "Event handler function body." },
{ "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." },
- { "name": "sourceName", "type": "string", "optional": true, "description": "Source script URL." }
+ { "name": "sourceName", "type": "string", "optional": true, "description": "Source script URL." },
+ { "name": "handler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." }
],
"description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type."
},
{
"name": "getEventListenersForNode",
"parameters": [
- { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." }
+ { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." },
+ { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name for handler value. Handler value is not returned without this parameter specified." }
],
"returns": [
{ "name": "listeners", "type": "array", "items": { "$ref": "EventListener"}, "description": "Array of relevant listeners." }
m_domEditor->replaceWholeText(toText(node), value, errorString);
}
-void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
+void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
{
listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create();
Node* node = assertNode(errorString, nodeId);
for (size_t j = 0; j < vector.size(); ++j) {
const RegisteredEventListener& listener = vector[j];
if (listener.useCapture)
- listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.node));
+ listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.node, objectGroup));
}
}
for (size_t j = 0; j < vector.size(); ++j) {
const RegisteredEventListener& listener = vector[j];
if (!listener.useCapture)
- listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.node));
+ listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.node, objectGroup));
}
}
}
return children.release();
}
-PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
+PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
{
RefPtr<EventListener> eventListener = registeredEventListener.listener;
+ Document* document = node->document();
RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create()
.setType(eventType)
.setUseCapture(registeredEventListener.useCapture)
.setIsAttribute(eventListener->isAttribute())
.setNodeId(pushNodePathToFrontend(node))
- .setHandlerBody(eventListenerHandlerBody(node->document(), eventListener.get()));
+ .setHandlerBody(eventListenerHandlerBody(document, eventListener.get()));
+ if (objectGroupId) {
+ ScriptValue functionValue = eventListenerHandler(document, eventListener.get());
+ if (!functionValue.hasNoValue()) {
+ Frame* frame = document->frame();
+ if (frame) {
+ ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get());
+ if (scriptState) {
+ InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
+ if (!injectedScript.hasNoValue()) {
+ RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId, true);
+ value->setHandler(valueJson);
+ }
+ }
+ }
+ }
+ }
String sourceName;
String scriptId;
int lineNumber;
virtual void getOuterHTML(ErrorString*, int nodeId, WTF::String* outerHTML);
virtual void setOuterHTML(ErrorString*, int nodeId, const String& outerHTML);
virtual void setNodeValue(ErrorString*, int nodeId, const String& value);
- virtual void getEventListenersForNode(ErrorString*, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray);
+ virtual void getEventListenersForNode(ErrorString*, int nodeId, const WTF::String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray);
virtual void performSearch(ErrorString*, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount);
virtual void getSearchResults(ErrorString*, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >&);
virtual void discardSearchResults(ErrorString*, const String& searchId);
PassRefPtr<TypeBuilder::DOM::Node> buildObjectForNode(Node*, int depth, NodeToIdMap*);
PassRefPtr<TypeBuilder::Array<String> > buildArrayForElementAttributes(Element*);
PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap);
- PassRefPtr<TypeBuilder::DOM::EventListener> buildObjectForEventListener(const RegisteredEventListener&, const AtomicString& eventType, Node*);
+ PassRefPtr<TypeBuilder::DOM::EventListener> buildObjectForEventListener(const RegisteredEventListener&, const AtomicString& eventType, Node*, const String* objectGroupId);
Node* nodeForPath(const String& path);
},
/**
+ * @param {string} objectGroupId
* @param {function(?Protocol.Error)=} callback
*/
- eventListeners: function(callback)
+ eventListeners: function(objectGroupId, callback)
{
- DOMAgent.getEventListenersForNode(this.id, callback);
+ DOMAgent.getEventListenersForNode(this.id, objectGroupId, callback);
},
/**
}
if (node)
- node.eventListeners(callback);
+ node.eventListeners(WebInspector.EventListenersSidebarPane._objectGroupName, callback);
this._selectedNode = node;
},
properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("isAttribute", this.eventListener.isAttribute));
if (nodeObject)
properties.push(new WebInspector.RemoteObjectProperty("node", nodeObject));
+ if (typeof this.eventListener.handler !== "undefined") {
+ var remoteObject = WebInspector.RemoteObject.fromPayload(this.eventListener.handler);
+ properties.push(new WebInspector.RemoteObjectProperty("handler", remoteObject));
+ }
if (typeof this.eventListener.handlerBody !== "undefined")
properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("listenerBody", this.eventListener.handlerBody));
if (this.eventListener.sourceName)