blob: 0c2d5581ac5d9ebabf8e80e0a97e19a9deca2a6a [file] [log] [blame]
andersca75fd42c2006-05-08 21:27:25 +00001/*
ap@webkit.org84b68192009-06-01 04:18:09 +00002 * Copyright (C) 2005 Frerich Raabe <raabe@kde.org>
darin@apple.com59755cf2013-10-10 02:46:27 +00003 * Copyright (C) 2006, 2009, 2013 Apple Inc. All rights reserved.
ap2a65b4c2007-01-30 19:01:59 +00004 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
andersca75fd42c2006-05-08 21:27:25 +00005 *
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 */
darin7ab31092006-05-10 04:59:57 +000027
andersca75fd42c2006-05-08 21:27:25 +000028#include "config.h"
darina9406af2006-06-04 23:03:41 +000029#include "XPathFunctions.h"
andersca75fd42c2006-05-08 21:27:25 +000030
darina51de912007-04-29 20:32:51 +000031#include "Element.h"
ap@webkit.org68a5bc62009-06-02 17:59:09 +000032#include "ProcessingInstruction.h"
rolandsteiner@chromium.org9d6b1e72011-05-03 17:17:44 +000033#include "TreeScope.h"
apd50012d2007-03-05 05:34:10 +000034#include "XMLNames.h"
apce79f362007-03-20 17:21:07 +000035#include "XPathUtil.h"
darina9406af2006-06-04 23:03:41 +000036#include <wtf/MathExtras.h>
darin@apple.com59755cf2013-10-10 02:46:27 +000037#include <wtf/NeverDestroyed.h>
commit-queue@webkit.orgc28243e2011-09-07 05:43:09 +000038#include <wtf/text/StringBuilder.h>
sfalkencd846002006-05-09 21:54:53 +000039
andersca75fd42c2006-05-08 21:27:25 +000040namespace WebCore {
41namespace XPath {
ap2a65b4c2007-01-30 19:01:59 +000042
43static inline bool isWhitespace(UChar c)
44{
45 return c == ' ' || c == '\n' || c == '\r' || c == '\t';
46}
47
darin@apple.com59755cf2013-10-10 02:46:27 +000048#define DEFINE_FUNCTION_CREATOR(Suffix) static std::unique_ptr<Function> createFunction##Suffix() { return std::make_unique<Fun##Suffix>(); }
andersca75fd42c2006-05-08 21:27:25 +000049
darina9406af2006-06-04 23:03:41 +000050class Interval {
andersca75fd42c2006-05-08 21:27:25 +000051public:
darina9406af2006-06-04 23:03:41 +000052 static const int Inf = -1;
andersca75fd42c2006-05-08 21:27:25 +000053
54 Interval();
55 Interval(int value);
56 Interval(int min, int max);
57
58 bool contains(int value) const;
59
andersca75fd42c2006-05-08 21:27:25 +000060private:
61 int m_min;
62 int m_max;
63};
64
andersca@apple.com16d2dd42014-01-16 23:08:24 +000065class FunLast final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +000066 virtual Value evaluate() const override;
67 virtual Value::Type resultType() const override { return Value::NumberValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +000068public:
69 FunLast() { setIsContextSizeSensitive(true); }
andersca75fd42c2006-05-08 21:27:25 +000070};
71
andersca@apple.com16d2dd42014-01-16 23:08:24 +000072class FunPosition final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +000073 virtual Value evaluate() const override;
74 virtual Value::Type resultType() const override { return Value::NumberValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +000075public:
76 FunPosition() { setIsContextPositionSensitive(true); }
andersca75fd42c2006-05-08 21:27:25 +000077};
78
andersca@apple.com16d2dd42014-01-16 23:08:24 +000079class FunCount final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +000080 virtual Value evaluate() const override;
81 virtual Value::Type resultType() const override { return Value::NumberValue; }
andersca75fd42c2006-05-08 21:27:25 +000082};
83
andersca@apple.com16d2dd42014-01-16 23:08:24 +000084class FunId final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +000085 virtual Value evaluate() const override;
86 virtual Value::Type resultType() const override { return Value::NodeSetValue; }
ap2a65b4c2007-01-30 19:01:59 +000087};
88
andersca@apple.com16d2dd42014-01-16 23:08:24 +000089class FunLocalName final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +000090 virtual Value evaluate() const override;
91 virtual Value::Type resultType() const override { return Value::StringValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +000092public:
93 FunLocalName() { setIsContextNodeSensitive(true); } // local-name() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +000094};
95
andersca@apple.com16d2dd42014-01-16 23:08:24 +000096class FunNamespaceURI final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +000097 virtual Value evaluate() const override;
98 virtual Value::Type resultType() const override { return Value::StringValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +000099public:
100 FunNamespaceURI() { setIsContextNodeSensitive(true); } // namespace-uri() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +0000101};
102
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000103class FunName final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000104 virtual Value evaluate() const override;
105 virtual Value::Type resultType() const override { return Value::StringValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +0000106public:
107 FunName() { setIsContextNodeSensitive(true); } // name() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +0000108};
109
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000110class FunString final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000111 virtual Value evaluate() const override;
112 virtual Value::Type resultType() const override { return Value::StringValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +0000113public:
114 FunString() { setIsContextNodeSensitive(true); } // string() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +0000115};
116
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000117class FunConcat final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000118 virtual Value evaluate() const override;
119 virtual Value::Type resultType() const override { return Value::StringValue; }
andersca75fd42c2006-05-08 21:27:25 +0000120};
121
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000122class FunStartsWith final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000123 virtual Value evaluate() const override;
124 virtual Value::Type resultType() const override { return Value::BooleanValue; }
andersca75fd42c2006-05-08 21:27:25 +0000125};
126
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000127class FunContains final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000128 virtual Value evaluate() const override;
129 virtual Value::Type resultType() const override { return Value::BooleanValue; }
andersca75fd42c2006-05-08 21:27:25 +0000130};
131
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000132class FunSubstringBefore final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000133 virtual Value evaluate() const override;
134 virtual Value::Type resultType() const override { return Value::StringValue; }
andersca75fd42c2006-05-08 21:27:25 +0000135};
136
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000137class FunSubstringAfter final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000138 virtual Value evaluate() const override;
139 virtual Value::Type resultType() const override { return Value::StringValue; }
andersca75fd42c2006-05-08 21:27:25 +0000140};
141
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000142class FunSubstring final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000143 virtual Value evaluate() const override;
144 virtual Value::Type resultType() const override { return Value::StringValue; }
andersca75fd42c2006-05-08 21:27:25 +0000145};
146
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000147class FunStringLength final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000148 virtual Value evaluate() const override;
149 virtual Value::Type resultType() const override { return Value::NumberValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +0000150public:
151 FunStringLength() { setIsContextNodeSensitive(true); } // string-length() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +0000152};
153
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000154class FunNormalizeSpace final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000155 virtual Value evaluate() const override;
156 virtual Value::Type resultType() const override { return Value::StringValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +0000157public:
158 FunNormalizeSpace() { setIsContextNodeSensitive(true); } // normalize-space() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +0000159};
160
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000161class FunTranslate final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000162 virtual Value evaluate() const override;
163 virtual Value::Type resultType() const override { return Value::StringValue; }
andersca75fd42c2006-05-08 21:27:25 +0000164};
165
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000166class FunBoolean final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000167 virtual Value evaluate() const override;
168 virtual Value::Type resultType() const override { return Value::BooleanValue; }
andersca75fd42c2006-05-08 21:27:25 +0000169};
170
darina9406af2006-06-04 23:03:41 +0000171class FunNot : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000172 virtual Value evaluate() const override;
173 virtual Value::Type resultType() const override { return Value::BooleanValue; }
andersca75fd42c2006-05-08 21:27:25 +0000174};
175
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000176class FunTrue final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000177 virtual Value evaluate() const override;
178 virtual Value::Type resultType() const override { return Value::BooleanValue; }
andersca75fd42c2006-05-08 21:27:25 +0000179};
180
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000181class FunFalse final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000182 virtual Value evaluate() const override;
183 virtual Value::Type resultType() const override { return Value::BooleanValue; }
andersca75fd42c2006-05-08 21:27:25 +0000184};
185
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000186class FunLang final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000187 virtual Value evaluate() const override;
188 virtual Value::Type resultType() const override { return Value::BooleanValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +0000189public:
190 FunLang() { setIsContextNodeSensitive(true); } // lang() always works on context node.
andersca75fd42c2006-05-08 21:27:25 +0000191};
192
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000193class FunNumber final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000194 virtual Value evaluate() const override;
195 virtual Value::Type resultType() const override { return Value::NumberValue; }
ap@webkit.org84b68192009-06-01 04:18:09 +0000196public:
197 FunNumber() { setIsContextNodeSensitive(true); } // number() with no arguments uses context node.
andersca75fd42c2006-05-08 21:27:25 +0000198};
199
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000200class FunSum final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000201 virtual Value evaluate() const override;
202 virtual Value::Type resultType() const override { return Value::NumberValue; }
andersca75fd42c2006-05-08 21:27:25 +0000203};
204
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000205class FunFloor final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000206 virtual Value evaluate() const override;
207 virtual Value::Type resultType() const override { return Value::NumberValue; }
andersca75fd42c2006-05-08 21:27:25 +0000208};
209
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000210class FunCeiling final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000211 virtual Value evaluate() const override;
212 virtual Value::Type resultType() const override { return Value::NumberValue; }
andersca75fd42c2006-05-08 21:27:25 +0000213};
214
andersca@apple.com16d2dd42014-01-16 23:08:24 +0000215class FunRound final : public Function {
commit-queue@webkit.org2dfe6cb2014-01-16 19:42:50 +0000216 virtual Value evaluate() const override;
217 virtual Value::Type resultType() const override { return Value::NumberValue; }
ap15b514e2007-03-05 20:39:41 +0000218public:
219 static double round(double);
andersca75fd42c2006-05-08 21:27:25 +0000220};
221
darin@apple.com59755cf2013-10-10 02:46:27 +0000222DEFINE_FUNCTION_CREATOR(Last)
223DEFINE_FUNCTION_CREATOR(Position)
224DEFINE_FUNCTION_CREATOR(Count)
225DEFINE_FUNCTION_CREATOR(Id)
226DEFINE_FUNCTION_CREATOR(LocalName)
227DEFINE_FUNCTION_CREATOR(NamespaceURI)
228DEFINE_FUNCTION_CREATOR(Name)
andersca75fd42c2006-05-08 21:27:25 +0000229
darin@apple.com59755cf2013-10-10 02:46:27 +0000230DEFINE_FUNCTION_CREATOR(String)
231DEFINE_FUNCTION_CREATOR(Concat)
232DEFINE_FUNCTION_CREATOR(StartsWith)
233DEFINE_FUNCTION_CREATOR(Contains)
234DEFINE_FUNCTION_CREATOR(SubstringBefore)
235DEFINE_FUNCTION_CREATOR(SubstringAfter)
236DEFINE_FUNCTION_CREATOR(Substring)
237DEFINE_FUNCTION_CREATOR(StringLength)
238DEFINE_FUNCTION_CREATOR(NormalizeSpace)
239DEFINE_FUNCTION_CREATOR(Translate)
andersca75fd42c2006-05-08 21:27:25 +0000240
darin@apple.com59755cf2013-10-10 02:46:27 +0000241DEFINE_FUNCTION_CREATOR(Boolean)
242DEFINE_FUNCTION_CREATOR(Not)
243DEFINE_FUNCTION_CREATOR(True)
244DEFINE_FUNCTION_CREATOR(False)
245DEFINE_FUNCTION_CREATOR(Lang)
andersca75fd42c2006-05-08 21:27:25 +0000246
darin@apple.com59755cf2013-10-10 02:46:27 +0000247DEFINE_FUNCTION_CREATOR(Number)
248DEFINE_FUNCTION_CREATOR(Sum)
249DEFINE_FUNCTION_CREATOR(Floor)
250DEFINE_FUNCTION_CREATOR(Ceiling)
251DEFINE_FUNCTION_CREATOR(Round)
andersca75fd42c2006-05-08 21:27:25 +0000252
253#undef DEFINE_FUNCTION_CREATOR
254
darina9406af2006-06-04 23:03:41 +0000255inline Interval::Interval()
256 : m_min(Inf), m_max(Inf)
andersca75fd42c2006-05-08 21:27:25 +0000257{
258}
259
darina9406af2006-06-04 23:03:41 +0000260inline Interval::Interval(int value)
261 : m_min(value), m_max(value)
andersca75fd42c2006-05-08 21:27:25 +0000262{
263}
264
darina9406af2006-06-04 23:03:41 +0000265inline Interval::Interval(int min, int max)
266 : m_min(min), m_max(max)
andersca75fd42c2006-05-08 21:27:25 +0000267{
268}
269
darina9406af2006-06-04 23:03:41 +0000270inline bool Interval::contains(int value) const
andersca75fd42c2006-05-08 21:27:25 +0000271{
272 if (m_min == Inf && m_max == Inf)
273 return true;
274
275 if (m_min == Inf)
276 return value <= m_max;
277
278 if (m_max == Inf)
279 return value >= m_min;
280
281 return value >= m_min && value <= m_max;
282}
283
darin@apple.com59755cf2013-10-10 02:46:27 +0000284void Function::setArguments(const String& name, Vector<std::unique_ptr<Expression>> arguments)
andersca75fd42c2006-05-08 21:27:25 +0000285{
darin@apple.com59755cf2013-10-10 02:46:27 +0000286 ASSERT(!subexpressionCount());
andersca75fd42c2006-05-08 21:27:25 +0000287
darin@apple.com59755cf2013-10-10 02:46:27 +0000288 // Functions that use the context node as an implicit argument are context node sensitive when they
289 // have no arguments, but when explicit arguments are added, they are no longer context node sensitive.
290 // As of this writing, the only exception to this is the "lang" function.
291 if (name != "lang" && !arguments.isEmpty())
ap@webkit.org84b68192009-06-01 04:18:09 +0000292 setIsContextNodeSensitive(false);
293
dbates@webkit.org0cefe4f2014-07-03 22:13:54 +0000294 setSubexpressions(WTF::move(arguments));
andersca75fd42c2006-05-08 21:27:25 +0000295}
296
weinig51128872007-03-08 03:47:34 +0000297Value FunLast::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000298{
darin7ab31092006-05-10 04:59:57 +0000299 return Expression::evaluationContext().size;
andersca75fd42c2006-05-08 21:27:25 +0000300}
301
weinig51128872007-03-08 03:47:34 +0000302Value FunPosition::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000303{
darin7ab31092006-05-10 04:59:57 +0000304 return Expression::evaluationContext().position;
andersca75fd42c2006-05-08 21:27:25 +0000305}
306
darin@apple.comded18ec2014-03-22 18:52:22 +0000307static AtomicString atomicSubstring(StringBuilder& builder, unsigned start, unsigned length)
308{
309 ASSERT(start <= builder.length());
310 ASSERT(length <= builder.length() - start);
311 if (builder.is8Bit())
312 return AtomicString(builder.characters8() + start, length);
313 return AtomicString(builder.characters16() + start, length);
314}
315
weinig51128872007-03-08 03:47:34 +0000316Value FunId::evaluate() const
ap2a65b4c2007-01-30 19:01:59 +0000317{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000318 Value a = argument(0).evaluate();
commit-queue@webkit.org585639b2014-01-13 06:26:50 +0000319 StringBuilder idList; // A whitespace-separated list of IDs
ap2a65b4c2007-01-30 19:01:59 +0000320
darin@apple.comded18ec2014-03-22 18:52:22 +0000321 if (!a.isNodeSet())
322 idList.append(a.toString());
323 else {
324 for (auto& node : a.toNodeSet()) {
325 idList.append(stringValue(node.get()));
commit-queue@webkit.org585639b2014-01-13 06:26:50 +0000326 idList.append(' ');
ap2a65b4c2007-01-30 19:01:59 +0000327 }
ap2a65b4c2007-01-30 19:01:59 +0000328 }
329
akling@apple.com3eb4c412013-10-06 02:30:52 +0000330 TreeScope& contextScope = evaluationContext().node->treeScope();
apce79f362007-03-20 17:21:07 +0000331 NodeSet result;
ap2a65b4c2007-01-30 19:01:59 +0000332 HashSet<Node*> resultSet;
333
commit-queue@webkit.orgc28243e2011-09-07 05:43:09 +0000334 unsigned startPos = 0;
335 unsigned length = idList.length();
ap2a65b4c2007-01-30 19:01:59 +0000336 while (true) {
337 while (startPos < length && isWhitespace(idList[startPos]))
338 ++startPos;
339
olivera0ee8b42007-10-12 15:16:31 +0000340 if (startPos == length)
341 break;
342
ap2a65b4c2007-01-30 19:01:59 +0000343 size_t endPos = startPos;
344 while (endPos < length && !isWhitespace(idList[endPos]))
345 ++endPos;
346
ap2a65b4c2007-01-30 19:01:59 +0000347 // If there are several nodes with the same id, id() should return the first one.
348 // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined.
darin@apple.comded18ec2014-03-22 18:52:22 +0000349 Node* node = contextScope.getElementById(atomicSubstring(idList, startPos, endPos - startPos));
caio.oliveira@openbossa.org4c11ee02012-03-29 18:48:23 +0000350 if (node && resultSet.add(node).isNewEntry)
ap2a65b4c2007-01-30 19:01:59 +0000351 result.append(node);
352
353 startPos = endPos;
354 }
355
apce79f362007-03-20 17:21:07 +0000356 result.markSorted(false);
357
dbates@webkit.org0cefe4f2014-07-03 22:13:54 +0000358 return Value(WTF::move(result));
ap2a65b4c2007-01-30 19:01:59 +0000359}
360
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000361static inline String expandedNameLocalPart(Node* node)
362{
363 // The local part of an XPath expanded-name matches DOM local name for most node types, except for namespace nodes and processing instruction nodes.
364 ASSERT(node->nodeType() != Node::XPATH_NAMESPACE_NODE); // Not supported yet.
cdumez@apple.coma9c60c92014-10-02 19:39:41 +0000365 if (is<ProcessingInstruction>(*node))
cdumez@apple.com6992a602014-09-28 05:21:30 +0000366 return downcast<ProcessingInstruction>(*node).target();
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000367 return node->localName().string();
368}
369
370static inline String expandedName(Node* node)
371{
372 const AtomicString& prefix = node->prefix();
373 return prefix.isEmpty() ? expandedNameLocalPart(node) : prefix + ":" + expandedNameLocalPart(node);
374}
375
weinig51128872007-03-08 03:47:34 +0000376Value FunLocalName::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000377{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000378 if (argumentCount() > 0) {
379 Value a = argument(0).evaluate();
apce79f362007-03-20 17:21:07 +0000380 if (!a.isNodeSet())
darin@apple.com59755cf2013-10-10 02:46:27 +0000381 return emptyString();
apce79f362007-03-20 17:21:07 +0000382
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000383 Node* node = a.toNodeSet().firstNode();
darin@apple.com59755cf2013-10-10 02:46:27 +0000384 return node ? expandedNameLocalPart(node) : emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000385 }
386
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000387 return expandedNameLocalPart(evaluationContext().node.get());
andersca75fd42c2006-05-08 21:27:25 +0000388}
389
weinig51128872007-03-08 03:47:34 +0000390Value FunNamespaceURI::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000391{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000392 if (argumentCount() > 0) {
393 Value a = argument(0).evaluate();
apce79f362007-03-20 17:21:07 +0000394 if (!a.isNodeSet())
darin@apple.com59755cf2013-10-10 02:46:27 +0000395 return emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000396
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000397 Node* node = a.toNodeSet().firstNode();
darin@apple.com59755cf2013-10-10 02:46:27 +0000398 return node ? node->namespaceURI().string() : emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000399 }
400
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000401 return evaluationContext().node->namespaceURI().string();
andersca75fd42c2006-05-08 21:27:25 +0000402}
403
weinig51128872007-03-08 03:47:34 +0000404Value FunName::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000405{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000406 if (argumentCount() > 0) {
407 Value a = argument(0).evaluate();
apce79f362007-03-20 17:21:07 +0000408 if (!a.isNodeSet())
darin@apple.com59755cf2013-10-10 02:46:27 +0000409 return emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000410
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000411 Node* node = a.toNodeSet().firstNode();
darin@apple.com59755cf2013-10-10 02:46:27 +0000412 return node ? expandedName(node) : emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000413 }
414
ap@webkit.org68a5bc62009-06-02 17:59:09 +0000415 return expandedName(evaluationContext().node.get());
andersca75fd42c2006-05-08 21:27:25 +0000416}
417
weinig51128872007-03-08 03:47:34 +0000418Value FunCount::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000419{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000420 Value a = argument(0).evaluate();
andersca75fd42c2006-05-08 21:27:25 +0000421
hausmann@webkit.orga2f0b902008-01-18 09:46:07 +0000422 return double(a.toNodeSet().size());
andersca75fd42c2006-05-08 21:27:25 +0000423}
424
weinig51128872007-03-08 03:47:34 +0000425Value FunString::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000426{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000427 if (!argumentCount())
ap142d0462007-02-05 06:06:53 +0000428 return Value(Expression::evaluationContext().node.get()).toString();
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000429 return argument(0).evaluate().toString();
andersca75fd42c2006-05-08 21:27:25 +0000430}
431
weinig51128872007-03-08 03:47:34 +0000432Value FunConcat::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000433{
commit-queue@webkit.orgc28243e2011-09-07 05:43:09 +0000434 StringBuilder result;
435 result.reserveCapacity(1024);
andersca75fd42c2006-05-08 21:27:25 +0000436
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000437 for (unsigned i = 0, count = argumentCount(); i < count; ++i) {
438 String str(argument(i).evaluate().toString());
commit-queue@webkit.orgc28243e2011-09-07 05:43:09 +0000439 result.append(str);
apf0b81e02007-03-30 20:31:26 +0000440 }
andersca75fd42c2006-05-08 21:27:25 +0000441
commit-queue@webkit.orgc28243e2011-09-07 05:43:09 +0000442 return result.toString();
andersca75fd42c2006-05-08 21:27:25 +0000443}
444
weinig51128872007-03-08 03:47:34 +0000445Value FunStartsWith::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000446{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000447 String s1 = argument(0).evaluate().toString();
448 String s2 = argument(1).evaluate().toString();
andersca75fd42c2006-05-08 21:27:25 +0000449
450 if (s2.isEmpty())
451 return true;
452
453 return s1.startsWith(s2);
454}
455
weinig51128872007-03-08 03:47:34 +0000456Value FunContains::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000457{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000458 String s1 = argument(0).evaluate().toString();
459 String s2 = argument(1).evaluate().toString();
andersca75fd42c2006-05-08 21:27:25 +0000460
461 if (s2.isEmpty())
462 return true;
463
464 return s1.contains(s2) != 0;
465}
466
weinig51128872007-03-08 03:47:34 +0000467Value FunSubstringBefore::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000468{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000469 String s1 = argument(0).evaluate().toString();
470 String s2 = argument(1).evaluate().toString();
andersca75fd42c2006-05-08 21:27:25 +0000471
472 if (s2.isEmpty())
darin@apple.com59755cf2013-10-10 02:46:27 +0000473 return emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000474
barraclough@apple.comd643fde2010-08-16 23:31:33 +0000475 size_t i = s1.find(s2);
andersca75fd42c2006-05-08 21:27:25 +0000476
barraclough@apple.comd643fde2010-08-16 23:31:33 +0000477 if (i == notFound)
darin@apple.com59755cf2013-10-10 02:46:27 +0000478 return emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000479
480 return s1.left(i);
481}
482
weinig51128872007-03-08 03:47:34 +0000483Value FunSubstringAfter::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000484{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000485 String s1 = argument(0).evaluate().toString();
486 String s2 = argument(1).evaluate().toString();
andersca75fd42c2006-05-08 21:27:25 +0000487
barraclough@apple.comd643fde2010-08-16 23:31:33 +0000488 size_t i = s1.find(s2);
489 if (i == notFound)
darin@apple.com59755cf2013-10-10 02:46:27 +0000490 return emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000491
oliver26d3e5b2007-10-12 15:36:14 +0000492 return s1.substring(i + s2.length());
andersca75fd42c2006-05-08 21:27:25 +0000493}
494
weinig51128872007-03-08 03:47:34 +0000495Value FunSubstring::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000496{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000497 String s = argument(0).evaluate().toString();
498 double doublePos = argument(1).evaluate().toNumber();
zandobersek@gmail.com9182d472013-02-13 23:01:21 +0000499 if (std::isnan(doublePos))
darin@apple.com59755cf2013-10-10 02:46:27 +0000500 return emptyString();
benm@google.com0c112c02010-07-09 14:53:59 +0000501 long pos = static_cast<long>(FunRound::round(doublePos));
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000502 bool haveLength = argumentCount() == 3;
andersca75fd42c2006-05-08 21:27:25 +0000503 long len = -1;
ap15b514e2007-03-05 20:39:41 +0000504 if (haveLength) {
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000505 double doubleLen = argument(2).evaluate().toNumber();
zandobersek@gmail.com9182d472013-02-13 23:01:21 +0000506 if (std::isnan(doubleLen))
darin@apple.com59755cf2013-10-10 02:46:27 +0000507 return emptyString();
ap15b514e2007-03-05 20:39:41 +0000508 len = static_cast<long>(FunRound::round(doubleLen));
509 }
andersca75fd42c2006-05-08 21:27:25 +0000510
511 if (pos > long(s.length()))
darin@apple.com59755cf2013-10-10 02:46:27 +0000512 return emptyString();
andersca75fd42c2006-05-08 21:27:25 +0000513
steveblock@google.comfbe6b1a2010-07-12 10:42:38 +0000514 if (pos < 1) {
515 if (haveLength) {
516 len -= 1 - pos;
517 if (len < 1)
darin@apple.com59755cf2013-10-10 02:46:27 +0000518 return emptyString();
steveblock@google.comfbe6b1a2010-07-12 10:42:38 +0000519 }
andersca75fd42c2006-05-08 21:27:25 +0000520 pos = 1;
andersca75fd42c2006-05-08 21:27:25 +0000521 }
522
darin7ab31092006-05-10 04:59:57 +0000523 return s.substring(pos - 1, len);
andersca75fd42c2006-05-08 21:27:25 +0000524}
525
weinig51128872007-03-08 03:47:34 +0000526Value FunStringLength::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000527{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000528 if (!argumentCount())
ap142d0462007-02-05 06:06:53 +0000529 return Value(Expression::evaluationContext().node.get()).toString().length();
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000530 return argument(0).evaluate().toString().length();
andersca75fd42c2006-05-08 21:27:25 +0000531}
532
weinig51128872007-03-08 03:47:34 +0000533Value FunNormalizeSpace::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000534{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000535 if (!argumentCount()) {
ap142d0462007-02-05 06:06:53 +0000536 String s = Value(Expression::evaluationContext().node.get()).toString();
apf0b81e02007-03-30 20:31:26 +0000537 return s.simplifyWhiteSpace();
andersca75fd42c2006-05-08 21:27:25 +0000538 }
539
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000540 String s = argument(0).evaluate().toString();
apf0b81e02007-03-30 20:31:26 +0000541 return s.simplifyWhiteSpace();
andersca75fd42c2006-05-08 21:27:25 +0000542}
543
weinig51128872007-03-08 03:47:34 +0000544Value FunTranslate::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000545{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000546 String s1 = argument(0).evaluate().toString();
547 String s2 = argument(1).evaluate().toString();
548 String s3 = argument(2).evaluate().toString();
abarth@webkit.org61a94cc2012-09-01 08:20:01 +0000549 StringBuilder result;
andersca75fd42c2006-05-08 21:27:25 +0000550
551 for (unsigned i1 = 0; i1 < s1.length(); ++i1) {
darin7ab31092006-05-10 04:59:57 +0000552 UChar ch = s1[i1];
barraclough@apple.comd643fde2010-08-16 23:31:33 +0000553 size_t i2 = s2.find(ch);
andersca75fd42c2006-05-08 21:27:25 +0000554
barraclough@apple.comd643fde2010-08-16 23:31:33 +0000555 if (i2 == notFound)
abarth@webkit.org61a94cc2012-09-01 08:20:01 +0000556 result.append(ch);
557 else if (i2 < s3.length())
558 result.append(s3[i2]);
andersca75fd42c2006-05-08 21:27:25 +0000559 }
560
abarth@webkit.org61a94cc2012-09-01 08:20:01 +0000561 return result.toString();
andersca75fd42c2006-05-08 21:27:25 +0000562}
563
weinig51128872007-03-08 03:47:34 +0000564Value FunBoolean::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000565{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000566 return argument(0).evaluate().toBoolean();
andersca75fd42c2006-05-08 21:27:25 +0000567}
568
weinig51128872007-03-08 03:47:34 +0000569Value FunNot::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000570{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000571 return !argument(0).evaluate().toBoolean();
andersca75fd42c2006-05-08 21:27:25 +0000572}
573
weinig51128872007-03-08 03:47:34 +0000574Value FunTrue::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000575{
576 return true;
577}
578
weinig51128872007-03-08 03:47:34 +0000579Value FunLang::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000580{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000581 String lang = argument(0).evaluate().toString();
andersca75fd42c2006-05-08 21:27:25 +0000582
cdumez@apple.coma3bc43f2014-09-30 19:59:49 +0000583 const Attribute* languageAttribute = nullptr;
andersca75fd42c2006-05-08 21:27:25 +0000584 Node* node = evaluationContext().node.get();
andersca75fd42c2006-05-08 21:27:25 +0000585 while (node) {
cdumez@apple.coma9c60c92014-10-02 19:39:41 +0000586 if (is<Element>(*node)) {
cdumez@apple.coma3bc43f2014-09-30 19:59:49 +0000587 Element& element = downcast<Element>(*node);
588 if (element.hasAttributes())
589 languageAttribute = element.findAttributeByName(XMLNames::langAttr);
caio.oliveira@openbossa.org062edfb2012-02-02 00:20:36 +0000590 }
darin@apple.come8c3a2a2009-02-02 17:29:54 +0000591 if (languageAttribute)
andersca75fd42c2006-05-08 21:27:25 +0000592 break;
andersca75fd42c2006-05-08 21:27:25 +0000593 node = node->parentNode();
594 }
595
darin@apple.come8c3a2a2009-02-02 17:29:54 +0000596 if (!languageAttribute)
andersca75fd42c2006-05-08 21:27:25 +0000597 return false;
598
darin@apple.come8c3a2a2009-02-02 17:29:54 +0000599 String langValue = languageAttribute->value();
apd50012d2007-03-05 05:34:10 +0000600 while (true) {
darin@apple.come8c3a2a2009-02-02 17:29:54 +0000601 if (equalIgnoringCase(langValue, lang))
apd50012d2007-03-05 05:34:10 +0000602 return true;
andersca75fd42c2006-05-08 21:27:25 +0000603
apd50012d2007-03-05 05:34:10 +0000604 // Remove suffixes one by one.
barraclough@apple.comd643fde2010-08-16 23:31:33 +0000605 size_t index = langValue.reverseFind('-');
606 if (index == notFound)
apd50012d2007-03-05 05:34:10 +0000607 break;
darin@apple.come8c3a2a2009-02-02 17:29:54 +0000608 langValue = langValue.left(index);
apd50012d2007-03-05 05:34:10 +0000609 }
darina9406af2006-06-04 23:03:41 +0000610
apd50012d2007-03-05 05:34:10 +0000611 return false;
andersca75fd42c2006-05-08 21:27:25 +0000612}
613
weinig51128872007-03-08 03:47:34 +0000614Value FunFalse::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000615{
616 return false;
617}
618
weinig51128872007-03-08 03:47:34 +0000619Value FunNumber::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000620{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000621 if (!argumentCount())
ap335b4ab2007-02-07 18:58:04 +0000622 return Value(Expression::evaluationContext().node.get()).toNumber();
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000623 return argument(0).evaluate().toNumber();
andersca75fd42c2006-05-08 21:27:25 +0000624}
625
weinig51128872007-03-08 03:47:34 +0000626Value FunSum::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000627{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000628 Value a = argument(0).evaluate();
apce79f362007-03-20 17:21:07 +0000629 if (!a.isNodeSet())
andersca75fd42c2006-05-08 21:27:25 +0000630 return 0.0;
andersca75fd42c2006-05-08 21:27:25 +0000631
632 double sum = 0.0;
apce79f362007-03-20 17:21:07 +0000633 const NodeSet& nodes = a.toNodeSet();
634 // To be really compliant, we should sort the node-set, as floating point addition is not associative.
635 // However, this is unlikely to ever become a practical issue, and sorting is slow.
636
commit-queue@webkit.org67f2be62015-05-19 16:29:37 +0000637 for (auto& node : nodes)
638 sum += Value(stringValue(node.get())).toNumber();
andersca75fd42c2006-05-08 21:27:25 +0000639
640 return sum;
641}
642
weinig51128872007-03-08 03:47:34 +0000643Value FunFloor::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000644{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000645 return floor(argument(0).evaluate().toNumber());
andersca75fd42c2006-05-08 21:27:25 +0000646}
647
weinig51128872007-03-08 03:47:34 +0000648Value FunCeiling::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000649{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000650 return ceil(argument(0).evaluate().toNumber());
andersca75fd42c2006-05-08 21:27:25 +0000651}
652
ap15b514e2007-03-05 20:39:41 +0000653double FunRound::round(double val)
654{
zandobersek@gmail.com9182d472013-02-13 23:01:21 +0000655 if (!std::isnan(val) && !std::isinf(val)) {
zandobersek@gmail.com8c24b7a2013-02-18 17:13:23 +0000656 if (std::signbit(val) && val >= -0.5)
ap15b514e2007-03-05 20:39:41 +0000657 val *= 0; // negative zero
658 else
659 val = floor(val + 0.5);
660 }
661 return val;
662}
663
weinig51128872007-03-08 03:47:34 +0000664Value FunRound::evaluate() const
andersca75fd42c2006-05-08 21:27:25 +0000665{
andersca@apple.com9f50b2a2013-08-28 14:31:16 +0000666 return round(argument(0).evaluate().toNumber());
andersca75fd42c2006-05-08 21:27:25 +0000667}
668
darin@apple.com59755cf2013-10-10 02:46:27 +0000669struct FunctionMapValue {
670 std::unique_ptr<Function> (*creationFunction)();
671 Interval argumentCountInterval;
eric@webkit.org44015492009-10-29 00:54:42 +0000672};
673
darin@apple.com59755cf2013-10-10 02:46:27 +0000674static void populateFunctionMap(HashMap<String, FunctionMapValue>& functionMap)
andersca75fd42c2006-05-08 21:27:25 +0000675{
darin@apple.com59755cf2013-10-10 02:46:27 +0000676 struct FunctionMapping {
677 const char* name;
678 FunctionMapValue function;
darina9406af2006-06-04 23:03:41 +0000679 };
darina9406af2006-06-04 23:03:41 +0000680
darin@apple.com59755cf2013-10-10 02:46:27 +0000681 static const FunctionMapping functions[] = {
682 { "boolean", { createFunctionBoolean, 1 } },
683 { "ceiling", { createFunctionCeiling, 1 } },
684 { "concat", { createFunctionConcat, Interval(2, Interval::Inf) } },
685 { "contains", { createFunctionContains, 2 } },
686 { "count", { createFunctionCount, 1 } },
687 { "false", { createFunctionFalse, 0 } },
688 { "floor", { createFunctionFloor, 1 } },
689 { "id", { createFunctionId, 1 } },
690 { "lang", { createFunctionLang, 1 } },
691 { "last", { createFunctionLast, 0 } },
692 { "local-name", { createFunctionLocalName, Interval(0, 1) } },
693 { "name", { createFunctionName, Interval(0, 1) } },
694 { "namespace-uri", { createFunctionNamespaceURI, Interval(0, 1) } },
695 { "normalize-space", { createFunctionNormalizeSpace, Interval(0, 1) } },
696 { "not", { createFunctionNot, 1 } },
697 { "number", { createFunctionNumber, Interval(0, 1) } },
698 { "position", { createFunctionPosition, 0 } },
699 { "round", { createFunctionRound, 1 } },
700 { "starts-with", { createFunctionStartsWith, 2 } },
701 { "string", { createFunctionString, Interval(0, 1) } },
702 { "string-length", { createFunctionStringLength, Interval(0, 1) } },
703 { "substring", { createFunctionSubstring, Interval(2, 3) } },
704 { "substring-after", { createFunctionSubstringAfter, 2 } },
705 { "substring-before", { createFunctionSubstringBefore, 2 } },
706 { "sum", { createFunctionSum, 1 } },
707 { "translate", { createFunctionTranslate, 3 } },
708 { "true", { createFunctionTrue, 0 } },
709 };
710
commit-queue@webkit.org67f2be62015-05-19 16:29:37 +0000711 for (auto& function : functions)
712 functionMap.add(function.name, function.function);
andersca75fd42c2006-05-08 21:27:25 +0000713}
714
darin@apple.com59755cf2013-10-10 02:46:27 +0000715std::unique_ptr<Function> Function::create(const String& name, unsigned numArguments)
andersca75fd42c2006-05-08 21:27:25 +0000716{
darin@apple.com59755cf2013-10-10 02:46:27 +0000717 static NeverDestroyed<HashMap<String, FunctionMapValue>> functionMap;
718 if (functionMap.get().isEmpty())
719 populateFunctionMap(functionMap);
darina9406af2006-06-04 23:03:41 +0000720
darin@apple.com59755cf2013-10-10 02:46:27 +0000721 auto it = functionMap.get().find(name);
722 if (it == functionMap.get().end())
723 return nullptr;
ap335b4ab2007-02-07 18:58:04 +0000724
darin@apple.com59755cf2013-10-10 02:46:27 +0000725 if (!it->value.argumentCountInterval.contains(numArguments))
726 return nullptr;
andersca75fd42c2006-05-08 21:27:25 +0000727
darin@apple.com59755cf2013-10-10 02:46:27 +0000728 return it->value.creationFunction();
729}
730
731std::unique_ptr<Function> Function::create(const String& name)
732{
733 return create(name, 0);
734}
735
736std::unique_ptr<Function> Function::create(const String& name, Vector<std::unique_ptr<Expression>> arguments)
737{
738 std::unique_ptr<Function> function = create(name, arguments.size());
739 if (function)
dbates@webkit.org0cefe4f2014-07-03 22:13:54 +0000740 function->setArguments(name, WTF::move(arguments));
andersca75fd42c2006-05-08 21:27:25 +0000741 return function;
742}
743
744}
745}