dom/xslt/xpath/txCoreFunctionCall.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/ArrayUtils.h"
michael@0 7 #include "mozilla/FloatingPoint.h"
michael@0 8
michael@0 9 #include "txExpr.h"
michael@0 10 #include "nsAutoPtr.h"
michael@0 11 #include "txNodeSet.h"
michael@0 12 #include "nsGkAtoms.h"
michael@0 13 #include "txIXPathContext.h"
michael@0 14 #include "nsWhitespaceTokenizer.h"
michael@0 15 #include "txXPathTreeWalker.h"
michael@0 16 #include <math.h>
michael@0 17 #include "txStringUtils.h"
michael@0 18 #include "txXMLUtils.h"
michael@0 19
michael@0 20 using namespace mozilla;
michael@0 21
michael@0 22 struct txCoreFunctionDescriptor
michael@0 23 {
michael@0 24 int8_t mMinParams;
michael@0 25 int8_t mMaxParams;
michael@0 26 Expr::ResultType mReturnType;
michael@0 27 nsIAtom** mName;
michael@0 28 };
michael@0 29
michael@0 30 // This must be ordered in the same order as txCoreFunctionCall::eType.
michael@0 31 // If you change one, change the other.
michael@0 32 static const txCoreFunctionDescriptor descriptTable[] =
michael@0 33 {
michael@0 34 { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::count }, // COUNT
michael@0 35 { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::id }, // ID
michael@0 36 { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::last }, // LAST
michael@0 37 { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::localName }, // LOCAL_NAME
michael@0 38 { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::namespaceUri }, // NAMESPACE_URI
michael@0 39 { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::name }, // NAME
michael@0 40 { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::position }, // POSITION
michael@0 41
michael@0 42 { 2, -1, Expr::STRING_RESULT, &nsGkAtoms::concat }, // CONCAT
michael@0 43 { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::contains }, // CONTAINS
michael@0 44 { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::normalizeSpace }, // NORMALIZE_SPACE
michael@0 45 { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::startsWith }, // STARTS_WITH
michael@0 46 { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::string }, // STRING
michael@0 47 { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::stringLength }, // STRING_LENGTH
michael@0 48 { 2, 3, Expr::STRING_RESULT, &nsGkAtoms::substring }, // SUBSTRING
michael@0 49 { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringAfter }, // SUBSTRING_AFTER
michael@0 50 { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringBefore }, // SUBSTRING_BEFORE
michael@0 51 { 3, 3, Expr::STRING_RESULT, &nsGkAtoms::translate }, // TRANSLATE
michael@0 52
michael@0 53 { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::number }, // NUMBER
michael@0 54 { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::round }, // ROUND
michael@0 55 { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::floor }, // FLOOR
michael@0 56 { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::ceiling }, // CEILING
michael@0 57 { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::sum }, // SUM
michael@0 58
michael@0 59 { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::boolean }, // BOOLEAN
michael@0 60 { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_false }, // _FALSE
michael@0 61 { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::lang }, // LANG
michael@0 62 { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::_not }, // _NOT
michael@0 63 { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_true } // _TRUE
michael@0 64 };
michael@0 65
michael@0 66
michael@0 67 /*
michael@0 68 * Evaluates this Expr based on the given context node and processor state
michael@0 69 * @param context the context node for evaluation of this Expr
michael@0 70 * @param ps the ContextState containing the stack information needed
michael@0 71 * for evaluation
michael@0 72 * @return the result of the evaluation
michael@0 73 */
michael@0 74 nsresult
michael@0 75 txCoreFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
michael@0 76 {
michael@0 77 *aResult = nullptr;
michael@0 78
michael@0 79 if (!requireParams(descriptTable[mType].mMinParams,
michael@0 80 descriptTable[mType].mMaxParams,
michael@0 81 aContext)) {
michael@0 82 return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
michael@0 83 }
michael@0 84
michael@0 85 nsresult rv = NS_OK;
michael@0 86 switch (mType) {
michael@0 87 case COUNT:
michael@0 88 {
michael@0 89 nsRefPtr<txNodeSet> nodes;
michael@0 90 rv = evaluateToNodeSet(mParams[0], aContext,
michael@0 91 getter_AddRefs(nodes));
michael@0 92 NS_ENSURE_SUCCESS(rv, rv);
michael@0 93
michael@0 94 return aContext->recycler()->getNumberResult(nodes->size(),
michael@0 95 aResult);
michael@0 96 }
michael@0 97 case ID:
michael@0 98 {
michael@0 99 nsRefPtr<txAExprResult> exprResult;
michael@0 100 rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
michael@0 101 NS_ENSURE_SUCCESS(rv, rv);
michael@0 102
michael@0 103 nsRefPtr<txNodeSet> resultSet;
michael@0 104 rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
michael@0 105 NS_ENSURE_SUCCESS(rv, rv);
michael@0 106
michael@0 107 txXPathTreeWalker walker(aContext->getContextNode());
michael@0 108
michael@0 109 if (exprResult->getResultType() == txAExprResult::NODESET) {
michael@0 110 txNodeSet* nodes = static_cast<txNodeSet*>
michael@0 111 (static_cast<txAExprResult*>
michael@0 112 (exprResult));
michael@0 113 int32_t i;
michael@0 114 for (i = 0; i < nodes->size(); ++i) {
michael@0 115 nsAutoString idList;
michael@0 116 txXPathNodeUtils::appendNodeValue(nodes->get(i), idList);
michael@0 117 nsWhitespaceTokenizer tokenizer(idList);
michael@0 118 while (tokenizer.hasMoreTokens()) {
michael@0 119 if (walker.moveToElementById(tokenizer.nextToken())) {
michael@0 120 resultSet->add(walker.getCurrentPosition());
michael@0 121 }
michael@0 122 }
michael@0 123 }
michael@0 124 }
michael@0 125 else {
michael@0 126 nsAutoString idList;
michael@0 127 exprResult->stringValue(idList);
michael@0 128 nsWhitespaceTokenizer tokenizer(idList);
michael@0 129 while (tokenizer.hasMoreTokens()) {
michael@0 130 if (walker.moveToElementById(tokenizer.nextToken())) {
michael@0 131 resultSet->add(walker.getCurrentPosition());
michael@0 132 }
michael@0 133 }
michael@0 134 }
michael@0 135
michael@0 136 *aResult = resultSet;
michael@0 137 NS_ADDREF(*aResult);
michael@0 138
michael@0 139 return NS_OK;
michael@0 140 }
michael@0 141 case LAST:
michael@0 142 {
michael@0 143 return aContext->recycler()->getNumberResult(aContext->size(),
michael@0 144 aResult);
michael@0 145 }
michael@0 146 case LOCAL_NAME:
michael@0 147 case NAME:
michael@0 148 case NAMESPACE_URI:
michael@0 149 {
michael@0 150 // Check for optional arg
michael@0 151 nsRefPtr<txNodeSet> nodes;
michael@0 152 if (!mParams.IsEmpty()) {
michael@0 153 rv = evaluateToNodeSet(mParams[0], aContext,
michael@0 154 getter_AddRefs(nodes));
michael@0 155 NS_ENSURE_SUCCESS(rv, rv);
michael@0 156
michael@0 157 if (nodes->isEmpty()) {
michael@0 158 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 159
michael@0 160 return NS_OK;
michael@0 161 }
michael@0 162 }
michael@0 163
michael@0 164 const txXPathNode& node = nodes ? nodes->get(0) :
michael@0 165 aContext->getContextNode();
michael@0 166 switch (mType) {
michael@0 167 case LOCAL_NAME:
michael@0 168 {
michael@0 169 StringResult* strRes = nullptr;
michael@0 170 rv = aContext->recycler()->getStringResult(&strRes);
michael@0 171 NS_ENSURE_SUCCESS(rv, rv);
michael@0 172
michael@0 173 *aResult = strRes;
michael@0 174 txXPathNodeUtils::getLocalName(node, strRes->mValue);
michael@0 175
michael@0 176 return NS_OK;
michael@0 177 }
michael@0 178 case NAMESPACE_URI:
michael@0 179 {
michael@0 180 StringResult* strRes = nullptr;
michael@0 181 rv = aContext->recycler()->getStringResult(&strRes);
michael@0 182 NS_ENSURE_SUCCESS(rv, rv);
michael@0 183
michael@0 184 *aResult = strRes;
michael@0 185 txXPathNodeUtils::getNamespaceURI(node, strRes->mValue);
michael@0 186
michael@0 187 return NS_OK;
michael@0 188 }
michael@0 189 case NAME:
michael@0 190 {
michael@0 191 // XXX Namespace: namespaces have a name
michael@0 192 if (txXPathNodeUtils::isAttribute(node) ||
michael@0 193 txXPathNodeUtils::isElement(node) ||
michael@0 194 txXPathNodeUtils::isProcessingInstruction(node)) {
michael@0 195 StringResult* strRes = nullptr;
michael@0 196 rv = aContext->recycler()->getStringResult(&strRes);
michael@0 197 NS_ENSURE_SUCCESS(rv, rv);
michael@0 198
michael@0 199 *aResult = strRes;
michael@0 200 txXPathNodeUtils::getNodeName(node, strRes->mValue);
michael@0 201 }
michael@0 202 else {
michael@0 203 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 204 }
michael@0 205
michael@0 206 return NS_OK;
michael@0 207 }
michael@0 208 default:
michael@0 209 {
michael@0 210 break;
michael@0 211 }
michael@0 212 }
michael@0 213 }
michael@0 214 case POSITION:
michael@0 215 {
michael@0 216 return aContext->recycler()->getNumberResult(aContext->position(),
michael@0 217 aResult);
michael@0 218 }
michael@0 219
michael@0 220 // String functions
michael@0 221
michael@0 222 case CONCAT:
michael@0 223 {
michael@0 224 nsRefPtr<StringResult> strRes;
michael@0 225 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
michael@0 226 NS_ENSURE_SUCCESS(rv, rv);
michael@0 227
michael@0 228 uint32_t i, len = mParams.Length();
michael@0 229 for (i = 0; i < len; ++i) {
michael@0 230 rv = mParams[i]->evaluateToString(aContext, strRes->mValue);
michael@0 231 NS_ENSURE_SUCCESS(rv, rv);
michael@0 232 }
michael@0 233
michael@0 234 NS_ADDREF(*aResult = strRes);
michael@0 235
michael@0 236 return NS_OK;
michael@0 237 }
michael@0 238 case CONTAINS:
michael@0 239 {
michael@0 240 nsAutoString arg2;
michael@0 241 rv = mParams[1]->evaluateToString(aContext, arg2);
michael@0 242 NS_ENSURE_SUCCESS(rv, rv);
michael@0 243
michael@0 244 if (arg2.IsEmpty()) {
michael@0 245 aContext->recycler()->getBoolResult(true, aResult);
michael@0 246 }
michael@0 247 else {
michael@0 248 nsAutoString arg1;
michael@0 249 rv = mParams[0]->evaluateToString(aContext, arg1);
michael@0 250 NS_ENSURE_SUCCESS(rv, rv);
michael@0 251
michael@0 252 aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1),
michael@0 253 aResult);
michael@0 254 }
michael@0 255
michael@0 256 return NS_OK;
michael@0 257 }
michael@0 258 case NORMALIZE_SPACE:
michael@0 259 {
michael@0 260 nsAutoString resultStr;
michael@0 261 if (!mParams.IsEmpty()) {
michael@0 262 rv = mParams[0]->evaluateToString(aContext, resultStr);
michael@0 263 NS_ENSURE_SUCCESS(rv, rv);
michael@0 264 }
michael@0 265 else {
michael@0 266 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
michael@0 267 resultStr);
michael@0 268 }
michael@0 269
michael@0 270 nsRefPtr<StringResult> strRes;
michael@0 271 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
michael@0 272 NS_ENSURE_SUCCESS(rv, rv);
michael@0 273
michael@0 274 bool addSpace = false;
michael@0 275 bool first = true;
michael@0 276 strRes->mValue.SetCapacity(resultStr.Length());
michael@0 277 char16_t c;
michael@0 278 uint32_t src;
michael@0 279 for (src = 0; src < resultStr.Length(); src++) {
michael@0 280 c = resultStr.CharAt(src);
michael@0 281 if (XMLUtils::isWhitespace(c)) {
michael@0 282 addSpace = true;
michael@0 283 }
michael@0 284 else {
michael@0 285 if (addSpace && !first)
michael@0 286 strRes->mValue.Append(char16_t(' '));
michael@0 287
michael@0 288 strRes->mValue.Append(c);
michael@0 289 addSpace = false;
michael@0 290 first = false;
michael@0 291 }
michael@0 292 }
michael@0 293 *aResult = strRes;
michael@0 294 NS_ADDREF(*aResult);
michael@0 295
michael@0 296 return NS_OK;
michael@0 297 }
michael@0 298 case STARTS_WITH:
michael@0 299 {
michael@0 300 nsAutoString arg2;
michael@0 301 rv = mParams[1]->evaluateToString(aContext, arg2);
michael@0 302 NS_ENSURE_SUCCESS(rv, rv);
michael@0 303
michael@0 304 bool result = false;
michael@0 305 if (arg2.IsEmpty()) {
michael@0 306 result = true;
michael@0 307 }
michael@0 308 else {
michael@0 309 nsAutoString arg1;
michael@0 310 rv = mParams[0]->evaluateToString(aContext, arg1);
michael@0 311 NS_ENSURE_SUCCESS(rv, rv);
michael@0 312
michael@0 313 result = StringBeginsWith(arg1, arg2);
michael@0 314 }
michael@0 315
michael@0 316 aContext->recycler()->getBoolResult(result, aResult);
michael@0 317
michael@0 318 return NS_OK;
michael@0 319 }
michael@0 320 case STRING:
michael@0 321 {
michael@0 322 nsRefPtr<StringResult> strRes;
michael@0 323 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
michael@0 324 NS_ENSURE_SUCCESS(rv, rv);
michael@0 325
michael@0 326 if (!mParams.IsEmpty()) {
michael@0 327 rv = mParams[0]->evaluateToString(aContext, strRes->mValue);
michael@0 328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 329 }
michael@0 330 else {
michael@0 331 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
michael@0 332 strRes->mValue);
michael@0 333 }
michael@0 334
michael@0 335 NS_ADDREF(*aResult = strRes);
michael@0 336
michael@0 337 return NS_OK;
michael@0 338 }
michael@0 339 case STRING_LENGTH:
michael@0 340 {
michael@0 341 nsAutoString resultStr;
michael@0 342 if (!mParams.IsEmpty()) {
michael@0 343 rv = mParams[0]->evaluateToString(aContext, resultStr);
michael@0 344 NS_ENSURE_SUCCESS(rv, rv);
michael@0 345 }
michael@0 346 else {
michael@0 347 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
michael@0 348 resultStr);
michael@0 349 }
michael@0 350 rv = aContext->recycler()->getNumberResult(resultStr.Length(),
michael@0 351 aResult);
michael@0 352 NS_ENSURE_SUCCESS(rv, rv);
michael@0 353
michael@0 354 return NS_OK;
michael@0 355 }
michael@0 356 case SUBSTRING:
michael@0 357 {
michael@0 358 nsAutoString src;
michael@0 359 rv = mParams[0]->evaluateToString(aContext, src);
michael@0 360 NS_ENSURE_SUCCESS(rv, rv);
michael@0 361
michael@0 362 double start;
michael@0 363 rv = evaluateToNumber(mParams[1], aContext, &start);
michael@0 364 NS_ENSURE_SUCCESS(rv, rv);
michael@0 365
michael@0 366 // check for NaN or +/-Inf
michael@0 367 if (mozilla::IsNaN(start) ||
michael@0 368 mozilla::IsInfinite(start) ||
michael@0 369 start >= src.Length() + 0.5) {
michael@0 370 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 371
michael@0 372 return NS_OK;
michael@0 373 }
michael@0 374
michael@0 375 start = floor(start + 0.5) - 1;
michael@0 376
michael@0 377 double end;
michael@0 378 if (mParams.Length() == 3) {
michael@0 379 rv = evaluateToNumber(mParams[2], aContext, &end);
michael@0 380 NS_ENSURE_SUCCESS(rv, rv);
michael@0 381
michael@0 382 end += start;
michael@0 383 if (mozilla::IsNaN(end) || end < 0) {
michael@0 384 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 385
michael@0 386 return NS_OK;
michael@0 387 }
michael@0 388
michael@0 389 if (end > src.Length())
michael@0 390 end = src.Length();
michael@0 391 else
michael@0 392 end = floor(end + 0.5);
michael@0 393 }
michael@0 394 else {
michael@0 395 end = src.Length();
michael@0 396 }
michael@0 397
michael@0 398 if (start < 0)
michael@0 399 start = 0;
michael@0 400
michael@0 401 if (start > end) {
michael@0 402 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 403
michael@0 404 return NS_OK;
michael@0 405 }
michael@0 406
michael@0 407 return aContext->recycler()->getStringResult(
michael@0 408 Substring(src, (uint32_t)start, (uint32_t)(end - start)),
michael@0 409 aResult);
michael@0 410 }
michael@0 411 case SUBSTRING_AFTER:
michael@0 412 {
michael@0 413 nsAutoString arg1;
michael@0 414 rv = mParams[0]->evaluateToString(aContext, arg1);
michael@0 415 NS_ENSURE_SUCCESS(rv, rv);
michael@0 416
michael@0 417 nsAutoString arg2;
michael@0 418 rv = mParams[1]->evaluateToString(aContext, arg2);
michael@0 419 NS_ENSURE_SUCCESS(rv, rv);
michael@0 420
michael@0 421 if (arg2.IsEmpty()) {
michael@0 422 return aContext->recycler()->getStringResult(arg1, aResult);
michael@0 423 }
michael@0 424
michael@0 425 int32_t idx = arg1.Find(arg2);
michael@0 426 if (idx == kNotFound) {
michael@0 427 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 428
michael@0 429 return NS_OK;
michael@0 430 }
michael@0 431
michael@0 432 const nsSubstring& result = Substring(arg1, idx + arg2.Length());
michael@0 433 return aContext->recycler()->getStringResult(result, aResult);
michael@0 434 }
michael@0 435 case SUBSTRING_BEFORE:
michael@0 436 {
michael@0 437 nsAutoString arg2;
michael@0 438 rv = mParams[1]->evaluateToString(aContext, arg2);
michael@0 439 NS_ENSURE_SUCCESS(rv, rv);
michael@0 440
michael@0 441 if (arg2.IsEmpty()) {
michael@0 442 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 443
michael@0 444 return NS_OK;
michael@0 445 }
michael@0 446
michael@0 447 nsAutoString arg1;
michael@0 448 rv = mParams[0]->evaluateToString(aContext, arg1);
michael@0 449 NS_ENSURE_SUCCESS(rv, rv);
michael@0 450
michael@0 451 int32_t idx = arg1.Find(arg2);
michael@0 452 if (idx == kNotFound) {
michael@0 453 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 454
michael@0 455 return NS_OK;
michael@0 456 }
michael@0 457
michael@0 458 return aContext->recycler()->getStringResult(StringHead(arg1, idx),
michael@0 459 aResult);
michael@0 460 }
michael@0 461 case TRANSLATE:
michael@0 462 {
michael@0 463 nsAutoString src;
michael@0 464 rv = mParams[0]->evaluateToString(aContext, src);
michael@0 465 NS_ENSURE_SUCCESS(rv, rv);
michael@0 466
michael@0 467 if (src.IsEmpty()) {
michael@0 468 aContext->recycler()->getEmptyStringResult(aResult);
michael@0 469
michael@0 470 return NS_OK;
michael@0 471 }
michael@0 472
michael@0 473 nsRefPtr<StringResult> strRes;
michael@0 474 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
michael@0 475 NS_ENSURE_SUCCESS(rv, rv);
michael@0 476
michael@0 477 strRes->mValue.SetCapacity(src.Length());
michael@0 478
michael@0 479 nsAutoString oldChars, newChars;
michael@0 480 rv = mParams[1]->evaluateToString(aContext, oldChars);
michael@0 481 NS_ENSURE_SUCCESS(rv, rv);
michael@0 482
michael@0 483 rv = mParams[2]->evaluateToString(aContext, newChars);
michael@0 484 NS_ENSURE_SUCCESS(rv, rv);
michael@0 485
michael@0 486 uint32_t i;
michael@0 487 int32_t newCharsLength = (int32_t)newChars.Length();
michael@0 488 for (i = 0; i < src.Length(); i++) {
michael@0 489 int32_t idx = oldChars.FindChar(src.CharAt(i));
michael@0 490 if (idx != kNotFound) {
michael@0 491 if (idx < newCharsLength)
michael@0 492 strRes->mValue.Append(newChars.CharAt((uint32_t)idx));
michael@0 493 }
michael@0 494 else {
michael@0 495 strRes->mValue.Append(src.CharAt(i));
michael@0 496 }
michael@0 497 }
michael@0 498
michael@0 499 NS_ADDREF(*aResult = strRes);
michael@0 500
michael@0 501 return NS_OK;
michael@0 502 }
michael@0 503
michael@0 504 // Number functions
michael@0 505
michael@0 506 case NUMBER:
michael@0 507 {
michael@0 508 double res;
michael@0 509 if (!mParams.IsEmpty()) {
michael@0 510 rv = evaluateToNumber(mParams[0], aContext, &res);
michael@0 511 NS_ENSURE_SUCCESS(rv, rv);
michael@0 512 }
michael@0 513 else {
michael@0 514 nsAutoString resultStr;
michael@0 515 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
michael@0 516 resultStr);
michael@0 517 res = txDouble::toDouble(resultStr);
michael@0 518 }
michael@0 519 return aContext->recycler()->getNumberResult(res, aResult);
michael@0 520 }
michael@0 521 case ROUND:
michael@0 522 {
michael@0 523 double dbl;
michael@0 524 rv = evaluateToNumber(mParams[0], aContext, &dbl);
michael@0 525 NS_ENSURE_SUCCESS(rv, rv);
michael@0 526
michael@0 527 if (mozilla::IsFinite(dbl)) {
michael@0 528 if (mozilla::IsNegative(dbl) && dbl >= -0.5) {
michael@0 529 dbl *= 0;
michael@0 530 }
michael@0 531 else {
michael@0 532 dbl = floor(dbl + 0.5);
michael@0 533 }
michael@0 534 }
michael@0 535
michael@0 536 return aContext->recycler()->getNumberResult(dbl, aResult);
michael@0 537 }
michael@0 538 case FLOOR:
michael@0 539 {
michael@0 540 double dbl;
michael@0 541 rv = evaluateToNumber(mParams[0], aContext, &dbl);
michael@0 542 NS_ENSURE_SUCCESS(rv, rv);
michael@0 543
michael@0 544 if (mozilla::IsFinite(dbl) && !mozilla::IsNegativeZero(dbl))
michael@0 545 dbl = floor(dbl);
michael@0 546
michael@0 547 return aContext->recycler()->getNumberResult(dbl, aResult);
michael@0 548 }
michael@0 549 case CEILING:
michael@0 550 {
michael@0 551 double dbl;
michael@0 552 rv = evaluateToNumber(mParams[0], aContext, &dbl);
michael@0 553 NS_ENSURE_SUCCESS(rv, rv);
michael@0 554
michael@0 555 if (mozilla::IsFinite(dbl)) {
michael@0 556 if (mozilla::IsNegative(dbl) && dbl > -1)
michael@0 557 dbl *= 0;
michael@0 558 else
michael@0 559 dbl = ceil(dbl);
michael@0 560 }
michael@0 561
michael@0 562 return aContext->recycler()->getNumberResult(dbl, aResult);
michael@0 563 }
michael@0 564 case SUM:
michael@0 565 {
michael@0 566 nsRefPtr<txNodeSet> nodes;
michael@0 567 nsresult rv = evaluateToNodeSet(mParams[0], aContext,
michael@0 568 getter_AddRefs(nodes));
michael@0 569 NS_ENSURE_SUCCESS(rv, rv);
michael@0 570
michael@0 571 double res = 0;
michael@0 572 int32_t i;
michael@0 573 for (i = 0; i < nodes->size(); ++i) {
michael@0 574 nsAutoString resultStr;
michael@0 575 txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr);
michael@0 576 res += txDouble::toDouble(resultStr);
michael@0 577 }
michael@0 578 return aContext->recycler()->getNumberResult(res, aResult);
michael@0 579 }
michael@0 580
michael@0 581 // Boolean functions
michael@0 582
michael@0 583 case BOOLEAN:
michael@0 584 {
michael@0 585 bool result;
michael@0 586 nsresult rv = mParams[0]->evaluateToBool(aContext, result);
michael@0 587 NS_ENSURE_SUCCESS(rv, rv);
michael@0 588
michael@0 589 aContext->recycler()->getBoolResult(result, aResult);
michael@0 590
michael@0 591 return NS_OK;
michael@0 592 }
michael@0 593 case _FALSE:
michael@0 594 {
michael@0 595 aContext->recycler()->getBoolResult(false, aResult);
michael@0 596
michael@0 597 return NS_OK;
michael@0 598 }
michael@0 599 case LANG:
michael@0 600 {
michael@0 601 txXPathTreeWalker walker(aContext->getContextNode());
michael@0 602
michael@0 603 nsAutoString lang;
michael@0 604 bool found;
michael@0 605 do {
michael@0 606 found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML,
michael@0 607 lang);
michael@0 608 } while (!found && walker.moveToParent());
michael@0 609
michael@0 610 if (!found) {
michael@0 611 aContext->recycler()->getBoolResult(false, aResult);
michael@0 612
michael@0 613 return NS_OK;
michael@0 614 }
michael@0 615
michael@0 616 nsAutoString arg;
michael@0 617 rv = mParams[0]->evaluateToString(aContext, arg);
michael@0 618 NS_ENSURE_SUCCESS(rv, rv);
michael@0 619
michael@0 620 bool result =
michael@0 621 StringBeginsWith(lang, arg,
michael@0 622 txCaseInsensitiveStringComparator()) &&
michael@0 623 (lang.Length() == arg.Length() ||
michael@0 624 lang.CharAt(arg.Length()) == '-');
michael@0 625
michael@0 626 aContext->recycler()->getBoolResult(result, aResult);
michael@0 627
michael@0 628 return NS_OK;
michael@0 629 }
michael@0 630 case _NOT:
michael@0 631 {
michael@0 632 bool result;
michael@0 633 rv = mParams[0]->evaluateToBool(aContext, result);
michael@0 634 NS_ENSURE_SUCCESS(rv, rv);
michael@0 635
michael@0 636 aContext->recycler()->getBoolResult(!result, aResult);
michael@0 637
michael@0 638 return NS_OK;
michael@0 639 }
michael@0 640 case _TRUE:
michael@0 641 {
michael@0 642 aContext->recycler()->getBoolResult(true, aResult);
michael@0 643
michael@0 644 return NS_OK;
michael@0 645 }
michael@0 646 }
michael@0 647
michael@0 648 aContext->receiveError(NS_LITERAL_STRING("Internal error"),
michael@0 649 NS_ERROR_UNEXPECTED);
michael@0 650 return NS_ERROR_UNEXPECTED;
michael@0 651 }
michael@0 652
michael@0 653 Expr::ResultType
michael@0 654 txCoreFunctionCall::getReturnType()
michael@0 655 {
michael@0 656 return descriptTable[mType].mReturnType;
michael@0 657 }
michael@0 658
michael@0 659 bool
michael@0 660 txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext)
michael@0 661 {
michael@0 662 switch (mType) {
michael@0 663 case COUNT:
michael@0 664 case CONCAT:
michael@0 665 case CONTAINS:
michael@0 666 case STARTS_WITH:
michael@0 667 case SUBSTRING:
michael@0 668 case SUBSTRING_AFTER:
michael@0 669 case SUBSTRING_BEFORE:
michael@0 670 case TRANSLATE:
michael@0 671 case ROUND:
michael@0 672 case FLOOR:
michael@0 673 case CEILING:
michael@0 674 case SUM:
michael@0 675 case BOOLEAN:
michael@0 676 case _NOT:
michael@0 677 case _FALSE:
michael@0 678 case _TRUE:
michael@0 679 {
michael@0 680 return argsSensitiveTo(aContext);
michael@0 681 }
michael@0 682 case ID:
michael@0 683 {
michael@0 684 return (aContext & NODE_CONTEXT) ||
michael@0 685 argsSensitiveTo(aContext);
michael@0 686 }
michael@0 687 case LAST:
michael@0 688 {
michael@0 689 return !!(aContext & SIZE_CONTEXT);
michael@0 690 }
michael@0 691 case LOCAL_NAME:
michael@0 692 case NAME:
michael@0 693 case NAMESPACE_URI:
michael@0 694 case NORMALIZE_SPACE:
michael@0 695 case STRING:
michael@0 696 case STRING_LENGTH:
michael@0 697 case NUMBER:
michael@0 698 {
michael@0 699 if (mParams.IsEmpty()) {
michael@0 700 return !!(aContext & NODE_CONTEXT);
michael@0 701 }
michael@0 702 return argsSensitiveTo(aContext);
michael@0 703 }
michael@0 704 case POSITION:
michael@0 705 {
michael@0 706 return !!(aContext & POSITION_CONTEXT);
michael@0 707 }
michael@0 708 case LANG:
michael@0 709 {
michael@0 710 return (aContext & NODE_CONTEXT) ||
michael@0 711 argsSensitiveTo(aContext);
michael@0 712 }
michael@0 713 }
michael@0 714
michael@0 715 NS_NOTREACHED("how'd we get here?");
michael@0 716 return true;
michael@0 717 }
michael@0 718
michael@0 719 // static
michael@0 720 bool
michael@0 721 txCoreFunctionCall::getTypeFromAtom(nsIAtom* aName, eType& aType)
michael@0 722 {
michael@0 723 uint32_t i;
michael@0 724 for (i = 0; i < ArrayLength(descriptTable); ++i) {
michael@0 725 if (aName == *descriptTable[i].mName) {
michael@0 726 aType = static_cast<eType>(i);
michael@0 727
michael@0 728 return true;
michael@0 729 }
michael@0 730 }
michael@0 731
michael@0 732 return false;
michael@0 733 }
michael@0 734
michael@0 735 #ifdef TX_TO_STRING
michael@0 736 nsresult
michael@0 737 txCoreFunctionCall::getNameAtom(nsIAtom** aAtom)
michael@0 738 {
michael@0 739 NS_ADDREF(*aAtom = *descriptTable[mType].mName);
michael@0 740 return NS_OK;
michael@0 741 }
michael@0 742 #endif

mercurial