Bug 1356415 - move devtools helper findCssSelector to shared module in toolkit;r?mixedpuppy draft
authorJulian Descottes <jdescottes@mozilla.com>
Wed, 26 Apr 2017 15:30:36 +0200
changeset 573061 1a2e0a57129ad428f89a7f32a76eef2d61246528
parent 573060 dd7e914a5fe453a0596ba9bf81fe29c7b617f103
child 627220 07e82d492a6930103331ed41b6e8aed703f390ac
push id57295
push userjdescottes@mozilla.com
push dateFri, 05 May 2017 09:23:05 +0000
reviewersmixedpuppy
bugs1356415
milestone55.0a1
Bug 1356415 - move devtools helper findCssSelector to shared module in toolkit;r?mixedpuppy MozReview-Commit-ID: 2KOeij1oJnT
browser/base/content/content.js
devtools/shared/inspector/css-logic.js
devtools/shared/tests/mochitest/chrome.ini
devtools/shared/tests/mochitest/test_css-logic.html
toolkit/modules/css-selector.js
toolkit/modules/moz.build
toolkit/modules/tests/chrome/chrome.ini
toolkit/modules/tests/chrome/test_findCssSelector.html
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -46,21 +46,18 @@ XPCOMUtils.defineLazyGetter(this, "PageM
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
   "resource://gre/modules/WebNavigationFrames.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
   "resource:///modules/Feeds.jsm");
