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

Side by Side Diff: Source/devtools/front_end/DOMAgent.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: Protect from ridiculous IDs 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved. 2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro 3 * Copyright (C) 2009 Joseph Pecoraro
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are 6 * modification, are permitted provided that the following conditions are
7 * met: 7 * met:
8 * 8 *
9 * * Redistributions of source code must retain the above copyright 9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 WebInspector.DOMNode.ShadowRootTypes = { 118 WebInspector.DOMNode.ShadowRootTypes = {
119 UserAgent: "user-agent", 119 UserAgent: "user-agent",
120 Author: "author" 120 Author: "author"
121 } 121 }
122 122
123 /** 123 /**
124 * @constructor 124 * @constructor
125 * @param {string} value 125 * @param {string} value
126 * @param {boolean} optimized 126 * @param {boolean} optimized
127 */ 127 */
128 WebInspector.DOMNode.XPathStep = function(value, optimized) 128 WebInspector.DOMNode.PathStep = function(value, optimized)
129 { 129 {
130 this.value = value; 130 this.value = value;
131 this.optimized = optimized; 131 this.optimized = optimized;
132 } 132 }
133 133
134 WebInspector.DOMNode.XPathStep.prototype = { 134 WebInspector.DOMNode.PathStep.prototype = {
135 toString: function() 135 toString: function()
136 { 136 {
137 return this.value; 137 return this.value;
138 } 138 }
139 } 139 }
140 140
141 WebInspector.DOMNode.prototype = { 141 WebInspector.DOMNode.prototype = {
142 /** 142 /**
143 * @return {Array.<WebInspector.DOMNode>} 143 * @return {Array.<WebInspector.DOMNode>}
144 */ 144 */
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after
423 { 423 {
424 if (!error) 424 if (!error)
425 InspectorFrontendHost.copyText(text); 425 InspectorFrontendHost.copyText(text);
426 } 426 }
427 DOMAgent.getOuterHTML(this.id, copy); 427 DOMAgent.getOuterHTML(this.id, copy);
428 }, 428 },
429 429
430 /** 430 /**
431 * @param {boolean} optimized 431 * @param {boolean} optimized
432 */ 432 */
433 copyCSSPath: function(optimized)
434 {
435 InspectorFrontendHost.copyText(this.cssPath(optimized));
436 },
437
438 /**
439 * @param {boolean} optimized
440 */
433 copyXPath: function(optimized) 441 copyXPath: function(optimized)
434 { 442 {
435 InspectorFrontendHost.copyText(this.xPath(optimized)); 443 InspectorFrontendHost.copyText(this.xPath(optimized));
436 }, 444 },
437 445
438 /** 446 /**
439 * @param {string} objectGroupId 447 * @param {string} objectGroupId
440 * @param {function(?Protocol.Error)=} callback 448 * @param {function(?Protocol.Error)=} callback
441 */ 449 */
442 eventListeners: function(objectGroupId, callback) 450 eventListeners: function(objectGroupId, callback)
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
678 */ 686 */
679 isXMLNode: function() 687 isXMLNode: function()
680 { 688 {
681 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion; 689 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
682 }, 690 },
683 691
684 /** 692 /**
685 * @param {boolean} optimized 693 * @param {boolean} optimized
686 * @return {string} 694 * @return {string}
687 */ 695 */
696 cssPath: function(optimized)
697 {
698 if (this._nodeType !== Node.ELEMENT_NODE)
699 return "";
700
701 var steps = [];
702 var contextNode = this;
703 while (contextNode) {
704 var step = contextNode._cssPathValue(optimized);
705 if (!step)
706 break; // Error - bail out early.
707 steps.push(step);
708 if (step.optimized)
709 break;
710 contextNode = contextNode.parentNode;
711 }
712
713 steps.reverse();
714 return steps.join(" > ");
715 },
716
717 /**
718 * @param {boolean} optimized
719 * @return {WebInspector.DOMNode.PathStep}
720 */
721 _cssPathValue: function(optimized)
722 {
723 if (this._nodeType !== Node.ELEMENT_NODE)
724 return null;
725 if (optimized) {
726 var id = this.getAttribute("id");
727 if (id)
728 return new WebInspector.DOMNode.PathStep(idSelector(id), true);
729 var nodeNameLower = this._nodeName.toLowerCase();
730 if (nodeNameLower === "body" || nodeNameLower === "head" || nodeName Lower === "html")
731 return new WebInspector.DOMNode.PathStep(this.nodeNameInCorrectC ase(), true);
732 }
733 var nodeName = this.nodeNameInCorrectCase();
734 var parent = this.parentNode;
735 if (!parent || parent._nodeType === Node.DOCUMENT_NODE)
736 return new WebInspector.DOMNode.PathStep(nodeName, true);
737
738 /**
739 * @param {WebInspector.DOMNode} node
740 * @return {Object.<string, boolean>}
741 */
742 function elementClassNames(node)
743 {
744 var classAttribute = node.getAttribute("class");
745 if (!classAttribute)
746 return {};
747
748 return classAttribute.split(/\s+/g).filter(Boolean).keySet();
749 }
750
751 /**
752 * @param {string} id
753 * @return {string}
754 */
755 function idSelector(id)
756 {
757 if (/^[A-Za-z0-9_-]+$/.test(id))
aandrey 2013/11/19 11:15:09 FYI. document.querySelectorAll("#---") ===> Synta
758 return "#" + id;
759
760 var quoteChar = "\"";
761 var hasQuotes = /"/.test(id);
aandrey 2013/11/19 11:11:18 indexOf
762 var hasApostrophes = /'/.test(id);
aandrey 2013/11/19 11:11:18 indexOf
763 if (hasQuotes) {
764 if (!hasApostrophes)
765 quoteChar = "'";
766 else
767 id = id.replace(/"/g, "\\\\\"");
aandrey 2013/11/19 11:11:18 this code path is not tested, and quite hard to re
768 }
769 return "[id=" + quoteChar + id + quoteChar + "]";
770 }
771
772 var uniqueClassNames = elementClassNames(this);
773 var uniqueClassNamesLeft = 0;
774 for (var name in uniqueClassNames)
775 ++uniqueClassNamesLeft;
776 var needsClassNames = false;
777 var needsNthChild = false;
778 var ownIndex = -1;
779 var siblings = parent.children();
780 for (var i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.leng th; ++i) {
781 var sibling = siblings[i];
782 if (sibling === this) {
783 ownIndex = i;
784 continue;
785 }
786 if (sibling.nodeNameInCorrectCase() !== nodeName)
787 continue;
788 if (!uniqueClassNamesLeft) {
789 needsNthChild = true;
790 continue;
791 }
792
793 needsClassNames = true;
794 var siblingClassNames = elementClassNames(sibling);
795 for (var siblingClass in siblingClassNames) {
796 if (!uniqueClassNames.hasOwnProperty(siblingClass))
797 continue;
798 delete uniqueClassNames[siblingClass];
799 if (!--uniqueClassNamesLeft) {
800 needsNthChild = true;
801 break;
802 }
803 }
804 }
805
806 var result = nodeName;
807 if (needsClassNames && uniqueClassNamesLeft) {
808 for (var className in uniqueClassNames)
809 result += "." + className;
aandrey 2013/11/19 11:11:18 And what about className containing odd characters
810 }
811 if (needsNthChild)
812 result += ":nth-child(" + (ownIndex + 1) + ")";
813 return new WebInspector.DOMNode.PathStep(result, false);
814 },
815
816 /**
817 * @param {boolean} optimized
818 * @return {string}
819 */
688 xPath: function(optimized) 820 xPath: function(optimized)
689 { 821 {
690 if (this._nodeType === Node.DOCUMENT_NODE) 822 if (this._nodeType === Node.DOCUMENT_NODE)
691 return "/"; 823 return "/";
692 824
693 var steps = []; 825 var steps = [];
694 var contextNode = this; 826 var contextNode = this;
695 while (contextNode) { 827 while (contextNode) {
696 var step = contextNode._xPathValue(optimized); 828 var step = contextNode._xPathValue(optimized);
697 if (!step) 829 if (!step)
698 break; // Error - bail out early. 830 break; // Error - bail out early.
699 steps.push(step); 831 steps.push(step);
700 if (step.optimized) 832 if (step.optimized)
701 break; 833 break;
702 contextNode = contextNode.parentNode; 834 contextNode = contextNode.parentNode;
703 } 835 }
704 836
705 steps.reverse(); 837 steps.reverse();
706 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/") ; 838 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/") ;
707 }, 839 },
708 840
709 /** 841 /**
710 * @param {boolean} optimized 842 * @param {boolean} optimized
711 * @return {WebInspector.DOMNode.XPathStep} 843 * @return {WebInspector.DOMNode.PathStep}
712 */ 844 */
713 _xPathValue: function(optimized) 845 _xPathValue: function(optimized)
714 { 846 {
715 var ownValue; 847 var ownValue;
716 var ownIndex = this._xPathIndex(); 848 var ownIndex = this._xPathIndex();
717 if (ownIndex === -1) 849 if (ownIndex === -1)
718 return null; // Error. 850 return null; // Error.
719 851
720 switch (this._nodeType) { 852 switch (this._nodeType) {
721 case Node.ELEMENT_NODE: 853 case Node.ELEMENT_NODE:
722 if (optimized && this.getAttribute("id")) 854 if (optimized && this.getAttribute("id"))
723 return new WebInspector.DOMNode.XPathStep("//*[@id=\"" + this.ge tAttribute("id") + "\"]", true); 855 return new WebInspector.DOMNode.PathStep("//*[@id=\"" + this.get Attribute("id") + "\"]", true);
724 ownValue = this._localName; 856 ownValue = this._localName;
725 break; 857 break;
726 case Node.ATTRIBUTE_NODE: 858 case Node.ATTRIBUTE_NODE:
727 ownValue = "@" + this._nodeName; 859 ownValue = "@" + this._nodeName;
728 break; 860 break;
729 case Node.TEXT_NODE: 861 case Node.TEXT_NODE:
730 case Node.CDATA_SECTION_NODE: 862 case Node.CDATA_SECTION_NODE:
731 ownValue = "text()"; 863 ownValue = "text()";
732 break; 864 break;
733 case Node.PROCESSING_INSTRUCTION_NODE: 865 case Node.PROCESSING_INSTRUCTION_NODE:
734 ownValue = "processing-instruction()"; 866 ownValue = "processing-instruction()";
735 break; 867 break;
736 case Node.COMMENT_NODE: 868 case Node.COMMENT_NODE:
737 ownValue = "comment()"; 869 ownValue = "comment()";
738 break; 870 break;
739 case Node.DOCUMENT_NODE: 871 case Node.DOCUMENT_NODE:
740 ownValue = ""; 872 ownValue = "";
741 break; 873 break;
742 default: 874 default:
743 ownValue = ""; 875 ownValue = "";
744 break; 876 break;
745 } 877 }
746 878
747 if (ownIndex > 0) 879 if (ownIndex > 0)
748 ownValue += "[" + ownIndex + "]"; 880 ownValue += "[" + ownIndex + "]";
749 881
750 return new WebInspector.DOMNode.XPathStep(ownValue, this._nodeType === N ode.DOCUMENT_NODE); 882 return new WebInspector.DOMNode.PathStep(ownValue, this._nodeType === No de.DOCUMENT_NODE);
751 }, 883 },
752 884
753 /** 885 /**
754 * @return {number} 886 * @return {number}
755 */ 887 */
756 _xPathIndex: function() 888 _xPathIndex: function()
757 { 889 {
758 // Returns -1 in case of error, 0 if no siblings matching the same expre ssion, <XPath index among the same expression-matching sibling nodes> otherwise. 890 // Returns -1 in case of error, 0 if no siblings matching the same expre ssion, <XPath index among the same expression-matching sibling nodes> otherwise.
759 function areNodesSimilar(left, right) 891 function areNodesSimilar(left, right)
760 { 892 {
(...skipping 988 matching lines...) Expand 10 before | Expand all | Expand 10 after
1749 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) 1881 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback)
1750 { 1882 {
1751 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callba ck); 1883 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callba ck);
1752 } 1884 }
1753 } 1885 }
1754 1886
1755 /** 1887 /**
1756 * @type {?WebInspector.DOMAgent} 1888 * @type {?WebInspector.DOMAgent}
1757 */ 1889 */
1758 WebInspector.domAgent = null; 1890 WebInspector.domAgent = null;
OLDNEW
« no previous file with comments | « LayoutTests/inspector/elements/elements-css-path-expected.txt ('k') | Source/devtools/front_end/ElementsTreeOutline.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698