Web Inspector: for event listener provide handler function value in protocol and...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Feb 2013 16:01:27 +0000 (16:01 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Feb 2013 16:01:27 +0000 (16:01 +0000)
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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/elements/event-listener-sidebar-expected.txt
LayoutTests/inspector/elements/event-listeners-about-blank-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/ScriptEventListener.cpp
Source/WebCore/bindings/js/ScriptEventListener.h
Source/WebCore/bindings/v8/ScriptEventListener.cpp
Source/WebCore/bindings/v8/ScriptEventListener.h
Source/WebCore/inspector/Inspector.json
Source/WebCore/inspector/InspectorDOMAgent.cpp
Source/WebCore/inspector/InspectorDOMAgent.h
Source/WebCore/inspector/front-end/DOMAgent.js
Source/WebCore/inspector/front-end/EventListenersSidebarPane.js

index e2290a1d24afa2e19f723d48c48be0579135a619..47cd63fb338f7ace04a4a7eb74d1658e75dc1e65 100644 (file)
@@ -1,3 +1,15 @@
+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
index c8358ac26889dadf2c06fbbc755f0931b84a8ec8..8228ccb3068f6c72960fe561344cab6e5ad1b1ce 100644 (file)
@@ -4,6 +4,7 @@ Inspect Me
 
 ======== 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"); }
@@ -12,12 +13,14 @@ Inspect Me
     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"); }
@@ -26,6 +29,7 @@ Inspect Me
     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)"); }
@@ -34,6 +38,7 @@ Inspect Me
     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"); }
@@ -42,6 +47,7 @@ Inspect Me
     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)"); }
@@ -50,6 +56,7 @@ Inspect Me
     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"); }
@@ -60,6 +67,7 @@ Inspect Me
 
 ======== custom event ========
 [expanded] body event-listener-sidebar.html:10
+    handler: function f() {}
     isAttribute: false
     lineNumber: 10
     listenerBody: function f() {}
@@ -70,6 +78,7 @@ Inspect Me
 
 ======== 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"); }
@@ -80,6 +89,7 @@ Inspect Me
 
 ======== 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"); }
@@ -88,12 +98,14 @@ Inspect Me
     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"); }
@@ -104,6 +116,7 @@ Inspect Me
 
 ======== custom event ========
 [expanded] body event-listener-sidebar.html:10
+    handler: function f() {}
     isAttribute: false
     lineNumber: 10
     listenerBody: function f() {}
index bbdf7260e10cfe76d2cb9a6b1ae4759826a3b313..1aa6774c698bf7990bc1f7bcc312ebbaed1afc36 100644 (file)
@@ -4,6 +4,7 @@ Tests event listeners output in the Elements sidebar panel when the listeners ar
 
 ======== click ========
 [expanded] body event-listeners-about-blank.html:9
+    handler: function f() {}
     isAttribute: false
     lineNumber: 9
     listenerBody: function f() {}
@@ -14,6 +15,7 @@ Tests event listeners output in the Elements sidebar panel when the listeners ar
 
 ======== hover ========
 [expanded] div#div-in-iframe event-listeners-about-blank.html:9
+    handler: function f() {}
     isAttribute: false
     lineNumber: 9
     listenerBody: function f() {}
index 75422342d2ab416f40dc7a020b2329f2138d16de..ff6ad034f99b884e88a1d9d081c8db6f29bcd5ad 100644 (file)
@@ -1,3 +1,37 @@
+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
index 1ace47f607976449eb7222af43d8196ade519fdd..9157124957930b4246066d1ffafd5543ae5b7a5c 100644 (file)
@@ -106,6 +106,31 @@ String eventListenerHandlerBody(Document* document, EventListener* eventListener
     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);
index 019e3d0eb8a00fd70306f5494d5fcb8bba8bf546..78bc1790db931227ebf24632742e7c6657b043e4 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "JSLazyEventListener.h"
 #include "ScriptState.h"
+#include "ScriptValue.h"
 
 #include <wtf/PassRefPtr.h>
 
@@ -46,6 +47,8 @@ namespace WebCore {
     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
 
index 550a24ecd776998463fee54d0af70e5595df855f..21d2460d8df601c5be1729e22d8378e97151eb6e 100644 (file)
@@ -103,6 +103,31 @@ String eventListenerHandlerBody(Document* document, EventListener* listener)
     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)
index 9108903b3090cca583b82b483de5202e583e658d..d9b22e908a3f808e90032d515bf6d551425d9349 100644 (file)
@@ -31,6 +31,7 @@
 #ifndef ScriptEventListener_h
 #define ScriptEventListener_h
 
+#include "ScriptValue.h"
 #include "V8LazyEventListener.h"
 
 #include <wtf/PassRefPtr.h>
@@ -46,6 +47,8 @@ namespace WebCore {
     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
index 540d2baefe6f683393abddac3a90e9d368fdab9c..7559f01bd7b6a842cc70d8caef0cd26b51fe5b7b 100644 (file)
                     { "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." }
index 84f6c6d157116639dd33a02e441612522028e358..5fdfbfbeeb7c0ffbe7bacf20aa62e3a9d2ab1921 100644 (file)
@@ -762,7 +762,7 @@ void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const
     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);
@@ -779,7 +779,7 @@ void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int n
         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));
         }
     }
 
@@ -790,7 +790,7 @@ void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int n
         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));
         }
     }
 }
@@ -1376,15 +1376,32 @@ PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::build
     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;
index 919e8693e9f38f92ddbb7f266cb50c2652f394e1..cc02e6a5e4538259fece248b352c64d7c0e09f9b 100644 (file)
@@ -131,7 +131,7 @@ public:
     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);
@@ -227,7 +227,7 @@ private:
     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);
 
index cc3dca60736b3f46efaa5ac5610a2bce3af1fa84..507f6808af9f45bf19293e94d2d2ab9ff5485b96 100644 (file)
@@ -363,11 +363,12 @@ WebInspector.DOMNode.prototype = {
     },
 
     /**
+     * @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);
     },
 
     /**
index 294afbbd7721bd9b972c0fc31d7564147b07a20e..2bd209b03ddf416d22bff51310abb4d68f061813 100644 (file)
@@ -119,7 +119,7 @@ WebInspector.EventListenersSidebarPane.prototype = {
         }
 
         if (node)
-            node.eventListeners(callback);
+            node.eventListeners(WebInspector.EventListenersSidebarPane._objectGroupName, callback);
         this._selectedNode = node;
     },
 
@@ -202,6 +202,10 @@ WebInspector.EventListenerBar.prototype = {
                 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)