1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/FloatingPoint.h"
10 #include "txNodeSet.h"
11 #include "nsGkAtoms.h"
12 #include "txIXPathContext.h"
13 #include "nsWhitespaceTokenizer.h"
14 #include "txXPathTreeWalker.h"
16 #include "txStringUtils.h"
17 #include "txXMLUtils.h"
19 using namespace mozilla
;
21 struct txCoreFunctionDescriptor
{
22 const int8_t mMinParams
;
23 const int8_t mMaxParams
;
24 const Expr::ResultType mReturnType
;
25 const nsStaticAtom
* const mName
;
28 // This must be ordered in the same order as txCoreFunctionCall::eType.
29 // If you change one, change the other.
30 static const txCoreFunctionDescriptor descriptTable
[] = {
31 {1, 1, Expr::NUMBER_RESULT
, nsGkAtoms::count
}, // COUNT
32 {1, 1, Expr::NODESET_RESULT
, nsGkAtoms::id
}, // ID
33 {0, 0, Expr::NUMBER_RESULT
, nsGkAtoms::last
}, // LAST
34 {0, 1, Expr::STRING_RESULT
, nsGkAtoms::localName
}, // LOCAL_NAME
35 {0, 1, Expr::STRING_RESULT
, nsGkAtoms::namespaceUri
}, // NAMESPACE_URI
36 {0, 1, Expr::STRING_RESULT
, nsGkAtoms::name
}, // NAME
37 {0, 0, Expr::NUMBER_RESULT
, nsGkAtoms::position
}, // POSITION
39 {2, -1, Expr::STRING_RESULT
, nsGkAtoms::concat
}, // CONCAT
40 {2, 2, Expr::BOOLEAN_RESULT
, nsGkAtoms::contains
}, // CONTAINS
41 {0, 1, Expr::STRING_RESULT
, nsGkAtoms::normalizeSpace
}, // NORMALIZE_SPACE
42 {2, 2, Expr::BOOLEAN_RESULT
, nsGkAtoms::startsWith
}, // STARTS_WITH
43 {0, 1, Expr::STRING_RESULT
, nsGkAtoms::string
}, // STRING
44 {0, 1, Expr::NUMBER_RESULT
, nsGkAtoms::stringLength
}, // STRING_LENGTH
45 {2, 3, Expr::STRING_RESULT
, nsGkAtoms::substring
}, // SUBSTRING
46 {2, 2, Expr::STRING_RESULT
, nsGkAtoms::substringAfter
}, // SUBSTRING_AFTER
47 {2, 2, Expr::STRING_RESULT
,
48 nsGkAtoms::substringBefore
}, // SUBSTRING_BEFORE
49 {3, 3, Expr::STRING_RESULT
, nsGkAtoms::translate
}, // TRANSLATE
51 {0, 1, Expr::NUMBER_RESULT
, nsGkAtoms::number
}, // NUMBER
52 {1, 1, Expr::NUMBER_RESULT
, nsGkAtoms::round
}, // ROUND
53 {1, 1, Expr::NUMBER_RESULT
, nsGkAtoms::floor
}, // FLOOR
54 {1, 1, Expr::NUMBER_RESULT
, nsGkAtoms::ceiling
}, // CEILING
55 {1, 1, Expr::NUMBER_RESULT
, nsGkAtoms::sum
}, // SUM
57 {1, 1, Expr::BOOLEAN_RESULT
, nsGkAtoms::boolean
}, // BOOLEAN
58 {0, 0, Expr::BOOLEAN_RESULT
, nsGkAtoms::_false
}, // _FALSE
59 {1, 1, Expr::BOOLEAN_RESULT
, nsGkAtoms::lang
}, // LANG
60 {1, 1, Expr::BOOLEAN_RESULT
, nsGkAtoms::_not
}, // _NOT
61 {0, 0, Expr::BOOLEAN_RESULT
, nsGkAtoms::_true
} // _TRUE
65 * Evaluates this Expr based on the given context node and processor state
66 * @param context the context node for evaluation of this Expr
67 * @param ps the ContextState containing the stack information needed
69 * @return the result of the evaluation
71 nsresult
txCoreFunctionCall::evaluate(txIEvalContext
* aContext
,
72 txAExprResult
** aResult
) {
75 if (!requireParams(descriptTable
[mType
].mMinParams
,
76 descriptTable
[mType
].mMaxParams
, aContext
)) {
77 return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT
;
83 RefPtr
<txNodeSet
> nodes
;
84 rv
= evaluateToNodeSet(mParams
[0], aContext
, getter_AddRefs(nodes
));
85 NS_ENSURE_SUCCESS(rv
, rv
);
87 return aContext
->recycler()->getNumberResult(nodes
->size(), aResult
);
90 RefPtr
<txAExprResult
> exprResult
;
91 rv
= mParams
[0]->evaluate(aContext
, getter_AddRefs(exprResult
));
92 NS_ENSURE_SUCCESS(rv
, rv
);
94 RefPtr
<txNodeSet
> resultSet
;
95 rv
= aContext
->recycler()->getNodeSet(getter_AddRefs(resultSet
));
96 NS_ENSURE_SUCCESS(rv
, rv
);
98 txXPathTreeWalker
walker(aContext
->getContextNode());
100 if (exprResult
->getResultType() == txAExprResult::NODESET
) {
102 static_cast<txNodeSet
*>(static_cast<txAExprResult
*>(exprResult
));
104 for (i
= 0; i
< nodes
->size(); ++i
) {
106 txXPathNodeUtils::appendNodeValue(nodes
->get(i
), idList
);
107 nsWhitespaceTokenizer
tokenizer(idList
);
108 while (tokenizer
.hasMoreTokens()) {
109 if (walker
.moveToElementById(tokenizer
.nextToken())) {
110 resultSet
->add(walker
.getCurrentPosition());
116 exprResult
->stringValue(idList
);
117 nsWhitespaceTokenizer
tokenizer(idList
);
118 while (tokenizer
.hasMoreTokens()) {
119 if (walker
.moveToElementById(tokenizer
.nextToken())) {
120 resultSet
->add(walker
.getCurrentPosition());
125 *aResult
= resultSet
;
131 return aContext
->recycler()->getNumberResult(aContext
->size(), aResult
);
135 case NAMESPACE_URI
: {
136 // Check for optional arg
137 RefPtr
<txNodeSet
> nodes
;
138 if (!mParams
.IsEmpty()) {
139 rv
= evaluateToNodeSet(mParams
[0], aContext
, getter_AddRefs(nodes
));
140 NS_ENSURE_SUCCESS(rv
, rv
);
142 if (nodes
->isEmpty()) {
143 aContext
->recycler()->getEmptyStringResult(aResult
);
149 const txXPathNode
& node
=
150 nodes
? nodes
->get(0) : aContext
->getContextNode();
153 StringResult
* strRes
= nullptr;
154 rv
= aContext
->recycler()->getStringResult(&strRes
);
155 NS_ENSURE_SUCCESS(rv
, rv
);
158 txXPathNodeUtils::getLocalName(node
, strRes
->mValue
);
162 case NAMESPACE_URI
: {
163 StringResult
* strRes
= nullptr;
164 rv
= aContext
->recycler()->getStringResult(&strRes
);
165 NS_ENSURE_SUCCESS(rv
, rv
);
168 txXPathNodeUtils::getNamespaceURI(node
, strRes
->mValue
);
173 // XXX Namespace: namespaces have a name
174 if (txXPathNodeUtils::isAttribute(node
) ||
175 txXPathNodeUtils::isElement(node
) ||
176 txXPathNodeUtils::isProcessingInstruction(node
)) {
177 StringResult
* strRes
= nullptr;
178 rv
= aContext
->recycler()->getStringResult(&strRes
);
179 NS_ENSURE_SUCCESS(rv
, rv
);
182 txXPathNodeUtils::getNodeName(node
, strRes
->mValue
);
184 aContext
->recycler()->getEmptyStringResult(aResult
);
190 MOZ_CRASH("Unexpected mType?!");
193 MOZ_CRASH("Inner mType switch should have returned!");
196 return aContext
->recycler()->getNumberResult(aContext
->position(),
203 RefPtr
<StringResult
> strRes
;
204 rv
= aContext
->recycler()->getStringResult(getter_AddRefs(strRes
));
205 NS_ENSURE_SUCCESS(rv
, rv
);
207 uint32_t i
, len
= mParams
.Length();
208 for (i
= 0; i
< len
; ++i
) {
209 rv
= mParams
[i
]->evaluateToString(aContext
, strRes
->mValue
);
210 NS_ENSURE_SUCCESS(rv
, rv
);
213 NS_ADDREF(*aResult
= strRes
);
219 rv
= mParams
[1]->evaluateToString(aContext
, arg2
);
220 NS_ENSURE_SUCCESS(rv
, rv
);
222 if (arg2
.IsEmpty()) {
223 aContext
->recycler()->getBoolResult(true, aResult
);
226 rv
= mParams
[0]->evaluateToString(aContext
, arg1
);
227 NS_ENSURE_SUCCESS(rv
, rv
);
229 aContext
->recycler()->getBoolResult(FindInReadable(arg2
, arg1
),
235 case NORMALIZE_SPACE
: {
236 nsAutoString resultStr
;
237 if (!mParams
.IsEmpty()) {
238 rv
= mParams
[0]->evaluateToString(aContext
, resultStr
);
239 NS_ENSURE_SUCCESS(rv
, rv
);
241 txXPathNodeUtils::appendNodeValue(aContext
->getContextNode(),
245 RefPtr
<StringResult
> strRes
;
246 rv
= aContext
->recycler()->getStringResult(getter_AddRefs(strRes
));
247 NS_ENSURE_SUCCESS(rv
, rv
);
249 bool addSpace
= false;
251 strRes
->mValue
.SetCapacity(resultStr
.Length());
254 for (src
= 0; src
< resultStr
.Length(); src
++) {
255 c
= resultStr
.CharAt(src
);
256 if (XMLUtils::isWhitespace(c
)) {
259 if (addSpace
&& !first
) strRes
->mValue
.Append(char16_t(' '));
261 strRes
->mValue
.Append(c
);
273 rv
= mParams
[1]->evaluateToString(aContext
, arg2
);
274 NS_ENSURE_SUCCESS(rv
, rv
);
277 if (arg2
.IsEmpty()) {
281 rv
= mParams
[0]->evaluateToString(aContext
, arg1
);
282 NS_ENSURE_SUCCESS(rv
, rv
);
284 result
= StringBeginsWith(arg1
, arg2
);
287 aContext
->recycler()->getBoolResult(result
, aResult
);
292 RefPtr
<StringResult
> strRes
;
293 rv
= aContext
->recycler()->getStringResult(getter_AddRefs(strRes
));
294 NS_ENSURE_SUCCESS(rv
, rv
);
296 if (!mParams
.IsEmpty()) {
297 rv
= mParams
[0]->evaluateToString(aContext
, strRes
->mValue
);
298 NS_ENSURE_SUCCESS(rv
, rv
);
300 txXPathNodeUtils::appendNodeValue(aContext
->getContextNode(),
304 NS_ADDREF(*aResult
= strRes
);
308 case STRING_LENGTH
: {
309 nsAutoString resultStr
;
310 if (!mParams
.IsEmpty()) {
311 rv
= mParams
[0]->evaluateToString(aContext
, resultStr
);
312 NS_ENSURE_SUCCESS(rv
, rv
);
314 txXPathNodeUtils::appendNodeValue(aContext
->getContextNode(),
317 rv
= aContext
->recycler()->getNumberResult(resultStr
.Length(), aResult
);
318 NS_ENSURE_SUCCESS(rv
, rv
);
324 rv
= mParams
[0]->evaluateToString(aContext
, src
);
325 NS_ENSURE_SUCCESS(rv
, rv
);
328 rv
= evaluateToNumber(mParams
[1], aContext
, &start
);
329 NS_ENSURE_SUCCESS(rv
, rv
);
331 // check for NaN or +/-Inf
332 if (std::isnan(start
) || std::isinf(start
) ||
333 start
>= src
.Length() + 0.5) {
334 aContext
->recycler()->getEmptyStringResult(aResult
);
339 start
= floor(start
+ 0.5) - 1;
342 if (mParams
.Length() == 3) {
343 rv
= evaluateToNumber(mParams
[2], aContext
, &end
);
344 NS_ENSURE_SUCCESS(rv
, rv
);
347 if (std::isnan(end
) || end
< 0) {
348 aContext
->recycler()->getEmptyStringResult(aResult
);
353 if (end
> src
.Length())
356 end
= floor(end
+ 0.5);
361 if (start
< 0) start
= 0;
364 aContext
->recycler()->getEmptyStringResult(aResult
);
369 return aContext
->recycler()->getStringResult(
370 Substring(src
, (uint32_t)start
, (uint32_t)(end
- start
)), aResult
);
372 case SUBSTRING_AFTER
: {
374 rv
= mParams
[0]->evaluateToString(aContext
, arg1
);
375 NS_ENSURE_SUCCESS(rv
, rv
);
378 rv
= mParams
[1]->evaluateToString(aContext
, arg2
);
379 NS_ENSURE_SUCCESS(rv
, rv
);
381 if (arg2
.IsEmpty()) {
382 return aContext
->recycler()->getStringResult(arg1
, aResult
);
385 int32_t idx
= arg1
.Find(arg2
);
386 if (idx
== kNotFound
) {
387 aContext
->recycler()->getEmptyStringResult(aResult
);
392 const nsAString
& result
= Substring(arg1
, idx
+ arg2
.Length());
393 return aContext
->recycler()->getStringResult(result
, aResult
);
395 case SUBSTRING_BEFORE
: {
397 rv
= mParams
[1]->evaluateToString(aContext
, arg2
);
398 NS_ENSURE_SUCCESS(rv
, rv
);
400 if (arg2
.IsEmpty()) {
401 aContext
->recycler()->getEmptyStringResult(aResult
);
407 rv
= mParams
[0]->evaluateToString(aContext
, arg1
);
408 NS_ENSURE_SUCCESS(rv
, rv
);
410 int32_t idx
= arg1
.Find(arg2
);
411 if (idx
== kNotFound
) {
412 aContext
->recycler()->getEmptyStringResult(aResult
);
417 return aContext
->recycler()->getStringResult(StringHead(arg1
, idx
),
422 rv
= mParams
[0]->evaluateToString(aContext
, src
);
423 NS_ENSURE_SUCCESS(rv
, rv
);
426 aContext
->recycler()->getEmptyStringResult(aResult
);
431 RefPtr
<StringResult
> strRes
;
432 rv
= aContext
->recycler()->getStringResult(getter_AddRefs(strRes
));
433 NS_ENSURE_SUCCESS(rv
, rv
);
435 strRes
->mValue
.SetCapacity(src
.Length());
437 nsAutoString oldChars
, newChars
;
438 rv
= mParams
[1]->evaluateToString(aContext
, oldChars
);
439 NS_ENSURE_SUCCESS(rv
, rv
);
441 rv
= mParams
[2]->evaluateToString(aContext
, newChars
);
442 NS_ENSURE_SUCCESS(rv
, rv
);
445 int32_t newCharsLength
= (int32_t)newChars
.Length();
446 for (i
= 0; i
< src
.Length(); i
++) {
447 int32_t idx
= oldChars
.FindChar(src
.CharAt(i
));
448 if (idx
!= kNotFound
) {
449 if (idx
< newCharsLength
)
450 strRes
->mValue
.Append(newChars
.CharAt((uint32_t)idx
));
452 strRes
->mValue
.Append(src
.CharAt(i
));
456 NS_ADDREF(*aResult
= strRes
);
465 if (!mParams
.IsEmpty()) {
466 rv
= evaluateToNumber(mParams
[0], aContext
, &res
);
467 NS_ENSURE_SUCCESS(rv
, rv
);
469 nsAutoString resultStr
;
470 txXPathNodeUtils::appendNodeValue(aContext
->getContextNode(),
472 res
= txDouble::toDouble(resultStr
);
474 return aContext
->recycler()->getNumberResult(res
, aResult
);
478 rv
= evaluateToNumber(mParams
[0], aContext
, &dbl
);
479 NS_ENSURE_SUCCESS(rv
, rv
);
481 if (std::isfinite(dbl
)) {
482 if (mozilla::IsNegative(dbl
) && dbl
>= -0.5) {
485 dbl
= floor(dbl
+ 0.5);
489 return aContext
->recycler()->getNumberResult(dbl
, aResult
);
493 rv
= evaluateToNumber(mParams
[0], aContext
, &dbl
);
494 NS_ENSURE_SUCCESS(rv
, rv
);
496 if (std::isfinite(dbl
) && !mozilla::IsNegativeZero(dbl
)) dbl
= floor(dbl
);
498 return aContext
->recycler()->getNumberResult(dbl
, aResult
);
502 rv
= evaluateToNumber(mParams
[0], aContext
, &dbl
);
503 NS_ENSURE_SUCCESS(rv
, rv
);
505 if (std::isfinite(dbl
)) {
506 if (mozilla::IsNegative(dbl
) && dbl
> -1)
512 return aContext
->recycler()->getNumberResult(dbl
, aResult
);
515 RefPtr
<txNodeSet
> nodes
;
517 evaluateToNodeSet(mParams
[0], aContext
, getter_AddRefs(nodes
));
518 NS_ENSURE_SUCCESS(rv
, rv
);
522 for (i
= 0; i
< nodes
->size(); ++i
) {
523 nsAutoString resultStr
;
524 txXPathNodeUtils::appendNodeValue(nodes
->get(i
), resultStr
);
525 res
+= txDouble::toDouble(resultStr
);
527 return aContext
->recycler()->getNumberResult(res
, aResult
);
534 nsresult rv
= mParams
[0]->evaluateToBool(aContext
, result
);
535 NS_ENSURE_SUCCESS(rv
, rv
);
537 aContext
->recycler()->getBoolResult(result
, aResult
);
542 aContext
->recycler()->getBoolResult(false, aResult
);
547 txXPathTreeWalker
walker(aContext
->getContextNode());
552 found
= walker
.getAttr(nsGkAtoms::lang
, kNameSpaceID_XML
, lang
);
553 } while (!found
&& walker
.moveToParent());
556 aContext
->recycler()->getBoolResult(false, aResult
);
562 rv
= mParams
[0]->evaluateToString(aContext
, arg
);
563 NS_ENSURE_SUCCESS(rv
, rv
);
566 StringBeginsWith(lang
, arg
, nsCaseInsensitiveStringComparator
) &&
567 (lang
.Length() == arg
.Length() || lang
.CharAt(arg
.Length()) == '-');
569 aContext
->recycler()->getBoolResult(result
, aResult
);
575 rv
= mParams
[0]->evaluateToBool(aContext
, result
);
576 NS_ENSURE_SUCCESS(rv
, rv
);
578 aContext
->recycler()->getBoolResult(!result
, aResult
);
583 aContext
->recycler()->getBoolResult(true, aResult
);
589 aContext
->receiveError(u
"Internal error"_ns
, NS_ERROR_UNEXPECTED
);
590 return NS_ERROR_UNEXPECTED
;
593 Expr::ResultType
txCoreFunctionCall::getReturnType() {
594 return descriptTable
[mType
].mReturnType
;
597 bool txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext
) {
604 case SUBSTRING_AFTER
:
605 case SUBSTRING_BEFORE
:
615 return argsSensitiveTo(aContext
);
618 return (aContext
& NODE_CONTEXT
) || argsSensitiveTo(aContext
);
621 return !!(aContext
& SIZE_CONTEXT
);
626 case NORMALIZE_SPACE
:
630 if (mParams
.IsEmpty()) {
631 return !!(aContext
& NODE_CONTEXT
);
633 return argsSensitiveTo(aContext
);
636 return !!(aContext
& POSITION_CONTEXT
);
639 return (aContext
& NODE_CONTEXT
) || argsSensitiveTo(aContext
);
643 MOZ_ASSERT_UNREACHABLE("how'd we get here?");
648 bool txCoreFunctionCall::getTypeFromAtom(nsAtom
* aName
, eType
& aType
) {
650 for (i
= 0; i
< std::size(descriptTable
); ++i
) {
651 if (aName
== descriptTable
[i
].mName
) {
652 aType
= static_cast<eType
>(i
);
662 void txCoreFunctionCall::appendName(nsAString
& aDest
) {
663 aDest
.Append(descriptTable
[mType
].mName
->GetUTF16String());