Bug 1226548 - Event listeners tooltip breaks the syntax in popup if there's a comment before event handler in JS code r?ochameau draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Mon, 18 Apr 2016 16:15:56 +0100
changeset 356098 056c38c9313d22ad541f491a8f63320ddc8d54c5
parent 355984 d65b48650a68a678f5d4c2aabca7e539e647e3d4
child 519328 d0df5e1d58e39eefac48cbeff7213aae3bf7efd0
push id16434
push usermratcliffe@mozilla.com
push dateMon, 25 Apr 2016 16:36:32 +0000
reviewersochameau
bugs1226548
milestone48.0a1
Bug 1226548 - Event listeners tooltip breaks the syntax in popup if there's a comment before event handler in JS code r?ochameau Now using a regex. The regex isn't perfect e.g. it allows multiple dots or JS keywords in function names but this doesn't matter because the code visible in the event popups is always valid JavaScript. This method is far more robust than backtracking and works well, even with our extended tests. The test was timing out on try so I have split it into three. Now uses only one regex: /((var|const|let)\s+)?([\w$.]+\s*[:=]\s*)*(function)?\s*\*?\s*([\w$]+)?\s*$/ MozReview-Commit-ID: 6BQL0a0tbYx
devtools/client/inspector/markup/test/browser.ini
devtools/client/inspector/markup/test/browser_markup_events.js
devtools/client/inspector/markup/test/browser_markup_events1.js
devtools/client/inspector/markup/test/browser_markup_events2.js
devtools/client/inspector/markup/test/browser_markup_events3.js
devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js
devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js
devtools/client/inspector/markup/test/doc_markup_events.html
devtools/client/inspector/markup/test/doc_markup_events1.html
devtools/client/inspector/markup/test/doc_markup_events2.html
devtools/client/inspector/markup/test/doc_markup_events3.html
devtools/server/actors/inspector.js
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -2,17 +2,19 @@
 tags = devtools
 subsuite = devtools
 support-files =
   actor_events_form.js
   doc_markup_anonymous.html
   doc_markup_dragdrop.html
   doc_markup_dragdrop_autoscroll.html
   doc_markup_edit.html
-  doc_markup_events.html
+  doc_markup_events1.html
+  doc_markup_events2.html
+  doc_markup_events3.html
   doc_markup_events_form.html
   doc_markup_events_jquery.html
   doc_markup_events-overflow.html
   doc_markup_flashing.html
   doc_markup_html_mixed_case.html
   doc_markup_image_and_canvas.html
   doc_markup_image_and_canvas_2.html
   doc_markup_links.html
@@ -58,17 +60,19 @@ skip-if = e10s # scratchpad.xul is not l
 skip-if = e10s && os == 'win'
 [browser_markup_dragdrop_distance.js]
 [browser_markup_dragdrop_draggable.js]
 [browser_markup_dragdrop_dragRootNode.js]
 [browser_markup_dragdrop_escapeKeyPress.js]
 [browser_markup_dragdrop_invalidNodes.js]
 [browser_markup_dragdrop_reorder.js]
 [browser_markup_dragdrop_tooltip.js]
-[browser_markup_events.js]
+[browser_markup_events1.js]
+[browser_markup_events2.js]
+[browser_markup_events3.js]
 [browser_markup_events_form.js]
 # [browser_markup_events-overflow.js]
 # disabled - See bug 1177550
 [browser_markup_events_jquery_1.0.js]
 [browser_markup_events_jquery_1.1.js]
 [browser_markup_events_jquery_1.2.js]
 [browser_markup_events_jquery_1.3.js]
 [browser_markup_events_jquery_1.4.js]
rename from devtools/client/inspector/markup/test/browser_markup_events.js
rename to devtools/client/inspector/markup/test/browser_markup_events1.js
--- a/devtools/client/inspector/markup/test/browser_markup_events.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events1.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 /* import-globals-from helper_events_test_runner.js */
 
 "use strict";
 
 // Test that markup view event bubbles show the correct event info for DOM
 // events.
 
