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