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