Bug 1041266 - Properly deal with event handlers that the debugger cannot unwrap. r=vporof, a=sledru
authorPanos Astithas <past@mozilla.com>
Mon, 21 Jul 2014 18:31:18 +0300
changeset 217239 91e719c1469037668c96d0341f568aff4b66d2c2
parent 217238 b452f0ac8a41b8241c8c428eb55679ece48f8680
child 217240 89dbad79b0b358a33e2e07e5450cdf0d0915f4c4
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvporof, sledru
bugs1041266
milestone33.0a2
Bug 1041266 - Properly deal with event handlers that the debugger cannot unwrap. r=vporof, a=sledru
browser/devtools/debugger/test/browser.ini
browser/devtools/debugger/test/browser_dbg_event-listeners-03.js
browser/devtools/debugger/test/doc_native-event-handler.html
toolkit/devtools/server/actors/script.js
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -60,16 +60,17 @@ support-files =
   doc_global-method-override.html
   doc_iframes.html
   doc_included-script.html
   doc_inline-debugger-statement.html
   doc_inline-script.html
   doc_large-array-buffer.html
   doc_minified.html
   doc_minified_bogus_map.html
+  doc_native-event-handler.html
   doc_no-page-sources.html
   doc_pause-exceptions.html
   doc_pretty-print.html
   doc_pretty-print-2.html
   doc_pretty-print-3.html
   doc_pretty-print-on-paused.html
   doc_random-javascript.html
   doc_recursion-stack.html
@@ -150,16 +151,17 @@ skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_server-conditional-bp-04.js]
 [browser_dbg_controller-evaluate-01.js]
 [browser_dbg_controller-evaluate-02.js]
 [browser_dbg_debugger-statement.js]
 [browser_dbg_editor-contextmenu.js]
 [browser_dbg_editor-mode.js]
 [browser_dbg_event-listeners-01.js]
 [browser_dbg_event-listeners-02.js]
+[browser_dbg_event-listeners-03.js]
 [browser_dbg_file-reload.js]
 [browser_dbg_function-display-name.js]
 [browser_dbg_global-method-override.js]
 [browser_dbg_globalactor.js]
 [browser_dbg_host-layout.js]
 [browser_dbg_iframes.js]
 [browser_dbg_instruments-pane-collapse.js]
 [browser_dbg_interrupts.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_event-listeners-03.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the eventListeners request works when there are event handlers
+ * that the debugger cannot unwrap.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
+
+let gClient;
+
+function test() {
+  if (!DebuggerServer.initialized) {
+    DebuggerServer.init(() => true);
+    DebuggerServer.addBrowserActors();
+  }
+
+  let transport = DebuggerServer.connectPipe();
+  gClient = new DebuggerClient(transport);
+  gClient.connect((aType, aTraits) => {
+    is(aType, "browser",
+      "Root actor should identify itself as a browser.");
+
+    addTab(TAB_URL)
+      .then(() => attachThreadActorForUrl(gClient, TAB_URL))
+      .then(pauseDebuggee)
+      .then(testEventListeners)
+      .then(closeConnection)
+      .then(finish)
+      .then(null, aError => {
+        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      });
+  });
+}
+
+function pauseDebuggee(aThreadClient) {
+  let deferred = promise.defer();
+
+  gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
+    is(aPacket.type, "paused",
+      "We should now be paused.");
+    is(aPacket.why.type, "debuggerStatement",
+      "The debugger statement was hit.");
+
+    deferred.resolve(aThreadClient);
+  });
+
+  // Spin the event loop before causing the debuggee to pause, to allow
+  // this function to return first.
+  executeSoon(() => {
+    EventUtils.sendMouseEvent({ type: "click" },
+      content.document.querySelector("button"),
+      content);
+  });
+
+  return deferred.promise;
+}
+
+function testEventListeners(aThreadClient) {
+  let deferred = promise.defer();
+
+  aThreadClient.eventListeners(aPacket => {
+    if (aPacket.error) {
+      let msg = "Error getting event listeners: " + aPacket.message;
+      ok(false, msg);
+      deferred.reject(msg);
+      return;
+    }
+
+    // There are 4 event listeners in the page: button.onclick, window.onload
+    // and two more from the video element controls.
+    is(aPacket.listeners.length, 4, "Found all event listeners.");
+    aThreadClient.resume(deferred.resolve);
+  });
+
+  return deferred.promise;
+}
+
+function closeConnection() {
+  let deferred = promise.defer();
+  gClient.close(deferred.resolve);
+  return deferred.promise;
+}
+
+registerCleanupFunction(function() {
+  removeTab(gBrowser.selectedTab);
+  gClient = null;
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_native-event-handler.html
@@ -0,0 +1,22 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>A video element with native event handlers</title>
+    <script type="text/javascript">
+      function initialSetup(event) {
+        debugger;
+      }
+
+      window.addEventListener("load", function() {}, false);
+    </script>
+  </head>
+  <body>
+    <button onclick="initialSetup()">Click me!</button>
+    <!-- the "controls" attribute ensures that there are extra event handlers in
+         the element. -->
+    <video controls></video>
+  </body>
+</html>
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -1856,16 +1856,21 @@ ThreadActor.prototype = {
         }
         if (!!node[handlerName]) {
           listenerForm.isEventHandler = !!node[handlerName];
         }
         // Get the Debugger.Object for the listener object.
         let listenerDO = this.globalDebugObject.makeDebuggeeValue(listener);
         // If the listener is an object with a 'handleEvent' method, use that.
         if (listenerDO.class == "Object" || listenerDO.class == "XULElement") {
+          // For some events we don't have permission to access the
+          // 'handleEvent' property when running in content scope.
+          if (!listenerDO.unwrap()) {
+            continue;
+          }
           let heDesc;
           while (!heDesc && listenerDO) {
             heDesc = listenerDO.getOwnPropertyDescriptor("handleEvent");
             listenerDO = listenerDO.proto;
           }
           if (heDesc && heDesc.value) {
             listenerDO = heDesc.value;
           }