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

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: Improve JsDoc 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 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 WebInspector.DOMNode.PseudoElementNames = { 112 WebInspector.DOMNode.PseudoElementNames = {
113 Before: "before", 113 Before: "before",
114 After: "after" 114 After: "after"
115 } 115 }
116 116
117 /** 117 /**
118 * @constructor 118 * @constructor
119 * @param {string} value 119 * @param {string} value
120 * @param {boolean} optimized 120 * @param {boolean} optimized
121 */ 121 */
122 WebInspector.DOMNode.XPathStep = function(value, optimized) 122 WebInspector.DOMNode.PathStep = function(value, optimized)
123 { 123 {
124 this.value = value; 124 this.value = value;
125 this.optimized = optimized; 125 this.optimized = optimized;
126 } 126 }
127 127
128 WebInspector.DOMNode.XPathStep.prototype = { 128 WebInspector.DOMNode.PathStep.prototype = {
129 toString: function() 129 toString: function()
130 { 130 {
131 return this.value; 131 return this.value;
132 } 132 }
133 } 133 }
134 134
135 WebInspector.DOMNode.prototype = { 135 WebInspector.DOMNode.prototype = {
136 /** 136 /**
137 * @return {Array.<WebInspector.DOMNode>} 137 * @return {Array.<WebInspector.DOMNode>}
138 */ 138 */
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 { 409 {
410 if (!error) 410 if (!error)
411 InspectorFrontendHost.copyText(text); 411 InspectorFrontendHost.copyText(text);
412 } 412 }
413 DOMAgent.getOuterHTML(this.id, copy); 413 DOMAgent.getOuterHTML(this.id, copy);
414 }, 414 },
415 415
416 /** 416 /**
417 * @param {boolean} optimized 417 * @param {boolean} optimized
418 */ 418 */
419 copyCSSPath: function(optimized)
420 {
421 InspectorFrontendHost.copyText(this.cssPath(optimized));
422 },
423
424 /**
425 * @param {boolean} optimized
426 */
419 copyXPath: function(optimized) 427 copyXPath: function(optimized)
420 { 428 {
421 InspectorFrontendHost.copyText(this.xPath(optimized)); 429 InspectorFrontendHost.copyText(this.xPath(optimized));
422 }, 430 },
423 431
424 /** 432 /**
425 * @param {string} objectGroupId 433 * @param {string} objectGroupId
426 * @param {function(?Protocol.Error)=} callback 434 * @param {function(?Protocol.Error)=} callback
427 */ 435 */
428 eventListeners: function(objectGroupId, callback) 436 eventListeners: function(objectGroupId, callback)
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 */ 672 */
665 isXMLNode: function() 673 isXMLNode: function()
666 { 674 {
667 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion; 675 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
668 }, 676 },
669 677
670 /** 678 /**
671 * @param {boolean} optimized 679 * @param {boolean} optimized
672 * @return {string} 680 * @return {string}
673 */ 681 */
682 cssPath: function(optimized)
683 {
684 if (this._nodeType !== Node.ELEMENT_NODE)
685 return "";
686
687 var steps = [];
688 var contextNode = this;
689 while (contextNode) {
690 var step = contextNode._cssPathValue(optimized);
691 if (!step)
692 break; // Error - bail out early.
693 steps.push(step);
694 if (step.optimized)
695 break;
696 contextNode = contextNode.parentNode;
697 }
698
699 steps.reverse();
700 return steps.join(" > ");
701 },
702
703 /**
704 * @param {boolean} optimized
705 * @return {WebInspector.DOMNode.PathStep}
706 */
707 _cssPathValue: function(optimized)
708 {
709 if (this._nodeType !== Node.ELEMENT_NODE)
710 return null;
711 if (optimized) {
712 if (this.getAttribute("id"))
713 return new WebInspector.DOMNode.PathStep("#" + this.getAttribute ("id"), true);
aandrey 2013/11/18 16:27:22 "id" can contain spaces. can it be written properl
714 var nodeNameLower = this._nodeName.toLowerCase();
715 if (nodeNameLower === "body" || nodeNameLower === "head" || nodeName Lower === "html")
716 return new WebInspector.DOMNode.PathStep(this.nodeNameInCorrectC ase(), true);
717 }
718 var nodeName = this.nodeNameInCorrectCase();
719 var parent = this.parentNode;
720 if (!parent || parent._nodeType === Node.DOCUMENT_NODE)
721 return new WebInspector.DOMNode.PathStep(nodeName, true);
722
723 /**
724 * @param {WebInspector.DOMNode} node
725 * @return {Object.<string, boolean>}
726 */
727 function elementClassNames(node)
728 {
729 var classAttribute = node.getAttribute("class");
730 if (!classAttribute)
731 return {};
732
733 return classAttribute.split(/\s+/g).filter(function(name) {
aandrey 2013/11/18 16:27:22 .filter(Boolean).keySet();
734 return !!name;
735 }).keySet();
736 }
737
738 var uniqueClassNames = elementClassNames(this);
739 var uniqueClassNamesLeft = 0;
740 for (var name in uniqueClassNames)
741 ++uniqueClassNamesLeft;
742 var needsClassNames = false;
743 var needsNthChild = false;
744 var ownIndex = -1;
745 var siblings = parent.children();
746 for (var i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.leng th; ++i) {
747 var sibling = siblings[i];
748 if (sibling === this) {
749 ownIndex = i;
750 continue;
751 }
752 if (sibling.nodeNameInCorrectCase() !== nodeName)
753 continue;
754 if (!uniqueClassNamesLeft) {
755 needsNthChild = true;
756 continue;
757 }
758
759 needsClassNames = true;
760 var siblingClassNames = elementClassNames(sibling);
761 for (var siblingClass in siblingClassNames) {
762 if (!uniqueClassNames.hasOwnProperty(siblingClass))
763 continue;
764 delete uniqueClassNames[siblingClass];
765 if (!--uniqueClassNamesLeft) {
766 needsNthChild = true;
767 break;
768 }
769 }
770 }
771
772 var result = nodeName;
773 if (needsClassNames && uniqueClassNamesLeft) {
774 for (var className in uniqueClassNames)
775 result += "." + className;
776 }
777 if (needsNthChild)
778 result += ":nth-child(" + (ownIndex + 1) + ")";
779 return new WebInspector.DOMNode.PathStep(result, false);
780 },
781
782 /**
783 * @param {boolean} optimized
784 * @return {string}
785 */
674 xPath: function(optimized) 786 xPath: function(optimized)
675 { 787 {
676 if (this._nodeType === Node.DOCUMENT_NODE) 788 if (this._nodeType === Node.DOCUMENT_NODE)
677 return "/"; 789 return "/";
678 790
679 var steps = []; 791 var steps = [];
680 var contextNode = this; 792 var contextNode = this;
681 while (contextNode) { 793 while (contextNode) {
682 var step = contextNode._xPathValue(optimized); 794 var step = contextNode._xPathValue(optimized);
683 if (!step) 795 if (!step)
684 break; // Error - bail out early. 796 break; // Error - bail out early.
685 steps.push(step); 797 steps.push(step);
686 if (step.optimized) 798 if (step.optimized)
687 break; 799 break;
688 contextNode = contextNode.parentNode; 800 contextNode = contextNode.parentNode;
689 } 801 }
690 802
691 steps.reverse(); 803 steps.reverse();
692 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/") ; 804 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/") ;
693 }, 805 },
694 806
695 /** 807 /**
696 * @param {boolean} optimized 808 * @param {boolean} optimized
697 * @return {WebInspector.DOMNode.XPathStep} 809 * @return {WebInspector.DOMNode.PathStep}
698 */ 810 */
699 _xPathValue: function(optimized) 811 _xPathValue: function(optimized)
700 { 812 {
701 var ownValue; 813 var ownValue;
702 var ownIndex = this._xPathIndex(); 814 var ownIndex = this._xPathIndex();
703 if (ownIndex === -1) 815 if (ownIndex === -1)
704 return null; // Error. 816 return null; // Error.
705 817
706 switch (this._nodeType) { 818 switch (this._nodeType) {
707 case Node.ELEMENT_NODE: 819 case Node.ELEMENT_NODE:
708 if (optimized && this.getAttribute("id")) 820 if (optimized && this.getAttribute("id"))
709 return new WebInspector.DOMNode.XPathStep("//*[@id=\"" + this.ge tAttribute("id") + "\"]", true); 821 return new WebInspector.DOMNode.PathStep("//*[@id=\"" + this.get Attribute("id") + "\"]", true);
710 ownValue = this._localName; 822 ownValue = this._localName;
711 break; 823 break;
712 case Node.ATTRIBUTE_NODE: 824 case Node.ATTRIBUTE_NODE:
713 ownValue = "@" + this._nodeName; 825 ownValue = "@" + this._nodeName;
714 break; 826 break;
715 case Node.TEXT_NODE: 827 case Node.TEXT_NODE:
716 case Node.CDATA_SECTION_NODE: 828 case Node.CDATA_SECTION_NODE:
717 ownValue = "text()"; 829 ownValue = "text()";
718 break; 830 break;
719 case Node.PROCESSING_INSTRUCTION_NODE: 831 case Node.PROCESSING_INSTRUCTION_NODE:
720 ownValue = "processing-instruction()"; 832 ownValue = "processing-instruction()";
721 break; 833 break;
722 case Node.COMMENT_NODE: 834 case Node.COMMENT_NODE:
723 ownValue = "comment()"; 835 ownValue = "comment()";
724 break; 836 break;
725 case Node.DOCUMENT_NODE: 837 case Node.DOCUMENT_NODE:
726 ownValue = ""; 838 ownValue = "";
727 break; 839 break;
728 default: 840 default:
729 ownValue = ""; 841 ownValue = "";
730 break; 842 break;
731 } 843 }
732 844
733 if (ownIndex > 0) 845 if (ownIndex > 0)
734 ownValue += "[" + ownIndex + "]"; 846 ownValue += "[" + ownIndex + "]";
735 847
736 return new WebInspector.DOMNode.XPathStep(ownValue, this._nodeType === N ode.DOCUMENT_NODE); 848 return new WebInspector.DOMNode.PathStep(ownValue, this._nodeType === No de.DOCUMENT_NODE);
737 }, 849 },
738 850
739 /** 851 /**
740 * @return {number} 852 * @return {number}
741 */ 853 */
742 _xPathIndex: function() 854 _xPathIndex: function()
743 { 855 {
744 // 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. 856 // 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.
745 function areNodesSimilar(left, right) 857 function areNodesSimilar(left, right)
746 { 858 {
(...skipping 988 matching lines...) Expand 10 before | Expand all | Expand 10 after
1735 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) 1847 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback)
1736 { 1848 {
1737 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callba ck); 1849 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callba ck);
1738 } 1850 }
1739 } 1851 }
1740 1852
1741 /** 1853 /**
1742 * @type {?WebInspector.DOMAgent} 1854 * @type {?WebInspector.DOMAgent}
1743 */ 1855 */
1744 WebInspector.domAgent = null; 1856 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