Line data Source code
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 0 : bool IsNonWordCharacter(const char16_t& aChar)
18 : {
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 : } else {
25 0 : return true;
26 : }
27 : }
28 :
29 : /**
30 : * Check whether a string contains a non-word character.
31 : * */
32 0 : bool ContainNonWordCharacter(const nsAString& aStr)
33 : {
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 : }
40 : }
41 0 : return false;
42 : }
43 :
44 : /**
45 : * Get the prefix according to the given namespace and assign the result to aResult.
46 : * */
47 0 : void GetPrefix(const nsINode* aNode, nsAString& aResult)
48 : {
49 0 : if (aNode->IsXULElement()) {
50 0 : aResult.Assign(NS_LITERAL_STRING("xul"));
51 0 : } else if (aNode->IsHTMLElement()) {
52 0 : aResult.Assign(NS_LITERAL_STRING("xhtml"));
53 : }
54 0 : }
55 :
56 0 : void GetNameAttribute(const nsINode* aNode, nsAString& aResult)
57 : {
58 0 : if (aNode->HasName()) {
59 0 : const Element* elem = aNode->AsElement();
60 0 : elem->GetAttr(kNameSpaceID_None, nsGkAtoms::name, aResult);
61 : }
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 0 : void GenerateConcatExpression(const nsAString& aStr, nsAString& aResult)
71 : {
72 0 : const char16_t* cur = aStr.BeginReading();
73 0 : const char16_t* end = aStr.EndReading();
74 :
75 : // 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 : }
85 0 : if (!quoteBeginPtr) {
86 0 : result.Append(NS_LITERAL_STRING("\',\""));
87 0 : quoteBeginPtr = cur;
88 : }
89 : } else {
90 0 : if (!nonQuoteBeginPtr) {
91 0 : nonQuoteBeginPtr = cur;
92 : }
93 0 : if (quoteBeginPtr) {
94 0 : result.Append(quoteBeginPtr, cur - quoteBeginPtr);
95 0 : result.Append(NS_LITERAL_STRING("\",\'"));
96 0 : quoteBeginPtr = nullptr;
97 : }
98 : }
99 : }
100 :
101 0 : if (quoteBeginPtr) {
102 0 : result.Append(quoteBeginPtr, cur - quoteBeginPtr);
103 0 : result.Append(NS_LITERAL_STRING("\",\'"));
104 0 : } else if (nonQuoteBeginPtr) {
105 0 : result.Append(nonQuoteBeginPtr, cur - nonQuoteBeginPtr);
106 : }
107 :
108 : // Prepend concat(' and append ').
109 0 : aResult.Assign(NS_LITERAL_STRING("concat(\'") + result + NS_LITERAL_STRING("\')"));
110 0 : }
111 :
112 0 : void XPathGenerator::QuoteArgument(const nsAString& aArg, nsAString& aResult)
113 : {
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 : } else {
119 0 : GenerateConcatExpression(aArg, aResult);
120 : }
121 0 : }
122 :
123 0 : void XPathGenerator::EscapeName(const nsAString& aName, nsAString& aResult)
124 : {
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 : } else {
130 0 : aResult.Assign(aName);
131 : }
132 0 : }
133 :
134 0 : void XPathGenerator::Generate(const nsINode* aNode, nsAString& aResult)
135 : {
136 0 : if (!aNode->GetParentNode()) {
137 0 : aResult.Truncate();
138 0 : return;
139 : }
140 :
141 0 : nsAutoString nodeNamespaceURI;
142 0 : aNode->GetNamespaceURI(nodeNamespaceURI);
143 0 : const nsString& nodeLocalName = aNode->LocalName();
144 :
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 : } else {
153 0 : tag.Assign(prefix + NS_LITERAL_STRING(":") + nodeEscapeName);
154 : }
155 :
156 0 : if (aNode->HasID()) {
157 : // 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 : }
167 :
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 : }
180 : }
181 :
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 : }
189 0 : if (count != 1) {
190 0 : countPart.Assign(NS_LITERAL_STRING("["));
191 0 : countPart.AppendInt(count);
192 0 : countPart.Append(NS_LITERAL_STRING("]"));
193 : }
194 0 : Generate(aNode->GetParentNode(), aResult);
195 0 : aResult.Append(NS_LITERAL_STRING("/") + tag + namePart + countPart);
196 : }
|