Chromium Code Reviews| 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 |