Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(912)

Unified Diff: Source/devtools/front_end/DOMPresentationUtils.js

Issue 75253002: DevTools: [Elements] Implement "Copy CSS Path" context menu item for elements (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: More comments addressed, __proto__ bug fixed Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698