-const TEST_URL = URL_ROOT + "doc_markup_events.html";
+const TEST_URL = URL_ROOT + "doc_markup_events1.html";
 
 loadHelperScript("helper_events_test_runner.js");
 
 const TEST_DATA = [ // eslint-disable-line
   {
     selector: "html",
     expected: [
       {
@@ -27,17 +27,17 @@ const TEST_DATA = [ // eslint-disable-li
       }
     ]
   },
   {
     selector: "#container",
     expected: [
       {
         type: "mouseover",
-        filename: TEST_URL + ":62",
+        filename: TEST_URL + ":45",
         attributes: [
           "Capturing",
           "DOM2"
         ],
         handler: "function mouseoverHandler(event) {\n" +
                  "  if (event.target.id !== \"container\") {\n" +
                  "    let output = document.getElementById(\"output\");\n" +
                  "    output.textContent = event.target.textContent;\n" +
@@ -46,135 +46,57 @@ const TEST_DATA = [ // eslint-disable-li
       }
     ]
   },
   {
     selector: "#multiple",
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":69",
+        filename: TEST_URL + ":52",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function clickHandler(event) {\n" +
                  "  let output = document.getElementById(\"output\");\n" +
                  "  output.textContent = \"click\";\n" +
                  "}"
       },
       {
         type: "mouseup",
-        filename: TEST_URL + ":78",
+        filename: TEST_URL + ":57",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function mouseupHandler(event) {\n" +
                  "  let output = document.getElementById(\"output\");\n" +
                  "  output.textContent = \"mouseup\";\n" +
                  "}"
       }
     ]
   },
-  {
-    selector: "#DOM0",
-    expected: [
-      {
-        type: "click",
-        filename: TEST_URL,
-        attributes: [
-          "Bubbling",
-          "DOM0"
-        ],
-        handler: "alert('hi')"
-      }
-    ]
-  },
-  {
-    selector: "#handleevent",
-    expected: [
-      {
-        type: "click",
-        filename: TEST_URL + ":89",
-        attributes: [
-          "Bubbling",
-          "DOM2"
-        ],
-        handler: "handleEvent: function(blah) {\n" +
-                 "  alert(\"handleEvent clicked\");\n" +
-                 "}"
-      }
-    ]
-  },
-  {
-    selector: "#fatarrow",
-    expected: [
-      {
-        type: "click",
-        filename: TEST_URL + ":57",
-        attributes: [
-          "Bubbling",
-          "DOM2"
-        ],
-        handler: "event => {\n" +
-                 "  alert(\"Yay for the fat arrow!\");\n" +
-                 "}"
-      }
-    ]
-  },
-  {
-    selector: "#boundhe",
-    expected: [
-      {
-        type: "click",
-        filename: TEST_URL + ":101",
-        attributes: [
-          "Bubbling",
-          "DOM2"
-        ],
-        handler: "handleEvent: function() {\n" +
-                 "  alert(\"boundHandleEvent clicked\");\n" +
-                 "}"
-      }
-    ]
-  },
-  {
-    selector: "#bound",
-    expected: [
-      {
-        type: "click",
-        filename: TEST_URL + ":74",
-        attributes: [
-          "Bubbling",
-          "DOM2"
-        ],
-        handler: "function boundClickHandler(event) {\n" +
-                 "  alert(\"Bound event clicked\");\n" +
-                 "}"
-      }
-    ]
-  },
   // #noevents tests check that dynamically added events are properly displayed
   // in the markupview
   {
     selector: "#noevents",
     expected: []
   },
   {
     selector: "#noevents",
     beforeTest: function* (inspector, testActor) {
       let nodeMutated = inspector.once("markupmutation");
       yield testActor.eval("window.wrappedJSObject.addNoeventsClickHandler();");
       yield nodeMutated;
     },
     expected: [
       {
         type: "click",
-        filename: TEST_URL + ":106",
+        filename: TEST_URL + ":72",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function noeventsClickHandler(event) {\n" +
                  "  alert(\"noevents has an event listener\");\n" +
                  "}"
       }
@@ -185,13 +107,43 @@ const TEST_DATA = [ // eslint-disable-li
     beforeTest: function* (inspector, testActor) {
       let nodeMutated = inspector.once("markupmutation");
       yield testActor.eval(
         "window.wrappedJSObject.removeNoeventsClickHandler();");
       yield nodeMutated;
     },
     expected: []
   },
+  {
+    selector: "#DOM0",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL,
+        attributes: [
+          "Bubbling",
+          "DOM0"
+        ],
+        handler: "alert('DOM0')"
+      }
+    ]
+  },
+  {
+    selector: "#handleevent",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":67",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "handleEvent: function(blah) {\n" +
+                 "  alert(\"handleEvent\");\n" +
+                 "}"
+      }
+    ]
+  }
 ];
 
 add_task(function* () {
   yield runEventPopupTests(TEST_URL, TEST_DATA);
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events2.js
@@ -0,0 +1,163 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for DOM
+// events.
+
+const TEST_URL = URL_ROOT + "doc_markup_events2.html";
+
+loadHelperScript("helper_events_test_runner.js");
+
+const TEST_DATA = [ // eslint-disable-line
+  {
+    selector: "#fatarrow",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":39",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "() => {\n" +
+                 "  alert(\"Fat arrow without params!\");\n" +
+                 "}"
+      },
+      {
+        type: "click",
+        filename: TEST_URL + ":43",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "event => {\n" +
+                 "  alert(\"Fat arrow with 1 param!\");\n" +
+                 "}"
+      },
+      {
+        type: "click",
+        filename: TEST_URL + ":47",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "(event, foo, bar) => {\n" +
+                 "  alert(\"Fat arrow with 3 params!\");\n" +
+                 "}"
+      },
+      {
+        type: "click",
+        filename: TEST_URL + ":51",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "b => b"
+      }
+    ]
+  },
+  {
+    selector: "#bound",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":62",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function boundClickHandler(event) {\n" +
+                 "  alert(\"Bound event\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#boundhe",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":85",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "handleEvent: function() {\n" +
+                 "  alert(\"boundHandleEvent\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#comment-inline",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":91",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function functionProceededByInlineComment() {\n" +
+                 "  alert(\"comment-inline\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#comment-streaming",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":96",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function functionProceededByStreamingComment() {\n" +
+                 "  alert(\"comment-streaming\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#anon-object-method",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":71",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "anonObjectMethod: function() {\n" +
+                 "  alert(\"obj.anonObjectMethod\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#object-method",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":75",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "objectMethod: function kay() {\n" +
+                 "  alert(\"obj.objectMethod\");\n" +
+                 "}"
+      }
+    ]
+  }
+];
+
+add_task(function* () {
+  yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events3.js
@@ -0,0 +1,161 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for DOM
+// events.
+
+const TEST_URL = URL_ROOT + "doc_markup_events3.html";
+
+loadHelperScript("helper_events_test_runner.js");
+
+const TEST_DATA = [ // eslint-disable-line
+  {
+    selector: "#es6-method",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":91",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "es6Method() {\n" +
+                 "  alert(\"obj.es6Method\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#generator",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":96",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function* generator() {\n" +
+                 "  alert(\"generator\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#anon-generator",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":55",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function*() {\n" +
+                 "  alert(\"anonGenerator\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#named-function-expression",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":23",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "let namedFunctionExpression =\n" +
+                 "  function foo() {\n" +
+                 "    alert(\"namedFunctionExpression\");\n" +
+                 "  }"
+      }
+    ]
+  },
+  {
+    selector: "#anon-function-expression",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":27",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "let anonFunctionExpression = function() {\n" +
+                 "  alert(\"anonFunctionExpression\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#returned-function",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":32",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "function bar() {\n" +
+                 "  alert(\"returnedFunction\");\n" +
+                 "}"
+      }
+    ]
+  },
+  {
+    selector: "#constructed-function",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":1",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: ""
+      }
+    ]
+  },
+  {
+    selector: "#constructed-function-with-body-string",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":1",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "alert(\"constructedFuncWithBodyString\");"
+      }
+    ]
+  },
+  {
+    selector: "#multiple-assignment",
+    expected: [
+      {
+        type: "click",
+        filename: TEST_URL + ":42",
+        attributes: [
+          "Bubbling",
+          "DOM2"
+        ],
+        handler: "let multipleAssignment = foo = bar = function multi() {\n" +
+                 "  alert(\"multipleAssignment\");\n" +
+                 "}"
+      }
+    ]
+  },
+];
+
+add_task(function* () {
+  yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
--- a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js
@@ -18,18 +18,17 @@ const TEST_DATA = [
     selector: "html",
     expected: [
       {
         type: "load",
         filename: URL_ROOT + TEST_LIB,
         attributes: [
           "jQuery"
         ],
-        handler: "// Handle when the DOM is ready\n" +
-                 "ready: function() {\n" +
+        handler: "ready: function() {\n" +
                  "  // Make sure that the DOM is not already loaded\n" +
                  "  if (!jQuery.isReady) {\n" +
                  "    // Remember that the DOM is ready\n" +
                  "    jQuery.isReady = true;\n" +
                  "\n" +
                  "    // If there are functions bound, to execute\n" +
                  "    if (jQuery.readyList) {\n" +
                  "      // Execute all of them\n" +
--- a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js
@@ -18,18 +18,17 @@ const TEST_DATA = [
     selector: "html",
     expected: [
       {
         type: "load",
         filename: URL_ROOT + TEST_LIB,
         attributes: [
           "jQuery"
         ],
-        handler: "// Handle when the DOM is ready\n" +
-                 "ready: function() {\n" +
+        handler: "ready: function() {\n" +
                  "  // Make sure that the DOM is not already loaded\n" +
                  "  if (!jQuery.isReady) {\n" +
                  "    // Remember that the DOM is ready\n" +
                  "    jQuery.isReady = true;\n" +
                  "\n" +
                  "    // If there are functions bound, to execute\n" +
                  "    if (jQuery.readyList) {\n" +
                  "      // Execute all of them\n" +
rename from devtools/client/inspector/markup/test/doc_markup_events.html
rename to devtools/client/inspector/markup/test/doc_markup_events1.html
--- a/devtools/client/inspector/markup/test/doc_markup_events.html
+++ b/devtools/client/inspector/markup/test/doc_markup_events1.html
@@ -14,116 +14,83 @@
       display: inline-block;
       margin: 2px;
     }
 
     #output,
     #noevents,
     #DOM0,
     #handleevent,
-    #fatarrow,
-    #bound,
-    #boundhe {
-      border: 1px solid #000;
-      width: 200px;
-      min-height: 1em;
-      cursor: pointer;
-    }
-
     #output,
     #noevents {
       cursor: auto;
     }
 
     #output {
       min-height: 1.5em;
     }
     </style>
     <script type="application/javascript;version=1.8">
       function init() {
         let container = document.getElementById("container");
         let multiple = document.getElementById("multiple");
-        let fatarrow = document.getElementById("fatarrow");
 
         container.addEventListener("mouseover", mouseoverHandler, true);
         multiple.addEventListener("click", clickHandler, false);
         multiple.addEventListener("mouseup", mouseupHandler, false);
 
-        new handleEventClick();
-        new boundHandleEventClick();
-
-        let bound = document.getElementById("bound");
-        boundClickHandler = boundClickHandler.bind(this);
-        bound.addEventListener("click", boundClickHandler);
-
-        fatarrow.addEventListener("click", event => {
-          alert("Yay for the fat arrow!");
-        });
+        let he = new handleEventClick();
+        let handleevent = document.getElementById("handleevent");
+        handleevent.addEventListener("click", he);
       }
 
       function mouseoverHandler(event) {
         if (event.target.id !== "container") {
           let output = document.getElementById("output");
           output.textContent = event.target.textContent;
         }
       }
 
       function clickHandler(event) {
         let output = document.getElementById("output");
         output.textContent = "click";
       }
 
-      function boundClickHandler(event) {
-        alert("Bound event clicked");
-      }
-
       function mouseupHandler(event) {
         let output = document.getElementById("output");
         output.textContent = "mouseup";
       }
 
       function handleEventClick(hehe) {
-        let handleevent = document.getElementById("handleevent");
-        handleevent.addEventListener("click", this);
+
       }
 
       handleEventClick.prototype = {
         handleEvent: function(blah) {
-          alert("handleEvent clicked");
-        }
-      };
-
-      function boundHandleEventClick() {
-        let boundhe = document.getElementById("boundhe");
-        this.handleEvent = this.handleEvent.bind(this);
-        boundhe.addEventListener("click", this);
-      }
-
-      boundHandleEventClick.prototype = {
-        handleEvent: function() {
-          alert("boundHandleEvent clicked");
+          alert("handleEvent");
         }
       };
 
       function noeventsClickHandler(event) {
         alert("noevents has an event listener");
-      };
+      }
 
       function addNoeventsClickHandler() {
         let noevents = document.getElementById("noevents");
         noevents.addEventListener("click", noeventsClickHandler);
-      };
+      }
 
       function removeNoeventsClickHandler() {
         let noevents = document.getElementById("noevents");
         noevents.removeEventListener("click", noeventsClickHandler);
-      };
+      }
     </script>
   </head>
   <body onload="init();">
+    <h1>Events test 1</h1>
     <div id="container">
       <div>1</div>
       <div>2</div>
       <div>3</div>
       <div>4</div>
       <div>5</div>
       <div>6</div>
       <div>7</div>
@@ -134,16 +101,13 @@
       <div>12</div>
       <div>13</div>
       <div>14</div>
       <div>15</div>
       <div>16</div>
       <div id="multiple">multiple</div>
     </div>
     <div id="output"></div>
-    <div id="noevents">No events here</div>
-    <div id="DOM0" onclick="alert('hi')">DOM0 event here</div>
-    <div id="handleevent">handleEvent event here</div>
-    <div id="fatarrow">Fat arrow event</div>
-    <div id="boundhe">Bound handleEvent</div>
-    <div id="bound">Bound event</div>
+    <div id="noevents">noevents</div>
+    <div id="DOM0" onclick="alert('DOM0')">DOM0 event here</div>
+    <div id="handleevent">handleEvent</div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events2.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style>
+    #fatarrow,
+    #bound,
+    #boundhe,
+    #comment-inline,
+    #comment-streaming,
+    #anon-object-method,
+    #object-method {
+      border: 1px solid #000;
+      width: 200px;
+      min-height: 1em;
+      cursor: pointer;
+    }
+    </style>
+    <script type="application/javascript;version=1.8">
+      function init() {
+        let fatarrow = document.getElementById("fatarrow");
+
+        let he = new handleEventClick();
+        let anonObjectMethod = document.getElementById("anon-object-method");
+        anonObjectMethod.addEventListener("click", he.anonObjectMethod);
+
+        let objectMethod = document.getElementById("object-method");
+        objectMethod.addEventListener("click", he.objectMethod);
+
+        let bhe = new boundHandleEventClick();
+        let boundheNode = document.getElementById("boundhe");
+        bhe.handleEvent = bhe.handleEvent.bind(bhe);
+        boundheNode.addEventListener("click", bhe);
+
+        let boundNode = document.getElementById("bound");
+        boundClickHandler = boundClickHandler.bind(this);
+        boundNode.addEventListener("click", boundClickHandler);
+
+        fatarrow.addEventListener("click", () => {
+          alert("Fat arrow without params!");
+        });
+
+        fatarrow.addEventListener("click", event => {
+          alert("Fat arrow with 1 param!");
+        });
+
+        fatarrow.addEventListener("click", (event, foo, bar) => {
+          alert("Fat arrow with 3 params!");
+        });
+
+        fatarrow.addEventListener("click", b => b);
+
+        let inlineCommentNode = document.getElementById("comment-inline");
+        inlineCommentNode
+          .addEventListener("click", functionProceededByInlineComment);
+
+        let streamingCommentNode = document.getElementById("comment-streaming");
+        streamingCommentNode
+          .addEventListener("click", functionProceededByStreamingComment);
+      }
+
+      function boundClickHandler(event) {
+        alert("Bound event");
+      }
+
+      function handleEventClick(hehe) {
+
+      }
+
+      handleEventClick.prototype = {
+        anonObjectMethod: function() {
+          alert("obj.anonObjectMethod");
+        },
+
+        objectMethod: function kay() {
+          alert("obj.objectMethod");
+        },
+      };
+
+      function boundHandleEventClick() {
+
+      }
+
+      boundHandleEventClick.prototype = {
+        handleEvent: function() {
+          alert("boundHandleEvent");
+        }
+      };
+
+      // A function proceeded with an inline comment
+      function functionProceededByInlineComment() {
+        alert("comment-inline");
+      }
+
+      /* A function proceeded with a streaming comment */
+      function functionProceededByStreamingComment() {
+        alert("comment-streaming");
+      }
+    </script>
+  </head>
+  <body onload="init();">
+    <h1>Events test 2</h1>
+    <div id="fatarrow">Fat arrows</div>
+    <div id="boundhe">Bound handleEvent</div>
+    <div id="bound">Bound event</div>
+    <div id="comment-inline">Event proceeded by an inline comment</div>
+    <div id="comment-streaming">Event proceeded by a streaming comment</div>
+    <div id="anon-object-method">Anonymous object method</div>
+    <div id="object-method">Object method</div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events3.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style>
+    #es6-method,
+    #generator,
+    #anon-generator,
+    #named-function-expression,
+    #anon-function-expression,
+    #returned-function,
+    #constructed-function,
+    #constructed-function-with-body-string,
+    #multiple-assignment {
+      border: 1px solid #000;
+      width: 200px;
+      min-height: 1em;
+      cursor: pointer;
+    }
+    </style>
+    <script type="application/javascript;version=1.8">
+      let namedFunctionExpression =
+      function foo() {
+        alert("namedFunctionExpression");
+      }
+
+      let anonFunctionExpression = function() {
+        alert("anonFunctionExpression");
+      };
+
+      let returnedFunction = (function() {
+        return function bar() {
+          alert("returnedFunction");
+        }
+      })();
+
+      let constructedFunc = new Function();
+
+      let constructedFuncWithBodyString =
+        new Function('a', 'b', 'c', 'alert("constructedFuncWithBodyString");');
+
+      let multipleAssignment = foo = bar = function multi() {
+        alert("multipleAssignment");
+      }
+
+      function init() {
+        let he = new handleEventClick();
+        let es6Method = document.getElementById("es6-method");
+        es6Method.addEventListener("click", he.es6Method);
+
+        let generatorNode = document.getElementById("generator");
+        generatorNode.addEventListener("click", generator);
+
+        let anonGenerator = document.getElementById("anon-generator");
+        anonGenerator.addEventListener("click", function* () {
+          alert("anonGenerator");
+        });
+
+        let namedFunctionExpressionNode =
+          document.getElementById("named-function-expression");
+        namedFunctionExpressionNode.addEventListener("click",
+                                                     namedFunctionExpression);
+
+        let anonFunctionExpressionNode =
+          document.getElementById("anon-function-expression");
+        anonFunctionExpressionNode.addEventListener("click",
+                                                     anonFunctionExpression);
+
+        let returnedFunctionNode = document.getElementById("returned-function");
+        returnedFunctionNode.addEventListener("click", returnedFunction);
+
+        let constructedFunctionNode =
+          document.getElementById("constructed-function");
+        constructedFunctionNode.addEventListener("click", constructedFunc);
+
+        let constructedFunctionWithBodyStringNode =
+          document.getElementById("constructed-function-with-body-string");
+        constructedFunctionWithBodyStringNode
+          .addEventListener("click", constructedFuncWithBodyString);
+
+        let multipleAssignmentNode =
+          document.getElementById("multiple-assignment");
+        multipleAssignmentNode.addEventListener("click", multipleAssignment);
+      }
+
+      function handleEventClick(hehe) {
+
+      }
+
+      handleEventClick.prototype = {
+        es6Method() {
+          alert("obj.es6Method");
+        }
+      };
+
+      function* generator() {
+        alert("generator");
+      }
+    </script>
+  </head>
+  <body onload="init();">
+    <h1>Events test 3</h1>
+    <div id="es6-method">ES6 method</div>
+    <div id="generator">Generator</div>
+    <div id="anon-generator">Anonymous Generator</div>
+    <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>
+  </body>
+</html>
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -82,18 +82,20 @@ loader.lazyRequireGetter(this, "CSS", "C
 
 const {EventParsers} = require("devtools/shared/event-parsers");
 
 const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
 const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const IMAGE_FETCHING_TIMEOUT = 500;
+const RX_FUNC_NAME = /((var|const|let)\s+)?([\w$.]+\s*[:=]\s*)*(function)?\s*\*?\s*([\w$]+)?\s*$/;
+
 // The possible completions to a ':' with added score to give certain values
 // some preference.
 const PSEUDO_SELECTORS = [
   [":active", 1],
   [":hover", 1],
   [":focus", 1],
   [":visited", 0],
   [":link", 0],
@@ -548,38 +550,32 @@ var NodeActor = exports.NodeActor = prot
     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:
+    something like this:
       () { doSomething(); }
 
-    So we need to work back to the preceeding \n, ; or } so we can get the
-      appropriate function info e.g.:
-      () => { doSomething(); }
-      function doit() { doSomething(); }
-      doit: function() { 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 lastEnding = Math.max(
-      scriptBeforeFunc.lastIndexOf(";"),
-      scriptBeforeFunc.lastIndexOf("}"),
-      scriptBeforeFunc.lastIndexOf("{"),
-      scriptBeforeFunc.lastIndexOf("("),
-      scriptBeforeFunc.lastIndexOf(","),
-      scriptBeforeFunc.lastIndexOf("!")
-    );
-
-    if (lastEnding !== -1) {
-      let functionPrefix = scriptBeforeFunc.substr(lastEnding + 1);
-      functionSource = functionPrefix + functionSource;
+    let matches = scriptBeforeFunc.match(RX_FUNC_NAME);
+    if (matches && matches.length > 0) {
+      functionSource = matches[0].trim() + functionSource;
     }
 
     let dom0 = false;
 
     if (typeof node.hasAttribute !== "undefined") {
       dom0 = !!node.hasAttribute("on" + type);
     } else {
       dom0 = !!node["on" + type];