-XPCOMUtils.defineLazyGetter(this, "findCssSelector", () => {
-  let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  let { findCssSelector } = require("devtools/shared/inspector/css-logic");
-  return findCssSelector;
-});
+XPCOMUtils.defineLazyModuleGetter(this, "findCssSelector",
+  "resource://gre/modules/css-selector.js");
 
 Cu.importGlobalProperties(["URL"]);
 
 // TabChildGlobal
 var global = this;
 
 // Load the form validation popup handler
 var formSubmitObserver = new FormSubmitObserver(content, this);
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -39,16 +39,19 @@ const { getTabPrefs } = require("devtool
  * styling information in the page, and present this to the user in a way that
  * helps them understand:
  * - why their expectations may not have been fulfilled
  * - how browsers process CSS
  * @constructor
  */
 
 const Services = require("Services");
+
+loader.lazyImporter(this, "findCssSelector", "resource://gre/modules/css-selector.js");
+
 const CSSLexer = require("devtools/shared/css/lexer");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const styleInspectorL10N =
   new LocalizationHelper("devtools/shared/locales/styleinspector.properties");
 
 /**
  * Special values for filter, in addition to an href these values can be used
  */
@@ -340,94 +343,20 @@ function prettifyCSS(text, ruleCount) {
   }
 
   return result;
 }
 
 exports.prettifyCSS = prettifyCSS;
 
 /**
- * Find the position of [element] in [nodeList].
- * @returns an index of the match, or -1 if there is no match
- */
-function positionInNodeList(element, nodeList) {
-  for (let i = 0; i < nodeList.length; i++) {
-    if (element === nodeList[i]) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-/**
  * Find a unique CSS selector for a given element
  * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
  * and ele.ownerDocument.querySelectorAll(reply).length === 1
  */
-function findCssSelector(ele) {
-  ele = getRootBindingParent(ele);
-  let document = ele.ownerDocument;
-  if (!document || !document.contains(ele)) {
-    throw new Error("findCssSelector received element not inside document");
-  }
-
-  // document.querySelectorAll("#id") returns multiple if elements share an ID
-  if (ele.id &&
-      document.querySelectorAll("#" + CSS.escape(ele.id)).length === 1) {
-    return "#" + CSS.escape(ele.id);
-  }
-
-  // Inherently unique by tag name
-  let tagName = ele.localName;
-  if (tagName === "html") {
-    return "html";
-  }
-  if (tagName === "head") {
-    return "head";
-  }
-  if (tagName === "body") {
-    return "body";
-  }
-
-  // We might be able to find a unique class name
-  let selector, index, matches;
-  if (ele.classList.length > 0) {
-    for (let i = 0; i < ele.classList.length; i++) {
-      // Is this className unique by itself?
-      selector = "." + CSS.escape(ele.classList.item(i));
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique with a tag name?
-      selector = tagName + selector;
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique using a tag name and nth-child
-      index = positionInNodeList(ele, ele.parentNode.children) + 1;
-      selector = selector + ":nth-child(" + index + ")";
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-    }
-  }
-
-  // Not unique enough yet.  As long as it's not a child of the document,
-  // continue recursing up until it is unique enough.
-  if (ele.parentNode !== document) {
-    index = positionInNodeList(ele, ele.parentNode.children) + 1;
-    selector = findCssSelector(ele.parentNode) + " > " +
-      tagName + ":nth-child(" + index + ")";
-  }
-
-  return selector;
-}
 exports.findCssSelector = findCssSelector;
 
 /**
  * Get the full CSS path for a given element.
  * @returns a string that can be used as a CSS selector for the element. It might not
  * match the element uniquely. It does however, represent the full path from the root
  * node to the element.
  */
--- a/devtools/shared/tests/mochitest/chrome.ini
+++ b/devtools/shared/tests/mochitest/chrome.ini
@@ -1,10 +1,9 @@
 [DEFAULT]
 tags = devtools
 skip-if = os == 'android'
 
 [test_css-logic-getCssPath.html]
-[test_css-logic.html]
 [test_devtools_extensions.html]
 [test_dom_matrix_2d.html]
 [test_eventemitter_basic.html]
 skip-if = os == 'linux' && debug # Bug 1205739
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/css-selector.js
@@ -0,0 +1,114 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["findCssSelector"];
+
+/**
+ * Traverse getBindingParent until arriving upon the bound element
+ * responsible for the generation of the specified node.
+ * See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/DOM_Interfaces#getBindingParent.
+ *
+ * @param {DOMNode} node
+ * @return {DOMNode}
+ *         If node is not anonymous, this will return node. Otherwise,
+ *         it will return the bound element
+ *
+ */
+function getRootBindingParent(node) {
+  let parent;
+  let doc = node.ownerDocument;
+  if (!doc) {
+    return node;
+  }
+  while ((parent = doc.getBindingParent(node))) {
+    node = parent;
+  }
+  return node;
+}
+
+/**
+ * Find the position of [element] in [nodeList].
+ * @returns an index of the match, or -1 if there is no match
+ */
+function positionInNodeList(element, nodeList) {
+  for (let i = 0; i < nodeList.length; i++) {
+    if (element === nodeList[i]) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Find a unique CSS selector for a given element
+ * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
+ * and ele.ownerDocument.querySelectorAll(reply).length === 1
+ */
+const findCssSelector = function(ele) {
+  ele = getRootBindingParent(ele);
+  let document = ele.ownerDocument;
+  if (!document || !document.contains(ele)) {
+    throw new Error("findCssSelector received element not inside document");
+  }
+
+  let cssEscape = ele.ownerGlobal.CSS.escape;
+
+  // document.querySelectorAll("#id") returns multiple if elements share an ID
+  if (ele.id &&
+      document.querySelectorAll("#" + cssEscape(ele.id)).length === 1) {
+    return "#" + cssEscape(ele.id);
+  }
+
+  // Inherently unique by tag name
+  let tagName = ele.localName;
+  if (tagName === "html") {
+    return "html";
+  }
+  if (tagName === "head") {
+    return "head";
+  }
+  if (tagName === "body") {
+    return "body";
+  }
+
+  // We might be able to find a unique class name
+  let selector, index, matches;
+  if (ele.classList.length > 0) {
+    for (let i = 0; i < ele.classList.length; i++) {
+      // Is this className unique by itself?
+      selector = "." + cssEscape(ele.classList.item(i));
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+      // Maybe it's unique with a tag name?
+      selector = tagName + selector;
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+      // Maybe it's unique using a tag name and nth-child
+      index = positionInNodeList(ele, ele.parentNode.children) + 1;
+      selector = selector + ":nth-child(" + index + ")";
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+    }
+  }
+
+  // Not unique enough yet.  As long as it's not a child of the document,
+  // continue recursing up until it is unique enough.
+  if (ele.parentNode !== document) {
+    index = positionInNodeList(ele, ele.parentNode.children) + 1;
+    selector = findCssSelector(ele.parentNode) + " > " +
+      tagName + ":nth-child(" + index + ")";
+  }
+
+  return selector;
+}
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -178,16 +178,17 @@ EXTRA_JS_MODULES += [
     'BinarySearch.jsm',
     'BrowserUtils.jsm',
     'CanonicalJSON.jsm',
     'CertUtils.jsm',
     'CharsetMenu.jsm',
     'ClientID.jsm',
     'Color.jsm',
     'Console.jsm',
+    'css-selector.js',
     'DateTimePickerHelper.jsm',
     'debug.js',
     'DeferredTask.jsm',
     'Deprecated.jsm',
     'EventEmitter.jsm',
     'FileUtils.jsm',
     'Finder.jsm',
     'FinderHighlighter.jsm',
--- a/toolkit/modules/tests/chrome/chrome.ini
+++ b/toolkit/modules/tests/chrome/chrome.ini
@@ -1,3 +1,4 @@
 [DEFAULT]
 
 [test_bug544442_checkCert.xul]
+[test_findCssSelector.html]
\ No newline at end of file
rename from devtools/shared/tests/mochitest/test_css-logic.html
rename to toolkit/modules/tests/chrome/test_findCssSelector.html
--- a/devtools/shared/tests/mochitest/test_css-logic.html
+++ b/toolkit/modules/tests/chrome/test_findCssSelector.html
@@ -7,18 +7,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug </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">
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
-let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const CssLogic = require("devtools/shared/inspector/css-logic");
+/* globals findCssSelector */
+Cu.import("resource://gre/modules/css-selector.js", this);
 
 var _tests = [];
 function addTest(test) {
   _tests.push(test);
 }
 
 function runNextTest() {
   if (_tests.length == 0) {
@@ -29,60 +29,60 @@ function runNextTest() {
 }
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 addTest(function findAllCssSelectors() {
-  var nodes = document.querySelectorAll('*');
+  var nodes = document.querySelectorAll("*");
   for (var i = 0; i < nodes.length; i++) {
-    var selector = CssLogic.findCssSelector(nodes[i]);
+    var selector = findCssSelector(nodes[i]);
     var matches = document.querySelectorAll(selector);
 
-    is(matches.length, 1, 'There is a single match: ' + selector);
-    is(matches[0], nodes[i], 'The selector matches the correct node: ' + selector);
+    is(matches.length, 1, "There is a single match: " + selector);
+    is(matches[0], nodes[i], "The selector matches the correct node: " + selector);
   }
 
   runNextTest();
 });
 
 addTest(function findCssSelectorNotContainedInDocument() {
 
   var unattached = document.createElement("div");
   unattached.id = "unattached";
   try {
-    CssLogic.findCssSelector(unattached);
-    ok (false, "Unattached node did not throw")
-  } catch(e) {
+    findCssSelector(unattached);
+    ok(false, "Unattached node did not throw")
+  } catch (e) {
     ok(e, "Unattached node throws an exception");
   }
 
   var unattachedChild = document.createElement("div");
   unattached.appendChild(unattachedChild);
   try {
-    CssLogic.findCssSelector(unattachedChild);
-    ok (false, "Unattached child node did not throw")
-  } catch(e) {
+    findCssSelector(unattachedChild);
+    ok(false, "Unattached child node did not throw")
+  } catch (e) {
     ok(e, "Unattached child node throws an exception");
   }
 
   var unattachedBody = document.createElement("body");
   try {
-    CssLogic.findCssSelector(unattachedBody);
-    ok (false, "Unattached body node did not throw")
-  } catch(e) {
+    findCssSelector(unattachedBody);
+    ok(false, "Unattached body node did not throw")
+  } catch (e) {
     ok(e, "Unattached body node throws an exception");
   }
 
   runNextTest();
 });
 
-addTest(function findCssSelector() {
+addTest(function findCssSelectorBasic() {
 
   let data = [
     "#one",
     "#" + CSS.escape("2"),
     ".three",
     "." + CSS.escape("4"),
     "#find-css-selector > div:nth-child(5)",
     "#find-css-selector > p:nth-child(6)",
@@ -92,21 +92,21 @@ addTest(function findCssSelector() {
     ".ten",
     "div.sameclass:nth-child(11)",
     "div.sameclass:nth-child(12)",
     "div.sameclass:nth-child(13)",
     "#" + CSS.escape("!, \", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \\, ], ^, `, {, |, }, ~"),
   ];
 
   let container = document.querySelector("#find-css-selector");
-  is (container.children.length, data.length, "Container has correct number of children.");
+  is(container.children.length, data.length, "Container has correct number of children.");
 
   for (let i = 0; i < data.length; i++) {
     let node = container.children[i];
-    is (CssLogic.findCssSelector(node), data[i], "matched id for index " + (i-1));
+    is(findCssSelector(node), data[i], "matched id for index " + (i - 1));
   }
 
   runNextTest();
 });
   </script>
 </head>
 <body>
   <div id="find-css-selector">