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/FloatingPoint.h"
7 
8 #include "nsReadableUtils.h"
9 #include "txExecutionState.h"
10 #include "txXSLTPatterns.h"
11 #include "txNodeSetContext.h"
12 #include "txForwardContext.h"
13 #include "txXMLUtils.h"
14 #include "txXSLTFunctions.h"
15 #include "nsWhitespaceTokenizer.h"
16 #include "nsIContent.h"
17 
18 /*
19  * Returns the default priority of this Pattern.
20  * UnionPatterns don't like this.
21  * This should be called on the simple patterns.
22  */
getDefaultPriority()23 double txUnionPattern::getDefaultPriority()
24 {
25     NS_ERROR("Don't call getDefaultPriority on txUnionPattern");
26     return mozilla::UnspecifiedNaN<double>();
27 }
28 
29 /*
30  * Determines whether this Pattern matches the given node within
31  * the given context
32  * This should be called on the simple patterns for xsl:template,
33  * but is fine for xsl:key and xsl:number
34  */
matches(const txXPathNode & aNode,txIMatchContext * aContext)35 bool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
36 {
37     uint32_t i, len = mLocPathPatterns.Length();
38     for (i = 0; i < len; ++i) {
39         if (mLocPathPatterns[i]->matches(aNode, aContext)) {
40             return true;
41         }
42     }
43     return false;
44 }
45 
46 txPattern::Type
getType()47 txUnionPattern::getType()
48 {
49   return UNION_PATTERN;
50 }
51 
TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txUnionPattern)52 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txUnionPattern)
53 txPattern*
54 txUnionPattern::getSubPatternAt(uint32_t aPos)
55 {
56     return mLocPathPatterns.SafeElementAt(aPos);
57 }
58 
59 void
setSubPatternAt(uint32_t aPos,txPattern * aPattern)60 txUnionPattern::setSubPatternAt(uint32_t aPos, txPattern* aPattern)
61 {
62     NS_ASSERTION(aPos < mLocPathPatterns.Length(),
63                  "setting bad subexpression index");
64     mLocPathPatterns[aPos] = aPattern;
65 }
66 
67 
68 #ifdef TX_TO_STRING
69 void
toString(nsAString & aDest)70 txUnionPattern::toString(nsAString& aDest)
71 {
72 #ifdef DEBUG
73     aDest.AppendLiteral("txUnionPattern{");
74 #endif
75     for (uint32_t i = 0; i < mLocPathPatterns.Length(); ++i) {
76         if (i != 0)
77             aDest.AppendLiteral(" | ");
78         mLocPathPatterns[i]->toString(aDest);
79     }
80 #ifdef DEBUG
81     aDest.Append(char16_t('}'));
82 #endif
83 }
84 #endif
85 
86 
87 /*
88  * LocationPathPattern
89  *
90  * a list of step patterns, can start with id or key
91  * (dealt with by the parser)
92  */
93 
addStep(txPattern * aPattern,bool isChild)94 nsresult txLocPathPattern::addStep(txPattern* aPattern, bool isChild)
95 {
96     Step* step = mSteps.AppendElement();
97     if (!step)
98         return NS_ERROR_OUT_OF_MEMORY;
99 
100     step->pattern = aPattern;
101     step->isChild = isChild;
102 
103     return NS_OK;
104 }
105 
matches(const txXPathNode & aNode,txIMatchContext * aContext)106 bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
107 {
108     NS_ASSERTION(mSteps.Length() > 1, "Internal error");
109 
110     /*
111      * The idea is to split up a path into blocks separated by descendant
112      * operators. For example "foo/bar//baz/bop//ying/yang" is split up into
113      * three blocks. The "ying/yang" block is handled by the first while-loop
114      * and the "foo/bar" and "baz/bop" blocks are handled by the second
115      * while-loop.
116      * A block is considered matched when we find a list of ancestors that
117      * match the block. If there are more than one list of ancestors that
118      * match a block we only need to find the one furthermost down in the
119      * tree.
120      */
121 
122     uint32_t pos = mSteps.Length();
123     Step* step = &mSteps[--pos];
124     if (!step->pattern->matches(aNode, aContext))
125         return false;
126 
127     txXPathTreeWalker walker(aNode);
128     bool hasParent = walker.moveToParent();
129 
130     while (step->isChild) {
131         if (!pos)
132             return true; // all steps matched
133         step = &mSteps[--pos];
134         if (!hasParent || !step->pattern->matches(walker.getCurrentPosition(), aContext))
135             return false; // no more ancestors or no match
136 
137         hasParent = walker.moveToParent();
138     }
139 
140     // We have at least one // path separator
141     txXPathTreeWalker blockWalker(walker);
142     uint32_t blockPos = pos;
143 
144     while (pos) {
145         if (!hasParent)
146             return false; // There are more steps in the current block
147                              // than ancestors of the tested node
148 
149         step = &mSteps[--pos];
150         if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
151             // Didn't match. We restart at beginning of block using a new
152             // start node
153             pos = blockPos;
154             hasParent = blockWalker.moveToParent();
155             walker.moveTo(blockWalker);
156         }
157         else {
158             hasParent = walker.moveToParent();
159             if (!step->isChild) {
160                 // We've matched an entire block. Set new start pos and start node
161                 blockPos = pos;
162                 blockWalker.moveTo(walker);
163             }
164         }
165     }
166 
167     return true;
168 } // txLocPathPattern::matches
169 
getDefaultPriority()170 double txLocPathPattern::getDefaultPriority()
171 {
172     NS_ASSERTION(mSteps.Length() > 1, "Internal error");
173 
174     return 0.5;
175 }
176 
TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txLocPathPattern)177 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txLocPathPattern)
178 txPattern*
179 txLocPathPattern::getSubPatternAt(uint32_t aPos)
180 {
181     return aPos < mSteps.Length() ? mSteps[aPos].pattern.get() : nullptr;
182 }
183 
184 void
setSubPatternAt(uint32_t aPos,txPattern * aPattern)185 txLocPathPattern::setSubPatternAt(uint32_t aPos, txPattern* aPattern)
186 {
187     NS_ASSERTION(aPos < mSteps.Length(), "setting bad subexpression index");
188     Step* step = &mSteps[aPos];
189     step->pattern.forget();
190     step->pattern = aPattern;
191 }
192 
193 #ifdef TX_TO_STRING
194 void
toString(nsAString & aDest)195 txLocPathPattern::toString(nsAString& aDest)
196 {
197 #ifdef DEBUG
198     aDest.AppendLiteral("txLocPathPattern{");
199 #endif
200     for (uint32_t i = 0; i < mSteps.Length(); ++i) {
201         if (i != 0) {
202             if (mSteps[i].isChild)
203                 aDest.Append(char16_t('/'));
204             else
205                 aDest.AppendLiteral("//");
206         }
207         mSteps[i].pattern->toString(aDest);
208     }
209 #ifdef DEBUG
210     aDest.Append(char16_t('}'));
211 #endif
212 }
213 #endif
214 
215 /*
216  * txRootPattern
217  *
218  * a txPattern matching the document node, or '/'
219  */
220 
matches(const txXPathNode & aNode,txIMatchContext * aContext)221 bool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
222 {
223     return txXPathNodeUtils::isRoot(aNode);
224 }
225 
getDefaultPriority()226 double txRootPattern::getDefaultPriority()
227 {
228     return 0.5;
229 }
230 
231 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txRootPattern)
TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txRootPattern)232 TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txRootPattern)
233 
234 #ifdef TX_TO_STRING
235 void
236 txRootPattern::toString(nsAString& aDest)
237 {
238 #ifdef DEBUG
239     aDest.AppendLiteral("txRootPattern{");
240 #endif
241     if (mSerialize)
242         aDest.Append(char16_t('/'));
243 #ifdef DEBUG
244     aDest.Append(char16_t('}'));
245 #endif
246 }
247 #endif
248 
249 /*
250  * txIdPattern
251  *
252  * txIdPattern matches if the given node has a ID attribute with one
253  * of the space delimited values.
254  * This looks like the id() function, but may only have LITERALs as
255  * argument.
256  */
txIdPattern(const nsSubstring & aString)257 txIdPattern::txIdPattern(const nsSubstring& aString)
258 {
259     nsWhitespaceTokenizer tokenizer(aString);
260     while (tokenizer.hasMoreTokens()) {
261         // this can fail, XXX move to a Init(aString) method
262         nsCOMPtr<nsIAtom> atom = NS_Atomize(tokenizer.nextToken());
263         mIds.AppendObject(atom);
264     }
265 }
266 
matches(const txXPathNode & aNode,txIMatchContext * aContext)267 bool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
268 {
269     if (!txXPathNodeUtils::isElement(aNode)) {
270         return false;
271     }
272 
273     // Get a ID attribute, if there is
274     nsIContent* content = txXPathNativeNode::getContent(aNode);
275     NS_ASSERTION(content, "a Element without nsIContent");
276 
277     nsIAtom* id = content->GetID();
278     return id && mIds.IndexOf(id) > -1;
279 }
280 
getDefaultPriority()281 double txIdPattern::getDefaultPriority()
282 {
283     return 0.5;
284 }
285 
286 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txIdPattern)
TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txIdPattern)287 TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txIdPattern)
288 
289 #ifdef TX_TO_STRING
290 void
291 txIdPattern::toString(nsAString& aDest)
292 {
293 #ifdef DEBUG
294     aDest.AppendLiteral("txIdPattern{");
295 #endif
296     aDest.AppendLiteral("id('");
297     uint32_t k, count = mIds.Count() - 1;
298     for (k = 0; k < count; ++k) {
299         nsAutoString str;
300         mIds[k]->ToString(str);
301         aDest.Append(str);
302         aDest.Append(char16_t(' '));
303     }
304     nsAutoString str;
305     mIds[count]->ToString(str);
306     aDest.Append(str);
307     aDest.AppendLiteral("')");
308 #ifdef DEBUG
309     aDest.Append(char16_t('}'));
310 #endif
311 }
312 #endif
313 
314 /*
315  * txKeyPattern
316  *
317  * txKeyPattern matches if the given node is in the evalation of
318  * the key() function
319  * This resembles the key() function, but may only have LITERALs as
320  * argument.
321  */
322 
matches(const txXPathNode & aNode,txIMatchContext * aContext)323 bool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
324 {
325     txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
326     nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
327     NS_ENSURE_TRUE(contextDoc, false);
328 
329     RefPtr<txNodeSet> nodes;
330     nsresult rv = es->getKeyNodes(mName, *contextDoc, mValue, true,
331                                   getter_AddRefs(nodes));
332     NS_ENSURE_SUCCESS(rv, false);
333 
334     return nodes->contains(aNode);
335 }
336 
getDefaultPriority()337 double txKeyPattern::getDefaultPriority()
338 {
339     return 0.5;
340 }
341 
342 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txKeyPattern)
TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txKeyPattern)343 TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txKeyPattern)
344 
345 #ifdef TX_TO_STRING
346 void
347 txKeyPattern::toString(nsAString& aDest)
348 {
349 #ifdef DEBUG
350     aDest.AppendLiteral("txKeyPattern{");
351 #endif
352     aDest.AppendLiteral("key('");
353     nsAutoString tmp;
354     if (mPrefix) {
355         mPrefix->ToString(tmp);
356         aDest.Append(tmp);
357         aDest.Append(char16_t(':'));
358     }
359     mName.mLocalName->ToString(tmp);
360     aDest.Append(tmp);
361     aDest.AppendLiteral(", ");
362     aDest.Append(mValue);
363     aDest.AppendLiteral("')");
364 #ifdef DEBUG
365     aDest.Append(char16_t('}'));
366 #endif
367 }
368 #endif
369 
370 /*
371  * txStepPattern
372  *
373  * a txPattern to hold the NodeTest and the Predicates of a StepPattern
374  */
375 
matches(const txXPathNode & aNode,txIMatchContext * aContext)376 bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
377 {
378     NS_ASSERTION(mNodeTest, "Internal error");
379 
380     if (!mNodeTest->matches(aNode, aContext))
381         return false;
382 
383     txXPathTreeWalker walker(aNode);
384     if ((!mIsAttr &&
385          txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) ||
386         !walker.moveToParent()) {
387         return false;
388     }
389     if (isEmpty()) {
390         return true;
391     }
392 
393     /*
394      * Evaluate Predicates
395      *
396      * Copy all siblings/attributes matching mNodeTest to nodes
397      * Up to the last Predicate do
398      *  Foreach node in nodes
399      *   evaluate Predicate with node as context node
400      *   if the result is a number, check the context position,
401      *    otherwise convert to bool
402      *   if result is true, copy node to newNodes
403      *  if aNode is not member of newNodes, return false
404      *  nodes = newNodes
405      *
406      * For the last Predicate, evaluate Predicate with aNode as
407      *  context node, if the result is a number, check the position,
408      *  otherwise return the result converted to boolean
409      */
410 
411     // Create the context node set for evaluating the predicates
412     RefPtr<txNodeSet> nodes;
413     nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
414     NS_ENSURE_SUCCESS(rv, false);
415 
416     bool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
417                                walker.moveToFirstChild();
418     while (hasNext) {
419         if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
420             nodes->append(walker.getCurrentPosition());
421         }
422         hasNext = mIsAttr ? walker.moveToNextAttribute() :
423                             walker.moveToNextSibling();
424     }
425 
426     Expr* predicate = mPredicates[0];
427     RefPtr<txNodeSet> newNodes;
428     rv = aContext->recycler()->getNodeSet(getter_AddRefs(newNodes));
429     NS_ENSURE_SUCCESS(rv, false);
430 
431     uint32_t i, predLen = mPredicates.Length();
432     for (i = 1; i < predLen; ++i) {
433         newNodes->clear();
434         bool contextIsInPredicate = false;
435         txNodeSetContext predContext(nodes, aContext);
436         while (predContext.hasNext()) {
437             predContext.next();
438             RefPtr<txAExprResult> exprResult;
439             rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
440             NS_ENSURE_SUCCESS(rv, false);
441 
442             switch(exprResult->getResultType()) {
443                 case txAExprResult::NUMBER:
444                     // handle default, [position() == numberValue()]
445                     if ((double)predContext.position() ==
446                         exprResult->numberValue()) {
447                         const txXPathNode& tmp = predContext.getContextNode();
448                         if (tmp == aNode)
449                             contextIsInPredicate = true;
450                         newNodes->append(tmp);
451                     }
452                     break;
453                 default:
454                     if (exprResult->booleanValue()) {
455                         const txXPathNode& tmp = predContext.getContextNode();
456                         if (tmp == aNode)
457                             contextIsInPredicate = true;
458                         newNodes->append(tmp);
459                     }
460                     break;
461             }
462         }
463         // Move new NodeSet to the current one
464         nodes->clear();
465         nodes->append(*newNodes);
466         if (!contextIsInPredicate) {
467             return false;
468         }
469         predicate = mPredicates[i];
470     }
471     txForwardContext evalContext(aContext, aNode, nodes);
472     RefPtr<txAExprResult> exprResult;
473     rv = predicate->evaluate(&evalContext, getter_AddRefs(exprResult));
474     NS_ENSURE_SUCCESS(rv, false);
475 
476     if (exprResult->getResultType() == txAExprResult::NUMBER)
477         // handle default, [position() == numberValue()]
478         return ((double)evalContext.position() == exprResult->numberValue());
479 
480     return exprResult->booleanValue();
481 } // matches
482 
getDefaultPriority()483 double txStepPattern::getDefaultPriority()
484 {
485     if (isEmpty())
486         return mNodeTest->getDefaultPriority();
487     return 0.5;
488 }
489 
490 txPattern::Type
getType()491 txStepPattern::getType()
492 {
493   return STEP_PATTERN;
494 }
495 
TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txStepPattern)496 TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txStepPattern)
497 Expr*
498 txStepPattern::getSubExprAt(uint32_t aPos)
499 {
500     return PredicateList::getSubExprAt(aPos);
501 }
502 
503 void
setSubExprAt(uint32_t aPos,Expr * aExpr)504 txStepPattern::setSubExprAt(uint32_t aPos, Expr* aExpr)
505 {
506     PredicateList::setSubExprAt(aPos, aExpr);
507 }
508 
509 #ifdef TX_TO_STRING
510 void
toString(nsAString & aDest)511 txStepPattern::toString(nsAString& aDest)
512 {
513 #ifdef DEBUG
514     aDest.AppendLiteral("txStepPattern{");
515 #endif
516     if (mIsAttr)
517         aDest.Append(char16_t('@'));
518     if (mNodeTest)
519         mNodeTest->toString(aDest);
520 
521     PredicateList::toString(aDest);
522 #ifdef DEBUG
523     aDest.Append(char16_t('}'));
524 #endif
525 }
526 #endif
527