forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			666 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			666 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/ArrayUtils.h"
 | |
| #include "mozilla/FloatingPoint.h"
 | |
| 
 | |
| #include "txExpr.h"
 | |
| #include "txNodeSet.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "txIXPathContext.h"
 | |
| #include "nsWhitespaceTokenizer.h"
 | |
| #include "txXPathTreeWalker.h"
 | |
| #include <math.h>
 | |
| #include "txStringUtils.h"
 | |
| #include "txXMLUtils.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| struct txCoreFunctionDescriptor {
 | |
|   const int8_t mMinParams;
 | |
|   const int8_t mMaxParams;
 | |
|   const Expr::ResultType mReturnType;
 | |
|   const nsStaticAtom* const mName;
 | |
| };
 | |
| 
 | |
| // This must be ordered in the same order as txCoreFunctionCall::eType.
 | |
| // If you change one, change the other.
 | |
| static const txCoreFunctionDescriptor descriptTable[] = {
 | |
|     {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::count},         // COUNT
 | |
|     {1, 1, Expr::NODESET_RESULT, nsGkAtoms::id},           // ID
 | |
|     {0, 0, Expr::NUMBER_RESULT, nsGkAtoms::last},          // LAST
 | |
|     {0, 1, Expr::STRING_RESULT, nsGkAtoms::localName},     // LOCAL_NAME
 | |
|     {0, 1, Expr::STRING_RESULT, nsGkAtoms::namespaceUri},  // NAMESPACE_URI
 | |
|     {0, 1, Expr::STRING_RESULT, nsGkAtoms::name},          // NAME
 | |
|     {0, 0, Expr::NUMBER_RESULT, nsGkAtoms::position},      // POSITION
 | |
| 
 | |
|     {2, -1, Expr::STRING_RESULT, nsGkAtoms::concat},         // CONCAT
 | |
|     {2, 2, Expr::BOOLEAN_RESULT, nsGkAtoms::contains},       // CONTAINS
 | |
|     {0, 1, Expr::STRING_RESULT, nsGkAtoms::normalizeSpace},  // NORMALIZE_SPACE
 | |
|     {2, 2, Expr::BOOLEAN_RESULT, nsGkAtoms::startsWith},     // STARTS_WITH
 | |
|     {0, 1, Expr::STRING_RESULT, nsGkAtoms::string},          // STRING
 | |
|     {0, 1, Expr::NUMBER_RESULT, nsGkAtoms::stringLength},    // STRING_LENGTH
 | |
|     {2, 3, Expr::STRING_RESULT, nsGkAtoms::substring},       // SUBSTRING
 | |
|     {2, 2, Expr::STRING_RESULT, nsGkAtoms::substringAfter},  // SUBSTRING_AFTER
 | |
|     {2, 2, Expr::STRING_RESULT,
 | |
|      nsGkAtoms::substringBefore},                       // SUBSTRING_BEFORE
 | |
|     {3, 3, Expr::STRING_RESULT, nsGkAtoms::translate},  // TRANSLATE
 | |
| 
 | |
|     {0, 1, Expr::NUMBER_RESULT, nsGkAtoms::number},   // NUMBER
 | |
|     {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::round},    // ROUND
 | |
|     {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::floor},    // FLOOR
 | |
|     {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::ceiling},  // CEILING
 | |
|     {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::sum},      // SUM
 | |
| 
 | |
|     {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::boolean},  // BOOLEAN
 | |
|     {0, 0, Expr::BOOLEAN_RESULT, nsGkAtoms::_false},   // _FALSE
 | |
|     {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::lang},     // LANG
 | |
|     {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::_not},     // _NOT
 | |
|     {0, 0, Expr::BOOLEAN_RESULT, nsGkAtoms::_true}     // _TRUE
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Evaluates this Expr based on the given context node and processor state
 | |
|  * @param context the context node for evaluation of this Expr
 | |
|  * @param ps the ContextState containing the stack information needed
 | |
|  * for evaluation
 | |
|  * @return the result of the evaluation
 | |
|  */
 | |
| nsresult txCoreFunctionCall::evaluate(txIEvalContext* aContext,
 | |
|                                       txAExprResult** aResult) {
 | |
|   *aResult = nullptr;
 | |
| 
 | |
|   if (!requireParams(descriptTable[mType].mMinParams,
 | |
|                      descriptTable[mType].mMaxParams, aContext)) {
 | |
|     return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
|   switch (mType) {
 | |
|     case COUNT: {
 | |
|       RefPtr<txNodeSet> nodes;
 | |
|       rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       return aContext->recycler()->getNumberResult(nodes->size(), aResult);
 | |
|     }
 | |
|     case ID: {
 | |
|       RefPtr<txAExprResult> exprResult;
 | |
|       rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       RefPtr<txNodeSet> resultSet;
 | |
|       rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       txXPathTreeWalker walker(aContext->getContextNode());
 | |
| 
 | |
|       if (exprResult->getResultType() == txAExprResult::NODESET) {
 | |
|         txNodeSet* nodes =
 | |
|             static_cast<txNodeSet*>(static_cast<txAExprResult*>(exprResult));
 | |
|         int32_t i;
 | |
|         for (i = 0; i < nodes->size(); ++i) {
 | |
|           nsAutoString idList;
 | |
|           txXPathNodeUtils::appendNodeValue(nodes->get(i), idList);
 | |
|           nsWhitespaceTokenizer tokenizer(idList);
 | |
|           while (tokenizer.hasMoreTokens()) {
 | |
|             if (walker.moveToElementById(tokenizer.nextToken())) {
 | |
|               resultSet->add(walker.getCurrentPosition());
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         nsAutoString idList;
 | |
|         exprResult->stringValue(idList);
 | |
|         nsWhitespaceTokenizer tokenizer(idList);
 | |
|         while (tokenizer.hasMoreTokens()) {
 | |
|           if (walker.moveToElementById(tokenizer.nextToken())) {
 | |
|             resultSet->add(walker.getCurrentPosition());
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       *aResult = resultSet;
 | |
|       NS_ADDREF(*aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case LAST: {
 | |
|       return aContext->recycler()->getNumberResult(aContext->size(), aResult);
 | |
|     }
 | |
|     case LOCAL_NAME:
 | |
|     case NAME:
 | |
|     case NAMESPACE_URI: {
 | |
|       // Check for optional arg
 | |
|       RefPtr<txNodeSet> nodes;
 | |
|       if (!mParams.IsEmpty()) {
 | |
|         rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes));
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         if (nodes->isEmpty()) {
 | |
|           aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|           return NS_OK;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       const txXPathNode& node =
 | |
|           nodes ? nodes->get(0) : aContext->getContextNode();
 | |
|       switch (mType) {
 | |
|         case LOCAL_NAME: {
 | |
|           StringResult* strRes = nullptr;
 | |
|           rv = aContext->recycler()->getStringResult(&strRes);
 | |
|           NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|           *aResult = strRes;
 | |
|           txXPathNodeUtils::getLocalName(node, strRes->mValue);
 | |
| 
 | |
|           return NS_OK;
 | |
|         }
 | |
|         case NAMESPACE_URI: {
 | |
|           StringResult* strRes = nullptr;
 | |
|           rv = aContext->recycler()->getStringResult(&strRes);
 | |
|           NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|           *aResult = strRes;
 | |
|           txXPathNodeUtils::getNamespaceURI(node, strRes->mValue);
 | |
| 
 | |
|           return NS_OK;
 | |
|         }
 | |
|         case NAME: {
 | |
|           // XXX Namespace: namespaces have a name
 | |
|           if (txXPathNodeUtils::isAttribute(node) ||
 | |
|               txXPathNodeUtils::isElement(node) ||
 | |
|               txXPathNodeUtils::isProcessingInstruction(node)) {
 | |
|             StringResult* strRes = nullptr;
 | |
|             rv = aContext->recycler()->getStringResult(&strRes);
 | |
|             NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|             *aResult = strRes;
 | |
|             txXPathNodeUtils::getNodeName(node, strRes->mValue);
 | |
|           } else {
 | |
|             aContext->recycler()->getEmptyStringResult(aResult);
 | |
|           }
 | |
| 
 | |
|           return NS_OK;
 | |
|         }
 | |
|         default: {
 | |
|           MOZ_CRASH("Unexpected mType?!");
 | |
|         }
 | |
|       }
 | |
|       MOZ_CRASH("Inner mType switch should have returned!");
 | |
|     }
 | |
|     case POSITION: {
 | |
|       return aContext->recycler()->getNumberResult(aContext->position(),
 | |
|                                                    aResult);
 | |
|     }
 | |
| 
 | |
|       // String functions
 | |
| 
 | |
|     case CONCAT: {
 | |
|       RefPtr<StringResult> strRes;
 | |
|       rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       uint32_t i, len = mParams.Length();
 | |
|       for (i = 0; i < len; ++i) {
 | |
|         rv = mParams[i]->evaluateToString(aContext, strRes->mValue);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       }
 | |
| 
 | |
|       NS_ADDREF(*aResult = strRes);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case CONTAINS: {
 | |
|       nsAutoString arg2;
 | |
|       rv = mParams[1]->evaluateToString(aContext, arg2);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (arg2.IsEmpty()) {
 | |
|         aContext->recycler()->getBoolResult(true, aResult);
 | |
|       } else {
 | |
|         nsAutoString arg1;
 | |
|         rv = mParams[0]->evaluateToString(aContext, arg1);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1),
 | |
|                                             aResult);
 | |
|       }
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case NORMALIZE_SPACE: {
 | |
|       nsAutoString resultStr;
 | |
|       if (!mParams.IsEmpty()) {
 | |
|         rv = mParams[0]->evaluateToString(aContext, resultStr);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       } else {
 | |
|         txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
 | |
|                                           resultStr);
 | |
|       }
 | |
| 
 | |
|       RefPtr<StringResult> strRes;
 | |
|       rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       bool addSpace = false;
 | |
|       bool first = true;
 | |
|       strRes->mValue.SetCapacity(resultStr.Length());
 | |
|       char16_t c;
 | |
|       uint32_t src;
 | |
|       for (src = 0; src < resultStr.Length(); src++) {
 | |
|         c = resultStr.CharAt(src);
 | |
|         if (XMLUtils::isWhitespace(c)) {
 | |
|           addSpace = true;
 | |
|         } else {
 | |
|           if (addSpace && !first) strRes->mValue.Append(char16_t(' '));
 | |
| 
 | |
|           strRes->mValue.Append(c);
 | |
|           addSpace = false;
 | |
|           first = false;
 | |
|         }
 | |
|       }
 | |
|       *aResult = strRes;
 | |
|       NS_ADDREF(*aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case STARTS_WITH: {
 | |
|       nsAutoString arg2;
 | |
|       rv = mParams[1]->evaluateToString(aContext, arg2);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       bool result = false;
 | |
|       if (arg2.IsEmpty()) {
 | |
|         result = true;
 | |
|       } else {
 | |
|         nsAutoString arg1;
 | |
|         rv = mParams[0]->evaluateToString(aContext, arg1);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         result = StringBeginsWith(arg1, arg2);
 | |
|       }
 | |
| 
 | |
|       aContext->recycler()->getBoolResult(result, aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case STRING: {
 | |
|       RefPtr<StringResult> strRes;
 | |
|       rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (!mParams.IsEmpty()) {
 | |
|         rv = mParams[0]->evaluateToString(aContext, strRes->mValue);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       } else {
 | |
|         txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
 | |
|                                           strRes->mValue);
 | |
|       }
 | |
| 
 | |
|       NS_ADDREF(*aResult = strRes);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case STRING_LENGTH: {
 | |
|       nsAutoString resultStr;
 | |
|       if (!mParams.IsEmpty()) {
 | |
|         rv = mParams[0]->evaluateToString(aContext, resultStr);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       } else {
 | |
|         txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
 | |
|                                           resultStr);
 | |
|       }
 | |
|       rv = aContext->recycler()->getNumberResult(resultStr.Length(), aResult);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case SUBSTRING: {
 | |
|       nsAutoString src;
 | |
|       rv = mParams[0]->evaluateToString(aContext, src);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       double start;
 | |
|       rv = evaluateToNumber(mParams[1], aContext, &start);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       // check for NaN or +/-Inf
 | |
|       if (mozilla::IsNaN(start) || mozilla::IsInfinite(start) ||
 | |
|           start >= src.Length() + 0.5) {
 | |
|         aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       start = floor(start + 0.5) - 1;
 | |
| 
 | |
|       double end;
 | |
|       if (mParams.Length() == 3) {
 | |
|         rv = evaluateToNumber(mParams[2], aContext, &end);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         end += start;
 | |
|         if (mozilla::IsNaN(end) || end < 0) {
 | |
|           aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|           return NS_OK;
 | |
|         }
 | |
| 
 | |
|         if (end > src.Length())
 | |
|           end = src.Length();
 | |
|         else
 | |
|           end = floor(end + 0.5);
 | |
|       } else {
 | |
|         end = src.Length();
 | |
|       }
 | |
| 
 | |
|       if (start < 0) start = 0;
 | |
| 
 | |
|       if (start > end) {
 | |
|         aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       return aContext->recycler()->getStringResult(
 | |
|           Substring(src, (uint32_t)start, (uint32_t)(end - start)), aResult);
 | |
|     }
 | |
|     case SUBSTRING_AFTER: {
 | |
|       nsAutoString arg1;
 | |
|       rv = mParams[0]->evaluateToString(aContext, arg1);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       nsAutoString arg2;
 | |
|       rv = mParams[1]->evaluateToString(aContext, arg2);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (arg2.IsEmpty()) {
 | |
|         return aContext->recycler()->getStringResult(arg1, aResult);
 | |
|       }
 | |
| 
 | |
|       int32_t idx = arg1.Find(arg2);
 | |
|       if (idx == kNotFound) {
 | |
|         aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       const nsAString& result = Substring(arg1, idx + arg2.Length());
 | |
|       return aContext->recycler()->getStringResult(result, aResult);
 | |
|     }
 | |
|     case SUBSTRING_BEFORE: {
 | |
|       nsAutoString arg2;
 | |
|       rv = mParams[1]->evaluateToString(aContext, arg2);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (arg2.IsEmpty()) {
 | |
|         aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       nsAutoString arg1;
 | |
|       rv = mParams[0]->evaluateToString(aContext, arg1);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       int32_t idx = arg1.Find(arg2);
 | |
|       if (idx == kNotFound) {
 | |
|         aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       return aContext->recycler()->getStringResult(StringHead(arg1, idx),
 | |
|                                                    aResult);
 | |
|     }
 | |
|     case TRANSLATE: {
 | |
|       nsAutoString src;
 | |
|       rv = mParams[0]->evaluateToString(aContext, src);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (src.IsEmpty()) {
 | |
|         aContext->recycler()->getEmptyStringResult(aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       RefPtr<StringResult> strRes;
 | |
|       rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       strRes->mValue.SetCapacity(src.Length());
 | |
| 
 | |
|       nsAutoString oldChars, newChars;
 | |
|       rv = mParams[1]->evaluateToString(aContext, oldChars);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       rv = mParams[2]->evaluateToString(aContext, newChars);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       uint32_t i;
 | |
|       int32_t newCharsLength = (int32_t)newChars.Length();
 | |
|       for (i = 0; i < src.Length(); i++) {
 | |
|         int32_t idx = oldChars.FindChar(src.CharAt(i));
 | |
|         if (idx != kNotFound) {
 | |
|           if (idx < newCharsLength)
 | |
|             strRes->mValue.Append(newChars.CharAt((uint32_t)idx));
 | |
|         } else {
 | |
|           strRes->mValue.Append(src.CharAt(i));
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       NS_ADDREF(*aResult = strRes);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|       // Number functions
 | |
| 
 | |
|     case NUMBER: {
 | |
|       double res;
 | |
|       if (!mParams.IsEmpty()) {
 | |
|         rv = evaluateToNumber(mParams[0], aContext, &res);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       } else {
 | |
|         nsAutoString resultStr;
 | |
|         txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
 | |
|                                           resultStr);
 | |
|         res = txDouble::toDouble(resultStr);
 | |
|       }
 | |
|       return aContext->recycler()->getNumberResult(res, aResult);
 | |
|     }
 | |
|     case ROUND: {
 | |
|       double dbl;
 | |
|       rv = evaluateToNumber(mParams[0], aContext, &dbl);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (mozilla::IsFinite(dbl)) {
 | |
|         if (mozilla::IsNegative(dbl) && dbl >= -0.5) {
 | |
|           dbl *= 0;
 | |
|         } else {
 | |
|           dbl = floor(dbl + 0.5);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return aContext->recycler()->getNumberResult(dbl, aResult);
 | |
|     }
 | |
|     case FLOOR: {
 | |
|       double dbl;
 | |
|       rv = evaluateToNumber(mParams[0], aContext, &dbl);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (mozilla::IsFinite(dbl) && !mozilla::IsNegativeZero(dbl))
 | |
|         dbl = floor(dbl);
 | |
| 
 | |
|       return aContext->recycler()->getNumberResult(dbl, aResult);
 | |
|     }
 | |
|     case CEILING: {
 | |
|       double dbl;
 | |
|       rv = evaluateToNumber(mParams[0], aContext, &dbl);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (mozilla::IsFinite(dbl)) {
 | |
|         if (mozilla::IsNegative(dbl) && dbl > -1)
 | |
|           dbl *= 0;
 | |
|         else
 | |
|           dbl = ceil(dbl);
 | |
|       }
 | |
| 
 | |
|       return aContext->recycler()->getNumberResult(dbl, aResult);
 | |
|     }
 | |
|     case SUM: {
 | |
|       RefPtr<txNodeSet> nodes;
 | |
|       nsresult rv =
 | |
|           evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       double res = 0;
 | |
|       int32_t i;
 | |
|       for (i = 0; i < nodes->size(); ++i) {
 | |
|         nsAutoString resultStr;
 | |
|         txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr);
 | |
|         res += txDouble::toDouble(resultStr);
 | |
|       }
 | |
|       return aContext->recycler()->getNumberResult(res, aResult);
 | |
|     }
 | |
| 
 | |
|       // Boolean functions
 | |
| 
 | |
|     case BOOLEAN: {
 | |
|       bool result;
 | |
|       nsresult rv = mParams[0]->evaluateToBool(aContext, result);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       aContext->recycler()->getBoolResult(result, aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case _FALSE: {
 | |
|       aContext->recycler()->getBoolResult(false, aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case LANG: {
 | |
|       txXPathTreeWalker walker(aContext->getContextNode());
 | |
| 
 | |
|       nsAutoString lang;
 | |
|       bool found;
 | |
|       do {
 | |
|         found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML, lang);
 | |
|       } while (!found && walker.moveToParent());
 | |
| 
 | |
|       if (!found) {
 | |
|         aContext->recycler()->getBoolResult(false, aResult);
 | |
| 
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       nsAutoString arg;
 | |
|       rv = mParams[0]->evaluateToString(aContext, arg);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       bool result =
 | |
|           StringBeginsWith(lang, arg, nsCaseInsensitiveStringComparator) &&
 | |
|           (lang.Length() == arg.Length() || lang.CharAt(arg.Length()) == '-');
 | |
| 
 | |
|       aContext->recycler()->getBoolResult(result, aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case _NOT: {
 | |
|       bool result;
 | |
|       rv = mParams[0]->evaluateToBool(aContext, result);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       aContext->recycler()->getBoolResult(!result, aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case _TRUE: {
 | |
|       aContext->recycler()->getBoolResult(true, aResult);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   aContext->receiveError(u"Internal error"_ns, NS_ERROR_UNEXPECTED);
 | |
|   return NS_ERROR_UNEXPECTED;
 | |
| }
 | |
| 
 | |
| Expr::ResultType txCoreFunctionCall::getReturnType() {
 | |
|   return descriptTable[mType].mReturnType;
 | |
| }
 | |
| 
 | |
| bool txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext) {
 | |
|   switch (mType) {
 | |
|     case COUNT:
 | |
|     case CONCAT:
 | |
|     case CONTAINS:
 | |
|     case STARTS_WITH:
 | |
|     case SUBSTRING:
 | |
|     case SUBSTRING_AFTER:
 | |
|     case SUBSTRING_BEFORE:
 | |
|     case TRANSLATE:
 | |
|     case ROUND:
 | |
|     case FLOOR:
 | |
|     case CEILING:
 | |
|     case SUM:
 | |
|     case BOOLEAN:
 | |
|     case _NOT:
 | |
|     case _FALSE:
 | |
|     case _TRUE: {
 | |
|       return argsSensitiveTo(aContext);
 | |
|     }
 | |
|     case ID: {
 | |
|       return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext);
 | |
|     }
 | |
|     case LAST: {
 | |
|       return !!(aContext & SIZE_CONTEXT);
 | |
|     }
 | |
|     case LOCAL_NAME:
 | |
|     case NAME:
 | |
|     case NAMESPACE_URI:
 | |
|     case NORMALIZE_SPACE:
 | |
|     case STRING:
 | |
|     case STRING_LENGTH:
 | |
|     case NUMBER: {
 | |
|       if (mParams.IsEmpty()) {
 | |
|         return !!(aContext & NODE_CONTEXT);
 | |
|       }
 | |
|       return argsSensitiveTo(aContext);
 | |
|     }
 | |
|     case POSITION: {
 | |
|       return !!(aContext & POSITION_CONTEXT);
 | |
|     }
 | |
|     case LANG: {
 | |
|       return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT_UNREACHABLE("how'd we get here?");
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool txCoreFunctionCall::getTypeFromAtom(nsAtom* aName, eType& aType) {
 | |
|   uint32_t i;
 | |
|   for (i = 0; i < ArrayLength(descriptTable); ++i) {
 | |
|     if (aName == descriptTable[i].mName) {
 | |
|       aType = static_cast<eType>(i);
 | |
| 
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| #ifdef TX_TO_STRING
 | |
| void txCoreFunctionCall::appendName(nsAString& aDest) {
 | |
|   aDest.Append(descriptTable[mType].mName->GetUTF16String());
 | |
| }
 | |
| #endif
 | 
