Bug 1268538 - Event listener popup can be empty if the listeners are from native code r?pbrosset draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Thu, 28 Apr 2016 22:57:56 +0100
changeset 357453 2404976eb9efe45aba68c49465f2f44bd2b461d1
parent 357423 4dc48f3688f053e2fa74bf6c7beb4dff274ffc1f
child 519659 8433e6a03ae6fd897cae27c21c08615a3da1506b
push id16796
push usermratcliffe@mozilla.com
push dateThu, 28 Apr 2016 22:02:58 +0000
reviewerspbrosset
bugs1268538
milestone49.0a1
Bug 1268538 - Event listener popup can be empty if the listeners are from native code r?pbrosset When native functions were used as handlers we were showing an empty event popup. We now display the following: function() { [native code] } MozReview-Commit-ID: 7Wz1bcLm4ph
devtools/client/inspector/markup/test/browser_markup_events3.js
devtools/client/inspector/markup/test/doc_markup_events3.html
devtools/client/themes/inspector.css
devtools/server/actors/inspector.js
--- a/devtools/client/inspector/markup/test/browser_markup_events3.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events3.js
@@ -13,98 +13,98 @@ const TEST_URL = URL_ROOT + "doc_markup_
 loadHelperScript("helper_events_test_runner.js");
 
 const TEST_DATA = [ // eslint-disable-line
   {
     selector: "#es6-method",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":91",
+        filename: TEST_URL + ":97",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "es6Method() {\n" +
                  "  alert(\"obj.es6Method\");\n" +
                  "}"
       }
     ]
   },
   {
     selector: "#generator",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":96",
+        filename: TEST_URL + ":102",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function* generator() {\n" +
                  "  alert(\"generator\");\n" +
                  "}"
       }
     ]
   },
   {
     selector: "#anon-generator",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":55",
+        filename: TEST_URL + ":56",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function*() {\n" +
                  "  alert(\"anonGenerator\");\n" +
                  "}"
       }
     ]
   },
   {
     selector: "#named-function-expression",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":23",
+        filename: TEST_URL + ":24",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "let namedFunctionExpression =\n" +
                  "  function foo() {\n" +
                  "    alert(\"namedFunctionExpression\");\n" +
                  "  }"
       }
     ]
   },
   {
     selector: "#anon-function-expression",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":27",
+        filename: TEST_URL + ":28",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "let anonFunctionExpression = function() {\n" +
                  "  alert(\"anonFunctionExpression\");\n" +
                  "}"
       }
     ]
   },
   {
     selector: "#returned-function",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":32",
+        filename: TEST_URL + ":33",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function bar() {\n" +
                  "  alert(\"returnedFunction\");\n" +
                  "}"
       }
@@ -138,24 +138,40 @@ const TEST_DATA = [ // eslint-disable-li
       }
     ]
   },
   {
     selector: "#multiple-assignment",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":42",
+        filename: TEST_URL + ":43",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "let multipleAssignment = foo = bar = function multi() {\n" +
                  "  alert(\"multipleAssignment\");\n" +
                  "}"
       }
     ]
   },
+  {
+    selector: "#promise",
+    expected: [
+      {
+        type: "click",
+        filename: ":0",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function() {\n" +
+                 "  [native code]\n" +
+                 "}"
+      }
+    ]
+  },
 ];
 
 add_task(function* () {
   yield runEventPopupTests(TEST_URL, TEST_DATA);
 });
--- a/devtools/client/inspector/markup/test/doc_markup_events3.html
+++ b/devtools/client/inspector/markup/test/doc_markup_events3.html
@@ -6,17 +6,18 @@
     #es6-method,
     #generator,
     #anon-generator,
     #named-function-expression,
     #anon-function-expression,
     #returned-function,
     #constructed-function,
     #constructed-function-with-body-string,
-    #multiple-assignment {
+    #multiple-assignment,
+    #promise {
       border: 1px solid #000;
       width: 200px;
       min-height: 1em;
       cursor: pointer;
     }
     </style>
     <script type="application/javascript;version=1.8">
       let namedFunctionExpression =
@@ -76,16 +77,21 @@
         let constructedFunctionWithBodyStringNode =
           document.getElementById("constructed-function-with-body-string");
         constructedFunctionWithBodyStringNode
           .addEventListener("click", constructedFuncWithBodyString);
 
         let multipleAssignmentNode =
           document.getElementById("multiple-assignment");
         multipleAssignmentNode.addEventListener("click", multipleAssignment);
