Bug 1356415 - move devtools helper findCssSelector to shared module in toolkit;r=mixedpuppy

MozReview-Commit-ID: 2KOeij1oJnT

--HG--
rename : devtools/shared/tests/mochitest/test_css-logic.html => toolkit/modules/tests/chrome/test_findCssSelector.html
extra : rebase_source : 940ee1fb814bc33836c1be8e569ed57a6c62ab2e
This commit is contained in:
Julian Descottes 2017-04-26 15:30:36 +02:00
parent 59ac4a80a5
commit d86bd44c46
7 changed files with 139 additions and 98 deletions

View File

@ -51,11 +51,8 @@ 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"]);

View File

@ -44,6 +44,9 @@ const { getTabPrefs } = require("devtools/shared/indentation");
*/
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 =
@ -344,85 +347,11 @@ function prettifyCSS(text, ruleCount) {
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;
/**

View File

@ -3,7 +3,6 @@ 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]

View File

@ -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;
}

View File

@ -183,6 +183,7 @@ EXTRA_JS_MODULES += [
'ClientID.jsm',
'Color.jsm',
'Console.jsm',
'css-selector.js',
'DateTimePickerHelper.jsm',
'debug.js',
'DeferredTask.jsm',

View File

@ -1,3 +1,4 @@
[DEFAULT]
[test_bug544442_checkCert.xul]
[test_findCssSelector.html]

View File

@ -12,8 +12,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=
<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) {
@ -34,13 +34,13 @@ window.onload = function() {
}
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();
@ -51,33 +51,33 @@ 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",
@ -97,11 +97,11 @@ addTest(function findCssSelector() {
];
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();