Coverage Report

Created: 2018-09-25 14:53

/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
}