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
--- 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,
+ };
+}