Index: Source/devtools/front_end/DOMPresentationUtils.js |
diff --git a/Source/devtools/front_end/DOMPresentationUtils.js b/Source/devtools/front_end/DOMPresentationUtils.js |
index 181fcd90dfbdcacc13943426fe3be6e92a5438d3..c9ee1317ea793f6d49a9f486d7af2fffb0f98a54 100644 |
--- a/Source/devtools/front_end/DOMPresentationUtils.js |
+++ b/Source/devtools/front_end/DOMPresentationUtils.js |
@@ -158,3 +158,179 @@ WebInspector.DOMPresentationUtils.buildImagePreviewContents = function(imageURL, |
userCallback(container); |
} |
} |
+ |
+/** |
+ * @param {!WebInspector.DOMNode} node |
+ * @param {boolean=} optimized |
+ */ |
+WebInspector.DOMPresentationUtils.cssPathValue = function(node, optimized) |
+{ |
+ if (node.nodeType() !== Node.ELEMENT_NODE) |
+ return null; |
+ |
+ var id = node.getAttribute("id"); |
+ if (optimized) { |
+ if (id) |
+ return new WebInspector.DOMNodePathStep(idSelector(id), true); |
+ var nodeNameLower = node.nodeName().toLowerCase(); |
+ if (nodeNameLower === "body" || nodeNameLower === "head" || nodeNameLower === "html") |
+ return new WebInspector.DOMNodePathStep(node.nodeNameInCorrectCase(), true); |
+ } |
+ var nodeName = node.nodeNameInCorrectCase(); |
+ |
+ if (id) |
+ return new WebInspector.DOMNodePathStep(nodeName + idSelector(id), true); |
+ var parent = node.parentNode; |
+ if (!parent || parent.nodeType() === Node.DOCUMENT_NODE) |
+ return new WebInspector.DOMNodePathStep(nodeName, true); |
+ |
+ /** |
+ * @param {WebInspector.DOMNode} node |
+ * @return {Array.<string>} |
+ */ |
+ function prefixedElementClassNames(node) |
+ { |
+ var classAttribute = node.getAttribute("class"); |
+ if (!classAttribute) |
+ return []; |
+ |
+ return classAttribute.split(/\s+/g).filter(Boolean).map(function(name) { |
+ // The prefix is required to store "__proto__" in a object-based map. |
+ return "$" + name; |
+ }); |
+ } |
+ |
+ /** |
+ * @param {string} id |
+ * @return {string} |
+ */ |
+ function idSelector(id) |
+ { |
+ return "#" + escapeIdentifierIfNeeded(id); |
+ } |
+ |
+ /** |
+ * @param {string} ident |
+ * @return {string} |
+ */ |
+ function escapeIdentifierIfNeeded(ident) |
+ { |
+ if (isCSSIdentifier(ident)) |
+ return ident; |
+ var shouldEscapeFirst = /^(?:[0-9]|-[0-9-]?)/.test(ident); |
+ var lastIndex = ident.length - 1; |
+ return ident.replace(/./g, function(c, i) { |
+ return ((shouldEscapeFirst && i === 0) || !isCSSIdentChar(c)) ? escapeAsciiChar(c, i === lastIndex) : c; |
+ }); |
+ } |
+ |
+ /** |
+ * @param {string} c |
+ * @param {boolean} isLast |
+ * @return {string} |
+ */ |
+ function escapeAsciiChar(c, isLast) |
+ { |
+ return "\\" + toHexByte(c) + (isLast ? "" : " "); |
+ } |
+ |
+ /** |
+ * @param {string} c |
+ */ |
+ function toHexByte(c) |
+ { |
+ var hexByte = c.charCodeAt(0).toString(16); |
+ if (hexByte.length === 1) |
+ hexByte = "0" + hexByte; |
+ return hexByte; |
+ } |
+ |
+ /** |
+ * @param {string} c |
+ * @return {boolean} |
+ */ |
+ function isCSSIdentChar(c) |
+ { |
+ if (/[a-zA-Z0-9_-]/.test(c)) |
+ return true; |
+ return c.charCodeAt(0) >= 0xA0; |
+ } |
+ |
+ /** |
+ * @param {string} value |
+ * @return {boolean} |
+ */ |
+ function isCSSIdentifier(value) |
+ { |
+ return /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value); |
+ } |
+ |
+ var prefixedOwnClassNamesArray = prefixedElementClassNames(node); |
+ var needsClassNames = false; |
+ var needsNthChild = false; |
+ var ownIndex = -1; |
+ var siblings = parent.children(); |
+ for (var i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.length; ++i) { |
+ var sibling = siblings[i]; |
+ if (sibling === node) { |
+ ownIndex = i; |
+ continue; |
+ } |
+ if (needsNthChild) |
+ continue; |
+ if (sibling.nodeNameInCorrectCase() !== nodeName) |
+ continue; |
+ |
+ needsClassNames = true; |
+ var ownClassNames = prefixedOwnClassNamesArray.keySet(); |
+ var ownClassNameCount = 0; |
+ for (var name in ownClassNames) |
+ ++ownClassNameCount; |
+ if (ownClassNameCount === 0) { |
+ needsNthChild = true; |
+ continue; |
+ } |
+ var siblingClassNamesArray = prefixedElementClassNames(sibling); |
+ for (var j = 0; j < siblingClassNamesArray.length; ++j) { |
+ var siblingClass = siblingClassNamesArray[j]; |
+ if (!ownClassNames.hasOwnProperty(siblingClass)) |
+ continue; |
+ delete ownClassNames[siblingClass]; |
+ if (!--ownClassNameCount) { |
+ needsNthChild = true; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ var result = nodeName; |
+ if (needsNthChild) { |
+ result += ":nth-child(" + (ownIndex + 1) + ")"; |
+ } else if (needsClassNames) { |
+ for (var prefixedName in prefixedOwnClassNamesArray.keySet()) |
+ result += "." + escapeIdentifierIfNeeded(prefixedName.substr(1)); |
+ } |
+ |
+ return new WebInspector.DOMNodePathStep(result, false); |
+} |
+ |
+/** |
+ * @constructor |
+ * @param {string} value |
+ * @param {boolean} optimized |
+ */ |
+WebInspector.DOMNodePathStep = function(value, optimized) |
+{ |
+ this.value = value; |
+ this.optimized = optimized; |
+} |
+ |
+WebInspector.DOMNodePathStep.prototype = { |
+ /** |
+ * @return {string} |
+ */ |
+ toString: function() |
+ { |
+ return this.value; |
+ } |
+} |