Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
5 :
6 : #include "mozilla/ArrayUtils.h"
7 : #include "mozilla/FloatingPoint.h"
8 :
9 : #include "nsIAtom.h"
10 : #include "nsGkAtoms.h"
11 : #include "txExecutionState.h"
12 : #include "txExpr.h"
13 : #include "txIXPathContext.h"
14 : #include "txNodeSet.h"
15 : #include "txOutputFormat.h"
16 : #include "txRtfHandler.h"
17 : #include "txXPathTreeWalker.h"
18 : #include "nsPrintfCString.h"
19 : #include "nsComponentManagerUtils.h"
20 : #include "nsContentCID.h"
21 : #include "nsContentCreatorFunctions.h"
22 : #include "nsIContent.h"
23 : #include "nsIDOMDocumentFragment.h"
24 : #include "txMozillaXMLOutput.h"
25 : #include "nsTextNode.h"
26 : #include "mozilla/dom/DocumentFragment.h"
27 : #include "prtime.h"
28 :
29 : using namespace mozilla;
30 : using namespace mozilla::dom;
31 :
32 : class txStylesheetCompilerState;
33 :
34 : // ------------------------------------------------------------------
35 : // Utility functions
36 : // ------------------------------------------------------------------
37 :
38 : static nsresult
39 0 : convertRtfToNode(txIEvalContext *aContext, txResultTreeFragment *aRtf)
40 : {
41 : txExecutionState* es =
42 0 : static_cast<txExecutionState*>(aContext->getPrivateContext());
43 0 : if (!es) {
44 0 : NS_ERROR("Need txExecutionState!");
45 :
46 0 : return NS_ERROR_UNEXPECTED;
47 : }
48 :
49 0 : const txXPathNode& document = es->getSourceDocument();
50 :
51 0 : nsIDocument *doc = txXPathNativeNode::getDocument(document);
52 : nsCOMPtr<nsIDOMDocumentFragment> domFragment =
53 0 : new DocumentFragment(doc->NodeInfoManager());
54 :
55 0 : txOutputFormat format;
56 0 : txMozillaXMLOutput mozHandler(&format, domFragment, true);
57 :
58 0 : nsresult rv = aRtf->flushToHandler(&mozHandler);
59 0 : NS_ENSURE_SUCCESS(rv, rv);
60 :
61 0 : rv = mozHandler.closePrevious(true);
62 0 : NS_ENSURE_SUCCESS(rv, rv);
63 :
64 : // The txResultTreeFragment will own this.
65 : const txXPathNode* node = txXPathNativeNode::createXPathNode(domFragment,
66 0 : true);
67 0 : NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
68 :
69 0 : aRtf->setNode(node);
70 :
71 0 : return NS_OK;
72 : }
73 :
74 : static nsresult
75 0 : createTextNode(txIEvalContext *aContext, nsString& aValue,
76 : txXPathNode* *aResult)
77 : {
78 : txExecutionState* es =
79 0 : static_cast<txExecutionState*>(aContext->getPrivateContext());
80 0 : if (!es) {
81 0 : NS_ERROR("Need txExecutionState!");
82 :
83 0 : return NS_ERROR_UNEXPECTED;
84 : }
85 :
86 0 : const txXPathNode& document = es->getSourceDocument();
87 :
88 0 : nsIDocument *doc = txXPathNativeNode::getDocument(document);
89 0 : nsCOMPtr<nsIContent> text = new nsTextNode(doc->NodeInfoManager());
90 :
91 0 : nsresult rv = text->SetText(aValue, false);
92 0 : NS_ENSURE_SUCCESS(rv, rv);
93 :
94 0 : *aResult = txXPathNativeNode::createXPathNode(text, true);
95 0 : NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
96 :
97 0 : return NS_OK;
98 : }
99 :
100 : static already_AddRefed<DocumentFragment>
101 0 : createDocFragment(txIEvalContext *aContext)
102 : {
103 : txExecutionState* es =
104 0 : static_cast<txExecutionState*>(aContext->getPrivateContext());
105 0 : if (!es) {
106 0 : NS_ERROR("Need txExecutionState!");
107 :
108 0 : return nullptr;
109 : }
110 :
111 0 : const txXPathNode& document = es->getSourceDocument();
112 0 : nsIDocument *doc = txXPathNativeNode::getDocument(document);
113 : RefPtr<DocumentFragment> fragment =
114 0 : new DocumentFragment(doc->NodeInfoManager());
115 :
116 0 : return fragment.forget();
117 : }
118 :
119 : static nsresult
120 0 : createAndAddToResult(nsIAtom* aName, const nsAString& aValue,
121 : txNodeSet* aResultSet, nsIContent* aResultHolder)
122 : {
123 0 : NS_ASSERTION(aResultHolder->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) &&
124 : aResultHolder->OwnerDoc(),
125 : "invalid result-holder");
126 :
127 0 : nsIDocument* doc = aResultHolder->OwnerDoc();
128 0 : nsCOMPtr<Element> elem = doc->CreateElem(nsDependentAtomString(aName),
129 0 : nullptr, kNameSpaceID_None);
130 0 : NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
131 :
132 0 : RefPtr<nsTextNode> text = new nsTextNode(doc->NodeInfoManager());
133 :
134 0 : nsresult rv = text->SetText(aValue, false);
135 0 : NS_ENSURE_SUCCESS(rv, rv);
136 :
137 0 : rv = elem->AppendChildTo(text, false);
138 0 : NS_ENSURE_SUCCESS(rv, rv);
139 :
140 0 : rv = aResultHolder->AppendChildTo(elem, false);
141 0 : NS_ENSURE_SUCCESS(rv, rv);
142 :
143 : nsAutoPtr<txXPathNode> xpathNode(
144 0 : txXPathNativeNode::createXPathNode(elem, true));
145 0 : NS_ENSURE_TRUE(xpathNode, NS_ERROR_OUT_OF_MEMORY);
146 :
147 0 : aResultSet->append(*xpathNode);
148 :
149 0 : return NS_OK;
150 : }
151 :
152 : // Need to update this array if types are added to the ResultType enum in
153 : // txAExprResult.
154 : static const char * const sTypes[] = {
155 : "node-set",
156 : "boolean",
157 : "number",
158 : "string",
159 : "RTF"
160 : };
161 :
162 : // ------------------------------------------------------------------
163 : // Function implementations
164 : // ------------------------------------------------------------------
165 :
166 : struct txEXSLTFunctionDescriptor
167 : {
168 : int8_t mMinParams;
169 : int8_t mMaxParams;
170 : Expr::ResultType mReturnType;
171 : int32_t mNamespaceID;
172 : nsIAtom** mName;
173 : const char* mNamespaceURI;
174 : };
175 :
176 : static const char kEXSLTCommonNS[] = "http://exslt.org/common";
177 : static const char kEXSLTSetsNS[] = "http://exslt.org/sets";
178 : static const char kEXSLTStringsNS[] = "http://exslt.org/strings";
179 : static const char kEXSLTMathNS[] = "http://exslt.org/math";
180 : static const char kEXSLTDatesAndTimesNS[] = "http://exslt.org/dates-and-times";
181 :
182 : // The order of this table must be the same as the
183 : // txEXSLTFunctionCall::eType enum
184 : static txEXSLTFunctionDescriptor descriptTable[] =
185 : {
186 : { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::nodeSet, kEXSLTCommonNS }, // NODE_SET
187 : { 1, 1, Expr::STRING_RESULT, 0, &nsGkAtoms::objectType, kEXSLTCommonNS }, // OBJECT_TYPE
188 : { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::difference, kEXSLTSetsNS }, // DIFFERENCE
189 : { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::distinct, kEXSLTSetsNS }, // DISTINCT
190 : { 2, 2, Expr::BOOLEAN_RESULT, 0, &nsGkAtoms::hasSameNode, kEXSLTSetsNS }, // HAS_SAME_NODE
191 : { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::intersection, kEXSLTSetsNS }, // INTERSECTION
192 : { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::leading, kEXSLTSetsNS }, // LEADING
193 : { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::trailing, kEXSLTSetsNS }, // TRAILING
194 : { 1, 1, Expr::STRING_RESULT, 0, &nsGkAtoms::concat, kEXSLTStringsNS }, // CONCAT
195 : { 1, 2, Expr::STRING_RESULT, 0, &nsGkAtoms::split, kEXSLTStringsNS }, // SPLIT
196 : { 1, 2, Expr::STRING_RESULT, 0, &nsGkAtoms::tokenize, kEXSLTStringsNS }, // TOKENIZE
197 : { 1, 1, Expr::NUMBER_RESULT, 0, &nsGkAtoms::max, kEXSLTMathNS }, // MAX
198 : { 1, 1, Expr::NUMBER_RESULT, 0, &nsGkAtoms::min, kEXSLTMathNS }, // MIN
199 : { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::highest, kEXSLTMathNS }, // HIGHEST
200 : { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::lowest, kEXSLTMathNS }, // LOWEST
201 : { 0, 0, Expr::STRING_RESULT, 0, &nsGkAtoms::dateTime, kEXSLTDatesAndTimesNS }, // DATE_TIME
202 :
203 : };
204 :
205 0 : class txEXSLTFunctionCall : public FunctionCall
206 : {
207 : public:
208 : // The order of this enum must be the same as the descriptTable
209 : // table above
210 : enum eType {
211 : // Set functions
212 : NODE_SET,
213 : OBJECT_TYPE,
214 : DIFFERENCE,
215 : DISTINCT,
216 : HAS_SAME_NODE,
217 : INTERSECTION,
218 : LEADING,
219 : TRAILING,
220 : CONCAT,
221 : SPLIT,
222 : TOKENIZE,
223 : MAX,
224 : MIN,
225 : HIGHEST,
226 : LOWEST,
227 : DATE_TIME
228 : };
229 :
230 0 : explicit txEXSLTFunctionCall(eType aType)
231 0 : : mType(aType)
232 : {
233 0 : }
234 :
235 : TX_DECL_FUNCTION
236 :
237 : private:
238 : eType mType;
239 : };
240 :
241 : nsresult
242 0 : txEXSLTFunctionCall::evaluate(txIEvalContext *aContext,
243 : txAExprResult **aResult)
244 : {
245 0 : *aResult = nullptr;
246 0 : if (!requireParams(descriptTable[mType].mMinParams,
247 0 : descriptTable[mType].mMaxParams,
248 0 : aContext)) {
249 0 : return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
250 : }
251 :
252 0 : nsresult rv = NS_OK;
253 0 : switch (mType) {
254 : case NODE_SET:
255 : {
256 0 : RefPtr<txAExprResult> exprResult;
257 0 : rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
258 0 : NS_ENSURE_SUCCESS(rv, rv);
259 :
260 0 : if (exprResult->getResultType() == txAExprResult::NODESET) {
261 0 : exprResult.forget(aResult);
262 : }
263 : else {
264 0 : RefPtr<txNodeSet> resultSet;
265 0 : rv = aContext->recycler()->
266 0 : getNodeSet(getter_AddRefs(resultSet));
267 0 : NS_ENSURE_SUCCESS(rv, rv);
268 :
269 0 : if (exprResult->getResultType() ==
270 : txAExprResult::RESULT_TREE_FRAGMENT) {
271 : txResultTreeFragment *rtf =
272 : static_cast<txResultTreeFragment*>
273 0 : (exprResult.get());
274 :
275 0 : const txXPathNode *node = rtf->getNode();
276 0 : if (!node) {
277 0 : rv = convertRtfToNode(aContext, rtf);
278 0 : NS_ENSURE_SUCCESS(rv, rv);
279 :
280 0 : node = rtf->getNode();
281 : }
282 :
283 0 : resultSet->append(*node);
284 : }
285 : else {
286 0 : nsAutoString value;
287 0 : exprResult->stringValue(value);
288 :
289 0 : nsAutoPtr<txXPathNode> node;
290 0 : rv = createTextNode(aContext, value,
291 0 : getter_Transfers(node));
292 0 : NS_ENSURE_SUCCESS(rv, rv);
293 :
294 0 : resultSet->append(*node);
295 : }
296 :
297 0 : NS_ADDREF(*aResult = resultSet);
298 : }
299 :
300 0 : return NS_OK;
301 : }
302 : case OBJECT_TYPE:
303 : {
304 0 : RefPtr<txAExprResult> exprResult;
305 0 : nsresult rv = mParams[0]->evaluate(aContext,
306 0 : getter_AddRefs(exprResult));
307 0 : NS_ENSURE_SUCCESS(rv, rv);
308 :
309 0 : RefPtr<StringResult> strRes;
310 0 : rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
311 0 : NS_ENSURE_SUCCESS(rv, rv);
312 :
313 0 : AppendASCIItoUTF16(sTypes[exprResult->getResultType()],
314 0 : strRes->mValue);
315 :
316 0 : NS_ADDREF(*aResult = strRes);
317 :
318 0 : return NS_OK;
319 : }
320 : case DIFFERENCE:
321 : case INTERSECTION:
322 : {
323 0 : RefPtr<txNodeSet> nodes1;
324 0 : rv = evaluateToNodeSet(mParams[0], aContext,
325 0 : getter_AddRefs(nodes1));
326 0 : NS_ENSURE_SUCCESS(rv, rv);
327 :
328 0 : RefPtr<txNodeSet> nodes2;
329 0 : rv = evaluateToNodeSet(mParams[1], aContext,
330 0 : getter_AddRefs(nodes2));
331 0 : NS_ENSURE_SUCCESS(rv, rv);
332 :
333 0 : RefPtr<txNodeSet> resultSet;
334 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
335 0 : NS_ENSURE_SUCCESS(rv, rv);
336 :
337 0 : bool insertOnFound = mType == INTERSECTION;
338 :
339 0 : int32_t searchPos = 0;
340 0 : int32_t i, len = nodes1->size();
341 0 : for (i = 0; i < len; ++i) {
342 0 : const txXPathNode& node = nodes1->get(i);
343 0 : int32_t foundPos = nodes2->indexOf(node, searchPos);
344 0 : if (foundPos >= 0) {
345 0 : searchPos = foundPos + 1;
346 : }
347 :
348 0 : if ((foundPos >= 0) == insertOnFound) {
349 0 : rv = resultSet->append(node);
350 0 : NS_ENSURE_SUCCESS(rv, rv);
351 : }
352 : }
353 :
354 0 : NS_ADDREF(*aResult = resultSet);
355 :
356 0 : return NS_OK;
357 : }
358 : case DISTINCT:
359 : {
360 0 : RefPtr<txNodeSet> nodes;
361 0 : rv = evaluateToNodeSet(mParams[0], aContext,
362 0 : getter_AddRefs(nodes));
363 0 : NS_ENSURE_SUCCESS(rv, rv);
364 :
365 0 : RefPtr<txNodeSet> resultSet;
366 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
367 0 : NS_ENSURE_SUCCESS(rv, rv);
368 :
369 0 : nsTHashtable<nsStringHashKey> hash;
370 :
371 0 : int32_t i, len = nodes->size();
372 0 : for (i = 0; i < len; ++i) {
373 0 : nsAutoString str;
374 0 : const txXPathNode& node = nodes->get(i);
375 0 : txXPathNodeUtils::appendNodeValue(node, str);
376 0 : if (!hash.GetEntry(str)) {
377 0 : hash.PutEntry(str);
378 0 : rv = resultSet->append(node);
379 0 : NS_ENSURE_SUCCESS(rv, rv);
380 : }
381 : }
382 :
383 0 : NS_ADDREF(*aResult = resultSet);
384 :
385 0 : return NS_OK;
386 : }
387 : case HAS_SAME_NODE:
388 : {
389 0 : RefPtr<txNodeSet> nodes1;
390 0 : rv = evaluateToNodeSet(mParams[0], aContext,
391 0 : getter_AddRefs(nodes1));
392 0 : NS_ENSURE_SUCCESS(rv, rv);
393 :
394 0 : RefPtr<txNodeSet> nodes2;
395 0 : rv = evaluateToNodeSet(mParams[1], aContext,
396 0 : getter_AddRefs(nodes2));
397 0 : NS_ENSURE_SUCCESS(rv, rv);
398 :
399 0 : bool found = false;
400 0 : int32_t i, len = nodes1->size();
401 0 : for (i = 0; i < len; ++i) {
402 0 : if (nodes2->contains(nodes1->get(i))) {
403 0 : found = true;
404 0 : break;
405 : }
406 : }
407 :
408 0 : aContext->recycler()->getBoolResult(found, aResult);
409 :
410 0 : return NS_OK;
411 : }
412 : case LEADING:
413 : case TRAILING:
414 : {
415 0 : RefPtr<txNodeSet> nodes1;
416 0 : rv = evaluateToNodeSet(mParams[0], aContext,
417 0 : getter_AddRefs(nodes1));
418 0 : NS_ENSURE_SUCCESS(rv, rv);
419 :
420 0 : RefPtr<txNodeSet> nodes2;
421 0 : rv = evaluateToNodeSet(mParams[1], aContext,
422 0 : getter_AddRefs(nodes2));
423 0 : NS_ENSURE_SUCCESS(rv, rv);
424 :
425 0 : if (nodes2->isEmpty()) {
426 0 : *aResult = nodes1;
427 0 : NS_ADDREF(*aResult);
428 :
429 0 : return NS_OK;
430 : }
431 :
432 0 : RefPtr<txNodeSet> resultSet;
433 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
434 0 : NS_ENSURE_SUCCESS(rv, rv);
435 :
436 0 : int32_t end = nodes1->indexOf(nodes2->get(0));
437 0 : if (end >= 0) {
438 0 : int32_t i = 0;
439 0 : if (mType == TRAILING) {
440 0 : i = end + 1;
441 0 : end = nodes1->size();
442 : }
443 0 : for (; i < end; ++i) {
444 0 : rv = resultSet->append(nodes1->get(i));
445 0 : NS_ENSURE_SUCCESS(rv, rv);
446 : }
447 : }
448 :
449 0 : NS_ADDREF(*aResult = resultSet);
450 :
451 0 : return NS_OK;
452 : }
453 : case CONCAT:
454 : {
455 0 : RefPtr<txNodeSet> nodes;
456 0 : rv = evaluateToNodeSet(mParams[0], aContext,
457 0 : getter_AddRefs(nodes));
458 0 : NS_ENSURE_SUCCESS(rv, rv);
459 :
460 0 : nsAutoString str;
461 0 : int32_t i, len = nodes->size();
462 0 : for (i = 0; i < len; ++i) {
463 0 : txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
464 : }
465 :
466 0 : return aContext->recycler()->getStringResult(str, aResult);
467 : }
468 : case SPLIT:
469 : case TOKENIZE:
470 : {
471 : // Evaluate parameters
472 0 : nsAutoString string;
473 0 : rv = mParams[0]->evaluateToString(aContext, string);
474 0 : NS_ENSURE_SUCCESS(rv, rv);
475 :
476 0 : nsAutoString pattern;
477 0 : if (mParams.Length() == 2) {
478 0 : rv = mParams[1]->evaluateToString(aContext, pattern);
479 0 : NS_ENSURE_SUCCESS(rv, rv);
480 : }
481 0 : else if (mType == SPLIT) {
482 0 : pattern.Assign(' ');
483 : }
484 : else {
485 0 : pattern.AssignLiteral("\t\r\n ");
486 : }
487 :
488 : // Set up holders for the result
489 0 : RefPtr<DocumentFragment> docFrag = createDocFragment(aContext);
490 0 : NS_ENSURE_STATE(docFrag);
491 :
492 0 : RefPtr<txNodeSet> resultSet;
493 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
494 0 : NS_ENSURE_SUCCESS(rv, rv);
495 :
496 : uint32_t tailIndex;
497 :
498 : // Start splitting
499 0 : if (pattern.IsEmpty()) {
500 0 : nsString::const_char_iterator start = string.BeginReading();
501 0 : nsString::const_char_iterator end = string.EndReading();
502 0 : for (; start < end; ++start) {
503 0 : rv = createAndAddToResult(nsGkAtoms::token,
504 0 : Substring(start, start + 1),
505 0 : resultSet, docFrag);
506 0 : NS_ENSURE_SUCCESS(rv, rv);
507 : }
508 :
509 0 : tailIndex = string.Length();
510 : }
511 0 : else if (mType == SPLIT) {
512 0 : nsAString::const_iterator strStart, strEnd;
513 0 : string.BeginReading(strStart);
514 0 : string.EndReading(strEnd);
515 0 : nsAString::const_iterator start = strStart, end = strEnd;
516 :
517 0 : while (FindInReadable(pattern, start, end)) {
518 0 : if (start != strStart) {
519 0 : rv = createAndAddToResult(nsGkAtoms::token,
520 0 : Substring(strStart, start),
521 0 : resultSet, docFrag);
522 0 : NS_ENSURE_SUCCESS(rv, rv);
523 : }
524 0 : strStart = start = end;
525 0 : end = strEnd;
526 : }
527 :
528 0 : tailIndex = strStart.get() - string.get();
529 : }
530 : else {
531 0 : int32_t found, start = 0;
532 0 : while ((found = string.FindCharInSet(pattern, start)) !=
533 : kNotFound) {
534 0 : if (found != start) {
535 0 : rv = createAndAddToResult(nsGkAtoms::token,
536 0 : Substring(string, start,
537 0 : found - start),
538 0 : resultSet, docFrag);
539 0 : NS_ENSURE_SUCCESS(rv, rv);
540 : }
541 0 : start = found + 1;
542 : }
543 :
544 0 : tailIndex = start;
545 : }
546 :
547 : // Add tail if needed
548 0 : if (tailIndex != (uint32_t)string.Length()) {
549 0 : rv = createAndAddToResult(nsGkAtoms::token,
550 0 : Substring(string, tailIndex),
551 0 : resultSet, docFrag);
552 0 : NS_ENSURE_SUCCESS(rv, rv);
553 : }
554 :
555 0 : NS_ADDREF(*aResult = resultSet);
556 :
557 0 : return NS_OK;
558 : }
559 : case MAX:
560 : case MIN:
561 : {
562 0 : RefPtr<txNodeSet> nodes;
563 0 : rv = evaluateToNodeSet(mParams[0], aContext,
564 0 : getter_AddRefs(nodes));
565 0 : NS_ENSURE_SUCCESS(rv, rv);
566 :
567 0 : if (nodes->isEmpty()) {
568 0 : return aContext->recycler()->
569 0 : getNumberResult(UnspecifiedNaN<double>(), aResult);
570 : }
571 :
572 0 : bool findMax = mType == MAX;
573 :
574 0 : double res = findMax ? mozilla::NegativeInfinity<double>() :
575 0 : mozilla::PositiveInfinity<double>();
576 0 : int32_t i, len = nodes->size();
577 0 : for (i = 0; i < len; ++i) {
578 0 : nsAutoString str;
579 0 : txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
580 0 : double val = txDouble::toDouble(str);
581 0 : if (mozilla::IsNaN(val)) {
582 0 : res = UnspecifiedNaN<double>();
583 0 : break;
584 : }
585 :
586 0 : if (findMax ? (val > res) : (val < res)) {
587 0 : res = val;
588 : }
589 : }
590 :
591 0 : return aContext->recycler()->getNumberResult(res, aResult);
592 : }
593 : case HIGHEST:
594 : case LOWEST:
595 : {
596 0 : RefPtr<txNodeSet> nodes;
597 0 : rv = evaluateToNodeSet(mParams[0], aContext,
598 0 : getter_AddRefs(nodes));
599 0 : NS_ENSURE_SUCCESS(rv, rv);
600 :
601 0 : if (nodes->isEmpty()) {
602 0 : NS_ADDREF(*aResult = nodes);
603 :
604 0 : return NS_OK;
605 : }
606 :
607 0 : RefPtr<txNodeSet> resultSet;
608 0 : rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
609 0 : NS_ENSURE_SUCCESS(rv, rv);
610 :
611 0 : bool findMax = mType == HIGHEST;
612 0 : double res = findMax ? mozilla::NegativeInfinity<double>() :
613 0 : mozilla::PositiveInfinity<double>();
614 0 : int32_t i, len = nodes->size();
615 0 : for (i = 0; i < len; ++i) {
616 0 : nsAutoString str;
617 0 : const txXPathNode& node = nodes->get(i);
618 0 : txXPathNodeUtils::appendNodeValue(node, str);
619 0 : double val = txDouble::toDouble(str);
620 0 : if (mozilla::IsNaN(val)) {
621 0 : resultSet->clear();
622 0 : break;
623 : }
624 0 : if (findMax ? (val > res) : (val < res)) {
625 0 : resultSet->clear();
626 0 : res = val;
627 : }
628 :
629 0 : if (res == val) {
630 0 : rv = resultSet->append(node);
631 0 : NS_ENSURE_SUCCESS(rv, rv);
632 : }
633 : }
634 :
635 0 : NS_ADDREF(*aResult = resultSet);
636 :
637 0 : return NS_OK;
638 : }
639 : case DATE_TIME:
640 : {
641 : // http://exslt.org/date/functions/date-time/
642 :
643 : PRExplodedTime prtime;
644 0 : PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prtime);
645 :
646 0 : int32_t offset = (prtime.tm_params.tp_gmt_offset +
647 0 : prtime.tm_params.tp_dst_offset) / 60;
648 :
649 0 : bool isneg = offset < 0;
650 0 : if (isneg) offset = -offset;
651 :
652 : StringResult* strRes;
653 0 : rv = aContext->recycler()->getStringResult(&strRes);
654 0 : NS_ENSURE_SUCCESS(rv, rv);
655 :
656 : // format: YYYY-MM-DDTTHH:MM:SS.sss+00:00
657 0 : CopyASCIItoUTF16(nsPrintfCString("%04hd-%02" PRId32 "-%02" PRId32
658 : "T%02" PRId32 ":%02" PRId32 ":%02" PRId32
659 : ".%03" PRId32 "%c%02" PRId32 ":%02" PRId32,
660 0 : prtime.tm_year, prtime.tm_month + 1, prtime.tm_mday,
661 : prtime.tm_hour, prtime.tm_min, prtime.tm_sec,
662 0 : prtime.tm_usec / 10000,
663 0 : isneg ? '-' : '+', offset / 60, offset % 60), strRes->mValue);
664 :
665 0 : *aResult = strRes;
666 :
667 0 : return NS_OK;
668 : }
669 : }
670 :
671 0 : aContext->receiveError(NS_LITERAL_STRING("Internal error"),
672 0 : NS_ERROR_UNEXPECTED);
673 0 : return NS_ERROR_UNEXPECTED;
674 : }
675 :
676 : Expr::ResultType
677 0 : txEXSLTFunctionCall::getReturnType()
678 : {
679 0 : return descriptTable[mType].mReturnType;
680 : }
681 :
682 : bool
683 0 : txEXSLTFunctionCall::isSensitiveTo(ContextSensitivity aContext)
684 : {
685 0 : if (mType == NODE_SET || mType == SPLIT || mType == TOKENIZE) {
686 0 : return (aContext & PRIVATE_CONTEXT) || argsSensitiveTo(aContext);
687 : }
688 0 : return argsSensitiveTo(aContext);
689 : }
690 :
691 : #ifdef TX_TO_STRING
692 : nsresult
693 0 : txEXSLTFunctionCall::getNameAtom(nsIAtom **aAtom)
694 : {
695 0 : NS_ADDREF(*aAtom = *descriptTable[mType].mName);
696 0 : return NS_OK;
697 : }
698 : #endif
699 :
700 : extern nsresult
701 0 : TX_ConstructEXSLTFunction(nsIAtom *aName,
702 : int32_t aNamespaceID,
703 : txStylesheetCompilerState* aState,
704 : FunctionCall **aResult)
705 : {
706 : uint32_t i;
707 0 : for (i = 0; i < ArrayLength(descriptTable); ++i) {
708 0 : txEXSLTFunctionDescriptor& desc = descriptTable[i];
709 0 : if (aName == *desc.mName && aNamespaceID == desc.mNamespaceID) {
710 0 : *aResult = new txEXSLTFunctionCall(
711 0 : static_cast<txEXSLTFunctionCall::eType>(i));
712 0 : return NS_OK;
713 : }
714 : }
715 :
716 0 : return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
717 : }
718 :
719 : extern bool
720 3 : TX_InitEXSLTFunction()
721 : {
722 : uint32_t i;
723 51 : for (i = 0; i < ArrayLength(descriptTable); ++i) {
724 48 : txEXSLTFunctionDescriptor& desc = descriptTable[i];
725 96 : NS_ConvertASCIItoUTF16 namespaceURI(desc.mNamespaceURI);
726 48 : desc.mNamespaceID =
727 48 : txNamespaceManager::getNamespaceID(namespaceURI);
728 :
729 48 : if (desc.mNamespaceID == kNameSpaceID_Unknown) {
730 0 : return false;
731 : }
732 : }
733 :
734 3 : return true;
735 : }
|