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