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/. */
7 #include "XPathGenerator.h"
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
18 bool IsNonWordCharacter(const char16_t
& aChar
) {
19 if (((char16_t('A') <= aChar
) && (aChar
<= char16_t('Z'))) ||
20 ((char16_t('a') <= aChar
) && (aChar
<= char16_t('z'))) ||
21 ((char16_t('0') <= aChar
) && (aChar
<= char16_t('9'))) ||
22 (aChar
== char16_t('_'))) {
30 * Check whether a string contains a non-word character.
32 bool ContainNonWordCharacter(const nsAString
& aStr
) {
33 const char16_t
* cur
= aStr
.BeginReading();
34 const char16_t
* end
= aStr
.EndReading();
35 for (; cur
< end
; ++cur
) {
36 if (IsNonWordCharacter(*cur
)) {
44 * Get the prefix according to the given namespace and assign the result to
47 void GetPrefix(const nsINode
* aNode
, nsAString
& aResult
) {
48 if (aNode
->IsXULElement()) {
49 aResult
.AssignLiteral(u
"xul");
50 } else if (aNode
->IsHTMLElement()) {
51 aResult
.AssignLiteral(u
"xhtml");
55 void GetNameAttribute(const nsINode
* aNode
, nsAString
& aResult
) {
56 if (aNode
->HasName()) {
57 const Element
* elem
= aNode
->AsElement();
58 elem
->GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, aResult
);
63 * Put all sequences of ' in a string in between '," and ",' . And then put
64 * the result string in between concat(' and ').
66 * For example, a string 'a'' will return result concat('',"'",'a',"''",'')
68 void GenerateConcatExpression(const nsAString
& aStr
, nsAString
& aResult
) {
69 const char16_t
* cur
= aStr
.BeginReading();
70 const char16_t
* end
= aStr
.EndReading();
72 // Put all sequences of ' in between '," and ",'
74 const char16_t
* nonQuoteBeginPtr
= nullptr;
75 const char16_t
* quoteBeginPtr
= nullptr;
76 for (; cur
< end
; ++cur
) {
77 if (char16_t('\'') == *cur
) {
78 if (nonQuoteBeginPtr
) {
79 result
.Append(nonQuoteBeginPtr
, cur
- nonQuoteBeginPtr
);
80 nonQuoteBeginPtr
= nullptr;
83 result
.AppendLiteral(u
"\',\"");
87 if (!nonQuoteBeginPtr
) {
88 nonQuoteBeginPtr
= cur
;
91 result
.Append(quoteBeginPtr
, cur
- quoteBeginPtr
);
92 result
.AppendLiteral(u
"\",\'");
93 quoteBeginPtr
= nullptr;
99 result
.Append(quoteBeginPtr
, cur
- quoteBeginPtr
);
100 result
.AppendLiteral(u
"\",\'");
101 } else if (nonQuoteBeginPtr
) {
102 result
.Append(nonQuoteBeginPtr
, cur
- nonQuoteBeginPtr
);
105 // Prepend concat(' and append ').
106 aResult
.Assign(NS_LITERAL_STRING("concat(\'") + result
+
107 NS_LITERAL_STRING("\')"));
110 void XPathGenerator::QuoteArgument(const nsAString
& aArg
, nsAString
& aResult
) {
111 if (!aArg
.Contains('\'')) {
112 aResult
.Assign(NS_LITERAL_STRING("\'") + aArg
+ NS_LITERAL_STRING("\'"));
113 } else if (!aArg
.Contains('\"')) {
114 aResult
.Assign(NS_LITERAL_STRING("\"") + aArg
+ NS_LITERAL_STRING("\""));
116 GenerateConcatExpression(aArg
, aResult
);
120 void XPathGenerator::EscapeName(const nsAString
& aName
, nsAString
& aResult
) {
121 if (ContainNonWordCharacter(aName
)) {
122 nsAutoString quotedArg
;
123 QuoteArgument(aName
, quotedArg
);
124 aResult
.Assign(NS_LITERAL_STRING("*[local-name()=") + quotedArg
+
125 NS_LITERAL_STRING("]"));
127 aResult
.Assign(aName
);
131 void XPathGenerator::Generate(const nsINode
* aNode
, nsAString
& aResult
) {
132 if (!aNode
->GetParentNode()) {
137 nsAutoString nodeNamespaceURI
;
138 aNode
->GetNamespaceURI(nodeNamespaceURI
);
139 const nsString
& nodeLocalName
= aNode
->LocalName();
143 nsAutoString nodeEscapeName
;
144 GetPrefix(aNode
, prefix
);
145 EscapeName(nodeLocalName
, nodeEscapeName
);
146 if (prefix
.IsEmpty()) {
147 tag
.Assign(nodeEscapeName
);
149 tag
.Assign(prefix
+ NS_LITERAL_STRING(":") + nodeEscapeName
);
152 if (aNode
->HasID()) {
153 // this must be an element
154 const Element
* elem
= aNode
->AsElement();
156 nsAutoString quotedArgument
;
158 QuoteArgument(elemId
, quotedArgument
);
159 aResult
.Assign(NS_LITERAL_STRING("//") + tag
+ NS_LITERAL_STRING("[@id=") +
160 quotedArgument
+ NS_LITERAL_STRING("]"));
165 nsAutoString nodeNameAttribute
;
166 GetNameAttribute(aNode
, nodeNameAttribute
);
167 for (const Element
* e
= aNode
->GetPreviousElementSibling(); e
;
168 e
= e
->GetPreviousElementSibling()) {
169 nsAutoString elementNamespaceURI
;
170 e
->GetNamespaceURI(elementNamespaceURI
);
171 nsAutoString elementNameAttribute
;
172 GetNameAttribute(e
, elementNameAttribute
);
173 if (e
->LocalName().Equals(nodeLocalName
) &&
174 elementNamespaceURI
.Equals(nodeNamespaceURI
) &&
175 (nodeNameAttribute
.IsEmpty() ||
176 elementNameAttribute
.Equals(nodeNameAttribute
))) {
181 nsAutoString namePart
;
182 nsAutoString countPart
;
183 if (!nodeNameAttribute
.IsEmpty()) {
184 nsAutoString quotedArgument
;
185 QuoteArgument(nodeNameAttribute
, quotedArgument
);
186 namePart
.Assign(NS_LITERAL_STRING("[@name=") + quotedArgument
+
187 NS_LITERAL_STRING("]"));
190 countPart
.AssignLiteral(u
"[");
191 countPart
.AppendInt(count
);
192 countPart
.AppendLiteral(u
"]");
194 Generate(aNode
->GetParentNode(), aResult
);
195 aResult
.Append(NS_LITERAL_STRING("/") + tag
+ namePart
+ countPart
);