1 /*
2 * Copyright (C) 2005 Frerich Raabe <raabe@kde.org>
3 * Copyright (C) 2006, 2009 Apple Inc.
4 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "third_party/blink/renderer/core/xml/xpath_functions.h"
29
30 #include "base/stl_util.h"
31 #include "third_party/blink/renderer/core/dom/attr.h"
32 #include "third_party/blink/renderer/core/dom/element.h"
33 #include "third_party/blink/renderer/core/dom/processing_instruction.h"
34 #include "third_party/blink/renderer/core/dom/tree_scope.h"
35 #include "third_party/blink/renderer/core/xml/xpath_util.h"
36 #include "third_party/blink/renderer/core/xml/xpath_value.h"
37 #include "third_party/blink/renderer/core/xml_names.h"
38 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
39 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
40
41 #include <algorithm>
42 #include <limits>
43
44 namespace blink {
45 namespace xpath {
46
IsWhitespace(UChar c)47 static inline bool IsWhitespace(UChar c) {
48 return c == ' ' || c == '\n' || c == '\r' || c == '\t';
49 }
50
51 #define DEFINE_FUNCTION_CREATOR(Class) \
52 static Function* Create##Class() { return MakeGarbageCollected<Class>(); }
53
54 class Interval {
55 public:
56 static const int kInf = -1;
57
58 Interval();
59 Interval(int value);
60 Interval(int min, int max);
61
62 bool Contains(int value) const;
63
64 private:
65 int min_;
66 int max_;
67 };
68
69 struct FunctionRec {
70 typedef Function* (*FactoryFn)();
71 FactoryFn factory_fn;
72 Interval args;
73 };
74
75 static HashMap<String, FunctionRec>* g_function_map;
76
77 class FunLast final : public Function {
78 Value Evaluate(EvaluationContext&) const override;
ResultType() const79 Value::Type ResultType() const override { return Value::kNumberValue; }
80
81 public:
FunLast()82 FunLast() { SetIsContextSizeSensitive(true); }
83 };
84
85 class FunPosition final : public Function {
86 Value Evaluate(EvaluationContext&) const override;
ResultType() const87 Value::Type ResultType() const override { return Value::kNumberValue; }
88
89 public:
FunPosition()90 FunPosition() { SetIsContextPositionSensitive(true); }
91 };
92
93 class FunCount final : public Function {
94 Value Evaluate(EvaluationContext&) const override;
ResultType() const95 Value::Type ResultType() const override { return Value::kNumberValue; }
96 };
97
98 class FunId final : public Function {
99 Value Evaluate(EvaluationContext&) const override;
ResultType() const100 Value::Type ResultType() const override { return Value::kNodeSetValue; }
101 };
102
103 class FunLocalName final : public Function {
104 Value Evaluate(EvaluationContext&) const override;
ResultType() const105 Value::Type ResultType() const override { return Value::kStringValue; }
106
107 public:
FunLocalName()108 FunLocalName() {
109 SetIsContextNodeSensitive(true);
110 } // local-name() with no arguments uses context node.
111 };
112
113 class FunNamespaceURI final : public Function {
114 Value Evaluate(EvaluationContext&) const override;
ResultType() const115 Value::Type ResultType() const override { return Value::kStringValue; }
116
117 public:
FunNamespaceURI()118 FunNamespaceURI() {
119 SetIsContextNodeSensitive(true);
120 } // namespace-uri() with no arguments uses context node.
121 };
122
123 class FunName final : public Function {
124 Value Evaluate(EvaluationContext&) const override;
ResultType() const125 Value::Type ResultType() const override { return Value::kStringValue; }
126
127 public:
FunName()128 FunName() {
129 SetIsContextNodeSensitive(true);
130 } // name() with no arguments uses context node.
131 };
132
133 class FunString final : public Function {
134 Value Evaluate(EvaluationContext&) const override;
ResultType() const135 Value::Type ResultType() const override { return Value::kStringValue; }
136
137 public:
FunString()138 FunString() {
139 SetIsContextNodeSensitive(true);
140 } // string() with no arguments uses context node.
141 };
142
143 class FunConcat final : public Function {
144 Value Evaluate(EvaluationContext&) const override;
ResultType() const145 Value::Type ResultType() const override { return Value::kStringValue; }
146 };
147
148 class FunStartsWith final : public Function {
149 Value Evaluate(EvaluationContext&) const override;
ResultType() const150 Value::Type ResultType() const override { return Value::kBooleanValue; }
151 };
152
153 class FunContains final : public Function {
154 Value Evaluate(EvaluationContext&) const override;
ResultType() const155 Value::Type ResultType() const override { return Value::kBooleanValue; }
156 };
157
158 class FunSubstringBefore final : public Function {
159 Value Evaluate(EvaluationContext&) const override;
ResultType() const160 Value::Type ResultType() const override { return Value::kStringValue; }
161 };
162
163 class FunSubstringAfter final : public Function {
164 Value Evaluate(EvaluationContext&) const override;
ResultType() const165 Value::Type ResultType() const override { return Value::kStringValue; }
166 };
167
168 class FunSubstring final : public Function {
169 Value Evaluate(EvaluationContext&) const override;
ResultType() const170 Value::Type ResultType() const override { return Value::kStringValue; }
171 };
172
173 class FunStringLength final : public Function {
174 Value Evaluate(EvaluationContext&) const override;
ResultType() const175 Value::Type ResultType() const override { return Value::kNumberValue; }
176
177 public:
FunStringLength()178 FunStringLength() {
179 SetIsContextNodeSensitive(true);
180 } // string-length() with no arguments uses context node.
181 };
182
183 class FunNormalizeSpace final : public Function {
184 Value Evaluate(EvaluationContext&) const override;
ResultType() const185 Value::Type ResultType() const override { return Value::kStringValue; }
186
187 public:
FunNormalizeSpace()188 FunNormalizeSpace() {
189 SetIsContextNodeSensitive(true);
190 } // normalize-space() with no arguments uses context node.
191 };
192
193 class FunTranslate final : public Function {
194 Value Evaluate(EvaluationContext&) const override;
ResultType() const195 Value::Type ResultType() const override { return Value::kStringValue; }
196 };
197
198 class FunBoolean final : public Function {
199 Value Evaluate(EvaluationContext&) const override;
ResultType() const200 Value::Type ResultType() const override { return Value::kBooleanValue; }
201 };
202
203 class FunNot final : public Function {
204 Value Evaluate(EvaluationContext&) const override;
ResultType() const205 Value::Type ResultType() const override { return Value::kBooleanValue; }
206 };
207
208 class FunTrue final : public Function {
209 Value Evaluate(EvaluationContext&) const override;
ResultType() const210 Value::Type ResultType() const override { return Value::kBooleanValue; }
211 };
212
213 class FunFalse final : public Function {
214 Value Evaluate(EvaluationContext&) const override;
ResultType() const215 Value::Type ResultType() const override { return Value::kBooleanValue; }
216 };
217
218 class FunLang final : public Function {
219 Value Evaluate(EvaluationContext&) const override;
ResultType() const220 Value::Type ResultType() const override { return Value::kBooleanValue; }
221
222 public:
FunLang()223 FunLang() {
224 SetIsContextNodeSensitive(true);
225 } // lang() always works on context node.
226 };
227
228 class FunNumber final : public Function {
229 Value Evaluate(EvaluationContext&) const override;
ResultType() const230 Value::Type ResultType() const override { return Value::kNumberValue; }
231
232 public:
FunNumber()233 FunNumber() {
234 SetIsContextNodeSensitive(true);
235 } // number() with no arguments uses context node.
236 };
237
238 class FunSum final : public Function {
239 Value Evaluate(EvaluationContext&) const override;
ResultType() const240 Value::Type ResultType() const override { return Value::kNumberValue; }
241 };
242
243 class FunFloor final : public Function {
244 Value Evaluate(EvaluationContext&) const override;
ResultType() const245 Value::Type ResultType() const override { return Value::kNumberValue; }
246 };
247
248 class FunCeiling final : public Function {
249 Value Evaluate(EvaluationContext&) const override;
ResultType() const250 Value::Type ResultType() const override { return Value::kNumberValue; }
251 };
252
253 class FunRound final : public Function {
254 Value Evaluate(EvaluationContext&) const override;
ResultType() const255 Value::Type ResultType() const override { return Value::kNumberValue; }
256
257 public:
258 static double Round(double);
259 };
260
261 DEFINE_FUNCTION_CREATOR(FunLast)
DEFINE_FUNCTION_CREATOR(FunPosition)262 DEFINE_FUNCTION_CREATOR(FunPosition)
263 DEFINE_FUNCTION_CREATOR(FunCount)
264 DEFINE_FUNCTION_CREATOR(FunId)
265 DEFINE_FUNCTION_CREATOR(FunLocalName)
266 DEFINE_FUNCTION_CREATOR(FunNamespaceURI)
267 DEFINE_FUNCTION_CREATOR(FunName)
268
269 DEFINE_FUNCTION_CREATOR(FunString)
270 DEFINE_FUNCTION_CREATOR(FunConcat)
271 DEFINE_FUNCTION_CREATOR(FunStartsWith)
272 DEFINE_FUNCTION_CREATOR(FunContains)
273 DEFINE_FUNCTION_CREATOR(FunSubstringBefore)
274 DEFINE_FUNCTION_CREATOR(FunSubstringAfter)
275 DEFINE_FUNCTION_CREATOR(FunSubstring)
276 DEFINE_FUNCTION_CREATOR(FunStringLength)
277 DEFINE_FUNCTION_CREATOR(FunNormalizeSpace)
278 DEFINE_FUNCTION_CREATOR(FunTranslate)
279
280 DEFINE_FUNCTION_CREATOR(FunBoolean)
281 DEFINE_FUNCTION_CREATOR(FunNot)
282 DEFINE_FUNCTION_CREATOR(FunTrue)
283 DEFINE_FUNCTION_CREATOR(FunFalse)
284 DEFINE_FUNCTION_CREATOR(FunLang)
285
286 DEFINE_FUNCTION_CREATOR(FunNumber)
287 DEFINE_FUNCTION_CREATOR(FunSum)
288 DEFINE_FUNCTION_CREATOR(FunFloor)
289 DEFINE_FUNCTION_CREATOR(FunCeiling)
290 DEFINE_FUNCTION_CREATOR(FunRound)
291
292 #undef DEFINE_FUNCTION_CREATOR
293
294 inline Interval::Interval() : min_(kInf), max_(kInf) {}
295
Interval(int value)296 inline Interval::Interval(int value) : min_(value), max_(value) {}
297
Interval(int min,int max)298 inline Interval::Interval(int min, int max) : min_(min), max_(max) {}
299
Contains(int value) const300 inline bool Interval::Contains(int value) const {
301 if (min_ == kInf && max_ == kInf)
302 return true;
303
304 if (min_ == kInf)
305 return value <= max_;
306
307 if (max_ == kInf)
308 return value >= min_;
309
310 return value >= min_ && value <= max_;
311 }
312
SetArguments(HeapVector<Member<Expression>> & args)313 void Function::SetArguments(HeapVector<Member<Expression>>& args) {
314 DCHECK(!SubExprCount());
315
316 // Some functions use context node as implicit argument, so when explicit
317 // arguments are added, they may no longer be context node sensitive.
318 if (name_ != "lang" && !args.IsEmpty())
319 SetIsContextNodeSensitive(false);
320
321 for (Expression* arg : args)
322 AddSubExpression(arg);
323 }
324
Evaluate(EvaluationContext & context) const325 Value FunLast::Evaluate(EvaluationContext& context) const {
326 return context.size;
327 }
328
Evaluate(EvaluationContext & context) const329 Value FunPosition::Evaluate(EvaluationContext& context) const {
330 return context.position;
331 }
332
Evaluate(EvaluationContext & context) const333 Value FunId::Evaluate(EvaluationContext& context) const {
334 Value a = Arg(0)->Evaluate(context);
335 StringBuilder id_list; // A whitespace-separated list of IDs
336
337 if (a.IsNodeSet()) {
338 for (const auto& node : a.ToNodeSet(&context)) {
339 id_list.Append(StringValue(node));
340 id_list.Append(' ');
341 }
342 } else {
343 id_list.Append(a.ToString());
344 }
345
346 TreeScope& context_scope = context.node->GetTreeScope();
347 NodeSet* result(NodeSet::Create());
348 HeapHashSet<Member<Node>> result_set;
349
350 unsigned start_pos = 0;
351 unsigned length = id_list.length();
352 while (true) {
353 while (start_pos < length && IsWhitespace(id_list[start_pos]))
354 ++start_pos;
355
356 if (start_pos == length)
357 break;
358
359 unsigned end_pos = start_pos;
360 while (end_pos < length && !IsWhitespace(id_list[end_pos]))
361 ++end_pos;
362
363 // If there are several nodes with the same id, id() should return the first
364 // one. In WebKit, getElementById behaves so, too, although its behavior in
365 // this case is formally undefined.
366 Node* node = context_scope.getElementById(
367 AtomicString(id_list.Substring(start_pos, end_pos - start_pos)));
368 if (node && result_set.insert(node).is_new_entry)
369 result->Append(node);
370
371 start_pos = end_pos;
372 }
373
374 result->MarkSorted(false);
375
376 return Value(result, Value::kAdopt);
377 }
378
ExpandedNameLocalPart(Node * node)379 static inline String ExpandedNameLocalPart(Node* node) {
380 // The local part of an XPath expanded-name matches DOM local name for most
381 // node types, except for namespace nodes and processing instruction nodes.
382 // But note that Blink does not support namespace nodes.
383 switch (node->getNodeType()) {
384 case Node::kElementNode:
385 return To<Element>(node)->localName();
386 case Node::kAttributeNode:
387 return To<Attr>(node)->localName();
388 case Node::kProcessingInstructionNode:
389 return To<ProcessingInstruction>(node)->target();
390 default:
391 return String();
392 }
393 }
394
ExpandedNamespaceURI(Node * node)395 static inline String ExpandedNamespaceURI(Node* node) {
396 switch (node->getNodeType()) {
397 case Node::kElementNode:
398 return To<Element>(node)->namespaceURI();
399 case Node::kAttributeNode:
400 return To<Attr>(node)->namespaceURI();
401 default:
402 return String();
403 }
404 }
405
ExpandedName(Node * node)406 static inline String ExpandedName(Node* node) {
407 AtomicString prefix;
408
409 switch (node->getNodeType()) {
410 case Node::kElementNode:
411 prefix = To<Element>(node)->prefix();
412 break;
413 case Node::kAttributeNode:
414 prefix = To<Attr>(node)->prefix();
415 break;
416 default:
417 break;
418 }
419
420 return prefix.IsEmpty() ? ExpandedNameLocalPart(node)
421 : prefix + ":" + ExpandedNameLocalPart(node);
422 }
423
Evaluate(EvaluationContext & context) const424 Value FunLocalName::Evaluate(EvaluationContext& context) const {
425 if (ArgCount() > 0) {
426 Value a = Arg(0)->Evaluate(context);
427 if (!a.IsNodeSet())
428 return "";
429
430 Node* node = a.ToNodeSet(&context).FirstNode();
431 return node ? ExpandedNameLocalPart(node) : "";
432 }
433
434 return ExpandedNameLocalPart(context.node);
435 }
436
Evaluate(EvaluationContext & context) const437 Value FunNamespaceURI::Evaluate(EvaluationContext& context) const {
438 if (ArgCount() > 0) {
439 Value a = Arg(0)->Evaluate(context);
440 if (!a.IsNodeSet())
441 return "";
442
443 Node* node = a.ToNodeSet(&context).FirstNode();
444 return node ? ExpandedNamespaceURI(node) : "";
445 }
446
447 return ExpandedNamespaceURI(context.node);
448 }
449
Evaluate(EvaluationContext & context) const450 Value FunName::Evaluate(EvaluationContext& context) const {
451 if (ArgCount() > 0) {
452 Value a = Arg(0)->Evaluate(context);
453 if (!a.IsNodeSet())
454 return "";
455
456 Node* node = a.ToNodeSet(&context).FirstNode();
457 return node ? ExpandedName(node) : "";
458 }
459
460 return ExpandedName(context.node);
461 }
462
Evaluate(EvaluationContext & context) const463 Value FunCount::Evaluate(EvaluationContext& context) const {
464 Value a = Arg(0)->Evaluate(context);
465
466 return double(a.ToNodeSet(&context).size());
467 }
468
Evaluate(EvaluationContext & context) const469 Value FunString::Evaluate(EvaluationContext& context) const {
470 if (!ArgCount())
471 return Value(context.node).ToString();
472 return Arg(0)->Evaluate(context).ToString();
473 }
474
Evaluate(EvaluationContext & context) const475 Value FunConcat::Evaluate(EvaluationContext& context) const {
476 StringBuilder result;
477 result.ReserveCapacity(1024);
478
479 unsigned count = ArgCount();
480 for (unsigned i = 0; i < count; ++i) {
481 EvaluationContext cloned_context(context);
482 result.Append(Arg(i)->Evaluate(cloned_context).ToString());
483 }
484
485 return result.ToString();
486 }
487
Evaluate(EvaluationContext & context) const488 Value FunStartsWith::Evaluate(EvaluationContext& context) const {
489 EvaluationContext cloned_context(context);
490 String s1 = Arg(0)->Evaluate(context).ToString();
491 String s2 = Arg(1)->Evaluate(cloned_context).ToString();
492
493 if (s2.IsEmpty())
494 return true;
495
496 return s1.StartsWith(s2);
497 }
498
Evaluate(EvaluationContext & context) const499 Value FunContains::Evaluate(EvaluationContext& context) const {
500 EvaluationContext cloned_context(context);
501 String s1 = Arg(0)->Evaluate(context).ToString();
502 String s2 = Arg(1)->Evaluate(cloned_context).ToString();
503
504 if (s2.IsEmpty())
505 return true;
506
507 return s1.Contains(s2) != 0;
508 }
509
Evaluate(EvaluationContext & context) const510 Value FunSubstringBefore::Evaluate(EvaluationContext& context) const {
511 EvaluationContext cloned_context(context);
512 String s1 = Arg(0)->Evaluate(context).ToString();
513 String s2 = Arg(1)->Evaluate(cloned_context).ToString();
514
515 if (s2.IsEmpty())
516 return "";
517
518 wtf_size_t i = s1.Find(s2);
519
520 if (i == kNotFound)
521 return "";
522
523 return s1.Left(i);
524 }
525
Evaluate(EvaluationContext & context) const526 Value FunSubstringAfter::Evaluate(EvaluationContext& context) const {
527 EvaluationContext cloned_context(context);
528 String s1 = Arg(0)->Evaluate(context).ToString();
529 String s2 = Arg(1)->Evaluate(cloned_context).ToString();
530
531 wtf_size_t i = s1.Find(s2);
532 if (i == kNotFound)
533 return "";
534
535 return s1.Substring(i + s2.length());
536 }
537
538 // Returns |value| clamped to the range [lo, hi].
539 // TODO(dominicc): Replace with std::clamp when C++17 is allowed
540 // per <https://chromium-cpp.appspot.com/>
Clamp(const double value,const double lo,const double hi)541 static double Clamp(const double value, const double lo, const double hi) {
542 return std::min(hi, std::max(lo, value));
543 }
544
545 // Computes the 1-based start and end (exclusive) string indices for
546 // substring. This is all the positions [1, maxLen (inclusive)] where
547 // start <= position < start + len
ComputeSubstringStartEnd(double start,double len,double max_len)548 static std::pair<unsigned, unsigned> ComputeSubstringStartEnd(double start,
549 double len,
550 double max_len) {
551 DCHECK(std::isfinite(max_len));
552 const double end = start + len;
553 if (std::isnan(start) || std::isnan(end))
554 return std::make_pair(1, 1);
555 // Neither start nor end are NaN, but may still be +/- Inf
556 const double clamped_start = Clamp(start, 1, max_len + 1);
557 const double clamped_end = Clamp(end, clamped_start, max_len + 1);
558 return std::make_pair(static_cast<unsigned>(clamped_start),
559 static_cast<unsigned>(clamped_end));
560 }
561
562 // substring(string, number pos, number? len)
563 //
564 // Characters in string are indexed from 1. Numbers are doubles and
565 // substring is specified to work with IEEE-754 infinity, NaN, and
566 // XPath's bespoke rounding function, round.
567 //
568 // <https://www.w3.org/TR/xpath/#function-substring>
Evaluate(EvaluationContext & context) const569 Value FunSubstring::Evaluate(EvaluationContext& context) const {
570 EvaluationContext cloned_context1(context);
571 EvaluationContext cloned_context2(context);
572 String source_string = Arg(0)->Evaluate(context).ToString();
573 const double pos =
574 FunRound::Round(Arg(1)->Evaluate(cloned_context1).ToNumber());
575 const double len =
576 ArgCount() == 3
577 ? FunRound::Round(Arg(2)->Evaluate(cloned_context2).ToNumber())
578 : std::numeric_limits<double>::infinity();
579 const auto bounds =
580 ComputeSubstringStartEnd(pos, len, source_string.length());
581 if (bounds.second <= bounds.first)
582 return "";
583 return source_string.Substring(bounds.first - 1,
584 bounds.second - bounds.first);
585 }
586
Evaluate(EvaluationContext & context) const587 Value FunStringLength::Evaluate(EvaluationContext& context) const {
588 if (!ArgCount())
589 return Value(context.node).ToString().length();
590 return Arg(0)->Evaluate(context).ToString().length();
591 }
592
Evaluate(EvaluationContext & context) const593 Value FunNormalizeSpace::Evaluate(EvaluationContext& context) const {
594 // https://www.w3.org/TR/1999/REC-xpath-19991116/#function-normalize-space
595 String s = (ArgCount() == 0 ? Value(context.node) : Arg(0)->Evaluate(context))
596 .ToString();
597 return s.SimplifyWhiteSpace(IsXMLSpace);
598 }
599
Evaluate(EvaluationContext & context) const600 Value FunTranslate::Evaluate(EvaluationContext& context) const {
601 EvaluationContext cloned_context1(context);
602 EvaluationContext cloned_context2(context);
603 String s1 = Arg(0)->Evaluate(context).ToString();
604 String s2 = Arg(1)->Evaluate(cloned_context1).ToString();
605 String s3 = Arg(2)->Evaluate(cloned_context2).ToString();
606 StringBuilder result;
607
608 for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
609 UChar ch = s1[i1];
610 wtf_size_t i2 = s2.find(ch);
611
612 if (i2 == kNotFound)
613 result.Append(ch);
614 else if (i2 < s3.length())
615 result.Append(s3[i2]);
616 }
617
618 return result.ToString();
619 }
620
Evaluate(EvaluationContext & context) const621 Value FunBoolean::Evaluate(EvaluationContext& context) const {
622 return Arg(0)->Evaluate(context).ToBoolean();
623 }
624
Evaluate(EvaluationContext & context) const625 Value FunNot::Evaluate(EvaluationContext& context) const {
626 return !Arg(0)->Evaluate(context).ToBoolean();
627 }
628
Evaluate(EvaluationContext &) const629 Value FunTrue::Evaluate(EvaluationContext&) const {
630 return true;
631 }
632
Evaluate(EvaluationContext & context) const633 Value FunLang::Evaluate(EvaluationContext& context) const {
634 String lang = Arg(0)->Evaluate(context).ToString();
635
636 const Attribute* language_attribute = nullptr;
637 Node* node = context.node;
638 while (node) {
639 if (auto* element = DynamicTo<Element>(node))
640 language_attribute = element->Attributes().Find(xml_names::kLangAttr);
641
642 if (language_attribute)
643 break;
644 node = node->parentNode();
645 }
646
647 if (!language_attribute)
648 return false;
649
650 String lang_value = language_attribute->Value();
651 while (true) {
652 if (DeprecatedEqualIgnoringCase(lang_value, lang))
653 return true;
654
655 // Remove suffixes one by one.
656 wtf_size_t index = lang_value.ReverseFind('-');
657 if (index == kNotFound)
658 break;
659 lang_value = lang_value.Left(index);
660 }
661
662 return false;
663 }
664
Evaluate(EvaluationContext &) const665 Value FunFalse::Evaluate(EvaluationContext&) const {
666 return false;
667 }
668
Evaluate(EvaluationContext & context) const669 Value FunNumber::Evaluate(EvaluationContext& context) const {
670 if (!ArgCount())
671 return Value(context.node).ToNumber();
672 return Arg(0)->Evaluate(context).ToNumber();
673 }
674
Evaluate(EvaluationContext & context) const675 Value FunSum::Evaluate(EvaluationContext& context) const {
676 Value a = Arg(0)->Evaluate(context);
677 if (!a.IsNodeSet())
678 return 0.0;
679
680 double sum = 0.0;
681 const NodeSet& nodes = a.ToNodeSet(&context);
682 // To be really compliant, we should sort the node-set, as floating point
683 // addition is not associative. However, this is unlikely to ever become a
684 // practical issue, and sorting is slow.
685
686 for (const auto& node : nodes)
687 sum += Value(StringValue(node)).ToNumber();
688
689 return sum;
690 }
691
Evaluate(EvaluationContext & context) const692 Value FunFloor::Evaluate(EvaluationContext& context) const {
693 return floor(Arg(0)->Evaluate(context).ToNumber());
694 }
695
Evaluate(EvaluationContext & context) const696 Value FunCeiling::Evaluate(EvaluationContext& context) const {
697 return ceil(Arg(0)->Evaluate(context).ToNumber());
698 }
699
Round(double val)700 double FunRound::Round(double val) {
701 if (!std::isnan(val) && !std::isinf(val)) {
702 if (std::signbit(val) && val >= -0.5)
703 val *= 0; // negative zero
704 else
705 val = floor(val + 0.5);
706 }
707 return val;
708 }
709
Evaluate(EvaluationContext & context) const710 Value FunRound::Evaluate(EvaluationContext& context) const {
711 return Round(Arg(0)->Evaluate(context).ToNumber());
712 }
713
714 struct FunctionMapping {
715 const char* name;
716 FunctionRec function;
717 };
718
CreateFunctionMap()719 static void CreateFunctionMap() {
720 DCHECK(!g_function_map);
721 const FunctionMapping functions[] = {
722 {"boolean", {&CreateFunBoolean, 1}},
723 {"ceiling", {&CreateFunCeiling, 1}},
724 {"concat", {&CreateFunConcat, Interval(2, Interval::kInf)}},
725 {"contains", {&CreateFunContains, 2}},
726 {"count", {&CreateFunCount, 1}},
727 {"false", {&CreateFunFalse, 0}},
728 {"floor", {&CreateFunFloor, 1}},
729 {"id", {&CreateFunId, 1}},
730 {"lang", {&CreateFunLang, 1}},
731 {"last", {&CreateFunLast, 0}},
732 {"local-name", {&CreateFunLocalName, Interval(0, 1)}},
733 {"name", {&CreateFunName, Interval(0, 1)}},
734 {"namespace-uri", {&CreateFunNamespaceURI, Interval(0, 1)}},
735 {"normalize-space", {&CreateFunNormalizeSpace, Interval(0, 1)}},
736 {"not", {&CreateFunNot, 1}},
737 {"number", {&CreateFunNumber, Interval(0, 1)}},
738 {"position", {&CreateFunPosition, 0}},
739 {"round", {&CreateFunRound, 1}},
740 {"starts-with", {&CreateFunStartsWith, 2}},
741 {"string", {&CreateFunString, Interval(0, 1)}},
742 {"string-length", {&CreateFunStringLength, Interval(0, 1)}},
743 {"substring", {&CreateFunSubstring, Interval(2, 3)}},
744 {"substring-after", {&CreateFunSubstringAfter, 2}},
745 {"substring-before", {&CreateFunSubstringBefore, 2}},
746 {"sum", {&CreateFunSum, 1}},
747 {"translate", {&CreateFunTranslate, 3}},
748 {"true", {&CreateFunTrue, 0}},
749 };
750
751 g_function_map = new HashMap<String, FunctionRec>;
752 for (size_t i = 0; i < base::size(functions); ++i)
753 g_function_map->Set(functions[i].name, functions[i].function);
754 }
755
CreateFunction(const String & name)756 Function* CreateFunction(const String& name) {
757 HeapVector<Member<Expression>> args;
758 return CreateFunction(name, args);
759 }
760
CreateFunction(const String & name,HeapVector<Member<Expression>> & args)761 Function* CreateFunction(const String& name,
762 HeapVector<Member<Expression>>& args) {
763 if (!g_function_map)
764 CreateFunctionMap();
765
766 HashMap<String, FunctionRec>::iterator function_map_iter =
767 g_function_map->find(name);
768 FunctionRec* function_rec = nullptr;
769
770 if (function_map_iter == g_function_map->end() ||
771 !(function_rec = &function_map_iter->value)->args.Contains(args.size()))
772 return nullptr;
773
774 Function* function = function_rec->factory_fn();
775 function->SetArguments(args);
776 function->SetName(name);
777 return function;
778 }
779
780 } // namespace xpath
781 } // namespace blink
782