/src/mozilla-central/dom/base/XPathGenerator.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "XPathGenerator.h" |
8 | | |
9 | | #include "nsGkAtoms.h" |
10 | | #include "Element.h" |
11 | | #include "nsTArray.h" |
12 | | |
13 | | /** |
14 | | * Check whether a character is a non-word character. A non-word character is a |
15 | | * character that isn't in ('a'..'z') or in ('A'..'Z') or a number or an underscore. |
16 | | * */ |
17 | | bool IsNonWordCharacter(const char16_t& aChar) |
18 | 0 | { |
19 | 0 | if (((char16_t('A') <= aChar) && (aChar <= char16_t('Z'))) || |
20 | 0 | ((char16_t('a') <= aChar) && (aChar <= char16_t('z'))) || |
21 | 0 | ((char16_t('0') <= aChar) && (aChar <= char16_t('9'))) || |
22 | 0 | (aChar == char16_t('_'))) { |
23 | 0 | return false; |
24 | 0 | } else { |
25 | 0 | return true; |
26 | 0 | } |
27 | 0 | } |
28 | | |
29 | | /** |
30 | | * Check whether a string contains a non-word character. |
31 | | * */ |
32 | | bool ContainNonWordCharacter(const nsAString& aStr) |
33 | 0 | { |
34 | 0 | const char16_t* cur = aStr.BeginReading(); |
35 | 0 | const char16_t* end = aStr.EndReading(); |
36 | 0 | for (; cur < end; ++cur) { |
37 | 0 | if (IsNonWordCharacter(*cur)) { |
38 | 0 | return true; |
39 | 0 | } |
40 | 0 | } |
41 | 0 | return false; |
42 | 0 | } |
43 | | |
44 | | /** |
45 | | * Get the prefix according to the given namespace and assign the result to aResult. |
46 | | * */ |
47 | | void GetPrefix(const nsINode* aNode, nsAString& aResult) |
48 | 0 | { |
49 | 0 | if (aNode->IsXULElement()) { |
50 | 0 | aResult.AssignLiteral(u"xul"); |
51 | 0 | } else if (aNode->IsHTMLElement()) { |
52 | 0 | aResult.AssignLiteral(u"xhtml"); |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | void GetNameAttribute(const nsINode* aNode, nsAString& aResult) |
57 | 0 | { |
58 | 0 | if (aNode->HasName()) { |
59 | 0 | const Element* elem = aNode->AsElement(); |
60 | 0 | elem->GetAttr(kNameSpaceID_None, nsGkAtoms::name, aResult); |
61 | 0 | } |
62 | 0 | } |
63 | | |
64 | | /** |
65 | | * Put all sequences of ' in a string in between '," and ",' . And then put |
66 | | * the result string in between concat(' and '). |
67 | | * |
68 | | * For example, a string 'a'' will return result concat('',"'",'a',"''",'') |
69 | | * */ |
70 | | void GenerateConcatExpression(const nsAString& aStr, nsAString& aResult) |
71 | 0 | { |
72 | 0 | const char16_t* cur = aStr.BeginReading(); |
73 | 0 | const char16_t* end = aStr.EndReading(); |
74 | 0 |
|
75 | 0 | // Put all sequences of ' in between '," and ",' |
76 | 0 | nsAutoString result; |
77 | 0 | const char16_t* nonQuoteBeginPtr = nullptr; |
78 | 0 | const char16_t* quoteBeginPtr = nullptr; |
79 | 0 | for (; cur < end; ++cur) { |
80 | 0 | if (char16_t('\'') == *cur) { |
81 | 0 | if (nonQuoteBeginPtr) { |
82 | 0 | result.Append(nonQuoteBeginPtr, cur - nonQuoteBeginPtr); |
83 | 0 | nonQuoteBeginPtr = nullptr; |
84 | 0 | } |
85 | 0 | if (!quoteBeginPtr) { |
86 | 0 | result.AppendLiteral(u"\',\""); |
87 | 0 | quoteBeginPtr = cur; |
88 | 0 | } |
89 | 0 | } else { |
90 | 0 | if (!nonQuoteBeginPtr) { |
91 | 0 | nonQuoteBeginPtr = cur; |
92 | 0 | } |
93 | 0 | if (quoteBeginPtr) { |
94 | 0 | result.Append(quoteBeginPtr, cur - quoteBeginPtr); |
95 | 0 | result.AppendLiteral(u"\",\'"); |
96 | 0 | quoteBeginPtr = nullptr; |
97 | 0 | } |
98 | 0 | } |
99 | 0 | } |
100 | 0 |
|
101 | 0 | if (quoteBeginPtr) { |
102 | 0 | result.Append(quoteBeginPtr, cur - quoteBeginPtr); |
103 | 0 | result.AppendLiteral(u"\",\'"); |
104 | 0 | } else if (nonQuoteBeginPtr) { |
105 | 0 | result.Append(nonQuoteBeginPtr, cur - nonQuoteBeginPtr); |
106 | 0 | } |
107 | 0 |
|
108 | 0 | // Prepend concat(' and append '). |
109 | 0 | aResult.Assign(NS_LITERAL_STRING("concat(\'") + result + NS_LITERAL_STRING("\')")); |
110 | 0 | } |
111 | | |
112 | | void XPathGenerator::QuoteArgument(const nsAString& aArg, nsAString& aResult) |
113 | 0 | { |
114 | 0 | if (!aArg.Contains('\'')) { |
115 | 0 | aResult.Assign(NS_LITERAL_STRING("\'") + aArg + NS_LITERAL_STRING("\'")); |
116 | 0 | } else if (!aArg.Contains('\"')) { |
117 | 0 | aResult.Assign(NS_LITERAL_STRING("\"") + aArg + NS_LITERAL_STRING("\"")); |
118 | 0 | } else { |
119 | 0 | GenerateConcatExpression(aArg, aResult); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | void XPathGenerator::EscapeName(const nsAString& aName, nsAString& aResult) |
124 | 0 | { |
125 | 0 | if (ContainNonWordCharacter(aName)) { |
126 | 0 | nsAutoString quotedArg; |
127 | 0 | QuoteArgument(aName, quotedArg); |
128 | 0 | aResult.Assign(NS_LITERAL_STRING("*[local-name()=") + quotedArg + NS_LITERAL_STRING("]")); |
129 | 0 | } else { |
130 | 0 | aResult.Assign(aName); |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | void XPathGenerator::Generate(const nsINode* aNode, nsAString& aResult) |
135 | 0 | { |
136 | 0 | if (!aNode->GetParentNode()) { |
137 | 0 | aResult.Truncate(); |
138 | 0 | return; |
139 | 0 | } |
140 | 0 | |
141 | 0 | nsAutoString nodeNamespaceURI; |
142 | 0 | aNode->GetNamespaceURI(nodeNamespaceURI); |
143 | 0 | const nsString& nodeLocalName = aNode->LocalName(); |
144 | 0 |
|
145 | 0 | nsAutoString prefix; |
146 | 0 | nsAutoString tag; |
147 | 0 | nsAutoString nodeEscapeName; |
148 | 0 | GetPrefix(aNode, prefix); |
149 | 0 | EscapeName(nodeLocalName, nodeEscapeName); |
150 | 0 | if (prefix.IsEmpty()) { |
151 | 0 | tag.Assign(nodeEscapeName); |
152 | 0 | } else { |
153 | 0 | tag.Assign(prefix + NS_LITERAL_STRING(":") + nodeEscapeName); |
154 | 0 | } |
155 | 0 |
|
156 | 0 | if (aNode->HasID()) { |
157 | 0 | // this must be an element |
158 | 0 | const Element* elem = aNode->AsElement(); |
159 | 0 | nsAutoString elemId; |
160 | 0 | nsAutoString quotedArgument; |
161 | 0 | elem->GetId(elemId); |
162 | 0 | QuoteArgument(elemId, quotedArgument); |
163 | 0 | aResult.Assign(NS_LITERAL_STRING("//") + tag + NS_LITERAL_STRING("[@id=") + |
164 | 0 | quotedArgument + NS_LITERAL_STRING("]")); |
165 | 0 | return; |
166 | 0 | } |
167 | 0 |
|
168 | 0 | int32_t count = 1; |
169 | 0 | nsAutoString nodeNameAttribute; |
170 | 0 | GetNameAttribute(aNode, nodeNameAttribute); |
171 | 0 | for (const Element* e = aNode->GetPreviousElementSibling(); e; e = e->GetPreviousElementSibling()) { |
172 | 0 | nsAutoString elementNamespaceURI; |
173 | 0 | e->GetNamespaceURI(elementNamespaceURI); |
174 | 0 | nsAutoString elementNameAttribute; |
175 | 0 | GetNameAttribute(e, elementNameAttribute); |
176 | 0 | if (e->LocalName().Equals(nodeLocalName) && elementNamespaceURI.Equals(nodeNamespaceURI) && |
177 | 0 | (nodeNameAttribute.IsEmpty() || elementNameAttribute.Equals(nodeNameAttribute))) { |
178 | 0 | ++count; |
179 | 0 | } |
180 | 0 | } |
181 | 0 |
|
182 | 0 | nsAutoString namePart; |
183 | 0 | nsAutoString countPart; |
184 | 0 | if (!nodeNameAttribute.IsEmpty()) { |
185 | 0 | nsAutoString quotedArgument; |
186 | 0 | QuoteArgument(nodeNameAttribute, quotedArgument); |
187 | 0 | namePart.Assign(NS_LITERAL_STRING("[@name=") + quotedArgument + NS_LITERAL_STRING("]")); |
188 | 0 | } |
189 | 0 | if (count != 1) { |
190 | 0 | countPart.AssignLiteral(u"["); |
191 | 0 | countPart.AppendInt(count); |
192 | 0 | countPart.AppendLiteral(u"]"); |
193 | 0 | } |
194 | 0 | Generate(aNode->GetParentNode(), aResult); |
195 | 0 | aResult.Append(NS_LITERAL_STRING("/") + tag + namePart + countPart); |
196 | 0 | } |