Bug 1356415 - move devtools helper findCssSelector to shared module in toolkit;r=mixedpuppy
authorJulian Descottes <jdescottes@mozilla.com>
Wed, 26 Apr 2017 15:30:36 +0200
changeset 356802 aa507b3a8893b16dabb02f6b5a08feb9e5546543
parent 356801 52a37a9d52a38524f37e3def10f768862e61d091
child 356803 c8e67ddc6f34278a29e50fa4e8bf9935d630032c
push id89970
push userkwierso@gmail.com
push dateFri, 05 May 2017 21:20:56 +0000
treeherdermozilla-inbound@c3d254b2070d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1356415
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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">