Bug 1449972 - part2: Move getCssPath and getXPath to toolkit css-selector;r=bgrins

getCssPath and getXPath will need to reuse the same logic as findCssSelector
to handle shadowDOM support.

This patch moves the methods next to findCssSelector, in toolkit's css-selector.js
to avoid duplicating logic between devtools/ and toolkit/

The content of the methods is stricltly the same, except for the Node global
not available in css-selector.js. Instead we use `ele.ownerGlobal.Node` here.

MozReview-Commit-ID: J0KuORWLUoO
This commit is contained in:
Julian Descottes
2018-07-18 07:33:21 +02:00
parent c4ea9c2ddd
commit c68fa50651
2 changed files with 121 additions and 99 deletions

View File

@@ -6,7 +6,6 @@
"use strict";
const { getRootBindingParent } = require("devtools/shared/layout/utils");
const { getTabPrefs } = require("devtools/shared/indentation");
const InspectorUtils = require("InspectorUtils");
@@ -49,6 +48,8 @@ const MAX_DATA_URL_LENGTH = 40;
const Services = require("Services");
loader.lazyImporter(this, "findCssSelector", "resource://gre/modules/css-selector.js");
loader.lazyImporter(this, "getCssPath", "resource://gre/modules/css-selector.js");
loader.lazyImporter(this, "getXPath", "resource://gre/modules/css-selector.js");
const CSSLexer = require("devtools/shared/css/lexer");
const {LocalizationHelper} = require("devtools/shared/l10n");
@@ -374,49 +375,6 @@ exports.findCssSelector = findCssSelector;
* match the element uniquely. It does however, represent the full path from the root
* node to the element.
*/
function getCssPath(ele) {
ele = getRootBindingParent(ele);
const document = ele.ownerDocument;
if (!document || !document.contains(ele)) {
// getCssPath received element not inside document.
return "";
}
const getElementSelector = element => {
if (!element.localName) {
return "";
}
let label = element.nodeName == element.nodeName.toUpperCase()
? element.localName.toLowerCase()
: element.localName;
if (element.id) {
label += "#" + element.id;
}
if (element.classList) {
for (const cl of element.classList) {
label += "." + cl;
}
}
return label;
};
const paths = [];
while (ele) {
if (!ele || ele.nodeType !== Node.ELEMENT_NODE) {
break;
}
paths.splice(0, 0, getElementSelector(ele));
ele = ele.parentNode;
}
return paths.length ? paths.join(" ") : "";
}
exports.getCssPath = getCssPath;
/**
@@ -424,60 +382,6 @@ exports.getCssPath = getCssPath;
* @param {DomNode} ele
* @returns a string that can be used as an XPath to find the element uniquely.
*/
function getXPath(ele) {
ele = getRootBindingParent(ele);
const document = ele.ownerDocument;
if (!document || !document.contains(ele)) {
// getXPath received element not inside document.
return "";
}
// Create a short XPath for elements with IDs.
if (ele.id) {
return `//*[@id="${ele.id}"]`;
}
// Otherwise walk the DOM up and create a part for each ancestor.
const parts = [];
// Use nodeName (instead of localName) so namespace prefix is included (if any).
while (ele && ele.nodeType === Node.ELEMENT_NODE) {
let nbOfPreviousSiblings = 0;
let hasNextSiblings = false;
// Count how many previous same-name siblings the element has.
let sibling = ele.previousSibling;
while (sibling) {
// Ignore document type declaration.
if (sibling.nodeType !== Node.DOCUMENT_TYPE_NODE &&
sibling.nodeName == ele.nodeName) {
nbOfPreviousSiblings++;
}
sibling = sibling.previousSibling;
}
// Check if the element has at least 1 next same-name sibling.
sibling = ele.nextSibling;
while (sibling) {
if (sibling.nodeName == ele.nodeName) {
hasNextSiblings = true;
break;
}
sibling = sibling.nextSibling;
}
const prefix = ele.prefix ? ele.prefix + ":" : "";
const nth = nbOfPreviousSiblings || hasNextSiblings
? `[${nbOfPreviousSiblings + 1}]` : "";
parts.push(prefix + ele.localName + nth);
ele = ele.parentNode;
}
return parts.length ? "/" + parts.reverse().join("/") : "";
}
exports.getXPath = getXPath;
/**