| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> | 2 * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> |
| 3 * Copyright (C) 2006, 2009 Apple Inc. | 3 * Copyright (C) 2006, 2009 Apple Inc. |
| 4 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> | 4 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
| 5 * | 5 * |
| 6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
| 7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
| 8 * are met: | 8 * are met: |
| 9 * | 9 * |
| 10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 #include "core/XMLNames.h" | 30 #include "core/XMLNames.h" |
| 31 #include "core/dom/Attr.h" | 31 #include "core/dom/Attr.h" |
| 32 #include "core/dom/Element.h" | 32 #include "core/dom/Element.h" |
| 33 #include "core/dom/ProcessingInstruction.h" | 33 #include "core/dom/ProcessingInstruction.h" |
| 34 #include "core/dom/TreeScope.h" | 34 #include "core/dom/TreeScope.h" |
| 35 #include "core/xml/XPathUtil.h" | 35 #include "core/xml/XPathUtil.h" |
| 36 #include "core/xml/XPathValue.h" | 36 #include "core/xml/XPathValue.h" |
| 37 #include "wtf/MathExtras.h" | 37 #include "wtf/MathExtras.h" |
| 38 #include "wtf/text/StringBuilder.h" | 38 #include "wtf/text/StringBuilder.h" |
| 39 | 39 |
| 40 #include <algorithm> |
| 41 #include <limits> |
| 42 |
| 40 namespace blink { | 43 namespace blink { |
| 41 namespace XPath { | 44 namespace XPath { |
| 42 | 45 |
| 43 static inline bool isWhitespace(UChar c) { | 46 static inline bool isWhitespace(UChar c) { |
| 44 return c == ' ' || c == '\n' || c == '\r' || c == '\t'; | 47 return c == ' ' || c == '\n' || c == '\r' || c == '\t'; |
| 45 } | 48 } |
| 46 | 49 |
| 47 #define DEFINE_FUNCTION_CREATOR(Class) \ | 50 #define DEFINE_FUNCTION_CREATOR(Class) \ |
| 48 static Function* create##Class() { return new Class; } | 51 static Function* create##Class() { return new Class; } |
| 49 | 52 |
| (...skipping 473 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 523 String s1 = arg(0)->evaluate(context).toString(); | 526 String s1 = arg(0)->evaluate(context).toString(); |
| 524 String s2 = arg(1)->evaluate(context).toString(); | 527 String s2 = arg(1)->evaluate(context).toString(); |
| 525 | 528 |
| 526 size_t i = s1.find(s2); | 529 size_t i = s1.find(s2); |
| 527 if (i == kNotFound) | 530 if (i == kNotFound) |
| 528 return ""; | 531 return ""; |
| 529 | 532 |
| 530 return s1.substring(i + s2.length()); | 533 return s1.substring(i + s2.length()); |
| 531 } | 534 } |
| 532 | 535 |
| 536 // Returns |value| clamped to the range [lo, hi]. |
| 537 // TODO(dominicc): Replace with std::clamp when C++17 is allowed |
| 538 // per <https://chromium-cpp.appspot.com/> |
| 539 static double clamp(const double value, const double lo, const double hi) { |
| 540 return std::min(hi, std::max(lo, value)); |
| 541 } |
| 542 |
| 543 // Computes the 1-based start and end (exclusive) string indices for |
| 544 // substring. This is all the positions [1, maxLen (inclusive)] where |
| 545 // start <= position < start + len |
| 546 static std::pair<unsigned, unsigned> computeSubstringStartEnd(double start, |
| 547 double len, |
| 548 double maxLen) { |
| 549 DCHECK(std::isfinite(maxLen)); |
| 550 const double end = start + len; |
| 551 if (std::isnan(start) || std::isnan(end)) |
| 552 return std::make_pair(1, 1); |
| 553 // Neither start nor end are NaN, but may still be +/- Inf |
| 554 const double clampedStart = clamp(start, 1, maxLen + 1); |
| 555 const double clampedEnd = clamp(end, clampedStart, maxLen + 1); |
| 556 return std::make_pair(static_cast<unsigned>(clampedStart), |
| 557 static_cast<unsigned>(clampedEnd)); |
| 558 } |
| 559 |
| 560 // substring(string, number pos, number? len) |
| 561 // |
| 562 // Characters in string are indexed from 1. Numbers are doubles and |
| 563 // substring is specified to work with IEEE-754 infinity, NaN, and |
| 564 // XPath's bespoke rounding function, round. |
| 565 // |
| 566 // <https://www.w3.org/TR/xpath/#function-substring> |
| 533 Value FunSubstring::evaluate(EvaluationContext& context) const { | 567 Value FunSubstring::evaluate(EvaluationContext& context) const { |
| 534 String s = arg(0)->evaluate(context).toString(); | 568 String sourceString = arg(0)->evaluate(context).toString(); |
| 535 double doublePos = arg(1)->evaluate(context).toNumber(); | 569 const double pos = FunRound::round(arg(1)->evaluate(context).toNumber()); |
| 536 if (std::isnan(doublePos)) | 570 const double len = argCount() == 3 |
| 571 ? FunRound::round(arg(2)->evaluate(context).toNumber()) |
| 572 : std::numeric_limits<double>::infinity(); |
| 573 const auto bounds = computeSubstringStartEnd(pos, len, sourceString.length()); |
| 574 if (bounds.second <= bounds.first) |
| 537 return ""; | 575 return ""; |
| 538 long pos = static_cast<long>(FunRound::round(doublePos)); | 576 return sourceString.substring(bounds.first - 1, bounds.second - bounds.first); |
| 539 bool haveLength = argCount() == 3; | |
| 540 long len = -1; | |
| 541 if (haveLength) { | |
| 542 double doubleLen = arg(2)->evaluate(context).toNumber(); | |
| 543 if (std::isnan(doubleLen)) | |
| 544 return ""; | |
| 545 len = static_cast<long>(FunRound::round(doubleLen)); | |
| 546 } | |
| 547 | |
| 548 if (pos > long(s.length())) | |
| 549 return ""; | |
| 550 | |
| 551 if (pos < 1) { | |
| 552 if (haveLength) { | |
| 553 len -= 1 - pos; | |
| 554 if (len < 1) | |
| 555 return ""; | |
| 556 } | |
| 557 pos = 1; | |
| 558 } | |
| 559 | |
| 560 return s.substring(pos - 1, len); | |
| 561 } | 577 } |
| 562 | 578 |
| 563 Value FunStringLength::evaluate(EvaluationContext& context) const { | 579 Value FunStringLength::evaluate(EvaluationContext& context) const { |
| 564 if (!argCount()) | 580 if (!argCount()) |
| 565 return Value(context.node.get()).toString().length(); | 581 return Value(context.node.get()).toString().length(); |
| 566 return arg(0)->evaluate(context).toString().length(); | 582 return arg(0)->evaluate(context).toString().length(); |
| 567 } | 583 } |
| 568 | 584 |
| 569 Value FunNormalizeSpace::evaluate(EvaluationContext& context) const { | 585 Value FunNormalizeSpace::evaluate(EvaluationContext& context) const { |
| 570 if (!argCount()) { | 586 if (!argCount()) { |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 return nullptr; | 766 return nullptr; |
| 751 | 767 |
| 752 Function* function = functionRec->factoryFn(); | 768 Function* function = functionRec->factoryFn(); |
| 753 function->setArguments(args); | 769 function->setArguments(args); |
| 754 function->setName(name); | 770 function->setName(name); |
| 755 return function; | 771 return function; |
| 756 } | 772 } |
| 757 | 773 |
| 758 } // namespace XPath | 774 } // namespace XPath |
| 759 } // namespace blink | 775 } // namespace blink |
| OLD | NEW |