OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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) | |
pfeldman
2013/11/20 14:08:48
This interesting little feature is about to become
apavlov
2013/11/20 14:23:19
Sounds good.
| |
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 return "#" + escapeIdentifierIfNeeded(id); | |
758 } | |
759 | |
760 /** | |
761 * @param {string} ident | |
762 * @return {string} | |
763 */ | |
764 function escapeIdentifierIfNeeded(ident) | |
765 { | |
766 if (isCSSIdentifier(ident)) | |
767 return ident; | |
768 var result = ""; | |
769 var shouldEscapeFirst = /^(?:[0-9]|-[0-9-]?)/.test(ident); | |
aandrey
2013/11/20 12:49:02
let's use similar regex to the one you already use
apavlov
2013/11/20 13:15:28
This regex does not cover the case of an identifie
aandrey
2013/11/20 16:20:49
in console:
/^(?:[0-9]|-[0-9-]?)/.test("--a")
!/^
apavlov
2013/11/21 07:31:35
Right. But I know what breaks. Valid unicode chars
| |
770 var lastIndex = ident.length - 1; | |
771 for (var i = 0; i <= lastIndex; ++i) { | |
772 var c = ident[i]; | |
773 result += ((!i && shouldEscapeFirst) || !isCSSIdentChar(c)) ? es capeAsciiChar(c, i === lastIndex) : c; | |
aandrey
2013/11/20 12:49:02
nit: hard to read, but I could not simplify this n
apavlov
2013/11/20 13:15:28
Fine with that, even though running this code migh
| |
774 } | |
775 return result; | |
776 } | |
777 | |
778 /** | |
779 * @param {string} c | |
780 * @param {boolean} isLast | |
781 * @return {string} | |
782 */ | |
783 function escapeAsciiChar(c, isLast) | |
784 { | |
785 return "\\" + toHexByte(c) + (isLast ? "" : " "); | |
786 } | |
787 | |
788 /** | |
789 * @param {string} c | |
790 */ | |
791 function toHexByte(c) | |
792 { | |
793 var hexByte = c.charCodeAt(0).toString(16); | |
794 if (hexByte.length === 1) | |
795 hexByte = "0" + hexByte; | |
796 return hexByte; | |
797 } | |
798 | |
799 /** | |
800 * @param {string} c | |
801 * @return {boolean} | |
802 */ | |
803 function isCSSIdentChar(c) | |
804 { | |
805 if (/[a-zA-Z0-9_-]/.test(c)) | |
aandrey
2013/11/20 12:49:02
/^[a-zA-Z0-9_-]$/
apavlov
2013/11/20 13:15:28
Does it make much sense given that |c| is guarante
aandrey
2013/11/20 16:20:49
I think adding ^ and $ restrictions should always
apavlov
2013/11/21 07:31:35
That's the case for multichar strings. In our case
| |
806 return true; | |
807 return c.charCodeAt(0) >= 0xA0; | |
808 } | |
809 | |
810 /** | |
811 * @param {string} value | |
812 * @return {boolean} | |
813 */ | |
814 function isCSSIdentifier(value) | |
pfeldman
2013/11/20 14:08:48
Also, DOM should not know this much about CSS
apavlov
2013/11/20 14:23:19
I'm fine with adding a lazy-loaded class. Or will
| |
815 { | |
816 return /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value); | |
817 } | |
818 | |
819 var uniqueClassNames = elementClassNames(this); | |
820 var uniqueClassNamesLeft = 0; | |
821 for (var name in uniqueClassNames) | |
822 ++uniqueClassNamesLeft; | |
823 var needsClassNames = false; | |
824 var needsNthChild = false; | |
825 var ownIndex = -1; | |
826 var siblings = parent.children(); | |
827 for (var i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.leng th; ++i) { | |
828 var sibling = siblings[i]; | |
829 if (sibling === this) { | |
830 ownIndex = i; | |
831 continue; | |
832 } | |
833 if (sibling.nodeNameInCorrectCase() !== nodeName) | |
834 continue; | |
835 if (!uniqueClassNamesLeft) { | |
836 needsNthChild = true; | |
837 continue; | |
838 } | |
839 | |
840 needsClassNames = true; | |
841 var siblingClassNames = elementClassNames(sibling); | |
842 for (var siblingClass in siblingClassNames) { | |
843 if (!uniqueClassNames.hasOwnProperty(siblingClass)) | |
844 continue; | |
845 delete uniqueClassNames[siblingClass]; | |
846 if (!--uniqueClassNamesLeft) { | |
847 needsNthChild = true; | |
848 break; | |
849 } | |
850 } | |
851 } | |
852 | |
853 var result = nodeName; | |
854 if (needsClassNames) { | |
855 for (var className in uniqueClassNames) | |
856 result += "." + escapeIdentifierIfNeeded(className); | |
857 } | |
858 if (needsNthChild) | |
859 result += ":nth-child(" + (ownIndex + 1) + ")"; | |
860 return new WebInspector.DOMNode.PathStep(result, false); | |
861 }, | |
862 | |
863 /** | |
864 * @param {boolean} optimized | |
865 * @return {string} | |
866 */ | |
688 xPath: function(optimized) | 867 xPath: function(optimized) |
689 { | 868 { |
690 if (this._nodeType === Node.DOCUMENT_NODE) | 869 if (this._nodeType === Node.DOCUMENT_NODE) |
691 return "/"; | 870 return "/"; |
692 | 871 |
693 var steps = []; | 872 var steps = []; |
694 var contextNode = this; | 873 var contextNode = this; |
695 while (contextNode) { | 874 while (contextNode) { |
696 var step = contextNode._xPathValue(optimized); | 875 var step = contextNode._xPathValue(optimized); |
697 if (!step) | 876 if (!step) |
698 break; // Error - bail out early. | 877 break; // Error - bail out early. |
699 steps.push(step); | 878 steps.push(step); |
700 if (step.optimized) | 879 if (step.optimized) |
701 break; | 880 break; |
702 contextNode = contextNode.parentNode; | 881 contextNode = contextNode.parentNode; |
703 } | 882 } |
704 | 883 |
705 steps.reverse(); | 884 steps.reverse(); |
706 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/") ; | 885 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/") ; |
707 }, | 886 }, |
708 | 887 |
709 /** | 888 /** |
710 * @param {boolean} optimized | 889 * @param {boolean} optimized |
711 * @return {WebInspector.DOMNode.XPathStep} | 890 * @return {WebInspector.DOMNode.PathStep} |
712 */ | 891 */ |
713 _xPathValue: function(optimized) | 892 _xPathValue: function(optimized) |
714 { | 893 { |
715 var ownValue; | 894 var ownValue; |
716 var ownIndex = this._xPathIndex(); | 895 var ownIndex = this._xPathIndex(); |
717 if (ownIndex === -1) | 896 if (ownIndex === -1) |
718 return null; // Error. | 897 return null; // Error. |
719 | 898 |
720 switch (this._nodeType) { | 899 switch (this._nodeType) { |
721 case Node.ELEMENT_NODE: | 900 case Node.ELEMENT_NODE: |
722 if (optimized && this.getAttribute("id")) | 901 if (optimized && this.getAttribute("id")) |
723 return new WebInspector.DOMNode.XPathStep("//*[@id=\"" + this.ge tAttribute("id") + "\"]", true); | 902 return new WebInspector.DOMNode.PathStep("//*[@id=\"" + this.get Attribute("id") + "\"]", true); |
724 ownValue = this._localName; | 903 ownValue = this._localName; |
725 break; | 904 break; |
726 case Node.ATTRIBUTE_NODE: | 905 case Node.ATTRIBUTE_NODE: |
727 ownValue = "@" + this._nodeName; | 906 ownValue = "@" + this._nodeName; |
728 break; | 907 break; |
729 case Node.TEXT_NODE: | 908 case Node.TEXT_NODE: |
730 case Node.CDATA_SECTION_NODE: | 909 case Node.CDATA_SECTION_NODE: |
731 ownValue = "text()"; | 910 ownValue = "text()"; |
732 break; | 911 break; |
733 case Node.PROCESSING_INSTRUCTION_NODE: | 912 case Node.PROCESSING_INSTRUCTION_NODE: |
734 ownValue = "processing-instruction()"; | 913 ownValue = "processing-instruction()"; |
735 break; | 914 break; |
736 case Node.COMMENT_NODE: | 915 case Node.COMMENT_NODE: |
737 ownValue = "comment()"; | 916 ownValue = "comment()"; |
738 break; | 917 break; |
739 case Node.DOCUMENT_NODE: | 918 case Node.DOCUMENT_NODE: |
740 ownValue = ""; | 919 ownValue = ""; |
741 break; | 920 break; |
742 default: | 921 default: |
743 ownValue = ""; | 922 ownValue = ""; |
744 break; | 923 break; |
745 } | 924 } |
746 | 925 |
747 if (ownIndex > 0) | 926 if (ownIndex > 0) |
748 ownValue += "[" + ownIndex + "]"; | 927 ownValue += "[" + ownIndex + "]"; |
749 | 928 |
750 return new WebInspector.DOMNode.XPathStep(ownValue, this._nodeType === N ode.DOCUMENT_NODE); | 929 return new WebInspector.DOMNode.PathStep(ownValue, this._nodeType === No de.DOCUMENT_NODE); |
751 }, | 930 }, |
752 | 931 |
753 /** | 932 /** |
754 * @return {number} | 933 * @return {number} |
755 */ | 934 */ |
756 _xPathIndex: function() | 935 _xPathIndex: function() |
757 { | 936 { |
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. | 937 // 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) | 938 function areNodesSimilar(left, right) |
760 { | 939 { |
(...skipping 988 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1749 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) | 1928 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) |
1750 { | 1929 { |
1751 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callba ck); | 1930 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callba ck); |
1752 } | 1931 } |
1753 } | 1932 } |
1754 | 1933 |
1755 /** | 1934 /** |
1756 * @type {?WebInspector.DOMAgent} | 1935 * @type {?WebInspector.DOMAgent} |
1757 */ | 1936 */ |
1758 WebInspector.domAgent = null; | 1937 WebInspector.domAgent = null; |
OLD | NEW |