Bug 1356415 - move devtools helper findCssSelector to shared module in toolkit;r?mixedpuppy
MozReview-Commit-ID: 2KOeij1oJnT
--- 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">