+
+        let promiseNode = document.getElementById("promise");
+        new Promise((resolve, reject) => {
+          promiseNode.addEventListener("click", resolve);
+        });
       }
 
       function handleEventClick(hehe) {
 
       }
 
       handleEventClick.prototype = {
         es6Method() {
@@ -106,10 +112,11 @@
     <div id="named-function-expression">Named Function Expression</div>
     <div id="anon-function-expression">Anonymous Function Expression</div>
     <div id="returned-function">Returned Function</div>
     <div id="constructed-function">Constructed Function</div>
     <div id="constructed-function-with-body-string">
       Constructed Function with body string
     </div>
     <div id="multiple-assignment">Multiple Assignment</div>
+    <div id="promise">Promise</div>
   </body>
 </html>
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -34,16 +34,17 @@
 #inspector-element-add-button {
   list-style-image: url("chrome://devtools/skin/images/add.svg");
 }
 
 /* Tooltip: Events */
 
 #devtools-tooltip-events-container {
   margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
+  min-width: 390px;
   max-width: 590px;
   overflow-y: auto;
 }
 
 .event-header {
   display: flex;
   align-items: center;
   cursor: pointer;
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -555,58 +555,66 @@ var NodeActor = exports.NodeActor = prot
       }
     }
 
     if (listenerDO.isBoundFunction) {
       listenerDO = listenerDO.boundTargetFunction;
     }
 
     let script = listenerDO.script;
-    let scriptSource = script.source.text;
-    let functionSource =
-      scriptSource.substr(script.sourceStart, script.sourceLength);
-
-    /*
-    The script returned is the whole script and
-    scriptSource.substr(script.sourceStart, script.sourceLength) returns
-    something like this:
-      () { doSomething(); }
-
-    So we need to use some regex magic to get the appropriate function info
-    e.g.:
-      () => { ... }
-      function doit() { ... }
-      doit: function() { ... }
-      es6func() { ... }
-      var|let|const foo = function () { ... }
-      function generator*() { ... }
-    */
-    let scriptBeforeFunc = scriptSource.substr(0, script.sourceStart);
-    let matches = scriptBeforeFunc.match(RX_FUNC_NAME);
-    if (matches && matches.length > 0) {
-      functionSource = matches[0].trim() + functionSource;
+
+    let functionSource;
+    if (script) {
+      let scriptSource = script.source.text;
+      functionSource =
+        scriptSource.substr(script.sourceStart, script.sourceLength);
+
+     /*
+      The script returned is the whole script and
+      scriptSource.substr(script.sourceStart, script.sourceLength) returns
+      something like this:
+        () { doSomething(); }
+
+      So we need to use some regex magic to get the appropriate function info
+      e.g.:
+        () => { ... }
+        function doit() { ... }
+        doit: function() { ... }
+        es6func() { ... }
+        var|let|const foo = function () { ... }
+        function generator*() { ... }
+      */
+      let scriptBeforeFunc = scriptSource.substr(0, script.sourceStart);
+      let matches = scriptBeforeFunc.match(RX_FUNC_NAME);
+      if (matches && matches.length > 0) {
+        functionSource = matches[0].trim() + functionSource;
+      }
+    } else {
+      functionSource = handler.toString();
     }
 
     let dom0 = false;
 
     if (typeof node.hasAttribute !== "undefined") {
       dom0 = !!node.hasAttribute("on" + type);
     } else {
       dom0 = !!node["on" + type];
     }
 
-    let line = script.startLine;
-    let url = script.url;
+    let line = script ? script.startLine : 0;
+    let url = script ? script.url : "";
     let origin = url + (dom0 ? "" : ":" + line);
     let searchString;
 
     if (dom0) {
-      searchString = "on" + type + "=\"" + script.source.text + "\"";
-    } else {
-      scriptSource = "    " + scriptSource;
+      if (script) {
+        searchString = "on" + type + "=\"" + script.source.text + "\"";
+      } else {
+        searchString = "on" + type + "=\"" + functionSource + "\"";
+      }
     }
 
     let eventObj = {
       type: typeof override.type !== "undefined" ? override.type : type,
       handler: functionSource.trim(),
       origin: typeof override.origin !== "undefined" ?
                      override.origin : origin,
       searchString: typeof override.searchString !== "undefined" ?