mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-09-26 23:54:56 +00:00
735 lines
24 KiB
C++
735 lines
24 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* 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 "nsIAtom.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "txExecutionState.h"
|
|
#include "txExpr.h"
|
|
#include "txIXPathContext.h"
|
|
#include "txNodeSet.h"
|
|
#include "txOutputFormat.h"
|
|
#include "txRtfHandler.h"
|
|
#include "txXPathTreeWalker.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsContentCID.h"
|
|
#include "nsContentCreatorFunctions.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIDOMDocumentFragment.h"
|
|
#include "txMozillaXMLOutput.h"
|
|
#include "nsTextNode.h"
|
|
#include "mozilla/dom/DocumentFragment.h"
|
|
#include "prtime.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
class txStylesheetCompilerState;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Utility functions
|
|
// ------------------------------------------------------------------
|
|
|
|
static nsresult
|
|
convertRtfToNode(txIEvalContext *aContext, txResultTreeFragment *aRtf)
|
|
{
|
|
txExecutionState* es =
|
|
static_cast<txExecutionState*>(aContext->getPrivateContext());
|
|
if (!es) {
|
|
NS_ERROR("Need txExecutionState!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
const txXPathNode& document = es->getSourceDocument();
|
|
|
|
nsIDocument *doc = txXPathNativeNode::getDocument(document);
|
|
nsCOMPtr<nsIDOMDocumentFragment> domFragment =
|
|
new DocumentFragment(doc->NodeInfoManager());
|
|
|
|
txOutputFormat format;
|
|
txMozillaXMLOutput mozHandler(&format, domFragment, true);
|
|
|
|
nsresult rv = aRtf->flushToHandler(&mozHandler);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = mozHandler.closePrevious(true);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// The txResultTreeFragment will own this.
|
|
const txXPathNode* node = txXPathNativeNode::createXPathNode(domFragment,
|
|
true);
|
|
NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
aRtf->setNode(node);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsresult
|
|
createTextNode(txIEvalContext *aContext, nsString& aValue,
|
|
txXPathNode* *aResult)
|
|
{
|
|
txExecutionState* es =
|
|
static_cast<txExecutionState*>(aContext->getPrivateContext());
|
|
if (!es) {
|
|
NS_ERROR("Need txExecutionState!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
const txXPathNode& document = es->getSourceDocument();
|
|
|
|
nsIDocument *doc = txXPathNativeNode::getDocument(document);
|
|
nsCOMPtr<nsIContent> text = new nsTextNode(doc->NodeInfoManager());
|
|
|
|
nsresult rv = text->SetText(aValue, false);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
*aResult = txXPathNativeNode::createXPathNode(text, true);
|
|
NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static already_AddRefed<DocumentFragment>
|
|
createDocFragment(txIEvalContext *aContext)
|
|
{
|
|
txExecutionState* es =
|
|
static_cast<txExecutionState*>(aContext->getPrivateContext());
|
|
if (!es) {
|
|
NS_ERROR("Need txExecutionState!");
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const txXPathNode& document = es->getSourceDocument();
|
|
nsIDocument *doc = txXPathNativeNode::getDocument(document);
|
|
RefPtr<DocumentFragment> fragment =
|
|
new DocumentFragment(doc->NodeInfoManager());
|
|
|
|
return fragment.forget();
|
|
}
|
|
|
|
static nsresult
|
|
createAndAddToResult(nsIAtom* aName, const nsSubstring& aValue,
|
|
txNodeSet* aResultSet, nsIContent* aResultHolder)
|
|
{
|
|
NS_ASSERTION(aResultHolder->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) &&
|
|
aResultHolder->OwnerDoc(),
|
|
"invalid result-holder");
|
|
|
|
nsIDocument* doc = aResultHolder->OwnerDoc();
|
|
nsCOMPtr<Element> elem = doc->CreateElem(nsDependentAtomString(aName),
|
|
nullptr, kNameSpaceID_None);
|
|
NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
|
|
|
|
RefPtr<nsTextNode> text = new nsTextNode(doc->NodeInfoManager());
|
|
|
|
nsresult rv = text->SetText(aValue, false);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = elem->AppendChildTo(text, false);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = aResultHolder->AppendChildTo(elem, false);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoPtr<txXPathNode> xpathNode(
|
|
txXPathNativeNode::createXPathNode(elem, true));
|
|
NS_ENSURE_TRUE(xpathNode, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
aResultSet->append(*xpathNode);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Need to update this array if types are added to the ResultType enum in
|
|
// txAExprResult.
|
|
static const char * const sTypes[] = {
|
|
"node-set",
|
|
"boolean",
|
|
"number",
|
|
"string",
|
|
"RTF"
|
|
};
|
|
|
|
// ------------------------------------------------------------------
|
|
// Function implementations
|
|
// ------------------------------------------------------------------
|
|
|
|
struct txEXSLTFunctionDescriptor
|
|
{
|
|
int8_t mMinParams;
|
|
int8_t mMaxParams;
|
|
Expr::ResultType mReturnType;
|
|
nsIAtom** mName;
|
|
int32_t mNamespaceID;
|
|
const char* mNamespaceURI;
|
|
};
|
|
|
|
static const char kEXSLTCommonNS[] = "http://exslt.org/common";
|
|
static const char kEXSLTSetsNS[] = "http://exslt.org/sets";
|
|
static const char kEXSLTStringsNS[] = "http://exslt.org/strings";
|
|
static const char kEXSLTMathNS[] = "http://exslt.org/math";
|
|
static const char kEXSLTDatesAndTimesNS[] = "http://exslt.org/dates-and-times";
|
|
|
|
// The order of this table must be the same as the
|
|
// txEXSLTFunctionCall::eType enum
|
|
static txEXSLTFunctionDescriptor descriptTable[] =
|
|
{
|
|
{ 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::nodeSet, 0, kEXSLTCommonNS }, // NODE_SET
|
|
{ 1, 1, Expr::STRING_RESULT, &nsGkAtoms::objectType, 0, kEXSLTCommonNS }, // OBJECT_TYPE
|
|
{ 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::difference, 0, kEXSLTSetsNS }, // DIFFERENCE
|
|
{ 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::distinct, 0, kEXSLTSetsNS }, // DISTINCT
|
|
{ 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::hasSameNode, 0, kEXSLTSetsNS }, // HAS_SAME_NODE
|
|
{ 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::intersection, 0, kEXSLTSetsNS }, // INTERSECTION
|
|
{ 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::leading, 0, kEXSLTSetsNS }, // LEADING
|
|
{ 2, 2, Expr::NODESET_RESULT, &nsGkAtoms::trailing, 0, kEXSLTSetsNS }, // TRAILING
|
|
{ 1, 1, Expr::STRING_RESULT, &nsGkAtoms::concat, 0, kEXSLTStringsNS }, // CONCAT
|
|
{ 1, 2, Expr::STRING_RESULT, &nsGkAtoms::split, 0, kEXSLTStringsNS }, // SPLIT
|
|
{ 1, 2, Expr::STRING_RESULT, &nsGkAtoms::tokenize, 0, kEXSLTStringsNS }, // TOKENIZE
|
|
{ 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::max, 0, kEXSLTMathNS }, // MAX
|
|
{ 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::min, 0, kEXSLTMathNS }, // MIN
|
|
{ 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::highest, 0, kEXSLTMathNS }, // HIGHEST
|
|
{ 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::lowest, 0, kEXSLTMathNS }, // LOWEST
|
|
{ 0, 0, Expr::STRING_RESULT, &nsGkAtoms::dateTime, 0, kEXSLTDatesAndTimesNS }, // DATE_TIME
|
|
|
|
};
|
|
|
|
class txEXSLTFunctionCall : public FunctionCall
|
|
{
|
|
public:
|
|
// The order of this enum must be the same as the descriptTable
|
|
// table above
|
|
enum eType {
|
|
// Set functions
|
|
NODE_SET,
|
|
OBJECT_TYPE,
|
|
DIFFERENCE,
|
|
DISTINCT,
|
|
HAS_SAME_NODE,
|
|
INTERSECTION,
|
|
LEADING,
|
|
TRAILING,
|
|
CONCAT,
|
|
SPLIT,
|
|
TOKENIZE,
|
|
MAX,
|
|
MIN,
|
|
HIGHEST,
|
|
LOWEST,
|
|
DATE_TIME
|
|
};
|
|
|
|
explicit txEXSLTFunctionCall(eType aType)
|
|
: mType(aType)
|
|
{
|
|
}
|
|
|
|
TX_DECL_FUNCTION
|
|
|
|
private:
|
|
eType mType;
|
|
};
|
|
|
|
nsresult
|
|
txEXSLTFunctionCall::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 NODE_SET:
|
|
{
|
|
RefPtr<txAExprResult> exprResult;
|
|
rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (exprResult->getResultType() == txAExprResult::NODESET) {
|
|
exprResult.swap(*aResult);
|
|
}
|
|
else {
|
|
RefPtr<txNodeSet> resultSet;
|
|
rv = aContext->recycler()->
|
|
getNodeSet(getter_AddRefs(resultSet));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (exprResult->getResultType() ==
|
|
txAExprResult::RESULT_TREE_FRAGMENT) {
|
|
txResultTreeFragment *rtf =
|
|
static_cast<txResultTreeFragment*>
|
|
(exprResult.get());
|
|
|
|
const txXPathNode *node = rtf->getNode();
|
|
if (!node) {
|
|
rv = convertRtfToNode(aContext, rtf);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
node = rtf->getNode();
|
|
}
|
|
|
|
resultSet->append(*node);
|
|
}
|
|
else {
|
|
nsAutoString value;
|
|
exprResult->stringValue(value);
|
|
|
|
nsAutoPtr<txXPathNode> node;
|
|
rv = createTextNode(aContext, value,
|
|
getter_Transfers(node));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
resultSet->append(*node);
|
|
}
|
|
|
|
NS_ADDREF(*aResult = resultSet);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
case OBJECT_TYPE:
|
|
{
|
|
RefPtr<txAExprResult> exprResult;
|
|
nsresult rv = mParams[0]->evaluate(aContext,
|
|
getter_AddRefs(exprResult));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<StringResult> strRes;
|
|
rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
AppendASCIItoUTF16(sTypes[exprResult->getResultType()],
|
|
strRes->mValue);
|
|
|
|
NS_ADDREF(*aResult = strRes);
|
|
|
|
return NS_OK;
|
|
}
|
|
case DIFFERENCE:
|
|
case INTERSECTION:
|
|
{
|
|
RefPtr<txNodeSet> nodes1;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes1));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<txNodeSet> nodes2;
|
|
rv = evaluateToNodeSet(mParams[1], aContext,
|
|
getter_AddRefs(nodes2));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<txNodeSet> resultSet;
|
|
rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool insertOnFound = mType == INTERSECTION;
|
|
|
|
int32_t searchPos = 0;
|
|
int32_t i, len = nodes1->size();
|
|
for (i = 0; i < len; ++i) {
|
|
const txXPathNode& node = nodes1->get(i);
|
|
int32_t foundPos = nodes2->indexOf(node, searchPos);
|
|
if (foundPos >= 0) {
|
|
searchPos = foundPos + 1;
|
|
}
|
|
|
|
if ((foundPos >= 0) == insertOnFound) {
|
|
rv = resultSet->append(node);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aResult = resultSet);
|
|
|
|
return NS_OK;
|
|
}
|
|
case DISTINCT:
|
|
{
|
|
RefPtr<txNodeSet> nodes;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<txNodeSet> resultSet;
|
|
rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsTHashtable<nsStringHashKey> hash;
|
|
|
|
int32_t i, len = nodes->size();
|
|
for (i = 0; i < len; ++i) {
|
|
nsAutoString str;
|
|
const txXPathNode& node = nodes->get(i);
|
|
txXPathNodeUtils::appendNodeValue(node, str);
|
|
if (!hash.GetEntry(str)) {
|
|
hash.PutEntry(str);
|
|
rv = resultSet->append(node);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aResult = resultSet);
|
|
|
|
return NS_OK;
|
|
}
|
|
case HAS_SAME_NODE:
|
|
{
|
|
RefPtr<txNodeSet> nodes1;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes1));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<txNodeSet> nodes2;
|
|
rv = evaluateToNodeSet(mParams[1], aContext,
|
|
getter_AddRefs(nodes2));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool found = false;
|
|
int32_t i, len = nodes1->size();
|
|
for (i = 0; i < len; ++i) {
|
|
if (nodes2->contains(nodes1->get(i))) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
aContext->recycler()->getBoolResult(found, aResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
case LEADING:
|
|
case TRAILING:
|
|
{
|
|
RefPtr<txNodeSet> nodes1;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes1));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
RefPtr<txNodeSet> nodes2;
|
|
rv = evaluateToNodeSet(mParams[1], aContext,
|
|
getter_AddRefs(nodes2));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (nodes2->isEmpty()) {
|
|
*aResult = nodes1;
|
|
NS_ADDREF(*aResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<txNodeSet> resultSet;
|
|
rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
int32_t end = nodes1->indexOf(nodes2->get(0));
|
|
if (end >= 0) {
|
|
int32_t i = 0;
|
|
if (mType == TRAILING) {
|
|
i = end + 1;
|
|
end = nodes1->size();
|
|
}
|
|
for (; i < end; ++i) {
|
|
rv = resultSet->append(nodes1->get(i));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aResult = resultSet);
|
|
|
|
return NS_OK;
|
|
}
|
|
case CONCAT:
|
|
{
|
|
RefPtr<txNodeSet> nodes;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString str;
|
|
int32_t i, len = nodes->size();
|
|
for (i = 0; i < len; ++i) {
|
|
txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
|
|
}
|
|
|
|
return aContext->recycler()->getStringResult(str, aResult);
|
|
}
|
|
case SPLIT:
|
|
case TOKENIZE:
|
|
{
|
|
// Evaluate parameters
|
|
nsAutoString string;
|
|
rv = mParams[0]->evaluateToString(aContext, string);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString pattern;
|
|
if (mParams.Length() == 2) {
|
|
rv = mParams[1]->evaluateToString(aContext, pattern);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
else if (mType == SPLIT) {
|
|
pattern.Assign(' ');
|
|
}
|
|
else {
|
|
pattern.AssignLiteral("\t\r\n ");
|
|
}
|
|
|
|
// Set up holders for the result
|
|
RefPtr<DocumentFragment> docFrag = createDocFragment(aContext);
|
|
NS_ENSURE_STATE(docFrag);
|
|
|
|
RefPtr<txNodeSet> resultSet;
|
|
rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
uint32_t tailIndex;
|
|
|
|
// Start splitting
|
|
if (pattern.IsEmpty()) {
|
|
nsString::const_char_iterator start = string.BeginReading();
|
|
nsString::const_char_iterator end = string.EndReading();
|
|
for (; start < end; ++start) {
|
|
rv = createAndAddToResult(nsGkAtoms::token,
|
|
Substring(start, start + 1),
|
|
resultSet, docFrag);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
tailIndex = string.Length();
|
|
}
|
|
else if (mType == SPLIT) {
|
|
nsAString::const_iterator strStart, strEnd;
|
|
string.BeginReading(strStart);
|
|
string.EndReading(strEnd);
|
|
nsAString::const_iterator start = strStart, end = strEnd;
|
|
|
|
while (FindInReadable(pattern, start, end)) {
|
|
if (start != strStart) {
|
|
rv = createAndAddToResult(nsGkAtoms::token,
|
|
Substring(strStart, start),
|
|
resultSet, docFrag);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
strStart = start = end;
|
|
end = strEnd;
|
|
}
|
|
|
|
tailIndex = strStart.get() - string.get();
|
|
}
|
|
else {
|
|
int32_t found, start = 0;
|
|
while ((found = string.FindCharInSet(pattern, start)) !=
|
|
kNotFound) {
|
|
if (found != start) {
|
|
rv = createAndAddToResult(nsGkAtoms::token,
|
|
Substring(string, start,
|
|
found - start),
|
|
resultSet, docFrag);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
start = found + 1;
|
|
}
|
|
|
|
tailIndex = start;
|
|
}
|
|
|
|
// Add tail if needed
|
|
if (tailIndex != (uint32_t)string.Length()) {
|
|
rv = createAndAddToResult(nsGkAtoms::token,
|
|
Substring(string, tailIndex),
|
|
resultSet, docFrag);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
NS_ADDREF(*aResult = resultSet);
|
|
|
|
return NS_OK;
|
|
}
|
|
case MAX:
|
|
case MIN:
|
|
{
|
|
RefPtr<txNodeSet> nodes;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (nodes->isEmpty()) {
|
|
return aContext->recycler()->
|
|
getNumberResult(UnspecifiedNaN<double>(), aResult);
|
|
}
|
|
|
|
bool findMax = mType == MAX;
|
|
|
|
double res = findMax ? mozilla::NegativeInfinity<double>() :
|
|
mozilla::PositiveInfinity<double>();
|
|
int32_t i, len = nodes->size();
|
|
for (i = 0; i < len; ++i) {
|
|
nsAutoString str;
|
|
txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
|
|
double val = txDouble::toDouble(str);
|
|
if (mozilla::IsNaN(val)) {
|
|
res = UnspecifiedNaN<double>();
|
|
break;
|
|
}
|
|
|
|
if (findMax ? (val > res) : (val < res)) {
|
|
res = val;
|
|
}
|
|
}
|
|
|
|
return aContext->recycler()->getNumberResult(res, aResult);
|
|
}
|
|
case HIGHEST:
|
|
case LOWEST:
|
|
{
|
|
RefPtr<txNodeSet> nodes;
|
|
rv = evaluateToNodeSet(mParams[0], aContext,
|
|
getter_AddRefs(nodes));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (nodes->isEmpty()) {
|
|
NS_ADDREF(*aResult = nodes);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<txNodeSet> resultSet;
|
|
rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool findMax = mType == HIGHEST;
|
|
double res = findMax ? mozilla::NegativeInfinity<double>() :
|
|
mozilla::PositiveInfinity<double>();
|
|
int32_t i, len = nodes->size();
|
|
for (i = 0; i < len; ++i) {
|
|
nsAutoString str;
|
|
const txXPathNode& node = nodes->get(i);
|
|
txXPathNodeUtils::appendNodeValue(node, str);
|
|
double val = txDouble::toDouble(str);
|
|
if (mozilla::IsNaN(val)) {
|
|
resultSet->clear();
|
|
break;
|
|
}
|
|
if (findMax ? (val > res) : (val < res)) {
|
|
resultSet->clear();
|
|
res = val;
|
|
}
|
|
|
|
if (res == val) {
|
|
rv = resultSet->append(node);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aResult = resultSet);
|
|
|
|
return NS_OK;
|
|
}
|
|
case DATE_TIME:
|
|
{
|
|
// http://exslt.org/date/functions/date-time/
|
|
// format: YYYY-MM-DDTTHH:MM:SS.sss+00:00
|
|
char formatstr[] = "%04hd-%02ld-%02ldT%02ld:%02ld:%02ld.%03ld%c%02ld:%02ld";
|
|
|
|
PRExplodedTime prtime;
|
|
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prtime);
|
|
|
|
int32_t offset = (prtime.tm_params.tp_gmt_offset +
|
|
prtime.tm_params.tp_dst_offset) / 60;
|
|
|
|
bool isneg = offset < 0;
|
|
if (isneg) offset = -offset;
|
|
|
|
StringResult* strRes;
|
|
rv = aContext->recycler()->getStringResult(&strRes);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
CopyASCIItoUTF16(nsPrintfCString(formatstr,
|
|
prtime.tm_year, prtime.tm_month + 1, prtime.tm_mday,
|
|
prtime.tm_hour, prtime.tm_min, prtime.tm_sec,
|
|
prtime.tm_usec / 10000,
|
|
isneg ? '-' : '+', offset / 60, offset % 60), strRes->mValue);
|
|
|
|
*aResult = strRes;
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
aContext->receiveError(NS_LITERAL_STRING("Internal error"),
|
|
NS_ERROR_UNEXPECTED);
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
Expr::ResultType
|
|
txEXSLTFunctionCall::getReturnType()
|
|
{
|
|
return descriptTable[mType].mReturnType;
|
|
}
|
|
|
|
bool
|
|
txEXSLTFunctionCall::isSensitiveTo(ContextSensitivity aContext)
|
|
{
|
|
if (mType == NODE_SET || mType == SPLIT || mType == TOKENIZE) {
|
|
return (aContext & PRIVATE_CONTEXT) || argsSensitiveTo(aContext);
|
|
}
|
|
return argsSensitiveTo(aContext);
|
|
}
|
|
|
|
#ifdef TX_TO_STRING
|
|
nsresult
|
|
txEXSLTFunctionCall::getNameAtom(nsIAtom **aAtom)
|
|
{
|
|
NS_ADDREF(*aAtom = *descriptTable[mType].mName);
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
extern nsresult
|
|
TX_ConstructEXSLTFunction(nsIAtom *aName,
|
|
int32_t aNamespaceID,
|
|
txStylesheetCompilerState* aState,
|
|
FunctionCall **aResult)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < ArrayLength(descriptTable); ++i) {
|
|
txEXSLTFunctionDescriptor& desc = descriptTable[i];
|
|
if (aName == *desc.mName && aNamespaceID == desc.mNamespaceID) {
|
|
*aResult = new txEXSLTFunctionCall(
|
|
static_cast<txEXSLTFunctionCall::eType>(i));
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
|
|
}
|
|
|
|
extern bool
|
|
TX_InitEXSLTFunction()
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < ArrayLength(descriptTable); ++i) {
|
|
txEXSLTFunctionDescriptor& desc = descriptTable[i];
|
|
NS_ConvertASCIItoUTF16 namespaceURI(desc.mNamespaceURI);
|
|
desc.mNamespaceID =
|
|
txNamespaceManager::getNamespaceID(namespaceURI);
|
|
|
|
if (desc.mNamespaceID == kNameSpaceID_Unknown) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|