Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / XPathGenerator.cpp
blob1ff4afd1c1c4944113adaeedae623f4ecc82d08d
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"
9 #include "nsGkAtoms.h"
10 #include "Element.h"
11 #include "nsTArray.h"
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
16 * underscore.
17 * */
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('_'))) {
23 return false;
24 } else {
25 return true;
29 /**
30 * Check whether a string contains a non-word character.
31 * */
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)) {
37 return true;
40 return false;
43 /**
44 * Get the prefix according to the given namespace and assign the result to
45 * aResult.
46 * */
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);
62 /**
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',"''",'')
67 * */
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 ",'
73 nsAutoString result;
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;
82 if (!quoteBeginPtr) {
83 result.AppendLiteral(u"\',\"");
84 quoteBeginPtr = cur;
86 } else {
87 if (!nonQuoteBeginPtr) {
88 nonQuoteBeginPtr = cur;
90 if (quoteBeginPtr) {
91 result.Append(quoteBeginPtr, cur - quoteBeginPtr);
92 result.AppendLiteral(u"\",\'");
93 quoteBeginPtr = nullptr;
98 if (quoteBeginPtr) {
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("\""));
115 } else {
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("]"));
126 } else {
127 aResult.Assign(aName);
131 void XPathGenerator::Generate(const nsINode* aNode, nsAString& aResult) {
132 if (!aNode->GetParentNode()) {
133 aResult.Truncate();
134 return;
137 nsAutoString nodeNamespaceURI;
138 aNode->GetNamespaceURI(nodeNamespaceURI);
139 const nsString& nodeLocalName = aNode->LocalName();
141 nsAutoString prefix;
142 nsAutoString tag;
143 nsAutoString nodeEscapeName;
144 GetPrefix(aNode, prefix);
145 EscapeName(nodeLocalName, nodeEscapeName);
146 if (prefix.IsEmpty()) {
147 tag.Assign(nodeEscapeName);
148 } else {
149 tag.Assign(prefix + NS_LITERAL_STRING(":") + nodeEscapeName);
152 if (aNode->HasID()) {
153 // this must be an element
154 const Element* elem = aNode->AsElement();
155 nsAutoString elemId;
156 nsAutoString quotedArgument;
157 elem->GetId(elemId);
158 QuoteArgument(elemId, quotedArgument);
159 aResult.Assign(NS_LITERAL_STRING("//") + tag + NS_LITERAL_STRING("[@id=") +
160 quotedArgument + NS_LITERAL_STRING("]"));
161 return;
164 int32_t count = 1;
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))) {
177 ++count;
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("]"));
189 if (count != 1) {
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);