Bug 1347490 - Return isConnected in node grip to indicate if a node is in the DOM tree. r=ochameau draft
authornchevobbe <nchevobbe@mozilla.com>
Tue, 04 Apr 2017 12:08:54 +0200
changeset 556169 b43644cf869663412a9ed6b956093e7a41afb01f
parent 555310 b5d8b27a753725c1de41ffae2e338798f3b5cacd
child 622822 00197ee71da2d6f311a69c17ed13540c16e6425e
push id52479
push userbmo:nchevobbe@mozilla.com
push dateWed, 05 Apr 2017 14:29:39 +0000
reviewersochameau
bugs1347490
milestone55.0a1
Bug 1347490 - Return isConnected in node grip to indicate if a node is in the DOM tree. r=ochameau node.isConnected (see https://dom.spec.whatwg.org/#dom-node-isconnected) returns true if the node is in the DOM tree (including in a shadowDOM tree), which can be used in the frontend to display additional tools and information. MozReview-Commit-ID: LjUbkc7VPcB
devtools/server/actors/object.js
devtools/server/tests/mochitest/chrome.ini
devtools/server/tests/mochitest/test_webconsole-node-grip.html
devtools/server/tests/mochitest/webconsole-helpers.js
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1647,16 +1647,17 @@ DebuggerServer.ObjectActorPreviewers.Obj
         !(rawObj instanceof Ci.nsIDOMNode)) {
       return false;
     }
 
     let preview = grip.preview = {
       kind: "DOMNode",
       nodeType: rawObj.nodeType,
       nodeName: rawObj.nodeName,
+      isConnected: rawObj.isConnected === true,
     };
 
     if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
       preview.location = hooks.createValueGrip(rawObj.location.href);
     } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
       preview.childNodesLength = rawObj.childNodes.length;
 
       if (hooks.getGripDepth() < 2) {
--- a/devtools/server/tests/mochitest/chrome.ini
+++ b/devtools/server/tests/mochitest/chrome.ini
@@ -19,16 +19,17 @@ support-files =
   inspector-styles-data.html
   inspector-traversal-data.html
   large-image.jpg
   memory-helpers.js
   nonchrome_unsafeDereference.html
   small-image.gif
   setup-in-child.js
   setup-in-parent.js
+  webconsole-helpers.js
 
 [test_animation_actor-lifetime.html]
 [test_connection-manager.html]
 [test_connectToChild.html]
 [test_css-logic.html]
 [test_css-logic-media-queries.html]
 [test_css-logic-specificity.html]
 [test_css-properties.html]
@@ -94,10 +95,11 @@ support-files =
 [test_setupInParentChild.html]
 [test_styles-applied.html]
 [test_styles-computed.html]
 [test_styles-layout.html]
 [test_styles-matched.html]
 [test_styles-modify.html]
 [test_styles-svg.html]
 [test_unsafeDereference.html]
+[test_webconsole-node-grip.html]
 [test_websocket-server.html]
 skip-if = false
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/mochitest/test_webconsole-node-grip.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>DOMNode Object actor test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript" src="webconsole-helpers.js"></script>
+  <script>
+"use strict";
+
+const TEST_URL = "data:text/html,<html><body>Hello</body></html>";
+
+window.onload = async function () {
+  SimpleTest.waitForExplicitFinish();
+
+  try {
+    let {
+      consoleClient,
+    } = await attachURL(TEST_URL);
+    await testNotInTreeElementNode(consoleClient);
+    await testInTreeElementNode(consoleClient);
+    await testNotInTreeTextNode(consoleClient);
+    await testInTreeTextNode(consoleClient);
+  } catch (e) {
+    ok(false, `Error thrown: ${e.message}`);
+  }
+  SimpleTest.finish();
+};
+
+async function testNotInTreeElementNode(consoleClient) {
+  info("Testing isConnected property on a ElementNode not in the DOM tree");
+  let {result} = await consoleClient.evaluateJS("document.createElement(\"div\")");
+  is(result.preview.isConnected, false,
+    "isConnected is false since we only created the element");
+}
+
+async function testInTreeElementNode(consoleClient) {
+  info("Testing isConnected property on a ElementNode in the DOM tree");
+  let {result} = await consoleClient.evaluateJS("document.body");
+  is(result.preview.isConnected, true,
+    "isConnected is true as expected, since the element was retrieved from the DOM tree");
+}
+
+async function testNotInTreeTextNode(consoleClient) {
+  info("Testing isConnected property on a TextNode not in the DOM tree");
+  let {result} = await consoleClient.evaluateJS("document.createTextNode(\"Hello\")");
+  is(result.preview.isConnected, false,
+    "isConnected is false since we only created the element");
+}
+
+async function testInTreeTextNode(consoleClient) {
+  info("Testing isConnected property on a TextNode in the DOM tree");
+  let {result} = await consoleClient.evaluateJS("document.body.firstChild");
+  is(result.preview.isConnected, true,
+    "isConnected is true as expected, since the element was retrieved from the DOM tree");
+}
+
+  </script>
+</head>
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none">
+  </div>
+  <pre id="test">
+  </pre>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/mochitest/webconsole-helpers.js
@@ -0,0 +1,73 @@
+/* exported attachURL, evaluateJS */
+"use strict";
+
+var Cu = Components.utils;
+
+const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const {DebuggerClient} = require("devtools/shared/client/main");
+const {DebuggerServer} = require("devtools/server/main");
+
+const Services = require("Services");
+
+// Always log packets when running tests.
+Services.prefs.setBoolPref("devtools.debugger.log", true);
+SimpleTest.registerCleanupFunction(function () {
+  Services.prefs.clearUserPref("devtools.debugger.log");
+});
+
+if (!DebuggerServer.initialized) {
+  DebuggerServer.init();
+  DebuggerServer.addBrowserActors();
+  SimpleTest.registerCleanupFunction(function () {
+    DebuggerServer.destroy();
+  });
+}
+
+/**
+ * Open a tab, load the url, find the tab with the debugger server,
+ * and attach the console to it.
+ *
+ * @param {string} url : url to navigate to
+ * @return {Promise} Promise resolving when the console is attached.
+ *         The Promise resolves with an object containing :
+ *           - tab: the attached tab
+ *           - tabClient: the tab client
+ *           - consoleClient: the console client
+ *           - cleanup: a generator function which can be called to close
+ *             the opened tab and disconnect its debugger client.
+ */
+async function attachURL(url) {
+  let win = window.open(url, "_blank");
+  let client = null;
+
+  let cleanup = function* () {
+    if (client) {
+      yield client.close();
+      client = null;
+    }
+    if (win) {
+      win.close();
+      win = null;
+    }
+  };
+  SimpleTest.registerCleanupFunction(cleanup);
+
+  client = new DebuggerClient(DebuggerServer.connectPipe());
+  await client.connect();
+  let {tabs} = await client.listTabs();
+  let attachedTab = tabs.find(tab => tab.url === url);
+
+  if (!attachedTab) {
+    throw new Error(`Could not find a tab matching URL ${url}`);
+  }
+
+  const [, tabClient] = await client.attachTab(attachedTab.actor);
+  const [, consoleClient] = await client.attachConsole(attachedTab.consoleActor, []);
+
+  return {
+    tab: attachedTab,
+    tabClient,
+    consoleClient,
+    cleanup,
+  };
+}